summaryrefslogtreecommitdiff
path: root/nist_curve_key_exchange.cpp
diff options
context:
space:
mode:
authorThai Duong <thaidn@google.com>2015-03-25 20:14:57 -0700
committerShawn Willden <swillden@google.com>2015-11-23 07:22:18 -0700
commitfabacaf3e6019804cc8a98a2b8296be1d0125519 (patch)
tree3def16f36418347652a82f487d69822be60fcd69 /nist_curve_key_exchange.cpp
parent1181779c5e6c8627b94067d86db6a2f7d5309674 (diff)
downloadkeymaster-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.cpp127
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