diff --git a/src/envelope.rs b/src/envelope.rs index e1f71cb..410b841 100644 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -32,9 +32,9 @@ const NONCE_LEN: usize = 32; fn build_inner_envelope_internal( random_pwd: &[u8], nonce: &[u8], -) -> Result { +) -> Result, InternalPakeError> { let h = Hkdf::::new(None, random_pwd); - let mut keypair_seed = vec![0u8; ::Len::to_usize()]; + let mut keypair_seed = vec![0u8; as SizedBytes>::Len::to_usize()]; h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed) .map_err(|_| InternalPakeError::HkdfError)?; let client_static_keypair = @@ -50,7 +50,7 @@ fn recover_keys_internal( nonce: &[u8], ) -> Result, InternalPakeError> { let h = Hkdf::::new(None, random_pwd); - let mut keypair_seed = vec![0u8; ::Len::to_usize()]; + let mut keypair_seed = vec![0u8; as SizedBytes>::Len::to_usize()]; h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed) .map_err(|_| InternalPakeError::HkdfError)?; let client_static_keypair = @@ -185,7 +185,7 @@ impl Envelope { ) -> Result< ( Self, - PublicKey, + PublicKey, GenericArray::OutputSize>, ), InternalPakeError, diff --git a/src/group.rs b/src/group.rs index 2d02a11..7a0af19 100644 --- a/src/group.rs +++ b/src/group.rs @@ -30,7 +30,7 @@ pub trait Group: Copy + Sized + for<'a> Mul<&'a ::Scalar, Output /// The type of base field scalars type Scalar: Zeroize + Clone; /// The byte length necessary to represent scalars - type ScalarLen: ArrayLength; + type ScalarLen: ArrayLength + 'static; /// Return a scalar from its fixed-length bytes representation fn from_scalar_slice( scalar_bits: &GenericArray, @@ -43,7 +43,7 @@ pub trait Group: Copy + Sized + for<'a> Mul<&'a ::Scalar, Output fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar; /// The byte length necessary to represent group elements - type ElemLen: ArrayLength; + type ElemLen: ArrayLength + 'static; /// Return an element from its fixed-length bytes representation fn from_element_slice( element_bits: &GenericArray, diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs index e8a62c1..a02eb54 100644 --- a/src/key_exchange/traits.rs +++ b/src/key_exchange/traits.rs @@ -30,8 +30,8 @@ pub trait KeyExchange { l1_bytes: Vec, l2_bytes: Vec, ke1_message: Self::KE1Message, - client_s_pk: PublicKey, - server_s_sk: PrivateKey, + client_s_pk: PublicKey, + server_s_sk: PrivateKey, id_u: Vec, id_s: Vec, context: Vec, @@ -43,8 +43,8 @@ pub trait KeyExchange { ke2_message: Self::KE2Message, ke1_state: &Self::KE1State, serialized_credential_request: &[u8], - server_s_pk: PublicKey, - client_s_sk: PrivateKey, + server_s_pk: PublicKey, + client_s_sk: PrivateKey, id_u: Vec, id_s: Vec, context: Vec, diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index fa7debf..d463f75 100644 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -43,10 +43,10 @@ static STR_OPAQUE: &[u8] = b"OPAQUE-"; pub struct TripleDH; impl KeyExchange for TripleDH { - type KE1State = Ke1State; + type KE1State = Ke1State; type KE2State = Ke2State<::OutputSize>; - type KE1Message = Ke1Message; - type KE2Message = Ke2Message<::OutputSize>; + type KE1Message = Ke1Message; + type KE2Message = Ke2Message::OutputSize>; type KE3Message = Ke3Message<::OutputSize>; fn generate_ke1( @@ -75,8 +75,8 @@ impl KeyExchange for TripleDH { serialized_credential_request: Vec, l2_bytes: Vec, ke1_message: Self::KE1Message, - client_s_pk: PublicKey, - server_s_sk: PrivateKey, + client_s_pk: PublicKey, + server_s_sk: PrivateKey, id_u: Vec, id_s: Vec, context: Vec, @@ -133,8 +133,8 @@ impl KeyExchange for TripleDH { ke2_message: Self::KE2Message, ke1_state: &Self::KE1State, serialized_credential_request: &[u8], - server_s_pk: PublicKey, - client_s_sk: PrivateKey, + server_s_pk: PublicKey, + client_s_sk: PrivateKey, id_u: Vec, id_s: Vec, context: Vec, @@ -208,23 +208,44 @@ impl KeyExchange for TripleDH { } /// The client state produced after the first key exchange message -#[derive(PartialEq, Eq, Debug, Hash, Zeroize, Clone)] #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -#[zeroize(drop)] -pub struct Ke1State { - client_e_sk: PrivateKey, +pub struct Ke1State { + client_e_sk: PrivateKey, client_nonce: GenericArray, } +impl_clone_for!( + struct Ke1State, + [client_e_sk, client_nonce], +); +impl_debug_eq_hash_for!( + struct Ke1State, + [client_e_sk, client_nonce], +); + +// This can't be derived because of the use of a generic parameter +impl Zeroize for Ke1State { + fn zeroize(&mut self) { + self.client_e_sk.zeroize(); + self.client_nonce.zeroize(); + } +} + +impl Drop for Ke1State { + fn drop(&mut self) { + self.zeroize(); + } +} + /// The first key exchange message #[derive(PartialEq, Eq, Debug, Hash, Clone)] #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -pub struct Ke1Message { +pub struct Ke1Message { pub(crate) client_nonce: GenericArray, - pub(crate) client_e_pk: PublicKey, + pub(crate) client_e_pk: PublicKey, } -impl FromBytes for Ke1State { +impl FromBytes for Ke1State { fn from_bytes(bytes: &[u8]) -> Result { let nonce_len = NonceLen::to_usize(); let checked_bytes = check_slice_size_atleast(bytes, KEY_LEN + nonce_len, "ke1_state")?; @@ -238,7 +259,7 @@ impl FromBytes for Ke1State { } } -impl ToBytesWithPointers for Ke1State { +impl ToBytesWithPointers for Ke1State { fn to_bytes(&self) -> Vec { let output: Vec = [&self.client_e_sk.to_arr(), &self.client_nonce[..]].concat(); output @@ -249,20 +270,20 @@ impl ToBytesWithPointers for Ke1State { vec![ ( self.client_e_sk.as_ptr(), - ::Len::to_usize(), + as SizedBytes>::Len::to_usize(), ), (self.client_nonce.as_ptr(), NonceLen::to_usize()), ] } } -impl ToBytes for Ke1Message { +impl ToBytes for Ke1Message { fn to_bytes(&self) -> Vec { [&self.client_nonce[..], &self.client_e_pk.to_arr()].concat() } } -impl FromBytes for Ke1Message { +impl FromBytes for Ke1Message { fn from_bytes(ke1_message_bytes: &[u8]) -> Result { let nonce_len = NonceLen::to_usize(); let checked_nonce = @@ -323,9 +344,9 @@ impl> ToBytesWithPointers for Ke2State { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serialize", serde(bound = ""))] -pub struct Ke2Message> { +pub struct Ke2Message> { server_nonce: GenericArray, - server_e_pk: PublicKey, + server_e_pk: PublicKey, mac: GenericArray, } @@ -344,19 +365,19 @@ impl> FromBytes for Ke2State { } } -impl> ToBytes for Ke2Message { +impl> ToBytes for Ke2Message { fn to_bytes(&self) -> Vec { [&self.to_bytes_without_info_or_mac(), &self.mac[..]].concat() } } -impl> Ke2Message { +impl> Ke2Message { fn to_bytes_without_info_or_mac(&self) -> Vec { [&self.server_nonce[..], &self.server_e_pk.to_arr()].concat() } } -impl> FromBytes for Ke2Message { +impl> FromBytes for Ke2Message { fn from_bytes(input: &[u8]) -> Result { let nonce_len = NonceLen::to_usize(); let checked_nonce = check_slice_size_atleast(input, nonce_len, "ke2_message nonce")?; @@ -387,13 +408,13 @@ impl> FromBytes for Ke2Message { #[allow(clippy::upper_case_acronyms)] // The triple of public and private components used in the 3DH computation -struct TripleDHComponents { - pk1: PublicKey, - sk1: PrivateKey, - pk2: PublicKey, - sk2: PrivateKey, - pk3: PublicKey, - sk3: PrivateKey, +struct TripleDHComponents { + pk1: PublicKey, + sk1: PrivateKey, + pk2: PublicKey, + sk2: PrivateKey, + pk3: PublicKey, + sk3: PrivateKey, } #[allow(clippy::upper_case_acronyms)] @@ -433,7 +454,7 @@ impl> FromBytes for Ke3Message { // Internal function which takes the public and private components of the client and server keypairs, along // with some auxiliary metadata, to produce the session key and two MAC keys fn derive_3dh_keys( - dh: TripleDHComponents, + dh: TripleDHComponents, hashed_derivation_transcript: &[u8], ) -> Result, ProtocolError> { let ikm: Vec = [ diff --git a/src/keypair.rs b/src/keypair.rs index 798487d..a79c734 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -11,7 +11,7 @@ use crate::errors::InternalPakeError; use crate::group::Group; #[cfg(test)] use generic_array::typenum::Unsigned; -use generic_array::{typenum::U32, GenericArray}; +use generic_array::{ArrayLength, GenericArray}; use generic_bytes::{SizedBytes, TryFromSizedBytesError}; #[cfg(test)] use proptest::prelude::*; @@ -35,20 +35,19 @@ pub trait SizedBytesExt: SizedBytes { impl SizedBytesExt for T where T: SizedBytes {} /// A Keypair trait with public-private verification -#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize), serde(bound = ""))] pub struct KeyPair { - pk: PublicKey, - sk: PrivateKey, - _g: PhantomData, + pk: PublicKey, + sk: PrivateKey, } impl_clone_for!( struct KeyPair, - [pk, sk, _g], + [pk, sk], ); impl_debug_eq_hash_for!( struct KeyPair, - [pk, sk, _g], + [pk, sk], ); // This can't be derived because of the use of a phantom parameter @@ -67,12 +66,12 @@ impl Drop for KeyPair { impl KeyPair { /// The public key component - pub fn public(&self) -> &PublicKey { + pub fn public(&self) -> &PublicKey { &self.pk } /// The private key component - pub fn private(&self) -> &PrivateKey { + pub fn private(&self) -> &PrivateKey { &self.sk } @@ -82,17 +81,16 @@ impl KeyPair { let sk_bytes = G::scalar_as_bytes(&sk); let pk = G::base_point().mult_by_slice(sk_bytes); Self { - pk: PublicKey(Key(pk.to_arr().to_vec())), - sk: PrivateKey(Key(sk_bytes.to_vec())), - _g: PhantomData, + pk: PublicKey::new(Key(pk.to_arr().to_vec())), + sk: PrivateKey::new(Key(sk_bytes.to_vec())), } } /// Obtaining a public key from secret bytes. At all times, we should have /// &public_from_private(self.private()) == self.public() - pub(crate) fn public_from_private(bytes: &PrivateKey) -> PublicKey { + pub(crate) fn public_from_private(bytes: &PrivateKey) -> PublicKey { let bytes_data = GenericArray::::from_slice(&bytes.0[..]); - PublicKey(Key(G::base_point() + PublicKey::new(Key(G::base_point() .mult_by_slice(bytes_data) .to_arr() .to_vec())) @@ -102,14 +100,14 @@ impl KeyPair { /// 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 - pub(crate) fn check_public_key(key: PublicKey) -> Result { + pub(crate) fn check_public_key(key: PublicKey) -> Result, InternalPakeError> { G::from_element_slice(GenericArray::from_slice(&key.0)).map(|_| key) } /// Computes the diffie hellman function on a public key and private key pub(crate) fn diffie_hellman( - pk: PublicKey, - sk: PrivateKey, + pk: PublicKey, + sk: PrivateKey, ) -> Result, InternalPakeError> { let pk_data = GenericArray::::from_slice(&pk.0[..]); let point = G::from_element_slice(pk_data)?; @@ -119,20 +117,19 @@ impl KeyPair { /// Obtains a KeyPair from a slice representing the private key pub fn from_private_key_slice(input: &[u8]) -> Result { - let sk = PrivateKey(Key::from_arr(GenericArray::from_slice(input))?); + let sk = PrivateKey::new(Key::from_arr::(GenericArray::from_slice(input))?); let pk = Self::public_from_private(&sk); Ok(Self { pk, sk, - _g: PhantomData, }) } #[cfg(test)] pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { vec![ - (self.pk.as_ptr(), KeyLen::to_usize()), - (self.sk.as_ptr(), KeyLen::to_usize()), + (self.pk.as_ptr(), G::ElemLen::to_usize()), + (self.sk.as_ptr(), G::ScalarLen::to_usize()), ] } } @@ -154,8 +151,6 @@ impl KeyPair { } } -type KeyLen = U32; - /// A minimalist key type built around a \[u8; 32\] #[derive(Debug, PartialEq, Eq, Clone, Hash, Zeroize)] #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] @@ -174,69 +169,131 @@ impl Deref for Key { // Don't make it implement SizedBytes so that it's not constructible outside of this module. impl Key { - fn to_arr(&self) -> GenericArray { + fn to_arr>(&self) -> GenericArray { GenericArray::clone_from_slice(&self.0[..]) } #[allow(clippy::unnecessary_wraps)] - fn from_arr(key_bytes: &GenericArray) -> Result { + fn from_arr>(key_bytes: &GenericArray) -> Result { Ok(Key(key_bytes.to_vec())) } } /// Wrapper around a Key to enforce that it's a private one. -#[derive(Debug, PartialEq, Eq, Clone, Hash, Zeroize)] #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -// Ensure Key material is zeroed after use. -#[zeroize(drop)] #[repr(transparent)] -pub struct PrivateKey(Key); +pub struct PrivateKey { + key: Key, + _g: PhantomData, +} -impl Deref for PrivateKey { - type Target = Key; +impl_clone_for!( + struct PrivateKey, + [key, _g], +); +impl_debug_eq_hash_for!( + struct PrivateKey, + [key, _g], +); - fn deref(&self) -> &Self::Target { - &self.0 +// This can't be derived because of the use of a phantom parameter +impl Zeroize for PrivateKey { + fn zeroize(&mut self) { + self.key.zeroize(); } } -impl SizedBytes for PrivateKey { - type Len = KeyLen; +impl Drop for PrivateKey { + fn drop(&mut self) { + self.zeroize(); + } +} + +impl Deref for PrivateKey { + type Target = Key; + + fn deref(&self) -> &Self::Target { + &self.key + } +} + +impl PrivateKey { + fn new(key: Key) -> Self { + Self { + key, + _g: PhantomData, + } + } +} + +impl SizedBytes for PrivateKey { + type Len = G::ScalarLen; fn to_arr(&self) -> GenericArray { - self.0.to_arr() + self.key.to_arr() } fn from_arr(key_bytes: &GenericArray) -> Result { - Ok(PrivateKey(Key::from_arr(key_bytes)?)) + Ok(PrivateKey::new(Key::from_arr(key_bytes)?)) } } /// Wrapper around a Key to enforce that it's a public one. -#[derive(Debug, PartialEq, Eq, Clone, Hash, Zeroize)] #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -// Ensure Key material is zeroed after use. -#[zeroize(drop)] #[repr(transparent)] -pub struct PublicKey(Key); +pub struct PublicKey { + key: Key, + _g: PhantomData, +} -impl Deref for PublicKey { - type Target = Key; +impl_clone_for!( + struct PublicKey, + [key, _g], +); +impl_debug_eq_hash_for!( + struct PublicKey, + [key, _g], +); - fn deref(&self) -> &Self::Target { - &self.0 +// This can't be derived because of the use of a phantom parameter +impl Zeroize for PublicKey { + fn zeroize(&mut self) { + self.key.zeroize(); } } -impl SizedBytes for PublicKey { - type Len = KeyLen; +impl Drop for PublicKey { + fn drop(&mut self) { + self.zeroize(); + } +} + +impl Deref for PublicKey { + type Target = Key; + + fn deref(&self) -> &Self::Target { + &self.key + } +} + +impl PublicKey { + fn new(key: Key) -> Self { + Self { + key, + _g: PhantomData, + } + } +} + +impl SizedBytes for PublicKey { + type Len = G::ElemLen; fn to_arr(&self) -> GenericArray { - self.0.to_arr() + self.key.to_arr() } fn from_arr(key_bytes: &GenericArray) -> Result { - Ok(PublicKey(Key::from_arr(key_bytes)?)) + Ok(PublicKey::new(Key::from_arr(key_bytes)?)) } } @@ -251,7 +308,7 @@ mod tests { #[test] fn test_zeroize_key() -> Result<(), ProtocolError> { - let key_len = KeyLen::to_usize(); + let key_len = ::ElemLen::to_usize(); let mut key = Key(vec![1u8; key_len]); let ptr = key.as_ptr(); diff --git a/src/messages.rs b/src/messages.rs index 04bf4fc..f14dfa0 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -79,7 +79,7 @@ pub struct RegistrationResponse { /// The server's oprf output pub(crate) beta: CS::Group, /// Server's static public key - pub(crate) server_s_pk: PublicKey, + pub(crate) server_s_pk: PublicKey, } // Cannot be derived because it would require for CS to be Clone. @@ -107,7 +107,7 @@ impl RegistrationResponse { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let elem_len = ::ElemLen::to_usize(); - let key_len = ::Len::to_usize(); + let key_len = as SizedBytes>::Len::to_usize(); let checked_slice = check_slice_size(input, elem_len + key_len, "registration_response_bytes")?; @@ -141,7 +141,7 @@ pub struct RegistrationUpload { /// The masking key used to mask the envelope pub(crate) masking_key: GenericArray::OutputSize>, /// The user's public key - pub(crate) client_s_pk: PublicKey, + pub(crate) client_s_pk: PublicKey, } impl_clone_for!( @@ -166,7 +166,7 @@ impl RegistrationUpload { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let key_len = ::Len::to_usize(); + let key_len = as SizedBytes>::Len::to_usize(); let hash_len = ::OutputSize::to_usize(); let checked_slice = check_slice_size_atleast(input, key_len + hash_len, "registration_upload_bytes")?; @@ -311,7 +311,7 @@ impl CredentialResponse { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let elem_len = ::ElemLen::to_usize(); - let key_len = ::Len::to_usize(); + let key_len = as SizedBytes>::Len::to_usize(); let nonce_len: usize = 32; let envelope_len = Envelope::::len(); let masked_response_len = key_len + envelope_len; diff --git a/src/opaque.rs b/src/opaque.rs index 1209461..eceeb66 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -69,7 +69,7 @@ impl ServerSetup { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let seed_len = ::OutputSize::to_usize(); - let key_len = ::Len::to_usize(); + let key_len = as SizedBytes>::Len::to_usize(); let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")?; Ok(Self { @@ -242,7 +242,7 @@ pub struct ClientRegistrationFinishResult { /// The export key output by client registration pub export_key: GenericArray::OutputSize>, /// The server's static public key - pub server_s_pk: PublicKey, + pub server_s_pk: PublicKey, /// Instance of the ClientRegistration, only used in tests for checking zeroize #[cfg(test)] pub state: ClientRegistration, @@ -517,7 +517,7 @@ pub struct ClientLoginFinishResult { /// The client-side export key pub export_key: GenericArray::OutputSize>, /// The server's static public key - pub server_s_pk: PublicKey, + pub server_s_pk: PublicKey, /// Instance of the ClientLogin, only used in tests for checking zeroize #[cfg(test)] pub state: ClientLogin, @@ -915,7 +915,7 @@ fn oprf_key_from_seed( oprf_seed: &GenericArray, credential_identifier: &[u8], ) -> Result { - let mut oprf_key_bytes = vec![0u8; ::Len::to_usize()]; + let mut oprf_key_bytes = vec![0u8; as SizedBytes>::Len::to_usize()]; Hkdf::::from_prk(oprf_seed) .map_err(|_| InternalPakeError::HkdfError)? .expand( @@ -929,10 +929,10 @@ fn oprf_key_from_seed( fn mask_response( masking_key: &[u8], masking_nonce: &[u8], - server_s_pk: &PublicKey, + server_s_pk: &PublicKey, envelope: &Envelope, ) -> Result, ProtocolError> { - let mut xor_pad = vec![0u8; ::Len::to_usize() + Envelope::::len()]; + let mut xor_pad = vec![0u8; as SizedBytes>::Len::to_usize() + Envelope::::len()]; Hkdf::::from_prk(masking_key) .map_err(|_| InternalPakeError::HkdfError)? .expand( @@ -954,8 +954,8 @@ fn unmask_response( masking_key: &[u8], masking_nonce: &[u8], masked_response: &[u8], -) -> Result<(PublicKey, Envelope), ProtocolError> { - let mut xor_pad = vec![0u8; ::Len::to_usize() + Envelope::::len()]; +) -> Result<(PublicKey, Envelope), ProtocolError> { + let mut xor_pad = vec![0u8; as SizedBytes>::Len::to_usize() + Envelope::::len()]; Hkdf::::from_prk(masking_key) .map_err(|_| InternalPakeError::HkdfError)? .expand( @@ -968,7 +968,7 @@ fn unmask_response( .zip(masked_response.iter()) .map(|(&x1, &x2)| x1 ^ x2) .collect(); - let key_len = ::Len::to_usize(); + let key_len = as SizedBytes>::Len::to_usize(); let unchecked_server_s_pk = PublicKey::from_arr(&GenericArray::clone_from_slice(&plaintext[..key_len]))?; let envelope = Envelope::deserialize(&plaintext[key_len..])?; diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs index aea155d..8a3aec6 100644 --- a/src/serialization/tests.rs +++ b/src/serialization/tests.rs @@ -212,7 +212,7 @@ fn credential_response_roundtrip() { rng.fill_bytes(&mut masking_nonce); let mut masked_response = - vec![0u8; ::Len::to_usize() + Envelope::::len()]; + vec![0u8; as SizedBytes>::Len::to_usize() + Envelope::::len()]; rng.fill_bytes(&mut masked_response); let server_e_kp = KeyPair::<::Group>::generate_random(&mut rng); diff --git a/src/tests/opaque_test_vectors.rs b/src/tests/opaque_test_vectors.rs index e6c6d3b..a3d78bd 100644 --- a/src/tests/opaque_test_vectors.rs +++ b/src/tests/opaque_test_vectors.rs @@ -445,7 +445,7 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters { dummy_private_key: parse_default!( values, "client_private_key", - vec![0u8; ::Len::to_usize()] + vec![0u8; as SizedBytes>::Len::to_usize()] ), dummy_masking_key: parse_default!(values, "masking_key", vec![0u8; 64]), context: parse!(values, "Context"),