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

Signature unstable

Digital signatures: Ed25519, ECDSA on P-256, P-384 and P-521, RSA-PSS, and RSASSA-PKCS1-v1_5.

The Signature service generates key pairs, signs, verifies, and computes a private key's public key. The top-level privateKey, publicKey, privateKeyFromCryptoKey, publicKeyFromCryptoKey, and toBytes functions build and export keys without a service.

The hash is part of the algorithm. ECDSA P-256, P-384 and P-521 sign with SHA-256, SHA-384 and SHA-512, as JOSE ES256, ES384 and ES512 do. RSA algorithms come from rsaPss and rsaPkcs1v15, with pre-made values such as rsaPssSha256.

Keys are opaque. They hold either bytes or a CryptoKey, display only their algorithm, and compare by reference. The bytes are:

  • Ed25519: the 32-byte seed and the 32-byte public key;
  • ECDSA: PKCS#8 for private keys, and the uncompressed SEC1 point for public keys;
  • RSA: PKCS#8 and SPKI DER.

Only Ed25519 private keys and Ed25519 and ECDSA public keys have a raw constructor. Other keys come from Signature.generateKeyPair, CryptoKeys, or key formats.

Gotchas

  • Ed25519 needs Chrome 137, Firefox 129, or Safari 17. There is no fallback for older browsers.
  • Ed25519 keys are generated from SecureRandom bytes, so a seeded SecureRandom reproduces them. ECDSA and RSA key generation, and ECDSA and RSA-PSS signatures, use WebCrypto's own randomness and cannot be seeded. Test them with round trips such as verify(sign(data)).
30 exports Since v4.0.0 Source

Algorithms

ecdsaP256

unstable
Since v4.0.0

ECDSA on P-256 with SHA-256 (JOSE ES256), with 64-byte signatures.

Signature

declare const ecdsaP256: Ecdsa<"P-256">

ecdsaP384

unstable
Since v4.0.0

ECDSA on P-384 with SHA-384 (JOSE ES384), with 96-byte signatures.

Signature

declare const ecdsaP384: Ecdsa<"P-384">

ecdsaP521

unstable
Since v4.0.0

ECDSA on P-521 with SHA-512 (JOSE ES512), with 132-byte signatures.

Signature

declare const ecdsaP521: Ecdsa<"P-521">

ed25519

unstable
Since v4.0.0

Ed25519 (RFC 8032).

Signature

declare const ed25519: Ed25519

rsaPkcs1v15

unstable
Since v4.0.0

Builds an RSASSA-PKCS1-v1_5 algorithm for a hash and a modulus length in bits.

Signature

declare function rsaPkcs1v15(options: {
readonly hash: Algorithm;
readonly modulusLength: number;
}): RsaPkcs1v15
Since v4.0.0

RSASSA-PKCS1-v1_5 with SHA-256 (JOSE RS256), generating 3072-bit keys.

Signature

declare const rsaPkcs1v15Sha256: RsaPkcs1v15
Since v4.0.0

RSASSA-PKCS1-v1_5 with SHA-384 (JOSE RS384), generating 3072-bit keys.

Signature

declare const rsaPkcs1v15Sha384: RsaPkcs1v15
Since v4.0.0

RSASSA-PKCS1-v1_5 with SHA-512 (JOSE RS512), generating 3072-bit keys.

Signature

declare const rsaPkcs1v15Sha512: RsaPkcs1v15

rsaPss

unstable
Since v4.0.0

Builds an RSA-PSS algorithm for a hash and a modulus length in bits.

Details

MGF1 uses the same hash. The salt length defaults to the hash's output length.

Signature

declare function rsaPss(options: {
readonly hash: Algorithm;
readonly modulusLength: number;
readonly saltLength?: number;
}): RsaPss
Since v4.0.0

RSA-PSS with SHA-256 (JOSE PS256), generating 3072-bit keys.

Signature

declare const rsaPssSha256: RsaPss
Since v4.0.0

RSA-PSS with SHA-384 (JOSE PS384), generating 3072-bit keys.

Signature

declare const rsaPssSha384: RsaPss
Since v4.0.0

RSA-PSS with SHA-512 (JOSE PS512), generating 3072-bit keys.

Signature

declare const rsaPssSha512: RsaPss

Constructors

privateKey

unstable
Since v4.0.0

Builds an Ed25519 private key from its 32-byte seed.

Details

The bytes are copied. Another length fails with an InvalidKey reason, including the 64-byte seedโ€–public key form libsodium and TweetNaCl use: pass its first 32 bytes. ECDSA and RSA private keys have no raw form, so passing them is a type error.

Signature

declare function privateKey<A extends Ed25519>(algorithm: A, bytes: Uint8Array): Effect<PrivateKey<A>, CryptoError>

Builds a private key from an existing CryptoKey, such as a non-extractable one.

Details

The CryptoKey must be a private key of the algorithm's WebCrypto name, curve or hash, with the sign usage. Otherwise the constructor fails with an InvalidKey reason. The modulus length of an RSA algorithm is not checked. The key must come from the Crypto object of the WebCrypto reference.

Signature

declare function privateKeyFromCryptoKey<A extends Algorithm>(algorithm: A, cryptoKey: CryptoKey): Effect<PrivateKey<A>, CryptoError>

publicKey

unstable
Since v4.0.0

Builds a public key from its raw form: 32 bytes for Ed25519, or an uncompressed SEC1 point for ECDSA.

Details

The bytes are copied. A wrong length, or a compressed point, fails with an InvalidKey reason. Only uncompressed points are taken because Safari and Firefox before 146 reject compressed ones in raw import, though they accept them inside SPKI: decode a compressed point with KeyFormat.spki. Whether a point is on the curve is checked when the key is first used. RSA public keys have no raw form, so passing them is a type error.

