hdiff_update_core/
security.rs1use base64::{engine::general_purpose::STANDARD, Engine};
2use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
3use serde::{Deserialize, Serialize};
4
5use crate::{Error, ManifestSignature, Result, UpdateManifest, MANIFEST_SIGNATURE_ALGORITHM};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct UpdateKeypair {
10 pub private_key: String,
11 pub public_key: String,
12 pub key_id: String,
13}
14
15pub fn generate_update_keypair() -> Result<UpdateKeypair> {
16 let mut secret = [0_u8; 32];
17 getrandom::fill(&mut secret).map_err(|error| Error::InvalidKeyMaterial {
18 kind: "random seed".to_string(),
19 message: error.to_string(),
20 })?;
21 let signing_key = SigningKey::from_bytes(&secret);
22 let public_key = signing_key.verifying_key().to_bytes();
23
24 Ok(UpdateKeypair {
25 private_key: STANDARD.encode(secret),
26 public_key: STANDARD.encode(public_key),
27 key_id: key_id(&public_key),
28 })
29}
30
31pub fn sign_manifest(
32 manifest: &mut UpdateManifest,
33 private_key: &str,
34 signature_key_id: Option<String>,
35) -> Result<ManifestSignature> {
36 let signing_key = signing_key_from_base64(private_key)?;
37 let signature: Signature = signing_key.sign(&manifest.signing_payload()?);
38 let signing_key_id =
39 signature_key_id.unwrap_or_else(|| key_id(&signing_key.verifying_key().to_bytes()));
40 let signature = ManifestSignature {
41 algorithm: MANIFEST_SIGNATURE_ALGORITHM.to_string(),
42 key_id: Some(signing_key_id),
43 value: STANDARD.encode(signature.to_bytes()),
44 };
45 manifest.signature = Some(signature.clone());
46 Ok(signature)
47}
48
49pub fn verify_manifest_signature(manifest: &UpdateManifest, public_key: &str) -> Result<()> {
50 let signature = manifest.signature.as_ref().ok_or(Error::SignatureMissing)?;
51 if signature.algorithm != MANIFEST_SIGNATURE_ALGORITHM {
52 return Err(Error::UnsupportedAlgorithm(signature.algorithm.clone()));
53 }
54
55 let verifying_key = verifying_key_from_base64(public_key)?;
56 let signature_bytes = decode_exact::<64>(&signature.value, "manifest signature")?;
57 let signature = Signature::from_bytes(&signature_bytes);
58 verifying_key
59 .verify_strict(&manifest.signing_payload()?, &signature)
60 .map_err(|_| Error::SignatureVerificationFailed)
61}
62
63pub fn key_id(public_key: &[u8; 32]) -> String {
64 let digest = crate::sha256_bytes(public_key);
65 digest[..16].to_string()
66}
67
68fn signing_key_from_base64(private_key: &str) -> Result<SigningKey> {
69 let bytes = decode_exact::<32>(private_key, "private key")?;
70 Ok(SigningKey::from_bytes(&bytes))
71}
72
73fn verifying_key_from_base64(public_key: &str) -> Result<VerifyingKey> {
74 let bytes = decode_exact::<32>(public_key, "public key")?;
75 VerifyingKey::from_bytes(&bytes).map_err(|error| Error::InvalidKeyMaterial {
76 kind: "public key".to_string(),
77 message: error.to_string(),
78 })
79}
80
81fn decode_exact<const N: usize>(value: &str, kind: &str) -> Result<[u8; N]> {
82 let bytes = STANDARD
83 .decode(value.trim())
84 .map_err(|error| Error::InvalidKeyMaterial {
85 kind: kind.to_string(),
86 message: error.to_string(),
87 })?;
88 bytes
89 .try_into()
90 .map_err(|bytes: Vec<u8>| Error::InvalidKeyMaterial {
91 kind: kind.to_string(),
92 message: format!("expected {N} bytes, got {}", bytes.len()),
93 })
94}