Files
opaque-vx/src/keypair.rs
T

391 lines
11 KiB
Rust
Raw Normal View History

2020-06-05 09:35:14 -07:00
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
//! Contains the keypair types that must be supplied for the OPAQUE API
#![allow(unsafe_code)]
2020-11-03 21:44:00 +00:00
use crate::errors::InternalPakeError;
2021-01-25 13:20:36 -08:00
use crate::group::Group;
#[cfg(test)]
use generic_array::typenum::Unsigned;
2021-07-06 13:27:13 +02:00
use generic_array::{ArrayLength, GenericArray};
2020-11-03 21:44:00 +00:00
use generic_bytes::{SizedBytes, TryFromSizedBytesError};
#[cfg(test)]
use proptest::prelude::*;
#[cfg(test)]
use rand::{rngs::StdRng, SeedableRng};
2021-02-11 18:10:48 -08:00
use rand::{CryptoRng, RngCore};
use std::fmt::Debug;
2020-11-03 21:44:00 +00:00
use std::ops::Deref;
2021-04-29 16:22:23 -05:00
use zeroize::Zeroize;
2020-06-05 09:35:14 -07:00
2021-02-11 18:10:48 -08:00
/// Convenience extension trait of SizedBytes
pub trait SizedBytesExt: SizedBytes {
/// Convert from bytes
2020-11-03 21:44:00 +00:00
fn from_bytes(bytes: &[u8]) -> Result<Self, TryFromSizedBytesError> {
<Self as SizedBytes>::from_arr(GenericArray::from_slice(bytes))
}
2020-06-05 09:35:14 -07:00
}
2020-11-03 21:44:00 +00:00
// blanket implementation
impl<T> SizedBytesExt for T where T: SizedBytes {}
2020-06-05 09:35:14 -07:00
/// A Keypair trait with public-private verification
2021-07-06 14:17:44 +02:00
#[cfg_attr(
feature = "serialize",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
2021-07-07 13:28:31 +02:00
pub struct KeyPair<G: Group> {
2021-07-06 13:27:13 +02:00
pk: PublicKey<G>,
sk: PrivateKey<G>,
2021-01-25 13:20:36 -08:00
}
2020-06-05 09:35:14 -07:00
2021-06-22 15:48:36 +02:00
impl_clone_for!(
2021-07-07 13:28:31 +02:00
struct KeyPair<G: Group>,
2021-07-06 13:27:13 +02:00
[pk, sk],
2021-06-22 15:48:36 +02:00
);
impl_debug_eq_hash_for!(
2021-07-07 13:28:31 +02:00
struct KeyPair<G: Group>,
2021-07-06 13:27:13 +02:00
[pk, sk],
2021-06-22 15:48:36 +02:00
);
// This can't be derived because of the use of a phantom parameter
2021-07-07 13:28:31 +02:00
impl<G: Group> Zeroize for KeyPair<G> {
fn zeroize(&mut self) {
self.pk.zeroize();
self.sk.zeroize();
}
}
2021-07-07 13:28:31 +02:00
impl<G: Group> Drop for KeyPair<G> {
fn drop(&mut self) {
self.zeroize();
}
}
2021-01-25 13:20:36 -08:00
impl<G: Group> KeyPair<G> {
2020-06-05 09:35:14 -07:00
/// The public key component
2021-07-06 13:27:13 +02:00
pub fn public(&self) -> &PublicKey<G> {
2021-01-25 13:20:36 -08:00
&self.pk
}
2020-06-05 09:35:14 -07:00
/// The private key component
2021-07-06 13:27:13 +02:00
pub fn private(&self) -> &PrivateKey<G> {
2021-01-25 13:20:36 -08:00
&self.sk
}
2020-06-05 09:35:14 -07:00
/// Generating a random key pair given a cryptographic rng
2021-01-25 13:20:36 -08:00
pub(crate) fn generate_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let sk = G::random_nonzero_scalar(rng);
2021-07-06 13:32:02 +02:00
let sk_bytes = G::scalar_as_bytes(sk);
let pk = G::base_point().mult_by_slice(&sk_bytes);
2021-01-25 13:20:36 -08:00
Self {
2021-07-07 13:28:31 +02:00
pk: PublicKey(Key(pk.to_arr())),
sk: PrivateKey(Key(sk_bytes)),
2021-01-25 13:20:36 -08:00
}
}
2020-06-05 09:35:14 -07:00
/// Obtaining a public key from secret bytes. At all times, we should have
/// &public_from_private(self.private()) == self.public()
2021-07-06 13:27:13 +02:00
pub(crate) fn public_from_private(bytes: &PrivateKey<G>) -> PublicKey<G> {
2021-01-25 13:20:36 -08:00
let bytes_data = GenericArray::<u8, G::ScalarLen>::from_slice(&bytes.0[..]);
2021-07-07 13:28:31 +02:00
PublicKey(Key(G::base_point().mult_by_slice(bytes_data).to_arr()))
2021-01-25 13:20:36 -08:00
}
2020-06-05 09:35:14 -07:00
/// Check whether a public key is valid. This is meant to be applied on
/// material provided through the network which fits the key
/// representation (i.e. can be mapped to a curve point), but presents
/// some risk - e.g. small subgroup check
2021-07-06 13:27:13 +02:00
pub(crate) fn check_public_key(key: PublicKey<G>) -> Result<PublicKey<G>, InternalPakeError> {
2021-01-25 13:20:36 -08:00
G::from_element_slice(GenericArray::from_slice(&key.0)).map(|_| key)
}
2020-06-05 09:35:14 -07:00
/// Computes the diffie hellman function on a public key and private key
2021-06-15 10:48:29 +02:00
pub(crate) fn diffie_hellman(
2021-07-06 13:27:13 +02:00
pk: PublicKey<G>,
sk: PrivateKey<G>,
2021-06-15 10:48:29 +02:00
) -> Result<Vec<u8>, InternalPakeError> {
2021-01-25 13:20:36 -08:00
let pk_data = GenericArray::<u8, G::ElemLen>::from_slice(&pk.0[..]);
2021-06-12 23:18:08 -07:00
let point = G::from_element_slice(pk_data)?;
2021-01-25 13:20:36 -08:00
let secret_data = GenericArray::<u8, G::ScalarLen>::from_slice(&sk.0[..]);
2021-06-12 23:18:08 -07:00
Ok(G::mult_by_slice(&point, secret_data).to_arr().to_vec())
2021-01-25 13:20:36 -08:00
}
/// Obtains a KeyPair from a slice representing the private key
2021-01-25 13:20:36 -08:00
pub fn from_private_key_slice(input: &[u8]) -> Result<Self, InternalPakeError> {
2021-07-07 13:28:31 +02:00
let sk = PrivateKey(Key(GenericArray::clone_from_slice(input)));
let pk = Self::public_from_private(&sk);
2021-07-06 14:17:44 +02:00
Ok(Self { pk, sk })
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
2021-07-06 13:27:13 +02:00
(self.pk.as_ptr(), G::ElemLen::to_usize()),
(self.sk.as_ptr(), G::ScalarLen::to_usize()),
]
}
}
#[cfg(test)]
2021-01-25 13:20:36 -08:00
impl<G: Group + Debug> KeyPair<G> {
/// Test-only strategy returning a proptest Strategy based on
/// generate_random
fn uniform_keypair_strategy() -> BoxedStrategy<Self> {
// The no_shrink is because keypairs should be fixed -- shrinking would cause a different
// keypair to be generated, which appears to not be very useful.
any::<[u8; 32]>()
.prop_filter_map("valid random keypair", |seed| {
let mut rng = StdRng::from_seed(seed);
2021-01-25 13:20:36 -08:00
Some(Self::generate_random(&mut rng))
})
.no_shrink()
.boxed()
}
2020-06-05 09:35:14 -07:00
}
2021-02-11 18:10:48 -08:00
/// A minimalist key type built around a \[u8; 32\]
2021-07-07 13:28:31 +02:00
#[cfg_attr(
feature = "serialize",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
2020-06-05 09:35:14 -07:00
#[repr(transparent)]
2021-07-07 13:28:31 +02:00
pub struct Key<L: ArrayLength<u8>>(GenericArray<u8, L>);
impl<L: ArrayLength<u8>> Clone for Key<L> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<L: ArrayLength<u8>> Debug for Key<L> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Key").field(&self.0).finish()
}
}
impl<L: ArrayLength<u8>> Eq for Key<L> {}
impl<L: ArrayLength<u8>> PartialEq for Key<L> {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
2020-06-05 09:35:14 -07:00
2021-07-07 13:28:31 +02:00
impl<L: ArrayLength<u8>> std::hash::Hash for Key<L> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
// This can't be derived because of the use of a generic parameter
impl<L: ArrayLength<u8>> Zeroize for Key<L> {
fn zeroize(&mut self) {
self.0.zeroize();
}
}
impl<L: ArrayLength<u8>> Drop for Key<L> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<L: ArrayLength<u8>> Deref for Key<L> {
type Target = GenericArray<u8, L>;
2020-06-05 09:35:14 -07:00
fn deref(&self) -> &Self::Target {
&self.0
}
}
2021-06-15 10:48:29 +02:00
// Don't make it implement SizedBytes so that it's not constructible outside of this module.
2021-07-07 13:28:31 +02:00
impl<L: ArrayLength<u8>> Key<L> {
fn to_arr(&self) -> GenericArray<u8, L> {
2021-06-15 10:48:29 +02:00
GenericArray::clone_from_slice(&self.0[..])
}
#[allow(clippy::unnecessary_wraps)]
2021-07-07 13:28:31 +02:00
fn from_arr(key_bytes: &GenericArray<u8, L>) -> Result<Self, TryFromSizedBytesError> {
Ok(Key(key_bytes.to_owned()))
2021-06-15 10:48:29 +02:00
}
}
/// Wrapper around a Key to enforce that it's a private one.
2021-06-22 15:48:36 +02:00
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
2021-06-15 10:48:29 +02:00
#[repr(transparent)]
2021-07-07 13:28:31 +02:00
pub struct PrivateKey<G: Group>(Key<G::ScalarLen>);
2021-07-06 13:27:13 +02:00
impl_clone_for!(
2021-07-07 13:28:31 +02:00
tuple PrivateKey<G: Group>,
[0],
2021-07-06 13:27:13 +02:00
);
impl_debug_eq_hash_for!(
2021-07-07 13:28:31 +02:00
tuple PrivateKey<G: Group>,
[0],
2021-07-06 13:27:13 +02:00
);
2021-06-15 10:48:29 +02:00
2021-07-06 13:27:13 +02:00
// This can't be derived because of the use of a phantom parameter
2021-07-07 13:28:31 +02:00
impl<G: Group> Zeroize for PrivateKey<G> {
2021-07-06 13:27:13 +02:00
fn zeroize(&mut self) {
2021-07-07 13:28:31 +02:00
self.0.zeroize();
2021-07-06 13:27:13 +02:00
}
}
2021-07-07 13:28:31 +02:00
impl<G: Group> Drop for PrivateKey<G> {
2021-07-06 13:27:13 +02:00
fn drop(&mut self) {
self.zeroize();
}
}
2021-07-07 13:28:31 +02:00
impl<G: Group> Deref for PrivateKey<G> {
type Target = Key<G::ScalarLen>;
2021-06-15 10:48:29 +02:00
fn deref(&self) -> &Self::Target {
2021-07-07 13:28:31 +02:00
&self.0
2021-07-06 13:27:13 +02:00
}
}
impl<G: Group> SizedBytes for PrivateKey<G> {
type Len = G::ScalarLen;
2020-06-05 09:35:14 -07:00
fn to_arr(&self) -> GenericArray<u8, Self::Len> {
2021-07-07 13:28:31 +02:00
self.0.to_arr()
2020-06-05 09:35:14 -07:00
}
2020-11-03 21:44:00 +00:00
fn from_arr(key_bytes: &GenericArray<u8, Self::Len>) -> Result<Self, TryFromSizedBytesError> {
2021-07-07 13:28:31 +02:00
Ok(PrivateKey(Key::from_arr(key_bytes)?))
2021-06-15 10:48:29 +02:00
}
}
/// Wrapper around a Key to enforce that it's a public one.
2021-06-22 15:48:36 +02:00
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
2021-06-15 10:48:29 +02:00
#[repr(transparent)]
2021-07-07 13:28:31 +02:00
pub struct PublicKey<G: Group>(Key<G::ElemLen>);
2021-06-15 10:48:29 +02:00
2021-07-06 13:27:13 +02:00
impl_clone_for!(
2021-07-07 13:28:31 +02:00
tuple PublicKey<G: Group>,
[0],
2021-07-06 13:27:13 +02:00
);
impl_debug_eq_hash_for!(
2021-07-07 13:28:31 +02:00
tuple PublicKey<G: Group>,
[0],
2021-07-06 13:27:13 +02:00
);
2021-07-07 13:28:31 +02:00
// This can't be derived because of the use of a generic parameter
impl<G: Group> Zeroize for PublicKey<G> {
2021-07-06 13:27:13 +02:00
fn zeroize(&mut self) {
2021-07-07 13:28:31 +02:00
self.0.zeroize();
2021-07-06 13:27:13 +02:00
}
}
2021-07-07 13:28:31 +02:00
impl<G: Group> Drop for PublicKey<G> {
2021-07-06 13:27:13 +02:00
fn drop(&mut self) {
self.zeroize();
}
}
2021-07-07 13:28:31 +02:00
impl<G: Group> Deref for PublicKey<G> {
type Target = Key<G::ElemLen>;
2021-06-15 10:48:29 +02:00
fn deref(&self) -> &Self::Target {
2021-07-07 13:28:31 +02:00
&self.0
2021-06-15 10:48:29 +02:00
}
}
2021-07-06 13:27:13 +02:00
impl<G: Group> SizedBytes for PublicKey<G> {
type Len = G::ElemLen;
2021-06-15 10:48:29 +02:00
fn to_arr(&self) -> GenericArray<u8, Self::Len> {
2021-07-07 13:28:31 +02:00
self.0.to_arr()
2021-06-15 10:48:29 +02:00
}
fn from_arr(key_bytes: &GenericArray<u8, Self::Len>) -> Result<Self, TryFromSizedBytesError> {
2021-07-07 13:28:31 +02:00
Ok(PublicKey(Key::from_arr(key_bytes)?))
2020-06-05 09:35:14 -07:00
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::*;
2021-01-25 13:20:36 -08:00
use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::typenum::Unsigned;
use rand::rngs::OsRng;
use std::slice::from_raw_parts;
#[test]
fn test_zeroize_key() -> Result<(), ProtocolError> {
2021-07-06 13:27:13 +02:00
let key_len = <RistrettoPoint as Group>::ElemLen::to_usize();
2021-07-07 13:28:31 +02:00
let mut key =
Key::<<RistrettoPoint as Group>::ElemLen>(GenericArray::clone_from_slice(&vec![
1u8;
key_len
]));
let ptr = key.as_ptr();
key.zeroize();
let bytes = unsafe { from_raw_parts(ptr, key_len) };
assert!(bytes.iter().all(|&x| x == 0));
Ok(())
}
#[test]
fn test_zeroize_keypair() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let mut keypair = KeyPair::<RistrettoPoint>::generate_random(&mut rng);
let ptrs = keypair.as_byte_ptrs();
keypair.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
proptest! {
#[test]
2021-01-25 13:20:36 -08:00
fn test_ristretto_check(kp in KeyPair::<RistrettoPoint>::uniform_keypair_strategy()) {
let pk = kp.public();
2021-01-25 13:20:36 -08:00
prop_assert!(KeyPair::<RistrettoPoint>::check_public_key(pk.clone()).is_ok());
}
#[test]
2021-01-25 13:20:36 -08:00
fn test_ristretto_pub_from_priv(kp in KeyPair::<RistrettoPoint>::uniform_keypair_strategy()) {
let pk = kp.public();
let sk = kp.private();
2021-01-25 13:20:36 -08:00
prop_assert_eq!(&KeyPair::<RistrettoPoint>::public_from_private(sk), pk);
}
2020-06-12 18:46:08 -04:00
#[test]
2021-01-25 13:20:36 -08:00
fn test_ristretto_dh(kp1 in KeyPair::<RistrettoPoint>::uniform_keypair_strategy(),
kp2 in KeyPair::<RistrettoPoint>::uniform_keypair_strategy()) {
2020-06-12 18:46:08 -04:00
2021-01-25 13:20:36 -08:00
let dh1 = KeyPair::<RistrettoPoint>::diffie_hellman(kp1.public().clone(), kp2.private().clone())?;
let dh2 = KeyPair::<RistrettoPoint>::diffie_hellman(kp2.public().clone(), kp1.private().clone())?;
2020-06-12 18:46:08 -04:00
2021-01-25 13:20:36 -08:00
prop_assert_eq!(dh1, dh2);
2020-06-12 18:46:08 -04:00
}
#[test]
2021-01-25 13:20:36 -08:00
fn test_private_key_slice(kp in KeyPair::<RistrettoPoint>::uniform_keypair_strategy()) {
let sk_bytes = kp.private().to_vec();
2021-01-25 13:20:36 -08:00
let kp2 = KeyPair::<RistrettoPoint>::from_private_key_slice(&sk_bytes)?;
let kp2_private_bytes = kp2.private().to_vec();
prop_assert_eq!(sk_bytes, kp2_private_bytes);
}
}
}