4df3d4971c1d445be410da2450b7481bc92a47cb
[WebKit-https.git] / Source / WebCore / bindings / js / JSCryptoKeySerializationJWK.cpp
1 /*
2  * Copyright (C) 2013, 2016 Apple Inc. 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 "JSCryptoKeySerializationJWK.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
31 #include "CryptoAlgorithm.h"
32 #include "CryptoAlgorithmHmacParams.h"
33 #include "CryptoAlgorithmRegistry.h"
34 #include "CryptoAlgorithmRsaKeyParamsWithHash.h"
35 #include "CryptoKey.h"
36 #include "CryptoKeyAES.h"
37 #include "CryptoKeyDataOctetSequence.h"
38 #include "CryptoKeyDataRSAComponents.h"
39 #include "CryptoKeyHMAC.h"
40 #include "CryptoKeyRSA.h"
41 #include "ExceptionCode.h"
42 #include "JSDOMBinding.h"
43 #include <heap/StrongInlines.h>
44 #include <runtime/JSCInlines.h>
45 #include <runtime/JSONObject.h>
46 #include <runtime/ObjectConstructor.h>
47 #include <wtf/text/Base64.h>
48
49 using namespace JSC;
50
51 namespace WebCore {
52
53 static bool getJSArrayFromJSON(ExecState* exec, JSObject* json, const char* key, JSArray*& result)
54 {
55     VM& vm = exec->vm();
56     auto scope = DECLARE_THROW_SCOPE(vm);
57
58     Identifier identifier = Identifier::fromString(exec, key);
59     PropertySlot slot(json, PropertySlot::InternalMethodType::Get);
60
61     if (!json->getPropertySlot(exec, identifier, slot))
62         return false;
63
64     JSValue value = slot.getValue(exec, identifier);
65     ASSERT(!scope.exception());
66     if (!isJSArray(value)) {
67         throwTypeError(exec, scope, String::format("Expected an array for \"%s\" JSON key",  key));
68         return false;
69     }
70
71     result = asArray(value);
72
73     return true;
74 }
75
76 static bool getStringFromJSON(ExecState* exec, JSObject* json, const char* key, String& result)
77 {
78     VM& vm = exec->vm();
79     auto scope = DECLARE_THROW_SCOPE(vm);
80
81     Identifier identifier = Identifier::fromString(exec, key);
82     PropertySlot slot(json, PropertySlot::InternalMethodType::Get);
83
84     if (!json->getPropertySlot(exec, identifier, slot))
85         return false;
86
87     JSValue jsValue = slot.getValue(exec, identifier);
88     ASSERT(!scope.exception());
89     if (!jsValue.getString(exec, result)) {
90         // Can get an out of memory exception.
91         RETURN_IF_EXCEPTION(scope, false);
92         throwTypeError(exec, scope, String::format("Expected a string value for \"%s\" JSON key",  key));
93         return false;
94     }
95
96     return true;
97 }
98
99 static bool getBooleanFromJSON(ExecState* exec, JSObject* json, const char* key, bool& result)
100 {
101     VM& vm = exec->vm();
102     auto scope = DECLARE_THROW_SCOPE(vm);
103
104     Identifier identifier = Identifier::fromString(exec, key);
105     PropertySlot slot(json, PropertySlot::InternalMethodType::Get);
106
107     if (!json->getPropertySlot(exec, identifier, slot))
108         return false;
109
110     JSValue jsValue = slot.getValue(exec, identifier);
111     ASSERT(!scope.exception());
112     if (!jsValue.isBoolean()) {
113         throwTypeError(exec, scope, String::format("Expected a boolean value for \"%s\" JSON key",  key));
114         return false;
115     }
116
117     result = jsValue.asBoolean();
118     return true;
119 }
120
121 static bool getBigIntegerVectorFromJSON(ExecState* exec, JSObject* json, const char* key, Vector<uint8_t>& result)
122 {
123     VM& vm = exec->vm();
124     auto scope = DECLARE_THROW_SCOPE(vm);
125
126     String base64urlEncodedNumber;
127     if (!getStringFromJSON(exec, json, key, base64urlEncodedNumber))
128         return false;
129
130     if (!base64URLDecode(base64urlEncodedNumber, result)) {
131         throwTypeError(exec, scope, ASCIILiteral("Cannot decode base64url key data in JWK"));
132         return false;
133     }
134
135     if (result[0] == 0) {
136         throwTypeError(exec, scope, ASCIILiteral("JWK BigInteger must utilize the minimum number of octets to represent the value"));
137         return false;
138     }
139
140     return true;
141 }
142
143 JSCryptoKeySerializationJWK::JSCryptoKeySerializationJWK(ExecState* exec, const String& jsonString)
144     : m_exec(exec)
145 {
146     VM& vm = exec->vm();
147     auto scope = DECLARE_THROW_SCOPE(vm);
148
149     JSValue jsonValue = JSONParse(exec, jsonString);
150     if (UNLIKELY(scope.exception()))
151         return;
152
153     if (!jsonValue || !jsonValue.isObject()) {
154         throwTypeError(exec, scope, ASCIILiteral("Invalid JWK serialization"));
155         return;
156     }
157
158     m_json.set(vm, asObject(jsonValue));
159 }
160
161 JSCryptoKeySerializationJWK::~JSCryptoKeySerializationJWK()
162 {
163 }
164
165 static Ref<CryptoAlgorithmParameters> createHMACParameters(CryptoAlgorithmIdentifier hashFunction)
166 {
167     auto hmacParameters = adoptRef(*new CryptoAlgorithmHmacParams);
168     hmacParameters->hash = hashFunction;
169     return WTFMove(hmacParameters);
170 }
171
172 static Ref<CryptoAlgorithmParameters> createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier hashFunction)
173 {
174     auto rsaKeyParameters = adoptRef(*new CryptoAlgorithmRsaKeyParamsWithHash);
175     rsaKeyParameters->hasHash = true;
176     rsaKeyParameters->hash = hashFunction;
177     return WTFMove(rsaKeyParameters);
178 }
179
180 Optional<CryptoAlgorithmPair> JSCryptoKeySerializationJWK::reconcileAlgorithm(CryptoAlgorithm* suggestedAlgorithm, CryptoAlgorithmParameters* suggestedParameters) const
181 {
182     VM& vm = m_exec->vm();
183     auto scope = DECLARE_THROW_SCOPE(vm);
184
185     if (!getStringFromJSON(m_exec, m_json.get(), "alg", m_jwkAlgorithmName)) {
186         // Algorithm is optional in JWK.
187         return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
188     }
189
190     auto& algorithmRegisty = CryptoAlgorithmRegistry::singleton();
191     RefPtr<CryptoAlgorithm> algorithm;
192     RefPtr<CryptoAlgorithmParameters> parameters;
193     if (m_jwkAlgorithmName == "HS256") {
194         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::HMAC);
195         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_256);
196     } else if (m_jwkAlgorithmName == "HS384") {
197         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::HMAC);
198         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_384);
199     } else if (m_jwkAlgorithmName == "HS512") {
200         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::HMAC);
201         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_512);
202     } else if (m_jwkAlgorithmName == "RS256") {
203         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
204         parameters = createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier::SHA_256);
205     } else if (m_jwkAlgorithmName == "RS384") {
206         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
207         parameters = createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier::SHA_384);
208     } else if (m_jwkAlgorithmName == "RS512") {
209         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
210         parameters = createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier::SHA_512);
211     } else if (m_jwkAlgorithmName == "RSA1_5") {
212         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5);
213         parameters = adoptRef(*new CryptoAlgorithmRsaKeyParamsWithHash);
214     } else if (m_jwkAlgorithmName == "RSA-OAEP") {
215         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSA_OAEP);
216         parameters = createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier::SHA_1);
217     } else if (m_jwkAlgorithmName == "A128CBC") {
218         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_CBC);
219         parameters = adoptRef(*new CryptoAlgorithmParameters);
220     } else if (m_jwkAlgorithmName == "A192CBC") {
221         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_CBC);
222         parameters = adoptRef(*new CryptoAlgorithmParameters);
223     } else if (m_jwkAlgorithmName == "A256CBC") {
224         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_CBC);
225         parameters = adoptRef(*new CryptoAlgorithmParameters);
226     } else if (m_jwkAlgorithmName == "A128KW") {
227         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_KW);
228         parameters = adoptRef(*new CryptoAlgorithmParameters);
229     } else if (m_jwkAlgorithmName == "A192KW") {
230         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_KW);
231         parameters = adoptRef(*new CryptoAlgorithmParameters);
232     } else if (m_jwkAlgorithmName == "A256KW") {
233         algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_KW);
234         parameters = adoptRef(*new CryptoAlgorithmParameters);
235     } else {
236         throwTypeError(m_exec, scope, "Unsupported JWK algorithm " + m_jwkAlgorithmName);
237         return Nullopt;
238     }
239
240     if (!suggestedAlgorithm)
241         return CryptoAlgorithmPair { algorithm, parameters };
242
243     if (!algorithm)
244         return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
245
246     if (algorithm->identifier() != suggestedAlgorithm->identifier())
247         return Nullopt;
248
249     if (algorithm->identifier() == CryptoAlgorithmIdentifier::HMAC) {
250         if (downcast<CryptoAlgorithmHmacParams>(*parameters).hash != downcast<CryptoAlgorithmHmacParams>(*suggestedParameters).hash)
251             return Nullopt;
252         return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
253     }
254     if (algorithm->identifier() == CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5
255         || algorithm->identifier() == CryptoAlgorithmIdentifier::RSA_OAEP) {
256         CryptoAlgorithmRsaKeyParamsWithHash& rsaKeyParameters = downcast<CryptoAlgorithmRsaKeyParamsWithHash>(*parameters);
257         CryptoAlgorithmRsaKeyParamsWithHash& suggestedRSAKeyParameters = downcast<CryptoAlgorithmRsaKeyParamsWithHash>(*suggestedParameters);
258         ASSERT(rsaKeyParameters.hasHash);
259         if (suggestedRSAKeyParameters.hasHash) {
260             if (suggestedRSAKeyParameters.hash != rsaKeyParameters.hash)
261                 return Nullopt;
262             return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
263         }
264         suggestedRSAKeyParameters.hasHash = true;
265         suggestedRSAKeyParameters.hash = rsaKeyParameters.hash;
266     }
267
268     // Other algorithms don't have parameters.
269     return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
270 }
271
272 static bool tryJWKKeyOpsValue(ExecState* exec, CryptoKeyUsage& usages, const String& operation, const String& tryOperation, CryptoKeyUsage tryUsage)
273 {
274     VM& vm = exec->vm();
275     auto scope = DECLARE_THROW_SCOPE(vm);
276
277     if (operation == tryOperation) {
278         if (usages & tryUsage) {
279             throwTypeError(exec, scope, ASCIILiteral("JWK key_ops contains a duplicate operation"));
280             return false;
281         }
282         usages |= tryUsage;
283     }
284     return true;
285 }
286
287 void JSCryptoKeySerializationJWK::reconcileUsages(CryptoKeyUsage& suggestedUsages) const
288 {
289     VM& vm = m_exec->vm();
290     auto scope = DECLARE_THROW_SCOPE(vm);
291
292     CryptoKeyUsage jwkUsages = 0;
293
294     JSArray* keyOps;
295     if (getJSArrayFromJSON(m_exec, m_json.get(), "key_ops", keyOps)) {
296         for (size_t i = 0; i < keyOps->length(); ++i) {
297             JSValue jsValue = keyOps->getIndex(m_exec, i);
298             String operation;
299             if (!jsValue.getString(m_exec, operation)) {
300                 if (!scope.exception())
301                     throwTypeError(m_exec, scope, ASCIILiteral("JWK key_ops attribute could not be processed"));
302                 return;
303             }
304             if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("sign"), CryptoKeyUsageSign))
305                 return;
306             if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("verify"), CryptoKeyUsageVerify))
307                 return;
308             if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("encrypt"), CryptoKeyUsageEncrypt))
309                 return;
310             if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("decrypt"), CryptoKeyUsageDecrypt))
311                 return;
312             if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("wrapKey"), CryptoKeyUsageWrapKey))
313                 return;
314             if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("unwrapKey"), CryptoKeyUsageUnwrapKey))
315                 return;
316             if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("deriveKey"), CryptoKeyUsageDeriveKey))
317                 return;
318             if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("deriveBits"), CryptoKeyUsageDeriveBits))
319                 return;
320         }
321     } else {
322         RETURN_IF_EXCEPTION(scope, void());
323
324         String jwkUseString;
325         if (!getStringFromJSON(m_exec, m_json.get(), "use", jwkUseString)) {
326             // We have neither key_ops nor use.
327             return;
328         }
329
330         if (jwkUseString == "enc")
331             jwkUsages |= (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey);
332         else if (jwkUseString == "sig")
333             jwkUsages |= (CryptoKeyUsageSign | CryptoKeyUsageVerify);
334         else {
335             throwTypeError(m_exec, scope, "Unsupported JWK key use value \"" + jwkUseString + "\"");
336             return;
337         }
338     }
339
340     suggestedUsages = suggestedUsages & jwkUsages;
341 }
342
343 void JSCryptoKeySerializationJWK::reconcileExtractable(bool& suggestedExtractable) const
344 {
345     bool jwkExtractable;
346     if (!getBooleanFromJSON(m_exec, m_json.get(), "ext", jwkExtractable))
347         return;
348
349     suggestedExtractable = suggestedExtractable && jwkExtractable;
350 }
351
352 bool JSCryptoKeySerializationJWK::keySizeIsValid(size_t sizeInBits) const
353 {
354     if (m_jwkAlgorithmName == "HS256")
355         return sizeInBits >= 256;
356     if (m_jwkAlgorithmName == "HS384")
357         return sizeInBits >= 384;
358     if (m_jwkAlgorithmName == "HS512")
359         return sizeInBits >= 512;
360     if (m_jwkAlgorithmName == "A128CBC")
361         return sizeInBits == 128;
362     if (m_jwkAlgorithmName == "A192CBC")
363         return sizeInBits == 192;
364     if (m_jwkAlgorithmName == "A256CBC")
365         return sizeInBits == 256;
366     if (m_jwkAlgorithmName == "A128KW")
367         return sizeInBits == 128;
368     if (m_jwkAlgorithmName == "A192KW")
369         return sizeInBits == 192;
370     if (m_jwkAlgorithmName == "A256KW")
371         return sizeInBits == 256;
372     if (m_jwkAlgorithmName == "RS256")
373         return sizeInBits >= 2048;
374     if (m_jwkAlgorithmName == "RS384")
375         return sizeInBits >= 2048;
376     if (m_jwkAlgorithmName == "RS512")
377         return sizeInBits >= 2048;
378     if (m_jwkAlgorithmName == "RSA1_5")
379         return sizeInBits >= 2048;
380     if (m_jwkAlgorithmName == "RSA_OAEP")
381         return sizeInBits >= 2048;
382     return true;
383 }
384
385 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyDataOctetSequence() const
386 {
387     VM& vm = m_exec->vm();
388     auto scope = DECLARE_THROW_SCOPE(vm);
389
390     String keyBase64URL;
391     if (!getStringFromJSON(m_exec, m_json.get(), "k", keyBase64URL)) {
392         if (!scope.exception())
393             throwTypeError(m_exec, scope, ASCIILiteral("Secret key data is not present is JWK"));
394         return nullptr;
395     }
396
397     Vector<uint8_t> octetSequence;
398     if (!base64URLDecode(keyBase64URL, octetSequence)) {
399         throwTypeError(m_exec, scope, ASCIILiteral("Cannot decode base64url key data in JWK"));
400         return nullptr;
401     }
402
403     if (!keySizeIsValid(octetSequence.size() * 8)) {
404         throwTypeError(m_exec, scope, "Key size is not valid for " + m_jwkAlgorithmName);
405         return nullptr;
406     }
407
408     return std::make_unique<CryptoKeyDataOctetSequence>(octetSequence);
409 }
410
411 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyDataRSAComponents() const
412 {
413     VM& vm = m_exec->vm();
414     auto scope = DECLARE_THROW_SCOPE(vm);
415
416     Vector<uint8_t> modulus;
417     Vector<uint8_t> exponent;
418     Vector<uint8_t> privateExponent;
419
420     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "n", modulus)) {
421         if (!scope.exception())
422             throwTypeError(m_exec, scope, ASCIILiteral("Required JWK \"n\" member is missing"));
423         return nullptr;
424     }
425
426     if (!keySizeIsValid(modulus.size() * 8)) {
427         throwTypeError(m_exec, scope, "Key size is not valid for " + m_jwkAlgorithmName);
428         return nullptr;
429     }
430
431     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "e", exponent)) {
432         if (!scope.exception())
433             throwTypeError(m_exec, scope, ASCIILiteral("Required JWK \"e\" member is missing"));
434         return nullptr;
435     }
436
437     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "d", modulus)) {
438         RETURN_IF_EXCEPTION(scope, nullptr);
439         return CryptoKeyDataRSAComponents::createPublic(modulus, exponent);
440     }
441
442     CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo;
443     CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo;
444     Vector<CryptoKeyDataRSAComponents::PrimeInfo> otherPrimeInfos;
445     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "p", firstPrimeInfo.primeFactor)) {
446         RETURN_IF_EXCEPTION(scope, nullptr);
447         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
448     }
449
450     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "dp", firstPrimeInfo.factorCRTExponent)) {
451         RETURN_IF_EXCEPTION(scope, nullptr);
452         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
453     }
454
455     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "q", secondPrimeInfo.primeFactor)) {
456         RETURN_IF_EXCEPTION(scope, nullptr);
457         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
458     }
459
460     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "dq", secondPrimeInfo.factorCRTExponent)) {
461         RETURN_IF_EXCEPTION(scope, nullptr);
462         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
463     }
464
465     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "qi", secondPrimeInfo.factorCRTCoefficient)) {
466         RETURN_IF_EXCEPTION(scope, nullptr);
467         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
468     }
469
470     JSArray* otherPrimeInfoJSArray;
471     if (!getJSArrayFromJSON(m_exec, m_json.get(), "oth", otherPrimeInfoJSArray)) {
472         RETURN_IF_EXCEPTION(scope, nullptr);
473         return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
474     }
475
476     for (size_t i = 0; i < otherPrimeInfoJSArray->length(); ++i) {
477         CryptoKeyDataRSAComponents::PrimeInfo info;
478         JSValue element = otherPrimeInfoJSArray->getIndex(m_exec, i);
479         RETURN_IF_EXCEPTION(scope, nullptr);
480         if (!element.isObject()) {
481             throwTypeError(m_exec, scope, ASCIILiteral("JWK \"oth\" array member is not an object"));
482             return nullptr;
483         }
484         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "r", info.primeFactor)) {
485             if (!scope.exception())
486                 throwTypeError(m_exec, scope, ASCIILiteral("Cannot get prime factor for a prime in \"oth\" dictionary"));
487             return nullptr;
488         }
489         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "d", info.factorCRTExponent)) {
490             if (!scope.exception())
491                 throwTypeError(m_exec, scope, ASCIILiteral("Cannot get factor CRT exponent for a prime in \"oth\" dictionary"));
492             return nullptr;
493         }
494         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "t", info.factorCRTCoefficient)) {
495             if (!scope.exception())
496                 throwTypeError(m_exec, scope, ASCIILiteral("Cannot get factor CRT coefficient for a prime in \"oth\" dictionary"));
497             return nullptr;
498         }
499         otherPrimeInfos.append(info);
500     }
501
502     return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
503 }
504
505 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyData() const
506 {
507     VM& vm = m_exec->vm();
508     auto scope = DECLARE_THROW_SCOPE(vm);
509
510     String jwkKeyType;
511     if (!getStringFromJSON(m_exec, m_json.get(), "kty", jwkKeyType)) {
512         if (!scope.exception())
513             throwTypeError(m_exec, scope, ASCIILiteral("Required JWK \"kty\" member is missing"));
514         return nullptr;
515     }
516
517     if (jwkKeyType == "oct")
518         return keyDataOctetSequence();
519
520     if (jwkKeyType == "RSA")
521         return keyDataRSAComponents();
522
523     throwTypeError(m_exec, scope, "Unsupported JWK key type " + jwkKeyType);
524     return nullptr;
525 }
526
527 static void addToJSON(ExecState* exec, JSObject* json, const char* key, const String& value)
528 {
529     VM& vm = exec->vm();
530     Identifier identifier = Identifier::fromString(&vm, key);
531     json->putDirect(vm, identifier, jsString(exec, value));
532 }
533
534 static void buildJSONForOctetSequence(ExecState* exec, const Vector<uint8_t>& keyData, JSObject* result)
535 {
536     addToJSON(exec, result, "kty", "oct");
537     addToJSON(exec, result, "k", base64URLEncode(keyData));
538 }
539
540 static void buildJSONForRSAComponents(JSC::ExecState* exec, const CryptoKeyDataRSAComponents& data, JSC::JSObject* result)
541 {
542     VM& vm = exec->vm();
543     auto scope = DECLARE_THROW_SCOPE(vm);
544
545     addToJSON(exec, result, "kty", "RSA");
546     addToJSON(exec, result, "n", base64URLEncode(data.modulus()));
547     addToJSON(exec, result, "e", base64URLEncode(data.exponent()));
548
549     if (data.type() == CryptoKeyDataRSAComponents::Type::Public)
550         return;
551
552     addToJSON(exec, result, "d", base64URLEncode(data.privateExponent()));
553
554     if (!data.hasAdditionalPrivateKeyParameters())
555         return;
556
557     addToJSON(exec, result, "p", base64URLEncode(data.firstPrimeInfo().primeFactor));
558     addToJSON(exec, result, "q", base64URLEncode(data.secondPrimeInfo().primeFactor));
559     addToJSON(exec, result, "dp", base64URLEncode(data.firstPrimeInfo().factorCRTExponent));
560     addToJSON(exec, result, "dq", base64URLEncode(data.secondPrimeInfo().factorCRTExponent));
561     addToJSON(exec, result, "qi", base64URLEncode(data.secondPrimeInfo().factorCRTCoefficient));
562
563     if (data.otherPrimeInfos().isEmpty())
564         return;
565
566     JSArray* oth = constructEmptyArray(exec, 0, exec->lexicalGlobalObject(), data.otherPrimeInfos().size());
567     RETURN_IF_EXCEPTION(scope, void());
568     for (size_t i = 0, size = data.otherPrimeInfos().size(); i < size; ++i) {
569         JSObject* jsPrimeInfo = constructEmptyObject(exec);
570         addToJSON(exec, jsPrimeInfo, "r", base64URLEncode(data.otherPrimeInfos()[i].primeFactor));
571         addToJSON(exec, jsPrimeInfo, "d", base64URLEncode(data.otherPrimeInfos()[i].factorCRTExponent));
572         addToJSON(exec, jsPrimeInfo, "t", base64URLEncode(data.otherPrimeInfos()[i].factorCRTCoefficient));
573         oth->putDirectIndex(exec, i, jsPrimeInfo);
574     }
575     result->putDirect(vm, Identifier::fromString(exec, "oth"), oth);
576 }
577
578 static void addBoolToJSON(ExecState* exec, JSObject* json, const char* key, bool value)
579 {
580     VM& vm = exec->vm();
581     Identifier identifier = Identifier::fromString(&vm, key);
582     json->putDirect(vm, identifier, jsBoolean(value));
583 }
584
585 static void addJWKAlgorithmToJSON(ExecState* exec, JSObject* json, const CryptoKey& key)
586 {
587     VM& vm = exec->vm();
588     auto scope = DECLARE_THROW_SCOPE(vm);
589
590     String jwkAlgorithm;
591     switch (key.algorithmIdentifier()) {
592     case CryptoAlgorithmIdentifier::HMAC:
593         switch (downcast<CryptoKeyHMAC>(key).hashAlgorithmIdentifier()) {
594         case CryptoAlgorithmIdentifier::SHA_256:
595             if (downcast<CryptoKeyHMAC>(key).key().size() * 8 >= 256)
596                 jwkAlgorithm = "HS256";
597             break;
598         case CryptoAlgorithmIdentifier::SHA_384:
599             if (downcast<CryptoKeyHMAC>(key).key().size() * 8 >= 384)
600                 jwkAlgorithm = "HS384";
601             break;
602         case CryptoAlgorithmIdentifier::SHA_512:
603             if (downcast<CryptoKeyHMAC>(key).key().size() * 8 >= 512)
604                 jwkAlgorithm = "HS512";
605             break;
606         default:
607             break;
608         }
609         break;
610     case CryptoAlgorithmIdentifier::AES_CBC:
611         switch (downcast<CryptoKeyAES>(key).key().size() * 8) {
612         case 128:
613             jwkAlgorithm = "A128CBC";
614             break;
615         case 192:
616             jwkAlgorithm = "A192CBC";
617             break;
618         case 256:
619             jwkAlgorithm = "A256CBC";
620             break;
621         }
622         break;
623     case CryptoAlgorithmIdentifier::AES_KW:
624         switch (downcast<CryptoKeyAES>(key).key().size() * 8) {
625         case 128:
626             jwkAlgorithm = "A128KW";
627             break;
628         case 192:
629             jwkAlgorithm = "A192KW";
630             break;
631         case 256:
632             jwkAlgorithm = "A256KW";
633             break;
634         }
635         break;
636     case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: {
637         const CryptoKeyRSA& rsaKey = downcast<CryptoKeyRSA>(key);
638         CryptoAlgorithmIdentifier hash;
639         if (!rsaKey.isRestrictedToHash(hash))
640             break;
641         if (rsaKey.keySizeInBits() < 2048)
642             break;
643         switch (hash) {
644         case CryptoAlgorithmIdentifier::SHA_256:
645             jwkAlgorithm = "RS256";
646             break;
647         case CryptoAlgorithmIdentifier::SHA_384:
648             jwkAlgorithm = "RS384";
649             break;
650         case CryptoAlgorithmIdentifier::SHA_512:
651             jwkAlgorithm = "RS512";
652             break;
653         default:
654             break;
655         }
656         break;
657     }
658     case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: {
659         const CryptoKeyRSA& rsaKey = downcast<CryptoKeyRSA>(key);
660         if (rsaKey.keySizeInBits() < 2048)
661             break;
662         jwkAlgorithm = "RSA1_5";
663         break;
664     }
665     case CryptoAlgorithmIdentifier::RSA_OAEP: {
666         const CryptoKeyRSA& rsaKey = downcast<CryptoKeyRSA>(key);
667         CryptoAlgorithmIdentifier hash;
668         // WebCrypto RSA-OAEP keys are not tied to any particular hash, unless previously imported from JWK, which only supports SHA-1.
669         if (rsaKey.isRestrictedToHash(hash) && hash != CryptoAlgorithmIdentifier::SHA_1)
670             break;
671         if (rsaKey.keySizeInBits() < 2048)
672             break;
673         jwkAlgorithm = "RSA-OAEP";
674         break;
675     }
676     default:
677         break;
678     }
679
680     if (jwkAlgorithm.isNull()) {
681         // The spec doesn't currently tell whether export should fail, or just skip "alg" (which is an optional key in JWK).
682         throwTypeError(exec, scope, ASCIILiteral("Key algorithm and size do not map to any JWK algorithm identifier"));
683         return;
684     }
685
686     addToJSON(exec, json, "alg", jwkAlgorithm);
687 }
688
689 static void addUsagesToJSON(ExecState* exec, JSObject* json, CryptoKeyUsage usages)
690 {
691     VM& vm = exec->vm();
692     auto scope = DECLARE_THROW_SCOPE(vm);
693     JSArray* keyOps = constructEmptyArray(exec, 0, exec->lexicalGlobalObject(), 0);
694     RETURN_IF_EXCEPTION(scope, void());
695
696     unsigned index = 0;
697     if (usages & CryptoKeyUsageSign)
698         keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("sign")));
699     if (usages & CryptoKeyUsageVerify)
700         keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("verify")));
701     if (usages & CryptoKeyUsageEncrypt)
702         keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("encrypt")));
703     if (usages & CryptoKeyUsageDecrypt)
704         keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("decrypt")));
705     if (usages & CryptoKeyUsageWrapKey)
706         keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("wrapKey")));
707     if (usages & CryptoKeyUsageUnwrapKey)
708         keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("unwrapKey")));
709     if (usages & CryptoKeyUsageDeriveKey)
710         keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("deriveKey")));
711     if (usages & CryptoKeyUsageDeriveBits)
712         keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("deriveBits")));
713
714     json->putDirect(vm, Identifier::fromString(exec, "key_ops"), keyOps);
715 }
716
717 String JSCryptoKeySerializationJWK::serialize(ExecState* exec, const CryptoKey& key)
718 {
719     VM& vm = exec->vm();
720     auto scope = DECLARE_THROW_SCOPE(vm);
721
722     std::unique_ptr<CryptoKeyData> keyData = key.exportData();
723     if (!keyData) {
724         // This generally shouldn't happen as long as all key types implement exportData(), but as underlying libraries return errors, there may be some rare failure conditions.
725         throwTypeError(exec, scope, ASCIILiteral("Couldn't export key material"));
726         return String();
727     }
728
729     JSObject* result = constructEmptyObject(exec);
730
731     addJWKAlgorithmToJSON(exec, result, key);
732     RETURN_IF_EXCEPTION(scope, String());
733
734     addBoolToJSON(exec, result, "ext", key.extractable());
735
736     addUsagesToJSON(exec, result, key.usagesBitmap());
737     RETURN_IF_EXCEPTION(scope, String());
738
739     if (is<CryptoKeyDataOctetSequence>(*keyData))
740         buildJSONForOctetSequence(exec, downcast<CryptoKeyDataOctetSequence>(*keyData).octetSequence(), result);
741     else if (is<CryptoKeyDataRSAComponents>(*keyData))
742         buildJSONForRSAComponents(exec, downcast<CryptoKeyDataRSAComponents>(*keyData), result);
743     else {
744         throwTypeError(exec, scope, ASCIILiteral("Key doesn't support exportKey"));
745         return String();
746     }
747     RETURN_IF_EXCEPTION(scope, String());
748
749     return JSONStringify(exec, result, 0);
750 }
751
752 } // namespace WebCore
753
754 #endif // ENABLE(SUBTLE_CRYPTO)