use aead::{Aead, AeadCore, KeyInit, generic_array::typenum::marker_traits::Unsigned};
#[cfg(feature = "rustcrypto-ec")]
use elliptic_curve::{
Curve, CurveArithmetic,
point::PointCompression,
sec1::{CompressedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint},
};
use sha3::{Digest, Sha3_256};
use crate::{
Hint, Hinting, Hints, cipher_from_shared_secret, curves,
curves::{KeyPair, sealed},
error::*,
};
#[cfg(feature = "rustcrypto-ec")]
use curves::{EllipticCurve, rcec};
#[cfg(feature = "dalek")]
use curves::{X25519, dalek};
pub trait TakeTheHint<K: KeyPair> {
fn take_the<A: Aead + KeyInit, const L: usize>(
&self,
hint: &Hint<K, A, L>,
salt: &[u8],
) -> Result<[u8; L]>;
fn take_all_the<A: Aead + KeyInit, const L: usize, const S: usize>(
&self,
hints: &Hints<Hint<K, A, L>, S>,
salt: &[u8],
) -> Vec<[u8; L]>
where
Hint<K, A, L>: Hinting<K, L>,
K::SecretKey: sealed::RandomSecretKey,
{
hints
.as_slice()
.iter()
.filter_map(|hint| self.take_the(hint, salt).ok())
.collect()
}
}
fn decrypt<A: Aead + KeyInit>(
nonce: &[u8],
shared_secret: impl AsRef<[u8]>,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
let cipher: A = cipher_from_shared_secret(shared_secret);
let nonce_size = <A as AeadCore>::NonceSize::to_usize();
let nonce = aead::Nonce::<A>::from_slice(&nonce[..nonce_size]);
Ok(cipher.decrypt(nonce, ciphertext)?)
}
#[cfg(feature = "dalek")]
impl TakeTheHint<X25519> for dalek::StaticSecret {
fn take_the<A: Aead + KeyInit, const L: usize>(
&self,
hint: &Hint<X25519, A, L>,
salt: &[u8],
) -> Result<[u8; L]> {
let raw_shared_secret = self.diffie_hellman(&hint.blinded_blinding_factor);
let mut hasher = <Sha3_256 as Digest>::new();
hasher.update(raw_shared_secret.as_bytes());
hasher.update(hint.blinded_blinding_factor.as_bytes());
hasher.update(salt);
let shared_secret = hasher.finalize();
<[u8; L]>::try_from(
decrypt::<A>(
hint.blinded_blinding_factor.as_bytes(),
shared_secret,
hint.ciphertext.as_slice(),
)?
.as_slice(),
)
.map_err(|_| Error::MessageLength)
}
}
#[cfg(feature = "rustcrypto-ec")]
impl<C: CurveArithmetic> TakeTheHint<EllipticCurve<C>> for rcec::SecretKey<C>
where
C: CurveArithmetic + PointCompression,
<C as Curve>::FieldBytesSize: ModulusSize,
<C as CurveArithmetic>::AffinePoint: ToEncodedPoint<C> + FromEncodedPoint<C>,
{
fn take_the<A: Aead + KeyInit, const L: usize>(
&self,
hint: &Hint<EllipticCurve<C>, A, L>,
salt: &[u8],
) -> Result<[u8; L]> {
let raw_shared_secret = elliptic_curve::ecdh::diffie_hellman(
self.to_nonzero_scalar(),
hint.blinded_blinding_factor.as_affine(),
);
let blinded_blinding_factor_cp = CompressedPoint::<C>::from(hint.blinded_blinding_factor);
let mut hasher = <Sha3_256 as Digest>::new();
hasher.update(raw_shared_secret.raw_secret_bytes());
hasher.update(blinded_blinding_factor_cp.as_slice());
hasher.update(salt);
let shared_secret = hasher.finalize();
<[u8; L]>::try_from(
decrypt::<A>(
blinded_blinding_factor_cp.as_slice(),
shared_secret,
hint.ciphertext.as_slice(),
)?
.as_slice(),
)
.map_err(|_| Error::MessageLength)
}
}