diff --git a/src/ecdsa/curve.rs b/src/ecdsa/curve.rs index 977af21..c0416b9 100644 --- a/src/ecdsa/curve.rs +++ b/src/ecdsa/curve.rs @@ -1,30 +1,54 @@ use cryptonum::signed::{I192,I256,I384,I576}; use cryptonum::unsigned::{Decoder}; use cryptonum::unsigned::{U192,U256,U384,U576}; +use ecdsa::point::Point; use std::fmt::Debug; +/// Elliptic curves must implement this trait in order to work with the rest +/// of the ECDSA system. I've included instances for the core NIST curves +/// used in most systems, but this could be extended without issues. +/// (Eventually the curves defined here should actually be extended in +/// interesting ways to make the math faster, but we haven't gotten there +/// yet.) #[allow(non_snake_case)] pub trait EllipticCurve { + /// The unsigned numeric type that fits constants for this curve. type Unsigned : Clone; + /// The signed numeric type that fits constants for this curve. type Signed : Clone + Debug + PartialEq; + /// The type of a point on the curve + type Point; + /// The size of the curve in bits. fn size() -> usize; + /// The `p` value for the curve. fn p() -> Self::Unsigned; + /// The `p` value for the curve. fn n() -> Self::Unsigned; + /// The seed value for the curve. fn SEED() -> Self::Unsigned; + /// The `c` value for the curve. fn c() -> Self::Unsigned; + /// The `a` value for the curve. fn a() -> Self::Unsigned; + /// The `b` value for the curve. fn b() -> Self::Unsigned; + /// The `x` coordinate of the base point for the curve. fn Gx() -> Self::Signed; + /// The `y` coordinate of the base point for the curve. fn Gy() -> Self::Signed; + /// Generate a point for the curve given the provided values. + fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point; } +/// NIST curve P-192 (FIPS 186-4, page 101-102), a.k.a. secp192r1 from RFC5480 #[derive(Debug,PartialEq)] -pub enum P192 {} +pub struct P192 {} impl EllipticCurve for P192 { type Unsigned = U192; type Signed = I192; + type Point = Point; fn size() -> usize { 192 @@ -61,14 +85,20 @@ impl EllipticCurve for P192 { fn Gy() -> I192 { I192::from(U192::from([0x73f977a11e794811, 0x631011ed6b24cdd5, 0x07192b95ffc8da78])) } + + fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point { + Point::{ x: I192::from(x), y: I192::from(y) } + } } +/// NIST curve P-224 (FIPS 186-4, page 102), a.k.a. secp224r1 from RFC5480 #[derive(Debug,PartialEq)] -pub enum P224 {} +pub struct P224 {} impl EllipticCurve for P224 { type Unsigned = U256; type Signed = I256; + type Point = Point; fn size() -> usize { 224 @@ -144,14 +174,20 @@ impl EllipticCurve for P224 { 0x85, 0x00, 0x7e, 0x34 ])) } + + fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point { + Point::{ x: I256::from(x), y: I256::from(y) } + } } +/// NIST curve P-256 (FIPS 186-4, page 102-103), a.k.a. secp256r1 from RFC5480 #[derive(Debug,PartialEq)] -pub enum P256 {} +pub struct P256 {} impl EllipticCurve for P256 { type Signed = I256; type Unsigned = U256; + type Point = Point; fn size() -> usize { 256 @@ -228,14 +264,20 @@ impl EllipticCurve for P256 { 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5 ])) } + + fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point { + Point::{ x: I256::from(x), y: I256::from(y) } + } } +/// NIST curve P-384 (FIPS 186-4, page 103-104), a.k.a. secp384r1 from RFC5480 #[derive(Debug,PartialEq)] -pub enum P384 {} +pub struct P384 {} impl EllipticCurve for P384 { type Signed = I384; type Unsigned = U384; + type Point = Point; fn size() -> usize { 384 @@ -325,14 +367,20 @@ impl EllipticCurve for P384 { 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f ])) } + + fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point { + Point::{ x: I384::from(x), y: I384::from(y) } + } } +/// NIST curve P-521 (FIPS 186-4, page 104), a.k.a. secp521r1 from RFC5480 #[derive(Debug,PartialEq)] -pub enum P521 {} +pub struct P521 {} impl EllipticCurve for P521 { type Signed = I576; type Unsigned = U576; + type Point = Point; fn size() -> usize { 521 @@ -443,4 +491,8 @@ impl EllipticCurve for P521 { 0x66, 0x50 ])) } + + fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point { + Point::{ x: I576::from(x), y: I576::from(y) } + } } diff --git a/src/ecdsa/mod.rs b/src/ecdsa/mod.rs index f977d1e..f0877b5 100644 --- a/src/ecdsa/mod.rs +++ b/src/ecdsa/mod.rs @@ -1,25 +1,28 @@ -pub mod curve; -pub mod point; -pub mod private; -pub mod public; +mod curve; +pub(crate) mod point; +mod private; +mod public; use cryptonum::signed::{I192,I256,I384,I576}; use cryptonum::unsigned::{CryptoNum,Decoder}; use cryptonum::unsigned::{U192,U256,U384,U576}; use rand::Rng; use rand::distributions::Standard; -use self::curve::{EllipticCurve,P192,P224,P256,P384,P521}; +pub use self::curve::{EllipticCurve,P192,P224,P256,P384,P521}; use self::point::{ECCPoint,Point}; pub use self::private::{ECDSAPrivate,ECCPrivateKey}; pub use self::public::{ECDSAPublic,ECCPublicKey}; pub use self::public::{ECDSADecodeErr,ECDSAEncodeErr}; use super::KeyPair; +/// An ECDSA key pair for the given curve. pub struct ECDSAKeyPair { pub public: ECCPublicKey, pub private: ECCPrivateKey } +/// A generic ECDSA key pair that implements one of our known curves, for cases +/// when you're not sure which one you're going to have. pub enum ECDSAPair { P192(ECCPublicKey,ECCPrivateKey), P224(ECCPublicKey,ECCPrivateKey), @@ -57,7 +60,12 @@ macro_rules! generate_impl { ECDSAKeyPair{ public, private } } } + impl ECDSAKeyPair<$curve> { + /// Generate a fresh ECDSA key pair for this curve, given the + /// provided random number generator. THIS MUST BE A CRYPTO + /// STRONG RNG. If it's not, then you're going to generate weak + /// keys and the crypto gremlins will get you. pub fn generate(rng: &mut G) -> ECDSAKeyPair<$curve> { loop { diff --git a/src/ecdsa/private.rs b/src/ecdsa/private.rs index dd44d10..2fe98b4 100644 --- a/src/ecdsa/private.rs +++ b/src/ecdsa/private.rs @@ -7,6 +7,7 @@ use ecdsa::point::{ECCPoint,Point}; use hmac::{Hmac,Mac}; use std::fmt; +/// A private key for the given curve. #[derive(PartialEq)] pub struct ECCPrivateKey { pub(crate) d: Curve::Unsigned @@ -19,6 +20,7 @@ impl fmt::Debug for ECCPrivateKey { } } +/// A generic private key. pub enum ECDSAPrivate { P192(ECCPrivateKey), P224(ECCPrivateKey), @@ -32,11 +34,14 @@ macro_rules! generate_privates ($curve: ident, $base: ident, $sig: ident, $dbl: ident, $quad: ident) => { impl ECCPrivateKey<$curve> { + /// Generate a new private key using the given private scalar. pub fn new(d: $base) -> ECCPrivateKey<$curve> { ECCPrivateKey{ d } } - + + /// Sign the given message with the current key, using the hash provided + /// in the type. pub fn sign(&self, m: &[u8]) -> DSASignature<$base> where Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset, diff --git a/src/ecdsa/public.rs b/src/ecdsa/public.rs index b3fb82c..cd72a4f 100644 --- a/src/ecdsa/public.rs +++ b/src/ecdsa/public.rs @@ -8,11 +8,14 @@ use hmac::{Hmac,Mac}; use simple_asn1::{ASN1Block,ASN1Class,ASN1DecodeErr,ASN1EncodeErr,FromASN1,ToASN1}; use std::cmp::min; +/// An ECDSA public key for the given curve. #[derive(Debug,PartialEq)] pub struct ECCPublicKey { pub(crate) q: Point } +/// A generic ECDSA public key, when you're not sure which curve you're +/// going to get. pub enum ECDSAPublic { P192(ECCPublicKey), P224(ECCPublicKey), @@ -21,6 +24,8 @@ pub enum ECDSAPublic { P521(ECCPublicKey), } +/// An error that can occur when encoding an ECDSA public key as an ASN.1 +/// object. pub enum ECDSAEncodeErr { ASN1EncodeErr(ASN1EncodeErr), XValueNegative, YValueNegative @@ -32,6 +37,8 @@ impl From for ECDSAEncodeErr { } } +/// An error that can occur when decoding an ECDSA public key from an +/// ASN.1 blob. #[derive(Debug)] pub enum ECDSADecodeErr { ASN1DecodeErr(ASN1DecodeErr), @@ -50,11 +57,14 @@ macro_rules! public_impl { ($curve: ident, $un: ident, $si: ident) => { impl ECCPublicKey<$curve> { + /// Generate a new public key object from the given public point. pub fn new(q: Point<$curve>) -> ECCPublicKey<$curve> { ECCPublicKey{ q } } + /// Returns true if the given message matches the given signature, + /// assuming the provided hash function. pub fn verify(&self, m: &[u8], sig: &DSASignature<$un>) -> bool where Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset, diff --git a/src/ssh/ecdsa.rs b/src/ssh/ecdsa.rs index c6017c9..690cc9a 100644 --- a/src/ssh/ecdsa.rs +++ b/src/ssh/ecdsa.rs @@ -1,8 +1,6 @@ -use cryptonum::signed::*; use cryptonum::unsigned::*; use ecdsa::{ECDSAPair,ECDSAPublic,ECCPublicKey,ECDSAPrivate,ECCPrivateKey}; -use ecdsa::curve::{P256,P384,P521}; -use ecdsa::point::Point; +use ecdsa::{EllipticCurve,P256,P384,P521}; use std::io::{Read,Write}; use ssh::errors::{SSHKeyParseError,SSHKeyRenderError}; use ssh::frame::*; @@ -33,7 +31,7 @@ impl SSHKey for ECDSAPair { } let x = U256::from_bytes(&val[1..33]); let y = U256::from_bytes(&val[33..]); - let p = Point::{ x: I256::from(x), y: I256::from(y) }; + let p = P256::new_point(x, y); let pbl = ECCPublicKey::::new(p); Ok(ECDSAPublic::P256(pbl)) } @@ -44,7 +42,7 @@ impl SSHKey for ECDSAPair { } let x = U384::from_bytes(&val[1..49]); let y = U384::from_bytes(&val[49..]); - let p = Point::{ x: I384::from(x), y: I384::from(y) }; + let p = P384::new_point(x, y); let pbl = ECCPublicKey::::new(p); Ok(ECDSAPublic::P384(pbl)) } @@ -55,7 +53,7 @@ impl SSHKey for ECDSAPair { } let x = U576::from_bytes(&val[1..67]); let y = U576::from_bytes(&val[67..]); - let p = Point::{ x: I576::from(x), y: I576::from(y) }; + let p = P521::new_point(x, y); let pbl = ECCPublicKey::::new(p); Ok(ECDSAPublic::P521(pbl)) } diff --git a/src/x509/publickey.rs b/src/x509/publickey.rs index 12b64d1..2742001 100644 --- a/src/x509/publickey.rs +++ b/src/x509/publickey.rs @@ -2,7 +2,7 @@ use cryptonum::unsigned::{U3072,U2048,U1024,U256,U192}; use dsa::{DSAPublic,DSAPublicKey,DSAParameters}; use dsa::{L3072N256,L2048N256,L2048N224,L1024N160}; use ecdsa::{ECDSAEncodeErr,ECDSAPublic,ECCPublicKey}; -use ecdsa::curve::{P192,P224,P256,P384,P521}; +use ecdsa::{P192,P224,P256,P384,P521}; use num::BigUint; use rsa::RSAPublic; use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1,