Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PLACEHOLDER] wire/stable version #81

Draft
wants to merge 20 commits into
base: feat/rfc9420
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fa8c32e
feat: Added support for Credential validation
OtaK Jan 24, 2024
883d9b1
fix!: Internal API changes to allow plugging credential validation
OtaK Jan 29, 2024
22b004c
fix: Wire together credential validation in usual operations
OtaK Jan 29, 2024
bf5347c
fix: Send/Sync handling for async validations
OtaK Jan 29, 2024
9b724b7
Appease rustfmt
OtaK Feb 13, 2024
8417551
Updated dependencies
OtaK Feb 14, 2024
9c596b1
fix: verify GroupInfo
beltram Dec 22, 2023
3dd1711
fix: validate of Lifetime in KeyPackages only for senders
beltram Feb 21, 2024
8e52f0a
fix: be lenient about KeyPackage lifetime when joining via external c…
beltram Feb 21, 2024
42fbe01
fix: remove x509 expiration validation when decoding a message
beltram Feb 28, 2024
8c2b1d5
fix: handle incomplete write in 'framed_content_tbs_serialized'
beltram Mar 25, 2024
8f9e4a9
chore: apply clippy lints for Rust 1.77
beltram Mar 25, 2024
945654c
feat: expose processed message's credential with its associated publi…
beltram Mar 19, 2024
58fed9f
feat: add 'signature_public_key_len' & 'validate_signature_key' to Cr…
beltram Apr 15, 2024
29163fe
feat: Full p521 support
OtaK Apr 9, 2024
6d2f5a2
chore: remove support for the Kyber-based KEM
istankovic Jul 4, 2024
8f5df06
chore: replace instant with web-time [WPB-14399]
typfel Dec 13, 2024
c83849e
chore: fix clippy warnings after upgrading to rustc 1.83
typfel Dec 13, 2024
41b0ab8
chore: replace -Zprofile with -Cinstrument-coverage since the former …
typfel Dec 13, 2024
54ee2f6
chore: fix broken code coverage job
typfel Dec 16, 2024
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
21 changes: 13 additions & 8 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,28 @@ jobs:
uses: actions/checkout@v3
with:
submodules: true

- uses: dtolnay/rust-toolchain@master
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: nightly
components: rustc, rust-std, cargo, llvm-tools, llvm-tools-preview
run:
- uses: Swatinem/rust-cache@v2
- name: Install grcov
run: cargo install grcov
- name: Run profiling tests
run: cargo test --no-fail-fast -p openmls
run: |
mkdir -p target/debug/coverage
cargo test --no-fail-fast -p openmls
env:
CARGO_INCREMENTAL: '0'
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
LLVM_PROFILE_FILE: 'target/debug/coverage/openmls-%p-%m.profraw'
RUSTFLAGS: '-Cinstrument-coverage -Ccodegen-units=1 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
RUSTDOCFLAGS: '-Cinstrument-coverage -Ccodegen-units=1 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'

- name: Run grcov
id: coverage
uses: actions-rs/[email protected]
run: grcov . -s . --binary-path ./target/debug/ -t lcov --branch --ignore-not-existing -o ./target/debug/coverage/

- name: Upload to codecov.io
uses: codecov/codecov-action@v3
with:
files: ${{ steps.coverage.outputs.report }}
files: target/debug/coverage/lcov
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ resolver = "2"
async-trait = "0.1"

[workspace.dependencies.tls_codec]
version = "0.4.0"
version = "0.4"
features = ["derive", "serde", "mls"]

