1 use std::marker::PhantomData;
2 use std::{mem, ptr};
3 use std::os::raw;
4 use std::os::raw::c_char;
5 use cose::SignatureAlgorithm;
6
7 type SECItemType = raw::c_uint; // TODO: actually an enum - is this the right size?
8 const SI_BUFFER: SECItemType = 0; // called siBuffer in NSS
9
10 #[repr(C)]
11 struct SECItem {
12 typ: SECItemType,
13 data: *const u8,
14 len: raw::c_uint,
15 }
16
17 impl SECItem {
maybe_new(data: &[u8]) -> Result<SECItem, NSSError>18 fn maybe_new(data: &[u8]) -> Result<SECItem, NSSError> {
19 if data.len() > u32::max_value() as usize {
20 return Err(NSSError::InputTooLarge);
21 }
22 Ok(SECItem {
23 typ: SI_BUFFER,
24 data: data.as_ptr(),
25 len: data.len() as u32,
26 })
27 }
28
maybe_from_parts(data: *const u8, len: usize) -> Result<SECItem, NSSError>29 fn maybe_from_parts(data: *const u8, len: usize) -> Result<SECItem, NSSError> {
30 if len > u32::max_value() as usize {
31 return Err(NSSError::InputTooLarge);
32 }
33 Ok(SECItem {
34 typ: SI_BUFFER,
35 data: data,
36 len: len as u32,
37 })
38 }
39 }
40
41 /// Many NSS APIs take constant data input as SECItems. Some, however, output data as SECItems.
42 /// To represent this, we define another type of mutable SECItem.
43 #[repr(C)]
44 struct SECItemMut<'a> {
45 typ: SECItemType,
46 data: *mut u8,
47 len: raw::c_uint,
48 _marker: PhantomData<&'a mut Vec<u8>>,
49 }
50
51 impl<'a> SECItemMut<'a> {
52 /// Given a mutable reference to a Vec<u8> that has a particular allocated capacity, create a
53 /// SECItemMut that points to the vec and has the same capacity.
54 /// The input vec is not expected to have any actual contents, and in any case is cleared.
maybe_from_empty_preallocated_vec(vec: &'a mut Vec<u8>) -> Result<SECItemMut<'a>, NSSError>55 fn maybe_from_empty_preallocated_vec(vec: &'a mut Vec<u8>) -> Result<SECItemMut<'a>, NSSError> {
56 if vec.capacity() > u32::max_value() as usize {
57 return Err(NSSError::InputTooLarge);
58 }
59 vec.clear();
60 Ok(SECItemMut {
61 typ: SI_BUFFER,
62 data: vec.as_mut_ptr(),
63 len: vec.capacity() as u32,
64 _marker: PhantomData,
65 })
66 }
67 }
68
69 #[repr(C)]
70 struct CkRsaPkcsPssParams {
71 // Called CK_RSA_PKCS_PSS_PARAMS in NSS
72 hash_alg: CkMechanismType, // Called hashAlg in NSS
73 mgf: CkRsaPkcsMgfType,
74 s_len: raw::c_ulong, // Called sLen in NSS
75 }
76
77 impl CkRsaPkcsPssParams {
new() -> CkRsaPkcsPssParams78 fn new() -> CkRsaPkcsPssParams {
79 CkRsaPkcsPssParams {
80 hash_alg: CKM_SHA256,
81 mgf: CKG_MGF1_SHA256,
82 s_len: 32,
83 }
84 }
85
get_params_item(&self) -> Result<SECItem, NSSError>86 fn get_params_item(&self) -> Result<SECItem, NSSError> {
87 // This isn't entirely NSS' fault, but it mostly is.
88 let params_ptr: *const CkRsaPkcsPssParams = self;
89 let params_ptr: *const u8 = params_ptr as *const u8;
90 let params_secitem =
91 SECItem::maybe_from_parts(params_ptr, mem::size_of::<CkRsaPkcsPssParams>())?;
92 Ok(params_secitem)
93 }
94 }
95
96 type CkMechanismType = raw::c_ulong; // called CK_MECHANISM_TYPE in NSS
97 const CKM_ECDSA: CkMechanismType = 0x0000_1041;
98 const CKM_RSA_PKCS_PSS: CkMechanismType = 0x0000_000D;
99 const CKM_SHA256: CkMechanismType = 0x0000_0250;
100
101 type CkRsaPkcsMgfType = raw::c_ulong; // called CK_RSA_PKCS_MGF_TYPE in NSS
102 const CKG_MGF1_SHA256: CkRsaPkcsMgfType = 0x0000_0002;
103
104 type SECStatus = raw::c_int; // TODO: enum - right size?
105 const SEC_SUCCESS: SECStatus = 0; // Called SECSuccess in NSS
106 const SEC_FAILURE: SECStatus = -1; // Called SECFailure in NSS
107
108 enum SECKEYPublicKey {}
109 enum SECKEYPrivateKey {}
110 enum PK11SlotInfo {}
111 enum CERTCertificate {}
112 enum CERTCertDBHandle {}
113
114 const SHA256_LENGTH: usize = 32;
115 const SHA384_LENGTH: usize = 48;
116 const SHA512_LENGTH: usize = 64;
117
118 // TODO: ugh this will probably have a platform-specific name...
119 #[link(name = "nss3")]
120 extern "C" {
PK11_HashBuf( hashAlg: HashAlgorithm, out: *mut u8, data_in: *const u8, len: raw::c_int, ) -> SECStatus121 fn PK11_HashBuf(
122 hashAlg: HashAlgorithm,
123 out: *mut u8,
124 data_in: *const u8, // called "in" in NSS
125 len: raw::c_int,
126 ) -> SECStatus;
PK11_VerifyWithMechanism( key: *const SECKEYPublicKey, mechanism: CkMechanismType, param: *const SECItem, sig: *const SECItem, hash: *const SECItem, wincx: *const raw::c_void, ) -> SECStatus127 fn PK11_VerifyWithMechanism(
128 key: *const SECKEYPublicKey,
129 mechanism: CkMechanismType,
130 param: *const SECItem,
131 sig: *const SECItem,
132 hash: *const SECItem,
133 wincx: *const raw::c_void,
134 ) -> SECStatus;
135
SECKEY_DestroyPublicKey(pubk: *const SECKEYPublicKey)136 fn SECKEY_DestroyPublicKey(pubk: *const SECKEYPublicKey);
137
CERT_GetDefaultCertDB() -> *const CERTCertDBHandle138 fn CERT_GetDefaultCertDB() -> *const CERTCertDBHandle;
CERT_DestroyCertificate(cert: *mut CERTCertificate)139 fn CERT_DestroyCertificate(cert: *mut CERTCertificate);
CERT_NewTempCertificate( handle: *const CERTCertDBHandle, derCert: *const SECItem, nickname: *const c_char, isperm: bool, copyDER: bool, ) -> *mut CERTCertificate140 fn CERT_NewTempCertificate(
141 handle: *const CERTCertDBHandle,
142 derCert: *const SECItem,
143 nickname: *const c_char,
144 isperm: bool,
145 copyDER: bool,
146 ) -> *mut CERTCertificate;
CERT_ExtractPublicKey(cert: *const CERTCertificate) -> *const SECKEYPublicKey147 fn CERT_ExtractPublicKey(cert: *const CERTCertificate) -> *const SECKEYPublicKey;
148
PK11_ImportDERPrivateKeyInfoAndReturnKey( slot: *mut PK11SlotInfo, derPKI: *const SECItem, nickname: *const SECItem, publicValue: *const SECItem, isPerm: bool, isPrivate: bool, keyUsage: u32, privk: *mut *mut SECKEYPrivateKey, wincx: *const u8, ) -> SECStatus149 fn PK11_ImportDERPrivateKeyInfoAndReturnKey(
150 slot: *mut PK11SlotInfo,
151 derPKI: *const SECItem,
152 nickname: *const SECItem,
153 publicValue: *const SECItem,
154 isPerm: bool,
155 isPrivate: bool,
156 keyUsage: u32,
157 privk: *mut *mut SECKEYPrivateKey,
158 wincx: *const u8,
159 ) -> SECStatus;
PK11_GetInternalSlot() -> *mut PK11SlotInfo160 fn PK11_GetInternalSlot() -> *mut PK11SlotInfo;
PK11_FreeSlot(slot: *mut PK11SlotInfo)161 fn PK11_FreeSlot(slot: *mut PK11SlotInfo);
PK11_SignatureLen(key: *const SECKEYPrivateKey) -> usize162 fn PK11_SignatureLen(key: *const SECKEYPrivateKey) -> usize;
PK11_SignWithMechanism( key: *const SECKEYPrivateKey, mech: CkMechanismType, param: *const SECItem, sig: *mut SECItemMut, hash: *const SECItem, ) -> SECStatus163 fn PK11_SignWithMechanism(
164 key: *const SECKEYPrivateKey,
165 mech: CkMechanismType,
166 param: *const SECItem,
167 sig: *mut SECItemMut,
168 hash: *const SECItem,
169 ) -> SECStatus;
170 }
171
172 /// An error type describing errors that may be encountered during verification.
173 #[derive(Debug, PartialEq)]
174 pub enum NSSError {
175 ImportCertError,
176 DecodingPKCS8Failed,
177 InputTooLarge,
178 LibraryFailure,
179 SignatureVerificationFailed,
180 SigningFailed,
181 ExtractPublicKeyFailed,
182 }
183
184 // https://searchfox.org/nss/rev/990c2e793aa731cd66238c6c4f00b9473943bc66/lib/util/secoidt.h#274
185 #[derive(Debug, PartialEq, Clone)]
186 #[repr(C)]
187 enum HashAlgorithm {
188 SHA256 = 191,
189 SHA384 = 192,
190 SHA512 = 193,
191 }
192
hash(payload: &[u8], signature_algorithm: &SignatureAlgorithm) -> Result<Vec<u8>, NSSError>193 fn hash(payload: &[u8], signature_algorithm: &SignatureAlgorithm) -> Result<Vec<u8>, NSSError> {
194 if payload.len() > raw::c_int::max_value() as usize {
195 return Err(NSSError::InputTooLarge);
196 }
197 let (hash_algorithm, digest_length) = match *signature_algorithm {
198 SignatureAlgorithm::ES256 => (HashAlgorithm::SHA256, SHA256_LENGTH),
199 SignatureAlgorithm::ES384 => (HashAlgorithm::SHA384, SHA384_LENGTH),
200 SignatureAlgorithm::ES512 => (HashAlgorithm::SHA512, SHA512_LENGTH),
201 SignatureAlgorithm::PS256 => (HashAlgorithm::SHA256, SHA256_LENGTH),
202 };
203 let mut hash_buf = vec![0; digest_length];
204 let len: raw::c_int = payload.len() as raw::c_int;
205 let hash_result =
206 unsafe { PK11_HashBuf(hash_algorithm, hash_buf.as_mut_ptr(), payload.as_ptr(), len) };
207 if hash_result != SEC_SUCCESS {
208 return Err(NSSError::LibraryFailure);
209 }
210 Ok(hash_buf)
211 }
212
213 /// Main entrypoint for verification. Given a signature algorithm, the bytes of a subject public key
214 /// info, a payload, and a signature over the payload, returns a result based on the outcome of
215 /// decoding the subject public key info and running the signature verification algorithm on the
216 /// signed data.
verify_signature( signature_algorithm: &SignatureAlgorithm, cert: &[u8], payload: &[u8], signature: &[u8], ) -> Result<(), NSSError>217 pub fn verify_signature(
218 signature_algorithm: &SignatureAlgorithm,
219 cert: &[u8],
220 payload: &[u8],
221 signature: &[u8],
222 ) -> Result<(), NSSError> {
223 let slot = unsafe { PK11_GetInternalSlot() };
224 if slot.is_null() {
225 return Err(NSSError::LibraryFailure);
226 }
227 defer!(unsafe {
228 PK11_FreeSlot(slot);
229 });
230
231 let hash_buf = hash(payload, signature_algorithm).unwrap();
232 let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;
233
234 // Import DER cert into NSS.
235 let der_cert = SECItem::maybe_new(cert)?;
236 let db_handle = unsafe { CERT_GetDefaultCertDB() };
237 if db_handle.is_null() {
238 // TODO #28
239 return Err(NSSError::LibraryFailure);
240 }
241 let nss_cert =
242 unsafe { CERT_NewTempCertificate(db_handle, &der_cert, ptr::null(), false, true) };
243 if nss_cert.is_null() {
244 return Err(NSSError::ImportCertError);
245 }
246 defer!(unsafe {
247 CERT_DestroyCertificate(nss_cert);
248 });
249
250 let key = unsafe { CERT_ExtractPublicKey(nss_cert) };
251 if key.is_null() {
252 return Err(NSSError::ExtractPublicKeyFailed);
253 }
254 defer!(unsafe {
255 SECKEY_DestroyPublicKey(key);
256 });
257 let signature_item = SECItem::maybe_new(signature)?;
258 let mechanism = match *signature_algorithm {
259 SignatureAlgorithm::ES256 => CKM_ECDSA,
260 SignatureAlgorithm::ES384 => CKM_ECDSA,
261 SignatureAlgorithm::ES512 => CKM_ECDSA,
262 SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
263 };
264 let rsa_pss_params = CkRsaPkcsPssParams::new();
265 let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
266 let params_item = match *signature_algorithm {
267 SignatureAlgorithm::ES256 => ptr::null(),
268 SignatureAlgorithm::ES384 => ptr::null(),
269 SignatureAlgorithm::ES512 => ptr::null(),
270 SignatureAlgorithm::PS256 => &rsa_pss_params_item,
271 };
272 let null_cx_ptr: *const raw::c_void = ptr::null();
273 let result = unsafe {
274 PK11_VerifyWithMechanism(
275 key,
276 mechanism,
277 params_item,
278 &signature_item,
279 &hash_item,
280 null_cx_ptr,
281 )
282 };
283 match result {
284 SEC_SUCCESS => Ok(()),
285 SEC_FAILURE => Err(NSSError::SignatureVerificationFailed),
286 _ => Err(NSSError::LibraryFailure),
287 }
288 }
289
sign( signature_algorithm: &SignatureAlgorithm, pk8: &[u8], payload: &[u8], ) -> Result<Vec<u8>, NSSError>290 pub fn sign(
291 signature_algorithm: &SignatureAlgorithm,
292 pk8: &[u8],
293 payload: &[u8],
294 ) -> Result<Vec<u8>, NSSError> {
295 let slot = unsafe { PK11_GetInternalSlot() };
296 if slot.is_null() {
297 return Err(NSSError::LibraryFailure);
298 }
299 defer!(unsafe {
300 PK11_FreeSlot(slot);
301 });
302 let pkcs8item = SECItem::maybe_new(pk8)?;
303 let mut key: *mut SECKEYPrivateKey = ptr::null_mut();
304 let ku_all = 0xFF;
305 let rv = unsafe {
306 PK11_ImportDERPrivateKeyInfoAndReturnKey(
307 slot,
308 &pkcs8item,
309 ptr::null(),
310 ptr::null(),
311 false,
312 false,
313 ku_all,
314 &mut key,
315 ptr::null(),
316 )
317 };
318 if rv != SEC_SUCCESS || key.is_null() {
319 return Err(NSSError::DecodingPKCS8Failed);
320 }
321 let mechanism = match *signature_algorithm {
322 SignatureAlgorithm::ES256 => CKM_ECDSA,
323 SignatureAlgorithm::ES384 => CKM_ECDSA,
324 SignatureAlgorithm::ES512 => CKM_ECDSA,
325 SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
326 };
327 let rsa_pss_params = CkRsaPkcsPssParams::new();
328 let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
329 let params_item = match *signature_algorithm {
330 SignatureAlgorithm::ES256 => ptr::null(),
331 SignatureAlgorithm::ES384 => ptr::null(),
332 SignatureAlgorithm::ES512 => ptr::null(),
333 SignatureAlgorithm::PS256 => &rsa_pss_params_item,
334 };
335 let signature_len = unsafe { PK11_SignatureLen(key) };
336 // Allocate enough space for the signature.
337 let mut signature: Vec<u8> = Vec::with_capacity(signature_len);
338 let hash_buf = hash(payload, signature_algorithm).unwrap();
339 let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;
340 {
341 // Get a mutable SECItem on the preallocated signature buffer. PK11_SignWithMechanism will
342 // fill the SECItem's buf with the bytes of the signature.
343 let mut signature_item = SECItemMut::maybe_from_empty_preallocated_vec(&mut signature)?;
344 let rv = unsafe {
345 PK11_SignWithMechanism(key, mechanism, params_item, &mut signature_item, &hash_item)
346 };
347 if rv != SEC_SUCCESS || signature_item.len as usize != signature_len {
348 return Err(NSSError::SigningFailed);
349 }
350 }
351 unsafe {
352 // Now that the bytes of the signature have been filled out, set its length.
353 signature.set_len(signature_len);
354 }
355 Ok(signature)
356 }
357