1/*
2 * Copyright (C) 2017-2019 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 "CryptoKeyEC.h"
28
29#if ENABLE(WEB_CRYPTO)
30
31#include "CryptoAlgorithmRegistry.h"
32#include "JsonWebKey.h"
33#include <wtf/text/Base64.h>
34
35namespace WebCore {
36
37static const ASCIILiteral P256 { "P-256"_s };
38static const ASCIILiteral P384 { "P-384"_s };
39static const ASCIILiteral P521 { "P-521"_s };
40
41static Optional<CryptoKeyEC::NamedCurve> toNamedCurve(const String& curve)
42{
43 if (curve == P256)
44 return CryptoKeyEC::NamedCurve::P256;
45 if (curve == P384)
46 return CryptoKeyEC::NamedCurve::P384;
47 if (curve == P521)
48 return CryptoKeyEC::NamedCurve::P521;
49
50 return WTF::nullopt;
51}
52
53CryptoKeyEC::CryptoKeyEC(CryptoAlgorithmIdentifier identifier, NamedCurve curve, CryptoKeyType type, PlatformECKeyContainer&& platformKey, bool extractable, CryptoKeyUsageBitmap usages)
54 : CryptoKey(identifier, type, extractable, usages)
55 , m_platformKey(WTFMove(platformKey))
56 , m_curve(curve)
57{
58 // Only CryptoKeyEC objects for supported curves should be created.
59 ASSERT(platformSupportedCurve(curve));
60}
61
62ExceptionOr<CryptoKeyPair> CryptoKeyEC::generatePair(CryptoAlgorithmIdentifier identifier, const String& curve, bool extractable, CryptoKeyUsageBitmap usages)
63{
64 auto namedCurve = toNamedCurve(curve);
65 if (!namedCurve || !platformSupportedCurve(*namedCurve))
66 return Exception { NotSupportedError };
67
68 auto result = platformGeneratePair(identifier, *namedCurve, extractable, usages);
69 if (!result)
70 return Exception { OperationError };
71
72 return WTFMove(*result);
73}
74
75RefPtr<CryptoKeyEC> CryptoKeyEC::importRaw(CryptoAlgorithmIdentifier identifier, const String& curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
76{
77 auto namedCurve = toNamedCurve(curve);
78 if (!namedCurve || !platformSupportedCurve(*namedCurve))
79 return nullptr;
80
81 return platformImportRaw(identifier, *namedCurve, WTFMove(keyData), extractable, usages);
82}
83
84RefPtr<CryptoKeyEC> CryptoKeyEC::importJwk(CryptoAlgorithmIdentifier identifier, const String& curve, JsonWebKey&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
85{
86 if (keyData.kty != "EC")
87 return nullptr;
88 if (keyData.key_ops && ((keyData.usages & usages) != usages))
89 return nullptr;
90 if (keyData.ext && !keyData.ext.value() && extractable)
91 return nullptr;
92
93 if (keyData.crv.isNull() || curve != keyData.crv)
94 return nullptr;
95 auto namedCurve = toNamedCurve(keyData.crv);
96 if (!namedCurve || !platformSupportedCurve(*namedCurve))
97 return nullptr;
98
99 if (keyData.x.isNull() || keyData.y.isNull())
100 return nullptr;
101 Vector<uint8_t> x;
102 if (!WTF::base64URLDecode(keyData.x, x))
103 return nullptr;
104 Vector<uint8_t> y;
105 if (!WTF::base64URLDecode(keyData.y, y))
106 return nullptr;
107 if (keyData.d.isNull()) {
108 // import public key
109 return platformImportJWKPublic(identifier, *namedCurve, WTFMove(x), WTFMove(y), extractable, usages);
110 }
111
112 Vector<uint8_t> d;
113 if (!WTF::base64URLDecode(keyData.d, d))
114 return nullptr;
115 // import private key
116 return platformImportJWKPrivate(identifier, *namedCurve, WTFMove(x), WTFMove(y), WTFMove(d), extractable, usages);
117}
118
119RefPtr<CryptoKeyEC> CryptoKeyEC::importSpki(CryptoAlgorithmIdentifier identifier, const String& curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
120{
121 auto namedCurve = toNamedCurve(curve);
122 if (!namedCurve || !platformSupportedCurve(*namedCurve))
123 return nullptr;
124
125 return platformImportSpki(identifier, *namedCurve, WTFMove(keyData), extractable, usages);
126}
127
128RefPtr<CryptoKeyEC> CryptoKeyEC::importPkcs8(CryptoAlgorithmIdentifier identifier, const String& curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
129{
130 auto namedCurve = toNamedCurve(curve);
131 if (!namedCurve || !platformSupportedCurve(*namedCurve))
132 return nullptr;
133
134 return platformImportPkcs8(identifier, *namedCurve, WTFMove(keyData), extractable, usages);
135}
136
137ExceptionOr<Vector<uint8_t>> CryptoKeyEC::exportRaw() const
138{
139 if (type() != CryptoKey::Type::Public)
140 return Exception { InvalidAccessError };
141
142 auto&& result = platformExportRaw();
143 if (result.isEmpty())
144 return Exception { OperationError };
145 return WTFMove(result);
146}
147
148ExceptionOr<JsonWebKey> CryptoKeyEC::exportJwk() const
149{
150 JsonWebKey result;
151 result.kty = "EC";
152 switch (m_curve) {
153 case NamedCurve::P256:
154 result.crv = P256;
155 break;
156 case NamedCurve::P384:
157 result.crv = P384;
158 break;
159 case NamedCurve::P521:
160 result.crv = P521;
161 break;
162 }
163 result.key_ops = usages();
164 result.ext = extractable();
165 if (!platformAddFieldElements(result))
166 return Exception { OperationError };
167 return result;
168}
169
170ExceptionOr<Vector<uint8_t>> CryptoKeyEC::exportSpki() const
171{
172 if (type() != CryptoKey::Type::Public)
173 return Exception { InvalidAccessError };
174
175 auto&& result = platformExportSpki();
176 if (result.isEmpty())
177 return Exception { OperationError };
178 return WTFMove(result);
179}
180
181ExceptionOr<Vector<uint8_t>> CryptoKeyEC::exportPkcs8() const
182{
183 if (type() != CryptoKey::Type::Private)
184 return Exception { InvalidAccessError };
185
186 auto&& result = platformExportPkcs8();
187 if (result.isEmpty())
188 return Exception { OperationError };
189 return WTFMove(result);
190}
191
192String CryptoKeyEC::namedCurveString() const
193{
194 switch (m_curve) {
195 case NamedCurve::P256:
196 return String(P256);
197 case NamedCurve::P384:
198 return String(P384);
199 case NamedCurve::P521:
200 return String(P521);
201 }
202
203 ASSERT_NOT_REACHED();
204 return emptyString();
205}
206
207bool CryptoKeyEC::isValidECAlgorithm(CryptoAlgorithmIdentifier algorithm)
208{
209 return algorithm == CryptoAlgorithmIdentifier::ECDSA || algorithm == CryptoAlgorithmIdentifier::ECDH;
210}
211
212auto CryptoKeyEC::algorithm() const -> KeyAlgorithm
213{
214 CryptoEcKeyAlgorithm result;
215 result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
216
217 switch (m_curve) {
218 case NamedCurve::P256:
219 result.namedCurve = P256;
220 break;
221 case NamedCurve::P384:
222 result.namedCurve = P384;
223 break;
224 case NamedCurve::P521:
225 result.namedCurve = P521;
226 break;
227 }
228
229 return result;
230}
231
232} // namespace WebCore
233
234#endif // ENABLE(WEB_CRYPTO)
235