Skip to content

std.crypto

API summary (generated from the Beans source by npm run coverage): 4 package functions · 2 types · 1 static method · 2 instance methods · 2 enum variants.

std.crypto provides two hashes and HMAC, and takes all of them from the operating system: CommonCrypto on macOS, CNG on Windows, and libcrypto loaded at runtime on Linux and BSD. No hash implementation ships here. The source is stdlib/std/crypto/crypto.b.

import std.crypto
  • Hashes use the platform provider. available() reports whether a provider is present. It is always true on macOS and Windows; elsewhere it depends on libcrypto being installed.
  • The package provides SHA-1, SHA-256, and HMAC. SHA-1 is used by the WebSocket handshake. This package is not a general cryptography toolkit.
  • HMAC is built here, on the platform digest, using the standard ipad/opad construction from RFC 2104.
  • finish consumes a Hasher. Later calls return an err with kind closed.

Error kinds you may see: unsupported (no provider on this platform), closed (a spent hasher), io (the provider failed).

Which digest to compute.

pub enum Algorithm {
sha1
sha256
}

Algorithm.sha1 produces 20 bytes, Algorithm.sha256 produces 32.

pub fn available() -> bool
pub fn sha1(data: Bytes) -> Result<Bytes>
pub fn sha256(data: Bytes) -> Result<Bytes>
pub fn hmac(algorithm: Algorithm, key: Bytes, data: Bytes) -> Result<Bytes>

sha1 and sha256 are the one-shot forms. hmac keys a digest with key; a key longer than the 64-byte block is replaced by its own digest, exactly as RFC 2104 specifies.

let digest: Bytes = crypto.sha256(Bytes.from("abc"))?
let mac: Bytes = crypto.hmac(crypto.Algorithm.sha256, key, message)?

A streaming digest, for data that arrives in pieces. Move-only and Send.

pub unique class Hasher implements Send
pub static fn open(algorithm: Algorithm) -> Result<Hasher>
pub fn update(data: Bytes) -> Result<bool>
pub fn finish() -> Result<Bytes>

update adds bytes to the running digest; finish produces the result and spends the hasher. Streaming and one-shot always agree:

let hasher: crypto.Hasher = crypto.Hasher.open(crypto.Algorithm.sha256)?
hasher.update(Bytes.from("a"))?
hasher.update(Bytes.from("bc"))?
let digest: Bytes = hasher.finish()? // the same digest as sha256("abc")

The one place SHA-1 is required rather than merely available. Sec-WebSocket-Accept is base64 of SHA-1 over the client key concatenated with one fixed UUID — std.websocket does this for you through accept_for_key, but the shape is worth seeing:

let joined: string = "{client_key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
let accept: string = base64.encode(crypto.sha1(Bytes.from(joined))?)