From 8f4784c530f2d018f3d51a51356d9c960d9e64d6 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sun, 28 Jan 2018 20:56:24 -0800 Subject: [PATCH] Now supports ECDSA signatures. --- src/algident.rs | 24 +++++++++---------- src/error.rs | 3 ++- src/lib.rs | 57 ++++++++++++++++++++++++++++++-------------- src/publickey.rs | 62 ++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 105 insertions(+), 41 deletions(-) diff --git a/src/algident.rs b/src/algident.rs index f64dc41..0c155ad 100644 --- a/src/algident.rs +++ b/src/algident.rs @@ -6,7 +6,7 @@ use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1}; pub enum HashAlgorithm { SHA1, SHA224, SHA256, SHA384, SHA512 } #[derive(Clone,Copy,Debug,PartialEq)] -pub enum PublicKeyInfo { RSA, DSA, EC } +pub enum PublicKeyInfo { RSA, DSA, ECDSA } #[derive(Clone,Debug,PartialEq)] pub struct AlgorithmIdentifier { @@ -80,31 +80,31 @@ pub fn decode_algorithm_ident(x: &ASN1Block) if oid == oid!(1,2,840,10045,4,1) { return Ok(AlgorithmIdentifier { hash: HashAlgorithm::SHA1, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }); } if oid == oid!(1,2,840,10045,4,3,1) { return Ok(AlgorithmIdentifier { hash: HashAlgorithm::SHA224, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }); } if oid == oid!(1,2,840,10045,4,3,2) { return Ok(AlgorithmIdentifier { hash: HashAlgorithm::SHA256, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }); } if oid == oid!(1,2,840,10045,4,3,3) { return Ok(AlgorithmIdentifier { hash: HashAlgorithm::SHA384, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }); } if oid == oid!(1,2,840,10045,4,3,4) { return Ok(AlgorithmIdentifier { hash: HashAlgorithm::SHA512, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }); } // if oid == oid!(2,16,840,1,101,3,4,2,1) { @@ -232,7 +232,7 @@ fn encode_algorithm_ident(c: ASN1Class, x: &AlgorithmIdentifier) Err(SigAlgEncodeError::InvalidHash), } } - PublicKeyInfo::EC => { + PublicKeyInfo::ECDSA=> { match x.hash { HashAlgorithm::SHA1 => { let o = oid!(1,2,840,10045,4,1); @@ -320,31 +320,31 @@ mod test { const EC1: AlgorithmIdentifier = AlgorithmIdentifier{ hash: HashAlgorithm::SHA1, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }; const EC224: AlgorithmIdentifier = AlgorithmIdentifier{ hash: HashAlgorithm::SHA224, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }; const EC256: AlgorithmIdentifier = AlgorithmIdentifier{ hash: HashAlgorithm::SHA256, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }; const EC384: AlgorithmIdentifier = AlgorithmIdentifier{ hash: HashAlgorithm::SHA384, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }; const EC512: AlgorithmIdentifier = AlgorithmIdentifier{ hash: HashAlgorithm::SHA512, - algo: PublicKeyInfo::EC + algo: PublicKeyInfo::ECDSA }; impl Arbitrary for AlgorithmIdentifier { diff --git a/src/error.rs b/src/error.rs index 93db02e..9be285d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,7 +17,8 @@ pub enum X509ParseError { IllFormedAlgoInfo, IllFormedKey, IllFormedEverything, IllegalStringValue, NoSerialNumber, InvalidDSAInfo, ItemNotFound, UnknownAlgorithm, InvalidRSAKey, InvalidDSAKey, InvalidSignatureData, - InvalidSignatureHash, InvalidECDSAKey, + InvalidSignatureHash, InvalidECDSAKey, InvalidPointForm, + CompressedPointUnsupported, KeyNotFound, SignatureNotFound, SignatureVerificationFailed } diff --git a/src/lib.rs b/src/lib.rs index cd96f66..356c4de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ use error::X509ParseError; use misc::{X509Serial,X509Version,decode_signature}; use publickey::X509PublicKey; use sha1::Sha1; -use sha2::{Sha224,Sha256}; +use sha2::{Sha224,Sha256,Sha384,Sha512}; use simple_asn1::{ASN1Block,FromASN1,der_decode,from_der}; use simple_rsa::{SIGNING_HASH_SHA1, SIGNING_HASH_SHA224, SIGNING_HASH_SHA256, SIGNING_HASH_SHA384, SIGNING_HASH_SHA512}; @@ -103,7 +103,6 @@ fn decode_certificate(x: &ASN1Block) pub fn parse_x509(buffer: &[u8]) -> Result { let blocks = from_der(&buffer[..])?; - println!("blocks: {:?}", blocks); match blocks.first() { None => Err(X509ParseError::NotEnoughData), @@ -160,6 +159,28 @@ fn check_signature(alg: &AlgorithmIdentifier, Err(X509ParseError::InvalidSignatureHash) } } + (PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(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) } @@ -185,22 +206,22 @@ mod tests { parse_x509(&buffer) } -// #[test] -// fn rsa_tests() { -// assert!(can_parse("test/rsa2048-1.der").is_ok()); -// assert!(can_parse("test/rsa2048-2.der").is_ok()); -// assert!(can_parse("test/rsa4096-1.der").is_ok()); -// assert!(can_parse("test/rsa4096-2.der").is_ok()); -// assert!(can_parse("test/rsa4096-3.der").is_ok()); -// } -// -// #[test] -// fn dsa_tests() { -// assert!(can_parse("test/dsa2048-1.der").is_ok()); -// assert!(can_parse("test/dsa2048-2.der").is_ok()); -// assert!(can_parse("test/dsa3072-1.der").is_ok()); -// assert!(can_parse("test/dsa3072-2.der").is_ok()); -// } + #[test] + fn rsa_tests() { + assert!(can_parse("test/rsa2048-1.der").is_ok()); + assert!(can_parse("test/rsa2048-2.der").is_ok()); + assert!(can_parse("test/rsa4096-1.der").is_ok()); + assert!(can_parse("test/rsa4096-2.der").is_ok()); + assert!(can_parse("test/rsa4096-3.der").is_ok()); + } + + #[test] + fn dsa_tests() { + assert!(can_parse("test/dsa2048-1.der").is_ok()); + assert!(can_parse("test/dsa2048-2.der").is_ok()); + assert!(can_parse("test/dsa3072-1.der").is_ok()); + assert!(can_parse("test/dsa3072-2.der").is_ok()); + } #[test] fn ecc_tests() { diff --git a/src/publickey.rs b/src/publickey.rs index b4c97d7..12b80aa 100644 --- a/src/publickey.rs +++ b/src/publickey.rs @@ -1,9 +1,10 @@ use error::X509ParseError; use num::{BigInt,BigUint}; -use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1, +use num::bigint::Sign; +use simple_asn1::{ASN1Block,ASN1Class,FromASN1,OID,ToASN1, der_decode,der_encode,from_der}; use simple_dsa::dsa::{DSAParameters,DSAPublicKey}; -use simple_dsa::ecdsa::{EllipticCurve,ECCPublicKey}; +use simple_dsa::ecdsa::{EllipticCurve,ECCPoint,ECCPublicKey}; use simple_rsa::RSAPublicKey; #[derive(Clone,Debug,PartialEq)] @@ -66,7 +67,6 @@ fn decode_public_key(block: &ASN1Block) } if id == oid!(1,2,840,10045,2,1) { if let Some(alginfo) = malginfo { - println!("alginfo: {:?}", alginfo); let curve = decode_ecc_info(&alginfo)?; let key = decode_ecc_key(&info[1], &curve)?; return Ok(X509PublicKey::ECDSA(key)); @@ -128,7 +128,7 @@ fn encode_dsa_pubkey(c: ASN1Class, key: &DSAPublicKey) fn encode_ecc_pubkey(c: ASN1Class, key: &ECCPublicKey) -> Result { - let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10045,4,1)); + let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10045,2,1)); let objparams = encode_ecc_info(c, &key.curve)?; let objkey = encode_ecc_key(c, key)?; let headinfo = ASN1Block::Sequence(c, 0, vec![objoid, objparams]); @@ -187,7 +187,26 @@ fn decode_dsa_key(b: &ASN1Block, params: &DSAParameters) fn encode_ecc_key(c: ASN1Class, k: &ECCPublicKey) -> Result { - unimplemented!() + let mut bytes = vec![4]; + let (xsign, mut xbytes) = k.Q.x.to_bytes_be(); + let (ysign, mut ybytes) = k.Q.y.to_bytes_be(); + let goalsize = (k.curve.n.bits() + 7) / 8; + + if (xsign != Sign::Plus) || (ysign != Sign::Plus) { + return Err(X509ParseError::InvalidPointForm); + } + + while xbytes.len() < goalsize { + xbytes.insert(0,0); + } + while ybytes.len() < goalsize { + ybytes.insert(0,0); + } + + bytes.append(&mut xbytes); + bytes.append(&mut ybytes); + + Ok(ASN1Block::BitString(c, 0, (goalsize + 1) * 8, bytes)) } fn decode_ecc_key(b: &ASN1Block, curve: &EllipticCurve) @@ -195,10 +214,22 @@ fn decode_ecc_key(b: &ASN1Block, curve: &EllipticCurve) { match b { &ASN1Block::BitString(_, _, size, ref vec) if size % 8 == 0 => { - println!("vec[{}/{}]: {:?}", size, vec.len(), vec); - let vals = from_der(&vec); - println!("vals: {:?}", vals); - unimplemented!() + match vec.split_first() { + Some((&2, _)) => + Err(X509ParseError::CompressedPointUnsupported), + Some((&3, _)) => + Err(X509ParseError::CompressedPointUnsupported), + Some((&4, input)) => { + let bytesize = (curve.n.bits() + 7) / 8; + let (xbytes, ybytes) = input.split_at(bytesize); + let x = BigInt::from_bytes_be(Sign::Plus, xbytes); + let y = BigInt::from_bytes_be(Sign::Plus, ybytes); + let point = ECCPoint::new(curve, x, y)?; + Ok(ECCPublicKey::new(curve, &point)) + } + _ => + Err(X509ParseError::InvalidPointForm) + } } _ => Err(X509ParseError::InvalidECDSAKey) @@ -265,6 +296,7 @@ fn decode_biguint(b: &ASN1Block) -> Result { #[cfg(test)] mod test { use simple_dsa::dsa::{DSAParameterSize,DSAKeyPair}; + use simple_dsa::ecdsa::{ECCKeyPair}; use simple_rsa::RSAKeyPair; use super::*; @@ -309,12 +341,22 @@ mod test { #[test] fn ecdsa_public_key_tests() { + let curve256 = EllipticCurve::p256(); ecc_info_test(&EllipticCurve::p192()); ecc_info_test(&EllipticCurve::p224()); - ecc_info_test(&EllipticCurve::p256()); + ecc_info_test(&curve256); ecc_info_test(&EllipticCurve::p384()); ecc_info_test(&EllipticCurve::p521()); for _ in 0..NUM_TESTS { + let pair = ECCKeyPair::generate(&curve256); + let public = pair.public; + let block = encode_ecc_key(ASN1Class::Universal, &public).unwrap(); + let public2 = decode_ecc_key(&block, &curve256).unwrap(); + assert_eq!(public, public2); + let x509public = X509PublicKey::ECDSA(public); + let block2 = encode_public_key(ASN1Class::Universal, &x509public).unwrap(); + let x509public2 = decode_public_key(&block2).unwrap(); + assert_eq!(x509public, x509public2); } } }