Skip to content

Commit

Permalink
Create an abstraction around public and private keys
Browse files Browse the repository at this point in the history
Since it is planned to add a "standalone" feature that will replace
OpenSSL by crates not linking to any external library, it is required to
abstract all the OpenSSL specific types. This is a huge work and
therefore is divided in several steps. This first one is dedicated to
public and private keys.

rel #2
  • Loading branch information
breard-r committed Jun 7, 2019
1 parent f2be8ba commit 63e3571
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 213 deletions.
3 changes: 3 additions & 0 deletions acme_common/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod openssl_keys;
pub use openssl_keys::{gen_keypair, KeyType, PrivateKey, PublicKey};
pub const DEFAULT_ALGO: &str = "rsa2048";
223 changes: 223 additions & 0 deletions acme_common/src/crypto/openssl_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
use crate::b64_encode;
use crate::error::Error;
use openssl::bn::{BigNum, BigNumContext};
use openssl::ec::{EcGroup, EcKey};
use openssl::ecdsa::EcdsaSig;
use openssl::nid::Nid;
use openssl::pkey::{Id, PKey, Private, Public};
use openssl::rsa::Rsa;
use serde_json::json;
use std::fmt;
use std::str::FromStr;

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,
_ => {
return Err("Unsupported EC key".into());
}
},
_ => {
return Err("Unsupported key type".into());
}
}
};
}

#[derive(Clone, Copy, Debug)]
pub enum KeyType {
Rsa2048,
Rsa4096,
EcdsaP256,
EcdsaP384,
}

impl FromStr for KeyType {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Error> {
match s.to_lowercase().as_str() {
"rsa2048" => Ok(KeyType::Rsa2048),
"rsa4096" => Ok(KeyType::Rsa4096),
"ecdsa_p256" => Ok(KeyType::EcdsaP256),
"ecdsa_p384" => Ok(KeyType::EcdsaP384),
_ => Err(format!("{}: unknown algorithm.", s).into()),
}
}
}

impl fmt::Display for KeyType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
KeyType::Rsa2048 => "rsa2048",
KeyType::Rsa4096 => "rsa4096",
KeyType::EcdsaP256 => "ecdsa-p256",
KeyType::EcdsaP384 => "ecdsa-p384",
};
write!(f, "{}", s)
}
}

pub struct PublicKey {
pub key_type: KeyType,
pub inner_key: PKey<Public>,
}

impl PublicKey {
pub fn from_pem(pem_data: &[u8]) -> Result<Self, Error> {
let inner_key = PKey::public_key_from_pem(pem_data)?;
let key_type = get_key_type!(inner_key);
Ok(PublicKey {
key_type,
inner_key,
})
}

pub fn to_pem(&self) -> Result<Vec<u8>, Error> {
self.inner_key
.public_key_to_pem()
.map_err(|e| Error::from(e))
}
}

pub struct PrivateKey {
pub key_type: KeyType,
pub inner_key: PKey<Private>,
}

impl PrivateKey {
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(PrivateKey {
key_type,
inner_key,
})
}

pub fn to_pem(&self) -> Result<Vec<u8>, Error> {
self.inner_key
.private_key_to_pem_pkcs8()
.map_err(|e| Error::from(e))
}

pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
match self.key_type {
KeyType::Rsa2048 | KeyType::Rsa4096 => {
// TODO: implement RSA signatures
Err("RSA 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)
}
}
}

pub fn get_jwk_thumbprint(&self) -> Result<String, Error> {
match self.key_type {
KeyType::EcdsaP256 | KeyType::EcdsaP384 => self.get_nist_ec_jwk(),
// TODO: implement RSA JWK thumbprint
KeyType::Rsa2048 | KeyType::Rsa4096 => {
Err("RSA jwk thumbprint are not implemented yet".into())
}
}
}

fn get_nist_ec_jwk(&self) -> Result<String, Error> {
let (x, y) = self.get_nist_ec_coordinates()?;
let crv = match self.key_type {
KeyType::EcdsaP256 => "P-256",
KeyType::EcdsaP384 => "P-384",
_ => {
return Err("Not a NIST elliptic curve.".into());
}
};
let jwk = json!({
"crv": crv,
"kty": "EC",
"x": x,
"y": y,
});
Ok(jwk.to_string())
}

