use std::{
convert::TryFrom,
sync::OnceLock,
};
use dyn_clone::DynClone;
use crate::HashAlgorithm;
use crate::packet::Key;
use crate::packet::UserID;
use crate::packet::UserAttribute;
use crate::packet::key;
use crate::packet::key::{Key4, Key6};
use crate::packet::Signature;
use crate::packet::signature::{self, Signature3, Signature4, Signature6};
use crate::Error;
use crate::Result;
use crate::types::{SignatureType, Timestamp};
use std::fs::{File, OpenOptions};
use std::io::{self, Write};
const DUMP_HASHED_VALUES: Option<&str> = None;
const ASN1_OID_MD5: &[u8] = &[
0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x02, 0x05, 0x05, 0x00, 0x04, 0x10,
];
const ASN1_OID_RIPEMD160: &[u8] = &[
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, 0x01, 0x05,
0x00, 0x04, 0x14,
];
const ASN1_OID_SHA1: &[u8] = &[
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05,
0x00, 0x04, 0x14,
];
const ASN1_OID_SHA224: &[u8] = &[
0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C,
];
const ASN1_OID_SHA256: &[u8] = &[
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
];
const ASN1_OID_SHA384: &[u8] = &[
0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30,
];
const ASN1_OID_SHA512: &[u8] = &[
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40,
];
const ASN1_OID_SHA3_256: &[u8] = &[
0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20
];
const ASN1_OID_SHA3_512: &[u8] = &[
0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x0a, 0x05, 0x00, 0x04, 0x40
];
pub(crate) fn default_hashes() -> &'static [HashAlgorithm] {
static DEFAULT_HASHES: OnceLock<Vec<HashAlgorithm>> = OnceLock::new();
DEFAULT_HASHES.get_or_init(|| vec![
HashAlgorithm::default(),
HashAlgorithm::SHA512,
HashAlgorithm::SHA384,
HashAlgorithm::SHA256,
HashAlgorithm::SHA224,
HashAlgorithm::SHA1,
HashAlgorithm::RipeMD,
HashAlgorithm::MD5,
])
}
pub(crate) fn default_hashes_sorted() -> &'static [HashAlgorithm] {
static DEFAULT_HASHES: OnceLock<Vec<HashAlgorithm>> = OnceLock::new();
DEFAULT_HASHES.get_or_init(|| {
let mut hashes = default_hashes().to_vec();
hashes.sort();
hashes
})
}
pub(crate) trait Digest: DynClone + Write + Send + Sync {
fn update(&mut self, data: &[u8]);
fn digest(&mut self, digest: &mut [u8]) -> Result<()>;
}
dyn_clone::clone_trait_object!(Digest);
impl Digest for Box<dyn Digest> {
fn update(&mut self, data: &[u8]) {
self.as_mut().update(data)
}
fn digest(&mut self, digest: &mut [u8]) -> Result<()>{
self.as_mut().digest(digest)
}
}
#[derive(Clone)]
pub struct Context {
algo: HashAlgorithm,
for_signature: Option<u8>,
ctx: Box<dyn Digest>,
}
impl Context {
pub fn algo(&self) -> HashAlgorithm {
self.algo
}
pub fn digest_size(&self) -> usize {
self.algo.digest_size()
.expect("we only create Contexts for known hash algos")
}
pub fn update(&mut self, data: &[u8]) {
self.ctx.update(data)
}
pub fn digest(&mut self, digest: &mut [u8]) -> Result<()>{
self.ctx.digest(digest)
}
pub fn into_digest(mut self) -> Result<Vec<u8>>
where Self: std::marker::Sized
{
let mut digest = vec![0u8; self.digest_size()];
self.digest(&mut digest)?;
Ok(digest)
}
fn for_signature(&self) -> Option<u8> {
self.for_signature.clone()
}
}
impl io::Write for Context {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.ctx.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.ctx.flush()
}
}
pub struct Builder(Context);
impl Builder {
pub fn for_signature(self, version: u8) -> Context {
let mut ctx = self.0;
ctx.for_signature = Some(version);
ctx
}
pub fn for_digest(self) -> Context {
self.0
}
}
impl HashAlgorithm {
pub fn context(self) -> Result<Builder> {
if ! self.is_supported() {
return Err(Error::UnsupportedHashAlgorithm(self).into());
}
let mut hasher: Box<dyn Digest> = match self {
HashAlgorithm::SHA1 if ! cfg!(feature = "crypto-fuzzing") =>
Box::new(crate::crypto::backend::sha1cd::build()),
_ => self.new_hasher()?,
};
if let Some(prefix) = DUMP_HASHED_VALUES {
hasher = Box::new(HashDumper::new(hasher, prefix))
}
Ok(Builder(Context {
algo: self,
for_signature: None,
ctx: hasher,
}))
}
pub fn oid(self) -> Result<&'static [u8]> {
match self {
HashAlgorithm::SHA1 => Ok(ASN1_OID_SHA1),
HashAlgorithm::SHA224 => Ok(ASN1_OID_SHA224),
HashAlgorithm::SHA256 => Ok(ASN1_OID_SHA256),
HashAlgorithm::SHA384 => Ok(ASN1_OID_SHA384),
HashAlgorithm::SHA512 => Ok(ASN1_OID_SHA512),
HashAlgorithm::SHA3_256 => Ok(ASN1_OID_SHA3_256),
HashAlgorithm::SHA3_512 => Ok(ASN1_OID_SHA3_512),
HashAlgorithm::MD5 => Ok(ASN1_OID_MD5),
HashAlgorithm::RipeMD => Ok(ASN1_OID_RIPEMD160),
HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) =>
Err(crate::Error::UnsupportedHashAlgorithm(self).into()),
}
}
}
struct HashDumper {
hasher: Box<dyn Digest>,
sink: File,
filename: String,
written: usize,
}
impl HashDumper {
fn new(hasher: Box<dyn Digest>, prefix: &str) -> Self {
let mut n = 0;
let mut filename;
let sink = loop {
filename = format!("{}-{}", prefix, n);
match OpenOptions::new().write(true).create_new(true)
.open(&filename)
{
Ok(f) => break f,
Err(_) => n += 1,
}
};
eprintln!("HashDumper: Writing to {}...", &filename);
HashDumper {
hasher,
sink,
filename,
written: 0,
}
}
}
impl Clone for HashDumper {
fn clone(&self) -> HashDumper {
let prefix = DUMP_HASHED_VALUES
.expect("cloning a HashDumper but DUMP_HASHED_VALUES wasn't specified");
HashDumper::new(self.hasher.clone(), prefix)
}
}
impl Drop for HashDumper {
fn drop(&mut self) {
eprintln!("HashDumper: Wrote {} bytes to {}...", self.written,
self.filename);
}
}
impl Digest for HashDumper {
fn update(&mut self, data: &[u8]) {
self.hasher.update(data);
self.sink.write_all(data).unwrap();
self.written += data.len();
}
fn digest(&mut self, digest: &mut [u8]) -> Result<()> {
self.hasher.digest(digest)
}
}
impl io::Write for HashDumper {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.update(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
self.hasher.flush()
}
}
pub trait Hash {
fn hash(&self, hash: &mut Context) -> Result<()>;
}
impl Hash for UserID {
fn hash(&self, hash: &mut Context) -> Result<()> {
let len = self.value().len() as u32;
let mut header = [0; 5];
header[0] = 0xB4;
header[1..5].copy_from_slice(&len.to_be_bytes());
hash.update(&header);
hash.update(self.value());
Ok(())
}
}
impl Hash for UserAttribute {
fn hash(&self, hash: &mut Context) -> Result<()> {
let len = self.value().len() as u32;
let mut header = [0; 5];
header[0] = 0xD1;
header[1..5].copy_from_slice(&len.to_be_bytes());
hash.update(&header);
hash.update(self.value());
Ok(())
}
}
impl<P, R> Hash for Key<P, R>
where P: key::KeyParts,
R: key::KeyRole,
{
fn hash(&self, hash: &mut Context) -> Result<()> {
match self {
Key::V4(k) => k.hash(hash),
Key::V6(k) => k.hash(hash),
}
}
}
fn write_key_hash_header(header: &mut Vec<u8>,
public_len: usize,
ctx: &Context)
-> Result<()>
{
match ctx.for_signature() {
None => Err(crate::Error::InvalidOperation(
"cannot hash key without knowing the signature version"
.into()).into()),
Some(3) | Some(4) => {
header.push(0x99);
header.extend_from_slice(
&u16::try_from(public_len)?.to_be_bytes());
Ok(())
},
Some(6) => {
header.push(0x9b);
header.extend_from_slice(
&u32::try_from(public_len)?.to_be_bytes());
Ok(())
},
Some(n) => Err(crate::Error::InvalidOperation(format!(
"don't know how to hash key for v{} signatures", n)
).into()),
}
}
impl<P, R> Hash for Key4<P, R>
where P: key::KeyParts,
R: key::KeyRole,
{
fn hash(&self, hash: &mut Context) -> Result<()> {
use crate::serialize::MarshalInto;
let len = (9 - 3) + self.mpis().serialized_len();
let mut header: Vec<u8> = Vec::with_capacity(9 + 2);
write_key_hash_header(&mut header, len, hash)?;
header.push(4);
let creation_time: u32 = self.creation_time_raw().into();
header.extend_from_slice(&creation_time.to_be_bytes());
header.push(self.pk_algo().into());
hash.update(&header[..]);
self.mpis().hash(hash)?;
Ok(())
}
}
impl<P, R> Hash for Key6<P, R>
where P: key::KeyParts,
R: key::KeyRole,
{
fn hash(&self, hash: &mut Context) -> Result<()> {
use crate::serialize::MarshalInto;
let len = (15 - 5) + self.mpis().serialized_len();
let mut header: Vec<u8> = Vec::with_capacity(15);
write_key_hash_header(&mut header, len, hash)?;
header.push(6);
let creation_time: u32 = self.creation_time_raw().into();
header.extend_from_slice(&creation_time.to_be_bytes());
header.push(self.pk_algo().into());
header.extend_from_slice(
&(self.mpis().serialized_len() as u32).to_be_bytes());
hash.update(&header[..]);
self.mpis().hash(hash)?;
Ok(())
}
}
impl Hash for Signature {
fn hash(&self, hash: &mut Context) -> Result<()> {
match self {
Signature::V3(sig) => sig.hash(hash),
Signature::V4(sig) => sig.hash(hash),
Signature::V6(sig) => sig.hash(hash),
}
}
}
impl Hash for Signature3 {
fn hash(&self, hash: &mut Context) -> Result<()> {
Self::hash_fields(hash, self)
}
}
impl Signature3 {
fn hash_fields(hash: &mut Context, f: &signature::SignatureFields)
-> Result<()>
{
let mut buffer = [0u8; 5];
buffer[0] = u8::from(f.typ());
let creation_time: u32 =
Timestamp::try_from(
f.signature_creation_time()
.unwrap_or(std::time::UNIX_EPOCH))
.unwrap_or_else(|_| Timestamp::from(0))
.into();
buffer[1] = (creation_time >> 24) as u8;
buffer[2] = (creation_time >> 16) as u8;
buffer[3] = (creation_time >> 8) as u8;
buffer[4] = (creation_time ) as u8;
hash.update(&buffer[..]);
Ok(())
}
}
impl Hash for Signature4 {
fn hash(&self, hash: &mut Context) -> Result<()> {
Self::hash_fields(hash, &self.fields)
}
}
impl Signature4 {
fn hash_fields(mut hash: &mut Context, f: &signature::SignatureFields)
-> Result<()>
{
use crate::serialize::{Marshal, MarshalInto};
let mut header = [0u8; 6];
header[0] = 4;
header[1] = f.typ().into();
header[2] = f.pk_algo().into();
header[3] = f.hash_algo().into();
let hashed_area_len = f.hashed_area().serialized_len();
header[4..6].copy_from_slice(&(hashed_area_len as u16).to_be_bytes());
hash.update(&header[..]);
f.hashed_area().serialize(&mut hash as &mut dyn Write)?;
let mut trailer = [0u8; 6];
trailer[0] = 4;
trailer[1] = 0xff;
let len = (header.len() + hashed_area_len) as u32;
trailer[2..6].copy_from_slice(&len.to_be_bytes());
hash.update(&trailer[..]);
Ok(())
}
}
impl Hash for Signature6 {
fn hash(&self, hash: &mut Context) -> Result<()> {
Self::hash_fields(hash, &self.fields)
}
}
impl Signature6 {
fn hash_fields(mut hash: &mut Context, sig: &signature::SignatureFields)
-> Result<()>
{
use crate::serialize::{Marshal, MarshalInto};
let mut header = [0u8; 8];
header[0] = 6;
header[1] = sig.typ().into();
header[2] = sig.pk_algo().into();
header[3] = sig.hash_algo().into();
let hashed_area_len = sig.hashed_area().serialized_len();
header[4..8].copy_from_slice(&(hashed_area_len as u32).to_be_bytes());
hash.update(&header[..]);
sig.hashed_area().serialize(&mut hash as &mut dyn Write)?;
let mut trailer = [0u8; 6];
trailer[0] = 6;
trailer[1] = 0xff;
let len = (header.len() + hashed_area_len) as u32;
trailer[2..6].copy_from_slice(&len.to_be_bytes());
hash.update(&trailer[..]);
Ok(())
}
}
impl Hash for signature::SignatureFields {
fn hash(&self, hash: &mut Context) -> Result<()> {
match self.version() {
3 => Signature3::hash_fields(hash, self),
4 => Signature4::hash_fields(hash, self),
6 => Signature6::hash_fields(hash, self),
n => Err(Error::InvalidOperation(format!(
"cannot hash a version {} signature packet", n)
).into()),
}
}
}
impl Hash for signature::SignatureBuilder {
fn hash(&self, hash: &mut Context) -> Result<()> {
match self.sb_version {
signature::SBVersion::V4 {} =>
Signature4::hash_fields(hash, &self.fields),
signature::SBVersion::V6 { .. } =>
Signature6::hash_fields(hash, &self.fields),
}
}
}
impl signature::SignatureBuilder {
pub fn hash_standalone(&self, hash: &mut Context)
-> Result<()>
{
match self.typ() {
SignatureType::Standalone => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.prefix_salt() {
hash.update(salt);
}
self.hash(hash)?;
Ok(())
}
pub fn hash_timestamp(&self, hash: &mut Context)
-> Result<()>
{
match self.typ() {
SignatureType::Timestamp => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.prefix_salt() {
hash.update(salt);
}
self.hash(hash)?;
Ok(())
}
pub fn hash_direct_key<P>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>)
-> Result<()>
where P: key::KeyParts,
{
match self.typ() {
SignatureType::DirectKey => (),
SignatureType::KeyRevocation => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.prefix_salt() {
hash.update(salt);
}
key.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_subkey_binding<P, Q>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
subkey: &Key<Q, key::SubordinateRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
{
match self.typ() {
SignatureType::SubkeyBinding => (),
SignatureType::SubkeyRevocation => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.prefix_salt() {
hash.update(salt);
}
key.hash(hash)?;
subkey.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_primary_key_binding<P, Q>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
subkey: &Key<Q, key::SubordinateRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
{
match self.typ() {
SignatureType::PrimaryKeyBinding => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.prefix_salt() {
hash.update(salt);
}
key.hash(hash)?;
subkey.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_userid_binding<P>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
userid: &UserID)
-> Result<()>
where P: key::KeyParts,
{
match self.typ() {
SignatureType::GenericCertification => (),
SignatureType::PersonaCertification => (),
SignatureType::CasualCertification => (),
SignatureType::PositiveCertification => (),
SignatureType::CertificationRevocation => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.prefix_salt() {
hash.update(salt);
}
key.hash(hash)?;
userid.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_user_attribute_binding<P>(
&self,
hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
ua: &UserAttribute)
-> Result<()>
where P: key::KeyParts,
{
match self.typ() {
SignatureType::GenericCertification => (),
SignatureType::PersonaCertification => (),
SignatureType::CasualCertification => (),
SignatureType::PositiveCertification => (),
SignatureType::CertificationRevocation => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.prefix_salt() {
hash.update(salt);
}
key.hash(hash)?;
ua.hash(hash)?;
self.hash(hash)?;
Ok(())
}
}
impl Signature {
pub fn hash_standalone(&self, hash: &mut Context)
-> Result<()>
{
match self.typ() {
SignatureType::Standalone => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
self.hash(hash)?;
Ok(())
}
pub fn hash_timestamp(&self, hash: &mut Context)
-> Result<()>
{
match self.typ() {
SignatureType::Timestamp => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
self.hash(hash)?;
Ok(())
}
pub fn hash_direct_key<P>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>)
-> Result<()>
where P: key::KeyParts,
{
match self.typ() {
SignatureType::DirectKey => (),
SignatureType::KeyRevocation => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
key.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_subkey_binding<P, Q>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
subkey: &Key<Q, key::SubordinateRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
{
match self.typ() {
SignatureType::SubkeyBinding => (),
SignatureType::SubkeyRevocation => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
key.hash(hash)?;
subkey.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_primary_key_binding<P, Q>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
subkey: &Key<Q, key::SubordinateRole>)
-> Result<()>
where P: key::KeyParts,
Q: key::KeyParts,
{
match self.typ() {
SignatureType::PrimaryKeyBinding => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
key.hash(hash)?;
subkey.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_userid_binding<P>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
userid: &UserID)
-> Result<()>
where P: key::KeyParts,
{
match self.typ() {
SignatureType::GenericCertification => (),
SignatureType::PersonaCertification => (),
SignatureType::CasualCertification => (),
SignatureType::PositiveCertification => (),
SignatureType::CertificationRevocation => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
key.hash(hash)?;
userid.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_user_attribute_binding<P>(
&self,
hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
ua: &UserAttribute)
-> Result<()>
where P: key::KeyParts,
{
match self.typ() {
SignatureType::GenericCertification => (),
SignatureType::PersonaCertification => (),
SignatureType::CasualCertification => (),
SignatureType::PositiveCertification => (),
SignatureType::CertificationRevocation => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
key.hash(hash)?;
ua.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_userid_approval<P>(&self, hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
userid: &UserID)
-> Result<()>
where P: key::KeyParts,
{
match self.typ() {
SignatureType::CertificationApproval => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
key.hash(hash)?;
userid.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_user_attribute_approval<P>(
&self,
hash: &mut Context,
key: &Key<P, key::PrimaryRole>,
ua: &UserAttribute)
-> Result<()>
where P: key::KeyParts,
{
match self.typ() {
SignatureType::CertificationApproval => (),
_ => return Err(Error::UnsupportedSignatureType(self.typ()).into()),
}
if let Some(salt) = self.salt() {
hash.update(salt);
}
key.hash(hash)?;
ua.hash(hash)?;
self.hash(hash)?;
Ok(())
}
pub fn hash_for_confirmation(&self, hash: &mut Context)
-> Result<()>
{
match self {
Signature::V3(s) => s.hash_for_confirmation(hash),
Signature::V4(s) => s.hash_for_confirmation(hash),
Signature::V6(s) => s.hash_for_confirmation(hash),
}
}
}
impl Signature4 {
pub fn hash_for_confirmation(&self, hash: &mut Context)
-> Result<()>
{
use crate::serialize::{Marshal, MarshalInto};
let mut body = vec![
self.version(),
self.typ().into(),
self.pk_algo().into(),
self.hash_algo().into(),
];
let l = self.hashed_area().serialized_len()
.min(std::u16::MAX as usize);
body.extend(&(l as u16).to_be_bytes());
self.hashed_area().serialize(&mut body)?;
body.extend(&[0, 0]);
body.extend(self.digest_prefix());
self.mpis().serialize(&mut body)?;
hash.update(&[0x88]);
hash.update(&(body.len() as u32).to_be_bytes());
hash.update(&body);
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::Cert;
use crate::parse::Parse;
#[test]
fn hash_verification() {
fn check(cert: Cert) -> (usize, usize, usize) {
let mut userid_sigs = 0;
for (i, binding) in cert.userids().enumerate() {
for selfsig in binding.self_signatures() {
let mut hash =
selfsig.hash_algo().context().unwrap()
.for_signature(selfsig.version());
selfsig.hash_userid_binding(
&mut hash,
cert.primary_key().key(),
binding.userid()).unwrap();
let h = hash.into_digest().unwrap();
if &h[..2] != selfsig.digest_prefix() {
eprintln!("{:?}: {:?} / {:?}",
i, binding.userid(), selfsig);
eprintln!(" Hash: {:?}", h);
}
assert_eq!(&h[..2], selfsig.digest_prefix());
userid_sigs += 1;
}
}
let mut ua_sigs = 0;
for (i, a) in cert.user_attributes().enumerate()
{
for selfsig in a.self_signatures() {
let mut hash =
selfsig.hash_algo().context().unwrap()
.for_signature(selfsig.version());
selfsig.hash_user_attribute_binding(
&mut hash,
cert.primary_key().key(),
a.user_attribute()).unwrap();
let h = hash.into_digest().unwrap();
if &h[..2] != selfsig.digest_prefix() {
eprintln!("{:?}: {:?} / {:?}",
i, a.user_attribute(), selfsig);
eprintln!(" Hash: {:?}", h);
}
assert_eq!(&h[..2], selfsig.digest_prefix());
ua_sigs += 1;
}
}
let mut subkey_sigs = 0;
for (i, binding) in cert.subkeys().enumerate() {
for selfsig in binding.self_signatures() {
let mut hash =
selfsig.hash_algo().context().unwrap()
.for_signature(selfsig.version());
selfsig.hash_subkey_binding(
&mut hash,
cert.primary_key().key(),
binding.key()).unwrap();
let h = hash.into_digest().unwrap();
if &h[..2] != selfsig.digest_prefix() {
eprintln!("{:?}: {:?}", i, binding);
eprintln!(" Hash: {:?}", h);
}
assert_eq!(h[0], selfsig.digest_prefix()[0]);
assert_eq!(h[1], selfsig.digest_prefix()[1]);
subkey_sigs += 1;
}
}
(userid_sigs, ua_sigs, subkey_sigs)
}
check(Cert::from_bytes(crate::tests::key("hash-algos/MD5.pgp")).unwrap());
check(Cert::from_bytes(crate::tests::key("hash-algos/RipeMD160.pgp")).unwrap());
check(Cert::from_bytes(crate::tests::key("hash-algos/SHA1.pgp")).unwrap());
check(Cert::from_bytes(crate::tests::key("hash-algos/SHA224.pgp")).unwrap());
check(Cert::from_bytes(crate::tests::key("hash-algos/SHA256.pgp")).unwrap());
check(Cert::from_bytes(crate::tests::key("hash-algos/SHA384.pgp")).unwrap());
check(Cert::from_bytes(crate::tests::key("hash-algos/SHA512.pgp")).unwrap());
check(Cert::from_bytes(crate::tests::key("bannon-all-uids-subkeys.pgp")).unwrap());
let (_userid_sigs, ua_sigs, _subkey_sigs)
= check(Cert::from_bytes(crate::tests::key("dkg.pgp")).unwrap());
assert!(ua_sigs > 0);
}
}