Skip to content

Commit

Permalink
chore: some refactoring around signature and manifest
Browse files Browse the repository at this point in the history
  • Loading branch information
evilsocket committed Oct 29, 2024
1 parent fb66d76 commit db3e613
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 86 deletions.
53 changes: 17 additions & 36 deletions src/cli/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,21 @@ pub(crate) fn create_key(args: CreateKeyArgs) -> anyhow::Result<()> {
}

pub(crate) fn sign(args: SignArgs) -> anyhow::Result<()> {
let signing_key = crate::core::signing::load_key(&args.key_path)?;
// determine handler
let handler = crate::core::handlers::handler_for(args.format, &args.file_path, Scope::Signing);
// load the private key for signing
let signing_key = crate::core::signing::load_key(&args.key_path)?;
// get the paths to sign
let mut paths_to_sign = if let Ok(handler) = handler {
handler.paths_to_sign(&args.file_path)?
} else {
println!("Warning: Unrecognized file format. Signing this file does not ensure that the model data will be signed in its entirety.");
vec![args.file_path.clone()]
};

paths_to_sign.sort();

let mut manifest = Manifest::for_signing(signing_key);

// compute checksums for all files
for path in paths_to_sign {
println!("Signing {} ...", path.display());

manifest.compute_checksum(&path)?;
}

// create the manifest
let mut manifest = Manifest::from_signing_key(signing_key);
// sign
let signature = manifest.create_signature()?;
let signature = manifest.sign(&mut paths_to_sign)?;
println!("Signature: {}", signature);

// write manifest to file
Expand All @@ -46,37 +39,25 @@ pub(crate) fn sign(args: SignArgs) -> anyhow::Result<()> {
}

pub(crate) fn verify(args: VerifyArgs) -> anyhow::Result<()> {
let manifest_path = if let Some(path) = args.signature {
// determine handler
let handler = crate::core::handlers::handler_for(args.format, &args.file_path, Scope::Signing);
// load signature file to verify
let signature = Manifest::from_signature_path(&if let Some(path) = args.signature {
path
} else {
args.file_path.with_extension("signature")
};

let raw = std::fs::read_to_string(&manifest_path)?;
let ref_manifest: Manifest = serde_json::from_str(&raw)?;

let raw = std::fs::read(&args.key_path)?;
let mut manifest = Manifest::for_verifying(raw);

let handler = crate::core::handlers::handler_for(args.format, &args.file_path, Scope::Signing);
})?;
// load the public key to verify against
let mut manifest = Manifest::from_public_key_path(&args.key_path)?;
// get the paths to verify
let mut paths_to_verify = if let Ok(handler) = handler {
handler.paths_to_sign(&args.file_path)?
} else {
println!("Warning: Unrecognized file format. Signing this file does not ensure that the model data will be signed in its entirety.");
vec![args.file_path.clone()]
};

paths_to_verify.sort();

// compute checksums for all files
for path in paths_to_verify {
println!("Hashing {} ...", path.display());

manifest.compute_checksum(&path)?;
}

// verify
manifest.verify(&ref_manifest)?;
// this will compute the checksums and verify the signature
manifest.verify(&mut paths_to_verify, &signature)?;

println!("Signature verified");

Expand Down
121 changes: 71 additions & 50 deletions src/core/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ pub(crate) struct Manifest {
}

impl Manifest {
pub(crate) fn for_signing(signing_key: signature::Ed25519KeyPair) -> Self {
pub(crate) fn from_signature_path(path: &Path) -> anyhow::Result<Self> {
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
}

pub(crate) fn from_signing_key(signing_key: signature::Ed25519KeyPair) -> Self {
let public_key = signing_key.public_key();
let mut hasher = Blake2b512::new();
hasher.update(public_key.as_ref());
Expand All @@ -107,13 +111,13 @@ impl Manifest {
}
}

pub(crate) fn for_verifying(public_key_bytes: Vec<u8>) -> Self {
pub(crate) fn from_public_key(public_key_bytes: Vec<u8>) -> anyhow::Result<Self> {
let public_key = UnparsedPublicKey::new(&ED25519, public_key_bytes);
let mut hasher = Blake2b512::new();
hasher.update(public_key.as_ref());
let hash = hasher.finalize();

Self {
Ok(Self {
version: Version::V1,
signed_at: chrono::Utc::now().to_rfc3339(),
signed_with: format!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")),
Expand All @@ -127,10 +131,15 @@ impl Manifest {
signature: String::new(),
signing_key: None,
verifying_key: Some(public_key),
}
})
}

pub(crate) fn compute_checksum(&mut self, path: &Path) -> anyhow::Result<()> {
pub(crate) fn from_public_key_path(public_key: &Path) -> anyhow::Result<Self> {
let public_key_bytes = std::fs::read(public_key)?;
Self::from_public_key(public_key_bytes)
}

fn compute_checksum(&mut self, path: &Path) -> anyhow::Result<()> {
// print!(" computing checksum for {} ...", path.to_string_lossy());
// std::io::stdout().flush().unwrap();

Expand Down Expand Up @@ -168,7 +177,7 @@ impl Manifest {
checksums.join(".")
}

pub(crate) fn create_signature(&mut self) -> anyhow::Result<&str> {
fn create_signature(&mut self) -> anyhow::Result<&str> {
let data_to_sign = self.data_to_sign();
// sign data
self.signature = hex::encode(
Expand Down Expand Up @@ -211,15 +220,38 @@ impl Manifest {
.map_err(|e| anyhow::anyhow!("signature verification failed: {}", e))
}

pub(crate) fn verify(&self, ref_manifest: &Self) -> anyhow::Result<()> {
pub(crate) fn sign(&mut self, paths: &mut [PathBuf]) -> anyhow::Result<&str> {
paths.sort();

// compute checksums for all files
for path in paths {
println!("Signing {} ...", path.display());

self.compute_checksum(path)?;
}

// sign
self.create_signature()
}

pub(crate) fn verify(&mut self, paths: &mut [PathBuf], signature: &Self) -> anyhow::Result<()> {
paths.sort();

// compute checksums for all files
for path in paths {
println!("Hashing {} ...", path.display());

self.compute_checksum(path)?;
}

// check public key fingerprint if set
if ref_manifest.public_key != self.public_key {
if signature.public_key != self.public_key {
anyhow::bail!("public key fingerprint mismatch");
}
// verify individual checksums
self.verify_checksums(&ref_manifest.checksums)?;
self.verify_checksums(&signature.checksums)?;
// verify signature
self.verify_signature(&ref_manifest.signature)
self.verify_signature(&signature.signature)
}
}

Expand All @@ -246,7 +278,7 @@ mod tests {
#[test]
fn test_will_create_a_signature() {
let keypair = create_test_keypair();
let mut manifest = Manifest::for_signing(keypair);
let mut manifest = Manifest::from_signing_key(keypair);

let temp_file = create_temp_file_with_content("test").unwrap();

Expand Down Expand Up @@ -277,18 +309,17 @@ mod tests {
let keypair = create_test_keypair();
let pub_key = keypair.public_key().as_ref().to_vec();

let mut ref_manifest = Manifest::for_signing(keypair);
let mut ref_manifest = Manifest::from_signing_key(keypair);

let temp_file = create_temp_file_with_content("test").unwrap();

ref_manifest.compute_checksum(&temp_file.path()).unwrap();
ref_manifest.create_signature().unwrap();
let mut paths = vec![temp_file.path().to_path_buf()];

let mut manifest = Manifest::for_verifying(pub_key);
_ = ref_manifest.sign(&mut paths).unwrap();

manifest.compute_checksum(&temp_file.path()).unwrap();
let mut manifest = Manifest::from_public_key(pub_key).unwrap();

manifest.verify(&ref_manifest).unwrap();
manifest.verify(&mut paths, &ref_manifest).unwrap();
}

#[test]
Expand All @@ -297,90 +328,78 @@ mod tests {
let other_keypair = create_test_keypair();
let pub_key = other_keypair.public_key().as_ref().to_vec();

let mut ref_manifest = Manifest::for_signing(keypair);
let mut ref_manifest = Manifest::from_signing_key(keypair);

let temp_file = create_temp_file_with_content("test").unwrap();
let mut paths = vec![temp_file.path().to_path_buf()];

ref_manifest.compute_checksum(&temp_file.path()).unwrap();
ref_manifest.create_signature().unwrap();

let mut manifest = Manifest::for_verifying(pub_key);
let mut manifest = Manifest::from_public_key(pub_key).unwrap();

manifest.compute_checksum(&temp_file.path()).unwrap();

assert!(manifest.verify(&ref_manifest).is_err());
assert!(manifest.verify(&mut paths, &ref_manifest).is_err());
}

#[test]
fn test_wont_verify_a_tampered_file() {
let keypair = create_test_keypair();
let pub_key = keypair.public_key().as_ref().to_vec();

let mut ref_manifest = Manifest::for_signing(keypair);
let mut ref_manifest = Manifest::from_signing_key(keypair);

let temp_file = create_temp_file_with_content("test").unwrap();
let mut paths = vec![temp_file.path().to_path_buf()];

ref_manifest.compute_checksum(&temp_file.path()).unwrap();
ref_manifest.create_signature().unwrap();

let mut manifest = Manifest::for_verifying(pub_key);
let mut manifest = Manifest::from_public_key(pub_key).unwrap();

let temp_file = create_temp_file_with_content("tost").unwrap();

manifest.compute_checksum(&temp_file.path()).unwrap();

assert!(manifest.verify(&ref_manifest).is_err());
assert!(manifest.verify(&mut paths, &ref_manifest).is_err());
}

#[test]
fn test_wont_verify_empty_file() {
let keypair = create_test_keypair();
let pub_key = keypair.public_key().as_ref().to_vec();

let mut ref_manifest = Manifest::for_signing(keypair);
let mut ref_manifest = Manifest::from_signing_key(keypair);

let temp_file = create_temp_file_with_content("test").unwrap();
let mut paths = vec![temp_file.path().to_path_buf()];

ref_manifest.compute_checksum(&temp_file.path()).unwrap();
ref_manifest.create_signature().unwrap();

let mut manifest = Manifest::for_verifying(pub_key);
let mut manifest = Manifest::from_public_key(pub_key).unwrap();

let empty_file = create_temp_file_with_content("").unwrap();
manifest.compute_checksum(&empty_file.path()).unwrap();

assert!(manifest.verify(&ref_manifest).is_err());
}

#[test]
fn test_wont_verify_missing_file() {
let keypair = create_test_keypair();
let pub_key = keypair.public_key().as_ref().to_vec();

let mut ref_manifest = Manifest::for_signing(keypair);

let temp_file = create_temp_file_with_content("test").unwrap();
ref_manifest.compute_checksum(&temp_file.path()).unwrap();
ref_manifest.create_signature().unwrap();

let manifest = Manifest::for_verifying(pub_key);

// Don't compute any checksum, leaving manifest.checksums empty

assert!(manifest.verify(&ref_manifest).is_err());
assert!(manifest.verify(&mut paths, &ref_manifest).is_err());
}

#[test]
fn test_wont_verify_extra_file() {
let keypair = create_test_keypair();
let pub_key = keypair.public_key().as_ref().to_vec();

let mut ref_manifest = Manifest::for_signing(keypair);
let mut ref_manifest = Manifest::from_signing_key(keypair);

let temp_file = create_temp_file_with_content("test").unwrap();
let mut paths = vec![temp_file.path().to_path_buf()];

ref_manifest.compute_checksum(&temp_file.path()).unwrap();
ref_manifest.create_signature().unwrap();

let mut manifest = Manifest::for_verifying(pub_key);
let mut manifest = Manifest::from_public_key(pub_key).unwrap();

// Compute checksum for original file
manifest.compute_checksum(&temp_file.path()).unwrap();
Expand All @@ -389,23 +408,25 @@ mod tests {
let extra_file = create_temp_file_with_content("extra").unwrap();
manifest.compute_checksum(&extra_file.path()).unwrap();

assert!(manifest.verify(&ref_manifest).is_err());
assert!(manifest.verify(&mut paths, &ref_manifest).is_err());
}

#[test]
fn test_wont_verify_without_signature() {
let keypair = create_test_keypair();
let pub_key = keypair.public_key().as_ref().to_vec();

let mut ref_manifest = Manifest::for_signing(keypair);
let mut ref_manifest = Manifest::from_signing_key(keypair);

let temp_file = create_temp_file_with_content("test").unwrap();
ref_manifest.compute_checksum(&temp_file.path()).unwrap();
// Deliberately skip creating signature

let mut manifest = Manifest::for_verifying(pub_key);
let mut manifest = Manifest::from_public_key(pub_key).unwrap();
manifest.compute_checksum(&temp_file.path()).unwrap();

assert!(manifest.verify(&ref_manifest).is_err());
let mut paths = vec![temp_file.path().to_path_buf()];

assert!(manifest.verify(&mut paths, &ref_manifest).is_err());
}
}

0 comments on commit db3e613

Please sign in to comment.