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
133
|
// Copyright 2014 The Chromium 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 <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/evp.h>
#include "base/logging.h"
#include "base/stl_util.h"
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/openssl/ec_algorithm_openssl.h"
#include "components/webcrypto/openssl/key_openssl.h"
#include "components/webcrypto/openssl/util_openssl.h"
#include "components/webcrypto/status.h"
#include "components/webcrypto/webcrypto_util.h"
#include "crypto/openssl_util.h"
#include "crypto/scoped_openssl_types.h"
#include "crypto/secure_util.h"
#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
#include "third_party/WebKit/public/platform/WebCryptoKey.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.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::WebCryptoKeyUsageDeriveKey |
blink::WebCryptoKeyUsageDeriveBits) {}
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,
bool has_optional_length_bits,
unsigned int optional_length_bits,
std::vector<uint8_t>* derived_bytes) const override {
if (base_key.type() != blink::WebCryptoKeyTypePrivate)
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.type() != blink::WebCryptoKeyTypePublic)
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::WebCryptoAlgorithmIdEcdh)
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();
}
crypto::ScopedEC_KEY public_key_ec(
EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(public_key)->key()));
const EC_POINT* public_key_point =
EC_KEY_get0_public_key(public_key_ec.get());
crypto::ScopedEC_KEY private_key_ec(
EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(base_key)->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.get())));
// If a desired key length was not specified, default to the field size
// (rounded up to nearest byte).
unsigned int length_bits =
has_optional_length_bits ? optional_length_bits : field_size_bytes * 8;
// 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 (length_bits == 0) {
derived_bytes->clear();
return Status::Success();
}
if (length_bits > static_cast<unsigned int>(field_size_bytes * 8))
return Status::ErrorEcdhLengthTooBig(field_size_bytes * 8);
// Resize to target length in bytes (BoringSSL can operate on a shorter
// buffer than field_size_bytes).
derived_bytes->resize(NumBitsToBytes(length_bits));
int result =
ECDH_compute_key(vector_as_array(derived_bytes), derived_bytes->size(),
public_key_point, private_key_ec.get(), 0);
if (result < 0 || static_cast<size_t>(result) != derived_bytes->size())
return Status::OperationError();
TruncateToBitLength(length_bits, derived_bytes);
return Status::Success();
}
};
} // namespace
AlgorithmImplementation* CreatePlatformEcdhImplementation() {
return new EcdhImplementation;
}
} // namespace webcrypto
|