use crate::{errors::AuthError, groups::*, utils::*};
use alloc::vec::Vec;
use bigint::{BoxedUint, Odd, Resize, modular::BoxedMontyForm};
use core::marker::PhantomData;
use digest::{Digest, Output};
use subtle::ConstantTimeEq;
#[deprecated(since = "0.7.0", note = "too small to be secure; use a larger group")]
#[allow(deprecated)]
pub type ServerG1024<D> = Server<G1024, D>;
#[deprecated(since = "0.7.0", note = "too small to be secure; use a larger group")]
#[allow(deprecated)]
pub type ServerG1536<D> = Server<G1536, D>;
pub type ServerG2048<D> = Server<G2048, D>;
pub type ServerG3072<D> = Server<G3072, D>;
pub type ServerG4096<D> = Server<G4096, D>;
#[cfg_attr(feature = "getrandom", doc = "```")]
#[cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
#[derive(Debug)]
pub struct Server<G: Group, D: Digest> {
g: BoxedMontyForm,
g_no_pad: bool,
d: PhantomData<(G, D)>,
}
impl<G: Group, D: Digest> Server<G, D> {
#[must_use]
pub fn new() -> Self {
Self {
g: G::generator(),
g_no_pad: false,
d: PhantomData,
}
}
#[must_use]
pub fn new_with_options(g_no_pad: bool) -> Self {
Self {
g_no_pad,
g: G::generator(),
d: PhantomData,
}
}
#[must_use]
pub fn compute_b_pub(&self, b: &BoxedUint, k: &BoxedUint, v: &BoxedUint) -> BoxedUint {
let k = monty_form(k, self.g.params());
let v = monty_form(v, self.g.params());
(k * v + self.g.pow(b)).retrieve()
}
#[must_use]
pub fn compute_premaster_secret(
&self,
a_pub: &BoxedUint,
v: &BoxedUint,
u: &BoxedUint,
b: &BoxedUint,
) -> BoxedUint {
let a_pub = monty_form(a_pub, self.g.params());
let v = monty_form(v, self.g.params());
(a_pub * v.pow(u)).pow(b).retrieve()
}
#[must_use]
pub fn compute_public_ephemeral(&self, b: &[u8], v: &[u8]) -> Vec<u8> {
self.compute_b_pub(
&BoxedUint::from_be_slice_vartime(b),
&compute_k::<D>(&self.g),
&BoxedUint::from_be_slice_vartime(v),
)
.to_be_bytes_trimmed_vartime()
.into()
}
pub fn process_reply(
&self,
username: &[u8],
salt: &[u8],
b: &[u8],
v: &[u8],
a_pub_bytes: &[u8],
) -> Result<ServerVerifier<D>, AuthError> {
let b = BoxedUint::from_be_slice_vartime(b);
let v = BoxedUint::from_be_slice_vartime(v);
let a_pub = BoxedUint::from_be_slice_vartime(a_pub_bytes);
let k = compute_k::<D>(&self.g);
let b_pub_bytes = self.compute_b_pub(&b, &k, &v).to_be_bytes_trimmed_vartime();
self.validate_a_pub(&a_pub)?;
let u = compute_u_padded::<D>(&self.g, a_pub_bytes, &b_pub_bytes);
let premaster_secret = self
.compute_premaster_secret(&a_pub, &v, &u, &b)
.to_be_bytes_trimmed_vartime();
let session_key = compute_hash::<D>(&premaster_secret);
let m1 = compute_m1_rfc5054::<D>(
&self.g,
self.g_no_pad,
username,
salt,
a_pub_bytes,
&b_pub_bytes,
session_key.as_slice(),
);
let m2 = compute_m2::<D>(a_pub_bytes, &m1, session_key.as_slice());
Ok(ServerVerifier {
m1,
m2,
key: premaster_secret.into(),
session_key: session_key.to_vec(),
})
}
#[deprecated(since = "0.7.0", note = "please use `Server::process_reply` (RFC5054)")]
#[allow(deprecated)]
pub fn process_reply_legacy(
&self,
b: &[u8],
v: &[u8],
a_pub_bytes: &[u8],
) -> Result<LegacyServerVerifier<D>, AuthError> {
let b = BoxedUint::from_be_slice_vartime(b);
let v = BoxedUint::from_be_slice_vartime(v);
let a_pub = BoxedUint::from_be_slice_vartime(a_pub_bytes);
let k = compute_k::<D>(&self.g);
let b_pub_bytes = self.compute_b_pub(&b, &k, &v).to_be_bytes_trimmed_vartime();
self.validate_a_pub(&a_pub)?;
let u = compute_u::<D>(a_pub_bytes, &b_pub_bytes);
let key = self.compute_premaster_secret(&a_pub, &v, &u, &b);
let m1 = compute_m1_legacy::<D>(
a_pub_bytes,
&b_pub_bytes,
&key.to_be_bytes_trimmed_vartime(),
);
let m2 = compute_m2::<D>(a_pub_bytes, &m1, &key.to_be_bytes_trimmed_vartime());
Ok(LegacyServerVerifier {
m1,
m2,
key: key.to_be_bytes_trimmed_vartime().into(),
})
}
fn n(&self) -> &Odd<BoxedUint> {
self.g.params().modulus()
}
fn validate_a_pub(&self, a_pub: &BoxedUint) -> Result<(), AuthError> {
let n = self.n().as_nz_ref();
if (a_pub.resize(n.bits_precision()) % n).is_zero().into() {
return Err(AuthError::IllegalParameter { name: "a_pub" });
}
Ok(())
}
}
impl<G: Group, D: Digest> Default for Server<G, D> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct ServerVerifier<D: Digest> {
m1: Output<D>,
m2: Output<D>,
key: Vec<u8>,
session_key: Vec<u8>,
}
impl<D: Digest> ServerVerifier<D> {
pub fn key(&self) -> &[u8] {
&self.key
}
pub fn proof(&self) -> &[u8] {
self.m2.as_slice()
}
pub fn verify_client(&self, reply: &[u8]) -> Result<&[u8], AuthError> {
if self.m1.ct_eq(reply).unwrap_u8() == 1 {
Ok(self.session_key.as_slice())
} else {
Err(AuthError::BadRecordMac { peer: "client" })
}
}
}
#[deprecated(since = "0.7.0", note = "please switch to `ServerVerifierRfc5054`")]
#[derive(Debug)]
pub struct LegacyServerVerifier<D: Digest> {
m1: Output<D>,
m2: Output<D>,
key: Vec<u8>,
}
#[allow(deprecated)]
impl<D: Digest> LegacyServerVerifier<D> {
pub fn key(&self) -> &[u8] {
&self.key
}
pub fn proof(&self) -> &[u8] {
self.m2.as_slice()
}
pub fn verify_client(&self, reply: &[u8]) -> Result<(), AuthError> {
if self.m1.ct_eq(reply).unwrap_u8() == 1 {
Ok(())
} else {
Err(AuthError::BadRecordMac { peer: "client" })
}
}
}