Skip to content

Commit

Permalink
feat: Include certificate roots and certificate policy in GroupContex…
Browse files Browse the repository at this point in the history
…t - WPB-1188

Co-authored-by: Mathieu Amiot <[email protected]>

Co-authored-by: beltram <[email protected]>
  • Loading branch information
2 people authored and augustocdias committed Jul 24, 2023
1 parent 0429aca commit e81b4b9
Show file tree
Hide file tree
Showing 20 changed files with 2,266 additions and 37 deletions.
64 changes: 63 additions & 1 deletion crypto-ffi/bindings/js/CoreCrypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@ export interface ConversationConfiguration {
* Implementation specific configuration
*/
custom?: CustomConfiguration;
/**
* Trust anchors to be added in the group's context extensions
*/
perDomainTrustAnchors?: PerDomainTrustAnchor[];
}

/**
* A wrapper containing the configuration for trust anchors to be added in the group's context
* extensions
*/
export interface PerDomainTrustAnchor {
/**
* Domain name of the owning backend this anchor refers to. One of the certificate in the chain has to have this domain in its SANs
*/
domain_name: string,
/**
* PEM encoded (partial) certificate chain. This contains the certificate chain for the CA certificate issuing the E2E Identity certificates
*/
intermediate_certificate_chain: string,
}

/**
Expand Down Expand Up @@ -884,11 +903,13 @@ export class CoreCrypto {
configuration: ConversationConfiguration = {}
) {
try {
const { ciphersuite, externalSenders, custom = {} } = configuration || {};
const { ciphersuite, externalSenders, custom = {}, perDomainTrustAnchors = [] } = configuration || {};
const config = new CoreCrypto.#module.ConversationConfiguration(
ciphersuite,
externalSenders,
custom?.keyRotationSpan,
custom?.wirePolicy,
perDomainTrustAnchors as any[],
);
const ret = await CoreCryptoError.asyncMapErr(this.#cc.create_conversation(conversationId, creatorCredentialType, config));
return ret;
Expand Down Expand Up @@ -953,6 +974,47 @@ export class CoreCrypto {
));
}

/**
* Updates the trust anchors for a conversation. This should be called when a federated event happens (new team added/removed).
* Clients should add and/or remove trust anchors from the new backend to the conversation. The method will check
* for duplicated domains and the validity of the certificate chain.
*
* **CAUTION**: {@link CoreCrypto.commitAccepted} **HAS TO** be called afterwards **ONLY IF** the Delivery Service responds
* '200 OK' to the {@link CommitBundle} upload. It will "merge" the commit locally i.e. increment the local group
* epoch, use new encryption secrets etc...
*
* @param conversationId - The ID of the conversation
* @param removeDomainNames - Domains to remove from the trust anchors
* @param addTrustAnchors - New trust anchors to add to the conversation
*
* @returns A {@link CommitBundle}
*/
async update_trust_anchors_from_conversation(conversationId: ConversationId, removeDomainNames: string[], addTrustAnchors: PerDomainTrustAnchor[]): Promise<CommitBundle> {
try {
const ffiRet: CoreCryptoFfiTypes.CommitBundle = await CoreCryptoError.asyncMapErr(this.#cc.update_trust_anchors_from_conversation(
conversationId,
removeDomainNames,
addTrustAnchors
));

const gi = ffiRet.group_info;

const ret: CommitBundle = {
welcome: ffiRet.welcome,
commit: ffiRet.commit,
groupInfo: {
encryptionType: gi.encryption_type,
ratchetTreeType: gi.ratchet_tree_type,
payload: gi.payload
},
};

return ret;
} catch(e) {
throw CoreCryptoError.fromStdError(e as Error);
}
}

