Skip to main content

cargo_codesign/
update.rs

1use base64::{engine::general_purpose::STANDARD, Engine};
2use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
3use std::path::Path;
4
5#[derive(Debug, thiserror::Error)]
6pub enum UpdateSignError {
7    #[error("invalid private key: {0}")]
8    InvalidPrivateKey(String),
9    #[error("invalid public key: {0}")]
10    InvalidPublicKey(String),
11    #[error("invalid signature: {0}")]
12    InvalidSignature(String),
13    #[error("signature verification failed")]
14    VerificationFailed,
15    #[error("io error: {0}")]
16    Io(#[from] std::io::Error),
17}
18
19pub fn sign_bytes(data: &[u8], private_key_b64: &str) -> Result<String, UpdateSignError> {
20    let key_bytes = STANDARD
21        .decode(private_key_b64)
22        .map_err(|e| UpdateSignError::InvalidPrivateKey(e.to_string()))?;
23    let key_array: [u8; 32] = key_bytes
24        .try_into()
25        .map_err(|_| UpdateSignError::InvalidPrivateKey("expected 32 bytes".into()))?;
26    let signing_key = SigningKey::from_bytes(&key_array);
27    let signature = signing_key.sign(data);
28    Ok(STANDARD.encode(signature.to_bytes()))
29}
30
31pub fn verify_bytes(
32    data: &[u8],
33    signature_b64: &str,
34    public_key_b64: &str,
35) -> Result<(), UpdateSignError> {
36    let pub_bytes = STANDARD
37        .decode(public_key_b64)
38        .map_err(|e| UpdateSignError::InvalidPublicKey(e.to_string()))?;
39    let pub_array: [u8; 32] = pub_bytes
40        .try_into()
41        .map_err(|_| UpdateSignError::InvalidPublicKey("expected 32 bytes".into()))?;
42    let verifying_key = VerifyingKey::from_bytes(&pub_array)
43        .map_err(|e| UpdateSignError::InvalidPublicKey(e.to_string()))?;
44
45    let sig_bytes = STANDARD
46        .decode(signature_b64)
47        .map_err(|e| UpdateSignError::InvalidSignature(e.to_string()))?;
48    let sig_array: [u8; 64] = sig_bytes
49        .try_into()
50        .map_err(|_| UpdateSignError::InvalidSignature("expected 64 bytes".into()))?;
51    let signature = Signature::from_bytes(&sig_array);
52
53    verifying_key
54        .verify(data, &signature)
55        .map_err(|_| UpdateSignError::VerificationFailed)
56}
57
58pub fn sign_file(
59    archive_path: &Path,
60    sig_path: &Path,
61    private_key_b64: &str,
62) -> Result<(), UpdateSignError> {
63    let data = std::fs::read(archive_path)?;
64    let signature_b64 = sign_bytes(&data, private_key_b64)?;
65    std::fs::write(sig_path, format!("{signature_b64}\n"))?;
66    Ok(())
67}
68
69pub fn verify_file(
70    archive_path: &Path,
71    sig_path: &Path,
72    public_key_b64: &str,
73) -> Result<(), UpdateSignError> {
74    let data = std::fs::read(archive_path)?;
75    let signature_b64 = std::fs::read_to_string(sig_path)?;
76    verify_bytes(&data, signature_b64.trim(), public_key_b64)
77}