Programming API
Web Crypto
The Web Crypto API provides a set of low-level functions for common cryptographic tasks. The Workers Runtime implements the full surface of this API, but with some differences in the supported algorithms compared to those implemented in most browsers.
Performing cryptographic operations using the Web Crypto API is significantly faster than performing them purely in JavaScript. If you want to perform CPU-intensive cryptographic operations, you should consider using the Web Crypto API.
The Web Crypto API is implemented through the SubtleCrypto
interface,
accessible via the global crypto.subtle
binding. A simple example of
calculating a digest (also known as a hash) is:
const myText = new TextEncoder().encode("Hello world!"); const myDigest = await crypto.subtle.digest( { name: "SHA-256", }, myText, // The data you want to hash as an ArrayBuffer ); console.log(new Uint8Array(myDigest));
Caution
The Web Crypto API differs significantly from Node’s Crypto API. If you want to port JavaScript code that relies on Node’s Crypto API, you will need to adapt it to use Web Crypto primitives.
Methods#
Crypto.getRandomValues()
#
The Crypto.getRandomValues()
method lets you get cryptographically strong
random values.
Refer to the MDN documentation for more information
Crypto.randomUUID()
#
Generates a v4 UUID using a cryptographically secure random number generator.
Refer to the MDN documentation for more information
const uuid = crypto.randomUUID();
HMAC Signatures#
The following code blocks show how to sign and verify a string using HMAC.
Sign a Value#
/** * Creates a signature for a value and a given secret * @param value - The value to check * @param secret - the secret * @returns The base64 encoded signature **/ async function sign(value: string, secret: string) { // You will need some super-secret data to use as a symmetric key. const encoder = new TextEncoder(); const secretKeyData = encoder.encode(secret); const key = await crypto.subtle.importKey( "raw", secretKeyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign"], ); const mac = await crypto.subtle.sign("HMAC", key, encoder.encode(value)); // `mac` is an ArrayBuffer, so you need to make a few changes to get // it into a ByteString, and then a Base64-encoded string. let base64Mac = btoa(String.fromCharCode(...new Uint8Array(mac))); // must convert "+" to "-" as urls encode "+" as " " base64Mac = base64Mac.replaceAll("+", "-"); return base64Mac; }
Verify a Value#
/** * Verifies a value against a signature and secret * @param signature - The base64 encoded signature * @param value - The value to check * @param secret - the secret * @returns true if the signature is value against the given value and secret **/ async function verify(signature: string, value: string, secret: string) { // You will need some super-secret data to use as a symmetric key. const encoder = new TextEncoder(); const secretKeyData = encoder.encode(secret); // Convert a ByteString (a string whose code units are all in the range // [0, 255]), to a Uint8Array. If you pass in a string with code units larger // than 255, their values will overflow. function byteStringToUint8Array(byteString) { const ui = new Uint8Array(byteString.length); for (let i = 0; i < byteString.length; ++i) { ui[i] = byteString.charCodeAt(i); } return ui; } const key = await crypto.subtle.importKey( "raw", secretKeyData, { name: "HMAC", hash: "SHA-256" }, false, ["verify"], ); // The received MAC is Base64-encoded, so you have to go to some trouble to // get it into a buffer type that crypto.subtle.verify() can read. const receivedMac = byteStringToUint8Array(atob(signature)); // Use crypto.subtle.verify() to guard against timing attacks. Since HMACs use // symmetric keys, you could implement this by calling crypto.subtle.sign() and // then doing a string comparison -- this is insecure, as string comparisons // bail out on the first mismatch, which leaks information to potential // attackers. const verified = await crypto.subtle.verify( "HMAC", key, receivedMac, encoder.encode(value), ); return verified; }
Supported algorithms#
The runtime implements all operations of the WebCrypto standard, as shown in the following table.
A checkmark (✓) indicates that this feature is believed to be fully supported according to the spec.
An x (✘) indicates that this feature is part of the specification but not implemented.
If a feature only implements the operation partially, details are listed.
Algorithm | sign() verify() | encrypt() decrypt() | digest() | deriveBits() deriveKey() | generateKey() | wrapKey() unwrapKey() | exportKey() | importKey() |
---|---|---|---|---|---|---|---|---|
RSASSA PKCS1 v1.5 | ✓ | ✓ | ✓ | ✓ | ||||
RSA PSS | ✓ | ✓ | ✓ | ✓ | ||||
RSA OAEP | ✓ | ✓ | ✓ | ✓ | ✓ | |||
ECDSA | ✓ | ✓ | ✓ | ✓ | ||||
ECDH | ✓ | ✓ | ✓ | ✓ | ||||
Ed255191 | ✓ | ✓ | ✓ | ✓ | ||||
X255191 | ✓ | ✓ | ✓ | ✓ | ||||
NODE ED255192 | ✓ | ✓ | ✓ | ✓ | ||||
AES CTR | ✓ | ✓ | ✓ | ✓ | ✓ | |||
AES CBC | ✓ | ✓ | ✓ | ✓ | ✓ | |||
AES GCM | ✓ | ✓ | ✓ | ✓ | ✓ | |||
AES KW | ✓ | ✓ | ✓ | ✓ | ||||
HMAC | ✓ | ✓ | ✓ | ✓ | ||||
SHA 1 | ✓ | |||||||
SHA 256 | ✓ | |||||||
SHA 384 | ✓ | |||||||
SHA 512 | ✓ | |||||||
MD53 | ✓ | |||||||
HKDF | ✓ | ✓ | ||||||
PBKDF2 | ✓ | ✓ |
Footnotes:
-
Algorithms as specified in the Secure Curves API.
-
Legacy non-standard EdDSA is supported for the Ed25519 curve in addition to the Secure Curves version. Since this algorithm is non-standard, note the following while using it:
- Use
NODE-ED25519
as the algorithm andnamedCurve
parameters. - Unlike NodeJS, Cloudflare will not support raw import of private keys.
- The algorithm implementation may change over time. While Cloudflare cannot guarantee it at this time, Cloudflare will strive to maintain backward compatibility and compatibility with NodeJS's behavior. Any notable compatibility notes will be communicated in release notes and via this developer documentation.
- Use
-
MD5 is not part of the WebCrypto standard but is supported in Cloudflare Workers for interacting with legacy systems that require MD5. MD5 is considered a weak algorithm. Do not rely upon MD5 for security.