Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

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 InvalidArgument reason.
  • 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.
24 exports Since v4.0.0 Source

Algorithms

aes128Kw

unstable
Since v4.0.0

AES-128-KW (JOSE A128KW), with a 16-byte key.

Signature

declare const aes128Kw: AesKw

aes256Kw

unstable
Since v4.0.0

AES-256-KW (JOSE A256KW), with a 32-byte key.

Signature

declare const aes256Kw: AesKw

rsaOaep

unstable
Since v4.0.0

Builds 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;
}): RsaOaep

rsaOaepSha1

unstable
Since v4.0.0

RSA-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: RsaOaep
Since v4.0.0

RSA-OAEP with SHA-256 (JOSE RSA-OAEP-256), generating 3072-bit keys. This is the default RSA-OAEP algorithm.

Signature

declare const rsaOaepSha256: RsaOaep

Constructors

Since v4.0.0

Builds 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>

key

unstable
Since v4.0.0

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>

Builds 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>

Builds 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

toBytes

unstable
Since v4.0.0

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

isKey

unstable
Since v4.0.0

Checks whether a value is an AES-KW Key.

Signature

declare function isKey(u: unknown): u is Key<AesKw>
Since v4.0.0

Checks whether a value is an RSA-OAEP PrivateKey.

Signature

declare function isPrivateKey(u: unknown): u is PrivateKey<RsaOaep>

isPublicKey

unstable
Since v4.0.0

Checks whether a value is an RSA-OAEP PublicKey.

Signature

declare function isPublicKey(u: unknown): u is PublicKey<RsaOaep>

Layers

Since v4.0.0

Layer 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

AesKw

interface unstable
Since v4.0.0

AES-KW (RFC 3394), from aes128Kw or aes256Kw.

Details

keyLength is in bytes.

Signature

interface AesKw {
readonly _tag: "AesKw";
readonly keyLength: 16 | 32;
readonly name: "aes128Kw" | "aes256Kw";
}

Algorithm

type unstable
Since v4.0.0

A key wrapping algorithm, such as aes256Kw or rsaOaepSha256.

Signature

type Algorithm = AesKw | RsaOaep

Key

interface unstable
Since v4.0.0

An 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;
}

KeyPair

interface unstable
Since v4.0.0

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>;
}

PrivateKey

interface unstable
Since v4.0.0

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;
}

PublicKey

interface unstable
Since v4.0.0

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;
}

RsaOaep

interface unstable
Since v4.0.0

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;
}

UnwrapKey

type unstable
Since v4.0.0

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> : never

WrapKey

type unstable
Since v4.0.0

A 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> : never

Services

KeyWrap

unstable
Since v4.0.0

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"]