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/Key.h"
33
34#include "bindings/v8/ExceptionState.h"
35#include "core/dom/ExceptionCode.h"
36#include "modules/crypto/Algorithm.h"
37#include "public/platform/WebCryptoAlgorithmParams.h"
38
39namespace WebCore {
40
41namespace {
42
43const char* keyTypeToString(blink::WebCryptoKeyType type)
44{
45    switch (type) {
46    case blink::WebCryptoKeyTypeSecret:
47        return "secret";
48    case blink::WebCryptoKeyTypePublic:
49        return "public";
50    case blink::WebCryptoKeyTypePrivate:
51        return "private";
52    }
53    ASSERT_NOT_REACHED();
54    return 0;
55}
56
57struct KeyUsageMapping {
58    blink::WebCryptoKeyUsage value;
59    const char* const name;
60};
61
62// Keep this array sorted.
63const KeyUsageMapping keyUsageMappings[] = {
64    { blink::WebCryptoKeyUsageDecrypt, "decrypt" },
65    { blink::WebCryptoKeyUsageDeriveKey, "deriveKey" },
66    { blink::WebCryptoKeyUsageEncrypt, "encrypt" },
67    { blink::WebCryptoKeyUsageSign, "sign" },
68    { blink::WebCryptoKeyUsageUnwrapKey, "unwrapKey" },
69    { blink::WebCryptoKeyUsageVerify, "verify" },
70    { blink::WebCryptoKeyUsageWrapKey, "wrapKey" },
71};
72
73COMPILE_ASSERT(blink::EndOfWebCryptoKeyUsage == (1 << 6) + 1, update_keyUsageMappings);
74
75const char* keyUsageToString(blink::WebCryptoKeyUsage usage)
76{
77    for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyUsageMappings); ++i) {
78        if (keyUsageMappings[i].value == usage)
79            return keyUsageMappings[i].name;
80    }
81    ASSERT_NOT_REACHED();
82    return 0;
83}
84
85blink::WebCryptoKeyUsageMask keyUsageStringToMask(const String& usageString)
86{
87    for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyUsageMappings); ++i) {
88        if (keyUsageMappings[i].name == usageString)
89            return keyUsageMappings[i].value;
90    }
91    return 0;
92}
93
94blink::WebCryptoKeyUsageMask toKeyUsage(AlgorithmOperation operation)
95{
96    switch (operation) {
97    case Encrypt:
98        return blink::WebCryptoKeyUsageEncrypt;
99    case Decrypt:
100        return blink::WebCryptoKeyUsageDecrypt;
101    case Sign:
102        return blink::WebCryptoKeyUsageSign;
103    case Verify:
104        return blink::WebCryptoKeyUsageVerify;
105    case DeriveKey:
106        return blink::WebCryptoKeyUsageDeriveKey;
107    case WrapKey:
108        return blink::WebCryptoKeyUsageWrapKey;
109    case UnwrapKey:
110        return blink::WebCryptoKeyUsageUnwrapKey;
111    case Digest:
112    case GenerateKey:
113    case ImportKey:
114        break;
115    }
116
117    ASSERT_NOT_REACHED();
118    return 0;
119}
120
121bool getHmacHashId(const blink::WebCryptoAlgorithm& algorithm, blink::WebCryptoAlgorithmId& hashId)
122{
123    if (algorithm.hmacParams()) {
124        hashId = algorithm.hmacParams()->hash().id();
125        return true;
126    }
127    if (algorithm.hmacKeyParams()) {
128        hashId = algorithm.hmacKeyParams()->hash().id();
129        return true;
130    }
131    return false;
132}
133
134} // namespace
135
136Key::~Key()
137{
138}
139
140Key::Key(const blink::WebCryptoKey& key)
141    : m_key(key)
142{
143    ScriptWrappable::init(this);
144}
145
146String Key::type() const
147{
148    return keyTypeToString(m_key.type());
149}
150
151bool Key::extractable() const
152{
153    return m_key.extractable();
154}
155
156Algorithm* Key::algorithm()
157{
158    if (!m_algorithm)
159        m_algorithm = Algorithm::create(m_key.algorithm());
160    return m_algorithm.get();
161}
162
163// FIXME: This creates a new javascript array each time. What should happen
164//        instead is return the same (immutable) array. (Javascript callers can
165//        distinguish this by doing an == test on the arrays and seeing they are
166//        different).
167Vector<String> Key::usages() const
168{
169    Vector<String> result;
170    for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyUsageMappings); ++i) {
171        blink::WebCryptoKeyUsage usage = keyUsageMappings[i].value;
172        if (m_key.usages() & usage)
173            result.append(keyUsageToString(usage));
174    }
175    return result;
176}
177
178bool Key::canBeUsedForAlgorithm(const blink::WebCryptoAlgorithm& algorithm, AlgorithmOperation op, ExceptionState& exceptionState) const
179{
180    if (!(m_key.usages() & toKeyUsage(op))) {
181        exceptionState.throwDOMException(NotSupportedError, "key.usages does not permit this operation");
182        return false;
183    }
184
185    if (m_key.algorithm().id() != algorithm.id()) {
186        exceptionState.throwDOMException(NotSupportedError, "key.algorithm does not match that of operation");
187        return false;
188    }
189
190    // Verify that the algorithm-specific parameters for the key conform to the
191    // algorithm.
192    // FIXME: Verify that this is complete.
193
194    if (m_key.algorithm().id() == blink::WebCryptoAlgorithmIdHmac) {
195        blink::WebCryptoAlgorithmId keyHash;
196        blink::WebCryptoAlgorithmId algorithmHash;
197        if (!getHmacHashId(m_key.algorithm(), keyHash) || !getHmacHashId(algorithm, algorithmHash) || keyHash != algorithmHash) {
198            exceptionState.throwDOMException(NotSupportedError, "key.algorithm does not match that of operation (HMAC's hash differs)");
199            return false;
200        }
201    }
202
203    return true;
204}
205
206bool Key::parseFormat(const String& formatString, blink::WebCryptoKeyFormat& format, ExceptionState& exceptionState)
207{
208    // There are few enough values that testing serially is fast enough.
209    if (formatString == "raw") {
210        format = blink::WebCryptoKeyFormatRaw;
211        return true;
212    }
213    if (formatString == "pkcs8") {
214        format = blink::WebCryptoKeyFormatPkcs8;
215        return true;
216    }
217    if (formatString == "spki") {
218        format = blink::WebCryptoKeyFormatSpki;
219        return true;
220    }
221    if (formatString == "jwk") {
222        format = blink::WebCryptoKeyFormatJwk;
223        return true;
224    }
225
226    exceptionState.throwTypeError("Invalid keyFormat argument");
227    return false;
228}
229
230bool Key::parseUsageMask(const Vector<String>& usages, blink::WebCryptoKeyUsageMask& mask, ExceptionState& exceptionState)
231{
232    mask = 0;
233    for (size_t i = 0; i < usages.size(); ++i) {
234        blink::WebCryptoKeyUsageMask usage = keyUsageStringToMask(usages[i]);
235        if (!usage) {
236            exceptionState.throwTypeError("Invalid keyUsages argument");
237            return false;
238        }
239        mask |= usage;
240    }
241    return true;
242}
243
244} // namespace WebCore
245