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::{
6 DirectoryUpdateManifest, Error, ManifestSignature, Result, MANIFEST_SIGNATURE_ALGORITHM,
7};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct UpdateKeypair {
12 pub private_key: String,
13 pub public_key: String,
14 pub key_id: String,
15}
16
17pub fn generate_update_keypair() -> Result<UpdateKeypair> {
18 let mut secret = [0_u8; 32];
19 getrandom::fill(&mut secret).map_err(|error| Error::InvalidKeyMaterial {
20 kind: "random seed".to_string(),
21 message: error.to_string(),
22 })?;
23 let signing_key = SigningKey::from_bytes(&secret);
24 let public_key = signing_key.verifying_key().to_bytes();
25
26 Ok(UpdateKeypair {
27 private_key: STANDARD.encode(secret),
28 public_key: STANDARD.encode(public_key),
29 key_id: key_id(&public_key),
30 })
31}
32
33pub fn sign_directory_manifest(
34 manifest: &mut DirectoryUpdateManifest,
35 private_key: &str,
36 signature_key_id: Option<String>,
37) -> Result<ManifestSignature> {
38 let signature = sign_payload(&manifest.signing_payload()?, private_key, signature_key_id)?;
39 manifest.signature = Some(signature.clone());
40 Ok(signature)
41}
42
43pub fn verify_directory_manifest_signature(
44 manifest: &DirectoryUpdateManifest,
45 public_key: &str,
46) -> Result<()> {
47 verify_directory_manifest_signature_with_keys(manifest, &[public_key.to_string()])
48}
49
50pub fn verify_directory_manifest_signature_with_keys(
51 manifest: &DirectoryUpdateManifest,
52 public_keys: &[String],
53) -> Result<()> {
54 if public_keys.is_empty() {
55 return Err(Error::SignaturePublicKeyMissing);
56 }
57 let signature = manifest.signature.as_ref().ok_or(Error::SignatureMissing)?;
58 let payload = manifest.signing_payload()?;
59 let mut ordered = public_keys.iter().collect::<Vec<_>>();
60 if let Some(expected_key_id) = signature.key_id.as_deref() {
61 ordered.sort_by_key(|public_key| {
62 public_key_id(public_key)
63 .map(|key_id| key_id != expected_key_id)
64 .unwrap_or(true)
65 });
66 }
67 let mut last_error = None;
68 for public_key in ordered {
69 match verify_payload(&payload, signature, public_key) {
70 Ok(()) => return Ok(()),
71 Err(error) => last_error = Some(error),
72 }
73 }
74 Err(last_error.unwrap_or(Error::SignatureVerificationFailed))
75}
76
77pub fn public_key_id(public_key: &str) -> Result<String> {
78 Ok(key_id(&verifying_key_from_base64(public_key)?.to_bytes()))
79}
80
81fn sign_payload(
82 payload: &[u8],
83 private_key: &str,
84 signature_key_id: Option<String>,
85) -> Result<ManifestSignature> {
86 let signing_key = signing_key_from_base64(private_key)?;
87 let signature: Signature = signing_key.sign(payload);
88 let signing_key_id =
89 signature_key_id.unwrap_or_else(|| key_id(&signing_key.verifying_key().to_bytes()));
90 Ok(ManifestSignature {
91 algorithm: MANIFEST_SIGNATURE_ALGORITHM.to_string(),
92 key_id: Some(signing_key_id),
93 value: STANDARD.encode(signature.to_bytes()),
94 })
95}
96
97fn verify_payload(payload: &[u8], signature: &ManifestSignature, public_key: &str) -> Result<()> {
98 if signature.algorithm != MANIFEST_SIGNATURE_ALGORITHM {
99 return Err(Error::UnsupportedAlgorithm(signature.algorithm.clone()));
100 }
101
102 let verifying_key = verifying_key_from_base64(public_key)?;
103 let signature_bytes = decode_exact::<64>(&signature.value, "manifest signature")?;
104 let signature = Signature::from_bytes(&signature_bytes);
105 verifying_key
106 .verify_strict(payload, &signature)
107 .map_err(|_| Error::SignatureVerificationFailed)
108}
109
110pub fn key_id(public_key: &[u8; 32]) -> String {
111 let digest = crate::sha256_bytes(public_key);
112 digest[..16].to_string()
113}
114
115fn signing_key_from_base64(private_key: &str) -> Result<SigningKey> {
116 let bytes = decode_exact::<32>(private_key, "private key")?;
117 Ok(SigningKey::from_bytes(&bytes))
118}
119
120fn verifying_key_from_base64(public_key: &str) -> Result<VerifyingKey> {
121 let bytes = decode_exact::<32>(public_key, "public key")?;
122 VerifyingKey::from_bytes(&bytes).map_err(|error| Error::InvalidKeyMaterial {
123 kind: "public key".to_string(),
124 message: error.to_string(),
125 })
126}
127
128fn decode_exact<const N: usize>(value: &str, kind: &str) -> Result<[u8; N]> {
129 let bytes = STANDARD
130 .decode(value.trim())
131 .map_err(|error| Error::InvalidKeyMaterial {
132 kind: kind.to_string(),
133 message: error.to_string(),
134 })?;
135 bytes
136 .try_into()
137 .map_err(|bytes: Vec<u8>| Error::InvalidKeyMaterial {
138 kind: kind.to_string(),
139 message: format!("expected {N} bytes, got {}", bytes.len()),
140 })
141}