Snap for 8352742 from d3c653bd11 to tm-release

Change-Id: I84af37a125e3fc4a895b941ad136fd63b3e35f21
This commit is contained in:
Android Build Coastguard Worker
2022-03-25 01:20:26 +00:00
21 changed files with 4195 additions and 40 deletions

View File

@@ -59,18 +59,30 @@ class UsageTableHeader {
// If restoring fails, then a new usage table is created.
// Note: No OEMCrypto session for the same security level should be
// opened before calling.
// Threading: Method requires care of caller for exclusive access
// (ie. test setup or CryptoSession).
bool Init(CdmSecurityLevel security_level, CryptoSession* crypto_session);
// |persistent_license| false indicates usage info record
// Adds a new entry to the OEMCrypto usage table header, and associates
// the entry with the provided |crypto_session|. The index of the new
// usage entry will be returned by |usage_entry_number|.
//
// Type of entry depends on the value of |persistent_license|:
// false - usage info / secure stop record
// true - offline license
//
// Threading: Method takes exclusive use of |usage_table_header_lock_|.
virtual CdmResponseType AddEntry(CryptoSession* crypto_session,
bool persistent_license,
const CdmKeySetId& key_set_id,
const std::string& usage_info_filename,
const CdmKeyResponse& license_message,
uint32_t* usage_entry_number);
// Threading: Method takes exclusive use of |usage_table_header_lock_|.
virtual CdmResponseType LoadEntry(CryptoSession* crypto_session,
const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number);
// Threading: Method takes exclusive use of |usage_table_header_lock_|.
virtual CdmResponseType UpdateEntry(uint32_t usage_entry_number,
CryptoSession* crypto_session,
CdmUsageEntry* usage_entry);
@@ -80,6 +92,8 @@ class UsageTableHeader {
// to InvalidateEntry and MoveEntry are made.
// If |defrag_table| is true, the table will be defragmented after
// the entry has been invalidated.
//
// Threading: Method takes exclusive use of |usage_table_header_lock_|.
virtual CdmResponseType InvalidateEntry(uint32_t usage_entry_number,
bool defrag_table,
DeviceFiles* device_files,
@@ -91,8 +105,14 @@ class UsageTableHeader {
// when InvalidateEntry is mocked. This allows one to test methods that are
// dependent on InvalidateEntry without having to set expectations
// for the objects that InvalidateEntry depends on.
//
// Threading: Method requires care of caller for exclusive access.
void InvalidateEntryForTest(uint32_t usage_entry_number);
// == Table information methods ==
// Threading: None of the following are thread safe. Intended for
// testing or internal use.
size_t size() { return usage_entry_info_.size(); }
size_t potential_table_capacity() const { return potential_table_capacity_; }
@@ -135,27 +155,36 @@ class UsageTableHeader {
// Creates a new, empty usage table. Any existing usage table files
// will be deleted.
// Threading: Method takes exclusive use of |usage_table_header_lock_|
// when required.
bool CreateNewTable(CryptoSession* const crypto_session);
// Attempts to restore the usage table from persistent storage, and
// loads the usage table header into OEMCrypto.
// Note: No other OEMCrypto session should be opened before calling.
// Threading: Method takes exclusive use of |usage_table_header_lock_|
// when required.
bool RestoreTable(CryptoSession* const crypto_session);
// Performs a check that there are no open OEMCrypto sessions for
// the current security level of the usage table.
// Threading: No special threading requirements.
bool OpenSessionCheck(CryptoSession* const crypto_session);
// Performs a check that the OEMCrypto table can support at least
// one more entry if the table is at or near the reported capacity.
// If this check fails, a new usage table SHOULD be created.
// Threading: Method requires caller to take exclusive use of
// |usage_table_header_lock_|.
bool CapacityCheck(CryptoSession* const crypto_session);
// Attempts to determine the capacity of the OEMCrypto usage table.
// Sets the result to |potential_table_capacity_|.
// Threading: Method requires caller to take exclusive use of
// |usage_table_header_lock_|.
bool DetermineTableCapacity(CryptoSession* crypto_session);
// == Table operation methods ==
// NOTE: The following "Table operation methods" require
// |usage_table_header_lock_| to be taken before calling.
// Threading: All of the following methods require caller to take
// exclusive use of |usage_table_header_lock_|.
// Creates a new entry for the provided crypto session. If the
// entry is created successfully in OEMCrypto, then a new entry

View File

@@ -1303,7 +1303,7 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
const OEMCryptoResult status = OEMCrypto_LoadOEMPrivateKey(oec_session_id_);
if (status != OEMCrypto_SUCCESS) {
return MapOEMCryptoResult(status, GET_TOKEN_FROM_OEM_CERT_ERROR,
"GetTokenFromOemCert");
"PrepareAndSignProvisioningRequest");
}
} else {
LOGE("Unknown method %d", pre_provision_token_type_);
@@ -1385,20 +1385,6 @@ CdmResponseType CryptoSession::LoadEntitledContentKeys(
CdmResponseType CryptoSession::LoadCertificatePrivateKey(
const CryptoWrappedKey& private_key) {
// TODO(b/141655126): Getting the OEM Cert no longer loads the private key.
// Call OEMCrypto_GetOEMPublicCertificate before OEMCrypto_LoadDRMPrivateKey
// so it caches the OEMCrypto Public Key and then throw away result
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
size_t buf_size = temp_buffer.size();
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
OEMCryptoResult sts = WithOecSessionLock(
"LoadCertificatePrivateKey() calling OEMCrypto_GetOEMPublicCertificate",
[&] {
return OEMCrypto_GetOEMPublicCertificate(buf, &buf_size,
requested_security_level_);
});
metrics_->oemcrypto_get_oem_public_certificate_.Increment(sts);
const OEMCrypto_PrivateKeyType key_type =
(private_key.type() == CryptoWrappedKey::kEcc)
? OEMCrypto_ECC_Private_Key
@@ -1407,6 +1393,7 @@ CdmResponseType CryptoSession::LoadCertificatePrivateKey(
LOGV("Loading device DRM key: id = %u", oec_session_id_);
// TODO(b/140813486): determine if cert is RSA or ECC.
OEMCryptoResult sts;
WithOecSessionLock(
"LoadCertificatePrivateKey() calling OEMCrypto_LoadDRMPrivateKey()", [&] {
M_TIME(sts = OEMCrypto_LoadDRMPrivateKey(

View File

@@ -77,6 +77,12 @@ message LicenseIdentification {
// which is from the LicenseRequest.request_time when set, or set by the
// server to be the time that the original license was processed.
optional int64 original_start_time_seconds = 9;
// Set by the SDK representing the renewal recovery duration from the initial
// license.
optional int64 original_renewal_recovery_duration_seconds = 10;
// Set by the SDK representing the renewal delay seconds from the original
// license.
optional int64 original_renewal_delay_seconds = 11;
}
// This message is used to indicate the license cateogry spec for a license as
@@ -163,8 +169,10 @@ message License {
// specified URL.
optional string renewal_server_url = 8;
// How many seconds after license_start_time, before renewal is first
// attempted.
// How many seconds after |license_start_time| before renewal is first
// attempted. If |renew_with_usage| is true in a new license, then this is
// the optional number of seconds after first playback, before renewal is
// first attempted.
optional int64 renewal_delay_seconds = 9 [default = 0];
// Specifies the delay in seconds between subsequent license
@@ -172,7 +180,8 @@ message License {
optional int64 renewal_retry_interval_seconds = 10 [default = 0];
// Indicates that the license shall be sent for renewal when usage is
// started.
// started, i.e. on first playback. This should only be used for a new
// license. The client shall ignore this if set in a renewal.
optional bool renew_with_usage = 11 [default = false];
// Indicates to client that license renewal and release requests ought to
@@ -181,10 +190,11 @@ message License {
// Duration of grace period before playback_duration_seconds (short window)
// goes into effect. Optional.
// Deprecated in V16.
optional int64 play_start_grace_period_seconds = 13 [default = 0];
// Enables "soft enforcement" of playback_duration_seconds, letting the user
// finish playback even if short window expires. Optional.
// finish playback even if playback window expires. Optional.
optional bool soft_enforce_playback_duration = 14 [default = false];
// Enables "soft enforcement" of rental_duration_seconds. Initial playback
@@ -277,6 +287,7 @@ message License {
// allow use of the key anyway.
CURRENT_SRM = 1;
}
optional HdcpSrmRule hdcp_srm_rule = 3 [default = HDCP_SRM_RULE_NONE];
// Optional requirement to indicate analog output is not allowed.
optional bool disable_analog_output = 4 [default = false];
@@ -365,7 +376,7 @@ message License {
// LicenseRequest.request_time. If this time is not set in the request,
// the local time at the license service is used in this field.
optional int64 license_start_time = 4;
// TODO(b/65054419): Deprecate remote_attestation_verified in favor of
// Deprecate remote_attestation_verified in favor of
// platform_verification_status, below.
optional bool remote_attestation_verified = 5 [default = false];
// Client token generated by the content provider. Optional.
@@ -391,6 +402,9 @@ message License {
// a license. It could be used as a part of initial license issuance or shown
// as a part of license in license response.
optional LicenseCategorySpec license_category_spec = 12;
// Optional: The provider key id indicates which provider key was used
// during provider key encryption.
optional uint32 provider_key_id = 13;
}
enum ProtocolVersion {

View File

@@ -170,11 +170,10 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
return false;
}
if (!IsValidCdmSecurityLevelForUsageInfo(security_level)) {
LOGE("Invalid security level provided: security_level = %d",
static_cast<int>(security_level));
LOGE("Invalid security level provided: security_level = %s",
CdmSecurityLevelToString(security_level));
return false;
}
security_level_ = security_level;
requested_security_level_ = CdmSecurityLevelToRequestedLevel(security_level);

View File

@@ -0,0 +1,61 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#ifndef WVOEC_UTIL_CMAC_H_
#define WVOEC_UTIL_CMAC_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include <openssl/cmac.h>
namespace wvoec {
namespace util {
class Cmac {
public:
// Creates an AES-128-CMAC or an AES-256-CMAC depending on |key_size|.
// Returns an empty pointer if the key size is not valid.
static std::unique_ptr<Cmac> Create(const uint8_t* key, size_t key_size);
static std::unique_ptr<Cmac> Create(const std::vector<uint8_t>& key);
// Updates the CMAC with more data. This allows for streaming or
// scatter-gather based MAC generation.
// Returns true if the data was updated successfully and false
// if any unexpected errors occur.
bool Update(const uint8_t* data, size_t data_length);
bool Update(const std::vector<uint8_t>& data);
bool Update(uint8_t datum);
// Generates the final MAC and stores it in the |mac| output
// parameter.
// After finalizing, one must reset the Cmac instance before it
// can digest additional information.
bool Finalize(std::vector<uint8_t>* mac);
// Similar to Finalize() except that the output is appended to
// the end of the provided |mac| buffer.
bool FinalizeAppend(std::vector<uint8_t>* mac);
// Clears the underlying CMAC without clearing the key. Resetting
// it to its post-initialization state.
void Reset();
~Cmac();
private:
Cmac() {}
// Assumes |key_size| is a valid AES-128 or AES-256 key.
bool Init(const uint8_t* key, size_t key_size);
CMAC_CTX* ctx_ = nullptr;
bool ready_ = false;
};
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_CMAC_H_

View File

@@ -0,0 +1,85 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#ifndef WVOEC_UTIL_DRM_KEY_H_
#define WVOEC_UTIL_DRM_KEY_H_
#include <memory>
#include <string>
#include <vector>
#include "OEMCryptoCENCCommon.h"
#include "oemcrypto_ecc_key.h"
#include "oemcrypto_rsa_key.h"
namespace wvoec {
namespace util {
// DRM private key performs all of the operations required by an
// OEMCrypto session's RSA/ECC private key.
class DrmPrivateKey {
public:
// Create an RSA-based DRM key.
static std::unique_ptr<DrmPrivateKey> Create(
std::shared_ptr<RsaPrivateKey>&& rsa_key);
static std::unique_ptr<DrmPrivateKey> Create(
std::unique_ptr<RsaPrivateKey>&& rsa_key);
// Create an ECC-based DRM key.
static std::unique_ptr<DrmPrivateKey> Create(
std::shared_ptr<EccPrivateKey>&& ecc_key);
static std::unique_ptr<DrmPrivateKey> Create(
std::unique_ptr<EccPrivateKey>&& ecc_key);
bool IsRsaKey() const { return static_cast<bool>(rsa_key_); }
bool IsEccKey() const { return static_cast<bool>(ecc_key_); }
// Generates a session key from the key source.
// For RSA keys, |key_source| is an encrypted session key.
// For ECC keys, |key_source| is a ephemeral public key to be
// used in ECDH.
OEMCryptoResult GetSessionKey(const uint8_t* key_source,
size_t key_source_size,
std::vector<uint8_t>* session_key) const;
std::vector<uint8_t> GetSessionKey(
const std::vector<uint8_t>& key_source) const;
// Generates a encryption key from the key source.
// For RSA keys, |key_source| is an encrypted encryption key.
// For ECC keys, this method is not supported.
std::vector<uint8_t> GetEncryptionKey(
const std::vector<uint8_t>& key_source) const;
// Generates a signature for the provided message.
// For RSA keys, the signature is RSASSA-PSS.
// For ECC keys, the signature is ECDSA.
OEMCryptoResult GenerateSignature(const uint8_t* message,
size_t message_length, uint8_t* signature,
size_t* signature_length) const;
std::vector<uint8_t> GenerateSignature(
const std::vector<uint8_t>& message) const;
size_t SignatureSize() const;
// Generates a signature for the provided message.
// For RSA keys, the signature is RSASSA-PKCS1.
// For ECC keys, this is not supported.
OEMCryptoResult GenerateRsaSignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length) const;
std::vector<uint8_t> GenerateRsaSignature(
const std::vector<uint8_t>& message) const;
~DrmPrivateKey() {}
private:
DrmPrivateKey() {}
// Only one will be set.
std::shared_ptr<EccPrivateKey> ecc_key_;
std::shared_ptr<RsaPrivateKey> rsa_key_;
};
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_DRM_KEY_H_

View File

@@ -0,0 +1,281 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#ifndef WVOEC_UTIL_ECC_KEY_H_
#define WVOEC_UTIL_ECC_KEY_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include <openssl/ec.h>
#include "OEMCryptoCENCCommon.h"
namespace wvoec {
namespace util {
enum EccCurve {
kEccCurveUnknown = 0,
kEccSecp256r1 = 256,
kEccSecp384r1 = 384,
kEccSecp521r1 = 521
};
// Returns the string representation of the provided curve.
// Intended for logging purposes.
std::string EccCurveToString(EccCurve curve);
class EccPrivateKey;
class EccPublicKey {
public:
// 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.
// The provided |buffer| must contain a valid ASN.1 DER encoded
// SubjectPublicKey. Only supported curves by this API are those
// enumerated by EccCurve.
//
// buffer: SubjectPublicKeyInfo = {
// algorithm: AlgorithmIdentifier = {
// algorithm: OID = id-ecPublicKey,
// parameters: ECParameters = {
// namedCurve: OID = secp256r1 | secp384r1 | secp521r1
// }
// },
// subjectPublicKey: BIT STRING = ... -- SEC1 encoded ECPoint
// }
//
// Failure will occur if the provided |buffer| does not contain a
// valid SubjectPublicKey, or if the specified curve is not
// supported.
static std::unique_ptr<EccPublicKey> Load(const uint8_t* buffer,
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 a serialized ECC private key, but only converting the public key.
static std::unique_ptr<EccPublicKey> LoadPrivateKeyInfo(const uint8_t* buffer,
size_t length);
static std::unique_ptr<EccPublicKey> LoadPrivateKeyInfo(
const std::string& buffer);
static std::unique_ptr<EccPublicKey> LoadPrivateKeyInfo(
const std::vector<uint8_t>& buffer);
EccCurve curve() const { return curve_; }
const EC_KEY* GetEcKey() const { return key_; }
// Checks if the provided |private_key| is the EC private key of this
// public key.
bool IsMatchingPrivateKey(const EccPrivateKey& private_key) const;
// Serializes the public key into an ASN.1 DER encoded SubjectPublicKey
// representation.
// On success, |buffer_size| is populated with the number of bytes
// written to |buffer|, and OEMCrypto_SUCCESS is returned.
// If the provided |buffer_size| is too small, ERROR_SHORT_BUFFER
// is returned and |buffer_size| is set to the required buffer size.
OEMCryptoResult Serialize(uint8_t* buffer, size_t* buffer_size) const;
// Same as above, except directly returns the serialized key.
// Returns an empty vector on error.
std::vector<uint8_t> Serialize() const;
// Verifies the |signature| matches the provided |message| by the
// private equivalent of this public key.
// The |signature| should be a valid ASN.1 DER encoded
// ECDSA-Sig-Value.
// This implementation uses ECDSA with the following digest
// algorithms for the supported curve types.
// - SHA-256 / secp256r1
// - SHA-384 / secp384r1 (optional support)
// - SHA-512 / secp521r1 (optional support)
// Returns:
// OEMCrypto_SUCCESS if signature is valid
// OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature is invalid
// Any other result indicates an unexpected error
OEMCryptoResult VerifySignature(const uint8_t* message, size_t message_length,
const uint8_t* signature,
size_t signature_length) const;
OEMCryptoResult VerifySignature(const std::string& message,
const std::string& signature) const;
OEMCryptoResult VerifySignature(const std::vector<uint8_t>& message,
const std::vector<uint8_t>& signature) const;
~EccPublicKey();
EccPublicKey(const EccPublicKey&) = delete;
EccPublicKey(EccPublicKey&&) = delete;
const EccPublicKey& operator=(const EccPublicKey&) = delete;
EccPublicKey& operator=(EccPublicKey&&) = delete;
private:
EccPublicKey() {}
// Initializes the public key object using the provided |buffer|.
// In case of any failure, false is return and the key should be
// discarded.
bool InitFromSubjectPublicKeyInfo(const uint8_t* buffer, size_t length);
bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
// Initializes the public key object from a private.
bool InitFromPrivateKey(const EccPrivateKey& private_key);
// OpenSSL/BoringSSL implementation of an ECC key.
// As a public key, this will only have key point initialized.
EC_KEY* key_ = nullptr;
EccCurve curve_ = kEccCurveUnknown;
}; // class EccPublicKey
class EccPrivateKey {
public:
// Creates a new, pseudorandom ECC private key belonging to the
// curve specified.
static std::unique_ptr<EccPrivateKey> New(EccCurve curve);
// Loads a serialized ECC private key.
// The provided |buffer| must contain a valid ASN.1 DER encoded
// PrivateKeyInfo containing a valid ECC key description. Only
// supported curves by this API are those enumerated by EccCurve.
//
// PrivateKeyInfo := {
// version: INTEGER = v1(0) | v2(1),
// privateKeyAlgorithm: AlgorithmIdentifier := {
// algorithm: OID = id-ecPublicKey,
// parameters: ECParameters = {
// namedCurve: OID = secp256r1 | secp384r1 | secp521r1
// }
// },
// privateKey: OCTET STRING = ..., -- BER encoding of ECPrivateKey
// }
//
// ECPrivateKey := {
// version: INTEGER = ecPrivateKeyVer1(1),
// privateKey: OCTET STRING = ..., -- I2OSP of private key point
// -- |parameters| are obtained from PrivateKeyInfo
// publicKey: BIT STRING OPTIONAL = ... -- SEC1 encoded ECPoint
// }
// Note: If the public key is not included, then it is computed from
// the private key.
//
// References:
// RFC 5208 - Description of PrivateKeyInfo
// RFC 5480 - Curve OIDs
// RFC 5915 - Description of ECPrivateKey in PrivateKeyInfo
//
// Failure will occur if the provided |buffer| does not contain a
// valid PrivateKeyInfo, key is not an ECC key, the specified
// curve is not supported, or the key is not valid.
static std::unique_ptr<EccPrivateKey> Load(const uint8_t* buffer,
size_t length);
static std::unique_ptr<EccPrivateKey> Load(const std::string& buffer);
static std::unique_ptr<EccPrivateKey> Load(
const std::vector<uint8_t>& buffer);
// Creates a new ECC public key of this private key.
// Equivalent to calling EccPublicKey::New with this private
// key.
std::unique_ptr<EccPublicKey> MakePublicKey() const;
EccCurve curve() const { return curve_; }
const EC_KEY* GetEcKey() const { return key_; }
// Checks if the provided |public_key| is the EC public key of this
// private key.
bool IsMatchingPublicKey(const EccPublicKey& public_key) const;
// Serializes the private key into an ASN.1 DER encoded PrivateKeyInfo
// representation.
// On success, |buffer_size| is populated with the number of bytes
// written to |buffer|, and SUCCESS is returned.
// If the provided |buffer_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |buffer_size| is
// set to the required buffer size.
OEMCryptoResult Serialize(uint8_t* buffer, size_t* buffer_size) const;
// Same as above, except directly returns the serialized key.
// Returns an empty vector on error.
std::vector<uint8_t> Serialize() const;
// Serializes the public component of the private key into an ASN.1
// DER encoded SubjectPublicKey representation.
// On success, |buffer_size| is populated with the number of bytes
// written to |buffer|, and SUCCESS is returned.
// If the provided |buffer_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |buffer_size| is
// set to the required buffer size.
OEMCryptoResult SerializeAsPublicKey(uint8_t* buffer,
size_t* buffer_size) const;
// Same as above, except directly returns the serialized key.
// Returns an empty vector on error.
std::vector<uint8_t> SerializeAsPublicKey() 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
// algorithms for the supported curve types.
// - SHA-256 / secp256r1
// - SHA-384 / secp384r1 (optional support)
// - SHA-512 / secp521r1 (optional support)
// On success, |signature_length| is populated with the number of
// bytes written to |signature|, and SUCCESS is returned.
// If the provided |signature_length| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |signature_length|
// is set to the required signature size.
OEMCryptoResult GenerateSignature(const uint8_t* message,
size_t message_length, uint8_t* signature,
size_t* signature_length) const;
// Same as above, except directly returns the serialized signature.
// Returns an empty vector on error.
std::vector<uint8_t> GenerateSignature(
const std::vector<uint8_t>& message) const;
std::vector<uint8_t> GenerateSignature(const std::string& message) const;
// Returns an upper bound for the signature size. May be larger than
// the actual signature generated by GenerateSignature().
size_t SignatureSize() 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
// bytes written to |session_key|, and OEMCrypto_SUCCESS is returned.
// If the provided |session_key_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |session_key_size|
// is set to the required buffer size.
OEMCryptoResult DeriveSessionKey(const EccPublicKey& public_key,
uint8_t* session_key,
size_t* session_key_size) const;
// Same as above, except directly returns the derived key.
std::vector<uint8_t> DeriveSessionKey(const EccPublicKey& public_key) const;
// Returns the byte length of the symmetric key that would be derived
// by DeriveSymmetricKey().
size_t SessionKeyLength() const;
~EccPrivateKey();
EccPrivateKey(const EccPrivateKey&) = delete;
EccPrivateKey(EccPrivateKey&&) = delete;
const EccPrivateKey& operator=(const EccPrivateKey&) = delete;
EccPrivateKey& operator=(EccPrivateKey&&) = delete;
private:
EccPrivateKey() {}
// Initializes the public key object using the provided |buffer|.
// In case of any failure, false is return and the key should be
// discarded.
bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
// Generates a new key based on the provided curve.
bool InitFromCurve(EccCurve curve);
// OpenSSL/BoringSSL implementation of an ECC key.
// The public point of the key will always be present.
EC_KEY* key_ = nullptr;
EccCurve curve_ = kEccCurveUnknown;
}; // class EccPrivateKey
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_ECC_KEY_H_

View File

@@ -0,0 +1,61 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#ifndef WVOEC_UTIL_KEY_DERIVER_H_
#define WVOEC_UTIL_KEY_DERIVER_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "cmac.h"
namespace wvoec {
namespace util {
class KeyDeriver {
public:
// Create a new key deriver using either the session key or the device
// key.
// Returns an empty pointer if the key size is not valid.
static std::unique_ptr<KeyDeriver> Create(const uint8_t* key,
size_t key_size);
static std::unique_ptr<KeyDeriver> Create(const std::vector<uint8_t>& key);
// Derive the mac_key[server] from the provided |mac_key_context|.
bool DeriveServerMacKey(const uint8_t* mac_key_context,
size_t mac_key_context_size,
std::vector<uint8_t>* mac_key_server);
bool DeriveServerMacKey(const std::vector<uint8_t>& mac_key_context,
std::vector<uint8_t>* mac_key_server);
// Derive the mac_key[client] from the provided |mac_key_context|.
bool DeriveClientMacKey(const uint8_t* mac_key_context,
size_t mac_key_context_size,
std::vector<uint8_t>* mac_key_client);
bool DeriveClientMacKey(const std::vector<uint8_t>& mac_key_context,
std::vector<uint8_t>* mac_key_client);
// Derive the enc_key from the provided |enc_key_context|.
bool DeriveEncryptionKey(const uint8_t* enc_key_context,
size_t enc_key_context_size,
std::vector<uint8_t>* enc_key);
bool DeriveEncryptionKey(const std::vector<uint8_t>& enc_key_context,
std::vector<uint8_t>* enc_key);
~KeyDeriver() {}
private:
KeyDeriver() {}
bool Init(const uint8_t* key, size_t key_size);
std::unique_ptr<Cmac> cmac_;
};
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_KEY_DERIVER_H_

View File

@@ -0,0 +1,104 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#ifndef WVOEC_UTIL_OEM_CERT_H_
#define WVOEC_UTIL_OEM_CERT_H_
#include <memory>
#include <vector>
#include "OEMCryptoCENCCommon.h"
namespace wvoec {
namespace util {
class OemPublicCertificate;
// An OEM Certificate is a factory provisioned root of trust
// certificate which consists of a public certificate and its
// matching private key.
// The public certificate must be an ASN.1 DER encoded PKCS #7
// ContentInfo of type signedData (RFC2315). The device's X.509
// certificate must be the first certificate in the chain of
// SignedContent |certificates|.
// The certificates are X.509 Certificate as defined in RFC 5280
// signed by the device manufacturers certificate which is signed
// by Google.
// The OEM Public Cert should only contain the device's certificate
// and the OEM's intermediate certificate.
// The private key storage format is at the discretion of the OEM;
// the reference implementation uses PKCS8 PrivateKeyInfo.
class OemCertificate {
public:
enum KeyType {
kNone = 0,
// Private key is an ASN.1 DER encoded PrivateKeyInfo specifying
// an RSA encryption key.
kRsa = 1
};
// Creates a new OEM Certificate and performs basic validation
// to ensure that the private key and public cert are well-formed.
// The |public_cert| provided is parsed as an X.509 Certificate
// and the public key is verified against the private key.
// The |private_key| is parsed depending on the key type.
// If any error occurs or if the provided data is malformed, an
// empty pointer is returned.
static std::unique_ptr<OemCertificate> Create(const uint8_t* private_key,
size_t private_key_size,
const uint8_t* public_cert,
size_t public_cert_size);
static std::unique_ptr<OemCertificate> Create(
const std::vector<uint8_t>& private_key,
const std::vector<uint8_t>& public_cert);
// Returns the key type of the OEM Public key and private key.
// As of OEMCrypto v16, the only supported key type is RSA.
KeyType key_type() const;
// Returns the private key data. Intended to be used for calls
// to OEMCrypto_LoadOEMPrivateKey().
const std::vector<uint8_t>& GetPrivateKey() const { return private_key_; }
// Returns a copy of the ASN.1 DER encoded PKCS #7 certificate chain.
// If |*public_cert_length| is large enough, the complete
// certificate is copied to the buffer specified by |public_cert|,
// |*public_cert_length| is adjusted to the actual size of the
// certificate data, and SUCCESS is returned.
// If |*public_cert_length| is not large enough, then it is
// set to size of the certificate and ERROR_SHORT_BUFFER is
// returned.
OEMCryptoResult GetPublicCertificate(uint8_t* public_cert,
size_t* public_cert_length) const;
// Returns the certificate directly. Intended to be used for
// testing.
const std::vector<uint8_t>& GetPublicCertificate() const;
// Verifies that the RSA key included in the OEM Cert is valid.
// The existence of an OemCertificate already ensures that the
// OEM Public Certificate and private key data are well-formed.
// This takes the check another step further and ensures that
// the private key matches the public key in the public cert
// (ie, same modulos and public exponent).
OEMCryptoResult IsCertificateValid() const;
~OemCertificate();
OemCertificate(const OemCertificate&) = delete;
OemCertificate(OemCertificate&&) = delete;
const OemCertificate& operator=(const OemCertificate&) = delete;
OemCertificate& operator=(OemCertificate&&) = delete;
private:
OemCertificate();
// Serialized private key matching the OEM certificate.
std::vector<uint8_t> private_key_;
// Serialized OEM Certificate.
std::unique_ptr<OemPublicCertificate> public_cert_;
}; // class OemCertificate
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_OEM_CERT_H_

View File

@@ -0,0 +1,376 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#ifndef WVOEC_UTIL_RSA_KEY_H_
#define WVOEC_UTIL_RSA_KEY_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include <openssl/rsa.h>
#include "OEMCryptoCENC.h"
namespace wvoec {
namespace util {
enum RsaFieldSize {
kRsaFieldUnknown = 0,
kRsa2048Bit = 2048,
kRsa3072Bit = 3084
};
// Identifies the RSA signature algorithm to be used when signing
// messages or verifying message signatures.
// The two standard signing algorithms specified by PKCS1 RSA V2.1
// are RSASSA-PKCS1 and RSASSA-PSS. Each require agreement on a
// set of options. For OEMCrypto, only one set of options are agreed
// upon for each RSA signature scheme. CAST receivers specify a
// special implementation of PKCS1 where the message is already
// digested and encoded when provided.
enum RsaSignatureAlgorithm {
// RSASSA-PSS with default options:
// Hash algorithm: SHA-1
// MGF: MGF1 with SHA-1
// Salt length: 20 bytes
// Trailer field: 0xbc
kRsaPssDefault = 0,
// RSASSA-PKCS1 for CAST receivers.
// Assumes message is already digested & encoded. Max message length
// is 83 bytes.
kRsaPkcs1Cast = 1
};
// Returns the string representation of the provided RSA field size.
// Intended for logging purposes.
std::string RsaFieldSizeToString(RsaFieldSize field_size);
// Compares two OpenSSL/BoringSSL RSA keys to see if their public RSA
// components are matching.
// This function assumes both keys are valid.
// Returns true if they are matching, false otherwise.
bool RsaKeysAreMatchingPair(const RSA* public_key, const RSA* private_key);
class RsaPrivateKey;
class RsaPublicKey {
public:
// Creates a new public key equivalent of the provided private key.
static std::unique_ptr<RsaPublicKey> New(const RsaPrivateKey& private_key);
// Creates an RSA public key from a native OpenSSL/BoringSSL RSA key handle.
// Ownership of the handle is NOT transferred.
static std::unique_ptr<RsaPublicKey> FromSslHandle(
const RSA* rsa_handle, uint32_t allowed_schemes = kSign_RSASSA_PSS);
// Loads a serialized RSA public key.
// The provided |buffer| must contain a valid ASN.1 DER encoded
// SubjectPublicKey. This API will reject any RSA key that is not
// approximately to 2048bits or 3072bits.
//
// buffer: SubjectPublicKeyInfo = {
// algorithm: AlgorithmIdentifier = {
// algorithm: OID = rsaEncryption,
// parameters: NULL = null
// },
// subjectPublicKey: BIT STRING = ... -- ASN.1 DER encoded RSAPublicKey
// }
//
// Failure will occur if the provided |buffer| does not contain a
// valid SubjectPublicKey, or if the specified curve is not
// supported.
static std::unique_ptr<RsaPublicKey> Load(const uint8_t* buffer,
size_t length);
static std::unique_ptr<RsaPublicKey> Load(const std::string& buffer);
static std::unique_ptr<RsaPublicKey> Load(const std::vector<uint8_t>& buffer);
// Loads a serialized RSA private key, but only converting the public key.
static std::unique_ptr<RsaPublicKey> LoadPrivateKeyInfo(const uint8_t* buffer,
size_t length);
static std::unique_ptr<RsaPublicKey> LoadPrivateKeyInfo(
const std::string& buffer);
static std::unique_ptr<RsaPublicKey> LoadPrivateKeyInfo(
const std::vector<uint8_t>& buffer);
RsaFieldSize field_size() const { return field_size_; }
uint32_t allowed_schemes() const { return allowed_schemes_; }
const RSA* GetRsaKey() const { return key_; }
// Checks if the provided |private_key| is the RSA private key of this
// public key.
bool IsMatchingPrivateKey(const RsaPrivateKey& private_key) const;
// Serializes the public key into an ASN.1 DER encoded SubjectPublicKey
// representation.
// On success, |buffer_size| is populated with the number of bytes
// written to |buffer|, and OEMCrypto_SUCCESS is returned.
// If the provided |buffer_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |buffer_size| is set
// to the required buffer size.
OEMCryptoResult Serialize(uint8_t* buffer, size_t* buffer_size) const;
// Same as above, except directly returns the serialized key.
// Returns an empty vector on error.
std::vector<uint8_t> Serialize() const;
// Verifies the |signature| matches the provided |message| by the
// private equivalent of this public key.
// The signature algorithm can be specified via the |algorithm| field.
// See RsaSignatureAlgorithm for details on each algorithm.
//
// Returns:
// OEMCrypto_SUCCESS if signature is valid
// OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature is invalid
// OEMCrypto_ERROR_UNKNOWN_FAILURE if any error occurs
OEMCryptoResult VerifySignature(
const uint8_t* message, size_t message_length, const uint8_t* signature,
size_t signature_length,
RsaSignatureAlgorithm algorithm = kRsaPssDefault) const;
OEMCryptoResult VerifySignature(
const std::string& message, const std::string& signature,
RsaSignatureAlgorithm algorithm = kRsaPssDefault) const;
OEMCryptoResult VerifySignature(
const std::vector<uint8_t>& message,
const std::vector<uint8_t>& signature,
RsaSignatureAlgorithm algorithm = kRsaPssDefault) const;
// Encrypts the OEMCrypto session key used for deriving other keys.
// On success, |enc_session_key_size| is populated with the number
// of bytes written to |enc_session_key|, and OEMCrypto_SUCCESS is
// returned. If the provided |enc_session_key_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and
// |enc_session_key_size| is set to the required buffer size.
OEMCryptoResult EncryptSessionKey(const uint8_t* session_key,
size_t session_key_size,
uint8_t* enc_session_key,
size_t* enc_session_key_size) const;
// Same as above, except directly returns the encrypted key.
std::vector<uint8_t> EncryptSessionKey(
const std::vector<uint8_t>& session_key) const;
std::vector<uint8_t> EncryptSessionKey(const std::string& session_key) const;
// Encrypts the OEMCrypto encryption key used for encrypting the
// DRM private key.
// On success, |enc_encryption_key_size| is populated with the
// number of bytes written to |enc_encryption_key|, and
// OEMCrypto_SUCCESS is returned.
// If the provided |enc_encryption_key_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and
// |enc_encryption_key_size| is set to the required buffer size.
OEMCryptoResult EncryptEncryptionKey(const uint8_t* encryption_key,
size_t encryption_key_size,
uint8_t* enc_encryption_key,
size_t* enc_encryption_key_size) const;
// Same as above, except directly returns the encrypted key.
std::vector<uint8_t> EncryptEncryptionKey(
const std::vector<uint8_t>& encryption_key) const;
std::vector<uint8_t> EncryptEncryptionKey(
const std::string& encryption_key) const;
~RsaPublicKey();
RsaPublicKey(const RsaPublicKey&) = delete;
RsaPublicKey(RsaPublicKey&&) = delete;
const RsaPublicKey& operator=(const RsaPublicKey&) = delete;
RsaPublicKey& operator=(RsaPublicKey&&) = delete;
private:
RsaPublicKey() {}
// Initializes the public key object using the provided |buffer|.
// In case of any failure, false is return and the key should be
// discarded.
bool InitFromSubjectPublicKeyInfo(const uint8_t* buffer, size_t length);
bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
// Initializes the public key object from a private.
bool InitFromPrivateKey(const RsaPrivateKey& private_key);
// Initializes the public key object from an existing
// OpenSSL/BoringSSL RSA key handle. The RSA key must be
// initialized and |allowed_schemes| must be a valid value.
bool InitFromSslHandle(const RSA* rsa_handle, uint32_t allowed_schemes);
// Signature specialization functions.
OEMCryptoResult VerifySignaturePss(const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length) const;
OEMCryptoResult VerifySignaturePkcs1Cast(const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length) const;
// RSAES-OAEP encrypt.
OEMCryptoResult EncryptOaep(const uint8_t* message, size_t message_size,
uint8_t* enc_message,
size_t* enc_message_length) const;
// OpenSSL/BoringSSL implementation of an RSA key.
// Will only include components of an RSA public key.
RSA* key_ = nullptr;
uint32_t allowed_schemes_ = 0;
RsaFieldSize field_size_ = kRsaFieldUnknown;
}; // class RsaPublicKey
class RsaPrivateKey {
public:
// Creates a new, pseudorandom RSA private key.
static std::unique_ptr<RsaPrivateKey> New(RsaFieldSize field_size);
// Loads a serialized RSA private key.
// The provided |buffer| must contain a valid ASN.1 DER encoded
// PrivateKeyInfo (RFC 5208).
//
// buffer: PrivateKeyInfo = {
// version: INTEGER = v1(0),
// privateKeyAlgorithm: OID = rsaEncryption,
// privateKey: OCTET STRING = ...,
// -- BER encoding of RSAPrivateKey (RFC 3447)
// attributes: Attributes = ... -- Optional, not used by OEMCrypto
// }
// Note: If the public key is not included, then it is computed from
// the private.
//
// Failure will occur if the provided |buffer| does not contain a
// valid RSAPrivateKey, or if the specified curve is not supported.
static std::unique_ptr<RsaPrivateKey> Load(const uint8_t* buffer,
size_t length);
static std::unique_ptr<RsaPrivateKey> Load(const std::string& buffer);
static std::unique_ptr<RsaPrivateKey> Load(
const std::vector<uint8_t>& buffer);
// Creates a new RSA public key of this private key.
// Equivalent to calling RsaPublicKey::New with this private
// key.
std::unique_ptr<RsaPublicKey> MakePublicKey() const;
RsaFieldSize field_size() const { return field_size_; }
uint32_t allowed_schemes() const { return allowed_schemes_; }
const RSA* GetRsaKey() const { return key_; }
// Checks if the provided |public_key| is the RSA public key of this
// private key.
bool IsMatchingPublicKey(const RsaPublicKey& public_key) const;
// Serializes the private key into an ASN.1 DER encoded X
// representation.
// On success, |buffer_size| is populated with the number of bytes
// written to |buffer|, and OEMCrypto_SUCCESS is returned.
// If the provided |buffer_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |buffer_size| is
// set to the required buffer size.
OEMCryptoResult Serialize(uint8_t* buffer, size_t* buffer_size) const;
// Same as above, except directly returns the serialized key.
// Returns an empty vector on error.
std::vector<uint8_t> Serialize() const;
// Signs the provided |message| using the RSA signing algorithm
// specified by |algorithm|. See RsaSignatureAlgorithm for
// details on each algorithm.
//
// On success, |signature_length| is populated with the number of
// bytes written to |signature|, and OEMCrypto_SUCCESS is returned.
// If the provided |signature_length| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |signature_length|
// is set to the required signature size.
OEMCryptoResult GenerateSignature(const uint8_t* message,
size_t message_length,
RsaSignatureAlgorithm algorithm,
uint8_t* signature,
size_t* signature_length) const;
// Same as above, except directly returns the serialized signature.
// Returns an empty vector on error.
std::vector<uint8_t> GenerateSignature(
const std::vector<uint8_t>& message,
RsaSignatureAlgorithm algorithm = kRsaPssDefault) const;
std::vector<uint8_t> GenerateSignature(
const std::string& message,
RsaSignatureAlgorithm algorithm = kRsaPssDefault) const;
// Returns an upper bound for the signature size. May be larger than
// the actual signature generated by GenerateSignature().
size_t SignatureSize() const;
// Decrypts the OEMCrypto session key used for deriving other keys.
// On success, |session_key_size| is populated with the number of
// bytes written to |session_key|, and OEMCrypto_SUCCESS is returned.
// If the provided |session_key_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |session_key_size|
// is set to the required buffer size.
OEMCryptoResult DecryptSessionKey(const uint8_t* enc_session_key,
size_t enc_session_key_size,
uint8_t* session_key,
size_t* session_key_size) const;
// Same as above, except directly returns the decrypted key.
std::vector<uint8_t> DecryptSessionKey(
const std::vector<uint8_t>& enc_session_key) const;
std::vector<uint8_t> DecryptSessionKey(
const std::string& enc_session_key) const;
// Returns the byte length of the symmetric key that would be derived
// by DecryptSessionKey().
size_t SessionKeyLength() const;
// Decrypts the OEMCrypto encryption key used for decrypting DRM
// private key.
// On success, |encryption_key_size| is populated with the number of
// bytes written to |encryption_key|, and OEMCrypto_SUCCESS is
// returned.
// If the provided |encryption_key_size| is too small,
// OEMCrypto_ERROR_SHORT_BUFFER is returned and |encryption_key_size|
// is set to the required buffer size.
OEMCryptoResult DecryptEncryptionKey(const uint8_t* enc_encryption_key,
size_t enc_encryption_key_size,
uint8_t* encryption_key,
size_t* encryption_key_size) const;
// Same as above, except directly returns the decrypted key.
std::vector<uint8_t> DecryptEncryptionKey(
const std::vector<uint8_t>& enc_encryption_key) const;
std::vector<uint8_t> DecryptEncryptionKey(
const std::string& enc_encryption_key) const;
~RsaPrivateKey();
RsaPrivateKey(const RsaPrivateKey&) = delete;
RsaPrivateKey(RsaPrivateKey&&) = delete;
const RsaPrivateKey& operator=(const RsaPrivateKey&) = delete;
RsaPrivateKey& operator=(RsaPrivateKey&&) = delete;
private:
RsaPrivateKey() {}
// Initializes the public key object using the provided |buffer|.
// In case of any failure, false is return and the key should be
// discarded.
bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
// Generates a new key based on the provided field size.
bool InitFromFieldSize(RsaFieldSize field_size);
// Signature specialization functions.
OEMCryptoResult GenerateSignaturePss(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length) const;
OEMCryptoResult GenerateSignaturePkcs1Cast(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length) const;
// RSAES-OAEP decrypt.
OEMCryptoResult DecryptOaep(const uint8_t* enc_message,
size_t enc_message_size, uint8_t* message,
size_t expected_message_length) const;
// OpenSSL/BoringSSL implementation of an RSA key.
// Will include all components of an RSA private key.
RSA* key_ = nullptr;
uint32_t allowed_schemes_ = 0;
// Set true if the deserialized key contained an allowed schemes.
bool explicit_schemes_ = false;
RsaFieldSize field_size_ = kRsaFieldUnknown;
}; // class RsaPrivateKey
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_RSA_KEY_H_

View File

@@ -0,0 +1,70 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#ifndef WVOEC_UTIL_SCOPED_OBJECT_H_
#define WVOEC_UTIL_SCOPED_OBJECT_H_
namespace wvoec {
namespace util {
// A generic wrapper around pointer. This allows for automatic
// memory clean up when the ScopedObject variable goes out of scope.
// This is intended to be used with OpenSSL/BoringSSL structs.
template <typename Type, void Destructor(Type*)>
class ScopedObject {
public:
ScopedObject() : ptr_(nullptr) {}
ScopedObject(Type* ptr) : ptr_(ptr) {}
~ScopedObject() {
if (ptr_) {
Destructor(ptr_);
ptr_ = nullptr;
}
}
// Copy construction and assignment are not allowed.
ScopedObject(const ScopedObject& other) = delete;
ScopedObject& operator=(const ScopedObject& other) = delete;
// Move construction and assignment are allowed.
ScopedObject(ScopedObject&& other) : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
ScopedObject& operator=(ScopedObject&& other) {
if (ptr_) {
Destructor(ptr_);
}
ptr_ = other.ptr_;
other.ptr_ = nullptr;
return *this;
}
explicit operator bool() const { return ptr_ != nullptr; }
Type& operator*() { return *ptr_; }
Type* get() const { return ptr_; }
Type* operator->() const { return ptr_; }
// Releasing the pointer will remove the responsibility of the
// ScopedObject to clean up the pointer.
Type* release() {
Type* temp = ptr_;
ptr_ = nullptr;
return temp;
}
void reset(Type* ptr = nullptr) {
if (ptr_) {
Destructor(ptr_);
}
ptr_ = ptr;
}
private:
Type* ptr_ = nullptr;
};
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_SCOPED_OBJECT_H_

View File

@@ -0,0 +1,22 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine
// License Agreement.
//
// Compute CRC32/MPEG2 Checksum. Needed for verification of WV Keybox.
//
#ifndef WVOEC_UTIL_WVCRC32_H_
#define WVOEC_UTIL_WVCRC32_H_
#include <stdint.h>
namespace wvoec {
namespace util {
uint32_t wvcrc32(const uint8_t* p_begin, size_t i_count);
uint32_t wvcrc32Init();
uint32_t wvcrc32Cont(const uint8_t* p_begin, size_t i_count, uint32_t prev_crc);
// Convert to network byte order
uint32_t wvcrc32n(const uint8_t* p_begin, size_t i_count);
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_WVCRC32_H_

View File

@@ -0,0 +1,173 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#include "cmac.h"
#include <openssl/evp.h>
#include "log.h"
#include "scoped_object.h"
namespace wvoec {
namespace util {
namespace {
using ScopedCmacCtx = ScopedObject<CMAC_CTX, CMAC_CTX_free>;
constexpr size_t kAes128KeySize = 16;
constexpr size_t kAes256KeySize = 32;
constexpr size_t kCmacOutputSize = 16;
// Gets the appropriate AES block cipher for the CMAC algortihm
// based on the key size.
// Ownership of the pointer returned by this function is retained by
// the OpenSSL/BoringSSL framework.
const EVP_CIPHER* KeySizeToCipher(size_t key_size) {
switch (key_size) {
case kAes128KeySize:
return EVP_aes_128_cbc();
case kAes256KeySize:
return EVP_aes_256_cbc();
}
LOGE("Unexpected key size: size = %zu", key_size);
return nullptr;
}
} // namespace
// static
std::unique_ptr<Cmac> Cmac::Create(const uint8_t* key, size_t key_size) {
std::unique_ptr<Cmac> cmac;
if (key == nullptr) {
LOGE("CMAC key is null");
return cmac;
}
if (key_size != kAes128KeySize && key_size != kAes256KeySize) {
LOGE("Invalid CMAC key size: size = %zu", key_size);
return cmac;
}
cmac.reset(new Cmac());
if (!cmac->Init(key, key_size)) {
cmac.reset();
}
return cmac;
}
// static
std::unique_ptr<Cmac> Cmac::Create(const std::vector<uint8_t>& key) {
if (key.empty()) {
LOGE("CMAC key is empty");
return std::unique_ptr<Cmac>();
}
return Create(key.data(), key.size());
}
bool Cmac::Init(const uint8_t* key, size_t key_size) {
const EVP_CIPHER* const cipher = KeySizeToCipher(key_size);
if (cipher == nullptr) {
LOGE("Failed to get block cipher for CMAC");
return false;
}
ScopedCmacCtx ctx(CMAC_CTX_new());
if (!ctx) {
LOGE("Failed allocate CMAC CTX");
return false;
}
if (!CMAC_Init(ctx.get(), key, key_size, cipher, nullptr)) {
LOGE("Failed to initialize CMAC CTX");
return false;
}
ctx_ = ctx.release();
ready_ = true;
return true;
}
bool Cmac::Update(const uint8_t* data, size_t data_length) {
if (data == nullptr) {
LOGE("Data is null");
return false;
}
if (data_length == 0) {
return true;
}
if (!ready_) {
LOGE("CMAC must be reset before updating");
return false;
}
if (!CMAC_Update(ctx_, data, data_length)) {
LOGE("Failed to update CMAC CTX");
ready_ = false;
return false;
}
return true;
}
bool Cmac::Update(const std::vector<uint8_t>& data) {
return Update(data.data(), data.size());
}
bool Cmac::Update(uint8_t datum) { return Update(&datum, 1); }
bool Cmac::Finalize(std::vector<uint8_t>* mac) {
if (mac == nullptr) {
LOGE("Output MAC buffer is null");
return false;
}
mac->clear();
return FinalizeAppend(mac);
}
bool Cmac::FinalizeAppend(std::vector<uint8_t>* mac) {
if (mac == nullptr) {
LOGE("Output MAC buffer is null");
return false;
}
if (!ready_) {
LOGE("CMAC must be reset before finalizing");
return false;
}
const size_t end = mac->size();
size_t mac_size = kCmacOutputSize;
mac->resize(end + mac_size);
if (!CMAC_Final(ctx_, &mac->at(end), &mac_size)) {
LOGE("Failed to finalize CMAC CTX");
mac->resize(end);
ready_ = false;
return false;
}
ready_ = false;
return true;
}
#ifdef OPENSSL_IS_BORINGSSL
// BoringSSL allows for resetting a CMAC context explicitly, whereas
// OpenSSL does so by reinitializing using all nulls/zeros. This
// causes segfaults on systems using BoringSSL.
void Cmac::Reset() {
if (!CMAC_Reset(ctx_)) {
LOGE("Failed to reset CMAC CTX");
ready_ = false;
} else {
ready_ = true;
}
}
#else // OpenSSL is OpenSSL
void Cmac::Reset() {
if (!CMAC_Init(ctx_, nullptr, 0, nullptr, nullptr)) {
LOGE("Failed to reset CMAC CTX");
ready_ = false;
} else {
ready_ = true;
}
}
#endif
Cmac::~Cmac() {
if (ctx_ != nullptr) {
CMAC_CTX_free(ctx_);
ctx_ = nullptr;
}
ready_ = false;
}
} // namespace util
} // namespace wvoec

View File

@@ -0,0 +1,186 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#include "oemcrypto_drm_key.h"
#include <utility>
#include "OEMCryptoCENC.h"
#include "log.h"
namespace wvoec {
namespace util {
// static
std::unique_ptr<DrmPrivateKey> DrmPrivateKey::Create(
std::shared_ptr<RsaPrivateKey>&& rsa_key) {
if (!rsa_key) {
LOGE("No RSA key provided");
return std::unique_ptr<DrmPrivateKey>();
}
std::unique_ptr<DrmPrivateKey> drm_key(new DrmPrivateKey());
drm_key->rsa_key_ = std::move(rsa_key);
return drm_key;
}
// static
std::unique_ptr<DrmPrivateKey> DrmPrivateKey::Create(
std::unique_ptr<RsaPrivateKey>&& rsa_key) {
if (!rsa_key) {
LOGE("No RSA key provided");
return std::unique_ptr<DrmPrivateKey>();
}
std::unique_ptr<DrmPrivateKey> drm_key(new DrmPrivateKey());
drm_key->rsa_key_ = std::move(rsa_key);
return drm_key;
}
// static
std::unique_ptr<DrmPrivateKey> DrmPrivateKey::Create(
std::shared_ptr<EccPrivateKey>&& ecc_key) {
if (!ecc_key) {
LOGE("No ECC key provided");
return std::unique_ptr<DrmPrivateKey>();
}
std::unique_ptr<DrmPrivateKey> drm_key(new DrmPrivateKey());
drm_key->ecc_key_ = std::move(ecc_key);
return drm_key;
}
// static
std::unique_ptr<DrmPrivateKey> DrmPrivateKey::Create(
std::unique_ptr<EccPrivateKey>&& ecc_key) {
if (!ecc_key) {
LOGE("No ECC key provided");
return std::unique_ptr<DrmPrivateKey>();
}
std::unique_ptr<DrmPrivateKey> drm_key(new DrmPrivateKey());
drm_key->ecc_key_ = std::move(ecc_key);
return drm_key;
}
OEMCryptoResult DrmPrivateKey::GetSessionKey(
const uint8_t* key_source, size_t key_source_size,
std::vector<uint8_t>* session_key) const {
if (session_key == nullptr) {
LOGE("Output session key is null");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// RSA -> Decrypt session key.
if (rsa_key_) {
if (!(rsa_key_->allowed_schemes() & kSign_RSASSA_PSS)) {
LOGE("RSA key cannot be used for session key decryption");
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
size_t session_key_size = rsa_key_->SessionKeyLength();
session_key->resize(session_key_size);
const OEMCryptoResult res = rsa_key_->DecryptSessionKey(
key_source, key_source_size, session_key->data(), &session_key_size);
if (res != OEMCrypto_SUCCESS) {
session_key->clear();
return res;
}
session_key->resize(session_key_size);
return OEMCrypto_SUCCESS;
}
// ECC -> ECDH.
// Step 1: Parse |key_source| as ECC key.
std::unique_ptr<EccPublicKey> ephemeral_ecc_key =
EccPublicKey::Load(key_source, key_source_size);
if (!ephemeral_ecc_key) {
LOGE("Failed to load server's ephemeral ECC key");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Step 2: Derive session key.
size_t session_key_size = ecc_key_->SessionKeyLength();
session_key->resize(session_key_size);
const OEMCryptoResult res = ecc_key_->DeriveSessionKey(
*ephemeral_ecc_key, session_key->data(), &session_key_size);
if (res != OEMCrypto_SUCCESS) {
session_key->clear();
return res;
}
session_key->resize(session_key_size);
return OEMCrypto_SUCCESS;
}
std::vector<uint8_t> DrmPrivateKey::GetSessionKey(
const std::vector<uint8_t>& key_source) const {
// RSA -> Decrypt session key.
if (rsa_key_) {
if (!(rsa_key_->allowed_schemes() & kSign_RSASSA_PSS)) {
LOGE("RSA key cannot be used for session key decryption");
return std::vector<uint8_t>();
}
return rsa_key_->DecryptSessionKey(key_source);
}
// ECC -> ECDH.
// Step 1: Parse |key_source| as ECC key.
std::unique_ptr<EccPublicKey> ephemeral_ecc_key =
EccPublicKey::Load(key_source);
if (!ephemeral_ecc_key) {
LOGE("Failed to load server's ephemeral ECC key");
return std::vector<uint8_t>();
}
// Step 2: Derive session key.
return ecc_key_->DeriveSessionKey(*ephemeral_ecc_key);
}
std::vector<uint8_t> DrmPrivateKey::GetEncryptionKey(
const std::vector<uint8_t>& key_source) const {
if (!rsa_key_) {
LOGE("Only RSA DRM keys can derive an encryption key");
return std::vector<uint8_t>();
}
return rsa_key_->DecryptEncryptionKey(key_source);
}
OEMCryptoResult DrmPrivateKey::GenerateSignature(
const uint8_t* message, size_t message_length, uint8_t* signature,
size_t* signature_length) const {
if (rsa_key_) {
return rsa_key_->GenerateSignature(message, message_length, kRsaPssDefault,
signature, signature_length);
}
return ecc_key_->GenerateSignature(message, message_length, signature,
signature_length);
}
std::vector<uint8_t> DrmPrivateKey::GenerateSignature(
const std::vector<uint8_t>& message) const {
if (rsa_key_) {
return rsa_key_->GenerateSignature(message, kRsaPssDefault);
}
return ecc_key_->GenerateSignature(message);
}
size_t DrmPrivateKey::SignatureSize() const {
if (rsa_key_) {
return rsa_key_->SignatureSize();
}
return ecc_key_->SignatureSize();
}
OEMCryptoResult DrmPrivateKey::GenerateRsaSignature(
const uint8_t* message, size_t message_length, uint8_t* signature,
size_t* signature_length) const {
if (!rsa_key_) {
LOGE("Only RSA DRM keys can generate PKCS1 signatures");
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
return rsa_key_->GenerateSignature(message, message_length, kRsaPkcs1Cast,
signature, signature_length);
}
std::vector<uint8_t> DrmPrivateKey::GenerateRsaSignature(
const std::vector<uint8_t>& message) const {
if (!rsa_key_) {
LOGE("Only RSA DRM keys can generate PKCS1 signatures");
return std::vector<uint8_t>();
}
return rsa_key_->GenerateSignature(message, kRsaPkcs1Cast);
}
} // namespace util
} // namespace wvoec

View File

@@ -0,0 +1,931 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#include "oemcrypto_ecc_key.h"
#include <assert.h>
#include <string.h>
#include <mutex>
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include "log.h"
#include "scoped_object.h"
namespace wvoec {
namespace util {
namespace {
// Estimated max size (in bytes) of a serialized ECC key (public or
// private). These values are based on rough calculations for
// secp521r1 (largest of the supported curves) and should be slightly
// larger needed.
constexpr size_t kPrivateKeySize = 250;
constexpr size_t kPublicKeySize = 164;
// 256 bit key, intended to be used with CMAC-AES-256.
constexpr size_t kEccSessionKeySize = 32;
using ScopedBigNum = ScopedObject<BIGNUM, BN_free>;
using ScopedBigNumCtx = ScopedObject<BN_CTX, BN_CTX_free>;
using ScopedBio = ScopedObject<BIO, BIO_vfree>;
using ScopedEcKey = ScopedObject<EC_KEY, EC_KEY_free>;
using ScopedEvpMdCtx = ScopedObject<EVP_MD_CTX, EVP_MD_CTX_free>;
using ScopedEvpPkey = ScopedObject<EVP_PKEY, EVP_PKEY_free>;
using ScopedPrivateKeyInfo =
ScopedObject<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
using ScopedSigPoint = ScopedObject<ECDSA_SIG, ECDSA_SIG_free>;
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.
// Maintaining a process-wide set of supported EC groups reduces
// the overhead of group operations.
static std::mutex group_mutex;
static EC_GROUP* group_256 = nullptr;
static EC_GROUP* group_384 = nullptr;
static EC_GROUP* group_521 = nullptr;
std::lock_guard<std::mutex> group_lock(group_mutex);
switch (curve) {
case kEccSecp256r1: {
if (group_256 == nullptr) {
LOGD("Creating secp256r1 group");
// The curve secp256r1 was originally named prime256v1
// in the X9.62 specification.
group_256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
assert(group_256 != nullptr);
}
return group_256;
}
case kEccSecp384r1: {
if (group_384 == nullptr) {
LOGD("Creating secp384r1 group");
group_384 = EC_GROUP_new_by_curve_name(NID_secp384r1);
assert(group_384 != nullptr);
}
return group_384;
}
case kEccSecp521r1: {
if (group_521 == nullptr) {
LOGD("Creating secp521r1 group");
group_521 = EC_GROUP_new_by_curve_name(NID_secp521r1);
assert(group_521 != nullptr);
}
return group_521;
}
default:
LOGE("Cannot get EC group for unknown curve: curve = %d",
static_cast<int>(curve));
return nullptr;
}
}
// Determines which of the supported ECC curves the provided |key|
// belongs to.
//
// This is intended to be used on keys that have been deserialized
// from an ASN.1 structure which may have contained a key which is
// supported by OpenSSL/BoringSSL but not necessarily by OEMCrypto.
//
// If the key group is unknown to OEMCrypto or if an error occurs,
// kEccCurveUnknown is returned.
EccCurve GetCurveFromKeyGroup(const EC_KEY* key) {
ScopedBigNumCtx ctx(BN_CTX_new());
if (!ctx) {
LOGE("Failed to allocate BN ctx");
return kEccCurveUnknown;
}
const EC_GROUP* group = EC_KEY_get0_group(key);
if (group == nullptr) {
LOGE("Provided key does not have a group");
return kEccCurveUnknown;
}
int rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp256r1), ctx.get());
if (rc == 0) {
return kEccSecp256r1;
}
if (rc == -1) {
LOGE("Error occurred while checking against secp256r1");
return kEccCurveUnknown;
}
rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp384r1), ctx.get());
if (rc == 0) {
return kEccSecp384r1;
}
if (rc == -1) {
LOGE("Error occurred while checking against secp384r1");
return kEccCurveUnknown;
}
rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp521r1), ctx.get());
if (rc == 0) {
return kEccSecp521r1;
}
if (rc == -1) {
LOGE("Error occurred while checking against secp521r1");
return kEccCurveUnknown;
}
LOGW("Unsupported curve group");
return kEccCurveUnknown;
}
// 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;
}
// Performs a SHA2 digest on the provided |message| and outputs the
// computed hash to |digest|.
// The digest algorithm used depends on which curve is used.
// - secp256r1 -> SHA-256
// - secp384r1 -> SHA-384
// - secp521r1 -> SHA-512
// This function assumes that all parameters are valid.
// Returns true on success, false otherwise.
bool DigestMessage(EccCurve curve, const uint8_t* message, size_t message_size,
std::vector<uint8_t>* digest) {
const EVP_MD* md_engine = nullptr;
switch (curve) {
case kEccSecp256r1: {
md_engine = EVP_sha256();
break;
}
case kEccSecp384r1: {
md_engine = EVP_sha384();
break;
}
case kEccSecp521r1: {
md_engine = EVP_sha512();
break;
}
case kEccCurveUnknown:
// This case is to suppress compiler warnings. It will never
// occur.
break;
}
if (md_engine == nullptr) {
LOGE("Failed to get MD engine: curve = %d", static_cast<int>(curve));
return false;
}
ScopedEvpMdCtx md_ctx(EVP_MD_CTX_new());
if (!md_ctx) {
LOGE("Failed to create MD CTX");
return false;
}
if (!EVP_DigestInit_ex(md_ctx.get(), md_engine, nullptr)) {
LOGE("Failed to init MD CTX");
return false;
}
if (message_size > 0 &&
!EVP_DigestUpdate(md_ctx.get(), message, message_size)) {
LOGE("Failed to update");
return false;
}
digest->resize(EVP_MD_CTX_size(md_ctx.get()), 0);
const int res = EVP_DigestFinal_ex(md_ctx.get(), digest->data(), nullptr);
if (!res) {
LOGE("Failed to finalize");
return false;
}
return true;
}
// This KDF function is defined by OEMCrypto ECC specification.
// Function signature is based on the |kdf| parameter of
// ECDH_compute_key(). This function assumes that all pointer
// parameters are not null.
void* WidevineEccKdf(const void* secret, size_t secret_length, void* key,
size_t* key_size) {
if (*key_size < kEccSessionKeySize) {
LOGE("Output buffer is too small: required = %zu, size = %zu",
kEccSessionKeySize, *key_size);
return nullptr;
}
std::vector<uint8_t> digest;
if (!DigestMessage(kEccSecp256r1 /* SHA-256 */,
reinterpret_cast<const uint8_t*>(secret), secret_length,
&digest)) {
LOGE("Cannot derive key: Failed to hash secret");
return nullptr;
}
if (digest.size() != kEccSessionKeySize) {
LOGE("Unexpected hash size: actual = %zu, expected = %zu", digest.size(),
kEccSessionKeySize);
return nullptr;
}
*key_size = kEccSessionKeySize;
memcpy(key, digest.data(), *key_size);
return key;
}
void OpensslFreeU8(uint8_t* ptr) { OPENSSL_free(ptr); }
// Internal ECC public key serialization.
OEMCryptoResult SerializeEccPublicKey(const EC_KEY* key, uint8_t* buffer,
size_t* buffer_size) {
if (buffer_size == nullptr) {
LOGE("Output buffer size is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer == nullptr && *buffer_size > 0) {
LOGE("Output buffer is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint8_t* der_key_raw = nullptr;
const int der_res = i2d_EC_PUBKEY(
const_cast<EC_KEY*>(key) /* Does not get modified */, &der_key_raw);
if (der_res < 0) {
LOGE("Public key serialization failed");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
ScopedObject<uint8_t, OpensslFreeU8> der_key(der_key_raw);
der_key_raw = nullptr;
if (!der_key) {
LOGE("Encoded key is unexpectedly null");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (der_res == 0) {
LOGE("Unexpected DER encoded size");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const size_t required_size = static_cast<size_t>(der_res);
if (buffer == nullptr || *buffer_size < required_size) {
*buffer_size = required_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(buffer, der_key.get(), required_size);
*buffer_size = required_size;
return OEMCrypto_SUCCESS;
}
std::vector<uint8_t> SerializeEccPublicKey(const EC_KEY* key) {
size_t key_size = kPublicKeySize;
std::vector<uint8_t> key_data(key_size, 0);
const OEMCryptoResult res =
SerializeEccPublicKey(key, key_data.data(), &key_size);
if (res != OEMCrypto_SUCCESS) {
LOGE("Failed to serialize public key: result = %d", static_cast<int>(res));
key_data.clear();
} else {
key_data.resize(key_size);
}
return key_data;
}
bool ParseEccPrivateKeyInfo(const uint8_t* buffer, size_t length,
ScopedEcKey* key, EccCurve* curve) {
if (length == 0) {
LOGE("Public key is too small: length = %zu", length);
return false;
}
ScopedBio bio(BIO_new_mem_buf(buffer, static_cast<int>(length)));
if (!bio) {
LOGE("Failed to allocate BIO buffer");
return false;
}
// Step 1: Deserializes PKCS8 PrivateKeyInfo containing an ECC key.
ScopedPrivateKeyInfo priv_info(
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
if (!priv_info) {
LOGE("Failed to parse private key");
return false;
}
// Step 2: Convert to EC_KEY.
ScopedEvpPkey pkey(EVP_PKCS82PKEY(priv_info.get()));
if (!pkey) {
LOGE("Failed to convert PKCS8 to EVP");
return false;
}
const int key_type = EVP_PKEY_base_id(pkey.get());
if (key_type != EVP_PKEY_EC) {
LOGE("Decoded private key is not ECC");
return false;
}
key->reset(EVP_PKEY_get1_EC_KEY(pkey.get()));
if (!*key) {
LOGE("Failed to get ECC key");
return false;
}
// Step 3: Verify key parameters and curve family.
const int check = EC_KEY_check_key(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;
}
*curve = GetCurveFromKeyGroup(key->get());
if (*curve == kEccCurveUnknown) {
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);
return true;
}
} // namespace
std::string EccCurveToString(EccCurve curve) {
switch (curve) {
case kEccSecp256r1:
return "secp256r1";
case kEccSecp384r1:
return "secp384r1";
case kEccSecp521r1:
return "secp521r1";
case kEccCurveUnknown:
return "Unknown";
}
return "Unknown(" + std::to_string(static_cast<int>(curve)) + ")";
}
// static
std::unique_ptr<EccPublicKey> EccPublicKey::New(
const EccPrivateKey& private_key) {
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
if (!key->InitFromPrivateKey(private_key)) {
LOGE("Failed to initialize public key from private key");
key.reset();
}
return key;
}
// static
std::unique_ptr<EccPublicKey> EccPublicKey::Load(const uint8_t* buffer,
size_t length) {
if (buffer == nullptr) {
LOGE("Provided public key buffer is null");
return nullptr;
}
if (length == 0) {
LOGE("Provided public key buffer is zero length");
return nullptr;
}
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
if (!key->InitFromSubjectPublicKeyInfo(buffer, length)) {
LOGE("Failed to initialize public key from SubjectPublicKeyInfo");
key.reset();
}
return key;
}
// static
std::unique_ptr<EccPublicKey> EccPublicKey::Load(const std::string& buffer) {
if (buffer.empty()) {
LOGE("Provided public key buffer is empty");
return std::unique_ptr<EccPublicKey>();
}
return Load(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
}
// static
std::unique_ptr<EccPublicKey> EccPublicKey::Load(
const std::vector<uint8_t>& buffer) {
if (buffer.empty()) {
LOGE("Provided public key buffer is empty");
return std::unique_ptr<EccPublicKey>();
}
return Load(buffer.data(), buffer.size());
}
// static
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
const uint8_t* buffer, size_t length) {
if (buffer == nullptr) {
LOGE("Provided public key buffer is null");
return nullptr;
}
if (length == 0) {
LOGE("Provided public key buffer is zero length");
return nullptr;
}
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
if (!key->InitFromPrivateKeyInfo(buffer, length)) {
LOGE("Failed to initialize public key from PrivateKeyInfo");
key.reset();
}
return key;
}
// static
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
const std::string& buffer) {
if (buffer.empty()) {
LOGE("Provided public key buffer is empty");
return std::unique_ptr<EccPublicKey>();
}
return LoadPrivateKeyInfo(reinterpret_cast<const uint8_t*>(buffer.data()),
buffer.size());
}
// static
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
const std::vector<uint8_t>& buffer) {
if (buffer.empty()) {
LOGE("Provided public key buffer is empty");
return std::unique_ptr<EccPublicKey>();
}
return LoadPrivateKeyInfo(buffer.data(), buffer.size());
}
bool EccPublicKey::IsMatchingPrivateKey(
const EccPrivateKey& private_key) const {
if (private_key.curve() != curve_) {
return false;
}
return IsMatchingKeyPair(GetEcKey(), private_key.GetEcKey());
}
OEMCryptoResult EccPublicKey::Serialize(uint8_t* buffer,
size_t* buffer_size) const {
return SerializeEccPublicKey(key_, buffer, buffer_size);
}
std::vector<uint8_t> EccPublicKey::Serialize() const {
return SerializeEccPublicKey(key_);
}
OEMCryptoResult EccPublicKey::VerifySignature(const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length) const {
if (signature == nullptr || signature_length == 0) {
LOGE("Signature is missing");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (message == nullptr && message_length > 0) {
LOGE("Bad message data");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// Step 1: Parse signature.
const uint8_t* tp = signature;
ScopedSigPoint sig_point(d2i_ECDSA_SIG(nullptr, &tp, signature_length));
if (!sig_point) {
LOGE("Failed to parse signature");
// Most likely an invalid signature than an OpenSSL error.
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Step 2: Hash message
std::vector<uint8_t> digest;
if (!DigestMessage(curve_, message, message_length, &digest)) {
LOGE("Failed to digest message");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Step 3: Verify signature
const int res = ECDSA_do_verify(
digest.data(), static_cast<int>(digest.size()), sig_point.get(), key_);
if (res == -1) {
LOGE("Error occurred checking signature");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (res == 0) {
LOGD("Signature did not match");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult EccPublicKey::VerifySignature(
const std::string& message, const std::string& signature) const {
if (signature.empty()) {
LOGE("Signature should not be empty");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return VerifySignature(
reinterpret_cast<const uint8_t*>(message.data()), message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size());
}
OEMCryptoResult EccPublicKey::VerifySignature(
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 VerifySignature(message.data(), message.size(), signature.data(),
signature.size());
}
EccPublicKey::~EccPublicKey() {
if (key_ != nullptr) {
EC_KEY_free(key_);
key_ = nullptr;
}
curve_ = kEccCurveUnknown;
}
bool EccPublicKey::InitFromSubjectPublicKeyInfo(const uint8_t* buffer,
size_t length) {
// Deserialize SubjectPublicKeyInfo
const uint8_t* tp = buffer;
ScopedEcKey key(d2i_EC_PUBKEY(nullptr, &tp, length));
if (!key) {
LOGE("Failed to parse ECC key");
return false;
}
// Verify key parameters and curve family.
const int check = EC_KEY_check_key(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;
}
curve_ = GetCurveFromKeyGroup(key.get());
if (curve_ == kEccCurveUnknown) {
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);
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_)) {
return false;
}
// TODO(sigquit): Strip private information.
key_ = private_key.release();
return true;
}
bool EccPublicKey::InitFromPrivateKey(const EccPrivateKey& private_key) {
ScopedEcKey key(EC_KEY_new());
if (!key) {
LOGE("Failed to allocate key");
return false;
}
if (!EC_KEY_set_group(key.get(), EC_KEY_get0_group(private_key.GetEcKey()))) {
LOGE("Failed to set group");
return false;
}
if (!EC_KEY_set_public_key(key.get(),
EC_KEY_get0_public_key(private_key.GetEcKey()))) {
LOGE("Failed to set public point");
return false;
}
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;
}
// static
std::unique_ptr<EccPrivateKey> EccPrivateKey::New(EccCurve curve) {
std::unique_ptr<EccPrivateKey> key(new EccPrivateKey());
if (!key->InitFromCurve(curve)) {
LOGE("Failed to initialize private key from curve");
key.reset();
}
return key;
}
// static
std::unique_ptr<EccPrivateKey> EccPrivateKey::Load(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<EccPrivateKey> key(new EccPrivateKey());
if (!key->InitFromPrivateKeyInfo(buffer, length)) {
LOGE("Failed to initialize private key from PrivateKeyInfo");
key.reset();
}
return key;
}
// static
std::unique_ptr<EccPrivateKey> EccPrivateKey::Load(const std::string& buffer) {
if (buffer.empty()) {
LOGE("Provided private key buffer is empty");
return std::unique_ptr<EccPrivateKey>();
}
return Load(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
}
// static
std::unique_ptr<EccPrivateKey> EccPrivateKey::Load(
const std::vector<uint8_t>& buffer) {
if (buffer.empty()) {
LOGE("Provided private key buffer is empty");
return std::unique_ptr<EccPrivateKey>();
}
return Load(buffer.data(), buffer.size());
}
std::unique_ptr<EccPublicKey> EccPrivateKey::MakePublicKey() const {
return EccPublicKey::New(*this);
}
bool EccPrivateKey::IsMatchingPublicKey(const EccPublicKey& public_key) const {
if (public_key.curve() != curve_) {
return false;
}
return IsMatchingKeyPair(public_key.GetEcKey(), GetEcKey());
}
OEMCryptoResult EccPrivateKey::Serialize(uint8_t* buffer,
size_t* buffer_size) const {
if (buffer_size == nullptr) {
LOGE("Output buffer size is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer == nullptr && *buffer_size > 0) {
LOGE("Output buffer is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// Step 1: Convert EC_KEY key to EVP.
ScopedEvpPkey pkey(EVP_PKEY_new());
if (!pkey) {
LOGE("Failed to allocate EVP");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!EVP_PKEY_set1_EC_KEY(pkey.get(), key_)) {
LOGE("Failed to set EVP ECC key");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Step 2: Convert ECC EVP to PKCS8 format.
ScopedPrivateKeyInfo priv_info(EVP_PKEY2PKCS8(pkey.get()));
if (!priv_info) {
LOGE("Failed to convert ECC key to PKCS8 info");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Step 3: Serialize PKCS8 to DER encoding.
ScopedBio bio(BIO_new(BIO_s_mem()));
if (!bio) {
LOGE("Failed to allocate IO buffer for ECC key");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), priv_info.get())) {
LOGE("Failed to serialize ECC key");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Step 4: Determine key size and copy.
char* key_ptr = nullptr;
const long key_size = BIO_get_mem_data(bio.get(), &key_ptr);
if (key_size < 0) {
LOGE("Failed to get ECC key size");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (key_ptr == nullptr) {
LOGE("Encoded key is unexpectedly null");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const size_t required_size = static_cast<size_t>(key_size);
if (*buffer_size < required_size) {
*buffer_size = required_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*buffer_size = required_size;
memcpy(buffer, key_ptr, required_size);
return OEMCrypto_SUCCESS;
}
std::vector<uint8_t> EccPrivateKey::Serialize() const {
size_t key_size = kPrivateKeySize;
std::vector<uint8_t> key_data(key_size, 0);
const OEMCryptoResult res = Serialize(key_data.data(), &key_size);
if (res != OEMCrypto_SUCCESS) {
LOGE("Failed to serialize private key: result = %d", static_cast<int>(res));
key_data.clear();
} else {
key_data.resize(key_size);
}
return key_data;
}
OEMCryptoResult EccPrivateKey::SerializeAsPublicKey(uint8_t* buffer,
size_t* buffer_size) const {
return SerializeEccPublicKey(key_, buffer, buffer_size);
}
std::vector<uint8_t> EccPrivateKey::SerializeAsPublicKey() const {
return SerializeEccPublicKey(key_);
}
OEMCryptoResult EccPrivateKey::GenerateSignature(
const uint8_t* message, size_t message_length, uint8_t* signature,
size_t* signature_length) const {
if (signature_length == nullptr) {
LOGE("Output signature size is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (signature == nullptr && *signature_length > 0) {
LOGE("Output signature is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (message == nullptr && message_length > 0) {
LOGE("Invalid message data");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
const size_t expected_signature_length = ECDSA_size(key_);
if (*signature_length < expected_signature_length) {
*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");
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
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);
if (res <= 0) {
LOGE("Failed to serialize signature");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const size_t required_size = static_cast<size_t>(res);
if (signature == nullptr || *signature_length < required_size) {
*signature_length = required_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(signature, temp.data(), required_size);
*signature_length = required_size;
return OEMCrypto_SUCCESS;
}
std::vector<uint8_t> EccPrivateKey::GenerateSignature(
const std::string& message) const {
size_t signature_size = SignatureSize();
std::vector<uint8_t> signature(signature_size, 0);
const OEMCryptoResult res =
GenerateSignature(reinterpret_cast<const uint8_t*>(message.data()),
message.size(), signature.data(), &signature_size);
if (res != OEMCrypto_SUCCESS) {
LOGE("Failed to generate signature: result = %d", static_cast<int>(res));
signature.clear();
} else {
signature.resize(signature_size);
}
return signature;
}
std::vector<uint8_t> EccPrivateKey::GenerateSignature(
const std::vector<uint8_t>& message) const {
size_t signature_size = SignatureSize();
std::vector<uint8_t> signature(signature_size, 0);
const OEMCryptoResult res = GenerateSignature(
message.data(), message.size(), signature.data(), &signature_size);
if (res != OEMCrypto_SUCCESS) {
LOGE("Failed to generate signature: result = %d", static_cast<int>(res));
signature.clear();
} else {
signature.resize(signature_size);
}
return signature;
}
size_t EccPrivateKey::SignatureSize() const { return ECDSA_size(key_); }
OEMCryptoResult EccPrivateKey::DeriveSessionKey(
const EccPublicKey& public_key, uint8_t* session_key,
size_t* session_key_size) const {
if (public_key.curve() != curve_) {
LOGE("Incompatible ECC keys: public = %s, private = %s",
EccCurveToString(public_key.curve()).c_str(),
EccCurveToString(curve_).c_str());
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (session_key_size == nullptr) {
LOGE("Output session key size buffer is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (session_key == nullptr && *session_key_size > 0) {
LOGE("Output session key buffer is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (*session_key_size < kEccSessionKeySize) {
*session_key_size = kEccSessionKeySize;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
const int res = ECDH_compute_key(
session_key, kEccSessionKeySize,
EC_KEY_get0_public_key(public_key.GetEcKey()), key_, WidevineEccKdf);
if (res < 0) {
LOGE("ECDH error occurred");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (static_cast<size_t>(res) != kEccSessionKeySize) {
LOGE("Unexpected key size: size = %d", res);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
*session_key_size = kEccSessionKeySize;
return OEMCrypto_SUCCESS;
}
std::vector<uint8_t> EccPrivateKey::DeriveSessionKey(
const EccPublicKey& public_key) const {
size_t session_key_size = kEccSessionKeySize;
std::vector<uint8_t> session_key(session_key_size, 0);
const OEMCryptoResult res =
DeriveSessionKey(public_key, session_key.data(), &session_key_size);
if (res != OEMCrypto_SUCCESS) {
LOGE("Failed to derive session key: result = %d", static_cast<int>(res));
session_key.clear();
} else {
session_key.resize(session_key_size);
}
return session_key;
}
size_t EccPrivateKey::SessionKeyLength() const { return kEccSessionKeySize; }
EccPrivateKey::~EccPrivateKey() {
if (key_ != nullptr) {
EC_KEY_free(key_);
key_ = nullptr;
}
curve_ = kEccCurveUnknown;
}
bool EccPrivateKey::InitFromPrivateKeyInfo(const uint8_t* buffer,
size_t length) {
ScopedEcKey key;
if (!ParseEccPrivateKeyInfo(buffer, length, &key, &curve_)) return false;
key_ = key.release();
return true;
}
bool EccPrivateKey::InitFromCurve(EccCurve curve) {
const EC_GROUP* group = GetEcGroup(curve);
if (group == nullptr) {
LOGE("Failed to get ECC group");
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;
}
// Generate random key.
if (!EC_KEY_generate_key(key.get())) {
LOGE("Failed to generate random key");
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);
key_ = key.release();
return true;
}
} // namespace util
} // namespace wvoec

View File

@@ -0,0 +1,154 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#include "oemcrypto_key_deriver.h"
#include "log.h"
namespace wvoec {
namespace util {
namespace {
bool Derive128KeyAppend(Cmac* cmac, uint8_t counter, const uint8_t* ctx,
size_t ctx_size, std::vector<uint8_t>* derived_key) {
cmac->Reset();
if (!cmac->Update(counter)) {
return false;
}
if (!cmac->Update(ctx, ctx_size)) {
return false;
}
if (!cmac->FinalizeAppend(derived_key)) {
return false;
}
return true;
}
bool Derive128Key(Cmac* cmac, uint8_t counter, const uint8_t* ctx,
size_t ctx_size, std::vector<uint8_t>* derived_key) {
derived_key->clear();
return Derive128KeyAppend(cmac, counter, ctx, ctx_size, derived_key);
}
bool Derive256Key(Cmac* cmac, uint8_t counter_base, const uint8_t* ctx,
size_t ctx_size, std::vector<uint8_t>* derived_key) {
derived_key->clear();
if (!Derive128KeyAppend(cmac, counter_base, ctx, ctx_size, derived_key)) {
return false;
}
return Derive128KeyAppend(cmac, counter_base + 1, ctx, ctx_size, derived_key);
}
} // namespace
// static
std::unique_ptr<KeyDeriver> KeyDeriver::Create(const uint8_t* key,
size_t key_size) {
if (key == nullptr) {
LOGE("Key deriver key is null");
return std::unique_ptr<KeyDeriver>();
}
std::unique_ptr<KeyDeriver> key_deriver(new KeyDeriver());
if (!key_deriver->Init(key, key_size)) {
key_deriver.reset();
}
return key_deriver;
}
// static
std::unique_ptr<KeyDeriver> KeyDeriver::Create(
const std::vector<uint8_t>& key) {
if (key.empty()) {
LOGE("Key deriver key is empty");
return std::unique_ptr<KeyDeriver>();
}
return Create(key.data(), key.size());
}
bool KeyDeriver::Init(const uint8_t* key, size_t key_size) {
cmac_ = Cmac::Create(key, key_size);
if (!cmac_) {
LOGE("Failed to create CMAC for key deriver");
return false;
}
return true;
}
bool KeyDeriver::DeriveServerMacKey(const uint8_t* mac_key_context,
size_t mac_key_context_size,
std::vector<uint8_t>* mac_key_server) {
if (mac_key_context == nullptr) {
LOGE("Server MAC key context is null");
return false;
}
if (mac_key_server == nullptr) {
LOGE("Output server MAC key buffer is null");
return false;
}
return Derive256Key(cmac_.get(), 0x01, mac_key_context, mac_key_context_size,
mac_key_server);
}
bool KeyDeriver::DeriveServerMacKey(const std::vector<uint8_t>& mac_key_context,
std::vector<uint8_t>* mac_key_server) {
if (mac_key_context.empty()) {
LOGE("Server MAC key context is empty");
return false;
}
return DeriveServerMacKey(mac_key_context.data(), mac_key_context.size(),
mac_key_server);
}
bool KeyDeriver::DeriveClientMacKey(const uint8_t* mac_key_context,
size_t mac_key_context_size,
std::vector<uint8_t>* mac_key_client) {
if (mac_key_context == nullptr) {
LOGE("Client MAC key context is null");
return false;
}
if (mac_key_client == nullptr) {
LOGE("Output client MAC key buffer is null");
return false;
}
return Derive256Key(cmac_.get(), 0x03, mac_key_context, mac_key_context_size,
mac_key_client);
}
bool KeyDeriver::DeriveClientMacKey(const std::vector<uint8_t>& mac_key_context,
std::vector<uint8_t>* mac_key_client) {
if (mac_key_context.empty()) {
LOGE("Client MAC key context is empty");
return false;
}
return DeriveClientMacKey(mac_key_context.data(), mac_key_context.size(),
mac_key_client);
}
bool KeyDeriver::DeriveEncryptionKey(const uint8_t* enc_key_context,
size_t enc_key_context_size,
std::vector<uint8_t>* enc_key) {
if (enc_key_context == nullptr) {
LOGE("Encryption key context is null");
return false;
}
if (enc_key == nullptr) {
LOGE("Output encryption key buffer is null");
return false;
}
return Derive128Key(cmac_.get(), 0x01, enc_key_context, enc_key_context_size,
enc_key);
}
bool KeyDeriver::DeriveEncryptionKey(
const std::vector<uint8_t>& enc_key_context,
std::vector<uint8_t>* enc_key) {
if (enc_key_context.empty()) {
LOGE("Encryption key context is empty");
return false;
}
return DeriveEncryptionKey(enc_key_context.data(), enc_key_context.size(),
enc_key);
}
} // namespace util
} // namespace wvoec

View File

@@ -0,0 +1,234 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#include "oemcrypto_oem_cert.h"
#include <string.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "log.h"
#include "oemcrypto_rsa_key.h"
#include "scoped_object.h"
namespace wvoec {
namespace util {
namespace {
using ScopedCertificate = ScopedObject<X509, X509_free>;
using ScopedEvpKey = ScopedObject<EVP_PKEY, EVP_PKEY_free>;
using ScopedPkcs7 = ScopedObject<PKCS7, PKCS7_free>;
constexpr size_t kExpectedCertCount = 2; // Leaf and intermediate.
constexpr int kDeviceCertIndex = 0;
// Checks that the |public_key| from an X.509 certificate is the
// correct public key of the serialized |private_key_data|.
OEMCryptoResult VerifyRsaKey(const RSA* public_key,
const std::vector<uint8_t>& private_key_data) {
if (public_key == nullptr) {
LOGE("RSA key is null");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
std::unique_ptr<RsaPrivateKey> private_key =
RsaPrivateKey::Load(private_key_data);
if (!private_key) {
LOGE("Failed to parse provided RSA private key");
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
if (!RsaKeysAreMatchingPair(public_key, private_key->GetRsaKey())) {
LOGE("OEM certificate keys do not match");
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
return OEMCrypto_SUCCESS;
}
} // namespace
// This utility class encapsulates the minimum functionality of an
// OEM Public Certificate required to verify a device's OEM Public
// Certificate.
class OemPublicCertificate {
public:
// Loads a PKCS #7 signedData message with certificate chain.
// Minimum validation is performed. Only checks that the
// device's public key is of a known type (RSA).
static std::unique_ptr<OemPublicCertificate> Load(const uint8_t* public_cert,
size_t public_cert_size) {
std::unique_ptr<OemPublicCertificate> oem_public_cert;
if (public_cert == nullptr) {
LOGE("Public cert buffer is null");
return oem_public_cert;
}
if (public_cert_size == 0) {
LOGE("Public cert buffer is empty");
return oem_public_cert;
}
oem_public_cert.reset(new OemPublicCertificate());
if (!oem_public_cert->InitFromBuffer(public_cert, public_cert_size)) {
oem_public_cert.reset();
}
return oem_public_cert;
}
OemCertificate::KeyType key_type() const { return key_type_; }
const std::vector<uint8_t>& cert_data() const { return cert_data_; }
const RSA* GetPublicRsaKey() const {
return EVP_PKEY_get0_RSA(device_public_key_.get());
}
~OemPublicCertificate() = default;
OemPublicCertificate(const OemPublicCertificate&) = delete;
OemPublicCertificate(OemPublicCertificate&&) = delete;
const OemPublicCertificate& operator=(const OemPublicCertificate&) = delete;
OemPublicCertificate& operator=(OemPublicCertificate&&) = delete;
private:
OemPublicCertificate() {}
bool InitFromBuffer(const uint8_t* public_cert, size_t public_cert_size) {
// Step 1: Parse the PKCS7 certificate chain as signedData.
const uint8_t* public_cert_ptr = public_cert;
pkcs7_.reset(d2i_PKCS7(nullptr, &public_cert_ptr, public_cert_size));
if (!pkcs7_) {
LOGE("Failed to parse PKCS#7 certificate chain");
return false;
}
if (!PKCS7_type_is_signed(pkcs7_.get())) {
LOGE("OEM Public Certificate is not PKCS#7 signed data");
return false;
}
PKCS7_SIGNED* signed_data = pkcs7_->d.sign;
// Step 2: Get the leaf certificate.
const size_t cert_count =
static_cast<size_t>(sk_X509_num(signed_data->cert));
if (cert_count != kExpectedCertCount) {
LOGE("Unexpected number of certificates: expected = %zu, actual = %zu",
kExpectedCertCount, cert_count);
return false;
}
X509* leaf_cert = sk_X509_value(signed_data->cert, kDeviceCertIndex);
// Step 3a: Get the device's public key.
device_public_key_.reset(X509_get_pubkey(leaf_cert));
if (!device_public_key_) {
LOGE("Device X.509 certificate is missing a public key");
return false;
}
// Step 3b: Check key type.
if (EVP_PKEY_get0_RSA(device_public_key_.get()) == nullptr) {
LOGE("Device public key is not RSA");
return false;
}
key_type_ = OemCertificate::kRsa;
cert_data_.assign(public_cert, public_cert + public_cert_size);
return true;
}
OemCertificate::KeyType key_type_ = OemCertificate::kNone;
// OpenSSL/BoringSSL's implementation of PKCS7 objects.
ScopedPkcs7 pkcs7_;
ScopedEvpKey device_public_key_;
std::vector<uint8_t> cert_data_;
};
// ===== ===== ===== OEM Certificate ===== ===== =====
// static
std::unique_ptr<OemCertificate> OemCertificate::Create(
const uint8_t* private_key_data, size_t private_key_size,
const uint8_t* public_cert_data, size_t public_cert_size) {
std::unique_ptr<OemCertificate> oem_cert;
// Step 1: Verify public cert is well-formed.
std::unique_ptr<OemPublicCertificate> oem_public_cert =
OemPublicCertificate::Load(public_cert_data, public_cert_size);
if (!oem_public_cert) {
LOGE("Invalid OEM Public Certificate");
return oem_cert;
}
// Step 2: Verify private key is well-formed.
switch (oem_public_cert->key_type()) {
case kRsa: {
std::unique_ptr<RsaPrivateKey> oem_private_key =
RsaPrivateKey::Load(private_key_data, private_key_size);
if (!oem_private_key) {
LOGE("Invalid OEM Private Key");
return oem_cert;
}
} break;
case kNone: // Suppress compiler warnings.
return oem_cert;
}
// Step 3: Copy over data.
oem_cert.reset(new OemCertificate());
oem_cert->private_key_.assign(private_key_data,
private_key_data + private_key_size);
oem_cert->public_cert_ = std::move(oem_public_cert);
return oem_cert;
}
// static
std::unique_ptr<OemCertificate> OemCertificate::Create(
const std::vector<uint8_t>& private_key,
const std::vector<uint8_t>& public_cert) {
if (private_key.empty()) {
LOGE("Private key buffer is empty");
return std::unique_ptr<OemCertificate>();
}
if (public_cert.empty()) {
LOGE("Public cert buffer is empty");
return std::unique_ptr<OemCertificate>();
}
return Create(private_key.data(), private_key.size(), public_cert.data(),
public_cert.size());
}
OemCertificate::KeyType OemCertificate::key_type() const {
return public_cert_->key_type();
}
OEMCryptoResult OemCertificate::GetPublicCertificate(
uint8_t* public_cert, size_t* public_cert_length) const {
if (public_cert_length == nullptr) {
LOGE("Output |public_cert_length| is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (public_cert == nullptr && *public_cert_length > 0) {
LOGE("Output |public_cert| is null");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
const std::vector<uint8_t>& cert_data = public_cert_->cert_data();
if (*public_cert_length < cert_data.size()) {
*public_cert_length = cert_data.size();
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*public_cert_length = cert_data.size();
memcpy(public_cert, cert_data.data(), cert_data.size());
return OEMCrypto_SUCCESS;
}
const std::vector<uint8_t>& OemCertificate::GetPublicCertificate() const {
return public_cert_->cert_data();
}
OEMCryptoResult OemCertificate::IsCertificateValid() const {
switch (key_type()) {
case kRsa:
return VerifyRsaKey(public_cert_->GetPublicRsaKey(), private_key_);
case kNone: // Suppress compiler warnings.
break;
}
LOGE("Unexpected error key type: type = %d", static_cast<int>(key_type()));
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Constructor and destructor do not perform anything special, but
// must be declared within a scope which defines OemPublicCertificate.
OemCertificate::OemCertificate() {}
OemCertificate::~OemCertificate() {}
} // namespace util
} // namespace wvoec

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,88 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine
// License Agreement.
//
// Compute CRC32/MPEG2 Checksum. Needed for verification of WV Keybox.
//
#include "platform.h"
#include "wvcrc32.h"
namespace wvoec {
namespace util {
#define INIT_CRC32 0xffffffff
uint32_t wvrunningcrc32(const uint8_t* p_begin, size_t i_count,
uint32_t i_crc) {
constexpr uint32_t CRC32[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
/* Calculate the CRC */
while (i_count > 0) {
i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin)];
p_begin++;
i_count--;
}
return(i_crc);
}
uint32_t wvcrc32(const uint8_t* p_begin, size_t i_count) {
return(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
}
uint32_t wvcrc32Init() {
return INIT_CRC32;
}
uint32_t wvcrc32Cont(const uint8_t* p_begin, size_t i_count,
uint32_t prev_crc) {
return(wvrunningcrc32(p_begin, i_count, prev_crc));
}
uint32_t wvcrc32n(const uint8_t* p_begin, size_t i_count) {
return htonl(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
}
} // namespace util
} // namespace wvoec

View File

@@ -4,20 +4,37 @@ To install, side load ExoPlayerDemo.apk app to your device:
adb install ExoPlayerDemo.apk
To run, launch ExoPlayer, then choose the clip to play. The
Widevine-encrypted DASH CENC assets are in the "WIDEVINE DASH GTS"
To run, launch ExoPlayer, then choose the clip to play.
The Widevine-encrypted DASH CENC assets are in the "WIDEVINE DASH (policy tests)"
section.
These assets test various configurations of the Key Control Block (KCB)
with various protections and expirations:
WV: HDCP not specified (KCB: Observe_HDCP=false)
WV: HDCP not required (KCB: Observe_HDCP=true && HDCP=not required && DataPath=normal)
WV: HDCP required (KCB: Observe_HDCP=true && HDCP=required && DataPath=normal)
WV: Secure video path required (KCB: Observe_HDCP=true && HDCP=not required && DataPath=secure)
WV: HDCP + secure video path required (KCB: Observe_HDCP=true && HDCP=required && DataPath=secure)
WV: 30s license duration (KCB: test timer expiration)
WV: Secure VP9 (Requires a secure VP9 decoder. Only shows on devices that support VP9 content.)
Security Level Checks:
SW secure crypto (SW crypto required)
SW secure decode (SW crypto and decode required)
HW secure crypto (HW crypto required)
HW secure decode (HW crypto and decode required)
HW secure all (HW secure video path required (L1))
License Timer Policy:
30s license (timer stops at 30 seconds, playback should stop)
HDCP Policies
HDCP not required (KCB: Observe_HDCP=true && HDCP=not required && DataPath=normal)
HDCP 1.0 required (KCB: Observe_HDCP=true && HDCP=required && DataPath=normal)
HDCP 2.0 required (Requires HDCP 2.0+ to play)
HDCP 2.1 required (Requires HDCP 2.1+ to play)
HDCP 2.2 required (Requires HDCP 2.2+ to play)
HDCP no digital output (Output to connected devices not allowed)
There are also Widevine-encrypted DASH CENC assets for various container
formats as well.
Widevine DASH (MP4, H264): Contains cenc and cbcs encryption samples
Widevine DASH (WebM, VP9): Contains full-sample and sub-sample cenc
samples
Widevine DASH (MP4, H265): Contains cenc samples only
Notes:
@@ -31,10 +48,6 @@ Notes:
will do seamless resolution switching. If the decoder doesn't claim this then you'll still get the
old nearly-seamless-switch (codec release/re-acquire) behavior.
- If your device is running KLP or later, the player will attempt to hook into the
- The player will attempt to hook into the
AudioTrack.getTimestamp API to do A/V sync. It will fall back to a legacy approach if
the API isn't available.
- Exoplayer will retrieve a new license when playing "WV: 30s license duration",
after the license duration has expired. Integrators should verify by means
other than visual inspection that license duration is being enforced.