pub fn get_nist_ec_coordinates(&self) -> Result<(String, String), Error> {
let curve = match self.key_type {
KeyType::EcdsaP256 => Nid::X9_62_PRIME256V1,
KeyType::EcdsaP384 => 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());
Ok((x, y))
}
}

fn gen_rsa_pair(nb_bits: u32) -> Result<(PKey<Public>, PKey<Private>), Error> {
let priv_key = Rsa::generate(nb_bits).unwrap();
let pub_key = Rsa::from_public_components(
priv_key.n().to_owned().unwrap(),
priv_key.e().to_owned().unwrap(),
)
.unwrap();
Ok((
PKey::from_rsa(pub_key).unwrap(),
PKey::from_rsa(priv_key).unwrap(),
))
}

fn gen_ec_pair(nid: Nid) -> Result<(PKey<Public>, PKey<Private>), Error> {
let group = EcGroup::from_curve_name(nid).unwrap();
let ec_priv_key = EcKey::generate(&group).unwrap();
let public_key_point = ec_priv_key.public_key();
let ec_pub_key = EcKey::from_public_key(&group, public_key_point).unwrap();
Ok((
PKey::from_ec_key(ec_pub_key).unwrap(),
PKey::from_ec_key(ec_priv_key).unwrap(),
))
}

pub fn gen_keypair(key_type: KeyType) -> Result<(PublicKey, PrivateKey), Error> {
let (pub_key, priv_key) = match key_type {
KeyType::Rsa2048 => gen_rsa_pair(2048),
KeyType::Rsa4096 => gen_rsa_pair(4096),
KeyType::EcdsaP256 => gen_ec_pair(Nid::X9_62_PRIME256V1),
KeyType::EcdsaP384 => gen_ec_pair(Nid::SECP384R1),
}
.map_err(|_| Error::from(format!("Unable to generate a {} key pair.", key_type)))?;
let pub_key = PublicKey {
key_type,
inner_key: pub_key,
};
let priv_key = PrivateKey {
key_type,
inner_key: priv_key,
};
Ok((pub_key, priv_key))
}
45 changes: 0 additions & 45 deletions acme_common/src/gen.rs

This file was deleted.

2 changes: 1 addition & 1 deletion acme_common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use daemonize::Daemonize;

pub mod crypto;
pub mod error;
pub mod gen;
pub mod logs;

pub fn b64_encode<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
Expand Down
16 changes: 8 additions & 8 deletions acmed/src/acme_proto/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ use crate::acme_proto::jws::encode_jwk;
use crate::acme_proto::structs::{Account, AccountResponse, Directory};
use crate::certificate::Certificate;
use crate::storage;
use acme_common::crypto::{PrivateKey, PublicKey};
use acme_common::error::Error;
use openssl::pkey::{PKey, Private, Public};
use std::str::FromStr;

pub struct AccountManager {
pub priv_key: PKey<Private>,
pub pub_key: PKey<Public>,
pub pub_key: PublicKey,
pub priv_key: PrivateKey,
pub account_url: String,
pub orders_url: String,
}
Expand All @@ -23,20 +23,20 @@ impl AccountManager {
root_certs: &[String],
) -> Result<(Self, String), Error> {
// TODO: store the key id (account url)
let (priv_key, pub_key) = if storage::account_files_exists(cert) {
let (pub_key, priv_key) = if storage::account_files_exists(cert) {
// TODO: check if the keys are suitable for the specified signature algorithm
// and, if not, initiate a key rollover.
(
storage::get_account_priv_key(cert)?,
storage::get_account_pub_key(cert)?,
storage::get_account_priv_key(cert)?,
)
} else {
// TODO: allow to change the signature algo
let sign_alg = SignatureAlgorithm::from_str(crate::DEFAULT_JWS_SIGN_ALGO)?;
let (priv_key, pub_key) = sign_alg.gen_key_pair()?;
let (pub_key, priv_key) = sign_alg.gen_key_pair()?;
storage::set_account_priv_key(cert, &priv_key)?;
storage::set_account_pub_key(cert, &pub_key)?;
(priv_key, pub_key)
(pub_key, priv_key)
};
let account = Account::new(cert);
let account = serde_json::to_string(&account)?;
Expand All @@ -50,8 +50,8 @@ impl AccountManager {
&nonce,
)?;
let ac = AccountManager {
priv_key,
pub_key,
priv_key,
account_url,
orders_url: acc_rep.orders.unwrap_or_default(),
};
Expand Down
Loading

0 comments on commit 63e3571

Please sign in to comment.