Streaming encryption for the browser, based on Encrypted Content-Encoding for HTTP (RFC 8188)
- install
- fork
- example
- API
new Keychain([key, [salt]])
keychain.key
keychain.keyB64
keychain.salt
keychain.saltB64
keychain.authToken()
keychain.authTokenB64()
keychain.authHeader()
keychain.setAuthToken(authToken)
keychain.encryptStream(stream)
keychain.decryptStream(encryptedStream)
keychain.decryptStreamRange(offset, length, totalEncryptedLength)
keychain.encryptMeta(meta)
keychain.decryptMeta(ivEncryptedMeta)
keychain.encryptBytes(bytes)
keychain.decryptBytes(bytes)
plaintextSize(encryptedSize)
encryptedSize(plaintextSize)
- credits
npm i -S @bicycle-codes/crypto-stream
This is a fork of SocketDev/wormhole-crypto.
import { Keychain } from '@bicycle-codes/crypto-stream'
// Create a new keychain. Since no arguments are specified, the key and salt
// are generated.
const keychain = new Keychain()
// Get a WHATWG stream somehow, from fetch(), from a Blob(), etc.
const stream = getStream()
// Create an encrypted version of that stream
const encryptedStream = await keychain.encryptStream(stream)
// Normally you'd now use `encryptedStream`, e.g. in fetch(), etc.
// However, for this example, we'll just decrypt the stream immediately
const plaintextStream = await keychain.decryptStream(encryptedStream)
// Now, you can use `plaintextStream` and it will be identical
// to if you had used `stream`.
See ./example for a version that uses blobs + a
local vite
server.
import { Keychain } from '@bicycle-codes/crypto-stream'
const encryptedData = await fetch(imgUrl)
const decryptedStream = await keychain.decryptStream(encryptedData.body)
const response = new Response(decryptedStream)
const blobUrl = window.URL.createObjectURL(await response.blob())
// ...
function Component () {
return html`<img src="${blobUrl}" />`
}
constructor (key?:string|Uint8Array, salt?:string|Uint8Array)
Type: Class
Returns: Keychain
Create a new keychain object. The keychain can be used to create encryption streams, decryption streams, and to encrypt or decrypt a "metadata" buffer.
Type: Uint8Array | string | null
Default: null
The main key. This should be 16 bytes in length. If a string
is given,
then it should be a base64-encoded string. If the argument is null
, then a
key will be automatically generated.
Type: Uint8Array | string | null
Default: null
The salt. This should be 16 bytes in length. If a string
is given,
then it should be a base64-encoded string. If this argument is null
, then a
salt will be automatically generated.
key:Uint8Array
The main key.
keyB64:string
The main key as a base64url-encoded string.
salt:Uint8Array
The salt.
Implementation note: The salt is used to derive the (internal) metadata key and authentication token.
saltB64:string
The salt as a base64-encoded string.
authToken ():Promise<ArrayBuffer>
Returns the authentication token. By default, the authentication token is automatically derived from the main key using HKDF SHA-256.
The authentication token can be used to communicate with the server and prove that the client has permission to fetch some data. Without a valid authentication token, the server can reject the request.
Since the authentication token is derived from the main key, the client would present it to the server as a "reader token" to prove that it is in possession of the main key without revealing the main key to the server.
For destructive operations, the client should instead present a "writer token", which is not derived from the main key but is provided by the server.
authTokenB64 ():Promise<string>
Returns the authentication token as a base64-encoded string.
authHeader ():Promise<string>
// => `Bearer sync-v1 ${authTokenB64}`
Returns a Promise
that resolves to the HTTP header value to be provided to the server, as a base64 string. It contains the authentication token.
setAuthToken (authToken:string|Uint8Array|null):void
Update the keychain authentication token to the given authToken
.
Type: Uint8Array | string | null
Default: null
The authentication token. This should be 16 bytes in length. If a string
is
given, then it should be a base64-encoded string. If this argument is null
,
then an authentication token will be automatically generated.
encryptStream (stream:ReadableStream):Promise<ReadableStream>
Type: Function
Returns: Promise<ReadableStream>
Returns a Promise
that resolves to a ReadableStream
encryption stream that
consumes the data in stream
and returns an encrypted version. Data is
encrypted with Encrypted Content-Encoding for HTTP (RFC 8188).
Type: ReadableStream
A WHATWG readable stream used as a data source for the encrypted stream.
Type: Function
Returns: Promise<ReadableStream>
Returns a Promise
that resolves to a ReadableStream
decryption stream that
consumes the data in encryptedStream
and returns a plaintext version.
function decryptStreamRange (
secretKey:CryptoKey,
offset:number,
length:number,
totalEncryptedLength:number,
rs:number = RECORD_SIZE
):{
ranges:{ offset:number, length:number }[],
decrypt:(streams:ReadableStream[])=>ReadableStream
}
Returns a Promise
that resolves to a object containing ranges
, which is
an array of objects containing offset
and length
integers specifying the
encrypted byte ranges that are needed to decrypt the client's specified range,
and a decrypt
function.
Once the client has gathered a stream for each byte range in ranges
,
the client should call decrypt(streams)
, where streams
is an array of
ReadableStream
objects, one for each of the requested ranges. decrypt
will then return a ReadableStream
containing the plaintext data for the
client's desired byte range.
Type: ReadableStream
A WHATWG readable stream used as a data source for the plaintext stream.
encryptMeta (meta:Uint8Array):Promise<Uint8Array>
Returns a Promise
that resolves to an encrypted version of meta
. The
metadata is encrypted with AES-GCM.
Implementation note: The metadata key is automatically derived from the main key using HKDF SHA-256. The value is not user-controlled.
Implementation note: The initialization vector (IV) is automatically generated and included in the encrypted output. No need to generate it or to manage it separately from the encrypted output.
Type: Uint8Array
The metadata buffer to encrypt.
decryptMeta (ivEncryptedMeta:Uint8Array):Promise<Uint8Array>
Returns: Promise<Uint8Array>
Returns a Promise
that resolves to a decrypted version of encryptedMeta
.
Type: Uint8Array
The encrypted metadata buffer to decrypt.
async function encryptBytes (
bytes:ArrayBuffer|Uint8Array,
opts?:{ iv?:Uint8Array },
):Promise<Uint8Array>
Encrypt and return the given data in-memory, not using streams.
async function decryptBytes (
bytes:Uint8Array,
):Promise<ArrayBuffer>
Decrypt the given data in-memory, without streaming.
function plaintextSize (
encryptedSize:number,
rs:number = RECORD_SIZE
):number
Given an encrypted size, return the corresponding plaintext size.
function encryptedSize (
plaintextSize:number,
rs:number = RECORD_SIZE
):number
Given a plaintext size, return the corresponding encrypted size.
Thank you Feross and SocketDev team for writing and publishing this.