Docs/Data & text

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#

Valk
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_hex return a hex digest.
  • hash(algorithm, data) returns the raw digest bytes, hash_hex the hex text.
  • hasher(algorithm) hashes input fed in pieces with update, then finish.
  • 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#

Valk
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#

Valk
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#

Valk
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#

Valk
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#

Valk
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, and PublicKey.from_pem (also certificates), from_der or from_jwk.
  • Algorithms: ed25519, ecdsa_sha256/384/512, rsa_pkcs1_sha256/384/512, rsa_pss_sha256/384/512.

Secure random values#

Valk
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#

Valk
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.