Files
vault/tests/interop_crypto.rs
T
Andy DixonandClaude c324f6a306 Rust crypto core: FrodoKEM port, hybrid suite, Fury, STREAM, key files
FrodoKEM-640-SHAKE is ported from CIRCL v1.6.4 (the RustCrypto crate
implements the incompatible ISO variant) and reproduces the reference
NIST KAT digest. ML-KEM-768 comes from RustCrypto ml-kem. Derivation,
encapsulation, implicit rejection, KEK, header MAC, header parsing,
STREAM and Argon2id key files all match vectors from the Go oracle, Go's
files decrypt in Rust, and Go decrypts Rust's.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-08 10:12:53 +01:00

61 lines
2.1 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-or-later
//! Rust-to-Go: files the Rust implementation encrypts must open in the
//! frozen Go oracle, and files it corrupts must not.
mod common;
use vault::crypto::{fury, kem};
fn pattern(n: usize, salt: u8) -> Vec<u8> {
(0..n).map(|i| (i.wrapping_mul(7) as u8) ^ salt).collect()
}
#[test]
fn go_decrypts_rust_files() {
if !common::have_go() {
return common::skip("go not installed");
}
let dir = tempfile_dir();
let id = kem::Identity::from_suite_seed(kem::SUITE_ID, &pattern(32, 1)).unwrap();
for size in [0usize, 1, 16, 65535, 65536, 65537, 131072, 300_001] {
let mut out = Vec::new();
fury::encrypt_to(&mut out, &id.recipient(), &pattern(size, 0x5a), &mut vault::rng::os());
std::fs::write(dir.join(format!("pattern-{size}.fury")), &out).unwrap();
if size == 65537 {
let mut bad = out.clone();
bad.truncate(bad.len() - 1);
std::fs::write(dir.join("bad-truncated.fury"), &bad).unwrap();
let mut bad = out.clone();
bad.push(0);
std::fs::write(dir.join("bad-trailing.fury"), &bad).unwrap();
let mut bad = out;
let mid = bad.len() / 2;
bad[mid] ^= 4;
std::fs::write(dir.join("bad-flipped.fury"), &bad).unwrap();
}
}
let o = common::go_test("./internal/pqcrypt", "TestOracleDecrypt", &[("VAULT_ORACLE_DECRYPT", &dir)]);
let log = String::from_utf8_lossy(&o.stdout);
assert!(
o.status.success(),
"go oracle rejected Rust output:\n{log}{}",
String::from_utf8_lossy(&o.stderr)
);
assert_eq!(log.matches(".fury ok").count(), 8, "{log}");
std::fs::remove_dir_all(dir).unwrap();
}
fn tempfile_dir() -> std::path::PathBuf {
let d = std::env::temp_dir().join(format!(
"vault-interop-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
std::fs::create_dir_all(&d).unwrap();
d
}