initial commit
Some checks failed
CI / lint (push) Successful in 1m37s
CI / test-python (push) Successful in 1m49s
CI / test-zig (push) Successful in 1m39s
CI / test-wasm (push) Successful in 1m54s
CI / test (push) Successful in 14m44s
CI / miri (push) Successful in 14m18s
CI / build (push) Successful in 1m9s
CI / fuzz-regression (push) Successful in 9m9s
CI / publish (push) Failing after 1m10s
CI / publish-python (push) Failing after 1m46s
CI / publish-wasm (push) Has been cancelled

Signed-off-by: Kamal Tufekcic <kamal@lo.sh>
This commit is contained in:
Kamal Tufekcic 2026-04-02 23:48:10 +03:00
commit 1d99048c95
No known key found for this signature in database
165830 changed files with 79062 additions and 0 deletions

View file

@ -0,0 +1,101 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use soliton::{
identity::{generate_identity, GeneratedIdentity, HybridSignature, IdentityPublicKey, IdentitySecretKey},
kex::{receive_session, SessionInit},
primitives::xwing,
};
use std::sync::LazyLock;
struct BobKeys {
ik_pk: IdentityPublicKey,
ik_sk: IdentitySecretKey,
spk_sk: xwing::SecretKey,
}
// Fixed Bob identity + SPK keypair — keygen is expensive, amortise across runs.
static BOB: LazyLock<BobKeys> = LazyLock::new(|| {
let GeneratedIdentity { public_key: ik_pk, secret_key: ik_sk, .. } = generate_identity().unwrap();
let (_spk_pk, spk_sk) = xwing::keygen().unwrap();
BobKeys { ik_pk, ik_sk, spk_sk }
});
// Fixed Alice identity (the "known sender" Bob trusts).
static ALICE_PK: LazyLock<IdentityPublicKey> = LazyLock::new(|| generate_identity().unwrap().public_key);
const SIG: usize = 3373;
const FP: usize = 32;
const XPK: usize = 1216;
const XCT: usize = 1120;
// Wire layout:
// sig (3373) | sender_fp (32) | recipient_fp (32) | sender_ek (1216)
// | ct_ik (1120) | ct_spk (1120) | spk_id (4 BE) | has_opk (1)
// | [ct_opk (1120) | opk_id (4 BE)] ← only if has_opk & 0x01
// | crypto_version (rest, strict UTF-8; bail if invalid)
const MIN: usize = SIG + FP + FP + XPK + XCT + XCT + 4 + 1;
fuzz_target!(|data: &[u8]| {
if data.len() < MIN {
return;
}
let mut off = 0;
let Ok(sig) = HybridSignature::from_bytes(data[off..off + SIG].to_vec()) else { return; };
off += SIG;
let sender_ik_fingerprint: [u8; FP] = data[off..off + FP].try_into().unwrap();
off += FP;
let recipient_ik_fingerprint: [u8; FP] = data[off..off + FP].try_into().unwrap();
off += FP;
let Ok(sender_ek) = xwing::PublicKey::from_bytes(data[off..off + XPK].to_vec()) else { return; };
off += XPK;
let Ok(ct_ik) = xwing::Ciphertext::from_bytes(data[off..off + XCT].to_vec()) else { return; };
off += XCT;
let Ok(ct_spk) = xwing::Ciphertext::from_bytes(data[off..off + XCT].to_vec()) else { return; };
off += XCT;
let spk_id = u32::from_be_bytes(data[off..off + 4].try_into().unwrap());
off += 4;
let has_opk = data[off] & 0x01 != 0;
off += 1;
let (ct_opk, opk_id) = if has_opk {
if data.len() < off + XCT + 4 {
return;
}
let Ok(ct) = xwing::Ciphertext::from_bytes(data[off..off + XCT].to_vec()) else { return; };
off += XCT;
let id = u32::from_be_bytes(data[off..off + 4].try_into().unwrap());
off += 4;
(Some(ct), Some(id))
} else {
(None, None)
};
// Use strict from_utf8 to match decode_session_init, which rejects
// non-UTF-8 bytes with Error::InvalidData. Lossy decoding would allow
// replacement-character strings that can never reach receive_session in
// real protocol flow (the wire parser rejects them first).
let Ok(crypto_version) = String::from_utf8(data[off..].to_vec()) else { return; };
let si = SessionInit {
crypto_version,
sender_ik_fingerprint,
recipient_ik_fingerprint,
sender_ek,
ct_ik,
ct_spk,
spk_id,
ct_opk,
opk_id,
};
// receive_session must never panic regardless of input.
// Exercises: crypto_version check, fingerprint checks, hybrid signature
// verification, KEM decapsulation, HKDF, and rollback on failure.
let _ = receive_session(&BOB.ik_pk, &BOB.ik_sk, &ALICE_PK, &si, &sig, &BOB.spk_sk, None);
});