1 | /* |
2 | * Copyright (C) 2014 Igalia S.L. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "CryptoKeyRSA.h" |
28 | |
29 | #if ENABLE(WEB_CRYPTO) |
30 | |
31 | #include "CryptoAlgorithmRegistry.h" |
32 | #include "CryptoKeyPair.h" |
33 | #include "CryptoKeyRSAComponents.h" |
34 | #include "GCryptUtilities.h" |
35 | #include "ScriptExecutionContext.h" |
36 | #include <JavaScriptCore/GenericTypedArrayViewInlines.h> |
37 | #include <JavaScriptCore/HeapInlines.h> |
38 | #include <JavaScriptCore/JSGenericTypedArrayViewInlines.h> |
39 | #include <pal/crypto/gcrypt/Utilities.h> |
40 | #include <pal/crypto/tasn1/Utilities.h> |
41 | |
42 | namespace WebCore { |
43 | |
44 | static size_t getRSAModulusLength(gcry_sexp_t keySexp) |
45 | { |
46 | // Retrieve the s-expression token for the public modulus N of the given RSA key. |
47 | PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(keySexp, "n" , 0)); |
48 | if (!nSexp) |
49 | return 0; |
50 | |
51 | // Retrieve the MPI length for the corresponding s-expression token, in bits. |
52 | auto length = mpiLength(nSexp); |
53 | if (!length) |
54 | return 0; |
55 | |
56 | return *length * 8; |
57 | } |
58 | |
59 | static Vector<uint8_t> getRSAKeyParameter(gcry_sexp_t keySexp, const char* name) |
60 | { |
61 | // Retrieve the s-expression token for the specified parameter of the given RSA key. |
62 | PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(keySexp, name, 0)); |
63 | if (!paramSexp) |
64 | return { }; |
65 | |
66 | // Retrieve the MPI data for the corresponding s-expression token. |
67 | auto data = mpiData(paramSexp); |
68 | if (!data) |
69 | return { }; |
70 | |
71 | return WTFMove(data.value()); |
72 | } |
73 | |
74 | RefPtr<CryptoKeyRSA> CryptoKeyRSA::create(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, const CryptoKeyRSAComponents& keyData, bool , CryptoKeyUsageBitmap usages) |
75 | { |
76 | // When creating a private key, we require the p and q prime information. |
77 | if (keyData.type() == CryptoKeyRSAComponents::Type::Private && !keyData.hasAdditionalPrivateKeyParameters()) |
78 | return nullptr; |
79 | |
80 | // But we don't currently support creating keys with any additional prime information. |
81 | if (!keyData.otherPrimeInfos().isEmpty()) |
82 | return nullptr; |
83 | |
84 | // Validate the key data. |
85 | { |
86 | bool valid = true; |
87 | |
88 | // For both public and private keys, we need the public modulus and exponent. |
89 | valid &= !keyData.modulus().isEmpty() && !keyData.exponent().isEmpty(); |
90 | |
91 | // For private keys, we require the private exponent, as well as p and q prime information. |
92 | if (keyData.type() == CryptoKeyRSAComponents::Type::Private) |
93 | valid &= !keyData.privateExponent().isEmpty() && !keyData.firstPrimeInfo().primeFactor.isEmpty() && !keyData.secondPrimeInfo().primeFactor.isEmpty(); |
94 | |
95 | if (!valid) |
96 | return nullptr; |
97 | } |
98 | |
99 | CryptoKeyType keyType; |
100 | switch (keyData.type()) { |
101 | case CryptoKeyRSAComponents::Type::Public: |
102 | keyType = CryptoKeyType::Public; |
103 | break; |
104 | case CryptoKeyRSAComponents::Type::Private: |
105 | keyType = CryptoKeyType::Private; |
106 | break; |
107 | } |
108 | |
109 | // Construct the key s-expression, using the data that's available. |
110 | PAL::GCrypt::Handle<gcry_sexp_t> keySexp; |
111 | { |
112 | gcry_error_t error = GPG_ERR_NO_ERROR; |
113 | |
114 | switch (keyType) { |
115 | case CryptoKeyType::Public: |
116 | error = gcry_sexp_build(&keySexp, nullptr, "(public-key(rsa(n %b)(e %b)))" , |
117 | keyData.modulus().size(), keyData.modulus().data(), |
118 | keyData.exponent().size(), keyData.exponent().data()); |
119 | break; |
120 | case CryptoKeyType::Private: |
121 | if (keyData.hasAdditionalPrivateKeyParameters()) { |
122 | error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)))" , |
123 | keyData.modulus().size(), keyData.modulus().data(), |
124 | keyData.exponent().size(), keyData.exponent().data(), |
125 | keyData.privateExponent().size(), keyData.privateExponent().data(), |
126 | keyData.secondPrimeInfo().primeFactor.size(), keyData.secondPrimeInfo().primeFactor.data(), |
127 | keyData.firstPrimeInfo().primeFactor.size(), keyData.firstPrimeInfo().primeFactor.data()); |
128 | break; |
129 | } |
130 | |
131 | error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)))" , |
132 | keyData.modulus().size(), keyData.modulus().data(), |
133 | keyData.exponent().size(), keyData.exponent().data(), |
134 | keyData.privateExponent().size(), keyData.privateExponent().data()); |
135 | break; |
136 | case CryptoKeyType::Secret: |
137 | ASSERT_NOT_REACHED(); |
138 | return nullptr; |
139 | } |
140 | |
141 | if (error != GPG_ERR_NO_ERROR) { |
142 | PAL::GCrypt::logError(error); |
143 | return nullptr; |
144 | } |
145 | } |
146 | |
147 | return adoptRef(new CryptoKeyRSA(identifier, hash, hasHash, keyType, PlatformRSAKeyContainer(keySexp.release()), extractable, usages)); |
148 | } |
149 | |
150 | CryptoKeyRSA::CryptoKeyRSA(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, CryptoKeyType type, PlatformRSAKeyContainer&& platformKey, bool , CryptoKeyUsageBitmap usage) |
151 | : CryptoKey(identifier, type, extractable, usage) |
152 | , m_platformKey(WTFMove(platformKey)) |
153 | , m_restrictedToSpecificHash(hasHash) |
154 | , m_hash(hash) |
155 | { |
156 | } |
157 | |
158 | bool CryptoKeyRSA::isRestrictedToHash(CryptoAlgorithmIdentifier& identifier) const |
159 | { |
160 | if (!m_restrictedToSpecificHash) |
161 | return false; |
162 | |
163 | identifier = m_hash; |
164 | return true; |
165 | } |
166 | |
167 | size_t CryptoKeyRSA::keySizeInBits() const |
168 | { |
169 | return getRSAModulusLength(m_platformKey.get()); |
170 | } |
171 | |
172 | // Convert the exponent vector to a 32-bit value, if possible. |
173 | static Optional<uint32_t> exponentVectorToUInt32(const Vector<uint8_t>& exponent) |
174 | { |
175 | if (exponent.size() > 4) { |
176 | if (std::any_of(exponent.begin(), exponent.end() - 4, [](uint8_t element) { return !!element; })) |
177 | return WTF::nullopt; |
178 | } |
179 | |
180 | uint32_t result = 0; |
181 | for (size_t size = exponent.size(), i = std::min<size_t>(4, size); i > 0; --i) { |
182 | result <<= 8; |
183 | result += exponent[size - i]; |
184 | } |
185 | |
186 | return result; |
187 | } |
188 | |
189 | void CryptoKeyRSA::generatePair(CryptoAlgorithmIdentifier algorithm, CryptoAlgorithmIdentifier hash, bool hasHash, unsigned modulusLength, const Vector<uint8_t>& publicExponent, bool , CryptoKeyUsageBitmap usage, KeyPairCallback&& callback, VoidCallback&& failureCallback, ScriptExecutionContext* context) |
190 | { |
191 | // libgcrypt doesn't report an error if the exponent is smaller than three or even. |
192 | auto e = exponentVectorToUInt32(publicExponent); |
193 | if (!e || *e < 3 || !(*e & 0x1)) { |
194 | failureCallback(); |
195 | return; |
196 | } |
197 | |
198 | // libgcrypt doesn't support generating primes of less than 16 bits. |
199 | if (modulusLength < 16) { |
200 | failureCallback(); |
201 | return; |
202 | } |
203 | |
204 | PAL::GCrypt::Handle<gcry_sexp_t> genkeySexp; |
205 | gcry_error_t error = gcry_sexp_build(&genkeySexp, nullptr, "(genkey(rsa(nbits %d)(rsa-use-e %d)))" , modulusLength, *e); |
206 | if (error != GPG_ERR_NO_ERROR) { |
207 | PAL::GCrypt::logError(error); |
208 | failureCallback(); |
209 | return; |
210 | } |
211 | |
212 | PAL::GCrypt::Handle<gcry_sexp_t> keyPairSexp; |
213 | error = gcry_pk_genkey(&keyPairSexp, genkeySexp); |
214 | if (error != GPG_ERR_NO_ERROR) { |
215 | PAL::GCrypt::logError(error); |
216 | failureCallback(); |
217 | return; |
218 | } |
219 | |
220 | PAL::GCrypt::Handle<gcry_sexp_t> publicKeySexp(gcry_sexp_find_token(keyPairSexp, "public-key" , 0)); |
221 | PAL::GCrypt::Handle<gcry_sexp_t> privateKeySexp(gcry_sexp_find_token(keyPairSexp, "private-key" , 0)); |
222 | if (!publicKeySexp || !privateKeySexp) { |
223 | failureCallback(); |
224 | return; |
225 | } |
226 | |
227 | context->postTask( |
228 | [algorithm, hash, hasHash, extractable, usage, publicKeySexp = PlatformRSAKeyContainer(publicKeySexp.release()), privateKeySexp = PlatformRSAKeyContainer(privateKeySexp.release()), callback = WTFMove(callback)](auto&) mutable { |
229 | auto publicKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Public, WTFMove(publicKeySexp), true, usage); |
230 | auto privateKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Private, WTFMove(privateKeySexp), extractable, usage); |
231 | |
232 | callback(CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) }); |
233 | }); |
234 | } |
235 | |
236 | static bool supportedAlgorithmIdentifier(const uint8_t* data, size_t size) |
237 | { |
238 | // FIXME: This is far from sufficient. Per the spec, when importing for key algorithm |
239 | // - RSASSA-PKCS1-v1_5: |
240 | // - rsaEncryption, sha{1,256,384,512}WithRSAEncryption OIDs must be supported |
241 | // - in case of sha{1,256,384,512}WithRSAEncryption OIDs the specified hash algorithm |
242 | // has to match the algorithm in the OID |
243 | // - RSA-PSS: |
244 | // - rsaEncryption, id-RSASSA-PSS OIDs must be supported |
245 | // - in case of id-RSASSA-PSS OID the parameters field of AlgorithmIdentifier has |
246 | // to be decoded as RSASSA-PSS-params ASN.1 structure, and the hashAlgorithm field |
247 | // of that structure has to contain one of id-sha{1,256,384,512} OIDs that match |
248 | // the specified hash algorithm |
249 | // - RSA-OAEP: |
250 | // - rsaEncryption, id-RSAES-OAEP OIDS must be supported |
251 | // - in case of id-RSAES-OAEP OID the parameters field of AlgorithmIdentifier has |
252 | // to be decoded as RSAES-OAEP-params ASN.1 structure, and the hashAlgorithm field |
253 | // of that structure has to contain one of id-sha{1,256,384,512} OIDs that match |
254 | // the specified hash algorithm |
255 | |
256 | if (CryptoConstants::matches(data, size, CryptoConstants::s_rsaEncryptionIdentifier)) |
257 | return true; |
258 | if (CryptoConstants::matches(data, size, CryptoConstants::s_RSAES_OAEPIdentifier)) |
259 | return false; // Not yet supported. |
260 | if (CryptoConstants::matches(data, size, CryptoConstants::s_RSASSA_PSSIdentifier)) |
261 | return false; // Not yet supported. |
262 | return false; |
263 | } |
264 | |
265 | RefPtr<CryptoKeyRSA> CryptoKeyRSA::importSpki(CryptoAlgorithmIdentifier identifier, Optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool , CryptoKeyUsageBitmap usages) |
266 | { |
267 | // Decode the `SubjectPublicKeyInfo` structure using the provided key data. |
268 | PAL::TASN1::Structure spki; |
269 | if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo" , keyData)) |
270 | return nullptr; |
271 | |
272 | // Validate `algorithm.algorithm`. |
273 | { |
274 | auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm" ); |
275 | if (!algorithm) |
276 | return nullptr; |
277 | |
278 | if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size())) |
279 | return nullptr; |
280 | } |
281 | |
282 | // Decode the `RSAPublicKey` structure using the `subjectPublicKey` data. |
283 | PAL::TASN1::Structure rsaPublicKey; |
284 | { |
285 | auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey" ); |
286 | if (!subjectPublicKey) |
287 | return nullptr; |
288 | |
289 | if (!PAL::TASN1::decodeStructure(&rsaPublicKey, "WebCrypto.RSAPublicKey" , *subjectPublicKey)) |
290 | return nullptr; |
291 | } |
292 | |
293 | // Retrieve the `modulus` and `publicExponent` data and embed it into the `public-key` s-expression. |
294 | PAL::GCrypt::Handle<gcry_sexp_t> platformKey; |
295 | { |
296 | auto modulus = PAL::TASN1::elementData(rsaPublicKey, "modulus" ); |
297 | auto publicExponent = PAL::TASN1::elementData(rsaPublicKey, "publicExponent" ); |
298 | if (!modulus || !publicExponent) |
299 | return nullptr; |
300 | |
301 | gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(rsa(n %b)(e %b)))" , |
302 | modulus->size(), modulus->data(), publicExponent->size(), publicExponent->data()); |
303 | if (error != GPG_ERR_NO_ERROR) { |
304 | PAL::GCrypt::logError(error); |
305 | return nullptr; |
306 | } |
307 | } |
308 | |
309 | return adoptRef(new CryptoKeyRSA(identifier, hash.valueOr(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Public, PlatformRSAKeyContainer(platformKey.release()), extractable, usages)); |
310 | } |
311 | |
312 | RefPtr<CryptoKeyRSA> CryptoKeyRSA::importPkcs8(CryptoAlgorithmIdentifier identifier, Optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool , CryptoKeyUsageBitmap usages) |
313 | { |
314 | // Decode the `PrivateKeyInfo` structure using the provided key data. |
315 | PAL::TASN1::Structure pkcs8; |
316 | if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo" , keyData)) |
317 | return nullptr; |
318 | |
319 | // Validate `version`. |
320 | { |
321 | auto version = PAL::TASN1::elementData(pkcs8, "version" ); |
322 | if (!version) |
323 | return nullptr; |
324 | |
325 | if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0)) |
326 | return nullptr; |
327 | } |
328 | |
329 | // Validate `privateKeyAlgorithm.algorithm`. |
330 | { |
331 | auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm" ); |
332 | if (!algorithm) |
333 | return nullptr; |
334 | |
335 | if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size())) |
336 | return nullptr; |
337 | } |
338 | |
339 | // Decode the `RSAPrivateKey` structure using the `privateKey` data. |
340 | PAL::TASN1::Structure rsaPrivateKey; |
341 | { |
342 | auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey" ); |
343 | if (!privateKey) |
344 | return nullptr; |
345 | |
346 | if (!PAL::TASN1::decodeStructure(&rsaPrivateKey, "WebCrypto.RSAPrivateKey" , *privateKey)) |
347 | return nullptr; |
348 | } |
349 | |
350 | // Validate `privateKey.version`. |
351 | { |
352 | auto version = PAL::TASN1::elementData(rsaPrivateKey, "version" ); |
353 | if (!version) |
354 | return nullptr; |
355 | |
356 | if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0)) |
357 | return nullptr; |
358 | } |
359 | |
360 | // Retrieve the `modulus`, `publicExponent`, `privateExponent`, `prime1`, `prime2`, |
361 | // `exponent1`, `exponent2` and `coefficient` data and embed it into the `public-key` s-expression. |
362 | PAL::GCrypt::Handle<gcry_sexp_t> platformKey; |
363 | { |
364 | auto modulus = PAL::TASN1::elementData(rsaPrivateKey, "modulus" ); |
365 | auto publicExponent = PAL::TASN1::elementData(rsaPrivateKey, "publicExponent" ); |
366 | auto privateExponent = PAL::TASN1::elementData(rsaPrivateKey, "privateExponent" ); |
367 | auto prime1 = PAL::TASN1::elementData(rsaPrivateKey, "prime1" ); |
368 | auto prime2 = PAL::TASN1::elementData(rsaPrivateKey, "prime2" ); |
369 | auto exponent1 = PAL::TASN1::elementData(rsaPrivateKey, "exponent1" ); |
370 | auto exponent2 = PAL::TASN1::elementData(rsaPrivateKey, "exponent2" ); |
371 | auto coefficient = PAL::TASN1::elementData(rsaPrivateKey, "coefficient" ); |
372 | |
373 | if (!modulus || !publicExponent || !privateExponent |
374 | || !prime1 || !prime2 || !exponent1 || !exponent2 || !coefficient) |
375 | return nullptr; |
376 | |
377 | // libgcrypt inverts the use of p and q parameters, so we have to recalculate the `coefficient` value. |
378 | PAL::GCrypt::Handle<gcry_mpi_t> uMPI(gcry_mpi_new(0)); |
379 | { |
380 | PAL::GCrypt::Handle<gcry_mpi_t> pMPI; |
381 | gcry_error_t error = gcry_mpi_scan(&pMPI, GCRYMPI_FMT_USG, prime1->data(), prime1->size(), nullptr); |
382 | if (error != GPG_ERR_NO_ERROR) |
383 | return nullptr; |
384 | |
385 | PAL::GCrypt::Handle<gcry_mpi_t> qMPI; |
386 | error = gcry_mpi_scan(&qMPI, GCRYMPI_FMT_USG, prime2->data(), prime2->size(), nullptr); |
387 | if (error != GPG_ERR_NO_ERROR) |
388 | return nullptr; |
389 | |
390 | gcry_mpi_invm(uMPI, qMPI, pMPI); |
391 | } |
392 | |
393 | gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %M)))" , |
394 | modulus->size(), modulus->data(), |
395 | publicExponent->size(), publicExponent->data(), |
396 | privateExponent->size(), privateExponent->data(), |
397 | prime2->size(), prime2->data(), prime1->size(), prime1->data(), uMPI.handle()); |
398 | if (error != GPG_ERR_NO_ERROR) { |
399 | PAL::GCrypt::logError(error); |
400 | return nullptr; |
401 | } |
402 | } |
403 | |
404 | return adoptRef(new CryptoKeyRSA(identifier, hash.valueOr(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Private, PlatformRSAKeyContainer(platformKey.release()), extractable, usages)); |
405 | } |
406 | |
407 | ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportSpki() const |
408 | { |
409 | if (type() != CryptoKeyType::Public) |
410 | return Exception { InvalidAccessError }; |
411 | |
412 | PAL::TASN1::Structure rsaPublicKey; |
413 | { |
414 | // Create the `RSAPublicKey` structure. |
415 | if (!PAL::TASN1::createStructure("WebCrypto.RSAPublicKey" , &rsaPublicKey)) |
416 | return Exception { OperationError }; |
417 | |
418 | // Retrieve the modulus and public exponent s-expressions. |
419 | PAL::GCrypt::Handle<gcry_sexp_t> modulusSexp(gcry_sexp_find_token(m_platformKey.get(), "n" , 0)); |
420 | PAL::GCrypt::Handle<gcry_sexp_t> publicExponentSexp(gcry_sexp_find_token(m_platformKey.get(), "e" , 0)); |
421 | if (!modulusSexp || !publicExponentSexp) |
422 | return Exception { OperationError }; |
423 | |
424 | // Retrieve MPI data for the modulus and public exponent components. |
425 | auto modulus = mpiSignedData(modulusSexp); |
426 | auto publicExponent = mpiSignedData(publicExponentSexp); |
427 | if (!modulus || !publicExponent) |
428 | return Exception { OperationError }; |
429 | |
430 | // Write out the modulus data under `modulus`. |
431 | if (!PAL::TASN1::writeElement(rsaPublicKey, "modulus" , modulus->data(), modulus->size())) |
432 | return Exception { OperationError }; |
433 | |
434 | // Write out the public exponent data under `publicExponent`. |
435 | if (!PAL::TASN1::writeElement(rsaPublicKey, "publicExponent" , publicExponent->data(), publicExponent->size())) |
436 | return Exception { OperationError }; |
437 | } |
438 | |
439 | PAL::TASN1::Structure spki; |
440 | { |
441 | // Create the `SubjectPublicKeyInfo` structure. |
442 | if (!PAL::TASN1::createStructure("WebCrypto.SubjectPublicKeyInfo" , &spki)) |
443 | return Exception { OperationError }; |
444 | |
445 | // Write out the id-rsaEncryption identifier under `algorithm.algorithm`. |
446 | // FIXME: In case the key algorithm is: |
447 | // - RSA-PSS: |
448 | // - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters` |
449 | // to a RSASSA-PSS-params structure |
450 | // - RSA-OAEP: |
451 | // - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters` |
452 | // to a RSAES-OAEP-params structure |
453 | if (!PAL::TASN1::writeElement(spki, "algorithm.algorithm" , CryptoConstants::s_rsaEncryptionIdentifier.data(), 1)) |
454 | return Exception { OperationError }; |
455 | |
456 | // Write out the null value under `algorithm.parameters`. |
457 | if (!PAL::TASN1::writeElement(spki, "algorithm.parameters" , CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size())) |
458 | return Exception { OperationError }; |
459 | |
460 | // Write out the `RSAPublicKey` data under `subjectPublicKey`. Because this is a |
461 | // bit string parameter, the data size has to be multiplied by 8. |
462 | { |
463 | auto data = PAL::TASN1::encodedData(rsaPublicKey, "" ); |
464 | if (!data || !PAL::TASN1::writeElement(spki, "subjectPublicKey" , data->data(), data->size() * 8)) |
465 | return Exception { OperationError }; |
466 | } |
467 | } |
468 | |
469 | // Retrieve the encoded `SubjectPublicKeyInfo` data and return it. |
470 | auto result = PAL::TASN1::encodedData(spki, "" ); |
471 | if (!result) |
472 | return Exception { OperationError }; |
473 | |
474 | return WTFMove(result.value()); |
475 | } |
476 | |
477 | ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportPkcs8() const |
478 | { |
479 | if (type() != CryptoKeyType::Private) |
480 | return Exception { InvalidAccessError }; |
481 | |
482 | PAL::TASN1::Structure rsaPrivateKey; |
483 | { |
484 | // Create the `RSAPrivateKey` structure. |
485 | if (!PAL::TASN1::createStructure("WebCrypto.RSAPrivateKey" , &rsaPrivateKey)) |
486 | return Exception { OperationError }; |
487 | |
488 | // Write out '0' under `version`. |
489 | if (!PAL::TASN1::writeElement(rsaPrivateKey, "version" , "0" , 0)) |
490 | return Exception { OperationError }; |
491 | |
492 | // Retrieve the `n`, `e`, `d`, `q` and `p` s-expression tokens. libgcrypt swaps the usage of |
493 | // the p and q primes internally, so we adjust the lookup accordingly. |
494 | PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(m_platformKey.get(), "n" , 0)); |
495 | PAL::GCrypt::Handle<gcry_sexp_t> eSexp(gcry_sexp_find_token(m_platformKey.get(), "e" , 0)); |
496 | PAL::GCrypt::Handle<gcry_sexp_t> dSexp(gcry_sexp_find_token(m_platformKey.get(), "d" , 0)); |
497 | PAL::GCrypt::Handle<gcry_sexp_t> pSexp(gcry_sexp_find_token(m_platformKey.get(), "q" , 0)); |
498 | PAL::GCrypt::Handle<gcry_sexp_t> qSexp(gcry_sexp_find_token(m_platformKey.get(), "p" , 0)); |
499 | if (!nSexp || !eSexp || !dSexp || !pSexp || !qSexp) |
500 | return Exception { OperationError }; |
501 | |
502 | // Write the MPI data of retrieved s-expression tokens under `modulus`, `publicExponent`, |
503 | // `privateExponent`, `prime1` and `prime2`. |
504 | { |
505 | auto modulus = mpiSignedData(nSexp); |
506 | auto publicExponent = mpiSignedData(eSexp); |
507 | auto privateExponent = mpiSignedData(dSexp); |
508 | auto prime1 = mpiSignedData(pSexp); |
509 | auto prime2 = mpiSignedData(qSexp); |
510 | if (!modulus || !publicExponent || !privateExponent || !prime1 || !prime2) |
511 | return Exception { OperationError }; |
512 | |
513 | if (!PAL::TASN1::writeElement(rsaPrivateKey, "modulus" , modulus->data(), modulus->size()) |
514 | || !PAL::TASN1::writeElement(rsaPrivateKey, "publicExponent" , publicExponent->data(), publicExponent->size()) |
515 | || !PAL::TASN1::writeElement(rsaPrivateKey, "privateExponent" , privateExponent->data(), privateExponent->size()) |
516 | || !PAL::TASN1::writeElement(rsaPrivateKey, "prime1" , prime1->data(), prime1->size()) |
517 | || !PAL::TASN1::writeElement(rsaPrivateKey, "prime2" , prime2->data(), prime2->size())) |
518 | return Exception { OperationError }; |
519 | } |
520 | |
521 | // Manually compute the MPI values for the `exponent1`, `exponent2` and `coefficient` |
522 | // parameters. Again note the swapped usage of the `p` and `q` s-expression parameters. |
523 | { |
524 | PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_sexp_nth_mpi(dSexp, 1, GCRYMPI_FMT_USG)); |
525 | PAL::GCrypt::Handle<gcry_mpi_t> pMPI(gcry_sexp_nth_mpi(pSexp, 1, GCRYMPI_FMT_USG)); |
526 | PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_sexp_nth_mpi(qSexp, 1, GCRYMPI_FMT_USG)); |
527 | if (!dMPI || !pMPI || !qMPI) |
528 | return Exception { OperationError }; |
529 | |
530 | // `exponent1` |
531 | { |
532 | PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_set_ui(nullptr, 0)); |
533 | PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_set(nullptr, pMPI)); |
534 | gcry_mpi_sub_ui(pm1MPI, pm1MPI, 1); |
535 | gcry_mpi_mod(dpMPI, dMPI, pm1MPI); |
536 | |
537 | auto dp = mpiSignedData(dpMPI); |
538 | if (!dp || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent1" , dp->data(), dp->size())) |
539 | return Exception { OperationError }; |
540 | } |
541 | |
542 | // `exponent2` |
543 | { |
544 | PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_set_ui(nullptr, 0)); |
545 | PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_set(nullptr, qMPI)); |
546 | gcry_mpi_sub_ui(qm1MPI, qm1MPI, 1); |
547 | gcry_mpi_mod(dqMPI, dMPI, qm1MPI); |
548 | |
549 | auto dq = mpiSignedData(dqMPI); |
550 | if (!dq || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent2" , dq->data(), dq->size())) |
551 | return Exception { OperationError }; |
552 | } |
553 | |
554 | // `coefficient` |
555 | { |
556 | PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_set_ui(nullptr, 0)); |
557 | gcry_mpi_invm(qiMPI, qMPI, pMPI); |
558 | |
559 | auto qi = mpiSignedData(qiMPI); |
560 | if (!qi || !PAL::TASN1::writeElement(rsaPrivateKey, "coefficient" , qi->data(), qi->size())) |
561 | return Exception { OperationError }; |
562 | } |
563 | } |
564 | |
565 | // Eliminate the optional `otherPrimeInfos` element. |
566 | // FIXME: this should be supported in the future, if there is such information available. |
567 | if (!PAL::TASN1::writeElement(rsaPrivateKey, "otherPrimeInfos" , nullptr, 0)) |
568 | return Exception { OperationError }; |
569 | } |
570 | |
571 | PAL::TASN1::Structure pkcs8; |
572 | { |
573 | // Create the `PrivateKeyInfo` structure. |
574 | if (!PAL::TASN1::createStructure("WebCrypto.PrivateKeyInfo" , &pkcs8)) |
575 | return Exception { OperationError }; |
576 | |
577 | // Write out '0' under `version`. |
578 | if (!PAL::TASN1::writeElement(pkcs8, "version" , "0" , 0)) |
579 | return Exception { OperationError }; |
580 | |
581 | // Write out the id-rsaEncryption identifier under `algorithm.algorithm`. |
582 | // FIXME: In case the key algorithm is: |
583 | // - RSA-PSS: |
584 | // - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters` |
585 | // to a RSASSA-PSS-params structure |
586 | // - RSA-OAEP: |
587 | // - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters` |
588 | // to a RSAES-OAEP-params structure |
589 | if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.algorithm" , "1.2.840.113549.1.1.1" , 1)) |
590 | return Exception { OperationError }; |
591 | |
592 | // Write out a null value under `algorithm.parameters`. |
593 | if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.parameters" , CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size())) |
594 | return Exception { OperationError }; |
595 | |
596 | // Write out the `RSAPrivateKey` data under `privateKey`. |
597 | { |
598 | auto data = PAL::TASN1::encodedData(rsaPrivateKey, "" ); |
599 | if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKey" , data->data(), data->size())) |
600 | return Exception { OperationError }; |
601 | } |
602 | |
603 | // Eliminate the optional `attributes` element. |
604 | if (!PAL::TASN1::writeElement(pkcs8, "attributes" , nullptr, 0)) |
605 | return Exception { OperationError }; |
606 | } |
607 | |
608 | // Retrieve the encoded `PrivateKeyInfo` data and return it. |
609 | auto result = PAL::TASN1::encodedData(pkcs8, "" ); |
610 | if (!result) |
611 | return Exception { OperationError }; |
612 | |
613 | return WTFMove(result.value()); |
614 | } |
615 | |
616 | auto CryptoKeyRSA::algorithm() const -> KeyAlgorithm |
617 | { |
618 | auto modulusLength = getRSAModulusLength(m_platformKey.get()); |
619 | auto publicExponent = getRSAKeyParameter(m_platformKey.get(), "e" ); |
620 | |
621 | if (m_restrictedToSpecificHash) { |
622 | CryptoRsaHashedKeyAlgorithm result; |
623 | result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier()); |
624 | result.modulusLength = modulusLength; |
625 | result.publicExponent = Uint8Array::tryCreate(publicExponent.data(), publicExponent.size()); |
626 | result.hash.name = CryptoAlgorithmRegistry::singleton().name(m_hash); |
627 | return result; |
628 | } |
629 | |
630 | CryptoRsaKeyAlgorithm result; |
631 | result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier()); |
632 | result.modulusLength = modulusLength; |
633 | result.publicExponent = Uint8Array::tryCreate(publicExponent.data(), publicExponent.size()); |
634 | return result; |
635 | } |
636 | |
637 | std::unique_ptr<CryptoKeyRSAComponents> CryptoKeyRSA::exportData() const |
638 | { |
639 | switch (type()) { |
640 | case CryptoKeyType::Public: |
641 | return CryptoKeyRSAComponents::createPublic(getRSAKeyParameter(m_platformKey.get(), "n" ), getRSAKeyParameter(m_platformKey.get(), "e" )); |
642 | case CryptoKeyType::Private: { |
643 | auto parameterMPI = |
644 | [](gcry_sexp_t sexp, const char* name) -> gcry_mpi_t { |
645 | PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(sexp, name, 0)); |
646 | if (!paramSexp) |
647 | return nullptr; |
648 | return gcry_sexp_nth_mpi(paramSexp, 1, GCRYMPI_FMT_USG); |
649 | }; |
650 | |
651 | PAL::GCrypt::Handle<gcry_mpi_t> dMPI(parameterMPI(m_platformKey.get(), "d" )); |
652 | // libgcrypt internally uses p and q such that p < q, while usually it's q < p. |
653 | // Switch the two primes here and continue with assuming the latter. |
654 | PAL::GCrypt::Handle<gcry_mpi_t> pMPI(parameterMPI(m_platformKey.get(), "q" )); |
655 | PAL::GCrypt::Handle<gcry_mpi_t> qMPI(parameterMPI(m_platformKey.get(), "p" )); |
656 | if (!dMPI || !pMPI || !qMPI) |
657 | return nullptr; |
658 | |
659 | CryptoKeyRSAComponents::PrimeInfo firstPrimeInfo; |
660 | if (auto data = mpiData(pMPI)) |
661 | firstPrimeInfo.primeFactor = WTFMove(data.value()); |
662 | |
663 | CryptoKeyRSAComponents::PrimeInfo secondPrimeInfo; |
664 | if (auto data = mpiData(qMPI)) |
665 | secondPrimeInfo.primeFactor = WTFMove(data.value()); |
666 | |
667 | // dp -- d mod (p - 1) |
668 | { |
669 | PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_new(0)); |
670 | PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_new(0)); |
671 | gcry_mpi_sub_ui(pm1MPI, pMPI, 1); |
672 | gcry_mpi_mod(dpMPI, dMPI, pm1MPI); |
673 | |
674 | if (auto data = mpiData(dpMPI)) |
675 | firstPrimeInfo.factorCRTExponent = WTFMove(data.value()); |
676 | } |
677 | |
678 | // dq -- d mod (q - 1) |
679 | { |
680 | PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_new(0)); |
681 | PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_new(0)); |
682 | gcry_mpi_sub_ui(qm1MPI, qMPI, 1); |
683 | gcry_mpi_mod(dqMPI, dMPI, qm1MPI); |
684 | |
685 | if (auto data = mpiData(dqMPI)) |
686 | secondPrimeInfo.factorCRTExponent = WTFMove(data.value()); |
687 | } |
688 | |
689 | // qi -- q^(-1) mod p |
690 | { |
691 | PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_new(0)); |
692 | gcry_mpi_invm(qiMPI, qMPI, pMPI); |
693 | |
694 | if (auto data = mpiData(qiMPI)) |
695 | secondPrimeInfo.factorCRTCoefficient = WTFMove(data.value()); |
696 | } |
697 | |
698 | Vector<uint8_t> privateExponent; |
699 | if (auto data = mpiData(dMPI)) |
700 | privateExponent = WTFMove(data.value()); |
701 | |
702 | return CryptoKeyRSAComponents::createPrivateWithAdditionalData( |
703 | getRSAKeyParameter(m_platformKey.get(), "n" ), getRSAKeyParameter(m_platformKey.get(), "e" ), WTFMove(privateExponent), |
704 | WTFMove(firstPrimeInfo), WTFMove(secondPrimeInfo), Vector<CryptoKeyRSAComponents::PrimeInfo> { }); |
705 | } |
706 | default: |
707 | ASSERT_NOT_REACHED(); |
708 | return nullptr; |
709 | } |
710 | } |
711 | |
712 | } // namespace WebCore |
713 | |
714 | #endif // ENABLE(WEB_CRYPTO) |
715 | |