[workspace.dependencies.tls_codec_derive]
version = "0.4.0"
version = "0.4"
features = ["derive", "serde", "mls"]
1 change: 1 addition & 0 deletions basic_credential/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ serde = "1.0"
ed25519-dalek = { version = "2.0.0-rc.3", features = ["rand_core"] }
p256 = "0.13"
p384 = "0.13"
p521 = "0.13"
secrecy = { version = "0.8", features = ["serde"] }
rand_core = "0.6"
getrandom = { version = "0.2", features = ["js"] }
Expand Down
33 changes: 28 additions & 5 deletions basic_credential/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ impl SignatureKeyPair {
let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into();
(sk.to_bytes().to_vec().into(), pk)
}
SignatureScheme::ECDSA_SECP521R1_SHA512 => {
let sk = p521::ecdsa::SigningKey::random(csprng);
let pk = p521::ecdsa::VerifyingKey::from(&sk)
.to_encoded_point(false)
.to_bytes()
.into();
(sk.to_bytes().to_vec().into(), pk)
}
SignatureScheme::ED25519 => {
let sk = ed25519_dalek::SigningKey::generate(csprng);
let pk = sk.verifying_key();
Expand Down Expand Up @@ -159,31 +167,45 @@ impl SignatureKeyPair {
&private[..ed25519_dalek::SECRET_KEY_LENGTH],
)
.map_err(|_| CryptoError::InvalidKey)?;
let pk = ed25519_dalek::VerifyingKey::try_from(&public[..])
let pk = ed25519_dalek::VerifyingKey::try_from(public.as_slice())
.map_err(|_| CryptoError::InvalidKey)?;

if sk.verifying_key() != pk {
return Err(CryptoError::MismatchKeypair);
}
}
SignatureScheme::ECDSA_SECP256R1_SHA256 => {
let sk = p256::ecdsa::SigningKey::try_from(&private[..])
let sk = p256::ecdsa::SigningKey::from_slice(&private)
.map_err(|_| CryptoError::InvalidKey)?;
let pk = p256::ecdsa::VerifyingKey::try_from(&public[..])
let pk = p256::ecdsa::VerifyingKey::from_sec1_bytes(&public)
.map_err(|_| CryptoError::InvalidKey)?;

if sk.verifying_key() != &pk {
return Err(CryptoError::MismatchKeypair);
}
}
SignatureScheme::ECDSA_SECP384R1_SHA384 => {
let sk = p384::ecdsa::SigningKey::try_from(&private[..])
let sk = p384::ecdsa::SigningKey::from_slice(&private)
.map_err(|_| CryptoError::InvalidKey)?;
let pk = p384::ecdsa::VerifyingKey::try_from(&public[..])

let pk = p384::ecdsa::VerifyingKey::from_sec1_bytes(&public)
.map_err(|_| CryptoError::InvalidKey)?;

if sk.verifying_key() != &pk {
return Err(CryptoError::MismatchKeypair);
}
}
SignatureScheme::ECDSA_SECP521R1_SHA512 => {
let sk = p521::ecdsa::SigningKey::from_slice(&private)
.map_err(|_| CryptoError::InvalidKey)?;
let pk = p521::ecdsa::VerifyingKey::from_sec1_bytes(&public)
.map_err(|_| CryptoError::InvalidKey)?;
let sk_pk = p521::ecdsa::VerifyingKey::from(&sk);

if sk_pk.to_encoded_point(false) != pk.to_encoded_point(false) {
return Err(CryptoError::MismatchKeypair);
}
}
_ => {}
};

