use super::{
COSEAlgorithm, COSEEC2Key, COSEKey, COSEKeyType,
ECDHSecret, ECDSACurve,
};
use openssl::bn::{BigNum, BigNumContext};
use openssl::derive::Deriver;
#[cfg(test)]
use openssl::ec::PointConversionForm;
use openssl::ec::{EcGroup, EcKey, EcPoint};
use openssl::error::ErrorStack;
use openssl::hash::{hash, MessageDigest};
use openssl::nid::Nid;
use openssl::pkey::{PKey, Private};
use openssl::sign::{Signer, Verifier};
use openssl::symm::{Cipher, Crypter, Mode};
use openssl::x509::X509;
use serde::{Serialize, Serializer};
use serde_bytes::ByteBuf;
fn openssl_string<S>(_: &ErrorStack, s: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str("OpenSSLError")
}
#[derive(Clone, Debug, Serialize)]
pub enum BackendError {
#[serde(serialize_with = "openssl_string")]
OpenSSL(ErrorStack),
UnsupportedCurve(ECDSACurve),
UnsupportedKeyType,
}
impl From<ErrorStack> for BackendError {
fn from(e: ErrorStack) -> Self {
BackendError::OpenSSL(e)
}
}
impl From<&ErrorStack> for BackendError {
fn from(e: &ErrorStack) -> Self {
BackendError::OpenSSL(e.clone())
}
}
pub type Result<T> = std::result::Result<T, BackendError>;
fn to_openssl_name(curve: ECDSACurve) -> Result<Nid> {
match curve {
ECDSACurve::SECP256R1 => Ok(Nid::X9_62_PRIME256V1),
ECDSACurve::SECP384R1 => Ok(Nid::SECP384R1),
ECDSACurve::SECP521R1 => Ok(Nid::SECP521R1),
x => Err(BackendError::UnsupportedCurve(x)),
}
}
fn affine_coordinates(curve: ECDSACurve, bytes: &[u8]) -> Result<(ByteBuf, ByteBuf)> {
let name = to_openssl_name(curve)?;
let group = EcGroup::from_curve_name(name)?;
let mut ctx = BigNumContext::new()?;
let point = EcPoint::from_bytes(&group, bytes, &mut ctx)?;
let mut x = BigNum::new()?;
let mut y = BigNum::new()?;
point.affine_coordinates_gfp(&group, &mut x, &mut y, &mut ctx)?;
Ok((ByteBuf::from(x.to_vec()), ByteBuf::from(y.to_vec())))
}
pub(crate) fn serialize_key(curve: ECDSACurve, key: &[u8]) -> Result<(ByteBuf, ByteBuf)> {
affine_coordinates(curve, key)
}
#[cfg(test)]
pub(crate) fn parse_key(curve: ECDSACurve, x: &[u8], y: &[u8]) -> Result<Vec<u8>> {
let name = to_openssl_name(curve)?;
let group = EcGroup::from_curve_name(name)?;
let mut ctx = BigNumContext::new()?;
let x = BigNum::from_slice(x)?;
let y = BigNum::from_slice(y)?;
let key = EcKey::from_public_key_affine_coordinates(&group, &x, &y)?;
let pub_key = key.public_key();
Ok(pub_key.to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx)?)
}
pub(crate) fn encapsulate(key: &COSEKey) -> Result<ECDHSecret> {
if let COSEKeyType::EC2(ec2key) = &key.key {
let curve_name = to_openssl_name(ec2key.curve)?;
let group = EcGroup::from_curve_name(curve_name)?;
let my_key = EcKey::generate(&group)?;
encapsulate_helper(&ec2key, key.alg, group, my_key)
} else {
Err(BackendError::UnsupportedKeyType)
}
}
pub(crate) fn encapsulate_helper(
key: &COSEEC2Key,
alg: COSEAlgorithm,
group: EcGroup,
my_key: EcKey<Private>,
) -> Result<ECDHSecret> {
let mut ctx = BigNumContext::new()?;
let mut x = BigNum::new()?;
let mut y = BigNum::new()?;
my_key
.public_key()
.affine_coordinates_gfp(&group, &mut x, &mut y, &mut ctx)?;
let my_public_key = COSEKey {
alg,
key: COSEKeyType::EC2(COSEEC2Key {
curve: key.curve.clone(),
x: x.to_vec(),
y: y.to_vec(),
}),
};
let peer_public_key = PKey::from_ec_key(EcKey::from_public_key_affine_coordinates(
&group,
BigNum::from_slice(&key.x).as_ref()?,
BigNum::from_slice(&key.y).as_ref()?,
)?)?;
let my_ec_key = PKey::from_ec_key(my_key)?;
let mut deriver = Deriver::new(my_ec_key.as_ref())?;
deriver.set_peer(&peer_public_key)?;
let shared_sec = deriver.derive_to_vec()?;
let digest = hash(MessageDigest::sha256(), &shared_sec)?;
Ok(ECDHSecret {
remote: COSEKey {
alg,
key: COSEKeyType::EC2(key.clone()),
},
my: my_public_key,
shared_secret: digest.as_ref().to_vec(),
})
}
#[cfg(test)]
pub(crate) fn test_encapsulate(
key: &COSEEC2Key,
alg: COSEAlgorithm,
my_pub_key: &[u8],
my_priv_key: &[u8],
) -> Result<ECDHSecret> {
let curve_name = to_openssl_name(key.curve)?;
let group = EcGroup::from_curve_name(curve_name)?;
let mut ctx = BigNumContext::new()?;
let my_pub_point = EcPoint::from_bytes(&group, &my_pub_key, &mut ctx)?;
let my_priv_bignum = BigNum::from_slice(my_priv_key)?;
let my_key = EcKey::from_private_components(&group, &my_priv_bignum, &my_pub_point)?;
encapsulate_helper(key, alg, group, my_key)
}
pub(crate) fn encrypt(
key: &[u8],
plain_text: &[u8],
) -> Result<Vec<u8> > {
let cipher = Cipher::aes_256_cbc();
let mut cypher_text = vec![0; plain_text.len() * 2];
cypher_text.resize(plain_text.len() * 2, 0);
let iv = [0u8; 16];
let mut encrypter = Crypter::new(cipher, Mode::Encrypt, key, Some(&iv))?;
encrypter.pad(false);
let mut out_size = 0;
out_size += encrypter.update(plain_text, cypher_text.as_mut_slice())?;
out_size += encrypter.finalize(cypher_text.as_mut_slice())?;
cypher_text.truncate(out_size);
Ok(cypher_text)
}
pub(crate) fn decrypt(
key: &[u8],
cypher_text: &[u8],
) -> Result<Vec<u8> > {
let cipher = Cipher::aes_256_cbc();
let mut plain_text = vec![0; cypher_text.len() * 2];
plain_text.resize(cypher_text.len() * 2, 0);
let iv = [0u8; 16];
let mut encrypter = Crypter::new(cipher, Mode::Decrypt, key, Some(&iv))?;
encrypter.pad(false);
let mut out_size = 0;
out_size += encrypter.update(cypher_text, plain_text.as_mut_slice())?;
out_size += encrypter.finalize(plain_text.as_mut_slice())?;
plain_text.truncate(out_size);
Ok(plain_text)
}
pub(crate) fn authenticate(token: &[u8], input: &[u8]) -> Result<Vec<u8>> {
let key = PKey::hmac(token)?;
let mut signer = Signer::new(MessageDigest::sha256(), &key)?;
signer.update(input)?;
let hmac = signer.sign_to_vec()?;
Ok(hmac)
}
#[allow(dead_code)]
pub(crate) fn verify(
sig_alg: ECDSACurve,
pub_key: &[u8],
signature: &[u8],
data: &[u8],
) -> Result<bool> {
let _alg = to_openssl_name(sig_alg)?; let pkey = X509::from_der(&pub_key)?;
let pubkey = pkey.public_key()?;
let mut verifier = Verifier::new(MessageDigest::sha256(), &pubkey)?;
verifier.update(data)?;
let res = verifier.verify(signature)?;
Ok(res)
}