KeyFormat unstable
Key formats: SPKI, PKCS#8, JWK, and PEM, with import-only PKCS#1 and SEC1.
decodePublic, decodePrivate, and decodeSecret build
typed keys from encoded data, and encode writes a key in a named
format. They are plain functions, not a service: each returns an Effect that
requires nothing and runs on the WebCrypto reference.
The algorithm decides the family of the result. Decoding with
Signature.ecdsaP256 gives a Signature key, and with
KeyAgreement.ecdhP256 a KeyAgreement key. Raw bytes are not a format
here: each family's own constructors read them.
Format values carry their role, direction, and data type in the types:
- spki (public) and pkcs8 (private) read and write DER bytes;
- jwk reads and writes JWK objects for every role;
- pkcs1 (RSA) and sec1 (EC) read private DER keys and cannot be written;
- pem turns any DER format into PEM text.
Decoded keys always hold bytes, in the forms each family stores, so they work with every layer.
Gotchas
- EC private keys must include their public point. Safari and Firefox
before 132 cannot import one without it, so every runtime fails with an
Unsupportedreason. - SPKI accepts compressed EC points. Safari and Firefox before 146 reject them in raw import, so the families' raw constructors take only uncompressed points.
- Ed25519 and X25519 private JWKs need a correct
x. Bun and Deno acceptdalone, and other runtimes reject it. - JWK members other than
kty,crv,alg, and the key material are ignored on import, and written JWKs contain onlykty,crv, and the key material. - Callers parse and serialize JSON themselves.
Decoding
decodePrivate
unstableDecodes a private key.
Details
The format must hold private keys: pkcs8, jwk,
pkcs1, sec1, or their PEM forms. PKCS#1 and SEC1 are
converted to PKCS#8 before the import. Malformed data, a key of another
algorithm or curve, or a JWK without d fails with an InvalidKey reason.
An EC private key without its public point fails with an Unsupported
reason on every runtime, because Safari and Firefox before 132 cannot import
it.
Signature
declare function decodePrivate<A extends AsymmetricAlgorithm, Data>(format: Format<"private", "decode", Data>, algorithm: A, data: NoInfer<Data>): Effect<PrivateKey<A>, CryptoError>decodePublic
unstableDecodes a public key.
Details
The format must hold public keys: spki, jwk, or
pem(spki). The key is imported on the WebCrypto reference and stored as
bytes. Malformed data, a key of another algorithm or curve, or a JWK with
d fails with an InvalidKey reason.
Signature
declare function decodePublic<A extends AsymmetricAlgorithm, Data>(format: Format<"public", "decode", Data>, algorithm: A, data: NoInfer<Data>): Effect<PublicKey<A>, CryptoError>Example Reading a PEM public key
import { Effect } from "effect"import { KeyFormat, Signature } from "effect/crypto"
declare const text: string
const publicKey = KeyFormat.decodePublic(KeyFormat.pem(KeyFormat.spki), Signature.ecdsaP256, text)decodeSecret
unstableDecodes a secret key from a JWK.
Details
The JWK must have kty oct when kty is present, and an alg matching
the algorithm when alg is present. The key bytes go through the family's
raw constructor, so a wrong length fails with an InvalidKey reason.
Secret keys never touch crypto.subtle here.
Signature
declare function decodeSecret<A extends SecretAlgorithm, Data>(format: Format<"secret", "decode", Data>, algorithm: A, data: NoInfer<Data>): Effect<SecretKey<A>, CryptoError>Encoding
Encodes a key in a format.
Details
The format must hold the key's role and be writable: spki for public keys, pkcs8 for private keys, jwk for every role, and the PEM forms of SPKI and PKCS#8. PKCS#1 and SEC1 cannot be written.
PEM output uses 64-column lines. JWK output contains only kty, crv, and
the key material. A non-extractable CryptoKey-backed key fails with a
NotExtractable reason.
Signature
declare function encode<K extends Key, Data>(format: Format<RoleOf<K>, "encode", Data>, key: K): Effect<Data, CryptoError>Example Writing a public key as JWK
import { Effect, Layer } from "effect"import { KeyFormat, SecureRandom, Signature } from "effect/crypto"
const program = Effect.gen(function*() { const signature = yield* Signature.Signature const { publicKey } = yield* signature.generateKeyPair(Signature.ed25519) const jwk = yield* KeyFormat.encode(KeyFormat.jwk, publicKey) return [jwk.kty, jwk.crv]})
const layer = Signature.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto))await Effect.runPromise(program.pipe(Effect.provide(layer))) // => ["OKP", "Ed25519"]Formats
JSON Web Key (RFC 7517), for every role.
Details
decodePublic rejects a JWK with d, and decodePrivate requires it.
kty, crv, and alg must match the algorithm when present. Written JWKs
contain only kty, crv, and the key material.
Ed25519 and X25519 private JWKs need a correct x: Bun and Deno accept d
alone, but other runtimes reject it.
Signature
declare const jwk: Format<Role, Direction, JsonWebKey>PEM (RFC 7468) over a DER format.
Details
Reading accepts exactly one block whose label matches the format:
PUBLIC KEY, PRIVATE KEY, RSA PRIVATE KEY, or EC PRIVATE KEY.
Writing uses 64-column lines.
Signature
declare function pem<R extends Role, D extends Direction>(format: Format<R, D, Uint8Array<ArrayBufferLike>>): Format<R, D, string>PKCS#1 RSAPrivateKey DER (RFC 8017), read only.
Signature
declare const pkcs1: Format<"private", "decode", Uint8Array>PKCS#8 PrivateKeyInfo DER (RFC 5208), for private keys.
Signature
declare const pkcs8: Format<"private", Direction, Uint8Array>SEC1 ECPrivateKey DER (RFC 5915), read only.
Details
The curve parameters, when present, must match the algorithm's curve. The public point must be present.
Signature
declare const sec1: Format<"private", "decode", Uint8Array>SubjectPublicKeyInfo DER (RFC 5280), for public keys.
Signature
declare const spki: Format<"public", Direction, Uint8Array>Models
The algorithms whose public and private keys KeyFormat reads and writes.
Signature
type AsymmetricAlgorithm = Signature.Algorithm | KeyAgreement.Algorithm | KeyWrap.RsaOaepWhether a format can be read, written, or both.
Signature
type Direction = "decode" | "encode"A key format, such as spki or pem(pkcs8).
Details
~tags exists only in the types. It records the roles the format holds,
whether it can be read and written, and the type of its data: Uint8Array
for DER, string for PEM, and JsonWebKey for JWK.
Signature
interface Format<R extends Role, D extends Direction, Data> { readonly _tag: "spki" | "pkcs8" | "jwk" | "pkcs1" | "sec1"; readonly "~tags"?: { readonly data: Data; readonly direction: (_: D) => void; readonly role: (_: R) => void; }; readonly pem: boolean;}Any key KeyFormat can write.
Signature
type Key = PublicKey | PrivateKey | SecretKeyThe private key type of an algorithm.
Signature
type PrivateKey<A extends AsymmetricAlgorithm = AsymmetricAlgorithm> = A extends Signature.Algorithm ? Signature.PrivateKey<A> : A extends KeyAgreement.Algorithm ? KeyAgreement.PrivateKey<A> : A extends KeyWrap.RsaOaep ? KeyWrap.PrivateKey<A> : neverThe public key type of an algorithm.
Signature
type PublicKey<A extends AsymmetricAlgorithm = AsymmetricAlgorithm> = A extends Signature.Algorithm ? Signature.PublicKey<A> : A extends KeyAgreement.Algorithm ? KeyAgreement.PublicKey<A> : A extends KeyWrap.RsaOaep ? KeyWrap.PublicKey<A> : neverThe role of a key: public, private, or secret (symmetric).
Signature
type Role = "public" | "private" | "secret"The role of a key type.
Signature
type RoleOf<K extends Key> = K extends PublicKey ? "public" : K extends PrivateKey ? "private" : "secret"The algorithms whose secret keys KeyFormat reads and writes, and that
KeyWrap wraps.
Signature
type SecretAlgorithm = Aead.Algorithm | Cipher.Algorithm | Mac.Algorithm | KeyWrap.AesKwThe secret key type of an algorithm.
Signature
type SecretKey<A extends SecretAlgorithm = SecretAlgorithm> = A extends Aead.Algorithm ? Aead.Key<A> : A extends Cipher.Algorithm ? Cipher.Key<A> : A extends Mac.Algorithm ? Mac.Key<A> : A extends KeyWrap.AesKw ? KeyWrap.Key<A> : never