From 8d8351e833518ceb7f0899dc77e32874ab3eba0f Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 18 Feb 2019 10:54:01 -0800 Subject: [PATCH] Preliminary x.509 support. This is some of the ugliest code I've ever written, but it works. Ish. --- Cargo.toml | 1 + src/dsa/mod.rs | 12 +- src/dsa/params.rs | 27 ++- src/dsa/private.rs | 16 +- src/dsa/public.rs | 29 ++- src/dsa/rfc6979.rs | 25 +-- src/dsa/tests.rs | 57 +++--- src/ecdsa/mod.rs | 9 +- src/ecdsa/public.rs | 116 +++++++++-- src/lib.rs | 5 + src/rsa/mod.rs | 1 + src/rsa/public.rs | 48 +++-- src/utils.rs | 28 ++- src/x509/algident.rs | 377 ++++++++++++++++++++++++++++++++++++ src/x509/atv.rs | 368 +++++++++++++++++++++++++++++++++++ src/x509/error.rs | 53 +++++ src/x509/misc.rs | 195 +++++++++++++++++++ src/x509/mod.rs | 301 ++++++++++++++++++++++++++++ src/x509/name.rs | 136 +++++++++++++ src/x509/publickey.rs | 328 +++++++++++++++++++++++++++++++ src/x509/validity.rs | 118 +++++++++++ testdata/x509/dsa2048-1.der | Bin 0 -> 1228 bytes testdata/x509/dsa2048-2.der | Bin 0 -> 1232 bytes testdata/x509/dsa3072-1.der | Bin 0 -> 1613 bytes testdata/x509/dsa3072-2.der | Bin 0 -> 1613 bytes testdata/x509/ec384-1.der | Bin 0 -> 544 bytes testdata/x509/ec384-2.der | Bin 0 -> 544 bytes testdata/x509/ec384-3.der | Bin 0 -> 515 bytes testdata/x509/rsa2048-1.der | Bin 0 -> 890 bytes testdata/x509/rsa2048-2.der | Bin 0 -> 872 bytes testdata/x509/rsa4096-1.der | Bin 0 -> 1384 bytes testdata/x509/rsa4096-2.der | Bin 0 -> 1392 bytes testdata/x509/rsa4096-3.der | Bin 0 -> 1392 bytes 33 files changed, 2147 insertions(+), 103 deletions(-) create mode 100644 src/x509/algident.rs create mode 100644 src/x509/atv.rs create mode 100644 src/x509/error.rs create mode 100644 src/x509/misc.rs create mode 100644 src/x509/mod.rs create mode 100644 src/x509/name.rs create mode 100644 src/x509/publickey.rs create mode 100644 src/x509/validity.rs create mode 100644 testdata/x509/dsa2048-1.der create mode 100644 testdata/x509/dsa2048-2.der create mode 100644 testdata/x509/dsa3072-1.der create mode 100644 testdata/x509/dsa3072-2.der create mode 100644 testdata/x509/ec384-1.der create mode 100644 testdata/x509/ec384-2.der create mode 100644 testdata/x509/ec384-3.der create mode 100644 testdata/x509/rsa2048-1.der create mode 100644 testdata/x509/rsa2048-2.der create mode 100644 testdata/x509/rsa4096-1.der create mode 100644 testdata/x509/rsa4096-2.der create mode 100644 testdata/x509/rsa4096-3.der diff --git a/Cargo.toml b/Cargo.toml index f1ecfca..83422c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/acw/simple_crypto" [dependencies] byteorder = "^1.2.7" +chrono = "^0.4.6" cryptonum = { path = "../cryptonum" } digest = "^0.8.0" hmac = "^0.7.0" diff --git a/src/dsa/mod.rs b/src/dsa/mod.rs index 03ecf52..3b00a38 100644 --- a/src/dsa/mod.rs +++ b/src/dsa/mod.rs @@ -20,17 +20,19 @@ pub struct DSAKeyPair pub public: DSAPubKey } -pub trait DSAKeyGeneration

