diff options
author | Thai Duong <thaidn@google.com> | 2015-03-25 20:14:57 -0700 |
---|---|---|
committer | Shawn Willden <swillden@google.com> | 2015-11-23 07:22:18 -0700 |
commit | fabacaf3e6019804cc8a98a2b8296be1d0125519 (patch) | |
tree | 3def16f36418347652a82f487d69822be60fcd69 /nist_curve_key_exchange.cpp | |
parent | 1181779c5e6c8627b94067d86db6a2f7d5309674 (diff) | |
download | keymaster-fabacaf3e6019804cc8a98a2b8296be1d0125519.tar.gz |
ECIES: add ECIES-KEM. This version supports HKDF and ECDH with NIST curves.
Change-Id: I5af3215e96bb015049574aa18327cd7f7499dbd3
Diffstat (limited to 'nist_curve_key_exchange.cpp')
-rw-r--r-- | nist_curve_key_exchange.cpp | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/nist_curve_key_exchange.cpp b/nist_curve_key_exchange.cpp new file mode 100644 index 0000000..0dd3a54 --- /dev/null +++ b/nist_curve_key_exchange.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2015 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 "nist_curve_key_exchange.h" + +#include <openssl/ec.h> +#include <openssl/ecdh.h> +#include <openssl/err.h> +#include <openssl/evp.h> + +#include "openssl_err.h" + +namespace keymaster { + +NistCurveKeyExchange::NistCurveKeyExchange(EC_KEY* private_key, keymaster_error_t* error) + : private_key_(private_key) { + if (!private_key_.get() || !EC_KEY_check_key(private_key_.get())) { + *error = KM_ERROR_INVALID_ARGUMENT; + return; + } + *error = ExtractPublicKey(); +} + +/* static */ +NistCurveKeyExchange* NistCurveKeyExchange::GenerateKeyExchange(keymaster_ec_curve_t curve) { + int curve_name; + switch (curve) { + case KM_EC_CURVE_P_224: + curve_name = NID_secp224r1; + break; + case KM_EC_CURVE_P_256: + curve_name = NID_X9_62_prime256v1; + break; + case KM_EC_CURVE_P_384: + curve_name = NID_secp384r1; + break; + case KM_EC_CURVE_P_521: + curve_name = NID_secp521r1; + break; + default: + LOG_E("Not a NIST curve: %d", curve); + return nullptr; + } + + UniquePtr<EC_KEY, EC_KEY_Delete> key(EC_KEY_new_by_curve_name(curve_name)); + if (!key.get() || !EC_KEY_generate_key(key.get())) { + return nullptr; + } + keymaster_error_t error; + NistCurveKeyExchange* key_exchange = new NistCurveKeyExchange(key.release(), &error); + if (error != KM_ERROR_OK) { + return nullptr; + } + return key_exchange; +} + +keymaster_error_t NistCurveKeyExchange::ExtractPublicKey() { + const EC_GROUP* group = EC_KEY_get0_group(private_key_.get()); + size_t field_len_bits; + keymaster_error_t error = ec_get_group_size(group, &field_len_bits); + if (error != KM_ERROR_OK) + return error; + + shared_secret_len_ = (field_len_bits + 7) / 8; + public_key_len_ = 1 + 2 * shared_secret_len_; + public_key_.reset(new uint8_t[public_key_len_]); + if (EC_POINT_point2oct(group, EC_KEY_get0_public_key(private_key_.get()), + POINT_CONVERSION_UNCOMPRESSED, public_key_.get(), public_key_len_, + nullptr /* ctx */) != public_key_len_) { + return TranslateLastOpenSslError(); + } + return KM_ERROR_OK; +} + +bool NistCurveKeyExchange::CalculateSharedKey(const Buffer& peer_public_value, + Buffer* out_result) const { + + return CalculateSharedKey(peer_public_value.peek_read(), peer_public_value.available_read(), + out_result); +} + +bool NistCurveKeyExchange::CalculateSharedKey(const uint8_t* peer_public_value, + size_t peer_public_value_len, + Buffer* out_result) const { + const EC_GROUP* group = EC_KEY_get0_group(private_key_.get()); + UniquePtr<EC_POINT, EC_POINT_Delete> point(EC_POINT_new(group)); + if (!point.get() || + !EC_POINT_oct2point(/* also test if point is on curve */ + group, point.get(), peer_public_value, peer_public_value_len, + nullptr /* ctx */) || + !EC_POINT_is_on_curve(group, point.get(), nullptr /* ctx */)) { + LOG_E("Can't convert peer public value to point: %d", TranslateLastOpenSslError()); + return false; + } + + UniquePtr<uint8_t[]> result(new uint8_t[shared_secret_len_]); + if (ECDH_compute_key(result.get(), shared_secret_len_, point.get(), private_key_.get(), + nullptr /* kdf */) != static_cast<int>(shared_secret_len_)) { + LOG_E("Can't compute ECDH shared key: %d", TranslateLastOpenSslError()); + return false; + } + + out_result->Reinitialize(result.get(), shared_secret_len_); + return true; +} + +bool NistCurveKeyExchange::public_value(Buffer* public_value) const { + if (public_key_.get() != nullptr && public_key_len_ != 0) { + return public_value->Reinitialize(public_key_.get(), public_key_len_); + } + return false; +} + +} // namespace keymaster
\ No newline at end of file |