Expand Down Expand Up @@ -238,6 +260,7 @@ pub mod tests {
SignatureScheme::ED25519,
SignatureScheme::ECDSA_SECP256R1_SHA256,
SignatureScheme::ECDSA_SECP384R1_SHA384,
SignatureScheme::ECDSA_SECP521R1_SHA512,
];
for scheme in schemes {
let kp = SignatureKeyPair::new(scheme, &mut rand::thread_rng()).unwrap();
Expand Down
8 changes: 4 additions & 4 deletions openmls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ openmls_basic_credential = { version = "0.2.0", path = "../basic_credential", fe
openmls_x509_credential = { version = "0.2.0", path = "../x509_credential" }
x509-cert = "0.2"
subtle = "2.5"
fluvio-wasm-timer = "0.2"
web-time = "1.1.0"
indexmap = "2.0"
itertools = "0.11"
itertools = "0.12"

# Only required for tests.
rand = { version = "0.8", optional = true, features = ["getrandom"] }
getrandom = { version = "0.2", optional = true, features = ["js"] }
serde_json = { version = "1.0", optional = true }
# Crypto backends required for KAT and testing - "test-utils" feature
openmls_rust_crypto = { version = "0.2.0", path = "../openmls_rust_crypto", optional = true }
async-lock = { version = "2.7", optional = true }
async-lock = { version = "3.3", optional = true }
rstest = { version = "0.18.2", optional = true }
rstest_reuse = { version = "0.6.0", optional = true }
tokio = { version = "1.24", optional = true, features = ["macros", "rt", "rt-multi-thread"] }
Expand Down Expand Up @@ -60,7 +60,7 @@ hex = { version = "0.4", features = ["serde"] }
lazy_static = "1.4"
openmls = { path = ".", features = ["test-utils"] }
openmls_traits = { version = "0.2.0", path = "../traits", features = ["test-utils"] }
pretty_env_logger = "0.4"
pretty_env_logger = "0.5"
rstest = "0.18.2"
rstest_reuse = "0.6.0"
tempfile = "3"
Expand Down
2 changes: 1 addition & 1 deletion openmls/src/binary_tree/array_representation/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl<'a, L: Clone + Debug + Default, P: Clone + Debug + Default> From<&'a ABinar
}
}

impl<'a, L: Clone + Debug + Default, P: Clone + Debug + Default> AbDiff<'a, L, P> {
impl<L: Clone + Debug + Default, P: Clone + Debug + Default> AbDiff<'_, L, P> {
// Functions handling interactions with leaves.
///////////////////////////////////////////////

Expand Down
2 changes: 1 addition & 1 deletion openmls/src/binary_tree/array_representation/treemath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl LeafNodeIndex {
}

/// Warning: Only use when the node index represents a leaf node
fn from_tree_index(node_index: u32) -> Self {
pub fn from_tree_index(node_index: u32) -> Self {
debug_assert!(node_index % 2 == 0);
LeafNodeIndex(node_index / 2)
}
Expand Down
15 changes: 12 additions & 3 deletions openmls/src/ciphersuite/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,22 @@ impl From<(&str, &[u8])> for SignContent {
}

/// A public signature key.
#[derive(
Eq, PartialEq, Hash, Debug, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize,
)]
#[allow(clippy::derived_hash_with_manual_eq)]
// because the manual PartialEq impl just turns it const time, it does not change the content of the operation
#[derive(Hash, Debug, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize)]
pub struct SignaturePublicKey {
pub(in crate::ciphersuite) value: VLBytes,
}

impl Eq for SignaturePublicKey {}

impl PartialEq for SignaturePublicKey {
fn eq(&self, other: &Self) -> bool {
use subtle::ConstantTimeEq as _;
self.value.as_slice().ct_eq(other.value.as_slice()).into()
}
}

