Source release 19.2.0
This commit is contained in:
96
oemcrypto/util/README.md
Normal file
96
oemcrypto/util/README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# OEMCrypto Reference Utils
|
||||
|
||||
Small collection of OEMCrypto utilities which perform general operations
|
||||
which hold true for **any** standard implementation of OEMCrypto.
|
||||
|
||||
The goal of the utils is to provide high-level components which can perform
|
||||
certain complex operations found in OEMCrypto, with the intention to be used
|
||||
in the testbed code and unittests. The most important aspect of this is to
|
||||
avoid providing any implementation-specific utilities here.
|
||||
|
||||
Practical exceptions to goal
|
||||
|
||||
* The use of C++
|
||||
* Techincally specific to a specific implementation
|
||||
* Our testbed and test code both use C++, and this is intended to be
|
||||
used by these systems
|
||||
* Raw data formats
|
||||
* Generally, we use `std::string`, `std::vector<uint8_t>` and `uint8_t*` to
|
||||
store and manipulate bytes of data
|
||||
* Reasonable resource limitations
|
||||
* Theoretical algorithms to usually take into consideration computer
|
||||
resources, but real implementations are still subjected to them
|
||||
* Ex. max buffer size which component operate on
|
||||
* Use of Widevine's logging system
|
||||
|
||||
Example of goal-aligned components
|
||||
|
||||
* Standard cryptographic operations wrappers
|
||||
* Schemes, protocols and algorithms which are publicly published
|
||||
* Mechanically similar to other cryptographical software libraries
|
||||
* OEMCrypto-specific RSA and ECC cryptographic operations
|
||||
* OEMCrypto has a small set of protocols based on RSA and ECC cryptography
|
||||
* These protocols are well-defined by the OEMCrypto specification, and
|
||||
any implementation would need to include an implementation of these exact
|
||||
protocols
|
||||
* DRM key cryptographic operations
|
||||
* Built upon RSA and ECC operations
|
||||
* Only performs DRM key operations which ANY implementation would also
|
||||
have to perform in the same way
|
||||
|
||||
Example of hypothetical anti-goal components
|
||||
|
||||
* DRM key wrapping algorithms
|
||||
* The OEMCrypto specification (as of v19) does not specify exactly how
|
||||
wrapping a DRM key is to be performed.
|
||||
* Although many OEMs likely use the same implementaion as the reference,
|
||||
it is still an undefined operation.
|
||||
* Note: Standard parts of a wrapping algorithm (such as AES encryption)
|
||||
may be provided.
|
||||
* Usage Table Header/Entry encryptors
|
||||
* Similar to DRM key wrapping, the OEMCrypto specification (as of v19) does
|
||||
not specify exactly how the Usage Table's header and entries are to be
|
||||
encrypted and signed
|
||||
* Key handle to session ID serialization
|
||||
* This is NOT defined by the OEMCrypto specification
|
||||
|
||||
## Current Components
|
||||
|
||||
This is a non-exhausted list of components found within this library which
|
||||
provided general OEMCrypto operations.
|
||||
|
||||
* `EccPublicKey` and `EccPrivateKey`
|
||||
* High-level wrappers around OEMCrypto-specific protocols of Elliptic
|
||||
Curve Cryptography
|
||||
* Generally aimed for how ECC-based DRM keys are used, as well as for
|
||||
a small set of BCC operations
|
||||
* `RsaPublicKey` and `RsaPrivateKey`
|
||||
* High-level wrappers around OEMCrypto-specific protocols of RSA Cryptography
|
||||
* Generally aimed for how RSA-based DRM and OEM Cert keys are used, as
|
||||
well as a small set of Cast-specific operations
|
||||
* `DrmPrivateKey`
|
||||
* A high-level wrapper around OEMCrypto's DRM key
|
||||
* Provides a generic DRM key interface, which internally handles the
|
||||
different cases of using either a RSA or ECC based DRM key
|
||||
* `wvcrc32.h`
|
||||
* Set of functions for OEMCrypto's CRC-32 algorithm
|
||||
* CRC-32 is a general term for 32-bit Cyclic Redundancy Checks
|
||||
* OEMCrypto has a well-defined set of parameters which all implementers
|
||||
must use to work with Widevine's software stack
|
||||
* `hmac.h` and `cmac.h`
|
||||
* HMAC and CMAC C++ wrappers, restricted to only the algorithm parameters
|
||||
which are used within OEMCrypto
|
||||
* `KeyDeriver`
|
||||
* High-level wrapper around OEMCrypto's key derivation algorithm
|
||||
* Intended to be used only for messaging key derivation, which is
|
||||
well-defined within the OEMCrypto standard
|
||||
* Note: Although the testbed uses this for other types of keys, the
|
||||
methods provided by `KeyDeriver` are only designed with messaging keys
|
||||
in mind.
|
||||
* `CborValidator` and specialized validators
|
||||
* High-level validator for CBOR messages found within the Provisioning 4.0
|
||||
protocol.
|
||||
* Note: Although the error messages provided by this library are not defined
|
||||
the primary function of these components do follow the specification, and
|
||||
the error messages are used to inform a human user, not another software
|
||||
system.
|
||||
@@ -38,7 +38,8 @@ class EccPublicKey {
|
||||
// Creates a new public key equivalent of the provided private key.
|
||||
static std::unique_ptr<EccPublicKey> New(const EccPrivateKey& private_key);
|
||||
|
||||
// Loads a serialized EC public key.
|
||||
// Loads a ASN.1 DER serialized EC public key.
|
||||
//
|
||||
// The provided |buffer| must contain a valid ASN.1 DER encoded
|
||||
// SubjectPublicKey. Only supported curves by this API are those
|
||||
// enumerated by EccCurve.
|
||||
@@ -50,7 +51,7 @@ class EccPublicKey {
|
||||
// namedCurve: OID = secp256r1 | secp384r1 | secp521r1
|
||||
// }
|
||||
// },
|
||||
// subjectPublicKey: BIT STRING = ... -- SEC1 encoded ECPoint
|
||||
// subjectPublicKey: BIT STRING = ... -- SEC 1 encoded ECPoint
|
||||
// }
|
||||
//
|
||||
// Failure will occur if the provided |buffer| does not contain a
|
||||
@@ -60,13 +61,39 @@ class EccPublicKey {
|
||||
size_t length);
|
||||
static std::unique_ptr<EccPublicKey> Load(const std::string& buffer);
|
||||
static std::unique_ptr<EccPublicKey> Load(const std::vector<uint8_t>& buffer);
|
||||
// Loads EC public key from the |curve| and |buffer|.
|
||||
// The provided |buffer| must contain an EC point serialized from raw X9.62
|
||||
// format. For uncompressed form, it is a 1-byte prefix plus two 32-byte
|
||||
// integers representing X, Y coordinates.
|
||||
|
||||
// Loads a SEC 1 serialized EC public key.
|
||||
//
|
||||
// The provided |buffer| must contain a valid SEC 1 encoded EC point
|
||||
// belonging to the provided |curve|.
|
||||
//
|
||||
// SEC 1 section 2.3.3 specifies two supported formats, compressed or
|
||||
// uncompressed.
|
||||
//
|
||||
// Case uncompressed:
|
||||
// buffer: 0x04 || X || Y
|
||||
//
|
||||
// Where X and Y are fixed-width byte encodings of the public keys
|
||||
// x and y component.
|
||||
//
|
||||
// Case compressed:
|
||||
// buffer: (0x02 or 0x03) || X
|
||||
//
|
||||
// Where X is a fixed-width byte encoding of the public keys x
|
||||
// component; and the y component is derived using the curve
|
||||
// equation, and the sign of y is positive if lead byte is 0x02,
|
||||
// or negative if lead byte is 0x03.
|
||||
//
|
||||
// Note: The EC point encoding in SEC 1 is derived from the X9.62
|
||||
// format; however, the "evenness" version of the X9.62 compressed
|
||||
// point is NOT supported and is generally not recommended.
|
||||
static std::unique_ptr<EccPublicKey> LoadKeyPoint(EccCurve curve,
|
||||
const uint8_t* buffer,
|
||||
size_t length);
|
||||
static std::unique_ptr<EccPublicKey> LoadKeyPoint(EccCurve curve,
|
||||
const std::string& buffer);
|
||||
static std::unique_ptr<EccPublicKey> LoadKeyPoint(
|
||||
EccCurve curve, const std::vector<uint8_t>& buffer);
|
||||
|
||||
// Loads a serialized ECC private key, but only converting the public key.
|
||||
static std::unique_ptr<EccPublicKey> LoadPrivateKeyInfo(const uint8_t* buffer,
|
||||
@@ -93,6 +120,17 @@ class EccPublicKey {
|
||||
// Same as above, except directly returns the serialized key.
|
||||
// Returns an empty vector on error.
|
||||
std::vector<uint8_t> Serialize() const;
|
||||
// Serializes the public key into a SEC 1 encoded public key point.
|
||||
// To restore a key from the returned key point, the caller must
|
||||
// keep track of the specific curve of the key.
|
||||
//
|
||||
// If |compressed| is false (default), the key point will be
|
||||
// serialized in its uncompressed form; otherwise, the key will be
|
||||
// serialized in its compressed form.
|
||||
//
|
||||
// Directly returns the serialized public key.
|
||||
// Returns an empty vector on error.
|
||||
std::vector<uint8_t> SerializeAsSec1KeyPoint(bool compressed = false) const;
|
||||
|
||||
// Verifies the |signature| matches the provided |message| by the
|
||||
// private equivalent of this public key.
|
||||
@@ -114,15 +152,26 @@ class EccPublicKey {
|
||||
const std::string& signature) const;
|
||||
OEMCryptoResult VerifySignature(const std::vector<uint8_t>& message,
|
||||
const std::vector<uint8_t>& signature) const;
|
||||
// Verifies the raw |signature| matches the provided |message| by the
|
||||
// private equivalent of this public key.
|
||||
// A raw ECDSA signature consists of a pair of integers (r,s). The |signature|
|
||||
// is a concatenation of two octet strings resulting from the integer-to-octet
|
||||
// encoding of the values of r and s, in the order of (r||s).
|
||||
// Verifies the raw |signature| matches the provided |message| by
|
||||
// the private equivalent of this public key.
|
||||
// A raw ECDSA signature consists of a pair of integers (r, s).
|
||||
// The |signature| is a concatenation of the unsigned integers r and
|
||||
// s as two equal length octet strings using big-endian encoding.
|
||||
//
|
||||
// The |message| is digested using the same ECDSA algorithm as
|
||||
// VerifySignature().
|
||||
//
|
||||
// Returns:
|
||||
// OEMCrypto_SUCCESS if signature is valid
|
||||
// OEMCrypto_ERROR_SIGNATURE_FAILURE if the |signature| is invalid
|
||||
// Any other result indicates an unexpected error
|
||||
OEMCryptoResult VerifyRawSignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) const;
|
||||
OEMCryptoResult VerifyRawSignature(
|
||||
const std::vector<uint8_t>& message,
|
||||
const std::vector<uint8_t>& signature) const;
|
||||
|
||||
~EccPublicKey();
|
||||
|
||||
@@ -141,11 +190,12 @@ class EccPublicKey {
|
||||
bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
|
||||
// Initializes the public key object from a private.
|
||||
bool InitFromPrivateKey(const EccPrivateKey& private_key);
|
||||
// Initializes the public key object from the provided curve and key point
|
||||
// |buffer|.
|
||||
bool InitFromKeyPoint(EccCurve curve, const uint8_t* buffer, size_t length);
|
||||
// Digests the |message| and verifies signature against the provided signature
|
||||
// point.
|
||||
// Initializes the public key object from the provided |curve| and
|
||||
// SEC 1 encoded EC key point |buffer|.
|
||||
bool InitFromSec1KeyPoint(EccCurve curve, const uint8_t* buffer,
|
||||
size_t length);
|
||||
// Digests the |message| and verifies signature against the provided
|
||||
// ECDSA signature point |sig_point|.
|
||||
OEMCryptoResult DigestAndVerify(const uint8_t* message, size_t message_length,
|
||||
const ECDSA_SIG* sig_point) const;
|
||||
|
||||
@@ -181,7 +231,7 @@ class EccPrivateKey {
|
||||
// version: INTEGER = ecPrivateKeyVer1(1),
|
||||
// privateKey: OCTET STRING = ..., -- I2OSP of private key point
|
||||
// -- |parameters| are obtained from PrivateKeyInfo
|
||||
// publicKey: BIT STRING OPTIONAL = ... -- SEC1 encoded ECPoint
|
||||
// publicKey: BIT STRING OPTIONAL = ... -- SEC 1 encoded ECPoint
|
||||
// }
|
||||
// Note: If the public key is not included, then it is computed from
|
||||
// the private key.
|
||||
@@ -237,6 +287,12 @@ class EccPrivateKey {
|
||||
// Returns an empty vector on error.
|
||||
std::vector<uint8_t> SerializeAsPublicKey() const;
|
||||
|
||||
// Serializes the public component of the private key into an SEC 1
|
||||
// public key point.
|
||||
// See EccPublicKey::SerializeAsSec1KeyPoint() for details.
|
||||
std::vector<uint8_t> SerializeAsPublicSec1KeyPoint(
|
||||
bool compressed = false) const;
|
||||
|
||||
// Signs the provided |message| and serializes the signature
|
||||
// point to |signature| as a ASN.1 DER encoded ECDSA-Sig-Value.
|
||||
// This implementation uses ECDSA with the following digest
|
||||
@@ -261,6 +317,12 @@ class EccPrivateKey {
|
||||
// the actual signature generated by GenerateSignature().
|
||||
size_t SignatureSize() const;
|
||||
|
||||
// Special test method used to generate a raw ECDSA signature.
|
||||
// A raw ECDSA signature is a concatination of a same-width-big-endian
|
||||
// encoding of the ECDSA signature point components r and s.
|
||||
std::vector<uint8_t> GenerateRawSignature(
|
||||
const std::vector<uint8_t>& message) const;
|
||||
|
||||
// Derives the OEMCrypto session key used for deriving other keys.
|
||||
// The provided public key must be of the same curve.
|
||||
// On success, |session_key_size| is populated with the number of
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine License
|
||||
// Agreement.
|
||||
//
|
||||
// Implements utility functions for serializing and deserializing the fake key
|
||||
// handles used by the Ref and Testbed.
|
||||
//
|
||||
#ifndef WVOEC_UTIL_KEY_HANDLE_H_
|
||||
#define WVOEC_UTIL_KEY_HANDLE_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace wvoec {
|
||||
namespace util {
|
||||
// Size of a key handle, which for this implementation is just a session ID.
|
||||
constexpr size_t kKeyHandleSize = sizeof(OEMCrypto_SESSION);
|
||||
|
||||
OEMCryptoResult SerializeSessionToKeyHandle(OEMCrypto_SESSION session,
|
||||
uint8_t* key_handle,
|
||||
size_t* key_handle_length) {
|
||||
if (key_handle_length == nullptr) {
|
||||
LOGE("Null key handle length");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
if (key_handle == nullptr || *key_handle_length < kKeyHandleSize) {
|
||||
*key_handle_length = kKeyHandleSize;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
*key_handle_length = kKeyHandleSize;
|
||||
memcpy(key_handle, &session, kKeyHandleSize);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult DeserializeKeyHandleToSession(const uint8_t* key_handle,
|
||||
size_t key_handle_length,
|
||||
OEMCrypto_SESSION* session) {
|
||||
if (key_handle == nullptr) {
|
||||
LOGE("Null key handle");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (session == nullptr) {
|
||||
LOGE("Null session");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
if (key_handle_length != kKeyHandleSize) {
|
||||
LOGE("Invalid key handle length");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
memcpy(session, key_handle, kKeyHandleSize);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
} // namespace util
|
||||
} // namespace wvoec
|
||||
#endif // WVOEC_UTIL_KEY_HANDLE_H_
|
||||
@@ -41,7 +41,8 @@ class ScopedObject {
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return ptr_ != nullptr; }
|
||||
bool ok() const { return ptr_ != nullptr; }
|
||||
explicit operator bool() const { return ok(); }
|
||||
|
||||
Type& operator*() { return *ptr_; }
|
||||
Type* get() const { return ptr_; }
|
||||
|
||||
@@ -31,10 +31,31 @@ namespace {
|
||||
// larger needed.
|
||||
constexpr size_t kPrivateKeySize = 250;
|
||||
constexpr size_t kPublicKeySize = 164;
|
||||
// Estimated max size (in bytes) of a SEC 1 serialized ECC public key
|
||||
// point. Similar to above, this is based on a rough calculation for
|
||||
// secp521r1 and is padded by a few bytes to ensure it is larger than
|
||||
// needed.
|
||||
constexpr size_t kSec1PublicKeyPointMaxSize = 150;
|
||||
|
||||
// 256 bit key, intended to be used with CMAC-AES-256.
|
||||
constexpr size_t kEccSessionKeySize = 32;
|
||||
|
||||
// SEC 1 EC public key point lead byte values.
|
||||
constexpr uint8_t kSec1LeadCompressedPositive = 0x02;
|
||||
constexpr uint8_t kSec1LeadCompressedNegative = 0x03;
|
||||
constexpr uint8_t kSec1LeadUncompressed = 0x04;
|
||||
|
||||
// Checks that the first byte of a SEC 1 encoded EC public key point.
|
||||
// Only supported values are compressed w/ sign and uncompressed.
|
||||
// Other X9.62 values are not supported.
|
||||
constexpr bool IsSupportedSec1LeadByte(uint8_t lead_byte) {
|
||||
return lead_byte == kSec1LeadCompressedPositive ||
|
||||
lead_byte == kSec1LeadCompressedNegative ||
|
||||
lead_byte == kSec1LeadUncompressed;
|
||||
}
|
||||
|
||||
const char* BoolToStatus(bool value) { return value ? "Ok" : "Bad"; }
|
||||
|
||||
using ScopedBigNum = ScopedObject<BIGNUM, BN_free>;
|
||||
using ScopedBigNumCtx = ScopedObject<BN_CTX, BN_CTX_free>;
|
||||
using ScopedBio = ScopedObject<BIO, BIO_vfree>;
|
||||
@@ -46,6 +67,11 @@ using ScopedPrivateKeyInfo =
|
||||
using ScopedSigPoint = ScopedObject<ECDSA_SIG, ECDSA_SIG_free>;
|
||||
using ScopedEcPoint = ScopedObject<EC_POINT, EC_POINT_free>;
|
||||
|
||||
void OpensslFreeU8(uint8_t* ptr) { OPENSSL_free(ptr); }
|
||||
using ScopedBuffer = ScopedObject<uint8_t, OpensslFreeU8>;
|
||||
|
||||
// == EC Group Utilts ==
|
||||
|
||||
const EC_GROUP* GetEcGroup(EccCurve curve) {
|
||||
// Creating a named EC_GROUP is an expensive operation, and they
|
||||
// are always used in a manner which does not transfer ownership.
|
||||
@@ -141,63 +167,7 @@ EccCurve GetCurveFromKeyGroup(const EC_KEY* key) {
|
||||
return kEccCurveUnknown;
|
||||
}
|
||||
|
||||
// Creates EC public key from |curve| and |key_point|, and sets the result in
|
||||
// *|public_key|.
|
||||
bool GetPublicKeyFromKeyPoint(EccCurve curve, const uint8_t* key_point,
|
||||
size_t key_point_length, EC_KEY** public_key) {
|
||||
if (key_point == nullptr || key_point_length == 0) {
|
||||
return false;
|
||||
}
|
||||
const EC_GROUP* group = GetEcGroup(curve);
|
||||
if (!group) {
|
||||
LOGE("Failed to get ECC group for curve %d", curve);
|
||||
return false;
|
||||
}
|
||||
ScopedEcPoint point(EC_POINT_new(group));
|
||||
if (!point) {
|
||||
LOGE("Failed to new EC_POINT");
|
||||
return false;
|
||||
}
|
||||
if (!EC_POINT_oct2point(group, point.get(), key_point, key_point_length,
|
||||
nullptr)) {
|
||||
LOGE("Failed to convert the serialized point to EC_POINT");
|
||||
return false;
|
||||
}
|
||||
ScopedEcKey key(EC_KEY_new());
|
||||
if (!key) {
|
||||
LOGE("Failed to allocate key");
|
||||
return false;
|
||||
}
|
||||
if (!EC_KEY_set_group(key.get(), group)) {
|
||||
LOGE("Failed to set group");
|
||||
return false;
|
||||
}
|
||||
if (EC_KEY_set_public_key(key.get(), point.get()) == 0) {
|
||||
LOGE("Failed to convert the EC_POINT to EC_KEY");
|
||||
return false;
|
||||
}
|
||||
*public_key = key.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compares the public EC points of both keys to see if they are the
|
||||
// equal.
|
||||
// Both |public_key| and |private_key| must be of the same group.
|
||||
bool IsMatchingKeyPair(const EC_KEY* public_key, const EC_KEY* private_key) {
|
||||
ScopedBigNumCtx ctx(BN_CTX_new());
|
||||
if (!ctx) {
|
||||
LOGE("Failed to allocate BN ctx");
|
||||
return false;
|
||||
}
|
||||
// Returns: 1 if not equal, 0 if equal, -1 if error.
|
||||
const int res = EC_POINT_cmp(EC_KEY_get0_group(public_key),
|
||||
EC_KEY_get0_public_key(public_key),
|
||||
EC_KEY_get0_public_key(private_key), ctx.get());
|
||||
if (res == -1) {
|
||||
LOGE("Error occurred comparing keys");
|
||||
}
|
||||
return res == 0;
|
||||
}
|
||||
// == EC Key Operation Utilities ==
|
||||
|
||||
// Performs a SHA2 digest on the provided |message| and outputs the
|
||||
// computed hash to |digest|.
|
||||
@@ -284,7 +254,57 @@ void* WidevineEccKdf(const void* secret, size_t secret_length, void* key,
|
||||
return key;
|
||||
}
|
||||
|
||||
void OpensslFreeU8(uint8_t* ptr) { OPENSSL_free(ptr); }
|
||||
// Generates an EC signature point using ECDSA.
|
||||
// Outputs the signature to |sig_point|.
|
||||
// Returns true on success, false otherwise.
|
||||
bool DigestAndSign(EccCurve curve, const EC_KEY* private_key,
|
||||
const uint8_t* message, size_t message_length,
|
||||
ScopedSigPoint* sig_point) {
|
||||
if (private_key == nullptr || (message == nullptr && message_length > 0) ||
|
||||
sig_point == nullptr) {
|
||||
// These are expected to be caught before calling this function.
|
||||
return false;
|
||||
}
|
||||
// Digest message based on |curve|.
|
||||
std::vector<uint8_t> digest;
|
||||
if (!DigestMessage(curve, message, message_length, &digest)) {
|
||||
LOGE("Failed to digest message");
|
||||
return false;
|
||||
}
|
||||
// Generate signature point.
|
||||
// Note: OpenSSL and BoringSSL have slightly different APIs.
|
||||
// For OpenSSL, EC_KEY is not marked consts, however, it does not
|
||||
// modify the key.
|
||||
sig_point->reset(
|
||||
ECDSA_do_sign(digest.data(), static_cast<int>(digest.size()),
|
||||
const_cast<EC_KEY*>(private_key) /* Does not modify */));
|
||||
if (!*sig_point) {
|
||||
LOGE("Failed to perform ECDSA");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// == EC Key Utilities ==
|
||||
|
||||
// Compares the public EC points of both keys to see if they are the
|
||||
// equal.
|
||||
// Both |public_key| and |private_key| must be of the same group.
|
||||
bool IsMatchingKeyPair(const EC_KEY* public_key, const EC_KEY* private_key) {
|
||||
ScopedBigNumCtx ctx(BN_CTX_new());
|
||||
if (!ctx) {
|
||||
LOGE("Failed to allocate BN ctx");
|
||||
return false;
|
||||
}
|
||||
// Returns: 1 if not equal, 0 if equal, -1 if error.
|
||||
const int res = EC_POINT_cmp(EC_KEY_get0_group(public_key),
|
||||
EC_KEY_get0_public_key(public_key),
|
||||
EC_KEY_get0_public_key(private_key), ctx.get());
|
||||
if (res == -1) {
|
||||
LOGE("Error occurred comparing keys");
|
||||
}
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
// Internal ECC public key serialization.
|
||||
OEMCryptoResult SerializeEccPublicKey(const EC_KEY* key, uint8_t* buffer,
|
||||
@@ -305,7 +325,7 @@ OEMCryptoResult SerializeEccPublicKey(const EC_KEY* key, uint8_t* buffer,
|
||||
LOGE("Public key serialization failed");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
ScopedObject<uint8_t, OpensslFreeU8> der_key(der_key_raw);
|
||||
ScopedBuffer der_key(der_key_raw);
|
||||
der_key_raw = nullptr;
|
||||
if (!der_key) {
|
||||
LOGE("Encoded key is unexpectedly null");
|
||||
@@ -339,6 +359,139 @@ std::vector<uint8_t> SerializeEccPublicKey(const EC_KEY* key) {
|
||||
return key_data;
|
||||
}
|
||||
|
||||
// Internal ECC public key serialization for SEC 1 key points.
|
||||
std::vector<uint8_t> SerializeEccPublicKeyAsSec1KeyPoint(const EC_KEY* key,
|
||||
bool compressed) {
|
||||
if (key == nullptr) {
|
||||
// Programmer error, internal to this module.
|
||||
LOGE("Input |key| is null");
|
||||
return {};
|
||||
}
|
||||
// Obtain key point and group.
|
||||
const EC_POINT* key_point = EC_KEY_get0_public_key(key);
|
||||
if (key_point == nullptr) {
|
||||
LOGE("Failed to obtain key point");
|
||||
return {};
|
||||
}
|
||||
const EC_GROUP* group = EC_KEY_get0_group(key);
|
||||
if (group == nullptr) {
|
||||
LOGE("Failed to obtain curve group");
|
||||
return {};
|
||||
}
|
||||
ScopedBigNumCtx ctx(BN_CTX_new());
|
||||
if (!ctx) {
|
||||
LOGE("Failed to allocate BN CTX");
|
||||
return {};
|
||||
}
|
||||
std::vector<uint8_t> sec1_key_point(kSec1PublicKeyPointMaxSize, 0);
|
||||
// EC_POINT_point2oct() follows SEC 1 section 2.3.3 protocol for
|
||||
// serialization of a public key point.
|
||||
const size_t res = EC_POINT_point2oct(
|
||||
group, key_point,
|
||||
compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED,
|
||||
sec1_key_point.data(), sec1_key_point.size(), ctx.get());
|
||||
if (res == 0) {
|
||||
LOGE(
|
||||
"Failed to serialize public key point in SEC 1 format: "
|
||||
"compressed = %s",
|
||||
compressed ? "true" : "false");
|
||||
return {};
|
||||
}
|
||||
if (res > kSec1PublicKeyPointMaxSize) {
|
||||
// This shouldn't happen. Could be a bug in OpenSSL/BoringSSL,
|
||||
// or a result of future support of larger curve groups without
|
||||
// updating the related size estimates.
|
||||
LOGE(
|
||||
"Key point is unexpectedly large: "
|
||||
"actual_size = %zu, expected_max_size = %zu",
|
||||
res, kSec1PublicKeyPointMaxSize);
|
||||
return {};
|
||||
}
|
||||
sec1_key_point.resize(res);
|
||||
return sec1_key_point;
|
||||
}
|
||||
|
||||
void SetIetfComplianceFlags(EC_KEY* key) {
|
||||
// Required flags for IETF compliance.
|
||||
EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE);
|
||||
EC_KEY_set_conv_form(key, POINT_CONVERSION_UNCOMPRESSED);
|
||||
}
|
||||
|
||||
// Parses the EC key from a SEC 1 section 2.3.4 encoded public key.
|
||||
// Uses |curve| to determine the group of the key, and then converts
|
||||
// the encoded points from |buffer| into an EC point.
|
||||
// If successful, which is then the group and public key point will
|
||||
// be assigned to the provided |public_key|.
|
||||
//
|
||||
// Returns true on success, false otherwise.
|
||||
bool ParseEccSec1PublicKey(EccCurve curve, const uint8_t* buffer, size_t length,
|
||||
ScopedEcKey* public_key) {
|
||||
if (buffer == nullptr || length <= 1) {
|
||||
LOGE("Invalid SEC 1 encoded key point buffer");
|
||||
return false;
|
||||
}
|
||||
if (public_key == nullptr) {
|
||||
LOGE("Provided public key buffer is null");
|
||||
return false;
|
||||
}
|
||||
const uint8_t lead_byte = buffer[0];
|
||||
if (!IsSupportedSec1LeadByte(lead_byte)) {
|
||||
LOGE("Unsupported SEC 1 encoding: lead_byte = 0x%02x",
|
||||
static_cast<uint32_t>(lead_byte));
|
||||
return false;
|
||||
}
|
||||
// Step 1: Validate curve is supported.
|
||||
const EC_GROUP* group = GetEcGroup(curve);
|
||||
if (group == nullptr) {
|
||||
LOGE("Failed to get ECC group");
|
||||
return false;
|
||||
}
|
||||
// Step 2: Deserialize from SEC 1 to EC_POINT.
|
||||
ScopedEcPoint point(EC_POINT_new(group));
|
||||
if (!point) {
|
||||
LOGE("Failed to allocate EC_POINT");
|
||||
return false;
|
||||
}
|
||||
ScopedBigNumCtx ctx(BN_CTX_new());
|
||||
if (!ctx) {
|
||||
LOGE("Failed to allocate BN CTX");
|
||||
return false;
|
||||
}
|
||||
// Note: EC_POINT_oct2point() follows SEC 1 section 2.3.4 rules for
|
||||
// deserializing. It should be able to handle uncompressed and
|
||||
// compressed points.
|
||||
if (!EC_POINT_oct2point(group, point.get(), buffer, length, ctx.get())) {
|
||||
LOGE("Failed to convert SEC 1 encoded key point to EC_POINT: type = %s",
|
||||
lead_byte == kSec1LeadUncompressed ? "Uncompressed" : "Compressed");
|
||||
return false;
|
||||
}
|
||||
// Step 3: Construct public key from EC_POINT.
|
||||
public_key->reset(EC_KEY_new());
|
||||
if (!*public_key) {
|
||||
LOGE("Failed to allocate key");
|
||||
return false;
|
||||
}
|
||||
if (!EC_KEY_set_group(public_key->get(), group)) {
|
||||
LOGE("Failed to set group");
|
||||
return false;
|
||||
}
|
||||
if (!EC_KEY_set_public_key(public_key->get(), point.get())) {
|
||||
LOGE("Failed to set public key point");
|
||||
return false;
|
||||
}
|
||||
// Step 4: Validate and set compliance flags.
|
||||
const int check = EC_KEY_check_key(public_key->get());
|
||||
if (check == 0) {
|
||||
LOGE("ECC key parameters are invalid");
|
||||
return false;
|
||||
} else if (check == -1) {
|
||||
LOGE("Failed to check ECC key");
|
||||
return false;
|
||||
}
|
||||
SetIetfComplianceFlags(public_key->get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseEccPrivateKeyInfo(const uint8_t* buffer, size_t length,
|
||||
ScopedEcKey* key, EccCurve* curve) {
|
||||
if (length == 0) {
|
||||
@@ -387,9 +540,7 @@ bool ParseEccPrivateKeyInfo(const uint8_t* buffer, size_t length,
|
||||
LOGE("Failed to determine key group");
|
||||
return false;
|
||||
}
|
||||
// Required flags for IETF compliance.
|
||||
EC_KEY_set_asn1_flag(key->get(), OPENSSL_EC_NAMED_CURVE);
|
||||
EC_KEY_set_conv_form(key->get(), POINT_CONVERSION_UNCOMPRESSED);
|
||||
SetIetfComplianceFlags(key->get());
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
@@ -458,14 +609,58 @@ std::unique_ptr<EccPublicKey> EccPublicKey::Load(
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
||||
const uint8_t* buffer, size_t length) {
|
||||
std::unique_ptr<EccPublicKey> EccPublicKey::LoadKeyPoint(EccCurve curve,
|
||||
const uint8_t* buffer,
|
||||
size_t length) {
|
||||
if (buffer == nullptr) {
|
||||
LOGE("Provided public key buffer is null");
|
||||
LOGE("Provided key point buffer is null");
|
||||
return nullptr;
|
||||
}
|
||||
if (length == 0) {
|
||||
LOGE("Provided public key buffer is zero length");
|
||||
LOGE("Provided key point buffer is zero length");
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
|
||||
if (!key->InitFromSec1KeyPoint(curve, buffer, length)) {
|
||||
LOGE(
|
||||
"Failed to initialize public key from SEC 1 key point: "
|
||||
"curve = %s, length = %zu",
|
||||
EccCurveToString(curve).c_str(), length);
|
||||
key.reset();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<EccPublicKey> EccPublicKey::LoadKeyPoint(
|
||||
EccCurve curve, const std::string& buffer) {
|
||||
if (buffer.empty()) {
|
||||
LOGE("Provided public key point buffer is empty");
|
||||
return std::unique_ptr<EccPublicKey>();
|
||||
}
|
||||
return LoadKeyPoint(curve, reinterpret_cast<const uint8_t*>(buffer.data()),
|
||||
buffer.size());
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<EccPublicKey> EccPublicKey::LoadKeyPoint(
|
||||
EccCurve curve, const std::vector<uint8_t>& buffer) {
|
||||
if (buffer.empty()) {
|
||||
LOGE("Provided public key point buffer is empty");
|
||||
return std::unique_ptr<EccPublicKey>();
|
||||
}
|
||||
return LoadKeyPoint(curve, buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
||||
const uint8_t* buffer, size_t length) {
|
||||
if (buffer == nullptr) {
|
||||
LOGE("Provided private key buffer is null");
|
||||
return nullptr;
|
||||
}
|
||||
if (length == 0) {
|
||||
LOGE("Provided private key buffer is zero length");
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
|
||||
@@ -480,7 +675,7 @@ std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
||||
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
||||
const std::string& buffer) {
|
||||
if (buffer.empty()) {
|
||||
LOGE("Provided public key buffer is empty");
|
||||
LOGE("Provided private key buffer is empty");
|
||||
return std::unique_ptr<EccPublicKey>();
|
||||
}
|
||||
return LoadPrivateKeyInfo(reinterpret_cast<const uint8_t*>(buffer.data()),
|
||||
@@ -491,32 +686,12 @@ std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
||||
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
||||
const std::vector<uint8_t>& buffer) {
|
||||
if (buffer.empty()) {
|
||||
LOGE("Provided public key buffer is empty");
|
||||
LOGE("Provided private key buffer is empty");
|
||||
return std::unique_ptr<EccPublicKey>();
|
||||
}
|
||||
return LoadPrivateKeyInfo(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<EccPublicKey> EccPublicKey::LoadKeyPoint(EccCurve curve,
|
||||
const uint8_t* buffer,
|
||||
size_t length) {
|
||||
if (buffer == nullptr) {
|
||||
LOGE("Provided key point buffer is null");
|
||||
return nullptr;
|
||||
}
|
||||
if (length == 0) {
|
||||
LOGE("Provided key point buffer is zero length");
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
|
||||
if (!key->InitFromKeyPoint(curve, buffer, length)) {
|
||||
LOGE("Failed to initialize public key from KeyPoint");
|
||||
key.reset();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
bool EccPublicKey::IsMatchingPrivateKey(
|
||||
const EccPrivateKey& private_key) const {
|
||||
if (private_key.curve() != curve_) {
|
||||
@@ -534,6 +709,11 @@ std::vector<uint8_t> EccPublicKey::Serialize() const {
|
||||
return SerializeEccPublicKey(key_);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> EccPublicKey::SerializeAsSec1KeyPoint(
|
||||
bool compressed) const {
|
||||
return SerializeEccPublicKeyAsSec1KeyPoint(key_, compressed);
|
||||
}
|
||||
|
||||
OEMCryptoResult EccPublicKey::VerifySignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
@@ -590,26 +770,48 @@ OEMCryptoResult EccPublicKey::VerifyRawSignature(
|
||||
LOGE("Bad message data");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (signature_length % 2 == 1) {
|
||||
if ((signature_length % 2) == 1) {
|
||||
// Raw ECDSA signatures should alawys be even length.
|
||||
LOGE("Bad signature size: %zu", signature_length);
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
// Parse signature.
|
||||
const int r_s_size = static_cast<int>(signature_length) / 2;
|
||||
ScopedBigNum r(BN_bin2bn(signature, r_s_size, nullptr));
|
||||
ScopedBigNum s(BN_bin2bn(&signature[r_s_size], r_s_size, nullptr));
|
||||
// Note: Must ensure both |r| and |s| are not null. If one is
|
||||
// null, then the other might not get freed when calling
|
||||
// ECDSA_SIG_set0().
|
||||
if (!s || !r) {
|
||||
LOGE("Failed to parse R/S values: r = %s, s = %s, r_s_size = %d",
|
||||
BoolToStatus(r.ok()), BoolToStatus(s.ok()), r_s_size);
|
||||
// Technically, any byte string should be convertable to an
|
||||
// integer. This must be an OpenSSL/BoringSSL error.
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
ScopedSigPoint sig_point(ECDSA_SIG_new());
|
||||
if (!sig_point) {
|
||||
LOGE("Error occurred in ECDSA_SIG_new()");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
const int r_s_size = static_cast<int>(signature_length) / 2;
|
||||
if (!ECDSA_SIG_set0(sig_point.get(), BN_bin2bn(signature, r_s_size, nullptr),
|
||||
BN_bin2bn(signature + r_s_size, r_s_size, nullptr))) {
|
||||
LOGE("Failed to parse signature");
|
||||
// Most likely an invalid signature than an OpenSSL error.
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
if (!ECDSA_SIG_set0(sig_point.get(), r.release(), s.release())) {
|
||||
LOGE("Failed to set ECDSA_SIG components");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return DigestAndVerify(message, message_length, sig_point.get());
|
||||
}
|
||||
|
||||
OEMCryptoResult EccPublicKey::VerifyRawSignature(
|
||||
const std::vector<uint8_t>& message,
|
||||
const std::vector<uint8_t>& signature) const {
|
||||
if (signature.empty()) {
|
||||
LOGE("Signature should not be empty");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
return VerifyRawSignature(message.data(), message.size(), signature.data(),
|
||||
signature.size());
|
||||
}
|
||||
|
||||
EccPublicKey::~EccPublicKey() {
|
||||
if (key_ != nullptr) {
|
||||
EC_KEY_free(key_);
|
||||
@@ -641,21 +843,21 @@ bool EccPublicKey::InitFromSubjectPublicKeyInfo(const uint8_t* buffer,
|
||||
LOGE("Failed to determine key group");
|
||||
return false;
|
||||
}
|
||||
// Required flags for IETF compliance.
|
||||
EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE);
|
||||
EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED);
|
||||
SetIetfComplianceFlags(key.get());
|
||||
key_ = key.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EccPublicKey::InitFromPrivateKeyInfo(const uint8_t* buffer,
|
||||
size_t length) {
|
||||
ScopedEcKey private_key;
|
||||
if (!ParseEccPrivateKeyInfo(buffer, length, &private_key, &curve_)) {
|
||||
ScopedEcKey key;
|
||||
if (!ParseEccPrivateKeyInfo(buffer, length, &key, &curve_)) {
|
||||
return false;
|
||||
}
|
||||
// TODO(sigquit): Strip private information.
|
||||
key_ = private_key.release();
|
||||
// OpenSSL allows for removing the private component of the
|
||||
// EC key; however, BoringSSL does not support it, and will
|
||||
// crash if attempted.
|
||||
key_ = key.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -674,35 +876,21 @@ bool EccPublicKey::InitFromPrivateKey(const EccPrivateKey& private_key) {
|
||||
LOGE("Failed to set public point");
|
||||
return false;
|
||||
}
|
||||
SetIetfComplianceFlags(key.get());
|
||||
curve_ = private_key.curve();
|
||||
// Required flags for IETF compliance.
|
||||
EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE);
|
||||
EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED);
|
||||
key_ = key.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EccPublicKey::InitFromKeyPoint(EccCurve curve, const uint8_t* buffer,
|
||||
size_t length) {
|
||||
if (buffer == nullptr || length == 0) {
|
||||
LOGE("Provided key point buffer is empty");
|
||||
bool EccPublicKey::InitFromSec1KeyPoint(EccCurve curve, const uint8_t* buffer,
|
||||
size_t length) {
|
||||
ScopedEcKey key;
|
||||
if (!ParseEccSec1PublicKey(curve, buffer, length, &key)) {
|
||||
LOGE("Failed to parse SEC 1 encoded public key point");
|
||||
return false;
|
||||
}
|
||||
EC_KEY* ec_key;
|
||||
if (!GetPublicKeyFromKeyPoint(curve, buffer, length, &ec_key)) {
|
||||
return false;
|
||||
}
|
||||
ScopedEcKey key(ec_key);
|
||||
if (EC_KEY_check_key(key.get()) != 1) {
|
||||
LOGE("Invalid public EC key");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Required flags for IETF compliance.
|
||||
EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE);
|
||||
EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED);
|
||||
key_ = key.release();
|
||||
curve_ = curve;
|
||||
key_ = key.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -871,6 +1059,11 @@ std::vector<uint8_t> EccPrivateKey::SerializeAsPublicKey() const {
|
||||
return SerializeEccPublicKey(key_);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> EccPrivateKey::SerializeAsPublicSec1KeyPoint(
|
||||
bool compressed) const {
|
||||
return SerializeEccPublicKeyAsSec1KeyPoint(key_, compressed);
|
||||
}
|
||||
|
||||
OEMCryptoResult EccPrivateKey::GenerateSignature(
|
||||
const uint8_t* message, size_t message_length, uint8_t* signature,
|
||||
size_t* signature_length) const {
|
||||
@@ -891,21 +1084,13 @@ OEMCryptoResult EccPrivateKey::GenerateSignature(
|
||||
*signature_length = expected_signature_length;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
// Step 1: Hash message.
|
||||
std::vector<uint8_t> digest;
|
||||
if (!DigestMessage(curve_, message, message_length, &digest)) {
|
||||
LOGE("Failed to digest message");
|
||||
// Generate sig point.
|
||||
ScopedSigPoint sig_point;
|
||||
if (!DigestAndSign(curve_, key_, message, message_length, &sig_point)) {
|
||||
LOGE("Failed to digest and sign");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
// Step 2: Generate signature point.
|
||||
ScopedSigPoint sig_point(
|
||||
ECDSA_do_sign(digest.data(), static_cast<int>(digest.size()), key_));
|
||||
if (!sig_point) {
|
||||
LOGE("Failed to perform ECDSA");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
// Step 3: Serialize
|
||||
// Serialize to DER encoded ECDSA-Sig-Value.
|
||||
std::vector<uint8_t> temp(expected_signature_length);
|
||||
uint8_t* sig_ptr = temp.data();
|
||||
const int res = i2d_ECDSA_SIG(sig_point.get(), &sig_ptr);
|
||||
@@ -915,6 +1100,8 @@ OEMCryptoResult EccPrivateKey::GenerateSignature(
|
||||
}
|
||||
const size_t required_size = static_cast<size_t>(res);
|
||||
if (signature == nullptr || *signature_length < required_size) {
|
||||
// Unlikely, but possible that ECDSA_size() returned too small
|
||||
// a value.
|
||||
*signature_length = required_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
@@ -956,6 +1143,52 @@ std::vector<uint8_t> EccPrivateKey::GenerateSignature(
|
||||
|
||||
size_t EccPrivateKey::SignatureSize() const { return ECDSA_size(key_); }
|
||||
|
||||
std::vector<uint8_t> EccPrivateKey::GenerateRawSignature(
|
||||
const std::vector<uint8_t>& message) const {
|
||||
// Generate sig point.
|
||||
ScopedSigPoint sig_point;
|
||||
if (!DigestAndSign(curve_, key_, message.data(), message.size(),
|
||||
&sig_point)) {
|
||||
LOGE("Failed to digest and sign");
|
||||
return {};
|
||||
}
|
||||
// Serialize to same-width-big-endian of both sig point components
|
||||
// r and s.
|
||||
const BIGNUM* r = ECDSA_SIG_get0_r(sig_point.get());
|
||||
const BIGNUM* s = ECDSA_SIG_get0_s(sig_point.get());
|
||||
if (r == nullptr || s == nullptr) {
|
||||
LOGE("Failed to extract R/S: r = %s, s = %s", BoolToStatus(r != nullptr),
|
||||
BoolToStatus(s != nullptr));
|
||||
return {};
|
||||
}
|
||||
// Determine the size, must use the larger of the two.
|
||||
const int r_size = BN_num_bytes(r);
|
||||
const int s_size = BN_num_bytes(s);
|
||||
if (r_size <= 0 || s_size <= 0) {
|
||||
LOGE("Failed to determine R/S size: r_size = %d, s_size = %d", r_size,
|
||||
s_size);
|
||||
return {};
|
||||
}
|
||||
const int component_size = (r_size >= s_size ? r_size : s_size);
|
||||
const size_t total_size = static_cast<size_t>(component_size) * 2;
|
||||
// Serialize R.
|
||||
std::vector<uint8_t> raw_signature(total_size, 0);
|
||||
int res = BN_bn2binpad(r, raw_signature.data(), component_size);
|
||||
if (res != component_size) {
|
||||
LOGE("Failed to serialize R component: result = %d, component_size = %d",
|
||||
res, component_size);
|
||||
return {};
|
||||
}
|
||||
// Serialize S.
|
||||
res = BN_bn2binpad(s, &raw_signature[component_size], component_size);
|
||||
if (res != component_size) {
|
||||
LOGE("Failed to serialize S component: result = %d, component_size = %d",
|
||||
res, component_size);
|
||||
return {};
|
||||
}
|
||||
return raw_signature;
|
||||
}
|
||||
|
||||
OEMCryptoResult EccPrivateKey::DeriveSessionKey(
|
||||
const EccPublicKey& public_key, uint8_t* session_key,
|
||||
size_t* session_key_size) const {
|
||||
@@ -1046,9 +1279,7 @@ bool EccPrivateKey::InitFromCurve(EccCurve curve) {
|
||||
return false;
|
||||
}
|
||||
curve_ = curve;
|
||||
// Required flags for IETF compliance.
|
||||
EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE);
|
||||
EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED);
|
||||
SetIetfComplianceFlags(key.get());
|
||||
key_ = key.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -202,14 +202,12 @@ OEMCryptoResult OemCertificate::GetPublicCertificate(
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const std::vector<uint8_t>& cert_data = public_cert_->cert_data();
|
||||
if (*public_cert_length < cert_data.size()) {
|
||||
if (public_cert == nullptr || *public_cert_length < cert_data.size()) {
|
||||
*public_cert_length = cert_data.size();
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
*public_cert_length = cert_data.size();
|
||||
if (public_cert != nullptr) {
|
||||
memcpy(public_cert, cert_data.data(), cert_data.size());
|
||||
}
|
||||
memcpy(public_cert, cert_data.data(), cert_data.size());
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -806,14 +806,12 @@ OEMCryptoResult RsaPublicKey::EncryptOaep(const uint8_t* message,
|
||||
}
|
||||
// Step 2: Copy encrypted data key.
|
||||
const size_t enc_size = static_cast<size_t>(enc_res);
|
||||
if (*enc_message_length < enc_size) {
|
||||
if (enc_message == nullptr || *enc_message_length < enc_size) {
|
||||
*enc_message_length = enc_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
*enc_message_length = enc_size;
|
||||
if (enc_message != nullptr) {
|
||||
memcpy(enc_message, encrypt_buffer.data(), enc_size);
|
||||
}
|
||||
memcpy(enc_message, encrypt_buffer.data(), enc_size);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,104 @@ TEST_P(OEMCryptoEccKeyTest, SerializeAndReloadPublicKey) {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that a public and private key can be serialized as a SEC 1
|
||||
// key point, and that a public key can be reloaded from the key point.
|
||||
// Uses the default option for point compression.
|
||||
TEST_P(OEMCryptoEccKeyTest, SerializeAndReloadAsPublicKeySec1KeyPoint) {
|
||||
auto pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
const std::vector<uint8_t> key_point_from_priv =
|
||||
key_->SerializeAsPublicSec1KeyPoint();
|
||||
ASSERT_FALSE(key_point_from_priv.empty())
|
||||
<< "Failed to serialize from private key";
|
||||
const std::vector<uint8_t> key_point_from_pub =
|
||||
pub_key->SerializeAsSec1KeyPoint();
|
||||
ASSERT_FALSE(key_point_from_pub.empty())
|
||||
<< "Failed to serialize from public key";
|
||||
|
||||
// Check that both are equal.
|
||||
EXPECT_EQ(key_point_from_priv, key_point_from_pub);
|
||||
|
||||
// Reload new public key from key point data.
|
||||
auto loaded_key =
|
||||
EccPublicKey::LoadKeyPoint(key_->curve(), key_point_from_priv);
|
||||
ASSERT_TRUE(loaded_key) << "Failed to load public key from key point";
|
||||
|
||||
const std::vector<uint8_t> key_point_from_loaded =
|
||||
loaded_key->SerializeAsSec1KeyPoint();
|
||||
ASSERT_FALSE(key_point_from_loaded.empty())
|
||||
<< "Failed to serialize from loaded key";
|
||||
|
||||
EXPECT_EQ(key_point_from_priv, key_point_from_loaded);
|
||||
}
|
||||
|
||||
// Same as above, except explicitly serializes key point in its
|
||||
// compressed form.
|
||||
TEST_P(OEMCryptoEccKeyTest,
|
||||
SerializeAndReloadAsPublicKeySec1KeyPointCompressed) {
|
||||
constexpr bool kCompressed = true;
|
||||
auto pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
const std::vector<uint8_t> key_point_from_priv =
|
||||
key_->SerializeAsPublicSec1KeyPoint(kCompressed);
|
||||
ASSERT_FALSE(key_point_from_priv.empty())
|
||||
<< "Failed to serialize from private key";
|
||||
const std::vector<uint8_t> key_point_from_pub =
|
||||
pub_key->SerializeAsSec1KeyPoint(kCompressed);
|
||||
ASSERT_FALSE(key_point_from_pub.empty())
|
||||
<< "Failed to serialize from public key";
|
||||
|
||||
// Check that both are equal.
|
||||
EXPECT_EQ(key_point_from_priv, key_point_from_pub);
|
||||
|
||||
// Reload new public key from key point data.
|
||||
auto loaded_key =
|
||||
EccPublicKey::LoadKeyPoint(key_->curve(), key_point_from_priv);
|
||||
ASSERT_TRUE(loaded_key) << "Failed to load public key from key point";
|
||||
|
||||
const std::vector<uint8_t> key_point_from_loaded =
|
||||
loaded_key->SerializeAsSec1KeyPoint(kCompressed);
|
||||
ASSERT_FALSE(key_point_from_loaded.empty())
|
||||
<< "Failed to serialize from loaded key";
|
||||
|
||||
EXPECT_EQ(key_point_from_priv, key_point_from_loaded);
|
||||
}
|
||||
|
||||
// Same as above, except explicitly serializes key point in its
|
||||
// uncompressed form.
|
||||
TEST_P(OEMCryptoEccKeyTest,
|
||||
SerializeAndReloadAsPublicKeySec1KeyPointUncompressed) {
|
||||
constexpr bool kUncompressed = false;
|
||||
auto pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
const std::vector<uint8_t> key_point_from_priv =
|
||||
key_->SerializeAsPublicSec1KeyPoint(kUncompressed);
|
||||
ASSERT_FALSE(key_point_from_priv.empty())
|
||||
<< "Failed to serialize from private key";
|
||||
const std::vector<uint8_t> key_point_from_pub =
|
||||
pub_key->SerializeAsSec1KeyPoint(kUncompressed);
|
||||
ASSERT_FALSE(key_point_from_pub.empty())
|
||||
<< "Failed to serialize from public key";
|
||||
|
||||
// Check that both are equal.
|
||||
EXPECT_EQ(key_point_from_priv, key_point_from_pub);
|
||||
|
||||
// Reload new public key from key point data.
|
||||
auto loaded_key =
|
||||
EccPublicKey::LoadKeyPoint(key_->curve(), key_point_from_priv);
|
||||
ASSERT_TRUE(loaded_key) << "Failed to load public key from key point";
|
||||
|
||||
const std::vector<uint8_t> key_point_from_loaded =
|
||||
loaded_key->SerializeAsSec1KeyPoint(kUncompressed);
|
||||
ASSERT_FALSE(key_point_from_loaded.empty())
|
||||
<< "Failed to serialize from loaded key";
|
||||
|
||||
EXPECT_EQ(key_point_from_priv, key_point_from_loaded);
|
||||
}
|
||||
|
||||
// Checks that the ECC signature generating API operates similar to
|
||||
// existing signature generation functions.
|
||||
TEST_P(OEMCryptoEccKeyTest, GenerateSignature) {
|
||||
@@ -194,12 +292,14 @@ TEST_P(OEMCryptoEccKeyTest, GenerateSignature) {
|
||||
EXPECT_LE(signature_size, key_->SignatureSize());
|
||||
}
|
||||
|
||||
// Checks that ECC signatures can be verified by an ECC public key.
|
||||
// Checks that ECC signatures (ASN.1 DER encoded ECDSA-Sig-Value) can
|
||||
// be verified by an ECC public key.
|
||||
TEST_P(OEMCryptoEccKeyTest, VerifySignature) {
|
||||
const std::vector<uint8_t> message = RandomData(kMessageSize);
|
||||
ASSERT_FALSE(message.empty()) << "CdmRandom failed";
|
||||
|
||||
const std::vector<uint8_t> signature = key_->GenerateSignature(message);
|
||||
ASSERT_FALSE(signature.empty());
|
||||
|
||||
std::unique_ptr<EccPublicKey> pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
@@ -217,6 +317,32 @@ TEST_P(OEMCryptoEccKeyTest, VerifySignature) {
|
||||
pub_key->VerifySignature(message, bad_signature));
|
||||
}
|
||||
|
||||
// Checks that ECC raw signatures (concatinated same-width-big-endian
|
||||
// encoded signature point values r and s) can be verified by an ECC
|
||||
// public key.
|
||||
TEST_P(OEMCryptoEccKeyTest, VerifyRawSignature) {
|
||||
const std::vector<uint8_t> message = RandomData(kMessageSize);
|
||||
ASSERT_FALSE(message.empty()) << "CdmRandom failed";
|
||||
|
||||
const std::vector<uint8_t> signature = key_->GenerateRawSignature(message);
|
||||
ASSERT_FALSE(signature.empty());
|
||||
|
||||
std::unique_ptr<EccPublicKey> pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->VerifyRawSignature(message, signature));
|
||||
|
||||
// Check with different message.
|
||||
const std::vector<uint8_t> message_two = RandomData(kMessageSize);
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
|
||||
pub_key->VerifyRawSignature(message_two, signature));
|
||||
|
||||
// Check with bad signature.
|
||||
const std::vector<uint8_t> bad_signature = RandomData(signature.size());
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
|
||||
pub_key->VerifyRawSignature(message, bad_signature));
|
||||
}
|
||||
|
||||
// Verifies the session key exchange protocol used by the licensing
|
||||
// server.
|
||||
TEST_P(OEMCryptoEccKeyTest, DeriveSessionKey) {
|
||||
|
||||
Reference in New Issue
Block a user