Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 13 additions & 12 deletions crypto-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,25 @@ default = ["proteus"]
proteus = ["core-crypto/proteus", "dep:proteus-wasm"]

[dependencies]
thiserror.workspace = true
cfg-if.workspace = true
futures-util.workspace = true
async-trait.workspace = true
tls_codec.workspace = true
async-lock.workspace = true
log.workspace = true
log-reload.workspace = true
serde_json.workspace = true
derive_more.workspace = true
proteus-wasm = { workspace = true, optional = true }
async-trait.workspace = true
cfg-if.workspace = true
core-crypto-keystore.workspace = true
core-crypto-macros.workspace = true
core-crypto.workspace = true
derive_more.workspace = true
futures-util.workspace = true
hex.workspace = true
log-reload.workspace = true
log.workspace = true
mls-crypto-provider.workspace = true
obfuscate.workspace = true
rmp-serde.workspace = true
paste = "1.0.15"
hex.workspace = true
proteus-wasm = { workspace = true, optional = true }
rmp-serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
tls_codec.workspace = true

# see https://github.com/RustCrypto/hashes/issues/404
[target.'cfg(not(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "x86")))'.dependencies]
Expand Down
1 change: 1 addition & 0 deletions crypto-ffi/bindings/js/src/CoreCrypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export type {
} from "./CoreCryptoInstance";

