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 as *mut core::ffi::c_void,
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{:X}",
154                    status
155                )));
156            }
157        }
158        Ok((hash, padding))
159    }
160}
161
162impl Signer for CngSigner {
163    fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
164        let (hash, padding) = self.hash(message)?;
165        let signature = self
166            .key
167            .sign(&hash, padding)
168            .map_err(|e| Error::Other(OtherError(Arc::new(e))))?;
169
170        if padding == SignaturePadding::None {
171            // For ECDSA keys Windows produces IEEE-P1363 signatures which must be converted to DER format
172            Ok(p1363_to_der(&signature))
173        } else {
174            Ok(signature)
175        }
176    }
177
178    fn scheme(&self) -> SignatureScheme {
179        self.scheme
180    }
181}
182
183impl SigningKey for CngSigningKey {
184    fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
185        let supported = self.supported_schemes();
186        for scheme in offered {
187            if supported.contains(scheme) {
188                return Some(Box::new(CngSigner {
189                    key: self.key.clone(),
190                    scheme: *scheme,
191                }));
192            }
193        }
194        None
195    }
196
197    fn algorithm(&self) -> SignatureAlgorithm {
198        match self.algorithm_group {
199            AlgorithmGroup::Rsa => SignatureAlgorithm::RSA,
200            AlgorithmGroup::Ecdsa | AlgorithmGroup::Ecdh => SignatureAlgorithm::ECDSA,
201        }
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    #[test]
208    fn test_p1363_to_der() {
209        let p1363 = [1, 2, 3, 4, 5, 6, 7, 8];
210        let der = super::p1363_to_der(&p1363);
211        assert_eq!(
212            der,
213            [0x30, 0x0c, 0x02, 0x04, 1, 2, 3, 4, 0x02, 0x04, 5, 6, 7, 8]
214        )
215    }
216
217    #[test]
218    fn test_p1363_to_der_signed() {
219        let p1363 = [0x81, 2, 3, 4, 0x85, 6, 7, 8];
220        let der = super::p1363_to_der(&p1363);
221        assert_eq!(
222            der,
223            [0x30, 0x0e, 0x02, 0x05, 0, 0x81, 2, 3, 4, 0x02, 0x05, 0, 0x85, 6, 7, 8]
224        )
225    }
226}