213 lines
6.9 KiB
Rust
213 lines
6.9 KiB
Rust
extern crate chrono;
|
|
extern crate digest;
|
|
extern crate num;
|
|
#[cfg(test)]
|
|
#[macro_use]
|
|
extern crate quickcheck;
|
|
extern crate sha1;
|
|
extern crate sha2;
|
|
#[macro_use]
|
|
extern crate simple_asn1;
|
|
extern crate simple_dsa;
|
|
extern crate simple_rsa;
|
|
|
|
mod algident;
|
|
mod atv;
|
|
mod error;
|
|
mod misc;
|
|
mod name;
|
|
mod publickey;
|
|
mod validity;
|
|
|
|
use digest::{Input,FixedOutput};
|
|
|
|
use algident::{AlgorithmIdentifier,HashAlgorithm,PublicKeyInfo,
|
|
decode_algorithm_ident};
|
|
use atv::InfoBlock;
|
|
use error::X509ParseError;
|
|
use misc::{X509Serial,X509Version,decode_signature};
|
|
use publickey::X509PublicKey;
|
|
use sha1::Sha1;
|
|
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
|
use simple_asn1::{ASN1Block,FromASN1,der_decode,from_der};
|
|
use simple_rsa::{SIGNING_HASH_SHA1, SIGNING_HASH_SHA224, SIGNING_HASH_SHA256,
|
|
SIGNING_HASH_SHA384, SIGNING_HASH_SHA512};
|
|
use validity::Validity;
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* The actual certificate data type and methods
|
|
*
|
|
******************************************************************************/
|
|
|
|
/// The type of an X.509 certificate.
|
|
#[derive(Debug)]
|
|
pub struct Certificate {
|
|
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<Certificate,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) => {
|
|
let (version, b1) = X509Version::from_asn1(b0)?;
|
|
let (serial, b2) = X509Serial::from_asn1(b1)?;
|
|
let (ident, b3) = AlgorithmIdentifier::from_asn1(b2)?;
|
|
let (issuer, b4) = InfoBlock::from_asn1(b3)?;
|
|
let (validity, b5) = Validity::from_asn1(b4)?;
|
|
let (subject, b6) = InfoBlock::from_asn1(b5)?;
|
|
let (subkey, b7) = X509PublicKey::from_asn1(b6)?;
|
|
Ok(Certificate {
|
|
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
|
|
*
|
|
******************************************************************************/
|
|
|
|
fn parse_x509(buffer: &[u8]) -> Result<Certificate,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(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)
|
|
}
|
|
}
|
|
_ =>
|
|
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<Certificate,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("test/rsa2048-1.der").is_ok());
|
|
assert!(can_parse("test/rsa2048-2.der").is_ok());
|
|
assert!(can_parse("test/rsa4096-1.der").is_ok());
|
|
assert!(can_parse("test/rsa4096-2.der").is_ok());
|
|
assert!(can_parse("test/rsa4096-3.der").is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn dsa_tests() {
|
|
assert!(can_parse("test/dsa2048-1.der").is_ok());
|
|
assert!(can_parse("test/dsa2048-2.der").is_ok());
|
|
assert!(can_parse("test/dsa3072-1.der").is_ok());
|
|
assert!(can_parse("test/dsa3072-2.der").is_ok());
|
|
}
|
|
//
|
|
// #[test]
|
|
// fn ecc_tests() {
|
|
// assert!(can_parse("test/ec384-1.der").is_ok());
|
|
// assert!(can_parse("test/ec384-2.der").is_ok());
|
|
// assert!(can_parse("test/ec384-3.der").is_ok());
|
|
// }
|
|
}
|