Fix Secret Key decode rejecting valid keys; verify build

The first base32 symbol carries 3 data bits (26 symbols = 130 bits, 2 spare),
but decode only accepted values 0-3, so ~half of generated keys failed to parse.
Adds an edge-case test, rustfmt config, Cargo.lock, and on-demand cross targets
instead of installing all seven in rust-toolchain.toml.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-15 21:14:15 +09:00
co-authored by Claude Opus 5.5
parent 462fbbc523
commit 4a152bde11
14 changed files with 1947 additions and 59 deletions
+15 -5
View File
@@ -44,10 +44,7 @@ impl SecretKey {
/// Accepts any case, spaces or hyphens, and the usual Crockford confusables (O→0, I/L→1).
pub fn parse(s: &str) -> Result<Self> {
let cleaned: Zeroizing<String> = Zeroizing::new(
s.chars()
.filter(|c| !c.is_whitespace() && *c != '-')
.map(|c| c.to_ascii_uppercase())
.collect(),
s.chars().filter(|c| !c.is_whitespace() && *c != '-').map(|c| c.to_ascii_uppercase()).collect(),
);
let rest = cleaned.strip_prefix(PREFIX).ok_or(CryptoError::BadSecretKey)?;
if rest.len() != 27 {
@@ -86,7 +83,8 @@ fn decode(s: &str) -> Result<[u8; 16]> {
let mut n: u128 = 0;
for (i, c) in s.bytes().enumerate() {
let v = symbol_value(c)? as u128;
if i == 0 && v > 0b11 {
// 26 symbols carry 130 bits; the 2 spare bits sit at the top, so the first symbol holds 3 data bits.
if i == 0 && v > 0b111 {
return Err(CryptoError::BadSecretKey); // would overflow 128 bits
}
n = (n << 5) | v;
@@ -109,6 +107,18 @@ mod tests {
}
}
#[test]
fn extremes_roundtrip() {
for b in [[0u8; 16], [0xff; 16], [0x80; 16]] {
let k = SecretKey::from_bytes(b);
assert_eq!(SecretKey::parse(&k.to_display()).unwrap().expose(), &b);
}
// First symbol 8 would need bit 128: reject.
let max = SecretKey::from_bytes([0xff; 16]).to_display().to_string();
assert!(max.starts_with("K1-7"));
assert!(SecretKey::parse(&max.replacen("K1-7", "K1-8", 1)).is_err());
}
#[test]
fn detects_typo() {
let k = SecretKey::from_bytes([0xAB; 16]);