diff options
author | Thai Duong <thaidn@google.com> | 2015-11-23 13:35:28 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2015-11-23 13:35:28 +0000 |
commit | a6ef4ee1a2e3527a9b4f37c3de2f2c7bac9a33a7 (patch) | |
tree | d0347f109cfaab09ac2a1902849efdb238ed5c08 | |
parent | 6ca6a6e489d4dc8cb59c2b5200a4770455d34f95 (diff) | |
parent | b366340c8f74f4b1d9277e98cd4c537b729306de (diff) | |
download | keymaster-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-- | .gitignore | 10 | ||||
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | Makefile | 30 | ||||
-rw-r--r-- | ec_key.h | 2 | ||||
-rw-r--r-- | ec_key_factory.cpp | 26 | ||||
-rw-r--r-- | ec_keymaster0_key.cpp | 2 | ||||
-rw-r--r-- | ec_keymaster1_key.cpp | 2 | ||||
-rw-r--r-- | ecdsa_operation.cpp | 4 | ||||
-rw-r--r-- | ecies_kem.cpp | 195 | ||||
-rw-r--r-- | ecies_kem.h | 61 | ||||
-rw-r--r-- | ecies_kem_test.cpp | 75 | ||||
-rw-r--r-- | hkdf.cpp | 55 | ||||
-rw-r--r-- | hkdf.h | 35 | ||||
-rw-r--r-- | hkdf_test.cpp | 15 | ||||
-rw-r--r-- | include/keymaster/ec_key_factory.h | 1 | ||||
-rw-r--r-- | include/keymaster/keymaster_tags.h | 4 | ||||
-rw-r--r-- | kdf.h | 40 | ||||
-rw-r--r-- | kem.h | 54 | ||||
-rw-r--r-- | key_exchange.h | 52 | ||||
-rw-r--r-- | keymaster0_engine.cpp | 4 | ||||
-rw-r--r-- | keymaster1_engine.cpp | 6 | ||||
-rw-r--r-- | keymaster_enforcement.cpp | 4 | ||||
-rw-r--r-- | nist_curve_key_exchange.cpp | 127 | ||||
-rw-r--r-- | nist_curve_key_exchange.h | 72 | ||||
-rw-r--r-- | nist_curve_key_exchange_test.cpp | 219 | ||||
-rw-r--r-- | openssl_utils.cpp | 46 | ||||
-rw-r--r-- | openssl_utils.h | 15 | ||||
-rw-r--r-- | soft_keymaster_context.cpp | 4 |
28 files changed, 1060 insertions, 102 deletions
@@ -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 @@ -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 \ @@ -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 \ @@ -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 @@ -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 @@ -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. @@ -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_ @@ -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); |