summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/sha1cd.rs
blob: d5c44ebb547a4148abc848a9aaf08dd9870259c4 (plain)
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(())
    }
}