#![forbid(unsafe_code)]
#![deny(missing_docs)]
extern crate yasna;
extern crate ring;
extern crate pem;
extern crate untrusted;
extern crate chrono;
extern crate bit_vec;
use yasna::Tag;
use yasna::models::ObjectIdentifier;
use pem::Pem;
use ring::digest;
use ring::signature::{EcdsaKeyPair, Ed25519KeyPair};
use ring::rand::SystemRandom;
use ring::signature::KeyPair;
use untrusted::Input;
use ring::signature::{self, EcdsaSigningAlgorithm, EdDSAParameters};
use yasna::DERWriter;
use yasna::models::GeneralizedTime;
use chrono::{DateTime, Timelike};
use chrono::{NaiveDate, Utc};
use std::collections::HashMap;
use bit_vec::BitVec;
pub struct Certificate {
params :CertificateParams,
key_pair :DynKeyPair,
key_pair_serialized :Vec<u8>,
}
pub fn generate_simple_self_signed(subject_alt_names :impl Into<Vec<String>>) -> Certificate {
Certificate::from_params(CertificateParams::new(subject_alt_names))
}
const OID_COUNTRY_NAME :&[u64] = &[2, 5, 4, 6];
const OID_ORG_NAME :&[u64] = &[2, 5, 4, 10];
const OID_COMMON_NAME :&[u64] = &[2, 5, 4, 3];
const OID_EC_PUBLIC_KEY :&[u64] = &[1, 2, 840, 10045, 2, 1];
const OID_EC_SECP_256_R1 :&[u64] = &[1, 2, 840, 10045, 3, 1, 7];
const OID_EC_SECP_384_R1 :&[u64] = &[1, 3, 132, 0, 34];
const OID_SUBJECT_ALT_NAME :&[u64] = &[2, 5, 29, 17];
const OID_BASIC_CONSTRAINTS :&[u64] = &[2, 5, 29, 19];
const OID_SUBJECT_KEY_IDENTIFIER :&[u64] = &[2, 5, 29, 14];
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[allow(missing_docs)]
pub enum DnType {
CountryName,
OrganizationName,
CommonName,
#[doc(hidden)]
_Nonexhaustive,
}
impl DnType {
fn to_oid(&self) -> ObjectIdentifier {
let sl = match self {
DnType::CountryName => OID_COUNTRY_NAME,
DnType::OrganizationName => OID_ORG_NAME,
DnType::CommonName => OID_COMMON_NAME,
DnType::_Nonexhaustive => unimplemented!(),
};
ObjectIdentifier::from_slice(sl)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DistinguishedName {
entries :HashMap<DnType, String>,
}
impl DistinguishedName {
pub fn new() -> Self {
Self {
entries : HashMap::new(),
}
}
pub fn push(&mut self, ty :DnType, s :impl Into<String>) {
self.entries.insert(ty, s.into());
}
}
#[allow(missing_docs)]
pub struct CertificateParams {
pub alg :&'static SignatureAlgorithm,
pub not_before :DateTime<Utc>,
pub not_after :DateTime<Utc>,
pub serial_number :Option<u64>,
pub subject_alt_names :Vec<String>,
pub distinguished_name :DistinguishedName,
pub is_ca :IsCa,
_hidden :(),
}
impl Default for CertificateParams {
fn default() -> Self {
let not_before = date_time_ymd(1975, 01, 01);
let not_after = date_time_ymd(4096, 01, 01);
let mut distinguished_name = DistinguishedName::new();
distinguished_name.push(DnType::CommonName, "rcgen self signed cert");
CertificateParams {
alg : &PKCS_ECDSA_P256_SHA256,
not_before,
not_after,
serial_number : None,
subject_alt_names : Vec::new(),
distinguished_name,
is_ca : IsCa::SelfSignedOnly,
_hidden :(),
}
}
}
pub enum IsCa {
SelfSignedOnly,
Ca(BasicConstraints),
}
pub enum BasicConstraints {
Unconstrained,
Constrained(u8),
}
impl CertificateParams {
pub fn new(subject_alt_names :impl Into<Vec<String>>) -> Self {
CertificateParams {
subject_alt_names : subject_alt_names.into(),
.. Default::default()
}
}
}
pub fn date_time_ymd(year :i32, month :u32, day :u32) -> DateTime<Utc> {
let naive_dt = NaiveDate::from_ymd(year, month, day).and_hms_milli(0, 0, 0, 0);
DateTime::<Utc>::from_utc(naive_dt, Utc)
}
fn dt_to_generalized(dt :&DateTime<Utc>) -> GeneralizedTime {
let mut date_time = *dt;
let nanos = if date_time.nanosecond() >= 1_000_000 {
1_000_000
} else {
0
};
date_time = date_time.with_nanosecond(nanos).unwrap();
GeneralizedTime::from_datetime::<Utc>(&date_time)
}
impl Certificate {
pub fn from_params(params :CertificateParams) -> Self {
let (key_pair, key_pair_serialized) = DynKeyPair::generate(¶ms.alg);
Certificate {
params,
key_pair,
key_pair_serialized,
}
}
fn write_name(&self, writer :DERWriter, ca :&Certificate) {
writer.write_sequence(|writer| {
writer.next().write_set(|writer| {
for (ty, content) in ca.params.distinguished_name.entries.iter() {
writer.next().write_sequence(|writer| {
writer.next().write_oid(&ty.to_oid());
writer.next().write_utf8_string(content);
});
}
});
});
}
fn write_cert(&self, writer :DERWriter, ca :&Certificate) {
writer.write_sequence(|writer| {
writer.next().write_tagged(Tag::context(0), |writer| {
writer.write_u8(2);
});
let serial = self.params.serial_number.unwrap_or(42);
writer.next().write_u64(serial);
writer.next().write_sequence(|writer| {
writer.next().write_oid(&self.params.alg.oid());
});
self.write_name(writer.next(), ca);
writer.next().write_sequence(|writer| {
let nb_gt = dt_to_generalized(&self.params.not_before);
writer.next().write_generalized_time(&nb_gt);
let na_gt = dt_to_generalized(&self.params.not_after);
writer.next().write_generalized_time(&na_gt);
});
self.write_name(writer.next(), self);
writer.next().write_sequence(|writer| {
writer.next().write_sequence(|writer| {
for oid in self.params.alg.oids_sign_alg {
let oid = ObjectIdentifier::from_slice(oid);
writer.next().write_oid(&oid);
}
});
let public_key = self.key_pair.public_key();
let pkbs = BitVec::from_bytes(&public_key);
writer.next().write_bitvec(&pkbs);
});
writer.next().write_tagged(Tag::context(3), |writer| {
writer.write_sequence(|writer| {
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(OID_SUBJECT_ALT_NAME);
writer.next().write_oid(&oid);
let bytes = yasna::construct_der(|writer| {
writer.write_sequence(|writer|{
for san in self.params.subject_alt_names.iter() {
writer.next().write_tagged_implicit(Tag::context(2), |writer| {
writer.write_utf8_string(san);
});
}
});
});
writer.next().write_bytes(&bytes);
});
if let IsCa::Ca(ref constraint) = self.params.is_ca {
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(OID_SUBJECT_KEY_IDENTIFIER);
writer.next().write_oid(&oid);
let digest = digest::digest(&self.params.alg.digest_alg, self.key_pair.public_key().as_ref());
writer.next().write_bytes(&digest.as_ref());
});
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(OID_BASIC_CONSTRAINTS);
writer.next().write_oid(&oid);
let bytes = yasna::construct_der(|writer| {
writer.write_sequence(|writer| {
writer.next().write_bool(true);
if let BasicConstraints::Constrained(path_len_constraint) = constraint {
writer.next().write_u8(*path_len_constraint);
}
});
});
writer.next().write_bytes(&bytes);
});
}
});
});
})
}
pub fn serialize_der(&self) -> Vec<u8> {
self.serialize_der_with_signer(&self)
}
pub fn serialize_der_with_signer(&self, ca :&Certificate) -> Vec<u8> {
yasna::construct_der(|writer| {
writer.write_sequence(|writer| {
let tbs_cert_list_serialized = yasna::construct_der(|writer| {
self.write_cert(writer, ca);
});
writer.next().write_der(&tbs_cert_list_serialized);
writer.next().write_sequence(|writer| {
writer.next().write_oid(&self.params.alg.oid());
});
let signature = ca.key_pair.sign(&tbs_cert_list_serialized);
let sig = BitVec::from_bytes(&signature.as_ref());
writer.next().write_bitvec(&sig);
})
})
}
pub fn serialize_pem(&self) -> String {
let p = Pem {
tag : "CERTIFICATE".to_string(),
contents : self.serialize_der(),
};
pem::encode(&p)
}
pub fn serialize_pem_with_signer(&self, ca :&Certificate) -> String {
let p = Pem {
tag : "CERTIFICATE".to_string(),
contents : self.serialize_der_with_signer(ca),
};
pem::encode(&p)
}
pub fn serialize_private_key_der(&self) -> Vec<u8> {
self.key_pair_serialized.clone()
}
pub fn serialize_private_key_pem(&self) -> String {
let p = Pem {
tag : "PRIVATE KEY".to_string(),
contents : self.serialize_private_key_der(),
};
pem::encode(&p)
}
}
enum SignAlgo {
EcDsa(&'static EcdsaSigningAlgorithm),
EdDsa(&'static EdDSAParameters),
}
enum DynKeyPair {
EcKp(EcdsaKeyPair),
EdKp(Ed25519KeyPair),
}
impl DynKeyPair {
fn generate(alg :&SignatureAlgorithm) -> (Self, Vec<u8>) {
let system_random = SystemRandom::new();
match alg.sign_alg {
SignAlgo::EcDsa(sign_alg) => {
let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, &system_random).unwrap();
let key_pair_serialized = key_pair_doc.as_ref().to_vec();
let key_pair = EcdsaKeyPair::from_pkcs8(&sign_alg, Input::from(&&key_pair_doc.as_ref())).unwrap();
(DynKeyPair::EcKp(key_pair), key_pair_serialized)
},
SignAlgo::EdDsa(_sign_alg) => {
let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&system_random).unwrap();
let key_pair_serialized = key_pair_doc.as_ref().to_vec();
let key_pair = Ed25519KeyPair::from_pkcs8(Input::from(&&key_pair_doc.as_ref())).unwrap();
(DynKeyPair::EdKp(key_pair), key_pair_serialized)
},
}
}
fn public_key(&self) -> &[u8] {
match self {
DynKeyPair::EcKp(kp) => kp.public_key().as_ref(),
DynKeyPair::EdKp(kp) => kp.public_key().as_ref(),
}
}
fn sign(&self, msg :&[u8]) -> signature::Signature {
match self {
DynKeyPair::EcKp(kp) => {
let msg_input = Input::from(&msg);
let system_random = SystemRandom::new();
kp.sign(&system_random, msg_input).unwrap()
},
DynKeyPair::EdKp(kp) => {
kp.sign(msg)
}
}
}
}
pub struct SignatureAlgorithm {
oids_sign_alg :&'static [&'static [u64]],
sign_alg :SignAlgo,
digest_alg :&'static ring::digest::Algorithm,
oid_components :&'static [u64],
}
pub static PKCS_ECDSA_P256_SHA256 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&OID_EC_PUBLIC_KEY, &OID_EC_SECP_256_R1],
sign_alg :SignAlgo::EcDsa(&signature::ECDSA_P256_SHA256_ASN1_SIGNING),
digest_alg :&digest::SHA256,
oid_components : &[1, 2, 840, 10045, 4, 3, 2],
};
pub static PKCS_ECDSA_P384_SHA384 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&OID_EC_PUBLIC_KEY, &OID_EC_SECP_384_R1],
sign_alg :SignAlgo::EcDsa(&signature::ECDSA_P384_SHA384_ASN1_SIGNING),
digest_alg :&digest::SHA384,
oid_components : &[1, 2, 840, 10045, 4, 3, 3],
};
pub static PKCS_ED25519 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&[1, 3, 101, 112]],
sign_alg :SignAlgo::EdDsa(&signature::ED25519),
digest_alg :&digest::SHA512,
oid_components : &[1, 3, 101, 112],
};
impl SignatureAlgorithm {
fn oid(&self) -> ObjectIdentifier {
ObjectIdentifier::from_slice(self.oid_components)
}
}