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
|
use crate::crypto::hash::Digest;
use crate::Result;
pub(crate) fn build() -> sha1collisiondetection::Sha1CD {
sha1collisiondetection::Builder::default()
.detect_collisions(true)
.use_ubc(true)
.safe_hash(true)
.build()
}
impl Digest for sha1collisiondetection::Sha1CD {
fn digest_size(&self) -> usize {
20
}
fn update(&mut self, data: &[u8]) {
digest::Update::update(self, data);
}
fn digest(&mut self, digest: &mut [u8]) -> Result<()> {
let mut d =
generic_array::GenericArray::<u8, digest::consts::U20>::default();
let r = self.finalize_into_dirty_cd(&mut d);
digest::Reset::reset(self);
let l = digest.len().min(d.len());
&mut digest[..l].copy_from_slice(&d[..l]);
r.map_err(Into::into)
}
}
#[cfg(test)]
mod test {
use crate::*;
use crate::parse::Parse;
/// Test vector from the "SHA-1 is a Shambles" paper.
///
/// The scenario is the following. Bob obtains a certification
/// from a CA, and transfers it to a key claiming to belong to
/// Alice. Now the CA certifies an illegitimate binding to
/// Alice's userid.
#[test]
fn shambles() -> Result<()> {
let alice =
PacketPile::from_bytes(crate::tests::key("sha-mbles.alice.asc"))?;
let bob =
PacketPile::from_bytes(crate::tests::key("sha-mbles.bob.asc"))?;
let ca_keyid: KeyID = "AFBB 1FED 6951 A956".parse()?;
assert_eq!(alice.children().count(), 4);
assert_eq!(bob.children().count(), 7);
let alice_sha1_fingerprint: Fingerprint =
"43CD 5C5B 04FF 5742 FA14 1ABC A9D7 55A9 6354 8C78".parse()?;
let bob_sha1_fingerprint: Fingerprint =
"C6BF E2FC BBE5 1A89 2BEB 7798 1233 D4CC 61DB D9C4".parse()?;
let alice_sha1cd_fingerprint: Fingerprint =
"4D84 B08A A181 21DB D79E EA05 9CD0 8D5B 1680 87E2".parse()?;
let bob_sha1cd_fingerprint: Fingerprint =
"6434 B04B 4648 BA41 15BD C5C2 B67A DB26 6F74 DF89".parse()?;
// The illegitimate certification is on Bob's user attribute.
assert_eq!(bob.path_ref(&[6]).unwrap(), alice.path_ref(&[3]).unwrap());
match bob.path_ref(&[6]).unwrap() {
Packet::Signature(s) => {
assert_eq!(s.issuers().nth(0).unwrap(), &ca_keyid);
},
o => panic!("unexpected packet: {:?}", o),
}
let alice = Cert::from_packets(alice.into_children())?;
let bob = Cert::from_packets(bob.into_children())?;
// Check mitigations. First, the illegitimate certification
// should be discarded.
assert_eq!(alice.bad_signatures().len(), 1);
// Bob's userid also got certified, hence there are two bad
// signatures.
assert_eq!(bob.bad_signatures().len(), 2);
// The mitigation also changes the identities of the keys
// containing the collision attack. This is a good thing,
// because we cannot trust SHA-1 to discriminate keys
// containing attacks.
assert!(alice.fingerprint() != alice_sha1_fingerprint);
assert_eq!(alice.fingerprint(), alice_sha1cd_fingerprint);
assert!(bob.fingerprint() != bob_sha1_fingerprint);
assert_eq!(bob.fingerprint(), bob_sha1cd_fingerprint);
Ok(())
}
}
|