Skip to content

Commit

Permalink
feat!: wpb-5608
Browse files Browse the repository at this point in the history
  • Loading branch information
beltram committed Nov 30, 2023
1 parent e16624f commit b36dd84
Show file tree
Hide file tree
Showing 10 changed files with 399 additions and 111 deletions.
28 changes: 16 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,28 +46,32 @@ git = "https://github.com/wireapp/proteus"
branch = "2.x"

[patch.crates-io.openmls]
package = "openmls"
git = "https://github.com/wireapp/openmls"
#package = "openmls"
#git = "https://github.com/wireapp/openmls"
#tag = "v1.0.0-pre.core-crypto-1.0.0"
branch = "feat/rfc9420"
#branch = "feat/rfc9420"
path = "../openmls/openmls"

[patch.crates-io.openmls_traits]
package = "openmls_traits"
git = "https://github.com/wireapp/openmls"
#package = "openmls_traits"
#git = "https://github.com/wireapp/openmls"
#tag = "v1.0.0-pre.core-crypto-1.0.0"
branch = "feat/rfc9420"
#branch = "feat/rfc9420"
path = "../openmls/traits"

[patch.crates-io.openmls_basic_credential]
package = "openmls_basic_credential"
git = "https://github.com/wireapp/openmls"
#package = "openmls_basic_credential"
#git = "https://github.com/wireapp/openmls"
#tag = "v1.0.0-pre.core-crypto-1.0.0"
branch = "feat/rfc9420"
#branch = "feat/rfc9420"
path = "../openmls/basic_credential"

[patch.crates-io.openmls_x509_credential]
package = "openmls_x509_credential"
git = "https://github.com/wireapp/openmls"
#package = "openmls_x509_credential"
#git = "https://github.com/wireapp/openmls"
#tag = "v1.0.0-pre.core-crypto-1.0.0"
branch = "feat/rfc9420"
#branch = "feat/rfc9420"
path = "../openmls/x509_credential"

[patch.crates-io.hpke]
git = "https://github.com/wireapp/rust-hpke.git"
Expand Down
2 changes: 2 additions & 0 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ oid-registry = "0.6"
async-recursion = "1"
uniffi = { workspace = true, optional = true }
itertools = "0.12"
base64-simd = "0.8"
uuid = { version = "1.6", features = ["v4"] }

[dependencies.proteus-wasm]
version = "2.1"
Expand Down
253 changes: 253 additions & 0 deletions crypto/src/e2e_identity/id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
use crate::{prelude::ClientId, CryptoError, CryptoResult};
use std::str::FromStr;

#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Into, derive_more::Deref)]
pub struct WireQualifiedClientId(ClientId);

impl WireQualifiedClientId {
pub fn get_user_id(&self) -> CryptoResult<String> {
const COLON: u8 = 58;
let mut split = self.0.split(|b| b == &COLON);
let user_id = split.next().ok_or(CryptoError::InvalidClientId)?;

let user_id = uuid::Uuid::try_parse_ascii(user_id).map_err(|_| CryptoError::InvalidClientId)?;
let mut buf = [0; uuid::fmt::Hyphenated::LENGTH];
let user_id = user_id.hyphenated().encode_lower(&mut buf);
Ok(user_id.to_string())
}
}

#[cfg(test)]
impl WireQualifiedClientId {
pub fn generate() -> ClientId {
let user_id = uuid::Uuid::new_v4().to_string();
let device_id = rand::random::<u64>();
let domain = "wire.com";
let client_id = format!("{user_id}:{device_id:x}@{domain}");
client_id.into_bytes().into()
}
}

impl ToString for WireQualifiedClientId {
fn to_string(&self) -> String {
String::from_utf8(self.0.to_vec()).unwrap()
}
}

impl<'a> TryFrom<&'a [u8]> for WireQualifiedClientId {
type Error = CryptoError;

fn try_from(bytes: &'a [u8]) -> CryptoResult<Self> {
const COLON: u8 = 58;
let mut split = bytes.split(|b| b == &COLON);
let user_id = split.next().ok_or(CryptoError::InvalidClientId)?;

let user_id = base64_simd::URL_SAFE_NO_PAD
.decode_to_vec(user_id)
.map_err(|_| CryptoError::InvalidClientId)?;

let user_id = uuid::Uuid::from_slice(&user_id).map_err(|_| CryptoError::InvalidClientId)?;
let mut buf = [0; uuid::fmt::Hyphenated::LENGTH];
let user_id = user_id.hyphenated().encode_lower(&mut buf);

let rest = split.next().ok_or(CryptoError::InvalidClientId)?;
if split.next().is_some() {
return Err(CryptoError::InvalidClientId);
}

let client_id = [user_id.as_bytes(), &[COLON], rest].concat();
let client_id = ClientId::from(client_id);
Ok(Self(client_id))
}
}

impl FromStr for WireQualifiedClientId {
type Err = CryptoError;

fn from_str(s: &str) -> CryptoResult<Self> {
s.as_bytes().try_into()
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Into, derive_more::Deref)]
pub struct QualifiedE2eiClientId(ClientId);

