1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
use std::hash::{Hash, Hasher};
use std::cmp::Ordering;
use crate::packet::Tag;
use crate::packet;
use crate::Packet;
use crate::policy::HashAlgoSecurity;
/// Holds an unknown packet.
///
/// This is used by the parser to hold packets that it doesn't know
/// how to process rather than abort.
///
/// This packet effectively holds a binary blob.
///
/// # A note on equality
///
/// Two `Unknown` packets are considered equal if their tags and their
/// bodies are equal.
#[derive(Debug)]
pub struct Unknown {
/// CTB packet header fields.
pub(crate) common: packet::Common,
/// Packet tag.
tag: Tag,
/// Error that caused parsing or processing to abort.
error: anyhow::Error,
/// The unknown data packet is a container packet, but cannot
/// store packets.
///
/// This is written when serialized, and set by the packet parser
/// if `buffer_unread_content` is used.
container: packet::Container,
}
assert_send_and_sync!(Unknown);
impl PartialEq for Unknown {
fn eq(&self, other: &Unknown) -> bool {
self.tag == other.tag
&& self.container == other.container
}
}
impl Eq for Unknown { }
impl Hash for Unknown {
fn hash<H: Hasher>(&self, state: &mut H) {
self.tag.hash(state);
self.container.hash(state);
}
}
impl Clone for Unknown {
fn clone(&self) -> Self {
Unknown {
common: self.common.clone(),
tag: self.tag,
error: {
// anyhow::Error isn't Clone, so we cannot, in
// general, duplicate the error without losing
// information. We can try to downcast to the most
// likely errors, and clone them, but this can never
// cover all possibilities.
use std::io;
if let Some(e) = self.error.downcast_ref::<crate::Error>() {
e.clone().into()
} else if let Some(e) = self.error.downcast_ref::<io::Error>() {
if let Some(wrapped) = e.get_ref() {
// The wrapped error isn't clone, so this
// loses information here. This will always
// be lossy, even once we changed this crate
// to return concrete errors.
io::Error::new(e.kind(), wrapped.to_string()).into()
} else {
io::Error::from(e.kind()).into()
}
} else {
// Here, we lose information, but the conversion
// was lossy before.
crate::Error::InvalidOperation(self.error.to_string())
.into()
}
},
container: self.container.clone(),
}
}
}
impl Unknown {
/// Returns a new `Unknown` packet.
pub fn new(tag: Tag, error: anyhow::Error) -> Self {
Unknown {
common: Default::default(),
tag,
error,
container: packet::Container::default_unprocessed(),
}
}
/// The security requirements of the hash algorithm for
/// self-signatures.
///
/// A cryptographic hash algorithm usually has [three security
/// properties]: pre-image resistance, second pre-image
/// resistance, and collision resistance. If an attacker can
/// influence the signed data, then the hash algorithm needs to
/// have both second pre-image resistance, and collision
/// resistance. If not, second pre-image resistance is
/// sufficient.
///
/// [three security properties]: https://en.wikipedia.org/wiki/Cryptographic_hash_function#Properties
///
/// In general, an attacker may be able to influence third-party
/// signatures. But direct key signatures, and binding signatures
/// are only over data fully determined by signer. And, an
/// attacker's control over self signatures over User IDs is
/// limited due to their structure.
///
/// These observations can be used to extend the life of a hash
/// algorithm after its collision resistance has been partially
/// compromised, but not completely broken. For more details,
/// please refer to the documentation for [HashAlgoSecurity].
///
/// [HashAlgoSecurity]: crate::policy::HashAlgoSecurity
pub fn hash_algo_security(&self) -> HashAlgoSecurity {
HashAlgoSecurity::CollisionResistance
}
/// Gets the unknown packet's tag.
pub fn tag(&self) -> Tag {
self.tag
}
/// Sets the unknown packet's tag.
pub fn set_tag(&mut self, tag: Tag) -> Tag {
::std::mem::replace(&mut self.tag, tag)
}
/// Gets the unknown packet's error.
///
/// This is the error that caused parsing or processing to abort.
pub fn error(&self) -> &anyhow::Error {
&self.error
}
/// Sets the unknown packet's error.
///
/// This is the error that caused parsing or processing to abort.
pub fn set_error(&mut self, error: anyhow::Error) -> anyhow::Error {
::std::mem::replace(&mut self.error, error)
}
/// Returns the error.
pub fn into_error(self) -> anyhow::Error {
self.error
}
/// Best effort Ord implementation.
///
/// The Cert canonicalization needs to order Unknown packets.
/// However, due to potential streaming, Unknown cannot implement
/// Eq. This is cheating a little, we simply ignore the streaming
/// case.
pub(crate) // For cert/mod.rs
fn best_effort_cmp(&self, other: &Unknown) -> Ordering {
self.tag.cmp(&other.tag).then_with(|| self.body().cmp(other.body()))
}
}
impl_body_forwards!(Unknown);
impl From<Unknown> for Packet {
fn from(s: Unknown) -> Self {
Packet::Unknown(s)
}
}
impl std::convert::TryFrom<Packet> for Unknown {
type Error = crate::Error;
/// Tries to convert a packet to an `Unknown`. Returns an error
/// if the given packet is a container packet (i.e. a compressed
/// data packet or an encrypted data packet of any kind).
fn try_from(p: Packet) -> std::result::Result<Self, Self::Error> {
use std::ops::Deref;
use packet::{Any, Body, Common, Container};
use crate::serialize::MarshalInto;
let tag = p.tag();
// First, short-circuit happy and unhappy paths so that we
// avoid copying the potentially large packet parser maps in
// common.
match &p {
// Happy path.
Packet::Unknown(_) =>
return Ok(p.downcast().expect("is an unknown")),
// The container packets we flat-out refuse to convert.
// The Unknown packet has an unprocessed body, and we
// cannot recreate that from processed or structured
// bodies.
Packet::CompressedData(_)
| Packet::SEIP(_)
| Packet::AED(_) =>
return Err(Self::Error::InvalidOperation(
format!("Cannot convert {} to unknown packets", tag))),
_ => (),
}
// Now we copy the common bits that we'll need.
let common = p.deref().clone();
fn convert<V>(tag: Tag, common: Common, body: V)
-> Result<Unknown, crate::Error>
where
V: MarshalInto,
{
let container = {
let mut c = Container::default_unprocessed();
c.set_body(Body::Unprocessed(
body.to_vec().expect("infallible serialization")));
c
};
Ok(Unknown {
container,
common,
tag,
error: crate::Error::MalformedPacket(
format!("Implicit conversion from {} to unknown packet",
tag)).into(),
})
}
match p {
// Happy path.
Packet::Unknown(_) => unreachable!("handled above"),
// These packets convert infallibly.
Packet::Signature(v) => convert(tag, common, v),
Packet::OnePassSig(v) => convert(tag, common, v),
Packet::PublicKey(v) => convert(tag, common, v),
Packet::PublicSubkey(v) => convert(tag, common, v),
Packet::SecretKey(v) => convert(tag, common, v),
Packet::SecretSubkey(v) => convert(tag, common, v),
Packet::Marker(v) => convert(tag, common, v),
Packet::Trust(v) => convert(tag, common, v),
Packet::UserID(v) => convert(tag, common, v),
Packet::UserAttribute(v) => convert(tag, common, v),
Packet::PKESK(v) => convert(tag, common, v),
Packet::SKESK(v) => convert(tag, common, v),
#[allow(deprecated)]
Packet::MDC(v) => convert(tag, common, v),
// Here we can avoid copying the body.
Packet::Literal(mut v) => {
let container = {
let mut c = Container::default_unprocessed();
// Get v's body out without copying.
c.set_body(Body::Unprocessed(v.set_body(
Vec::with_capacity(0))));
c
};
let common = v.common.clone(); // XXX why can't I decompose `p`?
Ok(Unknown {
container,
common,
tag,
error: crate::Error::MalformedPacket(
format!("Implicit conversion from {} to unknown packet",
tag)).into(),
})
},
// The container packets we flat-out refuse to convert.
// The Unknown packet has an unprocessed body, and we
// cannot recreate that from processed or structured
// bodies.
Packet::CompressedData(_)
| Packet::SEIP(_)
| Packet::AED(_) => unreachable!("handled above"),
}
}
}