b33240eb2a66da0210ccdd07f76d91efc0b9ddf0
[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 "CryptoAlgorithmRsaSsaKeyParams.h"
35 #include "CryptoKey.h"
36 #include "CryptoKeyAES.h"
37 #include "CryptoKeyDataOctetSequence.h"
38 #include "CryptoKeyDataRSAComponents.h"
39 #include "CryptoKeyHMAC.h"
40 #include "ExceptionCode.h"
41 #include "JSDOMBinding.h"
42 #include <heap/StrongInlines.h>
43 #include <runtime/JSONObject.h>
44 #include <runtime/ObjectConstructor.h>
45 #include <runtime/Operations.h>
46 #include <wtf/text/Base64.h>
47
48 using namespace JSC;
49
50 namespace WebCore {
51
52 static bool getJSArrayFromJSON(ExecState* exec, JSObject* json, const char* key, JSArray*& result)
53 {
54     Identifier identifier(exec, key);
55     PropertySlot slot(json);
56
57     if (!json->getPropertySlot(exec, identifier, slot))
58         return false;
59
60     JSValue value = slot.getValue(exec, identifier);
61     ASSERT(!exec->hadException());
62     if (isJSArray(value)) {
63         throwTypeError(exec, String::format("Expected an array for \"%s\" JSON key",  key));
64         return false;
65     }
66
67     result = asArray(value);
68
69     return true;
70 }
71
72 static bool getStringFromJSON(ExecState* exec, JSObject* json, const char* key, String& result)
73 {
74     Identifier identifier(exec, key);
75     PropertySlot slot(json);
76
77     if (!json->getPropertySlot(exec, identifier, slot))
78         return false;
79
80     JSValue jsValue = slot.getValue(exec, identifier);
81     ASSERT(!exec->hadException());
82     if (!jsValue.getString(exec, result)) {
83         // Can get an out of memory exception.
84         if (exec->hadException())
85             return false;
86         throwTypeError(exec, String::format("Expected a string value for \"%s\" JSON key",  key));
87         return false;
88     }
89
90     return true;
91 }
92
93 static bool getBooleanFromJSON(ExecState* exec, JSObject* json, const char* key, bool& result)
94 {
95     Identifier identifier(exec, key);
96     PropertySlot slot(json);
97
98     if (!json->getPropertySlot(exec, identifier, slot))
99         return false;
100
101     JSValue jsValue = slot.getValue(exec, identifier);
102     ASSERT(!exec->hadException());
103     if (!jsValue.isBoolean()) {
104         throwTypeError(exec, String::format("Expected a boolean value for \"%s\" JSON key",  key));
105         return false;
106     }
107
108     result = jsValue.asBoolean();
109     return true;
110 }
111
112 static bool getBigIntegerVectorFromJSON(ExecState* exec, JSObject* json, const char* key, Vector<char>& result)
113 {
114     String base64urlEncodedNumber;
115     if (!getStringFromJSON(exec, json, key, base64urlEncodedNumber))
116         return false;
117
118     if (!base64URLDecode(base64urlEncodedNumber, result)) {
119         throwTypeError(exec, "Cannot decode base64url key data in JWK");
120         return false;
121     }
122
123     if (result[0] == 0) {
124         throwTypeError(exec, "JWK BigInteger must utilize the minimum number of octets to represent the value");
125         return false;
126     }
127
128     return true;
129 }
130
131 JSCryptoKeySerializationJWK::JSCryptoKeySerializationJWK(ExecState* exec, const String& jsonString)
132     : m_exec(exec)
133 {
134     JSValue jsonValue = JSONParse(exec, jsonString);
135     if (exec->hadException())
136         return;
137
138     if (!jsonValue || !jsonValue.isObject()) {
139         throwTypeError(exec, "Invalid JWK serialization");
140         return;
141     }
142
143     m_json.set(m_exec->vm(), asObject(jsonValue));
144 }
145
146 JSCryptoKeySerializationJWK::~JSCryptoKeySerializationJWK()
147 {
148 }
149
150 static std::unique_ptr<CryptoAlgorithmParameters> createHMACParameters(CryptoAlgorithmIdentifier hashFunction)
151 {
152     std::unique_ptr<CryptoAlgorithmHmacParams> hmacParameters = std::make_unique<CryptoAlgorithmHmacParams>();
153     hmacParameters->hash = hashFunction;
154     return std::move(hmacParameters);
155 }
156
157 static std::unique_ptr<CryptoAlgorithmParameters> createRSASSAKeyParameters(CryptoAlgorithmIdentifier hashFunction)
158 {
159     std::unique_ptr<CryptoAlgorithmRsaSsaKeyParams> rsaSSAParameters = std::make_unique<CryptoAlgorithmRsaSsaKeyParams>();
160     rsaSSAParameters->hasHash = true;
161     rsaSSAParameters->hash = hashFunction;
162     return std::move(rsaSSAParameters);
163 }
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 = createRSASSAKeyParameters(CryptoAlgorithmIdentifier::SHA_256);
187     } else if (m_jwkAlgorithmName == "RS384") {
188         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
189         parameters = createRSASSAKeyParameters(CryptoAlgorithmIdentifier::SHA_384);
190     } else if (m_jwkAlgorithmName == "RS512") {
191         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
192         parameters = createRSASSAKeyParameters(CryptoAlgorithmIdentifier::SHA_512);
193     } else if (m_jwkAlgorithmName == "A128CBC") {
194         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
195         parameters = std::make_unique<CryptoAlgorithmParameters>();
196     } else if (m_jwkAlgorithmName == "A192CBC") {
197         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
198         parameters = std::make_unique<CryptoAlgorithmParameters>();
199     } else if (m_jwkAlgorithmName == "A256CBC") {
200         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
201         parameters = std::make_unique<CryptoAlgorithmParameters>();
202     } else {
203         throwTypeError(m_exec, "Unsupported JWK algorithm " + m_jwkAlgorithmName);
204         return false;
205     }
206
207     if (!suggestedAlgorithm) {
208         suggestedAlgorithm = std::move(algorithm);
209         suggestedParameters =  std::move(parameters);
210         return true;
211     }
212
213     if (!algorithm)
214         return true;
215
216     if (algorithm->identifier() != suggestedAlgorithm->identifier())
217         return false;
218
219     if (algorithm->identifier() == CryptoAlgorithmIdentifier::HMAC)
220         return toCryptoAlgorithmHmacParams(*parameters).hash == toCryptoAlgorithmHmacParams(*suggestedParameters).hash;
221     if (algorithm->identifier() == CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5) {
222         CryptoAlgorithmRsaSsaKeyParams& rsaSSAParameters = toCryptoAlgorithmRsaSsaKeyParams(*parameters);
223         CryptoAlgorithmRsaSsaKeyParams& suggestedRSASSAParameters = toCryptoAlgorithmRsaSsaKeyParams(*suggestedParameters);
224         ASSERT(rsaSSAParameters.hasHash);
225         if (suggestedRSASSAParameters.hasHash)
226             return suggestedRSASSAParameters.hash == rsaSSAParameters.hash;
227         suggestedRSASSAParameters.hasHash = true;
228         suggestedRSASSAParameters.hash = rsaSSAParameters.hash;
229     }
230
231     // Other algorithms don't have parameters.
232     return true;
233 }
234
235 void JSCryptoKeySerializationJWK::reconcileUsages(CryptoKeyUsage& suggestedUsage) const
236 {
237     String jwkUseString;
238     if (!getStringFromJSON(m_exec, m_json.get(), "use", jwkUseString)) {
239         // "use" is optional in JWK.
240         return;
241     }
242
243     // FIXME: CryptoKeyUsageDeriveKey, CryptoKeyUsageDeriveBits - should these be implicitly allowed by any JWK use value?
244     // FIXME: "use" mapping is in flux, see <https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796>.
245     if (jwkUseString == "sig")
246         suggestedUsage = suggestedUsage & (CryptoKeyUsageSign | CryptoKeyUsageVerify);
247     else if (jwkUseString == "enc")
248         suggestedUsage = suggestedUsage & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey);
249     else if (jwkUseString == "wrap")
250         suggestedUsage = suggestedUsage & (CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey);
251     else
252         suggestedUsage = 0; // Unknown usage, better be safe.
253 }
254
255 void JSCryptoKeySerializationJWK::reconcileExtractable(bool& suggestedExtractable) const
256 {
257     bool jwkExtractable;
258     if (!getBooleanFromJSON(m_exec, m_json.get(), "extractable", jwkExtractable)) {
259         // "extractable" is a Netflix proposal that's not in any spec yet. It will certainly be optional once specified.
260         return;
261     }
262
263     suggestedExtractable = suggestedExtractable && jwkExtractable;
264 }
265
266 bool JSCryptoKeySerializationJWK::keySizeIsValid(size_t sizeInBits) const
267 {
268     if (m_jwkAlgorithmName == "HS256")
269         return sizeInBits >= 256;
270     if (m_jwkAlgorithmName == "HS384")
271         return sizeInBits >= 384;
272     if (m_jwkAlgorithmName == "HS512")
273         return sizeInBits >= 512;
274     if (m_jwkAlgorithmName == "A128CBC")
275         return sizeInBits == 128;
276     if (m_jwkAlgorithmName == "A192CBC")
277         return sizeInBits == 192;
278     if (m_jwkAlgorithmName == "A256CBC")
279         return sizeInBits == 256;
280     return true;
281 }
282
283 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyDataOctetSequence() const
284 {
285     String keyBase64URL;
286     if (!getStringFromJSON(m_exec, m_json.get(), "k", keyBase64URL)) {
287         if (!m_exec->hadException())
288             throwTypeError(m_exec, "Secret key data is not present is JWK");
289         return nullptr;
290     }
291
292     Vector<char> octetSequence;
293     if (!base64URLDecode(keyBase64URL, octetSequence)) {
294         throwTypeError(m_exec, "Cannot decode base64url key data in JWK");
295         return nullptr;
296     }
297
298     if (!keySizeIsValid(octetSequence.size() * 8)) {
299         throwTypeError(m_exec, "Key size is not valid for " + m_jwkAlgorithmName);
300         return nullptr;
301     }
302
303     return CryptoKeyDataOctetSequence::create(octetSequence);
304 }
305
306 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyDataRSAComponents() const
307 {
308     Vector<char> modulus;
309     Vector<char> exponent;
310     Vector<char> privateExponent;
311
312     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "n", modulus)) {
313         if (!m_exec->hadException())
314             throwTypeError(m_exec, "Required JWK \"n\" member is missing");
315         return nullptr;
316     }
317
318     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "e", exponent)) {
319         if (!m_exec->hadException())
320             throwTypeError(m_exec, "Required JWK \"e\" member is missing");
321         return nullptr;
322     }
323
324     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "d", modulus)) {
325         if (m_exec->hadException())
326             return nullptr;
327         return CryptoKeyDataRSAComponents::createPublic(modulus, exponent);
328     }
329
330     CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo;
331     CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo;
332     Vector<CryptoKeyDataRSAComponents::PrimeInfo> otherPrimeInfos;
333     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "p", firstPrimeInfo.primeFactor)) {
334         if (m_exec->hadException())
335             return nullptr;
336         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
337     }
338
339     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "dp", firstPrimeInfo.factorCRTExponent)) {
340         if (m_exec->hadException())
341             return nullptr;
342         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
343     }
344
345     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "q", secondPrimeInfo.primeFactor)) {
346         if (m_exec->hadException())
347             return nullptr;
348         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
349     }
350
351     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "dq", secondPrimeInfo.factorCRTExponent)) {
352         if (m_exec->hadException())
353             return nullptr;
354         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
355     }
356
357     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "qi", secondPrimeInfo.factorCRTCoefficient)) {
358         if (m_exec->hadException())
359             return nullptr;
360         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
361     }
362
363     JSArray* otherPrimeInfoJSArray;
364     if (!getJSArrayFromJSON(m_exec, m_json.get(), "oth", otherPrimeInfoJSArray)) {
365         if (m_exec->hadException())
366             return nullptr;
367         return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
368     }
369
370     for (size_t i = 0; i < otherPrimeInfoJSArray->length(); ++i) {
371         CryptoKeyDataRSAComponents::PrimeInfo info;
372         JSValue element = otherPrimeInfoJSArray->getIndex(m_exec, i);
373         if (m_exec->hadException())
374             return nullptr;
375         if (!element.isObject()) {
376             throwTypeError(m_exec, "JWK \"oth\" array member is not an object");
377             return nullptr;
378         }
379         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "r", info.primeFactor)) {
380             if (!m_exec->hadException())
381                 throwTypeError(m_exec, "Cannot get prime factor for a prime in \"oth\" dictionary");
382             return nullptr;
383         }
384         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "d", info.factorCRTExponent)) {
385             if (!m_exec->hadException())
386                 throwTypeError(m_exec, "Cannot get factor CRT exponent for a prime in \"oth\" dictionary");
387             return nullptr;
388         }
389         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "t", info.factorCRTCoefficient)) {
390             if (!m_exec->hadException())
391                 throwTypeError(m_exec, "Cannot get factor CRT coefficient for a prime in \"oth\" dictionary");
392             return nullptr;
393         }
394         otherPrimeInfos.append(info);
395     }
396
397     return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
398 }
399
400 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyData() const
401 {
402     String jwkKeyType;
403     if (!getStringFromJSON(m_exec, m_json.get(), "kty", jwkKeyType)) {
404         if (!m_exec->hadException())
405             throwTypeError(m_exec, "Required JWK \"kty\" member is missing");
406         return nullptr;
407     }
408
409     if (jwkKeyType == "oct")
410         return keyDataOctetSequence();
411
412     if (jwkKeyType == "RSA")
413         return keyDataRSAComponents();
414
415     throwTypeError(m_exec, "Unsupported JWK key type " + jwkKeyType);
416     return nullptr;
417 }
418
419 void JSCryptoKeySerializationJWK::buildJSONForOctetSequence(ExecState* exec, const Vector<char>& keyData, JSObject* result)
420 {
421     addToJSON(exec, result, "kty", "oct");
422     addToJSON(exec, result, "k", base64URLEncode(keyData));
423 }
424
425 void JSCryptoKeySerializationJWK::addToJSON(ExecState* exec, JSObject* json, const char* key, const String& value)
426 {
427     VM& vm = exec->vm();
428     Identifier identifier(&vm, key);
429     json->putDirect(vm, identifier, jsString(exec, value));
430 }
431
432 void JSCryptoKeySerializationJWK::addBoolToJSON(ExecState* exec, JSObject* json, const char* key, bool value)
433 {
434     VM& vm = exec->vm();
435     Identifier identifier(&vm, key);
436     json->putDirect(vm, identifier, jsBoolean(value));
437 }
438
439 void JSCryptoKeySerializationJWK::addJWKAlgorithmToJSON(ExecState* exec, JSObject* json, const CryptoKey& key)
440 {
441     String jwkAlgorithm;
442     switch (key.algorithmIdentifier()) {
443     case CryptoAlgorithmIdentifier::HMAC:
444         switch (toCryptoKeyHMAC(key).hashAlgorithmIdentifier()) {
445         case CryptoAlgorithmIdentifier::SHA_256:
446             if (toCryptoKeyHMAC(key).key().size() * 8 >= 256)
447                 jwkAlgorithm = "HS256";
448             break;
449         case CryptoAlgorithmIdentifier::SHA_384:
450             if (toCryptoKeyHMAC(key).key().size() * 8 >= 384)
451                 jwkAlgorithm = "HS384";
452             break;
453         case CryptoAlgorithmIdentifier::SHA_512:
454             if (toCryptoKeyHMAC(key).key().size() * 8 >= 512)
455                 jwkAlgorithm = "HS512";
456             break;
457         default:
458             break;
459         }
460         break;
461     case CryptoAlgorithmIdentifier::AES_CBC:
462         switch (toCryptoKeyAES(key).key().size() * 8) {
463         case 128:
464             jwkAlgorithm = "A128CBC";
465             break;
466         case 192:
467             jwkAlgorithm = "A192CBC";
468             break;
469         case 256:
470             jwkAlgorithm = "A256CBC";
471             break;
472         }
473         break;
474     default:
475         break;
476     }
477
478     if (jwkAlgorithm.isNull()) {
479         // The spec doesn't currently tell whether export should fail, or just skip "alg" (which is an optional key in JWK).
480         // Perhaps this should depend on whether the key is extractable?
481         throwTypeError(exec, "Key algorithm and size do not map to any JWK algorithm identifier");
482         return;
483     }
484
485     addToJSON(exec, json, "alg", jwkAlgorithm);
486 }
487
488 void JSCryptoKeySerializationJWK::addJWKUseToJSON(ExecState* exec, JSObject* json, CryptoKeyUsage usages)
489 {
490     // FIXME: "use" mapping is in flux, see <https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796>.
491     switch (usages) {
492     case CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey:
493         addToJSON(exec, json, "use", "enc");
494         break;
495     case CryptoKeyUsageSign | CryptoKeyUsageVerify:
496         addToJSON(exec, json, "use", "sig");
497         break;
498     default:
499         throwTypeError(exec, "Key usages cannot be represented in JWK. Only two variants are supported: sign+verify and encrypt+decrypt+wrapKey+unwrapKey");
500     }
501 }
502
503 String JSCryptoKeySerializationJWK::serialize(ExecState* exec, const CryptoKey& key)
504 {
505     std::unique_ptr<CryptoKeyData> keyData = key.exportData();
506     if (!keyData) {
507         // FIXME: Shouldn't happen once all key types implement exportData().
508         throwTypeError(exec, "Key doesn't support exportKey");
509         return String();
510     }
511
512     JSObject* result = constructEmptyObject(exec);
513
514     addJWKAlgorithmToJSON(exec, result, key);
515     if (exec->hadException())
516         return String();
517
518     addBoolToJSON(exec, result, "extractable", key.extractable());
519
520     addJWKUseToJSON(exec, result, key.usagesBitmap());
521     if (exec->hadException())
522         return String();
523
524     if (isCryptoKeyDataOctetSequence(*keyData))
525         buildJSONForOctetSequence(exec, toCryptoKeyDataOctetSequence(*keyData).octetSequence(), result);
526     else {
527         throwTypeError(exec, "Key doesn't support exportKey");
528         return String();
529     }
530     ASSERT(!exec->hadException());
531
532     return JSONStringify(exec, result, 4);
533 }
534
535 } // namespace WebCore
536
537 #endif // ENABLE(SUBTLE_CRYPTO)