Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / crypto / SubtleCrypto.cpp
1 /*
2  * Copyright (C) 2013 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "modules/crypto/SubtleCrypto.h"
33
34 #include "bindings/core/v8/Dictionary.h"
35 #include "core/dom/ExecutionContext.h"
36 #include "modules/crypto/CryptoKey.h"
37 #include "modules/crypto/CryptoResultImpl.h"
38 #include "modules/crypto/NormalizeAlgorithm.h"
39 #include "platform/JSONValues.h"
40 #include "public/platform/Platform.h"
41 #include "public/platform/WebCrypto.h"
42 #include "public/platform/WebCryptoAlgorithm.h"
43
44 namespace blink {
45
46 // Seems like the generated bindings should take care of these however it
47 // currently doesn't. See also http://crbug.com/264520
48 static bool ensureNotNull(const DOMArrayPiece& x, const char* paramName, CryptoResult* result)
49 {
50     if (x.isNull()) {
51         String message = String("Invalid ") + paramName + String(" argument");
52         result->completeWithError(WebCryptoErrorTypeType, WebString(message));
53         return false;
54     }
55     return true;
56 }
57
58 static bool ensureNotNull(CryptoKey* key, const char* paramName, CryptoResult* result)
59 {
60     if (!key) {
61         String message = String("Invalid ") + paramName + String(" argument");
62         result->completeWithError(WebCryptoErrorTypeType, WebString(message));
63         return false;
64     }
65     return true;
66 }
67
68 static bool parseAlgorithm(const Dictionary& raw, WebCryptoOperation op, WebCryptoAlgorithm& algorithm, CryptoResult* result)
69 {
70     AlgorithmError error;
71     bool success = normalizeAlgorithm(raw, op, algorithm, &error);
72     if (!success)
73         result->completeWithError(error.errorType, error.errorDetails);
74     return success;
75 }
76
77 static bool canAccessWebCrypto(ScriptState* scriptState, CryptoResult* result)
78 {
79     const SecurityOrigin* origin = scriptState->executionContext()->securityOrigin();
80     String errorMessage;
81     if (!origin->canAccessFeatureRequiringSecureOrigin(errorMessage)) {
82         result->completeWithError(WebCryptoErrorTypeNotSupported, errorMessage);
83         return false;
84     }
85
86     return true;
87 }
88
89 static ScriptPromise startCryptoOperation(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, WebCryptoOperation operationType, const DOMArrayPiece& signature, const DOMArrayPiece& dataBuffer)
90 {
91     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
92     ScriptPromise promise = result->promise();
93
94     if (!canAccessWebCrypto(scriptState, result.get()))
95         return promise;
96
97     bool requiresKey = operationType != WebCryptoOperationDigest;
98
99     if (requiresKey && !ensureNotNull(key, "key", result.get()))
100         return promise;
101     if (operationType == WebCryptoOperationVerify && !ensureNotNull(signature, "signature", result.get()))
102         return promise;
103     if (!ensureNotNull(dataBuffer, "dataBuffer", result.get()))
104         return promise;
105
106     WebCryptoAlgorithm algorithm;
107     if (!parseAlgorithm(rawAlgorithm, operationType, algorithm, result.get()))
108         return promise;
109
110     if (requiresKey && !key->canBeUsedForAlgorithm(algorithm, operationType, result.get()))
111         return promise;
112
113     const unsigned char* data = dataBuffer.bytes();
114     unsigned dataSize = dataBuffer.byteLength();
115
116     switch (operationType) {
117     case WebCryptoOperationEncrypt:
118         Platform::current()->crypto()->encrypt(algorithm, key->key(), data, dataSize, result->result());
119         break;
120     case WebCryptoOperationDecrypt:
121         Platform::current()->crypto()->decrypt(algorithm, key->key(), data, dataSize, result->result());
122         break;
123     case WebCryptoOperationSign:
124         Platform::current()->crypto()->sign(algorithm, key->key(), data, dataSize, result->result());
125         break;
126     case WebCryptoOperationVerify:
127         Platform::current()->crypto()->verifySignature(algorithm, key->key(), signature.bytes(), signature.byteLength(), data, dataSize, result->result());
128         break;
129     case WebCryptoOperationDigest:
130         Platform::current()->crypto()->digest(algorithm, data, dataSize, result->result());
131         break;
132     default:
133         ASSERT_NOT_REACHED();
134         return ScriptPromise();
135     }
136
137     return promise;
138 }
139
140 static bool copyStringProperty(const char* property, const Dictionary& source, JSONObject* destination)
141 {
142     String value;
143     if (!DictionaryHelper::get(source, property, value))
144         return false;
145     destination->setString(property, value);
146     return true;
147 }
148
149 static bool copySequenceOfStringProperty(const char* property, const Dictionary& source, JSONObject* destination)
150 {
151     Vector<String> value;
152     if (!DictionaryHelper::get(source, property, value))
153         return false;
154     RefPtr<JSONArray> jsonArray = JSONArray::create();
155     for (unsigned i = 0; i < value.size(); ++i)
156         jsonArray->pushString(value[i]);
157     destination->setArray(property, jsonArray.release());
158     return true;
159 }
160
161 // FIXME: At the time of writing this is not a part of the spec. It is based an
162 // an unpublished editor's draft for:
163 //   https://www.w3.org/Bugs/Public/show_bug.cgi?id=24963
164 // See http://crbug.com/373917.
165 static bool copyJwkDictionaryToJson(const Dictionary& dict, CString& jsonUtf8, CryptoResult* result)
166 {
167     RefPtr<JSONObject> jsonObject = JSONObject::create();
168
169     if (!copyStringProperty("kty", dict, jsonObject.get())) {
170         result->completeWithError(WebCryptoErrorTypeData, "The required JWK property \"kty\" was missing");
171         return false;
172     }
173
174     copyStringProperty("use", dict, jsonObject.get());
175     copySequenceOfStringProperty("key_ops", dict, jsonObject.get());
176     copyStringProperty("alg", dict, jsonObject.get());
177
178     bool ext;
179     if (DictionaryHelper::get(dict, "ext", ext))
180         jsonObject->setBoolean("ext", ext);
181
182     const char* const propertyNames[] = { "d", "n", "e", "p", "q", "dp", "dq", "qi", "k" };
183     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(propertyNames); ++i)
184         copyStringProperty(propertyNames[i], dict, jsonObject.get());
185
186     String json = jsonObject->toJSONString();
187     jsonUtf8 = json.utf8();
188     return true;
189 }
190
191 SubtleCrypto::SubtleCrypto()
192 {
193 }
194
195 ScriptPromise SubtleCrypto::encrypt(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const DOMArrayPiece& data)
196 {
197     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationEncrypt, DOMArrayPiece(), data);
198 }
199
200 ScriptPromise SubtleCrypto::decrypt(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const DOMArrayPiece& data)
201 {
202     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationDecrypt, DOMArrayPiece(), data);
203 }
204
205 ScriptPromise SubtleCrypto::sign(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const DOMArrayPiece& data)
206 {
207     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationSign, DOMArrayPiece(), data);
208 }
209
210 ScriptPromise SubtleCrypto::verifySignature(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const DOMArrayPiece& signature, const DOMArrayPiece& data)
211 {
212     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationVerify, signature, data);
213 }
214
215 ScriptPromise SubtleCrypto::digest(ScriptState* scriptState, const Dictionary& rawAlgorithm, const DOMArrayPiece& data)
216 {
217     return startCryptoOperation(scriptState, rawAlgorithm, 0, WebCryptoOperationDigest, DOMArrayPiece(), data);
218 }
219
220 ScriptPromise SubtleCrypto::generateKey(ScriptState* scriptState, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
221 {
222     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
223     ScriptPromise promise = result->promise();
224
225     if (!canAccessWebCrypto(scriptState, result.get()))
226         return promise;
227
228     WebCryptoKeyUsageMask keyUsages;
229     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
230         return promise;
231
232     WebCryptoAlgorithm algorithm;
233     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationGenerateKey, algorithm, result.get()))
234         return promise;
235
236     Platform::current()->crypto()->generateKey(algorithm, extractable, keyUsages, result->result());
237     return promise;
238 }
239
240 ScriptPromise SubtleCrypto::importKey(ScriptState* scriptState, const String& rawFormat, const DOMArrayPiece& keyData, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
241 {
242     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
243     ScriptPromise promise = result->promise();
244
245     if (!canAccessWebCrypto(scriptState, result.get()))
246         return promise;
247
248     if (!ensureNotNull(keyData, "keyData", result.get()))
249         return promise;
250
251     WebCryptoKeyFormat format;
252     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
253         return promise;
254
255     if (format == WebCryptoKeyFormatJwk) {
256         result->completeWithError(WebCryptoErrorTypeData, "Key data must be an object for JWK import");
257         return promise;
258     }
259
260     WebCryptoKeyUsageMask keyUsages;
261     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
262         return promise;
263
264     WebCryptoAlgorithm algorithm;
265     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationImportKey, algorithm, result.get()))
266         return promise;
267
268     Platform::current()->crypto()->importKey(format, keyData.bytes(), keyData.byteLength(), algorithm, extractable, keyUsages, result->result());
269     return promise;
270 }
271
272 ScriptPromise SubtleCrypto::importKey(ScriptState* scriptState, const String& rawFormat, const Dictionary& keyData, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
273 {
274     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
275     ScriptPromise promise = result->promise();
276
277     if (!canAccessWebCrypto(scriptState, result.get()))
278         return promise;
279
280     WebCryptoKeyFormat format;
281     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
282         return promise;
283
284     WebCryptoKeyUsageMask keyUsages;
285     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
286         return promise;
287
288     if (format != WebCryptoKeyFormatJwk) {
289         result->completeWithError(WebCryptoErrorTypeData, "Key data must be a buffer for non-JWK formats");
290         return promise;
291     }
292
293     WebCryptoAlgorithm algorithm;
294     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationImportKey, algorithm, result.get()))
295         return promise;
296
297     CString jsonUtf8;
298     if (!copyJwkDictionaryToJson(keyData, jsonUtf8, result.get()))
299         return promise;
300
301     Platform::current()->crypto()->importKey(format, reinterpret_cast<const unsigned char*>(jsonUtf8.data()), jsonUtf8.length(), algorithm, extractable, keyUsages, result->result());
302     return promise;
303 }
304
305 ScriptPromise SubtleCrypto::exportKey(ScriptState* scriptState, const String& rawFormat, CryptoKey* key)
306 {
307     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
308     ScriptPromise promise = result->promise();
309
310     if (!canAccessWebCrypto(scriptState, result.get()))
311         return promise;
312
313     if (!ensureNotNull(key, "key", result.get()))
314         return promise;
315
316     WebCryptoKeyFormat format;
317     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
318         return promise;
319
320     if (!key->extractable()) {
321         result->completeWithError(WebCryptoErrorTypeInvalidAccess, "key is not extractable");
322         return promise;
323     }
324
325     Platform::current()->crypto()->exportKey(format, key->key(), result->result());
326     return promise;
327 }
328
329 ScriptPromise SubtleCrypto::wrapKey(ScriptState* scriptState, const String& rawFormat, CryptoKey* key, CryptoKey* wrappingKey, const Dictionary& rawWrapAlgorithm)
330 {
331     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
332     ScriptPromise promise = result->promise();
333
334     if (!canAccessWebCrypto(scriptState, result.get()))
335         return promise;
336
337     if (!ensureNotNull(key, "key", result.get()))
338         return promise;
339
340     if (!ensureNotNull(wrappingKey, "wrappingKey", result.get()))
341         return promise;
342
343     WebCryptoKeyFormat format;
344     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
345         return promise;
346
347     WebCryptoAlgorithm wrapAlgorithm;
348     if (!parseAlgorithm(rawWrapAlgorithm, WebCryptoOperationWrapKey, wrapAlgorithm, result.get()))
349         return promise;
350
351     if (!key->extractable()) {
352         result->completeWithError(WebCryptoErrorTypeInvalidAccess, "key is not extractable");
353         return promise;
354     }
355
356     if (!wrappingKey->canBeUsedForAlgorithm(wrapAlgorithm, WebCryptoOperationWrapKey, result.get()))
357         return promise;
358
359     Platform::current()->crypto()->wrapKey(format, key->key(), wrappingKey->key(), wrapAlgorithm, result->result());
360     return promise;
361 }
362
363 ScriptPromise SubtleCrypto::unwrapKey(ScriptState* scriptState, const String& rawFormat, const DOMArrayPiece& wrappedKey, CryptoKey* unwrappingKey, const Dictionary& rawUnwrapAlgorithm, const Dictionary& rawUnwrappedKeyAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
364 {
365     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
366     ScriptPromise promise = result->promise();
367
368     if (!canAccessWebCrypto(scriptState, result.get()))
369         return promise;
370
371     if (!ensureNotNull(wrappedKey, "wrappedKey", result.get()))
372         return promise;
373     if (!ensureNotNull(unwrappingKey, "unwrappingKey", result.get()))
374         return promise;
375
376     WebCryptoKeyFormat format;
377     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
378         return promise;
379
380     WebCryptoKeyUsageMask keyUsages;
381     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
382         return promise;
383
384     WebCryptoAlgorithm unwrapAlgorithm;
385     if (!parseAlgorithm(rawUnwrapAlgorithm, WebCryptoOperationUnwrapKey, unwrapAlgorithm, result.get()))
386         return promise;
387
388     WebCryptoAlgorithm unwrappedKeyAlgorithm;
389     if (!parseAlgorithm(rawUnwrappedKeyAlgorithm, WebCryptoOperationImportKey, unwrappedKeyAlgorithm, result.get()))
390         return promise;
391
392     if (!unwrappingKey->canBeUsedForAlgorithm(unwrapAlgorithm, WebCryptoOperationUnwrapKey, result.get()))
393         return promise;
394
395     Platform::current()->crypto()->unwrapKey(format, wrappedKey.bytes(), wrappedKey.byteLength(), unwrappingKey->key(), unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages, result->result());
396     return promise;
397 }
398
399 } // namespace blink