Skip to content
File

Blob: src/workerd/api/crypto/jwk.c++

11.0 KB
1#include "jwk.h"
2 
3#include <ncrypto.h>
4#include <openssl/curve25519.h>
5 
6namespace workerd::api {
7 
8using JsonWebKey = SubtleCrypto::JsonWebKey;
9using ncrypto::BignumPointer;
10using ncrypto::ECKeyPointer;
11using ncrypto::EVPKeyPointer;
12using ncrypto::RSAPointer;
13 
14namespace {
15 
16kj::String getCurveName(int nid) {
17 switch (nid) {
18 case NID_X9_62_prime256v1:
19 return kj::str("P-256");
20 case NID_secp256k1:
21 return kj::str("secp256k1");
22 case NID_secp384r1:
23 return kj::str("P-384");
24 case NID_secp521r1:
25 return kj::str("P-521");
26 default:
27 return kj::String();
28 }
29}
30 
31int getCurveFromName(kj::StringPtr name) {
32 int nid = EC_curve_nist2nid(name.begin());
33 if (nid == NID_undef) nid = OBJ_sn2nid(name.begin());
34 return nid;
35}
36 
37int getOKPCurveFromName(kj::StringPtr name) {
38 int nid;
39 if (name == "Ed25519") {
40 nid = EVP_PKEY_ED25519;
41 } else if (name == "X25519") {
42 nid = EVP_PKEY_X25519;
43 } else {
44 // 448 keys are not supported by boringssl
45 nid = NID_undef;
46 }
47 return nid;
48}
49 
50JsonWebKey jwkFromEdKey(const EVPKeyPointer& key, KeyType keyType) {
51 KJ_REQUIRE(key, "Key must not be null");
52 KJ_ASSERT(key.id() == EVP_PKEY_ED25519 || key.id() == EVP_PKEY_X25519,
53 "Key must be an Ed25519 or X25519 key");
54 
55 auto pkey = key.rawPublicKey();
56 JSG_REQUIRE(pkey, InternalDOMOperationError, "Failed to retrieve public key",
57 internalDescribeOpensslErrors());
58 KJ_ASSERT(pkey.size() == 32);
59 kj::ArrayPtr<const kj::byte> rawPublicKey(static_cast<const kj::byte*>(pkey.get()), pkey.size());
60 
61 JsonWebKey jwk;
62 jwk.kty = kj::str("OKP");
63 jwk.crv = key.id() == EVP_PKEY_X25519 ? kj::str("X25519") : kj::str("Ed25519");
64 jwk.x = fastEncodeBase64Url(rawPublicKey);
65 if (key.id() == EVP_PKEY_ED25519) {
66 jwk.alg = kj::str("EdDSA");
67 }
68 
69 if (keyType == KeyType::PRIVATE) {
70 // Deliberately use ED25519_PUBLIC_KEY_LEN here.
71 // BoringSSL defines ED25519_PRIVATE_KEY_LEN as 64B since it stores the private key together
72 // with public key data in some functions, but in the EVP interface only the 32B private key
73 // itself is returned.
74 uint8_t rawPrivateKey[ED25519_PUBLIC_KEY_LEN]{};
75 size_t privateKeyLen = ED25519_PUBLIC_KEY_LEN;
76 JSG_REQUIRE(1 == EVP_PKEY_get_raw_private_key(key.get(), rawPrivateKey, &privateKeyLen),
77 InternalDOMOperationError, "Failed to retrieve private key",
78 internalDescribeOpensslErrors());
79 KJ_ASSERT(privateKeyLen == 32, privateKeyLen);
80 jwk.d = fastEncodeBase64Url(kj::arrayPtr(rawPrivateKey, privateKeyLen));
81 }
82 
83 return jwk;
84}
85 
86JsonWebKey jwkFromEcKey(const EVPKeyPointer& key, KeyType keyType) {
87 KJ_REQUIRE(key, "Key must not be null");
88 KJ_ASSERT(key.id() == EVP_PKEY_EC, "Key must be an EC key");
89 
90 ncrypto::Ec ec = key;
91 
92 JSG_REQUIRE(ec.getX() && ec.getY(), InternalDOMOperationError,
93 "Error getting affine coordinates for export", internalDescribeOpensslErrors());
94 
95 JSG_REQUIRE(ec.getGroup() != nullptr, DOMOperationError, "No elliptic curve group in this key",
96 tryDescribeOpensslErrors());
97 JSG_REQUIRE(ec.getPublicKey(), DOMOperationError, "No public elliptic curve key data in this key",
98 tryDescribeOpensslErrors());
99 
100 auto groupDegreeInBytes = integerCeilDivision(ec.getDegree(), 8U);
101 // getDegree() returns number of bits. We need this because x, y, & d need
102 // to match the group degree according to JWK.
103 
104 SubtleCrypto::JsonWebKey jwk;
105 jwk.kty = kj::str("EC");
106 jwk.crv = getCurveName(ec.getCurve());
107 
108 static constexpr auto handleBn = [](const BIGNUM& bn, size_t size) {
109 return JSG_REQUIRE_NONNULL(bignumToArrayPadded(bn, size), InternalDOMOperationError,
110 "Error converting EC affine co-ordinates to padded array", internalDescribeOpensslErrors());
111 };
112 
113 // We check that getX and getY return good values above.
114 auto xa = handleBn(*ec.getX().get(), groupDegreeInBytes);
115 jwk.x = fastEncodeBase64Url(xa);
116 
117 auto ya = handleBn(*ec.getY().get(), groupDegreeInBytes);
118 jwk.y = fastEncodeBase64Url(ya);
119 
120 if (keyType == KeyType::PRIVATE) {
121 auto privateKey = ec.getPrivateKey();
122 JSG_REQUIRE(privateKey, InternalDOMOperationError,
123 "Error getting private key material for JSON Web Key export",
124 internalDescribeOpensslErrors());
125 auto pk = handleBn(*privateKey, groupDegreeInBytes);
126 jwk.d = fastEncodeBase64Url(pk);
127 }
128 return jwk;
129}
130 
131JsonWebKey jwkFromRsaKey(const EVPKeyPointer& key, KeyType keyType) {
132 SubtleCrypto::JsonWebKey jwk;
133 jwk.kty = kj::str("RSA");
134 
135 ncrypto::Rsa rsa = key;
136 auto publicKey = rsa.getPublicKey();
137 
138 if (publicKey.n != nullptr) {
139 jwk.n = fastEncodeBase64Url(KJ_REQUIRE_NONNULL(bignumToArray(*publicKey.n)));
140 }
141 if (publicKey.e != nullptr) {
142 jwk.e = fastEncodeBase64Url(KJ_REQUIRE_NONNULL(bignumToArray(*publicKey.e)));
143 }
144 
145 if (keyType == KeyType::PRIVATE) {
146 auto privateKey = rsa.getPrivateKey();
147 if (publicKey.d != nullptr) {
148 jwk.d = fastEncodeBase64Url(KJ_REQUIRE_NONNULL(bignumToArray(*publicKey.d)));
149 }
150 if (privateKey.p != nullptr) {
151 jwk.p = fastEncodeBase64Url(KJ_REQUIRE_NONNULL(bignumToArray(*privateKey.p)));
152 }
153 if (privateKey.q != nullptr) {
154 jwk.q = fastEncodeBase64Url(KJ_REQUIRE_NONNULL(bignumToArray(*privateKey.q)));
155 }
156 if (privateKey.dp != nullptr) {
157 jwk.dp = fastEncodeBase64Url(KJ_REQUIRE_NONNULL(bignumToArray(*privateKey.dp)));
158 }
159 if (privateKey.dq != nullptr) {
160 jwk.dq = fastEncodeBase64Url(KJ_REQUIRE_NONNULL(bignumToArray(*privateKey.dq)));
161 }
162 if (privateKey.qi != nullptr) {
163 jwk.qi = fastEncodeBase64Url(KJ_REQUIRE_NONNULL(bignumToArray(*privateKey.qi)));
164 }
165 }
166 
167 return jwk;
168}
169 
170EVPKeyPointer rsaKeyFromJwk(const JsonWebKey& jwk, KeyType keyType) {
171 ncrypto::ClearErrorOnReturn clearErrorOnReturn;
172 
173 RSAPointer rsa(RSA_new());
174 if (!rsa) return {};
175 ncrypto::Rsa rsa_view(rsa.get());
176 
177 auto& n = JSG_REQUIRE_NONNULL(jwk.n, Error, "RSA JWK missing n parameter");
178 auto& e = JSG_REQUIRE_NONNULL(jwk.e, Error, "RSA JWK missing e parameter");
179 auto N = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(n), Error, "RSA JWK invalid n parameter");
180 auto E = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(e), Error, "RSA JWK invalid e parameter");
181 
182 BignumPointer n_bn(N.begin(), N.size());
183 BignumPointer e_bn(E.begin(), E.size());
184 
185 JSG_REQUIRE(
186 rsa_view.setPublicKey(kj::mv(n_bn), kj::mv(e_bn)), Error, "RSA JWK invalid public key");
187 
188 if (keyType == KeyType::PRIVATE) {
189 auto& d = JSG_REQUIRE_NONNULL(jwk.d, Error, "RSA JWK missing d parameter");
190 auto& p = JSG_REQUIRE_NONNULL(jwk.p, Error, "RSA JWK missing p parameter");
191 auto& q = JSG_REQUIRE_NONNULL(jwk.q, Error, "RSA JWK missing q parameter");
192 auto& dp = JSG_REQUIRE_NONNULL(jwk.dp, Error, "RSA JWK missing dp parameter");
193 auto& dq = JSG_REQUIRE_NONNULL(jwk.dq, Error, "RSA JWK missing dq parameter");
194 auto& qi = JSG_REQUIRE_NONNULL(jwk.qi, Error, "RSA JWK missing qi parameter");
195 
196 auto D = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(d), Error, "RSA JWK invalid d parameter");
197 auto P = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(p), Error, "RSA JWK invalid p parameter");
198 auto Q = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(q), Error, "RSA JWK invalid q parameter");
199 auto DP =
200 JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(dp), Error, "RSA JWK invalid dp parameter");
201 auto DQ =
202 JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(dq), Error, "RSA JWK invalid dq parameter");
203 auto QI =
204 JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(qi), Error, "RSA JWK invalid qi parameter");
205 
206 BignumPointer d_bn(D.begin(), D.size());
207 BignumPointer p_bn(P.begin(), P.size());
208 BignumPointer q_bn(Q.begin(), Q.size());
209 BignumPointer dp_bn(DP.begin(), DP.size());
210 BignumPointer dq_bn(DQ.begin(), DQ.size());
211 BignumPointer qi_bn(QI.begin(), QI.size());
212 
213 JSG_REQUIRE(rsa_view.setPrivateKey(kj::mv(d_bn), kj::mv(q_bn), kj::mv(p_bn), kj::mv(dp_bn),
214 kj::mv(dq_bn), kj::mv(qi_bn)),
215 Error, "RSA JWK invalid private key");
216 }
217 
218 return EVPKeyPointer::NewRSA(std::move(rsa));
219}
220 
221EVPKeyPointer ecKeyFromJwk(const JsonWebKey& jwk, KeyType keyType) {
222 int nid = getCurveFromName(JSG_REQUIRE_NONNULL(jwk.crv, Error, "EC JWK missing crv parameter"));
223 JSG_REQUIRE(nid != NID_undef, Error, "EC JWK unsupported crv parameter");
224 
225 auto ec = ECKeyPointer::NewByCurveName(nid);
226 JSG_REQUIRE(ec, Error, "EC JWK unsupported curve");
227 
228 auto& x = JSG_REQUIRE_NONNULL(jwk.x, Error, "EC JWK missing x parameter");
229 auto& y = JSG_REQUIRE_NONNULL(jwk.y, Error, "EC JWK missing y parameter");
230 
231 auto X = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(x), Error, "EC JWK invalid x parameter");
232 auto Y = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(y), Error, "EC JWK invalid y parameter");
233 
234 BignumPointer x_bn(X.begin(), X.size());
235 BignumPointer y_bn(Y.begin(), Y.size());
236 
237 JSG_REQUIRE(ec.setPublicKeyRaw(kj::mv(x_bn), kj::mv(y_bn)), Error, "EC JWK invalid public key");
238 
239 if (keyType == KeyType::PRIVATE) {
240 auto& d = JSG_REQUIRE_NONNULL(jwk.d, Error, "EC JWK missing d parameter");
241 auto D = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(d), Error, "EC JWK invalid d parameter");
242 
243 BignumPointer d_bn(D.begin(), D.size());
244 JSG_REQUIRE(ec.setPrivateKey(kj::mv(d_bn)), Error, "EW JWK invalid private key");
245 }
246 
247 auto pkey = EVPKeyPointer::New();
248 if (!pkey || !pkey.set(ec)) return {};
249 return pkey;
250}
251 
252EVPKeyPointer edKeyFromJwk(const JsonWebKey& jwk, KeyType keyType) {
253 int nid =
254 getOKPCurveFromName(JSG_REQUIRE_NONNULL(jwk.crv, Error, "OKP JWK missing crv parameter"));
255 JSG_REQUIRE(nid != NID_undef, Error, "OKP JWK unsupported crv parameter");
256 
257 if (keyType == KeyType::PRIVATE) {
258 auto& d = JSG_REQUIRE_NONNULL(jwk.d, Error, "OKP JWK missing d parameter");
259 auto D = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(d), Error, "OKP JWK invalid d parameter");
260 return EVPKeyPointer::NewRawPrivate(nid, ToNcryptoBuffer(D.asPtr().asConst()));
261 }
262 
263 auto& x = JSG_REQUIRE_NONNULL(jwk.x, Error, "OKP JWK missing x parameter");
264 auto X = JSG_REQUIRE_NONNULL(simdutfBase64UrlDecode(x), Error, "OKP JWK invalid x parameter");
265 return EVPKeyPointer::NewRawPublic(nid, ToNcryptoBuffer(X.asPtr().asConst()));
266}
267} // namespace
268 
269JsonWebKey toJwk(const EVPKeyPointer& key, KeyType keyType) {
270 if (key) {
271 switch (key.id()) {
272 case EVP_PKEY_ED25519:
273 return jwkFromEdKey(key, keyType);
274 case EVP_PKEY_X25519:
275 return jwkFromEdKey(key, keyType);
276 case EVP_PKEY_EC:
277 return jwkFromEcKey(key, keyType);
278 case EVP_PKEY_RSA:
279 return jwkFromRsaKey(key, keyType);
280 case EVP_PKEY_RSA2:
281 return jwkFromRsaKey(key, keyType);
282 case EVP_PKEY_RSA_PSS:
283 return jwkFromRsaKey(key, keyType);
284 case EVP_PKEY_DSA: {
285 // DSA keys are not supported for JWK export.
286 break;
287 }
288 }
289 }
290 
291 return JsonWebKey{
292 .kty = kj::str("INVALID"),
293 };
294}
295 
296EVPKeyPointer fromJwk(const JsonWebKey& jwk, KeyType keyType) {
297 if (jwk.kty == "OKP") return edKeyFromJwk(jwk, keyType);
298 if (jwk.kty == "EC") return ecKeyFromJwk(jwk, keyType);
299 if (jwk.kty == "RSA") return rsaKeyFromJwk(jwk, keyType);
300 return {};
301}
302 
303} // namespace workerd::api