-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: canonicalize ClientId keeping only the regular version where t…
…he UserId portion is the hyphenated string representation of the UUID. Also apply this to 'getUserIdentities()'
- Loading branch information
Showing
24 changed files
with
348 additions
and
181 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1674,7 +1674,7 @@ export class CoreCrypto { | |
* Creates an enrollment instance with private key material you can use in order to fetch | ||
* a new x509 certificate from the acme server. | ||
* | ||
* @param clientId - client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
* @param clientId - client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
* @param displayName - human-readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
* @param handle - user handle e.g. `[email protected]` | ||
* @param expiryDays - generated x509 certificate expiry | ||
|
@@ -1691,7 +1691,7 @@ export class CoreCrypto { | |
* Generates an E2EI enrollment instance for a "regular" client (with a Basic credential) willing to migrate to E2EI. | ||
* Once the enrollment is finished, use the instance in {@link CoreCrypto.e2eiRotateAll} to do the rotation. | ||
* | ||
* @param clientId - client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
* @param clientId - client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
* @param displayName - human-readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
* @param handle - user handle e.g. `[email protected]` | ||
* @param expiryDays - generated x509 certificate expiry | ||
|
@@ -1710,7 +1710,7 @@ export class CoreCrypto { | |
* has been revoked. It lets you change the DisplayName or the handle | ||
* if you need to. Once the enrollment is finished, use the instance in {@link CoreCrypto.e2eiRotateAll} to do the rotation. | ||
* | ||
* @param clientId - client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
* @param clientId - client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
* @param expiryDays - generated x509 certificate expiry | ||
* @param ciphersuite - for generating signing key material | ||
* @param displayName - human-readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
|
@@ -1821,7 +1821,7 @@ export class CoreCrypto { | |
* If no member has a x509 certificate, it will return an empty Vec. | ||
* | ||
* @param conversationId - identifier of the conversation | ||
* @param userIds - user identifiers e.g. t6wRpI8BRSeviBwwiFp5MQ which is a base64UrlUnpadded UUIDv4 | ||
* @param userIds - user identifiers hyphenated UUIDv4 e.g. 'bd4c7053-1c5a-4020-9559-cd7bf7961954' | ||
* @returns a Map with all the identities for a given users. Consumers are then recommended to reduce those identities to determine the actual status of a user. | ||
*/ | ||
async getUserIdentities(conversationId: ConversationId, userIds: string[]): Promise<Map<string, WireIdentity[]>> { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -937,7 +937,7 @@ test("end-to-end-identity", async () => { | |
const encoder = new TextEncoder(); | ||
const jsonToByteArray = json => encoder.encode(JSON.stringify(json, null, 0)); | ||
|
||
const clientId = "t6wRpI8BRSeviBwwiFp5MQ:[email protected]"; | ||
const clientId = "b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]"; | ||
const displayName = "Alice Smith"; | ||
const handle = "alice_wire"; | ||
const expiryDays = 90; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,7 +56,7 @@ class CoreCryptoCentral private constructor(private val cc: CoreCrypto, private | |
/** | ||
* Creates an enrollment instance with private key material you can use in order to fetch a new x509 certificate from the acme server. | ||
* | ||
* @param clientId client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
* @param clientId client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
* @param displayName human-readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
* @param handle user handle e.g. `[email protected]` | ||
* @param expiryDays generated x509 certificate expiry | ||
|
@@ -79,7 +79,7 @@ class CoreCryptoCentral private constructor(private val cc: CoreCrypto, private | |
* Generates an E2EI enrollment instance for a "regular" client (with a Basic credential) willing to migrate to E2EI. | ||
* Once the enrollment is finished, use the instance in [e2eiRotateAll] to do the rotation. | ||
* | ||
* @param clientId client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
* @param clientId client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
* @param displayName human-readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
* @param handle user handle e.g. `[email protected]` | ||
* @param expiryDays generated x509 certificate expiry | ||
|
@@ -112,7 +112,7 @@ class CoreCryptoCentral private constructor(private val cc: CoreCrypto, private | |
* their credential, either because the former one is expired or it has been revoked. It lets you change the DisplayName | ||
* or the handle if you need to. Once the enrollment is finished, use the instance in [e2eiRotateAll] to do the rotation. | ||
* | ||
* @param clientId client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
* @param clientId client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
* @param expiryDays generated x509 certificate expiry | ||
* @param ciphersuite for generating signing key material | ||
* @param displayName human-readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,7 +35,7 @@ internal class E2EITest { | |
val keyStore = root.resolve("keystore-$aliceId") | ||
val cc = CoreCryptoCentral(keyStore.absolutePath, "secret") | ||
val enrollment = cc.e2eiNewEnrollment( | ||
clientId = "t6wRpI8BRSeviBwwiFp5MQ:[email protected]", | ||
clientId = "b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]", | ||
displayName = "Alice Smith", | ||
handle = "alice_wire", | ||
expiryDays = 90u, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1147,7 +1147,7 @@ public class CoreCryptoWrapper { | |
/// Creates an enrollment instance with private key material you can use in order to fetch | ||
/// a new x509 certificate from the acme server. | ||
/// | ||
/// - parameter clientId: client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
/// - parameter clientId: client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
/// - parameter displayName: human readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
/// - parameter handle: user handle e.g. `[email protected]` | ||
/// - parameter expiryDays: generated x509 certificate expiry | ||
|
@@ -1162,7 +1162,7 @@ public class CoreCryptoWrapper { | |
/// Generates an E2EI enrollment instance for a "regular" client (with a Basic credential) willing to migrate to E2EI. | ||
/// Once the enrollment is finished, use the instance in ``CoreCrypto/e2eiRotateAll`` to do the rotation. | ||
/// | ||
/// - parameter clientId: client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
/// - parameter clientId: client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
/// - parameter displayName: human readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
/// - parameter handle: user handle e.g. `[email protected]` | ||
/// - parameter expiryDays: generated x509 certificate expiry | ||
|
@@ -1178,7 +1178,7 @@ public class CoreCryptoWrapper { | |
/// their credential, either because the former one is expired or it has been revoked. It lets you change | ||
/// the DisplayName or the handle if you need to. Once the enrollment is finished, use the instance in ``CoreCrypto/e2eiRotateAll`` to do the rotation. | ||
/// | ||
/// - parameter clientId: client identifier with user b64Url encoded & clientId hex encoded e.g. `t6wRpI8BRSeviBwwiFp5MQ:[email protected]` | ||
/// - parameter clientId: client identifier with user b64Url encoded & clientId hex encoded e.g. `b7ac11a4-8f01-4527-af88-1c30885a7931:[email protected]` | ||
/// - parameter expiryDays: generated x509 certificate expiry | ||
/// - parameter ciphersuite: For generating signing key material. | ||
/// - parameter displayName: human readable name displayed in the application e.g. `Smith, Alice M (QA)` | ||
|
@@ -1258,7 +1258,7 @@ public class CoreCryptoWrapper { | |
/// If no member has a x509 certificate, it will return an empty Vec. | ||
/// | ||
/// - parameter conversationId: conversation identifier | ||
/// - parameter userIds: user identifiers e.g. t6wRpI8BRSeviBwwiFp5MQ which is a base64UrlUnpadded UUIDv4 | ||
/// - parameter userIds: user identifiers hyphenated UUIDv4 e.g. 'bd4c7053-1c5a-4020-9559-cd7bf7961954' | ||
/// - returns: a Map with all the identities for a given users. Consumers are then recommended to reduce those identities to determine the actual status of a user. | ||
public func getUserIdentities(conversationId: ConversationId, userIds: [String]) async throws -> [String: [WireIdentity]] { | ||
return try await self.coreCrypto.getUserIdentities(conversationId: conversationId, userIds: userIds) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
use crate::{prelude::ClientId, CryptoError, CryptoResult}; | ||
|
||
#[cfg(test)] | ||
const DOMAIN: &str = "wire.com"; | ||
const COLON: u8 = 58; | ||
|
||
/// This format: 'bd4c7053-1c5a-4020-9559-cd7bf7961954:[email protected]' | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Into, derive_more::Deref)] | ||
pub struct WireQualifiedClientId(ClientId); | ||
|
||
#[cfg(test)] | ||
impl WireQualifiedClientId { | ||
pub fn get_user_id(&self) -> String { | ||
let mut split = self.0.split(|b| b == &COLON); | ||
let user_id = split.next().unwrap(); | ||
uuid::Uuid::try_parse_ascii(user_id).unwrap().to_string() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
impl WireQualifiedClientId { | ||
pub fn generate() -> Self { | ||
let user_id = uuid::Uuid::new_v4().to_string(); | ||
let device_id = rand::random::<u64>(); | ||
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())) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
impl std::fmt::Display for WireQualifiedClientId { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{}", std::str::from_utf8(self.as_slice()).unwrap()) | ||
} | ||
} | ||
|
||
/// e.g. from 'vUxwUxxaQCCVWc1795YZVA:[email protected]' | ||
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(); | ||
Ok(Self(client_id.into())) | ||
} | ||
} | ||
|
||
impl std::str::FromStr for WireQualifiedClientId { | ||
type Err = CryptoError; | ||
|
||
fn from_str(s: &str) -> CryptoResult<Self> { | ||
s.as_bytes().try_into() | ||
} | ||
} | ||
|
||
/// This format: 'vUxwUxxaQCCVWc1795YZVA:[email protected]' | ||
#[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 { | ||
Self::generate_from_user_id(&uuid::Uuid::new_v4()) | ||
} | ||
|
||
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 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())) | ||
} | ||
|
||
pub fn from_str_unchecked(s: &str) -> Self { | ||
Self(s.as_bytes().into()) | ||
} | ||
} | ||
|
||
/// e.g. from 'bd4c7053-1c5a-4020-9559-cd7bf7961954:[email protected]' | ||
impl<'a> TryFrom<&'a [u8]> for QualifiedE2eiClientId { | ||
type Error = CryptoError; | ||
|
||
fn try_from(bytes: &'a [u8]) -> CryptoResult<Self> { | ||
let mut split = bytes.split(|b| b == &COLON); | ||
let user_id = split.next().ok_or(CryptoError::InvalidClientId)?; | ||
|
||
let user_id = std::str::from_utf8(user_id)? | ||
.parse::<uuid::Uuid>() | ||
.map_err(|_| CryptoError::InvalidClientId)?; | ||
|
||
let user_id = base64_simd::URL_SAFE_NO_PAD.encode_to_string(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(); | ||
Ok(Self(client_id.into())) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
impl std::str::FromStr for QualifiedE2eiClientId { | ||
type Err = CryptoError; | ||
|
||
fn from_str(s: &str) -> CryptoResult<Self> { | ||
s.as_bytes().try_into() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
impl std::fmt::Display for QualifiedE2eiClientId { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{}", std::str::from_utf8(self.as_slice()).unwrap()) | ||
} | ||
} |
Oops, something went wrong.