@portable/web-crypto
Functions
WebCrypto.derToRawEcdsa
Converts a DER-encoded ECDSA signature, as WebAuthn and OpenSSL produce, into the raw r || s
form that crypto.subtle.verify and JWS (ES256, ES384, ES512) use. Only strict DER is accepted:
BER length forms, extra leading zeros, negative integers and trailing bytes throw.
function derToRawEcdsa(
signature: Uint8Array,
options: derToRawEcdsa.Options = {},
): Uint8Array<ArrayBuffer>;| Option | Type | Default | Description |
|---|---|---|---|
namedCurve | NamedCurve | 'P-256' | The curve of the key that made the signature. It sets the width of r and s. |
import * as WebCrypto from '@portable/web-crypto';
const raw = WebCrypto.derToRawEcdsa(
new Uint8Array(assertion.signature),
);
await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
publicKey,
raw,
signedData,
);WebCrypto.rawToDerEcdsa
Converts a raw r || s ECDSA signature, as crypto.subtle.sign and JWS produce, into DER.
The curve follows from the length: 64 bytes for P-256, 96 for P-384 and 132 for P-521.
function rawToDerEcdsa(signature: Uint8Array): Uint8Array<ArrayBuffer>;import * as WebCrypto from '@portable/web-crypto';
const raw = await crypto.subtle.sign(
{ name: 'ECDSA', hash: 'SHA-256' },
privateKey,
data,
);
const der = WebCrypto.rawToDerEcdsa(new Uint8Array(raw));WebCrypto.digest
Hashes bytes, or the UTF-8 bytes of a string, with crypto.subtle.digest.
function digest(
hash: Hash,
data: string | Uint8Array,
options: digest.Options = {},
): Promise<Uint8Array<ArrayBuffer>>;| Option | Type | Default | Description |
|---|---|---|---|
signal | AbortSignal | Rejects with signal.reason once aborted. |
import * as WebCrypto from '@portable/web-crypto';
const hash = await WebCrypto.digest('SHA-256', 'hello');WebCrypto.hkdf
Derives length bytes from key with HKDF (RFC 5869), extract and expand in one call.
function hkdf(
hash: Hash,
key: string | Uint8Array,
options: hkdf.Options = {},
): Promise<Uint8Array<ArrayBuffer>>;| Option | Type | Default | Description |
|---|---|---|---|
info | string | Uint8Array<ArrayBufferLike> | empty | Context for the derived key. |
length | number | the hash length | Bytes to derive, at most 255 times the hash length. |
salt | string | Uint8Array<ArrayBufferLike> | empty, which RFC 5869 treats as a string of zero bytes as long as the hash | |
signal | AbortSignal | Rejects with signal.reason once aborted. |
import * as WebCrypto from '@portable/web-crypto';
const key = await WebCrypto.hkdf('SHA-256', sharedSecret, {
info: 'my app v1',
length: 32,
});WebCrypto.hmac
Computes an HMAC (RFC 2104) of data with key. Strings are read as UTF-8. The imported
CryptoKey is cached for up to 16 keys of at most 256 bytes, so signing again with the same
secret skips importKey.
function hmac(
hash: Hash,
key: string | Uint8Array,
data: string | Uint8Array,
options: hmac.Options = {},
): Promise<Uint8Array<ArrayBuffer>>;| Option | Type | Default | Description |
|---|---|---|---|
signal | AbortSignal | Rejects with signal.reason once aborted. |
import * as WebCrypto from '@portable/web-crypto';
const signature = await WebCrypto.hmac(
'SHA-256',
'whsec_secret',
'{"id":1}',
);WebCrypto.importSeed
Imports a 32-byte Ed25519 or X25519 private key, the form RFC 8032 and RFC 7748 use, and returns it with its public key. WebCrypto imports these keys only as PKCS #8 or JWK.
function importSeed(
algorithm: SeedAlgorithm,
seed: Uint8Array,
options: importSeed.Options = {},
): Promise<CryptoKeyPair>;| Option | Type | Default | Description |
|---|---|---|---|
extractable | boolean | false | Whether the private key can be exported. The public key always can. |
signal | AbortSignal | Rejects with signal.reason once aborted. |
import * as WebCrypto from '@portable/web-crypto';
const seed = crypto.getRandomValues(new Uint8Array(32));
const { privateKey, publicKey } = await WebCrypto.importSeed(
'Ed25519',
seed,
);
const signature = await crypto.subtle.sign('Ed25519', privateKey, data);WebCrypto.publicKeyOf
Returns the public key of an extractable private key: Ed25519, X25519, ECDSA, ECDH or RSA. The public key can verify what the private key signs and encrypt what it decrypts.
function publicKeyOf(
privateKey: CryptoKey,
options: publicKeyOf.Options = {},
): Promise<CryptoKey>;| Option | Type | Default | Description |
|---|---|---|---|
signal | AbortSignal | Rejects with signal.reason once aborted. |
import * as WebCrypto from '@portable/web-crypto';
const privateKey = await crypto.subtle.importKey(
'pkcs8',
der,
'Ed25519',
true,
['sign'],
);
const publicKey = await WebCrypto.publicKeyOf(privateKey);WebCrypto.importPem
Imports a PEM private or public key. WebCrypto imports only DER, and only as PKCS #8 or
SubjectPublicKeyInfo, so importPem decodes the base64 and wraps a PKCS #1 RSA PRIVATE KEY
or RSA PUBLIC KEY, the form openssl genrsa writes, in the structure WebCrypto takes. It
reads the first PRIVATE KEY, RSA PRIVATE KEY, PUBLIC KEY or RSA PUBLIC KEY block and
skips other blocks, such as a certificate before the key.
function importPem(
algorithm: importPem.Algorithm,
pem: string | Uint8Array,
options: importPem.Options = {},
): Promise<CryptoKey>;| Option | Type | Default | Description |
|---|---|---|---|
extractable | boolean | false | Whether the key can be exported. |
usages | readonly KeyUsage[] | What the key may be used for. The default depends on the algorithm and on whether the key is private or public, such as ['sign'] and ['verify'] for a signing algorithm. | |
signal | AbortSignal | Rejects with signal.reason once aborted. |
import * as WebCrypto from '@portable/web-crypto';
const privateKey = await WebCrypto.importPem(
{ name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
pem,
);
const signature = await crypto.subtle.sign(
'RSASSA-PKCS1-v1_5',
privateKey,
data,
);Errors
WebCrypto.InvalidInputError
Thrown for a malformed DER signature, a raw signature or seed of the wrong length, a key that is not an extractable private key, or a PEM key that doesn't decode or doesn't import for the algorithm.
class InvalidInputError extends Errors.InvalidInputError {
readonly kind: string;
}import * as WebCrypto from '@portable/web-crypto';
try {
WebCrypto.derToRawEcdsa(new Uint8Array([0x30, 0x00]));
} catch (error) {
if (error instanceof WebCrypto.InvalidInputError) {
console.log(error.message);
}
}WebCrypto.UnsupportedError
Thrown by importPem for a valid PEM block it doesn't import, such as an EC PRIVATE KEY, a
certificate or an encrypted key.
class UnsupportedError extends Errors.UnsupportedError {
readonly kind: string;
}import * as WebCrypto from '@portable/web-crypto';
try {
await WebCrypto.importPem(
{ name: 'ECDSA', namedCurve: 'P-256' },
pem,
);
} catch (error) {
if (error instanceof WebCrypto.UnsupportedError) {
console.log(
'Convert the key to PKCS #8 with `openssl pkcs8 -topk8 -nocrypt`.',
);
}
}Types
WebCrypto.NamedCurve
An ECDSA curve, named as WebCrypto names it.
type NamedCurve = | | ;WebCrypto.Digest
A function that hashes data with the algorithm hash and returns the digest or a promise of
it. Packages that hash take one as their digest option, and WebCrypto.digest is one.
type Digest = (
hash: Hash,
data: Uint8Array<ArrayBuffer>,
) => Uint8Array | Promise<Uint8Array>;WebCrypto.Hash
A hash algorithm, by its WebCrypto name. The @portable/* packages call a Digest or Hmac
function only with these names. If WebCrypto adds a hash later, such as SHA-3, it will get its
own type, so a switch over Hash in your code still compiles.
type Hash = | | | ;WebCrypto.Hasher
An incremental hash with the same methods as sha256.create() from @noble/hashes.
crypto.subtle.digest needs the whole input at once, so a package that hashes a stream takes a
hasher option, a function that returns one of these.
type Hasher = {
update(data: Uint8Array): unknown;
digest(): Uint8Array | Promise<Uint8Array>;
};WebCrypto.Hmac
Computes an HMAC of data with key and hash, sync or async. Packages that sign with HMAC
take one as their hmac option. WebCrypto.hmac fits this type, and so does a wrapper around
@noble/hashes, which is sync and faster on Node.
type Hmac = (
hash: Hash,
key: Uint8Array<ArrayBuffer>,
data: Uint8Array<ArrayBuffer>,
) => Uint8Array | Promise<Uint8Array>;WebCrypto.SeedAlgorithm
A curve whose private key is a 32-byte seed.
type SeedAlgorithm = | ;