summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThai Duong <thaidn@google.com>2015-11-23 13:35:28 +0000
committerandroid-build-merger <android-build-merger@google.com>2015-11-23 13:35:28 +0000
commita6ef4ee1a2e3527a9b4f37c3de2f2c7bac9a33a7 (patch)
treed0347f109cfaab09ac2a1902849efdb238ed5c08
parent6ca6a6e489d4dc8cb59c2b5200a4770455d34f95 (diff)
parentb366340c8f74f4b1d9277e98cd4c537b729306de (diff)
downloadkeymaster-a6ef4ee1a2e3527a9b4f37c3de2f2c7bac9a33a7.tar.gz
ECIES: add ECIES-KEM. This version supports HKDF and ECDH with NIST curves. am: 4199898833
am: b366340c8f * commit 'b366340c8f74f4b1d9277e98cd4c537b729306de': ECIES: add ECIES-KEM. This version supports HKDF and ECDH with NIST curves.
-rw-r--r--.gitignore10
-rw-r--r--Android.mk2
-rw-r--r--Makefile30
-rw-r--r--ec_key.h2
-rw-r--r--ec_key_factory.cpp26
-rw-r--r--ec_keymaster0_key.cpp2
-rw-r--r--ec_keymaster1_key.cpp2
-rw-r--r--ecdsa_operation.cpp4
-rw-r--r--ecies_kem.cpp195
-rw-r--r--ecies_kem.h61
-rw-r--r--ecies_kem_test.cpp75
-rw-r--r--hkdf.cpp55
-rw-r--r--hkdf.h35
-rw-r--r--hkdf_test.cpp15
-rw-r--r--include/keymaster/ec_key_factory.h1
-rw-r--r--include/keymaster/keymaster_tags.h4
-rw-r--r--kdf.h40
-rw-r--r--kem.h54
-rw-r--r--key_exchange.h52
-rw-r--r--keymaster0_engine.cpp4
-rw-r--r--keymaster1_engine.cpp6
-rw-r--r--keymaster_enforcement.cpp4
-rw-r--r--nist_curve_key_exchange.cpp127
-rw-r--r--nist_curve_key_exchange.h72
-rw-r--r--nist_curve_key_exchange_test.cpp219
-rw-r--r--openssl_utils.cpp46
-rw-r--r--openssl_utils.h15
-rw-r--r--soft_keymaster_context.cpp4
28 files changed, 1060 insertions, 102 deletions
diff --git a/.gitignore b/.gitignore
index 90f33d2..8c7799b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,14 +5,6 @@
*.run
*.memcheck
*.massif
-/abstract_factory_registry_test
-/android_keymaster_messages_test
-/android_keymaster_test
-/authorization_set_test
+/*_test
/coverage.info
/coverage/
-/hkdf_test
-/hmac_test
-/key_blob_test
-/keymaster_enforcement_test
-/trusty_keymaster_device_test
diff --git a/Android.mk b/Android.mk
index d8a31b8..3bbfb70 100644
--- a/Android.mk
+++ b/Android.mk
@@ -57,6 +57,7 @@ LOCAL_SRC_FILES:= \
ec_key.cpp \
ec_key_factory.cpp \
ecdsa_operation.cpp \
+ ecies_kem.cpp \
hkdf.cpp \
hmac.cpp \
hmac_key.cpp \
@@ -64,6 +65,7 @@ LOCAL_SRC_FILES:= \
integrity_assured_key_blob.cpp \
key.cpp \
keymaster_enforcement.cpp \
+ nist_curve_key_exchange.cpp \
ocb.c \
ocb_utils.cpp \
openssl_err.cpp \
diff --git a/Makefile b/Makefile
index a90b0a0..cf67486 100644
--- a/Makefile
+++ b/Makefile
@@ -80,6 +80,8 @@ CPPSRCS=\
ec_keymaster1_key.cpp \
ecdsa_keymaster1_operation.cpp \
ecdsa_operation.cpp \
+ ecies_kem.cpp \
+ ecies_kem_test.cpp \
gtest_main.cpp \
hkdf.cpp \
hkdf_test.cpp \
@@ -95,6 +97,8 @@ CPPSRCS=\
keymaster_enforcement.cpp \
keymaster_enforcement_test.cpp \
logger.cpp \
+ nist_curve_key_exchange.cpp \
+ nist_curve_key_exchange_test.cpp \
ocb_utils.cpp \
openssl_err.cpp \
openssl_utils.cpp \
@@ -121,10 +125,12 @@ BINARIES = \
android_keymaster_messages_test \
android_keymaster_test \
authorization_set_test \
+ ecies_kem_test \
hkdf_test \
hmac_test \
key_blob_test \
keymaster_enforcement_test \
+ nist_curve_key_exchange_test
.PHONY: coverage memcheck massif clean run
@@ -192,6 +198,30 @@ hkdf_test: hkdf_test.o \
serializable.o \
$(GTEST_OBJS)
+nist_curve_key_exchange_test: nist_curve_key_exchange_test.o \
+ android_keymaster_test_utils.o \
+ authorization_set.o \
+ logger.o \
+ nist_curve_key_exchange.o \
+ openssl_err.o \
+ openssl_utils.o \
+ serializable.o \
+ $(GTEST_OBJS)
+
+ecies_kem_test: ecies_kem_test.o \
+ android_keymaster_utils.o \
+ android_keymaster_test_utils.o \
+ authorization_set.o \
+ ecies_kem.o \
+ hkdf.o \
+ hmac.o \
+ logger.o \
+ nist_curve_key_exchange.o \
+ openssl_err.o \
+ openssl_utils.o \
+ serializable.o \
+ $(GTEST_OBJS)
+
authorization_set_test: authorization_set_test.o \
android_keymaster_test_utils.o \
authorization_set.o \
diff --git a/ec_key.h b/ec_key.h
index 96c2635..8230d23 100644
--- a/ec_key.h
+++ b/ec_key.h
@@ -43,7 +43,7 @@ class EcKey : public AsymmetricKey {
: AsymmetricKey(hw_enforced, sw_enforced, error), ec_key_(ec_key) {}
private:
- UniquePtr<EC_KEY, EC_Delete> ec_key_;
+ UniquePtr<EC_KEY, EC_KEY_Delete> ec_key_;
};
} // namespace keymaster
diff --git a/ec_key_factory.cpp b/ec_key_factory.cpp
index 4ebe543..14c8327 100644
--- a/ec_key_factory.cpp
+++ b/ec_key_factory.cpp
@@ -55,7 +55,7 @@ keymaster_error_t EcKeyFactory::GenerateKey(const AuthorizationSet& key_descript
return KM_ERROR_UNSUPPORTED_KEY_SIZE;
}
- UniquePtr<EC_KEY, EC_Delete> ec_key(EC_KEY_new());
+ UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EC_KEY_new());
UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
if (ec_key.get() == NULL || pkey.get() == NULL)
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
@@ -122,14 +122,14 @@ keymaster_error_t EcKeyFactory::UpdateImportKeyDescription(const AuthorizationSe
if (error != KM_ERROR_OK)
return error;
- UniquePtr<EC_KEY, EC_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
+ UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
if (!ec_key.get())
return TranslateLastOpenSslError();
updated_description->Reinitialize(key_description);
size_t extracted_key_size_bits;
- error = get_group_size(*EC_KEY_get0_group(ec_key.get()), &extracted_key_size_bits);
+ error = ec_get_group_size(EC_KEY_get0_group(ec_key.get()), &extracted_key_size_bits);
if (error != KM_ERROR_OK)
return error;
@@ -168,26 +168,6 @@ EC_GROUP* EcKeyFactory::choose_group(size_t key_size_bits) {
break;
}
}
-/* static */
-keymaster_error_t EcKeyFactory::get_group_size(const EC_GROUP& group, size_t* key_size_bits) {
- switch (EC_GROUP_get_curve_name(&group)) {
- case NID_secp224r1:
- *key_size_bits = 224;
- break;
- case NID_X9_62_prime256v1:
- *key_size_bits = 256;
- break;
- case NID_secp384r1:
- *key_size_bits = 384;
- break;
- case NID_secp521r1:
- *key_size_bits = 521;
- break;
- default:
- return KM_ERROR_UNSUPPORTED_EC_FIELD;
- }
- return KM_ERROR_OK;
-}
keymaster_error_t EcKeyFactory::CreateEmptyKey(const AuthorizationSet& hw_enforced,
const AuthorizationSet& sw_enforced,
diff --git a/ec_keymaster0_key.cpp b/ec_keymaster0_key.cpp
index c2112da..705ee73 100644
--- a/ec_keymaster0_key.cpp
+++ b/ec_keymaster0_key.cpp
@@ -106,7 +106,7 @@ keymaster_error_t EcdsaKeymaster0KeyFactory::LoadKey(const KeymasterKeyBlob& key
if (sw_enforced.GetTagCount(TAG_ALGORITHM) == 1)
return super::LoadKey(key_material, additional_params, hw_enforced, sw_enforced, key);
- unique_ptr<EC_KEY, EC_Delete> ec_key(engine_->BlobToEcKey(key_material));
+ unique_ptr<EC_KEY, EC_KEY_Delete> ec_key(engine_->BlobToEcKey(key_material));
if (!ec_key)
return KM_ERROR_UNKNOWN_ERROR;
diff --git a/ec_keymaster1_key.cpp b/ec_keymaster1_key.cpp
index f718408..5cc2539 100644
--- a/ec_keymaster1_key.cpp
+++ b/ec_keymaster1_key.cpp
@@ -89,7 +89,7 @@ keymaster_error_t EcdsaKeymaster1KeyFactory::LoadKey(const KeymasterKeyBlob& key
return KM_ERROR_OUTPUT_PARAMETER_NULL;
keymaster_error_t error;
- unique_ptr<EC_KEY, EC_Delete> ecdsa(
+ unique_ptr<EC_KEY, EC_KEY_Delete> ecdsa(
engine_->BuildEcKey(key_material, additional_params, &error));
if (!ecdsa)
return error;
diff --git a/ecdsa_operation.cpp b/ecdsa_operation.cpp
index 6da23f5..0e56344 100644
--- a/ecdsa_operation.cpp
+++ b/ecdsa_operation.cpp
@@ -144,7 +144,7 @@ keymaster_error_t EcdsaSignOperation::Finish(const AuthorizationSet& /* addition
size_t siglen;
if (digest_ == KM_DIGEST_NONE) {
- UniquePtr<EC_KEY, EC_Delete> ecdsa(EVP_PKEY_get1_EC_KEY(ecdsa_key_));
+ UniquePtr<EC_KEY, EC_KEY_Delete> ecdsa(EVP_PKEY_get1_EC_KEY(ecdsa_key_));
if (!ecdsa.get())
return TranslateLastOpenSslError();
@@ -201,7 +201,7 @@ keymaster_error_t EcdsaVerifyOperation::Finish(const AuthorizationSet& /* additi
AuthorizationSet* /* output_params */,
Buffer* /* output */) {
if (digest_ == KM_DIGEST_NONE) {
- UniquePtr<EC_KEY, EC_Delete> ecdsa(EVP_PKEY_get1_EC_KEY(ecdsa_key_));
+ UniquePtr<EC_KEY, EC_KEY_Delete> ecdsa(EVP_PKEY_get1_EC_KEY(ecdsa_key_));
if (!ecdsa.get())
return TranslateLastOpenSslError();
diff --git a/ecies_kem.cpp b/ecies_kem.cpp
new file mode 100644
index 0000000..d0920fa
--- /dev/null
+++ b/ecies_kem.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 "ecies_kem.h"
+
+#include "nist_curve_key_exchange.h"
+#include "openssl_err.h"
+
+namespace keymaster {
+
+EciesKem::EciesKem(const AuthorizationSet& kem_description, keymaster_error_t* error) {
+ AuthorizationSet authorizations(kem_description);
+
+ if (!authorizations.GetTagValue(TAG_EC_CURVE, &curve_)) {
+ LOG_E("%s", "EciesKem: no curve specified");
+ *error = KM_ERROR_INVALID_ARGUMENT;
+ return;
+ }
+
+ switch (curve_) {
+ case KM_EC_CURVE_P_224:
+ case KM_EC_CURVE_P_256:
+ case KM_EC_CURVE_P_384:
+ case KM_EC_CURVE_P_521:
+ break;
+ default:
+ LOG_E("EciesKem: curve %d is unsupported", curve_);
+ *error = KM_ERROR_UNSUPPORTED_EC_CURVE;
+ return;
+ }
+
+ keymaster_kdf_t kdf;
+ if (!authorizations.GetTagValue(TAG_KDF, &kdf)) {
+ LOG_E("EciesKem: No KDF specified", 0);
+ *error = KM_ERROR_UNSUPPORTED_KDF;
+ return;
+ }
+ switch (kdf) {
+ case KM_KDF_RFC5869_SHA256:
+ kdf_.reset(new Rfc5869Sha256Kdf());
+ break;
+ default:
+ LOG_E("Kdf %d is unsupported", kdf);
+ *error = KM_ERROR_UNSUPPORTED_KDF;
+ return;
+ }
+ if (!kdf_.get()) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return;
+ }
+
+ if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_bytes_to_generate_)) {
+ LOG_E("%s", "EciesKem: no key length specified");
+ *error = KM_ERROR_UNSUPPORTED_KEY_SIZE;
+ return;
+ }
+
+ keymaster_ec_point_format_t point_format_;
+ if (!authorizations.GetTagValue(TAG_EC_POINT_FORMAT, &point_format_)) {
+ LOG_E("%s", "EciesKem: no point format specify");
+ *error = KM_ERROR_UNSUPPORTED_EC_POINT_FORMAT;
+ return;
+ }
+ if (point_format_ != KM_EC_POINT_UNCOMPRESSED) {
+ LOG_E("EciesKem: point format %d unsupported", point_format_);
+ *error = KM_ERROR_UNSUPPORTED_EC_POINT_FORMAT;
+ return;
+ }
+
+ single_hash_mode_ = authorizations.GetTagValue(TAG_ECIES_SINGLE_HASH_MODE);
+ *error = KM_ERROR_OK;
+}
+
+bool EciesKem::Encrypt(const Buffer& peer_public_value, Buffer* output_clear_key,
+ Buffer* output_encrypted_key) {
+ return Encrypt(peer_public_value.peek_read(), peer_public_value.available_read(),
+ output_clear_key, output_encrypted_key);
+}
+
+// http://www.shoup.net/iso/std6.pdf, section 10.2.3.
+bool EciesKem::Encrypt(const uint8_t* peer_public_value, size_t peer_public_value_len,
+ Buffer* output_clear_key, Buffer* output_encrypted_key) {
+
+ key_exchange_.reset(NistCurveKeyExchange::GenerateKeyExchange(curve_));
+ if (!key_exchange_.get()) {
+ return false;
+ }
+
+ Buffer shared_secret;
+ if (!key_exchange_.get()->CalculateSharedKey(peer_public_value, peer_public_value_len,
+ &shared_secret)) {
+ LOG_E("EciesKem: ECDH failed, can't obtain shared secret", 0);
+ return false;
+ }
+ if (!key_exchange_.get()->public_value(output_encrypted_key)) {
+ LOG_E("EciesKem: Can't obtain public value", 0);
+ return false;
+ }
+
+ Buffer z;
+ if (single_hash_mode_) {
+ // z is empty.
+ } else {
+ // z = C0
+ z.Reinitialize(output_encrypted_key->peek_read(), output_encrypted_key->available_read());
+ }
+
+ Buffer actual_secret(z.available_read() + shared_secret.available_read());
+ actual_secret.write(z.peek_read(), z.available_read());
+ actual_secret.write(shared_secret.peek_read(), shared_secret.available_read());
+
+ if (!kdf_->Init(actual_secret.peek_read(), actual_secret.available_read(), nullptr /* salt */,
+ 0 /* salt_len */, nullptr /* info */, 0 /* info_len */,
+ key_bytes_to_generate_)) {
+ LOG_E("EciesKem: KDF failed, can't derived keys", 0);
+ return false;
+ }
+
+ if (!kdf_.get()->secret_key(output_clear_key)) {
+ LOG_E("EciesKem: KDF failed, can't derived keys", 0);
+ return false;
+ }
+
+ return true;
+}
+
+bool EciesKem::Decrypt(EC_KEY* private_key, const Buffer& encrypted_key, Buffer* output_key) {
+ return Decrypt(private_key, encrypted_key.peek_read(), encrypted_key.available_read(),
+ output_key);
+}
+
+// http://www.shoup.net/iso/std6.pdf, section 10.2.4.
+bool EciesKem::Decrypt(EC_KEY* private_key, const uint8_t* encrypted_key, size_t encrypted_key_len,
+ Buffer* output_key) {
+
+ keymaster_error_t error;
+ key_exchange_.reset(new NistCurveKeyExchange(private_key, &error));
+ if (!key_exchange_.get() || error != KM_ERROR_OK) {
+ return false;
+ }
+
+ Buffer shared_secret;
+ if (!key_exchange_.get()->CalculateSharedKey(encrypted_key, encrypted_key_len,
+ &shared_secret)) {
+ LOG_E("EciesKem: ECDH failed, can't obtain shared secret", 0);
+ return false;
+ }
+
+ Buffer public_value;
+ if (!key_exchange_.get()->public_value(&public_value)) {
+ LOG_E("%s", "EciesKem: Can't obtain public value");
+ return false;
+ }
+
+ Buffer z;
+ if (single_hash_mode_) {
+ // z is empty.
+ } else {
+ // z = C0
+ z.Reinitialize(public_value.peek_read(), public_value.available_read());
+ }
+
+ Buffer actual_secret(z.available_read() + shared_secret.available_read());
+ actual_secret.write(z.peek_read(), z.available_read());
+ actual_secret.write(shared_secret.peek_read(), shared_secret.available_read());
+
+ if (!kdf_.get()->Init(actual_secret.peek_read(), actual_secret.available_read(),
+ nullptr /* salt */, 0 /* salt_len */, nullptr /* info */,
+ 0 /* info_len */, key_bytes_to_generate_)) {
+ LOG_E("%s", "EciesKem: KDF failed, can't derived keys");
+ return false;
+ }
+
+ if (!kdf_.get()->secret_key(output_key)) {
+ LOG_E("%s", "EciesKem: KDF failed, can't derived keys");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace keymaster
diff --git a/ecies_kem.h b/ecies_kem.h
new file mode 100644
index 0000000..8c1b330
--- /dev/null
+++ b/ecies_kem.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_ECIES_KEM_H_
+#define SYSTEM_KEYMASTER_ECIES_KEM_H_
+
+#include "kem.h"
+
+#include <UniquePtr.h>
+#include <openssl/ec.h>
+
+#include <keymaster/authorization_set.h>
+
+#include "hkdf.h"
+#include "key_exchange.h"
+
+namespace keymaster {
+
+/**
+ * EciesKem is an implementation of the key encapsulation mechanism ECIES-KEM described in
+ * ISO 18033-2 (http://www.shoup.net/iso/std6.pdf, http://www.shoup.net/papers/iso-2_1.pdf).
+ */
+class EciesKem : public Kem {
+ public:
+ virtual ~EciesKem() override {}
+ EciesKem(const AuthorizationSet& kem_description, keymaster_error_t* error);
+
+ /* Kem interface. */
+ bool Encrypt(const Buffer& peer_public_value, Buffer* output_clear_key,
+ Buffer* output_encrypted_key) override;
+ bool Encrypt(const uint8_t* peer_public_value, size_t peer_public_value_len,
+ Buffer* output_clear_key, Buffer* output_encrypted_key) override;
+
+ bool Decrypt(EC_KEY* private_key, const Buffer& encrypted_key, Buffer* output_key) override;
+ bool Decrypt(EC_KEY* private_key, const uint8_t* encrypted_key, size_t encrypted_key_len,
+ Buffer* output_key) override;
+
+ private:
+ UniquePtr<KeyExchange> key_exchange_;
+ UniquePtr<Rfc5869Sha256Kdf> kdf_;
+ bool single_hash_mode_;
+ uint32_t key_bytes_to_generate_;
+ keymaster_ec_curve_t curve_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_ECIES_KEM_H_
diff --git a/ecies_kem_test.cpp b/ecies_kem_test.cpp
new file mode 100644
index 0000000..a3617a7
--- /dev/null
+++ b/ecies_kem_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "ecies_kem.h"
+
+#include <gtest/gtest.h>
+#include <openssl/evp.h>
+
+#include <hardware/keymaster_defs.h>
+#include <keymaster/android_keymaster_utils.h>
+
+#include "android_keymaster_test_utils.h"
+#include "nist_curve_key_exchange.h"
+
+using std::string;
+
+namespace keymaster {
+namespace test {
+
+StdoutLogger logger;
+
+static const keymaster_ec_curve_t kEcCurves[] = {KM_EC_CURVE_P_224, KM_EC_CURVE_P_256,
+ KM_EC_CURVE_P_384, KM_EC_CURVE_P_521};
+
+/**
+ * TestConsistency just tests that the basic key encapsulation hold.
+ */
+TEST(EciesKem, TestConsistency) {
+ static const uint32_t kKeyLen = 32;
+ for (auto& curve : kEcCurves) {
+ AuthorizationSet kem_description(
+ AuthorizationSetBuilder()
+ .Authorization(TAG_EC_CURVE, curve)
+ .Authorization(TAG_KDF, KM_KDF_RFC5869_SHA256)
+ .Authorization(TAG_ECIES_SINGLE_HASH_MODE)
+ .Authorization(TAG_KEY_SIZE, kKeyLen)
+ .Authorization(TAG_EC_POINT_FORMAT, KM_EC_POINT_UNCOMPRESSED));
+ keymaster_error_t error;
+ EciesKem* kem = new EciesKem(kem_description, &error);
+ ASSERT_EQ(KM_ERROR_OK, error);
+
+ NistCurveKeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve);
+ Buffer peer_public_value;
+ ASSERT_TRUE(key_exchange->public_value(&peer_public_value));
+
+ Buffer output_clear_key;
+ Buffer output_encrypted_key;
+ ASSERT_TRUE(kem->Encrypt(peer_public_value, &output_clear_key, &output_encrypted_key));
+ ASSERT_EQ(kKeyLen, output_clear_key.available_read());
+ ASSERT_EQ(peer_public_value.available_read(), output_encrypted_key.available_read());
+
+ Buffer decrypted_clear_key;
+ ASSERT_TRUE(
+ kem->Decrypt(key_exchange->private_key(), output_encrypted_key, &decrypted_clear_key));
+ ASSERT_EQ(kKeyLen, decrypted_clear_key.available_read());
+ EXPECT_EQ(0, memcmp(output_clear_key.peek_read(), decrypted_clear_key.peek_read(),
+ output_clear_key.available_read()));
+ }
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/hkdf.cpp b/hkdf.cpp
index 19c663d..7a9e121 100644
--- a/hkdf.cpp
+++ b/hkdf.cpp
@@ -16,8 +16,6 @@
#include "hkdf.h"
-#include <assert.h>
-
#include <new>
#include <keymaster/android_keymaster_utils.h>
@@ -28,17 +26,16 @@ namespace keymaster {
const size_t kSHA256HashLength = 32;
-Rfc5869HmacSha256Kdf::Rfc5869HmacSha256Kdf(Buffer& secret, Buffer& salt, Buffer& info,
- size_t key_bytes_to_generate, keymaster_error_t* error) {
- Rfc5869HmacSha256Kdf(secret.peek_read(), secret.available_read(), salt.peek_read(),
- salt.available_read(), info.peek_read(), info.available_read(),
- key_bytes_to_generate, error);
+bool Rfc5869Sha256Kdf::Init(Buffer& secret, Buffer& salt, Buffer& info,
+ size_t key_bytes_to_generate) {
+ return Init(secret.peek_read(), secret.available_read(), salt.peek_read(),
+ salt.available_read(), info.peek_read(), info.available_read(),
+ key_bytes_to_generate);
}
-Rfc5869HmacSha256Kdf::Rfc5869HmacSha256Kdf(const uint8_t* secret, size_t secret_len,
- const uint8_t* salt, size_t salt_len,
- const uint8_t* info, size_t info_len,
- size_t key_bytes_to_generate, keymaster_error_t* error) {
+bool Rfc5869Sha256Kdf::Init(const uint8_t* secret, size_t secret_len, const uint8_t* salt,
+ size_t salt_len, const uint8_t* info, size_t info_len,
+ size_t key_bytes_to_generate) {
// Step 1. Extract: PRK = HMAC-SHA256(actual_salt, secret)
// https://tools.ietf.org/html/rfc5869#section-2.2
HmacSha256 prk_hmac;
@@ -51,31 +48,37 @@ Rfc5869HmacSha256Kdf::Rfc5869HmacSha256Kdf(const uint8_t* secret, size_t secret_
memset(zeros, 0, sizeof(zeros));
result = prk_hmac.Init(zeros, sizeof(zeros));
}
- assert(result);
- // avoid the unused variable warning if asserts are disabled.
- (void)result;
+ if (!result) {
+ return false;
+ }
// |prk| is a pseudorandom key (of kSHA256HashLength octets).
uint8_t prk[kSHA256HashLength];
- assert(sizeof(prk) == prk_hmac.DigestLength());
+ if (sizeof(prk) != prk_hmac.DigestLength())
+ return false;
result = prk_hmac.Sign(secret, secret_len, prk, sizeof(prk));
- assert(result);
+ if (!result) {
+ return false;
+ }
// Step 2. Expand: OUTPUT = HKDF-Expand(PRK, info)
// https://tools.ietf.org/html/rfc5869#section-2.3
const size_t n = (key_bytes_to_generate + kSHA256HashLength - 1) / kSHA256HashLength;
- assert(n < 256u);
- output_.reset(new (std::nothrow) uint8_t[n * kSHA256HashLength]);
+ if (n >= 256u) {
+ return false;
+ }
+ output_.reset(new uint8_t[n * kSHA256HashLength]);
if (!output_.get()) {
- *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
- return;
+ return false;
}
uint8_t buf[kSHA256HashLength + info_len + 1];
uint8_t digest[kSHA256HashLength];
HmacSha256 hmac;
result = hmac.Init(prk, sizeof(prk));
- assert(result);
+ if (!result) {
+ return false;
+ }
for (size_t i = 1; i <= n; i++) {
size_t j = 0;
@@ -87,7 +90,8 @@ Rfc5869HmacSha256Kdf::Rfc5869HmacSha256Kdf(const uint8_t* secret, size_t secret_
j += info_len;
buf[j++] = static_cast<uint8_t>(i);
result = hmac.Sign(buf, j, digest, sizeof(digest));
- assert(result);
+ if (!result)
+ return false;
memcpy(output_.get() + (i - 1) * sizeof(digest), digest, sizeof(digest));
}
@@ -95,11 +99,12 @@ Rfc5869HmacSha256Kdf::Rfc5869HmacSha256Kdf(const uint8_t* secret, size_t secret_
secret_key_len_ = key_bytes_to_generate;
secret_key_.reset(dup_buffer(output_.get(), key_bytes_to_generate));
if (!secret_key_.get()) {
- *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
- return;
+ return false;
}
}
- *error = KM_ERROR_OK;
+ initalized_ = true;
+
+ return true;
}
} // namespace keymaster
diff --git a/hkdf.h b/hkdf.h
index 3ecdbe0..8d84097 100644
--- a/hkdf.h
+++ b/hkdf.h
@@ -17,39 +17,34 @@
#ifndef SYSTEM_KEYMASTER_HKDF_H_
#define SYSTEM_KEYMASTER_HKDF_H_
-#include <hardware/keymaster_defs.h>
+#include "kdf.h"
+
#include <keymaster/serializable.h>
#include <UniquePtr.h>
namespace keymaster {
-// Rfc5869HmacSha256Kdf implements the key derivation function specified in RFC 5869 (using
+// Rfc5869Sha256Kdf implements the key derivation function specified in RFC 5869 (using
// SHA256) and outputs key material, as needed by ECIES.
// See https://tools.ietf.org/html/rfc5869 for details.
-class Rfc5869HmacSha256Kdf {
+class Rfc5869Sha256Kdf : public Kdf {
public:
- // |secret|: the input shared secret (or, from RFC 5869, the IKM).
- // |salt|: an (optional) public salt / non-secret random value. While
- // optional, callers are strongly recommended to provide a salt. There is no
- // added security value in making this larger than the SHA-256 block size of
- // 64 bytes.
- // |info|: an (optional) label to distinguish different uses of HKDF. It is
- // optional context and application specific information (can be a zero-length
- // string).
- // |key_bytes_to_generate|: the number of bytes of key material to generate.
- Rfc5869HmacSha256Kdf(Buffer& secret, Buffer& salt, Buffer& info, size_t key_bytes_to_generate,
- keymaster_error_t* error);
-
- Rfc5869HmacSha256Kdf(const uint8_t* secret, size_t secret_len, const uint8_t* salt,
- size_t salt_len, const uint8_t* info, size_t info_len,
- size_t key_bytes_to_generate, keymaster_error_t* error);
+ Rfc5869Sha256Kdf() : initalized_(false) {}
+ ~Rfc5869Sha256Kdf() {}
+
+ // Kdf interface.
+ bool Init(Buffer& secret, Buffer& salt, Buffer& info, size_t key_bytes_to_generate);
+
+ bool Init(const uint8_t* secret, size_t secret_len, const uint8_t* salt, size_t salt_len,
+ const uint8_t* info, size_t info_len, size_t key_bytes_to_generate);
bool secret_key(Buffer* buf) const {
- return buf->Reinitialize(secret_key_.get(), secret_key_len_);
- };
+ return initalized_ && buf->Reinitialize(secret_key_.get(), secret_key_len_);
+ }
private:
+ bool initalized_;
UniquePtr<uint8_t[]> output_;
UniquePtr<uint8_t[]> secret_key_;
size_t secret_key_len_;
diff --git a/hkdf_test.cpp b/hkdf_test.cpp
index f65a617..8cf1e8d 100644
--- a/hkdf_test.cpp
+++ b/hkdf_test.cpp
@@ -65,20 +65,19 @@ static const HkdfTest kHkdfTests[] = {
};
TEST(HkdfTest, Hkdf) {
- for (size_t i = 0; i < 3; i++) {
- const HkdfTest& test(kHkdfTests[i]);
+ for (auto& test : kHkdfTests) {
const string key = hex2str(test.key_hex);
const string salt = hex2str(test.salt_hex);
const string info = hex2str(test.info_hex);
const string expected = hex2str(test.output_hex);
- keymaster_error_t error;
// We set the key_length to the length of the expected output and then take
// the result.
- Rfc5869HmacSha256Kdf hkdf(reinterpret_cast<const uint8_t*>(key.data()), key.size(),
- reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
- reinterpret_cast<const uint8_t*>(info.data()), info.size(),
- expected.size(), &error);
- ASSERT_EQ(error, KM_ERROR_OK);
+ Rfc5869Sha256Kdf hkdf;
+ ASSERT_TRUE(hkdf.Init(reinterpret_cast<const uint8_t*>(key.data()), key.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+ reinterpret_cast<const uint8_t*>(info.data()), info.size(),
+ expected.size()));
+
Buffer secret_key;
bool result = hkdf.secret_key(&secret_key);
ASSERT_TRUE(result);
diff --git a/include/keymaster/ec_key_factory.h b/include/keymaster/ec_key_factory.h
index 2371b7f..2715e79 100644
--- a/include/keymaster/ec_key_factory.h
+++ b/include/keymaster/ec_key_factory.h
@@ -53,7 +53,6 @@ class EcKeyFactory : public AsymmetricKeyFactory {
OperationFactory* GetOperationFactory(keymaster_purpose_t purpose) const override;
static EC_GROUP* choose_group(size_t key_size_bits);
- static keymaster_error_t get_group_size(const EC_GROUP& group, size_t* key_size_bits);
};
} // namespace keymaster
diff --git a/include/keymaster/keymaster_tags.h b/include/keymaster/keymaster_tags.h
index 21d6966..4cd2a6c 100644
--- a/include/keymaster/keymaster_tags.h
+++ b/include/keymaster/keymaster_tags.h
@@ -164,6 +164,7 @@ DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MAC_LENGTH);
DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_CALLER_NONCE);
DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MIN_MAC_LENGTH);
DEFINE_KEYMASTER_TAG(KM_ULONG, TAG_RSA_PUBLIC_EXPONENT);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ECIES_SINGLE_HASH_MODE);
DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ACTIVE_DATETIME);
DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ORIGINATION_EXPIRE_DATETIME);
DEFINE_KEYMASTER_TAG(KM_DATE, TAG_USAGE_EXPIRE_DATETIME);
@@ -204,6 +205,9 @@ DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_BLOB_USAGE_REQUIREMENTS,
keymaster_key_blob_usage_requirements_t);
DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ORIGIN, keymaster_key_origin_t);
DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_USER_AUTH_TYPE, hw_authenticator_type_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_KDF, keymaster_kdf_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_EC_CURVE, keymaster_ec_curve_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_EC_POINT_FORMAT, keymaster_ec_point_format_t);
//
// Overloaded function "Authorization" to create keymaster_key_param_t objects for all of tags.
diff --git a/kdf.h b/kdf.h
new file mode 100644
index 0000000..f65920d
--- /dev/null
+++ b/kdf.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_KDF_H_
+#define SYSTEM_KEYMASTER_KDF_H_
+
+#include <keymaster/serializable.h>
+
+namespace keymaster {
+
+// Kdf is an abstract class that provides an interface to a
+// key derivation function.
+class Kdf {
+ public:
+ virtual ~Kdf() {}
+
+ virtual bool Init(Buffer& secret, Buffer& salt, Buffer& info, size_t key_bytes_to_generate) = 0;
+ virtual bool Init(const uint8_t* secret, size_t secret_len, const uint8_t* salt,
+ size_t salt_len, const uint8_t* info, size_t info_len,
+ size_t key_bytes_to_generate) = 0;
+
+ virtual bool secret_key(Buffer* buf) const = 0;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KDF_H_
diff --git a/kem.h b/kem.h
new file mode 100644
index 0000000..f2fa073
--- /dev/null
+++ b/kem.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_KEM_H_
+#define SYSTEM_KEYMASTER_KEM_H_
+
+#include <keymaster/serializable.h>
+
+#include "openssl_utils.h"
+
+namespace keymaster {
+
+// Kem is an abstract class that provides an interface to a key encapsulation
+// mechansim primitive. A key encapsulation mechanism works just like a
+// public-key encryption scheme, except that the encryption algorithm takes no
+// input other than the recipient’s public key. Instead, the encryption algorithm
+// generates a pair (K, C0), where K is a bit string of some specified length,
+// and C0 is an encryption of K, that is, the decryption algorithm applied to C0
+// yields K.
+class Kem {
+ public:
+ virtual ~Kem(){};
+
+ // For a key encapsulation mechanism, the goal of encryption is to take the recipient's public
+ // key, and to generate a pair (K, C0), where K is a bit string of some specified length,
+ // and C0 is an encryption of K.
+ virtual bool Encrypt(const Buffer& peer_public_value, Buffer* output_clear_key,
+ Buffer* output_encrypted_key) = 0;
+ virtual bool Encrypt(const uint8_t* peer_public_value, size_t peer_public_value_len,
+ Buffer* output_clear_key, Buffer* output_encrypted_key) = 0;
+
+ // Decrypt takes an encrypted key, and outputs its clear text.
+ // Decrypt takes ownership of \p private_key.
+ virtual bool Decrypt(EC_KEY* private_key, const Buffer& encrypted_key, Buffer* output_key) = 0;
+ virtual bool Decrypt(EC_KEY* private_key, const uint8_t* encrypted_key,
+ size_t encrypted_key_len, Buffer* output_key) = 0;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KEM_H_
diff --git a/key_exchange.h b/key_exchange.h
new file mode 100644
index 0000000..1019e20
--- /dev/null
+++ b/key_exchange.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_KEY_EXCHANGE_H_
+#define SYSTEM_KEYMASTER_KEY_EXCHANGE_H_
+
+#include <openssl/ec.h>
+
+#include <keymaster/serializable.h>
+
+namespace keymaster {
+
+/**
+ * KeyExchange is an abstract class that provides an interface to a
+ * key-exchange primitive.
+ */
+class KeyExchange {
+ public:
+ virtual ~KeyExchange() {}
+
+ /**
+ * CalculateSharedKey computes the shared key between the local private key
+ * (which is implicitly known by a KeyExchange object) and a public value
+ * from the peer.
+ */
+ virtual bool CalculateSharedKey(const Buffer& peer_public_value, Buffer* shared_key) const = 0;
+ virtual bool CalculateSharedKey(const uint8_t* peer_public_value, size_t peer_public_value_len,
+ Buffer* shared_key) const = 0;
+
+ /**
+ * public_value writes to |public_value| the local public key which can be
+ * sent to a peer in order to complete a key exchange.
+ */
+ virtual bool public_value(Buffer* public_value) const = 0;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KEY_EXCHANGE_H_
diff --git a/keymaster0_engine.cpp b/keymaster0_engine.cpp
index 70c0d89..d752ae2 100644
--- a/keymaster0_engine.cpp
+++ b/keymaster0_engine.cpp
@@ -191,7 +191,7 @@ RSA* Keymaster0Engine::BlobToRsaKey(const KeymasterKeyBlob& blob) const {
EC_KEY* Keymaster0Engine::BlobToEcKey(const KeymasterKeyBlob& blob) const {
// Create new EC key (with engine methods) and insert blob
- unique_ptr<EC_KEY, EC_Delete> ec_key(EC_KEY_new_method(engine_));
+ unique_ptr<EC_KEY, EC_KEY_Delete> ec_key(EC_KEY_new_method(engine_));
if (!ec_key)
return nullptr;
@@ -204,7 +204,7 @@ EC_KEY* Keymaster0Engine::BlobToEcKey(const KeymasterKeyBlob& blob) const {
if (!pkey)
return nullptr;
- unique_ptr<EC_KEY, EC_Delete> public_ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
+ unique_ptr<EC_KEY, EC_KEY_Delete> public_ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
if (!public_ec_key)
return nullptr;
diff --git a/keymaster1_engine.cpp b/keymaster1_engine.cpp
index 866a255..6b40022 100644
--- a/keymaster1_engine.cpp
+++ b/keymaster1_engine.cpp
@@ -167,7 +167,7 @@ EC_KEY* Keymaster1Engine::BuildEcKey(const KeymasterKeyBlob& blob,
const AuthorizationSet& additional_params,
keymaster_error_t* error) const {
// Create new EC key (with engine methods) and insert blob
- unique_ptr<EC_KEY, EC_Delete> ec_key(EC_KEY_new_method(engine_.get()));
+ unique_ptr<EC_KEY, EC_KEY_Delete> ec_key(EC_KEY_new_method(engine_.get()));
if (!ec_key) {
*error = TranslateLastOpenSslError();
return nullptr;
@@ -188,7 +188,7 @@ EC_KEY* Keymaster1Engine::BuildEcKey(const KeymasterKeyBlob& blob,
return nullptr;
}
- unique_ptr<EC_KEY, EC_Delete> public_ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
+ unique_ptr<EC_KEY, EC_KEY_Delete> public_ec_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
if (!public_ec_key) {
*error = TranslateLastOpenSslError();
return nullptr;
@@ -212,7 +212,7 @@ Keymaster1Engine::KeyData* Keymaster1Engine::GetData(EVP_PKEY* key) const {
}
case EVP_PKEY_EC: {
- unique_ptr<EC_KEY, EC_Delete> ec_key(EVP_PKEY_get1_EC_KEY(key));
+ unique_ptr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(key));
return GetData(ec_key.get());
}
diff --git a/keymaster_enforcement.cpp b/keymaster_enforcement.cpp
index 4d0aaef..ae44f54 100644
--- a/keymaster_enforcement.cpp
+++ b/keymaster_enforcement.cpp
@@ -296,12 +296,16 @@ keymaster_error_t KeymasterEnforcement::AuthorizeBegin(const keymaster_purpose_t
case KM_TAG_PADDING:
case KM_TAG_NONCE:
case KM_TAG_MIN_MAC_LENGTH:
+ case KM_TAG_KDF:
+ case KM_TAG_EC_CURVE:
+ case KM_TAG_EC_POINT_FORMAT:
/* Tags not used for operations. */
case KM_TAG_BLOB_USAGE_REQUIREMENTS:
/* Algorithm specific parameters not used for access control. */
case KM_TAG_RSA_PUBLIC_EXPONENT:
+ case KM_TAG_ECIES_SINGLE_HASH_MODE:
/* Informational tags. */
case KM_TAG_CREATION_DATETIME:
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
diff --git a/nist_curve_key_exchange.h b/nist_curve_key_exchange.h
new file mode 100644
index 0000000..0a93882
--- /dev/null
+++ b/nist_curve_key_exchange.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_NIST_CURVE_KEY_EXCHANGE_H_
+#define SYSTEM_KEYMASTER_NIST_CURVE_KEY_EXCHANGE_H_
+
+#include "key_exchange.h"
+
+#include <keymaster/authorization_set.h>
+#include <hardware/keymaster_defs.h>
+
+#include <UniquePtr.h>
+
+#include "openssl_utils.h"
+
+namespace keymaster {
+
+/**
+ * NistCurveKeyExchange implements a KeyExchange using elliptic-curve
+ * Diffie-Hellman on NIST curves: P-224, P-256, P-384 and P-521.
+ */
+class NistCurveKeyExchange : public KeyExchange {
+ public:
+ ~NistCurveKeyExchange() override {}
+
+ /**
+ * NistCurveKeyExchange takes ownership of \p private_key.
+ */
+ NistCurveKeyExchange(EC_KEY* private_key, keymaster_error_t* error);
+
+ /**
+ * GenerateKeyExchange generates a new public/private key pair on a NIST curve and returns
+ * a new key exchange object.
+ */
+ static NistCurveKeyExchange* GenerateKeyExchange(keymaster_ec_curve_t curve);
+
+ /**
+ * KeyExchange interface.
+ */
+ bool CalculateSharedKey(const uint8_t* peer_public_value, size_t peer_public_value_len,
+ Buffer* shared_key) const override;
+ bool CalculateSharedKey(const Buffer& peer_public_value, Buffer* shared_key) const override;
+ bool public_value(Buffer* public_value) const override;
+
+ /* Caller takes ownership of \p private_key. */
+ EC_KEY* private_key() { return private_key_.release(); }
+
+ private:
+ keymaster_error_t ExtractPublicKey();
+
+ UniquePtr<EC_KEY, EC_KEY_Delete> private_key_;
+ UniquePtr<uint8_t[]> public_key_;
+ size_t public_key_len_;
+ size_t shared_secret_len_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_NIST_CURVE_KEY_EXCHANGE_H_ \ No newline at end of file
diff --git a/nist_curve_key_exchange_test.cpp b/nist_curve_key_exchange_test.cpp
new file mode 100644
index 0000000..39ea38b
--- /dev/null
+++ b/nist_curve_key_exchange_test.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <openssl/evp.h>
+
+#include <hardware/keymaster_defs.h>
+#include <keymaster/android_keymaster_utils.h>
+
+#include "android_keymaster_test_utils.h"
+
+using std::string;
+
+namespace keymaster {
+namespace test {
+
+StdoutLogger logger;
+
+static const keymaster_ec_curve_t kEcCurves[] = {KM_EC_CURVE_P_224, KM_EC_CURVE_P_256,
+ KM_EC_CURVE_P_384, KM_EC_CURVE_P_521};
+
+/**
+ * SharedKey just tests that the basic key exchange identity holds: that both
+ * parties end up with the same key.
+ */
+TEST(NistCurveKeyExchange, SharedKey) {
+ for (auto& curve : kEcCurves) {
+ AuthorizationSet kex_description(
+ AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve));
+ for (size_t j = 0; j < 5; j++) {
+ NistCurveKeyExchange* alice_keyex = NistCurveKeyExchange::GenerateKeyExchange(curve);
+ NistCurveKeyExchange* bob_keyex = NistCurveKeyExchange::GenerateKeyExchange(curve);
+
+ ASSERT_TRUE(alice_keyex != nullptr);
+ ASSERT_TRUE(bob_keyex != nullptr);
+
+ Buffer alice_public_value;
+ ASSERT_TRUE(alice_keyex->public_value(&alice_public_value));
+ Buffer bob_public_value;
+ ASSERT_TRUE(bob_keyex->public_value(&bob_public_value));
+
+ Buffer alice_shared, bob_shared;
+ ASSERT_TRUE(alice_keyex->CalculateSharedKey(bob_public_value, &alice_shared));
+ ASSERT_TRUE(bob_keyex->CalculateSharedKey(alice_public_value, &bob_shared));
+ EXPECT_EQ(alice_shared.available_read(), bob_shared.available_read());
+ EXPECT_EQ(0, memcmp(alice_shared.peek_read(), bob_shared.peek_read(),
+ alice_shared.available_read()));
+ }
+ }
+}
+
+/*
+ * This test tries a key agreement with a false public key (i.e. with
+ * a point not on the curve.)
+ * The expected result of such a protocol should be that the
+ * key agreement fails and returns an error.
+*/
+static const char* kInvalidPublicKeys[] = {
+ "04" // uncompressed public key
+ "deadbeef7f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
+ "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
+};
+
+TEST(NistCurveKeyExchange, InvalidPublicKey) {
+ for (auto& curve : kEcCurves) {
+ AuthorizationSet kex_description(
+ AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve));
+ KeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve);
+ ASSERT_TRUE(key_exchange != nullptr);
+
+ string peer_public_key = hex2str(kInvalidPublicKeys[0]);
+ Buffer computed_shared_secret;
+ ASSERT_FALSE(key_exchange->CalculateSharedKey(
+ reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(),
+ &computed_shared_secret));
+ }
+}
+
+/**
+ * Test that key exchange fails when peer public key is the point at infinity.
+ */
+TEST(NistCurveKeyExchange, TestInfinity) {
+ for (auto& curve : kEcCurves) {
+ /* Obtain the point at infinity */
+ EC_GROUP* group = ec_get_group(curve);
+ EC_POINT* point_at_infinity = EC_POINT_new(group);
+ EC_POINT_set_to_infinity(group, point_at_infinity);
+ EXPECT_EQ(1, EC_POINT_is_on_curve(group, point_at_infinity, nullptr));
+ size_t field_len_in_bits;
+ ec_get_group_size(group, &field_len_in_bits);
+ size_t field_len = (field_len_in_bits + 7) / 8;
+ size_t public_key_len = (field_len * 2) + 1;
+ uint8_t* public_key = new uint8_t[public_key_len];
+ public_key_len = EC_POINT_point2oct(group, point_at_infinity, POINT_CONVERSION_UNCOMPRESSED,
+ public_key, public_key_len, nullptr /* ctx */);
+
+ /* Perform the key exchange */
+ AuthorizationSet kex_description(
+ AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve));
+ NistCurveKeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve);
+ ASSERT_TRUE(key_exchange != nullptr);
+ Buffer computed_shared_secret;
+ /* It should fail */
+ ASSERT_FALSE(key_exchange->CalculateSharedKey(reinterpret_cast<const uint8_t*>(public_key),
+ public_key_len, &computed_shared_secret));
+
+ /* Explicitly test that ECDH_compute_key fails when the public key is the point at infinity
+ */
+ UniquePtr<uint8_t[]> result(new uint8_t[field_len]);
+ EXPECT_EQ(-1 /* error */, ECDH_compute_key(result.get(), field_len, point_at_infinity,
+ key_exchange->private_key(), nullptr /* kdf */));
+ }
+}
+
+/* Test vectors for P-256, downloaded from NIST. */
+struct NistCurveTest {
+ const keymaster_ec_curve_t curve;
+ const char* peer_public_key;
+ const char* my_private_key;
+ const char* shared_secret;
+};
+
+static const NistCurveTest kNistCurveTests[] = {
+ {
+ KM_EC_CURVE_P_256,
+ "04" // uncompressed public key
+ "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
+ "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
+ // https://tools.ietf.org/html/rfc5915
+ "30770201010420" // DER-encodeded EC private key header
+ "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534" // private key
+ "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID,
+ "04"
+ "ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230"
+ "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141",
+ "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
+ },
+ {
+ KM_EC_CURVE_P_256, "04"
+ "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae"
+ "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3",
+ // https://tools.ietf.org/html/rfc5915
+ "30770201010420" // DER-encodeded EC private key header
+ "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5" // private key
+ "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID,
+ "04"
+ "119f2f047902782ab0c9e27a54aff5eb9b964829ca99c06b02ddba95b0a3f6d0"
+ "8f52b726664cac366fc98ac7a012b2682cbd962e5acb544671d41b9445704d1d",
+ "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67",
+ },
+ {
+ KM_EC_CURVE_P_256, "04"
+ "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed"
+ "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4",
+ // https://tools.ietf.org/html/rfc5915
+ "30770201010420" // DER-encodeded EC private key header
+ "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d" // private key
+ "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID,
+ "04"
+ "24277c33f450462dcb3d4801d57b9ced05188f16c28eda873258048cd1607e0d"
+ "c4789753e2b1f63b32ff014ec42cd6a69fac81dfe6d0d6fd4af372ae27c46f88",
+ "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024",
+ },
+};
+
+/**
+ * Test that key exchange works with NIST test vectors.
+ */
+TEST(NistCurveKeyExchange, NistTestVectors) {
+ for (auto& test : kNistCurveTests) {
+ string private_key = hex2str(test.my_private_key);
+ string shared_secret = hex2str(test.shared_secret);
+
+ const uint8_t* private_key_data = reinterpret_cast<const uint8_t*>(private_key.data());
+ UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(
+ d2i_ECPrivateKey(nullptr, &private_key_data, private_key.size()));
+ ASSERT_TRUE(ec_key.get() && EC_KEY_check_key(ec_key.get()));
+
+ keymaster_error_t error;
+ NistCurveKeyExchange* key_exchange = new NistCurveKeyExchange(ec_key.release(), &error);
+ EXPECT_EQ(KM_ERROR_OK, error);
+ ASSERT_TRUE(key_exchange != nullptr);
+
+ Buffer computed_shared_secret;
+ string peer_public_key = hex2str(test.peer_public_key);
+ ASSERT_TRUE(key_exchange->CalculateSharedKey(
+ reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(),
+ &computed_shared_secret));
+ EXPECT_EQ(shared_secret.size(), computed_shared_secret.available_read());
+ EXPECT_EQ(0, memcmp(shared_secret.data(), computed_shared_secret.peek_read(),
+ shared_secret.size()));
+
+ for (size_t i = 0; i < peer_public_key.size(); i++) {
+ // randomly flip some bits in the peer public key to make it invalid
+ peer_public_key[i] ^= 0xff;
+ ASSERT_FALSE(key_exchange->CalculateSharedKey(
+ reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(),
+ &computed_shared_secret));
+ }
+ }
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/openssl_utils.cpp b/openssl_utils.cpp
index 1e25ca5..5d2cb1c 100644
--- a/openssl_utils.cpp
+++ b/openssl_utils.cpp
@@ -22,6 +22,52 @@
namespace keymaster {
+keymaster_error_t ec_get_group_size(const EC_GROUP* group, size_t* key_size_bits) {
+ switch (EC_GROUP_get_curve_name(group)) {
+ case NID_secp224r1:
+ *key_size_bits = 224;
+ break;
+ case NID_X9_62_prime256v1:
+ *key_size_bits = 256;
+ break;
+ case NID_secp384r1:
+ *key_size_bits = 384;
+ break;
+ case NID_secp521r1:
+ *key_size_bits = 521;
+ break;
+ default:
+ return KM_ERROR_UNSUPPORTED_EC_FIELD;
+ }
+ return KM_ERROR_OK;
+}
+
+EC_GROUP* ec_get_group(keymaster_ec_curve_t curve) {
+ switch (curve) {
+ case KM_EC_CURVE_P_224:
+ return EC_GROUP_new_by_curve_name(NID_secp224r1);
+ break;
+ case KM_EC_CURVE_P_256:
+ return EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ break;
+ case KM_EC_CURVE_P_384:
+ return EC_GROUP_new_by_curve_name(NID_secp384r1);
+ break;
+ case KM_EC_CURVE_P_521:
+ return EC_GROUP_new_by_curve_name(NID_secp521r1);
+ break;
+ default:
+ return nullptr;
+ break;
+ }
+}
+
+void convert_bn_to_blob(BIGNUM* bn, keymaster_blob_t* blob) {
+ blob->data_length = BN_num_bytes(bn);
+ blob->data = new uint8_t[blob->data_length];
+ BN_bn2bin(bn, const_cast<uint8_t*>(blob->data));
+}
+
static int convert_to_evp(keymaster_algorithm_t algorithm) {
switch (algorithm) {
case KM_ALGORITHM_RSA:
diff --git a/openssl_utils.h b/openssl_utils.h
index a66ef43..cd52ca1 100644
--- a/openssl_utils.h
+++ b/openssl_utils.h
@@ -32,6 +32,14 @@ namespace keymaster {
struct KeymasterKeyBlob;
+struct EC_KEY_Delete {
+ void operator()(EC_KEY* p) { EC_KEY_free(p); }
+};
+
+struct EC_POINT_Delete {
+ void operator()(EC_POINT* p) { EC_POINT_free(p); }
+};
+
struct EVP_PKEY_Delete {
void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
};
@@ -56,14 +64,13 @@ struct EC_GROUP_Delete {
void operator()(EC_GROUP* p) { EC_GROUP_free(p); }
};
-struct EC_Delete {
- void operator()(EC_KEY* p) { EC_KEY_free(p); }
-};
-
struct ENGINE_Delete {
void operator()(ENGINE* p) { ENGINE_free(p); }
};
+keymaster_error_t ec_get_group_size(const EC_GROUP* group, size_t* key_size_bits);
+EC_GROUP* ec_get_group(keymaster_ec_curve_t curve);
+
/**
* Many OpenSSL APIs take ownership of an argument on success but don't free the argument on
* failure. This means we need to tell our scoped pointers when we've transferred ownership, without
diff --git a/soft_keymaster_context.cpp b/soft_keymaster_context.cpp
index 5620457..1b1fa39 100644
--- a/soft_keymaster_context.cpp
+++ b/soft_keymaster_context.cpp
@@ -470,12 +470,12 @@ keymaster_error_t SoftKeymasterContext::FakeKeyAuthorizations(EVP_PKEY* pubkey,
sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN);
sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY);
- UniquePtr<EC_KEY, EC_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pubkey));
+ UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pubkey));
if (!ec_key.get())
return TranslateLastOpenSslError();
size_t key_size_bits;
keymaster_error_t error =
- EcKeyFactory::get_group_size(*EC_KEY_get0_group(ec_key.get()), &key_size_bits);
+ ec_get_group_size(EC_KEY_get0_group(ec_key.get()), &key_size_bits);
if (error != KM_ERROR_OK)
return error;
hw_enforced->push_back(TAG_KEY_SIZE, key_size_bits);