Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 49 additions & 37 deletions fuzz/fuzz_targets/fuzz_public_key.rs
Original file line number Diff line number Diff line change
@@ -1,75 +1,87 @@
//! Fuzz target for public key parsing
//!
//! This target tests various public key parsing formats:
//! - Raw bytes (with algorithm ID prefix)
//! - PEM encoding
//! - DER encoding
//! - OpenSSH format
//! - Auto-detection (from_any)
//! This target tests the public/secret key parser surface currently exposed
//! by `wsc::signature::keys`:
//!
//! - Raw bytes (with algorithm ID prefix, see `ED25519_PK_ID` / `ED25519_SK_ID`)
//! - PEM encoding (delegated to `ed25519_compact::PublicKey::from_pem`)
//! - DER encoding (delegated to `ed25519_compact::PublicKey::from_der`)
//!
//! Note: a previous revision of this target also exercised
//! `PublicKey::from_openssh`, `PublicKey::from_any`, `PublicKeySet::from_openssh`
//! and `SecretKey::from_openssh`. None of those exist on the current API —
//! OpenSSH ingestion and a polymorphic auto-detect parser were removed from
//! `signature/keys.rs`. We fuzz only the surface that is actually reachable.
//!
//! Oracles exercised:
//! - parsers must not panic on arbitrary bytes/strings
//! - `from_bytes` -> `to_bytes` -> `from_bytes` round-trips and yields the
//! same key id when one is attached
//! - `from_pem` / `from_der` round-trip back through `to_pem` / `to_der`
//! - `key_id` derivation via `attach_default_key_id` is deterministic
//!
//! Security concerns:
//! - Buffer overflows in key material handling
//! - Invalid key type IDs
//! - Malformed PEM/DER structures
//! - OpenSSH format parsing edge cases
//! - Key material validation
//! - Buffer overflows or panics in key material handling
//! - Invalid key type IDs (anything other than ED25519_PK_ID / ED25519_SK_ID)
//! - Malformed PEM/DER structures and oversize length fields

#![no_main]

use libfuzzer_sys::fuzz_target;
use wsc::{PublicKey, SecretKey, PublicKeySet};
use wsc::{PublicKey, SecretKey};

fuzz_target!(|data: &[u8]| {
// Test raw bytes parsing
// --- PublicKey: raw bytes ---------------------------------------------
if let Ok(pk) = PublicKey::from_bytes(data) {
// Try roundtrip
// Round-trip raw encoding.
let bytes = pk.to_bytes();
let _ = PublicKey::from_bytes(&bytes);
let pk2 = PublicKey::from_bytes(&bytes)
.expect("re-parsing freshly serialized public key must succeed");
assert_eq!(pk, pk2, "raw round-trip must preserve key");

// Try attaching key ID
let pk_with_id = pk.attach_default_key_id();
let _ = pk_with_id.key_id();
// Default key-id derivation must be deterministic.
let pk_with_id = pk.clone().attach_default_key_id();
let pk_with_id_again = pk.attach_default_key_id();
assert_eq!(
pk_with_id.key_id(),
pk_with_id_again.key_id(),
"attach_default_key_id must be deterministic",
);
assert!(pk_with_id.key_id().is_some());
}

// Test DER parsing
// --- PublicKey: DER ----------------------------------------------------
if let Ok(pk) = PublicKey::from_der(data) {
let der = pk.to_der();
let _ = PublicKey::from_der(&der);
}

// Test PEM parsing (if data is valid UTF-8)
// --- PublicKey: PEM (string surface) -----------------------------------
if let Ok(s) = std::str::from_utf8(data) {
if let Ok(pk) = PublicKey::from_pem(s) {
let pem = pk.to_pem();
let _ = PublicKey::from_pem(&pem);
}

// Test OpenSSH parsing
let _ = PublicKey::from_openssh(s);

// Test OpenSSH key set parsing
let _ = PublicKeySet::from_openssh(s);
}

// Test auto-detection (tries all formats)
if let Ok(pk) = PublicKey::from_any(data) {
let _ = pk.to_bytes();
}

// Test secret key parsing (less common attack surface but still important)
// --- SecretKey: raw bytes ---------------------------------------------
if let Ok(sk) = SecretKey::from_bytes(data) {
let _ = sk.to_bytes();
// `to_bytes` returns Zeroizing<Vec<u8>>; deref to &[u8] for re-parse.
let bytes = sk.to_bytes();
let _ = SecretKey::from_bytes(&bytes);
}

// --- SecretKey: DER ----------------------------------------------------
if let Ok(sk) = SecretKey::from_der(data) {
let _ = sk.to_der();
let der = sk.to_der();
let _ = SecretKey::from_der(&der);
}

// --- SecretKey: PEM ----------------------------------------------------
if let Ok(s) = std::str::from_utf8(data) {
if let Ok(sk) = SecretKey::from_pem(s) {
let _ = sk.to_pem();
let pem = sk.to_pem();
let _ = SecretKey::from_pem(&pem);
}

let _ = SecretKey::from_openssh(s);
}
});