Files
opaque-vx/src/keypair.rs
T

521 lines
17 KiB
Rust
Raw Normal View History

2023-05-22 23:04:26 -07:00
// Copyright (c) Meta Platforms, Inc. and affiliates.
2020-06-05 09:35:14 -07:00
//
2023-05-22 23:04:26 -07:00
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
2021-12-03 14:38:11 -08:00
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
2023-05-22 23:04:26 -07:00
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
2020-06-05 09:35:14 -07:00
//! Contains the keypair types that must be supplied for the OPAQUE API
#![allow(unsafe_code)]
2022-02-25 07:13:22 +01:00
use derive_where::derive_where;
2025-05-19 22:56:25 +02:00
use digest::{Output, OutputSizeUser};
2021-07-06 13:27:13 +02:00
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, Rng};
2020-06-05 09:35:14 -07:00
2025-05-19 22:56:25 +02:00
use crate::ciphersuite::CipherSuite;
use crate::errors::ProtocolError;
2025-05-19 22:56:25 +02:00
use crate::key_exchange::group::Group;
use crate::key_exchange::shared::DiffieHellman;
use crate::key_exchange::sigma_i::{Message, MessageBuilder, SignatureProtocol};
use crate::serialization::SliceExt;
2022-01-06 06:19:02 +01:00
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(
2022-01-04 00:50:40 +01:00
feature = "serde",
2022-04-02 01:10:00 +02:00
derive(serde::Deserialize, serde::Serialize),
2023-02-04 22:25:41 +01:00
serde(bound(
deserialize = "G::Pk: serde::Deserialize<'de>, SK: serde::Deserialize<'de>",
serialize = "G::Pk: serde::Serialize, SK: serde::Serialize"
2023-02-04 22:25:41 +01:00
))
2021-07-06 14:17:44 +02:00
)]
2022-02-25 07:13:22 +01:00
#[derive_where(Clone)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk, SK)]
2025-05-19 22:56:25 +02:00
pub struct KeyPair<G: Group, SK: Clone = PrivateKey<G>> {
pk: PublicKey<G>,
2025-05-05 13:12:54 +02:00
sk: SK,
2021-01-25 13:20:36 -08:00
}
2020-06-05 09:35:14 -07:00
2025-05-19 22:56:25 +02:00
impl<G: Group, SK: Clone> KeyPair<G, SK> {
/// Creates a new [`KeyPair`] from the given keys.
2025-05-19 22:56:25 +02:00
pub fn new(sk: SK, pk: PublicKey<G>) -> Self {
Self { pk, sk }
}
2020-06-05 09:35:14 -07:00
/// The public key component
2025-05-19 22:56:25 +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
2025-05-05 13:12:54 +02:00
pub fn private(&self) -> &SK {
2021-01-25 13:20:36 -08:00
&self.sk
}
}
2025-05-19 22:56:25 +02:00
impl<G: Group> KeyPair<G> {
pub(crate) fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
2025-05-19 22:56:25 +02:00
let sk = G::random_sk(rng);
2025-07-17 22:15:30 +02:00
let pk = G::public_key(&sk);
2022-01-04 00:50:40 +01:00
Self {
2022-02-25 07:13:22 +01:00
pk: PublicKey(pk),
sk: PrivateKey(sk),
2022-01-04 00:50:40 +01:00
}
}
2021-08-17 05:11:53 +02:00
2025-05-19 22:56:25 +02:00
/// Generating a random key pair given a cryptographic rng
pub(crate) fn derive_random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
2025-05-19 22:56:25 +02:00
let mut scalar_bytes = GenericArray::<_, <G as Group>::SkLen>::default();
rng.fill_bytes(&mut scalar_bytes);
let sk = G::derive_scalar(scalar_bytes).unwrap();
2025-07-17 22:15:30 +02:00
let pk = G::public_key(&sk);
2025-05-19 22:56:25 +02:00
Self {
pk: PublicKey(pk),
sk: PrivateKey(sk),
}
}
2020-06-05 09:35:14 -07:00
}
2021-06-15 10:48:29 +02:00
/// Wrapper around a Key to enforce that it's a private one.
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "G::Sk: serde::Deserialize<'de>",
serialize = "G::Sk: serde::Serialize"
))
)]
2022-04-02 01:10:00 +02:00
#[derive_where(Clone, ZeroizeOnDrop)]
2025-05-19 22:56:25 +02:00
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Sk)]
pub struct PrivateKey<G: Group>(G::Sk);
impl<G: Group> PrivateKey<G> {
pub(crate) fn new(key: G::Sk) -> Self {
Self(key)
}
2021-07-06 13:27:13 +02:00
/// Returns public key from private key
2025-05-19 22:56:25 +02:00
pub fn public_key(&self) -> PublicKey<G> {
2025-07-17 22:15:30 +02:00
PublicKey(G::public_key(&self.0))
}
2025-06-25 00:17:29 +02:00
/// Serializes this private key to a fixed-length byte array.
pub fn serialize(&self) -> GenericArray<u8, G::SkLen> {
2025-07-17 22:15:30 +02:00
G::serialize_sk(&self.0)
}
2021-07-20 11:49:37 +02:00
/// Creates a [`PrivateKey`] from the given bytes.
2025-05-19 22:56:25 +02:00
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
Self::deserialize_take(&mut input)
}
pub(crate) fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
G::deserialize_take_sk(input).map(Self)
}
}
2025-05-19 22:56:25 +02:00
impl<G: Group> PrivateKey<G>
where
2025-05-19 22:56:25 +02:00
G::Sk: DiffieHellman<G>,
{
/// Diffie-Hellman key exchange implementation
2025-05-19 22:56:25 +02:00
pub(crate) fn ke_diffie_hellman(&self, pk: &PublicKey<G>) -> GenericArray<u8, G::PkLen> {
2025-07-17 22:15:30 +02:00
self.0.diffie_hellman(&pk.0)
}
}
2025-05-19 22:56:25 +02:00
impl<G: Group> PrivateKey<G> {
/// Private-key signing implementation
pub(crate) fn sign<
R: CryptoRng + Rng,
2025-05-19 22:56:25 +02:00
CS: CipherSuite,
SIG: SignatureProtocol<Group = G>,
KE: Group,
>(
&self,
rng: &mut R,
message: &Message<CS, KE>,
) -> (SIG::Signature, SIG::VerifyState<CS, KE>) {
SIG::sign(&self.0, rng, message)
}
}
/// A trait to facilitate
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
2025-05-19 22:56:25 +02:00
pub trait PrivateKeySerialization<G: Group>: Clone {
/// Custom error type that can be passed down to `ProtocolError::Custom`
type Error;
/// Serialization size in bytes.
type Len: ArrayLength;
/// Serialization into bytes
2025-05-19 22:56:25 +02:00
fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len>;
/// Deserialization from bytes
2025-06-25 00:17:29 +02:00
///
/// The deserialized bytes must be taken from `bytes`.
2025-05-19 22:56:25 +02:00
fn deserialize_take_key_pair(
2025-06-25 00:17:29 +02:00
bytes: &mut &[u8],
2025-05-19 22:56:25 +02:00
) -> Result<KeyPair<G, Self>, ProtocolError<Self::Error>>;
}
2025-05-19 22:56:25 +02:00
impl<G: Group> PrivateKeySerialization<G> for PrivateKey<G> {
2021-08-12 06:25:07 +02:00
type Error = core::convert::Infallible;
2025-05-19 22:56:25 +02:00
type Len = G::SkLen;
2021-07-20 11:49:37 +02:00
2025-05-19 22:56:25 +02:00
fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len> {
key_pair.private().serialize()
}
2025-05-19 22:56:25 +02:00
fn deserialize_take_key_pair(input: &mut &[u8]) -> Result<KeyPair<G, Self>, ProtocolError> {
let sk = PrivateKey::deserialize_take(input)?;
let pk = sk.public_key();
Ok(KeyPair::new(sk, pk))
2022-04-02 01:10:00 +02:00
}
}
2021-06-15 10:48:29 +02:00
/// Wrapper around a Key to enforce that it's a public one.
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "G::Pk: serde::Deserialize<'de>",
serialize = "G::Pk: serde::Serialize"
))
)]
2025-07-17 22:15:30 +02:00
#[derive_where(Clone)]
2025-05-19 22:56:25 +02:00
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk)]
pub struct PublicKey<G: Group + ?Sized>(G::Pk);
2021-06-15 10:48:29 +02:00
2025-05-19 22:56:25 +02:00
impl<G: Group> PublicKey<G> {
2021-08-17 05:11:53 +02:00
/// Convert from bytes
2025-05-19 22:56:25 +02:00
pub fn deserialize(mut key_bytes: &[u8]) -> Result<Self, ProtocolError> {
Self::deserialize_take(&mut key_bytes)
}
pub(crate) fn deserialize_take(key_bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
G::deserialize_take_pk(key_bytes).map(Self)
2022-02-25 07:13:22 +01:00
}
/// Convert to bytes
2025-05-19 22:56:25 +02:00
pub fn serialize(&self) -> GenericArray<u8, G::PkLen> {
2025-07-17 22:15:30 +02:00
G::serialize_pk(&self.0)
2021-06-15 10:48:29 +02:00
}
2025-05-19 22:56:25 +02:00
/// Returns the inner [`Group::Pk`].
2025-07-17 22:15:30 +02:00
pub fn to_group_type(&self) -> &G::Pk {
&self.0
}
2022-04-02 01:10:00 +02:00
}
2025-05-19 22:56:25 +02:00
impl<G: Group> PublicKey<G> {
/// Public-key verifying implementation
pub(crate) fn verify<CS: CipherSuite, SIG: SignatureProtocol<Group = G>, KE: Group>(
&self,
message_builder: MessageBuilder<'_, CS>,
state: SIG::VerifyState<CS, KE>,
signature: &SIG::Signature,
) -> Result<(), ProtocolError> {
SIG::verify(&self.0, message_builder, state, signature)
}
}
/// Default OPRF seed container.
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, ZeroizeOnDrop)]
pub struct OprfSeed<H: OutputSizeUser>(pub(crate) Output<H>);
/// A trait to facilitate
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
///
/// Will be called with `E` being [`PrivateKeySerialization::Error`].
pub trait OprfSeedSerialization<H, E>: Sized {
/// Serialization size in bytes.
type Len: ArrayLength;
2025-05-19 22:56:25 +02:00
/// Serialization into bytes
fn serialize(&self) -> GenericArray<u8, Self::Len>;
/// Deserialization from bytes
2025-06-25 00:17:29 +02:00
///
/// The deserialized bytes must be taken from `bytes`.
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError<E>>;
2025-05-19 22:56:25 +02:00
}
impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for OprfSeed<H>
where
H::OutputSize: ArrayLength,
{
2025-05-19 22:56:25 +02:00
type Len = H::OutputSize;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
GenericArray::from_slice(self.0.as_slice()).clone()
2025-05-19 22:56:25 +02:00
}
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError<E>> {
Ok(Self(
input
.take_array("OPRF seed")
.map_err(ProtocolError::into_custom)?
.into_ha0_4(),
2025-05-19 22:56:25 +02:00
))
}
}
//////////////////////////
// Test Implementations //
//===================== //
//////////////////////////
#[cfg(test)]
impl<G: Group> KeyPair<G>
where
G::Pk: core::fmt::Debug,
G::Sk: core::fmt::Debug,
{
2025-05-19 22:56:25 +02:00
/// Test-only strategy returning a proptest Strategy based on
/// [`Self::derive_random`]
fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy<Self> {
use proptest::prelude::*;
use rand::SeedableRng;
use rand::rngs::StdRng;
2025-05-19 22:56:25 +02:00
// 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);
Some(Self::derive_random(&mut rng))
})
.no_shrink()
.boxed()
}
}
#[cfg(test)]
mod tests {
2022-01-06 06:19:02 +01:00
use super::*;
2025-05-19 22:56:25 +02:00
use crate::ciphersuite::{KeGroup, OprfHash};
use crate::{
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult,
ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters,
ClientRegistrationFinishResult, ClientRegistrationStartResult, ServerLogin,
ServerLoginParameters, ServerLoginStartResult, ServerRegistration,
ServerRegistrationStartResult, ServerSetup,
};
use hkdf::Hkdf;
use rand::rngs::SysRng;
use rand_core::UnwrapErr;
2022-01-04 00:50:40 +01:00
macro_rules! test {
($mod:ident, $point:ty) => {
mod $mod {
2022-01-06 06:19:02 +01:00
use proptest::prelude::*;
use super::*;
2022-01-04 00:50:40 +01:00
proptest! {
#[test]
2025-05-19 22:56:25 +02:00
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
2022-01-04 00:50:40 +01:00
let pk = kp.public();
let sk = kp.private();
prop_assert_eq!(sk.public_key().serialize(), pk.serialize());
2022-01-04 00:50:40 +01:00
}
2020-06-12 18:46:08 -04:00
2022-01-04 00:50:40 +01:00
#[test]
2025-05-19 22:56:25 +02:00
fn dh(kp1 in KeyPair::<$point>::uniform_keypair_strategy(),
kp2 in KeyPair::<$point>::uniform_keypair_strategy()) {
2020-06-12 18:46:08 -04:00
let dh1 = kp2.private().ke_diffie_hellman(&kp1.public());
let dh2 = kp1.private().ke_diffie_hellman(kp2.public());
2022-01-04 00:50:40 +01:00
prop_assert_eq!(dh1, dh2);
}
2022-01-04 00:50:40 +01:00
#[test]
2025-05-19 22:56:25 +02:00
fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
2022-02-25 07:13:22 +01:00
let sk_bytes = kp.private().serialize().to_vec();
2025-05-19 22:56:25 +02:00
let kp2 = PrivateKey::<$point>::deserialize_take_key_pair(&mut (sk_bytes.as_slice()))?;
2022-02-25 07:13:22 +01:00
let kp2_private_bytes = kp2.private().serialize().to_vec();
2022-01-04 00:50:40 +01:00
prop_assert_eq!(sk_bytes, kp2_private_bytes);
}
}
}
};
}
2021-07-20 14:16:59 +02:00
2022-01-04 00:50:40 +01:00
#[cfg(feature = "ristretto255")]
2022-02-25 07:13:22 +01:00
test!(ristretto, crate::Ristretto255);
test!(p256, ::p256::NistP256);
2023-03-06 20:28:19 +01:00
test!(p384, ::p384::NistP384);
2023-11-16 20:07:46 +01:00
test!(p521, ::p521::NistP521);
2022-01-04 00:50:40 +01:00
2025-05-19 22:56:25 +02:00
struct Default;
2021-07-20 14:16:59 +02:00
2025-05-19 22:56:25 +02:00
impl CipherSuite for Default {
#[cfg(feature = "ristretto255")]
type OprfCs = crate::Ristretto255;
#[cfg(not(feature = "ristretto255"))]
type OprfCs = ::p256::NistP256;
#[cfg(feature = "ristretto255")]
type KeyExchange = crate::TripleDh<crate::Ristretto255, sha2::Sha512>;
#[cfg(not(feature = "ristretto255"))]
type KeyExchange = crate::TripleDh<::p256::NistP256, sha2::Sha256>;
type Ksf = crate::ksf::Identity;
}
2021-07-20 14:16:59 +02:00
2025-05-19 22:56:25 +02:00
#[derive(Clone)]
struct RemoteSeed<H: OutputSizeUser>(Output<H>);
2022-02-25 07:13:22 +01:00
2025-05-19 22:56:25 +02:00
#[derive(Clone)]
struct RemoteKey(PrivateKey<KeGroup<Default>>);
2021-07-20 14:16:59 +02:00
2025-05-19 22:56:25 +02:00
const PASSWORD: &str = "password";
2021-07-20 14:16:59 +02:00
2025-05-19 22:56:25 +02:00
#[test]
fn remote_key() {
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut UnwrapErr(SysRng)));
let pk = sk.public_key();
let sk = RemoteKey(sk);
let keypair = KeyPair::new(sk, pk);
2021-07-20 14:16:59 +02:00
let server_setup =
ServerSetup::<Default, RemoteKey>::new_with_key_pair(&mut UnwrapErr(SysRng), keypair);
2021-07-20 14:16:59 +02:00
let ClientRegistrationStartResult {
message,
state: client,
} = ClientRegistration::<Default>::start(&mut UnwrapErr(SysRng), PASSWORD.as_bytes())
.unwrap();
2021-07-30 14:00:23 +02:00
let ServerRegistrationStartResult { message, .. } =
2021-08-12 06:25:07 +02:00
ServerRegistration::start(&server_setup, message, &[]).unwrap();
let ClientRegistrationFinishResult { message, .. } = client
.finish(
&mut UnwrapErr(SysRng),
2022-01-06 00:10:57 +01:00
PASSWORD.as_bytes(),
2021-08-12 06:25:07 +02:00
message,
2021-09-02 11:28:21 +02:00
ClientRegistrationFinishParameters::default(),
2021-08-12 06:25:07 +02:00
)
.unwrap();
2021-07-20 14:16:59 +02:00
let file = ServerRegistration::finish(message);
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::<Default>::start(&mut UnwrapErr(SysRng), PASSWORD.as_bytes()).unwrap();
let builder = ServerLogin::builder(
&mut UnwrapErr(SysRng),
2021-07-20 14:16:59 +02:00
&server_setup,
Some(file),
message,
&[],
2025-05-19 22:56:25 +02:00
ServerLoginParameters::default(),
2021-08-12 06:25:07 +02:00
)
.unwrap();
let shared_secret = builder.private_key().0.ke_diffie_hellman(builder.data());
let ServerLoginStartResult {
message,
state: server,
..
} = builder.build(shared_secret).unwrap();
2021-08-12 06:25:07 +02:00
let ClientLoginFinishResult { message, .. } = client
2022-01-06 00:10:57 +01:00
.finish(
&mut UnwrapErr(SysRng),
2022-01-06 00:10:57 +01:00
PASSWORD.as_bytes(),
message,
ClientLoginFinishParameters::default(),
)
2021-08-12 06:25:07 +02:00
.unwrap();
2025-05-19 22:56:25 +02:00
server
.finish(message, ServerLoginParameters::default())
.unwrap();
}
#[test]
fn remote_seed() {
let mut oprf_seed = RemoteSeed::<OprfHash<Default>>(GenericArray::default().into_ha0_4());
UnwrapErr(SysRng).fill_bytes(&mut oprf_seed.0);
2025-05-19 22:56:25 +02:00
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut UnwrapErr(SysRng)));
2025-05-19 22:56:25 +02:00
let pk = sk.public_key();
let sk = RemoteKey(sk);
let keypair = KeyPair::new(sk, pk);
let server_setup = ServerSetup::<Default, _, _>::new_with_key_pair_and_seed(
&mut UnwrapErr(SysRng),
keypair,
oprf_seed,
2025-05-19 22:56:25 +02:00
);
let ClientRegistrationStartResult {
message,
state: client,
} = ClientRegistration::<Default>::start(&mut UnwrapErr(SysRng), PASSWORD.as_bytes())
.unwrap();
2025-05-19 22:56:25 +02:00
let km = server_setup.key_material_info(&[]);
let mut ikm = GenericArray::default();
Hkdf::<OprfHash<Default>>::from_prk(&km.ikm.0)
.unwrap()
.expand_multi_info(&km.info, &mut ikm)
.unwrap();
let ServerRegistrationStartResult { message, .. } =
ServerRegistration::start_with_key_material(&server_setup, ikm, message).unwrap();
let ClientRegistrationFinishResult { message, .. } = client
.finish(
&mut UnwrapErr(SysRng),
2025-05-19 22:56:25 +02:00
PASSWORD.as_bytes(),
message,
ClientRegistrationFinishParameters::default(),
)
.unwrap();
let file = ServerRegistration::finish(message);
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::<Default>::start(&mut UnwrapErr(SysRng), PASSWORD.as_bytes()).unwrap();
2025-05-19 22:56:25 +02:00
let km = server_setup.key_material_info(&[]);
let mut ikm = GenericArray::default();
Hkdf::<OprfHash<Default>>::from_prk(&km.ikm.0)
.unwrap()
.expand_multi_info(&km.info, &mut ikm)
.unwrap();
let builder = ServerLogin::builder_with_key_material(
&mut UnwrapErr(SysRng),
2025-05-19 22:56:25 +02:00
&server_setup,
ikm,
Some(file),
message,
ServerLoginParameters::default(),
)
.unwrap();
let shared_secret = builder.private_key().0.ke_diffie_hellman(builder.data());
let ServerLoginStartResult {
message,
state: server,
..
} = builder.build(shared_secret).unwrap();
let ClientLoginFinishResult { message, .. } = client
.finish(
&mut UnwrapErr(SysRng),
2025-05-19 22:56:25 +02:00
PASSWORD.as_bytes(),
message,
ClientLoginFinishParameters::default(),
)
.unwrap();
server
.finish(message, ServerLoginParameters::default())
.unwrap();
2021-07-20 14:16:59 +02:00
}
}