use std::cmp::Ordering;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::time::SystemTime;
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
use crate::Error;
use crate::Result;
use crate::crypto::{
mpi,
hash::{self, Hash, Digest},
Signer,
};
use crate::KeyHandle;
use crate::HashAlgorithm;
use crate::PublicKeyAlgorithm;
use crate::SignatureType;
use crate::packet::Signature;
use crate::packet::{
key,
Key,
};
use crate::packet::UserID;
use crate::packet::UserAttribute;
use crate::Packet;
use crate::packet;
use crate::packet::signature::subpacket::{
Subpacket,
SubpacketArea,
SubpacketAreas,
SubpacketTag,
SubpacketValue,
};
#[cfg(test)]
trait ArbitraryBounded {
fn arbitrary_bounded<G: Gen>(g: &mut G, depth: usize) -> Self;
}
#[cfg(test)]
const DEFAULT_ARBITRARY_DEPTH: usize = 2;
#[cfg(test)]
macro_rules! impl_arbitrary_with_bound {
($typ:path) => {
impl Arbitrary for $typ {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Self::arbitrary_bounded(
g,
crate::packet::signature::DEFAULT_ARBITRARY_DEPTH)
}
}
}
}
pub mod subpacket;
pub(crate) const SIG_BACKDATE_BY: u64 = 60;
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct SignatureFields {
version: u8,
typ: SignatureType,
pk_algo: PublicKeyAlgorithm,
hash_algo: HashAlgorithm,
subpackets: SubpacketAreas,
}
assert_send_and_sync!(SignatureFields);
#[cfg(test)]
impl ArbitraryBounded for SignatureFields {
fn arbitrary_bounded<G: Gen>(g: &mut G, depth: usize) -> Self {
SignatureFields {
version: 4,
typ: Arbitrary::arbitrary(g),
pk_algo: PublicKeyAlgorithm::arbitrary_for_signing(g),
hash_algo: Arbitrary::arbitrary(g),
subpackets: ArbitraryBounded::arbitrary_bounded(g, depth),
}
}
}
#[cfg(test)]
impl_arbitrary_with_bound!(SignatureFields);
impl Deref for SignatureFields {
type Target = SubpacketAreas;
fn deref(&self) -> &Self::Target {
&self.subpackets
}
}
impl DerefMut for SignatureFields {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.subpackets
}
}
impl SignatureFields {
pub fn version(&self) -> u8 {
self.version
}
pub fn typ(&self) -> SignatureType {
self.typ
}
pub(crate) fn pk_algo(&self) -> PublicKeyAlgorithm {
self.pk_algo
}
pub fn hash_algo(&self) -> HashAlgorithm {
self.hash_algo
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct SignatureBuilder {
overrode_creation_time: bool,
original_creation_time: Option<SystemTime>,
fields: SignatureFields,
}
assert_send_and_sync!(SignatureBuilder);
impl Deref for SignatureBuilder {
type Target = SignatureFields;
fn deref(&self) -> &Self::Target {
&self.fields
}
}
impl DerefMut for SignatureBuilder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.fields
}
}
impl SignatureBuilder {
pub fn new(typ: SignatureType) -> Self {
SignatureBuilder {
overrode_creation_time: false,
original_creation_time: None,
fields: SignatureFields {
version: 4,
typ,
pk_algo: PublicKeyAlgorithm::Unknown(0),
hash_algo: HashAlgorithm::default(),
subpackets: SubpacketAreas::default(),
}
}
}
pub fn set_type(mut self, t: SignatureType) -> Self {
self.typ = t;
self
}
pub fn set_hash_algo(mut self, h: HashAlgorithm) -> Self {
self.hash_algo = h;
self
}
pub fn sign_standalone(mut self, signer: &mut dyn Signer)
-> Result<Signature>
{
match self.typ {
SignatureType::Standalone => (),
SignatureType::Unknown(_) => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ).into()),
}
self = self.pre_sign(signer)?;
let mut hash = self.hash_algo().context()?;
self.hash_standalone(&mut hash);
self.sign(signer, hash.into_digest()?)
}
pub fn sign_timestamp(mut self, signer: &mut dyn Signer)
-> Result<Signature>
{
match self.typ {
SignatureType::Timestamp => (),
SignatureType::Unknown(_) => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ).into()),
}
self = self.pre_sign(signer)?;
let mut hash = self.hash_algo().context()?;
self.hash_timestamp(&mut hash);
self.sign(signer, hash.into_digest()?)
}
pub fn sign_direct_key<'a, PK>(mut self, signer: &mut dyn Signer,
pk: PK)
-> Result<Signature>
where PK: Into<Option<&'a Key<key::PublicParts, key::PrimaryRole>>>
{
match self.typ {
SignatureType::DirectKey => (),
SignatureType::KeyRevocation => (),
SignatureType::Unknown(_) => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ).into()),
}
self = self.pre_sign(signer)?;
let mut hash = self.hash_algo().context()?;
let pk = pk.into().unwrap_or_else(|| signer.public().role_as_primary());
self.hash_direct_key(&mut hash, pk);
self.sign(signer, hash.into_digest()?)
}
pub fn sign_userid_binding<'a, PK>(mut self, signer: &mut dyn Signer,
key: PK, userid: &UserID)
-> Result<Signature>
where PK: Into<Option<&'a Key<key::PublicParts, key::PrimaryRole>>>
{
match self.typ {
SignatureType::GenericCertification => (),
SignatureType::PersonaCertification => (),
SignatureType::CasualCertification => (),
SignatureType::PositiveCertification => (),
SignatureType::CertificationRevocation => (),
SignatureType::Unknown(_) => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ).into()),
}
self = self.pre_sign(signer)?;
let key = key.into().unwrap_or_else(|| signer.public().role_as_primary());
let mut hash = self.hash_algo().context()?;
self.hash_userid_binding(&mut hash, key, userid);
self.sign(signer, hash.into_digest()?)
}
pub fn sign_subkey_binding<'a, PK, Q>(mut self, signer: &mut dyn Signer,
primary: PK,
subkey: &Key<Q, key::SubordinateRole>)
-> Result<Signature>
where Q: key::KeyParts,
PK: Into<Option<&'a Key<key::PublicParts, key::PrimaryRole>>>,
{
match self.typ {
SignatureType::SubkeyBinding => (),
SignatureType::SubkeyRevocation => (),
SignatureType::Unknown(_) => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ).into()),
}
self = self.pre_sign(signer)?;
let primary = primary.into().unwrap_or_else(|| signer.public().role_as_primary());
let mut hash = self.hash_algo().context()?;
self.hash_subkey_binding(&mut hash, primary, subkey);
self.sign(signer, hash.into_digest()?)
}
pub fn sign_primary_key_binding<P, Q>(mut self,
subkey_signer: &mut dyn Signer,
primary: &Key<P, key::PrimaryRole>,
subkey: &Key<Q, key::SubordinateRole>)
-> Result<Signature>
where P: key::KeyParts,
Q: key::KeyParts,
{
match self.typ {
SignatureType::PrimaryKeyBinding => (),
SignatureType::Unknown(_) => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ).into()),
}
self = self.pre_sign(subkey_signer)?;
let mut hash = self.hash_algo().context()?;
self.hash_primary_key_binding(&mut hash, primary, subkey);
self.sign(subkey_signer, hash.into_digest()?)
}
pub fn sign_user_attribute_binding<'a, PK>(mut self, signer: &mut dyn Signer,
key: PK, ua: &UserAttribute)
-> Result<Signature>
where PK: Into<Option<&'a Key<key::PublicParts, key::PrimaryRole>>>
{
match self.typ {
SignatureType::GenericCertification => (),
SignatureType::PersonaCertification => (),
SignatureType::CasualCertification => (),
SignatureType::PositiveCertification => (),
SignatureType::CertificationRevocation => (),
SignatureType::Unknown(_) => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ).into()),
}
self = self.pre_sign(signer)?;
let key = key.into().unwrap_or_else(|| signer.public().role_as_primary());
let mut hash = self.hash_algo().context()?;
self.hash_user_attribute_binding(&mut hash, key, ua);
self.sign(signer, hash.into_digest()?)
}
pub fn sign_hash(mut self, signer: &mut dyn Signer,
mut hash: Box<dyn hash::Digest>)
-> Result<Signature>
{
self.hash_algo = hash.algo();
self = self.pre_sign(signer)?;
self.hash(&mut hash);
let mut digest = vec![0u8; hash.digest_size()];
hash.digest(&mut digest)?;
self.sign(signer, digest)
}
pub fn sign_message<M>(mut self, signer: &mut dyn Signer, msg: M)
-> Result<Signature>
where M: AsRef<[u8]>
{
match self.typ {
SignatureType::Binary => (),
SignatureType::Text => (),
SignatureType::Unknown(_) => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ).into()),
}
let mut hash = self.hash_algo.context()?;
hash.update(msg.as_ref());
self = self.pre_sign(signer)?;
self.hash(&mut hash);
let mut digest = vec![0u8; hash.digest_size()];
hash.digest(&mut digest)?;
self.sign(signer, digest)
}
fn pre_sign(mut self, signer: &dyn Signer) -> Result<Self> {
use std::time;
self.pk_algo = signer.public().pk_algo();
if ! self.overrode_creation_time {
self =
if let Some(oct) = self.original_creation_time.clone() {
let t =
(oct + time::Duration::new(1, 0)).max(
time::SystemTime::now() -
time::Duration::new(SIG_BACKDATE_BY, 0));
if t > time::SystemTime::now() {
return Err(Error::InvalidOperation(
"Cannot create valid signature newer than template"
.into()).into());
}
self.set_signature_creation_time(t)?
} else {
self.set_signature_creation_time(time::SystemTime::now())?
};
}
if self.issuers().next().is_none()
&& self.issuer_fingerprints().next().is_none()
{
self = self.set_issuer(signer.public().keyid())?
.set_issuer_fingerprint(signer.public().fingerprint())?;
}
let mut salt = [0; 32];
crate::crypto::random(&mut salt);
self = self.set_notation("salt@notations.sequoia-pgp.org",
salt, None, false)?;
self.sort();
Ok(self)
}
fn sign(self, signer: &mut dyn Signer, digest: Vec<u8>)
-> Result<Signature>
{
let mpis = signer.sign(self.hash_algo, &digest)?;
Ok(Signature4 {
common: Default::default(),
fields: self.fields,
digest_prefix: [digest[0], digest[1]],
mpis,
computed_digest: Some(digest),
level: 0,
additional_issuers: Vec::with_capacity(0),
}.into())
}
}
impl From<Signature> for SignatureBuilder {
fn from(sig: Signature) -> Self {
match sig {
Signature::V4(sig) => sig.into(),
}
}
}
impl From<Signature4> for SignatureBuilder {
fn from(sig: Signature4) -> Self {
let mut fields = sig.fields;
fields.hash_algo = HashAlgorithm::default();
let creation_time = fields.signature_creation_time();
fields.hashed_area_mut().remove_all(SubpacketTag::SignatureCreationTime);
fields.hashed_area_mut().remove_all(SubpacketTag::Issuer);
fields.hashed_area_mut().remove_all(SubpacketTag::IssuerFingerprint);
fields.unhashed_area_mut().remove_all(SubpacketTag::SignatureCreationTime);
fields.unhashed_area_mut().remove_all(SubpacketTag::Issuer);
fields.unhashed_area_mut().remove_all(SubpacketTag::IssuerFingerprint);
SignatureBuilder {
overrode_creation_time: false,
original_creation_time: creation_time,
fields: fields,
}
}
}
#[derive(Clone)]
pub struct Signature4 {
pub(crate) common: packet::Common,
pub(crate) fields: SignatureFields,
digest_prefix: [u8; 2],
mpis: mpi::Signature,
computed_digest: Option<Vec<u8>>,
level: usize,
additional_issuers: Vec<KeyHandle>,
}
assert_send_and_sync!(Signature4);
impl fmt::Debug for Signature4 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Signature4")
.field("version", &self.version())
.field("typ", &self.typ())
.field("pk_algo", &self.pk_algo())
.field("hash_algo", &self.hash_algo())
.field("hashed_area", self.hashed_area())
.field("unhashed_area", self.unhashed_area())
.field("additional_issuers", &self.additional_issuers)
.field("digest_prefix",
&crate::fmt::to_hex(&self.digest_prefix, false))
.field("computed_digest",
&if let Some(ref hash) = self.computed_digest {
Some(crate::fmt::to_hex(&hash[..], false))
} else {
None
})
.field("level", &self.level)
.field("mpis", &self.mpis)
.finish()
}
}
impl PartialEq for Signature4 {
fn eq(&self, other: &Signature4) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for Signature4 {}
impl PartialOrd for Signature4 {
fn partial_cmp(&self, other: &Signature4) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Signature4 {
fn cmp(&self, other: &Signature4) -> Ordering {
self.fields.cmp(&other.fields)
.then_with(|| self.digest_prefix.cmp(&other.digest_prefix))
.then_with(|| self.mpis.cmp(&other.mpis))
}
}
impl std::hash::Hash for Signature4 {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use std::hash::Hash as StdHash;
StdHash::hash(&self.mpis, state);
StdHash::hash(&self.fields, state);
self.digest_prefix.hash(state);
}
}
impl Signature4 {
pub fn new(typ: SignatureType, pk_algo: PublicKeyAlgorithm,
hash_algo: HashAlgorithm, hashed_area: SubpacketArea,
unhashed_area: SubpacketArea,
digest_prefix: [u8; 2],
mpis: mpi::Signature) -> Self {
Signature4 {
common: Default::default(),
fields: SignatureFields {
version: 4,
typ,
pk_algo,
hash_algo,
subpackets: SubpacketAreas::new(hashed_area, unhashed_area),
},
digest_prefix,
mpis,
computed_digest: None,
level: 0,
additional_issuers: Vec::with_capacity(0),
}
}
pub fn pk_algo(&self) -> PublicKeyAlgorithm {
self.fields.pk_algo()
}
pub fn digest_prefix(&self) -> &[u8; 2] {
&self.digest_prefix
}
#[allow(dead_code)]
pub(crate) fn set_digest_prefix(&mut self, prefix: [u8; 2]) -> [u8; 2] {
::std::mem::replace(&mut self.digest_prefix, prefix)
}
pub fn mpis(&self) -> &mpi::Signature {
&self.mpis
}
#[allow(dead_code)]
pub(crate) fn set_mpis(&mut self, mpis: mpi::Signature) -> mpi::Signature
{
::std::mem::replace(&mut self.mpis, mpis)
}
pub fn computed_digest(&self) -> Option<&[u8]> {
self.computed_digest.as_ref().map(|d| &d[..])
}
pub(crate) fn set_computed_digest(&mut self, hash: Option<Vec<u8>>)
-> Option<Vec<u8>>
{
::std::mem::replace(&mut self.computed_digest, hash)
}
pub fn level(&self) -> usize {
self.level
}
pub(crate) fn set_level(&mut self, level: usize) -> usize {
::std::mem::replace(&mut self.level, level)
}
pub fn exportable(&self) -> Result<()> {
if ! self.exportable_certification().unwrap_or(true) {
return Err(Error::InvalidOperation(
"Cannot export non-exportable certification".into()).into());
}
if self.revocation_keys().any(|r| r.sensitive()) {
return Err(Error::InvalidOperation(
"Cannot export signature with sensitive designated revoker"
.into()).into());
}
Ok(())
}
}
impl crate::packet::Signature {
pub fn get_issuers(&self) -> Vec<crate::KeyHandle> {
let mut issuers: Vec<_> =
self.hashed_area().iter()
.chain(self.unhashed_area().iter())
.filter_map(|subpacket| {
match subpacket.value() {
SubpacketValue::Issuer(i) => Some(i.into()),
SubpacketValue::IssuerFingerprint(i) => Some(i.into()),
_ => None,
}
})
.collect();
issuers.sort_by(|a, b| {
use crate::KeyHandle::*;
use std::cmp::Ordering::*;
match (a, b) {
(Fingerprint(_), Fingerprint(_)) => Equal,
(KeyID(_), Fingerprint(_)) => Greater,
(Fingerprint(_), KeyID(_)) => Less,
(KeyID(_), KeyID(_)) => Equal,
}
});
issuers
}
pub fn normalized_eq(&self, other: &Signature) -> bool {
self.normalized_cmp(other) == Ordering::Equal
}
pub fn normalized_cmp(&self, other: &Signature)
-> Ordering {
self.version().cmp(&other.version())
.then_with(|| self.typ().cmp(&other.typ()))
.then_with(|| self.pk_algo().cmp(&other.pk_algo()))
.then_with(|| self.hash_algo().cmp(&other.hash_algo()))
.then_with(|| self.hashed_area().cmp(other.hashed_area()))
.then_with(|| self.digest_prefix().cmp(other.digest_prefix()))
.then_with(|| self.mpis().cmp(other.mpis()))
}
pub fn normalize(&self) -> Self {
use subpacket::SubpacketTag::*;
let mut sig = self.clone();
{
let area = sig.unhashed_area_mut();
area.clear();
for spkt in self.unhashed_area().iter()
.filter(|s| s.tag() == Issuer
|| s.tag() == IssuerFingerprint
|| s.tag() == EmbeddedSignature)
{
area.add(spkt.clone())
.expect("it did fit into the old area");
}
let _ = sig.add_missing_issuers();
sig.unhashed_area_mut().sort();
}
sig
}
pub fn add_missing_issuers(&mut self) -> Result<()> {
if self.additional_issuers.is_empty() {
return Ok(());
}
fn authenticated_subpacket(v: SubpacketValue) -> Result<Subpacket> {
let mut p = Subpacket::new(v, false)?;
p.set_authenticated(true);
Ok(p)
}
let issuers = self.get_issuers();
for id in std::mem::replace(&mut self.additional_issuers,
Vec::with_capacity(0)) {
if ! issuers.contains(&id) {
match id {
KeyHandle::KeyID(id) =>
self.unhashed_area_mut().add(authenticated_subpacket(
SubpacketValue::Issuer(id))?)?,
KeyHandle::Fingerprint(fp) =>
self.unhashed_area_mut().add(authenticated_subpacket(
SubpacketValue::IssuerFingerprint(fp))?)?,
}
}
}
Ok(())
}
pub fn merge(mut self, other: Signature) -> Result<Signature> {
self.merge_internal(&other)?;
Ok(self)
}
pub(crate) fn merge_internal(&mut self, other: &Signature) -> Result<()>
{
use crate::serialize::MarshalInto;
if ! self.normalized_eq(other) {
return Err(Error::InvalidArgument(
"Signatures are not equal modulo unhashed subpackets".into())
.into());
}
fn eligible(p: &Subpacket) -> bool {
use SubpacketTag::*;
match p.tag() {
SignatureCreationTime
| SignatureExpirationTime
| ExportableCertification
| TrustSignature
| RegularExpression
| Revocable
| KeyExpirationTime
| PlaceholderForBackwardCompatibility
| PreferredSymmetricAlgorithms
| RevocationKey
| PreferredHashAlgorithms
| PreferredCompressionAlgorithms
| KeyServerPreferences
| PreferredKeyServer
| PrimaryUserID
| PolicyURI
| KeyFlags
| SignersUserID
| ReasonForRevocation
| Features
| SignatureTarget
| PreferredAEADAlgorithms
| IntendedRecipient
| Reserved(_)
=> false,
Issuer
| NotationData
| EmbeddedSignature
| IssuerFingerprint
| Private(_)
| Unknown(_)
=> true,
}
}
fn prefer(p: &Subpacket) -> bool {
use SubpacketTag::*;
match p.tag() {
Issuer | EmbeddedSignature | IssuerFingerprint => true,
_ => false,
}
}
#[allow(clippy::mutable_key_type)]
let mut acc = std::collections::HashSet::new();
let mut size = 0;
for id in std::mem::replace(&mut self.additional_issuers,
Vec::with_capacity(0)).into_iter()
.chain(other.additional_issuers.iter().cloned())
{
let p = match id {
KeyHandle::KeyID(id) => Subpacket::new(
SubpacketValue::Issuer(id), false)?,
KeyHandle::Fingerprint(fp) => Subpacket::new(
SubpacketValue::IssuerFingerprint(fp), false)?,
};
let l = p.serialized_len();
if size + l <= std::u16::MAX as usize {
if acc.insert(p.clone()) {
size += l;
}
}
}
for p in
self.unhashed_area().iter()
.filter(|p| eligible(p) && p.authenticated())
.chain(other.unhashed_area().iter()
.filter(|p| eligible(p) && p.authenticated()))
.chain(self.unhashed_area().iter()
.filter(|p| eligible(p) && ! p.authenticated() && prefer(p)))
.chain(other.unhashed_area().iter()
.filter(|p| eligible(p) && ! p.authenticated() && prefer(p)))
.chain(self.unhashed_area().iter()
.filter(|p| eligible(p) && ! p.authenticated() && ! prefer(p)))
.chain(other.unhashed_area().iter()
.filter(|p| eligible(p) && ! p.authenticated() && ! prefer(p)))
{
let l = p.serialized_len();
if size + l <= std::u16::MAX as usize {
if acc.insert(p.clone()) {
size += l;
}
}
}
assert!(size <= std::u16::MAX as usize);
let mut a = SubpacketArea::new(acc.into_iter().collect())
.expect("must fit");
a.sort();
*self.unhashed_area_mut() = a;
Ok(())
}
}
impl Signature {
pub fn verify_hash<P, R>(&mut self, key: &Key<P, R>,
mut hash: Box<dyn hash::Digest>)
-> Result<()>
where P: key::KeyParts,
R: key::KeyRole,
{
self.hash(&mut hash);
let mut digest = vec![0u8; hash.digest_size()];
hash.digest(&mut digest)?;
self.verify_digest(key, digest)
}
pub fn verify_digest<P, R, D>(&mut self, key: &Key<P, R>, digest: D)
-> Result<()>
where P: key::KeyParts,
R: key::KeyRole,
D: AsRef<[u8]>,
{
if let Some(creation_time) = self.signature_creation_time() {
if creation_time < key.creation_time() {
return Err(Error::BadSignature(
format!("Signature (created {:?}) predates key ({:?})",
creation_time, key.creation_time())).into());
}
} else {
return Err(Error::BadSignature(
"Signature has no creation time subpacket".into()).into());
}
let result = key.verify(self.mpis(), self.hash_algo(), digest.as_ref());
if result.is_ok() {
self.hashed_area_mut().iter_mut().for_each(|p| {
p.set_authenticated(true);
});
self.unhashed_area_mut().iter_mut().for_each(|p| {
let authenticated = match p.value() {
SubpacketValue::Issuer(id) =>
id == &key.keyid(),
SubpacketValue::IssuerFingerprint(fp) =>
fp == &key.fingerprint(),
_ => false,
};
p.set_authenticated(authenticated);
});
let issuers = self.get_issuers();
let id = KeyHandle::from(key.keyid());
if ! (issuers.contains(&id)
|| self.additional_issuers.contains(&id)) {
self.additional_issuers.push(id);
}
let fp = KeyHandle::from(key.fingerprint());
if ! (issuers.contains(&fp)
|| self.additional_issuers.contains(&fp)) {
self.additional_issuers.push(fp);
}
}
result
}
pub fn verify<P, R>(&mut self, key: &Key<P, R>) -> Result<()>
where P: key::KeyParts,
R: key::KeyRole,
{
if !(self.typ() == SignatureType::Binary
|| self.typ() == SignatureType::Text) {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
if let Some(hash) = self.computed_digest.take() {
let result = self.verify_digest(key, &hash);
self.computed_digest = Some(hash);
result
} else {
Err(Error::BadSignature("Hash not computed.".to_string()).into())
}
}
pub fn verify_standalone<P, R>(&mut self, key: &Key<P, R>) -> Result<()>
where P: key::KeyParts,
R: key::KeyRole,
{
if self.typ() != SignatureType::Standalone {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_standalone(&mut hash);
self.verify_digest(key, &hash.into_digest()?[..])
}
pub fn verify_timestamp<P, R>(&mut self, key: &Key<P, R>) -> Result<()>
where P: key::KeyParts,
R: key::KeyRole,
{
if self.typ() != SignatureType::Timestamp {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_timestamp(&mut hash);
self.verify_digest(key, &hash.into_digest()?[..])
}
pub fn verify_direct_key<P, Q, R>(&mut self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
{
if self.typ() != SignatureType::DirectKey {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_direct_key(&mut hash, pk);
self.verify_digest(signer, &hash.into_digest()?[..])
}
pub fn verify_primary_key_revocation<P, Q, R>(&mut self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
{
if self.typ() != SignatureType::KeyRevocation {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_direct_key(&mut hash, pk);
self.verify_digest(signer, &hash.into_digest()?[..])
}
pub fn verify_subkey_binding<P, Q, R, S>(
&mut self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>,
subkey: &Key<S, key::SubordinateRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
S: key::KeyParts,
{
if self.typ() != SignatureType::SubkeyBinding {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_subkey_binding(&mut hash, pk, subkey);
self.verify_digest(signer, &hash.into_digest()?[..])?;
if self.key_flags().map(|kf| kf.for_signing()).unwrap_or(false) {
let mut last_result = Err(Error::BadSignature(
"Primary key binding signature missing".into()).into());
for backsig in self.subpackets_mut(SubpacketTag::EmbeddedSignature)
{
let result =
if let SubpacketValue::EmbeddedSignature(sig) =
backsig.value_mut()
{
sig.verify_primary_key_binding(pk, subkey)
} else {
unreachable!("subpackets_mut(EmbeddedSignature) returns \
EmbeddedSignatures");
};
if result.is_ok() {
backsig.set_authenticated(true);
return result;
}
last_result = result;
}
last_result
} else {
Ok(())
}
}
pub fn verify_primary_key_binding<P, Q>(
&mut self,
pk: &Key<P, key::PrimaryRole>,
subkey: &Key<Q, key::SubordinateRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
{
if self.typ() != SignatureType::PrimaryKeyBinding {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_primary_key_binding(&mut hash, pk, subkey);
self.verify_digest(subkey, &hash.into_digest()?[..])
}
pub fn verify_subkey_revocation<P, Q, R, S>(
&mut self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>,
subkey: &Key<S, key::SubordinateRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
S: key::KeyParts,
{
if self.typ() != SignatureType::SubkeyRevocation {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_subkey_binding(&mut hash, pk, subkey);
self.verify_digest(signer, &hash.into_digest()?[..])
}
pub fn verify_userid_binding<P, Q, R>(&mut self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>,
userid: &UserID)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
{
if !(self.typ() == SignatureType::GenericCertification
|| self.typ() == SignatureType::PersonaCertification
|| self.typ() == SignatureType::CasualCertification
|| self.typ() == SignatureType::PositiveCertification) {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_userid_binding(&mut hash, pk, userid);
self.verify_digest(signer, &hash.into_digest()?[..])
}
pub fn verify_userid_revocation<P, Q, R>(&mut self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>,
userid: &UserID)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
{
if self.typ() != SignatureType::CertificationRevocation {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_userid_binding(&mut hash, pk, userid);
self.verify_digest(signer, &hash.into_digest()?[..])
}
pub fn verify_user_attribute_binding<P, Q, R>(&mut self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>,
ua: &UserAttribute)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
{
if !(self.typ() == SignatureType::GenericCertification
|| self.typ() == SignatureType::PersonaCertification
|| self.typ() == SignatureType::CasualCertification
|| self.typ() == SignatureType::PositiveCertification) {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_user_attribute_binding(&mut hash, pk, ua);
self.verify_digest(signer, &hash.into_digest()?[..])
}
pub fn verify_user_attribute_revocation<P, Q, R>(
&mut self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>,
ua: &UserAttribute)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
{
if self.typ() != SignatureType::CertificationRevocation {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
self.hash_user_attribute_binding(&mut hash, pk, ua);
self.verify_digest(signer, &hash.into_digest()?[..])
}
pub fn verify_message<M, P, R>(&mut self, signer: &Key<P, R>,
msg: M)
-> Result<()>
where M: AsRef<[u8]>,
P: key::KeyParts,
R: key::KeyRole,
{
if self.typ() != SignatureType::Binary &&
self.typ() != SignatureType::Text {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
}
let mut hash = self.hash_algo().context()?;
let mut digest = vec![0u8; hash.digest_size()];
hash.update(msg.as_ref());
self.hash(&mut hash);
hash.digest(&mut digest)?;
self.verify_digest(signer, &digest[..])
}
}
impl From<Signature4> for Packet {
fn from(s: Signature4) -> Self {
Packet::Signature(s.into())
}
}
impl From<Signature4> for super::Signature {
fn from(s: Signature4) -> Self {
super::Signature::V4(s)
}
}
#[cfg(test)]
impl ArbitraryBounded for super::Signature {
fn arbitrary_bounded<G: Gen>(g: &mut G, depth: usize) -> Self {
Signature4::arbitrary_bounded(g, depth).into()
}
}
#[cfg(test)]
impl_arbitrary_with_bound!(super::Signature);
#[cfg(test)]
impl ArbitraryBounded for Signature4 {
fn arbitrary_bounded<G: Gen>(g: &mut G, depth: usize) -> Self {
use mpi::MPI;
use PublicKeyAlgorithm::*;
let fields = SignatureFields::arbitrary_bounded(g, depth);
#[allow(deprecated)]
let mpis = match fields.pk_algo() {
RSAEncryptSign | RSASign => mpi::Signature::RSA {
s: MPI::arbitrary(g),
},
DSA => mpi::Signature::DSA {
r: MPI::arbitrary(g),
s: MPI::arbitrary(g),
},
EdDSA => mpi::Signature::EdDSA {
r: MPI::arbitrary(g),
s: MPI::arbitrary(g),
},
ECDSA => mpi::Signature::ECDSA {
r: MPI::arbitrary(g),
s: MPI::arbitrary(g),
},
_ => unreachable!(),
};
Signature4 {
common: Arbitrary::arbitrary(g),
fields,
digest_prefix: [Arbitrary::arbitrary(g),
Arbitrary::arbitrary(g)],
mpis,
computed_digest: None,
level: 0,
additional_issuers: Vec::with_capacity(0),
}
}
}
#[cfg(test)]
impl_arbitrary_with_bound!(Signature4);
#[cfg(test)]
mod test {
use super::*;
use crate::KeyID;
use crate::cert::prelude::*;
use crate::crypto;
use crate::parse::Parse;
use crate::packet::Key;
use crate::packet::key::Key4;
use crate::types::Curve;
use crate::policy::StandardPolicy as P;
#[cfg(feature = "compression-deflate")]
#[test]
fn signature_verification_test() {
use super::*;
use crate::Cert;
use crate::parse::{PacketParserResult, PacketParser};
struct Test<'a> {
key: &'a str,
data: &'a str,
good: usize,
};
let tests = [
Test {
key: &"neal.pgp"[..],
data: &"signed-1.gpg"[..],
good: 1,
},
Test {
key: &"neal.pgp"[..],
data: &"signed-1-sha1-neal.gpg"[..],
good: 1,
},
Test {
key: &"testy.pgp"[..],
data: &"signed-1-sha256-testy.gpg"[..],
good: 1,
},
Test {
key: &"dennis-simon-anton.pgp"[..],
data: &"signed-1-dsa.pgp"[..],
good: 1,
},
Test {
key: &"erika-corinna-daniela-simone-antonia-nistp256.pgp"[..],
data: &"signed-1-ecdsa-nistp256.pgp"[..],
good: 1,
},
Test {
key: &"erika-corinna-daniela-simone-antonia-nistp384.pgp"[..],
data: &"signed-1-ecdsa-nistp384.pgp"[..],
good: 1,
},
Test {
key: &"erika-corinna-daniela-simone-antonia-nistp521.pgp"[..],
data: &"signed-1-ecdsa-nistp521.pgp"[..],
good: 1,
},
Test {
key: &"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"[..],
data: &"signed-1-eddsa-ed25519.pgp"[..],
good: 1,
},
Test {
key: &"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"[..],
data: &"signed-twice-by-ed25519.pgp"[..],
good: 2,
},
Test {
key: "neal.pgp",
data: "signed-1-notarized-by-ed25519.pgp",
good: 1,
},
Test {
key: "emmelie-dorothea-dina-samantha-awina-ed25519.pgp",
data: "signed-1-notarized-by-ed25519.pgp",
good: 1,
},
Test {
key: &"neal.pgp"[..],
data: &"signed-1-sha256-testy.gpg"[..],
good: 0,
},
Test {
key: &"neal.pgp"[..],
data: &"signed-2-partial-body.gpg"[..],
good: 1,
},
];
for test in tests.iter() {
eprintln!("{}, expect {} good signatures:",
test.data, test.good);
let cert = Cert::from_bytes(crate::tests::key(test.key)).unwrap();
let mut good = 0;
let mut ppr = PacketParser::from_bytes(
crate::tests::message(test.data)).unwrap();
while let PacketParserResult::Some(mut pp) = ppr {
if let Packet::Signature(sig) = &mut pp.packet {
let result = sig.verify(cert.primary_key().key())
.map(|_| true).unwrap_or(false);
eprintln!(" Primary {:?}: {:?}",
cert.fingerprint(), result);
if result {
good += 1;
}
for sk in cert.subkeys() {
let result = sig.verify(sk.key())
.map(|_| true).unwrap_or(false);
eprintln!(" Subkey {:?}: {:?}",
sk.key().fingerprint(), result);
if result {
good += 1;
}
}
}
ppr = pp.recurse().unwrap().1;
}
assert_eq!(good, test.good, "Signature verification failed.");
}
}
#[test]
fn signature_level() {
use crate::PacketPile;
let p = PacketPile::from_bytes(
crate::tests::message("signed-1-notarized-by-ed25519.pgp")).unwrap()
.into_children().collect::<Vec<Packet>>();
if let Packet::Signature(ref sig) = &p[3] {
assert_eq!(sig.level(), 0);
} else {
panic!("expected signature")
}
if let Packet::Signature(ref sig) = &p[4] {
assert_eq!(sig.level(), 1);
} else {
panic!("expected signature")
}
}
#[test]
fn sign_verify() {
let hash_algo = HashAlgorithm::SHA512;
let mut hash = vec![0; hash_algo.context().unwrap().digest_size()];
crypto::random(&mut hash);
for key in &[
"testy-private.pgp",
"dennis-simon-anton-private.pgp",
"erika-corinna-daniela-simone-antonia-nistp256-private.pgp",
"erika-corinna-daniela-simone-antonia-nistp384-private.pgp",
"erika-corinna-daniela-simone-antonia-nistp521-private.pgp",
"emmelie-dorothea-dina-samantha-awina-ed25519-private.pgp",
] {
let cert = Cert::from_bytes(crate::tests::key(key)).unwrap();
let mut pair = cert.primary_key().key().clone()
.parts_into_secret().unwrap()
.into_keypair()
.expect("secret key is encrypted/missing");
let sig = SignatureBuilder::new(SignatureType::Binary);
let hash = hash_algo.context().unwrap();
let mut sig = sig.sign_hash(&mut pair, hash).unwrap();
let mut hash = hash_algo.context().unwrap();
sig.hash(&mut hash);
let mut digest = vec![0u8; hash.digest_size()];
hash.digest(&mut digest).unwrap();
sig.verify_digest(pair.public(), &digest[..]).unwrap();
digest[0] ^= 0xff;
sig.verify_digest(pair.public(), &digest[..]).unwrap_err();
}
}
#[test]
fn sign_message() {
use crate::types::Curve;
let key: Key<key::SecretParts, key::PrimaryRole>
= Key4::generate_ecc(true, Curve::Ed25519)
.unwrap().into();
let msg = b"Hello, World";
let mut pair = key.into_keypair().unwrap();
let mut sig = SignatureBuilder::new(SignatureType::Binary)
.sign_message(&mut pair, msg).unwrap();
sig.verify_message(pair.public(), msg).unwrap();
}
#[test]
fn verify_message() {
let cert = Cert::from_bytes(crate::tests::key(
"emmelie-dorothea-dina-samantha-awina-ed25519.pgp")).unwrap();
let msg = crate::tests::manifesto();
let p = Packet::from_bytes(
crate::tests::message("a-cypherpunks-manifesto.txt.ed25519.sig"))
.unwrap();
let mut sig = if let Packet::Signature(s) = p {
s
} else {
panic!("Expected a Signature, got: {:?}", p);
};
sig.verify_message(cert.primary_key().key(), &msg[..]).unwrap();
}
#[test]
fn sign_with_short_ed25519_secret_key() {
let secret_key = [
0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x1,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,
0x1,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2
];
let key: key::SecretKey = Key4::import_secret_ed25519(&secret_key, None)
.unwrap().into();
let mut pair = key.into_keypair().unwrap();
let msg = b"Hello, World";
let mut hash = HashAlgorithm::SHA256.context().unwrap();
hash.update(&msg[..]);
SignatureBuilder::new(SignatureType::Text)
.sign_hash(&mut pair, hash).unwrap();
}
#[test]
fn verify_gpg_3rd_party_cert() {
use crate::Cert;
let p = &P::new();
let test1 = Cert::from_bytes(
crate::tests::key("test1-certification-key.pgp")).unwrap();
let cert_key1 = test1.keys().with_policy(p, None)
.for_certification()
.nth(0)
.map(|ka| ka.key())
.unwrap();
let test2 = Cert::from_bytes(
crate::tests::key("test2-signed-by-test1.pgp")).unwrap();
let uid = test2.userids().with_policy(p, None).nth(0).unwrap();
let mut cert = uid.certifications().nth(0).unwrap().clone();
cert.verify_userid_binding(cert_key1,
test2.primary_key().key(),
uid.userid()).unwrap();
}
#[test]
fn normalize() {
use crate::Fingerprint;
use crate::packet::signature::subpacket::*;
let key : key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519).unwrap().into();
let mut pair = key.into_keypair().unwrap();
let msg = b"Hello, World";
let mut hash = HashAlgorithm::SHA256.context().unwrap();
hash.update(&msg[..]);
let fp = Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb");
let keyid = KeyID::from(&fp);
let mut builder = SignatureBuilder::new(SignatureType::Text);
builder.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::IssuerFingerprint(fp.clone()), false).unwrap())
.unwrap();
builder.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::Issuer(keyid.clone()), false).unwrap())
.unwrap();
builder.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::PreferredSymmetricAlgorithms(Vec::new()),
false).unwrap()).unwrap();
let embedded_sig = SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
.sign_hash(&mut pair, hash.clone()).unwrap();
builder.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::EmbeddedSignature(embedded_sig.into()), false)
.unwrap()).unwrap();
let sig = builder.sign_hash(&mut pair,
hash.clone()).unwrap().normalize();
assert_eq!(sig.unhashed_area().iter().count(), 3);
assert_eq!(*sig.unhashed_area().iter().nth(0).unwrap(),
Subpacket::new(SubpacketValue::Issuer(keyid.clone()),
false).unwrap());
assert_eq!(sig.unhashed_area().iter().nth(1).unwrap().tag(),
SubpacketTag::EmbeddedSignature);
assert_eq!(*sig.unhashed_area().iter().nth(2).unwrap(),
Subpacket::new(SubpacketValue::IssuerFingerprint(fp.clone()),
false).unwrap());
}
#[test]
fn standalone_signature_roundtrip() {
let key : key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519).unwrap().into();
let mut pair = key.into_keypair().unwrap();
let mut sig = SignatureBuilder::new(SignatureType::Standalone)
.sign_standalone(&mut pair)
.unwrap();
sig.verify_standalone(pair.public()).unwrap();
}
#[test]
fn timestamp_signature() {
let alpha = Cert::from_bytes(crate::tests::file(
"contrib/gnupg/keys/alpha.pgp")).unwrap();
let p = Packet::from_bytes(crate::tests::file(
"contrib/gnupg/timestamp-signature-by-alice.asc")).unwrap();
if let Packet::Signature(mut sig) = p {
let mut hash = sig.hash_algo().context().unwrap();
sig.hash_standalone(&mut hash);
let digest = hash.into_digest().unwrap();
eprintln!("{}", crate::fmt::hex::encode(&digest));
sig.verify_timestamp(alpha.primary_key().key()).unwrap();
} else {
panic!("expected a signature packet");
}
}
#[test]
fn timestamp_signature_roundtrip() {
let key : key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519).unwrap().into();
let mut pair = key.into_keypair().unwrap();
let mut sig = SignatureBuilder::new(SignatureType::Timestamp)
.sign_timestamp(&mut pair)
.unwrap();
sig.verify_timestamp(pair.public()).unwrap();
}
#[test]
fn get_issuers_prefers_fingerprints() -> Result<()> {
use crate::KeyHandle;
for f in [
"messages/sig.gpg",
"contrib/gnupg/timestamp-signature-by-alice.asc",
].iter() {
let p = Packet::from_bytes(crate::tests::file(f))?;
if let Packet::Signature(sig) = p {
let issuers = sig.get_issuers();
assert_match!(KeyHandle::Fingerprint(_) = &issuers[0]);
assert_match!(KeyHandle::KeyID(_) = &issuers[1]);
} else {
panic!("expected a signature packet");
}
}
Ok(())
}
#[test]
fn binding_signatures_are_overrideable() -> Result<()> {
use crate::packet::signature::subpacket::NotationDataFlags;
let notation_key = "override-test@sequoia-pgp.org";
let p = &P::new();
let (mut alice, _) =
CertBuilder::general_purpose(None, Some("alice@example.org"))
.generate()?;
let mut primary_signer = alice.primary_key().key().clone()
.parts_into_secret()?.into_keypair()?;
assert_eq!(alice.userids().len(), 1);
assert_eq!(alice.userids().nth(0).unwrap().self_signatures().count(), 1);
let creation_time =
alice.userids().nth(0).unwrap().self_signatures().nth(0).unwrap()
.signature_creation_time().unwrap();
for i in 0..2 * SIG_BACKDATE_BY {
assert_eq!(alice.userids().nth(0).unwrap().self_signatures().count(),
1 + i as usize);
let sig = alice.with_policy(p, None)?.userids().nth(0).unwrap()
.binding_signature().clone();
assert_eq!(sig.signature_creation_time().unwrap(),
creation_time + std::time::Duration::new(i, 0));
let new_sig = match
SignatureBuilder::from(sig)
.set_notation(notation_key,
i.to_string().as_bytes(),
NotationDataFlags::empty().set_human_readable(),
false)?
.sign_userid_binding(&mut primary_signer,
alice.primary_key().component(),
&alice.userids().nth(0).unwrap()) {
Ok(v) => v,
Err(e) => if i < SIG_BACKDATE_BY {
return Err(e); } else {
assert!(e.to_string().contains(
"Cannot create valid signature newer than \
template"));
return Ok(()); },
};
alice = alice.insert_packets(new_sig.clone())?;
let sig = alice.with_policy(p, None)?.userids().nth(0).unwrap()
.binding_signature();
assert_eq!(sig, &new_sig);
}
panic!("We were unexpectedly able to update binding signatures {} \
times. This is either a very slow build environment, or \
there is a bug. Please get in contact.",
2 * SIG_BACKDATE_BY);
}
#[test]
fn subpacket_authentication() -> Result<()> {
use subpacket::{Subpacket, SubpacketValue};
let mut pp = crate::PacketPile::from_bytes(crate::tests::key(
"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"))?;
assert_eq!(pp.children().count(), 5);
if let Some(Packet::Signature(sig)) = pp.path_ref_mut(&[4]) {
assert!(sig.hashed_area().iter().all(|p| ! p.authenticated()));
assert!(sig.unhashed_area().iter().all(|p| ! p.authenticated()));
sig.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::Issuer("AAAA BBBB CCCC DDDD".parse()?),
false)?)?;
} else {
panic!("expected a signature");
}
if let Some(Packet::Signature(sig)) = pp.path_ref_mut(&[2]) {
assert!(sig.hashed_area().iter().all(|p| ! p.authenticated()));
assert!(sig.unhashed_area().iter().all(|p| ! p.authenticated()));
sig.hashed_area_mut().add(Subpacket::new(
SubpacketValue::Issuer("AAAA BBBB CCCC DDDD".parse()?),
false)?)?;
} else {
panic!("expected a signature");
}
use std::convert::TryFrom;
let cert = Cert::try_from(pp)?;
assert_eq!(cert.bad_signatures().count(), 1);
assert_eq!(cert.keys().subkeys().count(), 1);
let subkey = cert.keys().subkeys().nth(0).unwrap();
assert_eq!(subkey.self_signatures().count(), 1);
let sig = &subkey.self_signatures().nth(0).unwrap();
assert!(sig.hashed_area().iter().all(|p| p.authenticated()));
assert!(sig.unhashed_area().iter().all(|p| {
if let SubpacketValue::Issuer(id) = p.value() {
if id == &"AAAA BBBB CCCC DDDD".parse().unwrap() {
true
} else {
p.authenticated()
}
} else {
p.authenticated()
}
}));
let sig = sig.embedded_signatures().nth(0).unwrap();
assert!(sig.hashed_area().iter().all(|p| p.authenticated()));
assert!(sig.unhashed_area().iter().all(|p| p.authenticated()));
let sig = cert.bad_signatures().nth(0).unwrap();
assert!(sig.hashed_area().iter().all(|p| ! p.authenticated()));
assert!(sig.unhashed_area().iter().all(|p| ! p.authenticated()));
Ok(())
}
#[test]
fn normalization_adds_missing_issuers() -> Result<()> {
use subpacket::SubpacketTag;
let mut pp = crate::PacketPile::from_bytes(crate::tests::key(
"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"))?;
assert_eq!(pp.children().count(), 5);
if let Some(Packet::Signature(sig)) = pp.path_ref_mut(&[4]) {
sig.unhashed_area_mut().remove_all(SubpacketTag::Issuer);
assert_eq!(sig.get_issuers().len(), 1);
} else {
panic!("expected a signature");
}
let primary_key =
if let Some(Packet::PublicKey(key)) = pp.path_ref(&[0]) {
key
} else {
panic!("Expected a primary key");
};
let subkey =
if let Some(Packet::PublicSubkey(key)) = pp.path_ref(&[3]) {
key
} else {
panic!("Expected a subkey");
};
let mut sig =
if let Some(Packet::Signature(sig)) = pp.path_ref(&[4]) {
sig.clone()
} else {
panic!("expected a signature");
};
assert_eq!(sig.get_issuers().len(), 1);
assert_eq!(sig.subpackets(SubpacketTag::Issuer).count(), 0);
sig.verify_subkey_binding(&primary_key, &primary_key, &subkey)?;
let normalized_sig = sig.normalize();
assert_eq!(normalized_sig.subpackets(SubpacketTag::Issuer).count(), 1);
Ok(())
}
#[test]
fn merging() -> Result<()> {
use crate::packet::signature::subpacket::*;
let key: key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519)?.into();
let mut pair = key.into_keypair()?;
let msg = b"Hello, World";
let mut hash = HashAlgorithm::SHA256.context()?;
hash.update(&msg[..]);
let fp = pair.public().fingerprint();
let keyid = KeyID::from(&fp);
let sig = SignatureBuilder::new(SignatureType::Text)
.modify_unhashed_area(|mut a| {
a.add(Subpacket::new(
SubpacketValue::IssuerFingerprint(fp.clone()), false)?)?;
a.add(Subpacket::new(
SubpacketValue::Issuer(keyid.clone()), false)?)?;
Ok(a)
})?
.sign_hash(&mut pair, hash.clone())?;
let dummy: crate::KeyID = "AAAA BBBB CCCC DDDD".parse()?;
let mut malicious = sig.clone();
malicious.unhashed_area_mut().clear();
loop {
let r = malicious.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::Issuer(dummy.clone()), false)?);
if r.is_err() {
break;
}
}
let merged = sig.clone().merge(malicious.clone())?;
let issuers = merged.get_issuers();
assert_eq!(issuers.len(), 3);
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(issuers.contains(&KeyHandle::from(&keyid)));
assert!(issuers.contains(&KeyHandle::from(&dummy)));
let merged = malicious.clone().merge(sig.clone())?;
let issuers = merged.get_issuers();
assert_eq!(issuers.len(), 3);
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(issuers.contains(&KeyHandle::from(&keyid)));
assert!(issuers.contains(&KeyHandle::from(&dummy)));
let mut malicious = sig.clone();
malicious.unhashed_area_mut().clear();
let mut i: u64 = 0;
loop {
let r = malicious.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::Unknown {
tag: SubpacketTag::Unknown(231),
body: i.to_be_bytes().iter().cloned().collect(),
}, false)?);
if r.is_err() {
break;
}
i += 1;
}
let merged = sig.clone().merge(malicious.clone())?;
let issuers = merged.get_issuers();
assert_eq!(issuers.len(), 2);
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(issuers.contains(&KeyHandle::from(&keyid)));
let merged = malicious.clone().merge(sig.clone())?;
let issuers = merged.get_issuers();
assert_eq!(issuers.len(), 2);
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(issuers.contains(&KeyHandle::from(&keyid)));
let mut malicious = sig.clone();
malicious.unhashed_area_mut().clear();
let mut i: u64 = 1;
loop {
let r = malicious.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::Issuer(i.into()), false)?);
if r.is_err() {
break;
}
i += 1;
}
let mut verified = sig.clone();
verified.verify_hash(pair.public(), hash.clone())?;
let merged = verified.clone().merge(malicious.clone())?;
let issuers = merged.get_issuers();
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(issuers.contains(&KeyHandle::from(&keyid)));
let merged = malicious.clone().merge(verified.clone())?;
let issuers = merged.get_issuers();
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(issuers.contains(&KeyHandle::from(&keyid)));
Ok(())
}
}