use std::cell::RefCell;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
use std::fmt;
use std::io;
use time;
use quickcheck::{Arbitrary, Gen};
use buffered_reader::BufferedReader;
use {
Error,
Result,
packet::Signature,
packet::signature::{self, Signature4},
packet::Features,
packet::KeyFlags,
packet::KeyServerPreferences,
Packet,
Fingerprint,
packet::Key,
KeyID,
};
use constants::{
AEADAlgorithm,
CompressionAlgorithm,
HashAlgorithm,
PublicKeyAlgorithm,
ReasonForRevocation,
SymmetricAlgorithm,
};
use conversions::{
Time,
Duration,
};
#[derive(Debug)]
#[derive(PartialEq, Eq, Hash)]
#[derive(Clone, Copy)]
#[allow(missing_docs)]
pub enum SubpacketTag {
SignatureCreationTime,
SignatureExpirationTime,
ExportableCertification,
TrustSignature,
RegularExpression,
Revocable,
KeyExpirationTime,
PlaceholderForBackwardCompatibility,
PreferredSymmetricAlgorithms,
RevocationKey,
Issuer,
NotationData,
PreferredHashAlgorithms,
PreferredCompressionAlgorithms,
KeyServerPreferences,
PreferredKeyServer,
PrimaryUserID,
PolicyURI,
KeyFlags,
SignersUserID,
ReasonForRevocation,
Features,
SignatureTarget,
EmbeddedSignature,
IssuerFingerprint,
PreferredAEADAlgorithms,
IntendedRecipient,
Reserved(u8),
Private(u8),
Unknown(u8),
}
impl From<u8> for SubpacketTag {
fn from(u: u8) -> Self {
match u {
2 => SubpacketTag::SignatureCreationTime,
3 => SubpacketTag::SignatureExpirationTime,
4 => SubpacketTag::ExportableCertification,
5 => SubpacketTag::TrustSignature,
6 => SubpacketTag::RegularExpression,
7 => SubpacketTag::Revocable,
9 => SubpacketTag::KeyExpirationTime,
10 => SubpacketTag::PlaceholderForBackwardCompatibility,
11 => SubpacketTag::PreferredSymmetricAlgorithms,
12 => SubpacketTag::RevocationKey,
16 => SubpacketTag::Issuer,
20 => SubpacketTag::NotationData,
21 => SubpacketTag::PreferredHashAlgorithms,
22 => SubpacketTag::PreferredCompressionAlgorithms,
23 => SubpacketTag::KeyServerPreferences,
24 => SubpacketTag::PreferredKeyServer,
25 => SubpacketTag::PrimaryUserID,
26 => SubpacketTag::PolicyURI,
27 => SubpacketTag::KeyFlags,
28 => SubpacketTag::SignersUserID,
29 => SubpacketTag::ReasonForRevocation,
30 => SubpacketTag::Features,
31 => SubpacketTag::SignatureTarget,
32 => SubpacketTag::EmbeddedSignature,
33 => SubpacketTag::IssuerFingerprint,
34 => SubpacketTag::PreferredAEADAlgorithms,
35 => SubpacketTag::IntendedRecipient,
0| 1| 8| 13| 14| 15| 17| 18| 19 => SubpacketTag::Reserved(u),
100...110 => SubpacketTag::Private(u),
_ => SubpacketTag::Unknown(u),
}
}
}
impl From<SubpacketTag> for u8 {
fn from(t: SubpacketTag) -> Self {
match t {
SubpacketTag::SignatureCreationTime => 2,
SubpacketTag::SignatureExpirationTime => 3,
SubpacketTag::ExportableCertification => 4,
SubpacketTag::TrustSignature => 5,
SubpacketTag::RegularExpression => 6,
SubpacketTag::Revocable => 7,
SubpacketTag::KeyExpirationTime => 9,
SubpacketTag::PlaceholderForBackwardCompatibility => 10,
SubpacketTag::PreferredSymmetricAlgorithms => 11,
SubpacketTag::RevocationKey => 12,
SubpacketTag::Issuer => 16,
SubpacketTag::NotationData => 20,
SubpacketTag::PreferredHashAlgorithms => 21,
SubpacketTag::PreferredCompressionAlgorithms => 22,
SubpacketTag::KeyServerPreferences => 23,
SubpacketTag::PreferredKeyServer => 24,
SubpacketTag::PrimaryUserID => 25,
SubpacketTag::PolicyURI => 26,
SubpacketTag::KeyFlags => 27,
SubpacketTag::SignersUserID => 28,
SubpacketTag::ReasonForRevocation => 29,
SubpacketTag::Features => 30,
SubpacketTag::SignatureTarget => 31,
SubpacketTag::EmbeddedSignature => 32,
SubpacketTag::IssuerFingerprint => 33,
SubpacketTag::PreferredAEADAlgorithms => 34,
SubpacketTag::IntendedRecipient => 35,
SubpacketTag::Reserved(u) => u,
SubpacketTag::Private(u) => u,
SubpacketTag::Unknown(u) => u,
}
}
}
impl Arbitrary for SubpacketTag {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
u8::arbitrary(g).into()
}
}
#[cfg(test)]
mod tests {
use super::*;
quickcheck! {
fn roundtrip(tag: SubpacketTag) -> bool {
let val: u8 = tag.clone().into();
tag == SubpacketTag::from(val)
}
}
quickcheck! {
fn parse(tag: SubpacketTag) -> bool {
match tag {
SubpacketTag::Reserved(u) =>
(u == 0 || u == 1 || u == 8
|| u == 13 || u == 14 || u == 15
|| u == 17 || u == 18 || u == 19),
SubpacketTag::Private(u) => u >= 100 && u <= 110,
SubpacketTag::Unknown(u) => (u > 33 && u < 100) || u > 110,
_ => true
}
}
}
}
struct SubpacketRaw<'a> {
pub critical: bool,
pub tag: SubpacketTag,
pub value: &'a [u8],
}
impl<'a> fmt::Debug for SubpacketRaw<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = if self.value.len() > 16 {
&self.value[..16]
} else {
self.value
};
f.debug_struct("SubpacketRaw")
.field("critical", &self.critical)
.field("tag", &self.tag)
.field(&format!("value ({} bytes)", self.value.len())[..],
&value)
.finish()
}
}
#[derive(Clone, Eq)]
pub struct SubpacketArea {
pub data: Vec<u8>,
parsed: RefCell<Option<HashMap<SubpacketTag, (bool, u16, u16)>>>,
}
impl PartialEq for SubpacketArea {
fn eq(&self, other: &SubpacketArea) -> bool {
self.data == other.data
}
}
impl Hash for SubpacketArea {
fn hash<H: Hasher>(&self, state: &mut H) {
self.data.hash(state);
}
}
struct SubpacketAreaIterRaw<'a> {
reader: buffered_reader::Memory<'a, ()>,
data: &'a [u8],
}
impl<'a> Iterator for SubpacketAreaIterRaw<'a> {
type Item = (usize, usize, SubpacketRaw<'a>);
fn next(&mut self) -> Option<Self::Item> {
let len = SubpacketLength::parse(&mut self.reader);
if len.is_err() {
return None;
}
let len = len.unwrap() as usize;
if self.reader.data(len).unwrap().len() < len {
self.reader.drop_eof().unwrap();
eprintln!("Invalid subpacket: subpacket extends beyond \
end of hashed area ({} bytes, but {} bytes left).",
len, self.reader.data(0).unwrap().len());
return None;
}
if len == 0 {
return self.next();
}
let tag = if let Ok(tag) = self.reader.data_consume_hard(1) {
tag[0]
} else {
return None;
};
let len = len - 1;
let critical = tag & (1 << 7) != 0;
let tag = tag & !(1 << 7);
let start = self.reader.total_out();
assert!(start <= ::std::u16::MAX as usize);
assert!(len <= ::std::u16::MAX as usize);
let _ = self.reader.consume(len);
Some((start, len,
SubpacketRaw {
critical: critical,
tag: tag.into(),
value: &self.data[start..start + len],
}))
}
}
impl SubpacketArea {
fn iter_raw(&self) -> SubpacketAreaIterRaw {
SubpacketAreaIterRaw {
reader: buffered_reader::Memory::new(&self.data[..]),
data: &self.data[..],
}
}
}
impl fmt::Debug for SubpacketArea {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(
self.iter_raw().map(|(_start, _len, sb)| {
Subpacket::from(sb)
}))
.finish()
}
}
pub struct Iter<'a> {
inner: SubpacketAreaIterRaw<'a>,
}
impl<'a> Iterator for Iter<'a> {
type Item = (usize, usize, Subpacket<'a>);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
.map(|(start, len, raw)| (start, len, raw.into()))
}
}
impl<'a> IntoIterator for &'a SubpacketArea {
type Item = (usize, usize, Subpacket<'a>);
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> FromIterator<(usize, usize, Subpacket<'a>)> for SubpacketArea {
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item=(usize, usize, Subpacket<'a>)>
{
use serialize::Serialize;
let mut data = Vec::new();
iter.into_iter().for_each(|(_, _, s)| s.serialize(&mut data).unwrap());
Self::new(data)
}
}
impl SubpacketArea {
pub fn new(data: Vec<u8>) -> SubpacketArea {
SubpacketArea { data: data, parsed: RefCell::new(None) }
}
pub fn empty() -> SubpacketArea {
SubpacketArea::new(Vec::new())
}
}
impl SubpacketArea {
fn cache_init(&self) {
if self.parsed.borrow().is_none() {
let mut hash = HashMap::new();
for (start, len, sb) in self.iter_raw() {
hash.insert(sb.tag, (sb.critical, start as u16, len as u16));
}
*self.parsed.borrow_mut() = Some(hash);
}
}
fn cache_invalidate(&self) {
*self.parsed.borrow_mut() = None;
}
pub fn iter<'a>(&'a self) -> Iter<'a> {
Iter { inner: self.iter_raw(), }
}
pub fn lookup(&self, tag: SubpacketTag) -> Option<Subpacket> {
self.cache_init();
match self.parsed.borrow().as_ref().unwrap().get(&tag) {
Some(&(critical, start, len)) =>
return Some(SubpacketRaw {
critical: critical,
tag: tag,
value: &self.data[
start as usize..start as usize + len as usize]
}.into()),
None => None,
}
}
pub fn add(&mut self, packet: Subpacket) -> Result<()> {
use serialize::Serialize;
if self.data.len() + packet.len() > ::std::u16::MAX as usize {
return Err(Error::MalformedPacket(
"Subpacket area exceeds maximum size".into()).into());
}
self.cache_invalidate();
packet.serialize(&mut self.data)
}
pub fn replace(&mut self, packet: Subpacket) -> Result<()> {
let old = self.remove_all(packet.tag);
if let Err(e) = self.add(packet) {
self.data = old;
return Err(e);
}
Ok(())
}
pub fn remove_all(&mut self, tag: SubpacketTag) -> Vec<u8> {
let mut new = Vec::new();
for (_, _, raw) in self.iter_raw() {
if raw.tag == tag {
continue;
}
let l: SubpacketLength = 1 + raw.value.len() as u32;
let tag = u8::from(raw.tag)
| if raw.critical { 1 << 7 } else { 0 };
l.serialize(&mut new).unwrap();
new.push(tag);
new.extend_from_slice(raw.value);
}
self.cache_invalidate();
::std::mem::replace(&mut self.data, new)
}
pub fn clear(&mut self) {
self.cache_invalidate();
self.data.clear();
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct NotationData<'a> {
flags: NotationDataFlags,
name: &'a [u8],
value: &'a [u8],
}
impl<'a> NotationData<'a> {
pub fn new<F>(name: &'a str, value: &'a [u8], flags: F) -> Self
where F: Into<Option<NotationDataFlags>>
{
Self {
flags: flags.into().unwrap_or_default(),
name: name.as_bytes(),
value,
}
}
pub fn flags(&self) -> NotationDataFlags {
self.flags
}
pub fn name(&self) -> &'a [u8] {
self.name
}
pub fn value(&self) -> &'a [u8] {
self.value
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NotationDataFlags(u32);
impl Default for NotationDataFlags {
fn default() -> Self {
NotationDataFlags(0)
}
}
const NOTATION_DATA_FLAG_HUMAN_READABLE: u32 = 0x80000000;
impl NotationDataFlags {
pub fn human_readable(&self) -> bool {
self.0 & NOTATION_DATA_FLAG_HUMAN_READABLE > 0
}
pub fn set_human_readable(mut self, value: bool) -> Self {
if value {
self.0 |= NOTATION_DATA_FLAG_HUMAN_READABLE;
} else {
self.0 &= ! NOTATION_DATA_FLAG_HUMAN_READABLE;
}
self
}
pub(crate) fn raw(&self) -> u32 {
self.0
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum SubpacketValue<'a> {
Unknown(&'a [u8]),
Invalid(&'a [u8]),
SignatureCreationTime(time::Tm),
SignatureExpirationTime(time::Duration),
ExportableCertification(bool),
TrustSignature {
level: u8,
trust: u8,
},
RegularExpression(&'a [u8]),
Revocable(bool),
KeyExpirationTime(time::Duration),
PreferredSymmetricAlgorithms(Vec<SymmetricAlgorithm>),
RevocationKey {
class: u8,
pk_algo: PublicKeyAlgorithm,
fp: Fingerprint,
},
Issuer(KeyID),
NotationData(NotationData<'a>),
PreferredHashAlgorithms(Vec<HashAlgorithm>),
PreferredCompressionAlgorithms(Vec<CompressionAlgorithm>),
KeyServerPreferences(KeyServerPreferences),
PreferredKeyServer(&'a [u8]),
PrimaryUserID(bool),
PolicyURI(&'a [u8]),
KeyFlags(KeyFlags),
SignersUserID(&'a [u8]),
ReasonForRevocation {
code: ReasonForRevocation,
reason: &'a [u8],
},
Features(Features),
SignatureTarget {
pk_algo: PublicKeyAlgorithm,
hash_algo: HashAlgorithm,
digest: &'a [u8],
},
EmbeddedSignature(Packet),
IssuerFingerprint(Fingerprint),
PreferredAEADAlgorithms(Vec<AEADAlgorithm>),
IntendedRecipient(Fingerprint),
}
impl<'a> SubpacketValue<'a> {
pub fn len(&self) -> SubpacketLength {
use self::SubpacketValue::*;
(match self {
SignatureCreationTime(_) => 4,
SignatureExpirationTime(_) => 4,
ExportableCertification(_) => 1,
TrustSignature { .. } => 2,
RegularExpression(re) => re.len() + 1 ,
Revocable(_) => 1,
KeyExpirationTime(_) => 4,
PreferredSymmetricAlgorithms(p) => p.len(),
RevocationKey { ref fp, .. } => 1 + 1 + fp.as_slice().len(),
Issuer(_) => 8,
NotationData(nd) => 4 + 2 + 2 + nd.name.len() + nd.value.len(),
PreferredHashAlgorithms(p) => p.len(),
PreferredCompressionAlgorithms(p) => p.len(),
KeyServerPreferences(p) => p.as_vec().len(),
PreferredKeyServer(p) => p.len(),
PrimaryUserID(_) => 1,
PolicyURI(p) => p.len(),
KeyFlags(f) => f.as_vec().len(),
SignersUserID(u) => u.len(),
ReasonForRevocation { ref reason, .. } => 1 + reason.len(),
Features(f) => f.as_vec().len(),
SignatureTarget { ref digest, .. } => 1 + 1 + digest.len(),
EmbeddedSignature(p) => match p {
&Packet::Signature(Signature::V4(ref sig)) => {
use serialize::Serialize;
let mut w = Vec::new();
sig.serialize(&mut w).unwrap();
w.len()
},
_ => 0,
},
IssuerFingerprint(ref fp) => match fp {
Fingerprint::V4(_) => 1 + 20,
Fingerprint::Invalid(_) => 1 + fp.as_slice().len(),
},
PreferredAEADAlgorithms(ref p) => p.len(),
IntendedRecipient(ref fp) => match fp {
Fingerprint::V4(_) => 1 + 20,
Fingerprint::Invalid(_) => 1 + fp.as_slice().len(),
},
Unknown(u) => u.len(),
Invalid(i) => i.len(),
} as u32)
}
pub fn tag(&self) -> Result<SubpacketTag> {
use self::SubpacketValue::*;
match &self {
SignatureCreationTime(_) => Ok(SubpacketTag::SignatureCreationTime),
SignatureExpirationTime(_) =>
Ok(SubpacketTag::SignatureExpirationTime),
ExportableCertification(_) =>
Ok(SubpacketTag::ExportableCertification),
TrustSignature { .. } => Ok(SubpacketTag::TrustSignature),
RegularExpression(_) => Ok(SubpacketTag::RegularExpression),
Revocable(_) => Ok(SubpacketTag::Revocable),
KeyExpirationTime(_) => Ok(SubpacketTag::KeyExpirationTime),
PreferredSymmetricAlgorithms(_) =>
Ok(SubpacketTag::PreferredSymmetricAlgorithms),
RevocationKey { .. } => Ok(SubpacketTag::RevocationKey),
Issuer(_) => Ok(SubpacketTag::Issuer),
NotationData(_) => Ok(SubpacketTag::NotationData),
PreferredHashAlgorithms(_) =>
Ok(SubpacketTag::PreferredHashAlgorithms),
PreferredCompressionAlgorithms(_) =>
Ok(SubpacketTag::PreferredCompressionAlgorithms),
KeyServerPreferences(_) => Ok(SubpacketTag::KeyServerPreferences),
PreferredKeyServer(_) => Ok(SubpacketTag::PreferredKeyServer),
PrimaryUserID(_) => Ok(SubpacketTag::PrimaryUserID),
PolicyURI(_) => Ok(SubpacketTag::PolicyURI),
KeyFlags(_) => Ok(SubpacketTag::KeyFlags),
SignersUserID(_) => Ok(SubpacketTag::SignersUserID),
ReasonForRevocation { .. } => Ok(SubpacketTag::ReasonForRevocation),
Features(_) => Ok(SubpacketTag::Features),
SignatureTarget { .. } => Ok(SubpacketTag::SignatureTarget),
EmbeddedSignature(_) => Ok(SubpacketTag::EmbeddedSignature),
IssuerFingerprint(_) => Ok(SubpacketTag::IssuerFingerprint),
PreferredAEADAlgorithms(_) =>
Ok(SubpacketTag::PreferredAEADAlgorithms),
IntendedRecipient(_) => Ok(SubpacketTag::IntendedRecipient),
_ => Err(Error::InvalidArgument(
"Unknown or invalid subpacket value".into()).into()),
}
}
}
#[derive(PartialEq, Clone)]
pub struct Subpacket<'a> {
pub critical: bool,
pub tag: SubpacketTag,
pub value: SubpacketValue<'a>,
}
impl<'a> fmt::Debug for Subpacket<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = f.debug_struct("Subpacket");
if self.critical {
s.field("critical", &self.critical);
}
s.field("value", &self.value);
s.finish()
}
}
impl<'a> Subpacket<'a> {
pub fn new(value: SubpacketValue<'a>, critical: bool) -> Result<Subpacket<'a>> {
Ok(Subpacket {
critical: critical,
tag: value.tag()?,
value: value,
})
}
pub fn len(&self) -> usize {
let value_len = self.value.len();
1 + value_len.len() + value_len as usize
}
}
fn from_be_u16(value: &[u8]) -> Option<u16> {
if value.len() >= 2 {
Some((value[0] as u16) << 8
| (value[1] as u16))
} else {
None
}
}
fn from_be_u32(value: &[u8]) -> Option<u32> {
if value.len() >= 4 {
Some((value[0] as u32) << 24
| (value[1] as u32) << 16
| (value[2] as u32) << 8
| (value[3] as u32))
} else {
None
}
}
impl<'a> From<SubpacketRaw<'a>> for Subpacket<'a> {
fn from(raw: SubpacketRaw<'a>) -> Self {
let value : Option<SubpacketValue>
= match raw.tag {
SubpacketTag::SignatureCreationTime =>
from_be_u32(raw.value).map(|v| {
SubpacketValue::SignatureCreationTime(time::Tm::from_pgp(v))
}),
SubpacketTag::SignatureExpirationTime =>
from_be_u32(raw.value).map(|v| {
SubpacketValue::SignatureExpirationTime(
time::Duration::from_pgp(v))
}),
SubpacketTag::ExportableCertification =>
if raw.value.len() == 1 {
Some(SubpacketValue::ExportableCertification(
raw.value[0] == 1u8))
} else {
None
},
SubpacketTag::TrustSignature =>
if raw.value.len() == 2 {
Some(SubpacketValue::TrustSignature {
level: raw.value[0],
trust: raw.value[1],
})
} else {
None
},
SubpacketTag::RegularExpression => {
let trim = if raw.value.len() > 0
&& raw.value[raw.value.len() - 1] == 0 { 1 } else { 0 };
Some(SubpacketValue::RegularExpression(
&raw.value[..raw.value.len() - trim]))
},
SubpacketTag::Revocable =>
if raw.value.len() == 1 {
Some(SubpacketValue::Revocable(raw.value[0] != 0u8))
} else {
None
},
SubpacketTag::KeyExpirationTime =>
from_be_u32(raw.value).map(|v| {
SubpacketValue::KeyExpirationTime(
time::Duration::from_pgp(v))
}),
SubpacketTag::PreferredSymmetricAlgorithms =>
Some(SubpacketValue::PreferredSymmetricAlgorithms(
raw.value.iter().map(|o| (*o).into()).collect())),
SubpacketTag::RevocationKey =>
if raw.value.len() > 2 {
Some(SubpacketValue::RevocationKey {
class: raw.value[0],
pk_algo: raw.value[1].into(),
fp: Fingerprint::from_bytes(&raw.value[2..]),
})
} else {
None
},
SubpacketTag::Issuer =>
Some(SubpacketValue::Issuer(
KeyID::from_bytes(&raw.value[..]))),
SubpacketTag::NotationData =>
if raw.value.len() > 8 {
let flags = from_be_u32(raw.value).unwrap();
let name_len
= from_be_u16(&raw.value[4..]).unwrap() as usize;
let value_len
= from_be_u16(&raw.value[6..]).unwrap() as usize;
if raw.value.len() == 8 + name_len + value_len {
Some(SubpacketValue::NotationData(
NotationData {
flags: NotationDataFlags(flags),
name: &raw.value[8..8 + name_len],
value: &raw.value[8 + name_len..]
}))
} else {
None
}
} else {
None
},
SubpacketTag::PreferredHashAlgorithms =>
Some(SubpacketValue::PreferredHashAlgorithms(
raw.value.iter().map(|o| (*o).into()).collect())),
SubpacketTag::PreferredCompressionAlgorithms =>
Some(SubpacketValue::PreferredCompressionAlgorithms(
raw.value.iter().map(|o| (*o).into()).collect())),
SubpacketTag::KeyServerPreferences =>
Some(SubpacketValue::KeyServerPreferences(
KeyServerPreferences::new(raw.value))),
SubpacketTag::PreferredKeyServer =>
Some(SubpacketValue::PreferredKeyServer(
raw.value)),
SubpacketTag::PrimaryUserID =>
if raw.value.len() == 1 {
Some(SubpacketValue::PrimaryUserID(
raw.value[0] != 0u8))
} else {
None
},
SubpacketTag::PolicyURI =>
Some(SubpacketValue::PolicyURI(raw.value)),
SubpacketTag::KeyFlags =>
Some(SubpacketValue::KeyFlags(KeyFlags::new(&raw.value))),
SubpacketTag::SignersUserID =>
Some(SubpacketValue::SignersUserID(raw.value)),
SubpacketTag::ReasonForRevocation =>
if raw.value.len() >= 1 {
Some(SubpacketValue::ReasonForRevocation {
code: raw.value[0].into(),
reason: &raw.value[1..],
})
} else {
None
},
SubpacketTag::Features =>
Some(SubpacketValue::Features(Features::new(raw.value))),
SubpacketTag::SignatureTarget =>
if raw.value.len() > 2 {
Some(SubpacketValue::SignatureTarget {
pk_algo: raw.value[0].into(),
hash_algo: raw.value[1].into(),
digest: &raw.value[2..],
})
} else {
None
},
SubpacketTag::EmbeddedSignature => {
use parse::Parse;
Some(SubpacketValue::EmbeddedSignature(
match Signature::from_bytes(&raw.value) {
Ok(s) => Packet::Signature(s),
Err(e) => {
use packet::{Tag, Unknown};
let mut u = Unknown::new(Tag::Signature, e);
u.set_body(raw.value.to_vec());
Packet::Unknown(u)
},
}
))
},
SubpacketTag::IssuerFingerprint => {
let version = raw.value.get(0);
if let Some(version) = version {
if *version == 4 {
Some(SubpacketValue::IssuerFingerprint(
Fingerprint::from_bytes(&raw.value[1..])))
} else {
None
}
} else {
None
}
},
SubpacketTag::PreferredAEADAlgorithms =>
Some(SubpacketValue::PreferredAEADAlgorithms(
raw.value.iter().map(|o| (*o).into()).collect())),
SubpacketTag::IntendedRecipient => {
let version = raw.value.get(0);
if let Some(version) = version {
if *version == 4 {
Some(SubpacketValue::IntendedRecipient(
Fingerprint::from_bytes(&raw.value[1..])))
} else {
None
}
} else {
None
}
},
SubpacketTag::Reserved(_)
| SubpacketTag::PlaceholderForBackwardCompatibility
| SubpacketTag::Private(_)
| SubpacketTag::Unknown(_) =>
Some(SubpacketValue::Unknown(raw.value)),
};
if let Some(value) = value {
Subpacket {
critical: raw.critical,
tag: raw.tag,
value: value,
}
} else {
Subpacket {
critical: raw.critical,
tag: raw.tag,
value: SubpacketValue::Invalid(raw.value),
}
}
}
}
pub(crate) type SubpacketLength = u32;
pub(crate) trait SubpacketLengthTrait {
fn parse<C>(bio: &mut buffered_reader::Memory<C>) -> io::Result<u32>;
fn serialize(&self, sink: &mut dyn std::io::Write) -> io::Result<()>;
fn len(&self) -> usize;
}
impl SubpacketLengthTrait for SubpacketLength {
fn parse<C>(bio: &mut buffered_reader::Memory<C>) -> io::Result<u32> {
let octet1 = bio.data_consume_hard(1)?[0];
if octet1 < 192 {
return Ok(octet1 as u32);
}
if 192 <= octet1 && octet1 < 255 {
let octet2 = bio.data_consume_hard(1)?[0];
return Ok(((octet1 as u32 - 192) << 8) + octet2 as u32 + 192);
}
assert_eq!(octet1, 255);
Ok(bio.read_be_u32()?)
}
fn serialize(&self, sink: &mut dyn std::io::Write) -> io::Result<()> {
let v = *self;
if v < 192 {
sink.write_all(&[v as u8])
} else if v < 16320 {
let v = v - 192 + (192 << 8);
sink.write_all(&[(v >> 8) as u8,
(v >> 0) as u8])
} else {
sink.write_all(&[(v >> 24) as u8,
(v >> 16) as u8,
(v >> 8) as u8,
(v >> 0) as u8])
}
}
fn len(&self) -> usize {
if *self < 192 {
1
} else if *self < 16320 {
2
} else {
5
}
}
}
#[cfg(test)]
quickcheck! {
fn length_roundtrip(length: SubpacketLength) -> bool {
let mut encoded = Vec::new();
length.serialize(&mut encoded).unwrap();
assert_eq!(encoded.len(), length.len());
let mut reader = buffered_reader::Memory::new(&encoded);
SubpacketLength::parse(&mut reader).unwrap() == length
}
}
impl Signature4 {
fn subpacket<'a>(&'a self, tag: SubpacketTag) -> Option<Subpacket<'a>> {
if let Some(sb) = self.hashed_area().lookup(tag) {
return Some(sb);
}
if !(tag == SubpacketTag::Issuer
|| tag == SubpacketTag::EmbeddedSignature) {
return None;
}
self.unhashed_area().lookup(tag)
}
fn subpackets<'a>(&'a self, target: SubpacketTag) -> Vec<Subpacket<'a>> {
let mut result = Vec::new();
for (_start, _len, sb) in self.hashed_area().iter_raw() {
if sb.tag == target {
result.push(sb.into());
}
}
result
}
pub fn signature_creation_time(&self) -> Option<time::Tm> {
if let Some(sb)
= self.subpacket(SubpacketTag::SignatureCreationTime) {
if let SubpacketValue::SignatureCreationTime(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn signature_expiration_time(&self) -> Option<time::Duration> {
if let Some(sb)
= self.subpacket(SubpacketTag::SignatureExpirationTime) {
if let SubpacketValue::SignatureExpirationTime(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn signature_expired(&self) -> bool {
self.signature_expired_at(time::now_utc())
}
pub fn signature_expired_at(&self, tm: time::Tm) -> bool {
match (self.signature_creation_time(), self.signature_expiration_time())
{
(Some(_), Some(e)) if e.num_seconds() == 0 =>
false,
(Some(c), Some(e)) =>
(c + e) <= tm,
(None, Some(_)) =>
true,
(_, None) =>
false,
}
}
pub fn signature_alive(&self) -> bool {
self.signature_alive_at(time::now_utc())
}
pub fn signature_alive_at(&self, tm: time::Tm) -> bool {
if let Some(creation_time) = self.signature_creation_time() {
creation_time <= tm && ! self.signature_expired_at(tm)
} else {
false
}
}
pub fn exportable_certification(&self) -> Option<bool> {
if let Some(sb)
= self.subpacket(SubpacketTag::ExportableCertification) {
if let SubpacketValue::ExportableCertification(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn trust_signature(&self) -> Option<(u8, u8)> {
if let Some(sb) = self.subpacket(SubpacketTag::TrustSignature) {
if let SubpacketValue::TrustSignature{ level, trust } = sb.value {
Some((level, trust))
} else {
None
}
} else {
None
}
}
pub fn regular_expression(&self) -> Option<&[u8]> {
if let Some(sb)
= self.subpacket(SubpacketTag::RegularExpression) {
if let SubpacketValue::RegularExpression(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn revocable(&self) -> Option<bool> {
if let Some(sb)
= self.subpacket(SubpacketTag::Revocable) {
if let SubpacketValue::Revocable(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn key_expiration_time(&self) -> Option<time::Duration> {
if let Some(sb)
= self.subpacket(SubpacketTag::KeyExpirationTime) {
if let SubpacketValue::KeyExpirationTime(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn key_expired(&self, key: &Key) -> bool {
self.key_expired_at(key, time::now_utc())
}
pub fn key_expired_at(&self, key: &Key, tm: time::Tm) -> bool {
match self.key_expiration_time() {
Some(e) if e.num_seconds() == 0 =>
false,
Some(e) =>
*key.creation_time() + e <= tm,
None =>
false,
}
}
pub fn key_alive(&self, key: &Key) -> bool {
self.key_alive_at(key, time::now_utc())
}
pub fn key_alive_at(&self, key: &Key, tm: time::Tm) -> bool {
*key.creation_time() <= tm && ! self.key_expired_at(key, tm)
}
pub fn preferred_symmetric_algorithms(&self)
-> Option<Vec<SymmetricAlgorithm>> {
if let Some(sb)
= self.subpacket(
SubpacketTag::PreferredSymmetricAlgorithms) {
if let SubpacketValue::PreferredSymmetricAlgorithms(v)
= sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn revocation_key(&self) -> Option<(u8,
PublicKeyAlgorithm,
Fingerprint)> {
if let Some(sb) = self.subpacket(SubpacketTag::RevocationKey) {
if let SubpacketValue::RevocationKey {
class, pk_algo, fp,
} = sb.value {
Some((class, pk_algo, fp))
} else {
None
}
} else {
None
}
}
pub fn issuer(&self) -> Option<KeyID> {
if let Some(sb)
= self.subpacket(SubpacketTag::Issuer) {
if let SubpacketValue::Issuer(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn notation_data(&self) -> Vec<NotationData> {
self.subpackets(SubpacketTag::NotationData)
.into_iter().filter_map(|sb| {
if let SubpacketValue::NotationData(v) = sb.value {
Some(v)
} else {
None
}
})
.collect()
}
pub fn notation(&self, name: &str) -> Vec<&[u8]> {
self.subpackets(SubpacketTag::NotationData)
.into_iter().filter_map(|s| match s.value {
SubpacketValue::NotationData(ref v)
if v.name == name.as_bytes() => Some(v.value),
_ => None,
})
.collect()
}
pub fn preferred_hash_algorithms(&self) -> Option<Vec<HashAlgorithm>> {
if let Some(sb)
= self.subpacket(
SubpacketTag::PreferredHashAlgorithms) {
if let SubpacketValue::PreferredHashAlgorithms(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn preferred_compression_algorithms(&self)
-> Option<Vec<CompressionAlgorithm>>
{
if let Some(sb)
= self.subpacket(
SubpacketTag::PreferredCompressionAlgorithms) {
if let SubpacketValue::PreferredCompressionAlgorithms(v)
= sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn key_server_preferences(&self) -> KeyServerPreferences {
if let Some(sb) = self.subpacket(SubpacketTag::KeyServerPreferences) {
if let SubpacketValue::KeyServerPreferences(v) = sb.value {
v
} else {
KeyServerPreferences::default()
}
} else {
KeyServerPreferences::default()
}
}
pub fn preferred_key_server(&self) -> Option<&[u8]> {
if let Some(sb)
= self.subpacket(SubpacketTag::PreferredKeyServer) {
if let SubpacketValue::PreferredKeyServer(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn primary_userid(&self) -> Option<bool> {
if let Some(sb)
= self.subpacket(SubpacketTag::PrimaryUserID) {
if let SubpacketValue::PrimaryUserID(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn policy_uri(&self) -> Option<&[u8]> {
if let Some(sb)
= self.subpacket(SubpacketTag::PolicyURI) {
if let SubpacketValue::PolicyURI(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn key_flags(&self) -> KeyFlags {
if let Some(sb) = self.subpacket(SubpacketTag::KeyFlags) {
if let SubpacketValue::KeyFlags(v) = sb.value {
v
} else {
KeyFlags::default()
}
} else {
KeyFlags::default()
}
}
pub fn signers_user_id(&self) -> Option<&[u8]> {
if let Some(sb)
= self.subpacket(SubpacketTag::SignersUserID) {
if let SubpacketValue::SignersUserID(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn reason_for_revocation(&self)
-> Option<(ReasonForRevocation, &[u8])> {
if let Some(sb) = self.subpacket(SubpacketTag::ReasonForRevocation) {
if let SubpacketValue::ReasonForRevocation {
code, reason,
} = sb.value {
Some((code, reason))
} else {
None
}
} else {
None
}
}
pub fn features(&self) -> Features {
if let Some(sb) = self.subpacket(SubpacketTag::Features) {
if let SubpacketValue::Features(v) = sb.value {
v
} else {
Features::default()
}
} else {
Features::default()
}
}
pub fn signature_target(&self) -> Option<(PublicKeyAlgorithm,
HashAlgorithm,
&[u8])> {
if let Some(sb) = self.subpacket(SubpacketTag::SignatureTarget) {
if let SubpacketValue::SignatureTarget {
pk_algo, hash_algo, digest,
} = sb.value {
Some((pk_algo, hash_algo, digest))
} else {
None
}
} else {
None
}
}
pub fn embedded_signature(&self) -> Option<Packet> {
if let Some(sb)
= self.subpacket(SubpacketTag::EmbeddedSignature) {
if let SubpacketValue::EmbeddedSignature(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn issuer_fingerprint(&self) -> Option<Fingerprint> {
if let Some(sb)
= self.subpacket(SubpacketTag::IssuerFingerprint) {
if let SubpacketValue::IssuerFingerprint(v) = sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn preferred_aead_algorithms(&self)
-> Option<Vec<AEADAlgorithm>> {
if let Some(sb)
= self.subpacket(
SubpacketTag::PreferredAEADAlgorithms) {
if let SubpacketValue::PreferredAEADAlgorithms(v)
= sb.value {
Some(v)
} else {
None
}
} else {
None
}
}
pub fn intended_recipients(&self) -> Vec<Fingerprint> {
let mut result = Vec::new();
for (_start, _len, sb) in self.hashed_area().iter_raw() {
if sb.tag == SubpacketTag::IntendedRecipient {
let s = Subpacket::from(sb);
if let SubpacketValue::IntendedRecipient(fp) = s.value {
result.push(fp);
}
}
}
result
}
}
impl signature::Builder {
pub fn set_signature_creation_time(mut self, creation_time: time::Tm)
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::SignatureCreationTime(creation_time.canonicalize()),
true)?)?;
Ok(self)
}
pub fn set_signature_expiration_time(mut self,
expiration: Option<time::Duration>)
-> Result<Self> {
if let Some(e) = expiration {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::SignatureExpirationTime(e.canonicalize()),
true)?)?;
} else {
self.hashed_area.remove_all(SubpacketTag::SignatureExpirationTime);
}
Ok(self)
}
pub fn set_exportable_certification(mut self, exportable: bool)
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::ExportableCertification(exportable),
true)?)?;
Ok(self)
}
pub fn set_trust_signature(mut self, level: u8, trust: u8)
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::TrustSignature {
level: level,
trust: trust,
},
true)?)?;
Ok(self)
}
pub fn set_regular_expression(mut self, re: &[u8]) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::RegularExpression(re),
true)?)?;
Ok(self)
}
pub fn set_revocable(mut self, revocable: bool) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::Revocable(revocable),
true)?)?;
Ok(self)
}
pub fn set_key_expiration_time(mut self,
expiration: Option<time::Duration>)
-> Result<Self> {
if let Some(e) = expiration {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::KeyExpirationTime(e.canonicalize()),
true)?)?;
} else {
self.hashed_area.remove_all(SubpacketTag::KeyExpirationTime);
}
Ok(self)
}
pub fn set_preferred_symmetric_algorithms(mut self,
preferences: Vec<SymmetricAlgorithm>)
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::PreferredSymmetricAlgorithms(preferences),
false)?)?;
Ok(self)
}
pub fn set_revocation_key(mut self, class: u8, pk_algo: PublicKeyAlgorithm,
fp: Fingerprint) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::RevocationKey {
class: class,
pk_algo: pk_algo,
fp: fp,
},
true)?)?;
Ok(self)
}
pub fn set_issuer(mut self, id: KeyID) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::Issuer(id),
false)?)?;
Ok(self)
}
pub fn set_notation<F>(mut self, name: &str, value: &[u8], flags: F,
critical: bool)
-> Result<Self>
where F: Into<Option<NotationDataFlags>>
{
self.hashed_area = self.hashed_area.iter().filter_map(|s| {
match s.2.value {
SubpacketValue::NotationData(ref v)
if v.name == name.as_bytes() => None,
_ => Some(s),
}
}).collect();
self.add_notation(name, value,
flags.into().unwrap_or_default(),
critical)
}
pub fn add_notation<F>(mut self, name: &str, value: &[u8], flags: F,
critical: bool)
-> Result<Self>
where F: Into<Option<NotationDataFlags>>
{
self.hashed_area.add(Subpacket::new(SubpacketValue::NotationData(
NotationData::new(name, value,
flags.into().unwrap_or_default())),
critical)?)?;
Ok(self)
}
pub fn set_preferred_hash_algorithms(mut self,
preferences: Vec<HashAlgorithm>)
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::PreferredHashAlgorithms(preferences),
false)?)?;
Ok(self)
}
pub fn set_preferred_compression_algorithms(mut self,
preferences: Vec<CompressionAlgorithm>)
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::PreferredCompressionAlgorithms(preferences),
false)?)?;
Ok(self)
}
pub fn set_key_server_preferences(mut self,
preferences: KeyServerPreferences)
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::KeyServerPreferences(preferences),
false)?)?;
Ok(self)
}
pub fn set_preferred_key_server(mut self, uri: &[u8])
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::PreferredKeyServer(uri),
false)?)?;
Ok(self)
}
pub fn set_primary_userid(mut self, primary: bool) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::PrimaryUserID(primary),
true)?)?;
Ok(self)
}
pub fn set_policy_uri(mut self, uri: &[u8]) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::PolicyURI(uri),
false)?)?;
Ok(self)
}
pub fn set_key_flags(mut self, flags: &KeyFlags) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::KeyFlags(flags.clone()),
true)?)?;
Ok(self)
}
pub fn set_signers_user_id(mut self, uid: &[u8]) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::SignersUserID(uid),
true)?)?;
Ok(self)
}
pub fn set_reason_for_revocation(mut self, code: ReasonForRevocation,
reason: &[u8])
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::ReasonForRevocation {
code: code,
reason: reason,
},
false)?)?;
Ok(self)
}
pub fn set_features(mut self, features: &Features) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::Features(features.clone()),
false)?)?;
Ok(self)
}
pub fn set_signature_target(mut self,
pk_algo: PublicKeyAlgorithm,
hash_algo: HashAlgorithm,
digest: &[u8])
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::SignatureTarget {
pk_algo: pk_algo,
hash_algo: hash_algo,
digest: digest
},
true)?)?;
Ok(self)
}
pub fn set_embedded_signature(mut self, signature: Signature)
-> Result<Self> {
self.unhashed_area.replace(Subpacket::new(
SubpacketValue::EmbeddedSignature(signature.into()),
true)?)?;
Ok(self)
}
pub fn set_issuer_fingerprint(mut self, fp: Fingerprint) -> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::IssuerFingerprint(fp),
false)?)?;
Ok(self)
}
pub fn set_preferred_aead_algorithms(mut self,
preferences: Vec<AEADAlgorithm>)
-> Result<Self> {
self.hashed_area.replace(Subpacket::new(
SubpacketValue::PreferredAEADAlgorithms(preferences),
false)?)?;
Ok(self)
}
pub fn set_intended_recipients(mut self, recipients: Vec<Fingerprint>)
-> Result<Self> {
self.hashed_area.remove_all(SubpacketTag::IntendedRecipient);
for fp in recipients.into_iter() {
self.hashed_area.add(
Subpacket::new(SubpacketValue::IntendedRecipient(fp), false)?)?;
}
Ok(self)
}
}
#[test]
fn accessors() {
use constants::Curve;
let pk_algo = PublicKeyAlgorithm::EdDSA;
let hash_algo = HashAlgorithm::SHA512;
let hash = hash_algo.context().unwrap();
let mut sig = signature::Builder::new(::constants::SignatureType::Binary);
let mut key: ::packet::Key =
::packet::key::Key4::generate_ecc(true, Curve::Ed25519).unwrap().into();
let mut keypair = key.clone().into_keypair().unwrap();
let now = time::Tm::from_pgp(time::now_utc().to_pgp().unwrap());
sig = sig.set_signature_creation_time(now).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.signature_creation_time(), Some(now));
let five_minutes = time::Duration::minutes(5);
let ten_minutes = time::Duration::minutes(10);
sig = sig.set_signature_expiration_time(Some(five_minutes)).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.signature_expiration_time(), Some(five_minutes));
assert!(!sig_.signature_expired());
assert!(!sig_.signature_expired_at(now));
assert!(sig_.signature_expired_at(now + ten_minutes));
assert!(sig_.signature_alive());
assert!(sig_.signature_alive_at(now));
assert!(!sig_.signature_alive_at(now - five_minutes));
assert!(!sig_.signature_alive_at(now + ten_minutes));
sig = sig.set_signature_expiration_time(None).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.signature_expiration_time(), None);
assert!(!sig_.signature_expired());
assert!(!sig_.signature_expired_at(now));
assert!(!sig_.signature_expired_at(now + ten_minutes));
assert!(sig_.signature_alive());
assert!(sig_.signature_alive_at(now));
assert!(!sig_.signature_alive_at(now - five_minutes));
assert!(sig_.signature_alive_at(now + ten_minutes));
sig = sig.set_exportable_certification(true).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.exportable_certification(), Some(true));
sig = sig.set_exportable_certification(false).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.exportable_certification(), Some(false));
sig = sig.set_trust_signature(2, 3).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.trust_signature(), Some((2, 3)));
sig = sig.set_regular_expression(b"foobar").unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.regular_expression(), Some(&b"foobar"[..]));
sig = sig.set_revocable(true).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.revocable(), Some(true));
sig = sig.set_revocable(false).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.revocable(), Some(false));
key.set_creation_time(now);
sig = sig.set_key_expiration_time(Some(five_minutes)).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.key_expiration_time(), Some(five_minutes));
assert!(!sig_.key_expired(&key));
assert!(!sig_.key_expired_at(&key, now));
assert!(sig_.key_expired_at(&key, now + ten_minutes));
assert!(sig_.key_alive(&key));
assert!(sig_.key_alive_at(&key, now));
assert!(!sig_.key_alive_at(&key, now - five_minutes));
assert!(!sig_.key_alive_at(&key, now + ten_minutes));
sig = sig.set_key_expiration_time(None).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.key_expiration_time(), None);
assert!(!sig_.key_expired(&key));
assert!(!sig_.key_expired_at(&key, now));
assert!(!sig_.key_expired_at(&key, now + ten_minutes));
assert!(sig_.key_alive(&key));
assert!(sig_.key_alive_at(&key, now));
assert!(!sig_.key_alive_at(&key, now - five_minutes));
assert!(sig_.key_alive_at(&key, now + ten_minutes));
let pref = vec![SymmetricAlgorithm::AES256,
SymmetricAlgorithm::AES192,
SymmetricAlgorithm::AES128];
sig = sig.set_preferred_symmetric_algorithms(pref.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.preferred_symmetric_algorithms(), Some(pref));
let fp = Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb");
sig = sig.set_revocation_key(2, pk_algo, fp.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.revocation_key(),
Some((2, pk_algo.into(), fp.clone())));
sig = sig.set_issuer(fp.to_keyid()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.issuer(), Some(fp.to_keyid()));
let pref = vec![HashAlgorithm::SHA512,
HashAlgorithm::SHA384,
HashAlgorithm::SHA256];
sig = sig.set_preferred_hash_algorithms(pref.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.preferred_hash_algorithms(), Some(pref));
let pref = vec![CompressionAlgorithm::BZip2,
CompressionAlgorithm::Zlib,
CompressionAlgorithm::Zip];
sig = sig.set_preferred_compression_algorithms(pref.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.preferred_compression_algorithms(), Some(pref));
let pref = KeyServerPreferences::default()
.set_no_modify(true);
sig = sig.set_key_server_preferences(pref.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.key_server_preferences(), pref);
sig = sig.set_primary_userid(true).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.primary_userid(), Some(true));
sig = sig.set_primary_userid(false).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.primary_userid(), Some(false));
sig = sig.set_policy_uri(b"foobar").unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.policy_uri(), Some(&b"foobar"[..]));
let key_flags = KeyFlags::default()
.set_certify(true)
.set_sign(true);
sig = sig.set_key_flags(&key_flags).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.key_flags(), key_flags);
sig = sig.set_signers_user_id(b"foobar").unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.signers_user_id(), Some(&b"foobar"[..]));
sig = sig.set_reason_for_revocation(ReasonForRevocation::KeyRetired,
b"foobar").unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.reason_for_revocation(),
Some((ReasonForRevocation::KeyRetired, &b"foobar"[..])));
let feats = Features::default().set_mdc(true);
sig = sig.set_features(&feats).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.features(), feats);
let feats = Features::default().set_aead(true);
sig = sig.set_features(&feats).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.features(), feats);
let digest = vec![0; hash_algo.context().unwrap().digest_size()];
sig = sig.set_signature_target(pk_algo, hash_algo, &digest).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.signature_target(), Some((pk_algo.into(),
hash_algo.into(),
&digest[..])));
let embedded_sig = sig_.clone();
sig = sig.set_embedded_signature(embedded_sig.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.embedded_signature(), Some(Packet::Signature(embedded_sig)));
sig = sig.set_issuer_fingerprint(fp.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.issuer_fingerprint(), Some(fp));
let pref = vec![AEADAlgorithm::EAX,
AEADAlgorithm::OCB];
sig = sig.set_preferred_aead_algorithms(pref.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.preferred_aead_algorithms(), Some(pref));
let fps = vec![
Fingerprint::from_bytes(b"aaaaaaaaaaaaaaaaaaaa"),
Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb"),
];
sig = sig.set_intended_recipients(fps.clone()).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.intended_recipients(), fps);
sig = sig.set_notation("test@example.org", &[0, 1, 2], None, false)
.unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.notation("test@example.org"), vec![&[0, 1, 2]]);
sig = sig.add_notation("test@example.org", &[3, 4, 5], None, false)
.unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.notation("test@example.org"), vec![&[0, 1, 2],
&[3, 4, 5]]);
sig = sig.set_notation("test@example.org", &[6, 7, 8], None, false)
.unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash_algo, hash.clone()).unwrap();
assert_eq!(sig_.notation("test@example.org"), vec![&[6, 7, 8]]);
}
#[cfg(feature = "compression-deflate")]
#[test]
fn subpacket_test_1 () {
use PacketPile;
use parse::Parse;
let pile = PacketPile::from_bytes(::tests::message("signed.gpg")).unwrap();
eprintln!("PacketPile has {} top-level packets.", pile.children().len());
eprintln!("PacketPile: {:?}", pile);
let mut count = 0;
for p in pile.descendants() {
if let &Packet::Signature(ref sig) = p {
count += 1;
let mut got2 = false;
let mut got16 = false;
let mut got33 = false;
for i in 0..255 {
if let Some(sb) = sig.subpacket(i.into()) {
if i == 2 {
got2 = true;
assert!(!sb.critical);
} else if i == 16 {
got16 = true;
assert!(!sb.critical);
} else if i == 33 {
got33 = true;
assert!(!sb.critical);
} else {
panic!("Unexpectedly found subpacket {}", i);
}
}
}
assert!(got2 && got16 && got33);
let fp = sig.issuer_fingerprint().unwrap().to_string();
assert!(
fp == "7FAF 6ED7 2381 4355 7BDF 7ED2 6863 C9AD 5B4D 22D3"
|| fp == "C03F A641 1B03 AE12 5764 6118 7223 B566 78E0 2528");
let hex = sig.issuer_fingerprint().unwrap().to_hex();
assert!(
hex == "7FAF6ED7238143557BDF7ED26863C9AD5B4D22D3"
|| hex == "C03FA6411B03AE12576461187223B56678E02528");
}
}
assert_eq!(count, 2);
}
#[test]
fn subpacket_test_2() {
use conversions::Time;
use parse::Parse;
use PacketPile;
let pile = PacketPile::from_bytes(
::tests::key("subpackets/shaw.gpg")).unwrap();
if let (Some(&Packet::PublicKey(ref key)),
Some(&Packet::Signature(ref sig)))
= (pile.children().nth(0), pile.children().nth(2))
{
assert_eq!(sig.signature_creation_time(),
Some(time::Tm::from_pgp(1515791508)));
assert_eq!(sig.subpacket(SubpacketTag::SignatureCreationTime),
Some(Subpacket {
critical: false,
tag: SubpacketTag::SignatureCreationTime,
value: SubpacketValue::SignatureCreationTime(
time::Tm::from_pgp(1515791508))
}));
assert!(! sig.signature_expired());
assert_eq!(sig.key_expiration_time(),
Some(time::Duration::from_pgp(63072000)));
assert_eq!(sig.subpacket(SubpacketTag::KeyExpirationTime),
Some(Subpacket {
critical: false,
tag: SubpacketTag::KeyExpirationTime,
value: SubpacketValue::KeyExpirationTime(
time::Duration::from_pgp(63072000))
}));
assert!(! sig.key_expired_at(
key,
*key.creation_time() + time::Duration::seconds(63072000 - 1)));
assert!(sig.key_expired_at(
key,
*key.creation_time() + time::Duration::seconds(63072000)));
assert_eq!(sig.preferred_symmetric_algorithms(),
Some(vec![SymmetricAlgorithm::AES256,
SymmetricAlgorithm::AES192,
SymmetricAlgorithm::AES128,
SymmetricAlgorithm::TripleDES]));
assert_eq!(sig.subpacket(SubpacketTag::PreferredSymmetricAlgorithms),
Some(Subpacket {
critical: false,
tag: SubpacketTag::PreferredSymmetricAlgorithms,
value: SubpacketValue::PreferredSymmetricAlgorithms(
vec![SymmetricAlgorithm::AES256,
SymmetricAlgorithm::AES192,
SymmetricAlgorithm::AES128,
SymmetricAlgorithm::TripleDES]
)}));
assert_eq!(sig.preferred_hash_algorithms(),
Some(vec![HashAlgorithm::SHA256,
HashAlgorithm::SHA384,
HashAlgorithm::SHA512,
HashAlgorithm::SHA224,
HashAlgorithm::SHA1]));
assert_eq!(sig.subpacket(SubpacketTag::PreferredHashAlgorithms),
Some(Subpacket {
critical: false,
tag: SubpacketTag::PreferredHashAlgorithms,
value: SubpacketValue::PreferredHashAlgorithms(
vec![HashAlgorithm::SHA256,
HashAlgorithm::SHA384,
HashAlgorithm::SHA512,
HashAlgorithm::SHA224,
HashAlgorithm::SHA1]
)}));
assert_eq!(sig.preferred_compression_algorithms(),
Some(vec![CompressionAlgorithm::Zlib,
CompressionAlgorithm::BZip2,
CompressionAlgorithm::Zip]));
assert_eq!(sig.subpacket(SubpacketTag::PreferredCompressionAlgorithms),
Some(Subpacket {
critical: false,
tag: SubpacketTag::PreferredCompressionAlgorithms,
value: SubpacketValue::PreferredCompressionAlgorithms(
vec![CompressionAlgorithm::Zlib,
CompressionAlgorithm::BZip2,
CompressionAlgorithm::Zip]
)}));
assert_eq!(sig.key_server_preferences(),
KeyServerPreferences::default().set_no_modify(true));
assert_eq!(sig.subpacket(SubpacketTag::KeyServerPreferences),
Some(Subpacket {
critical: false,
tag: SubpacketTag::KeyServerPreferences,
value: SubpacketValue::KeyServerPreferences(
KeyServerPreferences::default().set_no_modify(true)),
}));
assert!(sig.key_flags().can_certify() && sig.key_flags().can_sign());
assert_eq!(sig.subpacket(SubpacketTag::KeyFlags),
Some(Subpacket {
critical: false,
tag: SubpacketTag::KeyFlags,
value: SubpacketValue::KeyFlags(
KeyFlags::default().set_certify(true).set_sign(true))
}));
assert_eq!(sig.features(), Features::default().set_mdc(true));
assert_eq!(sig.subpacket(SubpacketTag::Features),
Some(Subpacket {
critical: false,
tag: SubpacketTag::Features,
value: SubpacketValue::Features(
Features::default().set_mdc(true))
}));
let keyid = KeyID::from_hex("F004 B9A4 5C58 6126").unwrap();
assert_eq!(sig.issuer(), Some(keyid.clone()));
assert_eq!(sig.subpacket(SubpacketTag::Issuer),
Some(Subpacket {
critical: false,
tag: SubpacketTag::Issuer,
value: SubpacketValue::Issuer(keyid)
}));
let fp = Fingerprint::from_hex(
"361A96BDE1A65B6D6C25AE9FF004B9A45C586126").unwrap();
assert_eq!(sig.issuer_fingerprint(), Some(fp.clone()));
assert_eq!(sig.subpacket(SubpacketTag::IssuerFingerprint),
Some(Subpacket {
critical: false,
tag: SubpacketTag::IssuerFingerprint,
value: SubpacketValue::IssuerFingerprint(fp)
}));
let n = NotationData {
flags: NotationDataFlags::default().set_human_readable(true),
name: "rank@navy.mil".as_bytes(),
value: "midshipman".as_bytes()
};
assert_eq!(sig.notation_data(), vec![n.clone()]);
assert_eq!(sig.subpacket(SubpacketTag::NotationData),
Some(Subpacket {
critical: false,
tag: SubpacketTag::NotationData,
value: SubpacketValue::NotationData(n.clone())
}));
assert_eq!(sig.subpackets(SubpacketTag::NotationData),
vec![(Subpacket {
critical: false,
tag: SubpacketTag::NotationData,
value: SubpacketValue::NotationData(n.clone())
})]);
} else {
panic!("Expected signature!");
}
if let Some(&Packet::Signature(ref sig)) = pile.children().nth(3) {
assert_eq!(sig.signature_creation_time(),
Some(time::Tm::from_pgp(1515791490)));
assert_eq!(sig.subpacket(SubpacketTag::SignatureCreationTime),
Some(Subpacket {
critical: false,
tag: SubpacketTag::SignatureCreationTime,
value: SubpacketValue::SignatureCreationTime(
time::Tm::from_pgp(1515791490))
}));
assert_eq!(sig.exportable_certification(), Some(false));
assert_eq!(sig.subpacket(SubpacketTag::ExportableCertification),
Some(Subpacket {
critical: false,
tag: SubpacketTag::ExportableCertification,
value: SubpacketValue::ExportableCertification(false)
}));
}
let pile = PacketPile::from_bytes(
::tests::key("subpackets/marven.gpg")).unwrap();
if let Some(&Packet::Signature(ref sig)) = pile.children().nth(1) {
assert_eq!(sig.signature_creation_time(),
Some(time::Tm::from_pgp(1515791376)));
assert_eq!(sig.subpacket(SubpacketTag::SignatureCreationTime),
Some(Subpacket {
critical: false,
tag: SubpacketTag::SignatureCreationTime,
value: SubpacketValue::SignatureCreationTime(
time::Tm::from_pgp(1515791376))
}));
assert_eq!(sig.revocable(), Some(false));
assert_eq!(sig.subpacket(SubpacketTag::Revocable),
Some(Subpacket {
critical: false,
tag: SubpacketTag::Revocable,
value: SubpacketValue::Revocable(false)
}));
let fp = Fingerprint::from_hex(
"361A96BDE1A65B6D6C25AE9FF004B9A45C586126").unwrap();
assert_eq!(sig.revocation_key(),
Some((128, PublicKeyAlgorithm::RSAEncryptSign, fp.clone())));
assert_eq!(sig.subpacket(SubpacketTag::RevocationKey),
Some(Subpacket {
critical: false,
tag: SubpacketTag::RevocationKey,
value: SubpacketValue::RevocationKey {
class: 0x80,
pk_algo: PublicKeyAlgorithm::RSAEncryptSign,
fp: fp,
},
}));
let keyid = KeyID::from_hex("CEAD 0621 0934 7957").unwrap();
assert_eq!(sig.issuer(), Some(keyid.clone()));
assert_eq!(sig.subpacket(SubpacketTag::Issuer),
Some(Subpacket {
critical: false,
tag: SubpacketTag::Issuer,
value: SubpacketValue::Issuer(keyid)
}));
let fp = Fingerprint::from_hex(
"B59B8817F519DCE10AFD85E4CEAD062109347957").unwrap();
assert_eq!(sig.issuer_fingerprint(), Some(fp.clone()));
assert_eq!(sig.subpacket(SubpacketTag::IssuerFingerprint),
Some(Subpacket {
critical: false,
tag: SubpacketTag::IssuerFingerprint,
value: SubpacketValue::IssuerFingerprint(fp)
}));
assert_eq!(sig.notation_data(), vec![]);
assert_eq!(sig.subpacket(SubpacketTag::NotationData),
None);
assert_eq!(sig.subpackets(SubpacketTag::NotationData),
vec![]);
} else {
panic!("Expected signature!");
}
if let Some(&Packet::Signature(ref sig)) = pile.children().nth(6) {
assert_eq!(sig.signature_creation_time(),
Some(time::Tm::from_pgp(1515886658)));
assert_eq!(sig.subpacket(SubpacketTag::SignatureCreationTime),
Some(Subpacket {
critical: false,
tag: SubpacketTag::SignatureCreationTime,
value: SubpacketValue::SignatureCreationTime(
time::Tm::from_pgp(1515886658))
}));
assert_eq!(sig.reason_for_revocation(),
Some((ReasonForRevocation::Unspecified,
&b"Forgot to set a sig expiration."[..])));
assert_eq!(sig.subpacket(SubpacketTag::ReasonForRevocation),
Some(Subpacket {
critical: false,
tag: SubpacketTag::ReasonForRevocation,
value: SubpacketValue::ReasonForRevocation {
code: ReasonForRevocation::Unspecified,
reason: &b"Forgot to set a sig expiration."[..],
},
}));
}
if let Some(&Packet::Signature(ref sig)) = pile.children().nth(7) {
assert_eq!(sig.signature_creation_time(),
Some(time::Tm::from_pgp(1515791467)));
assert_eq!(sig.subpacket(SubpacketTag::SignatureCreationTime),
Some(Subpacket {
critical: false,
tag: SubpacketTag::SignatureCreationTime,
value: SubpacketValue::SignatureCreationTime(
time::Tm::from_pgp(1515791467))
}));
let n1 = NotationData {
flags: NotationDataFlags::default().set_human_readable(true),
name: "rank@navy.mil".as_bytes(),
value: "third lieutenant".as_bytes()
};
let n2 = NotationData {
flags: NotationDataFlags::default().set_human_readable(true),
name: "foo@navy.mil".as_bytes(),
value: "bar".as_bytes()
};
let n3 = NotationData {
flags: NotationDataFlags::default().set_human_readable(true),
name: "whistleblower@navy.mil".as_bytes(),
value: "true".as_bytes()
};
assert_eq!(sig.notation_data(), vec![n1.clone(), n2.clone(), n3.clone()]);
assert_eq!(sig.subpacket(SubpacketTag::NotationData),
Some(Subpacket {
critical: false,
tag: SubpacketTag::NotationData,
value: SubpacketValue::NotationData(n3.clone())
}));
assert_eq!(sig.subpackets(SubpacketTag::NotationData),
vec![
Subpacket {
critical: false,
tag: SubpacketTag::NotationData,
value: SubpacketValue::NotationData(n1)
},
Subpacket {
critical: false,
tag: SubpacketTag::NotationData,
value: SubpacketValue::NotationData(n2)
},
Subpacket {
critical: false,
tag: SubpacketTag::NotationData,
value: SubpacketValue::NotationData(n3)
},
]);
}
if let Some(&Packet::Signature(ref sig)) = pile.children().nth(8) {
assert_eq!(sig.signature_creation_time(),
Some(time::Tm::from_pgp(1515791223)));
assert_eq!(sig.subpacket(SubpacketTag::SignatureCreationTime),
Some(Subpacket {
critical: false,
tag: SubpacketTag::SignatureCreationTime,
value: SubpacketValue::SignatureCreationTime(
time::Tm::from_pgp(1515791223))
}));
assert_eq!(sig.trust_signature(), Some((2, 120)));
assert_eq!(sig.subpacket(SubpacketTag::TrustSignature),
Some(Subpacket {
critical: false,
tag: SubpacketTag::TrustSignature,
value: SubpacketValue::TrustSignature {
level: 2,
trust: 120,
},
}));
let regex = &b"<[^>]+[@.]navy\\.mil>$"[..];
assert_eq!(sig.regular_expression(), Some(regex));
assert_eq!(sig.subpacket(SubpacketTag::RegularExpression),
Some(Subpacket {
critical: true,
tag: SubpacketTag::RegularExpression,
value: SubpacketValue::RegularExpression(regex)
}));
}
if let Some(&Packet::Signature(ref sig)) = pile.children().nth(11) {
assert_eq!(sig.key_expiration_time(),
Some(time::Duration::from_pgp(63072000)));
assert_eq!(sig.subpacket(SubpacketTag::KeyExpirationTime),
Some(Subpacket {
critical: false,
tag: SubpacketTag::KeyExpirationTime,
value: SubpacketValue::KeyExpirationTime(
time::Duration::from_pgp(63072000))
}));
let keyid = KeyID::from_hex("CEAD 0621 0934 7957").unwrap();
assert_eq!(sig.issuer(), Some(keyid.clone()));
assert_eq!(sig.subpacket(SubpacketTag::Issuer),
Some(Subpacket {
critical: false,
tag: SubpacketTag::Issuer,
value: SubpacketValue::Issuer(keyid)
}));
let fp = Fingerprint::from_hex(
"B59B8817F519DCE10AFD85E4CEAD062109347957").unwrap();
assert_eq!(sig.issuer_fingerprint(), Some(fp.clone()));
assert_eq!(sig.subpacket(SubpacketTag::IssuerFingerprint),
Some(Subpacket {
critical: false,
tag: SubpacketTag::IssuerFingerprint,
value: SubpacketValue::IssuerFingerprint(fp)
}));
assert!(sig.embedded_signature().is_some());
assert!(sig.subpacket(SubpacketTag::EmbeddedSignature)
.is_some());
}
}