rustls_cng/
signer.rs

1//! SigningKey implementation
2
3use std::sync::Arc;
4
5use rustls::{
6    sign::{Signer, SigningKey},
7    Error, OtherError, SignatureAlgorithm, SignatureScheme,
8};
9
10use crate::key::{AlgorithmGroup, NCryptKey, SignaturePadding};
11
12use windows_sys::Win32::Security::Cryptography::BCryptHash;
13use windows_sys::Win32::Security::Cryptography::{
14    BCRYPT_SHA256_ALG_HANDLE, BCRYPT_SHA384_ALG_HANDLE, BCRYPT_SHA512_ALG_HANDLE,
15};
16
17// Convert IEEE-P1363 signature format to DER encoding.
18// We assume the length of the r and s parts is less than 256 bytes.
19fn p1363_to_der(data: &[u8]) -> Vec<u8> {
20    let (r, s) = data.split_at(data.len() / 2);
21
22    let r_sign: &[u8] = if r[0] >= 0x80 { &[0] } else { &[] };
23    let s_sign: &[u8] = if s[0] >= 0x80 { &[0] } else { &[] };
24
25    let length = data.len() + 2 + 4 + r_sign.len() + s_sign.len();
26
27    let mut buf = Vec::with_capacity(length);
28
29    buf.push(0x30); // SEQUENCE
30    buf.push((length - 2) as u8);
31
32    buf.push(0x02); // INTEGER
33    buf.push((r.len() + r_sign.len()) as u8);
34    buf.extend(r_sign);
35    buf.extend(r);
36
37    buf.push(0x02); // INTEGER
38    buf.push((s.len() + s_sign.len()) as u8);
39    buf.extend(s_sign);
40    buf.extend(s);
41
42    buf
43}
44
45/// Custom implementation of `rustls` SigningKey trait
46#[derive(Debug)]
47pub struct CngSigningKey {
48    key: NCryptKey,
49    algorithm_group: AlgorithmGroup,
50    bits: u32,
51}
52
53impl CngSigningKey {
54    /// Create instance from the CNG key
55    pub fn new(key: NCryptKey) -> crate::Result<Self> {
56        let group = key.algorithm_group()?;
57        let bits = key.bits()?;
58        Ok(Self {
59            key,
60            algorithm_group: group,
61            bits,
62        })
63    }
64
65    /// Return a reference to the CNG key
66    pub fn key(&self) -> &NCryptKey {
67        &self.key
68    }
69
70    /// Return algorithm group of the key
71    pub fn algorithm_group(&self) -> &AlgorithmGroup {
72        &self.algorithm_group
73    }
74
75    /// Return number of bits in the key material
76    pub fn bits(&self) -> u32 {
77        self.bits
78    }
79
80    /// Return supported signature schemes
81    pub fn supported_schemes(&self) -> &[SignatureScheme] {
82        match self.algorithm_group {
83            AlgorithmGroup::Rsa => &[
84                SignatureScheme::RSA_PKCS1_SHA256,
85                SignatureScheme::RSA_PKCS1_SHA384,
86                SignatureScheme::RSA_PKCS1_SHA512,
87                SignatureScheme::RSA_PSS_SHA256,
88                SignatureScheme::RSA_PSS_SHA384,
89                SignatureScheme::RSA_PSS_SHA512,
90            ],
91            AlgorithmGroup::Ecdsa | AlgorithmGroup::Ecdh => match self.bits {
92                256 => &[SignatureScheme::ECDSA_NISTP256_SHA256],
93                384 => &[SignatureScheme::ECDSA_NISTP384_SHA384],
94                _ => &[],
95            },
96        }
97    }
98}
99
100#[derive(Debug)]
101struct CngSigner {
102    key: NCryptKey,
103    scheme: SignatureScheme,
104}
105
106impl CngSigner {
107    // hash function using BCryptHash function which uses FIPS certified SymCrypt
108    fn hash(&self, message: &[u8]) -> Result<(Vec<u8>, SignaturePadding), Error> {
109        let (alg, padding) = match self.scheme {
110            SignatureScheme::RSA_PKCS1_SHA256 => {
111                (BCRYPT_SHA256_ALG_HANDLE, SignaturePadding::Pkcs1)
112            }
113            SignatureScheme::RSA_PKCS1_SHA384 => {
114                (BCRYPT_SHA384_ALG_HANDLE, SignaturePadding::Pkcs1)
115            }
116            SignatureScheme::RSA_PKCS1_SHA512 => {
117                (BCRYPT_SHA512_ALG_HANDLE, SignaturePadding::Pkcs1)
118            }
119            SignatureScheme::RSA_PSS_SHA256 => (BCRYPT_SHA256_ALG_HANDLE, SignaturePadding::Pss),
120            SignatureScheme::RSA_PSS_SHA384 => (BCRYPT_SHA384_ALG_HANDLE, SignaturePadding::Pss),
121            SignatureScheme::RSA_PSS_SHA512 => (BCRYPT_SHA512_ALG_HANDLE, SignaturePadding::Pss),
122            SignatureScheme::ECDSA_NISTP256_SHA256 => {
123                (BCRYPT_SHA256_ALG_HANDLE, SignaturePadding::None)
124            }
125            SignatureScheme::ECDSA_NISTP384_SHA384 => {
126                (BCRYPT_SHA384_ALG_HANDLE, SignaturePadding::None)
127            }
128            _ => return Err(Error::General("Unsupported signature scheme".to_owned())),
129        };
130
131        let hash_len = match alg {
132            BCRYPT_SHA256_ALG_HANDLE => 32,
133            BCRYPT_SHA384_ALG_HANDLE => 48,
134            BCRYPT_SHA512_ALG_HANDLE => 64,
135            _ => return Err(Error::General("Unsupported hash algorithm!".to_owned())),
136        };
137
138        let mut hash = vec![0u8; hash_len];
139
140        unsafe {
141            let status = BCryptHash(
142                alg,
143                std::ptr::null_mut(), // pbSecret
144                0,                    // cbSecret
145                message.as_ptr() as *mut u8,
146                message.len() as u32,
147                hash.as_mut_ptr(),
148                hash_len as u32,
149            );
150
151            if status != 0 {
152                return Err(Error::General(format!(
153                    "BCryptHash failed with status: 0x{status:X}"
154                )));
155            }
156        }
157        Ok((hash, padding))
158    }
159}
160
161impl Signer for CngSigner {
162    fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
163        let (hash, padding) = self.hash(message)?;
164        let signature = self
165            .key
166            .sign(&hash, padding)
167            .map_err(|e| Error::Other(OtherError(Arc::new(e))))?;
168
169        if padding == SignaturePadding::None {
170            // For ECDSA keys Windows produces IEEE-P1363 signatures which must be converted to DER format
171            Ok(p1363_to_der(&signature))
172        } else {
173            Ok(signature)
174        }
175    }
176
177    fn scheme(&self) -> SignatureScheme {
178        self.scheme
179    }
180}
181
182impl SigningKey for CngSigningKey {
183    fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
184        let supported = self.supported_schemes();
185        for scheme in offered {
186            if supported.contains(scheme) {
187                return Some(Box::new(CngSigner {
188                    key: self.key.clone(),
189                    scheme: *scheme,
190                }));
191            }
192        }
193        None
194    }
195
196    fn algorithm(&self) -> SignatureAlgorithm {
197        match self.algorithm_group {
198            AlgorithmGroup::Rsa => SignatureAlgorithm::RSA,
199            AlgorithmGroup::Ecdsa | AlgorithmGroup::Ecdh => SignatureAlgorithm::ECDSA,
200        }
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    #[test]
207    fn test_p1363_to_der() {
208        let p1363 = [1, 2, 3, 4, 5, 6, 7, 8];
209        let der = super::p1363_to_der(&p1363);
210        assert_eq!(
211            der,
212            [0x30, 0x0c, 0x02, 0x04, 1, 2, 3, 4, 0x02, 0x04, 5, 6, 7, 8]
213        )
214    }
215
216    #[test]
217    fn test_p1363_to_der_signed() {
218        let p1363 = [0x81, 2, 3, 4, 0x85, 6, 7, 8];
219        let der = super::p1363_to_der(&p1363);
220        assert_eq!(
221            der,
222            [0x30, 0x0e, 0x02, 0x05, 0, 0x81, 2, 3, 4, 0x02, 0x05, 0, 0x85, 6, 7, 8]
223        )
224    }
225}