Signature

declare function publicKey<A extends Ed25519 | Ecdsa<Curve>>(algorithm: A, bytes: Uint8Array): Effect<PublicKey<A>, CryptoError>

Builds a public key from an existing CryptoKey.

Details

The CryptoKey must be a public key of the algorithm's WebCrypto name, curve or hash, with the verify 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 Algorithm>(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 seed or raw public key for Ed25519, PKCS#8 or the uncompressed point for ECDSA, and PKCS#8 or SPKI DER for RSA. 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: PrivateKey<Algorithm> | PublicKey<Algorithm>): Effect<Uint8Array<ArrayBufferLike>, CryptoError>

Guards

Since v4.0.0

Checks whether a value is a PrivateKey.

Signature

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

isPublicKey

unstable
Since v4.0.0

Checks whether a value is a PublicKey.

Signature

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

Layers

Since v4.0.0

Layer that provides Signature on the WebCrypto reference.

Details

Every algorithm runs on crypto.subtle. The layer imports a byte-backed key on first use and caches the CryptoKey. Ed25519 private keys come from SecureRandom, so a seeded SecureRandom reproduces them.

Signature

declare const layerWebCrypto: Layer.Layer<Signature, never, SecureRandom.SecureRandom>

Models

Algorithm

type unstable
Since v4.0.0

A signature algorithm, such as ed25519 or ecdsaP256.

Signature

type Algorithm = Ed25519 | Ecdsa | RsaPss | RsaPkcs1v15

Curve

type unstable
Since v4.0.0

An elliptic curve for ECDSA.

Signature

type Curve = "P-256" | "P-384" | "P-521"

Ecdsa

interface unstable
Since v4.0.0

ECDSA on a NIST curve, with the curve's hash and raw rโ€–s signatures.

Signature

interface Ecdsa<C extends Curve = Curve> {
readonly _tag: "Ecdsa";
readonly hash: Algorithm;
readonly name: {
readonly "P-256": "ecdsaP256";
readonly "P-384": "ecdsaP384";
readonly "P-521": "ecdsaP521";
}[C];
readonly namedCurve: C;
}

Ed25519

interface unstable
Since v4.0.0

Ed25519 (RFC 8032), with 64-byte signatures.

Signature

interface Ed25519 {
readonly _tag: "Ed25519";
readonly name: "ed25519";
}

KeyPair

interface unstable
Since v4.0.0

A private key and its public key.

Signature

interface KeyPair<out A extends Algorithm = Algorithm> {
readonly privateKey: PrivateKey<A>;
readonly publicKey: PublicKey<A>;
}

PrivateKey

interface unstable
Since v4.0.0

An opaque signing key for the algorithm A.

Details

The key holds either 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 Algorithm = Algorithm> extends Inspectable {
readonly "~effect/crypto/Signature/PrivateKey": "~effect/crypto/Signature/PrivateKey";
readonly algorithm: A;
}

PublicKey

interface unstable
Since v4.0.0

An opaque verification key for the algorithm A.

Details

The key holds either bytes or a CryptoKey from publicKeyFromCryptoKey. Keys compare by reference and do not implement Equal.

Signature

interface PublicKey<out A extends Algorithm = Algorithm> extends Inspectable {
readonly "~effect/crypto/Signature/PublicKey": "~effect/crypto/Signature/PublicKey";
readonly algorithm: A;
}

RsaPkcs1v15

interface unstable
Since v4.0.0

RSASSA-PKCS1-v1_5 (RFC 8017), from rsaPkcs1v15.

Details

modulusLength is in bits and is only read by key generation: an imported key's algorithm.modulusLength is its real size.

Signature

interface RsaPkcs1v15 {
readonly _tag: "RsaPkcs1v15";
readonly hash: Algorithm;
readonly modulusLength: number;
readonly name: string;
}

RsaPss

interface unstable
Since v4.0.0

RSA-PSS (RFC 8017), from rsaPss.

Details

modulusLength is in bits and is only read by key generation: an imported key's algorithm.modulusLength is its real size. saltLength is in bytes.

Signature

interface RsaPss {
readonly _tag: "RsaPss";
readonly hash: Algorithm;
readonly modulusLength: number;
readonly name: string;
readonly saltLength: number;
}

Services

Signature

unstable
Since v4.0.0

Service that generates key pairs, signs, verifies, and computes public keys.

Signature

declare class Signature extends Shape<"effect/crypto/Signature", {
readonly generateKeyPair: <A extends Algorithm>(algorithm: A) => Effect<KeyPair<A>, CryptoError>;
readonly publicKeyOf: <A extends Algorithm>(privateKey: PrivateKey<A>) => Effect<PublicKey<A>, CryptoError>;
readonly sign: (privateKey: PrivateKey, data: Uint8Array) => Effect<Uint8Array<ArrayBufferLike>, CryptoError>;
readonly verify: (publicKey: PublicKey, signature: Uint8Array, data: Uint8Array) => Effect<boolean, CryptoError>;
}, this> {
constructor(_: never);
}

Example Signing and verifying with Ed25519

import { Effect, Layer } from "effect"
import { SecureRandom, Signature } from "effect/crypto"
const program = Effect.gen(function*() {
const signature = yield* Signature.Signature
const { privateKey, publicKey } = yield* signature.generateKeyPair(Signature.ed25519)
const data = new TextEncoder().encode("message")
const signed = yield* signature.sign(privateKey, data)
return [signed.length, yield* signature.verify(publicKey, signed, data)]
})
const layer = Signature.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto))
await Effect.runPromise(program.pipe(Effect.provide(layer))) // => [64, true]