1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "base/containers/span.h"
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/algorithms/ec.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "crypto/secure_util.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
#include "third_party/boringssl/src/include/openssl/ec.h"
#include "third_party/boringssl/src/include/openssl/ecdh.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
namespace webcrypto {
namespace {
// TODO(eroman): Support the "raw" format for ECDH key import + export, as
// specified by WebCrypto spec.
// TODO(eroman): Allow id-ecDH in SPKI and PKCS#8 import
// (http://crbug.com/389400)
class EcdhImplementation : public EcAlgorithm {
public:
EcdhImplementation()
: EcAlgorithm(0,
blink::kWebCryptoKeyUsageDeriveKey |
blink::kWebCryptoKeyUsageDeriveBits) {}
const char* GetJwkAlgorithm(
const blink::WebCryptoNamedCurve curve) const override {
// JWK import for ECDH does not enforce any required value for "alg".
return "";
}
Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& base_key,
std::optional<unsigned int> length_bits,
std::vector<uint8_t>* derived_bytes) const override {
if (base_key.GetType() != blink::kWebCryptoKeyTypePrivate)
return Status::ErrorUnexpectedKeyType();
// Verify the "publicKey" parameter. The only guarantee from Blink is that
// it is a valid WebCryptoKey, but it could be any type.
const blink::WebCryptoKey& public_key =
algorithm.EcdhKeyDeriveParams()->PublicKey();
if (public_key.GetType() != blink::kWebCryptoKeyTypePublic)
return Status::ErrorEcdhPublicKeyWrongType();
// Make sure it is an EC key.
if (!public_key.Algorithm().EcParams())
return Status::ErrorEcdhPublicKeyWrongType();
// TODO(eroman): This is not described by the spec:
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27404
if (public_key.Algorithm().Id() != blink::kWebCryptoAlgorithmIdEcdh)
return Status::ErrorEcdhPublicKeyWrongAlgorithm();
// The public and private keys come from different key pairs, however their
// curves must match.
if (public_key.Algorithm().EcParams()->NamedCurve() !=
base_key.Algorithm().EcParams()->NamedCurve()) {
return Status::ErrorEcdhCurveMismatch();
}
EC_KEY* public_key_ec = EVP_PKEY_get0_EC_KEY(GetEVP_PKEY(public_key));
const EC_POINT* public_key_point = EC_KEY_get0_public_key(public_key_ec);
EC_KEY* private_key_ec = EVP_PKEY_get0_EC_KEY(GetEVP_PKEY(base_key));
// The size of the shared secret is the field size in bytes (rounded up).
// Note that, if rounding was required, the most significant bits of the
// secret are zero. So for P-521, the maximum length is 528 bits, not 521.
int field_size_bytes =
NumBitsToBytes(EC_GROUP_get_degree(EC_KEY_get0_group(private_key_ec)));
unsigned int field_size_bits =
static_cast<unsigned int>(field_size_bytes * 8);
// If a desired key length was not specified, default to the field size
// (rounded up to nearest byte).
unsigned int actual_length_bits = length_bits.value_or(field_size_bits);
// Short-circuit when deriving an empty key.
// TODO(eroman): ECDH_compute_key() is not happy when given a NULL output.
// http://crbug.com/464194.
if (actual_length_bits == 0) {
derived_bytes->clear();
return Status::Success();
}
if (actual_length_bits > field_size_bits) {
return Status::ErrorEcdhLengthTooBig(field_size_bits);
}
// Resize to target length in bytes (BoringSSL can operate on a shorter
// buffer than field_size_bytes).
derived_bytes->resize(NumBitsToBytes(actual_length_bits));
int result = ECDH_compute_key(derived_bytes->data(), derived_bytes->size(),
public_key_point, private_key_ec, nullptr);
if (result < 0 || static_cast<size_t>(result) != derived_bytes->size())
return Status::OperationError();
TruncateToBitLength(actual_length_bits, derived_bytes);
return actual_length_bits < field_size_bits
? Status::SuccessDeriveBitsTruncation()
: Status::Success();
}
};
} // namespace
std::unique_ptr<AlgorithmImplementation> CreateEcdhImplementation() {
return std::make_unique<EcdhImplementation>();
}
} // namespace webcrypto
|