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