libp2p_webrtc_utils/
fingerprint.rs

1// Copyright 2023 Doug Anderson.
2// Copyright 2022 Parity Technologies (UK) Ltd.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22use std::fmt;
23
24use libp2p_core::multihash;
25use sha2::Digest as _;
26
27pub const SHA256: &str = "sha-256";
28const MULTIHASH_SHA256_CODE: u64 = 0x12;
29
30type Multihash = multihash::Multihash<64>;
31
32/// A certificate fingerprint that is assumed to be created using the SHA256 hash algorithm.
33#[derive(Eq, PartialEq, Copy, Clone)]
34pub struct Fingerprint([u8; 32]);
35
36impl Fingerprint {
37    pub const FF: Fingerprint = Fingerprint([0xFF; 32]);
38
39    pub const fn raw(digest: [u8; 32]) -> Self {
40        Fingerprint(digest)
41    }
42
43    /// Creates a new [Fingerprint] from a raw certificate by hashing the given bytes with SHA256.
44    pub fn from_certificate(bytes: &[u8]) -> Self {
45        Fingerprint(sha2::Sha256::digest(bytes).into())
46    }
47
48    /// Converts [`Multihash`](multihash::Multihash) to [`Fingerprint`].
49    pub fn try_from_multihash(hash: Multihash) -> Option<Self> {
50        if hash.code() != MULTIHASH_SHA256_CODE {
51            // Only support SHA256 for now.
52            return None;
53        }
54
55        let bytes = hash.digest().try_into().ok()?;
56
57        Some(Self(bytes))
58    }
59
60    /// Converts this fingerprint to [`Multihash`](multihash::Multihash).
61    pub fn to_multihash(self) -> Multihash {
62        Multihash::wrap(MULTIHASH_SHA256_CODE, &self.0).expect("fingerprint's len to be 32 bytes")
63    }
64
65    /// Formats this fingerprint as uppercase hex, separated by colons (`:`).
66    ///
67    /// This is the format described in <https://www.rfc-editor.org/rfc/rfc4572#section-5>.
68    pub fn to_sdp_format(self) -> String {
69        self.0.map(|byte| format!("{byte:02X}")).join(":")
70    }
71
72    /// Returns the algorithm used (e.g. "sha-256").
73    /// See <https://datatracker.ietf.org/doc/html/rfc8122#section-5>
74    pub fn algorithm(&self) -> String {
75        SHA256.to_owned()
76    }
77}
78
79impl fmt::Debug for Fingerprint {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        f.write_str(&hex::encode(self.0))
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    const SDP_FORMAT: &str = "7D:E3:D8:3F:81:A6:80:59:2A:47:1E:6B:6A:BB:07:47:AB:D3:53:85:A8:09:3F:DF:E1:12:C1:EE:BB:6C:C6:AC";
90    const REGULAR_FORMAT: [u8; 32] =
91        hex_literal::hex!("7DE3D83F81A680592A471E6B6ABB0747ABD35385A8093FDFE112C1EEBB6CC6AC");
92
93    #[test]
94    fn sdp_format() {
95        let fp = Fingerprint::raw(REGULAR_FORMAT);
96
97        let formatted = fp.to_sdp_format();
98
99        assert_eq!(formatted, SDP_FORMAT)
100    }
101
102    #[test]
103    fn from_sdp() {
104        let mut bytes = [0; 32];
105        bytes.copy_from_slice(&hex::decode(SDP_FORMAT.replace(':', "")).unwrap());
106
107        let fp = Fingerprint::raw(bytes);
108        assert_eq!(fp, Fingerprint::raw(REGULAR_FORMAT));
109    }
110}