1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/child/webcrypto/openssl/util_openssl.h"
7 #include <openssl/evp.h>
8 #include <openssl/pkcs12.h>
9 #include <openssl/rand.h>
11 #include "base/stl_util.h"
12 #include "content/child/webcrypto/crypto_data.h"
13 #include "content/child/webcrypto/generate_key_result.h"
14 #include "content/child/webcrypto/openssl/key_openssl.h"
15 #include "content/child/webcrypto/platform_crypto.h"
16 #include "content/child/webcrypto/status.h"
17 #include "content/child/webcrypto/webcrypto_util.h"
18 #include "crypto/openssl_util.h"
26 // Exports an EVP_PKEY public key to the SPKI format.
27 Status
ExportPKeySpki(EVP_PKEY
* key
, std::vector
<uint8_t>* buffer
) {
28 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
29 crypto::ScopedBIO
bio(BIO_new(BIO_s_mem()));
31 // TODO(eroman): Use the OID specified by webcrypto spec.
32 // http://crbug.com/373545
33 if (!i2d_PUBKEY_bio(bio
.get(), key
))
34 return Status::ErrorUnexpected();
37 long len
= BIO_get_mem_data(bio
.get(), &data
);
39 return Status::ErrorUnexpected();
41 buffer
->assign(data
, data
+ len
);
42 return Status::Success();
45 // Exports an EVP_PKEY private key to the PKCS8 format.
46 Status
ExportPKeyPkcs8(EVP_PKEY
* key
, std::vector
<uint8_t>* buffer
) {
47 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
48 crypto::ScopedBIO
bio(BIO_new(BIO_s_mem()));
50 // TODO(eroman): Use the OID specified by webcrypto spec.
51 // http://crbug.com/373545
52 if (!i2d_PKCS8PrivateKeyInfo_bio(bio
.get(), key
))
53 return Status::ErrorUnexpected();
56 long len
= BIO_get_mem_data(bio
.get(), &data
);
58 return Status::ErrorUnexpected();
60 buffer
->assign(data
, data
+ len
);
61 return Status::Success();
67 crypto::EnsureOpenSSLInit();
70 const EVP_MD
* GetDigest(blink::WebCryptoAlgorithmId id
) {
72 case blink::WebCryptoAlgorithmIdSha1
:
74 case blink::WebCryptoAlgorithmIdSha256
:
76 case blink::WebCryptoAlgorithmIdSha384
:
78 case blink::WebCryptoAlgorithmIdSha512
:
85 Status
AeadEncryptDecrypt(EncryptOrDecrypt mode
,
86 const std::vector
<uint8_t>& raw_key
,
87 const CryptoData
& data
,
88 unsigned int tag_length_bytes
,
90 const CryptoData
& additional_data
,
91 const EVP_AEAD
* aead_alg
,
92 std::vector
<uint8_t>* buffer
) {
93 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
97 return Status::ErrorUnexpected();
99 if (!EVP_AEAD_CTX_init(&ctx
, aead_alg
, vector_as_array(&raw_key
),
100 raw_key
.size(), tag_length_bytes
, NULL
)) {
101 return Status::OperationError();
104 crypto::ScopedOpenSSL
<EVP_AEAD_CTX
, EVP_AEAD_CTX_cleanup
> ctx_cleanup(&ctx
);
109 if (mode
== DECRYPT
) {
110 if (data
.byte_length() < tag_length_bytes
)
111 return Status::ErrorDataTooSmall();
113 buffer
->resize(data
.byte_length() - tag_length_bytes
);
115 ok
= EVP_AEAD_CTX_open(&ctx
, vector_as_array(buffer
), &len
, buffer
->size(),
116 iv
.bytes(), iv
.byte_length(), data
.bytes(),
117 data
.byte_length(), additional_data
.bytes(),
118 additional_data
.byte_length());
120 // No need to check for unsigned integer overflow here (seal fails if
121 // the output buffer is too small).
122 buffer
->resize(data
.byte_length() + EVP_AEAD_max_overhead(aead_alg
));
124 ok
= EVP_AEAD_CTX_seal(&ctx
, vector_as_array(buffer
), &len
, buffer
->size(),
125 iv
.bytes(), iv
.byte_length(), data
.bytes(),
126 data
.byte_length(), additional_data
.bytes(),
127 additional_data
.byte_length());
131 return Status::OperationError();
133 return Status::Success();
136 Status
GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm
& algorithm
,
138 blink::WebCryptoKeyUsageMask usages
,
139 unsigned int keylen_bits
,
140 GenerateKeyResult
* result
) {
141 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
143 unsigned int keylen_bytes
= NumBitsToBytes(keylen_bits
);
144 std::vector
<unsigned char> random_bytes(keylen_bytes
, 0);
146 if (keylen_bytes
> 0) {
147 if (!(RAND_bytes(&random_bytes
[0], keylen_bytes
)))
148 return Status::OperationError();
149 TruncateToBitLength(keylen_bits
, &random_bytes
);
152 result
->AssignSecretKey(blink::WebCryptoKey::create(
153 new SymKeyOpenSsl(CryptoData(random_bytes
)),
154 blink::WebCryptoKeyTypeSecret
, extractable
, algorithm
, usages
));
156 return Status::Success();
159 Status
CreateWebCryptoSecretKey(const CryptoData
& key_data
,
160 const blink::WebCryptoKeyAlgorithm
& algorithm
,
162 blink::WebCryptoKeyUsageMask usages
,
163 blink::WebCryptoKey
* key
) {
164 *key
= blink::WebCryptoKey::create(new SymKeyOpenSsl(key_data
),
165 blink::WebCryptoKeyTypeSecret
, extractable
,
167 return Status::Success();
170 Status
CreateWebCryptoPublicKey(crypto::ScopedEVP_PKEY public_key
,
171 const blink::WebCryptoKeyAlgorithm
& algorithm
,
173 blink::WebCryptoKeyUsageMask usages
,
174 blink::WebCryptoKey
* key
) {
175 // Serialize the key at creation time so that if structured cloning is
176 // requested it can be done synchronously from the Blink thread.
177 std::vector
<uint8_t> spki_data
;
178 Status status
= ExportPKeySpki(public_key
.get(), &spki_data
);
179 if (status
.IsError())
182 *key
= blink::WebCryptoKey::create(
183 new AsymKeyOpenSsl(public_key
.Pass(), CryptoData(spki_data
)),
184 blink::WebCryptoKeyTypePublic
, extractable
, algorithm
, usages
);
185 return Status::Success();
188 Status
CreateWebCryptoPrivateKey(crypto::ScopedEVP_PKEY private_key
,
189 const blink::WebCryptoKeyAlgorithm
& algorithm
,
191 blink::WebCryptoKeyUsageMask usages
,
192 blink::WebCryptoKey
* key
) {
193 // Serialize the key at creation time so that if structured cloning is
194 // requested it can be done synchronously from the Blink thread.
195 std::vector
<uint8_t> pkcs8_data
;
196 Status status
= ExportPKeyPkcs8(private_key
.get(), &pkcs8_data
);
197 if (status
.IsError())
200 *key
= blink::WebCryptoKey::create(
201 new AsymKeyOpenSsl(private_key
.Pass(), CryptoData(pkcs8_data
)),
202 blink::WebCryptoKeyTypePrivate
, extractable
, algorithm
, usages
);
203 return Status::Success();
206 Status
ImportUnverifiedPkeyFromSpki(const CryptoData
& key_data
,
207 int expected_pkey_id
,
208 crypto::ScopedEVP_PKEY
* pkey
) {
209 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
211 const uint8_t* ptr
= key_data
.bytes();
212 pkey
->reset(d2i_PUBKEY(nullptr, &ptr
, key_data
.byte_length()));
213 if (!pkey
->get() || ptr
!= key_data
.bytes() + key_data
.byte_length())
214 return Status::DataError();
216 if (EVP_PKEY_id(pkey
->get()) != expected_pkey_id
)
217 return Status::DataError(); // Data did not define expected key type.
219 return Status::Success();
222 Status
ImportUnverifiedPkeyFromPkcs8(const CryptoData
& key_data
,
223 int expected_pkey_id
,
224 crypto::ScopedEVP_PKEY
* pkey
) {
225 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
227 const uint8_t* ptr
= key_data
.bytes();
228 crypto::ScopedOpenSSL
<PKCS8_PRIV_KEY_INFO
, PKCS8_PRIV_KEY_INFO_free
> p8inf(
229 d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr
, key_data
.byte_length()));
230 if (!p8inf
.get() || ptr
!= key_data
.bytes() + key_data
.byte_length())
231 return Status::DataError();
233 pkey
->reset(EVP_PKCS82PKEY(p8inf
.get()));
235 return Status::DataError();
237 if (EVP_PKEY_id(pkey
->get()) != expected_pkey_id
)
238 return Status::DataError(); // Data did not define expected key type.
240 return Status::Success();
243 BIGNUM
* CreateBIGNUM(const std::string
& n
) {
244 return BN_bin2bn(reinterpret_cast<const uint8_t*>(n
.data()), n
.size(), NULL
);
247 std::vector
<uint8_t> BIGNUMToVector(const BIGNUM
* n
) {
248 std::vector
<uint8_t> v(BN_num_bytes(n
));
249 BN_bn2bin(n
, vector_as_array(&v
));
253 } // namespace webcrypto
255 } // namespace content