minisign/
public_key.rs

1use std::cmp;
2use std::convert::TryInto;
3use std::fmt::Write as fmtWrite;
4use std::fs;
5use std::io::{Cursor, Read};
6use std::path::Path;
7
8use ct_codecs::{Base64, Decoder, Encoder};
9
10use crate::crypto::util::fixed_time_eq;
11use crate::errors::*;
12use crate::helpers::*;
13use crate::keynum::*;
14use crate::{constants::*, SecretKey};
15
16/// A public key and its metadata.
17///
18/// A `PublicKeyBox` represents a raw public key, along with a key
19/// identifier and an untrusted description.
20///
21/// This is what usually gets exported to disk.
22///
23/// A `PublicKeyBox` can be directly converted to/from a single-line string.
24#[derive(Clone, Debug)]
25pub struct PublicKeyBox(String);
26
27impl From<PublicKeyBox> for String {
28    fn from(pkb: PublicKeyBox) -> String {
29        pkb.0
30    }
31}
32
33impl From<String> for PublicKeyBox {
34    fn from(s: String) -> PublicKeyBox {
35        PublicKeyBox(s)
36    }
37}
38
39impl ToString for PublicKeyBox {
40    fn to_string(&self) -> String {
41        self.0.to_string()
42    }
43}
44
45impl From<PublicKeyBox> for PublicKey {
46    fn from(pkb: PublicKeyBox) -> PublicKey {
47        pkb.into_public_key().unwrap()
48    }
49}
50
51impl PublicKeyBox {
52    /// Create a new `PublicKeyBox` from a string.
53    pub fn from_string(s: &str) -> Result<PublicKeyBox> {
54        Ok(s.to_string().into())
55    }
56
57    /// Return a `PublicKeyBox` for a string, for storage.
58    pub fn into_string(self) -> String {
59        self.into()
60    }
61
62    /// Convert a `PublicKeyBox` to a string, for storage.
63    pub fn into_public_key(self) -> Result<PublicKey> {
64        PublicKey::from_box(self)
65    }
66
67    /// Return a byte representation of the public key, for storage.
68    pub fn to_bytes(&self) -> Vec<u8> {
69        self.to_string().as_bytes().to_vec()
70    }
71}
72
73/// A `PublicKey` is used to verify signatures.
74#[derive(Clone, Debug)]
75pub struct PublicKey {
76    pub(crate) sig_alg: [u8; TWOBYTES],
77    pub(crate) keynum_pk: KeynumPK,
78}
79
80impl PublicKey {
81    /// The key identifier of this public key.
82    pub fn keynum(&self) -> &[u8] {
83        &self.keynum_pk.keynum[..]
84    }
85
86    /// Deserialize a `PublicKey`.
87    ///
88    /// For storage, a `PublicKeyBox` is usually what you need instead.
89    pub fn from_bytes(buf: &[u8]) -> Result<PublicKey> {
90        let mut buf = Cursor::new(buf);
91        let mut sig_alg = [0u8; TWOBYTES];
92        let mut keynum = [0u8; KEYNUM_BYTES];
93        let mut pk = [0u8; PUBLICKEY_BYTES];
94        buf.read_exact(&mut sig_alg)?;
95        buf.read_exact(&mut keynum)?;
96        buf.read_exact(&mut pk)?;
97        Ok(PublicKey {
98            sig_alg,
99            keynum_pk: KeynumPK { keynum, pk },
100        })
101    }
102
103    /// Regenerate a `PublicKey` from `SecretKey`
104    pub fn from_secret_key(sk: &SecretKey) -> Result<PublicKey> {
105        let sig_alg = sk.sig_alg;
106        let keynum = sk.keynum_sk.keynum;
107        let pk = sk.keynum_sk.sk[PUBLICKEY_BYTES..].try_into().map_err(|_| {
108            PError::new(
109                ErrorKind::Misc,
110                "secret key does not contain public key".to_string(),
111            )
112        })?;
113
114        Ok(PublicKey {
115            sig_alg,
116            keynum_pk: KeynumPK { keynum, pk },
117        })
118    }
119
120    /// Serialize a `PublicKey`.
121    ///
122    /// For storage, a `PublicKeyBox` is usually what you want to use instead.
123    pub fn to_bytes(&self) -> Vec<u8> {
124        let mut iters = Vec::new();
125        iters.push(self.sig_alg.iter());
126        iters.push(self.keynum_pk.keynum.iter());
127        iters.push(self.keynum_pk.pk.iter());
128        let v: Vec<u8> = iters
129            .iter()
130            .flat_map(|b| {
131                let b = b.clone();
132                b.cloned()
133            })
134            .collect();
135        v
136    }
137
138    /// Convert a `PublicKeyBox` to a `PublicKey`.
139    pub fn from_box(pk_box: PublicKeyBox) -> Result<PublicKey> {
140        let s = pk_box.0;
141        let mut lines = s.lines();
142        lines.next().ok_or_else(|| {
143            PError::new(ErrorKind::Io, "Missing comment in public key".to_string())
144        })?;
145        let encoded_pk = lines.next().ok_or_else(|| {
146            PError::new(
147                ErrorKind::Io,
148                "Missing encoded key in public key".to_string(),
149            )
150        })?;
151        if encoded_pk.len() != PK_B64_ENCODED_LEN {
152            return Err(PError::new(
153                ErrorKind::Io,
154                "Base64 conversion failed - was an actual public key given?".to_string(),
155            ));
156        }
157        let decoded_buf = Base64::decode_to_vec(encoded_pk.trim(), None).map_err(|e| {
158            PError::new(
159                ErrorKind::Io,
160                format!("Base64 conversion failed - was an actual public key given?: {e}"),
161            )
162        })?;
163        PublicKey::from_bytes(&decoded_buf)
164    }
165
166    /// Convert a `PublicKey` to a `PublicKeyBox`.
167    pub fn to_box(&self) -> Result<PublicKeyBox> {
168        let mut s = String::new();
169        write!(s, "{COMMENT_PREFIX}minisign public key: ")?;
170        writeln!(s, "{:016X}", load_u64_le(&self.keynum_pk.keynum[..]))?;
171        writeln!(s, "{}", self.to_base64())?;
172        Ok(s.into())
173    }
174
175    /// Create a minimal public key from a base64-encoded string.
176    pub fn from_base64(pk_string: &str) -> Result<PublicKey> {
177        let encoded_string = pk_string.to_string();
178        if encoded_string.trim().len() != PK_B64_ENCODED_LEN {
179            return Err(PError::new(
180                ErrorKind::Io,
181                "base64 conversion failed - was an actual public key given?".to_string(),
182            ));
183        }
184        let decoded_string =
185            Base64::decode_to_vec(encoded_string.as_bytes(), None).map_err(|e| {
186                PError::new(
187                    ErrorKind::Io,
188                    format!("base64 conversion failed - was an actual public key given?: {e}"),
189                )
190            })?;
191        PublicKey::from_bytes(&decoded_string)
192    }
193
194    /// Encode a public key as a base64-encoded string.
195    pub fn to_base64(&self) -> String {
196        Base64::encode_to_string(self.to_bytes().as_slice()).unwrap()
197    }
198
199    /// Load a `PublicKeyBox` from a file, and returns a `PublicKey` from it.
200    pub fn from_file<P>(pk_path: P) -> Result<PublicKey>
201    where
202        P: AsRef<Path>,
203    {
204        let s = fs::read_to_string(pk_path)?;
205        PublicKey::from_box(s.into())
206    }
207}
208
209impl cmp::PartialEq for PublicKey {
210    fn eq(&self, other: &PublicKey) -> bool {
211        fixed_time_eq(&self.keynum_pk.pk, &other.keynum_pk.pk)
212    }
213}
214
215impl cmp::Eq for PublicKey {}