use std::borrow::Cow;
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::fmt;
use std::hash::Hasher;
use std::ops::{Deref, DerefMut};
use std::sync::OnceLock;
use std::time::SystemTime;
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
use crate::Error;
use crate::Result;
use crate::crypto::{
mpi,
hash::{self, Hash},
Signer,
};
use crate::KeyID;
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,
};
use crate::types::Timestamp;
#[cfg(test)]
pub(crate) trait ArbitraryBounded {
fn arbitrary_bounded(g: &mut Gen, 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: &mut Gen) -> Self {
Self::arbitrary_bounded(
g,
crate::packet::signature::DEFAULT_ARBITRARY_DEPTH)
}
}
}
}
pub mod subpacket;
pub mod cache;
mod v6;
pub use v6::Signature6;
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: &mut Gen, 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, Debug, Hash, PartialEq, Eq)]
pub(crate) enum SBVersion {
V4 {},
V6 {
salt: Vec<u8>,
},
}
impl Default for SBVersion {
fn default() -> Self {
SBVersion::V4 {}
}
}
impl SBVersion {
fn to_u8(&self) -> u8 {
match self {
SBVersion::V4 { .. } => 4,
SBVersion::V6 { .. } => 6,
}
}
pub(crate) fn salt(&self) -> Option<&[u8]> {
match self {
SBVersion::V4 { .. } => None,
SBVersion::V6 { salt, .. } => Some(&salt),
}
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct SignatureBuilder {
reference_time: Option<SystemTime>,
overrode_creation_time: bool,
original_creation_time: Option<SystemTime>,
pub(crate) fields: SignatureFields,
pub(crate) sb_version: SBVersion,
}
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 {
reference_time: None,
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(),
},
sb_version: SBVersion::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>
{
self = self.pre_sign(signer)?;
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
self.hash_standalone(&mut hash)?;
self.sign(signer, hash.into_digest()?)
}
pub fn sign_timestamp(mut self, signer: &mut dyn Signer)
-> Result<Signature>
{
self = self.pre_sign(signer)?;
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
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>>>
{
self = self.pre_sign(signer)?;
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
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>>>
{
self = self.pre_sign(signer)?;
let key = key.into().unwrap_or_else(|| signer.public().role_as_primary());
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
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>>>,
{
self = self.pre_sign(signer)?;
let primary = primary.into().unwrap_or_else(|| signer.public().role_as_primary());
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
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,
{
self = self.pre_sign(subkey_signer)?;
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
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>>>
{
self = self.pre_sign(signer)?;
let key = key.into().unwrap_or_else(|| signer.public().role_as_primary());
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
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: hash::Context)
-> 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()),
}
self = self.pre_sign(signer)?;
let mut hash =
self.hash_algo.context()?.for_signature(self.version());
if let Some(salt) = self.sb_version.salt() {
hash.update(salt);
}
hash.update(msg.as_ref());
self.hash(&mut hash)?;
let mut digest = vec![0u8; hash.digest_size()];
hash.digest(&mut digest)?;
self.sign(signer, digest)
}
pub fn set_reference_time<T>(mut self, reference_time: T) -> Result<Self>
where
T: Into<Option<SystemTime>>,
{
let reference_time = reference_time.into();
if let Some(t) = reference_time.clone() {
Timestamp::try_from(t)?;
}
self.reference_time = reference_time;
Ok(self)
}
pub fn effective_signature_creation_time(&self)
-> Result<Option<SystemTime>>
{
use std::time;
let now = || -> Result<SystemTime> {
let rt = self.reference_time.unwrap_or_else(crate::now);
Ok(SystemTime::from(Timestamp::try_from(rt)?))
};
if ! self.overrode_creation_time {
if let Some(orig) = self.original_creation_time {
let now = now()?;
let t =
(orig + time::Duration::new(1, 0)).max(
now - time::Duration::new(SIG_BACKDATE_BY, 0));
if t > now {
return Err(Error::InvalidOperation(
"Cannot create valid signature newer than SignatureBuilder template"
.into()).into());
}
Ok(Some(t))
} else {
Ok(Some(now()?))
}
} else {
Ok(self.signature_creation_time())
}
}
pub fn pre_sign(mut self, signer: &dyn Signer) -> Result<Self> {
let pk = signer.public();
self.pk_algo = pk.pk_algo();
self.sb_version = match (self.sb_version, pk.version()) {
(SBVersion::V4 {}, 4) => SBVersion::V4 {},
(SBVersion::V6 { .. }, 4) => SBVersion::V4 {},
(SBVersion::V4 {}, 6) => {
let mut salt = vec![0; self.fields.hash_algo().salt_size()?];
crate::crypto::random(&mut salt)?;
SBVersion::V6 { salt }
},
(SBVersion::V6 { salt }, 6) => SBVersion::V6 { salt },
(_, n) => return Err(Error::InvalidOperation(
format!("Unsupported key version {}", n)).into()),
};
self.fields.version = self.sb_version.to_u8();
if ! self.overrode_creation_time {
if let Some(t) = self.effective_signature_creation_time()? {
self = self.set_signature_creation_time(t)?;
}
}
match &self.sb_version {
SBVersion::V4 {} => {
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)?;
},
SBVersion::V6 { .. } => {
if self.issuer_fingerprints().next().is_none() {
self = self
.set_issuer_fingerprint(signer.public().fingerprint())?;
}
},
}
self.sort();
Ok(self)
}
pub fn prefix_salt(&self) -> Option<&[u8]> {
match &self.sb_version {
SBVersion::V4 {} => None,
SBVersion::V6 { salt } => Some(salt),
}
}
pub fn set_prefix_salt(mut self, new_salt: Vec<u8>) -> (Self, Option<Vec<u8>>) {
let mut old = None;
self.sb_version = match std::mem::take(&mut self.sb_version) {
SBVersion::V4 {} => SBVersion::V6 { salt: new_salt },
SBVersion::V6 { salt } => {
old = Some(salt);
SBVersion::V6 { salt: new_salt }
},
};
(self, old)
}
fn sign(self, signer: &mut dyn Signer, digest: Vec<u8>)
-> Result<Signature>
{
#[allow(deprecated)]
if matches!(self.sb_version, SBVersion::V6 { .. })
&& self.fields.pk_algo() == PublicKeyAlgorithm::DSA
{
return Err(Error::BadSignature(
"Version 6 signatures using DSA MUST NOT be created".into())
.into());
}
let mpis = signer.sign(self.hash_algo, &digest)?;
let v4 = Signature4 {
common: Default::default(),
fields: self.fields,
digest_prefix: [digest[0], digest[1]],
mpis,
computed_digest: digest.into(),
level: 0,
additional_issuers: OnceLock::new(),
};
match self.sb_version {
SBVersion::V4 {} => Ok(v4.into()),
SBVersion::V6 { salt } =>
Ok(Signature6::from_common(v4, salt)?.into())
}
}
}
impl From<Signature> for SignatureBuilder {
fn from(sig: Signature) -> Self {
match sig {
Signature::V3(sig) => sig.into(),
Signature::V4(sig) => sig.into(),
Signature::V6(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 {
reference_time: None,
overrode_creation_time: false,
original_creation_time: creation_time,
fields,
sb_version: Default::default(),
}
}
}
impl From<Signature6> for SignatureBuilder {
fn from(sig: Signature6) -> Self {
SignatureBuilder::from(sig.common)
}
}
#[derive(Clone)]
pub struct Signature4 {
pub(crate) common: packet::Common,
pub(crate) fields: SignatureFields,
digest_prefix: [u8; 2],
mpis: mpi::Signature,
computed_digest: OnceLock<Vec<u8>>,
level: usize,
additional_issuers: OnceLock<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",
&self
.computed_digest
.get()
.map(|hash| crate::fmt::to_hex(&hash[..], false)),
)
.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: OnceLock::new(),
level: 0,
additional_issuers: OnceLock::new(),
}
}
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.get().map(|d| &d[..])
}
pub(crate) fn set_computed_digest(&self, hash: Option<Vec<u8>>)
{
let _ = self.computed_digest.set(hash.unwrap_or_default());
}
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(())
}
fn additional_issuers(&self) -> &[KeyHandle] {
self.additional_issuers.get().map(|v| v.as_slice()).unwrap_or(&[])
}
}
impl From<Signature3> for SignatureBuilder {
fn from(sig: Signature3) -> Self {
SignatureBuilder::from(sig.intern)
}
}
#[derive(Clone)]
pub struct Signature3 {
pub(crate) intern: Signature4,
}
assert_send_and_sync!(Signature3);
impl TryFrom<Signature> for Signature3 {
type Error = anyhow::Error;
fn try_from(sig: Signature) -> Result<Self> {
match sig {
Signature::V3(sig) => Ok(sig),
sig => Err(
Error::InvalidArgument(
format!(
"Got a v{}, require a v3 signature",
sig.version()))
.into()),
}
}
}
impl Deref for Signature3 {
type Target = Signature4;
fn deref(&self) -> &Self::Target {
&self.intern
}
}
impl DerefMut for Signature3 {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.intern
}
}
impl fmt::Debug for Signature3 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Signature3")
.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",
&self
.computed_digest
.get()
.map(|hash| crate::fmt::to_hex(&hash[..], false)),
)
.field("level", &self.level)
.field("mpis", &self.mpis)
.finish()
}
}
impl PartialEq for Signature3 {
fn eq(&self, other: &Signature3) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for Signature3 {}
impl PartialOrd for Signature3 {
fn partial_cmp(&self, other: &Signature3) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Signature3 {
fn cmp(&self, other: &Signature3) -> Ordering {
self.intern.cmp(&other.intern)
}
}
impl std::hash::Hash for Signature3 {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use std::hash::Hash as StdHash;
StdHash::hash(&self.intern, state);
}
}
impl Signature3 {
pub fn new(typ: SignatureType, creation_time: Timestamp,
issuer: KeyID,
pk_algo: PublicKeyAlgorithm,
hash_algo: HashAlgorithm,
digest_prefix: [u8; 2],
mpis: mpi::Signature) -> Self {
let hashed_area = SubpacketArea::new(vec![
Subpacket::new(
SubpacketValue::SignatureCreationTime(creation_time),
true).expect("fits"),
]).expect("fits");
let unhashed_area = SubpacketArea::new(vec![
Subpacket::new(
SubpacketValue::Issuer(issuer),
false).expect("fits"),
]).expect("fits");
let mut sig = Signature4::new(typ,
pk_algo, hash_algo,
hashed_area, unhashed_area,
digest_prefix, mpis);
sig.version = 3;
Signature3 {
intern: sig,
}
}
pub fn pk_algo(&self) -> PublicKeyAlgorithm {
self.fields.pk_algo()
}
pub fn digest_prefix(&self) -> &[u8; 2] {
&self.digest_prefix
}
pub fn mpis(&self) -> &mpi::Signature {
&self.mpis
}
pub fn computed_digest(&self) -> Option<&[u8]> {
self.computed_digest.get().map(|d| &d[..])
}
pub fn level(&self) -> usize {
self.level
}
}
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 normalized_hash<H>(&self, state: &mut H)
where H: Hasher
{
use std::hash::Hash;
self.version.hash(state);
self.typ.hash(state);
self.pk_algo.hash(state);
self.hash_algo.hash(state);
self.hashed_area().hash(state);
self.digest_prefix().hash(state);
Hash::hash(&self.mpis(), state);
}
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(());
}
if self.version() == 3 {
return Err(Error::InvalidOperation(
"cannot add information to v3 signature".into()).into());
}
let issuers = self.get_issuers();
for id in self.additional_issuers.take().expect("not empty") {
if ! issuers.contains(&id) {
match id {
KeyHandle::KeyID(id) =>
self.unhashed_area_mut().add_internal(
Subpacket::new(SubpacketValue::Issuer(id), false)?,
true)?,
KeyHandle::Fingerprint(fp) =>
self.unhashed_area_mut().add_internal(
Subpacket::new(SubpacketValue::IssuerFingerprint(fp), false)?,
true)?,
}
}
}
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::*;
#[allow(deprecated)]
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
| ApprovedCertifications
| PreferredAEADCiphersuites
| Reserved(_)
=> false,
Issuer
| NotationData
| EmbeddedSignature
| IssuerFingerprint
| Private(_)
| Unknown(_)
=> true,
}
}
fn prefer(p: &Subpacket) -> bool {
use SubpacketTag::*;
matches!(p.tag(), Issuer | EmbeddedSignature | IssuerFingerprint)
}
#[allow(clippy::mutable_key_type)]
let mut acc = std::collections::HashSet::new();
let mut size = 0;
for id in self.additional_issuers.take().unwrap_or_default().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 && acc.insert(p) {
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 && 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_signature<P, R>(&self, key: &Key<P, R>) -> Result<()>
where P: key::KeyParts,
R: key::KeyRole,
{
self.verify_digest_internal(
key.parts_as_public().role_as_unspecified(), None)
}
pub fn verify_hash<P, R>(&self, key: &Key<P, R>,
mut hash: hash::Context)
-> Result<()>
where P: key::KeyParts,
R: key::KeyRole,
{
self.hash(&mut hash)?;
self.verify_digest_internal(
key.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_digest<P, R, D>(&self, key: &Key<P, R>, digest: D)
-> Result<()>
where P: key::KeyParts,
R: key::KeyRole,
D: AsRef<[u8]>,
{
self.verify_digest_internal(
key.parts_as_public().role_as_unspecified(),
Some(digest.as_ref().into()))
}
fn verify_digest_internal(&self,
key: &Key<key::PublicParts, key::UnspecifiedRole>,
computed_digest: Option<Cow<[u8]>>)
-> Result<()>
{
if (self.version() == 6) != (key.version() == 6) {
return Err(Error::BadSignature(
format!("Signature (v{}) and key (v{}) version mismatch",
self.version(), key.version())).into());
}
if self.version() == 6 &&
Some(self.hash_algo().salt_size()?) != self.salt().map(|s| s.len())
{
return Err(Error::BadSignature(
format!("Salt of size {} bytes is wrong, expected {} bytes ",
self.salt().map(|s| s.len()).unwrap_or(0),
self.hash_algo().salt_size()?)).into());
}
#[allow(deprecated)]
if self.version() == 6 && self.pk_algo() == PublicKeyAlgorithm::DSA {
return Err(Error::BadSignature(
"Version 6 signatures using DSA MUST be rejected".into())
.into());
}
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 digest = computed_digest.as_ref().map(AsRef::as_ref)
.or(self.computed_digest())
.ok_or_else(|| Error::BadSignature("Hash not computed.".into()))?;
let result = if let Ok(entry) = cache::Entry::new(
self, digest, key.parts_as_public().role_as_unspecified())
{
if entry.present() {
Ok(())
} else {
let result = key.verify(
self.mpis(), self.hash_algo(), digest);
entry.insert(result.is_ok());
result
}
} else {
key.verify(self.mpis(), self.hash_algo(), digest)
};
if let Ok(expected_salt_len) = self.hash_algo().salt_size() {
let salt_len = self.salt().map(|s| s.len()).unwrap_or(0);
if self.version() == 6 && salt_len != expected_salt_len {
return Err(Error::BadSignature(format!(
"bad salt length, expected {} got {}",
expected_salt_len, salt_len)).into());
}
}
if result.is_ok() {
self.hashed_area().iter().for_each(|p| {
p.set_authenticated(true);
});
self.unhashed_area().iter().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 mut additional_issuers = Vec::with_capacity(0);
let id = KeyHandle::from(key.keyid());
if self.version() <= 4 && ! issuers.contains(&id) {
additional_issuers.push(id);
}
if self.version() >= 4 {
let fp = KeyHandle::from(key.fingerprint());
if ! issuers.contains(&fp) {
additional_issuers.push(fp);
}
}
let _ = self.additional_issuers.set(additional_issuers);
if let Some(digest) = computed_digest {
self.set_computed_digest(Some(digest.into_owned()));
}
}
result
}
pub fn verify_document<P, R>(&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());
}
self.verify_digest_internal(
key.parts_as_public().role_as_unspecified(), None)
}
pub fn verify_standalone<P, R>(&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()?.for_signature(self.version());
self.hash_standalone(&mut hash)?;
self.verify_digest_internal(key.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_timestamp<P, R>(&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()?.for_signature(self.version());
self.hash_timestamp(&mut hash)?;
self.verify_digest_internal(
key.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_direct_key<P, Q, R>(&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()?.for_signature(self.version());
self.hash_direct_key(&mut hash, pk)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_primary_key_revocation<P, Q, R>(&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()?.for_signature(self.version());
self.hash_direct_key(&mut hash, pk)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_subkey_binding<P, Q, R, S>(
&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()?.for_signature(self.version());
self.hash_subkey_binding(&mut hash, pk, subkey)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))?;
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(SubpacketTag::EmbeddedSignature)
{
let result =
if let SubpacketValue::EmbeddedSignature(sig) =
backsig.value()
{
sig.verify_primary_key_binding(pk, subkey)
} else {
unreachable!("subpackets(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>(
&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()?.for_signature(self.version());
self.hash_primary_key_binding(&mut hash, pk, subkey)?;
self.verify_digest_internal(
subkey.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_subkey_revocation<P, Q, R, S>(
&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()?.for_signature(self.version());
self.hash_subkey_binding(&mut hash, pk, subkey)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_userid_binding<P, Q, R>(&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()?.for_signature(self.version());
self.hash_userid_binding(&mut hash, pk, userid)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_userid_revocation<P, Q, R>(&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()?.for_signature(self.version());
self.hash_userid_binding(&mut hash, pk, userid)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_userid_approval<P, Q, R>(
&self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>,
userid: &UserID)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
{
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
if self.approved_certifications()?
.any(|d| d.len() != hash.digest_size())
{
return Err(Error::BadSignature(
"Wrong number of bytes in certification subpacket".into())
.into());
}
self.hash_userid_approval(&mut hash, pk, userid)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_user_attribute_binding<P, Q, R>(&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()?.for_signature(self.version());
self.hash_user_attribute_binding(&mut hash, pk, ua)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_user_attribute_revocation<P, Q, R>(
&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()?.for_signature(self.version());
self.hash_user_attribute_binding(&mut hash, pk, ua)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_user_attribute_approval<P, Q, R>(
&self,
signer: &Key<P, R>,
pk: &Key<Q, key::PrimaryRole>,
ua: &UserAttribute)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
R: key::KeyRole,
{
let mut hash =
self.hash_algo().context()?.for_signature(self.version());
if self.approved_certifications()?
.any(|d| d.len() != hash.digest_size())
{
return Err(Error::BadSignature(
"Wrong number of bytes in certification subpacket".into())
.into());
}
self.hash_user_attribute_approval(&mut hash, pk, ua)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
pub fn verify_message<M, P, R>(&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()?.for_signature(self.version());
if let Some(salt) = self.salt() {
hash.update(salt);
}
hash.update(msg.as_ref());
self.hash(&mut hash)?;
self.verify_digest_internal(
signer.parts_as_public().role_as_unspecified(),
Some(hash.into_digest()?.into()))
}
}
impl From<Signature3> for Packet {
fn from(s: Signature3) -> Self {
Packet::Signature(s.into())
}
}
impl From<Signature3> for super::Signature {
fn from(s: Signature3) -> Self {
super::Signature::V3(s)
}
}
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: &mut Gen, depth: usize) -> Self {
match u8::arbitrary(g) % 3 {
0 => Signature3::arbitrary_bounded(g, depth).into(),
1 => Signature4::arbitrary_bounded(g, depth).into(),
2 => Signature6::arbitrary_bounded(g, depth).into(),
_ => unreachable!(),
}
}
}
#[cfg(test)]
impl_arbitrary_with_bound!(super::Signature);
#[cfg(test)]
impl ArbitraryBounded for Signature4 {
fn arbitrary_bounded(g: &mut Gen, 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),
},
Ed25519 => mpi::Signature::Ed25519 {
s: {
let mut s = [0; 64];
s.iter_mut().for_each(|p| *p = u8::arbitrary(g));
Box::new(s)
},
},
Ed448 => mpi::Signature::Ed448 {
s: {
let mut s = [0; 114];
s.iter_mut().for_each(|p| *p = u8::arbitrary(g));
Box::new(s)
},
},
ElGamalEncryptSign |
RSAEncrypt | ElGamalEncrypt | ECDH |
X25519 | X448 |
Private(_) | Unknown(_) => unreachable!(),
};
Signature4 {
common: Arbitrary::arbitrary(g),
fields,
digest_prefix: [Arbitrary::arbitrary(g),
Arbitrary::arbitrary(g)],
mpis,
computed_digest: OnceLock::new(),
level: 0,
additional_issuers: OnceLock::new(),
}
}
}
#[cfg(test)]
impl_arbitrary_with_bound!(Signature4);
#[cfg(test)]
impl ArbitraryBounded for Signature3 {
fn arbitrary_bounded(g: &mut Gen, _depth: usize) -> Self {
use mpi::{arbitrarize, MPI};
use PublicKeyAlgorithm::*;
let pk_algo = PublicKeyAlgorithm::arbitrary_for_signing(g);
#[allow(deprecated)]
let mpis = match 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),
},
Ed25519 => mpi::Signature::Ed25519 {
s: Box::new(arbitrarize(g, [0; 64])),
},
Ed448 => mpi::Signature::Ed448 {
s: Box::new(arbitrarize(g, [0; 114])),
},
_ => unreachable!(),
};
Signature3::new(
SignatureType::arbitrary(g),
Timestamp::arbitrary(g),
KeyID::arbitrary(g),
pk_algo,
HashAlgorithm::arbitrary(g),
[Arbitrary::arbitrary(g), Arbitrary::arbitrary(g)],
mpis)
}
}
#[cfg(test)]
impl_arbitrary_with_bound!(Signature3);
#[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, Key6};
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::{
PacketParserBuilder,
PacketParserResult,
PacketParser,
};
struct Test<'a> {
key: &'a str,
data: &'a str,
good: usize,
}
let tests = [
Test {
key: "neal.pgp",
data: "signed-1.pgp",
good: 1,
},
Test {
key: "neal.pgp",
data: "signed-1-sha1-neal.pgp",
good: 1,
},
Test {
key: "testy.pgp",
data: "signed-1-sha256-testy.pgp",
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.pgp",
good: 0,
},
Test {
key: "neal.pgp",
data: "signed-2-partial-body.pgp",
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();
if ! cert.keys().all(|ka| ka.key().pk_algo().is_supported()) {
eprintln!("Skipping because one algorithm is not supported");
continue;
}
if let Some(curve) = match cert.primary_key().key().mpis() {
mpi::PublicKey::EdDSA { curve, .. } => Some(curve),
mpi::PublicKey::ECDSA { curve, .. } => Some(curve),
_ => None,
} {
if ! curve.is_supported() {
eprintln!("Skipping because we don't support {}", curve);
continue;
}
}
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_document(cert.primary_key().key()).is_ok();
eprintln!(" Primary {:?}: {:?}",
cert.fingerprint(), result);
if result {
good += 1;
}
for sk in cert.subkeys() {
let result = sig.verify_document(sk.key()).is_ok();
eprintln!(" Subkey {:?}: {:?}",
sk.key().fingerprint(), result);
if result {
good += 1;
}
}
}
ppr = pp.recurse().unwrap().1;
}
assert_eq!(good, test.good, "Signature verification failed.");
let mut good = 0;
let mut ppr = PacketParserBuilder::from_bytes(
crate::tests::message(test.data)).unwrap()
.automatic_hashing(false)
.build().unwrap();
while let PacketParserResult::Some(mut pp) = ppr {
if let Packet::OnePassSig(_) = &pp.packet {
pp.start_hashing().unwrap();
}
if let Packet::Signature(sig) = &mut pp.packet {
let result = sig.verify_document(cert.primary_key().key()).is_ok();
eprintln!(" Primary {:?}: {:?}",
cert.fingerprint(), result);
if result {
good += 1;
}
for sk in cert.subkeys() {
let result = sig.verify_document(sk.key()).is_ok();
eprintln!(" Subkey {:?}: {:?}",
sk.key().fingerprint(), result);
if result {
good += 1;
}
}
}
ppr = pp.recurse().unwrap().1;
}
assert_eq!(good, test.good, "Signature verification with \
explicit hashing 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.digest_size().unwrap()];
crypto::random(&mut hash).unwrap();
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",
] {
eprintln!("{}...", key);
let cert = Cert::from_bytes(crate::tests::key(key)).unwrap();
if ! cert.primary_key().key().pk_algo().is_supported() {
eprintln!("Skipping because we don't support the algo");
continue;
}
if let Some(curve) = match cert.primary_key().key().mpis() {
mpi::PublicKey::EdDSA { curve, .. } => Some(curve),
mpi::PublicKey::ECDSA { curve, .. } => Some(curve),
_ => None,
} {
if ! curve.is_supported() {
eprintln!("Skipping because we don't support {}", curve);
continue;
}
}
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()
.for_signature(pair.public().version());
let sig = sig.sign_hash(&mut pair, hash).unwrap();
let mut hash = hash_algo.context().unwrap()
.for_signature(sig.version());
sig.hash(&mut hash).unwrap();
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::*;
for curve in vec![
Ed25519,
NistP256,
NistP384,
NistP521,
] {
if ! curve.is_supported() {
eprintln!("Skipping unsupported {:?}", curve);
continue;
}
let key: Key<key::SecretParts, key::PrimaryRole>
= Key6::generate_ecc(true, curve).unwrap().into();
let msg = b"Hello, World";
let mut pair = key.into_keypair().unwrap();
let 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 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]
#[allow(deprecated)]
fn verify_v3_sig() {
if ! PublicKeyAlgorithm::DSA.is_supported() {
return;
}
let cert = Cert::from_bytes(crate::tests::key(
"dennis-simon-anton-private.pgp")).unwrap();
let msg = crate::tests::manifesto();
let p = Packet::from_bytes(
crate::tests::message("a-cypherpunks-manifesto.txt.dennis-simon-anton-v3.sig"))
.unwrap();
let sig = if let Packet::Signature(s) = p {
assert_eq!(s.version(), 3);
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()
.for_signature(pair.public().version());
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()
.next()
.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).next().unwrap();
let cert = uid.certifications().next().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
= Key6::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()
.for_signature(pair.public().version());
hash.update(&msg[..]);
let fp =
Fingerprint::from_bytes(4, b"bbbbbbbbbbbbbbbbbbbb").unwrap();
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), 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().next().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
= Key6::generate_ecc(true, Curve::Ed25519).unwrap().into();
let mut pair = key.into_keypair().unwrap();
let sig = SignatureBuilder::new(SignatureType::Standalone)
.sign_standalone(&mut pair)
.unwrap();
sig.verify_standalone(pair.public()).unwrap();
}
#[test]
#[allow(deprecated)]
fn timestamp_signature() {
if ! PublicKeyAlgorithm::DSA.is_supported() {
eprintln!("Skipping test, algorithm is not supported.");
return;
}
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(sig) = p {
let mut hash = sig.hash_algo().context().unwrap()
.for_signature(sig.version());
sig.hash_timestamp(&mut hash).unwrap();
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
= Key6::generate_ecc(true, Curve::Ed25519).unwrap().into();
let mut pair = key.into_keypair().unwrap();
let 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.pgp",
"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(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().next().unwrap().self_signatures().count(), 1);
const TRIES: u64 = 5;
assert!(TRIES * 10 < SIG_BACKDATE_BY);
for i in 0..TRIES {
assert_eq!(alice.userids().next().unwrap().self_signatures().count(),
1 + i as usize);
let sig = alice.with_policy(p, None)?.userids().next().unwrap()
.binding_signature().clone();
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().next().unwrap().userid()) {
Ok(v) => v,
Err(e) => {
eprintln!("Failed to make {} signatures on top of \
the original one.", i);
return Err(e); },
};
alice = alice.insert_packets(new_sig.clone())?.0;
let sig = alice.with_policy(p, None)?.userids().next().unwrap()
.binding_signature();
assert_eq!(sig, &new_sig);
}
Ok(())
}
#[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().next().unwrap();
assert_eq!(subkey.self_signatures().count(), 1);
let sig = &subkey.self_signatures().next().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().next().unwrap();
assert!(sig.hashed_area().iter().all(|p| p.authenticated()));
assert!(sig.unhashed_area().iter().all(|p| p.authenticated()));
let sig = cert.bad_signatures().next().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 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
= Key6::generate_ecc(true, Curve::Ed25519)?.into();
let mut pair = key.into_keypair()?;
let msg = b"Hello, World";
let mut hash = HashAlgorithm::SHA256.context()?
.for_signature(pair.public().version());
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();
let keyid_issuers = merged.issuers().collect::<Vec<&KeyID>>();
assert_eq!(issuers.len(), 3);
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(keyid_issuers.contains(&&keyid));
assert!(keyid_issuers.contains(&&dummy));
let merged = malicious.clone().merge(sig.clone())?;
let issuers = merged.get_issuers();
let keyid_issuers = merged.issuers().collect::<Vec<_>>();
assert_eq!(issuers.len(), 3);
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(keyid_issuers.contains(&&keyid));
assert!(keyid_issuers.contains(&&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();
let keyid_issuers = merged.issuers().collect::<Vec<_>>();
assert_eq!(issuers.len(), 2);
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(keyid_issuers.contains(&&keyid));
let merged = malicious.clone().merge(sig.clone())?;
let issuers = merged.get_issuers();
let keyid_issuers = merged.issuers().collect::<Vec<_>>();
assert_eq!(issuers.len(), 2);
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(keyid_issuers.contains(&&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 verified = sig.clone();
verified.verify_hash(pair.public(), hash.clone())?;
let merged = verified.clone().merge(malicious.clone())?;
let issuers = merged.get_issuers();
let keyid_issuers = merged.issuers().collect::<Vec<_>>();
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(keyid_issuers.contains(&&keyid));
let merged = malicious.clone().merge(verified.clone())?;
let issuers = merged.get_issuers();
let keyid_issuers = merged.issuers().collect::<Vec<_>>();
assert!(issuers.contains(&KeyHandle::from(&fp)));
assert!(keyid_issuers.contains(&&keyid));
Ok(())
}
#[test]
fn issue_998() -> Result<()> {
let now_t = Timestamp::try_from(crate::now())?;
let now = SystemTime::from(now_t);
let hour = std::time::Duration::new(3600, 0);
let hour_t = crate::types::Duration::from(3600);
let past = now - 2 * hour;
let sig = SignatureBuilder::new(SignatureType::PositiveCertification)
.modify_hashed_area(|mut a| {
a.add(Subpacket::new(
SubpacketValue::SignatureCreationTime(now_t), true)?)?;
a.add(Subpacket::new(
SubpacketValue::SignatureExpirationTime(hour_t), true)?)?;
Ok(a)
})?;
let sig = sig.set_reference_time(now)?;
assert_eq!(sig.signature_expiration_time(), Some(now + hour));
let sig = sig.set_reference_time(past)?;
assert_eq!(sig.signature_expiration_time(), Some(now - hour));
Ok(())
}
}