Upstream version 10.39.225.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 #include "wtf/ArrayBufferView.h"
44
45 namespace blink {
46
47 // Seems like the generated bindings should take care of these however it
48 // currently doesn't. See also http://crbug.com/264520
49 static bool ensureNotNull(const ArrayPiece& x, const char* paramName, CryptoResult* result)
50 {
51     if (x.isNull()) {
52         String message = String("Invalid ") + paramName + String(" argument");
53         result->completeWithError(WebCryptoErrorTypeType, WebString(message));
54         return false;
55     }
56     return true;
57 }
58
59 static bool ensureNotNull(CryptoKey* key, const char* paramName, CryptoResult* result)
60 {
61     if (!key) {
62         String message = String("Invalid ") + paramName + String(" argument");
63         result->completeWithError(WebCryptoErrorTypeType, WebString(message));
64         return false;
65     }
66     return true;
67 }
68
69 static bool parseAlgorithm(const Dictionary& raw, WebCryptoOperation op, WebCryptoAlgorithm& algorithm, CryptoResult* result)
70 {
71     AlgorithmError error;
72     bool success = normalizeAlgorithm(raw, op, algorithm, &error);
73     if (!success)
74         result->completeWithError(error.errorType, error.errorDetails);
75     return success;
76 }
77
78 static bool canAccessWebCrypto(ScriptState* scriptState, CryptoResult* result)
79 {
80     const SecurityOrigin* origin = scriptState->executionContext()->securityOrigin();
81     String errorMessage;
82     if (!origin->canAccessFeatureRequiringSecureOrigin(errorMessage)) {
83         result->completeWithError(WebCryptoErrorTypeNotSupported, errorMessage);
84         return false;
85     }
86
87     return true;
88 }
89
90 static ScriptPromise startCryptoOperation(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, WebCryptoOperation operationType, const ArrayPiece& signature, const ArrayPiece& dataBuffer)
91 {
92     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
93     ScriptPromise promise = result->promise();
94
95     if (!canAccessWebCrypto(scriptState, result.get()))
96         return promise;
97
98     bool requiresKey = operationType != WebCryptoOperationDigest;
99
100     if (requiresKey && !ensureNotNull(key, "key", result.get()))
101         return promise;
102     if (operationType == WebCryptoOperationVerify && !ensureNotNull(signature, "signature", result.get()))
103         return promise;
104     if (!ensureNotNull(dataBuffer, "dataBuffer", result.get()))
105         return promise;
106
107     WebCryptoAlgorithm algorithm;
108     if (!parseAlgorithm(rawAlgorithm, operationType, algorithm, result.get()))
109         return promise;
110
111     if (requiresKey && !key->canBeUsedForAlgorithm(algorithm, operationType, result.get()))
112         return promise;
113
114     const unsigned char* data = dataBuffer.bytes();
115     unsigned dataSize = dataBuffer.byteLength();
116
117     switch (operationType) {
118     case WebCryptoOperationEncrypt:
119         Platform::current()->crypto()->encrypt(algorithm, key->key(), data, dataSize, result->result());
120         break;
121     case WebCryptoOperationDecrypt:
122         Platform::current()->crypto()->decrypt(algorithm, key->key(), data, dataSize, result->result());
123         break;
124     case WebCryptoOperationSign:
125         Platform::current()->crypto()->sign(algorithm, key->key(), data, dataSize, result->result());
126         break;
127     case WebCryptoOperationVerify:
128         Platform::current()->crypto()->verifySignature(algorithm, key->key(), signature.bytes(), signature.byteLength(), data, dataSize, result->result());
129         break;
130     case WebCryptoOperationDigest:
131         Platform::current()->crypto()->digest(algorithm, data, dataSize, result->result());
132         break;
133     default:
134         ASSERT_NOT_REACHED();
135         return ScriptPromise();
136     }
137
138     return promise;
139 }
140
141 static bool copyStringProperty(const char* property, const Dictionary& source, JSONObject* destination)
142 {
143     String value;
144     if (!DictionaryHelper::get(source, property, value))
145         return false;
146     destination->setString(property, value);
147     return true;
148 }
149
150 static bool copySequenceOfStringProperty(const char* property, const Dictionary& source, JSONObject* destination)
151 {
152     Vector<String> value;
153     if (!DictionaryHelper::get(source, property, value))
154         return false;
155     RefPtr<JSONArray> jsonArray = JSONArray::create();
156     for (unsigned i = 0; i < value.size(); ++i)
157         jsonArray->pushString(value[i]);
158     destination->setArray(property, jsonArray.release());
159     return true;
160 }
161
162 // FIXME: At the time of writing this is not a part of the spec. It is based an
163 // an unpublished editor's draft for:
164 //   https://www.w3.org/Bugs/Public/show_bug.cgi?id=24963
165 // See http://crbug.com/373917.
166 static bool copyJwkDictionaryToJson(const Dictionary& dict, CString& jsonUtf8, CryptoResult* result)
167 {
168     RefPtr<JSONObject> jsonObject = JSONObject::create();
169
170     if (!copyStringProperty("kty", dict, jsonObject.get())) {
171         result->completeWithError(WebCryptoErrorTypeData, "The required JWK property \"kty\" was missing");
172         return false;
173     }
174
175     copyStringProperty("use", dict, jsonObject.get());
176     copySequenceOfStringProperty("key_ops", dict, jsonObject.get());
177     copyStringProperty("alg", dict, jsonObject.get());
178
179     bool ext;
180     if (DictionaryHelper::get(dict, "ext", ext))
181         jsonObject->setBoolean("ext", ext);
182
183     const char* const propertyNames[] = { "d", "n", "e", "p", "q", "dp", "dq", "qi", "k" };
184     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(propertyNames); ++i)
185         copyStringProperty(propertyNames[i], dict, jsonObject.get());
186
187     String json = jsonObject->toJSONString();
188     jsonUtf8 = json.utf8();
189     return true;
190 }
191
192 SubtleCrypto::SubtleCrypto()
193 {
194 }
195
196 ScriptPromise SubtleCrypto::encrypt(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const ArrayPiece& data)
197 {
198     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationEncrypt, ArrayPiece(), data);
199 }
200
201 ScriptPromise SubtleCrypto::decrypt(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const ArrayPiece& data)
202 {
203     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationDecrypt, ArrayPiece(), data);
204 }
205
206 ScriptPromise SubtleCrypto::sign(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const ArrayPiece& data)
207 {
208     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationSign, ArrayPiece(), data);
209 }
210
211 ScriptPromise SubtleCrypto::verifySignature(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const ArrayPiece& signature, const ArrayPiece& data)
212 {
213     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationVerify, signature, data);
214 }
215
216 ScriptPromise SubtleCrypto::digest(ScriptState* scriptState, const Dictionary& rawAlgorithm, const ArrayPiece& data)
217 {
218     return startCryptoOperation(scriptState, rawAlgorithm, 0, WebCryptoOperationDigest, ArrayPiece(), data);
219 }
220
221 ScriptPromise SubtleCrypto::generateKey(ScriptState* scriptState, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
222 {
223     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
224     ScriptPromise promise = result->promise();
225
226     if (!canAccessWebCrypto(scriptState, result.get()))
227         return promise;
228
229     WebCryptoKeyUsageMask keyUsages;
230     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
231         return promise;
232
233     WebCryptoAlgorithm algorithm;
234     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationGenerateKey, algorithm, result.get()))
235         return promise;
236
237     Platform::current()->crypto()->generateKey(algorithm, extractable, keyUsages, result->result());
238     return promise;
239 }
240
241 ScriptPromise SubtleCrypto::importKey(ScriptState* scriptState, const String& rawFormat, const ArrayPiece& keyData, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
242 {
243     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
244     ScriptPromise promise = result->promise();
245
246     if (!canAccessWebCrypto(scriptState, result.get()))
247         return promise;
248
249     if (!ensureNotNull(keyData, "keyData", result.get()))
250         return promise;
251
252     WebCryptoKeyFormat format;
253     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
254         return promise;
255
256     if (format == WebCryptoKeyFormatJwk) {
257         result->completeWithError(WebCryptoErrorTypeData, "Key data must be an object for JWK import");
258         return promise;
259     }
260
261     WebCryptoKeyUsageMask keyUsages;
262     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
263         return promise;
264
265     WebCryptoAlgorithm algorithm;
266     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationImportKey, algorithm, result.get()))
267         return promise;
268
269     Platform::current()->crypto()->importKey(format, keyData.bytes(), keyData.byteLength(), algorithm, extractable, keyUsages, result->result());
270     return promise;
271 }
272
273 ScriptPromise SubtleCrypto::importKey(ScriptState* scriptState, const String& rawFormat, const Dictionary& keyData, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
274 {
275     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
276     ScriptPromise promise = result->promise();
277
278     if (!canAccessWebCrypto(scriptState, result.get()))
279         return promise;
280
281     WebCryptoKeyFormat format;
282     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
283         return promise;
284
285     WebCryptoKeyUsageMask keyUsages;
286     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
287         return promise;
288
289     if (format != WebCryptoKeyFormatJwk) {
290         result->completeWithError(WebCryptoErrorTypeData, "Key data must be a buffer for non-JWK formats");
291         return promise;
292     }
293
294     WebCryptoAlgorithm algorithm;
295     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationImportKey, algorithm, result.get()))
296         return promise;
297
298     CString jsonUtf8;
299     if (!copyJwkDictionaryToJson(keyData, jsonUtf8, result.get()))
300         return promise;
301
302     Platform::current()->crypto()->importKey(format, reinterpret_cast<const unsigned char*>(jsonUtf8.data()), jsonUtf8.length(), algorithm, extractable, keyUsages, result->result());
303     return promise;
304 }
305
306 ScriptPromise SubtleCrypto::exportKey(ScriptState* scriptState, const String& rawFormat, CryptoKey* key)
307 {
308     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
309     ScriptPromise promise = result->promise();
310
311     if (!canAccessWebCrypto(scriptState, result.get()))
312         return promise;
313
314     if (!ensureNotNull(key, "key", result.get()))
315         return promise;
316
317     WebCryptoKeyFormat format;
318     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
319         return promise;
320
321     if (!key->extractable()) {
322         result->completeWithError(WebCryptoErrorTypeInvalidAccess, "key is not extractable");
323         return promise;
324     }
325
326     Platform::current()->crypto()->exportKey(format, key->key(), result->result());
327     return promise;
328 }
329
330 ScriptPromise SubtleCrypto::wrapKey(ScriptState* scriptState, const String& rawFormat, CryptoKey* key, CryptoKey* wrappingKey, const Dictionary& rawWrapAlgorithm)
331 {
332     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
333     ScriptPromise promise = result->promise();
334
335     if (!canAccessWebCrypto(scriptState, result.get()))
336         return promise;
337
338     if (!ensureNotNull(key, "key", result.get()))
339         return promise;
340
341     if (!ensureNotNull(wrappingKey, "wrappingKey", result.get()))
342         return promise;
343
344     WebCryptoKeyFormat format;
345     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
346         return promise;
347
348     WebCryptoAlgorithm wrapAlgorithm;
349     if (!parseAlgorithm(rawWrapAlgorithm, WebCryptoOperationWrapKey, wrapAlgorithm, result.get()))
350         return promise;
351
352     if (!key->extractable()) {
353         result->completeWithError(WebCryptoErrorTypeInvalidAccess, "key is not extractable");
354         return promise;
355     }
356
357     if (!wrappingKey->canBeUsedForAlgorithm(wrapAlgorithm, WebCryptoOperationWrapKey, result.get()))
358         return promise;
359
360     Platform::current()->crypto()->wrapKey(format, key->key(), wrappingKey->key(), wrapAlgorithm, result->result());
361     return promise;
362 }
363
364 ScriptPromise SubtleCrypto::unwrapKey(ScriptState* scriptState, const String& rawFormat, const ArrayPiece& wrappedKey, CryptoKey* unwrappingKey, const Dictionary& rawUnwrapAlgorithm, const Dictionary& rawUnwrappedKeyAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
365 {
366     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
367     ScriptPromise promise = result->promise();
368
369     if (!canAccessWebCrypto(scriptState, result.get()))
370         return promise;
371
372     if (!ensureNotNull(wrappedKey, "wrappedKey", result.get()))
373         return promise;
374     if (!ensureNotNull(unwrappingKey, "unwrappingKey", result.get()))
375         return promise;
376
377     WebCryptoKeyFormat format;
378     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
379         return promise;
380
381     WebCryptoKeyUsageMask keyUsages;
382     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
383         return promise;
384
385     WebCryptoAlgorithm unwrapAlgorithm;
386     if (!parseAlgorithm(rawUnwrapAlgorithm, WebCryptoOperationUnwrapKey, unwrapAlgorithm, result.get()))
387         return promise;
388
389     WebCryptoAlgorithm unwrappedKeyAlgorithm;
390     if (!parseAlgorithm(rawUnwrappedKeyAlgorithm, WebCryptoOperationImportKey, unwrappedKeyAlgorithm, result.get()))
391         return promise;
392
393     if (!unwrappingKey->canBeUsedForAlgorithm(unwrapAlgorithm, WebCryptoOperationUnwrapKey, result.get()))
394         return promise;
395
396     Platform::current()->crypto()->unwrapKey(format, wrappedKey.bytes(), wrappedKey.byteLength(), unwrappingKey->key(), unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages, result->result());
397     return promise;
398 }
399
400 } // namespace blink