blob: 316000294388ff3248e253dd36c8cf9ae5a3fdfa [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/cryptorecovery/recovery_crypto.h"
#include <algorithm>
#include <utility>
#include <vector>
#include <base/logging.h>
#include <base/stl_util.h>
#include <brillo/secure_blob.h>
#include "cryptohome/crypto/aes.h"
#include "cryptohome/crypto/big_num_util.h"
#include "cryptohome/crypto/ecdh_hkdf.h"
#include "cryptohome/crypto/elliptic_curve.h"
#include "cryptohome/crypto/error_util.h"
#include "cryptohome/crypto/secure_blob_util.h"
#include "cryptohome/cryptohome_common.h"
#include "cryptohome/cryptorecovery/recovery_crypto_hsm_cbor_serialization.h"
#include "cryptohome/cryptorecovery/recovery_crypto_util.h"
namespace cryptohome {
namespace cryptorecovery {
namespace {
brillo::SecureBlob GetRecoveryKeyHkdfInfo() {
return brillo::SecureBlob("CryptoHome Wrapping Key");
}
brillo::SecureBlob GetMediatorShareHkdfInfo() {
return brillo::SecureBlob(RecoveryCrypto::kMediatorShareHkdfInfoValue);
}
brillo::SecureBlob GetRequestPayloadPlainTextHkdfInfo() {
return brillo::SecureBlob(
RecoveryCrypto::kRequestPayloadPlainTextHkdfInfoValue);
}
brillo::SecureBlob GetResponsePayloadPlainTextHkdfInfo() {
return brillo::SecureBlob(
RecoveryCrypto::kResponsePayloadPlainTextHkdfInfoValue);
}
} // namespace
const char RecoveryCrypto::kMediatorShareHkdfInfoValue[] = "HSM-Payload Key";
const char RecoveryCrypto::kRequestPayloadPlainTextHkdfInfoValue[] =
"REQUEST-Payload Key";
const char RecoveryCrypto::kResponsePayloadPlainTextHkdfInfoValue[] =
"RESPONSE-Payload Key";
const EllipticCurve::CurveType RecoveryCrypto::kCurve =
EllipticCurve::CurveType::kPrime256;
const HkdfHash RecoveryCrypto::kHkdfHash = HkdfHash::kSha256;
const unsigned int RecoveryCrypto::kHkdfSaltLength = 32;
namespace {
// Cryptographic operations for cryptohome recovery performed on CPU (software
// emulation).
class RecoveryCryptoImpl : public RecoveryCrypto {
public:
explicit RecoveryCryptoImpl(EllipticCurve ec);
~RecoveryCryptoImpl() override;
bool GenerateRecoveryRequest(
const HsmPayload& hsm_payload,
const brillo::SecureBlob& request_meta_data,
const brillo::SecureBlob& channel_priv_key,
const brillo::SecureBlob& channel_pub_key,
const brillo::SecureBlob& epoch_pub_key,
brillo::SecureBlob* recovery_request,
brillo::SecureBlob* ephemeral_pub_key) const override;
bool GenerateHsmPayload(const brillo::SecureBlob& mediator_pub_key,
const brillo::SecureBlob& rsa_pub_key,
const brillo::SecureBlob& onboarding_metadata,
HsmPayload* hsm_payload,
brillo::SecureBlob* destination_share,
brillo::SecureBlob* recovery_key,
brillo::SecureBlob* channel_pub_key,
brillo::SecureBlob* channel_priv_key) const override;
bool RecoverDestination(const brillo::SecureBlob& dealer_pub_key,
const brillo::SecureBlob& destination_share,
const brillo::SecureBlob& ephemeral_pub_key,
const brillo::SecureBlob& mediated_publisher_pub_key,
brillo::SecureBlob* destination_dh) const override;
bool DecryptResponsePayload(
const brillo::SecureBlob& channel_priv_key,
const brillo::SecureBlob& epoch_pub_key,
const brillo::SecureBlob& recovery_response_cbor,
HsmResponsePlainText* response_plain_text) const override;
private:
// Encrypts mediator share and stores as `encrypted_ms` with
// embedded ephemeral public key, AES-GCM tag and iv. Returns false if error
// occurred.
bool EncryptMediatorShare(const brillo::SecureBlob& mediator_pub_key,
const brillo::SecureBlob& mediator_share,
EncryptedMediatorShare* encrypted_ms,
BN_CTX* context) const;
bool GenerateRecoveryKey(const crypto::ScopedEC_POINT& recovery_pub_point,
const crypto::ScopedEC_KEY& dealer_key_pair,
brillo::SecureBlob* recovery_key) const;
// Generate ephemeral public and inverse public keys {G*x, G*-x}
bool GenerateEphemeralKey(brillo::SecureBlob* ephemeral_pub_key,
brillo::SecureBlob* ephemeral_inv_pub_key) const;
bool GenerateHsmAssociatedData(const brillo::SecureBlob& channel_pub_key,
const brillo::SecureBlob& rsa_pub_key,
const brillo::SecureBlob& onboarding_metadata,
brillo::SecureBlob* hsm_associated_data,
brillo::SecureBlob* publisher_priv_key,
brillo::SecureBlob* publisher_pub_key) const;
EllipticCurve ec_;
};
RecoveryCryptoImpl::RecoveryCryptoImpl(EllipticCurve ec) : ec_(std::move(ec)) {}
RecoveryCryptoImpl::~RecoveryCryptoImpl() = default;
bool RecoveryCryptoImpl::EncryptMediatorShare(
const brillo::SecureBlob& mediator_pub_key,
const brillo::SecureBlob& mediator_share,
RecoveryCrypto::EncryptedMediatorShare* encrypted_ms,
BN_CTX* context) const {
brillo::SecureBlob ephemeral_priv_key;
if (!ec_.GenerateKeysAsSecureBlobs(&encrypted_ms->ephemeral_pub_key,
&ephemeral_priv_key, context)) {
LOG(ERROR) << "Failed to generate EC keys";
return false;
}
brillo::SecureBlob aes_gcm_key;
// |hkdf_salt| can be empty here because the input already has a high entropy.
// Bruteforce attacks are not an issue here and as we generate an ephemeral
// key as input to HKDF the output will already be non-deterministic.
if (!GenerateEcdhHkdfSenderKey(ec_, mediator_pub_key,
encrypted_ms->ephemeral_pub_key,
ephemeral_priv_key, GetMediatorShareHkdfInfo(),
/*hkdf_salt=*/brillo::SecureBlob(), kHkdfHash,
kAesGcm256KeySize, &aes_gcm_key)) {
LOG(ERROR) << "Failed to generate ECDH+HKDF sender keys";
return false;
}
// Dispose private key.
ephemeral_priv_key.clear();
if (!AesGcmEncrypt(mediator_share, /*ad=*/base::nullopt, aes_gcm_key,
&encrypted_ms->iv, &encrypted_ms->tag,
&encrypted_ms->encrypted_data)) {
LOG(ERROR) << "Failed to perform AES-GCM encryption";
return false;
}
return true;
}
bool RecoveryCryptoImpl::GenerateRecoveryKey(
const crypto::ScopedEC_POINT& recovery_pub_point,
const crypto::ScopedEC_KEY& dealer_key_pair,
brillo::SecureBlob* recovery_key) const {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return false;
}
const BIGNUM* dealer_priv_key =
EC_KEY_get0_private_key(dealer_key_pair.get());
crypto::ScopedEC_POINT point_dh =
ec_.Multiply(*recovery_pub_point, *dealer_priv_key, context.get());
if (!point_dh) {
LOG(ERROR) << "Failed to perform point multiplication";
return false;
}
// Get point's affine X coordinate.
crypto::ScopedBIGNUM recovery_dh_x =
ec_.GetAffineCoordinateX(*point_dh, context.get());
if (!recovery_dh_x) {
LOG(ERROR) << "Failed to get affine X coordinate of point_dh";
return false;
}
brillo::SecureBlob hkdf_secret;
// Convert X coordinate to fixed-size blob.
if (!BigNumToSecureBlob(*recovery_dh_x, ec_.FieldElementSizeInBytes(),
&hkdf_secret)) {
LOG(ERROR) << "Failed to convert recovery_dh_x BIGNUM to SecureBlob";
return false;
}
const EC_POINT* dealer_pub_point =
EC_KEY_get0_public_key(dealer_key_pair.get());
brillo::SecureBlob dealer_pub_key;
if (!ec_.PointToSecureBlob(*dealer_pub_point, &dealer_pub_key,
context.get())) {
LOG(ERROR) << "Failed to convert dealer_pub_key to a SecureBlob";
return false;
}
if (!ComputeHkdfWithInfoSuffix(hkdf_secret, GetRecoveryKeyHkdfInfo(),
dealer_pub_key, /*salt=*/brillo::SecureBlob(),
HkdfHash::kSha256, /*result_len=*/0,
recovery_key)) {
LOG(ERROR) << "Failed to compute HKDF of recovery_dh";
return false;
}
return true;
}
bool RecoveryCryptoImpl::GenerateEphemeralKey(
brillo::SecureBlob* ephemeral_pub_key,
brillo::SecureBlob* ephemeral_inv_pub_key) const {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return false;
}
// Generate ephemeral key pair {`ephemeral_secret`, `ephemeral_pub_key`} ({x,
// G*x}), and the inverse public key G*-x.
crypto::ScopedBIGNUM ephemeral_priv_key_bn =
ec_.RandomNonZeroScalar(context.get());
if (!ephemeral_priv_key_bn) {
LOG(ERROR) << "Failed to generate ephemeral_priv_key_bn";
return false;
}
crypto::ScopedEC_POINT ephemeral_pub_point =
ec_.MultiplyWithGenerator(*ephemeral_priv_key_bn, context.get());
if (!ephemeral_pub_point) {
LOG(ERROR) << "Failed to perform MultiplyWithGenerator operation for "
"ephemeral_priv_key_bn";
return false;
}
if (!ec_.PointToSecureBlob(*ephemeral_pub_point, ephemeral_pub_key,
context.get())) {
LOG(ERROR) << "Failed to convert ephemeral_pub_point to a SecureBlob";
return false;
}
if (!ec_.InvertPoint(ephemeral_pub_point.get(), context.get())) {
LOG(ERROR) << "Failed to invert the ephemeral_pub_point";
return false;
}
if (!ec_.PointToSecureBlob(*ephemeral_pub_point, ephemeral_inv_pub_key,
context.get())) {
LOG(ERROR)
<< "Failed to convert inverse ephemeral_pub_point to a SecureBlob";
return false;
}
return true;
}
bool RecoveryCryptoImpl::GenerateRecoveryRequest(
const HsmPayload& hsm_payload,
const brillo::SecureBlob& request_meta_data,
const brillo::SecureBlob& channel_priv_key,
const brillo::SecureBlob& channel_pub_key,
const brillo::SecureBlob& epoch_pub_key,
brillo::SecureBlob* recovery_request,
brillo::SecureBlob* ephemeral_pub_key) const {
RequestPayload request_payload;
RecoveryRequestAssociatedData request_ad;
request_ad.hsm_payload = hsm_payload;
request_ad.request_meta_data = request_meta_data;
request_ad.epoch_pub_key = epoch_pub_key;
request_ad.request_payload_salt = CreateSecureRandomBlob(kHkdfSaltLength);
if (!SerializeRecoveryRequestAssociatedDataToCbor(
request_ad, &request_payload.associated_data)) {
LOG(ERROR) << "Failed to generate associated data cbor";
return false;
}
brillo::SecureBlob aes_gcm_key;
// The static nature of `channel_pub_key` (G*s) and `epoch_pub_key` (G*r)
// requires the need to utilize a randomized salt value in the HKDF
// computation.
if (!GenerateEcdhHkdfSenderKey(
ec_, epoch_pub_key, channel_pub_key, channel_priv_key,
GetRequestPayloadPlainTextHkdfInfo(), request_ad.request_payload_salt,
kHkdfHash, kAesGcm256KeySize, &aes_gcm_key)) {
LOG(ERROR) << "Failed to generate ECDH+HKDF sender keys";
return false;
}
brillo::SecureBlob ephemeral_inverse_pub_key;
if (!GenerateEphemeralKey(ephemeral_pub_key, &ephemeral_inverse_pub_key)) {
LOG(ERROR) << "Failed to generate Ephemeral keys";
return false;
}
brillo::SecureBlob plain_text_cbor;
RecoveryRequestPlainText plain_text;
plain_text.ephemeral_pub_inv_key = ephemeral_inverse_pub_key;
if (!SerializeRecoveryRequestPlainTextToCbor(plain_text, &plain_text_cbor)) {
LOG(ERROR) << "Failed to generate Recovery Request plain text cbor";
return false;
}
if (!AesGcmEncrypt(plain_text_cbor, request_payload.associated_data,
aes_gcm_key, &request_payload.iv, &request_payload.tag,
&request_payload.cipher_text)) {
LOG(ERROR) << "Failed to perform AES-GCM encryption of plain_text_cbor";
return false;
}
RecoveryRequest request;
request.request_payload = std::move(request_payload);
if (!SerializeRecoveryRequestToCbor(request, recovery_request)) {
LOG(ERROR) << "Failed to serialize Recovery Request to CBOR";
return false;
}
return true;
}
bool RecoveryCryptoImpl::GenerateHsmPayload(
const brillo::SecureBlob& mediator_pub_key,
const brillo::SecureBlob& rsa_pub_key,
const brillo::SecureBlob& onboarding_metadata,
HsmPayload* hsm_payload,
brillo::SecureBlob* destination_share,
brillo::SecureBlob* recovery_key,
brillo::SecureBlob* channel_pub_key,
brillo::SecureBlob* channel_priv_key) const {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return false;
}
// Generate dealer key pair.
crypto::ScopedEC_KEY dealer_key_pair = ec_.GenerateKey(context.get());
if (!dealer_key_pair) {
LOG(ERROR) << "Failed to generate dealer key pair.";
return false;
}
// Generate two shares and a secret equal to the sum.
// Loop until the sum of two shares is non-zero (modulo order).
crypto::ScopedBIGNUM secret;
crypto::ScopedBIGNUM destination_share_bn =
ec_.RandomNonZeroScalar(context.get());
if (!destination_share_bn) {
LOG(ERROR) << "Failed to generate secret";
return false;
}
crypto::ScopedBIGNUM mediator_share_bn;
do {
mediator_share_bn = ec_.RandomNonZeroScalar(context.get());
if (!mediator_share_bn) {
LOG(ERROR) << "Failed to generate secret";
return false;
}
secret =
ec_.ModAdd(*mediator_share_bn, *destination_share_bn, context.get());
if (!secret) {
LOG(ERROR) << "Failed to perform modulo addition";
return false;
}
} while (BN_is_zero(secret.get()));
if (!BigNumToSecureBlob(*destination_share_bn, ec_.ScalarSizeInBytes(),
destination_share)) {
LOG(ERROR) << "Failed to convert BIGNUM to SecureBlob";
return false;
}
crypto::ScopedEC_POINT recovery_pub_point =
ec_.MultiplyWithGenerator(*secret, context.get());
if (!recovery_pub_point) {
LOG(ERROR) << "Failed to perform MultiplyWithGenerator operation";
return false;
}
// Generate channel key pair.
// TODO(b/194678588): channel private key should be protected via TPM.
crypto::ScopedEC_KEY channel_key_pair = ec_.GenerateKey(context.get());
if (!channel_key_pair) {
LOG(ERROR) << "Failed to generate channel key pair.";
return false;
}
const EC_POINT* channel_pub_point =
EC_KEY_get0_public_key(channel_key_pair.get());
if (!ec_.PointToSecureBlob(*channel_pub_point, channel_pub_key,
context.get())) {
LOG(ERROR) << "Failed to convert channel_pub_key to a SecureBlob";
return false;
}
const BIGNUM* channel_priv_key_bn =
EC_KEY_get0_private_key(channel_key_pair.get());
if (!BigNumToSecureBlob(*channel_priv_key_bn, ec_.ScalarSizeInBytes(),
channel_priv_key)) {
LOG(ERROR) << "Failed to convert channel_priv_key_bn to a SecureBlob";
return false;
}
// Construct associated data for HSM payload: AD = CBOR({publisher_pub_key,
// channel_pub_key, rsa_pub_key, onboarding_metadata}).
brillo::SecureBlob publisher_priv_key;
brillo::SecureBlob publisher_pub_key;
if (!GenerateHsmAssociatedData(*channel_pub_key, rsa_pub_key,
onboarding_metadata,
&hsm_payload->associated_data,
&publisher_priv_key, &publisher_pub_key)) {
LOG(ERROR) << "Failed to generate associated data cbor";
return false;
}
// Construct plain text for HSM payload PT = CBOR({dealer_pub_key,
// mediator_share, kav}).
const EC_POINT* dealer_pub_point =
EC_KEY_get0_public_key(dealer_key_pair.get());
brillo::SecureBlob dealer_pub_key;
if (!ec_.PointToSecureBlob(*dealer_pub_point, &dealer_pub_key,
context.get())) {
LOG(ERROR) << "Failed to convert dealer_pub_key to a SecureBlob";
return false;
}
brillo::SecureBlob mediator_share;
if (!BigNumToSecureBlob(*mediator_share_bn, ec_.ScalarSizeInBytes(),
&mediator_share)) {
LOG(ERROR) << "Failed to convert mediator_share to a SecureBlob";
return false;
}
// TODO(mslus): in the initial version kav will be empty (as it should for
// TPM 2.0). In the next iteration we will generate kav if a non-empty value
// of `rsa_pub_key` is provided.
brillo::SecureBlob plain_text_cbor;
HsmPlainText hsm_plain_text;
hsm_plain_text.mediator_share = mediator_share;
hsm_plain_text.dealer_pub_key = dealer_pub_key;
if (!SerializeHsmPlainTextToCbor(hsm_plain_text, &plain_text_cbor)) {
LOG(ERROR) << "Failed to generate HSM plain text cbor";
return false;
}
brillo::SecureBlob aes_gcm_key;
// |hkdf_salt| can be empty here because the input already has a high entropy.
// Bruteforce attacks are not an issue here and as we generate an ephemeral
// key as input to HKDF the output will already be non-deterministic.
if (!GenerateEcdhHkdfSenderKey(ec_, mediator_pub_key, publisher_pub_key,
publisher_priv_key, GetMediatorShareHkdfInfo(),
/*hkdf_salt=*/brillo::SecureBlob(), kHkdfHash,
kAesGcm256KeySize, &aes_gcm_key)) {
LOG(ERROR) << "Failed to generate ECDH+HKDF sender keys";
return false;
}
if (!AesGcmEncrypt(plain_text_cbor, hsm_payload->associated_data, aes_gcm_key,
&hsm_payload->iv, &hsm_payload->tag,
&hsm_payload->cipher_text)) {
LOG(ERROR) << "Failed to perform AES-GCM encryption";
return false;
}
// Cleanup: all intermediate secrets must be securely disposed at the end of
// HSM payload generation.
aes_gcm_key.clear();
plain_text_cbor.clear();
mediator_share.clear();
dealer_pub_key.clear();
publisher_pub_key.clear();
publisher_priv_key.clear();
GenerateRecoveryKey(recovery_pub_point, dealer_key_pair, recovery_key);
return true;
}
bool RecoveryCryptoImpl::RecoverDestination(
const brillo::SecureBlob& dealer_pub_key,
const brillo::SecureBlob& destination_share,
const brillo::SecureBlob& ephemeral_pub_key,
const brillo::SecureBlob& mediated_publisher_pub_key,
brillo::SecureBlob* destination_recovery_key) const {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return false;
}
crypto::ScopedBIGNUM destination_share_bn =
SecureBlobToBigNum(destination_share);
if (!destination_share_bn) {
LOG(ERROR) << "Failed to convert SecureBlob to BIGNUM";
return false;
}
crypto::ScopedEC_POINT dealer_pub_point =
ec_.SecureBlobToPoint(dealer_pub_key, context.get());
if (!dealer_pub_point) {
LOG(ERROR) << "Failed to convert SecureBlob to EC_POINT";
return false;
}
crypto::ScopedEC_POINT mediated_point =
ec_.SecureBlobToPoint(mediated_publisher_pub_key, context.get());
if (!mediated_point) {
LOG(ERROR) << "Failed to convert SecureBlob to EC_POINT";
return false;
}
// Performs addition of mediated_point and ephemeral_pub_point.
crypto::ScopedEC_POINT ephemeral_pub_point =
ec_.SecureBlobToPoint(ephemeral_pub_key, context.get());
if (!ephemeral_pub_point) {
LOG(ERROR) << "Failed to convert SecureBlob to EC_POINT";
return false;
}
crypto::ScopedEC_POINT mediator_dh =
ec_.Add(*mediated_point, *ephemeral_pub_point, context.get());
if (!mediator_dh) {
LOG(ERROR) << "Failed to add mediated_point and ephemeral_pub_point";
return false;
}
// Performs scalar multiplication of dealer_pub_point and destination_share.
crypto::ScopedEC_POINT point_dh =
ec_.Multiply(*dealer_pub_point, *destination_share_bn, context.get());
if (!point_dh) {
LOG(ERROR) << "Failed to perform scalar multiplication";
return false;
}
crypto::ScopedEC_POINT point_dest =
ec_.Add(*point_dh, *mediator_dh, context.get());
if (!point_dest) {
LOG(ERROR) << "Failed to perform point addition";
return false;
}
// Get point's affine X coordinate.
crypto::ScopedBIGNUM destination_dh_x =
ec_.GetAffineCoordinateX(*point_dest, context.get());
if (!destination_dh_x) {
LOG(ERROR) << "Failed to get affine X coordinate of point_dest";
return false;
}
brillo::SecureBlob hkdf_secret;
// Convert X coordinate to fixed-size blob.
if (!BigNumToSecureBlob(*destination_dh_x, ec_.FieldElementSizeInBytes(),
&hkdf_secret)) {
LOG(ERROR) << "Failed to convert destination_dh_x BIGNUM to SecureBlob";
return false;
}
if (!ComputeHkdfWithInfoSuffix(hkdf_secret, GetRecoveryKeyHkdfInfo(),
dealer_pub_key, /*salt=*/brillo::SecureBlob(),
HkdfHash::kSha256, /*result_len=*/0,
destination_recovery_key)) {
LOG(ERROR) << "Failed to compute HKDF of destination_dh";
return false;
}
return true;
}
bool RecoveryCryptoImpl::DecryptResponsePayload(
const brillo::SecureBlob& channel_priv_key,
const brillo::SecureBlob& epoch_pub_key,
const brillo::SecureBlob& recovery_response_cbor,
HsmResponsePlainText* response_plain_text) const {
RecoveryResponse recovery_response;
if (!DeserializeRecoveryResponseFromCbor(recovery_response_cbor,
&recovery_response)) {
LOG(ERROR) << "Unable to deserialize Recovery Response from CBOR";
return false;
}
HsmResponseAssociatedData response_ad;
if (!DeserializeHsmResponseAssociatedDataFromCbor(
recovery_response.response_payload.associated_data, &response_ad)) {
LOG(ERROR) << "Unable to deserialize Response payload associated data";
return false;
}
brillo::SecureBlob aes_gcm_key;
if (!GenerateEcdhHkdfRecipientKey(ec_, channel_priv_key, epoch_pub_key,
GetResponsePayloadPlainTextHkdfInfo(),
response_ad.response_payload_salt,
RecoveryCrypto::kHkdfHash,
kAesGcm256KeySize, &aes_gcm_key)) {
LOG(ERROR) << "Failed to generate ECDH+HKDF recipient key for mediator "
"share decryption";
return false;
}
brillo::SecureBlob response_plain_text_cbor;
if (!AesGcmDecrypt(recovery_response.response_payload.cipher_text,
recovery_response.response_payload.associated_data,
recovery_response.response_payload.tag, aes_gcm_key,
recovery_response.response_payload.iv,
&response_plain_text_cbor)) {
LOG(ERROR) << "Failed to perform AES-GCM decryption";
return false;
}
if (!DeserializeHsmResponsePlainTextFromCbor(response_plain_text_cbor,
response_plain_text)) {
LOG(ERROR) << "Failed to deserialize Response plain text";
return false;
}
return true;
}
bool RecoveryCryptoImpl::GenerateHsmAssociatedData(
const brillo::SecureBlob& channel_pub_key,
const brillo::SecureBlob& rsa_pub_key,
const brillo::SecureBlob& onboarding_metadata,
brillo::SecureBlob* hsm_associated_data,
brillo::SecureBlob* publisher_priv_key,
brillo::SecureBlob* publisher_pub_key) const {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return false;
}
// Generate publisher key pair.
crypto::ScopedEC_KEY publisher_key_pair = ec_.GenerateKey(context.get());
if (!publisher_key_pair) {
LOG(ERROR) << "Failed to generate publisher key pair.";
return false;
}
// Construct associated data for HSM payload: AD = CBOR({publisher_pub_key,
// channel_pub_key, rsa_pub_key, onboarding_metadata}).
const EC_POINT* publisher_pub_point =
EC_KEY_get0_public_key(publisher_key_pair.get());
if (!ec_.PointToSecureBlob(*publisher_pub_point, publisher_pub_key,
context.get())) {
LOG(ERROR) << "Failed to convert publisher_pub_key to a SecureBlob";
return false;
}
const BIGNUM* publisher_priv_secret =
EC_KEY_get0_private_key(publisher_key_pair.get());
if (!BigNumToSecureBlob(*publisher_priv_secret, ec_.ScalarSizeInBytes(),
publisher_priv_key)) {
LOG(ERROR) << "Failed to convert publisher_priv_key to a SecureBlob";
return false;
}
HsmAssociatedData hsm_ad;
hsm_ad.publisher_pub_key = *publisher_pub_key;
hsm_ad.channel_pub_key = channel_pub_key;
hsm_ad.rsa_public_key = rsa_pub_key;
hsm_ad.onboarding_meta_data = onboarding_metadata;
if (!SerializeHsmAssociatedDataToCbor(hsm_ad, hsm_associated_data)) {
LOG(ERROR) << "Failed to generate associated data cbor";
return false;
}
return true;
}
} // namespace
std::unique_ptr<RecoveryCrypto> RecoveryCrypto::Create() {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return nullptr;
}
base::Optional<EllipticCurve> ec =
EllipticCurve::Create(kCurve, context.get());
if (!ec) {
LOG(ERROR) << "Failed to create EllipticCurve";
return nullptr;
}
return std::make_unique<RecoveryCryptoImpl>(std::move(*ec));
}
RecoveryCrypto::~RecoveryCrypto() = default;
} // namespace cryptorecovery
} // namespace cryptohome