mirror of https://github.com/breard-r/acmed.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
5.8 KiB
174 lines
5.8 KiB
use crate::b64_encode;
|
|
use crate::crypto::KeyType;
|
|
use crate::error::Error;
|
|
use openssl::bn::{BigNum, BigNumContext};
|
|
use openssl::ec::{EcGroup, EcKey, Asn1Flag};
|
|
use openssl::ecdsa::EcdsaSig;
|
|
use openssl::nid::Nid;
|
|
use openssl::pkey::{Id, PKey, Private};
|
|
use openssl::rsa::Rsa;
|
|
use serde_json::json;
|
|
use serde_json::value::Value;
|
|
|
|
macro_rules! get_key_type {
|
|
($key: expr) => {
|
|
match $key.id() {
|
|
Id::RSA => match $key.rsa()?.size() {
|
|
2048 => KeyType::Rsa2048,
|
|
4096 => KeyType::Rsa4096,
|
|
s => {
|
|
return Err(format!("{}: unsupported RSA key size", s).into());
|
|
}
|
|
},
|
|
Id::EC => match $key.ec_key()?.group().curve_name() {
|
|
Some(Nid::X9_62_PRIME256V1) => KeyType::EcdsaP256,
|
|
Some(Nid::SECP384R1) => KeyType::EcdsaP384,
|
|
Some(nid) => {
|
|
return Err(format!("{:?}: Unsupported EC key.", nid).into());
|
|
}
|
|
None => {
|
|
return Err("None: Unsupported EC key".into());
|
|
}
|
|
},
|
|
_ => {
|
|
return Err("Unsupported key type".into());
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
pub struct KeyPair {
|
|
pub key_type: KeyType,
|
|
pub inner_key: PKey<Private>,
|
|
}
|
|
|
|
impl KeyPair {
|
|
pub fn from_pem(pem_data: &[u8]) -> Result<Self, Error> {
|
|
let inner_key = PKey::private_key_from_pem(pem_data)?;
|
|
let key_type = get_key_type!(inner_key);
|
|
Ok(KeyPair {
|
|
key_type,
|
|
inner_key,
|
|
})
|
|
}
|
|
|
|
pub fn private_key_to_pem(&self) -> Result<Vec<u8>, Error> {
|
|
self.inner_key
|
|
.private_key_to_pem_pkcs8()
|
|
.map_err(Error::from)
|
|
}
|
|
|
|
pub fn public_key_to_pem(&self) -> Result<Vec<u8>, Error> {
|
|
self.inner_key.public_key_to_pem().map_err(Error::from)
|
|
}
|
|
|
|
pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
|
|
match self.key_type {
|
|
KeyType::Curve25519 => Err("Curve25519 signatures are not implemented yet".into()),
|
|
KeyType::EcdsaP256 | KeyType::EcdsaP384 => {
|
|
let signature = EcdsaSig::sign(data, self.inner_key.ec_key()?.as_ref())?;
|
|
let r = signature.r().to_vec();
|
|
let mut s = signature.s().to_vec();
|
|
let mut signature = r;
|
|
signature.append(&mut s);
|
|
Ok(signature)
|
|
}
|
|
KeyType::Rsa2048 | KeyType::Rsa4096 => {
|
|
// TODO: implement RSA signatures
|
|
Err("RSA signatures are not implemented yet".into())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn jwk_public_key(&self) -> Result<Value, Error> {
|
|
self.get_jwk_public_key(false)
|
|
}
|
|
|
|
pub fn jwk_public_key_thumbprint(&self) -> Result<Value, Error> {
|
|
self.get_jwk_public_key(true)
|
|
}
|
|
|
|
fn get_jwk_public_key(&self, thumbprint: bool) -> Result<Value, Error> {
|
|
match self.key_type {
|
|
KeyType::Curve25519 => Err("Curve25519 thumbprint are not implemented yet".into()),
|
|
KeyType::EcdsaP256 | KeyType::EcdsaP384 => self.get_nist_ec_jwk(thumbprint),
|
|
KeyType::Rsa2048 | KeyType::Rsa4096 => {
|
|
Err("RSA jwk thumbprint are not implemented yet".into())
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_nist_ec_jwk(&self, thumbprint: bool) -> Result<Value, Error> {
|
|
let (crv, curve) = match self.key_type {
|
|
KeyType::EcdsaP256 => ("P-256", Nid::X9_62_PRIME256V1),
|
|
KeyType::EcdsaP384 => ("P-384", Nid::SECP384R1),
|
|
_ => {
|
|
return Err("Not a NIST elliptic curve.".into());
|
|
}
|
|
};
|
|
let group = EcGroup::from_curve_name(curve).unwrap();
|
|
let mut ctx = BigNumContext::new().unwrap();
|
|
let mut x = BigNum::new().unwrap();
|
|
let mut y = BigNum::new().unwrap();
|
|
self.inner_key
|
|
.ec_key()
|
|
.unwrap()
|
|
.public_key()
|
|
.affine_coordinates_gfp(&group, &mut x, &mut y, &mut ctx)?;
|
|
let x = b64_encode(&x.to_vec());
|
|
let y = b64_encode(&y.to_vec());
|
|
let jwk = if thumbprint {
|
|
json!({
|
|
"crv": crv,
|
|
"kty": "EC",
|
|
"x": x,
|
|
"y": y,
|
|
})
|
|
} else {
|
|
json!({
|
|
"alg": "ES256",
|
|
"crv": crv,
|
|
"kty": "EC",
|
|
"use": "sig",
|
|
"x": x,
|
|
"y": y,
|
|
})
|
|
};
|
|
Ok(jwk)
|
|
}
|
|
}
|
|
|
|
fn gen_rsa_pair(nb_bits: u32) -> Result<PKey<Private>, Error> {
|
|
// TODO: check if map_err is required
|
|
let priv_key = Rsa::generate(nb_bits).map_err(|_| Error::from(""))?;
|
|
let pk = PKey::from_rsa(priv_key).map_err(|_| Error::from(""))?;
|
|
Ok(pk)
|
|
}
|
|
|
|
fn gen_ec_pair(nid: Nid) -> Result<PKey<Private>, Error> {
|
|
// TODO: check if map_err is required
|
|
let mut group = EcGroup::from_curve_name(nid).map_err(|_| Error::from(""))?;
|
|
|
|
// Use NAMED_CURVE format; OpenSSL 1.0.1 and 1.0.2 default to EXPLICIT_CURVE which won't work (see #9)
|
|
group.set_asn1_flag(Asn1Flag::NAMED_CURVE);
|
|
|
|
let ec_priv_key = EcKey::generate(&group).map_err(|_| Error::from(""))?;
|
|
let pk = PKey::from_ec_key(ec_priv_key).map_err(|_| Error::from(""))?;
|
|
Ok(pk)
|
|
}
|
|
|
|
pub fn gen_keypair(key_type: KeyType) -> Result<KeyPair, Error> {
|
|
let priv_key = match key_type {
|
|
KeyType::Curve25519 => Err(Error::from("")),
|
|
KeyType::EcdsaP256 => gen_ec_pair(Nid::X9_62_PRIME256V1),
|
|
KeyType::EcdsaP384 => gen_ec_pair(Nid::SECP384R1),
|
|
KeyType::Rsa2048 => gen_rsa_pair(2048),
|
|
KeyType::Rsa4096 => gen_rsa_pair(4096),
|
|
}
|
|
.map_err(|_| Error::from(format!("Unable to generate a {} key pair.", key_type)))?;
|
|
let key_pair = KeyPair {
|
|
key_type,
|
|
inner_key: priv_key,
|
|
};
|
|
Ok(key_pair)
|
|
}
|