Skip to content
File

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

20.8 KB
1#include "keys.h"
2 
3#include <openssl/crypto.h>
4#include <openssl/ec_key.h>
5#include <openssl/pem.h>
6#include <openssl/x509.h>
7 
8namespace workerd::api {
9 
10namespace {
11static const char EMPTY_PASSPHRASE[] = "";
12} // namespace
13 
14kj::StringPtr toStringPtr(KeyType type) {
15 switch (type) {
16 case KeyType::SECRET:
17 return "secret"_kj;
18 case KeyType::PUBLIC:
19 return "public"_kj;
20 case KeyType::PRIVATE:
21 return "private"_kj;
22 }
23 KJ_UNREACHABLE;
24}
25 
26AsymmetricKeyCryptoKeyImpl::AsymmetricKeyCryptoKeyImpl(AsymmetricKeyData&& key, bool extractable)
27 : CryptoKey::Impl(extractable, key.usages),
28 keyData(kj::mv(key.evpPkey)),
29 keyType(key.keyType) {
30 KJ_DASSERT(keyType != KeyType::SECRET);
31}
32 
33jsg::JsArrayBuffer AsymmetricKeyCryptoKeyImpl::signatureSslToWebCrypto(
34 jsg::Lock& js, kj::ArrayPtr<kj::byte> signature) const {
35 return jsg::JsArrayBuffer::create(js, signature);
36}
37 
38jsg::JsArrayBuffer AsymmetricKeyCryptoKeyImpl::signatureWebCryptoToSsl(
39 jsg::Lock& js, kj::ArrayPtr<const kj::byte> signature) const {
40 return jsg::JsArrayBuffer::create(js, signature);
41}
42 
43SubtleCrypto::ExportKeyData AsymmetricKeyCryptoKeyImpl::exportKey(
44 jsg::Lock& js, kj::StringPtr format) const {
45 // EVP_marshal_{public,private}_key() functions are BoringSSL
46 // extensions which export asymmetric keys in DER format.
47 // DER is the binary format which *should* work to export any EVP_PKEY.
48 
49 uint8_t* der = nullptr;
50 // The caller takes ownership of the buffer and, unless the buffer was fixed with CBB_init_fixed,
51 // must call OPENSSL_free when done.
52 // https://commondatastorage.googleapis.com/chromium-boringssl-docs/bytestring.h.html#CBB_finish
53 KJ_DEFER(if (der != nullptr) { OPENSSL_free(der); });
54 size_t derLen;
55 bssl::ScopedCBB cbb;
56 if (format == "pkcs8"_kj) {
57 JSG_REQUIRE(keyType == KeyType::PRIVATE, DOMInvalidAccessError,
58 "Asymmetric pkcs8 export requires private key (not \"", toStringPtr(keyType), "\").");
59 if (!CBB_init(cbb.get(), 0) || !EVP_marshal_private_key(cbb.get(), keyData.get()) ||
60 !CBB_finish(cbb.get(), &der, &derLen)) {
61 JSG_FAIL_REQUIRE(DOMOperationError, "Private key export failed.");
62 }
63 } else if (format == "spki"_kj) {
64 JSG_REQUIRE(keyType == KeyType::PUBLIC, DOMInvalidAccessError,
65 "Asymmetric spki export requires public key (not \"", toStringPtr(keyType), "\").");
66 if (!CBB_init(cbb.get(), 0) || !EVP_marshal_public_key(cbb.get(), keyData.get()) ||
67 !CBB_finish(cbb.get(), &der, &derLen)) {
68 JSG_FAIL_REQUIRE(DOMOperationError, "Public key export failed.");
69 }
70 } else if (format == "jwk"_kj) {
71 auto jwk = exportJwk();
72 // Implicitly extractable since the normative part of the implementation validates that
73 // already.
74 jwk.ext = true;
75 jwk.key_ops = getUsages().map([](auto usage) { return kj::str(usage.name()); });
76 return jwk;
77 } else if (format == "raw"_kj) {
78 return exportRaw(js).addRef(js);
79 } else {
80 JSG_FAIL_REQUIRE(DOMInvalidAccessError, "Cannot export \"", getAlgorithmName(), "\" in \"",
81 format, "\" format.");
82 }
83 
84 return jsg::JsArrayBuffer::create(js, kj::arrayPtr(der, derLen)).addRef(js);
85}
86 
87jsg::JsUint8Array AsymmetricKeyCryptoKeyImpl::exportKeyExt(jsg::Lock& js,
88 kj::StringPtr format,
89 kj::StringPtr type,
90 jsg::Optional<kj::String> cipher,
91 jsg::Optional<kj::Array<kj::byte>> passphrase) const {
92 KJ_REQUIRE(isExtractable(), "Key is not extractable.");
93 MarkPopErrorOnReturn mark_pop_error_on_return;
94 KJ_REQUIRE(format != "jwk", "jwk export not supported for exportKeyExt");
95 auto pkey = getEvpPkey();
96 auto bio = OSSL_BIO_MEM();
97 
98 struct EncDetail {
99 // const_cast is acceptable, pass will be reassigned before it is written to.
100 char* pass = const_cast<char*>(&EMPTY_PASSPHRASE[0]);
101 size_t pass_len = 0;
102 const EVP_CIPHER* cipher = nullptr;
103 };
104 
105 const auto getEncDetail = [&] {
106 EncDetail detail;
107 KJ_IF_SOME(pw, passphrase) {
108 detail.pass = reinterpret_cast<char*>(pw.begin());
109 detail.pass_len = pw.size();
110 }
111 KJ_IF_SOME(ciph, cipher) {
112 detail.cipher = EVP_get_cipherbyname(ciph.cStr());
113 JSG_REQUIRE(detail.cipher != nullptr, TypeError, "Unknown cipher ", ciph);
114 KJ_REQUIRE(detail.pass != nullptr);
115 }
116 return detail;
117 };
118 
119 const auto fromBio = [&](kj::StringPtr format) {
120 BUF_MEM* bptr;
121 BIO_get_mem_ptr(bio.get(), &bptr);
122 auto src = kj::arrayPtr(bptr->data, bptr->length);
123 return jsg::JsUint8Array::create(js, src.asBytes());
124 };
125 
126 if (getType() == "public"_kj) {
127 // Here we only care about the format and the type.
128 if (type == "pkcs1"_kj) {
129 // PKCS#1 is only for RSA keys.
130 JSG_REQUIRE(EVP_PKEY_id(pkey) == EVP_PKEY_RSA, TypeError,
131 "The pkcs1 type is only valid for RSA keys.");
132 auto rsa = EVP_PKEY_get1_RSA(pkey);
133 KJ_DEFER(RSA_free(rsa));
134 if (format == "pem"_kj) {
135 if (PEM_write_bio_RSAPublicKey(bio.get(), rsa) == 1) {
136 return fromBio(format);
137 }
138 } else if (format == "der"_kj) {
139 if (i2d_RSAPublicKey_bio(bio.get(), rsa) == 1) {
140 return fromBio(format);
141 }
142 }
143 } else if (type == "spki"_kj) {
144 if (format == "pem"_kj) {
145 if (PEM_write_bio_PUBKEY(bio.get(), pkey) == 1) {
146 return fromBio(format);
147 }
148 } else if (format == "der"_kj) {
149 if (i2d_PUBKEY_bio(bio.get(), pkey) == 1) {
150 return fromBio(format);
151 }
152 }
153 }
154 JSG_FAIL_REQUIRE(TypeError, "Failed to encode public key");
155 }
156 
157 // Otherwise it's a private key.
158 KJ_REQUIRE(getType() == "private"_kj);
159 
160 if (type == "pkcs1"_kj) {
161 // PKCS#1 is only for RSA keys.
162 JSG_REQUIRE(
163 EVP_PKEY_id(pkey) == EVP_PKEY_RSA, TypeError, "The pkcs1 type is only valid for RSA keys.");
164 auto rsa = EVP_PKEY_get1_RSA(pkey);
165 KJ_DEFER(RSA_free(rsa));
166 if (format == "pem"_kj) {
167 auto enc = getEncDetail();
168 if (PEM_write_bio_RSAPrivateKey(bio.get(), rsa, enc.cipher,
169 reinterpret_cast<unsigned char*>(enc.pass), enc.pass_len, nullptr, nullptr) == 1) {
170 return fromBio(format);
171 }
172 } else if (format == "der"_kj) {
173 // The cipher and passphrase are ignored for DER with PKCS#1.
174 if (i2d_RSAPrivateKey_bio(bio.get(), rsa) == 1) {
175 return fromBio(format);
176 }
177 }
178 } else if (type == "pkcs8"_kj) {
179 auto enc = getEncDetail();
180 if (format == "pem"_kj) {
181 if (PEM_write_bio_PKCS8PrivateKey(
182 bio.get(), pkey, enc.cipher, enc.pass, enc.pass_len, nullptr, nullptr) == 1) {
183 return fromBio(format);
184 }
185 } else if (format == "der"_kj) {
186 if (i2d_PKCS8PrivateKey_bio(
187 bio.get(), pkey, enc.cipher, enc.pass, enc.pass_len, nullptr, nullptr) == 1) {
188 return fromBio(format);
189 }
190 }
191 } else if (type == "sec1"_kj) {
192 // SEC1 is only used for EC keys.
193 JSG_REQUIRE(
194 EVP_PKEY_id(pkey) == EVP_PKEY_EC, TypeError, "The sec1 type is only valid for EC keys.");
195 auto ec = EVP_PKEY_get1_EC_KEY(pkey);
196 KJ_DEFER(EC_KEY_free(ec));
197 if (format == "pem"_kj) {
198 auto enc = getEncDetail();
199 if (PEM_write_bio_ECPrivateKey(bio.get(), ec, enc.cipher,
200 reinterpret_cast<unsigned char*>(enc.pass), enc.pass_len, nullptr, nullptr) == 1) {
201 return fromBio(format);
202 }
203 } else if (format == "der"_kj) {
204 // The cipher and passphrase are ignored for DER with SEC1
205 if (i2d_ECPrivateKey_bio(bio.get(), ec) == 1) {
206 return fromBio(format);
207 }
208 }
209 }
210 
211 JSG_FAIL_REQUIRE(TypeError, "Failed to encode private key");
212}
213 
214jsg::JsArrayBuffer AsymmetricKeyCryptoKeyImpl::sign(jsg::Lock& js,
215 SubtleCrypto::SignAlgorithm&& algorithm,
216 kj::ArrayPtr<const kj::byte> data) const {
217 JSG_REQUIRE(keyType == KeyType::PRIVATE, DOMInvalidAccessError,
218 "Asymmetric signing requires a private key.");
219 
220 auto type = lookupDigestAlgorithm(chooseHash(algorithm.hash)).second;
221 if (getAlgorithmName() == "RSASSA-PKCS1-v1_5") {
222 // RSASSA-PKCS1-v1_5 requires the RSA key to be at least as big as the digest size
223 // plus a 15 to 19 byte digest-specific prefix (see BoringSSL's RSA_add_pkcs1_prefix) plus 11
224 // bytes for padding (see RSA_PKCS1_PADDING_SIZE). For simplicity, require the key to be at
225 // least 32 bytes larger than the hash digest.
226 // Similar checks could also be adopted for more detailed error handling in verify(), but the
227 // current approach should be sufficient to avoid internal errors.
228 RSA& rsa = JSG_REQUIRE_NONNULL(EVP_PKEY_get0_RSA(getEvpPkey()), DOMDataError, "Missing RSA key",
229 tryDescribeOpensslErrors());
230 
231 JSG_REQUIRE(EVP_MD_size(type) + 32 <= RSA_size(&rsa), DOMOperationError,
232 "key too small for signing with given digest, need at least ", 8 * (EVP_MD_size(type) + 32),
233 "bits.");
234 } else if (getAlgorithmName() == "RSA-PSS") {
235 // Similarly, RSA-PSS requires keys to be at least the size of the digest and salt plus 2
236 // bytes, see https://developer.mozilla.org/en-US/docs/Web/API/RsaPssParams for details.
237 RSA& rsa = JSG_REQUIRE_NONNULL(EVP_PKEY_get0_RSA(getEvpPkey()), DOMDataError, "Missing RSA key",
238 tryDescribeOpensslErrors());
239 auto salt = JSG_REQUIRE_NONNULL(algorithm.saltLength, DOMDataError,
240 "Failed to provide salt for RSA-PSS key operation which requires a salt");
241 JSG_REQUIRE(salt >= 0, DOMDataError, "SaltLength for RSA-PSS must be non-negative ",
242 "(provided ", salt, ").");
243 JSG_REQUIRE(EVP_MD_size(type) + 2 <= RSA_size(&rsa), DOMOperationError,
244 "key too small for signing with given digest");
245 JSG_REQUIRE(salt <= RSA_size(&rsa) - EVP_MD_size(type) - 2, DOMOperationError,
246 "key too small for signing with given digest and salt length");
247 }
248 
249 auto digestCtx = OSSL_NEW(EVP_MD_CTX);
250 
251 OSSLCALL(EVP_DigestSignInit(digestCtx.get(), nullptr, type, nullptr, keyData.get()));
252 addSalt(digestCtx->pctx, algorithm);
253 // No-op call unless CryptoKey is RsaPss
254 OSSLCALL(EVP_DigestSignUpdate(digestCtx.get(), data.begin(), data.size()));
255 size_t signatureSize = 0;
256 OSSLCALL(EVP_DigestSignFinal(digestCtx.get(), nullptr, &signatureSize));
257 
258 KJ_STACK_ARRAY(kj::byte, signature, signatureSize, 256, 256);
259 OSSLCALL(EVP_DigestSignFinal(digestCtx.get(), signature.begin(), &signatureSize));
260 
261 KJ_ASSERT(signatureSize <= signature.size());
262 if (signatureSize < signature.size()) {
263 return signatureSslToWebCrypto(js, signature.first(signatureSize));
264 }
265 
266 return signatureSslToWebCrypto(js, signature);
267}
268 
269bool AsymmetricKeyCryptoKeyImpl::verify(jsg::Lock& js,
270 SubtleCrypto::SignAlgorithm&& algorithm,
271 kj::ArrayPtr<const kj::byte> signature,
272 kj::ArrayPtr<const kj::byte> data) const {
273 ClearErrorOnReturn clearErrorOnReturn;
274 
275 JSG_REQUIRE(keyType == KeyType::PUBLIC, DOMInvalidAccessError,
276 "Asymmetric verification requires a public key.");
277 
278 auto sslSignature = signatureWebCryptoToSsl(js, signature);
279 
280 auto type = lookupDigestAlgorithm(chooseHash(algorithm.hash)).second;
281 
282 auto digestCtx = OSSL_NEW(EVP_MD_CTX);
283 
284 OSSLCALL(EVP_DigestVerifyInit(digestCtx.get(), nullptr, type, nullptr, keyData.get()));
285 addSalt(digestCtx->pctx, algorithm);
286 // No-op call unless CryptoKey is RsaPss
287 OSSLCALL(EVP_DigestVerifyUpdate(digestCtx.get(), data.begin(), data.size()));
288 // EVP_DigestVerifyFinal() returns 1 on success, 0 on invalid signature, and any other value
289 // indicates "a more serious error".
290 auto result = EVP_DigestVerifyFinal(
291 digestCtx.get(), sslSignature.asArrayPtr().begin(), sslSignature.size());
292 JSG_REQUIRE(result == 0 || result == 1, InternalDOMOperationError,
293 "Unexpected return code from digest verify", getAlgorithmName());
294 return !!result;
295}
296 
297bool AsymmetricKeyCryptoKeyImpl::equals(const CryptoKey::Impl& other) const {
298 if (this == &other) return true;
299 KJ_IF_SOME(otherImpl, kj::dynamicDowncastIfAvailable<const AsymmetricKeyCryptoKeyImpl>(other)) {
300 // EVP_PKEY_cmp will return 1 if the inputs match, 0 if they don't match,
301 // -1 if the key types are different, and -2 if the operation is not supported.
302 // We only really care about the first two cases.
303 return EVP_PKEY_cmp(keyData.get(), otherImpl.keyData.get()) == 1;
304 }
305 return false;
306}
307 
308kj::StringPtr AsymmetricKeyCryptoKeyImpl::getType() const {
309 return toStringPtr(keyType);
310}
311 
312bool AsymmetricKeyCryptoKeyImpl::verifyX509Public(const X509* cert) const {
313 ClearErrorOnReturn clearErrorOnReturn;
314 return X509_verify(const_cast<X509*>(cert), getEvpPkey()) > 0;
315}
316 
317bool AsymmetricKeyCryptoKeyImpl::verifyX509Private(const X509* cert) const {
318 ClearErrorOnReturn clearErrorOnReturn;
319 return X509_check_private_key(const_cast<X509*>(cert), getEvpPkey()) == 1;
320}
321 
322// ======================================================================================
323 
324AsymmetricKeyData importAsymmetricForWebCrypto(jsg::Lock& js,
325 kj::StringPtr format,
326 SubtleCrypto::ImportKeyData keyData,
327 kj::StringPtr normalizedName,
328 bool extractable,
329 kj::ArrayPtr<const kj::String> keyUsages,
330 kj::FunctionParam<kj::Own<EVP_PKEY>(SubtleCrypto::JsonWebKey)> readJwk,
331 CryptoKeyUsageSet allowedUsages) {
332 CryptoKeyUsageSet usages;
333 if (format == "jwk") {
334 // I found jww's SO answer immeasurably helpful while writing this:
335 // https://stackoverflow.com/questions/24093272/how-to-load-a-private-key-from-a-jwk-into-openssl
336 
337 auto& keyDataJwk = JSG_REQUIRE_NONNULL(keyData.tryGet<SubtleCrypto::JsonWebKey>(), DOMDataError,
338 "JSON Web Key import requires a JSON Web Key object.");
339 
340 KeyType keyType = KeyType::PRIVATE;
341 if (keyDataJwk.d != kj::none) {
342 // Private key (`d` is the private exponent, per RFC 7518).
343 keyType = KeyType::PRIVATE;
344 usages =
345 CryptoKeyUsageSet::validate(normalizedName, CryptoKeyUsageSet::Context::importPrivate,
346 keyUsages, allowedUsages & CryptoKeyUsageSet::privateKeyMask());
347 
348 // https://tools.ietf.org/html/rfc7518#section-6.3.2.7
349 // We don't support keys with > 2 primes, so error out.
350 JSG_REQUIRE(keyDataJwk.oth == kj::none, DOMNotSupportedError,
351 "Multi-prime private keys not supported.");
352 } else {
353 // Public key.
354 keyType = KeyType::PUBLIC;
355 auto strictCrypto = FeatureFlags::get(js).getStrictCrypto();
356 // restrict key usages to public key usages. In the case of ECDH, usages must be empty, but
357 // if the strict crypto compat flag is not enabled allow the same usages as with private ECDH
358 // keys, i.e. derivationKeyMask().
359 usages = CryptoKeyUsageSet::validate(normalizedName, CryptoKeyUsageSet::Context::importPublic,
360 keyUsages,
361 allowedUsages &
362 (normalizedName == "ECDH"
363 ? strictCrypto ? CryptoKeyUsageSet() : CryptoKeyUsageSet::derivationKeyMask()
364 : CryptoKeyUsageSet::publicKeyMask()));
365 }
366 
367 auto [expectedUse, op0, op1] = [&, normalizedName] {
368 if (normalizedName == "RSA-OAEP") {
369 return std::make_tuple("enc", "encrypt", "wrapKey");
370 }
371 if (normalizedName == "ECDH" || normalizedName == "X25519") {
372 return std::make_tuple("enc", "unused", "unused");
373 }
374 return std::make_tuple("sig", "sign", "verify");
375 }();
376 
377 if (keyUsages.size() > 0) {
378 KJ_IF_SOME(use, keyDataJwk.use) {
379 JSG_REQUIRE(use == expectedUse, DOMDataError,
380 "Asymmetric \"jwk\" key import with usages requires a JSON Web Key with "
381 "Public Key Use parameter \"use\" (\"",
382 use, "\") equal to \"sig\".");
383 }
384 }
385 
386 KJ_IF_SOME(ops, keyDataJwk.key_ops) {
387 // TODO(cleanup): When we implement other JWK import functions, factor this part out into a
388 // JWK validation function.
389 
390 // "The key operation values are case-sensitive strings. Duplicate key operation values MUST
391 // NOT be present in the array." -- RFC 7517, section 4.3
392 std::sort(ops.begin(), ops.end());
393 JSG_REQUIRE(std::adjacent_find(ops.begin(), ops.end()) == ops.end(), DOMDataError,
394 "A JSON Web Key's Key Operations parameter (\"key_ops\") "
395 "must not contain duplicates.");
396 
397 KJ_IF_SOME(use, keyDataJwk.use) {
398 // "The "use" and "key_ops" JWK members SHOULD NOT be used together; however, if both are
399 // used, the information they convey MUST be consistent." -- RFC 7517, section 4.3.
400 
401 JSG_REQUIRE(use == expectedUse, DOMDataError,
402 "Asymmetric \"jwk\" import requires a JSON "
403 "Web Key with Public Key Use \"use\" (\"",
404 use, "\") equal to \"", expectedUse, "\".");
405 
406 for (const auto& op: ops) {
407 JSG_REQUIRE(normalizedName != "ECDH" && normalizedName != "X25519", DOMDataError,
408 "A JSON Web Key should have either a Public Key Use parameter (\"use\") or a Key "
409 "Operations parameter (\"key_ops\"); otherwise, the parameters must be consistent "
410 "with each other. For public ",
411 normalizedName,
412 " keys, there are no valid usages,"
413 "so keys with a non-empty \"key_ops\" parameter are not allowed.");
414 
415 // TODO(conform): Can a JWK private key actually be used to verify? Not
416 // using the Web Crypto API...
417 JSG_REQUIRE(op == op0 || op == op1, DOMDataError,
418 "A JSON Web Key should have either a Public Key Use parameter (\"use\") or a Key "
419 "Operations parameter (\"key_ops\"); otherwise, the parameters must be consistent "
420 "with each other. A Public Key Use for ",
421 normalizedName,
422 " would allow a Key "
423 "Operations array with only \"",
424 op0, "\" and/or \"", op1, "\" values (not \"", op, "\").");
425 }
426 }
427 
428 // We're supposed to verify that `ops` contains all the values listed in `keyUsages`. For any
429 // of the supported algorithms, a key may have at most two distinct usages ('sig' type keys
430 // have at most one valid usage, but there may be two for e.g. ECDH). Test the first usage
431 // and the next usages. Test the first usage and the first usage distinct from the first, if
432 // present (i.e. the second allowed usage, even if there are duplicates).
433 if (keyUsages.size() > 0) {
434 JSG_REQUIRE(std::find(ops.begin(), ops.end(), keyUsages.front()) != ops.end(), DOMDataError,
435 "All specified key usages must be present in the JSON "
436 "Web Key's Key Operations parameter (\"key_ops\").");
437 auto secondUsage = std::find_end(keyUsages.begin(), keyUsages.end(), keyUsages.begin(),
438 keyUsages.begin() + 1) +
439 1;
440 if (secondUsage != keyUsages.end()) {
441 JSG_REQUIRE(std::find(ops.begin(), ops.end(), *secondUsage) != ops.end(), DOMDataError,
442 "All specified key usages must be present in the JSON "
443 "Web Key's Key Operations parameter (\"key_ops\").");
444 }
445 }
446 }
447 
448 KJ_IF_SOME(ext, keyDataJwk.ext) {
449 // If the user requested this key to be extractable, make sure the JWK does not disallow it.
450 JSG_REQUIRE(!extractable || ext, DOMDataError,
451 "Cannot create an extractable CryptoKey from an unextractable JSON Web Key.");
452 }
453 
454 return {readJwk(kj::mv(keyDataJwk)), keyType, usages};
455 } else if (format == "spki") {
456 kj::ArrayPtr<const kj::byte> keyBytes =
457 JSG_REQUIRE_NONNULL(keyData.tryGet<kj::Array<kj::byte>>(), DOMDataError,
458 "SPKI import requires an ArrayBuffer.");
459 const kj::byte* ptr = keyBytes.begin();
460 auto evpPkey = OSSLCALL_OWN(
461 EVP_PKEY, d2i_PUBKEY(nullptr, &ptr, keyBytes.size()), DOMDataError, "Invalid SPKI input.");
462 if (ptr != keyBytes.end()) {
463 JSG_FAIL_REQUIRE(
464 DOMDataError, "Invalid ", keyBytes.end() - ptr, " trailing bytes after SPKI input.");
465 }
466 
467 // usages must be empty for ECDH public keys, so use CryptoKeyUsageSet() when validating the
468 // usage set.
469 usages = CryptoKeyUsageSet::validate(normalizedName, CryptoKeyUsageSet::Context::importPublic,
470 keyUsages,
471 allowedUsages &
472 (normalizedName == "ECDH" ? CryptoKeyUsageSet() : CryptoKeyUsageSet::publicKeyMask()));
473 return {kj::mv(evpPkey), KeyType::PUBLIC, usages};
474 } else if (format == "pkcs8") {
475 kj::ArrayPtr<const kj::byte> keyBytes =
476 JSG_REQUIRE_NONNULL(keyData.tryGet<kj::Array<kj::byte>>(), DOMDataError,
477 "PKCS8 import requires an ArrayBuffer.");
478 const kj::byte* ptr = keyBytes.begin();
479 auto evpPkey = OSSLCALL_OWN(EVP_PKEY, d2i_AutoPrivateKey(nullptr, &ptr, keyBytes.size()),
480 DOMDataError, "Invalid PKCS8 input.");
481 if (ptr != keyBytes.end()) {
482 JSG_FAIL_REQUIRE(
483 DOMDataError, "Invalid ", keyBytes.end() - ptr, " trailing bytes after PKCS8 input.");
484 }
485 usages = CryptoKeyUsageSet::validate(normalizedName, CryptoKeyUsageSet::Context::importPrivate,
486 keyUsages, allowedUsages & CryptoKeyUsageSet::privateKeyMask());
487 return {kj::mv(evpPkey), KeyType::PRIVATE, usages};
488 } else {
489 JSG_FAIL_REQUIRE(DOMNotSupportedError, "Unrecognized key import format \"", format, "\".");
490 }
491}
492 
493} // namespace workerd::api