OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/child/webcrypto/openssl/util_openssl.h" | |
6 | |
7 #include <openssl/evp.h> | |
8 #include <openssl/pkcs12.h> | |
9 #include <openssl/rand.h> | |
10 | |
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" | |
19 | |
20 namespace content { | |
21 | |
22 namespace webcrypto { | |
23 | |
24 namespace { | |
25 | |
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())); | |
30 | |
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(); | |
35 | |
36 char* data = NULL; | |
37 long len = BIO_get_mem_data(bio.get(), &data); | |
38 if (!data || len < 0) | |
39 return Status::ErrorUnexpected(); | |
40 | |
41 buffer->assign(data, data + len); | |
42 return Status::Success(); | |
43 } | |
44 | |
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())); | |
49 | |
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(); | |
54 | |
55 char* data = NULL; | |
56 long len = BIO_get_mem_data(bio.get(), &data); | |
57 if (!data || len < 0) | |
58 return Status::ErrorUnexpected(); | |
59 | |
60 buffer->assign(data, data + len); | |
61 return Status::Success(); | |
62 } | |
63 | |
64 } // namespace | |
65 | |
66 void PlatformInit() { | |
67 crypto::EnsureOpenSSLInit(); | |
68 } | |
69 | |
70 const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id) { | |
71 switch (id) { | |
72 case blink::WebCryptoAlgorithmIdSha1: | |
73 return EVP_sha1(); | |
74 case blink::WebCryptoAlgorithmIdSha256: | |
75 return EVP_sha256(); | |
76 case blink::WebCryptoAlgorithmIdSha384: | |
77 return EVP_sha384(); | |
78 case blink::WebCryptoAlgorithmIdSha512: | |
79 return EVP_sha512(); | |
80 default: | |
81 return NULL; | |
82 } | |
83 } | |
84 | |
85 Status AeadEncryptDecrypt(EncryptOrDecrypt mode, | |
86 const std::vector<uint8_t>& raw_key, | |
87 const CryptoData& data, | |
88 unsigned int tag_length_bytes, | |
89 const CryptoData& iv, | |
90 const CryptoData& additional_data, | |
91 const EVP_AEAD* aead_alg, | |
92 std::vector<uint8_t>* buffer) { | |
93 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
94 EVP_AEAD_CTX ctx; | |
95 | |
96 if (!aead_alg) | |
97 return Status::ErrorUnexpected(); | |
98 | |
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(); | |
102 } | |
103 | |
104 crypto::ScopedOpenSSL<EVP_AEAD_CTX, EVP_AEAD_CTX_cleanup> ctx_cleanup(&ctx); | |
105 | |
106 size_t len; | |
107 int ok; | |
108 | |
109 if (mode == DECRYPT) { | |
110 if (data.byte_length() < tag_length_bytes) | |
111 return Status::ErrorDataTooSmall(); | |
112 | |
113 buffer->resize(data.byte_length() - tag_length_bytes); | |
114 | |
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()); | |
119 } else { | |
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)); | |
123 | |
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()); | |
128 } | |
129 | |
130 if (!ok) | |
131 return Status::OperationError(); | |
132 buffer->resize(len); | |
133 return Status::Success(); | |
134 } | |
135 | |
136 Status GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm& algorithm, | |
137 bool extractable, | |
138 blink::WebCryptoKeyUsageMask usages, | |
139 unsigned int keylen_bits, | |
140 GenerateKeyResult* result) { | |
141 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
142 | |
143 unsigned int keylen_bytes = NumBitsToBytes(keylen_bits); | |
144 std::vector<unsigned char> random_bytes(keylen_bytes, 0); | |
145 | |
146 if (keylen_bytes > 0) { | |
147 if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) | |
148 return Status::OperationError(); | |
149 TruncateToBitLength(keylen_bits, &random_bytes); | |
150 } | |
151 | |
152 result->AssignSecretKey(blink::WebCryptoKey::create( | |
153 new SymKeyOpenSsl(CryptoData(random_bytes)), | |
154 blink::WebCryptoKeyTypeSecret, extractable, algorithm, usages)); | |
155 | |
156 return Status::Success(); | |
157 } | |
158 | |
159 Status CreateWebCryptoSecretKey(const CryptoData& key_data, | |
160 const blink::WebCryptoKeyAlgorithm& algorithm, | |
161 bool extractable, | |
162 blink::WebCryptoKeyUsageMask usages, | |
163 blink::WebCryptoKey* key) { | |
164 *key = blink::WebCryptoKey::create(new SymKeyOpenSsl(key_data), | |
165 blink::WebCryptoKeyTypeSecret, extractable, | |
166 algorithm, usages); | |
167 return Status::Success(); | |
168 } | |
169 | |
170 Status CreateWebCryptoPublicKey(crypto::ScopedEVP_PKEY public_key, | |
171 const blink::WebCryptoKeyAlgorithm& algorithm, | |
172 bool extractable, | |
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()) | |
180 return status; | |
181 | |
182 *key = blink::WebCryptoKey::create( | |
183 new AsymKeyOpenSsl(public_key.Pass(), CryptoData(spki_data)), | |
184 blink::WebCryptoKeyTypePublic, extractable, algorithm, usages); | |
185 return Status::Success(); | |
186 } | |
187 | |
188 Status CreateWebCryptoPrivateKey(crypto::ScopedEVP_PKEY private_key, | |
189 const blink::WebCryptoKeyAlgorithm& algorithm, | |
190 bool extractable, | |
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()) | |
198 return status; | |
199 | |
200 *key = blink::WebCryptoKey::create( | |
201 new AsymKeyOpenSsl(private_key.Pass(), CryptoData(pkcs8_data)), | |
202 blink::WebCryptoKeyTypePrivate, extractable, algorithm, usages); | |
203 return Status::Success(); | |
204 } | |
205 | |
206 Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data, | |
207 int expected_pkey_id, | |
208 crypto::ScopedEVP_PKEY* pkey) { | |
209 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
210 | |
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(); | |
215 | |
216 if (EVP_PKEY_id(pkey->get()) != expected_pkey_id) | |
217 return Status::DataError(); // Data did not define expected key type. | |
218 | |
219 return Status::Success(); | |
220 } | |
221 | |
222 Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data, | |
223 int expected_pkey_id, | |
224 crypto::ScopedEVP_PKEY* pkey) { | |
225 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
226 | |
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(); | |
232 | |
233 pkey->reset(EVP_PKCS82PKEY(p8inf.get())); | |
234 if (!pkey->get()) | |
235 return Status::DataError(); | |
236 | |
237 if (EVP_PKEY_id(pkey->get()) != expected_pkey_id) | |
238 return Status::DataError(); // Data did not define expected key type. | |
239 | |
240 return Status::Success(); | |
241 } | |
242 | |
243 BIGNUM* CreateBIGNUM(const std::string& n) { | |
244 return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(), NULL); | |
245 } | |
246 | |
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)); | |
250 return v; | |
251 } | |
252 | |
253 } // namespace webcrypto | |
254 | |
255 } // namespace content | |
OLD | NEW |