Skip to main content

rtc_dtls/signature_hash_algorithm/
mod.rs

1#[cfg(test)]
2mod signature_hash_algorithm_test;
3
4use std::fmt;
5
6use crate::crypto::*;
7use shared::error::*;
8
9// HashAlgorithm is used to indicate the hash algorithm used
10// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
11// Supported hash hash algorithms
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13/// The hash algorithms that may be paired with a signature algorithm.
14pub enum HashAlgorithm {
15    /// `MD2` (`0`).
16    Md2 = 0, // Blacklisted
17    /// `MD5` (`1`).
18    Md5 = 1, // Blacklisted
19    /// `SHA1` (`2`).
20    Sha1 = 2, // Blacklisted
21    /// `SHA224` (`3`).
22    Sha224 = 3,
23    /// `SHA256` (`4`).
24    Sha256 = 4,
25    /// `SHA384` (`5`).
26    Sha384 = 5,
27    /// `SHA512` (`6`).
28    Sha512 = 6,
29    /// `ED25519` (`8`).
30    Ed25519 = 8,
31    /// An algorithm this crate does not implement.
32    Unsupported,
33}
34
35impl From<u8> for HashAlgorithm {
36    fn from(val: u8) -> Self {
37        match val {
38            0 => HashAlgorithm::Md2,
39            1 => HashAlgorithm::Md5,
40            2 => HashAlgorithm::Sha1,
41            3 => HashAlgorithm::Sha224,
42            4 => HashAlgorithm::Sha256,
43            5 => HashAlgorithm::Sha384,
44            6 => HashAlgorithm::Sha512,
45            8 => HashAlgorithm::Ed25519,
46            _ => HashAlgorithm::Unsupported,
47        }
48    }
49}
50
51impl fmt::Display for HashAlgorithm {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match *self {
54            HashAlgorithm::Md2 => write!(f, "md2"),
55            HashAlgorithm::Md5 => write!(f, "md5"), // [RFC3279]
56            HashAlgorithm::Sha1 => write!(f, "sha-1"), // [RFC3279]
57            HashAlgorithm::Sha224 => write!(f, "sha-224"), // [RFC4055]
58            HashAlgorithm::Sha256 => write!(f, "sha-256"), // [RFC4055]
59            HashAlgorithm::Sha384 => write!(f, "sha-384"), // [RFC4055]
60            HashAlgorithm::Sha512 => write!(f, "sha-512"), // [RFC4055]
61            HashAlgorithm::Ed25519 => write!(f, "null"), // [RFC4055]
62            _ => write!(f, "unknown or unsupported hash algorithm"),
63        }
64    }
65}
66
67impl HashAlgorithm {
68    pub(crate) fn insecure(&self) -> bool {
69        matches!(
70            *self,
71            HashAlgorithm::Md2 | HashAlgorithm::Md5 | HashAlgorithm::Sha1
72        )
73    }
74
75    pub(crate) fn invalid(&self) -> bool {
76        matches!(*self, HashAlgorithm::Md2)
77    }
78}
79
80// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
81#[derive(Copy, Clone, Debug, PartialEq, Eq)]
82/// The signature algorithms this crate can verify and produce.
83pub enum SignatureAlgorithm {
84    /// `RSA` (`1`).
85    Rsa = 1,
86    /// `ECDSA` (`3`).
87    Ecdsa = 3,
88    /// `ED25519` (`7`).
89    Ed25519 = 7,
90    /// An algorithm this crate does not implement.
91    Unsupported,
92}
93
94impl From<u8> for SignatureAlgorithm {
95    fn from(val: u8) -> Self {
96        match val {
97            1 => SignatureAlgorithm::Rsa,
98            3 => SignatureAlgorithm::Ecdsa,
99            7 => SignatureAlgorithm::Ed25519,
100            _ => SignatureAlgorithm::Unsupported,
101        }
102    }
103}
104
105#[derive(Copy, Clone, Debug, PartialEq, Eq)]
106/// A signature and hash pair, as negotiated for certificate verification.
107pub struct SignatureHashAlgorithm {
108    /// The hash to digest the signed data with.
109    pub hash: HashAlgorithm,
110    /// The signature algorithm to apply.
111    pub signature: SignatureAlgorithm,
112}
113
114impl SignatureHashAlgorithm {
115    // is_compatible checks that given private key is compatible with the signature scheme.
116    pub(crate) fn is_compatible(&self, private_key: &CryptoPrivateKey) -> bool {
117        match &private_key.kind {
118            CryptoPrivateKeyKind::Ed25519(_) => self.signature == SignatureAlgorithm::Ed25519,
119            CryptoPrivateKeyKind::Ecdsa256(_) => self.signature == SignatureAlgorithm::Ecdsa,
120            CryptoPrivateKeyKind::Rsa256(_) => self.signature == SignatureAlgorithm::Rsa,
121            CryptoPrivateKeyKind::Custom(_) => true,
122        }
123    }
124}
125
126pub(crate) fn default_signature_schemes() -> Vec<SignatureHashAlgorithm> {
127    vec![
128        SignatureHashAlgorithm {
129            hash: HashAlgorithm::Sha256,
130            signature: SignatureAlgorithm::Ecdsa,
131        },
132        SignatureHashAlgorithm {
133            hash: HashAlgorithm::Sha384,
134            signature: SignatureAlgorithm::Ecdsa,
135        },
136        SignatureHashAlgorithm {
137            hash: HashAlgorithm::Sha512,
138            signature: SignatureAlgorithm::Ecdsa,
139        },
140        SignatureHashAlgorithm {
141            hash: HashAlgorithm::Sha256,
142            signature: SignatureAlgorithm::Rsa,
143        },
144        SignatureHashAlgorithm {
145            hash: HashAlgorithm::Sha384,
146            signature: SignatureAlgorithm::Rsa,
147        },
148        SignatureHashAlgorithm {
149            hash: HashAlgorithm::Sha512,
150            signature: SignatureAlgorithm::Rsa,
151        },
152        SignatureHashAlgorithm {
153            hash: HashAlgorithm::Ed25519,
154            signature: SignatureAlgorithm::Ed25519,
155        },
156    ]
157}
158
159// select Signature Scheme returns most preferred and compatible scheme.
160pub(crate) fn select_signature_scheme(
161    sigs: &[SignatureHashAlgorithm],
162    private_key: &CryptoPrivateKey,
163) -> Result<SignatureHashAlgorithm> {
164    for ss in sigs {
165        if ss.is_compatible(private_key) {
166            return Ok(*ss);
167        }
168    }
169
170    Err(Error::ErrNoAvailableSignatureSchemes)
171}
172
173// SignatureScheme identifies a signature algorithm supported by TLS. See
174// RFC 8446, Section 4.2.3.
175#[derive(Copy, Clone, Debug, PartialEq, Eq)]
176/// A TLS signature scheme, which names a signature and hash together ([RFC 8446] ยง4.2.3).
177pub enum SignatureScheme {
178    // RSASSA-PKCS1-v1_5 algorithms.
179    /// `PKCS1_WITH_SHA256` (`0x0401`).
180    Pkcs1WithSha256 = 0x0401,
181    /// `PKCS1_WITH_SHA384` (`0x0501`).
182    Pkcs1WithSha384 = 0x0501,
183    /// `PKCS1_WITH_SHA512` (`0x0601`).
184    Pkcs1WithSha512 = 0x0601,
185
186    // RSASSA-PSS algorithms with public key OID rsaEncryption.
187    /// `PSS_WITH_SHA256` (`0x0804`).
188    PssWithSha256 = 0x0804,
189    /// `PSS_WITH_SHA384` (`0x0805`).
190    PssWithSha384 = 0x0805,
191    /// `PSS_WITH_SHA512` (`0x0806`).
192    PssWithSha512 = 0x0806,
193
194    // ECDSA algorithms. Only constrained to a specific curve in TLS 1.3.
195    /// `ECDSA_WITH_P256_AND_SHA256` (`0x0403`).
196    EcdsaWithP256AndSha256 = 0x0403,
197    /// `ECDSA_WITH_P384_AND_SHA384` (`0x0503`).
198    EcdsaWithP384AndSha384 = 0x0503,
199    /// `ECDSA_WITH_P521_AND_SHA512` (`0x0603`).
200    EcdsaWithP521AndSha512 = 0x0603,
201
202    // EdDSA algorithms.
203    /// `ED25519` (`0x0807`).
204    Ed25519 = 0x0807,
205
206    // Legacy signature and hash algorithms for TLS 1.2.
207    /// `PKCS1_WITH_SHA1` (`0x0201`).
208    Pkcs1WithSha1 = 0x0201,
209    /// `ECDSA_WITH_SHA1` (`0x0203`).
210    EcdsaWithSha1 = 0x0203,
211}
212
213// parse_signature_schemes translates []tls.SignatureScheme to []signatureHashAlgorithm.
214// It returns default signature scheme list if no SignatureScheme is passed.
215pub(crate) fn parse_signature_schemes(
216    sigs: &[u16],
217    insecure_hashes: bool,
218) -> Result<Vec<SignatureHashAlgorithm>> {
219    if sigs.is_empty() {
220        return Ok(default_signature_schemes());
221    }
222
223    let mut out = vec![];
224    for ss in sigs {
225        let sig: SignatureAlgorithm = ((*ss & 0xFF) as u8).into();
226        if sig == SignatureAlgorithm::Unsupported {
227            return Err(Error::ErrInvalidSignatureAlgorithm);
228        }
229        let h: HashAlgorithm = (((*ss >> 8) & 0xFF) as u8).into();
230        if h == HashAlgorithm::Unsupported || h.invalid() {
231            return Err(Error::ErrInvalidHashAlgorithm);
232        }
233        if h.insecure() && !insecure_hashes {
234            continue;
235        }
236        out.push(SignatureHashAlgorithm {
237            hash: h,
238            signature: sig,
239        })
240    }
241
242    if out.is_empty() {
243        Err(Error::ErrNoAvailableSignatureSchemes)
244    } else {
245        Ok(out)
246    }
247}