Repository: novifinancial/opaque-ke
Pull request #77 (2020-11-03 21:46:27)

Start integrating the derivable SizedBytes
- just derive it on Key and KeyPair for now.

 Eliminates a complex derivation.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 2dab8d2..95d3d43 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -335,6 +335,26 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "generic-bytes"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6638d839bbd1cea640d8c5348dd82e0d545dbd364f3c2a251646eaf2ef0773b"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "generic-bytes-derive"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4257fe99e64e321c197164da8ae22f7a3c074a5b2519072866e2d38bc1ca59e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "getrandom"
 version = "0.1.14"
@@ -518,6 +538,8 @@ dependencies = [
  "displaydoc",
  "fiat-crypto",
  "generic-array",
+ "generic-bytes",
+ "generic-bytes-derive",
  "hex",
  "hkdf",
  "hmac 0.9.0",
diff --git a/Cargo.toml b/Cargo.toml
index 3f5cdc6..6eb7168 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,6 +22,8 @@ digest = "0.9.0"
 displaydoc = "0.1.7"
 fiat-crypto = { version = "0.1.5"}
 generic-array = "0.14.4"
+generic-bytes = { version = "0.1.0" }
+generic-bytes-derive = { version = "0.1.0" }
 hkdf = "0.9.0"
 hmac = "0.9.0"
 rand_core = "0.5.1"
diff --git a/src/errors.rs b/src/errors.rs
index 359ad13..7fb161f 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -10,6 +10,8 @@ use thiserror::Error;
 /// Represents an error in the manipulation of internal cryptographic data
 #[derive(Debug, Display, Error)]
 pub enum InternalPakeError {
+    /// Deserializing from a byte sequence failed
+    InvalidByteSequence,
     /// Invalid length for {name}: expected {len}, but is actually {actual_len}.
     SizeError {
         /// name
@@ -116,6 +118,24 @@ impl From<::std::convert::Infallible> for ProtocolError {
     }
 }
 
+impl From<generic_bytes::TryFromSizedBytesError> for InternalPakeError {
+    fn from(_: generic_bytes::TryFromSizedBytesError) -> Self {
+        InternalPakeError::InvalidByteSequence
+    }
+}
+
+impl From<generic_bytes::TryFromSizedBytesError> for PakeError {
+    fn from(e: generic_bytes::TryFromSizedBytesError) -> Self {
+        PakeError::CryptoError(e.into())
+    }
+}
+
+impl From<generic_bytes::TryFromSizedBytesError> for ProtocolError {
+    fn from(e: generic_bytes::TryFromSizedBytesError) -> Self {
+        PakeError::CryptoError(e.into()).into()
+    }
+}
+
 pub(crate) mod utils {
     use super::*;
 
diff --git a/src/group.rs b/src/group.rs
index de3e4d7..a04e812 100644
--- a/src/group.rs
+++ b/src/group.rs
@@ -17,6 +17,7 @@ use generic_array::{
     typenum::{U32, U64},
     ArrayLength, GenericArray,
 };
+
 use rand_core::{CryptoRng, RngCore};
 use std::ops::Mul;
 use zeroize::Zeroize;
diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs
index 3ab08b5..5194596 100644
--- a/src/key_exchange/tripledh.rs
+++ b/src/key_exchange/tripledh.rs
@@ -8,7 +8,7 @@ use crate::{
     errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError},
     hash::Hash,
     key_exchange::traits::{KeyExchange, ToBytes},
-    keypair::{KeyPair, SizedBytes},
+    keypair::{KeyPair, SizedBytesExt},
     serialization::serialize,
 };
 use digest::{Digest, FixedOutput};
@@ -16,6 +16,7 @@ use generic_array::{
     typenum::{Unsigned, U32},
     ArrayLength, GenericArray,
 };
+use generic_bytes::SizedBytes;
 use hkdf::Hkdf;
 use hmac::{Hmac, Mac, NewMac};
 use rand_core::{CryptoRng, RngCore};
diff --git a/src/keypair.rs b/src/keypair.rs
index 0c6bd8b..711175b 100644
--- a/src/keypair.rs
+++ b/src/keypair.rs
@@ -5,12 +5,10 @@
 
 //! Contains the keypair types that must be supplied for the OPAQUE API
 
-use crate::errors::{utils::check_slice_size, InternalPakeError};
-use generic_array::{
-    sequence::Concat,
-    typenum::{Sum, Unsigned, U32},
-    ArrayLength, GenericArray,
-};
+use crate::errors::InternalPakeError;
+use generic_array::{typenum::U32, GenericArray};
+use generic_bytes::{SizedBytes, TryFromSizedBytesError};
+use generic_bytes_derive::{SizedBytes, TryFromForSizedBytes};
 #[cfg(test)]
 use proptest::prelude::*;
 #[cfg(test)]
@@ -20,25 +18,18 @@ use std::convert::TryInto;
 use std::fmt::Debug;
 use x25519_dalek::{PublicKey, StaticSecret};
 
-use std::convert::TryFrom;
+use std::ops::Deref;
 
-use std::ops::{Add, Deref};
-
-/// A trait for sized key material that can be represented within a fixed byte
-/// array size, used to represent our DH key types
-pub trait SizedBytes: Sized + PartialEq {
-    /// The typed representation of the byte length
-    type Len: ArrayLength<u8>;
-
-    /// Converts this sized key material to a `GenericArray` of the same
-    /// size. One can convert this to a `&[u8]` with `GenericArray::as_slice()`
-    /// but the size information is then lost from the type.
-    fn to_arr(&self) -> GenericArray<u8, Self::Len>;
-
-    /// How to parse such sized material from a byte slice.
-    fn from_bytes(key_bytes: &[u8]) -> Result<Self, InternalPakeError>;
+// Pub(crate) convenience extension trait of SizedBytes for our purposes
+pub(crate) trait SizedBytesExt: SizedBytes {
+    fn from_bytes(bytes: &[u8]) -> Result<Self, TryFromSizedBytesError> {
+        <Self as SizedBytes>::from_arr(GenericArray::from_slice(bytes))
+    }
 }
 
+// blanket implementation
+impl<T> SizedBytesExt for T where T: SizedBytes {}
+
 /// A Keypair trait with public-private verification
 pub trait KeyPair: Sized {
     /// The single key representation must have a specific byte size itself
@@ -92,62 +83,9 @@ trait KeyPairExt: KeyPair + Debug {
 #[cfg(test)]
 impl<KP> KeyPairExt for KP where KP: KeyPair + Debug {}
 
-/// This assumes you have defined a SizedBytes instance for a `T`, and defines:
-/// - an `impl TryFrom<&[u8b], Error = InternalPakeError>` for a non-generic `T`
-/// - an `fn to_bytes(&self) -> Vec<u8>` in an `impl T` block
-///
-/// Because SizedBytes has a strong notion of size, and TryFrom/to_bytes does
-/// not, it's better to use this macro than the one above, where possible.
-macro_rules! try_from_and_to_bytes_using_sized_bytes {
-    ($sized_type: ident) => {
-        impl TryFrom<&[u8]> for $sized_type {
-            type Error = InternalPakeError;
-
-            fn try_from(bytes: &[u8]) -> Result<Self, InternalPakeError> {
-                <$sized_type as SizedBytes>::from_bytes(bytes)
-            }
-        }
-
-        #[allow(dead_code)]
-        impl $sized_type {
-            fn to_bytes(&self) -> Vec<u8> {
-                self.to_arr().to_vec()
-            }
-        }
-    };
-}
-
-/// This is a blanket implementation of SizedBytes for any instance of KeyPair
-/// with any length of keys. This encodes that we serialize the public key
-/// first, followed by the private key in binary formats (and expect it in this
-/// order upon decoding).
-impl<T, KP> SizedBytes for KP
-where
-    T: SizedBytes + Clone,
-    KP: KeyPair<Repr = T> + PartialEq,
-    T::Len: Add<T::Len>,
-    Sum<T::Len, T::Len>: ArrayLength<u8>,
-{
-    type Len = Sum<T::Len, T::Len>;
-
-    fn to_arr(&self) -> GenericArray<u8, Self::Len> {
-        let private = self.private().to_arr();
-        let public = self.public().to_arr();
-        public.concat(private)
-    }
-
-    fn from_bytes(key_bytes: &[u8]) -> Result<Self, InternalPakeError> {
-        let checked_bytes =
-            check_slice_size(key_bytes, <Self::Len as Unsigned>::to_usize(), "key_bytes")?;
-        let single_key_len = <<KP::Repr as SizedBytes>::Len as Unsigned>::to_usize();
-        let public = <T as SizedBytes>::from_bytes(&checked_bytes[..single_key_len])?;
-        let private = <T as SizedBytes>::from_bytes(&checked_bytes[single_key_len..])?;
-        KP::new(public, private)
-    }
-}
-
 /// A minimalist key type built around [u8;32]
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, TryFromForSizedBytes)]
+#[ErrorType = "::generic_bytes::TryFromSizedBytesError"]
 #[repr(transparent)]
 pub struct Key(Vec<u8>);
 
@@ -166,17 +104,14 @@ impl SizedBytes for Key {
         GenericArray::clone_from_slice(&self.0[..])
     }
 
-    fn from_bytes(key_bytes: &[u8]) -> Result<Self, InternalPakeError> {
-        let checked_bytes =
-            check_slice_size(key_bytes, <Self::Len as Unsigned>::to_usize(), "key_bytes")?;
-        Ok(Key(checked_bytes.to_vec()))
+    fn from_arr(key_bytes: &GenericArray<u8, Self::Len>) -> Result<Self, TryFromSizedBytesError> {
+        Ok(Key(key_bytes.to_vec()))
     }
 }
 
-try_from_and_to_bytes_using_sized_bytes!(Key);
-
 /// A representation of an X25519 keypair according to RFC7748
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, SizedBytes, TryFromForSizedBytes)]
+#[ErrorType = "::generic_bytes::TryFromSizedBytesError"]
 pub struct X25519KeyPair {
     pk: Key,
     sk: Key,
diff --git a/src/lib.rs b/src/lib.rs
index 87cdba5..7505315 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,7 +40,7 @@
 //! ## Setup
 //! To setup the protocol, the server begins by generating a static keypair:
 //! ```
-//! # use opaque_ke::keypair::{KeyPair, X25519KeyPair, SizedBytes};
+//! # use opaque_ke::keypair::{KeyPair, X25519KeyPair};
 //! # use opaque_ke::errors::ProtocolError;
 //! # use opaque_ke::ciphersuite::CipherSuite;
 //! # struct Default;
@@ -72,7 +72,7 @@
 //! # use opaque_ke::{
 //! #   errors::ProtocolError,
 //! #   opaque::ServerRegistration,
-//! #   keypair::{KeyPair, X25519KeyPair, SizedBytes},
+//! #   keypair::{KeyPair, X25519KeyPair},
 //! #   slow_hash::NoOpHash,
 //! # };
 //! # use opaque_ke::ciphersuite::CipherSuite;
@@ -102,7 +102,7 @@
 //! # use opaque_ke::{
 //! #   errors::ProtocolError,
 //! #   opaque::ClientRegistration,
-//! #   keypair::{KeyPair, X25519KeyPair, SizedBytes},
+//! #   keypair::{KeyPair, X25519KeyPair},
 //! #   slow_hash::NoOpHash,
 //! # };
 //! # use opaque_ke::ciphersuite::CipherSuite;
@@ -135,7 +135,7 @@
 //! # use opaque_ke::{
 //! #   errors::ProtocolError,
 //! #   opaque::{ClientRegistration, ServerRegistration},
-//! #   keypair::{KeyPair, X25519KeyPair, SizedBytes},
+//! #   keypair::{KeyPair, X25519KeyPair},
 //! #   slow_hash::NoOpHash,
 //! # };
 //! # use opaque_ke::ciphersuite::CipherSuite;
@@ -169,7 +169,7 @@
 //! # use opaque_ke::{
 //! #   errors::ProtocolError,
 //! #   opaque::{ClientRegistration, ServerRegistration},
-//! #   keypair::{KeyPair, X25519KeyPair, SizedBytes},
+//! #   keypair::{KeyPair, X25519KeyPair},
 //! #   slow_hash::NoOpHash,
 //! # };
 //! # use opaque_ke::ciphersuite::CipherSuite;
@@ -211,7 +211,7 @@
 //! # use opaque_ke::{
 //! #   errors::ProtocolError,
 //! #   opaque::{ClientRegistration, ServerRegistration, ServerLogin, LoginThirdMessage},
-//! #   keypair::{KeyPair, X25519KeyPair, SizedBytes},
+//! #   keypair::{KeyPair, X25519KeyPair},
 //! #   slow_hash::NoOpHash,
 //! # };
 //! # use opaque_ke::ciphersuite::CipherSuite;
@@ -241,7 +241,7 @@
 //! # use opaque_ke::{
 //! #   errors::ProtocolError,
 //! #   opaque::{ClientRegistration, ServerRegistration, ClientLogin, LoginThirdMessage},
-//! #   keypair::{KeyPair, X25519KeyPair, SizedBytes},
+//! #   keypair::{KeyPair, X25519KeyPair},
 //! #   slow_hash::NoOpHash,
 //! # };
 //! # use opaque_ke::ciphersuite::CipherSuite;
@@ -285,7 +285,7 @@
 //! # use opaque_ke::{
 //! #   errors::ProtocolError,
 //! #   opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage},
-//! #   keypair::{KeyPair, X25519KeyPair, SizedBytes},
+//! #   keypair::{KeyPair, X25519KeyPair},
 //! #   slow_hash::NoOpHash,
 //! # };
 //! # use opaque_ke::ciphersuite::CipherSuite;
@@ -340,7 +340,7 @@
 //! # use opaque_ke::{
 //! #   errors::ProtocolError,
 //! #   opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage},
-//! #   keypair::{KeyPair, X25519KeyPair, SizedBytes},
+//! #   keypair::{KeyPair, X25519KeyPair},
 //! #   slow_hash::NoOpHash,
 //! # };
 //! # use opaque_ke::ciphersuite::CipherSuite;
diff --git a/src/opaque.rs b/src/opaque.rs
index d016d1f..676ef94 100644
--- a/src/opaque.rs
+++ b/src/opaque.rs
@@ -15,7 +15,7 @@ use crate::{
     group::Group,
     hash::Hash,
     key_exchange::traits::{KeyExchange, ToBytes},
-    keypair::{KeyPair, SizedBytes},
+    keypair::{KeyPair, SizedBytesExt},
     oprf,
     serialization::{
         serialize, tokenize, u8_to_credential_type, CredentialType, ProtocolMessageType,
@@ -23,6 +23,7 @@ use crate::{
     slow_hash::SlowHash,
 };
 use generic_array::{typenum::Unsigned, GenericArray};
+use generic_bytes::SizedBytes;
 use rand_core::{CryptoRng, RngCore};
 use std::collections::HashMap;
 use std::{convert::TryFrom, marker::PhantomData};
@@ -640,7 +641,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
     /// # Example
     ///
     /// ```
-    /// use opaque_ke::{opaque::{ClientRegistration, ServerRegistration}, keypair::{X25519KeyPair, SizedBytes}};
+    /// use opaque_ke::{opaque::{ClientRegistration, ServerRegistration}, keypair::X25519KeyPair};
     /// # use opaque_ke::errors::ProtocolError;
     /// # use opaque_ke::keypair::KeyPair;
     /// use rand_core::{OsRng, RngCore};
@@ -822,7 +823,7 @@ where
     /// # Example
     ///
     /// ```
-    /// use opaque_ke::{opaque::*, keypair::{X25519KeyPair, SizedBytes}};
+    /// use opaque_ke::{opaque::*, keypair::X25519KeyPair};
     /// # use opaque_ke::errors::ProtocolError;
     /// use rand_core::{OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
@@ -898,7 +899,7 @@ where
     /// # Example
     ///
     /// ```
-    /// use opaque_ke::{opaque::*, keypair::{KeyPair, X25519KeyPair, SizedBytes}};
+    /// use opaque_ke::{opaque::*, keypair::{KeyPair, X25519KeyPair}};
     /// # use opaque_ke::errors::ProtocolError;
     /// use rand_core::{OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;


Test patch:
diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs
index 66e5aad..4015014 100644
--- a/src/serialization/tests.rs
+++ b/src/serialization/tests.rs
@@ -11,12 +11,13 @@ use crate::{
         traits::{KeyExchange, ToBytes},
         tripledh::{TripleDH, NONCE_LEN},
     },
-    keypair::{KeyPair, SizedBytes, X25519KeyPair},
+    keypair::{KeyPair, X25519KeyPair},
     opaque::*,
     serialization::{serialize, ProtocolMessageType},
 };
 
 use curve25519_dalek::ristretto::RistrettoPoint;
+use generic_bytes::SizedBytes;
 use proptest::{collection::vec, prelude::*};
 use rand_core::{OsRng, RngCore};
 
diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs
index 848afb0..b4f5b36 100644
--- a/src/tests/opaque_ke_test.rs
+++ b/src/tests/opaque_ke_test.rs
@@ -8,13 +8,14 @@ use crate::{
     errors::*,
     group::Group,
     key_exchange::tripledh::{TripleDH, NONCE_LEN},
-    keypair::{Key, KeyPair, SizedBytes, X25519KeyPair},
+    keypair::{Key, KeyPair, X25519KeyPair},
     opaque::*,
     slow_hash::NoOpHash,
     tests::mock_rng::CycleRng,
 };
 use curve25519_dalek::edwards::EdwardsPoint;
 use generic_array::GenericArray;
+use generic_bytes::SizedBytes;
 use rand_core::{OsRng, RngCore};
 use serde_json::Value;
 use std::convert::TryFrom;