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
49using namespace JSC;
50
51namespace WebCore {
52
53static 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
73static 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
94static 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
113static 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
132JSCryptoKeySerializationJWK::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
147JSCryptoKeySerializationJWK::~JSCryptoKeySerializationJWK()
148{
149}
150
151static 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
158static 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
166bool 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
251static 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
263void 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
317void 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
326bool 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
359std::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
382std::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
481std::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
500static 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
507static 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
513static 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
547static 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
554static 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
655static 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++, jsString(exec, ASCIILiteral("sign")));
662    if (usages & CryptoKeyUsageVerify)
663        keyOps->putDirectIndex(exec, index++, jsString(exec, ASCIILiteral("verify")));
664    if (usages & CryptoKeyUsageEncrypt)
665        keyOps->putDirectIndex(exec, index++, jsString(exec, ASCIILiteral("encrypt")));
666    if (usages & CryptoKeyUsageDecrypt)
667        keyOps->putDirectIndex(exec, index++, jsString(exec, ASCIILiteral("decrypt")));
668    if (usages & CryptoKeyUsageWrapKey)
669        keyOps->putDirectIndex(exec, index++, jsString(exec, ASCIILiteral("wrapKey")));
670    if (usages & CryptoKeyUsageUnwrapKey)
671        keyOps->putDirectIndex(exec, index++, jsString(exec, ASCIILiteral("unwrapKey")));
672    if (usages & CryptoKeyUsageDeriveKey)
673        keyOps->putDirectIndex(exec, index++, jsString(exec, ASCIILiteral("deriveKey")));
674    if (usages & CryptoKeyUsageDeriveBits)
675        keyOps->putDirectIndex(exec, index++, jsString(exec, ASCIILiteral("deriveBits")));
676
677    json->putDirect(exec->vm(), Identifier(exec, "key_ops"), keyOps);
678}
679
680String 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)
718