File
Blob: src/workerd/api/crypto/impl.h
| 1 | // Copyright (c) 2017-2022 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | #pragma once |
| 6 | // INTERNAL CRYPTO IMPLEMENTATION FILE |
| 7 | // |
| 8 | // Don't include this file unless your name is "crypto*.c++". |
| 9 | |
| 10 | #include "crypto.h" |
| 11 | |
| 12 | #include <workerd/api/util.h> |
| 13 | #include <workerd/jsg/jsvalue.h> |
| 14 | |
| 15 | #include <ncrypto.h> |
| 16 | #include <openssl/base.h> |
| 17 | #include <openssl/bn.h> |
| 18 | #include <openssl/err.h> |
| 19 | |
| 20 | #include <kj/encoding.h> |
| 21 | |
| 22 | using BIGNUM = struct bignum_st; |
| 23 | |
| 24 | // Wrap calls to OpenSSL's EVP_* interface (and similar APIs) in this macro to |
| 25 | // deal with errors. |
| 26 | #define OSSLCALL(...) \ |
| 27 | if ((__VA_ARGS__) != 1) ::workerd::api::throwOpensslError(__FILE__, __LINE__, #__VA_ARGS__) |
| 28 | |
| 29 | #define UNWRAP_JWK_BIGNUM(value, ...) \ |
| 30 | JSG_REQUIRE_NONNULL(decodeBase64Url(JSG_REQUIRE_NONNULL((value), __VA_ARGS__)), __VA_ARGS__) |
| 31 | |
| 32 | namespace workerd::api { |
| 33 | |
| 34 | struct OpensslUntranslatedError { |
| 35 | kj::StringPtr library; |
| 36 | kj::StringPtr reasonName; |
| 37 | }; |
| 38 | |
| 39 | // Call to throw an exception based on the OpenSSL error code. Usually, you should wrap your call |
| 40 | // in OSSLCALL() to have this invoked automatically. |
| 41 | // |
| 42 | // Some error codes are translated into application-visible errors of type |
| 43 | // `DOMException(OperationError)`, but most errors are considered internal errors. |
| 44 | KJ_NORETURN(void throwOpensslError(const char* file, int line, kj::StringPtr code)); |
| 45 | |
| 46 | // Consumes the entire OpenSSL error queue & converts it either into friendly names or the raw |
| 47 | // (unfriendly) name that OpenSSL gives the error code. |
| 48 | kj::Vector<kj::OneOf<kj::StringPtr, OpensslUntranslatedError>> consumeAllOpensslErrors(); |
| 49 | |
| 50 | // Returns a description of the OpenSSL errors (starting with ": ") in the stack & clears them if |
| 51 | // there are any. The expected usage is something like: |
| 52 | // JSG_REQUIRE(<some OpenSSL call succeeds>, OperationError, "This thing went wrong", |
| 53 | // tryDescribeOpensslErrors()); |
| 54 | // This way if there are any OpenSSL errors to describe it will get rendered as: |
| 55 | // "jsg.DOMException(OperationError): This thing went wrong: <description>." |
| 56 | // and if there aren't, then this will get rendered as: |
| 57 | // "jsg.DOMException(OperationError): This thing went wrong." |
| 58 | kj::String tryDescribeOpensslErrors(kj::StringPtr defaultIfNoError = nullptr); |
| 59 | |
| 60 | // Like tryDescribeOpensslErrors but dumps all OpenSSL errors even if not user-facing. This is for |
| 61 | // use with `Internal` errors passed to JSG which automagically strip all contextual information so |
| 62 | // that these errors only end up in Sentry. |
| 63 | kj::String internalDescribeOpensslErrors(); |
| 64 | |
| 65 | // Helper for implementing `sign()`, `digest()` and `importKey()`. Returns a pair containing a |
| 66 | // StringPtr to the normalized name of the given algorithm and the EVP_MD type to use with |
| 67 | // OpenSSL's EVP interface. |
| 68 | // |
| 69 | // Throws if the given algorithm isn't supported. |
| 70 | std::pair<kj::StringPtr, const EVP_MD*> lookupDigestAlgorithm(kj::StringPtr algorithm); |
| 71 | |
| 72 | // kj::decodeBase64 doesn't know how to parse URL-encoded variants. |
| 73 | // https://en.wikipedia.org/wiki/Base64#URL_applications |
| 74 | // Due to this, the input string is modified prior to passing to kj::decodeBase64. The mutation |
| 75 | // isn't actually required & it's possible that a non-mutating variant could be written. That's more |
| 76 | // complex to implement outside of kj::decodeBase64 though and the mutating variant is easier to |
| 77 | // implement as a wrapper. Could be sufficient to just add a "urlEncoded" boolean so that |
| 78 | // kj::decodeBase64 can do this in-situ for both cases. |
| 79 | kj::EncodingResult<kj::Array<kj::byte>> decodeBase64Url(kj::String text); |
| 80 | |
| 81 | // WebCrypto likes to allow algorithms to be specified as a simple string name, or as a struct |
| 82 | // containing a `name` field and possibly other fields. This helper collapses that. |
| 83 | template <typename T> |
| 84 | T interpretAlgorithmParam(kj::OneOf<kj::String, T>&& param) { |
| 85 | if (param.template is<kj::String>()) { |
| 86 | T result; |
| 87 | result.name = kj::mv(param.template get<kj::String>()); |
| 88 | return result; |
| 89 | } else { |
| 90 | return kj::mv(param.template get<T>()); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | // Like `interpretAlgorithmParam` but just get the algorithm name. Works with const input. |
| 95 | template <typename T> |
| 96 | kj::StringPtr getAlgorithmName(const kj::OneOf<kj::String, T>& param) { |
| 97 | if (param.template is<kj::String>()) { |
| 98 | return param.template get<kj::String>(); |
| 99 | } else { |
| 100 | return param.template get<T>().name; |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | class CryptoKey::Impl { |
| 105 | public: |
| 106 | // C++ API |
| 107 | |
| 108 | using ImportFunc = kj::Own<Impl>(jsg::Lock& js, |
| 109 | kj::StringPtr normalizedName, |
| 110 | kj::StringPtr format, |
| 111 | SubtleCrypto::ImportKeyData keyData, |
| 112 | SubtleCrypto::ImportKeyAlgorithm&& algorithm, |
| 113 | bool extractable, |
| 114 | kj::ArrayPtr<const kj::String> keyUsages); |
| 115 | |
| 116 | static ImportFunc importAes; |
| 117 | static ImportFunc importHmac; |
| 118 | static ImportFunc importPbkdf2; |
| 119 | static ImportFunc importHkdf; |
| 120 | static ImportFunc importRsa; |
| 121 | static ImportFunc importEcdsa; |
| 122 | static ImportFunc importEcdh; |
| 123 | static ImportFunc importEddsa; |
| 124 | static ImportFunc importRsaRaw; |
| 125 | |
| 126 | using GenerateFunc = kj::OneOf<jsg::Ref<CryptoKey>, CryptoKeyPair>(jsg::Lock& js, |
| 127 | kj::StringPtr normalizedName, |
| 128 | SubtleCrypto::GenerateKeyAlgorithm&& algorithm, |
| 129 | bool extractable, |
| 130 | kj::ArrayPtr<const kj::String> keyUsages); |
| 131 | |
| 132 | static GenerateFunc generateAes; |
| 133 | static GenerateFunc generateHmac; |
| 134 | static GenerateFunc generateRsa; |
| 135 | static GenerateFunc generateEcdsa; |
| 136 | static GenerateFunc generateEcdh; |
| 137 | static GenerateFunc generateEddsa; |
| 138 | |
| 139 | Impl(bool extractable, CryptoKeyUsageSet usages): extractable(extractable), usages(usages) {} |
| 140 | |
| 141 | static kj::Own<CryptoKey::Impl> from(jsg::Lock& js, kj::Own<EVP_PKEY> key); |
| 142 | |
| 143 | bool isExtractable() const { |
| 144 | return extractable; |
| 145 | } |
| 146 | CryptoKeyUsageSet getUsages() const { |
| 147 | return usages; |
| 148 | } |
| 149 | |
| 150 | virtual jsg::JsArrayBuffer encrypt(jsg::Lock& js, |
| 151 | SubtleCrypto::EncryptAlgorithm&& algorithm, |
| 152 | kj::ArrayPtr<const kj::byte> plainText) const { |
| 153 | JSG_FAIL_REQUIRE(DOMNotSupportedError, "The encrypt operation is not implemented for \"", |
| 154 | getAlgorithmName(), "\"."); |
| 155 | } |
| 156 | virtual jsg::JsArrayBuffer decrypt(jsg::Lock& js, |
| 157 | SubtleCrypto::EncryptAlgorithm&& algorithm, |
| 158 | kj::ArrayPtr<const kj::byte> cipherText) const { |
| 159 | JSG_FAIL_REQUIRE(DOMNotSupportedError, "The decrypt operation is not implemented for \"", |
| 160 | getAlgorithmName(), "\"."); |
| 161 | } |
| 162 | |
| 163 | virtual jsg::JsArrayBuffer sign(jsg::Lock& js, |
| 164 | SubtleCrypto::SignAlgorithm&& algorithm, |
| 165 | kj::ArrayPtr<const kj::byte> data) const { |
| 166 | JSG_FAIL_REQUIRE(DOMNotSupportedError, "The sign operation is not implemented for \"", |
| 167 | getAlgorithmName(), "\"."); |
| 168 | } |
| 169 | virtual bool verify(jsg::Lock& js, |
| 170 | SubtleCrypto::SignAlgorithm&& algorithm, |
| 171 | kj::ArrayPtr<const kj::byte> signature, |
| 172 | kj::ArrayPtr<const kj::byte> data) const { |
| 173 | JSG_FAIL_REQUIRE(DOMNotSupportedError, "The verify operation is not implemented for \"", |
| 174 | getAlgorithmName(), "\"."); |
| 175 | } |
| 176 | |
| 177 | virtual jsg::JsArrayBuffer deriveBits(jsg::Lock& js, |
| 178 | SubtleCrypto::DeriveKeyAlgorithm&& algorithm, |
| 179 | kj::Maybe<uint32_t> length) const { |
| 180 | JSG_FAIL_REQUIRE(DOMNotSupportedError, |
| 181 | "The deriveKey and deriveBits operations are not implemented for \"", getAlgorithmName(), |
| 182 | "\"."); |
| 183 | } |
| 184 | |
| 185 | virtual jsg::JsArrayBuffer wrapKey(jsg::Lock& js, |
| 186 | SubtleCrypto::EncryptAlgorithm&& algorithm, |
| 187 | kj::ArrayPtr<const kj::byte> unwrappedKey) const { |
| 188 | // For many algorithms, wrapKey() is the same as encrypt(), so as a convenience the default |
| 189 | // implementation just forwards to it. |
| 190 | return encrypt(js, kj::mv(algorithm), unwrappedKey); |
| 191 | } |
| 192 | |
| 193 | virtual jsg::JsArrayBuffer unwrapKey(jsg::Lock& js, |
| 194 | SubtleCrypto::EncryptAlgorithm&& algorithm, |
| 195 | kj::ArrayPtr<const kj::byte> wrappedKey) const { |
| 196 | // For many algorithms, unwrapKey() is the same as decrypt(), so as a convenience the default |
| 197 | // implementation just forwards to it. |
| 198 | return decrypt(js, kj::mv(algorithm), wrappedKey); |
| 199 | } |
| 200 | |
| 201 | virtual SubtleCrypto::ExportKeyData exportKey(jsg::Lock& js, kj::StringPtr format) const { |
| 202 | JSG_FAIL_REQUIRE(DOMNotSupportedError, "Unrecognized or unsupported export of \"", |
| 203 | getAlgorithmName(), "\" requested."); |
| 204 | } |
| 205 | |
| 206 | // The exportKeyExt variant is used by the Node.js crypto module. It allows the caller to |
| 207 | // specify a broader range of export formats and types that are not supported by Web |
| 208 | // Crypto. For instance, Web Crypto limits the export of public keys to only the spki or |
| 209 | // jwk formats, while Node.js allows pkcs1 or spki formatted as either pem, der, or jwk. |
| 210 | // For private keys, Node.js allows optionally encrypting the private key using a given |
| 211 | // cipher and passphrase. |
| 212 | // Rather than modify the existing exportKey API, we add this new variant to support the |
| 213 | // Node.js implementation without risking breaking the Web Crypto impl. |
| 214 | virtual jsg::JsUint8Array exportKeyExt(jsg::Lock& js, |
| 215 | kj::StringPtr format, |
| 216 | kj::StringPtr type, |
| 217 | jsg::Optional<kj::String> cipher = kj::none, |
| 218 | jsg::Optional<kj::Array<kj::byte>> passphrase = kj::none) const { |
| 219 | JSG_FAIL_REQUIRE(DOMNotSupportedError, "Unrecognized or unsupported export of \"", |
| 220 | getAlgorithmName(), "\" requested."); |
| 221 | } |
| 222 | |
| 223 | virtual kj::StringPtr getAlgorithmName() const = 0; |
| 224 | |
| 225 | virtual CryptoKey::AsymmetricKeyDetails getAsymmetricKeyDetail(jsg::Lock& js) const { |
| 226 | JSG_FAIL_REQUIRE(DOMNotSupportedError, |
| 227 | "The getAsymmetricKeyDetail operation is not implemented for \"", getAlgorithmName(), |
| 228 | "\"."); |
| 229 | } |
| 230 | |
| 231 | // JS API implementation |
| 232 | |
| 233 | virtual AlgorithmVariant getAlgorithm(jsg::Lock& js) const = 0; |
| 234 | virtual kj::StringPtr getType() const { |
| 235 | return "secret"_kj; |
| 236 | } |
| 237 | |
| 238 | virtual bool equals(const Impl& other) const = 0; |
| 239 | virtual bool equals(const kj::Array<kj::byte>& other) const; |
| 240 | |
| 241 | virtual kj::StringPtr jsgGetMemoryName() const { |
| 242 | return "CryptoKey::Impl"; |
| 243 | } |
| 244 | virtual size_t jsgGetMemorySelfSize() const { |
| 245 | return sizeof(Impl); |
| 246 | } |
| 247 | virtual void jsgGetMemoryInfo(jsg::MemoryTracker& tracker) const {} |
| 248 | |
| 249 | virtual bool verifyX509Public(const X509* cert) const { |
| 250 | return false; |
| 251 | } |
| 252 | virtual bool verifyX509Private(const X509* cert) const { |
| 253 | return false; |
| 254 | } |
| 255 | |
| 256 | virtual void visitForGc(jsg::GcVisitor& visitor) { |
| 257 | // By default, nothing to visit. |
| 258 | } |
| 259 | |
| 260 | private: |
| 261 | const bool extractable; |
| 262 | const CryptoKeyUsageSet usages; |
| 263 | }; |
| 264 | |
| 265 | struct CryptoAlgorithm { |
| 266 | // Name, in canonical (all-uppercase) format. |
| 267 | kj::StringPtr name; |
| 268 | |
| 269 | // Functions to import / generate keys for this algorithm. If nullptr, the respective |
| 270 | // operation isn't allowed. |
| 271 | CryptoKey::Impl::ImportFunc* importFunc = nullptr; |
| 272 | |
| 273 | // Functions to import / generate keys for this algorithm. If nullptr, the respective |
| 274 | // operation isn't allowed. |
| 275 | CryptoKey::Impl::GenerateFunc* generateFunc = nullptr; |
| 276 | // TODO(cleanup): I have these as pointers instead of maybe-references because the references |
| 277 | // would have to be const in order to enable const-copying, but it turns out you cannot specify |
| 278 | // `const` on a reference-to-function (the compiler ignores it as "redundant", but then |
| 279 | // template metaprogramming cannot recognize it as const). Maybe we can fix this in KJ, by |
| 280 | // making `RemoveConstOrDisable` recognize function references are inherently const. |
| 281 | |
| 282 | // Allow comparison by name, case-insensitive. This is a convenience for placing in an std::set. |
| 283 | inline bool operator==(const CryptoAlgorithm& other) const { |
| 284 | return strcasecmp(name.cStr(), other.name.cStr()) == 0; |
| 285 | } |
| 286 | // Allow comparison by name, case-insensitive. This is a convenience for placing in an std::set. |
| 287 | inline bool operator<(const CryptoAlgorithm& other) const { |
| 288 | return strcasecmp(name.cStr(), other.name.cStr()) < 0; |
| 289 | } |
| 290 | // TODO(cleanup): I'd rather use kj::Table with HashIndex but we need a case-insensitive hash |
| 291 | // function, which seemed slightly too annoying to implement now. |
| 292 | }; |
| 293 | |
| 294 | class SslArrayDisposer: public kj::ArrayDisposer { |
| 295 | public: |
| 296 | static const SslArrayDisposer INSTANCE; |
| 297 | |
| 298 | void disposeImpl(void* firstElement, |
| 299 | size_t elementSize, |
| 300 | size_t elementCount, |
| 301 | size_t capacity, |
| 302 | void (*destroyElement)(void*)) const override; |
| 303 | }; |
| 304 | |
| 305 | template <typename T, void (*sslFree)(T*)> |
| 306 | class SslDisposer: public kj::Disposer { |
| 307 | public: |
| 308 | static const SslDisposer INSTANCE; |
| 309 | |
| 310 | protected: |
| 311 | void disposeImpl(void* pointer) const override { |
| 312 | sslFree(reinterpret_cast<T*>(pointer)); |
| 313 | } |
| 314 | }; |
| 315 | |
| 316 | template <typename T, void (*sslFree)(T*)> |
| 317 | const SslDisposer<T, sslFree> SslDisposer<T, sslFree>::INSTANCE; |
| 318 | |
| 319 | #define OSSLCALL_OWN(T, code, ...) \ |
| 320 | ({ \ |
| 321 | T* result = code; \ |
| 322 | JSG_REQUIRE(result != nullptr, ##__VA_ARGS__); \ |
| 323 | kj::Own<T>(result, workerd::api::SslDisposer<T, &T##_free>::INSTANCE); \ |
| 324 | }) |
| 325 | |
| 326 | #define OSSL_NEW(T, ...) \ |
| 327 | OSSLCALL_OWN(T, T##_new(__VA_ARGS__), InternalDOMOperationError, "Error allocating crypto") |
| 328 | |
| 329 | #define BIGNUM_new BN_new |
| 330 | #define BIGNUM_free BN_clear_free |
| 331 | // BIGNUM obnoxiously doesn't follow the naming convention... |
| 332 | // Using BN_clear_free here ensures that any potentially sensitive information in the |
| 333 | // BIGNUM is also cleansed when it is freed. |
| 334 | |
| 335 | using UniqueBignum = std::unique_ptr<BIGNUM, void (*)(BIGNUM*)>; |
| 336 | kj::Maybe<kj::Own<BIGNUM>> toBignum(kj::ArrayPtr<const kj::byte> data); |
| 337 | BIGNUM* toBignumUnowned(kj::ArrayPtr<const kj::byte> data); |
| 338 | // Like toBignumUnowned but returns a UniqueBignum for RAII. Use .release() to transfer |
| 339 | // ownership to RSA_set0_key etc. |
| 340 | UniqueBignum toBignumOwned(kj::ArrayPtr<const kj::byte> data); |
| 341 | kj::Maybe<kj::Array<kj::byte>> bignumToArray(const BIGNUM& bignum); |
| 342 | kj::Maybe<kj::Array<kj::byte>> bignumToArrayPadded(const BIGNUM& bignum); |
| 343 | kj::Maybe<kj::Array<kj::byte>> bignumToArrayPadded(const BIGNUM& bignum, size_t paddedLength); |
| 344 | kj::Maybe<jsg::JsUint8Array> bignumToArray(jsg::Lock& js, const BIGNUM& bignum); |
| 345 | kj::Maybe<jsg::JsUint8Array> bignumToArrayPadded(jsg::Lock& js, const BIGNUM& bignum); |
| 346 | kj::Maybe<jsg::JsUint8Array> bignumToArrayPadded( |
| 347 | jsg::Lock& js, const BIGNUM& bignum, size_t paddedLength); |
| 348 | kj::Own<BIGNUM> newBignum(); |
| 349 | |
| 350 | #define OSSL_BIO_MEM() \ |
| 351 | ({ \ |
| 352 | BIO* result = BIO_new(BIO_s_mem()); \ |
| 353 | JSG_REQUIRE(result != nullptr, InternalDOMOperationError, "Error allocating crypto"); \ |
| 354 | kj::Own<BIO>(result, workerd::api::SslDisposer<BIO, &BIO_free_all>::INSTANCE); \ |
| 355 | }) |
| 356 | |
| 357 | // Adopted from Node.js' crypto implementation. the MarkPopErrorOnReturn |
| 358 | // and ClearErrorOnReturn mechanisms make working with the openssl error |
| 359 | // stack a bit easier... |
| 360 | struct MarkPopErrorOnReturn { |
| 361 | MarkPopErrorOnReturn() { |
| 362 | ERR_set_mark(); |
| 363 | } |
| 364 | ~MarkPopErrorOnReturn() { |
| 365 | ERR_pop_to_mark(); |
| 366 | } |
| 367 | KJ_DISALLOW_COPY_AND_MOVE(MarkPopErrorOnReturn); |
| 368 | }; |
| 369 | |
| 370 | struct ClearErrorOnReturn { |
| 371 | ClearErrorOnReturn() { |
| 372 | ERR_clear_error(); |
| 373 | } |
| 374 | ~ClearErrorOnReturn() { |
| 375 | ERR_clear_error(); |
| 376 | } |
| 377 | KJ_DISALLOW_COPY_AND_MOVE(ClearErrorOnReturn); |
| 378 | |
| 379 | uint32_t peekError() { |
| 380 | return ERR_peek_error(); |
| 381 | } |
| 382 | uint32_t consumeError() { |
| 383 | return ERR_get_error(); |
| 384 | } |
| 385 | }; |
| 386 | |
| 387 | // Returns ceil(a / b) for integers (std::ceil always returns a floating point result). |
| 388 | template <typename T> |
| 389 | static inline T integerCeilDivision(T a, T b) { |
| 390 | static_assert(std::is_unsigned_v<T>); |
| 391 | return a == 0 ? 0 : 1 + (a - 1) / b; |
| 392 | } |
| 393 | |
| 394 | // A wrapper for kj::Array<kj::byte> that will ensure the memory is overwritten |
| 395 | // with zeroes when destroyed. |
| 396 | class ZeroOnFree { |
| 397 | public: |
| 398 | inline ZeroOnFree(kj::Array<kj::byte>&& inner): inner(kj::mv(inner)) {} |
| 399 | ~ZeroOnFree() noexcept(false); |
| 400 | |
| 401 | inline size_t size() const { |
| 402 | return inner.size(); |
| 403 | } |
| 404 | inline const kj::byte* begin() const { |
| 405 | return inner.begin(); |
| 406 | } |
| 407 | inline operator kj::ArrayPtr<const kj::byte>() const { |
| 408 | return inner.asPtr(); |
| 409 | } |
| 410 | inline operator const kj::Array<kj::byte>&() const { |
| 411 | return inner; |
| 412 | } |
| 413 | inline kj::ArrayPtr<kj::byte> asPtr() { |
| 414 | return inner.asPtr(); |
| 415 | } |
| 416 | inline kj::ArrayPtr<const kj::byte> asPtr() const { |
| 417 | return inner.asPtr(); |
| 418 | } |
| 419 | |
| 420 | private: |
| 421 | kj::Array<kj::byte> inner; |
| 422 | }; |
| 423 | |
| 424 | // Check that the requested number of iterations for a key-derivation function |
| 425 | // is acceptable. If the requested iterations is not acceptable, a JS error will |
| 426 | // be thrown. Otherwise the method will return normally. |
| 427 | void checkPbkdfLimits(jsg::Lock& js, size_t iterations); |
| 428 | |
| 429 | // Either succeeds with exactly |length| bytes of cryptographically |
| 430 | // strong pseudo-random data, or fails. This function may block. |
| 431 | // Don't assume anything about the contents of |buffer| on error. |
| 432 | // As a special case, |length == 0| can be used to check if the CSPRNG |
| 433 | // is properly seeded without consuming entropy. |
| 434 | bool CSPRNG(kj::ArrayPtr<kj::byte> buffer); |
| 435 | |
| 436 | kj::Own<CryptoKey::Impl> fromRsaKey(jsg::Lock& js, kj::Own<EVP_PKEY> key); |
| 437 | kj::Own<CryptoKey::Impl> fromEcKey(kj::Own<EVP_PKEY> key); |
| 438 | kj::Own<CryptoKey::Impl> fromEd25519Key(kj::Own<EVP_PKEY> key); |
| 439 | |
| 440 | // If the input bytes are a valid ASN.1 sequence, return them minus the prefix. |
| 441 | kj::Maybe<kj::ArrayPtr<const kj::byte>> tryGetAsn1Sequence(kj::ArrayPtr<const kj::byte> data); |
| 442 | |
| 443 | template <typename T = const kj::byte> |
| 444 | ncrypto::Buffer<T> ToNcryptoBuffer(kj::ArrayPtr<T> array) { |
| 445 | return ncrypto::Buffer<T>(array.begin(), array.size()); |
| 446 | } |
| 447 | |
| 448 | kj::Maybe<kj::Array<kj::byte>> simdutfBase64UrlDecode(kj::StringPtr input); |
| 449 | kj::Maybe<jsg::JsUint8Array> simdutfBase64UrlDecode(jsg::Lock& js, kj::StringPtr input); |
| 450 | jsg::JsUint8Array simdutfBase64UrlDecodeChecked( |
| 451 | jsg::Lock& js, kj::StringPtr input, kj::StringPtr error); |
| 452 | |
| 453 | } // namespace workerd::api |
| 454 | |
| 455 | KJ_DECLARE_NON_POLYMORPHIC(DH); |
| 456 | KJ_DECLARE_NON_POLYMORPHIC(EC_KEY); |
| 457 | KJ_DECLARE_NON_POLYMORPHIC(EC_POINT); |
| 458 | KJ_DECLARE_NON_POLYMORPHIC(EC_GROUP); |
| 459 | KJ_DECLARE_NON_POLYMORPHIC(BN_CTX); |
| 460 | KJ_DECLARE_NON_POLYMORPHIC(EVP_PKEY); |
| 461 | KJ_DECLARE_NON_POLYMORPHIC(EVP_PKEY_CTX); |
| 462 | KJ_DECLARE_NON_POLYMORPHIC(RSA); |
| 463 | // Tell KJ that these OpenSSL types are non-polymorphic so that they can be wrapped in kj::Own. |