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