pgp/packet/
secret_key_parser.rs

1use std::io::BufRead;
2
3use chrono::{DateTime, TimeZone, Utc};
4use log::debug;
5
6use crate::{
7    crypto::public_key::PublicKeyAlgorithm,
8    errors::{ensure, format_err, unsupported_err, Result},
9    parsing_reader::BufReadParsing,
10    types::{KeyVersion, PublicParams, SecretParams},
11};
12
13/// Parse the whole private key, both public and private fields.
14fn parse_pub_priv_fields<B: BufRead>(
15    key_ver: KeyVersion,
16    typ: PublicKeyAlgorithm,
17    pub_len: Option<usize>,
18    mut i: B,
19) -> Result<(PublicParams, SecretParams)> {
20    debug!("KeyVersion: {key_ver:?}, alg: {typ:?}, len {pub_len:?}");
21    let pub_params = match pub_len {
22        Some(pub_len) => {
23            // Use the pub_len hint to make sure we consume no more or less
24            let mut public = i.read_take(pub_len);
25            let pp = PublicParams::try_from_reader(typ, Some(pub_len), &mut public)?;
26
27            ensure!(
28                !public.has_remaining()?,
29                "PublicParams::try_from_reader didn't consume all data"
30            );
31            pp
32        }
33        None => PublicParams::try_from_reader(typ, None, &mut i)?,
34    };
35
36    let v = i.rest()?;
37    debug!("rest: {}", hex::encode(&v));
38
39    let secret_params = SecretParams::from_slice(&v, key_ver, typ, &pub_params)?;
40    Ok((pub_params, secret_params))
41}
42
43fn private_key_parser_v4_v6<B: BufRead>(
44    key_ver: &KeyVersion,
45    mut i: B,
46) -> Result<(
47    KeyVersion,
48    PublicKeyAlgorithm,
49    DateTime<Utc>,
50    Option<u16>,
51    PublicParams,
52    SecretParams,
53)> {
54    let created_at = i
55        .read_be_u32()
56        .map(|v| Utc.timestamp_opt(i64::from(v), 0).single())?
57        .ok_or_else(|| format_err!("invalid timestamp"))?;
58
59    let alg = i.read_u8().map(PublicKeyAlgorithm::from)?;
60
61    let pub_len = if *key_ver == KeyVersion::V6 {
62        // "scalar octet count for the following public key material" -> pass on for checking
63        let pub_len = i.read_be_u32()?;
64
65        Some(pub_len as usize)
66    } else {
67        None
68    };
69
70    let params = parse_pub_priv_fields(*key_ver, alg, pub_len, i)?;
71    Ok((*key_ver, alg, created_at, None, params.0, params.1))
72}
73
74fn private_key_parser_v2_v3<B: BufRead>(
75    key_ver: &KeyVersion,
76    mut i: B,
77) -> Result<(
78    KeyVersion,
79    PublicKeyAlgorithm,
80    DateTime<Utc>,
81    Option<u16>,
82    PublicParams,
83    SecretParams,
84)> {
85    let created_at = i
86        .read_be_u32()
87        .map(|v| Utc.timestamp_opt(i64::from(v), 0).single())?
88        .ok_or_else(|| format_err!("invalid imestamp"))?;
89
90    let exp = i.read_be_u16()?;
91    let alg = i.read_u8().map(PublicKeyAlgorithm::from)?;
92    let params = parse_pub_priv_fields(*key_ver, alg, None, i)?;
93
94    Ok((*key_ver, alg, created_at, Some(exp), params.0, params.1))
95}
96
97/// Parse a secret key packet (Tag 5)
98/// Ref: https://www.rfc-editor.org/rfc/rfc9580.html#name-secret-key-packet-formats
99#[allow(clippy::type_complexity)]
100pub(crate) fn parse<B: BufRead>(
101    mut i: B,
102) -> Result<(
103    KeyVersion,
104    PublicKeyAlgorithm,
105    DateTime<Utc>,
106    Option<u16>,
107    PublicParams,
108    SecretParams,
109)> {
110    let key_ver = i.read_u8().map(KeyVersion::from)?;
111    let key = match key_ver {
112        KeyVersion::V2 | KeyVersion::V3 => private_key_parser_v2_v3(&key_ver, i)?,
113        KeyVersion::V4 | KeyVersion::V6 => private_key_parser_v4_v6(&key_ver, i)?,
114        KeyVersion::V5 | KeyVersion::Other(_) => {
115            unsupported_err!("key version {:?}", key_ver);
116        }
117    };
118    Ok(key)
119}