use openmls::credentials::{Credential, CredentialBundle};
use openmls::extensions::{Extension, LifetimeExtension};
use openmls::key_packages::{KeyPackage, KeyPackageBundle};
use openmls::prelude::SignatureKeypair;
use openmls_traits::key_store::OpenMlsKeyStore;
use openmls_traits::OpenMlsCryptoProvider;
use tls_codec::Serialize;
use crate::identity::KeyPair;
use crate::secret_group::mls::{MlsError, MLS_CIPHERSUITE_NAME, MLS_LIFETIME_EXTENSION_DAYS};
#[derive(Debug, Clone)]
pub struct MlsMember {
credential_bundle: CredentialBundle,
}
impl MlsMember {
pub fn new(
provider: &impl OpenMlsCryptoProvider,
key_pair: &KeyPair,
) -> Result<Self, MlsError> {
let public_key = key_pair.public_key().to_bytes();
let private_key = key_pair.private_key().to_bytes();
let full_key = [private_key, public_key].concat();
let signature_key_pair = SignatureKeypair::from_bytes(
MLS_CIPHERSUITE_NAME.into(),
full_key.to_vec(),
public_key.to_vec(),
);
let credential_bundle =
CredentialBundle::from_parts(public_key.to_vec(), signature_key_pair);
let name = credential_bundle
.credential()
.signature_key()
.tls_serialize_detached()
.map_err(|_| MlsError::KeyStoreSerialization)?;
provider
.key_store()
.store(&name, &credential_bundle)
.map_err(|_| MlsError::KeyStoreSerialization)?;
Ok(Self { credential_bundle })
}
pub fn credential(&self) -> &Credential {
self.credential_bundle.credential()
}
pub fn key_package(
&self,
provider: &impl OpenMlsCryptoProvider,
) -> Result<KeyPackage, MlsError> {
let lifetime_extension =
Extension::LifeTime(LifetimeExtension::new(MLS_LIFETIME_EXTENSION_DAYS));
let key_package_bundle = KeyPackageBundle::new(
&[MLS_CIPHERSUITE_NAME],
&self.credential_bundle,
provider,
vec![lifetime_extension],
)?;
let key_package = key_package_bundle.key_package().clone();
let key_package_hash = key_package.hash_ref(provider.crypto())?;
provider
.key_store()
.store(key_package_hash.as_slice(), &key_package_bundle)
.map_err(|_| MlsError::KeyStoreSerialization)?;
Ok(key_package)
}
}
#[cfg(test)]
mod tests {
use openmls::ciphersuite::signable::Verifiable;
use crate::identity::KeyPair;
use crate::secret_group::MlsProvider;
use super::MlsMember;
#[test]
fn panda_identity() {
let key_pair = KeyPair::new();
let provider = MlsProvider::new();
let member = MlsMember::new(&provider, &key_pair).unwrap();
assert_eq!(
member.credential().identity(),
key_pair.public_key().to_bytes()
);
}
#[test]
fn key_package_verify() {
let key_pair = KeyPair::new();
let provider = MlsProvider::new();
let member = MlsMember::new(&provider, &key_pair).unwrap();
let key_package = member.key_package(&provider).unwrap();
assert!(key_package
.verify_no_out(&provider, member.credential())
.is_ok());
}
}