use super::CryptoError;
use nss_gk_api::p11::{
PK11Origin, PK11_CreateContextBySymKey, PK11_Decrypt, PK11_DigestFinal, PK11_DigestOp,
PK11_Encrypt, PK11_ExportDERPrivateKeyInfo, PK11_GenerateKeyPairWithOpFlags,
PK11_GenerateRandom, PK11_HashBuf, PK11_ImportDERPrivateKeyInfoAndReturnKey, PK11_ImportSymKey,
PK11_PubDeriveWithKDF, PK11_SignWithMechanism, PrivateKey, PublicKey,
SECKEY_DecodeDERSubjectPublicKeyInfo, SECKEY_ExtractPublicKey, SECOidTag, Slot,
SubjectPublicKeyInfo, AES_BLOCK_SIZE, PK11_ATTR_EXTRACTABLE, PK11_ATTR_INSENSITIVE,
PK11_ATTR_SESSION, SHA256_LENGTH,
};
use nss_gk_api::{IntoResult, SECItem, SECItemBorrowed, ScopedSECItem, PR_FALSE};
use pkcs11_bindings::{
CKA_DERIVE, CKA_ENCRYPT, CKA_SIGN, CKD_NULL, CKF_DERIVE, CKM_AES_CBC, CKM_ECDH1_DERIVE,
CKM_ECDSA_SHA256, CKM_EC_KEY_PAIR_GEN, CKM_SHA256_HMAC, CKM_SHA512_HMAC,
};
use std::convert::TryFrom;
use std::os::raw::{c_int, c_uint};
use std::ptr;
use super::der;
#[cfg(test)]
use nss_gk_api::p11::PK11_VerifyWithMechanism;
impl From<nss_gk_api::Error> for CryptoError {
fn from(e: nss_gk_api::Error) -> Self {
CryptoError::Backend(format!("{e}"))
}
}
pub type Result<T> = std::result::Result<T, CryptoError>;
fn nss_public_key_from_der_spki(spki: &[u8]) -> Result<PublicKey> {
let mut spki_item = SECItemBorrowed::wrap(spki);
let spki_item_ptr: *mut SECItem = spki_item.as_mut();
let nss_spki = unsafe {
SubjectPublicKeyInfo::from_ptr(SECKEY_DecodeDERSubjectPublicKeyInfo(spki_item_ptr))?
};
let public_key = unsafe { PublicKey::from_ptr(SECKEY_ExtractPublicKey(*nss_spki))? };
Ok(public_key)
}
fn ecdh_nss_raw(client_private: PrivateKey, peer_public: PublicKey) -> Result<Vec<u8>> {
let ecdh_x_coord = unsafe {
PK11_PubDeriveWithKDF(
*client_private,
*peer_public,
PR_FALSE,
std::ptr::null_mut(),
std::ptr::null_mut(),
CKM_ECDH1_DERIVE,
CKM_SHA512_HMAC, CKA_DERIVE, 0,
CKD_NULL,
std::ptr::null_mut(),
std::ptr::null_mut(),
)
.into_result()?
};
let ecdh_x_coord_bytes = ecdh_x_coord.as_bytes()?;
Ok(ecdh_x_coord_bytes.to_vec())
}
fn generate_p256_nss() -> Result<(PrivateKey, PublicKey)> {
let oid_bytes = der::object_id(der::OID_SECP256R1_BYTES)?;
let mut oid = SECItemBorrowed::wrap(&oid_bytes);
let oid_ptr: *mut SECItem = oid.as_mut();
let slot = Slot::internal()?;
let mut client_public_ptr = ptr::null_mut();
unsafe {
let client_private =
PK11_GenerateKeyPairWithOpFlags(
*slot,
CKM_EC_KEY_PAIR_GEN,
oid_ptr.cast(),
&mut client_public_ptr,
PK11_ATTR_EXTRACTABLE | PK11_ATTR_INSENSITIVE | PK11_ATTR_SESSION,
CKF_DERIVE,
CKF_DERIVE,
ptr::null_mut(),
)
.into_result()?;
let client_public = PublicKey::from_ptr(client_public_ptr)?;
Ok((client_private, client_public))
}
}
pub fn gen_p256() -> Result<(Vec<u8>, Vec<u8>)> {
nss_gk_api::init();
let (client_private, client_public) = generate_p256_nss()?;
let pkcs8_priv = unsafe {
let pkcs8_priv_item: ScopedSECItem =
PK11_ExportDERPrivateKeyInfo(*client_private, ptr::null_mut()).into_result()?;
pkcs8_priv_item.into_vec()
};
let sec1_pub = client_public.key_data()?;
Ok((pkcs8_priv, sec1_pub))
}
pub fn ecdsa_p256_sha256_sign_raw(private: &[u8], data: &[u8]) -> Result<Vec<u8>> {
nss_gk_api::init();
let slot = Slot::internal()?;
let imported_private: PrivateKey = unsafe {
let mut imported_private_ptr = ptr::null_mut();
PK11_ImportDERPrivateKeyInfoAndReturnKey(
*slot,
SECItemBorrowed::wrap(private).as_mut(),
ptr::null_mut(),
ptr::null_mut(),
PR_FALSE,
PR_FALSE,
255,
&mut imported_private_ptr,
ptr::null_mut(),
);
imported_private_ptr.into_result()?
};
let signature_buf = vec![0; 64];
unsafe {
PK11_SignWithMechanism(
*imported_private,
CKM_ECDSA_SHA256,
ptr::null_mut(),
SECItemBorrowed::wrap(&signature_buf).as_mut(),
SECItemBorrowed::wrap(data).as_mut(),
)
.into_result()?;
}
let (r, s) = signature_buf.split_at(32);
der::sequence(&[&der::integer(r)?, &der::integer(s)?])
}
pub fn ecdhe_p256_raw(peer: &super::COSEEC2Key) -> Result<(Vec<u8>, Vec<u8>)> {
let peer_spki = peer.der_spki()?;
nss_gk_api::init();
let peer_public = nss_public_key_from_der_spki(&peer_spki)?;
let (client_private, client_public) = generate_p256_nss()?;
let shared_point = ecdh_nss_raw(client_private, peer_public)?;
Ok((shared_point, client_public.key_data()?))
}
pub fn encrypt_aes_256_cbc_no_pad(key: &[u8], iv: Option<&[u8]>, data: &[u8]) -> Result<Vec<u8>> {
nss_gk_api::init();
if key.len() != 32 {
return Err(CryptoError::LibraryFailure);
}
let iv = iv.unwrap_or(&[0u8; AES_BLOCK_SIZE]);
if iv.len() != AES_BLOCK_SIZE {
return Err(CryptoError::LibraryFailure);
}
let in_len = match c_uint::try_from(data.len()) {
Ok(in_len) => in_len,
_ => return Err(CryptoError::LibraryFailure),
};
if data.len() % AES_BLOCK_SIZE != 0 {
return Err(CryptoError::LibraryFailure);
}
let slot = Slot::internal()?;
let sym_key = unsafe {
PK11_ImportSymKey(
*slot,
CKM_AES_CBC,
PK11Origin::PK11_OriginUnwrap,
CKA_ENCRYPT,
SECItemBorrowed::wrap(key).as_mut(),
ptr::null_mut(),
)
.into_result()?
};
let mut params = SECItemBorrowed::wrap(iv);
let params_ptr: *mut SECItem = params.as_mut();
let mut out_len: c_uint = 0;
let mut out = vec![0; data.len()];
unsafe {
PK11_Encrypt(
*sym_key,
CKM_AES_CBC,
params_ptr,
out.as_mut_ptr(),
&mut out_len,
in_len,
data.as_ptr(),
in_len,
)
.into_result()?
}
debug_assert_eq!(out_len, in_len);
Ok(out)
}
pub fn decrypt_aes_256_cbc_no_pad(key: &[u8], iv: Option<&[u8]>, data: &[u8]) -> Result<Vec<u8>> {
nss_gk_api::init();
if key.len() != 32 {
return Err(CryptoError::LibraryFailure);
}
let iv = iv.unwrap_or(&[0u8; AES_BLOCK_SIZE]);
if iv.len() != AES_BLOCK_SIZE {
return Err(CryptoError::LibraryFailure);
}
let in_len = match c_uint::try_from(data.len()) {
Ok(in_len) => in_len,
_ => return Err(CryptoError::LibraryFailure),
};
if data.len() % AES_BLOCK_SIZE != 0 {
return Err(CryptoError::LibraryFailure);
}
let slot = Slot::internal()?;
let sym_key = unsafe {
PK11_ImportSymKey(
*slot,
CKM_AES_CBC,
PK11Origin::PK11_OriginUnwrap,
CKA_ENCRYPT,
SECItemBorrowed::wrap(key).as_mut(),
ptr::null_mut(),
)
.into_result()?
};
let mut params = SECItemBorrowed::wrap(iv);
let params_ptr: *mut SECItem = params.as_mut();
let mut out_len: c_uint = 0;
let mut out = vec![0; data.len()];
unsafe {
PK11_Decrypt(
*sym_key,
CKM_AES_CBC,
params_ptr,
out.as_mut_ptr(),
&mut out_len,
in_len,
data.as_ptr(),
in_len,
)
.into_result()?
}
debug_assert_eq!(out_len, in_len);
Ok(out)
}
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
nss_gk_api::init();
let data_len = match u32::try_from(data.len()) {
Ok(data_len) => data_len,
_ => return Err(CryptoError::LibraryFailure),
};
let slot = Slot::internal()?;
let sym_key = unsafe {
PK11_ImportSymKey(
*slot,
CKM_SHA256_HMAC,
PK11Origin::PK11_OriginUnwrap,
CKA_SIGN,
SECItemBorrowed::wrap(key).as_mut(),
ptr::null_mut(),
)
.into_result()?
};
let param = SECItemBorrowed::make_empty();
let context = unsafe {
PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, *sym_key, param.as_ref())
.into_result()?
};
unsafe { PK11_DigestOp(*context, data.as_ptr(), data_len).into_result()? };
let mut digest = vec![0u8; SHA256_LENGTH];
let mut digest_len = 0u32;
unsafe {
PK11_DigestFinal(
*context,
digest.as_mut_ptr(),
&mut digest_len,
digest.len() as u32,
)
.into_result()?
}
assert_eq!(digest_len as usize, SHA256_LENGTH);
Ok(digest)
}
pub fn sha256(data: &[u8]) -> Result<Vec<u8>> {
nss_gk_api::init();
let data_len: i32 = match i32::try_from(data.len()) {
Ok(data_len) => data_len,
_ => return Err(CryptoError::LibraryFailure),
};
let mut digest = vec![0u8; SHA256_LENGTH];
unsafe {
PK11_HashBuf(
SECOidTag::SEC_OID_SHA256,
digest.as_mut_ptr(),
data.as_ptr(),
data_len,
)
.into_result()?
};
Ok(digest)
}
pub fn random_bytes(count: usize) -> Result<Vec<u8>> {
nss_gk_api::init();
let count_cint: c_int = match c_int::try_from(count) {
Ok(c) => c,
_ => return Err(CryptoError::LibraryFailure),
};
let mut out = vec![0u8; count];
unsafe { PK11_GenerateRandom(out.as_mut_ptr(), count_cint).into_result()? };
Ok(out)
}
#[cfg(test)]
pub fn test_ecdh_p256_raw(
peer: &super::COSEEC2Key,
client_public_x: &[u8],
client_public_y: &[u8],
client_private: &[u8],
) -> Result<Vec<u8>> {
nss_gk_api::init();
let peer_spki = peer.der_spki()?;
let peer_public = nss_public_key_from_der_spki(&peer_spki)?;
let priv_key_info = der::sequence(&[
&der::integer(&[0x00])?,
&der::sequence(&[
&der::object_id(der::OID_EC_PUBLIC_KEY_BYTES)?,
&der::object_id(der::OID_SECP256R1_BYTES)?,
])?,
&der::octet_string(
&der::sequence(&[
&der::integer(&[0x01])?,
&der::octet_string(client_private)?,
&der::context_specific_explicit_tag(
1, &der::bit_string(&[&[0x04], client_public_x, client_public_y].concat())?,
)?,
])?,
)?,
])?;
let slot = Slot::internal()?;
let mut priv_key_info_item = SECItemBorrowed::wrap(&priv_key_info);
let priv_key_info_item_ptr: *mut SECItem = priv_key_info_item.as_mut();
let mut client_private_ptr = ptr::null_mut();
unsafe {
PK11_ImportDERPrivateKeyInfoAndReturnKey(
*slot,
priv_key_info_item_ptr,
ptr::null_mut(),
ptr::null_mut(),
PR_FALSE,
PR_FALSE,
255,
&mut client_private_ptr,
ptr::null_mut(),
)
};
let client_private = unsafe { PrivateKey::from_ptr(client_private_ptr) }?;
let shared_point = ecdh_nss_raw(client_private, peer_public)?;
Ok(shared_point)
}
#[cfg(test)]
pub fn test_ecdsa_p256_sha256_verify_raw(
public: &[u8],
signature: &[u8],
data: &[u8],
) -> Result<()> {
nss_gk_api::init();
let signature = der::read_p256_sig(signature)?;
let public = nss_public_key_from_der_spki(public)?;
unsafe {
PK11_VerifyWithMechanism(
*public,
CKM_ECDSA_SHA256,
ptr::null_mut(),
SECItemBorrowed::wrap(&signature).as_mut(),
SECItemBorrowed::wrap(data).as_mut(),
ptr::null_mut(),
)
.into_result()?
}
Ok(())
}