#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
#[cfg_attr(test, macro_use)]
extern crate std;
mod ed25519;
mod error;
mod group;
pub use self::{
ed25519::Ed25519Group,
error::{Error, Result},
group::Group,
};
use alloc::{borrow::ToOwned, vec::Vec};
use core::{fmt, ops::Deref, str};
use curve25519_dalek::{edwards::EdwardsPoint as c2_Element, scalar::Scalar as c2_Scalar};
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "getrandom")]
use rand_core::OsRng;
#[derive(PartialEq, Eq, Clone)]
pub struct Password(Vec<u8>);
impl Password {
pub fn new(p: impl AsRef<[u8]>) -> Password {
Password(p.as_ref().to_vec())
}
}
impl Deref for Password {
type Target = Vec<u8>;
fn deref(&self) -> &Vec<u8> {
&self.0
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct Identity(Vec<u8>);
impl Deref for Identity {
type Target = Vec<u8>;
fn deref(&self) -> &Vec<u8> {
&self.0
}
}
impl Identity {
pub fn new(p: &[u8]) -> Identity {
Identity(p.to_vec())
}
}
#[derive(PartialEq, Eq)]
enum Side {
A { id_a: Vec<u8>, id_b: Vec<u8> },
B { id_a: Vec<u8>, id_b: Vec<u8> },
Symmetric { id_s: Vec<u8> },
}
#[derive(Eq, PartialEq)]
pub struct Spake2<G: Group> {
side: Side,
xy_scalar: G::Scalar,
password_vec: Vec<u8>,
msg1: Vec<u8>,
password_scalar: G::Scalar,
}
impl<G: Group> Spake2<G> {
#[cfg(feature = "getrandom")]
pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (Spake2<G>, Vec<u8>) {
Self::start_a_with_rng(password, id_a, id_b, OsRng)
}
#[cfg(feature = "getrandom")]
pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (Spake2<G>, Vec<u8>) {
Self::start_b_with_rng(password, id_a, id_b, OsRng)
}
#[cfg(feature = "getrandom")]
pub fn start_symmetric(password: &Password, id_s: &Identity) -> (Spake2<G>, Vec<u8>) {
Self::start_symmetric_with_rng(password, id_s, OsRng)
}
pub fn start_a_with_rng(
password: &Password,
id_a: &Identity,
id_b: &Identity,
mut csrng: impl CryptoRng + RngCore,
) -> (Spake2<G>, Vec<u8>) {
let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
Self::start_a_internal(password, id_a, id_b, xy_scalar)
}
pub fn start_b_with_rng(
password: &Password,
id_a: &Identity,
id_b: &Identity,
mut csrng: impl CryptoRng + RngCore,
) -> (Spake2<G>, Vec<u8>) {
let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
Self::start_b_internal(password, id_a, id_b, xy_scalar)
}
pub fn start_symmetric_with_rng(
password: &Password,
id_s: &Identity,
mut csrng: impl CryptoRng + RngCore,
) -> (Spake2<G>, Vec<u8>) {
let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
Self::start_symmetric_internal(password, id_s, xy_scalar)
}
pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>> {
if msg2.len() != 1 + G::element_length() {
return Err(Error::WrongLength);
}
let msg_side = msg2[0];
match self.side {
Side::A { id_a: _, id_b: _ } => match msg_side {
0x42 => (), _ => return Err(Error::BadSide),
},
Side::B { id_a: _, id_b: _ } => match msg_side {
0x41 => (), _ => return Err(Error::BadSide),
},
Side::Symmetric { id_s: _ } => match msg_side {
0x53 => (), _ => return Err(Error::BadSide),
},
}
let msg2_element = match G::bytes_to_element(&msg2[1..]) {
Some(x) => x,
None => return Err(Error::CorruptMessage),
};
let unblinding = match self.side {
Side::A { id_a: _, id_b: _ } => G::const_n(),
Side::B { id_a: _, id_b: _ } => G::const_m(),
Side::Symmetric { id_s: _ } => G::const_s(),
};
let tmp1 = G::scalarmult(&unblinding, &G::scalar_neg(&self.password_scalar));
let tmp2 = G::add(&msg2_element, &tmp1);
let key_element = G::scalarmult(&tmp2, &self.xy_scalar);
let key_bytes = G::element_to_bytes(&key_element);
Ok(match self.side {
Side::A { id_a, id_b } => ed25519::hash_ab(
&self.password_vec,
&id_a,
&id_b,
self.msg1.as_slice(),
&msg2[1..],
&key_bytes,
),
Side::B { id_a, id_b } => ed25519::hash_ab(
&self.password_vec,
&id_a,
&id_b,
&msg2[1..],
self.msg1.as_slice(),
&key_bytes,
),
Side::Symmetric { id_s } => ed25519::hash_symmetric(
&self.password_vec,
&id_s,
&self.msg1,
&msg2[1..],
&key_bytes,
),
})
}
fn start_internal(
side: Side,
password: &Password,
xy_scalar: G::Scalar,
) -> (Spake2<G>, Vec<u8>) {
let password_scalar: G::Scalar = G::hash_to_scalar(password);
let blinding = match side {
Side::A { id_a: _, id_b: _ } => G::const_m(),
Side::B { id_a: _, id_b: _ } => G::const_n(),
Side::Symmetric { id_s: _ } => G::const_s(),
};
let m1: G::Element = G::add(
&G::basepoint_mult(&xy_scalar),
&G::scalarmult(&blinding, &password_scalar),
);
let msg1: Vec<u8> = G::element_to_bytes(&m1);
let mut password_vec = Vec::new();
password_vec.extend_from_slice(password);
let mut msg_and_side = vec![match side {
Side::A { id_a: _, id_b: _ } => 0x41, Side::B { id_a: _, id_b: _ } => 0x42, Side::Symmetric { id_s: _ } => 0x53, }];
msg_and_side.extend_from_slice(&msg1);
(
Spake2 {
side,
xy_scalar,
password_vec, msg1,
password_scalar, },
msg_and_side,
)
}
fn start_a_internal(
password: &Password,
id_a: &Identity,
id_b: &Identity,
xy_scalar: G::Scalar,
) -> (Spake2<G>, Vec<u8>) {
Self::start_internal(
Side::A {
id_a: id_a.to_owned().0,
id_b: id_b.to_owned().0,
},
password,
xy_scalar,
)
}
fn start_b_internal(
password: &Password,
id_a: &Identity,
id_b: &Identity,
xy_scalar: G::Scalar,
) -> (Spake2<G>, Vec<u8>) {
Self::start_internal(
Side::B {
id_a: id_a.to_owned().0,
id_b: id_b.to_owned().0,
},
password,
xy_scalar,
)
}
fn start_symmetric_internal(
password: &Password,
id_s: &Identity,
xy_scalar: G::Scalar,
) -> (Spake2<G>, Vec<u8>) {
Self::start_internal(
Side::Symmetric {
id_s: id_s.to_owned().0,
},
password,
xy_scalar,
)
}
}
impl fmt::Debug for Side {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Side::A { id_a, id_b } => fmt
.debug_struct("Side::A")
.field("idA", &MaybeUtf8(id_a))
.field("idB", &MaybeUtf8(id_b))
.finish(),
Side::B { id_a, id_b } => fmt
.debug_struct("Side::B")
.field("idA", &MaybeUtf8(id_a))
.field("idB", &MaybeUtf8(id_b))
.finish(),
Side::Symmetric { id_s } => fmt
.debug_struct("Side::Symmetric")
.field("idS", &MaybeUtf8(id_s))
.finish(),
}
}
}
impl<G: Group> fmt::Debug for Spake2<G> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("SPAKE2")
.field("group", &G::name())
.field("side", &self.side)
.finish()
}
}
struct MaybeUtf8<'a>(&'a [u8]);
impl fmt::Debug for MaybeUtf8<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Ok(s) = str::from_utf8(self.0) {
write!(fmt, "(s={})", s)
} else {
write!(fmt, "(hex=")?;
for byte in self.0 {
write!(fmt, "{:x}", byte)?;
}
write!(fmt, ")")
}
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use crate::*;
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
use num_bigint::BigUint;
fn decimal_to_scalar(d: &[u8]) -> c2_Scalar {
let bytes = BigUint::parse_bytes(d, 10).unwrap().to_bytes_le();
assert_eq!(bytes.len(), 32);
let mut b2 = [0u8; 32];
b2.copy_from_slice(&bytes);
c2_Scalar::from_bytes_mod_order(b2)
}
#[test]
fn test_convert() {
let t1_decimal =
b"2238329342913194256032495932344128051776374960164957527413114840482143558222";
let t1_scalar = decimal_to_scalar(t1_decimal);
let t1_bytes = t1_scalar.to_bytes();
let expected = [
0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84, 0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2,
0x7d, 0x52, 0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44, 0xd4, 0x49, 0xf4, 0xa8,
0x79, 0xd9, 0xf2, 0x04,
];
assert_eq!(t1_bytes, expected);
}
#[test]
fn test_serialize_basepoint() {
let exp = "5866666666666666666666666666666666666666666666666666666666666666";
let base_vec = ED25519_BASEPOINT_POINT.compress().as_bytes().to_vec();
let base_hex = hex::encode(base_vec);
println!("exp: {:?}", exp);
println!("got: {:?}", base_hex);
assert_eq!(exp, base_hex);
}
#[test]
fn test_password_to_scalar() {
let password = Password::new(b"password");
let expected_pw_scalar = decimal_to_scalar(
b"3515301705789368674385125653994241092664323519848410154015274772661223168839",
);
let pw_scalar = Ed25519Group::hash_to_scalar(&password);
println!("exp: {:?}", hex::encode(expected_pw_scalar.as_bytes()));
println!("got: {:?}", hex::encode(pw_scalar.as_bytes()));
assert_eq!(&pw_scalar, &expected_pw_scalar);
}
#[test]
fn test_sizes() {
let (s1, msg1) = Spake2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
assert_eq!(msg1.len(), 1 + 32);
let (s2, msg2) = Spake2::<Ed25519Group>::start_b(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
assert_eq!(msg2.len(), 1 + 32);
let key1 = s1.finish(&msg2).unwrap();
let key2 = s2.finish(&msg1).unwrap();
assert_eq!(key1.len(), 32);
assert_eq!(key2.len(), 32);
let (s1, msg1) = Spake2::<Ed25519Group>::start_symmetric(
&Password::new(b"password"),
&Identity::new(b"idS"),
);
assert_eq!(msg1.len(), 1 + 32);
let (s2, msg2) = Spake2::<Ed25519Group>::start_symmetric(
&Password::new(b"password"),
&Identity::new(b"idS"),
);
assert_eq!(msg2.len(), 1 + 32);
let key1 = s1.finish(&msg2).unwrap();
let key2 = s2.finish(&msg1).unwrap();
assert_eq!(key1.len(), 32);
assert_eq!(key2.len(), 32);
}
#[test]
fn test_hash_ab() {
let key = ed25519::hash_ab(
b"pw",
b"idA",
b"idB",
b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", b"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
b"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK",
);
let expected_key = "d59d9ba920f7092565cec747b08d5b2e981d553ac32fde0f25e5b4a4cfca3efd";
assert_eq!(hex::encode(key), expected_key);
}
#[test]
fn test_hash_symmetric() {
let key = ed25519::hash_symmetric(
b"pw",
b"idSymmetric",
b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
b"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
b"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK",
);
let expected_key = "b0b31e4401aae37d91a9a8bf6fbb1298cafc005ff9142e3ffc5b9799fb11128b";
assert_eq!(hex::encode(key), expected_key);
}
#[test]
fn test_asymmetric() {
let scalar_a = decimal_to_scalar(
b"2611694063369306139794446498317402240796898290761098242657700742213257926693",
);
let scalar_b = decimal_to_scalar(
b"7002393159576182977806091886122272758628412261510164356026361256515836884383",
);
let expected_pw_scalar = decimal_to_scalar(
b"3515301705789368674385125653994241092664323519848410154015274772661223168839",
);
println!("scalar_a is {}", hex::encode(scalar_a.as_bytes()));
let (s1, msg1) = Spake2::<Ed25519Group>::start_a_internal(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
scalar_a,
);
let expected_msg1 = "416fc960df73c9cf8ed7198b0c9534e2e96a5984bfc5edc023fd24dacf371f2af9";
println!();
println!("xys1: {:?}", hex::encode(s1.xy_scalar.as_bytes()));
println!();
println!("pws1: {:?}", hex::encode(s1.password_scalar.as_bytes()));
println!("exp : {:?}", hex::encode(expected_pw_scalar.as_bytes()));
println!();
println!("msg1: {:?}", hex::encode(&msg1));
println!("exp : {:?}", expected_msg1);
println!();
assert_eq!(
hex::encode(expected_pw_scalar.as_bytes()),
hex::encode(s1.password_scalar.as_bytes())
);
assert_eq!(hex::encode(&msg1), expected_msg1);
let (s2, msg2) = Spake2::<Ed25519Group>::start_b_internal(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
scalar_b,
);
assert_eq!(expected_pw_scalar, s2.password_scalar);
assert_eq!(
hex::encode(&msg2),
"42354e97b88406922b1df4bea1d7870f17aed3dba7c720b313edae315b00959309"
);
let key1 = s1.finish(&msg2).unwrap();
let key2 = s2.finish(&msg1).unwrap();
assert_eq!(key1, key2);
assert_eq!(
hex::encode(key1),
"712295de7219c675ddd31942184aa26e0a957cf216bc230d165b215047b520c1"
);
}
#[test]
fn test_debug() {
let (s1, _msg1) = Spake2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
println!("s1: {:?}", s1);
assert_eq!(
format!("{:?}", s1),
"SPAKE2 { group: \"Ed25519\", side: Side::A { idA: (s=idA), idB: (s=idB) } }"
);
let (s2, _msg1) = Spake2::<Ed25519Group>::start_symmetric(
&Password::new(b"password"),
&Identity::new(b"idS"),
);
println!("s2: {:?}", s2);
assert_eq!(
format!("{:?}", s2),
"SPAKE2 { group: \"Ed25519\", side: Side::Symmetric { idS: (s=idS) } }"
);
}
}