From 4cf73914f253c8c8d46ffc94b73213e0313343b3 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 12 Jan 2021 09:14:21 +0100 Subject: openpgp: Implement verification of attested key signatures. - Note that the spec is still in flux, therefore we don't expose it. --- openpgp/src/packet/signature.rs | 130 ++++++++++++++++++++++++++++++ openpgp/src/packet/signature/subpacket.rs | 5 ++ openpgp/src/types/mod.rs | 6 ++ 3 files changed, 141 insertions(+) diff --git a/openpgp/src/packet/signature.rs b/openpgp/src/packet/signature.rs index 079d09d1..0cdbf70b 100644 --- a/openpgp/src/packet/signature.rs +++ b/openpgp/src/packet/signature.rs @@ -2787,6 +2787,71 @@ impl Signature { self.verify_digest(signer, &hash.into_digest()?[..]) } + /// Verifies an attested key signature on a user id. + /// + /// `self` is the attested key signature, `signer` is the key that + /// allegedly made the signature, `pk` is the primary key, and + /// `userid` is the user id. + /// + /// Note: Due to limited context, this only verifies the + /// cryptographic signature, checks the signature's type, and + /// checks that the key predates the signature. Further + /// constraints on the signature, like creation and expiration + /// time, or signature revocations must be checked by the caller. + /// + /// Likewise, this function does not check whether `signer` can + /// made valid signatures; it is up to the caller to make sure the + /// key is not revoked, not expired, has a valid self-signature, + /// has a subkey binding signature (if appropriate), has the + /// signing capability, etc. + #[allow(dead_code)] + pub(crate) fn verify_userid_attestation( + &mut self, + signer: &Key, + pk: &Key, + userid: &UserID) + -> Result<()> + where P: key::KeyParts, + Q: key::KeyParts, + R: key::KeyRole, + { + use crate::types::SignatureType__AttestedKey; + use crate::packet::signature::subpacket + ::SubpacketTag__AttestedCertifications; + + if self.typ() != SignatureType__AttestedKey { + return Err(Error::UnsupportedSignatureType(self.typ()).into()); + } + + let mut hash = self.hash_algo().context()?; + + if self.hashed_area() + .subpackets(SubpacketTag__AttestedCertifications).count() != 1 + || self.unhashed_area() + .subpackets(SubpacketTag__AttestedCertifications).count() != 0 + { + return Err(Error::BadSignature( + "Wrong number of attested certification subpackets".into()) + .into()); + } + + if let SubpacketValue::Unknown { body, .. } = + self.subpacket(SubpacketTag__AttestedCertifications).unwrap() + .value() + { + if body.len() % hash.digest_size() != 0 { + return Err(Error::BadSignature( + "Wrong number of bytes in certification subpacket".into()) + .into()); + } + } else { + unreachable!("Selected attested certifications, got wrong value"); + } + + self.hash_userid_binding(&mut hash, pk, userid); + self.verify_digest(signer, &hash.into_digest()?[..]) + } + /// Verifies the user attribute binding. /// /// `self` is the user attribute binding signature, `signer` is @@ -2865,6 +2930,71 @@ impl Signature { self.verify_digest(signer, &hash.into_digest()?[..]) } + /// Verifies an attested key signature on a user attribute. + /// + /// `self` is the attested key signature, `signer` is the key that + /// allegedly made the signature, `pk` is the primary key, and + /// `ua` is the user attribute. + /// + /// Note: Due to limited context, this only verifies the + /// cryptographic signature, checks the signature's type, and + /// checks that the key predates the signature. Further + /// constraints on the signature, like creation and expiration + /// time, or signature revocations must be checked by the caller. + /// + /// Likewise, this function does not check whether `signer` can + /// made valid signatures; it is up to the caller to make sure the + /// key is not revoked, not expired, has a valid self-signature, + /// has a subkey binding signature (if appropriate), has the + /// signing capability, etc. + #[allow(dead_code)] + pub(crate) fn verify_user_attribute_attestation( + &mut self, + signer: &Key, + pk: &Key, + ua: &UserAttribute) + -> Result<()> + where P: key::KeyParts, + Q: key::KeyParts, + R: key::KeyRole, + { + use crate::types::SignatureType__AttestedKey; + use crate::packet::signature::subpacket + ::SubpacketTag__AttestedCertifications; + + if self.typ() != SignatureType__AttestedKey { + return Err(Error::UnsupportedSignatureType(self.typ()).into()); + } + + let mut hash = self.hash_algo().context()?; + + if self.hashed_area() + .subpackets(SubpacketTag__AttestedCertifications).count() != 1 + || self.unhashed_area() + .subpackets(SubpacketTag__AttestedCertifications).count() != 0 + { + return Err(Error::BadSignature( + "Wrong number of attested certification subpackets".into()) + .into()); + } + + if let SubpacketValue::Unknown { body, .. } = + self.subpacket(SubpacketTag__AttestedCertifications).unwrap() + .value() + { + if body.len() % hash.digest_size() != 0 { + return Err(Error::BadSignature( + "Wrong number of bytes in certification subpacket".into()) + .into()); + } + } else { + unreachable!("Selected attested certifications, got wrong value"); + } + + self.hash_user_attribute_binding(&mut hash, pk, ua); + self.verify_digest(signer, &hash.into_digest()?[..]) + } + /// Verifies a signature of a message. /// /// `self` is the message signature, `signer` is diff --git a/openpgp/src/packet/signature/subpacket.rs b/openpgp/src/packet/signature/subpacket.rs index d97593c0..601649b2 100644 --- a/openpgp/src/packet/signature/subpacket.rs +++ b/openpgp/src/packet/signature/subpacket.rs @@ -326,6 +326,11 @@ pub enum SubpacketTag { } assert_send_and_sync!(SubpacketTag); +/// The proposed Attested Certifications subpacket. +#[allow(non_upper_case_globals)] +pub(crate) const SubpacketTag__AttestedCertifications: SubpacketTag = + SubpacketTag::Unknown(37); + impl fmt::Display for SubpacketTag { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) diff --git a/openpgp/src/types/mod.rs b/openpgp/src/types/mod.rs index cc408770..951cfa8e 100644 --- a/openpgp/src/types/mod.rs +++ b/openpgp/src/types/mod.rs @@ -1127,6 +1127,12 @@ pub enum SignatureType { } assert_send_and_sync!(SignatureType); +/// An attested key signature. +#[allow(non_upper_case_globals)] +pub(crate) const SignatureType__AttestedKey: SignatureType = + SignatureType::Unknown(0x16); + + impl From for SignatureType { fn from(u: u8) -> Self { match u { -- cgit v1.2.3