use sha1::{Sha1, Digest};
use std::time::{SystemTime, UNIX_EPOCH};
pub fn calculate_fingerprint_rsa(
n: &[u8], e: &[u8], timestamp: u32,
) -> Vec<u8> {
let n_bits = (n.len() * 8) as u16;
let e_bits = (e.len() * 8) as u16;
let mut packet = Vec::new();
packet.push(4); packet.extend_from_slice(×tamp.to_be_bytes());
packet.push(1);
packet.extend_from_slice(&n_bits.to_be_bytes());
packet.extend_from_slice(n);
packet.extend_from_slice(&e_bits.to_be_bytes());
packet.extend_from_slice(e);
let packet_len = packet.len() as u16;
let mut hasher = Sha1::new();
hasher.update([0x99]);
hasher.update(packet_len.to_be_bytes());
hasher.update(&packet);
hasher.finalize().to_vec()
}
pub fn calculate_fingerprint_eddsa(
public_key: &[u8], timestamp: u32,
) -> Vec<u8> {
let oid = [0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01];
let mut packet = Vec::new();
packet.push(4); packet.extend_from_slice(×tamp.to_be_bytes());
packet.push(22);
packet.push(oid.len() as u8);
packet.extend_from_slice(&oid);
let pk_with_prefix = [&[0x40], public_key].concat();
let pk_bits = (pk_with_prefix.len() * 8) as u16;
packet.extend_from_slice(&pk_bits.to_be_bytes());
packet.extend_from_slice(&pk_with_prefix);
let packet_len = packet.len() as u16;
let mut hasher = Sha1::new();
hasher.update([0x99]);
hasher.update(packet_len.to_be_bytes());
hasher.update(&packet);
hasher.finalize().to_vec()
}
pub fn calculate_fingerprint_ecdh_x25519(
public_key: &[u8], timestamp: u32,
) -> Vec<u8> {
let oid = [0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01];
let mut packet = Vec::new();
packet.push(4); packet.extend_from_slice(×tamp.to_be_bytes());
packet.push(18);
packet.push(oid.len() as u8);
packet.extend_from_slice(&oid);
let pk_with_prefix = [&[0x40], public_key].concat();
let pk_bits = (pk_with_prefix.len() * 8) as u16;
packet.extend_from_slice(&pk_bits.to_be_bytes());
packet.extend_from_slice(&pk_with_prefix);
packet.push(3); packet.push(1); packet.push(8); packet.push(7);
let packet_len = packet.len() as u16;
let mut hasher = Sha1::new();
hasher.update([0x99]);
hasher.update(packet_len.to_be_bytes());
hasher.update(&packet);
hasher.finalize().to_vec()
}
pub fn calculate_fingerprint_ecdsa(
public_key: &[u8], curve_oid: &[u8],
timestamp: u32,
) -> Vec<u8> {
let mut packet = Vec::new();
packet.push(4); packet.extend_from_slice(×tamp.to_be_bytes());
packet.push(19);
packet.push(curve_oid.len() as u8);
packet.extend_from_slice(curve_oid);
let pk_bits = (public_key.len() * 8) as u16;
packet.extend_from_slice(&pk_bits.to_be_bytes());
packet.extend_from_slice(public_key);
let packet_len = packet.len() as u16;
let mut hasher = Sha1::new();
hasher.update([0x99]);
hasher.update(packet_len.to_be_bytes());
hasher.update(&packet);
hasher.finalize().to_vec()
}
pub fn calculate_fingerprint_ecdh(
public_key: &[u8], curve_oid: &[u8],
timestamp: u32,
) -> Vec<u8> {
let mut packet = Vec::new();
packet.push(4); packet.extend_from_slice(×tamp.to_be_bytes());
packet.push(18);
packet.push(curve_oid.len() as u8);
packet.extend_from_slice(curve_oid);
let pk_bits = (public_key.len() * 8) as u16;
packet.extend_from_slice(&pk_bits.to_be_bytes());
packet.extend_from_slice(public_key);
let (hash_algo, cipher_algo) = if public_key.len() == 97 {
(9, 9) } else {
(8, 7) };
packet.push(3); packet.push(1); packet.push(hash_algo);
packet.push(cipher_algo);
let packet_len = packet.len() as u16;
let mut hasher = Sha1::new();
hasher.update([0x99]);
hasher.update(packet_len.to_be_bytes());
hasher.update(&packet);
hasher.finalize().to_vec()
}
pub fn current_timestamp() -> u32 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as u32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fingerprint_length() {
let n = vec![0u8; 256]; let e = vec![0x01, 0x00, 0x01]; let fp = calculate_fingerprint_rsa(&n, &e, 0);
assert_eq!(fp.len(), 20); }
#[test]
fn test_fingerprint_deterministic() {
let n = vec![0u8; 256];
let e = vec![0x01, 0x00, 0x01];
let timestamp = 1234567890u32;
let fp1 = calculate_fingerprint_rsa(&n, &e, timestamp);
let fp2 = calculate_fingerprint_rsa(&n, &e, timestamp);
assert_eq!(fp1, fp2);
}
#[test]
fn test_fingerprint_ed25519_length() {
let public_key = vec![0u8; 32];
let fp = calculate_fingerprint_eddsa(&public_key, 0);
assert_eq!(fp.len(), 20);
}
#[test]
fn test_fingerprint_x25519_length() {
let public_key = vec![0u8; 32];
let fp = calculate_fingerprint_ecdh_x25519(&public_key, 0);
assert_eq!(fp.len(), 20);
}
}