export {
Credential,
CredentialType,
WirePolicy,
GroupInfoEncryptionType,
Expand Down
2 changes: 2 additions & 0 deletions crypto-ffi/bindings/js/src/CoreCryptoMLS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
BufferedDecryptedMessage as BufferedDecryptedMessageFfi,
CommitBundle as CommitBundleFfi,
CredentialType,
Credential,
DecryptedMessage as DecryptedMessageFfi,
DeviceStatus,
MlsGroupInfoEncryptionType as GroupInfoEncryptionType,
Expand All @@ -20,6 +21,7 @@ import {
} from "./autogenerated/core-crypto-ffi";

export {
Credential,
CredentialType,
DeviceStatus,
GroupInfoEncryptionType,
Expand Down
27 changes: 27 additions & 0 deletions crypto-ffi/bindings/js/test/bun/credential.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { setup, teardown } from "./utils";
import { afterEach, test, beforeEach, describe, expect } from "bun:test";
import {
ciphersuiteDefault,
ClientId,
Credential,
CredentialType,
} from "../../src/CoreCrypto";

beforeEach(async () => {
await setup();
});

afterEach(async () => {
await teardown();
});

describe("credentials", () => {
test("basic credential can be created", async () => {
const credential = Credential.basic(
ciphersuiteDefault(),
new ClientId(Buffer.from("any random client id here"))
);
expect(credential.type()).toEqual(CredentialType.Basic);
expect(credential.earliest_validity()).toEqual(0n);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@ fun ByteArray.toAvsSecret() = SecretKey(this)

/** Construct a GroupInfo from bytes */
fun ByteArray.toGroupInfo() = GroupInfo(this)

/** Construct a new Credential from ciphersuite and client id */
@Throws(CoreCryptoException::class)
fun Credential.Companion.basic(
ciphersuite: Ciphersuite,
clientId: ClientId
): Credential = credentialBasic(ciphersuite, clientId)
Original file line number Diff line number Diff line change
Expand Up @@ -475,4 +475,14 @@ class MLSTest : HasMockDeliveryService() {
)
}
}

@Test
fun can_construct_basic_credential(): TestResult {
val scope = TestScope()
return scope.runTest {
val credential = Credential.basic(CIPHERSUITE_DEFAULT, genClientId())
assertEquals(credential.type(), CredentialType.BASIC)
assertEquals<ULong>(credential.earliestValidity(), 0u)
}
}
}
18 changes: 9 additions & 9 deletions crypto-ffi/src/ciphersuite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! it doesn't work on newtypes around external enums. We therefore redefine the ciphersuites enum
//! here with appropriate annotations such that it gets exported to all relevant bindings.

use core_crypto::{CiphersuiteName, MlsCiphersuite};
use core_crypto::{Ciphersuite as CryptoCiphersuite, MlsCiphersuite};
#[cfg(target_family = "wasm")]
use wasm_bindgen::prelude::*;

Expand Down Expand Up @@ -42,7 +42,7 @@ pub enum Ciphersuite {
MLS_256_DHKEMP384_AES256GCM_SHA384_P384 = 0x0007,
}

impl From<Ciphersuite> for CiphersuiteName {
impl From<Ciphersuite> for MlsCiphersuite {
#[inline]
fn from(value: Ciphersuite) -> Self {
(value as u16)
Expand All @@ -51,26 +51,26 @@ impl From<Ciphersuite> for CiphersuiteName {
}
}

impl From<CiphersuiteName> for Ciphersuite {
impl From<MlsCiphersuite> for Ciphersuite {
#[inline]
fn from(value: CiphersuiteName) -> Self {
fn from(value: MlsCiphersuite) -> Self {
(value as u16)
.try_into()
.expect("mls Ciphersuite is a subset of ffi Ciphersuite")
}
}

impl From<Ciphersuite> for MlsCiphersuite {
impl From<Ciphersuite> for CryptoCiphersuite {
#[inline]
fn from(value: Ciphersuite) -> Self {
CiphersuiteName::from(value).into()
MlsCiphersuite::from(value).into()
}
}

impl From<MlsCiphersuite> for Ciphersuite {
impl From<CryptoCiphersuite> for Ciphersuite {
#[inline]
fn from(value: MlsCiphersuite) -> Self {
CiphersuiteName::from(value).into()
fn from(value: CryptoCiphersuite) -> Self {
MlsCiphersuite::from(value).into()
}
}

Expand Down
2 changes: 1 addition & 1 deletion crypto-ffi/src/core_crypto/conversation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl CoreCryptoFfi {
.map_err(RecursiveError::mls_client("getting raw conversation by id"))?
.ciphersuite()
.await;
Ok(Ciphersuite::from(core_crypto::CiphersuiteName::from(cs)))
Ok(Ciphersuite::from(core_crypto::MlsCiphersuite::from(cs)))
}

/// See [core_crypto::Session::conversation_exists]
Expand Down
14 changes: 10 additions & 4 deletions crypto-ffi/src/core_crypto/e2ei/identities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ impl CoreCryptoFfi {
.get_device_identities(&device_ids)
.await?
.into_iter()
.map(WireIdentity::from)
.collect::<Vec<_>>();
.map(WireIdentity::try_from)
.collect::<CoreCryptoResult<Vec<WireIdentity>>>()?;
#[cfg(target_family = "wasm")]
let wire_identities =
serde_wasm_bindgen::to_value(&wire_identities).expect("device identities can always be serialized");
Expand All @@ -64,8 +64,14 @@ impl CoreCryptoFfi {
let identities = conversation.get_user_identities(user_ids.as_slice()).await?;
let identities = identities
.into_iter()
.map(|(k, v)| (k, v.into_iter().map(WireIdentity::from).collect()))
.collect::<HashMap<_, Vec<_>>>();
.map(|(k, v)| -> CoreCryptoResult<_> {
let identities = v
.into_iter()
.map(WireIdentity::try_from)
.collect::<CoreCryptoResult<Vec<_>>>()?;
Ok((k, identities))
})
.collect::<CoreCryptoResult<HashMap<_, _>>>()?;
#[cfg(target_family = "wasm")]
let identities = serde_wasm_bindgen::to_value(&identities).expect("user identities can always be serialized");
Ok(identities)
Expand Down
2 changes: 1 addition & 1 deletion crypto-ffi/src/core_crypto/e2ei/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl CoreCryptoFfi {

/// See [core_crypto::Session::e2ei_is_enabled]
pub async fn e2ei_is_enabled(&self, ciphersuite: Ciphersuite) -> CoreCryptoResult<bool> {
let signature_scheme = core_crypto::MlsCiphersuite::from(ciphersuite).signature_algorithm();
let signature_scheme = core_crypto::Ciphersuite::from(ciphersuite).signature_algorithm();
self.inner
.e2ei_is_enabled(signature_scheme)
.await
Expand Down
14 changes: 10 additions & 4 deletions crypto-ffi/src/core_crypto_context/e2ei.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ impl CoreCryptoContext {

/// See [core_crypto::Session::e2ei_is_enabled]
pub async fn e2ei_is_enabled(&self, ciphersuite: Ciphersuite) -> CoreCryptoResult<bool> {
let sc = core_crypto::MlsCiphersuite::from(ciphersuite).signature_algorithm();
let sc = core_crypto::Ciphersuite::from(ciphersuite).signature_algorithm();
self.inner
.e2ei_is_enabled(sc)
.await
Expand All @@ -216,7 +216,7 @@ impl CoreCryptoContext {

let conversation = self.inner.conversation(conversation_id.as_ref()).await?;
let wire_ids = conversation.get_device_identities(device_ids.as_slice()).await?;
Ok(wire_ids.into_iter().map(Into::into).collect())
wire_ids.into_iter().map(TryInto::try_into).collect()
}

/// See [core_crypto::mls::conversation::Conversation::get_user_identities]
Expand All @@ -233,8 +233,14 @@ impl CoreCryptoContext {
let user_ids = conversation.get_user_identities(user_ids.as_slice()).await?;
let user_ids = user_ids
.into_iter()
.map(|(k, v)| (k, v.into_iter().map(WireIdentity::from).collect()))
.collect::<HashMap<_, Vec<_>>>();
.map(|(k, v)| -> CoreCryptoResult<_> {
let identities = v
.into_iter()
.map(WireIdentity::try_from)
.collect::<CoreCryptoResult<Vec<_>>>()?;
Ok((k, identities))
})
.collect::<CoreCryptoResult<HashMap<_, _>>>()?;
#[cfg(target_family = "wasm")]
let user_ids = serde_wasm_bindgen::to_value(&user_ids)?;
Ok(user_ids)
Expand Down
9 changes: 6 additions & 3 deletions crypto-ffi/src/core_crypto_context/mls.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core_crypto::{
ClientIdentifier, KeyPackageIn, MlsCiphersuite, MlsConversationConfiguration, RecursiveError, VerifiableGroupInfo,
mls::conversation::Conversation as _, transaction_context::Error as TransactionError,
Ciphersuite as CryptoCiphersuite, ClientIdentifier, KeyPackageIn, MlsConversationConfiguration, RecursiveError,
VerifiableGroupInfo, mls::conversation::Conversation as _, transaction_context::Error as TransactionError,
};
use tls_codec::{Deserialize as _, Serialize as _};
#[cfg(target_family = "wasm")]
Expand Down Expand Up @@ -60,7 +60,10 @@ impl CoreCryptoContext {
self.inner
.mls_init(
ClientIdentifier::Basic(client_id.as_cc()),
&ciphersuites.into_iter().map(MlsCiphersuite::from).collect::<Vec<_>>(),
&ciphersuites
.into_iter()
.map(CryptoCiphersuite::from)
.collect::<Vec<_>>(),
)
.await?;
Ok(())
Expand Down
66 changes: 66 additions & 0 deletions crypto-ffi/src/credential.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use core_crypto::{Ciphersuite as CryptoCiphersuite, Credential as CryptoCredential};
use mls_crypto_provider::RustCrypto;
#[cfg(target_family = "wasm")]
use wasm_bindgen::prelude::*;

use crate::{Ciphersuite, CoreCryptoResult, CredentialType, client_id::ClientIdMaybeArc};

/// A cryptographic credential.
///
/// This is tied to a particular client via either its client id or certificate bundle,
/// depending on its credential type, but is independent of any client instance or storage.
///
/// To attach to a particular client instance and store, see [`CoreCryptoContext::add_credential`][crate::CoreCryptoContext::add_credential].
#[derive(Debug, Clone, derive_more::From, derive_more::Into)]
#[cfg_attr(target_family = "wasm", wasm_bindgen, derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(not(target_family = "wasm"), derive(uniffi::Object))]
pub struct Credential(CryptoCredential);

impl Credential {
fn basic_impl(ciphersuite: Ciphersuite, client_id: ClientIdMaybeArc) -> CoreCryptoResult<Self> {
let crypto = RustCrypto::default();
CryptoCredential::basic(
CryptoCiphersuite::from(ciphersuite).signature_algorithm(),
&client_id.as_cc(),
crypto,
)
.map(Into::into)
.map_err(Into::into)
}
}

/// Generate a basic credential.
///
/// The result is independent of any client instance and the database; it lives in memory only.
#[cfg(not(target_family = "wasm"))]
#[uniffi::export]
pub fn credential_basic(ciphersuite: Ciphersuite, client_id: ClientIdMaybeArc) -> CoreCryptoResult<Credential> {
Credential::basic_impl(ciphersuite, client_id)
}

#[cfg(target_family = "wasm")]
#[wasm_bindgen]
impl Credential {
/// Generate a basic credential.
///
/// The result is independent of any client instance and the database; it lives in memory only.
pub fn basic(ciphersuite: Ciphersuite, client_id: ClientIdMaybeArc) -> CoreCryptoResult<Self> {
Credential::basic_impl(ciphersuite, client_id)
}
}

#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[cfg_attr(not(target_family = "wasm"), uniffi::export)]
impl Credential {
/// Get the type of this credential.
pub fn r#type(&self) -> CoreCryptoResult<CredentialType> {
self.0.credential().credential_type().try_into()
}

/// Get the earliest possible validity of this credential, expressed as seconds after the unix epoch.
///
/// Basic credentials have no defined earliest validity and will always return 0.
pub fn earliest_validity(&self) -> u64 {
self.0.earliest_validity()
}
}
20 changes: 12 additions & 8 deletions crypto-ffi/src/credential_type.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#[cfg(target_family = "wasm")]
use wasm_bindgen::prelude::*;

use crate::CoreCryptoError;

/// Type of Credential
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(target_family = "wasm", wasm_bindgen, derive(serde::Serialize, serde::Deserialize))]
Expand All @@ -14,20 +16,22 @@ pub enum CredentialType {
X509 = 0x02,
}

impl From<core_crypto::MlsCredentialType> for CredentialType {
fn from(value: core_crypto::MlsCredentialType) -> Self {
impl TryFrom<core_crypto::CredentialType> for CredentialType {
type Error = CoreCryptoError;
fn try_from(value: core_crypto::CredentialType) -> Result<CredentialType, CoreCryptoError> {
match value {
core_crypto::MlsCredentialType::Basic => Self::Basic,
core_crypto::MlsCredentialType::X509 => Self::X509,
core_crypto::CredentialType::Basic => Ok(Self::Basic),
core_crypto::CredentialType::X509 => Ok(Self::X509),
core_crypto::CredentialType::Unknown(_) => Err(CoreCryptoError::ad_hoc("unknown credential type")),
}
}
}

impl From<CredentialType> for core_crypto::MlsCredentialType {
fn from(value: CredentialType) -> core_crypto::MlsCredentialType {
impl From<CredentialType> for core_crypto::CredentialType {
fn from(value: CredentialType) -> core_crypto::CredentialType {
match value {
CredentialType::Basic => core_crypto::MlsCredentialType::Basic,
CredentialType::X509 => core_crypto::MlsCredentialType::X509,
CredentialType::Basic => core_crypto::CredentialType::Basic,
CredentialType::X509 => core_crypto::CredentialType::X509,
}
}
}
Loading
Loading