pretty_good/
key.rs

1use std::time::Duration;
2
3use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
4use digest::Digest;
5use failure::Error;
6use md5::Md5;
7use nom::{be_u16, be_u8, ErrorKind, IResult};
8use nom::Err as NomErr;
9use num::BigUint;
10use sha1::Sha1;
11
12use s2k::{StringToKey, s2k};
13use types::*;
14use util::{pgp_mpi, pgp_time};
15
16named!(
17    rsa_pubkey<RsaPublicKey>,
18    do_parse!(
19        n: pgp_mpi >>
20        e: pgp_mpi >>
21        (RsaPublicKey { n, e })
22    )
23);
24
25named!(
26    rsa_privkey<RsaPrivateKey>,
27    do_parse!(
28        d: pgp_mpi >>
29        p: pgp_mpi >>
30        q: pgp_mpi >>
31        u: pgp_mpi >>
32        (RsaPrivateKey { d, p, q, u })
33    )
34);
35
36named!(
37    dsa_pubkey<DsaPublicKey>,
38    do_parse!(
39        p: pgp_mpi >>
40        q: pgp_mpi >>
41        g: pgp_mpi >>
42        y: pgp_mpi >>
43        (DsaPublicKey { p, q, g, y })
44    )
45);
46
47named!(dsa_privkey<DsaPrivateKey>, map!(pgp_mpi, DsaPrivateKey));
48
49named!(
50    elgamal_pubkey<ElgamalPublicKey>,
51    do_parse!(
52        p: pgp_mpi >>
53        g: pgp_mpi >>
54        y: pgp_mpi >>
55        (ElgamalPublicKey { p, g, y })
56    )
57);
58
59named!(elgamal_privkey<ElgamalPrivateKey>, map!(pgp_mpi, ElgamalPrivateKey));
60
61named!(
62    v3_pubkey<Key>,
63    do_parse!(
64        alt!(tag!(&[2_u8]) | tag!(&[3_u8])) >>
65        created: pgp_time >>
66        expires_days: be_u16 >>
67        pubkey_algo: be_u8 >>
68        rsa_n: pgp_mpi >>
69        rsa_e: pgp_mpi >>
70        (Key {
71            version: KeyVersion::V3,
72            creation_time: created,
73            expiration_time: Some(Duration::from_secs(expires_days as u64 * 24 * 60 * 60)),
74            pubkey_algorithm: PublicKeyAlgorithm::from(pubkey_algo),
75            key_material: KeyMaterial::Rsa(RsaPublicKey { n: rsa_n, e: rsa_e }, None),
76            encryption_method: None,
77            privkey_checksum: None,
78        })
79    )
80);
81
82named!(
83    v4_pubkey<Key>,
84    do_parse!(
85        tag!(&[4_u8]) >>
86        created: pgp_time >>
87        pubkey_algorithm: peek!(be_u8) >>
88        pubkey_material: switch!(map!(be_u8, PublicKeyAlgorithm::from),
89            PublicKeyAlgorithm::Rsa => map!(call!(rsa_pubkey), |k| KeyMaterial::Rsa(k, None)) |
90            PublicKeyAlgorithm::RsaSignOnly => map!(
91                call!(rsa_pubkey),
92                |k| KeyMaterial::Rsa(k, None)
93            ) |
94            PublicKeyAlgorithm::RsaEncryptOnly => map!(
95                call!(rsa_pubkey),
96                |k| KeyMaterial::Rsa(k, None)
97            ) |
98            PublicKeyAlgorithm::Dsa => map!(call!(dsa_pubkey), |k| KeyMaterial::Dsa(k, None)) |
99            PublicKeyAlgorithm::Elgamal => map!(
100                call!(elgamal_pubkey),
101                |k| KeyMaterial::Elgamal(k, None)
102            ) |
103            PublicKeyAlgorithm::ElgamalEncryptOnly => map!(
104                call!(elgamal_pubkey),
105                |k| KeyMaterial::Elgamal(k, None)
106            )) >>
107        (Key {
108            version: KeyVersion::V4,
109            creation_time: created,
110            expiration_time: None,
111            pubkey_algorithm: PublicKeyAlgorithm::from(pubkey_algorithm),
112            key_material: pubkey_material,
113            encryption_method: None,
114            privkey_checksum: None,
115        })
116    )
117);
118
119named!(pubkey<Key>, alt!(v3_pubkey | v4_pubkey));
120
121named!(privkey_prefix_unencrypted<KeyEncryptionMethod>,
122    do_parse!(
123        tag!(&[0u8]) >>
124        (KeyEncryptionMethod::Unencrypted)
125    )
126);
127
128named!(privkey_prefix_symmetric<KeyEncryptionMethod>,
129    do_parse!(
130        enc_type: map!(be_u8, SymmetricKeyAlgorithm::from) >>
131        iv: take!(enc_type.block_bytes()) >>
132        (KeyEncryptionMethod::SymmetricKey(enc_type, Vec::from(iv)))
133    )
134);
135
136named!(privkey_prefix_s2k<KeyEncryptionMethod>,
137    do_parse!(
138        tag!(&[255u8]) >>
139        enc_type: map!(be_u8, SymmetricKeyAlgorithm::from) >>
140        s2k_specifier: s2k >>
141        iv: take!(enc_type.block_bytes()) >>
142        (KeyEncryptionMethod::StringToKey(enc_type, Vec::from(iv), s2k_specifier))
143    )
144);
145
146named!(privkey_prefix_s2k_sha1<KeyEncryptionMethod>,
147    do_parse!(
148        tag!(&[254u8]) >>
149        enc_type: map!(be_u8, SymmetricKeyAlgorithm::from) >>
150        s2k_specifier: s2k >>
151        iv: take!(enc_type.block_bytes()) >>
152        (KeyEncryptionMethod::StringToKeySha1(enc_type, Vec::from(iv), s2k_specifier))
153    )
154);
155
156named!(privkey_prefix<KeyEncryptionMethod>,
157    alt!(privkey_prefix_unencrypted |
158         privkey_prefix_s2k |
159         privkey_prefix_s2k_sha1 |
160         privkey_prefix_symmetric
161    )
162);
163
164fn parse_key(inp: &[u8]) -> IResult<&[u8], Key> {
165    let (remaining, mut key) = match pubkey(inp) {
166        IResult::Done(remaining, key) => (remaining, key),
167        IResult::Error(e) => return IResult::Error(e),
168        IResult::Incomplete(i) => return IResult::Incomplete(i),
169    };
170
171    let (remaining, privkey_prefix) = match privkey_prefix(remaining) {
172        IResult::Done(remaining, prefix) => (remaining, prefix),
173        IResult::Error(e) => return IResult::Error(e),
174        IResult::Incomplete(_) => return IResult::Done(remaining, key),
175    };
176
177    let (remaining, privkey_material) = match key.key_material {
178        KeyMaterial::Rsa(ref pub_material, _) => match rsa_privkey(remaining) {
179            IResult::Done(remaining, privkey) => (remaining, KeyMaterial::Rsa(pub_material.clone(), Some(privkey))),
180            IResult::Error(e) => return IResult::Error(e),
181            IResult::Incomplete(i) => return IResult::Incomplete(i),
182        },
183        KeyMaterial::Dsa(ref pub_material, _) => match dsa_privkey(remaining) {
184            IResult::Done(remaining, privkey) => (remaining, KeyMaterial::Dsa(pub_material.clone(), Some(privkey))),
185            IResult::Error(e) => return IResult::Error(e),
186            IResult::Incomplete(i) => return IResult::Incomplete(i),
187        },
188        KeyMaterial::Elgamal(ref pub_material, _) => match elgamal_privkey(remaining) {
189            IResult::Done(remaining, privkey) => (remaining, KeyMaterial::Elgamal(pub_material.clone(), Some(privkey))),
190            IResult::Error(e) => return IResult::Error(e),
191            IResult::Incomplete(i) => return IResult::Incomplete(i),
192        }
193    };
194
195    let (remaining, checksum) = match privkey_prefix {
196        KeyEncryptionMethod::Unencrypted
197        | KeyEncryptionMethod::SymmetricKey(_, _)
198        | KeyEncryptionMethod::StringToKey(_, _, _) => match take!(remaining, 2) {
199            IResult::Done(remaining, checksum) => (remaining, checksum),
200            IResult::Error(e) => return IResult::Error(e),
201            IResult::Incomplete(i) => return IResult::Incomplete(i),
202        }
203        KeyEncryptionMethod::StringToKeySha1(_, _, _) => match take!(remaining, 20) {
204            IResult::Done(remaining, checksum) => (remaining, checksum),
205            IResult::Error(e) => return IResult::Error(e),
206            IResult::Incomplete(i) => return IResult::Incomplete(i),
207        }
208    };
209
210    key.key_material = privkey_material;
211    key.encryption_method = Some(privkey_prefix);
212    key.privkey_checksum = Some(Vec::from(checksum));
213
214    IResult::Done(remaining, key)
215}
216
217#[derive(Clone, Debug)]
218pub struct Key {
219    version: KeyVersion,
220    pub creation_time: Duration,
221    expiration_time: Option<Duration>,
222    pub pubkey_algorithm: PublicKeyAlgorithm,
223    pub key_material: KeyMaterial,
224    pub encryption_method: Option<KeyEncryptionMethod>,
225    pub privkey_checksum: Option<Vec<u8>>,
226}
227
228impl Key {
229    pub fn from_bytes(bytes: &[u8]) -> Result<Key, Error> {
230        let (_, key) = match parse_key(bytes) {
231            IResult::Done(remaining, key) => (remaining, key),
232            IResult::Error(NomErr::Code(ErrorKind::Custom(e))) => {
233                let e = NomError::from(e);
234
235                bail!(KeyError::InvalidFormat {
236                    reason: format!("{:?}", e),
237                })
238            }
239            IResult::Error(e) => bail!(KeyError::InvalidFormat {
240                reason: format!("{}", e),
241            }),
242            IResult::Incomplete(i) => bail!(KeyError::InvalidFormat {
243                reason: format!("{:?}", i),
244            }),
245        };
246
247        Ok(key)
248    }
249
250    pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
251        let mut out = Vec::new();
252
253        out.push(4u8);
254        out.write_u32::<BigEndian>(self.creation_time.as_secs() as u32)?;
255        out.push(self.pubkey_algorithm as u8);
256        out.extend(&self.key_material.public_to_bytes()?);
257
258        let private_bytes = self.key_material.private_to_bytes()?;
259        if !private_bytes.is_empty() {
260            match self.encryption_method {
261                None => bail!(KeyError::InvalidEncryption),
262                Some(KeyEncryptionMethod::Unencrypted) => {
263                    out.push(0u8);
264                    match self.privkey_checksum {
265                        None => bail!(KeyError::BadChecksum),
266                        Some(ref checksum) => {
267                            out.extend(&private_bytes);
268                            out.extend(checksum);
269                        }
270                    }
271                }
272                Some(ref e) => bail!(KeyError::UnimplementedEncryption {
273                    method: format!("{:?}", e),
274                })
275            }
276        }
277
278        Ok(out)
279    }
280
281    pub fn expiration_time(&self) -> Option<Duration> {
282        self.expiration_time
283    }
284
285    pub fn fingerprint(&self) -> Result<Vec<u8>, Error> {
286        match self.version {
287            KeyVersion::V3 => {
288                let hash_payload = self.key_material.public_to_bytes()?;
289                Ok(Vec::from(Md5::digest(&hash_payload).as_ref()))
290            }
291            KeyVersion::V4 => {
292                let mut hash_payload = Vec::new();
293                hash_payload.push(0x99);
294
295                let mut key_data = Vec::new();
296                key_data.push(0x4);
297                key_data.write_u32::<BigEndian>(self.creation_time.as_secs() as u32)?;
298                key_data.push(self.pubkey_algorithm as u8);
299                key_data.extend(self.key_material.public_to_bytes()?);
300
301                hash_payload.write_u16::<BigEndian>(key_data.len() as u16)?;
302                hash_payload.extend(&key_data);
303
304                Ok(Vec::from(Sha1::digest(&hash_payload).as_ref()))
305            }
306        }
307    }
308
309    pub fn id(&self) -> Result<u64, Error> {
310        let bytes = match self.version {
311            KeyVersion::V3 => match self.key_material {
312                KeyMaterial::Rsa(ref pubkey, _) => {
313                    let n = pubkey.n.to_bytes_be();
314                    Ok(Vec::from(&n[n.len() - 8..]))
315                }
316                KeyMaterial::Dsa(_, _) => bail!("v3 DSA keys are unsupported"),
317                KeyMaterial::Elgamal(_, _) => bail!("v3 Elgamal keys are unsupported"),
318            },
319            KeyVersion::V4 => self.fingerprint().map(|mut f| {
320                let len = f.len();
321                f.split_off(len - 8)
322            }),
323        }?;
324
325        Ok(BigEndian::read_u64(&bytes))
326    }
327}
328
329#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
330enum KeyVersion {
331    V3,
332    V4,
333}
334
335#[derive(Clone, Debug)]
336pub enum KeyMaterial {
337    Rsa(RsaPublicKey, Option<RsaPrivateKey>),
338    Dsa(DsaPublicKey, Option<DsaPrivateKey>),
339    Elgamal(ElgamalPublicKey, Option<ElgamalPrivateKey>),
340}
341
342impl KeyMaterial {
343    pub fn public_to_bytes(&self) -> Result<Vec<u8>, Error> {
344        let mut out = Vec::new();
345
346        match self {
347            &KeyMaterial::Rsa(ref public, _) => {
348                out.write_u16::<BigEndian>(public.n.bits() as u16)?;
349                out.extend(&public.n.to_bytes_be());
350                out.write_u16::<BigEndian>(public.e.bits() as u16)?;
351                out.extend(&public.e.to_bytes_be());
352            }
353            &KeyMaterial::Dsa(ref public, _) => {
354                out.write_u16::<BigEndian>(public.p.bits() as u16)?;
355                out.extend(&public.p.to_bytes_be());
356                out.write_u16::<BigEndian>(public.q.bits() as u16)?;
357                out.extend(&public.q.to_bytes_be());
358                out.write_u16::<BigEndian>(public.g.bits() as u16)?;
359                out.extend(&public.g.to_bytes_be());
360                out.write_u16::<BigEndian>(public.y.bits() as u16)?;
361                out.extend(&public.y.to_bytes_be());
362            }
363            &KeyMaterial::Elgamal(ref public, _) => {
364                out.write_u16::<BigEndian>(public.p.bits() as u16)?;
365                out.extend(&public.p.to_bytes_be());
366                out.write_u16::<BigEndian>(public.g.bits() as u16)?;
367                out.extend(&public.g.to_bytes_be());
368                out.write_u16::<BigEndian>(public.y.bits() as u16)?;
369                out.extend(&public.y.to_bytes_be());
370            }
371        }
372
373        Ok(out)
374    }
375
376    pub fn private_to_bytes(&self) -> Result<Vec<u8>, Error> {
377        let mut out = Vec::new();
378
379        match self {
380            &KeyMaterial::Rsa(_, Some(ref private)) => {
381                out.write_u16::<BigEndian>(private.d.bits() as u16)?;
382                out.extend(&private.d.to_bytes_be());
383                out.write_u16::<BigEndian>(private.p.bits() as u16)?;
384                out.extend(&private.p.to_bytes_be());
385                out.write_u16::<BigEndian>(private.q.bits() as u16)?;
386                out.extend(&private.q.to_bytes_be());
387                out.write_u16::<BigEndian>(private.u.bits() as u16)?;
388                out.extend(&private.u.to_bytes_be());
389            }
390            &KeyMaterial::Dsa(_, Some(DsaPrivateKey(ref private))) => {
391                out.write_u16::<BigEndian>(private.bits() as u16)?;
392                out.extend(&private.to_bytes_be());
393            }
394            &KeyMaterial::Elgamal(_, Some(ElgamalPrivateKey(ref private))) => {
395                out.write_u16::<BigEndian>(private.bits() as u16)?;
396                out.extend(&private.to_bytes_be());
397            }
398            &KeyMaterial::Rsa(_, None)
399            | &KeyMaterial::Dsa(_, None)
400            | &KeyMaterial::Elgamal(_, None) => {}
401        }
402
403        Ok(out)
404    }
405}
406
407#[derive(Clone, Debug)]
408pub enum KeyEncryptionMethod {
409    Unencrypted,
410    SymmetricKey(SymmetricKeyAlgorithm, Vec<u8>),
411    StringToKey(SymmetricKeyAlgorithm, Vec<u8>, StringToKey),
412    StringToKeySha1(SymmetricKeyAlgorithm, Vec<u8>, StringToKey)
413}
414
415#[derive(Clone, Debug)]
416pub struct RsaPublicKey {
417    pub n: BigUint,
418    pub e: BigUint,
419}
420
421#[derive(Clone, Debug)]
422pub struct RsaPrivateKey {
423    pub d: BigUint,
424    pub p: BigUint,
425    pub q: BigUint,
426    pub u: BigUint,
427}
428
429#[derive(Clone, Debug)]
430pub struct DsaPublicKey {
431    pub p: BigUint,
432    pub q: BigUint,
433    pub g: BigUint,
434    pub y: BigUint,
435}
436
437#[derive(Clone, Debug)]
438pub struct DsaPrivateKey(pub BigUint);
439
440#[derive(Clone, Debug)]
441pub struct ElgamalPublicKey {
442    pub p: BigUint,
443    pub g: BigUint,
444    pub y: BigUint,
445}
446
447#[derive(Clone, Debug)]
448pub struct ElgamalPrivateKey(pub BigUint);
449
450#[derive(Debug, Fail)]
451pub enum KeyError {
452    #[fail(display = "Invalid key format: {}", reason)]
453    InvalidFormat { reason: String },
454    #[fail(display = "Invalid/no encryption set")]
455    InvalidEncryption,
456    #[fail(display = "Bad checksum")]
457    BadChecksum,
458    #[fail(display = "Unimplemented key encryption method: {}", method)]
459    UnimplementedEncryption { method: String },
460    #[fail(display = "Malformed MPI payload")]
461    MalformedMpi,
462}