Skip to main content

mini_sign/
public_key.rs

1use std::{fmt::Display, io::Read};
2
3use crate::{
4    errors::Result, prehash, signature::Signature, ErrorKind, SError, SignatureBox, ALG_SIZE,
5    COMPONENT_SIZE, KEY_SIG_ALG, KID_SIZE,
6};
7use base64::Engine;
8use ed25519_dalek::ed25519::{self, ComponentBytes};
9/// A `PublicKeyBox` represents a minisign public key.
10///
11/// also can be output to a string and parse from a str.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct PublicKeyBox<'s> {
14    pub(crate) untrusted_comment: Option<&'s str>,
15    pub(crate) public_key: PublicKey,
16}
17
18impl<'s> PublicKeyBox<'s> {
19    pub(crate) fn new(untrusted_comment: Option<&'s str>, public_key: PublicKey) -> Self {
20        Self {
21            untrusted_comment,
22            public_key,
23        }
24    }
25    pub fn from_verifying_key(
26        key: ed25519_dalek::VerifyingKey,
27        key_id: &[u8; 8],
28        untrusted_comment: Option<&'s str>,
29    ) -> Result<Self> {
30        let pk = RawPk::new(key.to_bytes());
31        let public_key = PublicKey::new(KEY_SIG_ALG, *key_id, pk);
32        Ok(Self::new(untrusted_comment, public_key))
33    }
34    /// Parse a `PublicKeyBox` from str.
35    ///
36    /// as it store in a file.
37    #[allow(clippy::should_implement_trait)]
38    pub fn from_str(s: &'s str) -> Result<Self> {
39        parse_public_key(s)
40    }
41    /// Get the public key from a raw string,without untrusted comment.
42    /// only one line.
43    pub fn from_raw_str(s: &'s str) -> Result<Self> {
44        let public_key = s.trim();
45        let decoder = base64::engine::general_purpose::STANDARD;
46        let pk_format = decoder
47            .decode(public_key.as_bytes())
48            .map_err(|e| SError::new(crate::ErrorKind::PublicKey, e))?;
49        if pk_format.len() != ALG_SIZE + KID_SIZE + COMPONENT_SIZE {
50            return Err(SError::new(
51                crate::ErrorKind::PublicKey,
52                "invalid public key length",
53            ));
54        }
55        let pk_sig_alg = &pk_format[..ALG_SIZE];
56        let pk_key_id = &pk_format[ALG_SIZE..ALG_SIZE + KID_SIZE];
57        let pk_key = &pk_format[ALG_SIZE + KID_SIZE..];
58        let pk = RawPk::new(pk_key.try_into().unwrap());
59        let public_key = PublicKey::new(
60            pk_sig_alg.try_into().unwrap(),
61            pk_key_id.try_into().unwrap(),
62            pk,
63        );
64        Ok(PublicKeyBox::new(None, public_key))
65    }
66    /// Get the untrusted comment.
67    pub fn untrusted_comment(&self) -> Option<&'s str> {
68        self.untrusted_comment
69    }
70    pub(crate) fn verify_mini(
71        &self,
72        msg: &[u8],
73        sig: &Signature,
74        trusted_comment: Option<&str>,
75    ) -> Result<bool> {
76        if !(self.public_key.key.verify(msg, &sig.sig)?) {
77            return Err(SError::new(
78                crate::ErrorKind::PublicKey,
79                "verify sig failed",
80            ));
81        }
82        let mut global_data = vec![];
83        global_data.extend_from_slice(&sig.sig.to_bytes());
84        global_data.extend_from_slice(trusted_comment.unwrap_or("").as_bytes());
85        if !(self.public_key.key.verify(&global_data, &sig.global_sig)?) {
86            return Err(SError::new(
87                crate::ErrorKind::PublicKey,
88                "verify global sig failed",
89            ));
90        }
91        Ok(true)
92    }
93    pub(crate) fn self_verify(&self) -> Result<bool> {
94        if self.public_key.sig_alg != KEY_SIG_ALG {
95            return Err(SError::new(
96                crate::ErrorKind::PublicKey,
97                "invalid public key signature algorithm",
98            ));
99        }
100        Ok(true)
101    }
102    /// Get the key id of the public key.
103    pub fn key_id(&self) -> &[u8; 8] {
104        &self.public_key.key_id
105    }
106    /// Get the signature algorithm of the public key.
107    pub fn sig_alg(&self) -> &[u8; 2] {
108        &self.public_key.sig_alg
109    }
110    /// Verify a signature with the public key.
111    ///
112    /// # Arguments
113    /// * `signature_box` - The signature to verify
114    /// * `data_reader` - The data to verify
115    /// # Returns
116    /// A Result containing a boolean indicating whether the signature is valid
117    /// # Errors
118    /// * `ErrorKind::Io` - If there is an error reading the data
119    /// * `ErrorKind::PublicKey` - If the public key is invalid or not matching the signature
120    /// * `ErrorKind::PrehashedMismatch` - If the signature is not prehashed
121    pub fn verify<R>(&self, signature_box: &SignatureBox, mut data_reader: R) -> Result<bool>
122    where
123        R: Read,
124    {
125        let prehashed = prehash(&mut data_reader)?;
126        verify_prehashed(self, signature_box, &prehashed)
127    }
128}
129pub(crate) fn verify_prehashed(
130    pk: &PublicKeyBox,
131    signature_box: &SignatureBox,
132    prehashed: &[u8],
133) -> Result<bool> {
134    if !signature_box.is_prehashed() {
135        return Err(SError::new(
136            ErrorKind::PrehashedMismatch,
137            "SignatureBox is not prehashed",
138        ));
139    }
140    if !pk.self_verify()? {
141        return Err(SError::new(
142            ErrorKind::PublicKey,
143            "public key self verification failed",
144        ));
145    }
146    if pk.public_key.key_id != *signature_box.key_id() {
147        return Err(SError::new(
148            ErrorKind::PublicKey,
149            "public key key_id mismatch",
150        ));
151    }
152    pk.verify_mini(
153        prehashed,
154        &signature_box.signature,
155        signature_box.trusted_comment(),
156    )
157}
158
159impl Display for PublicKeyBox<'_> {
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        let mut s = String::new();
162        s.push_str("untrusted comment: ");
163        if let Some(c) = self.untrusted_comment {
164            s.push_str(c);
165        }
166        s.push('\n');
167        let encoder = base64::engine::general_purpose::STANDARD;
168        let mut pk_format = vec![];
169        pk_format.extend_from_slice(&self.public_key.sig_alg);
170        pk_format.extend_from_slice(&self.public_key.key_id);
171        pk_format.extend_from_slice(&self.public_key.key.0);
172        let pk = encoder.encode(&pk_format);
173        s.push_str(&pk);
174        s.push('\n');
175        write!(f, "{}", s)
176    }
177}
178fn parse_raw_public_key(public_key: &str) -> Result<PublicKey> {
179    let decoder = base64::engine::general_purpose::STANDARD;
180    let pk_format = decoder
181        .decode(public_key.as_bytes())
182        .map_err(|e| SError::new(crate::ErrorKind::PublicKey, e))?;
183    if pk_format.len() != ALG_SIZE + KID_SIZE + COMPONENT_SIZE {
184        return Err(SError::new(
185            crate::ErrorKind::PublicKey,
186            "invalid public key length",
187        ));
188    }
189    let pk_sig_alg = &pk_format[..ALG_SIZE];
190    let pk_key_id = &pk_format[ALG_SIZE..ALG_SIZE + KID_SIZE];
191    let pk_key = &pk_format[ALG_SIZE + KID_SIZE..];
192    let pk = RawPk::new(pk_key.try_into().unwrap());
193    let public_key = PublicKey::new(
194        pk_sig_alg.try_into().unwrap(),
195        pk_key_id.try_into().unwrap(),
196        pk,
197    );
198    Ok(public_key)
199}
200fn parse_public_key(s: &str) -> Result<PublicKeyBox<'_>> {
201    let mut lines = s.lines();
202    if let Some(c) = lines.next() {
203        let untrusted_comment = c.strip_prefix("untrusted comment: ");
204        let public_key = lines
205            .next()
206            .ok_or_else(|| SError::new(crate::ErrorKind::PublicKey, "missing public key"))?;
207        Ok(PublicKeyBox::new(
208            untrusted_comment,
209            parse_raw_public_key(public_key)?,
210        ))
211    } else {
212        Err(SError::new(crate::ErrorKind::PublicKey, "empty public key"))
213    }
214}
215#[cfg(test)]
216#[test]
217fn test_parse_public_key() {
218    use crate::KeyPairBox;
219    let password = b"password";
220    let k = KeyPairBox::generate(Some(password), None, None).unwrap();
221    let file = k.public_key_box.to_string();
222    let pk = parse_public_key(&file).unwrap();
223    assert_eq!(file, pk.to_string());
224}
225/// A `PublicKey` is used to verify signatures.
226#[derive(Clone, Debug, PartialEq, Eq)]
227pub(crate) struct PublicKey {
228    pub sig_alg: [u8; 2],
229    pub key_id: [u8; 8],
230    pub key: RawPk,
231}
232impl PublicKey {
233    pub fn new(sig_alg: [u8; 2], key_id: [u8; 8], key: RawPk) -> Self {
234        Self {
235            sig_alg,
236            key_id,
237            key,
238        }
239    }
240}
241#[derive(Clone, Debug, PartialEq, Eq)]
242pub(crate) struct RawPk(pub ComponentBytes);
243impl RawPk {
244    pub fn new(key: ComponentBytes) -> Self {
245        Self(key)
246    }
247    pub fn verify(&self, msg: &[u8], sig: &ed25519::Signature) -> Result<bool> {
248        let pk = ed25519_dalek::VerifyingKey::from_bytes(&self.0)?;
249        Ok(pk.verify_strict(msg, sig).map(|_| true)?)
250    }
251}