Cryptography
Hashes, passwords, HMAC, encryption, and signatures.
On this page
valk.crypto has hashes, password hashing, HMAC, key derivation, encryption, signatures, secure random bytes, and hex and Base64 encoding.
Hash data#
use valk.crypto
fn main() {
println(crypto.sha256_hex("Valk"))
println(crypto.hash_hex(crypto.HashAlgorithm.sha512, "Valk").length) // 128
let sha = crypto.hasher(crypto.HashAlgorithm.sha256)
sha.update("ab")
sha.update("c")
let digest = [u8]{ 0 x sha.digest_size() }
sha.finish(digest)
println(crypto.hex_encode(digest) == crypto.sha256_hex("abc")) // true
}
md5_hex,sha1_hex,sha256_hex,sha384_hex,sha512_hexreturn a hex digest.hash(algorithm, data)returns the raw digest bytes,hash_hexthe hex text.hasher(algorithm)hashes input fed in pieces withupdate, thenfinish.Blake2b.hash_string(data)gives a BLAKE2b digest;Blake2b.new(size, key)streams.
A fast hash is not the right tool for passwords.
Store a password#
use valk.crypto
fn main() {
let hash = crypto.bcrypt_hash("correct horse") ! {
println("Could not hash the password")
return
}
println(crypto.bcrypt_verify("correct horse", hash)) // true
println(crypto.bcrypt_verify("wrong", hash)) // false
}
bcrypt_hash returns text that includes its own salt; store it as is. A second argument sets the cost (12 by default). Bcrypt is slow on purpose, which makes guessing passwords expensive.
Sign messages with HMAC#
use valk.crypto
fn main() {
let algo = crypto.HashAlgorithm.sha256
let mac = crypto.Hmac.sign(algo, "secret", "user=17")
println(crypto.Hmac.verify(algo, "secret", "user=17", mac)) // true
println(crypto.Hmac.sign_hex(algo, "secret", "user=17").length) // 64
}
Compare MACs and tokens with Hmac.verify or crypto.constant_time_equals, never with ==, which leaks timing.
Derive keys#
use valk.crypto
fn main() {
let algo = crypto.HashAlgorithm.sha256
let salt = crypto.random_bytes(16)
let key = crypto.pbkdf2(algo, "password", salt, 600000, 32) ! return
let session = crypto.hkdf(algo, key, "", "session v1", 32) ! return
println(session.bytes) // 32
}
pbkdf2 turns a password into a key of any length. hkdf derives keys from material that is already random, such as a shared secret; info separates keys for different uses.
Encrypt data#
use valk.crypto
fn main() {
let key = crypto.random_bytes(32)
let sealed = crypto.seal(key, "user=17") ! return
let text = crypto.unseal(key, sealed) ! {
println("Altered, or the wrong key")
return
}
println(text) // user=17
}
seal encrypts with AES-256-GCM under a 32-byte key and a fresh random nonce; unseal decrypts and rejects anything that was altered. An optional third argument is extra data that is authenticated but not encrypted.
For another cipher or your own nonces, encrypt(cipher, key, nonce, plaintext) and decrypt take a Cipher: aes_128_gcm, aes_256_gcm or chacha20_poly1305. The nonce is 12 bytes and must never repeat for the same key.
Signatures#
use valk.crypto
fn main() {
let key = crypto.PrivateKey.generate_ed25519() ! return
let algo = crypto.SignatureAlgorithm.ed25519
let signature = key.sign(algo, "hello") ! return
let public = key.public_key() ! return
println(public.verify(algo, "hello", signature)) // true
let pem = public.to_pem() ! return
let loaded = crypto.PublicKey.from_pem(pem) ! return
println(loaded.verify(algo, "changed", signature)) // false
}
- Keys:
generate_ed25519(),generate_ec(Curve.p256),generate_rsa(2048). - Load keys with
PrivateKey.from_pem/from_der, andPublicKey.from_pem(also certificates),from_derorfrom_jwk. - Algorithms:
ed25519,ecdsa_sha256/384/512,rsa_pkcs1_sha256/384/512,rsa_pss_sha256/384/512.
Secure random values#
use valk.crypto
fn main() {
let key = crypto.random_bytes(32) // raw bytes
let token = String.random(24) // letters and digits
let id = uint.random()
println(key.bytes) // 32
}
These come from the operating system's secure random source, suitable for keys and session tokens. For fast, non-secret numbers, see Random numbers.
Hex and Base64#
use valk.crypto
fn main() {
println(crypto.hex_encode("Valk")) // 56616c6b
println(crypto.base64_encode("Valk")) // VmFsaw==
println(crypto.base64url_encode("??>")) // Pz8-
let raw = crypto.base64_decode("VmFsaw==") ! {
println("Not Base64")
return
}
println(raw) // Valk
}
hex_decode, base64_decode and base64url_decode reverse the encoders and throw on malformed input. base64url_encode writes the unpadded URL-safe form used by JWT.

