KeyWrap unstable
Key wrapping: AES-KW (RFC 3394) and RSA-OAEP (RFC 8017).
The KeyWrap service generates AES-KW keys and RSA-OAEP key pairs, wraps keys, and unwraps them. The top-level key, fromCryptoKey, privateKeyFromCryptoKey, publicKeyFromCryptoKey, and toBytes functions build and export keys without a service.
wrap takes an Aead, Cipher, Mac, or KeyWrap key, never raw bytes,
which keeps RSA-OAEP limited to key transport. unwrap returns a typed key
of the algorithm it is given.
A key-encryption key is the key most worth keeping non-extractable, so in browsers fromCryptoKey is the main way to build one.
Keys are opaque. They hold either bytes or a CryptoKey, display only their
algorithm, and compare by reference. The bytes are the AES-KW key, and
PKCS#8 or SPKI DER for RSA-OAEP.
Gotchas
- AES-KW wraps keys whose length is a multiple of 8 bytes and at least 16
bytes. Other lengths fail with an
InvalidArgumentreason. - RSA-OAEP key generation and encryption use WebCrypto's own randomness and
cannot be seeded through
SecureRandom. - Safari rejects RSA-OAEP keys over 4096 bits.
Algorithms
AES-128-KW (JOSE A128KW), with a 16-byte key.
Signature
declare const aes128Kw: AesKwAES-256-KW (JOSE A256KW), with a 32-byte key.
Signature
declare const aes256Kw: AesKwBuilds an RSA-OAEP algorithm for a hash and a modulus length in bits.
Details
MGF1 uses the same hash. There is no OAEP label.
Signature
declare function rsaOaep(options: { readonly hash: Algorithm; readonly modulusLength: number;}): RsaOaeprsaOaepSha1
unstableRSA-OAEP with SHA-1 (JOSE RSA-OAEP), generating 3072-bit keys.
When to use
Use to interoperate with JOSE RSA-OAEP. Otherwise prefer
rsaOaepSha256.
Signature
declare const rsaOaepSha1: RsaOaeprsaOaepSha256
unstableRSA-OAEP with SHA-256 (JOSE RSA-OAEP-256), generating 3072-bit keys. This is the default RSA-OAEP algorithm.
Signature
declare const rsaOaepSha256: RsaOaepConstructors
fromCryptoKey
unstableBuilds an AES-KW key from an existing CryptoKey, such as a
non-extractable one.
Details
The CryptoKey must be a secret AES-KW key of the algorithm's length with
both the wrapKey and unwrapKey usages, since a key reaches both
operations. Otherwise the constructor fails with an InvalidKey reason. It
must come from the Crypto object of the WebCrypto reference.
Signature
declare function fromCryptoKey<A extends AesKw>(algorithm: A, cryptoKey: CryptoKey): Effect<Key<A>, CryptoError>Builds an AES-KW key from its bytes.
Details
The bytes are copied. A length other than the algorithm's key length fails
with an InvalidKey reason. RSA-OAEP keys have no raw form, so passing
them is a type error.
Signature
declare function key<A extends AesKw>(algorithm: A, bytes: Uint8Array): Effect<Key<A>, CryptoError>privateKeyFromCryptoKey
unstableBuilds an RSA-OAEP private key from an existing CryptoKey, such as a
non-extractable one.
Details
The CryptoKey must be an RSA-OAEP private key with the algorithm's hash
and the decrypt or unwrapKey usage. Otherwise the constructor fails
with an InvalidKey reason. The modulus length is not checked. The key must
come from the Crypto object of the WebCrypto reference.
Signature
declare function privateKeyFromCryptoKey<A extends RsaOaep>(algorithm: A, cryptoKey: CryptoKey): Effect<PrivateKey<A>, CryptoError>publicKeyFromCryptoKey
unstableBuilds an RSA-OAEP public key from an existing CryptoKey.
Details
The CryptoKey must be an RSA-OAEP public key with the algorithm's hash and
the encrypt or wrapKey usage. Otherwise the constructor fails with an
InvalidKey reason. The key must come from the Crypto object of the
WebCrypto reference.
Signature
declare function publicKeyFromCryptoKey<A extends RsaOaep>(algorithm: A, cryptoKey: CryptoKey): Effect<PublicKey<A>, CryptoError>Converting
Exports a key's stored bytes.
Details
Returns a fresh Uint8Array: the AES-KW key, or PKCS#8 or SPKI DER for
RSA-OAEP. A CryptoKey-backed key is exported on the WebCrypto
reference, and fails with a NotExtractable reason when the CryptoKey is
not extractable.
Signature
declare function toBytes(key: PublicKey<RsaOaep> | PrivateKey<RsaOaep> | Key<AesKw>): Effect<Uint8Array<ArrayBufferLike>, CryptoError>Guards
Checks whether a value is an AES-KW Key.
Signature
declare function isKey(u: unknown): u is Key<AesKw>isPrivateKey
unstableChecks whether a value is an RSA-OAEP PrivateKey.
Signature
declare function isPrivateKey(u: unknown): u is PrivateKey<RsaOaep>isPublicKey
unstableChecks whether a value is an RSA-OAEP PublicKey.
Signature
declare function isPublicKey(u: unknown): u is PublicKey<RsaOaep>Layers
layerWebCrypto
unstableLayer that provides KeyWrap on the WebCrypto reference.
Details
The layer imports a byte-backed key on first use and caches the
CryptoKey. AES-KW keys come from SecureRandom, so a seeded
SecureRandom reproduces them.
WebCrypto's AES-KW only wraps CryptoKeys, so the layer imports the target
bytes as a temporary extractable HMAC key and wraps that. RSA-OAEP uses
encrypt and decrypt when the key allows them, and otherwise the same
route through wrapKey and unwrapKey. Both give the same ciphertext.
Signature
declare const layerWebCrypto: Layer.Layer<KeyWrap, never, SecureRandom.SecureRandom>Models
Details
keyLength is in bytes.
Signature
interface AesKw { readonly _tag: "AesKw"; readonly keyLength: 16 | 32; readonly name: "aes128Kw" | "aes256Kw";}A key wrapping algorithm, such as aes256Kw or rsaOaepSha256.
Signature
type Algorithm = AesKw | RsaOaepAn opaque AES-KW key-encryption key for the algorithm A, which both wraps
and unwraps.
Details
The key holds either key bytes or a CryptoKey from fromCryptoKey.
toString, toJSON, and inspect show only the algorithm. Keys compare by
reference and do not implement Equal.
Signature
interface Key<out A extends AesKw = AesKw> extends Inspectable { readonly "~effect/crypto/KeyWrap/Key": "~effect/crypto/KeyWrap/Key"; readonly algorithm: A;}An RSA-OAEP private key and its public key.
Signature
interface KeyPair<out A extends RsaOaep = RsaOaep> { readonly privateKey: PrivateKey<A>; readonly publicKey: PublicKey<A>;}An opaque RSA-OAEP private key for the algorithm A, which unwraps.
Details
The key holds either PKCS#8 bytes or a CryptoKey from
privateKeyFromCryptoKey. toString, toJSON, and inspect show
only the algorithm. Keys compare by reference and do not implement Equal.
Signature
interface PrivateKey<out A extends RsaOaep = RsaOaep> extends Inspectable { readonly "~effect/crypto/KeyWrap/PrivateKey": "~effect/crypto/KeyWrap/PrivateKey"; readonly algorithm: A;}An opaque RSA-OAEP public key for the algorithm A, which wraps.
Details
The key holds either SPKI bytes or a CryptoKey from
publicKeyFromCryptoKey. Keys compare by reference and do not
implement Equal.
Signature
interface PublicKey<out A extends RsaOaep = RsaOaep> extends Inspectable { readonly "~effect/crypto/KeyWrap/PublicKey": "~effect/crypto/KeyWrap/PublicKey"; readonly algorithm: A;}RSA-OAEP (RFC 8017), from rsaOaep.
Details
MGF1 uses the same hash, and there is no label. modulusLength is in bits
and is only read by key generation: an imported key's
algorithm.modulusLength is its real size.
Signature
interface RsaOaep { readonly _tag: "RsaOaep"; readonly hash: Algorithm; readonly modulusLength: number; readonly name: string;}A key that unwraps: an AES-KW key or an RSA-OAEP private key.
Signature
type UnwrapKey<A extends Algorithm = Algorithm> = A extends AesKw ? Key<A> : never | A extends RsaOaep ? PrivateKey<A> : neverA key that wraps: an AES-KW key or an RSA-OAEP public key.
Signature
type WrapKey<A extends Algorithm = Algorithm> = A extends AesKw ? Key<A> : never | A extends RsaOaep ? PublicKey<A> : neverServices
Service that generates keys, wraps keys, and unwraps them.
Signature
declare class KeyWrap extends Shape<"effect/crypto/KeyWrap", { readonly generateKey: <A extends AesKw>(algorithm: A) => Effect<Key<A>, CryptoError>; readonly generateKeyPair: <A extends RsaOaep>(algorithm: A) => Effect<KeyPair<A>, CryptoError>; readonly unwrap: <A extends SecretAlgorithm>(unwrapKey: UnwrapKey, algorithm: A, wrapped: Uint8Array) => Effect<SecretKey<A>, CryptoError>; readonly wrap: (wrapKey: WrapKey, key: Key<Algorithm> | Key<Algorithm> | Key<Algorithm> | Key<AesKw>) => Effect<Uint8Array<ArrayBufferLike>, CryptoError>;}, this> { constructor(_: never);}Example Wrapping an AES-GCM key with AES-KW
import { Effect, Layer } from "effect"import { Aead, KeyWrap, SecureRandom } from "effect/crypto"
const program = Effect.gen(function*() { const keyWrap = yield* KeyWrap.KeyWrap const kek = yield* keyWrap.generateKey(KeyWrap.aes256Kw) const key = yield* Aead.key(Aead.aes256Gcm, new Uint8Array(32)) const wrapped = yield* keyWrap.wrap(kek, key) const unwrapped = yield* keyWrap.unwrap(kek, Aead.aes256Gcm, wrapped) return [wrapped.length, unwrapped.algorithm.name]})
const layer = KeyWrap.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto))await Effect.runPromise(program.pipe(Effect.provide(layer))) // => [40, "aes256Gcm"]