use std::sync::Arc;
use rustls::{
sign::{Signer, SigningKey},
SignatureAlgorithm, SignatureScheme, {Error, OtherError},
};
use windows_sys::Win32::Security::Cryptography::{
BCryptHash, CryptEncodeObjectEx, BCRYPT_SHA256_ALG_HANDLE, BCRYPT_SHA384_ALG_HANDLE,
BCRYPT_SHA512_ALG_HANDLE, CERT_ECC_SIGNATURE, CRYPT_INTEGER_BLOB, X509_ASN_ENCODING,
X509_ECC_SIGNATURE,
};
use crate::key::{AlgorithmGroup, NCryptKey, SignaturePadding};
fn p1363_to_der(data: &mut [u8]) -> Result<Vec<u8>, Error> {
if data.is_empty() || !data.len().is_multiple_of(2) {
return Err(Error::General("Invalid signature size".to_owned()));
}
let (r, s) = data.split_at_mut(data.len() / 2);
r.reverse();
s.reverse();
let sig = CERT_ECC_SIGNATURE {
r: CRYPT_INTEGER_BLOB {
cbData: r.len() as u32,
pbData: r.as_mut_ptr(),
},
s: CRYPT_INTEGER_BLOB {
cbData: s.len() as u32,
pbData: s.as_mut_ptr(),
},
};
let sig_ptr = std::ptr::from_ref(&sig).cast();
unsafe {
let mut len = 0u32;
let status = CryptEncodeObjectEx(
X509_ASN_ENCODING,
X509_ECC_SIGNATURE,
sig_ptr,
0,
std::ptr::null(),
std::ptr::null_mut(),
&mut len,
);
if status == 0 {
return Err(Error::General(
"CryptEncodeObjectEx failed to size the signature".to_owned(),
));
}
let mut der = vec![0u8; len as usize];
let status = CryptEncodeObjectEx(
X509_ASN_ENCODING,
X509_ECC_SIGNATURE,
sig_ptr,
0,
std::ptr::null(),
der.as_mut_ptr().cast(),
&mut len,
);
if status == 0 {
return Err(Error::General(
"CryptEncodeObjectEx failed to encode the signature".to_owned(),
));
}
der.truncate(len as usize);
Ok(der)
}
}
#[derive(Debug, Clone)]
pub struct CngSigningKey {
key: NCryptKey,
algorithm_group: AlgorithmGroup,
bits: u32,
}
impl CngSigningKey {
pub fn new(key: NCryptKey) -> crate::Result<Self> {
let group = key.algorithm_group()?;
let bits = key.bits()?;
Ok(Self {
key,
algorithm_group: group,
bits,
})
}
pub fn key(&self) -> &NCryptKey {
&self.key
}
pub fn algorithm_group(&self) -> AlgorithmGroup {
self.algorithm_group
}
pub fn bits(&self) -> u32 {
self.bits
}
pub fn supported_schemes(&self) -> &[SignatureScheme] {
match self.algorithm_group {
AlgorithmGroup::Rsa => &[
SignatureScheme::RSA_PKCS1_SHA256,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA512,
],
AlgorithmGroup::Ecdsa | AlgorithmGroup::Ecdh => match self.bits {
256 => &[SignatureScheme::ECDSA_NISTP256_SHA256],
384 => &[SignatureScheme::ECDSA_NISTP384_SHA384],
521 => &[SignatureScheme::ECDSA_NISTP521_SHA512],
_ => &[],
},
}
}
}
#[derive(Debug)]
struct CngSigner {
key: NCryptKey,
scheme: SignatureScheme,
}
impl CngSigner {
fn hash(&self, message: &[u8]) -> Result<(Vec<u8>, SignaturePadding), Error> {
let (alg, padding) = match self.scheme {
SignatureScheme::RSA_PKCS1_SHA256 => {
(BCRYPT_SHA256_ALG_HANDLE, SignaturePadding::Pkcs1)
}
SignatureScheme::RSA_PKCS1_SHA384 => {
(BCRYPT_SHA384_ALG_HANDLE, SignaturePadding::Pkcs1)
}
SignatureScheme::RSA_PKCS1_SHA512 => {
(BCRYPT_SHA512_ALG_HANDLE, SignaturePadding::Pkcs1)
}
SignatureScheme::RSA_PSS_SHA256 => (BCRYPT_SHA256_ALG_HANDLE, SignaturePadding::Pss),
SignatureScheme::RSA_PSS_SHA384 => (BCRYPT_SHA384_ALG_HANDLE, SignaturePadding::Pss),
SignatureScheme::RSA_PSS_SHA512 => (BCRYPT_SHA512_ALG_HANDLE, SignaturePadding::Pss),
SignatureScheme::ECDSA_NISTP256_SHA256 => {
(BCRYPT_SHA256_ALG_HANDLE, SignaturePadding::None)
}
SignatureScheme::ECDSA_NISTP384_SHA384 => {
(BCRYPT_SHA384_ALG_HANDLE, SignaturePadding::None)
}
SignatureScheme::ECDSA_NISTP521_SHA512 => {
(BCRYPT_SHA512_ALG_HANDLE, SignaturePadding::None)
}
_ => return Err(Error::General("Unsupported signature scheme".to_owned())),
};
let hash_len = match alg {
BCRYPT_SHA256_ALG_HANDLE => 32,
BCRYPT_SHA384_ALG_HANDLE => 48,
BCRYPT_SHA512_ALG_HANDLE => 64,
_ => return Err(Error::General("Unsupported hash algorithm!".to_owned())),
};
let mut hash = vec![0u8; hash_len];
unsafe {
let status = BCryptHash(
alg,
std::ptr::null_mut(), 0, message.as_ptr().cast(),
message.len() as u32,
hash.as_mut_ptr(),
hash_len as u32,
);
if status != 0 {
return Err(Error::General(format!(
"BCryptHash failed with status: 0x{status:X}"
)));
}
}
Ok((hash, padding))
}
}
impl Signer for CngSigner {
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
let (hash, padding) = self.hash(message)?;
let mut signature = self
.key
.sign(&hash, padding)
.map_err(|e| Error::Other(OtherError(Arc::new(e))))?;
if padding == SignaturePadding::None {
Ok(p1363_to_der(&mut signature)?)
} else {
Ok(signature)
}
}
fn scheme(&self) -> SignatureScheme {
self.scheme
}
}
impl SigningKey for CngSigningKey {
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
let supported = self.supported_schemes();
for scheme in offered {
if supported.contains(scheme) {
return Some(Box::new(CngSigner {
key: self.key.clone(),
scheme: *scheme,
}));
}
}
None
}
fn algorithm(&self) -> SignatureAlgorithm {
match self.algorithm_group {
AlgorithmGroup::Rsa => SignatureAlgorithm::RSA,
AlgorithmGroup::Ecdsa | AlgorithmGroup::Ecdh => SignatureAlgorithm::ECDSA,
}
}
}
#[cfg(test)]
mod tests {
use std::ptr;
use windows_sys::Win32::Security::Cryptography::{
CryptDecodeObjectEx, CERT_ECC_SIGNATURE, CRYPT_INTEGER_BLOB, X509_ASN_ENCODING,
X509_ECC_SIGNATURE,
};
unsafe fn blob_to_be(blob: &CRYPT_INTEGER_BLOB) -> Vec<u8> {
let le = unsafe { std::slice::from_raw_parts(blob.pbData, blob.cbData as usize) };
let mut be = le.iter().rev().copied().collect::<Vec<u8>>();
while be.len() > 1 && be[0] == 0 {
be.remove(0);
}
be
}
fn decode_der(data: &[u8]) -> (Vec<u8>, Vec<u8>) {
unsafe {
let mut len = 0u32;
let status = CryptDecodeObjectEx(
X509_ASN_ENCODING,
X509_ECC_SIGNATURE,
data.as_ptr(),
data.len() as u32,
0,
ptr::null(),
ptr::null_mut(),
&mut len,
);
assert_ne!(status, 0, "CryptDecodeObjectEx failed to size the output");
let mut buf = vec![0u8; len as usize];
let status = CryptDecodeObjectEx(
X509_ASN_ENCODING,
X509_ECC_SIGNATURE,
data.as_ptr(),
data.len() as u32,
0,
ptr::null(),
buf.as_mut_ptr().cast(),
&mut len,
);
assert_ne!(status, 0, "CryptDecodeObjectEx failed to decode");
let sig: &CERT_ECC_SIGNATURE = &*buf.as_ptr().cast();
(blob_to_be(&sig.r), blob_to_be(&sig.s))
}
}
fn validate_der(data: &[u8], r: &[u8], s: &[u8]) {
let (parsed_r, parsed_s) = decode_der(data);
assert_eq!(parsed_r, r);
assert_eq!(parsed_s, s);
}
#[test]
fn test_p1363_to_der() {
let mut p1363 = [1, 2, 3, 4, 5, 6, 7, 8];
let der = super::p1363_to_der(&mut p1363).unwrap();
validate_der(&der, &[1, 2, 3, 4], &[5, 6, 7, 8]);
}
#[test]
fn test_p1363_to_der_signed() {
let mut p1363 = [0x81, 2, 3, 4, 0x85, 6, 7, 8];
let der = super::p1363_to_der(&mut p1363).unwrap();
validate_der(&der, &[0x81, 2, 3, 4], &[0x85, 6, 7, 8]);
}
#[test]
fn test_p1363_to_der_zeroes_stripped() {
let mut p1363 = [0, 1, 2, 3, 4, 0, 5, 6, 7, 8];
let der = super::p1363_to_der(&mut p1363).unwrap();
validate_der(&der, &[1, 2, 3, 4], &[5, 6, 7, 8]);
}
#[test]
fn test_p1363_to_der_signed_zeroes_stripped() {
let mut p1363 = [0, 0x81, 2, 3, 4, 0, 0x85, 6, 7, 8];
let der = super::p1363_to_der(&mut p1363).unwrap();
validate_der(&der, &[0x81, 2, 3, 4], &[0x85, 6, 7, 8]);
}
#[test]
fn test_p1363_to_der_long() {
let r = (1..128).collect::<Vec<u8>>();
let s = (128..254).chain([0]).rev().collect::<Vec<u8>>();
let mut p1363 = r.clone().into_iter().chain(s.clone()).collect::<Vec<u8>>();
let der = super::p1363_to_der(&mut p1363).unwrap();
let expected_s = (128..254).rev().collect::<Vec<u8>>();
validate_der(&der, &r, &expected_s);
}
}