Source release 19.2.0

This commit is contained in:
Alex Dale
2024-06-25 14:03:53 -07:00
parent b8bdfccebe
commit cd8256726f
89 changed files with 2747 additions and 35949 deletions

96
oemcrypto/util/README.md Normal file
View 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.

View File

@@ -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

View File

@@ -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_

View File

@@ -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_; }

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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) {