use std::io::BufRead;
use log::debug;
use crate::pgp::{
crypto::public_key::PublicKeyAlgorithm,
errors::{ensure, unsupported_err, Result},
parsing_reader::BufReadParsing,
types::{KeyVersion, PublicParams, SecretParams, Timestamp},
};
fn parse_pub_priv_fields<B: BufRead>(
key_ver: KeyVersion,
typ: PublicKeyAlgorithm,
pub_len: Option<usize>,
mut i: B,
) -> Result<(PublicParams, SecretParams)> {
debug!("KeyVersion: {key_ver:?}, alg: {typ:?}, len {pub_len:?}");
let pub_params = match pub_len {
Some(pub_len) => {
let mut public = i.read_take(pub_len);
let pp = PublicParams::try_from_reader(typ, Some(pub_len), &mut public)?;
ensure!(
!public.has_remaining()?,
"PublicParams::try_from_reader didn't consume all data"
);
pp
}
None => PublicParams::try_from_reader(typ, None, &mut i)?,
};
let v = i.rest()?;
debug!("rest: {}", hex::encode(&v));
let secret_params = SecretParams::from_slice(&v, key_ver, typ, &pub_params)?;
Ok((pub_params, secret_params))
}
fn private_key_parser_v4_v6<B: BufRead>(
key_ver: &KeyVersion,
mut i: B,
) -> Result<(
KeyVersion,
PublicKeyAlgorithm,
Timestamp,
Option<u16>,
PublicParams,
SecretParams,
)> {
let created_at = i.read_timestamp()?;
let alg = i.read_u8().map(PublicKeyAlgorithm::from)?;
let pub_len = if *key_ver == KeyVersion::V6 {
let pub_len = i.read_be_u32()?;
Some(pub_len as usize)
} else {
None
};
let params = parse_pub_priv_fields(*key_ver, alg, pub_len, i)?;
Ok((*key_ver, alg, created_at, None, params.0, params.1))
}
fn private_key_parser_v2_v3<B: BufRead>(
key_ver: &KeyVersion,
mut i: B,
) -> Result<(
KeyVersion,
PublicKeyAlgorithm,
Timestamp,
Option<u16>,
PublicParams,
SecretParams,
)> {
let created_at = i.read_timestamp()?;
let exp = i.read_be_u16()?;
let alg = i.read_u8().map(PublicKeyAlgorithm::from)?;
let params = parse_pub_priv_fields(*key_ver, alg, None, i)?;
Ok((*key_ver, alg, created_at, Some(exp), params.0, params.1))
}
#[allow(clippy::type_complexity)]
pub(crate) fn parse<B: BufRead>(
mut i: B,
) -> Result<(
KeyVersion,
PublicKeyAlgorithm,
Timestamp,
Option<u16>,
PublicParams,
SecretParams,
)> {
let key_ver = i.read_u8().map(KeyVersion::from)?;
let key = match key_ver {
KeyVersion::V2 | KeyVersion::V3 => private_key_parser_v2_v3(&key_ver, i)?,
KeyVersion::V4 | KeyVersion::V6 => private_key_parser_v4_v6(&key_ver, i)?,
KeyVersion::V5 | KeyVersion::Other(_) => {
unsupported_err!("key version {:?}", key_ver);
}
};
Ok(key)
}