/**
* Ingest a TLS-serialized MLS welcome message to join an existing MLS group
*
Expand Down
54 changes: 35 additions & 19 deletions crypto-ffi/bindings/kt/main/com/wire/crypto/client/MLSClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,20 @@ typealias MLSKeyPackage = ByteArray
open class GroupInfoBundle(
var encryptionType: MlsGroupInfoEncryptionType,
var ratchetTreeType: MlsRatchetTreeType,
var payload: ByteArray
var payload: ByteArray,
)

open class CommitBundle(
val commit: ByteArray,
val welcome: ByteArray?,
val groupInfoBundle: GroupInfoBundle
val groupInfoBundle: GroupInfoBundle,
)

class DecryptedMessageBundle(
val message: ByteArray?,
val commitDelay: Long?,
val senderClientId: ClientId?,
val hasEpochChanged: Boolean
val hasEpochChanged: Boolean,
)

interface MLSClient {
Expand All @@ -75,7 +75,7 @@ interface MLSClient {
groupId: MLSGroupId,
epoch: ULong,
ciphersuite: Ciphersuite,
credentialType: MlsCredentialType
credentialType: MlsCredentialType,
): HandshakeMessage

suspend fun joinByExternalCommit(groupInfo: ByteArray, credentialType: MlsCredentialType): CommitBundle
Expand All @@ -87,7 +87,8 @@ interface MLSClient {
suspend fun createConversation(
groupId: MLSGroupId,
creatorCredentialType: MlsCredentialType,
externalSenders: List<Ed22519Key> = emptyList()
externalSenders: List<Ed22519Key> = emptyList(),
perDomainTrustAnchors: List<PerDomainTrustAnchor> = emptyList(),
)

suspend fun wipeConversation(groupId: MLSGroupId)
Expand All @@ -98,6 +99,12 @@ interface MLSClient {

suspend fun decryptMessage(groupId: MLSGroupId, message: ApplicationMessage): DecryptedMessageBundle

suspend fun updateTrustAnchorsFromConversation(
groupId: MLSGroupId,
removeDomainNames: List<String>,
addTrustAnchors: List<PerDomainTrustAnchor>,
): CommitBundle?

suspend fun commitAccepted(groupId: MLSGroupId)

suspend fun commitPendingProposals(groupId: MLSGroupId): CommitBundle?
Expand All @@ -108,12 +115,12 @@ interface MLSClient {

suspend fun addMember(
groupId: MLSGroupId,
members: List<Pair<ClientId, MLSKeyPackage>>
members: List<Pair<ClientId, MLSKeyPackage>>,
): CommitBundle?

suspend fun removeMember(
groupId: MLSGroupId,
members: List<ClientId>
members: List<ClientId>,
): CommitBundle

suspend fun deriveSecret(groupId: MLSGroupId, keyLength: UInt): ByteArray
Expand Down Expand Up @@ -159,7 +166,7 @@ class MLSClientImpl(
groupId: MLSGroupId,
epoch: ULong,
ciphersuite: Ciphersuite,
credentialType: MlsCredentialType
credentialType: MlsCredentialType,
): HandshakeMessage {
return cc.newExternalAddProposal(
conversationId = groupId.toUByteList(),
Expand All @@ -173,7 +180,7 @@ class MLSClientImpl(
return cc.joinByExternalCommit(
groupInfo.toUByteList(),
defaultGroupConfiguration,
credentialType
credentialType,
).toCommitBundle()
}

Expand All @@ -186,11 +193,12 @@ class MLSClientImpl(
cc.clearPendingGroupFromExternalCommit(groupId.toUByteList())
}

override suspend fun createConversation(groupId: MLSGroupId, creatorCredentialType: MlsCredentialType, externalSenders: List<Ed22519Key>) {
override suspend fun createConversation(groupId: MLSGroupId, creatorCredentialType: MlsCredentialType, externalSenders: List<Ed22519Key>, perDomainTrustAnchors: List<PerDomainTrustAnchor>) {
val conf = ConversationConfiguration(
DEFAULT_CIPHERSUITE,
externalSenders,
defaultGroupConfiguration
defaultGroupConfiguration,
perDomainTrustAnchors,
)

val groupIdAsBytes = groupId.toUByteList()
Expand All @@ -211,6 +219,15 @@ class MLSClientImpl(
return applicationMessage.toByteArray()
}

override suspend fun updateTrustAnchorsFromConversation(
groupId: MLSGroupId,
removeDomainNames: List<String>,
addTrustAnchors: List<PerDomainTrustAnchor>,
): CommitBundle? {
val result = cc.updateTrustAnchorsFromConversation(groupId.toUByteList(), removeDomainNames, addTrustAnchors)
return result.toCommitBundle()
}

override suspend fun decryptMessage(groupId: MLSGroupId, message: ApplicationMessage): DecryptedMessageBundle {
return cc.decryptMessage(groupId.toUByteList(), message.toUByteList()).toDecryptedMessageBundle()
}
Expand All @@ -233,7 +250,7 @@ class MLSClientImpl(

override suspend fun addMember(
groupId: MLSGroupId,
members: List<Pair<ClientId, MLSKeyPackage>>
members: List<Pair<ClientId, MLSKeyPackage>>,
): CommitBundle? {
if (members.isEmpty()) {
return null
Expand All @@ -248,7 +265,7 @@ class MLSClientImpl(

override suspend fun removeMember(
groupId: MLSGroupId,
members: List<ClientId>
members: List<ClientId>,
): CommitBundle {
val clientIds = members.map { it.encodeToByteArray().toUByteList() }
return cc.removeClientsFromConversation(groupId.toUByteList(), clientIds).toCommitBundle()
Expand All @@ -275,33 +292,32 @@ class MLSClientImpl(
fun MemberAddedMessages.toCommitBundle() = CommitBundle(
commit,
welcome,
groupInfo.toGroupInfoBundle()
groupInfo.toGroupInfoBundle(),
)

fun com.wire.crypto.CommitBundle.toCommitBundle() = CommitBundle(
commit,
welcome,
groupInfo.toGroupInfoBundle()
groupInfo.toGroupInfoBundle(),
)

fun ConversationInitBundle.toCommitBundle() = CommitBundle(
commit,
null,
groupInfo.toGroupInfoBundle()
groupInfo.toGroupInfoBundle(),
)

fun com.wire.crypto.GroupInfoBundle.toGroupInfoBundle() = GroupInfoBundle(
encryptionType,
ratchetTreeType,
payload
payload,
)

fun DecryptedMessage.toDecryptedMessageBundle() = DecryptedMessageBundle(
message,
commitDelay?.toLong(),
senderClientId?.let { String(it.toByteArray()) },
hasEpochChanged
hasEpochChanged,
)
}

}
38 changes: 36 additions & 2 deletions crypto-ffi/bindings/swift/Sources/CoreCrypto/CoreCrypto.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public struct ProteusAutoPrekeyBundle: ConvertToInner {
public struct ConversationConfiguration: ConvertToInner {
typealias Inner = CoreCryptoSwift.ConversationConfiguration
func convert() -> Inner {
return CoreCryptoSwift.ConversationConfiguration(ciphersuite: self.ciphersuite, externalSenders: self.externalSenders, custom: self.custom.convert())
return CoreCryptoSwift.ConversationConfiguration(ciphersuite: self.ciphersuite, externalSenders: self.externalSenders, custom: self.custom.convert(), self.perDomainTrustAnchor.convert())
}

/// Conversation ciphersuite
Expand All @@ -192,11 +192,32 @@ public struct ConversationConfiguration: ConvertToInner {
public var externalSenders: [[UInt8]]
/// Implementation specific configuration
public var custom: CustomConfiguration
/// Trust anchors to be added in the group's context extensions
public var perDomainTrustAnchor: PerDomainTrustAnchor

public init(ciphersuite: UInt16, externalSenders: [[UInt8]], custom: CustomConfiguration) {
public init(ciphersuite: UInt16, externalSenders: [[UInt8]], custom: CustomConfiguration, perDomainTrustAnchor: PerDomainTrustAnchor) {
self.ciphersuite = ciphersuite
self.externalSenders = externalSenders
self.custom = custom
self.perDomainTrustAnchor = perDomainTrustAnchor
}
}

/// A wrapper containing the configuration for trust anchors to be added in the group's context extensions
public struct PerDomainTrustAnchor: ConvertToInner {
typealias Inner = CoreCryptoSwift.PerDomainTrustAnchor
func convert() -> Inner {
return CoreCryptoSwift.PerDomainTrustAnchor(domain_name: self.domainName, intermediate_certificate_chain: self.intermediateCertificateChain)
}

/// Domain name in which the trust anchor belongs to
public var domainName: String
/// PEM encoded certificate chain
public var intermediateCertificateChain: String

public init(domainName: String, intermediateCertificateChain: String) {
self.domainName = domainName
self.intermediateCertificateChain = intermediateCertificateChain
}
}

Expand Down Expand Up @@ -715,6 +736,19 @@ public class CoreCryptoWrapper {
return try await self.coreCrypto.encryptMessage(conversationId: conversationId, message: message)
}

/// Updates the trust anchors for a conversation
///
/// - parameter conversationId - The ID of the conversation
/// - parameter removeDomainNames - Domains to remove from the trust anchors
/// - parameter addTrustAnchors - New trust anchors to add to the conversation
///
/// - returns: A ``CommitBundle`` byte array to fan out to the Delivery Service
public func updateTrustAnchorsFromConversation(conversationId: ConversationId, removeDomainNames: [string], addTrustAnchors: [PerDomainTrustAnchor]) async throws -> CommitBundle {
return try await self.coreCrypto.update_trust_anchors_from_conversation(conversationId: conversationId, removeDomainNames: removeDomainNames, addTrustAnchors: addTrustAnchors.map({ (anchor) -> CoreCryptoSwift.PerDomainTrustAnchor in
return anchor.convert()
})).convertTo()
}

/// Creates a new add proposal within a group
///
/// - parameter conversationId: conversation identifier
Expand Down
14 changes: 14 additions & 0 deletions crypto-ffi/src/CoreCrypto.udl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ dictionary ConversationConfiguration {
Ciphersuite ciphersuite;
sequence<bytes> external_senders;
CustomConfiguration custom;
sequence<PerDomainTrustAnchor> per_domain_trust_anchors;
};

dictionary PerDomainTrustAnchor {
string domain_name;
string intermediate_certificate_chain;
};

dictionary CustomConfiguration {
Expand Down Expand Up @@ -179,6 +185,14 @@ enum CryptoError {
"ClearingPendingCommitError",
"SelfCommitIgnored",
"UnmergedPendingGroup",
"X509CertDerError",
"PemError",
"DomainNameNotFound",
"DomainNamesDontMatch",
"DuplicateDomainName",
"InvalidCertificateChain",
"EmptyTrustAnchorUdpate",
"DuplicateCertificateChain",
};

enum MlsWirePolicy {
Expand Down
Loading

0 comments on commit e81b4b9

Please sign in to comment.