blob: 78c13c5ad1bab181276790f0003789bea40539d6 [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/crypto/recovery_crypto.h"
#include <utility>
#include <base/logging.h>
#include <base/stl_util.h>
#include "cryptohome/crypto/elliptic_curve.h"
#include "cryptohome/crypto/openssl_util.h"
namespace cryptohome {
namespace {
constexpr EllipticCurve::CurveType kCurve = EllipticCurve::CurveType::kPrime256;
// Cryptographic operations for cryptohome recovery performed on CPU (software
// emulation).
class RecoveryCryptoImpl : public RecoveryCrypto {
public:
explicit RecoveryCryptoImpl(EllipticCurve ec);
~RecoveryCryptoImpl() override;
bool GenerateShares(brillo::SecureBlob* mediator_share,
brillo::SecureBlob* destination_share,
brillo::SecureBlob* dealer_pub_key) const override;
bool GeneratePublisherKeys(const brillo::SecureBlob& dealer_pub_key,
brillo::SecureBlob* publisher_pub_key,
brillo::SecureBlob* publisher_dh) const override;
bool Mediate(const brillo::SecureBlob& publisher_pub_key,
const brillo::SecureBlob& mediator_share,
brillo::SecureBlob* mediated_publisher_pub_key) const override;
bool RecoverDestination(const brillo::SecureBlob& publisher_pub_key,
const brillo::SecureBlob& destination_share,
const brillo::SecureBlob& mediated_publisher_pub_key,
brillo::SecureBlob* destination_dh) const override;
private:
EllipticCurve ec_;
};
RecoveryCryptoImpl::RecoveryCryptoImpl(EllipticCurve ec) : ec_(std::move(ec)) {}
RecoveryCryptoImpl::~RecoveryCryptoImpl() = default;
bool RecoveryCryptoImpl::GenerateShares(
brillo::SecureBlob* mediator_share,
brillo::SecureBlob* destination_share,
brillo::SecureBlob* dealer_pub_key) const {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
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()));
crypto::ScopedEC_POINT dealer_pub_point =
ec_.MultiplyWithGenerator(*secret, context.get());
if (!dealer_pub_point) {
LOG(ERROR) << "Failed to perform MultiplyWithGenerator operation";
return false;
}
if (!BigNumToSecureBlob(*mediator_share_bn, mediator_share)) {
LOG(ERROR) << "Failed to convert BIGNUM to SecureBlob";
return false;
}
if (!BigNumToSecureBlob(*destination_share_bn, destination_share)) {
LOG(ERROR) << "Failed to convert BIGNUM to SecureBlob";
return false;
}
if (!ec_.PointToSecureBlob(*dealer_pub_point, dealer_pub_key,
context.get())) {
LOG(ERROR) << "Failed to convert EC_POINT to SecureBlob";
return false;
}
return true;
}
bool RecoveryCryptoImpl::GeneratePublisherKeys(
const brillo::SecureBlob& dealer_pub_key,
brillo::SecureBlob* publisher_pub_key,
brillo::SecureBlob* publisher_dh) const {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return false;
}
crypto::ScopedBIGNUM secret = ec_.RandomNonZeroScalar(context.get());
if (!secret) {
LOG(ERROR) << "Failed to generate secret";
return false;
}
crypto::ScopedEC_POINT publisher_pub_point =
ec_.MultiplyWithGenerator(*secret, context.get());
if (!publisher_pub_point) {
LOG(ERROR) << "Failed to perform MultiplyWithGenerator operation";
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 point_dh =
ec_.Multiply(*dealer_pub_point, *secret, context.get());
if (!point_dh) {
LOG(ERROR) << "Failed to perform point multiplication";
return false;
}
if (!ec_.PointToSecureBlob(*publisher_pub_point, publisher_pub_key,
context.get())) {
LOG(ERROR) << "Failed to convert EC_POINT to SecureBlob";
return false;
}
if (!ec_.PointToSecureBlob(*point_dh, publisher_dh, context.get())) {
LOG(ERROR) << "Failed to convert EC_POINT to SecureBlob";
return false;
}
return true;
}
bool RecoveryCryptoImpl::Mediate(
const brillo::SecureBlob& publisher_pub_key,
const brillo::SecureBlob& mediator_share,
brillo::SecureBlob* mediated_publisher_pub_key) const {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return false;
}
crypto::ScopedBIGNUM mediator_share_bn = SecureBlobToBigNum(mediator_share);
if (!mediator_share_bn) {
LOG(ERROR) << "Failed to convert SecureBlob to BIGNUM";
return false;
}
crypto::ScopedEC_POINT publisher_pub_point =
ec_.SecureBlobToPoint(publisher_pub_key, context.get());
if (!publisher_pub_point) {
LOG(ERROR) << "Failed to convert SecureBlob to EC_POINT";
return false;
}
// Performs scalar multiplication of publisher_pub_key and mediator_share.
crypto::ScopedEC_POINT point_dh =
ec_.Multiply(*publisher_pub_point, *mediator_share_bn, context.get());
if (!point_dh) {
LOG(ERROR) << "Failed to perform scalar multiplication";
return false;
}
if (!ec_.PointToSecureBlob(*point_dh, mediated_publisher_pub_key,
context.get())) {
LOG(ERROR) << "Failed to convert EC_POINT to SecureBlob";
return false;
}
return true;
}
bool RecoveryCryptoImpl::RecoverDestination(
const brillo::SecureBlob& publisher_pub_key,
const brillo::SecureBlob& destination_share,
const brillo::SecureBlob& mediated_publisher_pub_key,
brillo::SecureBlob* destination_dh) 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 publisher_pub_point =
ec_.SecureBlobToPoint(publisher_pub_key, context.get());
if (!publisher_pub_point) {
LOG(ERROR) << "Failed to convert SecureBlob to EC_POINT";
return false;
}
crypto::ScopedEC_POINT mediated_publisher_pub_point =
ec_.SecureBlobToPoint(mediated_publisher_pub_key, context.get());
if (!mediated_publisher_pub_point) {
LOG(ERROR) << "Failed to convert SecureBlob to EC_POINT";
return false;
}
// Performs scalar multiplication of publisher_pub_key and destination_share.
crypto::ScopedEC_POINT point_dh =
ec_.Multiply(*publisher_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, *mediated_publisher_pub_point, context.get());
if (!point_dest) {
LOG(ERROR) << "Failed to perform point addition";
return false;
}
if (!ec_.PointToSecureBlob(*point_dest, destination_dh, context.get())) {
LOG(ERROR) << "Failed to convert EC_POINT to SecureBlob";
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 cryptohome