#[cfg(test)]
use core::convert::TryFrom;
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
use crate::bigint;
use rand::{thread_rng, RngCore};
use crate::error::InvalidPublicKeyError;
#[cfg(test)]
use crate::hex::*;
use crate::primes::LARGE_SAFE_PRIME_LENGTH;
use crate::LARGE_SAFE_PRIME_LITTLE_ENDIAN;
macro_rules! key_bigint {
($name: ident) => {
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
impl $name {
pub(crate) fn as_bigint(&self) -> bigint::Integer {
bigint::Integer::from_bytes_le(&self.key)
}
}
};
}
macro_rules! key_new {
($name: ident; $size: expr) => {
impl Default for $name {
fn default() -> Self {
let mut key = [0_u8; $size];
thread_rng().fill_bytes(&mut key);
Self::from_le_bytes(key)
}
}
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
impl $name {
pub(crate) fn randomized() -> Self {
Self::default()
}
}
};
}
fn check_public_key(key: &[u8; PUBLIC_KEY_LENGTH as usize]) -> Result<(), InvalidPublicKeyError> {
for (i, value) in key.iter().enumerate() {
if *value != LARGE_SAFE_PRIME_LITTLE_ENDIAN[i] && *value != 0 {
return Ok(());
}
}
match key[0] {
0 => Err(InvalidPublicKeyError::PublicKeyIsZero),
_ => Err(InvalidPublicKeyError::PublicKeyModLargeSafePrimeIsZero),
}
}
macro_rules! key_no_checks_initialization {
($name: ident; $size: expr) => {
impl $name {
#[allow(dead_code)]
pub const fn from_le_bytes(key: [u8; $size]) -> Self {
Self { key }
}
#[cfg(test)]
#[allow(dead_code)]
pub fn from_be_hex_str(s: &str) -> Self {
let mut key = hex_decode(&s);
key.reverse();
while key.len() < $size {
key.push(0);
}
let key = <[u8; $size]>::try_from(key).unwrap();
Self { key }
}
}
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
impl From<bigint::Integer> for $name {
fn from(b: bigint::Integer) -> Self {
let mut key = [0_u8; $size];
let b = b.to_bytes_le().to_vec();
key[0..b.len()].clone_from_slice(&b);
Self { key }
}
}
};
}
macro_rules! key_wrapper {
($name: ident; $size: expr) => {
#[derive(Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq, Hash)]
pub struct $name {
key: [u8; $size],
}
impl $name {
#[allow(unused)] pub const fn as_le_bytes(&self) -> &[u8; $size] {
&self.key
}
#[allow(dead_code)]
#[cfg(test)]
pub(crate) fn as_be_hex_string(&self) -> String {
let mut key = self.key;
key.reverse();
hex_encode_upper(&key)
}
#[allow(dead_code)]
#[cfg(test)]
pub(crate) fn from_le_hex_str(s: &str) -> Self {
let key = hex_decode(&s);
let key = <[u8; $size]>::try_from(key).unwrap();
Self { key }
}
}
};
}
#[doc(alias = "salt")]
pub const SALT_LENGTH: u8 = 32;
key_wrapper!(Salt; SALT_LENGTH as usize);
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
key_new!(Salt; SALT_LENGTH as usize);
key_no_checks_initialization!(Salt; SALT_LENGTH as usize);
#[doc(alias = "a")]
#[doc(alias = "b")]
pub const PRIVATE_KEY_LENGTH: u8 = LARGE_SAFE_PRIME_LENGTH;
key_wrapper!(PrivateKey; PRIVATE_KEY_LENGTH as usize);
key_new!(PrivateKey; PRIVATE_KEY_LENGTH as usize);
key_bigint!(PrivateKey);
key_no_checks_initialization!(PrivateKey; PRIVATE_KEY_LENGTH as usize);
#[doc(alias = "A")]
#[doc(alias = "B")]
pub const PUBLIC_KEY_LENGTH: u8 = LARGE_SAFE_PRIME_LENGTH;
key_wrapper!(PublicKey; PUBLIC_KEY_LENGTH as usize);
key_bigint!(PublicKey);
impl PublicKey {
pub fn from_le_bytes(
key: [u8; PUBLIC_KEY_LENGTH as usize],
) -> Result<Self, InvalidPublicKeyError> {
let key_is_valid = check_public_key(&key);
match key_is_valid {
Ok(_) => Ok(Self { key }),
Err(e) => Err(e),
}
}
#[cfg(test)]
#[allow(dead_code)]
pub(crate) fn from_be_hex_str(s: &str) -> Result<Self, InvalidPublicKeyError> {
let mut key = hex_decode(s);
key.reverse();
if key.len() > PUBLIC_KEY_LENGTH as usize {
panic!(
"PublicKey from_be_hex_str length is greater than {}",
PUBLIC_KEY_LENGTH
);
}
while key.len() < PUBLIC_KEY_LENGTH as usize {
key.push(0);
}
let key = <[u8; PUBLIC_KEY_LENGTH as usize]>::try_from(key).unwrap();
Self::from_le_bytes(key)
}
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
pub(crate) fn client_try_from_bigint(
b: bigint::Integer,
large_safe_prime: &crate::primes::LargeSafePrime,
) -> Result<Self, InvalidPublicKeyError> {
if b.is_zero() {
return Err(InvalidPublicKeyError::PublicKeyIsZero);
}
if b.mod_large_safe_prime_is_zero(large_safe_prime) {
return Err(InvalidPublicKeyError::PublicKeyModLargeSafePrimeIsZero);
}
let mut key = [0_u8; PUBLIC_KEY_LENGTH as usize];
let b = b.to_bytes_le().to_vec();
key[0..b.len()].clone_from_slice(&b);
Ok(Self { key })
}
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
pub(crate) fn try_from_bigint(b: bigint::Integer) -> Result<Self, InvalidPublicKeyError> {
let mut key = [0_u8; PUBLIC_KEY_LENGTH as usize];
let b = b.to_bytes_le().to_vec();
key[0..b.len()].clone_from_slice(&b);
Self::from_le_bytes(key)
}
}
pub const SHA1_HASH_LENGTH: u8 = 20;
key_wrapper!(Sha1Hash; SHA1_HASH_LENGTH as usize);
key_bigint!(Sha1Hash);
key_no_checks_initialization!(Sha1Hash; SHA1_HASH_LENGTH as usize);
#[doc(alias = "v")]
pub const PASSWORD_VERIFIER_LENGTH: u8 = LARGE_SAFE_PRIME_LENGTH;
key_wrapper!(Verifier; PASSWORD_VERIFIER_LENGTH as usize);
key_bigint!(Verifier);
key_no_checks_initialization!(Verifier; PASSWORD_VERIFIER_LENGTH as usize);
#[doc(alias = "M1")]
#[doc(alias = "M2")]
#[doc(alias = "M")]
pub const PROOF_LENGTH: u8 = 20;
key_wrapper!(Proof; PROOF_LENGTH as usize);
key_no_checks_initialization!(Proof; PROOF_LENGTH as usize);
pub const S_LENGTH: u8 = LARGE_SAFE_PRIME_LENGTH;
key_wrapper!(SKey; S_LENGTH as usize);
key_no_checks_initialization!(SKey; S_LENGTH as usize);
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
impl SKey {
pub fn as_equal_slice(&self) -> &[u8] {
let mut s = &self.key[..];
let mut lead = 0;
while s[lead] == 0 {
lead += 1;
}
if lead % 2 != 0 {
lead += 1;
}
s = &s[lead..];
s
}
}
pub const RECONNECT_CHALLENGE_DATA_LENGTH: u8 = 16;
key_wrapper!(ReconnectData; RECONNECT_CHALLENGE_DATA_LENGTH as usize);
key_new!(ReconnectData; RECONNECT_CHALLENGE_DATA_LENGTH as usize);
key_no_checks_initialization!(ReconnectData; RECONNECT_CHALLENGE_DATA_LENGTH as usize);
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
impl ReconnectData {
pub fn randomize_data(&mut self) {
thread_rng().fill_bytes(&mut self.key);
}
}
#[doc(alias = "K")]
#[doc(alias = "S")]
pub const SESSION_KEY_LENGTH: u8 = PROOF_LENGTH * 2;
key_wrapper!(SessionKey; SESSION_KEY_LENGTH as usize);
key_no_checks_initialization!(SessionKey; SESSION_KEY_LENGTH as usize);
#[cfg(test)]
#[cfg(any(feature = "srp-default-math", feature = "srp-fast-math"))]
mod test {
use crate::bigint::Integer;
use crate::key::{PrivateKey, PublicKey, PUBLIC_KEY_LENGTH};
use crate::primes::LargeSafePrime;
use crate::LARGE_SAFE_PRIME_LITTLE_ENDIAN;
#[test]
fn double_large_safe_prime_is_unrepresentable() {
let p = Integer::from_bytes_le(&LARGE_SAFE_PRIME_LITTLE_ENDIAN);
let p = p * Integer::from(2);
assert!(p.to_bytes_le().len() > PUBLIC_KEY_LENGTH as usize);
}
#[test]
fn public_key_should_not_be_zero() {
let key = [0_u8; PUBLIC_KEY_LENGTH as usize];
let p = PublicKey::from_le_bytes(key);
assert!(p.is_err());
}
#[test]
fn client_public_key_should_not_be_mod_zero() {
let key = Integer::from_bytes_le(&LARGE_SAFE_PRIME_LITTLE_ENDIAN);
let large_safe_prime = LargeSafePrime::default();
let p = PublicKey::client_try_from_bigint(key, &large_safe_prime);
assert!(p.is_err());
}
#[test]
fn client_public_key_should_not_be_zero() {
let key = Integer::from_bytes_le(&[0_u8; PUBLIC_KEY_LENGTH as usize]);
let large_safe_prime = LargeSafePrime::default();
let p = PublicKey::client_try_from_bigint(key, &large_safe_prime);
assert!(p.is_err());
}
#[test]
fn public_key_should_not_be_zero_from_hex() {
let p = PublicKey::from_be_hex_str("00");
assert!(p.is_err());
}
#[test]
fn public_key_should_not_be_mod_large_safe_prime() {
let p = PublicKey::from_le_bytes(LARGE_SAFE_PRIME_LITTLE_ENDIAN);
assert!(p.is_err());
}
#[test]
fn public_key_should_not_be_mod_large_safe_prime_from_hex() {
let p = PublicKey::from_be_hex_str(
"894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7",
);
assert!(p.is_err());
}
#[test]
fn hex_to_hex() {
const PADDED_DEADBEEF: &str =
"00000000000000000000000000000000000000000000000000000000DEADBEEF";
const DEADBEEF: &str = "DEADBEEF";
let k = PrivateKey::from_be_hex_str(DEADBEEF);
assert_eq!(&k.as_be_hex_string(), PADDED_DEADBEEF);
}
#[test]
#[should_panic]
fn public_key_from_hex_string_panic() {
const TOO_LONG_DEADBEEF: &str =
"0000000000000000000000000000000000000000000000000000000000DEADBEEF";
let _ = PublicKey::from_be_hex_str(TOO_LONG_DEADBEEF);
}
}