1use std::collections::HashMap;
17use std::convert::TryFrom;
18use std::marker::PhantomData;
19use std::sync::LazyLock;
20
21use delegate::delegate;
22use digest::typenum::{U20, U32, U64};
23use hmac::Hmac;
24use sha1::Sha1;
25use sha2::{Sha256, Sha512};
26use ssh_encoding::Encode;
27
28use self::crypto::CryptoMacAlgorithm;
29use self::crypto_etm::CryptoEtmMacAlgorithm;
30use self::none::NoMacAlgorithm;
31
32mod crypto;
33mod crypto_etm;
34mod none;
35
36pub(crate) trait MacAlgorithm {
37 fn key_len(&self) -> usize;
38 fn make_mac(&self, key: &[u8]) -> Box<dyn Mac + Send>;
39}
40
41pub(crate) trait Mac {
42 fn mac_len(&self) -> usize;
43 fn is_etm(&self) -> bool {
44 false
45 }
46 fn compute(&self, sequence_number: u32, payload: &[u8], output: &mut [u8]);
47 fn verify(&self, sequence_number: u32, payload: &[u8], mac: &[u8]) -> bool;
48}
49
50#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
51pub struct Name(&'static str);
52impl AsRef<str> for Name {
53 fn as_ref(&self) -> &str {
54 self.0
55 }
56}
57
58impl Encode for Name {
59 delegate! { to self.as_ref() {
60 fn encoded_len(&self) -> Result<usize, ssh_encoding::Error>;
61 fn encode(&self, writer: &mut impl ssh_encoding::Writer) -> Result<(), ssh_encoding::Error>;
62 }}
63}
64
65impl TryFrom<&str> for Name {
66 type Error = ();
67 fn try_from(s: &str) -> Result<Name, ()> {
68 MACS.keys().find(|x| x.0 == s).map(|x| **x).ok_or(())
69 }
70}
71
72pub const NONE: Name = Name("none");
74pub const HMAC_SHA1: Name = Name("hmac-sha1");
76pub const HMAC_SHA256: Name = Name("hmac-sha2-256");
78pub const HMAC_SHA512: Name = Name("hmac-sha2-512");
80pub const HMAC_SHA1_ETM: Name = Name("hmac-sha1-etm@openssh.com");
82pub const HMAC_SHA256_ETM: Name = Name("hmac-sha2-256-etm@openssh.com");
84pub const HMAC_SHA512_ETM: Name = Name("hmac-sha2-512-etm@openssh.com");
86
87pub(crate) static _NONE: NoMacAlgorithm = NoMacAlgorithm {};
88pub(crate) static _HMAC_SHA1: CryptoMacAlgorithm<Hmac<Sha1>, U20> =
89 CryptoMacAlgorithm(PhantomData, PhantomData);
90pub(crate) static _HMAC_SHA256: CryptoMacAlgorithm<Hmac<Sha256>, U32> =
91 CryptoMacAlgorithm(PhantomData, PhantomData);
92pub(crate) static _HMAC_SHA512: CryptoMacAlgorithm<Hmac<Sha512>, U64> =
93 CryptoMacAlgorithm(PhantomData, PhantomData);
94pub(crate) static _HMAC_SHA1_ETM: CryptoEtmMacAlgorithm<Hmac<Sha1>, U20> =
95 CryptoEtmMacAlgorithm(PhantomData, PhantomData);
96pub(crate) static _HMAC_SHA256_ETM: CryptoEtmMacAlgorithm<Hmac<Sha256>, U32> =
97 CryptoEtmMacAlgorithm(PhantomData, PhantomData);
98pub(crate) static _HMAC_SHA512_ETM: CryptoEtmMacAlgorithm<Hmac<Sha512>, U64> =
99 CryptoEtmMacAlgorithm(PhantomData, PhantomData);
100
101pub const ALL_MAC_ALGORITHMS: &[&Name] = &[
102 &NONE,
103 &HMAC_SHA1,
104 &HMAC_SHA256,
105 &HMAC_SHA512,
106 &HMAC_SHA1_ETM,
107 &HMAC_SHA256_ETM,
108 &HMAC_SHA512_ETM,
109];
110
111pub(crate) static MACS: LazyLock<HashMap<&'static Name, &(dyn MacAlgorithm + Send + Sync)>> =
112 LazyLock::new(|| {
113 let mut h: HashMap<&'static Name, &(dyn MacAlgorithm + Send + Sync)> = HashMap::new();
114 h.insert(&NONE, &_NONE);
115 h.insert(&HMAC_SHA1, &_HMAC_SHA1);
116 h.insert(&HMAC_SHA256, &_HMAC_SHA256);
117 h.insert(&HMAC_SHA512, &_HMAC_SHA512);
118 h.insert(&HMAC_SHA1_ETM, &_HMAC_SHA1_ETM);
119 h.insert(&HMAC_SHA256_ETM, &_HMAC_SHA256_ETM);
120 h.insert(&HMAC_SHA512_ETM, &_HMAC_SHA512_ETM);
121 assert_eq!(h.len(), ALL_MAC_ALGORITHMS.len());
122 h
123 });