Functional Weave
Code in TypeScript

auth.password-hash@1.0.0

impl/rust/hash_password.rs

1,784 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::crypto_pbkdf2_sha256::pbkdf2_sha256;  ← from crypto.pbkdf2-sha256 ^1.0.0 · built alongside by fune
use super::encoding_base64_base64_encode::base64_encode;

/// A password as pbkdf2_sha256$<iterations>$<salt>$<hash>, the salt and the
/// 32-byte PBKDF2-HMAC-SHA256 key in standard base64. The salt is passed in
/// because a capability may not read randomness: generate 16 or more fresh
/// random bytes for every call.
///
/// # Panics
/// Panics on a salt under 16 bytes, a value outside 0-255, or fewer than
/// 1000 iterations.
pub fn hash_password(password: &str, salt: &[i64], iterations: i64) -> String {
    if salt.len() < 16 {
        panic!("salt must be at least 16 bytes, received {}", salt.len());
    }
    if iterations < 1000 {
        panic!("iterations must be a whole number of at least 1000");
    }
    let bytes: Vec<i64> = password.bytes().map(|b| b as i64).collect();
    let derived = pbkdf2_sha256(&bytes, salt, iterations, 32);
    format!("pbkdf2_sha256${}${}${}", iterations, base64_encode(salt), base64_encode(&derived))
}

pub fn fune_vector(args: &[Value]) -> Value {
    let password = match &args[0] {
        Value::Str(s) => s.as_str(),
        _ => panic!("password must be a string"),
    };
    let salt: Vec<i64> = match &args[1] {
        Value::Arr(items) => items
            .iter()
            .map(|item| match item {
                Value::Int(i) => *i,
                _ => panic!("salt must be a list of integers from 0 to 255"),
            })
            .collect(),
        _ => panic!("salt must be a list of integers from 0 to 255"),
    };
    let iterations = match &args[2] {
        Value::Int(i) => *i,
        _ => panic!("iterations must be a whole number of at least 1000"),
    };
    Value::str(&hash_password(password, &salt, iterations))
}