impl From<Vec<u8>> for SignaturePublicKey {
fn from(value: Vec<u8>) -> Self {
Self {
Expand Down
5 changes: 4 additions & 1 deletion openmls/src/credentials/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
//! This module exposes [`CredentialError`].

use crate::error::LibraryError;
use openmls_traits::authentication_service::CredentialAuthenticationStatus;
use thiserror::Error;

/// An error that occurs in methods of a [`super::Credential`].
#[derive(Error, Debug, PartialEq, Clone)]
#[derive(Error, Debug, PartialEq, Eq, Clone)]
pub enum CredentialError {
/// A library error occurred.
#[error(transparent)]
Expand All @@ -26,4 +27,6 @@ pub enum CredentialError {
/// x509 certificate chain is either unordered or a child is missigned by its issuer
#[error("Invalid x509 certificate chain.")]
InvalidCertificateChain,
#[error("The Authentication Service callback rejected this credential for the following reason: {0}")]
AuthenticationServiceValidationFailure(CredentialAuthenticationStatus),
}
57 changes: 56 additions & 1 deletion openmls/src/credentials/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@

use std::io::{Read, Write};

use openmls_traits::{
authentication_service::{AuthenticationServiceDelegate, CredentialAuthenticationStatus},
OpenMlsCryptoProvider,
};
use serde::{Deserialize, Serialize};
use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize, VLBytes};

Expand All @@ -33,6 +37,7 @@ mod codec;
mod tests;

use errors::*;

use openmls_x509_credential::X509Ext;
use x509_cert::{der::Decode, PkiPath};

Expand Down Expand Up @@ -271,6 +276,56 @@ impl Credential {
MlsCredentialType::X509(cert) => cert.identity.as_slice(),
}
}

pub async fn validate(
&self,
backend: &impl OpenMlsCryptoProvider,
) -> Result<(), CredentialError> {
let tmp_certs = if let MlsCredentialType::X509(x509_certs) = &self.credential {
Some(
x509_certs
.certificates
.iter()
.map(|bytes| bytes.as_slice())
.collect::<Vec<_>>(),
)
} else {
None
};

let credential_ref = match &self.credential {
MlsCredentialType::Basic(basic_cred) => {
openmls_traits::authentication_service::CredentialRef::Basic {
identity: basic_cred.identity.as_slice(),
}
}

MlsCredentialType::X509(_) => {
let credential_ref = openmls_traits::authentication_service::CredentialRef::X509 {
certificates: tmp_certs.as_ref().unwrap().as_slice(),
};
credential_ref
}
};

let credential_authentication = backend
.authentication_service()
.validate_credential(credential_ref)
.await;

if !matches!(
credential_authentication,
CredentialAuthenticationStatus::Valid | CredentialAuthenticationStatus::Expired
) {
return Err(CredentialError::AuthenticationServiceValidationFailure(
credential_authentication,
));
}

drop(tmp_certs);

Ok(())
}
}

impl From<MlsCredentialType> for Credential {
Expand Down Expand Up @@ -302,7 +357,7 @@ pub struct BasicCredential {
identity: VLBytes,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
/// A wrapper around a credential with a corresponding public key.
pub struct CredentialWithKey {
/// The [`Credential`].
Expand Down
1 change: 1 addition & 0 deletions openmls/src/framing/message_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ impl MlsMessageIn {
/// Enum containing a message for use with `process_message` and an
/// [`MlsGroup`]. Both [`PublicMessage`] and [`PrivateMessage`] implement
/// [`Into<ProtocolMessage>`].
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
pub enum ProtocolMessage {
/// A [`ProtocolMessage`] containing a [`PrivateMessage`].
Expand Down
25 changes: 15 additions & 10 deletions openmls/src/framing/mls_auth_content_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

use std::io::Read;

use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite};
use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider};
use tls_codec::Serialize as TlsSerializeTrait;

use super::{mls_auth_content::*, mls_content_in::*, *};
Expand Down Expand Up @@ -47,23 +47,28 @@ pub struct AuthenticatedContentIn {

impl AuthenticatedContentIn {
/// Returns a [`AuthenticatedContent`] after successful validation.
pub fn validate(
pub async fn validate(
self,
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
backend: &impl OpenMlsCryptoProvider,
sender_context: Option<SenderContext>,
protocol_version: ProtocolVersion,
group: &PublicGroup,
sender: bool,
) -> Result<AuthenticatedContent, ValidationError> {
Ok(AuthenticatedContent {
wire_format: self.wire_format,
content: self.content.validate(
ciphersuite,
crypto,
sender_context,
protocol_version,
group,
)?,
content: self
.content
.validate(
ciphersuite,
backend,
sender_context,
protocol_version,
group,
sender,
)
.await?,
auth: self.auth,
})
}
Expand Down
3 changes: 2 additions & 1 deletion openmls/src/framing/mls_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ pub(crate) fn framed_content_tbs_serialized<'context, W: Write>(
// NewMemberCommit.
written += match serialized_context.into() {
Some(context) if matches!(sender, Sender::Member(_) | Sender::NewMemberCommit) => {
writer.write(context)?
writer.write_all(context)?;
context.len()
}
_ => 0,
};
Expand Down
Loading
Loading