2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "modules/crypto/NormalizeAlgorithm.h"
34 #include "bindings/core/v8/Dictionary.h"
35 #include "platform/NotImplemented.h"
36 #include "public/platform/WebCryptoAlgorithmParams.h"
37 #include "public/platform/WebString.h"
38 #include "wtf/ArrayBuffer.h"
39 #include "wtf/ArrayBufferView.h"
40 #include "wtf/MathExtras.h"
41 #include "wtf/Uint8Array.h"
42 #include "wtf/Vector.h"
43 #include "wtf/text/StringBuilder.h"
50 struct AlgorithmNameMapping {
51 // Must be an upper case ASCII string.
52 const char* const algorithmName;
53 // Must be strlen(algorithmName).
54 unsigned char algorithmNameLength;
55 WebCryptoAlgorithmId algorithmId;
58 bool operator<(const AlgorithmNameMapping&) const;
62 // Must be sorted by length, and then by reverse string.
63 // Also all names must be upper case ASCII.
64 const AlgorithmNameMapping algorithmNameMappings[] = {
65 {"HMAC", 4, WebCryptoAlgorithmIdHmac},
66 {"SHA-1", 5, WebCryptoAlgorithmIdSha1},
67 {"AES-KW", 6, WebCryptoAlgorithmIdAesKw},
68 {"SHA-512", 7, WebCryptoAlgorithmIdSha512},
69 {"SHA-384", 7, WebCryptoAlgorithmIdSha384},
70 {"SHA-256", 7, WebCryptoAlgorithmIdSha256},
71 {"AES-CBC", 7, WebCryptoAlgorithmIdAesCbc},
72 {"AES-GCM", 7, WebCryptoAlgorithmIdAesGcm},
73 {"AES-CTR", 7, WebCryptoAlgorithmIdAesCtr},
74 {"RSA-OAEP", 8, WebCryptoAlgorithmIdRsaOaep},
75 {"RSASSA-PKCS1-V1_5", 17, WebCryptoAlgorithmIdRsaSsaPkcs1v1_5},
80 // Essentially std::is_sorted() (however that function is new to C++11).
81 template <typename Iterator>
82 bool isSorted(Iterator begin, Iterator end)
87 Iterator prev = begin;
88 Iterator cur = begin + 1;
100 bool AlgorithmNameMapping::operator<(const AlgorithmNameMapping& o) const
102 if (algorithmNameLength < o.algorithmNameLength)
104 if (algorithmNameLength > o.algorithmNameLength)
107 for (size_t i = 0; i < algorithmNameLength; ++i) {
108 size_t reverseIndex = algorithmNameLength - i - 1;
109 char c1 = algorithmName[reverseIndex];
110 char c2 = o.algorithmName[reverseIndex];
121 bool verifyAlgorithmNameMappings(const AlgorithmNameMapping* begin, const AlgorithmNameMapping* end)
123 for (const AlgorithmNameMapping* it = begin; it != end; ++it) {
124 if (it->algorithmNameLength != strlen(it->algorithmName))
126 String str(it->algorithmName, it->algorithmNameLength);
127 if (!str.containsOnlyASCII())
129 if (str.upper() != str)
133 return isSorted(begin, end);
137 template <typename CharType>
138 bool algorithmNameComparator(const AlgorithmNameMapping& a, StringImpl* b)
140 if (a.algorithmNameLength < b->length())
142 if (a.algorithmNameLength > b->length())
145 // Because the algorithm names contain many common prefixes, it is better
146 // to compare starting at the end of the string.
147 for (size_t i = 0; i < a.algorithmNameLength; ++i) {
148 size_t reverseIndex = a.algorithmNameLength - i - 1;
149 CharType c1 = a.algorithmName[reverseIndex];
150 CharType c2 = b->getCharacters<CharType>()[reverseIndex];
153 c2 = toASCIIUpper(c2);
164 bool lookupAlgorithmIdByName(const String& algorithmName, WebCryptoAlgorithmId& id)
166 const AlgorithmNameMapping* begin = algorithmNameMappings;
167 const AlgorithmNameMapping* end = algorithmNameMappings + WTF_ARRAY_LENGTH(algorithmNameMappings);
169 ASSERT(verifyAlgorithmNameMappings(begin, end));
171 const AlgorithmNameMapping* it;
172 if (algorithmName.impl()->is8Bit())
173 it = std::lower_bound(begin, end, algorithmName.impl(), &algorithmNameComparator<LChar>);
175 it = std::lower_bound(begin, end, algorithmName.impl(), &algorithmNameComparator<UChar>);
180 if (it->algorithmNameLength != algorithmName.length() || !equalIgnoringCase(algorithmName, it->algorithmName))
183 id = it->algorithmId;
187 void setSyntaxError(const String& message, AlgorithmError* error)
189 error->errorType = WebCryptoErrorTypeSyntax;
190 error->errorDetails = message;
193 void setNotSupportedError(const String& message, AlgorithmError* error)
195 error->errorType = WebCryptoErrorTypeNotSupported;
196 error->errorDetails = message;
199 void setDataError(const String& message, AlgorithmError* error)
201 error->errorType = WebCryptoErrorTypeData;
202 error->errorDetails = message;
205 // ErrorContext holds a stack of string literals which describe what was
206 // happening at the time the error occurred. This is helpful because
207 // parsing of the algorithm dictionary can be recursive and it is difficult to
208 // tell what went wrong from a failure alone.
211 void add(const char* message)
213 m_messages.append(message);
218 m_messages.removeLast();
221 // Join all of the string literals into a single String.
222 String toString() const
224 if (m_messages.isEmpty())
227 StringBuilder result;
228 const char* Separator = ": ";
230 size_t length = (m_messages.size() - 1) * strlen(Separator);
231 for (size_t i = 0; i < m_messages.size(); ++i)
232 length += strlen(m_messages[i]);
233 result.reserveCapacity(length);
235 for (size_t i = 0; i < m_messages.size(); ++i) {
237 result.append(Separator, strlen(Separator));
238 result.append(m_messages[i], strlen(m_messages[i]));
241 return result.toString();
244 String toString(const char* message) const
246 ErrorContext stack(*this);
248 return stack.toString();
251 String toString(const char* message1, const char* message2) const
253 ErrorContext stack(*this);
256 return stack.toString();
260 // This inline size is large enough to avoid having to grow the Vector in
261 // the majority of cases (up to 1 nested algorithm identifier).
262 Vector<const char*, 10> m_messages;
265 // Defined by the WebCrypto spec as:
267 // typedef (ArrayBuffer or ArrayBufferView) CryptoOperationData;
269 // FIXME: Currently only supports ArrayBufferView.
270 bool getOptionalCryptoOperationData(const Dictionary& raw, const char* propertyName, bool& hasProperty, RefPtr<ArrayBufferView>& buffer, const ErrorContext& context, AlgorithmError* error)
272 if (!DictionaryHelper::get(raw, propertyName, buffer)) {
280 setSyntaxError(context.toString(propertyName, "Not an ArrayBufferView"), error);
287 // Defined by the WebCrypto spec as:
289 // typedef (ArrayBuffer or ArrayBufferView) CryptoOperationData;
291 // FIXME: Currently only supports ArrayBufferView.
292 bool getCryptoOperationData(const Dictionary& raw, const char* propertyName, RefPtr<ArrayBufferView>& buffer, const ErrorContext& context, AlgorithmError* error)
295 bool ok = getOptionalCryptoOperationData(raw, propertyName, hasProperty, buffer, context, error);
297 setSyntaxError(context.toString(propertyName, "Missing required property"), error);
303 bool getUint8Array(const Dictionary& raw, const char* propertyName, RefPtr<Uint8Array>& array, const ErrorContext& context, AlgorithmError* error)
305 if (!DictionaryHelper::get(raw, propertyName, array) || !array) {
306 setSyntaxError(context.toString(propertyName, "Missing or not a Uint8Array"), error);
312 // Defined by the WebCrypto spec as:
314 // typedef Uint8Array BigInteger;
315 bool getBigInteger(const Dictionary& raw, const char* propertyName, RefPtr<Uint8Array>& array, const ErrorContext& context, AlgorithmError* error)
317 if (!getUint8Array(raw, propertyName, array, context, error))
320 if (!array->byteLength()) {
321 setSyntaxError(context.toString(propertyName, "BigInteger should not be empty"), error);
325 if (!DictionaryHelper::get(raw, propertyName, array) || !array) {
326 setSyntaxError(context.toString(propertyName, "Missing or not a Uint8Array"), error);
332 // Gets an integer according to WebIDL's [EnforceRange].
333 bool getOptionalInteger(const Dictionary& raw, const char* propertyName, bool& hasProperty, double& value, double minValue, double maxValue, const ErrorContext& context, AlgorithmError* error)
336 bool ok = DictionaryHelper::get(raw, propertyName, number, hasProperty);
341 if (!ok || std::isnan(number)) {
342 setSyntaxError(context.toString(propertyName, "Is not a number"), error);
346 number = trunc(number);
348 if (std::isinf(number) || number < minValue || number > maxValue) {
349 setSyntaxError(context.toString(propertyName, "Outside of numeric range"), error);
357 bool getInteger(const Dictionary& raw, const char* propertyName, double& value, double minValue, double maxValue, const ErrorContext& context, AlgorithmError* error)
360 if (!getOptionalInteger(raw, propertyName, hasProperty, value, minValue, maxValue, context, error))
364 setSyntaxError(context.toString(propertyName, "Missing required property"), error);
371 bool getUint32(const Dictionary& raw, const char* propertyName, uint32_t& value, const ErrorContext& context, AlgorithmError* error)
374 if (!getInteger(raw, propertyName, number, 0, 0xFFFFFFFF, context, error))
380 bool getUint16(const Dictionary& raw, const char* propertyName, uint16_t& value, const ErrorContext& context, AlgorithmError* error)
383 if (!getInteger(raw, propertyName, number, 0, 0xFFFF, context, error))
389 bool getUint8(const Dictionary& raw, const char* propertyName, uint8_t& value, const ErrorContext& context, AlgorithmError* error)
392 if (!getInteger(raw, propertyName, number, 0, 0xFF, context, error))
398 bool getOptionalUint32(const Dictionary& raw, const char* propertyName, bool& hasValue, uint32_t& value, const ErrorContext& context, AlgorithmError* error)
401 if (!getOptionalInteger(raw, propertyName, hasValue, number, 0, 0xFFFFFFFF, context, error))
408 // Defined by the WebCrypto spec as:
410 // dictionary AesCbcParams : Algorithm {
411 // CryptoOperationData iv;
413 bool parseAesCbcParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
415 RefPtr<ArrayBufferView> iv;
416 if (!getCryptoOperationData(raw, "iv", iv, context, error))
419 if (iv->byteLength() != 16) {
420 setDataError(context.toString("iv", "Must be 16 bytes"), error);
424 params = adoptPtr(new WebCryptoAesCbcParams(static_cast<unsigned char*>(iv->baseAddress()), iv->byteLength()));
428 // Defined by the WebCrypto spec as:
430 // dictionary AesKeyGenParams : Algorithm {
431 // [EnforceRange] unsigned short length;
433 bool parseAesKeyGenParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
436 if (!getUint16(raw, "length", length, context, error))
439 params = adoptPtr(new WebCryptoAesKeyGenParams(length));
443 bool parseAlgorithm(const Dictionary&, WebCryptoOperation, WebCryptoAlgorithm&, ErrorContext, AlgorithmError*);
445 bool parseHash(const Dictionary& raw, WebCryptoAlgorithm& hash, ErrorContext context, AlgorithmError* error)
448 if (!DictionaryHelper::get(raw, "hash", rawHash)) {
449 setSyntaxError(context.toString("hash", "Missing or not a dictionary"), error);
454 return parseAlgorithm(rawHash, WebCryptoOperationDigest, hash, context, error);
457 // Defined by the WebCrypto spec as:
459 // dictionary HmacImportParams : Algorithm {
460 // AlgorithmIdentifier hash;
462 bool parseHmacImportParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
464 WebCryptoAlgorithm hash;
465 if (!parseHash(raw, hash, context, error))
468 params = adoptPtr(new WebCryptoHmacImportParams(hash));
472 // Defined by the WebCrypto spec as:
474 // dictionary HmacKeyGenParams : Algorithm {
475 // AlgorithmIdentifier hash;
476 // // The length (in bits) of the key to generate. If unspecified, the
477 // // recommended length will be used, which is the size of the associated hash function's block
479 // unsigned long length;
481 bool parseHmacKeyGenParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
483 WebCryptoAlgorithm hash;
484 if (!parseHash(raw, hash, context, error))
489 if (!getOptionalUint32(raw, "length", hasLength, length, context, error))
492 params = adoptPtr(new WebCryptoHmacKeyGenParams(hash, hasLength, length));
496 // Defined by the WebCrypto spec as:
498 // dictionary RsaHashedImportParams {
499 // AlgorithmIdentifier hash;
501 bool parseRsaHashedImportParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
503 WebCryptoAlgorithm hash;
504 if (!parseHash(raw, hash, context, error))
507 params = adoptPtr(new WebCryptoRsaHashedImportParams(hash));
511 // Defined by the WebCrypto spec as:
513 // dictionary RsaHashedKeyGenParams : RsaKeyGenParams {
514 // AlgorithmIdentifier hash;
517 // dictionary RsaKeyGenParams : Algorithm {
518 // unsigned long modulusLength;
519 // BigInteger publicExponent;
521 bool parseRsaHashedKeyGenParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
523 uint32_t modulusLength;
524 if (!getUint32(raw, "modulusLength", modulusLength, context, error))
527 RefPtr<Uint8Array> publicExponent;
528 if (!getBigInteger(raw, "publicExponent", publicExponent, context, error))
531 WebCryptoAlgorithm hash;
532 if (!parseHash(raw, hash, context, error))
535 params = adoptPtr(new WebCryptoRsaHashedKeyGenParams(hash, modulusLength, static_cast<const unsigned char*>(publicExponent->baseAddress()), publicExponent->byteLength()));
539 // Defined by the WebCrypto spec as:
541 // dictionary AesCtrParams : Algorithm {
542 // CryptoOperationData counter;
543 // [EnforceRange] octet length;
545 bool parseAesCtrParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
547 RefPtr<ArrayBufferView> counter;
548 if (!getCryptoOperationData(raw, "counter", counter, context, error))
552 if (!getUint8(raw, "length", length, context, error))
555 params = adoptPtr(new WebCryptoAesCtrParams(length, static_cast<const unsigned char*>(counter->baseAddress()), counter->byteLength()));
559 // Defined by the WebCrypto spec as:
561 // dictionary AesGcmParams : Algorithm {
562 // CryptoOperationData iv;
563 // CryptoOperationData? additionalData;
564 // [EnforceRange] octet? tagLength; // May be 0-128
566 bool parseAesGcmParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
568 RefPtr<ArrayBufferView> iv;
569 if (!getCryptoOperationData(raw, "iv", iv, context, error))
572 bool hasAdditionalData;
573 RefPtr<ArrayBufferView> additionalData;
574 if (!getOptionalCryptoOperationData(raw, "additionalData", hasAdditionalData, additionalData, context, error))
579 if (!getOptionalInteger(raw, "tagLength", hasTagLength, tagLength, 0, 128, context, error))
582 const unsigned char* ivStart = static_cast<const unsigned char*>(iv->baseAddress());
583 unsigned ivLength = iv->byteLength();
585 const unsigned char* additionalDataStart = hasAdditionalData ? static_cast<const unsigned char*>(additionalData->baseAddress()) : 0;
586 unsigned additionalDataLength = hasAdditionalData ? additionalData->byteLength() : 0;
588 params = adoptPtr(new WebCryptoAesGcmParams(ivStart, ivLength, hasAdditionalData, additionalDataStart, additionalDataLength, hasTagLength, tagLength));
592 // Defined by the WebCrypto spec as:
594 // dictionary RsaOaepParams : Algorithm {
595 // CryptoOperationData? label;
597 bool parseRsaOaepParams(const Dictionary& raw, OwnPtr<WebCryptoAlgorithmParams>& params, const ErrorContext& context, AlgorithmError* error)
600 RefPtr<ArrayBufferView> label;
601 if (!getOptionalCryptoOperationData(raw, "label", hasLabel, label, context, error))
604 const unsigned char* labelStart = hasLabel ? static_cast<const unsigned char*>(label->baseAddress()) : 0;
605 unsigned labelLength = hasLabel ? label->byteLength() : 0;
607 params = adoptPtr(new WebCryptoRsaOaepParams(hasLabel, labelStart, labelLength));
611 bool parseAlgorithmParams(const Dictionary& raw, WebCryptoAlgorithmParamsType type, OwnPtr<WebCryptoAlgorithmParams>& params, ErrorContext& context, AlgorithmError* error)
614 case WebCryptoAlgorithmParamsTypeNone:
616 case WebCryptoAlgorithmParamsTypeAesCbcParams:
617 context.add("AesCbcParams");
618 return parseAesCbcParams(raw, params, context, error);
619 case WebCryptoAlgorithmParamsTypeAesKeyGenParams:
620 context.add("AesKeyGenParams");
621 return parseAesKeyGenParams(raw, params, context, error);
622 case WebCryptoAlgorithmParamsTypeHmacImportParams:
623 context.add("HmacImportParams");
624 return parseHmacImportParams(raw, params, context, error);
625 case WebCryptoAlgorithmParamsTypeHmacKeyGenParams:
626 context.add("HmacKeyGenParams");
627 return parseHmacKeyGenParams(raw, params, context, error);
628 case WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams:
629 context.add("RsaHashedKeyGenParams");
630 return parseRsaHashedKeyGenParams(raw, params, context, error);
631 case WebCryptoAlgorithmParamsTypeRsaHashedImportParams:
632 context.add("RsaHashedImportParams");
633 return parseRsaHashedImportParams(raw, params, context, error);
634 case WebCryptoAlgorithmParamsTypeAesCtrParams:
635 context.add("AesCtrParams");
636 return parseAesCtrParams(raw, params, context, error);
637 case WebCryptoAlgorithmParamsTypeAesGcmParams:
638 context.add("AesGcmParams");
639 return parseAesGcmParams(raw, params, context, error);
640 case WebCryptoAlgorithmParamsTypeRsaOaepParams:
641 context.add("RsaOaepParams");
642 return parseRsaOaepParams(raw, params, context, error);
645 ASSERT_NOT_REACHED();
649 const char* operationToString(WebCryptoOperation op)
652 case WebCryptoOperationEncrypt:
654 case WebCryptoOperationDecrypt:
656 case WebCryptoOperationSign:
658 case WebCryptoOperationVerify:
660 case WebCryptoOperationDigest:
662 case WebCryptoOperationGenerateKey:
663 return "generateKey";
664 case WebCryptoOperationImportKey:
666 case WebCryptoOperationDeriveKey:
668 case WebCryptoOperationDeriveBits:
670 case WebCryptoOperationWrapKey:
672 case WebCryptoOperationUnwrapKey:
678 bool parseAlgorithm(const Dictionary& raw, WebCryptoOperation op, WebCryptoAlgorithm& algorithm, ErrorContext context, AlgorithmError* error)
680 context.add("Algorithm");
682 if (!raw.isObject()) {
683 setSyntaxError(context.toString("Not an object"), error);
687 String algorithmName;
688 if (!DictionaryHelper::get(raw, "name", algorithmName)) {
689 setSyntaxError(context.toString("name", "Missing or not a string"), error);
693 WebCryptoAlgorithmId algorithmId;
694 if (!lookupAlgorithmIdByName(algorithmName, algorithmId)) {
695 // FIXME: The spec says to return a SyntaxError if the input contains
696 // any non-ASCII characters.
697 setNotSupportedError(context.toString("Unrecognized name"), error);
701 // Remove the "Algorithm:" prefix for all subsequent errors.
702 context.removeLast();
704 const WebCryptoAlgorithmInfo* algorithmInfo = WebCryptoAlgorithm::lookupAlgorithmInfo(algorithmId);
706 if (algorithmInfo->operationToParamsType[op] == WebCryptoAlgorithmInfo::Undefined) {
707 context.add(algorithmInfo->name);
708 setNotSupportedError(context.toString("Unsupported operation", operationToString(op)), error);
712 WebCryptoAlgorithmParamsType paramsType = static_cast<WebCryptoAlgorithmParamsType>(algorithmInfo->operationToParamsType[op]);
714 OwnPtr<WebCryptoAlgorithmParams> params;
715 if (!parseAlgorithmParams(raw, paramsType, params, context, error))
718 algorithm = WebCryptoAlgorithm(algorithmId, params.release());
724 bool normalizeAlgorithm(const Dictionary& raw, WebCryptoOperation op, WebCryptoAlgorithm& algorithm, AlgorithmError* error)
726 return parseAlgorithm(raw, op, algorithm, ErrorContext(), error);