summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cose/examples/sign_verify/nss.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/cose/examples/sign_verify/nss.rs')
-rw-r--r--third_party/rust/cose/examples/sign_verify/nss.rs356
1 files changed, 356 insertions, 0 deletions
diff --git a/third_party/rust/cose/examples/sign_verify/nss.rs b/third_party/rust/cose/examples/sign_verify/nss.rs
new file mode 100644
index 0000000000..a5a827ceba
--- /dev/null
+++ b/third_party/rust/cose/examples/sign_verify/nss.rs
@@ -0,0 +1,356 @@
+use std::marker::PhantomData;
+use std::{mem, ptr};
+use std::os::raw;
+use std::os::raw::c_char;
+use cose::SignatureAlgorithm;
+
+type SECItemType = raw::c_uint; // TODO: actually an enum - is this the right size?
+const SI_BUFFER: SECItemType = 0; // called siBuffer in NSS
+
+#[repr(C)]
+struct SECItem {
+ typ: SECItemType,
+ data: *const u8,
+ len: raw::c_uint,
+}
+
+impl SECItem {
+ fn maybe_new(data: &[u8]) -> Result<SECItem, NSSError> {
+ if data.len() > u32::max_value() as usize {
+ return Err(NSSError::InputTooLarge);
+ }
+ Ok(SECItem {
+ typ: SI_BUFFER,
+ data: data.as_ptr(),
+ len: data.len() as u32,
+ })
+ }
+
+ fn maybe_from_parts(data: *const u8, len: usize) -> Result<SECItem, NSSError> {
+ if len > u32::max_value() as usize {
+ return Err(NSSError::InputTooLarge);
+ }
+ Ok(SECItem {
+ typ: SI_BUFFER,
+ data: data,
+ len: len as u32,
+ })
+ }
+}
+
+/// Many NSS APIs take constant data input as SECItems. Some, however, output data as SECItems.
+/// To represent this, we define another type of mutable SECItem.
+#[repr(C)]
+struct SECItemMut<'a> {
+ typ: SECItemType,
+ data: *mut u8,
+ len: raw::c_uint,
+ _marker: PhantomData<&'a mut Vec<u8>>,
+}
+
+impl<'a> SECItemMut<'a> {
+ /// Given a mutable reference to a Vec<u8> that has a particular allocated capacity, create a
+ /// SECItemMut that points to the vec and has the same capacity.
+ /// The input vec is not expected to have any actual contents, and in any case is cleared.
+ fn maybe_from_empty_preallocated_vec(vec: &'a mut Vec<u8>) -> Result<SECItemMut<'a>, NSSError> {
+ if vec.capacity() > u32::max_value() as usize {
+ return Err(NSSError::InputTooLarge);
+ }
+ vec.clear();
+ Ok(SECItemMut {
+ typ: SI_BUFFER,
+ data: vec.as_mut_ptr(),
+ len: vec.capacity() as u32,
+ _marker: PhantomData,
+ })
+ }
+}
+
+#[repr(C)]
+struct CkRsaPkcsPssParams {
+ // Called CK_RSA_PKCS_PSS_PARAMS in NSS
+ hash_alg: CkMechanismType, // Called hashAlg in NSS
+ mgf: CkRsaPkcsMgfType,
+ s_len: raw::c_ulong, // Called sLen in NSS
+}
+
+impl CkRsaPkcsPssParams {
+ fn new() -> CkRsaPkcsPssParams {
+ CkRsaPkcsPssParams {
+ hash_alg: CKM_SHA256,
+ mgf: CKG_MGF1_SHA256,
+ s_len: 32,
+ }
+ }
+
+ fn get_params_item(&self) -> Result<SECItem, NSSError> {
+ // This isn't entirely NSS' fault, but it mostly is.
+ let params_ptr: *const CkRsaPkcsPssParams = self;
+ let params_ptr: *const u8 = params_ptr as *const u8;
+ let params_secitem =
+ SECItem::maybe_from_parts(params_ptr, mem::size_of::<CkRsaPkcsPssParams>())?;
+ Ok(params_secitem)
+ }
+}
+
+type CkMechanismType = raw::c_ulong; // called CK_MECHANISM_TYPE in NSS
+const CKM_ECDSA: CkMechanismType = 0x0000_1041;
+const CKM_RSA_PKCS_PSS: CkMechanismType = 0x0000_000D;
+const CKM_SHA256: CkMechanismType = 0x0000_0250;
+
+type CkRsaPkcsMgfType = raw::c_ulong; // called CK_RSA_PKCS_MGF_TYPE in NSS
+const CKG_MGF1_SHA256: CkRsaPkcsMgfType = 0x0000_0002;
+
+type SECStatus = raw::c_int; // TODO: enum - right size?
+const SEC_SUCCESS: SECStatus = 0; // Called SECSuccess in NSS
+const SEC_FAILURE: SECStatus = -1; // Called SECFailure in NSS
+
+enum SECKEYPublicKey {}
+enum SECKEYPrivateKey {}
+enum PK11SlotInfo {}
+enum CERTCertificate {}
+enum CERTCertDBHandle {}
+
+const SHA256_LENGTH: usize = 32;
+const SHA384_LENGTH: usize = 48;
+const SHA512_LENGTH: usize = 64;
+
+// TODO: ugh this will probably have a platform-specific name...
+#[link(name = "nss3")]
+extern "C" {
+ fn PK11_HashBuf(
+ hashAlg: HashAlgorithm,
+ out: *mut u8,
+ data_in: *const u8, // called "in" in NSS
+ len: raw::c_int,
+ ) -> SECStatus;
+ fn PK11_VerifyWithMechanism(
+ key: *const SECKEYPublicKey,
+ mechanism: CkMechanismType,
+ param: *const SECItem,
+ sig: *const SECItem,
+ hash: *const SECItem,
+ wincx: *const raw::c_void,
+ ) -> SECStatus;
+
+ fn SECKEY_DestroyPublicKey(pubk: *const SECKEYPublicKey);
+
+ fn CERT_GetDefaultCertDB() -> *const CERTCertDBHandle;
+ fn CERT_DestroyCertificate(cert: *mut CERTCertificate);
+ fn CERT_NewTempCertificate(
+ handle: *const CERTCertDBHandle,
+ derCert: *const SECItem,
+ nickname: *const c_char,
+ isperm: bool,
+ copyDER: bool,
+ ) -> *mut CERTCertificate;
+ fn CERT_ExtractPublicKey(cert: *const CERTCertificate) -> *const SECKEYPublicKey;
+
+ fn 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,
+ ) -> SECStatus;
+ fn PK11_GetInternalSlot() -> *mut PK11SlotInfo;
+ fn PK11_FreeSlot(slot: *mut PK11SlotInfo);
+ fn PK11_SignatureLen(key: *const SECKEYPrivateKey) -> usize;
+ fn PK11_SignWithMechanism(
+ key: *const SECKEYPrivateKey,
+ mech: CkMechanismType,
+ param: *const SECItem,
+ sig: *mut SECItemMut,
+ hash: *const SECItem,
+ ) -> SECStatus;
+}
+
+/// An error type describing errors that may be encountered during verification.
+#[derive(Debug, PartialEq)]
+pub enum NSSError {
+ ImportCertError,
+ DecodingPKCS8Failed,
+ InputTooLarge,
+ LibraryFailure,
+ SignatureVerificationFailed,
+ SigningFailed,
+ ExtractPublicKeyFailed,
+}
+
+// https://searchfox.org/nss/rev/990c2e793aa731cd66238c6c4f00b9473943bc66/lib/util/secoidt.h#274
+#[derive(Debug, PartialEq, Clone)]
+#[repr(C)]
+enum HashAlgorithm {
+ SHA256 = 191,
+ SHA384 = 192,
+ SHA512 = 193,
+}
+
+fn hash(payload: &[u8], signature_algorithm: &SignatureAlgorithm) -> Result<Vec<u8>, NSSError> {
+ if payload.len() > raw::c_int::max_value() as usize {
+ return Err(NSSError::InputTooLarge);
+ }
+ let (hash_algorithm, digest_length) = match *signature_algorithm {
+ SignatureAlgorithm::ES256 => (HashAlgorithm::SHA256, SHA256_LENGTH),
+ SignatureAlgorithm::ES384 => (HashAlgorithm::SHA384, SHA384_LENGTH),
+ SignatureAlgorithm::ES512 => (HashAlgorithm::SHA512, SHA512_LENGTH),
+ SignatureAlgorithm::PS256 => (HashAlgorithm::SHA256, SHA256_LENGTH),
+ };
+ let mut hash_buf = vec![0; digest_length];
+ let len: raw::c_int = payload.len() as raw::c_int;
+ let hash_result =
+ unsafe { PK11_HashBuf(hash_algorithm, hash_buf.as_mut_ptr(), payload.as_ptr(), len) };
+ if hash_result != SEC_SUCCESS {
+ return Err(NSSError::LibraryFailure);
+ }
+ Ok(hash_buf)
+}
+
+/// Main entrypoint for verification. Given a signature algorithm, the bytes of a subject public key
+/// info, a payload, and a signature over the payload, returns a result based on the outcome of
+/// decoding the subject public key info and running the signature verification algorithm on the
+/// signed data.
+pub fn verify_signature(
+ signature_algorithm: &SignatureAlgorithm,
+ cert: &[u8],
+ payload: &[u8],
+ signature: &[u8],
+) -> Result<(), NSSError> {
+ let slot = unsafe { PK11_GetInternalSlot() };
+ if slot.is_null() {
+ return Err(NSSError::LibraryFailure);
+ }
+ defer!(unsafe {
+ PK11_FreeSlot(slot);
+ });
+
+ let hash_buf = hash(payload, signature_algorithm).unwrap();
+ let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;
+
+ // Import DER cert into NSS.
+ let der_cert = SECItem::maybe_new(cert)?;
+ let db_handle = unsafe { CERT_GetDefaultCertDB() };
+ if db_handle.is_null() {
+ // TODO #28
+ return Err(NSSError::LibraryFailure);
+ }
+ let nss_cert =
+ unsafe { CERT_NewTempCertificate(db_handle, &der_cert, ptr::null(), false, true) };
+ if nss_cert.is_null() {
+ return Err(NSSError::ImportCertError);
+ }
+ defer!(unsafe {
+ CERT_DestroyCertificate(nss_cert);
+ });
+
+ let key = unsafe { CERT_ExtractPublicKey(nss_cert) };
+ if key.is_null() {
+ return Err(NSSError::ExtractPublicKeyFailed);
+ }
+ defer!(unsafe {
+ SECKEY_DestroyPublicKey(key);
+ });
+ let signature_item = SECItem::maybe_new(signature)?;
+ let mechanism = match *signature_algorithm {
+ SignatureAlgorithm::ES256 => CKM_ECDSA,
+ SignatureAlgorithm::ES384 => CKM_ECDSA,
+ SignatureAlgorithm::ES512 => CKM_ECDSA,
+ SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
+ };
+ let rsa_pss_params = CkRsaPkcsPssParams::new();
+ let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
+ let params_item = match *signature_algorithm {
+ SignatureAlgorithm::ES256 => ptr::null(),
+ SignatureAlgorithm::ES384 => ptr::null(),
+ SignatureAlgorithm::ES512 => ptr::null(),
+ SignatureAlgorithm::PS256 => &rsa_pss_params_item,
+ };
+ let null_cx_ptr: *const raw::c_void = ptr::null();
+ let result = unsafe {
+ PK11_VerifyWithMechanism(
+ key,
+ mechanism,
+ params_item,
+ &signature_item,
+ &hash_item,
+ null_cx_ptr,
+ )
+ };
+ match result {
+ SEC_SUCCESS => Ok(()),
+ SEC_FAILURE => Err(NSSError::SignatureVerificationFailed),
+ _ => Err(NSSError::LibraryFailure),
+ }
+}
+
+pub fn sign(
+ signature_algorithm: &SignatureAlgorithm,
+ pk8: &[u8],
+ payload: &[u8],
+) -> Result<Vec<u8>, NSSError> {
+ let slot = unsafe { PK11_GetInternalSlot() };
+ if slot.is_null() {
+ return Err(NSSError::LibraryFailure);
+ }
+ defer!(unsafe {
+ PK11_FreeSlot(slot);
+ });
+ let pkcs8item = SECItem::maybe_new(pk8)?;
+ let mut key: *mut SECKEYPrivateKey = ptr::null_mut();
+ let ku_all = 0xFF;
+ let rv = unsafe {
+ PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ slot,
+ &pkcs8item,
+ ptr::null(),
+ ptr::null(),
+ false,
+ false,
+ ku_all,
+ &mut key,
+ ptr::null(),
+ )
+ };
+ if rv != SEC_SUCCESS || key.is_null() {
+ return Err(NSSError::DecodingPKCS8Failed);
+ }
+ let mechanism = match *signature_algorithm {
+ SignatureAlgorithm::ES256 => CKM_ECDSA,
+ SignatureAlgorithm::ES384 => CKM_ECDSA,
+ SignatureAlgorithm::ES512 => CKM_ECDSA,
+ SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
+ };
+ let rsa_pss_params = CkRsaPkcsPssParams::new();
+ let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
+ let params_item = match *signature_algorithm {
+ SignatureAlgorithm::ES256 => ptr::null(),
+ SignatureAlgorithm::ES384 => ptr::null(),
+ SignatureAlgorithm::ES512 => ptr::null(),
+ SignatureAlgorithm::PS256 => &rsa_pss_params_item,
+ };
+ let signature_len = unsafe { PK11_SignatureLen(key) };
+ // Allocate enough space for the signature.
+ let mut signature: Vec<u8> = Vec::with_capacity(signature_len);
+ let hash_buf = hash(payload, signature_algorithm).unwrap();
+ let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;
+ {
+ // Get a mutable SECItem on the preallocated signature buffer. PK11_SignWithMechanism will
+ // fill the SECItem's buf with the bytes of the signature.
+ let mut signature_item = SECItemMut::maybe_from_empty_preallocated_vec(&mut signature)?;
+ let rv = unsafe {
+ PK11_SignWithMechanism(key, mechanism, params_item, &mut signature_item, &hash_item)
+ };
+ if rv != SEC_SUCCESS || signature_item.len as usize != signature_len {
+ return Err(NSSError::SigningFailed);
+ }
+ }
+ unsafe {
+ // Now that the bytes of the signature have been filled out, set its length.
+ signature.set_len(signature_len);
+ }
+ Ok(signature)
+}