+pub trait DSAKeyGeneration { - fn generate(params: &P, rng: &mut G) -> Self; + type Params; + + fn generate(params: &Self::Params, rng: &mut G) -> Self; } macro_rules! generate_dsa_pair { ($ptype: ident, $ltype: ident, $ntype: ident, $nbig: ident) => { - impl DSAKeyGeneration<$ptype> for DSAKeyPair<$ptype,$ltype,$ntype> - where - DSAPrivKey<$ptype,$ntype>: DSAPrivateKey<$ptype,$ltype,$ntype>, + impl DSAKeyGeneration for DSAKeyPair<$ptype,$ltype,$ntype> { + type Params = $ptype; + fn generate(params: &$ptype, rng: &mut G) -> Self { // 1. N = len(q); L = len(p); diff --git a/src/dsa/params.rs b/src/dsa/params.rs index f808b7c..c33ece1 100644 --- a/src/dsa/params.rs +++ b/src/dsa/params.rs @@ -2,11 +2,16 @@ use cryptonum::unsigned::{CryptoNum,Decoder,Encoder,ModExp,PrimeGen}; use cryptonum::unsigned::{U192,U256,U1024,U2048,U3072}; use digest::Digest; use sha2::Sha256; +use simple_asn1::{ToASN1,ASN1Block,ASN1Class,ASN1EncodeErr}; use rand::Rng; +use utils::TranslateNums; -pub trait DSAParameters +pub trait DSAParameters : ToASN1 { - fn new(p: L, g: L, q: N) -> Self; + type L; + type N; + + fn new(p: Self::L, g: Self::L, q: Self::N) -> Self; fn generate(rng: &mut G) -> Self; fn n_size() -> usize; fn l_size() -> usize; @@ -25,8 +30,24 @@ macro_rules! generate_parameters { pub q: $ntype } - impl DSAParameters<$ltype,$ntype> for $name + impl ToASN1 for $name { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,ASN1EncodeErr> + { + let p = ASN1Block::Integer(c, 0, self.p.to_num()); + let q = ASN1Block::Integer(c, 0, self.q.to_num()); + let g = ASN1Block::Integer(c, 0, self.g.to_num()); + Ok(vec![ASN1Block::Sequence(c, 0, vec![p, q, g])]) + } + } + + impl DSAParameters for $name { + type L = $ltype; + type N = $ntype; + fn new(p: $ltype, g: $ltype, q: $ntype) -> $name { $name{ p: p, g: g, q: q } diff --git a/src/dsa/private.rs b/src/dsa/private.rs index 5e87deb..6d27f3d 100644 --- a/src/dsa/private.rs +++ b/src/dsa/private.rs @@ -5,13 +5,17 @@ use dsa::params::*; use dsa::rfc6979::*; use hmac::{Hmac,Mac}; -pub trait DSAPrivateKey { +pub trait DSAPrivateKey { + type Params; + type L; + type N; + /// Generate a new private key using the given DSA parameters and private /// key value. - fn new(params: Params, x: N) -> Self; + fn new(params: Self::Params, x: Self::N) -> Self; /// Generate a DSA signature for the given message, using the appropriate /// hash included in the type invocation. - fn sign(&self, m: &[u8]) -> DSASignature + fn sign(&self, m: &[u8]) -> DSASignature where Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset, Hmac: Mac; @@ -32,8 +36,12 @@ pub enum DSAPrivate { macro_rules! privkey_impls { ($ptype: ident, $ltype: ident, $ntype: ident, $big: ident, $bigger: ident, $biggest: ident) => { - impl DSAPrivateKey<$ptype,$ltype,$ntype> for DSAPrivKey<$ptype,$ntype> + impl DSAPrivateKey for DSAPrivKey<$ptype,$ntype> { + type Params = $ptype; + type L = $ltype; + type N = $ntype; + fn new(params: $ptype, x: $ntype) -> DSAPrivKey<$ptype,$ntype> { DSAPrivKey{ params, x } diff --git a/src/dsa/public.rs b/src/dsa/public.rs index 415c5d6..6379cf0 100644 --- a/src/dsa/public.rs +++ b/src/dsa/public.rs @@ -3,17 +3,20 @@ use cryptonum::signed::ModInv; use digest::Digest; use dsa::params::*; use dsa::rfc6979::DSASignature; -use num::BigInt; -use simple_asn1::{ToASN1,ASN1Block,ASN1Class,ASN1EncodeErr}; +use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,ToASN1}; use std::cmp::min; use utils::TranslateNums; -pub trait DSAPublicKey { +pub trait DSAPublicKey { + type Params : DSAParameters; + type L; + type N; + /// Generate a new public key given the parameters and public value. - fn new(params: Params, y: L) -> Self; + fn new(params: Self::Params, y: Self::L) -> Self; /// Verify the given signature against the given message, using the /// appropriate hash function. - fn verify(&self, m: &[u8], sig: &DSASignature) -> bool + fn verify(&self, m: &[u8], sig: &DSASignature) -> bool where Hash: Digest; } @@ -22,10 +25,21 @@ pub struct DSAPubKey { pub(crate) y: L } +pub enum DSAPublic { + DSAPublicL1024N160(DSAPubKey), + DSAPublicL2048N224(DSAPubKey), + DSAPublicL2048N256(DSAPubKey), + DSAPublicL3072N256(DSAPubKey) +} + macro_rules! pubkey_impls { ($ptype: ident, $ltype: ident, $ntype: ident, $dbl: ident, $bdbl: ident) => { - impl DSAPublicKey<$ptype,$ltype,$ntype> for DSAPubKey<$ptype,$ltype> + impl DSAPublicKey for DSAPubKey<$ptype,$ltype> { + type Params = $ptype; + type L = $ltype; + type N = $ntype; + fn new(params: $ptype, y: $ltype) -> DSAPubKey<$ptype,$ltype> { DSAPubKey{ params, y } @@ -72,8 +86,7 @@ macro_rules! pubkey_impls { fn to_asn1_class(&self, c: ASN1Class) -> Result,ASN1EncodeErr> { - let uinty = self.y.to_num(); - let inty = BigInt::from(uinty); + let inty = self.y.to_num(); let yblock = ASN1Block::Integer(c, 0, inty); Ok(vec![yblock]) } diff --git a/src/dsa/rfc6979.rs b/src/dsa/rfc6979.rs index 365c647..4ac7004 100644 --- a/src/dsa/rfc6979.rs +++ b/src/dsa/rfc6979.rs @@ -250,8 +250,8 @@ fn hmac(k: &[u8], m: &[u8]) -> Vec pub enum DSADecodeError { ASN1Error(ASN1DecodeErr), NoSignatureFound, - NegativeSigValues, - RValueTooBig, SValueTooBig + InvalidRValue, + InvalidSValue } impl From for DSADecodeError { @@ -261,7 +261,7 @@ impl From for DSADecodeError { } impl FromASN1 for DSASignature - where N: TranslateNums + where N: TranslateNums { type Error = DSADecodeError; @@ -275,16 +275,9 @@ impl FromASN1 for DSASignature match (&info[0], &info[1]) { (&ASN1Block::Integer(_,_,ref rint), &ASN1Block::Integer(_,_,ref sint)) => { - match (rint.to_biguint(), sint.to_biguint()) { - (Some(rnum), Some(snum)) => { - let r = N::from_num(rnum).ok_or(DSADecodeError::RValueTooBig)?; - let s = N::from_num(snum).ok_or(DSADecodeError::SValueTooBig)?; - Ok((DSASignature{ r, s }, rest)) - - } - _ => - Err(DSADecodeError::NegativeSigValues) - } + let r = N::from_num(rint).ok_or(DSADecodeError::InvalidRValue)?; + let s = N::from_num(sint).ok_or(DSADecodeError::InvalidSValue)?; + Ok((DSASignature{ r, s }, rest)) } _ => Err(DSADecodeError::NoSignatureFound) } @@ -295,15 +288,15 @@ impl FromASN1 for DSASignature } impl ToASN1 for DSASignature - where N: TranslateNums + where N: TranslateNums { type Error = ASN1EncodeErr; fn to_asn1_class(&self, c: ASN1Class) -> Result,ASN1EncodeErr> { - let rb = ASN1Block::Integer(c, 0, BigInt::from(self.r.to_num())); - let sb = ASN1Block::Integer(c, 0, BigInt::from(self.s.to_num())); + let rb = ASN1Block::Integer(c, 0, self.r.to_num()); + let sb = ASN1Block::Integer(c, 0, self.s.to_num()); Ok(vec![ASN1Block::Sequence(c, 0, vec![rb,sb])]) } } diff --git a/src/dsa/tests.rs b/src/dsa/tests.rs index 9a7494b..46d7770 100644 --- a/src/dsa/tests.rs +++ b/src/dsa/tests.rs @@ -1,12 +1,15 @@ +use cryptonum::unsigned::*; use digest::Digest; use sha1::Sha1; use sha2::{Sha224,Sha256,Sha384,Sha512}; use simple_asn1::{der_decode,der_encode}; -use super::*; +use dsa::params::{DSAParameters,L1024N160,L2048N256}; +use dsa::private::{DSAPrivateKey,DSAPrivKey}; +use dsa::public::{DSAPublicKey,DSAPubKey}; use dsa::rfc6979::KIterator; macro_rules! run_rfc6979_test { - ($hash: ty, $ntype: ident, $val: ident, $public: ident, $private: ident, + ($hash: ty, $ntype: ident, $val: ident, $params: ident, $public: ident, $private: ident, k $k: expr, r $r: expr, s $s: expr) => ({ @@ -15,9 +18,7 @@ macro_rules! run_rfc6979_test { let sbytes = $s; let r = $ntype::from_bytes(&rbytes); let s = $ntype::from_bytes(&sbytes); - let mut iter = KIterator::<$hash,$ntype>::new(&h1, $public.params.n_bits(), - &$public.params.q, - &$private.x); + let mut iter = KIterator::<$hash,$ntype>::new(&h1, $params.n_bits(), &$params.q, &$private.x); let mut k1 = iter.next().unwrap().to_bytes().to_vec(); while k1.len() > $k.len() { assert_eq!(k1[0], 0); @@ -98,7 +99,7 @@ fn appendix_a21() { let params = L1024N160::new(p, g, q); let x = U192::from_bytes(&xbytes); let y = U1024::from_bytes(&ybytes); - let private = DSAPrivKey::::new(params.clone(), x); + let private = DSAPrivKey::new(params.clone(), x); let public = DSAPubKey::::new(params.clone(), y); // let sample: [u8; 6] = [115, 97, 109, 112, 108, 101]; // "sample", ASCII @@ -107,7 +108,7 @@ fn appendix_a21() { // k = 7BDB6B0FF756E1BB5D53583EF979082F9AD5BD5B // r = 2E1A0C2562B2912CAAF89186FB0F42001585DA55 // s = 29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5 - run_rfc6979_test!(Sha1, U192, sample, public, private, + run_rfc6979_test!(Sha1, U192, sample, params, public, private, k vec![0x7B, 0xDB, 0x6B, 0x0F, 0xF7, 0x56, 0xE1, 0xBB, 0x5D, 0x53, 0x58, 0x3E, 0xF9, 0x79, 0x08, 0x2F, 0x9A, 0xD5, 0xBD, 0x5B], @@ -121,7 +122,7 @@ fn appendix_a21() { // k = 562097C06782D60C3037BA7BE104774344687649 // r = 4BC3B686AEA70145856814A6F1BB53346F02101E // s = 410697B92295D994D21EDD2F4ADA85566F6F94C1 - run_rfc6979_test!(Sha224, U192, sample, public, private, + run_rfc6979_test!(Sha224, U192, sample, params, public, private, k vec![0x56, 0x20, 0x97, 0xC0, 0x67, 0x82, 0xD6, 0x0C, 0x30, 0x37, 0xBA, 0x7B, 0xE1, 0x04, 0x77, 0x43, 0x44, 0x68, 0x76, 0x49], @@ -135,7 +136,7 @@ fn appendix_a21() { // k = 519BA0546D0C39202A7D34D7DFA5E760B318BCFB // r = 81F2F5850BE5BC123C43F71A3033E9384611C545 // s = 4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89 - run_rfc6979_test!(Sha256, U192, sample, public, private, + run_rfc6979_test!(Sha256, U192, sample, params, public, private, k vec![0x51, 0x9B, 0xA0, 0x54, 0x6D, 0x0C, 0x39, 0x20, 0x2A, 0x7D, 0x34, 0xD7, 0xDF, 0xA5, 0xE7, 0x60, 0xB3, 0x18, 0xBC, 0xFB], @@ -149,7 +150,7 @@ fn appendix_a21() { // k = 95897CD7BBB944AA932DBC579C1C09EB6FCFC595 // r = 07F2108557EE0E3921BC1774F1CA9B410B4CE65A // s = 54DF70456C86FAC10FAB47C1949AB83F2C6F7595 - run_rfc6979_test!(Sha384, U192, sample, public, private, + run_rfc6979_test!(Sha384, U192, sample, params, public, private, k vec![0x95, 0x89, 0x7C, 0xD7, 0xBB, 0xB9, 0x44, 0xAA, 0x93, 0x2D, 0xBC, 0x57, 0x9C, 0x1C, 0x09, 0xEB, 0x6F, 0xCF, 0xC5, 0x95], @@ -163,7 +164,7 @@ fn appendix_a21() { // k = 09ECE7CA27D0F5A4DD4E556C9DF1D21D28104F8B // r = 16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B // s = 02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C - run_rfc6979_test!(Sha512, U192, sample, public, private, + run_rfc6979_test!(Sha512, U192, sample, params, public, private, k vec![0x09, 0xEC, 0xE7, 0xCA, 0x27, 0xD0, 0xF5, 0xA4, 0xDD, 0x4E, 0x55, 0x6C, 0x9D, 0xF1, 0xD2, 0x1D, 0x28, 0x10, 0x4F, 0x8B], @@ -177,7 +178,7 @@ fn appendix_a21() { // k = 5C842DF4F9E344EE09F056838B42C7A17F4A6433 // r = 42AB2052FD43E123F0607F115052A67DCD9C5C77 // s = 183916B0230D45B9931491D4C6B0BD2FB4AAF088 - run_rfc6979_test!(Sha1, U192, test, public, private, + run_rfc6979_test!(Sha1, U192, test, params, public, private, k vec![0x5C, 0x84, 0x2D, 0xF4, 0xF9, 0xE3, 0x44, 0xEE, 0x09, 0xF0, 0x56, 0x83, 0x8B, 0x42, 0xC7, 0xA1, 0x7F, 0x4A, 0x64, 0x33], @@ -191,7 +192,7 @@ fn appendix_a21() { // k = 4598B8EFC1A53BC8AECD58D1ABBB0C0C71E67297 // r = 6868E9964E36C1689F6037F91F28D5F2C30610F2 // s = 49CEC3ACDC83018C5BD2674ECAAD35B8CD22940F - run_rfc6979_test!(Sha224, U192, test, public, private, + run_rfc6979_test!(Sha224, U192, test, params, public, private, k vec![0x45, 0x98, 0xB8, 0xEF, 0xC1, 0xA5, 0x3B, 0xC8, 0xAE, 0xCD, 0x58, 0xD1, 0xAB, 0xBB, 0x0C, 0x0C, 0x71, 0xE6, 0x72, 0x97], @@ -205,7 +206,7 @@ fn appendix_a21() { // k = 5A67592E8128E03A417B0484410FB72C0B630E1A // r = 22518C127299B0F6FDC9872B282B9E70D0790812 // s = 6837EC18F150D55DE95B5E29BE7AF5D01E4FE160 - run_rfc6979_test!(Sha256, U192, test, public, private, + run_rfc6979_test!(Sha256, U192, test, params, public, private, k vec![0x5A, 0x67, 0x59, 0x2E, 0x81, 0x28, 0xE0, 0x3A, 0x41, 0x7B, 0x04, 0x84, 0x41, 0x0F, 0xB7, 0x2C, 0x0B, 0x63, 0x0E, 0x1A], @@ -219,7 +220,7 @@ fn appendix_a21() { // k = 220156B761F6CA5E6C9F1B9CF9C24BE25F98CD89 // r = 854CF929B58D73C3CBFDC421E8D5430CD6DB5E66 // s = 91D0E0F53E22F898D158380676A871A157CDA622 - run_rfc6979_test!(Sha384, U192, test, public, private, + run_rfc6979_test!(Sha384, U192, test, params, public, private, k vec![0x22, 0x01, 0x56, 0xB7, 0x61, 0xF6, 0xCA, 0x5E, 0x6C, 0x9F, 0x1B, 0x9C, 0xF9, 0xC2, 0x4B, 0xE2, 0x5F, 0x98, 0xCD, 0x89], @@ -233,7 +234,7 @@ fn appendix_a21() { // k = 65D2C2EEB175E370F28C75BFCDC028D22C7DBE9C // r = 8EA47E475BA8AC6F2D821DA3BD212D11A3DEB9A0 // s = 7C670C7AD72B6C050C109E1790008097125433E8 - run_rfc6979_test!(Sha512, U192, test, public, private, + run_rfc6979_test!(Sha512, U192, test, params, public, private, k vec![0x65, 0xD2, 0xC2, 0xEE, 0xB1, 0x75, 0xE3, 0x70, 0xF2, 0x8C, 0x75, 0xBF, 0xCD, 0xC0, 0x28, 0xD2, 0x2C, 0x7D, 0xBE, 0x9C], @@ -358,8 +359,8 @@ fn appendix_a22() { let params = L2048N256::new(p, g, q); let x = U256::from_bytes(&xbytes); let y = U2048::from_bytes(&ybytes); - let private = DSAPrivKey::new(params.clone(), x); - let public = DSAPubKey::new(params.clone(), y); + let private = DSAPrivKey::::new(params.clone(), x); + let public = DSAPubKey::::new(params.clone(), y); // let sample: [u8; 6] = [115, 97, 109, 112, 108, 101]; // "sample", ASCII let test: [u8; 4] = [116, 101, 115, 116]; // "test", ASCII @@ -367,7 +368,7 @@ fn appendix_a22() { // k = 888FA6F7738A41BDC9846466ABDB8174C0338250AE50CE955CA16230F9CBD53E // r = 3A1B2DBD7489D6ED7E608FD036C83AF396E290DBD602408E8677DAABD6E7445A // s = D26FCBA19FA3E3058FFC02CA1596CDBB6E0D20CB37B06054F7E36DED0CDBBCCF - run_rfc6979_test!(Sha1, U256, sample, public, private, + run_rfc6979_test!(Sha1, U256, sample, params, public, private, k vec![0x88,0x8F,0xA6,0xF7,0x73,0x8A,0x41,0xBD, 0xC9,0x84,0x64,0x66,0xAB,0xDB,0x81,0x74, 0xC0,0x33,0x82,0x50,0xAE,0x50,0xCE,0x95, @@ -384,7 +385,7 @@ fn appendix_a22() { // k = BC372967702082E1AA4FCE892209F71AE4AD25A6DFD869334E6F153BD0C4D806 // r = DC9F4DEADA8D8FF588E98FED0AB690FFCE858DC8C79376450EB6B76C24537E2C // s = A65A9C3BC7BABE286B195D5DA68616DA8D47FA0097F36DD19F517327DC848CEC - run_rfc6979_test!(Sha224, U256, sample, public, private, + run_rfc6979_test!(Sha224, U256, sample, params, public, private, k vec![0xBC,0x37,0x29,0x67,0x70,0x20,0x82,0xE1, 0xAA,0x4F,0xCE,0x89,0x22,0x09,0xF7,0x1A, 0xE4,0xAD,0x25,0xA6,0xDF,0xD8,0x69,0x33, @@ -401,7 +402,7 @@ fn appendix_a22() { // k = 8926A27C40484216F052F4427CFD5647338B7B3939BC6573AF4333569D597C52 // r = EACE8BDBBE353C432A795D9EC556C6D021F7A03F42C36E9BC87E4AC7932CC809 // s = 7081E175455F9247B812B74583E9E94F9EA79BD640DC962533B0680793A38D53 - run_rfc6979_test!(Sha256, U256, sample, public, private, + run_rfc6979_test!(Sha256, U256, sample, params, public, private, k vec![0x89,0x26,0xA2,0x7C,0x40,0x48,0x42,0x16, 0xF0,0x52,0xF4,0x42,0x7C,0xFD,0x56,0x47, 0x33,0x8B,0x7B,0x39,0x39,0xBC,0x65,0x73, @@ -418,7 +419,7 @@ fn appendix_a22() { // k = C345D5AB3DA0A5BCB7EC8F8FB7A7E96069E03B206371EF7D83E39068EC564920 // r = B2DA945E91858834FD9BF616EBAC151EDBC4B45D27D0DD4A7F6A22739F45C00B // s = 19048B63D9FD6BCA1D9BAE3664E1BCB97F7276C306130969F63F38FA8319021B - run_rfc6979_test!(Sha384, U256, sample, public, private, + run_rfc6979_test!(Sha384, U256, sample, params, public, private, k vec![0xC3,0x45,0xD5,0xAB,0x3D,0xA0,0xA5,0xBC, 0xB7,0xEC,0x8F,0x8F,0xB7,0xA7,0xE9,0x60, 0x69,0xE0,0x3B,0x20,0x63,0x71,0xEF,0x7D, @@ -435,7 +436,7 @@ fn appendix_a22() { // k = 5A12994431785485B3F5F067221517791B85A597B7A9436995C89ED0374668FC // r = 2016ED092DC5FB669B8EFB3D1F31A91EECB199879BE0CF78F02BA062CB4C942E // s = D0C76F84B5F091E141572A639A4FB8C230807EEA7D55C8A154A224400AFF2351 - run_rfc6979_test!(Sha512, U256, sample, public, private, + run_rfc6979_test!(Sha512, U256, sample, params, public, private, k vec![0x5A,0x12,0x99,0x44,0x31,0x78,0x54,0x85, 0xB3,0xF5,0xF0,0x67,0x22,0x15,0x17,0x79, 0x1B,0x85,0xA5,0x97,0xB7,0xA9,0x43,0x69, @@ -452,7 +453,7 @@ fn appendix_a22() { // k = 6EEA486F9D41A037B2C640BC5645694FF8FF4B98D066A25F76BE641CCB24BA4F // r = C18270A93CFC6063F57A4DFA86024F700D980E4CF4E2CB65A504397273D98EA0 // s = 414F22E5F31A8B6D33295C7539C1C1BA3A6160D7D68D50AC0D3A5BEAC2884FAA - run_rfc6979_test!(Sha1, U256, test, public, private, + run_rfc6979_test!(Sha1, U256, test, params, public, private, k vec![0x6E,0xEA,0x48,0x6F,0x9D,0x41,0xA0,0x37, 0xB2,0xC6,0x40,0xBC,0x56,0x45,0x69,0x4F, 0xF8,0xFF,0x4B,0x98,0xD0,0x66,0xA2,0x5F, @@ -469,7 +470,7 @@ fn appendix_a22() { // k = 06BD4C05ED74719106223BE33F2D95DA6B3B541DAD7BFBD7AC508213B6DA6670 // r = 272ABA31572F6CC55E30BF616B7A265312018DD325BE031BE0CC82AA17870EA3 // s = E9CC286A52CCE201586722D36D1E917EB96A4EBDB47932F9576AC645B3A60806 - run_rfc6979_test!(Sha224, U256, test, public, private, + run_rfc6979_test!(Sha224, U256, test, params, public, private, k vec![0x06,0xBD,0x4C,0x05,0xED,0x74,0x71,0x91, 0x06,0x22,0x3B,0xE3,0x3F,0x2D,0x95,0xDA, 0x6B,0x3B,0x54,0x1D,0xAD,0x7B,0xFB,0xD7, @@ -486,7 +487,7 @@ fn appendix_a22() { // k = 1D6CE6DDA1C5D37307839CD03AB0A5CBB18E60D800937D67DFB4479AAC8DEAD7 // r = 8190012A1969F9957D56FCCAAD223186F423398D58EF5B3CEFD5A4146A4476F0 // s = 7452A53F7075D417B4B013B278D1BB8BBD21863F5E7B1CEE679CF2188E1AB19E - run_rfc6979_test!(Sha256, U256, test, public, private, + run_rfc6979_test!(Sha256, U256, test, params, public, private, k vec![0x1D,0x6C,0xE6,0xDD,0xA1,0xC5,0xD3,0x73, 0x07,0x83,0x9C,0xD0,0x3A,0xB0,0xA5,0xCB, 0xB1,0x8E,0x60,0xD8,0x00,0x93,0x7D,0x67, @@ -503,7 +504,7 @@ fn appendix_a22() { // k = 206E61F73DBE1B2DC8BE736B22B079E9DACD974DB00EEBBC5B64CAD39CF9F91C // r = 239E66DDBE8F8C230A3D071D601B6FFBDFB5901F94D444C6AF56F732BEB954BE // s = 6BD737513D5E72FE85D1C750E0F73921FE299B945AAD1C802F15C26A43D34961 - run_rfc6979_test!(Sha384, U256, test, public, private, + run_rfc6979_test!(Sha384, U256, test, params, public, private, k vec![0x20,0x6E,0x61,0xF7,0x3D,0xBE,0x1B,0x2D, 0xC8,0xBE,0x73,0x6B,0x22,0xB0,0x79,0xE9, 0xDA,0xCD,0x97,0x4D,0xB0,0x0E,0xEB,0xBC, @@ -520,7 +521,7 @@ fn appendix_a22() { // k = AFF1651E4CD6036D57AA8B2A05CCF1A9D5A40166340ECBBDC55BE10B568AA0AA // r = 89EC4BB1400ECCFF8E7D9AA515CD1DE7803F2DAFF09693EE7FD1353E90A68307 // s = C9F0BDABCC0D880BB137A994CC7F3980CE91CC10FAF529FC46565B15CEA854E1 - run_rfc6979_test!(Sha512, U256, test, public, private, + run_rfc6979_test!(Sha512, U256, test, params, public, private, k vec![0xAF,0xF1,0x65,0x1E,0x4C,0xD6,0x03,0x6D, 0x57,0xAA,0x8B,0x2A,0x05,0xCC,0xF1,0xA9, 0xD5,0xA4,0x01,0x66,0x34,0x0E,0xCB,0xBD, diff --git a/src/ecdsa/mod.rs b/src/ecdsa/mod.rs index 2e7cb0c..2f28388 100644 --- a/src/ecdsa/mod.rs +++ b/src/ecdsa/mod.rs @@ -11,7 +11,8 @@ use rand::distributions::Standard; use self::curve::{EllipticCurve,P192,P224,P256,P384,P521}; use self::point::{ECCPoint,Point}; pub use self::private::{ECCPrivateKey,ECCPrivate}; -pub use self::public::{ECCPublicKey,ECCPublic}; +pub use self::public::{ECCPublicKey,ECDSAPublic,ECCPubKey}; +pub use self::public::{ECDSADecodeErr,ECDSAEncodeErr}; pub trait ECDSAKeyPair { fn generate(g: &mut G) -> (Public, Private); @@ -19,8 +20,8 @@ pub trait ECDSAKeyPair { macro_rules! generate_impl { ($curve: ident, $un: ident, $si: ident) => { - impl ECDSAKeyPair,ECCPrivate<$curve>> for $curve { - fn generate(rng: &mut G) -> (ECCPublic<$curve>, ECCPrivate<$curve>) + impl ECDSAKeyPair,ECCPrivate<$curve>> for $curve { + fn generate(rng: &mut G) -> (ECCPubKey<$curve>, ECCPrivate<$curve>) { loop { let size = ($curve::size() + 7) / 8; @@ -37,7 +38,7 @@ macro_rules! generate_impl { let d = $si::from(&proposed_d); let public_point = Point::<$curve>::default().scale(&d); - let public = ECCPublic::<$curve>::new(public_point); + let public = ECCPubKey::<$curve>::new(public_point); let private = ECCPrivate::<$curve>::new(proposed_d); return (public, private); } diff --git a/src/ecdsa/public.rs b/src/ecdsa/public.rs index cb37c37..3fb4505 100644 --- a/src/ecdsa/public.rs +++ b/src/ecdsa/public.rs @@ -5,36 +5,70 @@ use dsa::rfc6979::DSASignature; use ecdsa::curve::{EllipticCurve,P192,P224,P256,P384,P521}; use ecdsa::point::{ECCPoint,Point}; use hmac::{Hmac,Mac}; +use simple_asn1::{ASN1Block,ASN1Class,ASN1DecodeErr,ASN1EncodeErr,FromASN1,ToASN1}; use std::cmp::min; -pub struct ECCPublic { +pub struct ECCPubKey { q: Point } +pub enum ECDSAPublic { + ECCPublicP192(ECCPubKey), + ECCPublicP224(ECCPubKey), + ECCPublicP256(ECCPubKey), + ECCPublicP384(ECCPubKey), + ECCPublicP521(ECCPubKey), +} + pub trait ECCPublicKey { type Curve : EllipticCurve; type Unsigned; fn new(d: Point) -> Self; - fn verify(&self, m: &[u8], sig: DSASignature) -> bool + fn verify(&self, m: &[u8], sig: &DSASignature) -> bool where Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset, Hmac: Mac; } +pub enum ECDSAEncodeErr { + ASN1EncodeErr(ASN1EncodeErr), + XValueNegative, YValueNegative +} + +impl From for ECDSAEncodeErr { + fn from(x: ASN1EncodeErr) -> ECDSAEncodeErr { + ECDSAEncodeErr::ASN1EncodeErr(x) + } +} + +#[derive(Debug)] +pub enum ECDSADecodeErr { + ASN1DecodeErr(ASN1DecodeErr), + NoKeyFound, + InvalidKeyFormat, + InvalidKeyBlockSize +} + +impl From for ECDSADecodeErr { + fn from(x: ASN1DecodeErr) -> ECDSADecodeErr { + ECDSADecodeErr::ASN1DecodeErr(x) + } +} + macro_rules! public_impl { ($curve: ident, $un: ident, $si: ident) => { - impl ECCPublicKey for ECCPublic<$curve> + impl ECCPublicKey for ECCPubKey<$curve> { type Curve = $curve; type Unsigned = $un; - fn new(q: Point<$curve>) -> ECCPublic<$curve> + fn new(q: Point<$curve>) -> ECCPubKey<$curve> { - ECCPublic{ q } + ECCPubKey{ q } } - fn verify(&self, m: &[u8], sig: DSASignature) -> bool + fn verify(&self, m: &[u8], sig: &DSASignature) -> bool where Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset, Hmac: Mac @@ -68,6 +102,66 @@ macro_rules! public_impl { } } } + + impl ToASN1 for ECCPubKey<$curve> { + type Error = ECDSAEncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) -> Result,ECDSAEncodeErr> + { + if self.q.x.is_negative() { + return Err(ECDSAEncodeErr::XValueNegative); + } + if self.q.y.is_negative() { + return Err(ECDSAEncodeErr::YValueNegative); + } + + let xval = $un::from(&self.q.x); + let yval = $un::from(&self.q.y); + let mut xbytes = xval.to_bytes(); + let mut ybytes = yval.to_bytes(); + let goalsize = ($curve::size() + 7) / 8; + let mut target = Vec::with_capacity(1 + (goalsize * 2)); + + while xbytes.len() > goalsize { xbytes.remove(0); }; + while xbytes.len() < goalsize { xbytes.insert(0,0) }; + while ybytes.len() > goalsize { ybytes.remove(0); }; + while ybytes.len() < goalsize { ybytes.insert(0,0) }; + + target.push(4); + target.append(&mut xbytes); + target.append(&mut ybytes); + + let result = ASN1Block::BitString(c, 0, target.len() * 8, target); + Ok(vec![result]) + } + } + + impl FromASN1 for ECCPubKey<$curve> { + type Error = ECDSADecodeErr; + + fn from_asn1(bs: &[ASN1Block]) -> Result<(ECCPubKey<$curve>,&[ASN1Block]),ECDSADecodeErr> + { + let (x, rest) = bs.split_first().ok_or(ECDSADecodeErr::NoKeyFound)?; + if let ASN1Block::BitString(_, _, _, target) = x { + let (hdr, xy_bstr) = target.split_first().ok_or(ECDSADecodeErr::InvalidKeyFormat)?; + if *hdr != 4 { + return Err(ECDSADecodeErr::InvalidKeyFormat); + } + let goalsize = ($curve::size() + 7) / 8; + if xy_bstr.len() != (2 * goalsize) { + return Err(ECDSADecodeErr::InvalidKeyBlockSize); + } + let (xbstr, ybstr) = xy_bstr.split_at(goalsize); + let x = $un::from_bytes(xbstr); + let y = $un::from_bytes(ybstr); + let point = Point::<$curve>{ x: $si::from(x), y: $si::from(y) }; + let res = ECCPubKey::<$curve>::new(point); + Ok((res, rest)) + } else { + Err(ECDSADecodeErr::InvalidKeyFormat) + } + } + } }; } @@ -107,13 +201,13 @@ macro_rules! test_impl { let s = $un::from_bytes(sbytes); let point = Point::<$curve>{ x: $si::from(x), y: $si::from(y) }; - let public = ECCPublic::<$curve>::new(point); + let public = ECCPubKey::<$curve>::new(point); let sig = DSASignature::new(r, s); match usize::from(h) { - 224 => assert!(public.verify::(mbytes, sig)), - 256 => assert!(public.verify::(mbytes, sig)), - 384 => assert!(public.verify::(mbytes, sig)), - 512 => assert!(public.verify::(mbytes, sig)), + 224 => assert!(public.verify::(mbytes, &sig)), + 256 => assert!(public.verify::(mbytes, &sig)), + 384 => assert!(public.verify::(mbytes, &sig)), + 512 => assert!(public.verify::(mbytes, &sig)), x => panic!("Unknown hash algorithm {}", x) }; }); diff --git a/src/lib.rs b/src/lib.rs index 4c71e2a..1c26bb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ //! when they should use it, and examples. For now, it mostly just fowards //! off to more detailed modules. Help requested! extern crate byteorder; +extern crate chrono; extern crate cryptonum; extern crate digest; extern crate hmac; @@ -20,6 +21,7 @@ extern crate quickcheck; extern crate rand; extern crate sha1; extern crate sha2; +#[macro_use] extern crate simple_asn1; /// The `rsa` module provides bare-bones support for RSA signing, verification, @@ -33,6 +35,9 @@ pub mod dsa; /// The `ecdsa` module provides bare-bones support for ECDSA signing, /// verification, and key generation. pub mod ecdsa; +/// The `x509` module supports parsing and generating x.509 certificates, as +/// used by TLS and others. +pub mod x509; #[cfg(test)] mod testing; diff --git a/src/rsa/mod.rs b/src/rsa/mod.rs index 7199697..7b0bd6b 100644 --- a/src/rsa/mod.rs +++ b/src/rsa/mod.rs @@ -24,6 +24,7 @@ mod private; mod public; mod signing_hashes; +pub use self::errors::RSAError; pub use self::signing_hashes::{SigningHash, SIGNING_HASH_NULL, SIGNING_HASH_SHA1, diff --git a/src/rsa/public.rs b/src/rsa/public.rs index 1ff693b..846574a 100644 --- a/src/rsa/public.rs +++ b/src/rsa/public.rs @@ -1,6 +1,5 @@ use cryptonum::unsigned::*; use digest::{Digest,FixedOutput}; -use num::BigInt; use rand::Rng; use rand::rngs::OsRng; use rsa::core::{decode_biguint,pkcs1_pad,xor_vecs}; @@ -71,6 +70,21 @@ pub enum RSAPublic { Key15360(RSA15360Public) } +impl RSAPublic { + pub fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool + { + match self { + RSAPublic::Key512(x) => x.verify(signhash, msg, sig), + RSAPublic::Key1024(x) => x.verify(signhash, msg, sig), + RSAPublic::Key2048(x) => x.verify(signhash, msg, sig), + RSAPublic::Key3072(x) => x.verify(signhash, msg, sig), + RSAPublic::Key4096(x) => x.verify(signhash, msg, sig), + RSAPublic::Key8192(x) => x.verify(signhash, msg, sig), + RSAPublic::Key15360(x) => x.verify(signhash, msg, sig) + } + } +} + impl FromASN1 for RSAPublic { type Error = RSAError; @@ -95,44 +109,44 @@ impl FromASN1 for RSAPublic { } match rsa_size { 512 => { - let n2 = U512::from_num(n).ok_or(RSAError::InvalidKey)?; - let e2 = U512::from_num(e).ok_or(RSAError::InvalidKey)?; + let n2 = U512::from_num(&n).ok_or(RSAError::InvalidKey)?; + let e2 = U512::from_num(&e).ok_or(RSAError::InvalidKey)?; let res = RSA512Public::new(n2, e2); Ok((RSAPublic::Key512(res), rest)) } 1024 => { - let n2 = U1024::from_num(n).ok_or(RSAError::InvalidKey)?; - let e2 = U1024::from_num(e).ok_or(RSAError::InvalidKey)?; + let n2 = U1024::from_num(&n).ok_or(RSAError::InvalidKey)?; + let e2 = U1024::from_num(&e).ok_or(RSAError::InvalidKey)?; let res = RSA1024Public::new(n2, e2); Ok((RSAPublic::Key1024(res), rest)) } 2048 => { - let n2 = U2048::from_num(n).ok_or(RSAError::InvalidKey)?; - let e2 = U2048::from_num(e).ok_or(RSAError::InvalidKey)?; + let n2 = U2048::from_num(&n).ok_or(RSAError::InvalidKey)?; + let e2 = U2048::from_num(&e).ok_or(RSAError::InvalidKey)?; let res = RSA2048Public::new(n2, e2); Ok((RSAPublic::Key2048(res), rest)) } 3072 => { - let n2 = U3072::from_num(n).ok_or(RSAError::InvalidKey)?; - let e2 = U3072::from_num(e).ok_or(RSAError::InvalidKey)?; + let n2 = U3072::from_num(&n).ok_or(RSAError::InvalidKey)?; + let e2 = U3072::from_num(&e).ok_or(RSAError::InvalidKey)?; let res = RSA3072Public::new(n2, e2); Ok((RSAPublic::Key3072(res), rest)) } 4096 => { - let n2 = U4096::from_num(n).ok_or(RSAError::InvalidKey)?; - let e2 = U4096::from_num(e).ok_or(RSAError::InvalidKey)?; + let n2 = U4096::from_num(&n).ok_or(RSAError::InvalidKey)?; + let e2 = U4096::from_num(&e).ok_or(RSAError::InvalidKey)?; let res = RSA4096Public::new(n2, e2); Ok((RSAPublic::Key4096(res), rest)) } 8192 => { - let n2 = U8192::from_num(n).ok_or(RSAError::InvalidKey)?; - let e2 = U8192::from_num(e).ok_or(RSAError::InvalidKey)?; + let n2 = U8192::from_num(&n).ok_or(RSAError::InvalidKey)?; + let e2 = U8192::from_num(&e).ok_or(RSAError::InvalidKey)?; let res = RSA8192Public::new(n2, e2); Ok((RSAPublic::Key8192(res), rest)) } 15360 => { - let n2 = U15360::from_num(n).ok_or(RSAError::InvalidKey)?; - let e2 = U15360::from_num(e).ok_or(RSAError::InvalidKey)?; + let n2 = U15360::from_num(&n).ok_or(RSAError::InvalidKey)?; + let e2 = U15360::from_num(&e).ok_or(RSAError::InvalidKey)?; let res = RSA15360Public::new(n2, e2); Ok((RSAPublic::Key15360(res), rest)) } @@ -301,8 +315,8 @@ macro_rules! generate_rsa_public fn to_asn1_class(&self, c: ASN1Class) -> Result,Self::Error> { - let n = BigInt::from(self.n.to_num()); - let e = BigInt::from(self.e.to_num()); + let n = self.n.to_num(); + let e = self.e.to_num(); let enc_n = ASN1Block::Integer(c, 0, n); let enc_e = ASN1Block::Integer(c, 0, e); let seq = ASN1Block::Sequence(c, 0, vec![enc_n, enc_e]); diff --git a/src/utils.rs b/src/utils.rs index 257263b..912624f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,15 +1,16 @@ use cryptonum::unsigned::*; -use num::BigUint; +use num::{BigInt,BigUint}; +use num::bigint::Sign; -pub trait TranslateNums: Sized { - fn from_num(x: BigUint) -> Option; - fn to_num(&self) -> BigUint; +pub trait TranslateNums: Sized { + fn from_num(x: &N) -> Option; + fn to_num(&self) -> N; } macro_rules! from_biguint { ($uname: ident, $size: expr) => { - impl TranslateNums for $uname { - fn from_num(x: BigUint) -> Option<$uname> { + impl TranslateNums for $uname { + fn from_num(x: &BigUint) -> Option<$uname> { let mut base_vec = x.to_bytes_be(); let target_bytes = $size / 8; @@ -29,15 +30,28 @@ macro_rules! from_biguint { BigUint::from_bytes_be(&bytes) } } + + impl TranslateNums for $uname { + fn from_num(x: &BigInt) -> Option<$uname> { + let ux = x.to_biguint()?; + $uname::from_num(&ux) + } + + fn to_num(&self) -> BigInt { + BigInt::from_biguint(Sign::Plus, self.to_num()) + } + } }; } from_biguint!(U192, 192); from_biguint!(U256, 256); +from_biguint!(U384, 384); from_biguint!(U512, 512); +from_biguint!(U576, 576); from_biguint!(U1024, 1024); from_biguint!(U2048, 2048); from_biguint!(U3072, 3072); from_biguint!(U4096, 4096); from_biguint!(U8192, 8192); -from_biguint!(U15360, 15360); +from_biguint!(U15360, 15360); \ No newline at end of file diff --git a/src/x509/algident.rs b/src/x509/algident.rs new file mode 100644 index 0000000..fbc7252 --- /dev/null +++ b/src/x509/algident.rs @@ -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 ::= 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 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,SigAlgEncodeError> + { + let block = encode_algorithm_ident(c, self)?; + Ok(vec![block]) + } +} + +fn encode_algorithm_ident(c: ASN1Class, x: &AlgorithmIdentifier) + -> Result +{ + 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: &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 + } + } + } + } + } +} + diff --git a/src/x509/atv.rs b/src/x509/atv.rs new file mode 100644 index 0000000..6d3f615 --- /dev/null +++ b/src/x509/atv.rs @@ -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 +} + +const EMPTY_STRING: &'static str = ""; + +impl Index 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 +{ + // 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 +{ + 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,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 +{ + // 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 +{ + 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,ASN1EncodeErr> + { + let block = encode_attribute_type_value(c, self)?; + Ok(vec![block]) + } +} + +fn get_atv_string(n: X520Name, x: &ASN1Block) + -> Result +{ + 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 +{ + 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 +{ + match a { + &ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()), + _ => + Err(X509ParseError::IllegalStringValue) + } +} + +fn get_ia5_val(a: &ASN1Block) -> Result +{ + 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: &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: &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: &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: &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::() as char); + } + String::from_iter(items.iter()) + } + + fn gen_utf8(g: &mut G) -> String { + String::arbitrary(g) + } + + impl Arbitrary for InfoBlock { + fn arbitrary(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 + } + } + } + } +} diff --git a/src/x509/error.rs b/src/x509/error.rs new file mode 100644 index 0000000..45238a6 --- /dev/null +++ b/src/x509/error.rs @@ -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 for X509ParseError { + fn from(e: ASN1DecodeErr) -> X509ParseError { + X509ParseError::ASN1DecodeError(e) + } +} + +impl From for X509ParseError { + fn from(e: ASN1EncodeErr) -> X509ParseError { + X509ParseError::ASN1EncodeError(e) + } +} + +impl From for X509ParseError { + fn from(e: RSAError) -> X509ParseError { + X509ParseError::RSAError(e) + } +} + +impl From for X509ParseError { + fn from(e: ECDSADecodeErr) -> X509ParseError { + X509ParseError::ECDSADecodeError(e) + } +} + +impl From for X509ParseError { + fn from(e: DSADecodeError) -> X509ParseError { + X509ParseError::DSADecodeError(e) + } +} \ No newline at end of file diff --git a/src/x509/misc.rs b/src/x509/misc.rs new file mode 100644 index 0000000..f499c85 --- /dev/null +++ b/src/x509/misc.rs @@ -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 { + 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,ASN1EncodeErr> + { + Ok(encode_version(c, *self)) + } +} + +/******************************************************************************/ + +#[derive(Clone,Debug,PartialEq)] +pub struct X509Serial { + num: BigUint +} + +fn decode_serial(x: &ASN1Block) + -> Result +{ + 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 for SerialEncodeErr { + fn from(e: ASN1EncodeErr) -> SerialEncodeErr { + SerialEncodeErr::ASN1Error(e) + } +} + +fn encode_serial(c: ASN1Class, serial: &X509Serial) + -> Result +{ + 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,SerialEncodeErr> + { + let v = encode_serial(c, self)?; + Ok(vec![v]) + } +} + +pub fn decode_signature(x: &ASN1Block) + -> Result,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: &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 + } + } + } + } +} diff --git a/src/x509/mod.rs b/src/x509/mod.rs new file mode 100644 index 0000000..2e650b9 --- /dev/null +++ b/src/x509/mod.rs @@ -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 +{ + // + // 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 { + 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) + -> 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::(block, &dsa_sig) => Ok(()), + HashAlgorithm::SHA224 + if key.verify::(block, &dsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(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::(block, &dsa_sig) => Ok(()), + HashAlgorithm::SHA224 + if key.verify::(block, &dsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(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::(block, &dsa_sig) => Ok(()), + HashAlgorithm::SHA224 + if key.verify::(block, &dsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(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::(block, &dsa_sig) => Ok(()), + HashAlgorithm::SHA224 + if key.verify::(block, &dsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(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::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA224 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA384 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA512 if key.verify::(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::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA224 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA384 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA512 if key.verify::(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::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA224 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA384 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA512 if key.verify::(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::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA224 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA384 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA512 if key.verify::(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::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA224 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA256 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA384 if key.verify::(block, &ecdsa_sig) => Ok(()), + HashAlgorithm::SHA512 if key.verify::(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 { + 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()); + } +} diff --git a/src/x509/name.rs b/src/x509/name.rs new file mode 100644 index 0000000..ce9ce06 --- /dev/null +++ b/src/x509/name.rs @@ -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 +{ + 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,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); + } +} + diff --git a/src/x509/publickey.rs b/src/x509/publickey.rs new file mode 100644 index 0000000..358fa5b --- /dev/null +++ b/src/x509/publickey.rs @@ -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 for Option { + fn from(x: X509PublicKey) -> Option { + match x { + X509PublicKey::DSA(x) => Some(x), + _ => None + } + } +} + +impl From for Option { + fn from(x: X509PublicKey) -> Option { + match x { + X509PublicKey::RSA(x) => Some(x), + _ => None + } + } +} + +impl From for Option { + fn from(x: X509PublicKey) -> Option { + match x { + X509PublicKey::ECDSA(x) => Some(x), + _ => None + } + } +} + +pub enum X509EncodeErr { + ASN1EncodeErr(ASN1EncodeErr), + ECDSAEncodeErr(ECDSAEncodeErr) +} + +impl From for X509EncodeErr { + fn from(x: ASN1EncodeErr) -> X509EncodeErr { + X509EncodeErr::ASN1EncodeErr(x) + } +} + +impl From 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,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 +{ + 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 +{ + 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 +{ + 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 +{ + 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::::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::::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::::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::::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 +{ + 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 +{ + if let ASN1Block::ObjectIdentifier(_, _, oid) = info { + if oid == oid!(1,2,840,10045,3,1,1) { + let (res, _) = ECCPubKey::::from_asn1(keybls)?; + return Ok(ECDSAPublic::ECCPublicP192(res)); + } + + if oid == oid!(1,3,132,0,33) { + let (res, _) = ECCPubKey::::from_asn1(keybls)?; + return Ok(ECDSAPublic::ECCPublicP224(res)); + } + + if oid == oid!(1,2,840,10045,3,1,7) { + let (res, _) = ECCPubKey::::from_asn1(keybls)?; + return Ok(ECDSAPublic::ECCPublicP256(res)); + } + + if oid == oid!(1,3,132,0,34) { + let (res, _) = ECCPubKey::::from_asn1(keybls)?; + return Ok(ECDSAPublic::ECCPublicP384(res)); + } + + if oid == oid!(1,3,132,0,35) { + let (res, _) = ECCPubKey::::from_asn1(keybls)?; + return Ok(ECDSAPublic::ECCPublicP521(res)); + } + } + + Err(X509ParseError::UnknownEllipticCurve) +} + +fn strip_algident(block: &ASN1Block) + -> Result<(OID, Option),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 { + match b { + &ASN1Block::Integer(_, _, ref v) => { + match v.to_biguint() { + Some(sn) => Ok(sn), + _ => Err(X509ParseError::InvalidDSAInfo) + } + } + _ => + Err(X509ParseError::InvalidDSAInfo) + } +} \ No newline at end of file diff --git a/src/x509/validity.rs b/src/x509/validity.rs new file mode 100644 index 0000000..4e273d1 --- /dev/null +++ b/src/x509/validity.rs @@ -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, + not_after: DateTime +} + +fn decode_validity_data(bs: &ASN1Block) -> Result { + // 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,ASN1EncodeErr> + { + let block = encode_validity_data(c, self); + Ok(vec![block]) + } +} + +fn get_time(b: &ASN1Block) -> Result, 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: &mut G) -> DateTime { + 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: &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 + } + } + } +} diff --git a/testdata/x509/dsa2048-1.der b/testdata/x509/dsa2048-1.der new file mode 100644 index 0000000000000000000000000000000000000000..ac8ae314033b5c582019eb773775af599ce364a2 GIT binary patch literal 1228 zcmXqLVmV>Z#FEFv$?&<~(fr|!zZC}DY@7*g9*n8XEX+&>C5GGvoNUaYENsF|p}{Z? z2M?2fkfERfKS+k1ha(`rs3a#bFU3&QKo}&%#lsU}YGA40lbKYMSX61qYrqW>=H_7u zNi8lhlsAxNb41ysV4hZnXOx8@JFdnzc=R9%$EG# zX*H!yE-$-hd=qkYTFDu3Al7Yp$nrBz{IOfw_9x?@@QlZ!~sAQ&OCXo4?wc>j%&E11HXx-Da@v+G4pPp=#l*m6Nu7Y+ZF_(6fw;13XWTO19j* v^#0!r>80IF3hH+^wk|pn4PGVk)p{Ri{NQjGvC&JXgQo$!PsVK3i(va7H z8zjul!xEBOTw*A1Aj`(7)#lOmotKf5QA{AQJTp1lp){kov?x_CIX~AxPMp`!+|bC# z!oa}L#MCTGoY&C8z|hDX${j$LH8HyxG%;Di0*TR}iAjg4iII`vWcSRJ-+@eN;WoFQ z&wi)&{(X_y0{{8W^E8ybt&g7;cy0e-ne@#S|0{Or-V*+0{wU4fICkm$faL9O7ad<7 zsUiJI_H)|otvoS#A6MkJ@4b5R$hqjQG`_u8R<})vOf}iR%52>#gFi|=`#5!8-%oh` zS^R8GZtP5+HFbi=xcpdx{geE;X4F32xs*w3nKEB@lUNt^4j3vvE0`M{%>be zWSC^Bd-#j^zwh0X^7r3gb6Dw+Y1cl!cbvWl4==Soa$1#$)LzbU$;*Z_ZCci4Br|aE`3$42ke2=}rumVWW&G!JN{(|ZnkdR(z^`2P zdDY8=pOWHC-2BznTt9fOA2@No>^6gS*A~ka2~`Vct(>&wW7BKboj1jo<}9iB{nzM^ zhs4L++JmJ{?;Y4boeeaXVY#sRln{u{pJ;UAfPIklR=p%K?(;Pai zbfovZsL=3aJ9vI^MVFEBva{Qc-Mh8_KDU3^qQloO&br-xbKU&lM-z^7@1Ay?a|3e| zBP#@nl{x%UWfZvE~1_lLyyMj88CHW%2%wm}lzY zg1Oztnyf!m&Hm}UQsYIO%ad?J?c~6|2i2?UjSuqIUv5$6$mZvh`#!J3sW!p+_LBt{ z9CDPJSQdUdp4+UxCj9e^w!df88bdlB^RLQCc$_u+-THMYX;n!@h8Bh_IgD!e7Dm4; zVP&7q@O(*tB+k1U;Uv1yT!F#VYwj2OF3;s_4 literal 0 HcmV?d00001 diff --git a/testdata/x509/dsa3072-1.der b/testdata/x509/dsa3072-1.der new file mode 100644 index 0000000000000000000000000000000000000000..dde41348de34b06a1c462228fb8c004e850ce1bd GIT binary patch literal 1613 zcmXqLV)Ha;VtvQN$*}$O_Lf&OtK1E^**Fv0JQ!1%S(up&N({LTIN6v(S=fY`LW5x( z4jv}|AVWa|evk}14@W?LQAti>UW%cpfiOsji-#w|)WA}~Co`!iv8d9J*MJ)&%+13R zl3HA1C~qLk#;Mij(e|B}k&{tOAhA3%IoqK$qqwvvRWCU|*Fa93*U;S1$jHLLz|hpl zJW8C`(89pb$Q;TYAXhc995ZNQwq#@1YV$Z}!NO?J#H_>A#MsEt6xMxqed(W7owKLg zu2mLRsT1vB-Y=)m(uMA@5APMzbudIt&{v{ z(ZZC|P%Xpx!^+ygu3S$3a9=r=-o1HRCpYxk>`mFVv)-HQ+VZ$foCf(TJ#Z%h; zU#jWy-^qRZXuERvhnd&Hk8*bXx^b_{D1rUxvdBw}3qEf?ao@{og=R)0(~i|Yg`XS0 zoae&1bB&Fn%k_hy2^S=pL=rE(XX$9Y^e}&)yu_@1cK`RBn0Ze6`R#e!Om_|H_@^_@ zI&gF$BhOl+%VE1Xb{j;ma%3vqJS{r#6MMU!=j4YvjKRE3_J0ab`z(4F#?@j~>+0ll z=``C5m6C*&S69dsUwt=K*sS8^n(%o%4`ma0nG_k$Gi@l?w<7-Alc;p}u6l#SQw#Q7 zmcR0({MOn%7VFnBs53P&HmDW&kt@&wwNBMnx?>Y}HQ*G%zd3JJ{ zX2aX5tXDW5M#M39cKOx4;^z7_Q?{Dt*0HsYQ}vdu?zm*6Hq|dBpW- z!AngpFbLiITr!inW#`$@1xq;|WGHXjve^8Mzxd+S%jW%)Y5I~cCC|Vk@of3;t4xgB zxc0{cecM``xXkFxrTt$$b4_iEC8irN-!NJvsV^^IeOokczPjyvPsba&!5Y;T|5pX5 zFXR1FFW1($&+SV|e>88yuQNQ)R6hRk$_&`JZ5GXHIabUw?k+>Phx~_ZCKr_9uo`rOr}2aqMW-d#@0i>*9%X?`Zh%cNJ$@ z_B8(Zg}c1RIw$Vg{`FRUXRwU#PQ#8Zyz_pS`s`H5LmhHuASFJ5*{c~gn!$ql~)PxG8_liO&rS|j{7-=~WAKQ**XbxR&t zyLFYWi79+lxoEP)Q_tD^etO)tKXBySl%VMWzH0v#uG?a{jsJ>M_%Ed^I_ErI2hUp{ zbhPWq8+#@&s`!9ZSVO^#% zb48Nzn^oUf`tqZGD%GDUWjP*xbhTiyq_j-!i{Lp?`<1@hDg?zW6zZ)0Ah=ms@zUPw z|7PdEQLm7+{H0anAU##4Mn^S_!L-^vENJ4j-{%c%*qxLW6PfI)Onp8+KB{qbK1Z#` zs|y=7Up}(G{HR<8e0 literal 0 HcmV?d00001 diff --git a/testdata/x509/dsa3072-2.der b/testdata/x509/dsa3072-2.der new file mode 100644 index 0000000000000000000000000000000000000000..fe1d75118b727b602acca0071e3643f154ca35e4 GIT binary patch literal 1613 zcmXqLV)Ha;VtvQN$F)G88o62g$JWa0KKRmEYOoLX%jZQpqrIT^(S63a7_vmHt^ic5=9^^)^*4dldm4b2UWj4TWc49!g~ zqQrR(Ees5e%%R)?a#a({F@q*%OEz|`Hji@_EQ|(C%sNa>jExLUVclofm;PDRIeWV8 zT4iCCI?)d1{c`#|O@fRmJDyvtzbP;WB>eqJxz1TD`(hvpe6kP>!CA| z`-22lm6{(|`>fB0N9EP+mJ3I^s%KPd|JiMD>h!q;pV*syt7bd3N4E90miD)b&8&Xo zeEG1c_o*P&TcT;%ZpjbN6kjn9+fnnv`Ok`jQ95C*UrThQMD=d+K5V}C%ksG1I?0a~ zElfEL)iRtvtgH>}%H`x=?!G&r= z14kD!@~kzw9JY&Nw?XtON2b!v)1m`EvA64aPJXDv7|h#b|EKV@&!Tr>TrF0$u1-Fe zPP4sGDM?s)b%jjv)pt{c%_?5537^OFP&R>=Ns-|^(}se5E8@RBiAs0xsy9eHwP4R> z`72M#Z>`;9v3?zcI#Uy4gIe)l#@@$FY8ts5_w`O2Oo=vBO)v^L@Z+Xt@g%RAlI6#j zZ8l%^|EWFK;@g$Sui4kS8&8q#+COPx!nVRo@4W8UnxE!(l;6kquJh0`)t26qXD63w zHoTq6dWGX*L>zNxmtWm0ZmwT5Wvh8^9b4--Rd3nqj!Q;rQ~fg5-c1i|(=bYqe(cE< zywv0ZgV4RtB{P{@cAgDgu$1FLhVr&8i_PEoi!WZiY~DYarZ4$Y@(er@&zAqb%EY*h zYky48x2?sA%Z$!k+W*xv*VLw1V!8qI4WmVp`ttJCw?*UTtJ}`^biAP(tWj<8e^r3` zGTuM+a&3M4+`g3bNAouPI>Yl!<>Mc(%z%wcJ}|~xd!6Y@&uMXSr$BtIL_X@GOE}l5|j)wn!S8IJMVX?&wj>deUn}1ndGeN`dOK>_xu06e_^^e zA8dTlz}&>x$^eQ8H-|4`u`2&(^3L!Kxn_`Ammw%I+uc0+-S(35w}&qNIu&$x>nc|b z{$SoIA#Tr|t{?1e_;&2|;$_#AHQ$yQSx8#Af zTUY6tn8H_;izZ7v^_;!$r^jvk14qtH37Q_@tM+f?q0Z_Lf}52UFYUel zZ+8A0^$JPLUs^Q|(obX3C_Osn0)f+k-3ecr%^-AP$7k;$&g)aT>lqZ(J|bJTjg zy0B66UuR}rR`jXtc=444m!pA; zXLT)k>pMHe@sPRVBDQsJHeK`8UKU;~z4y~({q@gY&Y1Eqs{5$q39J9lre3gi-jvQB zatNh}XZBz)aAi`co}hE9u`cSOx7*8Ae)`ewcBYX}%)GzH{x$ilGyAE#Ig=v8*)IZp iPnzp8Ge7-IE14eNbC6k?Gwy5JB^A3r2E_-qe*pk)L$|m9 literal 0 HcmV?d00001 diff --git a/testdata/x509/ec384-1.der b/testdata/x509/ec384-1.der new file mode 100644 index 0000000000000000000000000000000000000000..fdc7c92642a33b05ce22f19426529b60b401a98a GIT binary patch literal 544 zcmXqLVv;dvVqDC`$?zyEeCM6xg3k@O*f_M>JkHs&Ff$oc7;+nMvN4CUun9AT1{?Al z@ParTJZ%0&spQgNQKccT z0XImPn};PNwYbDk-awX(Q>)FR?K>|cC!?4^VtHnAwnJ$~acNPiUUGh}ft)z6p}C=v zk%ghTfuV^>lsK=Ug@K`wIg~qqE-Nz-U}Fb|5ECOCt9By`gA#KR155IyZ*8s%tdBqS z``j608SA26VUjHI+W1qE|LdvyA0J)iqW5Tj2fqu)E7v$Cac+fR-i?Y$`UdmYiO6kG z?#u|>oupP*_V(84u%|Mo8#k=^a3zU%`9}MygI~N_{wqB^qL7aqFU;u-1}RJiWs@(~ zWmz5fE$uBhWwb?JSPVNrf8eq_8{nZcWu&`ukb@WGN;CU-P3+ y#jL8f|6_Q#lS?jnI*Wn9t2*Z|QNkI4+S~aZjP+Vh^0tV~o4$o5xK!qm$3Xzg0i}lk literal 0 HcmV?d00001 diff --git a/testdata/x509/ec384-2.der b/testdata/x509/ec384-2.der new file mode 100644 index 0000000000000000000000000000000000000000..bea37b56388795cba6b1fef9a0d22c63a0d8ea70 GIT binary patch literal 544 zcmXqLVv;dvVqDC`$?$aY%x5CJzc>xJ*f_M>JkHs&Ff$ue7;+nMvN4CUun9AT1{?Al z@ParTJZ%0&spQgNQKccT z0XImPn};PNwYbDk-awX(Q>)FR?K>|cC!?4^VtHnAwnJ$~acNPiUUGh}ft)z6p}C=v zk%ghTfsv_slsK=Ug@K`wIg~qqE-Nz-U}Fb|5ECOCt9By`gA#KR155IyZ*8s%tdBqS z``j608SA26VUjHI+W1qE|LdvyA0J)iqW5Tj2fqu)E7v$Cac+fR-i?Y$`UdmYiO6kG z?#u|>oupP*_V(84u%|Mo8#k=^a3zU%`9}MygI~N_{wqB^qL7aqFU;u-1}RJi^^N@6 zx97?@^FBVXAWWmqb$)t%*r{~kyAID6o#{=V`>?1_%+s&y;LfO?Gw)d^iq|q3czFnY z+;xBJXX{OQnjulbbphKO?z-;Q6L@*_aGC$DJ(3oVD(6Z(Ty-OTftY#dr`9_MUXm{|-m4Y>_C*_cCF*o2uvgAMr& zctIQv9yb4?)b#v3LqP+6kN`UmM?ij2Nls#3ilL~1Fi41thbO|+z*4~{GpQ)CsM1j0 zK$eYDtIebBJ1-+AqnJQqd1i99Lup2FX;G?Pa(=FXoH(zcxuKDfg`v5Dv5`@fIIp3F zfuWH(luJ*ilo<%Hv4g{aiII&}yOD)Ki8+aZCHc~~HrEB#$DjIr?u@aFby2S{NtSqR z{He(Q_0;{3kFIjjd$hlU--Y9qYaEj}w?Z)QM#Ut3gZb-39^8`gZdlEk}wqkYxEFJ3MGl^z~Z$VU!&<_rddR3<})Gv$77i*IG?&8+ZDF7>@? zU}W=HE;=B7_l(HscuNkQ`?H&bW;jkVG*#)=UvWrv=5HngM~|AGw^2$Zsa?kx#g%7$ mzmz*uYjU_fPBX;>+=S@Y@Awc9&O)w85y}*84PL-xeYkkm_u3EgqcEv z4fzdtK^zVqHvgj3^!z+SK?8n}06PyyKz>n4PGVk)p{Ri{NQjGvCp06tv?x_a!80#e z&rrxf03^)K!x@raq!3(EnwDlLZy*b|j+0SLAhA3%IoqK$qqwvvRWCU|*Fa93*U;S1 zz{tqJ(8$EtAWEFq&;p4&(9UaOR6-6gMpg#qCPsb+gC<5UrY1&4hJ!y3v@O>*h-YC? zdvWiGIji8NULVa5*S;0364#C^eDnBum~2&h)$KV=Ax%^7J8qQwRX&5`TUFqPR+nFU zk~!w79-F0nYvuoQWrklfqXiRREr~I)%<5S4;F$cOvoosLyKhBC=XuR2KC=J9oEWFK zOD@GI-Rasmuk?Q6y??Dz}~od*xJy{^8VS=TF+aQW?u zkMAF8sk8MpCtSaCHtm!|<3_<44>j-TdjGi{WljF84qZDjbIyXk;?SkJw=~k(jg&&W ziz_recs=RxnB3ma41CMP z%*eoq975dS5MpG|dTL&Cv?)?^UEIQ*uQE74s+YQ$`}ViR8Qlz*VU!b)JG4h~yT{Yo zmH+O&DpTv2lUAP2u780$QP1lr>zf^gx}S6>7{wMl8m6DA-t2y__0K=6hc|d0W~^R# zu<5Ol-kLq#GBc7UZ%K67b!_H|O3tsv%U#v|daeph*yr~)_-nE2dOq_iiQ1AKwo|=@ zL#Ktju3I!E=XZUlzI#@VXygIjX@YlD{>NW0K5Z>4CB6Qk+_kHXYNwX%Z+4W}U?+5I zBjcN^SKm+b_}RQ=;nusRPfx70@4LQk5$8WR#MUc literal 0 HcmV?d00001 diff --git a/testdata/x509/rsa2048-2.der b/testdata/x509/rsa2048-2.der new file mode 100644 index 0000000000000000000000000000000000000000..9b73c8018d021d7ce44257153bae743ca607cfb0 GIT binary patch literal 872 zcmXqLVoot=V)9|)WSC~ra&UH)%RU2MHcqWJkGAi;jEvl@3mz6C)$Tt3y0(rE3-)n-?|X_S@toug|}AeQT37 z;ZnBT_3H1g72+K4V#HF)yI)>8S*jJsygzIgb5M)%yvO~OY3x%>8YMIK>6`nM+z5;{ zn`Hd3@8ng*r>{azIpsLAOkX89=W4*K>C0;iKClTeTv>G@KDf_b{^P|R;SVJaJYRbI ze%k(&dGTJ3@7$b}{A!uP?k48G6|G*`U3b6d;QE@*>2epR%v&J+U`g{!-JL&<{jB>} znx`*up2t4@m}NtcO_0Eng7fR%t1dVyz+|cUzI{QJ-rLn>IX#ONvyLsBGVh=4+FkcN z{$6Fcvg*_Qx~-)iZqFy2K7S{S`RF%;EcYY+ch6|IhwB`CGe44vnUR4JIfS^uA;ieg z%@wnHQ+K4O|KA-~K3(JS^Iyx@#hK-yY_|68MdpXWuChOyru?lFXkzKR-MX6lXRG2B zt&EQ1g>UC_=bKjjb7x!KSl;lPQ`|lK(yx<3Ptu<`27G;(wrnDEd*mcj*%=if-{<`0 z*PieDUCeym%k>Rqd~D`|uLy@E|!SI4VVeLvy(`|i;!g&w7UPDw&nU!Nh^~*T#7{q OTSAkrB^XAlE&%{_c2Vd6 literal 0 HcmV?d00001 diff --git a/testdata/x509/rsa4096-1.der b/testdata/x509/rsa4096-1.der new file mode 100644 index 0000000000000000000000000000000000000000..9d6d253dc16a0e30d13ddb81f70c91ac43bbbb6b GIT binary patch literal 1384 zcmXqLVofnmzl#%81Pe8cdr{aEPy4#8( znbO$@=ZN~*CQZ1UDQNfh<%_iZGWS)->mG%3-xS^K@LTr##i}hw{@WjY^z{mB=&Pg; ztXF#Me)-vPpA$c#aeA%al$L-;hEj_}zgcW5+TY}tGf|~JK2G&~R;tOa+q%t%S?4Xi zGF495vV`UC|0z0d<*Qb7UINZ)Tr-FZjyMAbv-;e|$@h zaS6Zh^G%v;sUcr8mu-gd4&IHE4j;IFkt3c_;bW3amFC7}pLRJNezW=*|4ysjF#$Gr zR)sk~F#G8#%{QhMvy~cs)>gBd)^PH8Cd>}-oGS&foB0g_6Qgqr`|+8sV|pKdPVDPrGe+d~#ey(N`vBMg~UY z6v7QoA&d+UHJDB=X*>Tz)#kFCYxG(XYk^lGpHCDO`%n5*Fz1xR#+zrux5q5>U8(=` z`zpCjGp=7bzW(6l%--+vD=z-)$nJDW_Vjrs^{%NVyU#LhhW%8{5W}@s)*g>JB4*>~ zvEV|rz5N39#TU<8N^j%I6AXy{w3T0Xy+fU7@TQEX$9^A|ezNT4a-T`Ep?g<+59)eq z82c%c=hy1y^cz;z226i9^{?Ol^Xk4#&OVQZc#%}|>2Gx2Ze6>maKVI?$Ljs_uU}Ap z6Z>(g@}yOo#)+Byk&%~ID5aj7Fz;gIkx5;KQ{7HK&1#*Wzsysw_`qcO=^Y!+?CE6q z`r6PU#&C7s%rn1k9LU{z@4E5vMV(sJ|G#GNvTb|1%Qx$2j=31yJ0s&Zk$)%IBOl&8 zYd$Gi{q6&=@2-!ow|w0=8+4CA3xN_5Gc}(wc6Wybg$YM3S|6|pL3u|X@ hcbuZow*0^?-iVvO%^DY|7P&0Q5Zi5^-F(}48UUwpM=1aR literal 0 HcmV?d00001 diff --git a/testdata/x509/rsa4096-2.der b/testdata/x509/rsa4096-2.der new file mode 100644 index 0000000000000000000000000000000000000000..8d83d8a803b9507ee4571cb661e4735c62c9fe70 GIT binary patch literal 1392 zcmXqLV$CsVVh&;AWH@3w@86#l9_n4PGVk)p{Ri{NQjGvC&JXgQo$!PsVK3i z(va7H8zjul!xEBOTw*A1APcvNlTl0{u{<+5+o3e0xU?u$FF8NgKu(<3(A?0-$il$D zz}(a*N}Siw0*O0-E^A^^LJlTIRtDxKCVmEkCMGVXCMHIP{p~4RM9WY9J$#fiVxI1_ z)hFWTFV=jyLhn=ObUv%szo!+R(4SR#x;5q5-+H}^oE}Xjy!`FoQckdjEA#xjdiaHE zN89r4b5DGIJF({Sa!U^ro+qnnerEe>_ZTPWJeuaL)1|n3ZlH;tjECPLp+DNHb2sqR zhuf^uQ#pV0hR8e1rN6=|m%R7gxLM>_x#p^OO{-=$hKHU`KuOj?i<*6AYft zwD3>76}z5)8PYmL=DQu6pd0z5$#{Ux&Hyq8s8WT{IXW6Y% z>K_q&b>_nRwvS$?GQ^2qtLRSBC};n#vv69i_wk5>=R(ZiO?Y(f;o0y~bxjW28;+CT zWE$si7TvfJav}8Yt*GYc`CVVHJxP>xJ7knzvOSvrUr0_(hL!fq4qk7r1-?fA=I65< zH@qT~*Q>Hi`CPGk(G0KB<%`dp$+v|&C zr*|&<_WNdo1TDhJswF4Q}g zSH!j1wp(pc#kZbJhg?V5ng2@l$&$N5e>km= z&$1VeZ)9R-WMD*2Aw1v|!pPtpob^Zg(u4g0GcH9>ZodD-km+RUsZAZ{d?zLJ?0fdW zUiE=r0>_`!j}vU4FP*Tmqj%@~U*1eAuUAOiNcz@fa{14vPfOl}EYEwq%H)aE=B&r9 zk9V0HJcxPAki5iprc!dikI1Vx9?KkkP@b9=x|H*P&W)@HyOwe)Y;%)7w@?FEKBOWP36tRcPyN`zr_L z-rm3P%+D9kcP&`hckS7$I@7cjGjE7Z*dWpPKVNv-qj%O~oV?4cQ{JRcc=z!87wgtc zR_!<1odR4hCmq!kuh}HytvbDO;gd~r^KQPqAY}b=pXpOAkv9iLG>^w~vo{IOl7A|) zCt?nJ*t07eOcyVIu=(J*Gkg+-&68%i^2e=pu6|Ud_Eu0(qO&c`Kjd#Vv$Ot8>pPVN z-CyN+&kJ_dTAR<0s9G?KtGjE(>7&PI1okz!tyb*{?cNjdwq&pIl0v)X2Mo7Jnl7wV zlUs5xh^dn$_W*Oy!ARfLjG>`ZB2*+#u6K|xl$v+_$=5uyluvgG>#tcgFBk3oo3o$A qMDw=J#8M95K!4$Q`Q1tFC7#B!mp=P1{G$F`>dqqhFS>SnQ!4<$Kv)t0 literal 0 HcmV?d00001 diff --git a/testdata/x509/rsa4096-3.der b/testdata/x509/rsa4096-3.der new file mode 100644 index 0000000000000000000000000000000000000000..77031eacd96de1bc14c517884eb6b67c1f57dbd3 GIT binary patch literal 1392 zcmXqLV$CsVVh&;AWcbx{F@e27iQj;ijZ>@5qwPB{BO@;>gF%HMw*e;`b0`a&FjHu- zAxNG%l!b$b&A%u$JwMM-(10H#%g)0QkY7}ilbDxcC~6=K65`_Fi7+*=RPf16DoQM> zG~_kl1_^WXu!N)*ml(<$$ii*nWE2xfEYD2Nb|}p#E-gycOU}`&Zq^qO|E_!TE51*u zo2IODOF7A=v;KE})`hgiN9C5E4ileh;CJu(g{E^-u6upfIlnAxQ@z{k68O-oiCv;@ z-fyR+2Ieh7)m#0&JN`E+eY(ptS1YLE%k&*z4m^lo`F@q)x5Gw(XB_6TA5@uIaOdG2 zHEZD=i>5H;HWodrPFrVqqI~1#=l;vpV|{1r$}Ub?d1hs#-(rI$nHN^S4e;L0wEO1$ z_H_OBu-8heUo2`~yx3uA7|-0Hx%k}=ZjsC8ZHL=ite0K-v{Uiie?Q3$b)}|f&R8y} zd-?vo=m{78&zjr)jwJq)*fjr1XE*n=P3d!|sNB37@KK}9=%Y;+BDTJ>!VZKAV{PP1y&XZtO5FFkcCxjEkV0L#g3B`>6M-+7+**=4l`L$U(OC$UUnk5=-GR_%@$McSgl&mQy?(^dwj&y12fN51w}Z^tc@== zT2u6FyCo;v`MdM7g{D0bijaTT`}Fw>ZKc~CCAGG(v$lS{74FC%uky8jSIE64W>{`|Pvz>3+CE4s>QM2_;=Kpw;?z)p>*=kYm8D|&9wtt$i zpm>V!pPZ%p%~V+L`tLTaWZUsuEFjbI-a2L>!Md&jW7n6vPWlGv%Ml3uFB+h0v8it3 z#z#$i-`f8-u8;MTV-eC4YuuK7q&rdJWO76B`4iJ`u%4PW^Fa=CE_1SE`nKy4wztIY z=>};Pwl16`9Nqok;`=FbA7=z>`WJ4Cn!Q!51*qB73{NOZ#{HDI^p;7;!BsA_uUUaSj&)|oB8|pj9J=d63O2_ z&N|L76(SJEIyvjuMAP=%Q!9IW*E@^cjnb%G_F2RsV&0zpkq5u*`f~N*y6-QOK2Bei zWpXBqwf5QrvCD7tPc66Lzw{<~wynzVwYG-+cA^)Gy%t8hz1sFB*n>YxOlI22qc!%Q z*UNwYbM9!=s(owETg*=2sGGN;1j66Zc0c%EWN4 zbt#_kd+OA~H$2u)*t1RZ!Q6{WKg;%I9@x+PX|<`J+C#(VzqMXXZO{K5Yb1Qjm20)5 qPE_g4-I+&EX=QwwH1EgC6QZpmufl~4`ltT<^yyIN+yz!F