use std::fmt;
use std::ops::{Deref, DerefMut};
use crate::Error;
use crate::Result;
use crate::crypto::{
mpis,
hash::{self, Hash},
Signer,
};
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::{
SubpacketArea,
SubpacketAreas,
};
pub mod subpacket;
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct Builder {
version: u8,
typ: SignatureType,
pk_algo: PublicKeyAlgorithm,
hash_algo: HashAlgorithm,
subpackets: SubpacketAreas,
}
impl Deref for Builder {
type Target = SubpacketAreas;
fn deref(&self) -> &Self::Target {
&self.subpackets
}
}
impl DerefMut for Builder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.subpackets
}
}
impl Builder {
pub fn new(typ: SignatureType) -> Self {
Builder {
version: 4,
typ: typ,
pk_algo: PublicKeyAlgorithm::Unknown(0),
hash_algo: HashAlgorithm::default(),
subpackets: SubpacketAreas::default(),
}
.set_signature_creation_time(
std::time::SystemTime::now())
.expect("area is empty, insertion cannot fail; \
time is representable for the foreseeable future")
}
pub fn version(&self) -> u8 {
self.version
}
pub fn typ(&self) -> SignatureType {
self.typ
}
pub fn set_type(mut self, t: SignatureType) -> Self {
self.typ = t;
self
}
pub fn pk_algo(&self) -> PublicKeyAlgorithm {
self.pk_algo
}
pub fn hash_algo(&self) -> HashAlgorithm {
self.hash_algo
}
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.pk_algo = signer.public().pk_algo();
let digest = Signature::hash_standalone(&self)?;
self.sign(signer, digest)
}
pub fn sign_timestamp(mut self, signer: &mut dyn Signer)
-> Result<Signature>
{
self.pk_algo = signer.public().pk_algo();
let digest = Signature::hash_timestamp(&self)?;
self.sign(signer, digest)
}
pub fn sign_direct_key(mut self, signer: &mut dyn Signer)
-> Result<Signature>
{
self.pk_algo = signer.public().pk_algo();
let digest =
Signature::hash_direct_key(&self,
signer.public()
.mark_role_primary_ref())?;
self.sign(signer, digest)
}
pub fn sign_userid_binding<P>(mut self, signer: &mut dyn Signer,
key: &Key<P, key::PrimaryRole>,
userid: &UserID)
-> Result<Signature>
where P: key::KeyParts,
{
self.pk_algo = signer.public().pk_algo();
let digest = Signature::hash_userid_binding(&self, key, userid)?;
self.sign(signer, digest)
}
pub fn sign_subkey_binding<P, Q>(mut self, signer: &mut dyn Signer,
primary: &Key<P, key::PrimaryRole>,
subkey: &Key<Q, key::SubordinateRole>)
-> Result<Signature>
where P: key:: KeyParts,
Q: key:: KeyParts,
{
self.pk_algo = signer.public().pk_algo();
let digest = Signature::hash_subkey_binding(&self, primary, subkey)?;
self.sign(signer, 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.pk_algo = subkey_signer.public().pk_algo();
let digest =
Signature::hash_primary_key_binding(&self, primary, subkey)?;
self.sign(subkey_signer, digest)
}
pub fn sign_user_attribute_binding<P>(mut self, signer: &mut dyn Signer,
key: &Key<P, key::PrimaryRole>,
ua: &UserAttribute)
-> Result<Signature>
where P: key::KeyParts,
{
self.pk_algo = signer.public().pk_algo();
let digest =
Signature::hash_user_attribute_binding(&self, key, ua)?;
self.sign(signer, digest)
}
pub fn sign_hash(mut self, signer: &mut dyn Signer,
mut hash: hash::Context)
-> Result<Signature>
{
self.pk_algo = signer.public().pk_algo();
self.hash_algo = hash.algo();
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]>
{
let mut hash = self.hash_algo.context()?;
hash.update(msg.as_ref());
self.pk_algo = signer.public().pk_algo();
self.hash(&mut hash);
let mut digest = vec![0u8; hash.digest_size()];
hash.digest(&mut digest);
self.sign(signer, digest)
}
fn sign(self, signer: &mut dyn Signer, digest: Vec<u8>)
-> Result<Signature>
{
let algo = self.hash_algo;
let mpis = signer.sign(algo, &digest)?;
Ok(Signature4 {
common: Default::default(),
fields: self,
digest_prefix: [digest[0], digest[1]],
mpis: mpis,
computed_digest: Some(digest),
level: 0,
}.into())
}
}
impl From<Signature> for Builder {
fn from(sig: Signature) -> Self {
match sig {
Signature::V4(sig) => sig.into(),
Signature::__Nonexhaustive => unreachable!(),
}
}
}
impl From<Signature4> for Builder {
fn from(sig: Signature4) -> Self {
sig.fields
}
}
impl<'a> From<&'a Signature> for &'a Builder {
fn from(sig: &'a Signature) -> Self {
match sig {
Signature::V4(ref sig) => sig.into(),
Signature::__Nonexhaustive => unreachable!(),
}
}
}
impl<'a> From<&'a Signature4> for &'a Builder {
fn from(sig: &'a Signature4) -> Self {
&sig.fields
}
}
#[derive(Clone)]
pub struct Signature4 {
pub(crate) common: packet::Common,
pub(crate) fields: Builder,
digest_prefix: [u8; 2],
mpis: mpis::Signature,
computed_digest: Option<Vec<u8>>,
level: usize,
}
impl fmt::Debug for Signature4 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let issuer = if let Some(tmp) = self.issuer_fingerprint() {
tmp.to_string()
} else if let Some(tmp) = self.issuer() {
tmp.to_string()
} else {
"Unknown".to_string()
};
f.debug_struct("Signature4")
.field("version", &self.version())
.field("typ", &self.typ())
.field("issuer", &issuer)
.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("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.mpis == other.mpis
&& self.fields.version == other.fields.version
&& self.fields.typ == other.fields.typ
&& self.fields.pk_algo == other.fields.pk_algo
&& self.fields.hash_algo == other.fields.hash_algo
&& self.fields.hashed_area() == other.fields.hashed_area()
}
}
impl Eq for Signature4 {}
impl std::hash::Hash for Signature4 {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use std::hash::Hash as StdHash;
self.fields.version.hash(state);
self.fields.typ.hash(state);
self.fields.pk_algo.hash(state);
self.fields.hash_algo.hash(state);
self.fields.hashed_area().hash(state);
StdHash::hash(&self.mpis, 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: mpis::Signature) -> Self {
Signature4 {
common: Default::default(),
fields: Builder {
version: 4,
typ: typ,
pk_algo: pk_algo,
hash_algo: hash_algo,
subpackets: SubpacketAreas::new(hashed_area, unhashed_area),
},
digest_prefix: digest_prefix,
mpis: mpis,
computed_digest: None,
level: 0,
}
}
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) -> &mpis::Signature {
&self.mpis
}
#[allow(dead_code)]
pub(crate) fn set_mpis(&mut self, mpis: mpis::Signature) -> mpis::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)
}
}
impl crate::packet::Signature {
pub fn get_issuers(&self) -> Vec<crate::KeyHandle> {
use crate::packet::signature::subpacket:: SubpacketValue;
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()
}
pub fn normalize(&self) -> Self {
use crate::packet::signature::subpacket::{Subpacket, SubpacketTag,
SubpacketValue};
let mut sig = self.clone();
{
let area = sig.unhashed_area_mut();
area.clear();
if let Some(issuer) = self.issuer_fingerprint() {
area.add(Subpacket::new(
SubpacketValue::Issuer(issuer.into()), false).unwrap())
.unwrap();
} else if let Some(issuer) = self.issuer() {
area.add(Subpacket::new(
SubpacketValue::Issuer(issuer.clone()), false).unwrap())
.unwrap();
}
if let Some(embedded_sig) =
self.unhashed_area().iter().find_map(|v| {
if v.tag() == SubpacketTag::EmbeddedSignature {
Some(v)
} else {
None
}
})
{
area.add(embedded_sig.clone()).unwrap();
}
}
sig
}
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]>,
{
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());
}
key.verify(self, digest.as_ref())
}
pub fn verify<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());
}
if let Some(ref hash) = self.computed_digest {
self.verify_digest(key, hash)
} else {
Err(Error::BadSignature("Hash not computed.".to_string()).into())
}
}
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 digest = Signature::hash_standalone(self)?;
self.verify_digest(key, &digest[..])
}
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 digest = Signature::hash_timestamp(self)?;
self.verify_digest(key, &digest[..])
}
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 hash = Signature::hash_direct_key(self, pk)?;
self.verify_digest(signer, &hash[..])
}
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 hash = Signature::hash_direct_key(self, pk)?;
self.verify_digest(signer, &hash[..])
}
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 hash = Signature::hash_subkey_binding(self, pk, subkey)?;
self.verify_digest(signer, &hash[..])?;
if self.key_flags().map(|kf| kf.for_signing()).unwrap_or(false) {
if let Some(backsig) = self.embedded_signature() {
backsig.verify_primary_key_binding(pk, subkey)
} else {
Err(Error::BadSignature(
"Primary key binding signature missing".into()).into())
}
} 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 hash = Signature::hash_primary_key_binding(self, pk, subkey)?;
self.verify_digest(subkey, &hash[..])
}
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 hash = Signature::hash_subkey_binding(self, pk, subkey)?;
self.verify_digest(signer, &hash[..])
}
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 hash = Signature::hash_userid_binding(self, pk, userid)?;
self.verify_digest(signer, &hash[..])
}
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 hash = Signature::hash_userid_binding(self, pk, userid)?;
self.verify_digest(signer, &hash[..])
}
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 hash = Signature::hash_user_attribute_binding(self, pk, ua)?;
self.verify_digest(signer, &hash[..])
}
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 hash = Signature::hash_user_attribute_binding(self, pk, ua)?;
self.verify_digest(signer, &hash[..])
}
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()?;
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)]
mod test {
use super::*;
use crate::KeyID;
use crate::crypto;
use crate::crypto::mpis::MPI;
use crate::Cert;
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(pp) = ppr {
if let Packet::Signature(ref sig) = 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()
.mark_parts_secret().unwrap()
.into_keypair()
.expect("secret key is encrypted/missing");
let sig = Builder::new(SignatureType::Binary);
let hash = hash_algo.context().unwrap();
let 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);
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 sig = Builder::new(SignatureType::Binary)
.set_signature_creation_time(
std::time::SystemTime::now()).unwrap()
.set_issuer_fingerprint(pair.public().fingerprint()).unwrap()
.set_issuer(pair.public().keyid()).unwrap()
.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]
fn sign_with_short_ed25519_secret_key() {
let sec = [
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 mut pnt = [0x40u8; nettle::ed25519::ED25519_KEY_SIZE + 1];
nettle::ed25519::public_key(&mut pnt[1..], &sec[..]).unwrap();
let public_mpis = mpis::PublicKey::EdDSA {
curve: Curve::Ed25519,
q: MPI::new(&pnt[..]),
};
let private_mpis = mpis::SecretKeyMaterial::EdDSA {
scalar: MPI::new(&sec[..]).into(),
};
let key : key::SecretKey
= Key4::with_secret(std::time::SystemTime::now(),
PublicKeyAlgorithm::EdDSA,
public_mpis, private_mpis.into())
.unwrap()
.into();
let mut pair = key.into_keypair().unwrap();
let msg = b"Hello, World";
let mut hash = HashAlgorithm::SHA256.context().unwrap();
hash.update(&msg[..]);
Builder::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_binding =
test2.userids().with_policy(p, None).nth(0).unwrap().bundle();
let cert = &uid_binding.certifications()[0];
cert.verify_userid_binding(cert_key1,
test2.primary_key().key(),
uid_binding.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 = Builder::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();
let embedded_sig = Builder::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(), 2);
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);
let sig = Builder::new(SignatureType::Text)
.set_issuer_fingerprint(fp).unwrap()
.sign_hash(&mut pair,
hash.clone()).unwrap().normalize();
assert_eq!(sig.unhashed_area().iter().count(), 1);
assert_eq!(*sig.unhashed_area().iter().nth(0).unwrap(),
Subpacket::new(SubpacketValue::Issuer(keyid.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 sig = Builder::new(SignatureType::Standalone)
.set_signature_creation_time(
std::time::SystemTime::now()).unwrap()
.set_issuer_fingerprint(pair.public().fingerprint()).unwrap()
.set_issuer(pair.public().keyid()).unwrap()
.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(sig) = p {
let digest = Signature::hash_standalone(&sig).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 sig = Builder::new(SignatureType::Timestamp)
.set_signature_creation_time(
std::time::SystemTime::now()).unwrap()
.set_issuer_fingerprint(pair.public().fingerprint()).unwrap()
.set_issuer(pair.public().keyid()).unwrap()
.sign_timestamp(&mut pair)
.unwrap();
sig.verify_timestamp(pair.public()).unwrap();
}
}