#[cfg(test)]
impl QualifiedE2eiClientId {
pub fn generate() -> Self {
use base64::Engine as _;

let user_id = uuid::Uuid::new_v4();
let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(user_id.as_bytes());

let device_id = rand::random::<u64>();
let domain = "wire.com";
let client_id = format!("{user_id}:{device_id:x}@{domain}");
Self(client_id.into_bytes().into())
}

pub fn generate_from_user_id(user_id: &uuid::Uuid) -> Self {
use base64::Engine as _;

let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(user_id.as_bytes());

let device_id = rand::random::<u64>();
let domain = "wire.com";
let client_id = format!("{user_id}:{device_id:x}@{domain}");
Self(client_id.into_bytes().into())
}

pub fn to_static_str(&self) -> &'static str {
Box::leak(Box::new(self.to_string()))
}
}

impl<'a> TryFrom<&'a [u8]> for QualifiedE2eiClientId {
type Error = CryptoError;

fn try_from(bytes: &'a [u8]) -> CryptoResult<Self> {
const COLON: u8 = 58;
let mut split = bytes.split(|b| b == &COLON);
let user_id = split.next().ok_or(CryptoError::InvalidClientId)?;

let user_id = uuid::Uuid::from_str(std::str::from_utf8(user_id)?).map_err(|_| CryptoError::InvalidClientId)?;
// let mut buf = [0; uuid::fmt::Hyphenated::LENGTH];
// let user_id = user_id.hyphenated().encode_lower(&mut buf);

let user_id = base64_simd::URL_SAFE_NO_PAD.encode_to_string(user_id.as_bytes());
// let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(user_id.as_bytes());

let rest = split.next().ok_or(CryptoError::InvalidClientId)?;
if split.next().is_some() {
return Err(CryptoError::InvalidClientId);
}

let client_id = [user_id.as_bytes(), &[COLON], rest].concat();
let client_id = ClientId::from(client_id);
Ok(Self(client_id))
}
}

#[cfg(test)]
impl FromStr for QualifiedE2eiClientId {
type Err = CryptoError;

fn from_str(s: &str) -> CryptoResult<Self> {
s.as_bytes().try_into()
}
}

impl ToString for QualifiedE2eiClientId {
fn to_string(&self) -> String {
String::from_utf8(self.0.to_vec()).unwrap()
}
}

#[cfg(test)]
impl TryFrom<WireQualifiedClientId> for QualifiedE2eiClientId {
type Error = CryptoError;

fn try_from(wire_id: WireQualifiedClientId) -> CryptoResult<Self> {
wire_id.as_slice().try_into()
}
}

impl TryFrom<QualifiedE2eiClientId> for WireQualifiedClientId {
type Error = CryptoError;

fn try_from(e2ei_id: QualifiedE2eiClientId) -> CryptoResult<Self> {
const COLON: u8 = 58;

let mut split = e2ei_id.0.split(|b| b == &COLON);
let user_id = split.next().ok_or(CryptoError::InvalidClientId)?;

let user_id = base64_simd::URL_SAFE_NO_PAD.decode_to_vec(user_id).unwrap();
let user_id = uuid::Uuid::from_slice(&user_id).map_err(|_| CryptoError::InvalidClientId)?;

let mut buf = [0; uuid::fmt::Hyphenated::LENGTH];
let user_id = user_id.hyphenated().encode_lower(&mut buf);

let rest = split.next().ok_or(CryptoError::InvalidClientId)?;
if split.next().is_some() {
return Err(CryptoError::InvalidClientId);
}

let x = user_id.as_bytes();
let client_id = [x, &[COLON], rest].concat();
let client_id = ClientId::from(client_id);
Ok(Self(client_id))
}
}

/*#[cfg(test)]
pub mod tests {
use super::*;
use base64::Engine;
const WIRE_ID: &'static str = "e77cf2a2-100f-49c4-872f-5041a407c3db:[email protected]";
const E2EI_ID: &'static str = "53zyohAPScSHL1BBpAfD2w:[email protected]";
#[test]
fn toto() {
let uid = "e77cf2a2-100f-49c4-872f-5041a407c3db";
let uid = uuid::Uuid::from_str(uid).unwrap();
let uid = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(uid.as_bytes());
println!("{}", uid);
}
mod parse {
use super::*;
#[test]
fn should_parse_wire_qualified_client_id() {
assert!(WIRE_ID.parse::<WireQualifiedClientId>().is_ok());
assert!(matches!(
WIRE_ID.parse::<E2eiClientId>().unwrap_err(),
CryptoError::InvalidClientId
));
}
#[test]
fn should_parse_e2ei_client_id() {
assert!(E2EI_ID.parse::<E2eiClientId>().is_ok());
assert!(matches!(
E2EI_ID.parse::<WireQualifiedClientId>().unwrap_err(),
CryptoError::InvalidClientId
));
}
}
mod convert {
use super::*;
#[test]
fn to_wire_qualified_client_id_should_work() {
let e2ei_id = E2EI_ID.parse::<E2eiClientId>().unwrap();
WireQualifiedClientId::try_from(e2ei_id).unwrap();
}
#[test]
fn to_e2ei_should_work() {
let wire_id = WIRE_ID.parse::<WireQualifiedClientId>().unwrap();
E2eiClientId::try_from(wire_id).unwrap();
}
}
mod generate {
use super::*;
#[test]
fn e2ei() {
let new = E2eiClientId::generate();
let g = WireQualifiedClientId::try_from(new).unwrap();
let fog = E2eiClientId::try_from(g).unwrap();
}
#[test]
fn wire_qualified_client_id() {
let new = WireQualifiedClientId::generate();
let g = E2eiClientId::try_from(new).unwrap();
let fog = WireQualifiedClientId::try_from(g).unwrap();
}
}
}*/
Loading

0 comments on commit b36dd84

Please sign in to comment.