1/*
2 * Copyright (C) 2013 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CryptoAlgorithmRSA_OAEP.h"
28
29#if ENABLE(WEB_CRYPTO)
30
31#include "CryptoAlgorithmRsaHashedImportParams.h"
32#include "CryptoAlgorithmRsaHashedKeyGenParams.h"
33#include "CryptoAlgorithmRsaOaepParams.h"
34#include "CryptoKeyPair.h"
35#include "CryptoKeyRSA.h"
36#include <wtf/CrossThreadCopier.h>
37#include <wtf/Variant.h>
38
39namespace WebCore {
40
41namespace CryptoAlgorithmRSA_OAEPInternal {
42static const char* const ALG1 = "RSA-OAEP";
43static const char* const ALG224 = "RSA-OAEP-224";
44static const char* const ALG256 = "RSA-OAEP-256";
45static const char* const ALG384 = "RSA-OAEP-384";
46static const char* const ALG512 = "RSA-OAEP-512";
47}
48
49Ref<CryptoAlgorithm> CryptoAlgorithmRSA_OAEP::create()
50{
51 return adoptRef(*new CryptoAlgorithmRSA_OAEP);
52}
53
54CryptoAlgorithmIdentifier CryptoAlgorithmRSA_OAEP::identifier() const
55{
56 return s_identifier;
57}
58
59void CryptoAlgorithmRSA_OAEP::encrypt(const CryptoAlgorithmParameters& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& plainText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
60{
61 if (key->type() != CryptoKeyType::Public) {
62 exceptionCallback(InvalidAccessError);
63 return;
64 }
65
66 dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback),
67 [parameters = crossThreadCopy(downcast<CryptoAlgorithmRsaOaepParams>(parameters)), key = WTFMove(key), plainText = WTFMove(plainText)] {
68 return platformEncrypt(parameters, downcast<CryptoKeyRSA>(key.get()), plainText);
69 });
70}
71
72void CryptoAlgorithmRSA_OAEP::decrypt(const CryptoAlgorithmParameters& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& cipherText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
73{
74 if (key->type() != CryptoKeyType::Private) {
75 exceptionCallback(InvalidAccessError);
76 return;
77 }
78
79 dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback),
80 [parameters = crossThreadCopy(downcast<CryptoAlgorithmRsaOaepParams>(parameters)), key = WTFMove(key), cipherText = WTFMove(cipherText)] {
81 return platformDecrypt(parameters, downcast<CryptoKeyRSA>(key.get()), cipherText);
82 });
83}
84
85void CryptoAlgorithmRSA_OAEP::generateKey(const CryptoAlgorithmParameters& parameters, bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context)
86{
87 const auto& rsaParameters = downcast<CryptoAlgorithmRsaHashedKeyGenParams>(parameters);
88
89 if (usages & (CryptoKeyUsageSign | CryptoKeyUsageVerify | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits)) {
90 exceptionCallback(SyntaxError);
91 return;
92 }
93
94 auto keyPairCallback = [capturedCallback = WTFMove(callback)](CryptoKeyPair&& pair) {
95 pair.publicKey->setUsagesBitmap(pair.publicKey->usagesBitmap() & (CryptoKeyUsageEncrypt | CryptoKeyUsageWrapKey));
96 pair.privateKey->setUsagesBitmap(pair.privateKey->usagesBitmap() & (CryptoKeyUsageDecrypt | CryptoKeyUsageUnwrapKey));
97 capturedCallback(WTFMove(pair));
98 };
99 auto failureCallback = [capturedCallback = WTFMove(exceptionCallback)]() {
100 capturedCallback(OperationError);
101 };
102 CryptoKeyRSA::generatePair(CryptoAlgorithmIdentifier::RSA_OAEP, rsaParameters.hashIdentifier, true, rsaParameters.modulusLength, rsaParameters.publicExponentVector(), extractable, usages, WTFMove(keyPairCallback), WTFMove(failureCallback), &context);
103}
104
105void CryptoAlgorithmRSA_OAEP::importKey(CryptoKeyFormat format, KeyData&& data, const CryptoAlgorithmParameters& parameters, bool extractable, CryptoKeyUsageBitmap usages, KeyCallback&& callback, ExceptionCallback&& exceptionCallback)
106{
107 using namespace CryptoAlgorithmRSA_OAEPInternal;
108
109 const auto& rsaParameters = downcast<CryptoAlgorithmRsaHashedImportParams>(parameters);
110
111 RefPtr<CryptoKeyRSA> result;
112 switch (format) {
113 case CryptoKeyFormat::Jwk: {
114 JsonWebKey key = WTFMove(WTF::get<JsonWebKey>(data));
115
116 bool isUsagesAllowed = false;
117 if (!key.d.isNull()) {
118 isUsagesAllowed = isUsagesAllowed || !(usages ^ CryptoKeyUsageDecrypt);
119 isUsagesAllowed = isUsagesAllowed || !(usages ^ CryptoKeyUsageUnwrapKey);
120 isUsagesAllowed = isUsagesAllowed || !(usages ^ (CryptoKeyUsageDecrypt | CryptoKeyUsageUnwrapKey));
121 } else {
122 isUsagesAllowed = isUsagesAllowed || !(usages ^ CryptoKeyUsageEncrypt);
123 isUsagesAllowed = isUsagesAllowed || !(usages ^ CryptoKeyUsageWrapKey);
124 isUsagesAllowed = isUsagesAllowed || !(usages ^ (CryptoKeyUsageEncrypt | CryptoKeyUsageWrapKey));
125 }
126 isUsagesAllowed = isUsagesAllowed || !usages;
127 if (!isUsagesAllowed) {
128 exceptionCallback(SyntaxError);
129 return;
130 }
131
132 if (usages && !key.use.isNull() && key.use != "enc") {
133 exceptionCallback(DataError);
134 return;
135 }
136
137 bool isMatched = false;
138 switch (rsaParameters.hashIdentifier) {
139 case CryptoAlgorithmIdentifier::SHA_1:
140 isMatched = key.alg.isNull() || key.alg == ALG1;
141 break;
142 case CryptoAlgorithmIdentifier::SHA_224:
143 isMatched = key.alg.isNull() || key.alg == ALG224;
144 break;
145 case CryptoAlgorithmIdentifier::SHA_256:
146 isMatched = key.alg.isNull() || key.alg == ALG256;
147 break;
148 case CryptoAlgorithmIdentifier::SHA_384:
149 isMatched = key.alg.isNull() || key.alg == ALG384;
150 break;
151 case CryptoAlgorithmIdentifier::SHA_512:
152 isMatched = key.alg.isNull() || key.alg == ALG512;
153 break;
154 default:
155 break;
156 }
157 if (!isMatched) {
158 exceptionCallback(DataError);
159 return;
160 }
161
162 result = CryptoKeyRSA::importJwk(rsaParameters.identifier, rsaParameters.hashIdentifier, WTFMove(key), extractable, usages);
163 break;
164 }
165 case CryptoKeyFormat::Spki: {
166 if (usages && (usages ^ CryptoKeyUsageEncrypt) && (usages ^ CryptoKeyUsageWrapKey) && (usages ^ (CryptoKeyUsageEncrypt | CryptoKeyUsageWrapKey))) {
167 exceptionCallback(SyntaxError);
168 return;
169 }
170 // FIXME: <webkit.org/b/165436>
171 result = CryptoKeyRSA::importSpki(rsaParameters.identifier, rsaParameters.hashIdentifier, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages);
172 break;
173 }
174 case CryptoKeyFormat::Pkcs8: {
175 if (usages && (usages ^ CryptoKeyUsageDecrypt) && (usages ^ CryptoKeyUsageUnwrapKey) && (usages ^ (CryptoKeyUsageDecrypt | CryptoKeyUsageUnwrapKey))) {
176 exceptionCallback(SyntaxError);
177 return;
178 }
179 // FIXME: <webkit.org/b/165436>
180 result = CryptoKeyRSA::importPkcs8(parameters.identifier, rsaParameters.hashIdentifier, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages);
181 break;
182 }
183 default:
184 exceptionCallback(NotSupportedError);
185 return;
186 }
187 if (!result) {
188 exceptionCallback(DataError);
189 return;
190 }
191
192 callback(*result);
193}
194
195void CryptoAlgorithmRSA_OAEP::exportKey(CryptoKeyFormat format, Ref<CryptoKey>&& key, KeyDataCallback&& callback, ExceptionCallback&& exceptionCallback)
196{
197 using namespace CryptoAlgorithmRSA_OAEPInternal;
198 const auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
199
200 if (!rsaKey.keySizeInBits()) {
201 exceptionCallback(OperationError);
202 return;
203 }
204
205 KeyData result;
206 switch (format) {
207 case CryptoKeyFormat::Jwk: {
208 JsonWebKey jwk = rsaKey.exportJwk();
209 switch (rsaKey.hashAlgorithmIdentifier()) {
210 case CryptoAlgorithmIdentifier::SHA_1:
211 jwk.alg = String(ALG1);
212 break;
213 case CryptoAlgorithmIdentifier::SHA_224:
214 jwk.alg = String(ALG224);
215 break;
216 case CryptoAlgorithmIdentifier::SHA_256:
217 jwk.alg = String(ALG256);
218 break;
219 case CryptoAlgorithmIdentifier::SHA_384:
220 jwk.alg = String(ALG384);
221 break;
222 case CryptoAlgorithmIdentifier::SHA_512:
223 jwk.alg = String(ALG512);
224 break;
225 default:
226 ASSERT_NOT_REACHED();
227 }
228 result = WTFMove(jwk);
229 break;
230 }
231 case CryptoKeyFormat::Spki: {
232 // FIXME: <webkit.org/b/165437>
233 auto spki = rsaKey.exportSpki();
234 if (spki.hasException()) {
235 exceptionCallback(spki.releaseException().code());
236 return;
237 }
238 result = spki.releaseReturnValue();
239 break;
240 }
241 case CryptoKeyFormat::Pkcs8: {
242 // FIXME: <webkit.org/b/165437>
243 auto pkcs8 = rsaKey.exportPkcs8();
244 if (pkcs8.hasException()) {
245 exceptionCallback(pkcs8.releaseException().code());
246 return;
247 }
248 result = pkcs8.releaseReturnValue();
249 break;
250 }
251 default:
252 exceptionCallback(NotSupportedError);
253 return;
254 }
255
256 callback(format, WTFMove(result));
257}
258
259}
260
261#endif // ENABLE(WEB_CRYPTO)
262