mini_sign/
public_key.rs

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