From ef6f8e4c9d66e9a91ba3c1f648acdc81c33a8237 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 10 Dec 2025 16:07:58 +0100 Subject: [PATCH 1/2] Fix small things --- src/crypto.rs | 6 +++--- src/io/ascii/mod.rs | 2 +- src/io/ascii/parser.rs | 38 +++++++++++++++++--------------------- src/verify.rs | 1 - 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/crypto.rs b/src/crypto.rs index 3ac8ea3..061fee5 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -3,9 +3,9 @@ use std::fmt; use ed25519_dalek::ed25519::signature::Verifier as _; use sha2::{Digest, Sha256}; -const HASH_SIZE: usize = 32; -const PUBKEY_SIZE: usize = 32; -const SIGNATURE_SIZE: usize = 64; +pub(crate) const HASH_SIZE: usize = 32; +pub(crate) const PUBKEY_SIZE: usize = 32; +pub(crate) const SIGNATURE_SIZE: usize = 64; /// An Ed25519 public key. #[derive(Clone, Eq, Hash, PartialEq)] diff --git a/src/io/ascii/mod.rs b/src/io/ascii/mod.rs index 8d39a2b..2dcd2b8 100644 --- a/src/io/ascii/mod.rs +++ b/src/io/ascii/mod.rs @@ -81,7 +81,7 @@ impl Protoleaf { let mut p = Parser::new(input); let message = p.parse("message")?; let signature = p.parse("signature")?; - let public_key = p.parse::<[u8; 32]>("public_key")?.into(); + let public_key = p.parse("public_key")?; Ok(Protoleaf { message, signature, diff --git a/src/io/ascii/parser.rs b/src/io/ascii/parser.rs index e267f50..c97e5f8 100644 --- a/src/io/ascii/parser.rs +++ b/src/io/ascii/parser.rs @@ -3,7 +3,10 @@ use std::fmt; use std::iter::Peekable; use std::str::Lines; -use crate::{Hash, Signature}; +use crate::{ + crypto::{HASH_SIZE, PUBKEY_SIZE, SIGNATURE_SIZE}, + Hash, PublicKey, Signature, +}; #[derive(Debug)] pub struct ParseAsciiError(pub(super) String); @@ -68,39 +71,32 @@ impl AsciiValue for u64 { impl AsciiValue for Hash { fn from_ascii(s: &str) -> Result { - let input = s.as_bytes(); - if input.len() != 64 { - bail!("invalid hex length"); - } - let mut bytes = [0; 32]; - for (i, b) in bytes.iter_mut().enumerate() { - *b = hexbyte(input, i)?; - } + let bytes = <[u8; HASH_SIZE]>::from_ascii(s)?; Ok(Hash::from(bytes)) } } impl AsciiValue for Signature { fn from_ascii(s: &str) -> Result { - let input = s.as_bytes(); - if input.len() != 128 { - bail!("invalid hex length"); - } - let mut bytes = [0; 64]; - for (i, b) in bytes.iter_mut().enumerate() { - *b = hexbyte(input, i)?; - } + let bytes = <[u8; SIGNATURE_SIZE]>::from_ascii(s)?; Ok(Self::from(bytes)) } } -impl AsciiValue for [u8; 32] { +impl AsciiValue for PublicKey { fn from_ascii(s: &str) -> Result { + let bytes = <[u8; PUBKEY_SIZE]>::from_ascii(s)?; + Ok(Self::from(bytes)) + } +} + +impl AsciiValue for [u8; N] { + fn from_ascii(s: &str) -> Result<[u8; N]> { let input = s.as_bytes(); - if input.len() != 64 { + if input.len() != N * 2 { bail!("invalid hex length"); } - let mut bytes = [0; 32]; + let mut bytes = [0; N]; for (i, b) in bytes.iter_mut().enumerate() { *b = hexbyte(input, i)?; } @@ -120,7 +116,7 @@ impl AsciiValue for (V0, V1) { impl AsciiValue for (V0, V1, V2) { fn from_ascii(s: &str) -> Result { - let splits: Vec<&str> = s.splitn(3, ' ').collect(); + let splits: Vec<&str> = s.split(' ').collect(); if splits.len() != 3 { bail!("expected 3 values, found {}", splits.len()) } diff --git a/src/verify.rs b/src/verify.rs index e3f22fb..a50cf00 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -80,7 +80,6 @@ fn verify_leaf( for key in signers.iter() { if Hash::new(key) == *keyhash { let signed = [b"sigsum.org/v1/tree-leaf\x00".as_slice(), checksum.as_ref()].concat(); - signed.len(); if key.verify_signature(&signed, signature) { return Ok(()); } else { From ee598aba9af8859bd6868ca804d941d893c00159 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 10 Dec 2025 16:23:08 +0100 Subject: [PATCH 2/2] Make parse fields constants --- src/io/ascii/mod.rs | 72 ++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/src/io/ascii/mod.rs b/src/io/ascii/mod.rs index 2dcd2b8..43cf604 100644 --- a/src/io/ascii/mod.rs +++ b/src/io/ascii/mod.rs @@ -9,16 +9,28 @@ use crate::{ pub use parser::ParseAsciiError; use parser::{Parser, Result}; +const LOG_FIELD: &str = "log"; +const SIZE_FIELD: &str = "size"; +const LEAF_FIELD: &str = "leaf"; +const MESSAGE_FIELD: &str = "message"; +const VERSION_FIELD: &str = "version"; +const ROOT_HASH_FIELD: &str = "root_hash"; +const SIGNATURE_FIELD: &str = "signature"; +const NODE_HASH_FIELD: &str = "node_hash"; +const LEAF_INDEX_FIELD: &str = "leaf_index"; +const PUBLIC_KEY_FIELD: &str = "public_key"; +const COSIGNATURE_FIELD: &str = "cosignature"; + impl SignedTreeHead { pub fn from_ascii(input: &str) -> Result { let mut p = Parser::new(input); - let size: u64 = p.parse("size")?; - let root_hash: Hash = p.parse("root_hash")?; - let signature: Signature = p.parse("signature")?; + let size: u64 = p.parse(SIZE_FIELD)?; + let root_hash: Hash = p.parse(ROOT_HASH_FIELD)?; + let signature: Signature = p.parse(SIGNATURE_FIELD)?; let mut cosignatures = Vec::new(); while !p.at_end() { let (keyhash, timestamp, cosignature): (Hash, u64, Signature) = - p.parse("cosignature")?; + p.parse(COSIGNATURE_FIELD)?; cosignatures.push(WitnessCosignature { keyhash, timestamp, @@ -36,13 +48,13 @@ impl SignedTreeHead { #[allow(unused_must_use)] pub fn to_ascii(&self) -> String { let mut ascii = String::new(); - writeln!(ascii, "size={}", self.size); - writeln!(ascii, "root_hash={:x}", self.root_hash); - writeln!(ascii, "signature={:x}", self.signature); + writeln!(ascii, "{SIZE_FIELD}={}", self.size); + writeln!(ascii, "{ROOT_HASH_FIELD}={:x}", self.root_hash); + writeln!(ascii, "{SIGNATURE_FIELD}={:x}", self.signature); for cosig in self.cosignatures.iter() { writeln!( ascii, - "cosignature={:x} {} {:x}", + "{COSIGNATURE_FIELD}={:x} {} {:x}", cosig.keyhash, cosig.timestamp, cosig.cosignature ); } @@ -53,10 +65,10 @@ impl SignedTreeHead { impl InclusionProof { pub fn from_ascii(input: &str) -> Result { let mut p = Parser::new(input); - let leaf_index = p.parse("leaf_index")?; + let leaf_index = p.parse(LEAF_INDEX_FIELD)?; let mut node_hashes = Vec::new(); while !p.at_end() { - let hash = p.parse("node_hash")?; + let hash = p.parse(NODE_HASH_FIELD)?; node_hashes.push(hash); } Ok(Self { @@ -68,9 +80,9 @@ impl InclusionProof { #[allow(unused_must_use)] pub fn to_ascii(&self) -> String { let mut ascii = String::new(); - writeln!(ascii, "leaf_index={}", self.leaf_index); + writeln!(ascii, "{LEAF_INDEX_FIELD}={}", self.leaf_index); for h in self.node_hashes.iter() { - writeln!(ascii, "node_hash={h:x}"); + writeln!(ascii, "{NODE_HASH_FIELD}={h:x}"); } ascii } @@ -79,9 +91,9 @@ impl InclusionProof { impl Protoleaf { pub fn from_ascii(input: &str) -> Result { let mut p = Parser::new(input); - let message = p.parse("message")?; - let signature = p.parse("signature")?; - let public_key = p.parse("public_key")?; + let message = p.parse(MESSAGE_FIELD)?; + let signature = p.parse(SIGNATURE_FIELD)?; + let public_key = p.parse(PUBLIC_KEY_FIELD)?; Ok(Protoleaf { message, signature, @@ -92,9 +104,9 @@ impl Protoleaf { #[allow(unused_must_use)] pub fn to_ascii(&self) -> String { let mut ascii = String::new(); - writeln!(ascii, "message={:x}", self.message); - writeln!(ascii, "signature={:x}", self.signature); - writeln!(ascii, "public_key={:x}", self.public_key); + writeln!(ascii, "{MESSAGE_FIELD}={:x}", self.message); + writeln!(ascii, "{SIGNATURE_FIELD}={:x}", self.signature); + writeln!(ascii, "{PUBLIC_KEY_FIELD}={:x}", self.public_key); ascii } } @@ -109,12 +121,12 @@ impl SigsumSignature { ))); } let mut p = Parser::new(parts[0]); - let version: u64 = p.parse("version")?; + let version: u64 = p.parse(VERSION_FIELD)?; if version != 2 { return Err(ParseAsciiError(format!("version {version} not supported"))); } - let log_keyhash = p.parse("log")?; - let (leaf_keyhash, leaf_signature) = p.parse("leaf")?; + let log_keyhash = p.parse(LOG_FIELD)?; + let (leaf_keyhash, leaf_signature) = p.parse(LEAF_FIELD)?; if !p.at_end() { return Err(ParseAsciiError( "expected an empty line after 'leaf'".into(), @@ -134,28 +146,28 @@ impl SigsumSignature { #[allow(unused_must_use)] pub fn to_ascii(&self) -> String { let mut ascii = String::new(); - writeln!(ascii, "version=2"); - writeln!(ascii, "log={:x}", self.log_keyhash); + writeln!(ascii, "{VERSION_FIELD}=2"); + writeln!(ascii, "{LOG_FIELD}={:x}", self.log_keyhash); writeln!( ascii, - "leaf={:x} {:x}", + "{LEAF_FIELD}={:x} {:x}", self.leaf_keyhash, self.leaf_signature ); writeln!(ascii); - writeln!(ascii, "size={}", self.sth.size); - writeln!(ascii, "root_hash={:x}", self.sth.root_hash); - writeln!(ascii, "signature={:x}", self.sth.signature); + writeln!(ascii, "{SIZE_FIELD}={}", self.sth.size); + writeln!(ascii, "{ROOT_HASH_FIELD}={:x}", self.sth.root_hash); + writeln!(ascii, "{SIGNATURE_FIELD}={:x}", self.sth.signature); for cosig in self.sth.cosignatures.iter() { writeln!( ascii, - "cosignature={:x} {} {:x}", + "{COSIGNATURE_FIELD}={:x} {} {:x}", cosig.keyhash, cosig.timestamp, cosig.cosignature ); } writeln!(ascii); - writeln!(ascii, "leaf_index={}", self.proof.leaf_index); + writeln!(ascii, "{LEAF_INDEX_FIELD}={}", self.proof.leaf_index); for h in self.proof.node_hashes.iter() { - writeln!(ascii, "node_hash={h:x}"); + writeln!(ascii, "{NODE_HASH_FIELD}={h:x}"); } ascii }