use log::{debug, warn};
use crate::pgp::{
composed::{SignedKeyDetails, SignedPublicSubKey, SignedSecretSubKey},
errors::{err_opt, format_err, Error, Result},
packet::{self, Packet, PacketTrait, Signature, SignatureType, UserAttribute, UserId},
types::{KeyDetails, KeyVersion, SignedUser, SignedUserAttribute, Tag},
};
#[allow(clippy::complexity)]
pub fn next<I, IKT>(
packets: &mut std::iter::Peekable<I>,
key_tag: Tag,
parse_secrect_subkeys: bool,
) -> Option<
Result<(
IKT,
SignedKeyDetails,
Vec<SignedPublicSubKey>,
Vec<SignedSecretSubKey>,
)>,
>
where
I: Sized + Iterator<Item = Result<Packet>>,
IKT: TryFrom<packet::Packet, Error = crate::pgp::errors::Error> + KeyDetails,
{
let packets = packets.by_ref();
packets.peek()?;
while let Some(packet) = packets.next_if(|p| p.as_ref().is_ok_and(|p| p.tag() != key_tag)) {
match packet {
Ok(p) => {
warn!(
"ignoring unexpected packet: expected {:?}, got {:?}",
key_tag,
p.tag()
);
}
Err(e) => return Some(Err(e)),
}
}
let next = match packets.next() {
Some(Ok(n)) => n,
Some(Err(e)) => return Some(Err(e)),
None => return None,
};
let primary_key: IKT = match next.try_into() {
Ok(key) => key,
Err(err) => {
return Some(Err(err));
}
};
debug!(" signatures");
let mut revocation_signatures = Vec::new();
let mut direct_signatures = Vec::new();
while let Some(packet) =
packets.next_if(|p| p.as_ref().is_ok_and(|p| p.tag() == Tag::Signature))
{
match packet {
Ok(packet) => {
debug!("parsing signature {:?}", packet.tag());
let sig: Signature = err_opt!(packet.try_into());
let typ = sig.typ();
if typ == Some(SignatureType::KeyRevocation) {
revocation_signatures.push(sig);
} else {
if primary_key.version() != KeyVersion::V4 {
warn!("unexpected signature: {typ:?}");
}
direct_signatures.push(sig);
}
}
Err(e) => return Some(Err(e)),
}
}
debug!(" user");
let mut users = Vec::new();
let mut user_attributes = Vec::new();
while let Some(packet) = packets.next_if(|p| {
p.as_ref().is_ok_and(|p| {
debug!("peek {:?}", p.tag());
p.tag() == Tag::UserId || p.tag() == Tag::UserAttribute
})
}) {
let packet = match packet {
Ok(packet) => packet,
Err(err) => return Some(Err(err)),
};
let tag = packet.tag();
debug!(" user data: {tag:?}");
match tag {
Tag::UserId => {
let id: UserId = err_opt!(packet.try_into());
let mut sigs = Vec::new();
while let Some(res) =
packets.next_if(|p| p.as_ref().is_ok_and(|p| p.tag() == Tag::Signature))
{
let packet = match res {
Ok(packet) => packet,
Err(e) => return Some(Err(e)),
};
let sig: Signature = err_opt!(packet.try_into());
sigs.push(sig);
}
users.push(SignedUser::new(id, sigs));
}
Tag::UserAttribute => {
let attr: UserAttribute = err_opt!(packet.try_into());
let mut sigs = Vec::new();
while let Some(res) =
packets.next_if(|p| p.as_ref().is_ok_and(|p| p.tag() == Tag::Signature))
{
let packet = match res {
Ok(packet) => packet,
Err(e) => return Some(Err(e)),
};
let sig: Signature = err_opt!(packet.try_into());
sigs.push(sig);
}
user_attributes.push(SignedUserAttribute::new(attr, sigs));
}
_ => break,
}
}
if users.is_empty() {
warn!("missing user ids");
}
let mut public_subkey_container = Vec::new();
let mut secret_subkey_container = Vec::new();
debug!(" subkeys");
while let Some(res) = packets.next_if(|p| {
p.as_ref().is_ok_and(|p| {
debug!(" peek {:?}", p.tag());
p.tag() == Tag::PublicSubkey || (parse_secrect_subkeys && p.tag() == Tag::SecretSubkey)
})
}) {
if primary_key.version() == KeyVersion::V2 || primary_key.version() == KeyVersion::V3 {
return Some(Err(format_err!("V2/3 keys can not have subkeys")));
}
let packet = match res {
Ok(packet) => packet,
Err(e) => return Some(Err(e)),
};
let tag = packet.tag();
match tag {
Tag::PublicSubkey => {
let subkey: packet::PublicSubkey = err_opt!(packet.try_into());
let mut sigs = Vec::new();
while let Some(res) = packets.next_if(|packet| {
packet.is_ok() && packet.as_ref().expect("just checked").tag() == Tag::Signature
}) {
match res {
Ok(packet) => {
let sig: Signature = err_opt!(packet.try_into());
sigs.push(sig);
}
Err(e) => return Some(Err(e)),
}
}
public_subkey_container.push(SignedPublicSubKey::new(subkey, sigs));
}
Tag::SecretSubkey => {
if parse_secrect_subkeys {
let subkey: packet::SecretSubkey = err_opt!(packet.try_into());
let mut sigs = Vec::new();
while let Some(res) = packets.next_if(|packet| {
packet.is_ok()
&& packet.as_ref().expect("just checked").tag() == Tag::Signature
}) {
match res {
Ok(packet) => {
let sig: Signature = err_opt!(packet.try_into());
sigs.push(sig);
}
Err(e) => return Some(Err(e)),
}
}
secret_subkey_container.push(SignedSecretSubKey::new(subkey, sigs));
}
}
_ => unreachable!(),
}
}
if let Some(Err(e)) = packets.next_if(|peek| peek.is_err()) {
match e {
Error::Unsupported { .. } => {}
_ => {
return Some(Err(format_err!(
"error while parsing composed key: {:?}",
e
)))
}
}
}
if primary_key.version() == KeyVersion::V6 {
for sub in &public_subkey_container {
if sub.version() != KeyVersion::V6 {
return Some(Err(format_err!(
"Illegal public subkey {:?} in v6 key",
sub.version()
)));
}
}
for sub in &secret_subkey_container {
if sub.version() != KeyVersion::V6 {
return Some(Err(format_err!(
"Illegal secret subkey {:?} in v6 key",
sub.version()
)));
}
}
}
Some(Ok((
primary_key,
SignedKeyDetails::new(
revocation_signatures,
direct_signatures,
users,
user_attributes,
),
public_subkey_container,
secret_subkey_container,
)))
}