Skip to main content

trustless_protocol/
scheme.rs

1/// Parse a signature scheme name string (e.g., `"ECDSA_NISTP256_SHA256"`) into a rustls `SignatureScheme`.
2///
3/// Returns `None` for unrecognized names.
4pub fn parse_scheme(name: &str) -> Option<rustls::SignatureScheme> {
5    match name {
6        "RSA_PKCS1_SHA256" => Some(rustls::SignatureScheme::RSA_PKCS1_SHA256),
7        "RSA_PKCS1_SHA384" => Some(rustls::SignatureScheme::RSA_PKCS1_SHA384),
8        "RSA_PKCS1_SHA512" => Some(rustls::SignatureScheme::RSA_PKCS1_SHA512),
9        "RSA_PSS_SHA256" => Some(rustls::SignatureScheme::RSA_PSS_SHA256),
10        "RSA_PSS_SHA384" => Some(rustls::SignatureScheme::RSA_PSS_SHA384),
11        "RSA_PSS_SHA512" => Some(rustls::SignatureScheme::RSA_PSS_SHA512),
12        "ECDSA_NISTP256_SHA256" => Some(rustls::SignatureScheme::ECDSA_NISTP256_SHA256),
13        "ECDSA_NISTP384_SHA384" => Some(rustls::SignatureScheme::ECDSA_NISTP384_SHA384),
14        "ECDSA_NISTP521_SHA512" => Some(rustls::SignatureScheme::ECDSA_NISTP521_SHA512),
15        "ED25519" => Some(rustls::SignatureScheme::ED25519),
16        "ED448" => Some(rustls::SignatureScheme::ED448),
17        _ => None,
18    }
19}
20
21/// Convert a rustls `SignatureScheme` to its canonical string name.
22///
23/// Returns `"UNKNOWN"` for unrecognized schemes.
24pub fn scheme_to_string(scheme: rustls::SignatureScheme) -> &'static str {
25    match scheme {
26        rustls::SignatureScheme::RSA_PKCS1_SHA256 => "RSA_PKCS1_SHA256",
27        rustls::SignatureScheme::RSA_PKCS1_SHA384 => "RSA_PKCS1_SHA384",
28        rustls::SignatureScheme::RSA_PKCS1_SHA512 => "RSA_PKCS1_SHA512",
29        rustls::SignatureScheme::RSA_PSS_SHA256 => "RSA_PSS_SHA256",
30        rustls::SignatureScheme::RSA_PSS_SHA384 => "RSA_PSS_SHA384",
31        rustls::SignatureScheme::RSA_PSS_SHA512 => "RSA_PSS_SHA512",
32        rustls::SignatureScheme::ECDSA_NISTP256_SHA256 => "ECDSA_NISTP256_SHA256",
33        rustls::SignatureScheme::ECDSA_NISTP384_SHA384 => "ECDSA_NISTP384_SHA384",
34        rustls::SignatureScheme::ECDSA_NISTP521_SHA512 => "ECDSA_NISTP521_SHA512",
35        rustls::SignatureScheme::ED25519 => "ED25519",
36        rustls::SignatureScheme::ED448 => "ED448",
37        _ => "UNKNOWN",
38    }
39}
40
41fn algorithm_for_scheme(scheme: rustls::SignatureScheme) -> rustls::SignatureAlgorithm {
42    match scheme {
43        rustls::SignatureScheme::RSA_PKCS1_SHA256
44        | rustls::SignatureScheme::RSA_PKCS1_SHA384
45        | rustls::SignatureScheme::RSA_PKCS1_SHA512
46        | rustls::SignatureScheme::RSA_PSS_SHA256
47        | rustls::SignatureScheme::RSA_PSS_SHA384
48        | rustls::SignatureScheme::RSA_PSS_SHA512 => rustls::SignatureAlgorithm::RSA,
49        rustls::SignatureScheme::ECDSA_NISTP256_SHA256
50        | rustls::SignatureScheme::ECDSA_NISTP384_SHA384
51        | rustls::SignatureScheme::ECDSA_NISTP521_SHA512 => rustls::SignatureAlgorithm::ECDSA,
52        rustls::SignatureScheme::ED25519 => rustls::SignatureAlgorithm::ED25519,
53        rustls::SignatureScheme::ED448 => rustls::SignatureAlgorithm::ED448,
54        _ => rustls::SignatureAlgorithm::Unknown(0),
55    }
56}
57
58/// Determine the common `SignatureAlgorithm` for a list of schemes.
59///
60/// Returns `None` if the list is empty or contains schemes from mixed algorithm families
61/// (e.g., both RSA and ECDSA).
62pub fn algorithm_for_schemes(
63    schemes: &[rustls::SignatureScheme],
64) -> Option<rustls::SignatureAlgorithm> {
65    let first = schemes.first()?;
66    let algo = algorithm_for_scheme(*first);
67    // Verify all schemes map to the same algorithm
68    if schemes
69        .iter()
70        .skip(1)
71        .any(|s| algorithm_for_scheme(*s) != algo)
72    {
73        return None;
74    }
75    Some(algo)
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn parse_scheme_round_trip() {
84        let names = [
85            "RSA_PKCS1_SHA256",
86            "RSA_PKCS1_SHA384",
87            "RSA_PKCS1_SHA512",
88            "RSA_PSS_SHA256",
89            "RSA_PSS_SHA384",
90            "RSA_PSS_SHA512",
91            "ECDSA_NISTP256_SHA256",
92            "ECDSA_NISTP384_SHA384",
93            "ECDSA_NISTP521_SHA512",
94            "ED25519",
95            "ED448",
96        ];
97        for name in names {
98            let scheme = parse_scheme(name).unwrap_or_else(|| panic!("failed to parse {name}"));
99            assert_eq!(scheme_to_string(scheme), name);
100        }
101    }
102
103    #[test]
104    fn parse_scheme_unknown() {
105        assert!(parse_scheme("UNKNOWN_SCHEME").is_none());
106    }
107
108    #[test]
109    fn algorithm_for_rsa_schemes() {
110        let schemes = vec![
111            rustls::SignatureScheme::RSA_PSS_SHA256,
112            rustls::SignatureScheme::RSA_PSS_SHA384,
113        ];
114        assert_eq!(
115            algorithm_for_schemes(&schemes),
116            Some(rustls::SignatureAlgorithm::RSA),
117        );
118    }
119
120    #[test]
121    fn algorithm_for_ecdsa_schemes() {
122        let schemes = vec![rustls::SignatureScheme::ECDSA_NISTP256_SHA256];
123        assert_eq!(
124            algorithm_for_schemes(&schemes),
125            Some(rustls::SignatureAlgorithm::ECDSA),
126        );
127    }
128
129    #[test]
130    fn algorithm_for_ed25519() {
131        let schemes = vec![rustls::SignatureScheme::ED25519];
132        assert_eq!(
133            algorithm_for_schemes(&schemes),
134            Some(rustls::SignatureAlgorithm::ED25519),
135        );
136    }
137
138    #[test]
139    fn algorithm_for_empty() {
140        assert_eq!(algorithm_for_schemes(&[]), None);
141    }
142
143    #[test]
144    fn algorithm_for_mixed_algorithms() {
145        let schemes = vec![
146            rustls::SignatureScheme::RSA_PSS_SHA256,
147            rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
148        ];
149        assert_eq!(algorithm_for_schemes(&schemes), None);
150    }
151}