1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/child/webcrypto/algorithm_dispatch.h"
6
7#include "base/logging.h"
8#include "content/child/webcrypto/algorithm_implementation.h"
9#include "content/child/webcrypto/algorithm_registry.h"
10#include "content/child/webcrypto/crypto_data.h"
11#include "content/child/webcrypto/platform_crypto.h"
12#include "content/child/webcrypto/status.h"
13#include "content/child/webcrypto/webcrypto_util.h"
14#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
15
16namespace content {
17
18namespace webcrypto {
19
20namespace {
21
22Status DecryptDontCheckKeyUsage(const blink::WebCryptoAlgorithm& algorithm,
23                                const blink::WebCryptoKey& key,
24                                const CryptoData& data,
25                                std::vector<uint8_t>* buffer) {
26  if (algorithm.id() != key.algorithm().id())
27    return Status::ErrorUnexpected();
28
29  const AlgorithmImplementation* impl = NULL;
30  Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
31  if (status.IsError())
32    return status;
33
34  return impl->Decrypt(algorithm, key, data, buffer);
35}
36
37Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm,
38                             const blink::WebCryptoKey& key,
39                             const CryptoData& data,
40                             std::vector<uint8_t>* buffer) {
41  if (algorithm.id() != key.algorithm().id())
42    return Status::ErrorUnexpected();
43
44  const AlgorithmImplementation* impl = NULL;
45  Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
46  if (status.IsError())
47    return status;
48
49  return impl->Encrypt(algorithm, key, data, buffer);
50}
51
52Status ExportKeyDontCheckExtractability(blink::WebCryptoKeyFormat format,
53                                        const blink::WebCryptoKey& key,
54                                        std::vector<uint8_t>* buffer) {
55  const AlgorithmImplementation* impl = NULL;
56  Status status = GetAlgorithmImplementation(key.algorithm().id(), &impl);
57  if (status.IsError())
58    return status;
59
60  switch (format) {
61    case blink::WebCryptoKeyFormatRaw:
62      return impl->ExportKeyRaw(key, buffer);
63    case blink::WebCryptoKeyFormatSpki:
64      return impl->ExportKeySpki(key, buffer);
65    case blink::WebCryptoKeyFormatPkcs8:
66      return impl->ExportKeyPkcs8(key, buffer);
67    case blink::WebCryptoKeyFormatJwk:
68      return impl->ExportKeyJwk(key, buffer);
69    default:
70      return Status::ErrorUnsupported();
71  }
72}
73
74}  // namespace
75
76Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
77               const blink::WebCryptoKey& key,
78               const CryptoData& data,
79               std::vector<uint8_t>* buffer) {
80  if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt))
81    return Status::ErrorUnexpected();
82  return EncryptDontCheckUsage(algorithm, key, data, buffer);
83}
84
85Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
86               const blink::WebCryptoKey& key,
87               const CryptoData& data,
88               std::vector<uint8_t>* buffer) {
89  if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageDecrypt))
90    return Status::ErrorUnexpected();
91  return DecryptDontCheckKeyUsage(algorithm, key, data, buffer);
92}
93
94Status Digest(const blink::WebCryptoAlgorithm& algorithm,
95              const CryptoData& data,
96              std::vector<uint8_t>* buffer) {
97  const AlgorithmImplementation* impl = NULL;
98  Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
99  if (status.IsError())
100    return status;
101
102  return impl->Digest(algorithm, data, buffer);
103}
104
105Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
106                         bool extractable,
107                         blink::WebCryptoKeyUsageMask usage_mask,
108                         blink::WebCryptoKey* key) {
109  const AlgorithmImplementation* impl = NULL;
110  Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
111  if (status.IsError())
112    return status;
113
114  status = impl->VerifyKeyUsagesBeforeGenerateKey(usage_mask);
115  if (status.IsError())
116    return status;
117
118  return impl->GenerateSecretKey(algorithm, extractable, usage_mask, key);
119}
120
121Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm,
122                       bool extractable,
123                       blink::WebCryptoKeyUsageMask combined_usage_mask,
124                       blink::WebCryptoKey* public_key,
125                       blink::WebCryptoKey* private_key) {
126  const AlgorithmImplementation* impl = NULL;
127  Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
128  if (status.IsError())
129    return status;
130
131  blink::WebCryptoKeyUsageMask public_usage_mask;
132  blink::WebCryptoKeyUsageMask private_usage_mask;
133  status = impl->VerifyKeyUsagesBeforeGenerateKeyPair(
134      combined_usage_mask, &public_usage_mask, &private_usage_mask);
135  if (status.IsError())
136    return status;
137
138  return impl->GenerateKeyPair(algorithm,
139                               extractable,
140                               public_usage_mask,
141                               private_usage_mask,
142                               public_key,
143                               private_key);
144}
145
146// Note that this function may be called from the target Blink thread.
147Status ImportKey(blink::WebCryptoKeyFormat format,
148                 const CryptoData& key_data,
149                 const blink::WebCryptoAlgorithm& algorithm,
150                 bool extractable,
151                 blink::WebCryptoKeyUsageMask usage_mask,
152                 blink::WebCryptoKey* key) {
153  const AlgorithmImplementation* impl = NULL;
154  Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
155  if (status.IsError())
156    return status;
157
158  status = impl->VerifyKeyUsagesBeforeImportKey(format, usage_mask);
159  if (status.IsError())
160    return status;
161
162  switch (format) {
163    case blink::WebCryptoKeyFormatRaw:
164      return impl->ImportKeyRaw(
165          key_data, algorithm, extractable, usage_mask, key);
166    case blink::WebCryptoKeyFormatSpki:
167      return impl->ImportKeySpki(
168          key_data, algorithm, extractable, usage_mask, key);
169    case blink::WebCryptoKeyFormatPkcs8:
170      return impl->ImportKeyPkcs8(
171          key_data, algorithm, extractable, usage_mask, key);
172    case blink::WebCryptoKeyFormatJwk:
173      return impl->ImportKeyJwk(
174          key_data, algorithm, extractable, usage_mask, key);
175    default:
176      return Status::ErrorUnsupported();
177  }
178}
179
180Status ExportKey(blink::WebCryptoKeyFormat format,
181                 const blink::WebCryptoKey& key,
182                 std::vector<uint8_t>* buffer) {
183  if (!key.extractable())
184    return Status::ErrorKeyNotExtractable();
185  return ExportKeyDontCheckExtractability(format, key, buffer);
186}
187
188Status Sign(const blink::WebCryptoAlgorithm& algorithm,
189            const blink::WebCryptoKey& key,
190            const CryptoData& data,
191            std::vector<uint8_t>* buffer) {
192  if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageSign))
193    return Status::ErrorUnexpected();
194  if (algorithm.id() != key.algorithm().id())
195    return Status::ErrorUnexpected();
196
197  const AlgorithmImplementation* impl = NULL;
198  Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
199  if (status.IsError())
200    return status;
201
202  return impl->Sign(algorithm, key, data, buffer);
203}
204
205Status Verify(const blink::WebCryptoAlgorithm& algorithm,
206              const blink::WebCryptoKey& key,
207              const CryptoData& signature,
208              const CryptoData& data,
209              bool* signature_match) {
210  if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageVerify))
211    return Status::ErrorUnexpected();
212  if (algorithm.id() != key.algorithm().id())
213    return Status::ErrorUnexpected();
214
215  const AlgorithmImplementation* impl = NULL;
216  Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
217  if (status.IsError())
218    return status;
219
220  return impl->Verify(algorithm, key, signature, data, signature_match);
221}
222
223Status WrapKey(blink::WebCryptoKeyFormat format,
224               const blink::WebCryptoKey& key_to_wrap,
225               const blink::WebCryptoKey& wrapping_key,
226               const blink::WebCryptoAlgorithm& wrapping_algorithm,
227               std::vector<uint8_t>* buffer) {
228  if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageWrapKey))
229    return Status::ErrorUnexpected();
230
231  std::vector<uint8_t> exported_data;
232  Status status = ExportKey(format, key_to_wrap, &exported_data);
233  if (status.IsError())
234    return status;
235  return EncryptDontCheckUsage(
236      wrapping_algorithm, wrapping_key, CryptoData(exported_data), buffer);
237}
238
239Status UnwrapKey(blink::WebCryptoKeyFormat format,
240                 const CryptoData& wrapped_key_data,
241                 const blink::WebCryptoKey& wrapping_key,
242                 const blink::WebCryptoAlgorithm& wrapping_algorithm,
243                 const blink::WebCryptoAlgorithm& algorithm,
244                 bool extractable,
245                 blink::WebCryptoKeyUsageMask usage_mask,
246                 blink::WebCryptoKey* key) {
247  if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageUnwrapKey))
248    return Status::ErrorUnexpected();
249  if (wrapping_algorithm.id() != wrapping_key.algorithm().id())
250    return Status::ErrorUnexpected();
251
252  // Fail fast if the import is doomed to fail.
253  const AlgorithmImplementation* import_impl = NULL;
254  Status status = GetAlgorithmImplementation(algorithm.id(), &import_impl);
255  if (status.IsError())
256    return status;
257
258  status = import_impl->VerifyKeyUsagesBeforeImportKey(format, usage_mask);
259  if (status.IsError())
260    return status;
261
262  std::vector<uint8_t> buffer;
263  status = DecryptDontCheckKeyUsage(
264      wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer);
265  if (status.IsError())
266    return status;
267
268  // NOTE that returning the details of ImportKey() failures may leak
269  // information about the plaintext of the encrypted key (for instance the JWK
270  // key_ops). As long as the ImportKey error messages don't describe actual
271  // key bytes however this should be OK. For more discussion see
272  // http://crubg.com/372040
273  return ImportKey(
274      format, CryptoData(buffer), algorithm, extractable, usage_mask, key);
275}
276
277scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
278    blink::WebCryptoAlgorithmId algorithm) {
279  PlatformInit();
280  return CreatePlatformDigestor(algorithm);
281}
282
283}  // namespace webcrypto
284
285}  // namespace content
286