webrtc_dtls/signature_hash_algorithm/
mod.rs

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