Preliminary x.509 support. This is some of the ugliest code I've ever written, but it works. Ish.
This commit is contained in:
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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user