blob: ce3a0ef1a8aeff262080833762380d7d58380cdf [file] [log] [blame]
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "import_key.h"
#include "proto_utils.h"
#include <android-base/logging.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/rsa.h>
#include <openssl/pkcs8.h>
namespace android {
namespace hardware {
namespace keymaster {
// HAL
using ::android::hardware::keymaster::V4_0::Algorithm;
using ::android::hardware::keymaster::V4_0::EcCurve;
using ::android::hardware::keymaster::V4_0::KeyFormat;
using ::android::hardware::keymaster::V4_0::Tag;
using ::android::hardware::keymaster::V4_0::TagType;
// App
using ::nugget::app::keymaster::ECKey;
// BoringSSL
using bssl::UniquePtr;
// std
using std::unique_ptr;
static EVP_PKEY *evp_from_pkcs8_bytes(const uint8_t *bytes, size_t len)
{
bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> pkcs8(
d2i_PKCS8_PRIV_KEY_INFO(NULL, &bytes, len));
if (pkcs8.get() == NULL) {
// Translate error.
return nullptr;
}
return EVP_PKCS82PKEY(pkcs8.get());
}
static ErrorCode import_key_rsa(const tag_map_t& params,
const hidl_vec<uint8_t>& keyData,
ImportKeyRequest *request)
{
const uint32_t *keySize = nullptr;
if (params.find(Tag::KEY_SIZE) != params.end()) {
const vector<KeyParameter>& v = params.find(Tag::KEY_SIZE)->second;
keySize = &v[0].f.integer;
}
const uint64_t *publicExponent = nullptr;
if (params.find(Tag::RSA_PUBLIC_EXPONENT) != params.end()) {
const vector<KeyParameter>& v =
params.find(Tag::RSA_PUBLIC_EXPONENT)->second;
publicExponent = &v[0].f.longInteger;
}
bssl::UniquePtr<EVP_PKEY> pkey;
pkey.reset(evp_from_pkcs8_bytes(&keyData[0], keyData.size()));
if (pkey.get() == nullptr) {
// Parse error.
LOG(ERROR) << "ImportKey request: failed to parse PKCS8";
return ErrorCode::INVALID_ARGUMENT;
}
const RSA *rsa = EVP_PKEY_get0_RSA(pkey.get());
if (rsa == nullptr) {
LOG(ERROR) << "ImportKey request: PKCS8 key is not RSA";
return ErrorCode::INVALID_ARGUMENT;
}
size_t parsedKeySize = RSA_size(rsa) * 8;
if (keySize != nullptr && parsedKeySize != *keySize) {
// If specified, key size must match the PKCS8 blob.
LOG(ERROR) << "ImportKey request: key size parameter mis-match";
return ErrorCode::IMPORT_PARAMETER_MISMATCH;
}
const BIGNUM *n;
const BIGNUM *e;
const BIGNUM *d;
RSA_get0_key(rsa, &n, &e, &d);
if (publicExponent != nullptr && BN_get_word(e) != *publicExponent) {
// If specified, the public exponent must match the PKCS8 blob.
LOG(ERROR) << "ImportKey request: invalid publicExponent tag: "
<< *publicExponent
<< " expected: "
<< BN_get_word(e);
return ErrorCode::IMPORT_PARAMETER_MISMATCH;
}
// Key data may be invalid, and will be validated on the device
// anyway, so avoid duplicate work here.
// Public exponent.
request->mutable_rsa()->set_e(BN_get_word(e));
// Private exponent, zero-pad upto size of n.
bssl::UniquePtr<uint8_t> d_buf(
reinterpret_cast<uint8_t *>(OPENSSL_malloc(BN_num_bytes(n))));
if (!BN_bn2le_padded(d_buf.get(), BN_num_bytes(n), d)) {
LOG(ERROR) << "ImportKey request: bn2le failed";
return ErrorCode::UNKNOWN_ERROR;
}
request->mutable_rsa()->set_d(d_buf.get(), BN_num_bytes(n));
// Modulus.
bssl::UniquePtr<uint8_t> n_buf(
reinterpret_cast<uint8_t *>(OPENSSL_malloc(BN_num_bytes(n))));
if (!BN_bn2le_padded(n_buf.get(), BN_num_bytes(n), n)) {
LOG(ERROR) << "ImportKey request: bn2le_padded failed";
return ErrorCode::UNKNOWN_ERROR;
}
request->mutable_rsa()->set_n(n_buf.get(), BN_num_bytes(n));
return ErrorCode::OK;
}
static ErrorCode import_key_ec(const tag_map_t& params,
const hidl_vec<uint8_t>& keyData,
ImportKeyRequest *request)
{
const EcCurve *curve_id = nullptr;
if (params.find(Tag::EC_CURVE) != params.end()) {
const vector<KeyParameter>& v = params.find(Tag::EC_CURVE)->second;
curve_id = &v[0].f.ecCurve;
}
const uint32_t *key_size = nullptr;
if (params.find(Tag::KEY_SIZE) != params.end()) {
const vector<KeyParameter>& v = params.find(Tag::KEY_SIZE)->second;
key_size = &v[0].f.integer;
}
bssl::UniquePtr<EVP_PKEY> pkey;
pkey.reset(evp_from_pkcs8_bytes(&keyData[0], keyData.size()));
if (pkey.get() == nullptr) {
// Parse error.
LOG(ERROR) << "ImportKey request: failed to parse PKCS8";
return ErrorCode::INVALID_ARGUMENT;
}
const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey.get());
if (ec_key == nullptr) {
LOG(ERROR) << "ImportKey request: PKCS8 key is not EC";
return ErrorCode::INVALID_ARGUMENT;
}
EcCurve parsed_curve_id;
size_t parsed_key_size;
const EC_GROUP *group = EC_KEY_get0_group(ec_key);
switch (EC_GROUP_get_curve_name(group)) {
case NID_secp224r1:
parsed_curve_id = EcCurve::P_224;
parsed_key_size = 224;
break;
case NID_X9_62_prime256v1:
parsed_curve_id = EcCurve::P_256;
parsed_key_size = 256;
break;
case NID_secp384r1:
parsed_curve_id = EcCurve::P_384;
parsed_key_size = 384;
break;
case NID_secp521r1:
parsed_curve_id = EcCurve::P_521;
parsed_key_size = 521;
break;
default:
// Unsupported curve.
return ErrorCode::INVALID_ARGUMENT;
}
if (curve_id != nullptr && *curve_id != parsed_curve_id) {
// Parameter mis-match.
LOG(ERROR) << "ImportKey: curve-id does not match PKCS8";
return ErrorCode::IMPORT_PARAMETER_MISMATCH;
}
if (key_size != nullptr && *key_size != parsed_key_size) {
// Parameter mis-match.
LOG(ERROR) << "ImportKey: key-size does not match PKCS8";
return ErrorCode::IMPORT_PARAMETER_MISMATCH;
}
const BIGNUM *d = EC_KEY_get0_private_key(ec_key);
const EC_POINT *pub_key = EC_KEY_get0_public_key(ec_key);
bssl::UniquePtr<BIGNUM> x(BN_new());
bssl::UniquePtr<BIGNUM> y(BN_new());
if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key),
pub_key, x.get(), y.get(), NULL)) {
LOG(ERROR) << "ImportKey: failed to get public key in affine coordinates";
return ErrorCode::INVALID_ARGUMENT;
}
// Key data may be invalid, and will be validated on the device
// anyway, so avoid duplicate work here.
// Curve parameter.
request->mutable_ec()->set_curve_id((uint32_t)parsed_curve_id);
// Private key.
const size_t field_size = (parsed_key_size + 7) >> 3;
unique_ptr<uint8_t[]> d_buf(new uint8_t[field_size]);
if (!BN_bn2le_padded(d_buf.get(), field_size, d)) {
LOG(ERROR) << "ImportKey request: bn2le(d) failed";
return ErrorCode::UNKNOWN_ERROR;
}
request->mutable_ec()->set_d(d_buf.get(), field_size);
// Public key.
unique_ptr<uint8_t[]> x_buf(new uint8_t[field_size]);
if (!BN_bn2le_padded(x_buf.get(), field_size, x.get())) {
LOG(ERROR) << "ImportKey request: bn2le(x) failed";
return ErrorCode::UNKNOWN_ERROR;
}
request->mutable_ec()->set_x(x_buf.get(), field_size);
unique_ptr<uint8_t[]> y_buf(new uint8_t[field_size]);
if (!BN_bn2le_padded(y_buf.get(), field_size, y.get())) {
LOG(ERROR) << "ImportKey request: bn2le(y) failed";
return ErrorCode::UNKNOWN_ERROR;
}
request->mutable_ec()->set_y(y_buf.get(), field_size);
return ErrorCode::OK;
}
static ErrorCode import_key_raw(const tag_map_t& params,
Algorithm algorithm,
const hidl_vec<uint8_t>& keyData,
ImportKeyRequest *request)
{
if (algorithm != Algorithm::AES && algorithm != Algorithm::TRIPLE_DES &&
algorithm != Algorithm::HMAC) {
LOG(ERROR) << "ImportKey request: unsupported algorithm";
return ErrorCode::UNSUPPORTED_ALGORITHM;
}
const uint32_t *key_size = nullptr;
if (params.find(Tag::KEY_SIZE) != params.end()) {
const vector<KeyParameter>& v = params.find(Tag::KEY_SIZE)->second;
key_size = &v[0].f.integer;
}
if (algorithm != Algorithm::TRIPLE_DES) {
if (key_size != nullptr && *key_size != keyData.size() * 8) {
LOG(ERROR) << "ImportKey request: mis-matched KEY_SIZE tag: "
<< ((key_size == NULL) ? -1 : *key_size)
<< " provided data size: "
<< keyData.size();
return ErrorCode::IMPORT_PARAMETER_MISMATCH;
}
} else {
if ((key_size != nullptr && *key_size != 168) ||
keyData.size() != 24) {
LOG(ERROR) << "ImportKey request: mis-matched DES KEY_SIZE tag: "
<< ((key_size == NULL) ? -1 : *key_size)
<< " provided data size: "
<< keyData.size();
return ErrorCode::IMPORT_PARAMETER_MISMATCH;
}
LOG(ERROR) << "ImportKey request: DES OK: ";
}
if (keyData.size() == 0) {
LOG(ERROR) << "ImportKey request: invalid key size 0";
return ErrorCode::IMPORT_PARAMETER_MISMATCH;
}
request->mutable_symmetric_key()->set_material(
keyData.data(), keyData.size());
return ErrorCode::OK;
}
ErrorCode import_key_request(const hidl_vec<KeyParameter>& params,
KeyFormat keyFormat,
const hidl_vec<uint8_t>& keyData,
ImportKeyRequest *request) {
const enum Algorithm *algorithm;
if (keyFormat != KeyFormat::PKCS8 && keyFormat != KeyFormat::RAW) {
return ErrorCode::UNSUPPORTED_KEY_FORMAT;
}
ErrorCode error;
tag_map_t tag_map;
error = hidl_params_to_map(params, &tag_map);
if (error != ErrorCode::OK) {
return error;
}
if (tag_map.find(Tag::ALGORITHM) != tag_map.end()) {
// Algorithm is a required parameter.
const vector<KeyParameter>& v = tag_map.find(Tag::ALGORITHM)->second;
algorithm = &v[0].f.algorithm;
} else {
LOG(ERROR) << "ImportKey request: Algorithm Tag missing";
return ErrorCode::INVALID_ARGUMENT;
}
if (keyFormat == KeyFormat::PKCS8) {
switch (*algorithm) {
case Algorithm::RSA:
error = import_key_rsa(tag_map, keyData, request);
break;
case Algorithm::EC:
error = import_key_ec(tag_map, keyData, request);
break;
default:
LOG(ERROR) << "ImportKey request: unsupported algoritm: "
<< (uint32_t)*algorithm;
return ErrorCode::UNSUPPORTED_ALGORITHM;
break;
}
} else {
error = import_key_raw(tag_map, *algorithm, keyData, request);
}
if (error != ErrorCode::OK) {
return error;
}
error = map_params_to_pb(tag_map, request->mutable_params());
if (error != ErrorCode::OK) {
LOG(ERROR) << "ImportKey request: failed to map params to pb: "
<< (uint32_t) error;
return error;
}
return ErrorCode::OK;
}
} // namespace keymaster
} // hardware
} // android