Add support for all digests for ECDSA.
Also, switch to useing the EVP API rather than the ECDSA API.
Cherry-picked from internal.
Bug: 21048758
Change-Id: Id1bf19f469de56bbe9adcf65cebee8ab4ea84212
diff --git a/android_keymaster_test.cpp b/android_keymaster_test.cpp
index 5a4cba7..698e426 100644
--- a/android_keymaster_test.cpp
+++ b/android_keymaster_test.cpp
@@ -188,7 +188,10 @@
ASSERT_EQ(KM_ERROR_OK, device()->get_supported_digests(device(), KM_ALGORITHM_EC,
KM_PURPOSE_SIGN, &digests, &len));
- EXPECT_TRUE(ResponseContains(KM_DIGEST_NONE, digests, len));
+ EXPECT_TRUE(
+ ResponseContains({KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224,
+ KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512},
+ digests, len));
free(digests);
EXPECT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE,
@@ -620,6 +623,17 @@
EXPECT_EQ(3, GetParam()->keymaster0_calls());
}
+TEST_P(SigningOperationsTest, EcdsaSha256Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(
+ KM_DIGEST_SHA_2_256)));
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256);
+
+ if (GetParam()->algorithm_in_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
TEST_P(SigningOperationsTest, AesEcbSign) {
ASSERT_EQ(KM_ERROR_OK,
GenerateKey(AuthorizationSetBuilder().AesEncryptionKey(128).Authorization(
@@ -1245,6 +1259,31 @@
EXPECT_EQ(4, GetParam()->keymaster0_calls());
}
+TEST_P(VerificationOperationsTest, EcdsaSha256Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(256)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Digest(KM_DIGEST_NONE)));
+ string message = "123456789012345678901234567890123456789012345678";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_256);
+
+ if (GetParam()->algorithm_in_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+
+ // Just for giggles, try verifying with the wrong digest.
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result));
+}
+
TEST_P(VerificationOperationsTest, HmacSha1Success) {
GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA1));
string message = "123456789012345678901234567890123456789012345678";
diff --git a/ec_key_factory.cpp b/ec_key_factory.cpp
index db094fa..e500a39 100644
--- a/ec_key_factory.cpp
+++ b/ec_key_factory.cpp
@@ -16,6 +16,8 @@
#include <keymaster/ec_key_factory.h>
+#include <openssl/evp.h>
+
#include <keymaster/keymaster_context.h>
#include "ec_key.h"
diff --git a/ecdsa_operation.cpp b/ecdsa_operation.cpp
index a28209a..5f9767f 100644
--- a/ecdsa_operation.cpp
+++ b/ecdsa_operation.cpp
@@ -14,16 +14,19 @@
* limitations under the License.
*/
+#include "ecdsa_operation.h"
+
#include <openssl/ecdsa.h>
#include "ec_key.h"
-#include "ecdsa_operation.h"
#include "openssl_err.h"
#include "openssl_utils.h"
namespace keymaster {
-static const keymaster_digest_t supported_digests[] = {KM_DIGEST_NONE};
+static const keymaster_digest_t supported_digests[] = {
+ KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224,
+ KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512};
Operation* EcdsaOperationFactory::CreateOperation(const Key& key,
const AuthorizationSet& begin_params,
@@ -34,23 +37,18 @@
return nullptr;
}
- *error = KM_ERROR_UNSUPPORTED_DIGEST;
- keymaster_digest_t digest;
- if (!begin_params.GetTagValue(TAG_DIGEST, &digest)) {
- LOG_E("%d digests specified in begin params", begin_params.GetTagCount(TAG_DIGEST));
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
+ if (!ecdsa_key->InternalToEvp(pkey.get())) {
+ *error = KM_ERROR_UNKNOWN_ERROR;
return nullptr;
- } else if (!supported(digest)) {
- LOG_E("Digest %d not supported", digest);
- return nullptr;
- } else if (!ecdsa_key->authorizations().Contains(TAG_DIGEST, digest) &&
- !ecdsa_key->authorizations().Contains(TAG_DIGEST_OLD, digest)) {
- LOG_E("Digest %d was specified, but not authorized by key", digest);
- *error = KM_ERROR_INCOMPATIBLE_DIGEST;
- return NULL;
}
- *error = KM_ERROR_OK;
- Operation* op = InstantiateOperation(digest, ecdsa_key->key());
+ keymaster_digest_t digest;
+ if (!GetAndValidateDigest(begin_params, key, &digest, error))
+ return nullptr;
+
+ *error = KM_ERROR_OK;
+ Operation* op = InstantiateOperation(digest, pkey.release());
if (!op)
*error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
return op;
@@ -63,19 +61,34 @@
EcdsaOperation::~EcdsaOperation() {
if (ecdsa_key_ != NULL)
- EC_KEY_free(ecdsa_key_);
+ EVP_PKEY_free(ecdsa_key_);
+ EVP_MD_CTX_cleanup(&digest_ctx_);
}
-keymaster_error_t EcdsaOperation::Update(const AuthorizationSet& /* additional_params */,
- const Buffer& input, Buffer* /* output */,
- size_t* input_consumed) {
- assert(input_consumed);
- switch (purpose()) {
+keymaster_error_t EcdsaOperation::InitDigest() {
+ switch (digest_) {
+ case KM_DIGEST_NONE:
+ return KM_ERROR_OK;
+ case KM_DIGEST_MD5:
+ digest_algorithm_ = EVP_md5();
+ return KM_ERROR_OK;
+ case KM_DIGEST_SHA1:
+ digest_algorithm_ = EVP_sha1();
+ return KM_ERROR_OK;
+ case KM_DIGEST_SHA_2_224:
+ digest_algorithm_ = EVP_sha224();
+ return KM_ERROR_OK;
+ case KM_DIGEST_SHA_2_256:
+ digest_algorithm_ = EVP_sha256();
+ return KM_ERROR_OK;
+ case KM_DIGEST_SHA_2_384:
+ digest_algorithm_ = EVP_sha384();
+ return KM_ERROR_OK;
+ case KM_DIGEST_SHA_2_512:
+ digest_algorithm_ = EVP_sha512();
+ return KM_ERROR_OK;
default:
- return KM_ERROR_UNIMPLEMENTED;
- case KM_PURPOSE_SIGN:
- case KM_PURPOSE_VERIFY:
- return StoreData(input, input_consumed);
+ return KM_ERROR_UNSUPPORTED_DIGEST;
}
}
@@ -87,28 +100,110 @@
return KM_ERROR_OK;
}
+keymaster_error_t EcdsaSignOperation::Begin(const AuthorizationSet& /* input_params */,
+ AuthorizationSet* /* output_params */) {
+ keymaster_error_t error = InitDigest();
+ if (error != KM_ERROR_OK)
+ return error;
+
+ if (digest_ == KM_DIGEST_NONE)
+ return KM_ERROR_OK;
+
+ EVP_PKEY_CTX* pkey_ctx;
+ if (EVP_DigestSignInit(&digest_ctx_, &pkey_ctx, digest_algorithm_, nullptr /* engine */,
+ ecdsa_key_) != 1)
+ return TranslateLastOpenSslError();
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t EcdsaSignOperation::Update(const AuthorizationSet& /* additional_params */,
+ const Buffer& input, Buffer* /* output */,
+ size_t* input_consumed) {
+ if (digest_ == KM_DIGEST_NONE)
+ return StoreData(input, input_consumed);
+
+ if (EVP_DigestSignUpdate(&digest_ctx_, input.peek_read(), input.available_read()) != 1)
+ return TranslateLastOpenSslError();
+ *input_consumed = input.available_read();
+ return KM_ERROR_OK;
+}
+
keymaster_error_t EcdsaSignOperation::Finish(const AuthorizationSet& /* additional_params */,
const Buffer& /* signature */, Buffer* output) {
- assert(output);
- output->Reinitialize(ECDSA_size(ecdsa_key_));
- unsigned int siglen;
- if (!ECDSA_sign(0 /* type -- ignored */, data_.peek_read(), data_.available_read(),
- output->peek_write(), &siglen, ecdsa_key_))
- return TranslateLastOpenSslError();
+ if (!output)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ size_t siglen;
+ if (digest_ == KM_DIGEST_NONE) {
+ UniquePtr<EC_KEY, EC_Delete> ecdsa(EVP_PKEY_get1_EC_KEY(ecdsa_key_));
+ if (!ecdsa.get())
+ return TranslateLastOpenSslError();
+
+ output->Reinitialize(ECDSA_size(ecdsa.get()));
+ unsigned int siglen_tmp;
+ if (!ECDSA_sign(0 /* type -- ignored */, data_.peek_read(), data_.available_read(),
+ output->peek_write(), &siglen_tmp, ecdsa.get()))
+ return TranslateLastOpenSslError();
+ siglen = siglen_tmp;
+ } else {
+ if (EVP_DigestSignFinal(&digest_ctx_, nullptr /* signature */, &siglen) != 1)
+ return TranslateLastOpenSslError();
+ if (!output->Reinitialize(siglen))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ if (EVP_DigestSignFinal(&digest_ctx_, output->peek_write(), &siglen) <= 0)
+ return TranslateLastOpenSslError();
+ }
output->advance_write(siglen);
return KM_ERROR_OK;
}
+keymaster_error_t EcdsaVerifyOperation::Begin(const AuthorizationSet& /* input_params */,
+ AuthorizationSet* /* output_params */) {
+ keymaster_error_t error = InitDigest();
+ if (error != KM_ERROR_OK)
+ return error;
+
+ if (digest_ == KM_DIGEST_NONE)
+ return KM_ERROR_OK;
+
+ EVP_PKEY_CTX* pkey_ctx;
+ if (EVP_DigestVerifyInit(&digest_ctx_, &pkey_ctx, digest_algorithm_, nullptr /* engine */,
+ ecdsa_key_) != 1)
+ return TranslateLastOpenSslError();
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t EcdsaVerifyOperation::Update(const AuthorizationSet& /* additional_params */,
+ const Buffer& input, Buffer* /* output */,
+ size_t* input_consumed) {
+ if (digest_ == KM_DIGEST_NONE)
+ return StoreData(input, input_consumed);
+
+ if (EVP_DigestVerifyUpdate(&digest_ctx_, input.peek_read(), input.available_read()) != 1)
+ return TranslateLastOpenSslError();
+ *input_consumed = input.available_read();
+ return KM_ERROR_OK;
+}
+
keymaster_error_t EcdsaVerifyOperation::Finish(const AuthorizationSet& /* additional_params */,
const Buffer& signature, Buffer* /* output */) {
- int result = ECDSA_verify(0 /* type -- ignored */, data_.peek_read(), data_.available_read(),
- signature.peek_read(), signature.available_read(), ecdsa_key_);
- if (result < 0)
- return TranslateLastOpenSslError();
- else if (result == 0)
+ if (digest_ == KM_DIGEST_NONE) {
+ UniquePtr<EC_KEY, EC_Delete> ecdsa(EVP_PKEY_get1_EC_KEY(ecdsa_key_));
+ if (!ecdsa.get())
+ return TranslateLastOpenSslError();
+
+ int result =
+ ECDSA_verify(0 /* type -- ignored */, data_.peek_read(), data_.available_read(),
+ signature.peek_read(), signature.available_read(), ecdsa.get());
+ if (result < 0)
+ return TranslateLastOpenSslError();
+ else if (result == 0)
+ return KM_ERROR_VERIFICATION_FAILED;
+ } else if (!EVP_DigestVerifyFinal(&digest_ctx_, signature.peek_read(),
+ signature.available_read()))
return KM_ERROR_VERIFICATION_FAILED;
- else
- return KM_ERROR_OK;
+
+ return KM_ERROR_OK;
}
} // namespace keymaster
diff --git a/ecdsa_operation.h b/ecdsa_operation.h
index db986c8..297253e 100644
--- a/ecdsa_operation.h
+++ b/ecdsa_operation.h
@@ -18,6 +18,7 @@
#define SYSTEM_KEYMASTER_ECDSA_OPERATION_H_
#include <openssl/ec.h>
+#include <openssl/evp.h>
#include <UniquePtr.h>
@@ -27,42 +28,47 @@
class EcdsaOperation : public Operation {
public:
- EcdsaOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, EC_KEY* key)
- : Operation(purpose), digest_(digest), ecdsa_key_(key) {}
+ EcdsaOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, EVP_PKEY* key)
+ : Operation(purpose), digest_(digest), ecdsa_key_(key) {
+ EVP_MD_CTX_init(&digest_ctx_);
+ }
~EcdsaOperation();
- virtual keymaster_error_t Begin(const AuthorizationSet& /* input_params */,
- AuthorizationSet* /* output_params */) {
- if (digest_ != KM_DIGEST_NONE)
- return KM_ERROR_UNSUPPORTED_DIGEST;
- return KM_ERROR_OK;
- }
- virtual keymaster_error_t Update(const AuthorizationSet& /* additional_params */,
- const Buffer& input, Buffer* output, size_t* input_consumed);
- virtual keymaster_error_t Abort() { return KM_ERROR_OK; }
+ keymaster_error_t Abort() override { return KM_ERROR_OK; }
protected:
keymaster_error_t StoreData(const Buffer& input, size_t* input_consumed);
+ keymaster_error_t InitDigest();
keymaster_digest_t digest_;
- EC_KEY* ecdsa_key_;
+ const EVP_MD* digest_algorithm_;
+ EVP_PKEY* ecdsa_key_;
+ EVP_MD_CTX digest_ctx_;
Buffer data_;
};
class EcdsaSignOperation : public EcdsaOperation {
public:
- EcdsaSignOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, EC_KEY* key)
+ EcdsaSignOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, EVP_PKEY* key)
: EcdsaOperation(purpose, digest, key) {}
- virtual keymaster_error_t Finish(const AuthorizationSet& additional_params,
- const Buffer& signature, Buffer* output);
+ keymaster_error_t Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) override;
+ keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
+ Buffer* output, size_t* input_consumed) override;
+ keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& signature,
+ Buffer* output) override;
};
class EcdsaVerifyOperation : public EcdsaOperation {
public:
- EcdsaVerifyOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, EC_KEY* key)
+ EcdsaVerifyOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, EVP_PKEY* key)
: EcdsaOperation(purpose, digest, key) {}
- virtual keymaster_error_t Finish(const AuthorizationSet& additional_params,
- const Buffer& signature, Buffer* output);
+ keymaster_error_t Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) override;
+ keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
+ Buffer* output, size_t* input_consumed) override;
+ keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& signature,
+ Buffer* output) override;
};
class EcdsaOperationFactory : public OperationFactory {
@@ -73,13 +79,13 @@
const keymaster_digest_t* SupportedDigests(size_t* digest_count) const override;
virtual keymaster_purpose_t purpose() const = 0;
- virtual Operation* InstantiateOperation(keymaster_digest_t digest, EC_KEY* key) = 0;
+ virtual Operation* InstantiateOperation(keymaster_digest_t digest, EVP_PKEY* key) = 0;
};
class EcdsaSignOperationFactory : public EcdsaOperationFactory {
private:
keymaster_purpose_t purpose() const override { return KM_PURPOSE_SIGN; }
- Operation* InstantiateOperation(keymaster_digest_t digest, EC_KEY* key) {
+ Operation* InstantiateOperation(keymaster_digest_t digest, EVP_PKEY* key) {
return new EcdsaSignOperation(purpose(), digest, key);
}
};
@@ -87,7 +93,7 @@
class EcdsaVerifyOperationFactory : public EcdsaOperationFactory {
public:
keymaster_purpose_t purpose() const override { return KM_PURPOSE_VERIFY; }
- Operation* InstantiateOperation(keymaster_digest_t digest, EC_KEY* key) {
+ Operation* InstantiateOperation(keymaster_digest_t digest, EVP_PKEY* key) {
return new EcdsaVerifyOperation(KM_PURPOSE_VERIFY, digest, key);
}
};