CryptoKey bindings in Cloudflare Workers - importKey at publish time!
If you use the Web Crypto API, chances are you're using importKey
- this is an async method that takes the format, usages, algorithm & the key and gives you a CryptoKey object.
If your key is never changing (or very infrequently), having to import on every request is wasteful - both for CPU & wall-time. You can somewhat mitigate this by throwing it in the global scope & Worker executions on the same isolate will be able to access it, but Workers can be evicted at any time and this isn't a reliable method.
When the Cloudflare Workers runtime went open-source, it didn't take long for people to find some bindings that aren't documented - in particular, CryptoKey
. This allows you to do the importKey
at publish time and the resulting CryptoKey
object will be available to the Worker like any other binding.
How do we use it? Well, it's not officially in Wrangler yet - but Wrangler has an 'unsafe binding' functionality where you can push any object you like onto the bindings payload which is often used for early betas that haven't decided on a stable binding.
Let's take a look at how they're used in workerd
...
How does this translate into our wrangler.toml
? Let's take a look:
[[unsafe.bindings]]
type = "secret_key"
name = "my_key"
format = "raw"
algorithm = { name = "HMAC", hash = "SHA-256" }
usages = ["sign"]
key_base64 = "key_data_here"
How can we leverage it in a Worker? Just reference the CryptoKey available under env.my_key
:
interface Env {
my_key: CryptoKey
}
export default <ExportedHandler<Env>> {
fetch(req, env, ctx) {
console.log(env.my_key.usages)
return new Response('')
}
}
Here's an example of how to use the CryptoKey
object:
interface Env {
my_key: CryptoKey
}
export default <ExportedHandler<Env>> {
async fetch(req, env, ctx) {
const algo = { name: "HMAC", hash: "SHA-256" }
const enc = new TextEncoder();
const data = enc.encode("lol")
const signed = await crypto.subtle.sign(algo, env.my_key, data)
const verified = await crypto.subtle.verify(algo, env.my_key, signed, data)
console.log(verified) // true
return new Response('')
}
}
Take a look at the workerd source code to see the different key types you can use.