1use 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#[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#[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#[derive(Clone, Debug)]
69pub struct NCryptKey {
70 inner: Arc<InnerKey>,
71 silent: bool,
72}
73
74impl NCryptKey {
75 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 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 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 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 pub fn algorithm_group(&self) -> Result<AlgorithmGroup> {
146 self.get_string_property(NCRYPT_ALGORITHM_GROUP_PROPERTY)?
147 .parse()
148 }
149
150 pub fn algorithm(&self) -> Result<String> {
152 self.get_string_property(NCRYPT_ALGORITHM_PROPERTY)
153 }
154
155 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 pub fn set_silent(&mut self, silent: bool) {
174 self.silent = silent;
175 }
176
177 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}