rustls_cng/
key.rs

1//! CNG key wrapper
2
3use std::{os::raw::c_void, ptr, str::FromStr, sync::Arc};
4
5use windows_sys::{
6    core::PCWSTR,
7    Win32::Security::{Cryptography::*, OBJECT_SECURITY_INFORMATION},
8};
9
10use crate::{error::CngError, Result};
11
12/// Algorithm group of the CNG private key
13#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
14pub enum AlgorithmGroup {
15    Rsa,
16    Ecdsa,
17    Ecdh,
18}
19
20impl FromStr for AlgorithmGroup {
21    type Err = CngError;
22
23    fn from_str(s: &str) -> Result<Self> {
24        match s {
25            "RSA" => Ok(Self::Rsa),
26            "ECDSA" => Ok(Self::Ecdsa),
27            "ECDH" => Ok(Self::Ecdh),
28            _ => Err(CngError::UnsupportedKeyAlgorithmGroup),
29        }
30    }
31}
32
33/// Signature padding. Used with RSA keys.
34#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd)]
35pub enum SignaturePadding {
36    None,
37    Pkcs1,
38    Pss,
39}
40
41#[derive(Debug)]
42enum InnerKey {
43    Owned(NCRYPT_KEY_HANDLE),
44    Borrowed(NCRYPT_KEY_HANDLE),
45}
46
47impl InnerKey {
48    fn inner(&self) -> NCRYPT_KEY_HANDLE {
49        match self {
50            Self::Owned(handle) => *handle,
51            Self::Borrowed(handle) => *handle,
52        }
53    }
54}
55
56impl Drop for InnerKey {
57    fn drop(&mut self) {
58        match self {
59            Self::Owned(handle) => unsafe {
60                let _ = NCryptFreeObject(*handle);
61            },
62            Self::Borrowed(_) => {}
63        }
64    }
65}
66
67/// CNG private key wrapper
68#[derive(Clone, Debug)]
69pub struct NCryptKey {
70    inner: Arc<InnerKey>,
71    silent: bool,
72}
73
74impl NCryptKey {
75    /// Create an owned instance which frees the underlying handle automatically
76    pub fn new_owned(handle: NCRYPT_KEY_HANDLE) -> Self {
77        NCryptKey {
78            inner: Arc::new(InnerKey::Owned(handle)),
79            silent: true,
80        }
81    }
82
83    /// Create a borrowed instance which doesn't free the key handle
84    pub fn new_borrowed(handle: NCRYPT_KEY_HANDLE) -> Self {
85        NCryptKey {
86            inner: Arc::new(InnerKey::Borrowed(handle)),
87            silent: true,
88        }
89    }
90
91    /// Return an inner CNG key handle
92    pub fn inner(&self) -> NCRYPT_KEY_HANDLE {
93        self.inner.inner()
94    }
95
96    fn get_string_property(&self, property: PCWSTR) -> Result<String> {
97        let mut result: u32 = 0;
98        unsafe {
99            CngError::from_hresult(NCryptGetProperty(
100                self.inner(),
101                property,
102                ptr::null_mut(),
103                0,
104                &mut result,
105                OBJECT_SECURITY_INFORMATION::default(),
106            ))?;
107
108            let mut prop_value = vec![0u8; result as usize];
109
110            CngError::from_hresult(NCryptGetProperty(
111                self.inner(),
112                property,
113                prop_value.as_mut_ptr(),
114                prop_value.len() as u32,
115                &mut result,
116                OBJECT_SECURITY_INFORMATION::default(),
117            ))?;
118
119            Ok(String::from_utf16_lossy(std::slice::from_raw_parts(
120                prop_value.as_ptr() as *const u16,
121                prop_value.len() / 2 - 1,
122            )))
123        }
124    }
125
126    /// Return a number of bits in the key material
127    pub fn bits(&self) -> Result<u32> {
128        let mut bits = [0u8; 4];
129        let mut result: u32 = 0;
130        unsafe {
131            CngError::from_hresult(NCryptGetProperty(
132                self.inner(),
133                NCRYPT_LENGTH_PROPERTY,
134                bits.as_mut_ptr(),
135                4,
136                &mut result,
137                OBJECT_SECURITY_INFORMATION::default(),
138            ))?;
139
140            Ok(u32::from_ne_bytes(bits))
141        }
142    }
143
144    /// Return algorithm group of the key
145    pub fn algorithm_group(&self) -> Result<AlgorithmGroup> {
146        self.get_string_property(NCRYPT_ALGORITHM_GROUP_PROPERTY)?
147            .parse()
148    }
149
150    /// Return algorithm name of the key
151    pub fn algorithm(&self) -> Result<String> {
152        self.get_string_property(NCRYPT_ALGORITHM_PROPERTY)
153    }
154
155    /// Set a pin code for hardware tokens
156    pub fn set_pin(&self, pin: &str) -> Result<()> {
157        let pin_val = pin.encode_utf16().chain([0]).collect::<Vec<u16>>();
158
159        let result = unsafe {
160            NCryptSetProperty(
161                self.inner(),
162                NCRYPT_PIN_PROPERTY,
163                pin_val.as_ptr() as *const u8,
164                pin.len() as u32,
165                NCRYPT_FLAGS::default(),
166            )
167        };
168
169        CngError::from_hresult(result)
170    }
171
172    /// Enable or disable silent key operations without prompting the user. The default value is 'true'.
173    pub fn set_silent(&mut self, silent: bool) {
174        self.silent = silent;
175    }
176
177    /// Sign a given digest with this key. The `hash` slice must be 32, 48 or 64 bytes long.
178    pub fn sign(&self, hash: &[u8], padding: SignaturePadding) -> Result<Vec<u8>> {
179        unsafe {
180            let hash_alg = match hash.len() {
181                32 => BCRYPT_SHA256_ALGORITHM,
182                48 => BCRYPT_SHA384_ALGORITHM,
183                64 => BCRYPT_SHA512_ALGORITHM,
184                _ => return Err(CngError::InvalidHashLength),
185            };
186
187            let pkcs1;
188            let pss;
189
190            let (info, flag) = match padding {
191                SignaturePadding::Pkcs1 => {
192                    pkcs1 = BCRYPT_PKCS1_PADDING_INFO { pszAlgId: hash_alg };
193                    (&pkcs1 as *const _ as *const c_void, BCRYPT_PAD_PKCS1)
194                }
195                SignaturePadding::Pss => {
196                    pss = BCRYPT_PSS_PADDING_INFO {
197                        pszAlgId: hash_alg,
198                        cbSalt: hash.len() as u32,
199                    };
200                    (&pss as *const _ as *const c_void, BCRYPT_PAD_PSS)
201                }
202                SignaturePadding::None => (ptr::null(), NCRYPT_FLAGS::default()),
203            };
204
205            let mut result = 0;
206            let dwflags = flag | if self.silent { NCRYPT_SILENT_FLAG } else { 0 };
207
208            CngError::from_hresult(NCryptSignHash(
209                self.inner(),
210                info,
211                hash.as_ptr(),
212                hash.len() as u32,
213                ptr::null_mut(),
214                0,
215                &mut result,
216                dwflags,
217            ))?;
218
219            let mut signature = vec![0u8; result as usize];
220
221            CngError::from_hresult(NCryptSignHash(
222                self.inner(),
223                info,
224                hash.as_ptr(),
225                hash.len() as u32,
226                signature.as_mut_ptr(),
227                signature.len() as u32,
228                &mut result,
229                dwflags,
230            ))?;
231
232            Ok(signature)
233        }
234    }
235}