use base64ct::{Base64, Encoding};
pub(crate) const WS_MAGIC_GUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
pub(crate) fn b64_encode(input: &[u8]) -> String {
Base64::encode_string(input)
}
pub(crate) fn compute_accept(key_b64: &str) -> String {
let mut buf = String::with_capacity(key_b64.len() + WS_MAGIC_GUID.len());
buf.push_str(key_b64);
buf.push_str(WS_MAGIC_GUID);
b64_encode(&sha1(buf.as_bytes()))
}
fn sha1(input: &[u8]) -> [u8; 20] {
let mut h0: u32 = 0x67452301;
let mut h1: u32 = 0xEFCDAB89;
let mut h2: u32 = 0x98BADCFE;
let mut h3: u32 = 0x10325476;
let mut h4: u32 = 0xC3D2E1F0;
let bit_len = (input.len() as u64).wrapping_mul(8);
let mut padded = Vec::with_capacity(input.len() + 64);
padded.extend_from_slice(input);
padded.push(0x80);
while padded.len() % 64 != 56 {
padded.push(0);
}
padded.extend_from_slice(&bit_len.to_be_bytes());
let mut w = [0u32; 80];
for chunk in padded.chunks_exact(64) {
for (i, word) in chunk.chunks_exact(4).enumerate() {
w[i] = u32::from_be_bytes([word[0], word[1], word[2], word[3]]);
}
for i in 16..80 {
w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
}
let (mut a, mut b, mut c, mut d, mut e) = (h0, h1, h2, h3, h4);
for (i, &wi) in w.iter().enumerate() {
let (f, k) = match i {
0..=19 => ((b & c) | ((!b) & d), 0x5A827999u32),
20..=39 => (b ^ c ^ d, 0x6ED9EBA1u32),
40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1BBCDCu32),
_ => (b ^ c ^ d, 0xCA62C1D6u32),
};
let temp = a
.rotate_left(5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(wi);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = temp;
}
h0 = h0.wrapping_add(a);
h1 = h1.wrapping_add(b);
h2 = h2.wrapping_add(c);
h3 = h3.wrapping_add(d);
h4 = h4.wrapping_add(e);
}
let mut out = [0u8; 20];
for (i, h) in [h0, h1, h2, h3, h4].iter().enumerate() {
out[i * 4..i * 4 + 4].copy_from_slice(&h.to_be_bytes());
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sec_websocket_accept_matches_rfc6455_example() {
assert_eq!(
compute_accept("dGhlIHNhbXBsZSBub25jZQ=="),
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
);
}
#[test]
fn sha1_empty_input_matches_rfc3174_example() {
let d = sha1(b"");
assert_eq!(
d,
[
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60,
0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
]
);
}
#[test]
fn b64_encode_round_trips_simple_bytes() {
assert_eq!(b64_encode(b"hello"), "aGVsbG8=");
assert_eq!(b64_encode(b""), "");
}
}