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 0000000..ac8ae31 Binary files /dev/null and b/testdata/x509/dsa2048-1.der differ diff --git a/testdata/x509/dsa2048-2.der b/testdata/x509/dsa2048-2.der new file mode 100644 index 0000000..a71af80 Binary files /dev/null and b/testdata/x509/dsa2048-2.der differ diff --git a/testdata/x509/dsa3072-1.der b/testdata/x509/dsa3072-1.der new file mode 100644 index 0000000..dde4134 Binary files /dev/null and b/testdata/x509/dsa3072-1.der differ diff --git a/testdata/x509/dsa3072-2.der b/testdata/x509/dsa3072-2.der new file mode 100644 index 0000000..fe1d751 Binary files /dev/null and b/testdata/x509/dsa3072-2.der differ diff --git a/testdata/x509/ec384-1.der b/testdata/x509/ec384-1.der new file mode 100644 index 0000000..fdc7c92 Binary files /dev/null and b/testdata/x509/ec384-1.der differ diff --git a/testdata/x509/ec384-2.der b/testdata/x509/ec384-2.der new file mode 100644 index 0000000..bea37b5 Binary files /dev/null and b/testdata/x509/ec384-2.der differ diff --git a/testdata/x509/ec384-3.der b/testdata/x509/ec384-3.der new file mode 100644 index 0000000..2917289 Binary files /dev/null and b/testdata/x509/ec384-3.der differ diff --git a/testdata/x509/rsa2048-1.der b/testdata/x509/rsa2048-1.der new file mode 100644 index 0000000..17107db Binary files /dev/null and b/testdata/x509/rsa2048-1.der differ diff --git a/testdata/x509/rsa2048-2.der b/testdata/x509/rsa2048-2.der new file mode 100644 index 0000000..9b73c80 Binary files /dev/null and b/testdata/x509/rsa2048-2.der differ diff --git a/testdata/x509/rsa4096-1.der b/testdata/x509/rsa4096-1.der new file mode 100644 index 0000000..9d6d253 Binary files /dev/null and b/testdata/x509/rsa4096-1.der differ diff --git a/testdata/x509/rsa4096-2.der b/testdata/x509/rsa4096-2.der new file mode 100644 index 0000000..8d83d8a Binary files /dev/null and b/testdata/x509/rsa4096-2.der differ diff --git a/testdata/x509/rsa4096-3.der b/testdata/x509/rsa4096-3.der new file mode 100644 index 0000000..77031ea Binary files /dev/null and b/testdata/x509/rsa4096-3.der differ