Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

crates/gunnar-crypt/examples/sk_v1_fixture.rs

//! **A TEST fixture for the console's browser matrix** (CRYPT-PQ-SK): the v1
//! (X25519) wrap of a person's encryption key to a security key, as a console
//! from before the wrap v2 made it, so the matrix can check that the console
//! re-wraps such a method to v2 on the next unlock.
//!
//! ```text
//! sk_v1_fixture <public key> <recovery-wrap file> <credential id, base64url> <PRF output, base64url>
//!   (the recovery code on stdin)  →  the v1 wrap (an armored age file) on stdout
//! ```
//!
//! The person's key is opened with their printed recovery code (made in the
//! console a moment before), checked against the public half, and wrapped to
//! the credential's v1 recipient. Nothing secret is printed: the output is
//! ciphertext. Run only against a throwaway rig.

use std::io::Read as _;

fn b64url(text: &str) -> Vec<u8> {
    use base64::Engine as _;
    base64::engine::general_purpose::URL_SAFE_NO_PAD
        .decode(text.trim().trim_end_matches('='))
        .unwrap_or_else(|e| fail(&format!("not base64url: {e}")))
}

fn fail(why: &str) -> ! {
    eprintln!("sk_v1_fixture: {why}");
    std::process::exit(2)
}

fn main() {
    use gunnar_crypt::userkey::{self, security_key};
    let args: Vec<String> = std::env::args().skip(1).collect();
    let [public, wrap_file, credential, prf] = args.as_slice() else {
        fail("usage: sk_v1_fixture <public key> <recovery-wrap file> <credential id> <PRF output>; the recovery code on stdin");
    };
    let mut code = String::new();
    std::io::stdin()
        .read_to_string(&mut code)
        .unwrap_or_else(|e| fail(&format!("stdin: {e}")));
    let wrap = std::fs::read(wrap_file).unwrap_or_else(|e| fail(&format!("{wrap_file}: {e}")));
    let key = userkey::open_with_recovery_code(&wrap, code.trim(), public)
        .unwrap_or_else(|e| fail(&e.to_string()));
    let prf: [u8; 32] = b64url(prf)
        .try_into()
        .unwrap_or_else(|_| fail("the PRF output is not 32 bytes"));
    let v1 = key
        .wrap_to_security_key(&security_key::recipient(&prf, &b64url(credential)))
        .unwrap_or_else(|e| fail(&e.to_string()));
    print!("{v1}");
}