Skip to main content

codetics_ocpp_ws/
sha1.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Kevin Vavelin, Codetics (https://codetics.fr)
3
4//! SHA-1 (FIPS 180-4), used only to compute `Sec-WebSocket-Accept`.
5//! SHA-1 is not used for any security property here; RFC 6455 mandates it
6//! purely as a handshake checksum.
7
8pub fn sha1(data: &[u8]) -> [u8; 20] {
9    let mut h: [u32; 5] = [
10        0x6745_2301,
11        0xEFCD_AB89,
12        0x98BA_DCFE,
13        0x1032_5476,
14        0xC3D2_E1F0,
15    ];
16    let bit_len = (data.len() as u64).wrapping_mul(8);
17    let mut msg = data.to_vec();
18    msg.push(0x80);
19    while msg.len() % 64 != 56 {
20        msg.push(0);
21    }
22    msg.extend_from_slice(&bit_len.to_be_bytes());
23
24    for chunk in msg.as_chunks::<64>().0 {
25        let mut w = [0u32; 80];
26        for (i, word) in chunk.as_chunks::<4>().0.iter().enumerate() {
27            w[i] = u32::from_be_bytes([word[0], word[1], word[2], word[3]]);
28        }
29        for i in 16..80 {
30            w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
31        }
32        let [mut a, mut b, mut c, mut d, mut e] = h;
33        for (i, wi) in w.iter().enumerate() {
34            let (f, k) = match i {
35                0..=19 => ((b & c) | (!b & d), 0x5A82_7999),
36                20..=39 => (b ^ c ^ d, 0x6ED9_EBA1),
37                40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1B_BCDC),
38                _ => (b ^ c ^ d, 0xCA62_C1D6),
39            };
40            let tmp = a
41                .rotate_left(5)
42                .wrapping_add(f)
43                .wrapping_add(e)
44                .wrapping_add(k)
45                .wrapping_add(*wi);
46            e = d;
47            d = c;
48            c = b.rotate_left(30);
49            b = a;
50            a = tmp;
51        }
52        h[0] = h[0].wrapping_add(a);
53        h[1] = h[1].wrapping_add(b);
54        h[2] = h[2].wrapping_add(c);
55        h[3] = h[3].wrapping_add(d);
56        h[4] = h[4].wrapping_add(e);
57    }
58    let mut out = [0u8; 20];
59    for (i, v) in h.iter().enumerate() {
60        out[i * 4..i * 4 + 4].copy_from_slice(&v.to_be_bytes());
61    }
62    out
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    fn hex(b: &[u8]) -> String {
70        b.iter().map(|x| format!("{x:02x}")).collect()
71    }
72
73    #[test]
74    fn vectors() {
75        assert_eq!(hex(&sha1(b"")), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
76        assert_eq!(
77            hex(&sha1(b"abc")),
78            "a9993e364706816aba3e25717850c26c9cd0d89d"
79        );
80        assert_eq!(
81            hex(&sha1(
82                b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
83            )),
84            "84983e441c3bd26ebaae4aa1f95129e5e54670f1"
85        );
86        assert_eq!(
87            hex(&sha1(&[b'a'; 1_000_000])),
88            "34aa973cd4c4daa4f61eeb2bdbad27316534016f"
89        );
90    }
91}