Preliminary x.509 support. This is some of the ugliest code I've ever written, but it works. Ish.
This commit is contained in:
377
src/x509/algident.rs
Normal file
377
src/x509/algident.rs
Normal file
@@ -0,0 +1,377 @@
|
||||
use num::BigUint;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1};
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
pub enum HashAlgorithm { SHA1, SHA224, SHA256, SHA384, SHA512 }
|
||||
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
pub enum PublicKeyInfo { RSA, DSA, ECDSA }
|
||||
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct AlgorithmIdentifier {
|
||||
pub hash: HashAlgorithm,
|
||||
pub algo: PublicKeyInfo
|
||||
}
|
||||
|
||||
impl FromASN1 for AlgorithmIdentifier {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(AlgorithmIdentifier,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_algorithm_ident(&x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_algorithm_ident(x: &ASN1Block)
|
||||
-> Result<AlgorithmIdentifier,X509ParseError>
|
||||
{
|
||||
// AlgorithmIdentifier ::= SEQUENCE {
|
||||
// algorithm OBJECT IDENTIFIER,
|
||||
// parameters ANY DEFINED BY algorithm OPTIONAL }
|
||||
match x {
|
||||
&ASN1Block::Sequence(_, _, ref v) if v.len() >= 1 => {
|
||||
match v[0] {
|
||||
ASN1Block::ObjectIdentifier(_, _, ref oid) => {
|
||||
if oid == oid!(1,2,840,113549,1,1,5) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,1,11) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,1,12) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA384,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,1,13) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA512,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,1,14) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10040,4,3) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::DSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,1) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,3,1) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,3,2) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,3,3) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA384,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,3,4) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA512,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
// if oid == oid!(2,16,840,1,101,3,4,2,1) {
|
||||
// return Ok(AlgorithmIdentifier {
|
||||
// hash: HashAlgorithm::SHA256,
|
||||
// algo: PublicKeyInfo::RSAPSS
|
||||
// });
|
||||
// }
|
||||
// if oid == oid!(2,16,840,1,101,3,4,2,2) {
|
||||
// return Ok(AlgorithmIdentifier {
|
||||
// hash: HashAlgorithm::SHA384,
|
||||
// algo: PublicKeyInfo::RSAPSS
|
||||
// });
|
||||
// }
|
||||
// if oid == oid!(2,16,840,1,101,3,4,2,3) {
|
||||
// return Ok(AlgorithmIdentifier {
|
||||
// hash: HashAlgorithm::SHA512,
|
||||
// algo: PublicKeyInfo::RSAPSS
|
||||
// });
|
||||
// }
|
||||
// if oid == oid!(2,16,840,1,101,3,4,2,4) {
|
||||
// return Ok(AlgorithmIdentifier {
|
||||
// hash: HashAlgorithm::SHA224,
|
||||
// algo: PublicKeyInfo::RSAPSS
|
||||
// });
|
||||
// }
|
||||
if oid == oid!(2,16,840,1,101,3,4,3,1) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::DSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(2,16,840,1,101,3,4,3,2) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::DSA
|
||||
});
|
||||
}
|
||||
Err(X509ParseError::UnknownAlgorithm)
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::UnknownAlgorithm)
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedAlgoInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub enum SigAlgEncodeError {
|
||||
ASN1Error(ASN1EncodeErr),
|
||||
InvalidDSAValue, InvalidHash
|
||||
}
|
||||
|
||||
impl From<ASN1EncodeErr> for SigAlgEncodeError {
|
||||
fn from(e: ASN1EncodeErr) -> SigAlgEncodeError {
|
||||
SigAlgEncodeError::ASN1Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ToASN1 for AlgorithmIdentifier {
|
||||
type Error = SigAlgEncodeError;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,SigAlgEncodeError>
|
||||
{
|
||||
let block = encode_algorithm_ident(c, self)?;
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_algorithm_ident(c: ASN1Class, x: &AlgorithmIdentifier)
|
||||
-> Result<ASN1Block,SigAlgEncodeError>
|
||||
{
|
||||
match x.algo {
|
||||
PublicKeyInfo::RSA => {
|
||||
match x.hash {
|
||||
HashAlgorithm::SHA1 => {
|
||||
let o = oid!(1,2,840,113549,1,1,5);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA224 => {
|
||||
let o = oid!(1,2,840,113549,1,1,14);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA256 => {
|
||||
let o = oid!(1,2,840,113549,1,1,11);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA384 => {
|
||||
let o = oid!(1,2,840,113549,1,1,12);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA512 => {
|
||||
let o = oid!(1,2,840,113549,1,1,13);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
}
|
||||
}
|
||||
PublicKeyInfo::DSA => {
|
||||
match x.hash {
|
||||
HashAlgorithm::SHA1 => {
|
||||
let o = oid!(1,2,840,10040,4,3);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA224 => {
|
||||
let o = oid!(2,16,840,1,101,3,4,3,1);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA256 => {
|
||||
let o = oid!(2,16,840,1,101,3,4,3,2);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
_ =>
|
||||
Err(SigAlgEncodeError::InvalidHash),
|
||||
}
|
||||
}
|
||||
PublicKeyInfo::ECDSA=> {
|
||||
match x.hash {
|
||||
HashAlgorithm::SHA1 => {
|
||||
let o = oid!(1,2,840,10045,4,1);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA224 => {
|
||||
let o = oid!(1,2,840,10045,4,3,1);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA256 => {
|
||||
let o = oid!(1,2,840,10045,4,3,2);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA384 => {
|
||||
let o = oid!(1,2,840,10045,4,3,3);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA512 => {
|
||||
let o = oid!(1,2,840,10045,4,3,4);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rand::prelude::SliceRandom;
|
||||
use super::*;
|
||||
|
||||
const RSA1: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const RSA224: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const RSA256: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const RSA384: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA384,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const RSA512: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA512,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const DSA1: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::DSA
|
||||
};
|
||||
|
||||
const DSA224: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::DSA
|
||||
};
|
||||
|
||||
const DSA256: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::DSA
|
||||
};
|
||||
|
||||
const EC1: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
const EC224: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
const EC256: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
const EC384: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA384,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
const EC512: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA512,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
impl Arbitrary for AlgorithmIdentifier {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> AlgorithmIdentifier {
|
||||
let opts = [RSA1, RSA224, RSA256, RSA384, RSA512,
|
||||
DSA1, DSA224, DSA256,
|
||||
EC1, EC224, EC256, EC384, EC512];
|
||||
opts.choose(g).unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck!{
|
||||
fn algident_roundtrips(v: AlgorithmIdentifier) -> bool {
|
||||
match encode_algorithm_ident(ASN1Class::Universal, &v) {
|
||||
Err(_) =>
|
||||
false,
|
||||
Ok(block) => {
|
||||
match decode_algorithm_ident(&block) {
|
||||
Err(_) =>
|
||||
false,
|
||||
Ok(v2) =>
|
||||
v == v2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
368
src/x509/atv.rs
Normal file
368
src/x509/atv.rs
Normal file
@@ -0,0 +1,368 @@
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
||||
use std::ops::Index;
|
||||
use x509::error::X509ParseError;
|
||||
use x509::name::X520Name;
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct InfoBlock {
|
||||
fields: Vec<AttributeTypeValue>
|
||||
}
|
||||
|
||||
const EMPTY_STRING: &'static str = "";
|
||||
|
||||
impl Index<X520Name> for InfoBlock {
|
||||
type Output = str;
|
||||
|
||||
fn index(&self, name: X520Name) -> &str {
|
||||
for atv in self.fields.iter() {
|
||||
if name == atv.attrtype {
|
||||
return &atv.value;
|
||||
}
|
||||
}
|
||||
&EMPTY_STRING
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InfoBlock {
|
||||
fn eq(&self, other: &InfoBlock) -> bool {
|
||||
for x in self.fields.iter() {
|
||||
if !other.fields.contains(x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for x in other.fields.iter() {
|
||||
if !self.fields.contains(x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_info_block(x: &ASN1Block)
|
||||
-> Result<InfoBlock,X509ParseError>
|
||||
{
|
||||
// Name ::= CHOICE { -- only one possibility for now --
|
||||
// rdnSequence RDNSequence }
|
||||
//
|
||||
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
|
||||
//
|
||||
// RelativeDistinguishedName ::=
|
||||
// SET SIZE (1..MAX) OF AttributeTypeAndValue
|
||||
match x {
|
||||
&ASN1Block::Sequence(_, _, ref items) => {
|
||||
let mut atvs = Vec::new();
|
||||
|
||||
for set in items.iter() {
|
||||
match set {
|
||||
&ASN1Block::Set(_, _, ref setitems) => {
|
||||
for atv in setitems.iter() {
|
||||
let v = decode_attribute_type_value(atv)?;
|
||||
atvs.push(v);
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
return Err(X509ParseError::IllFormedInfoBlock)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(InfoBlock{ fields: atvs })
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedInfoBlock)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for InfoBlock {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(InfoBlock,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_info_block(&x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_info_block(c: ASN1Class, b: &InfoBlock)
|
||||
-> Result<ASN1Block,ASN1EncodeErr>
|
||||
{
|
||||
let mut encoded_fields = Vec::with_capacity(b.fields.len());
|
||||
|
||||
for fld in b.fields.iter() {
|
||||
let val = encode_attribute_type_value(c, fld)?;
|
||||
encoded_fields.push(val);
|
||||
}
|
||||
|
||||
let set = ASN1Block::Set(c, 0, encoded_fields);
|
||||
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![set]))
|
||||
}
|
||||
|
||||
impl ToASN1 for InfoBlock {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let block = encode_info_block(c, self)?;
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
struct AttributeTypeValue {
|
||||
attrtype: X520Name,
|
||||
value: String
|
||||
}
|
||||
|
||||
fn decode_attribute_type_value(x: &ASN1Block)
|
||||
-> Result<AttributeTypeValue,X509ParseError>
|
||||
{
|
||||
// AttributeTypeAndValue ::= SEQUENCE {
|
||||
// type AttributeType,
|
||||
// value AttributeValue }
|
||||
match x {
|
||||
&ASN1Block::Sequence(_, _, ref xs) => {
|
||||
let (name, rest) = X520Name::from_asn1(xs)?;
|
||||
match rest.first() {
|
||||
None => Err(X509ParseError::NotEnoughData),
|
||||
Some(ref x) => {
|
||||
let atvstr = get_atv_string(name, x)?;
|
||||
Ok(AttributeTypeValue{
|
||||
attrtype: name,
|
||||
value: atvstr
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedAttrTypeValue)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for AttributeTypeValue {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(AttributeTypeValue,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_attribute_type_value(&x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_attribute_type_value(c: ASN1Class, x: &AttributeTypeValue)
|
||||
-> Result<ASN1Block,ASN1EncodeErr>
|
||||
{
|
||||
let mut resvec = x.attrtype.to_asn1_class(c)?;
|
||||
let value = match x.attrtype {
|
||||
X520Name::CountryName =>
|
||||
ASN1Block::PrintableString(c,0,x.value.clone()),
|
||||
X520Name::SerialNumber =>
|
||||
ASN1Block::PrintableString(c,0,x.value.clone()),
|
||||
X520Name::DomainComponent =>
|
||||
ASN1Block::IA5String(c,0,x.value.clone()),
|
||||
X520Name::EmailAddress =>
|
||||
ASN1Block::IA5String(c,0,x.value.clone()),
|
||||
_ =>
|
||||
ASN1Block::UTF8String(c,0,x.value.clone())
|
||||
};
|
||||
resvec.push(value);
|
||||
Ok(ASN1Block::Sequence(c, 0, resvec))
|
||||
}
|
||||
|
||||
impl ToASN1 for AttributeTypeValue {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let block = encode_attribute_type_value(c, self)?;
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
fn get_atv_string(n: X520Name, x: &ASN1Block)
|
||||
-> Result<String,X509ParseError>
|
||||
{
|
||||
match n {
|
||||
X520Name::CountryName => {
|
||||
let res = get_printable_val(x)?;
|
||||
if res.len() != 2 {
|
||||
return Err(X509ParseError::IllegalStringValue);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
X520Name::SerialNumber => get_printable_val(x),
|
||||
X520Name::DomainComponent => get_ia5_val(x),
|
||||
X520Name::EmailAddress => get_ia5_val(x),
|
||||
_ => get_string_val(x),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_string_val(a: &ASN1Block) -> Result<String,X509ParseError>
|
||||
{
|
||||
match a {
|
||||
&ASN1Block::TeletexString(_,_,ref v) => Ok(v.clone()),
|
||||
&ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()),
|
||||
&ASN1Block::UniversalString(_,_,ref v) => Ok(v.clone()),
|
||||
&ASN1Block::UTF8String(_,_,ref v) => Ok(v.clone()),
|
||||
&ASN1Block::BMPString(_,_,ref v) => Ok(v.clone()),
|
||||
_ =>
|
||||
Err(X509ParseError::IllegalStringValue)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_printable_val(a: &ASN1Block) -> Result<String,X509ParseError>
|
||||
{
|
||||
match a {
|
||||
&ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()),
|
||||
_ =>
|
||||
Err(X509ParseError::IllegalStringValue)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ia5_val(a: &ASN1Block) -> Result<String,X509ParseError>
|
||||
{
|
||||
match a {
|
||||
&ASN1Block::IA5String(_,_,ref v) => Ok(v.clone()),
|
||||
_ =>
|
||||
Err(X509ParseError::IllegalStringValue)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rand::Rng;
|
||||
use rand::prelude::SliceRandom;
|
||||
use std::iter::FromIterator;
|
||||
use super::*;
|
||||
|
||||
impl Arbitrary for X520Name {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> X520Name {
|
||||
let names = vec![X520Name::Name,
|
||||
X520Name::Surname,
|
||||
X520Name::GivenName,
|
||||
X520Name::Initials,
|
||||
X520Name::GenerationQualifier,
|
||||
X520Name::CommonName,
|
||||
X520Name::LocalityName,
|
||||
X520Name::StateOrProvinceName,
|
||||
X520Name::OrganizationName,
|
||||
X520Name::OrganizationalUnit,
|
||||
X520Name::Title,
|
||||
X520Name::DNQualifier,
|
||||
X520Name::CountryName,
|
||||
X520Name::SerialNumber,
|
||||
X520Name::Pseudonym,
|
||||
X520Name::DomainComponent,
|
||||
X520Name::EmailAddress];
|
||||
names.choose(g).unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for AttributeTypeValue {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> AttributeTypeValue {
|
||||
let name = X520Name::arbitrary(g);
|
||||
let val = match name {
|
||||
X520Name::CountryName => {
|
||||
let mut base = gen_printable(g);
|
||||
base.push('U');
|
||||
base.push('S');
|
||||
base.truncate(2);
|
||||
base
|
||||
}
|
||||
X520Name::SerialNumber => gen_printable(g),
|
||||
X520Name::DomainComponent => gen_ia5(g),
|
||||
X520Name::EmailAddress => gen_ia5(g),
|
||||
_ => gen_utf8(g)
|
||||
};
|
||||
AttributeTypeValue{ attrtype: name, value: val }
|
||||
}
|
||||
}
|
||||
|
||||
const PRINTABLE_CHARS: &'static str =
|
||||
"ABCDEFGHIJKLMOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+,-./:=? ";
|
||||
|
||||
fn gen_printable<G: Gen>(g: &mut G) -> String {
|
||||
let count = g.gen_range(0, 384);
|
||||
let mut items = Vec::with_capacity(count);
|
||||
|
||||
for _ in 0..count {
|
||||
let v = PRINTABLE_CHARS.as_bytes().choose(g).unwrap();
|
||||
items.push(*v as char);
|
||||
}
|
||||
String::from_iter(items.iter())
|
||||
}
|
||||
|
||||
fn gen_ia5<G: Gen>(g: &mut G) -> String {
|
||||
let count = g.gen_range(0, 384);
|
||||
let mut items = Vec::with_capacity(count);
|
||||
|
||||
for _ in 0..count {
|
||||
items.push(g.gen::<u8>() as char);
|
||||
}
|
||||
String::from_iter(items.iter())
|
||||
}
|
||||
|
||||
fn gen_utf8<G: Gen>(g: &mut G) -> String {
|
||||
String::arbitrary(g)
|
||||
}
|
||||
|
||||
impl Arbitrary for InfoBlock {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> InfoBlock {
|
||||
let count = g.gen_range(0,12);
|
||||
let mut items = Vec::with_capacity(count);
|
||||
let mut names = Vec::with_capacity(count);
|
||||
|
||||
while items.len() < count {
|
||||
let atv = AttributeTypeValue::arbitrary(g);
|
||||
if !names.contains(&atv.attrtype) {
|
||||
names.push(atv.attrtype);
|
||||
items.push(atv);
|
||||
}
|
||||
}
|
||||
|
||||
InfoBlock{ fields: items }
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn attrtypeval_roundtrips(v: AttributeTypeValue) -> bool {
|
||||
match encode_attribute_type_value(ASN1Class::Universal, &v) {
|
||||
Err(_) => false,
|
||||
Ok(bstr) =>
|
||||
match decode_attribute_type_value(&bstr) {
|
||||
Err(_) => false,
|
||||
Ok(v2) => v == v2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn infoblock_roundtrips(v: InfoBlock) -> bool {
|
||||
match encode_info_block(ASN1Class::Universal, &v) {
|
||||
Err(_) => false,
|
||||
Ok(bstr) =>
|
||||
match decode_info_block(&bstr) {
|
||||
Err(_) => false,
|
||||
Ok(v2) => v == v2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/x509/error.rs
Normal file
53
src/x509/error.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use dsa::rfc6979::DSADecodeError;
|
||||
use ecdsa::ECDSADecodeErr;
|
||||
use rsa::RSAError;
|
||||
use simple_asn1::{ASN1DecodeErr,ASN1EncodeErr};
|
||||
|
||||
/// The error type for parsing and validating an X.509 certificate.
|
||||
#[derive(Debug)]
|
||||
pub enum X509ParseError {
|
||||
ASN1DecodeError(ASN1DecodeErr), ASN1EncodeError(ASN1EncodeErr),
|
||||
RSAError(RSAError), DSADecodeError(DSADecodeError), ECDSADecodeError(ECDSADecodeErr),
|
||||
RSASignatureWrong, DSASignatureWrong,
|
||||
NotEnoughData,
|
||||
IllFormedName, IllFormedAttrTypeValue, IllFormedInfoBlock,
|
||||
IllFormedValidity, IllFormedCertificateInfo, IllFormedSerialNumber,
|
||||
IllFormedAlgoInfo, IllFormedKey, IllFormedEverything,
|
||||
IllegalStringValue, NoSerialNumber, InvalidDSAInfo, ItemNotFound,
|
||||
UnknownAlgorithm, InvalidRSAKey, InvalidDSAKey, InvalidSignatureData,
|
||||
InvalidSignatureHash, InvalidECDSAKey, InvalidPointForm,
|
||||
UnknownEllipticCurve,
|
||||
CompressedPointUnsupported,
|
||||
KeyNotFound,
|
||||
SignatureNotFound, SignatureVerificationFailed
|
||||
}
|
||||
|
||||
impl From<ASN1DecodeErr> for X509ParseError {
|
||||
fn from(e: ASN1DecodeErr) -> X509ParseError {
|
||||
X509ParseError::ASN1DecodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ASN1EncodeErr> for X509ParseError {
|
||||
fn from(e: ASN1EncodeErr) -> X509ParseError {
|
||||
X509ParseError::ASN1EncodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RSAError> for X509ParseError {
|
||||
fn from(e: RSAError) -> X509ParseError {
|
||||
X509ParseError::RSAError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ECDSADecodeErr> for X509ParseError {
|
||||
fn from(e: ECDSADecodeErr) -> X509ParseError {
|
||||
X509ParseError::ECDSADecodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DSADecodeError> for X509ParseError {
|
||||
fn from(e: DSADecodeError) -> X509ParseError {
|
||||
X509ParseError::DSADecodeError(e)
|
||||
}
|
||||
}
|
||||
195
src/x509/misc.rs
Normal file
195
src/x509/misc.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
use num::{BigInt,BigUint,One,ToPrimitive,Zero};
|
||||
use num::bigint::ToBigInt;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
pub enum X509Version { V1, V2, V3 }
|
||||
|
||||
fn decode_version(bs: &[ASN1Block])
|
||||
-> Result<(X509Version,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match bs.split_first() {
|
||||
Some((&ASN1Block::Integer(_, _, ref v), rest)) => {
|
||||
match v.to_u8() {
|
||||
Some(0) => Ok((X509Version::V1, rest)),
|
||||
Some(1) => Ok((X509Version::V2, rest)),
|
||||
Some(2) => Ok((X509Version::V3, rest)),
|
||||
_ => Ok((X509Version::V1, &bs))
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::NotEnoughData)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for X509Version {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(X509Version,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
decode_version(v)
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_version(c: ASN1Class, v: X509Version) -> Vec<ASN1Block> {
|
||||
match v {
|
||||
X509Version::V1 => {
|
||||
let zero: BigInt = Zero::zero();
|
||||
let block = ASN1Block::Integer(c, 0, zero);
|
||||
vec![block]
|
||||
}
|
||||
X509Version::V2 => {
|
||||
let one: BigInt = One::one();
|
||||
let block = ASN1Block::Integer(c, 0, one);
|
||||
vec![block]
|
||||
}
|
||||
X509Version::V3 => {
|
||||
let two: BigInt = BigInt::from(2 as u64);
|
||||
let block = ASN1Block::Integer(c, 0, two);
|
||||
vec![block]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for X509Version {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
Ok(encode_version(c, *self))
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct X509Serial {
|
||||
num: BigUint
|
||||
}
|
||||
|
||||
fn decode_serial(x: &ASN1Block)
|
||||
-> Result<X509Serial,X509ParseError>
|
||||
{
|
||||
match x {
|
||||
&ASN1Block::Integer(_, _, ref v) => {
|
||||
match v.to_biguint() {
|
||||
None =>
|
||||
Err(X509ParseError::IllFormedSerialNumber),
|
||||
Some(n) =>
|
||||
Ok(X509Serial{ num: n })
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::NoSerialNumber)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for X509Serial {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(X509Serial,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NoSerialNumber),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_serial(x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SerialEncodeErr { ASN1Error(ASN1EncodeErr), InvalidSerialNumber }
|
||||
|
||||
impl From<ASN1EncodeErr> for SerialEncodeErr {
|
||||
fn from(e: ASN1EncodeErr) -> SerialEncodeErr {
|
||||
SerialEncodeErr::ASN1Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_serial(c: ASN1Class, serial: &X509Serial)
|
||||
-> Result<ASN1Block,SerialEncodeErr>
|
||||
{
|
||||
match serial.num.to_bigint() {
|
||||
None =>
|
||||
Err(SerialEncodeErr::InvalidSerialNumber),
|
||||
Some(n) =>
|
||||
Ok(ASN1Block::Integer(c, 0, n))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for X509Serial {
|
||||
type Error = SerialEncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,SerialEncodeErr>
|
||||
{
|
||||
let v = encode_serial(c, self)?;
|
||||
Ok(vec![v])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_signature(x: &ASN1Block)
|
||||
-> Result<Vec<u8>,X509ParseError>
|
||||
{
|
||||
match x {
|
||||
&ASN1Block::BitString(_, _, size, ref sig) if size % 8 == 0 => {
|
||||
Ok(sig.to_vec())
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::SignatureNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rand::Rng;
|
||||
use rand::distributions::Uniform;
|
||||
use super::*;
|
||||
|
||||
fn check_version_roundtrip(v: X509Version) {
|
||||
let blocks = encode_version(ASN1Class::Universal, v);
|
||||
match decode_version(&blocks) {
|
||||
Err(_) =>
|
||||
assert!(false),
|
||||
Ok((v2,_)) =>
|
||||
assert_eq!(v, v2)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn versions_roundtrip() {
|
||||
check_version_roundtrip(X509Version::V1);
|
||||
check_version_roundtrip(X509Version::V2);
|
||||
check_version_roundtrip(X509Version::V3);
|
||||
}
|
||||
|
||||
impl Arbitrary for X509Serial {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> X509Serial {
|
||||
let count = g.gen_range(0,16);
|
||||
let dist = Uniform::new_inclusive(0,255);
|
||||
let bits = g.sample_iter(&dist).take(count).collect();
|
||||
let val = BigUint::new(bits);
|
||||
X509Serial{ num: val }
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn serial_roundtrips(s: X509Serial) -> bool {
|
||||
match encode_serial(ASN1Class::Universal, &s) {
|
||||
Err(_) => false,
|
||||
Ok(block) =>
|
||||
match decode_serial(&block) {
|
||||
Err(_) => false,
|
||||
Ok(s2) => s == s2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
301
src/x509/mod.rs
Normal file
301
src/x509/mod.rs
Normal file
@@ -0,0 +1,301 @@
|
||||
mod algident;
|
||||
mod atv;
|
||||
mod error;
|
||||
mod misc;
|
||||
mod name;
|
||||
mod publickey;
|
||||
mod validity;
|
||||
|
||||
use dsa::{DSAPublic,DSAPublicKey};
|
||||
use ecdsa::{ECDSAPublic,ECCPublicKey};
|
||||
use rsa::{SIGNING_HASH_SHA1,SIGNING_HASH_SHA224,SIGNING_HASH_SHA256,SIGNING_HASH_SHA384,SIGNING_HASH_SHA512};
|
||||
use sha1::Sha1;
|
||||
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
||||
use simple_asn1::{ASN1Block,FromASN1,der_decode,from_der};
|
||||
use x509::validity::Validity;
|
||||
use x509::algident::{AlgorithmIdentifier,HashAlgorithm,PublicKeyInfo,
|
||||
decode_algorithm_ident};
|
||||
use x509::atv::InfoBlock;
|
||||
use x509::error::X509ParseError;
|
||||
use x509::misc::{X509Serial,X509Version,decode_signature};
|
||||
use x509::publickey::X509PublicKey;
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* The actual certificate data type and methods
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/// The type of an X.509 certificate.
|
||||
pub struct GenericCertificate {
|
||||
pub version: X509Version,
|
||||
pub serial: X509Serial,
|
||||
pub signature_alg: AlgorithmIdentifier,
|
||||
pub issuer: InfoBlock,
|
||||
pub subject: InfoBlock,
|
||||
pub validity: Validity,
|
||||
pub subject_key: X509PublicKey,
|
||||
pub extensions: Vec<()>
|
||||
}
|
||||
|
||||
fn decode_certificate(x: &ASN1Block)
|
||||
-> Result<GenericCertificate,X509ParseError>
|
||||
{
|
||||
//
|
||||
// TBSCertificate ::= SEQUENCE {
|
||||
// version [0] Version DEFAULT v1,
|
||||
// serialNumber CertificateSerialNumber,
|
||||
// signature AlgorithmIdentifier,
|
||||
// issuer Name,
|
||||
// validity Validity,
|
||||
// subject Name,
|
||||
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
||||
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
// -- If present, version MUST be v2 or v3
|
||||
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
// -- If present, version MUST be v2 or v3
|
||||
// extensions [3] Extensions OPTIONAL
|
||||
// -- If present, version MUST be v3 -- }
|
||||
//
|
||||
match x {
|
||||
&ASN1Block::Sequence(_, _, ref b0) => {
|
||||
println!("STEP1");
|
||||
let (version, b1) = X509Version::from_asn1(b0)?;
|
||||
println!("STEP2");
|
||||
let (serial, b2) = X509Serial::from_asn1(b1)?;
|
||||
println!("STEP3");
|
||||
let (ident, b3) = AlgorithmIdentifier::from_asn1(b2)?;
|
||||
println!("STEP4");
|
||||
let (issuer, b4) = InfoBlock::from_asn1(b3)?;
|
||||
println!("STEP5");
|
||||
let (validity, b5) = Validity::from_asn1(b4)?;
|
||||
println!("STEP6");
|
||||
let (subject, b6) = InfoBlock::from_asn1(b5)?;
|
||||
println!("STEP7");
|
||||
let (subkey, _ ) = X509PublicKey::from_asn1(b6)?;
|
||||
println!("STEP8");
|
||||
Ok(GenericCertificate {
|
||||
version: version,
|
||||
serial: serial,
|
||||
signature_alg: ident,
|
||||
issuer: issuer,
|
||||
subject: subject,
|
||||
validity: validity,
|
||||
subject_key: subkey,
|
||||
extensions: vec![]
|
||||
})
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedCertificateInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* X.509 parsing routines
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
pub fn parse_x509(buffer: &[u8]) -> Result<GenericCertificate,X509ParseError> {
|
||||
let blocks = from_der(&buffer[..])?;
|
||||
match blocks.first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some(&ASN1Block::Sequence(_, _, ref x)) => {
|
||||
let cert = decode_certificate(&x[0])?;
|
||||
let cert_block_start = x[0].offset();
|
||||
let cert_block_end = x[1].offset();
|
||||
let cert_block = &buffer[cert_block_start..cert_block_end];
|
||||
let alginfo = decode_algorithm_ident(&x[1])?;
|
||||
let sig = decode_signature(&x[2])?;
|
||||
check_signature(&alginfo, &cert.subject_key, cert_block, sig)?;
|
||||
Ok(cert)
|
||||
}
|
||||
Some(_) =>
|
||||
Err(X509ParseError::IllFormedEverything)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_signature(alg: &AlgorithmIdentifier,
|
||||
key: &X509PublicKey,
|
||||
block: &[u8],
|
||||
sig: Vec<u8>)
|
||||
-> Result<(),X509ParseError>
|
||||
{
|
||||
match (alg.algo, key) {
|
||||
(PublicKeyInfo::RSA, &X509PublicKey::RSA(ref key)) => {
|
||||
let sighash = match alg.hash {
|
||||
HashAlgorithm::SHA1 => &SIGNING_HASH_SHA1,
|
||||
HashAlgorithm::SHA224 => &SIGNING_HASH_SHA224,
|
||||
HashAlgorithm::SHA256 => &SIGNING_HASH_SHA256,
|
||||
HashAlgorithm::SHA384 => &SIGNING_HASH_SHA384,
|
||||
HashAlgorithm::SHA512 => &SIGNING_HASH_SHA512,
|
||||
};
|
||||
|
||||
if !key.verify(sighash, block, &sig) {
|
||||
return Err(X509ParseError::RSASignatureWrong);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
(PublicKeyInfo::DSA, &X509PublicKey::DSA(DSAPublic::DSAPublicL1024N160(ref key))) => {
|
||||
let dsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1
|
||||
if key.verify::<Sha1>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224
|
||||
if key.verify::<Sha224>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &dsa_sig) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::DSA, &X509PublicKey::DSA(DSAPublic::DSAPublicL2048N224(ref key))) => {
|
||||
let dsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1
|
||||
if key.verify::<Sha1>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224
|
||||
if key.verify::<Sha224>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &dsa_sig) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::DSA, &X509PublicKey::DSA(DSAPublic::DSAPublicL2048N256(ref key))) => {
|
||||
let dsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1
|
||||
if key.verify::<Sha1>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224
|
||||
if key.verify::<Sha224>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &dsa_sig) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::DSA, &X509PublicKey::DSA(DSAPublic::DSAPublicL3072N256(ref key))) => {
|
||||
let dsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1
|
||||
if key.verify::<Sha1>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224
|
||||
if key.verify::<Sha224>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &dsa_sig) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::ECCPublicP192(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<Sha1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<Sha224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<Sha384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<Sha512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::ECCPublicP224(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<Sha1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<Sha224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<Sha384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<Sha512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::ECCPublicP256(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<Sha1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<Sha224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<Sha384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<Sha512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::ECCPublicP384(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<Sha1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<Sha224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<Sha384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<Sha512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::ECCPublicP521(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<Sha1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<Sha224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<Sha256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<Sha384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<Sha512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Testing is for winners!
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use super::*;
|
||||
|
||||
fn can_parse(f: &str) -> Result<GenericCertificate,X509ParseError> {
|
||||
let mut fd = File::open(f).unwrap();
|
||||
let mut buffer = Vec::new();
|
||||
let _amt = fd.read_to_end(&mut buffer);
|
||||
parse_x509(&buffer)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rsa_tests() {
|
||||
assert!(can_parse("testdata/x509/rsa2048-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/rsa2048-2.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/rsa4096-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/rsa4096-2.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/rsa4096-3.der").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dsa_tests() {
|
||||
assert!(can_parse("testdata/x509/dsa2048-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/dsa2048-2.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/dsa3072-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/dsa3072-2.der").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecc_tests() {
|
||||
assert!(can_parse("testdata/x509/ec384-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/ec384-2.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/ec384-3.der").is_ok());
|
||||
}
|
||||
}
|
||||
136
src/x509/name.rs
Normal file
136
src/x509/name.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use num::BigUint;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1};
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
#[derive(Copy,Clone,Debug,Eq,Hash,PartialEq)]
|
||||
pub enum X520Name {
|
||||
Name, Surname, GivenName, Initials, GenerationQualifier, CommonName,
|
||||
LocalityName, StateOrProvinceName, OrganizationName, OrganizationalUnit,
|
||||
Title, DNQualifier, CountryName, SerialNumber, Pseudonym, DomainComponent,
|
||||
EmailAddress
|
||||
}
|
||||
|
||||
impl FromASN1 for X520Name {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(X520Name,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x,rest)) => {
|
||||
let name = decode_name(&x)?;
|
||||
Ok((name,rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_name(val: &ASN1Block)
|
||||
-> Result<X520Name,X509ParseError>
|
||||
{
|
||||
match val {
|
||||
&ASN1Block::ObjectIdentifier(_, _, ref oid) => {
|
||||
if oid == oid!(2,5,4,41) {return Ok(X520Name::Name) }
|
||||
if oid == oid!(2,5,4,4) {return Ok(X520Name::Surname) }
|
||||
if oid == oid!(2,5,4,42) {return Ok(X520Name::GivenName) }
|
||||
if oid == oid!(2,5,4,43) {return Ok(X520Name::Initials) }
|
||||
if oid == oid!(2,5,4,44) {return Ok(X520Name::GenerationQualifier)}
|
||||
if oid == oid!(2,5,4,3) {return Ok(X520Name::CommonName) }
|
||||
if oid == oid!(2,5,4,7) {return Ok(X520Name::LocalityName) }
|
||||
if oid == oid!(2,5,4,8) {return Ok(X520Name::StateOrProvinceName)}
|
||||
if oid == oid!(2,5,4,10) {return Ok(X520Name::OrganizationName) }
|
||||
if oid == oid!(2,5,4,11) {return Ok(X520Name::OrganizationalUnit) }
|
||||
if oid == oid!(2,5,4,12) {return Ok(X520Name::Title) }
|
||||
if oid == oid!(2,5,4,46) {return Ok(X520Name::DNQualifier) }
|
||||
if oid == oid!(2,5,4,6) {return Ok(X520Name::CountryName) }
|
||||
if oid == oid!(2,5,4,5) {return Ok(X520Name::SerialNumber) }
|
||||
if oid == oid!(2,5,4,65) {return Ok(X520Name::Pseudonym) }
|
||||
if oid == oid!(0,9,2342,19200300,100,1,25) {
|
||||
return Ok(X520Name::DomainComponent);
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,9,1) {
|
||||
return Ok(X520Name::EmailAddress);
|
||||
}
|
||||
Err(X509ParseError::IllFormedName)
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedName)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for X520Name {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let block = encode_name(c, *self);
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_name(class: ASN1Class, name: X520Name)
|
||||
-> ASN1Block
|
||||
{
|
||||
let oid = match name {
|
||||
X520Name::Name => oid!(2,5,4,41),
|
||||
X520Name::Surname => oid!(2,5,4,4),
|
||||
X520Name::GivenName => oid!(2,5,4,42),
|
||||
X520Name::Initials => oid!(2,5,4,43),
|
||||
X520Name::GenerationQualifier => oid!(2,5,4,44),
|
||||
X520Name::CommonName => oid!(2,5,4,3),
|
||||
X520Name::LocalityName => oid!(2,5,4,7),
|
||||
X520Name::StateOrProvinceName => oid!(2,5,4,8),
|
||||
X520Name::OrganizationName => oid!(2,5,4,10),
|
||||
X520Name::OrganizationalUnit => oid!(2,5,4,11),
|
||||
X520Name::Title => oid!(2,5,4,12),
|
||||
X520Name::DNQualifier => oid!(2,5,4,46),
|
||||
X520Name::CountryName => oid!(2,5,4,6),
|
||||
X520Name::SerialNumber => oid!(2,5,4,5),
|
||||
X520Name::Pseudonym => oid!(2,5,4,65),
|
||||
X520Name::DomainComponent => oid!(0,9,2342,19200300,100,1,25),
|
||||
X520Name::EmailAddress => oid!(1,2,840,113549,1,9,1)
|
||||
};
|
||||
|
||||
ASN1Block::ObjectIdentifier(class, 0, oid)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn encdec_test(n: X520Name) {
|
||||
let block = encode_name(ASN1Class::Universal, n);
|
||||
let vec = vec![block];
|
||||
match X520Name::from_asn1(&vec) {
|
||||
Err(_) =>
|
||||
assert!(false),
|
||||
Ok((m, _)) =>
|
||||
assert_eq!(n,m)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn name_encoding_roundtrips() {
|
||||
encdec_test(X520Name::Name);
|
||||
encdec_test(X520Name::Surname);
|
||||
encdec_test(X520Name::GivenName);
|
||||
encdec_test(X520Name::Initials);
|
||||
encdec_test(X520Name::GenerationQualifier);
|
||||
encdec_test(X520Name::CommonName);
|
||||
encdec_test(X520Name::LocalityName);
|
||||
encdec_test(X520Name::StateOrProvinceName);
|
||||
encdec_test(X520Name::OrganizationName);
|
||||
encdec_test(X520Name::OrganizationalUnit);
|
||||
encdec_test(X520Name::Title);
|
||||
encdec_test(X520Name::DNQualifier);
|
||||
encdec_test(X520Name::CountryName);
|
||||
encdec_test(X520Name::SerialNumber);
|
||||
encdec_test(X520Name::Pseudonym);
|
||||
encdec_test(X520Name::DomainComponent);
|
||||
encdec_test(X520Name::EmailAddress);
|
||||
}
|
||||
}
|
||||
|
||||
328
src/x509/publickey.rs
Normal file
328
src/x509/publickey.rs
Normal file
@@ -0,0 +1,328 @@
|
||||
use cryptonum::unsigned::{U3072,U2048,U1024,U256,U192};
|
||||
use dsa::{DSAPublic,DSAPublicKey,DSAPubKey,DSAParameters};
|
||||
use dsa::{L3072N256,L2048N256,L2048N224,L1024N160};
|
||||
use ecdsa::{ECDSAEncodeErr,ECDSAPublic,ECCPubKey};
|
||||
use ecdsa::curve::{P192,P224,P256,P384,P521};
|
||||
use num::BigUint;
|
||||
use rsa::RSAPublic;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1,
|
||||
der_decode,der_encode,from_der};
|
||||
use utils::TranslateNums;
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
pub enum X509PublicKey {
|
||||
DSA(DSAPublic),
|
||||
RSA(RSAPublic),
|
||||
ECDSA(ECDSAPublic)
|
||||
}
|
||||
|
||||
impl From<X509PublicKey> for Option<DSAPublic> {
|
||||
fn from(x: X509PublicKey) -> Option<DSAPublic> {
|
||||
match x {
|
||||
X509PublicKey::DSA(x) => Some(x),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<X509PublicKey> for Option<RSAPublic> {
|
||||
fn from(x: X509PublicKey) -> Option<RSAPublic> {
|
||||
match x {
|
||||
X509PublicKey::RSA(x) => Some(x),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<X509PublicKey> for Option<ECDSAPublic> {
|
||||
fn from(x: X509PublicKey) -> Option<ECDSAPublic> {
|
||||
match x {
|
||||
X509PublicKey::ECDSA(x) => Some(x),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum X509EncodeErr {
|
||||
ASN1EncodeErr(ASN1EncodeErr),
|
||||
ECDSAEncodeErr(ECDSAEncodeErr)
|
||||
}
|
||||
|
||||
impl From<ASN1EncodeErr> for X509EncodeErr {
|
||||
fn from(x: ASN1EncodeErr) -> X509EncodeErr {
|
||||
X509EncodeErr::ASN1EncodeErr(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ECDSAEncodeErr> for X509EncodeErr {
|
||||
fn from(x: ECDSAEncodeErr) -> X509EncodeErr {
|
||||
X509EncodeErr::ECDSAEncodeErr(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for X509PublicKey {
|
||||
type Error = X509EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class) -> Result<Vec<ASN1Block>,X509EncodeErr> {
|
||||
let block = match self {
|
||||
X509PublicKey::RSA(x) => encode_rsa_key(c, x)?,
|
||||
X509PublicKey::DSA(x) => encode_dsa_key(c, x)?,
|
||||
X509PublicKey::ECDSA(x) => encode_ecdsa_key(c, x)?,
|
||||
};
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for X509PublicKey {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block]) -> Result<(X509PublicKey, &[ASN1Block]), Self::Error>
|
||||
{
|
||||
let (block, rest) = v.split_first().ok_or(X509ParseError::NotEnoughData)?;
|
||||
|
||||
// SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
// algorithm AlgorithmIdentifier,
|
||||
// subjectPublicKey BIT STRING }
|
||||
if let &ASN1Block::Sequence(_, _, ref info) = block {
|
||||
let (id, malginfo) = strip_algident(&info[0])?;
|
||||
|
||||
if id == oid!(1,2,840,113549,1,1,1) {
|
||||
let key = decode_rsa_key(&info[1])?;
|
||||
return Ok((X509PublicKey::RSA(key), rest));
|
||||
}
|
||||
|
||||
if id == oid!(1,2,840,10040,4,1) {
|
||||
if let Some(alginfo) = malginfo {
|
||||
let key = decode_dsa_key(alginfo, &info[1])?;
|
||||
return Ok((X509PublicKey::DSA(key), rest));
|
||||
}
|
||||
}
|
||||
|
||||
if id == oid!(1,2,840,10045,2,1) {
|
||||
if let Some(alginfo) = malginfo {
|
||||
let key = decode_ecdsa_key(alginfo, &info[1..])?;
|
||||
return Ok((X509PublicKey::ECDSA(key), rest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(X509ParseError::IllFormedKey)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// RSA Public Key encoding / decoding
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fn encode_rsa_key(c: ASN1Class, x: &RSAPublic) -> Result<ASN1Block,ASN1EncodeErr>
|
||||
{
|
||||
let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,113549,1,1,1));
|
||||
let bstr = der_encode(x)?;
|
||||
let objkey = ASN1Block::BitString(c, 0, bstr.len() * 8, bstr);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![objoid, objkey]))
|
||||
}
|
||||
|
||||
fn decode_rsa_key(x: &ASN1Block) -> Result<RSAPublic,X509ParseError>
|
||||
{
|
||||
if let &ASN1Block::BitString(_, _, _, ref bstr) = x {
|
||||
der_decode(bstr).map_err(|x| X509ParseError::RSAError(x))
|
||||
} else {
|
||||
Err(X509ParseError::NotEnoughData)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// DSA Public Key encoding / decoding
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fn encode_dsa_key(c: ASN1Class, x: &DSAPublic) -> Result<ASN1Block,ASN1EncodeErr>
|
||||
{
|
||||
let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10040,4,1));
|
||||
let (mut objparams, bstr) = match x {
|
||||
DSAPublic::DSAPublicL1024N160(x) => (x.params.to_asn1_class(c)?, der_encode(x)?),
|
||||
DSAPublic::DSAPublicL2048N224(x) => (x.params.to_asn1_class(c)?, der_encode(x)?),
|
||||
DSAPublic::DSAPublicL2048N256(x) => (x.params.to_asn1_class(c)?, der_encode(x)?),
|
||||
DSAPublic::DSAPublicL3072N256(x) => (x.params.to_asn1_class(c)?, der_encode(x)?)
|
||||
};
|
||||
objparams.insert(0, objoid);
|
||||
let headinfo = ASN1Block::Sequence(c, 0, objparams);
|
||||
let objkey = ASN1Block::BitString(c, 0, bstr.len() * 8, bstr);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![headinfo, objkey]))
|
||||
}
|
||||
|
||||
fn decode_dsa_key(info: ASN1Block, key: &ASN1Block) -> Result<DSAPublic,X509ParseError>
|
||||
{
|
||||
if let ASN1Block::Sequence(_, _, pqg) = info {
|
||||
if pqg.len() != 3 { return Err(X509ParseError::InvalidDSAInfo); }
|
||||
|
||||
let puint = decode_biguint(&pqg[0])?;
|
||||
let guint = decode_biguint(&pqg[1])?;
|
||||
let quint = decode_biguint(&pqg[2])?;
|
||||
|
||||
if puint.bits() > 2048 {
|
||||
let p = U3072::from_num(&puint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let q = U3072::from_num(&quint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let g = U256::from_num(&guint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let params = L3072N256::new(p, q, g);
|
||||
|
||||
if let ASN1Block::BitString(_, _, _, ybstr) = key {
|
||||
let blocks = from_der(ybstr)?;
|
||||
let (iblk,_) = blocks.split_first().ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
if let ASN1Block::Integer(_,_,ynum) = iblk {
|
||||
let y = U3072::from_num(ynum).ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
let key = DSAPubKey::<L3072N256,U3072>::new(params, y);
|
||||
let reskey = DSAPublic::DSAPublicL3072N256(key);
|
||||
return Ok(reskey);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(X509ParseError::InvalidDSAKey)
|
||||
}
|
||||
|
||||
if puint.bits() > 1024 {
|
||||
if guint.bits() > 224 {
|
||||
let p = U2048::from_num(&puint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let q = U2048::from_num(&quint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let g = U256::from_num(&guint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let params = L2048N256::new(p, q, g);
|
||||
|
||||
if let ASN1Block::BitString(_, _, _, ybstr) = key {
|
||||
let blocks = from_der(ybstr)?;
|
||||
let (iblk,_) = blocks.split_first().ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
if let ASN1Block::Integer(_,_,ynum) = iblk {
|
||||
let y = U2048::from_num(ynum).ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
let key = DSAPubKey::<L2048N256,U2048>::new(params, y);
|
||||
let reskey = DSAPublic::DSAPublicL2048N256(key);
|
||||
return Ok(reskey);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(X509ParseError::InvalidDSAKey)
|
||||
} else {
|
||||
let p = U2048::from_num(&puint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let q = U2048::from_num(&quint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let g = U256::from_num(&guint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let params = L2048N224::new(p, q, g);
|
||||
|
||||
if let ASN1Block::BitString(_, _, _, ybstr) = key {
|
||||
let blocks = from_der(ybstr)?;
|
||||
let (iblk,_) = blocks.split_first().ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
if let ASN1Block::Integer(_,_,ynum) = iblk {
|
||||
let y = U2048::from_num(ynum).ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
let key = DSAPubKey::<L2048N224,U2048>::new(params, y);
|
||||
let reskey = DSAPublic::DSAPublicL2048N224(key);
|
||||
return Ok(reskey);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(X509ParseError::InvalidDSAKey)
|
||||
}
|
||||
}
|
||||
|
||||
let p = U1024::from_num(&puint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let q = U1024::from_num(&quint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let g = U192::from_num(&guint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let params = L1024N160::new(p, q, g);
|
||||
|
||||
if let ASN1Block::BitString(_, _, _, ybstr) = key {
|
||||
let blocks = from_der(ybstr)?;
|
||||
let (iblk,_) = blocks.split_first().ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
if let ASN1Block::Integer(_,_,ynum) = iblk {
|
||||
let y = U1024::from_num(ynum).ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
let key = DSAPubKey::<L1024N160,U1024>::new(params, y);
|
||||
let reskey = DSAPublic::DSAPublicL1024N160(key);
|
||||
return Ok(reskey);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(X509ParseError::InvalidDSAKey)
|
||||
}
|
||||
|
||||
Err(X509ParseError::InvalidDSAInfo)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// ECDSA Public Key encoding
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fn encode_ecdsa_key(c: ASN1Class, x: &ECDSAPublic) -> Result<ASN1Block,ECDSAEncodeErr>
|
||||
{
|
||||
let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10045,2,1));
|
||||
let (base_curve_oid, mut keyvec) = match x {
|
||||
ECDSAPublic::ECCPublicP192(k) => (oid!(1,2,840,10045,3,1,1), k.to_asn1_class(c)?),
|
||||
ECDSAPublic::ECCPublicP224(k) => (oid!(1,3,132,0,33), k.to_asn1_class(c)?),
|
||||
ECDSAPublic::ECCPublicP256(k) => (oid!(1,2,840,10045,3,1,7), k.to_asn1_class(c)?),
|
||||
ECDSAPublic::ECCPublicP384(k) => (oid!(1,3,132,0,34), k.to_asn1_class(c)?),
|
||||
ECDSAPublic::ECCPublicP521(k) => (oid!(1,3,132,0,35), k.to_asn1_class(c)?),
|
||||
};
|
||||
let curve_oid = ASN1Block::ObjectIdentifier(c, 0, base_curve_oid);
|
||||
let header = ASN1Block::Sequence(c, 0, vec![objoid, curve_oid]);
|
||||
keyvec.insert(0, header);
|
||||
Ok(ASN1Block::Sequence(c, 0, keyvec))
|
||||
}
|
||||
|
||||
fn decode_ecdsa_key(info: ASN1Block, keybls: &[ASN1Block]) -> Result<ECDSAPublic,X509ParseError>
|
||||
{
|
||||
if let ASN1Block::ObjectIdentifier(_, _, oid) = info {
|
||||
if oid == oid!(1,2,840,10045,3,1,1) {
|
||||
let (res, _) = ECCPubKey::<P192>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::ECCPublicP192(res));
|
||||
}
|
||||
|
||||
if oid == oid!(1,3,132,0,33) {
|
||||
let (res, _) = ECCPubKey::<P224>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::ECCPublicP224(res));
|
||||
}
|
||||
|
||||
if oid == oid!(1,2,840,10045,3,1,7) {
|
||||
let (res, _) = ECCPubKey::<P256>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::ECCPublicP256(res));
|
||||
}
|
||||
|
||||
if oid == oid!(1,3,132,0,34) {
|
||||
let (res, _) = ECCPubKey::<P384>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::ECCPublicP384(res));
|
||||
}
|
||||
|
||||
if oid == oid!(1,3,132,0,35) {
|
||||
let (res, _) = ECCPubKey::<P521>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::ECCPublicP521(res));
|
||||
}
|
||||
}
|
||||
|
||||
Err(X509ParseError::UnknownEllipticCurve)
|
||||
}
|
||||
|
||||
fn strip_algident(block: &ASN1Block)
|
||||
-> Result<(OID, Option<ASN1Block>),X509ParseError>
|
||||
{
|
||||
match block {
|
||||
&ASN1Block::ObjectIdentifier(_, _, ref oid) => {
|
||||
Ok((oid.clone(), None))
|
||||
}
|
||||
&ASN1Block::Sequence(_, _, ref items) => {
|
||||
let (oid, _) = strip_algident(&items[0])?;
|
||||
Ok((oid, Some(items[1].clone())))
|
||||
}
|
||||
_ => Err(X509ParseError::IllFormedAlgoInfo)
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_biguint(b: &ASN1Block) -> Result<BigUint,X509ParseError> {
|
||||
match b {
|
||||
&ASN1Block::Integer(_, _, ref v) => {
|
||||
match v.to_biguint() {
|
||||
Some(sn) => Ok(sn),
|
||||
_ => Err(X509ParseError::InvalidDSAInfo)
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidDSAInfo)
|
||||
}
|
||||
}
|
||||
118
src/x509/validity.rs
Normal file
118
src/x509/validity.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
use chrono::{DateTime,Utc};
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct Validity {
|
||||
not_before: DateTime<Utc>,
|
||||
not_after: DateTime<Utc>
|
||||
}
|
||||
|
||||
fn decode_validity_data(bs: &ASN1Block) -> Result<Validity,X509ParseError> {
|
||||
// Validity ::= SEQUENCE {
|
||||
// notBefore Time,
|
||||
// notAfter Time }
|
||||
match bs {
|
||||
&ASN1Block::Sequence(_, _, ref valxs) => {
|
||||
if valxs.len() != 2 {
|
||||
return Err(X509ParseError::IllFormedValidity);
|
||||
}
|
||||
let nb = get_time(&valxs[0])?;
|
||||
let na = get_time(&valxs[1])?;
|
||||
Ok(Validity{ not_before: nb, not_after: na })
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedValidity)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for Validity {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(Validity,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_validity_data(&x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_validity_data(c: ASN1Class, v: &Validity) -> ASN1Block {
|
||||
let mut vs = Vec::with_capacity(2);
|
||||
vs.push(ASN1Block::GeneralizedTime(c, 0, v.not_before));
|
||||
vs.push(ASN1Block::GeneralizedTime(c, 0, v.not_after));
|
||||
ASN1Block::Sequence(c, 0, vs)
|
||||
}
|
||||
|
||||
impl ToASN1 for Validity {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let block = encode_validity_data(c, self);
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
fn get_time(b: &ASN1Block) -> Result<DateTime<Utc>, X509ParseError> {
|
||||
match b {
|
||||
&ASN1Block::UTCTime(_, _, v) => Ok(v.clone()),
|
||||
&ASN1Block::GeneralizedTime(_, _, v) => Ok(v.clone()),
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedValidity)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use chrono::TimeZone;
|
||||
use chrono::offset::LocalResult;
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rand::Rng;
|
||||
use super::*;
|
||||
|
||||
fn arbitrary_date<G: Gen>(g: &mut G) -> DateTime<Utc> {
|
||||
loop {
|
||||
let y = g.gen_range(1900,3000);
|
||||
let mo = g.gen_range(0,12);
|
||||
let d = g.gen_range(0,31);
|
||||
let h = g.gen_range(0,24);
|
||||
let mi = g.gen_range(0,60);
|
||||
let s = g.gen_range(0,60);
|
||||
match Utc.ymd_opt(y,mo,d).and_hms_opt(h,mi,s) {
|
||||
LocalResult::None =>
|
||||
continue,
|
||||
LocalResult::Single(x) =>
|
||||
return x,
|
||||
LocalResult::Ambiguous(x,_) =>
|
||||
return x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Validity {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> Validity {
|
||||
Validity {
|
||||
not_before: arbitrary_date(g),
|
||||
not_after: arbitrary_date(g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn validity_roundtrips(v: Validity) -> bool {
|
||||
let bstr = encode_validity_data(ASN1Class::Universal, &v);
|
||||
match decode_validity_data(&bstr) {
|
||||
Err(_) => false,
|
||||
Ok(v2) => v == v2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user