From 4a065adc33172883a0b12253cf0bd99606056a58 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Mon, 21 Mar 2022 21:22:19 -0700 Subject: [PATCH] Copied OEMCrypto utils to Android. The OEMCrypto utils have been copied over from the CDM repo. Tests have been excluded for this CL. Files represent a snapshot taken from http://go/wvgerrit/148270 and http://go/wvgerrit/148372. Bug: 205902021 Change-Id: I1a58952cd1436a48974367c5436bf7296163e6f1 --- libwvdrmengine/oemcrypto/util/include/cmac.h | 61 + .../util/include/oemcrypto_drm_key.h | 85 ++ .../util/include/oemcrypto_ecc_key.h | 281 ++++ .../util/include/oemcrypto_key_deriver.h | 61 + .../util/include/oemcrypto_oem_cert.h | 104 ++ .../util/include/oemcrypto_rsa_key.h | 376 +++++ .../oemcrypto/util/include/scoped_object.h | 70 + .../oemcrypto/util/include/wvcrc32.h | 22 + libwvdrmengine/oemcrypto/util/src/cmac.cpp | 173 +++ .../oemcrypto/util/src/oemcrypto_drm_key.cpp | 186 +++ .../oemcrypto/util/src/oemcrypto_ecc_key.cpp | 931 ++++++++++++ .../util/src/oemcrypto_key_deriver.cpp | 154 ++ .../oemcrypto/util/src/oemcrypto_oem_cert.cpp | 234 +++ .../oemcrypto/util/src/oemcrypto_rsa_key.cpp | 1287 +++++++++++++++++ libwvdrmengine/oemcrypto/util/src/wvcrc.cpp | 88 ++ 15 files changed, 4113 insertions(+) create mode 100644 libwvdrmengine/oemcrypto/util/include/cmac.h create mode 100644 libwvdrmengine/oemcrypto/util/include/oemcrypto_drm_key.h create mode 100644 libwvdrmengine/oemcrypto/util/include/oemcrypto_ecc_key.h create mode 100644 libwvdrmengine/oemcrypto/util/include/oemcrypto_key_deriver.h create mode 100644 libwvdrmengine/oemcrypto/util/include/oemcrypto_oem_cert.h create mode 100644 libwvdrmengine/oemcrypto/util/include/oemcrypto_rsa_key.h create mode 100644 libwvdrmengine/oemcrypto/util/include/scoped_object.h create mode 100644 libwvdrmengine/oemcrypto/util/include/wvcrc32.h create mode 100644 libwvdrmengine/oemcrypto/util/src/cmac.cpp create mode 100644 libwvdrmengine/oemcrypto/util/src/oemcrypto_drm_key.cpp create mode 100644 libwvdrmengine/oemcrypto/util/src/oemcrypto_ecc_key.cpp create mode 100644 libwvdrmengine/oemcrypto/util/src/oemcrypto_key_deriver.cpp create mode 100644 libwvdrmengine/oemcrypto/util/src/oemcrypto_oem_cert.cpp create mode 100644 libwvdrmengine/oemcrypto/util/src/oemcrypto_rsa_key.cpp create mode 100644 libwvdrmengine/oemcrypto/util/src/wvcrc.cpp diff --git a/libwvdrmengine/oemcrypto/util/include/cmac.h b/libwvdrmengine/oemcrypto/util/include/cmac.h new file mode 100644 index 00000000..85ef7c49 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/cmac.h @@ -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 +#include + +#include +#include + +#include + +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 Create(const uint8_t* key, size_t key_size); + static std::unique_ptr Create(const std::vector& 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& 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* mac); + // Similar to Finalize() except that the output is appended to + // the end of the provided |mac| buffer. + bool FinalizeAppend(std::vector* 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_ diff --git a/libwvdrmengine/oemcrypto/util/include/oemcrypto_drm_key.h b/libwvdrmengine/oemcrypto/util/include/oemcrypto_drm_key.h new file mode 100644 index 00000000..257429da --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/oemcrypto_drm_key.h @@ -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 +#include +#include + +#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 Create( + std::shared_ptr&& rsa_key); + static std::unique_ptr Create( + std::unique_ptr&& rsa_key); + // Create an ECC-based DRM key. + static std::unique_ptr Create( + std::shared_ptr&& ecc_key); + static std::unique_ptr Create( + std::unique_ptr&& ecc_key); + + bool IsRsaKey() const { return static_cast(rsa_key_); } + bool IsEccKey() const { return static_cast(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* session_key) const; + std::vector GetSessionKey( + const std::vector& 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 GetEncryptionKey( + const std::vector& 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 GenerateSignature( + const std::vector& 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 GenerateRsaSignature( + const std::vector& message) const; + + ~DrmPrivateKey() {} + + private: + DrmPrivateKey() {} + + // Only one will be set. + std::shared_ptr ecc_key_; + std::shared_ptr rsa_key_; +}; +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_DRM_KEY_H_ diff --git a/libwvdrmengine/oemcrypto/util/include/oemcrypto_ecc_key.h b/libwvdrmengine/oemcrypto/util/include/oemcrypto_ecc_key.h new file mode 100644 index 00000000..62df66da --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/oemcrypto_ecc_key.h @@ -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 +#include + +#include +#include +#include + +#include + +#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 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 Load(const uint8_t* buffer, + size_t length); + static std::unique_ptr Load(const std::string& buffer); + static std::unique_ptr Load(const std::vector& buffer); + + // Loads a serialized ECC private key, but only converting the public key. + static std::unique_ptr LoadPrivateKeyInfo(const uint8_t* buffer, + size_t length); + static std::unique_ptr LoadPrivateKeyInfo( + const std::string& buffer); + static std::unique_ptr LoadPrivateKeyInfo( + const std::vector& 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 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& message, + const std::vector& 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 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 Load(const uint8_t* buffer, + size_t length); + static std::unique_ptr Load(const std::string& buffer); + static std::unique_ptr Load( + const std::vector& buffer); + + // Creates a new ECC public key of this private key. + // Equivalent to calling EccPublicKey::New with this private + // key. + std::unique_ptr 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 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 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 GenerateSignature( + const std::vector& message) const; + std::vector 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 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_ diff --git a/libwvdrmengine/oemcrypto/util/include/oemcrypto_key_deriver.h b/libwvdrmengine/oemcrypto/util/include/oemcrypto_key_deriver.h new file mode 100644 index 00000000..4a525470 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/oemcrypto_key_deriver.h @@ -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 +#include + +#include +#include + +#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 Create(const uint8_t* key, + size_t key_size); + static std::unique_ptr Create(const std::vector& 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* mac_key_server); + bool DeriveServerMacKey(const std::vector& mac_key_context, + std::vector* 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* mac_key_client); + bool DeriveClientMacKey(const std::vector& mac_key_context, + std::vector* 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* enc_key); + bool DeriveEncryptionKey(const std::vector& enc_key_context, + std::vector* enc_key); + + ~KeyDeriver() {} + + private: + KeyDeriver() {} + + bool Init(const uint8_t* key, size_t key_size); + + std::unique_ptr cmac_; +}; +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_KEY_DERIVER_H_ diff --git a/libwvdrmengine/oemcrypto/util/include/oemcrypto_oem_cert.h b/libwvdrmengine/oemcrypto/util/include/oemcrypto_oem_cert.h new file mode 100644 index 00000000..f6332808 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/oemcrypto_oem_cert.h @@ -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 +#include + +#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 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 Create( + const std::vector& private_key, + const std::vector& 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& 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& 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 private_key_; + // Serialized OEM Certificate. + std::unique_ptr public_cert_; +}; // class OemCertificate +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_OEM_CERT_H_ diff --git a/libwvdrmengine/oemcrypto/util/include/oemcrypto_rsa_key.h b/libwvdrmengine/oemcrypto/util/include/oemcrypto_rsa_key.h new file mode 100644 index 00000000..d66f527a --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/oemcrypto_rsa_key.h @@ -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 +#include + +#include +#include +#include + +#include + +#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 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 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 Load(const uint8_t* buffer, + size_t length); + static std::unique_ptr Load(const std::string& buffer); + static std::unique_ptr Load(const std::vector& buffer); + + // Loads a serialized RSA private key, but only converting the public key. + static std::unique_ptr LoadPrivateKeyInfo(const uint8_t* buffer, + size_t length); + static std::unique_ptr LoadPrivateKeyInfo( + const std::string& buffer); + static std::unique_ptr LoadPrivateKeyInfo( + const std::vector& 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 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& message, + const std::vector& 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 EncryptSessionKey( + const std::vector& session_key) const; + std::vector 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 EncryptEncryptionKey( + const std::vector& encryption_key) const; + std::vector 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 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 Load(const uint8_t* buffer, + size_t length); + static std::unique_ptr Load(const std::string& buffer); + static std::unique_ptr Load( + const std::vector& buffer); + + // Creates a new RSA public key of this private key. + // Equivalent to calling RsaPublicKey::New with this private + // key. + std::unique_ptr 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 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 GenerateSignature( + const std::vector& message, + RsaSignatureAlgorithm algorithm = kRsaPssDefault) const; + std::vector 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 DecryptSessionKey( + const std::vector& enc_session_key) const; + std::vector 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 DecryptEncryptionKey( + const std::vector& enc_encryption_key) const; + std::vector 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_ diff --git a/libwvdrmengine/oemcrypto/util/include/scoped_object.h b/libwvdrmengine/oemcrypto/util/include/scoped_object.h new file mode 100644 index 00000000..115b6178 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/scoped_object.h @@ -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 +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_ diff --git a/libwvdrmengine/oemcrypto/util/include/wvcrc32.h b/libwvdrmengine/oemcrypto/util/include/wvcrc32.h new file mode 100644 index 00000000..3d913ef3 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/wvcrc32.h @@ -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 + +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_ diff --git a/libwvdrmengine/oemcrypto/util/src/cmac.cpp b/libwvdrmengine/oemcrypto/util/src/cmac.cpp new file mode 100644 index 00000000..e22e729d --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/cmac.cpp @@ -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 + +#include "log.h" +#include "scoped_object.h" + +namespace wvoec { +namespace util { +namespace { +using ScopedCmacCtx = ScopedObject; +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::Create(const uint8_t* key, size_t key_size) { + std::unique_ptr 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::Create(const std::vector& key) { + if (key.empty()) { + LOGE("CMAC key is empty"); + return std::unique_ptr(); + } + 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& data) { + return Update(data.data(), data.size()); +} + +bool Cmac::Update(uint8_t datum) { return Update(&datum, 1); } + +bool Cmac::Finalize(std::vector* mac) { + if (mac == nullptr) { + LOGE("Output MAC buffer is null"); + return false; + } + mac->clear(); + return FinalizeAppend(mac); +} + +bool Cmac::FinalizeAppend(std::vector* 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 diff --git a/libwvdrmengine/oemcrypto/util/src/oemcrypto_drm_key.cpp b/libwvdrmengine/oemcrypto/util/src/oemcrypto_drm_key.cpp new file mode 100644 index 00000000..207a0831 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/oemcrypto_drm_key.cpp @@ -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 + +#include "OEMCryptoCENC.h" +#include "log.h" + +namespace wvoec { +namespace util { +// static +std::unique_ptr DrmPrivateKey::Create( + std::shared_ptr&& rsa_key) { + if (!rsa_key) { + LOGE("No RSA key provided"); + return std::unique_ptr(); + } + std::unique_ptr drm_key(new DrmPrivateKey()); + drm_key->rsa_key_ = std::move(rsa_key); + return drm_key; +} + +// static +std::unique_ptr DrmPrivateKey::Create( + std::unique_ptr&& rsa_key) { + if (!rsa_key) { + LOGE("No RSA key provided"); + return std::unique_ptr(); + } + std::unique_ptr drm_key(new DrmPrivateKey()); + drm_key->rsa_key_ = std::move(rsa_key); + return drm_key; +} + +// static +std::unique_ptr DrmPrivateKey::Create( + std::shared_ptr&& ecc_key) { + if (!ecc_key) { + LOGE("No ECC key provided"); + return std::unique_ptr(); + } + std::unique_ptr drm_key(new DrmPrivateKey()); + drm_key->ecc_key_ = std::move(ecc_key); + return drm_key; +} + +// static +std::unique_ptr DrmPrivateKey::Create( + std::unique_ptr&& ecc_key) { + if (!ecc_key) { + LOGE("No ECC key provided"); + return std::unique_ptr(); + } + std::unique_ptr 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* 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 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 DrmPrivateKey::GetSessionKey( + const std::vector& 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(); + } + return rsa_key_->DecryptSessionKey(key_source); + } + // ECC -> ECDH. + // Step 1: Parse |key_source| as ECC key. + std::unique_ptr ephemeral_ecc_key = + EccPublicKey::Load(key_source); + if (!ephemeral_ecc_key) { + LOGE("Failed to load server's ephemeral ECC key"); + return std::vector(); + } + // Step 2: Derive session key. + return ecc_key_->DeriveSessionKey(*ephemeral_ecc_key); +} + +std::vector DrmPrivateKey::GetEncryptionKey( + const std::vector& key_source) const { + if (!rsa_key_) { + LOGE("Only RSA DRM keys can derive an encryption key"); + return std::vector(); + } + 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 DrmPrivateKey::GenerateSignature( + const std::vector& 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 DrmPrivateKey::GenerateRsaSignature( + const std::vector& message) const { + if (!rsa_key_) { + LOGE("Only RSA DRM keys can generate PKCS1 signatures"); + return std::vector(); + } + return rsa_key_->GenerateSignature(message, kRsaPkcs1Cast); +} +} // namespace util +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/util/src/oemcrypto_ecc_key.cpp b/libwvdrmengine/oemcrypto/util/src/oemcrypto_ecc_key.cpp new file mode 100644 index 00000000..e226ed3a --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/oemcrypto_ecc_key.cpp @@ -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 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +using ScopedBigNumCtx = ScopedObject; +using ScopedBio = ScopedObject; +using ScopedEcKey = ScopedObject; +using ScopedEvpMdCtx = ScopedObject; +using ScopedEvpPkey = ScopedObject; +using ScopedPrivateKeyInfo = + ScopedObject; +using ScopedSigPoint = ScopedObject; + +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 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(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* 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(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 digest; + if (!DigestMessage(kEccSecp256r1 /* SHA-256 */, + reinterpret_cast(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(key) /* Does not get modified */, &der_key_raw); + if (der_res < 0) { + LOGE("Public key serialization failed"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + ScopedObject 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(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 SerializeEccPublicKey(const EC_KEY* key) { + size_t key_size = kPublicKeySize; + std::vector 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(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(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(curve)) + ")"; +} + +// static +std::unique_ptr EccPublicKey::New( + const EccPrivateKey& private_key) { + std::unique_ptr 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::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 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::Load(const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return std::unique_ptr(); + } + return Load(reinterpret_cast(buffer.data()), buffer.size()); +} + +// static +std::unique_ptr EccPublicKey::Load( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return std::unique_ptr(); + } + return Load(buffer.data(), buffer.size()); +} + +// static +std::unique_ptr 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 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::LoadPrivateKeyInfo( + const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return std::unique_ptr(); + } + return LoadPrivateKeyInfo(reinterpret_cast(buffer.data()), + buffer.size()); +} + +// static +std::unique_ptr EccPublicKey::LoadPrivateKeyInfo( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return std::unique_ptr(); + } + 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 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 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(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(message.data()), message.size(), + reinterpret_cast(signature.data()), signature.size()); +} + +OEMCryptoResult EccPublicKey::VerifySignature( + const std::vector& message, + const std::vector& 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::New(EccCurve curve) { + std::unique_ptr key(new EccPrivateKey()); + if (!key->InitFromCurve(curve)) { + LOGE("Failed to initialize private key from curve"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr 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 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::Load(const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided private key buffer is empty"); + return std::unique_ptr(); + } + return Load(reinterpret_cast(buffer.data()), buffer.size()); +} + +// static +std::unique_ptr EccPrivateKey::Load( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided private key buffer is empty"); + return std::unique_ptr(); + } + return Load(buffer.data(), buffer.size()); +} + +std::unique_ptr 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(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 EccPrivateKey::Serialize() const { + size_t key_size = kPrivateKeySize; + std::vector 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(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 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 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(digest.size()), key_)); + if (!sig_point) { + LOGE("Failed to perform ECDSA"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Serialize + std::vector 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(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 EccPrivateKey::GenerateSignature( + const std::string& message) const { + size_t signature_size = SignatureSize(); + std::vector signature(signature_size, 0); + const OEMCryptoResult res = + GenerateSignature(reinterpret_cast(message.data()), + message.size(), signature.data(), &signature_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to generate signature: result = %d", static_cast(res)); + signature.clear(); + } else { + signature.resize(signature_size); + } + return signature; +} + +std::vector EccPrivateKey::GenerateSignature( + const std::vector& message) const { + size_t signature_size = SignatureSize(); + std::vector 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(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(res) != kEccSessionKeySize) { + LOGE("Unexpected key size: size = %d", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *session_key_size = kEccSessionKeySize; + return OEMCrypto_SUCCESS; +} + +std::vector EccPrivateKey::DeriveSessionKey( + const EccPublicKey& public_key) const { + size_t session_key_size = kEccSessionKeySize; + std::vector 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(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 diff --git a/libwvdrmengine/oemcrypto/util/src/oemcrypto_key_deriver.cpp b/libwvdrmengine/oemcrypto/util/src/oemcrypto_key_deriver.cpp new file mode 100644 index 00000000..2d65a29c --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/oemcrypto_key_deriver.cpp @@ -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* 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* 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* 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::Create(const uint8_t* key, + size_t key_size) { + if (key == nullptr) { + LOGE("Key deriver key is null"); + return std::unique_ptr(); + } + std::unique_ptr key_deriver(new KeyDeriver()); + if (!key_deriver->Init(key, key_size)) { + key_deriver.reset(); + } + return key_deriver; +} + +// static +std::unique_ptr KeyDeriver::Create( + const std::vector& key) { + if (key.empty()) { + LOGE("Key deriver key is empty"); + return std::unique_ptr(); + } + 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* 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& mac_key_context, + std::vector* 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* 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& mac_key_context, + std::vector* 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* 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& enc_key_context, + std::vector* 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 diff --git a/libwvdrmengine/oemcrypto/util/src/oemcrypto_oem_cert.cpp b/libwvdrmengine/oemcrypto/util/src/oemcrypto_oem_cert.cpp new file mode 100644 index 00000000..8141a370 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/oemcrypto_oem_cert.cpp @@ -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 + +#include +#include +#include + +#include "log.h" +#include "oemcrypto_rsa_key.h" +#include "scoped_object.h" + +namespace wvoec { +namespace util { +namespace { +using ScopedCertificate = ScopedObject; +using ScopedEvpKey = ScopedObject; +using ScopedPkcs7 = ScopedObject; + +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& private_key_data) { + if (public_key == nullptr) { + LOGE("RSA key is null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + std::unique_ptr 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 Load(const uint8_t* public_cert, + size_t public_cert_size) { + std::unique_ptr 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& 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(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 cert_data_; +}; + +// ===== ===== ===== OEM Certificate ===== ===== ===== + +// static +std::unique_ptr 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 oem_cert; + // Step 1: Verify public cert is well-formed. + std::unique_ptr 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 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::Create( + const std::vector& private_key, + const std::vector& public_cert) { + if (private_key.empty()) { + LOGE("Private key buffer is empty"); + return std::unique_ptr(); + } + if (public_cert.empty()) { + LOGE("Public cert buffer is empty"); + return std::unique_ptr(); + } + 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& 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& 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(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 diff --git a/libwvdrmengine/oemcrypto/util/src/oemcrypto_rsa_key.cpp b/libwvdrmengine/oemcrypto/util/src/oemcrypto_rsa_key.cpp new file mode 100644 index 00000000..68539ffb --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/oemcrypto_rsa_key.cpp @@ -0,0 +1,1287 @@ +// 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_rsa_key.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "oemcrypto_types.h" +#include "scoped_object.h" + +namespace wvoec { +namespace util { +namespace { +// Estimated size of an RSA private key. +// The private key constists of: +// n : Modulos : byteSize(n) ~= bitSize(n)/8 +// e : Public exponent : 4 bytes +// d : Private exponent : ~byteSize(n) +// p : Prime 1 : ~byteSize(n)/2 +// q : Prime 2 : ~byteSize(n)/2 +// d (mod p-1) : Exponent 1 : ~byteSize(n)/2 +// d (mod q-1) : Exponent 2 : ~byteSize(n)/2 +// q^-1 (mod p) : Coefficient : ~byteSize(n)/2 +// And the ASN.1 tags for each component (roughly 25 bytes). +constexpr size_t kPrivateKeySize = (3072 / 8) * 5 + 25; +// Estimated size of an RSA public key. +// The public key constists of: +// The private key constists of: +// n : Modulos : byteSize(n) ~= bitSize(n)/8 +// e : Public exponent : 4 bytes +// And the ASN.1 tags + outer structure. +constexpr size_t kPublicKeySize = (3072 / 8) + 100; + +// 128 bit key, intended to be used with CMAC-AES-128. +constexpr size_t kRsaSessionKeySize = 16; +// Encryption key used by OEMCrypto session for encrypting and +// decrypting data. +constexpr size_t kEncryptionKeySize = wvoec::KEY_SIZE; + +// Salt length used by OEMCrypto's RSASSA-PSS implementation. +// See description of kRsaPssDefault for more information. +constexpr size_t kPssSaltLength = 20; +// Requirement of CAST receivers. +constexpr size_t kRsaPkcs1CastMaxMessageSize = 83; + +using ScopedBigNum = ScopedObject; +using ScopedBio = ScopedObject; +using ScopedEvpMdCtx = ScopedObject; +using ScopedEvpPkey = ScopedObject; +using ScopedPrivateKeyInfo = + ScopedObject; +using ScopedRsaKey = ScopedObject; + +// Estimates the RSA rough field size from the real bit size of the +// RSA modulos. The actual bit length could vary by a few bits. +RsaFieldSize RealBitSizeToFieldSize(int bits) { + if (bits > 1800 && bits < 2200) { + return kRsa2048Bit; + } + if (bits > 2800 && bits < 3200) { + return kRsa3072Bit; + } + return kRsaFieldUnknown; +} + +bool IsValidAllowedSchemes(uint32_t allowed_schemes) { + static constexpr uint32_t kAllSchemesMask = + kSign_RSASSA_PSS | kSign_PKCS1_Block1; + return (allowed_schemes & kAllSchemesMask) != 0; +} + +bool ParseRsaPrivateKeyInfo(const uint8_t* buffer, size_t length, + ScopedRsaKey* key, uint32_t* allowed_schemes, + bool* explicit_schemes, RsaFieldSize* field_size) { + if (length < 8) { + LOGE("Public key is too small: length = %zu", length); + return false; + } + ScopedBio bio; + // Check allowed scheme type. + if (!memcmp("SIGN", buffer, 4)) { + uint32_t allowed_schemes_bno; + memcpy(&allowed_schemes_bno, reinterpret_cast(&buffer[4]), + 4); + *allowed_schemes = ntohl(allowed_schemes_bno); + if (!IsValidAllowedSchemes(*allowed_schemes)) { + LOGE("Invalid allowed schemes value: allowed_schemes = %08x", + *allowed_schemes); + return false; + } + bio.reset(BIO_new_mem_buf(&buffer[8], static_cast(length - 8))); + *explicit_schemes = true; + } else { + *allowed_schemes = kSign_RSASSA_PSS; + bio.reset(BIO_new_mem_buf(buffer, static_cast(length))); + *explicit_schemes = false; + } + if (!bio) { + LOGE("Failed to allocate BIO buffer"); + return false; + } + // Step 1: Deserializes PKCS8 PrivateKeyInfo containing an RSA 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 RSA 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_RSA) { + LOGE("Decoded private key is not RSA"); + return false; + } + key->reset(EVP_PKEY_get1_RSA(pkey.get())); + if (!*key) { + LOGE("Failed to get RSA key"); + return false; + } + // Step 3: Verify key parameters and field width. + const int check = RSA_check_key(key->get()); + if (check == 0) { + LOGE("RSA key parameters are invalid"); + return false; + } else if (check == -1) { + LOGE("Failed to check RSA key"); + return false; + } + const int bits = RSA_bits(key->get()); + *field_size = RealBitSizeToFieldSize(bits); + if (*field_size == kRsaFieldUnknown) { + LOGE("Unsupported RSA key size: bits = %d", bits); + return false; + } + return true; +} + +void OpensslFreeU8(uint8_t* ptr) { OPENSSL_free(ptr); } +} // namespace + +std::string RsaFieldSizeToString(RsaFieldSize field_size) { + switch (field_size) { + case kRsa2048Bit: + return "RSA-2048"; + case kRsa3072Bit: + return "RSA-3072"; + case kRsaFieldUnknown: + return "Unknown"; + } + return "Unknown(" + std::to_string(static_cast(field_size)) + ")"; +} + +bool RsaKeysAreMatchingPair(const RSA* public_key, const RSA* private_key) { + if (public_key == nullptr) { + LOGE("Public key is null"); + return false; + } + if (private_key == nullptr) { + LOGE("Private key is null"); + return false; + } + // Step 1: Extract public key components. + const BIGNUM* public_n = nullptr; + const BIGNUM* public_e = nullptr; + const BIGNUM* d = nullptr; + RSA_get0_key(public_key, &public_n, &public_e, &d); + if (public_n == nullptr || public_e == nullptr) { + LOGE("Failed to get RSA public key components"); + return false; + } + // Step 2: Extract private key components. + const BIGNUM* private_n = nullptr; + const BIGNUM* private_e = nullptr; + RSA_get0_key(private_key, &private_n, &private_e, &d); + if (private_n == nullptr || private_e == nullptr) { + LOGE("Failed to get RSA private key components"); + return false; + } + // Step 3: Compare RSA components. + if (BN_cmp(public_n, private_n)) { + LOGD("RSA modulos do not match"); + return false; + } + if (BN_cmp(public_e, private_e)) { + LOGD("RSA exponents do not match"); + return false; + } + return true; +} + +// ===== ===== ===== RSA Public Key ===== ===== ===== + +// static +std::unique_ptr RsaPublicKey::New( + const RsaPrivateKey& private_key) { + std::unique_ptr key(new RsaPublicKey()); + if (!key->InitFromPrivateKey(private_key)) { + LOGE("Failed to initialize public key from private key"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPublicKey::FromSslHandle( + const RSA* rsa_handle, uint32_t allowed_schemes) { + if (rsa_handle == nullptr) { + LOGE("Provided OpenSSL/BoringSSL RSA key is null"); + return nullptr; + } + if (!IsValidAllowedSchemes(allowed_schemes)) { + LOGE("Invalid |allowed_schemes| value: allowed_schemes = %08x", + allowed_schemes); + return nullptr; + } + std::unique_ptr key(new RsaPublicKey()); + if (!key->InitFromSslHandle(rsa_handle, allowed_schemes)) { + LOGE("Failed to initialize public key from OpenSSL/BoringSSL RSA handle"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPublicKey::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 key(new RsaPublicKey()); + if (!key->InitFromSubjectPublicKeyInfo(buffer, length)) { + LOGE("Failed to initialize public key from SubjectPublicKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPublicKey::Load(const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return nullptr; + } + return Load(reinterpret_cast(buffer.data()), buffer.size()); +} + +// static +std::unique_ptr RsaPublicKey::Load( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return nullptr; + } + return Load(buffer.data(), buffer.size()); +} + +// static +std::unique_ptr RsaPublicKey::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 key(new RsaPublicKey()); + if (!key->InitFromPrivateKeyInfo(buffer, length)) { + LOGE("Failed to initialize public key from PrivateKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPublicKey::LoadPrivateKeyInfo( + const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return nullptr; + } + return LoadPrivateKeyInfo(reinterpret_cast(buffer.data()), + buffer.size()); +} + +// static +std::unique_ptr RsaPublicKey::LoadPrivateKeyInfo( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return nullptr; + } + return LoadPrivateKeyInfo(buffer.data(), buffer.size()); +} + +bool RsaPublicKey::IsMatchingPrivateKey( + const RsaPrivateKey& private_key) const { + if (private_key.field_size() != field_size_) { + return false; + } + return RsaKeysAreMatchingPair(GetRsaKey(), private_key.GetRsaKey()); +} + +OEMCryptoResult RsaPublicKey::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; + } + + uint8_t* der_key_raw = nullptr; + const int der_res = i2d_RSA_PUBKEY(key_, &der_key_raw); + if (der_res < 0) { + LOGE("Public key serialization failed"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + ScopedObject 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(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 RsaPublicKey::Serialize() const { + size_t key_size = kPublicKeySize; + std::vector key_data(key_size, 0); + const OEMCryptoResult res = Serialize(key_data.data(), &key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to serialize public key: result = %d", static_cast(res)); + key_data.clear(); + } else { + key_data.resize(key_size); + } + return key_data; +} + +OEMCryptoResult RsaPublicKey::VerifySignature( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length, RsaSignatureAlgorithm algorithm) 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; + } + switch (algorithm) { + case kRsaPssDefault: + return VerifySignaturePss(message, message_length, signature, + signature_length); + case kRsaPkcs1Cast: + return VerifySignaturePkcs1Cast(message, message_length, signature, + signature_length); + } + LOGE("Unknown RSA signature algorithm: %d", static_cast(algorithm)); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult RsaPublicKey::VerifySignature( + const std::string& message, const std::string& signature, + RsaSignatureAlgorithm algorithm) const { + if (signature.empty()) { + LOGE("Signature should not be empty"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return VerifySignature(reinterpret_cast(message.data()), + message.size(), + reinterpret_cast(signature.data()), + signature.size(), algorithm); +} + +OEMCryptoResult RsaPublicKey::VerifySignature( + const std::vector& message, const std::vector& signature, + RsaSignatureAlgorithm algorithm) 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(), algorithm); +} + +OEMCryptoResult RsaPublicKey::EncryptSessionKey( + const uint8_t* session_key, size_t session_key_size, + uint8_t* enc_session_key, size_t* enc_session_key_size) const { + if (session_key == nullptr) { + LOGE("Session key is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (session_key_size != kRsaSessionKeySize) { + LOGE("Unexpected session key size: expected = %zu, actual = %zu", + kRsaSessionKeySize, session_key_size); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (enc_session_key_size == nullptr) { + LOGE("Output encrypted session key size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (enc_session_key == nullptr && *enc_session_key_size > 0) { + LOGE("Output encrypted session key is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return EncryptOaep(session_key, session_key_size, enc_session_key, + enc_session_key_size); +} + +std::vector RsaPublicKey::EncryptSessionKey( + const std::vector& session_key) const { + if (session_key.empty()) { + LOGE("Session key is empty"); + return std::vector(); + } + size_t enc_session_key_size = static_cast(RSA_size(key_)); + std::vector enc_session_key(enc_session_key_size); + const OEMCryptoResult res = + EncryptSessionKey(session_key.data(), session_key.size(), + enc_session_key.data(), &enc_session_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to encrypt session key: result = %d", static_cast(res)); + enc_session_key.clear(); + } else { + enc_session_key.resize(enc_session_key_size); + } + return enc_session_key; +} + +std::vector RsaPublicKey::EncryptSessionKey( + const std::string& session_key) const { + if (session_key.empty()) { + LOGE("Session key is empty"); + return std::vector(); + } + size_t enc_session_key_size = static_cast(RSA_size(key_)); + std::vector enc_session_key(enc_session_key_size); + const OEMCryptoResult res = EncryptSessionKey( + reinterpret_cast(session_key.data()), session_key.size(), + enc_session_key.data(), &enc_session_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to encrypt session key: result = %d", static_cast(res)); + enc_session_key.clear(); + } else { + enc_session_key.resize(enc_session_key_size); + } + return enc_session_key; +} + +OEMCryptoResult RsaPublicKey::EncryptEncryptionKey( + const uint8_t* encryption_key, size_t encryption_key_size, + uint8_t* enc_encryption_key, size_t* enc_encryption_key_size) const { + if (encryption_key == nullptr) { + LOGE("Encryption key is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (encryption_key_size != kEncryptionKeySize) { + LOGE("Unexpected encryption key size: expected = %zu, actual = %zu", + kEncryptionKeySize, encryption_key_size); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (enc_encryption_key_size == nullptr) { + LOGE("Output encrypted encryption key size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (enc_encryption_key == nullptr && *enc_encryption_key_size > 0) { + LOGE("Output encrypted encryption key is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return EncryptOaep(encryption_key, encryption_key_size, enc_encryption_key, + enc_encryption_key_size); +} + +std::vector RsaPublicKey::EncryptEncryptionKey( + const std::vector& encryption_key) const { + if (encryption_key.empty()) { + LOGE("Session key is empty"); + return std::vector(); + } + size_t enc_encryption_key_size = static_cast(RSA_size(key_)); + std::vector enc_encryption_key(enc_encryption_key_size); + const OEMCryptoResult res = + EncryptSessionKey(encryption_key.data(), encryption_key.size(), + enc_encryption_key.data(), &enc_encryption_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to encrypt encryption key: result = %d", + static_cast(res)); + enc_encryption_key.clear(); + } else { + enc_encryption_key.resize(enc_encryption_key_size); + } + return enc_encryption_key; +} + +std::vector RsaPublicKey::EncryptEncryptionKey( + const std::string& encryption_key) const { + if (encryption_key.empty()) { + LOGE("Session key is empty"); + return std::vector(); + } + size_t enc_encryption_key_size = static_cast(RSA_size(key_)); + std::vector enc_encryption_key(enc_encryption_key_size); + const OEMCryptoResult res = + EncryptSessionKey(reinterpret_cast(encryption_key.data()), + encryption_key.size(), enc_encryption_key.data(), + &enc_encryption_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to encrypt encryption key: result = %d", + static_cast(res)); + enc_encryption_key.clear(); + } else { + enc_encryption_key.resize(enc_encryption_key_size); + } + return enc_encryption_key; +} + +RsaPublicKey::~RsaPublicKey() { + if (key_ != nullptr) { + RSA_free(key_); + key_ = nullptr; + } + allowed_schemes_ = 0; + field_size_ = kRsaFieldUnknown; +} + +bool RsaPublicKey::InitFromSubjectPublicKeyInfo(const uint8_t* buffer, + size_t length) { + // Step 1: Deserialize SubjectPublicKeyInfo as RSA key. + const uint8_t* tp = buffer; + ScopedRsaKey key(d2i_RSA_PUBKEY(nullptr, &tp, length)); + if (!key) { + LOGE("Failed to parse key"); + return false; + } + // Step 2: Verify key. + const int bits = RSA_bits(key.get()); + field_size_ = RealBitSizeToFieldSize(bits); + if (field_size_ == kRsaFieldUnknown) { + LOGE("Unsupported RSA key size: bits = %d", bits); + return false; + } + allowed_schemes_ = kSign_RSASSA_PSS; + key_ = key.release(); + return true; +} + +bool RsaPublicKey::InitFromPrivateKeyInfo(const uint8_t* buffer, + size_t length) { + ScopedRsaKey private_key; + bool explicit_schemes = false; + if (!ParseRsaPrivateKeyInfo(buffer, length, &private_key, &allowed_schemes_, + &explicit_schemes, &field_size_)) { + return false; + } + // Need to strip the private key information. + return InitFromSslHandle(private_key.get(), allowed_schemes_); +} + +bool RsaPublicKey::InitFromPrivateKey(const RsaPrivateKey& private_key) { + return InitFromSslHandle(private_key.GetRsaKey(), + private_key.allowed_schemes()); +} + +bool RsaPublicKey::InitFromSslHandle(const RSA* rsa_handle, + uint32_t allowed_schemes) { + ScopedRsaKey key(RSA_new()); + if (!key) { + LOGE("Failed to allocate key"); + return false; + } + const BIGNUM* n = nullptr; + const BIGNUM* e = nullptr; + const BIGNUM* d = nullptr; + RSA_get0_key(rsa_handle, &n, &e, &d); + if (n == nullptr || e == nullptr) { + LOGE("Failed to get RSA private key components"); + return false; + } + ScopedBigNum dub_n(BN_dup(n)); + ScopedBigNum dub_e(BN_dup(e)); + if (!dub_n || !dub_e) { + LOGE("Failed to duplicate RSA public key components"); + return false; + } + if (!RSA_set0_key(key.get(), dub_n.get(), dub_e.get(), nullptr)) { + LOGE("Failed to RSA public key components"); + return false; + } + // Ownership has transferred to the RSA key. + dub_n.release(); + dub_e.release(); + + key_ = key.release(); + allowed_schemes_ = allowed_schemes; + const int bits = RSA_bits(rsa_handle); + field_size_ = RealBitSizeToFieldSize(bits); + if (field_size_ == kRsaFieldUnknown) { + LOGE("Unsupported RSA key size: bits = %d", bits); + return false; + } + return true; +} + +OEMCryptoResult RsaPublicKey::VerifySignaturePss( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length) const { + // Step 0: Ensure the signature algorithm is supported by key. + if (!(allowed_schemes_ & kSign_RSASSA_PSS)) { + LOGE("RSA key cannot verify using PSS"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + // Step 1: Create a high-level key from RSA key. + ScopedEvpPkey pkey(EVP_PKEY_new()); + if (!pkey) { + LOGE("Failed to allocate PKEY"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!EVP_PKEY_set1_RSA(pkey.get(), key_)) { + LOGE("Failed to set PKEY RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2a: Setup a EVP MD CTX for PSS Verification. + ScopedEvpMdCtx md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + LOGE("Failed to allocate MD CTX"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + EVP_PKEY_CTX* pkey_ctx = nullptr; // Ownership is maintained by |md_ctx| + int res = EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha1(), nullptr, + pkey.get()); + if (res != 1) { + LOGE("Failed to initialize MD CTX for verification"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (pkey_ctx == nullptr) { + LOGE("PKEY CTX is unexpectedly null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2b: Configure OEMCrypto RSASSA-PSS options. + res = EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING); + if (res != 1) { + LOGE("Failed to set PSS padding"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, kPssSaltLength); + if (res != 1) { + LOGE("Failed to set PSS salt length"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha1()); + if (res != 1) { + LOGE("Failed to set PSS MGF1 MD"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Digest message. + if (message_length > 0) { + res = EVP_DigestVerifyUpdate(md_ctx.get(), message, message_length); + if (res != 1) { + LOGE("Failed to update MD"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + // Step 4: Verify message. + res = EVP_DigestVerifyFinal(md_ctx.get(), signature, signature_length); + if (res < 0) { + LOGE("Failed to perform RSASSA-PSS-VERIFY"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return res ? OEMCrypto_SUCCESS : OEMCrypto_ERROR_SIGNATURE_FAILURE; +} + +OEMCryptoResult RsaPublicKey::VerifySignaturePkcs1Cast( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length) const { + // Step 0: Ensure the signature algorithm is supported by key. + if (!(allowed_schemes_ & kSign_PKCS1_Block1)) { + LOGE("RSA key cannot verify using PKCS1"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + if (message_length > kRsaPkcs1CastMaxMessageSize) { + LOGE("Message is too large for CAST PKCS1 signature: size = %zu", + message_length); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + // Step 1: Convert encrypted blob into digest. + std::vector digest(RSA_size(key_)); + const int res = + RSA_public_decrypt(static_cast(signature_length), signature, + digest.data(), key_, RSA_PKCS1_PADDING); + if (res <= 0) { + LOGE("Failed to perform public decryption"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Compare digests. + const size_t digest_size = static_cast(res); + if (digest_size != message_length) { + LOGD("Digest size does not match"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + digest.resize(digest_size); + for (size_t i = 0; i < digest_size; i++) { + if (message[i] != digest[i]) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult RsaPublicKey::EncryptOaep(const uint8_t* message, + size_t message_size, + uint8_t* enc_message, + size_t* enc_message_length) const { + // Step 1: Encrypt using RSAES-OAEP. + std::vector encrypt_buffer(RSA_size(key_)); + const int enc_res = + RSA_public_encrypt(static_cast(message_size), message, + encrypt_buffer.data(), key_, RSA_PKCS1_OAEP_PADDING); + if (enc_res < 0) { + LOGE("Failed to perform RSAES-OAEP encrypt"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Copy encrypted data key. + const size_t enc_size = static_cast(enc_res); + if (*enc_message_length < enc_size) { + *enc_message_length = enc_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *enc_message_length = enc_size; + memcpy(enc_message, encrypt_buffer.data(), enc_size); + return OEMCrypto_SUCCESS; +} + +// ===== ===== ===== RSA Private Key ===== ===== ===== + +// static +std::unique_ptr RsaPrivateKey::New(RsaFieldSize field_size) { + std::unique_ptr key(new RsaPrivateKey()); + if (!key->InitFromFieldSize(field_size)) { + LOGE("Failed to initialize private key from field size"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPrivateKey::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 key(new RsaPrivateKey()); + if (!key->InitFromPrivateKeyInfo(buffer, length)) { + LOGE("Failed to initialize private key from PrivateKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPrivateKey::Load(const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided private key buffer is empty"); + return std::unique_ptr(); + } + return Load(reinterpret_cast(buffer.data()), buffer.size()); +} + +// static +std::unique_ptr RsaPrivateKey::Load( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided private key buffer is empty"); + return std::unique_ptr(); + } + return Load(buffer.data(), buffer.size()); +} + +std::unique_ptr RsaPrivateKey::MakePublicKey() const { + return RsaPublicKey::New(*this); +} + +bool RsaPrivateKey::IsMatchingPublicKey(const RsaPublicKey& public_key) const { + return RsaKeysAreMatchingPair(public_key.GetRsaKey(), GetRsaKey()); +} + +OEMCryptoResult RsaPrivateKey::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 RSA key to EVP. + ScopedEvpPkey pkey(EVP_PKEY_new()); + if (!pkey) { + LOGE("Failed to allocate EVP"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!EVP_PKEY_set1_RSA(pkey.get(), key_)) { + LOGE("Failed to set EVP RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Convert RSA EVP to PKCS8 format. + ScopedPrivateKeyInfo priv_info(EVP_PKEY2PKCS8(pkey.get())); + if (!priv_info) { + LOGE("Failed to convert RSA 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 RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), priv_info.get())) { + LOGE("Failed to serialize RSA 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 RSA 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(key_size) + (explicit_schemes_ ? 8 : 0); + if (*buffer_size < required_size) { + *buffer_size = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *buffer_size = required_size; + if (explicit_schemes_) { + memcpy(buffer, "SIGN", 4); + const uint32_t allowed_schemes = htonl(allowed_schemes_); + memcpy(&buffer[4], &allowed_schemes, 4); + memcpy(&buffer[8], key_ptr, required_size - 8); + } else { + memcpy(buffer, key_ptr, required_size); + } + return OEMCrypto_SUCCESS; +} + +std::vector RsaPrivateKey::Serialize() const { + size_t key_size = kPrivateKeySize; + std::vector key_data(key_size, 0); + OEMCryptoResult res = Serialize(key_data.data(), &key_size); + if (res == OEMCrypto_ERROR_SHORT_BUFFER) { + LOGD( + "Actual RSA private key size is larger than expected: " + "expected = %zu, actual = %zu", + kPrivateKeySize, key_size); + key_data.resize(key_size); + res = Serialize(key_data.data(), &key_size); + } + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to serialize private key: result = %d", static_cast(res)); + key_data.clear(); + } else { + key_data.resize(key_size); + } + return key_data; +} + +OEMCryptoResult RsaPrivateKey::GenerateSignature( + const uint8_t* message, size_t message_length, + RsaSignatureAlgorithm algorithm, 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; + } + switch (algorithm) { + case kRsaPssDefault: + return GenerateSignaturePss(message, message_length, signature, + signature_length); + case kRsaPkcs1Cast: + return GenerateSignaturePkcs1Cast(message, message_length, signature, + signature_length); + } + LOGE("Unknown RSA signature algorithm: %d", static_cast(algorithm)); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +std::vector RsaPrivateKey::GenerateSignature( + const std::string& message, RsaSignatureAlgorithm algorithm) const { + size_t signature_size = SignatureSize(); + std::vector signature(signature_size); + const OEMCryptoResult res = GenerateSignature( + reinterpret_cast(message.data()), message.size(), + algorithm, signature.data(), &signature_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to generate signature: result = %d", static_cast(res)); + signature.clear(); + } else { + signature.resize(signature_size); + } + return signature; +} + +std::vector RsaPrivateKey::GenerateSignature( + const std::vector& message, + RsaSignatureAlgorithm algorithm) const { + size_t signature_size = SignatureSize(); + std::vector signature(signature_size, 0); + const OEMCryptoResult res = + GenerateSignature(message.data(), message.size(), algorithm, + signature.data(), &signature_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to generate signature: result = %d", static_cast(res)); + signature.clear(); + } else { + signature.resize(signature_size); + } + return signature; +} + +size_t RsaPrivateKey::SignatureSize() const { return RSA_size(key_); } + +OEMCryptoResult RsaPrivateKey::DecryptSessionKey( + const uint8_t* enc_session_key, size_t enc_session_key_size, + uint8_t* session_key, size_t* session_key_size) const { + if (enc_session_key == nullptr || enc_session_key_size == 0) { + LOGE("Encrypted session key is %s", enc_session_key ? "empty" : "null"); + 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 < kRsaSessionKeySize) { + *session_key_size = kRsaSessionKeySize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + const OEMCryptoResult result = DecryptOaep( + enc_session_key, enc_session_key_size, session_key, kRsaSessionKeySize); + if (result == OEMCrypto_SUCCESS) { + *session_key_size = kRsaSessionKeySize; + } else { + LOGE("Failed to decrypt session key"); + } + return result; +} + +std::vector RsaPrivateKey::DecryptSessionKey( + const std::vector& enc_session_key) const { + size_t session_key_size = kRsaSessionKeySize; + std::vector session_key(session_key_size, 0); + const OEMCryptoResult res = + DecryptSessionKey(enc_session_key.data(), enc_session_key.size(), + session_key.data(), &session_key_size); + if (res != OEMCrypto_SUCCESS) { + session_key.clear(); + } else { + session_key.resize(session_key_size); + } + return session_key; +} + +std::vector RsaPrivateKey::DecryptSessionKey( + const std::string& enc_session_key) const { + size_t session_key_size = kRsaSessionKeySize; + std::vector session_key(session_key_size, 0); + const OEMCryptoResult res = DecryptSessionKey( + reinterpret_cast(enc_session_key.data()), + enc_session_key.size(), session_key.data(), &session_key_size); + if (res != OEMCrypto_SUCCESS) { + session_key.clear(); + } else { + session_key.resize(session_key_size); + } + return session_key; +} + +size_t RsaPrivateKey::SessionKeyLength() const { return kRsaSessionKeySize; } + +OEMCryptoResult RsaPrivateKey::DecryptEncryptionKey( + const uint8_t* enc_encryption_key, size_t enc_encryption_key_size, + uint8_t* encryption_key, size_t* encryption_key_size) const { + if (enc_encryption_key == nullptr || enc_encryption_key_size == 0) { + LOGE("Encrypted encryption key is %s", + enc_encryption_key ? "empty" : "null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (encryption_key_size == nullptr) { + LOGE("Output encryption key size buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (encryption_key == nullptr && *encryption_key_size > 0) { + LOGE("Output encryption key buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (*encryption_key_size < kEncryptionKeySize) { + *encryption_key_size = kEncryptionKeySize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + const OEMCryptoResult result = + DecryptOaep(enc_encryption_key, enc_encryption_key_size, encryption_key, + kEncryptionKeySize); + if (result == OEMCrypto_SUCCESS) { + *encryption_key_size = kEncryptionKeySize; + } else { + LOGE("Failed to decrypt encryption key"); + } + return result; +} + +std::vector RsaPrivateKey::DecryptEncryptionKey( + const std::vector& enc_encryption_key) const { + size_t encryption_key_size = kEncryptionKeySize; + std::vector encryption_key(encryption_key_size, 0); + const OEMCryptoResult res = + DecryptEncryptionKey(enc_encryption_key.data(), enc_encryption_key.size(), + encryption_key.data(), &encryption_key_size); + if (res != OEMCrypto_SUCCESS) { + encryption_key.clear(); + } else { + encryption_key.resize(encryption_key_size); + } + return encryption_key; +} + +std::vector RsaPrivateKey::DecryptEncryptionKey( + const std::string& enc_encryption_key) const { + size_t encryption_key_size = kEncryptionKeySize; + std::vector encryption_key(encryption_key_size, 0); + const OEMCryptoResult res = DecryptEncryptionKey( + reinterpret_cast(enc_encryption_key.data()), + enc_encryption_key.size(), encryption_key.data(), &encryption_key_size); + if (res != OEMCrypto_SUCCESS) { + encryption_key.clear(); + } else { + encryption_key.resize(encryption_key_size); + } + return encryption_key; +} + +RsaPrivateKey::~RsaPrivateKey() { + if (key_ != nullptr) { + RSA_free(key_); + key_ = nullptr; + } + allowed_schemes_ = 0; + explicit_schemes_ = false; + field_size_ = kRsaFieldUnknown; +} + +bool RsaPrivateKey::InitFromPrivateKeyInfo(const uint8_t* buffer, + size_t length) { + ScopedRsaKey key; + if (!ParseRsaPrivateKeyInfo(buffer, length, &key, &allowed_schemes_, + &explicit_schemes_, &field_size_)) { + return false; + } + key_ = key.release(); + return true; +} + +bool RsaPrivateKey::InitFromFieldSize(RsaFieldSize field_size) { + if (field_size != kRsa2048Bit && field_size != kRsa3072Bit) { + LOGE("Unsupported RSA field size: bits = %d", static_cast(field_size)); + return false; + } + // Step 1: Create exponent. + ScopedBigNum exp(BN_new()); + if (!exp) { + LOGE("Failed to allocate RSA exponent"); + return false; + } + if (!BN_set_word(exp.get(), RSA_F4)) { + LOGE("Failed to set RSA exponent"); + return false; + } + // Step 2: Generate RSA key. + ScopedRsaKey key(RSA_new()); + if (!key) { + LOGE("Failed to allocate RSA key"); + return false; + } + if (!RSA_generate_key_ex(key.get(), static_cast(field_size), exp.get(), + nullptr)) { + LOGE("Failed to generate RSA key"); + return false; + } + key_ = key.release(); + allowed_schemes_ = kSign_RSASSA_PSS; + field_size_ = field_size; + return true; +} + +OEMCryptoResult RsaPrivateKey::GenerateSignaturePss( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length) const { + // Step 0: Ensure the signature algorithm is supported by key. + if (!(allowed_schemes_ & kSign_RSASSA_PSS)) { + LOGE("RSA key cannot sign using PSS"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + // Step 1: Create a high-level key from RSA key. + ScopedEvpPkey pkey(EVP_PKEY_new()); + if (!pkey) { + LOGE("Failed to allocate PKEY"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!EVP_PKEY_set1_RSA(pkey.get(), key_)) { + LOGE("Failed to set PKEY RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2a: Setup a EVP MD CTX for PSS Signature Generation. + ScopedEvpMdCtx md_ctx(EVP_MD_CTX_new()); + if (!md_ctx) { + LOGE("Failed to allocate MD CTX"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + EVP_PKEY_CTX* pkey_ctx = nullptr; // Ownership is maintained by |md_ctx| + int res = EVP_DigestSignInit(md_ctx.get(), &pkey_ctx, EVP_sha1(), nullptr, + pkey.get()); + if (res != 1) { + LOGE("Failed to initialize MD CTX for signing"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (pkey_ctx == nullptr) { + LOGE("PKEY CTX is unexpectedly null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2b: Configure OEMCrypto RSASSA-PSS options. + res = EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING); + if (res != 1) { + LOGE("Failed to set PSS padding"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, kPssSaltLength); + if (res != 1) { + LOGE("Failed to set PSS salt length"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha1()); + if (res != 1) { + LOGE("Failed to set PSS MGF1 MD"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Digest message. + if (message_length > 0) { + res = EVP_DigestSignUpdate(md_ctx.get(), message, message_length); + if (res != 1) { + LOGE("Failed to update MD"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + // Step 4a: Determine size of signature. + size_t actual_signature_length = 0; + res = EVP_DigestSignFinal(md_ctx.get(), nullptr, &actual_signature_length); + if (res != 1) { + LOGE("Failed to determine signature length"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (*signature_length < actual_signature_length) { + *signature_length = actual_signature_length; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + // Step 4b: Generate signature. + res = EVP_DigestSignFinal(md_ctx.get(), signature, signature_length); + if (res != 1) { + LOGE("Failed to perform RSASSA-PSS-SIGN"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult RsaPrivateKey::GenerateSignaturePkcs1Cast( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length) const { + // Step 0: Ensure the signature algorithm is supported by key. + if (!(allowed_schemes_ & kSign_PKCS1_Block1)) { + LOGE("RSA key cannot sign PKCS1"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + if (message_length > kRsaPkcs1CastMaxMessageSize) { + LOGE("Message is too large for CAST PKCS1 signature: size = %zu", + message_length); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + // Step 1: Ensure signature buffer is large enough. + const size_t expected_signature_size = static_cast(RSA_size(key_)); + if (*signature_length < expected_signature_size) { + *signature_length = expected_signature_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + // Step 2: Encrypt with PKCS1 padding. + const int enc_res = + RSA_private_encrypt(static_cast(message_length), message, signature, + key_, RSA_PKCS1_PADDING); + if (enc_res < 0) { + LOGE("Failed to perform private encryption"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *signature_length = expected_signature_size; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult RsaPrivateKey::DecryptOaep( + const uint8_t* enc_message, size_t enc_message_size, uint8_t* message, + size_t expected_message_length) const { + // Step 1: Decrypt using RSAES-OAEP. + std::vector decrypt_buffer(RSA_size(key_)); + const int dec_res = + RSA_private_decrypt(static_cast(enc_message_size), enc_message, + decrypt_buffer.data(), key_, RSA_PKCS1_OAEP_PADDING); + if (dec_res < 0) { + LOGE("Failed to perform RSAES-OAEP decrypt"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (static_cast(dec_res) != expected_message_length) { + LOGE("Unexpected key size: expected = %zu, actual = %d", + expected_message_length, dec_res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Copy decrypted data key. + memcpy(message, decrypt_buffer.data(), expected_message_length); + return OEMCrypto_SUCCESS; +} +} // namespace util +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/util/src/wvcrc.cpp b/libwvdrmengine/oemcrypto/util/src/wvcrc.cpp new file mode 100644 index 00000000..097ca707 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/wvcrc.cpp @@ -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