use crate::{
ciphersuite::{
hash_ref::{make_key_package_ref, KeyPackageRef},
signable::*,
*,
},
credentials::*,
error::LibraryError,
extensions::{Extension, ExtensionType, Extensions, LastResortExtension},
storage::OpenMlsProvider,
treesync::{
node::{
encryption_keys::{EncryptionKeyPair, EncryptionPrivateKey},
leaf_node::{Capabilities, LeafNodeSource, NewLeafNodeParams, TreeInfoTbs},
},
LeafNode,
},
versions::ProtocolVersion,
};
use openmls_traits::{
crypto::OpenMlsCrypto, signatures::Signer, storage::StorageProvider, types::Ciphersuite,
};
use serde::{Deserialize, Serialize};
use tls_codec::{
Serialize as TlsSerializeTrait, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
};
use errors::*;
pub mod errors;
pub mod key_package_in;
mod lifetime;
#[cfg(test)]
pub(crate) mod tests;
pub use key_package_in::KeyPackageIn;
pub use lifetime::Lifetime;
#[derive(Debug, Clone, PartialEq, TlsSize, TlsSerialize, Serialize, Deserialize)]
struct KeyPackageTbs {
protocol_version: ProtocolVersion,
ciphersuite: Ciphersuite,
init_key: InitKey,
leaf_node: LeafNode,
extensions: Extensions,
}
impl Signable for KeyPackageTbs {
type SignedOutput = KeyPackage;
fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
self.tls_serialize_detached()
}
fn label(&self) -> &str {
SIGNATURE_KEY_PACKAGE_LABEL
}
}
impl From<KeyPackage> for KeyPackageTbs {
fn from(kp: KeyPackage) -> Self {
kp.payload
}
}
#[derive(Debug, Clone, Serialize, Deserialize, TlsSize, TlsSerialize)]
pub struct KeyPackage {
payload: KeyPackageTbs,
signature: Signature,
}
impl PartialEq for KeyPackage {
fn eq(&self, other: &Self) -> bool {
self.payload == other.payload
}
}
impl SignedStruct<KeyPackageTbs> for KeyPackage {
fn from_payload(payload: KeyPackageTbs, signature: Signature) -> Self {
Self { payload, signature }
}
}
const SIGNATURE_KEY_PACKAGE_LABEL: &str = "KeyPackageTBS";
pub(crate) struct KeyPackageCreationResult {
pub key_package: KeyPackage,
pub encryption_keypair: EncryptionKeyPair,
pub init_private_key: HpkePrivateKey,
}
#[derive(
Debug,
Clone,
PartialEq,
TlsSize,
TlsSerialize,
Serialize,
Deserialize,
TlsDeserialize,
TlsDeserializeBytes,
)]
pub struct InitKey {
key: HpkePublicKey,
}
impl InitKey {
pub fn key(&self) -> &HpkePublicKey {
&self.key
}
pub fn as_slice(&self) -> &[u8] {
self.key.as_slice()
}
}
impl From<Vec<u8>> for InitKey {
fn from(key: Vec<u8>) -> Self {
Self {
key: HpkePublicKey::from(key),
}
}
}
impl From<HpkePublicKey> for InitKey {
fn from(key: HpkePublicKey) -> Self {
Self { key }
}
}
impl KeyPackage {
pub fn builder() -> KeyPackageBuilder {
KeyPackageBuilder::new()
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn create(
ciphersuite: Ciphersuite,
provider: &impl OpenMlsProvider,
signer: &impl Signer,
credential_with_key: CredentialWithKey,
lifetime: Lifetime,
extensions: Extensions,
leaf_node_capabilities: Capabilities,
leaf_node_extensions: Extensions,
) -> Result<KeyPackageCreationResult, KeyPackageNewError> {
if ciphersuite.signature_algorithm() != signer.signature_scheme() {
return Err(KeyPackageNewError::CiphersuiteSignatureSchemeMismatch);
}
let ikm = Secret::random(ciphersuite, provider.rand())
.map_err(LibraryError::unexpected_crypto_error)?;
let init_key = provider
.crypto()
.derive_hpke_keypair(ciphersuite.hpke_config(), ikm.as_slice())
.map_err(|e| {
KeyPackageNewError::LibraryError(LibraryError::unexpected_crypto_error(e))
})?;
let (key_package, encryption_keypair) = Self::new_from_keys(
ciphersuite,
provider,
signer,
credential_with_key,
lifetime,
extensions,
leaf_node_capabilities,
leaf_node_extensions,
init_key.public.into(),
)?;
Ok(KeyPackageCreationResult {
key_package,
encryption_keypair,
init_private_key: init_key.private,
})
}
#[allow(clippy::too_many_arguments)]
fn new_from_keys(
ciphersuite: Ciphersuite,
provider: &impl OpenMlsProvider,
signer: &impl Signer,
credential_with_key: CredentialWithKey,
lifetime: Lifetime,
extensions: Extensions,
capabilities: Capabilities,
leaf_node_extensions: Extensions,
init_key: InitKey,
) -> Result<(Self, EncryptionKeyPair), KeyPackageNewError> {
let new_leaf_node_params = NewLeafNodeParams {
ciphersuite,
leaf_node_source: LeafNodeSource::KeyPackage(lifetime),
credential_with_key,
capabilities,
extensions: leaf_node_extensions,
tree_info_tbs: TreeInfoTbs::KeyPackage,
};
let (leaf_node, encryption_key_pair) =
LeafNode::new(provider, signer, new_leaf_node_params)?;
let key_package_tbs = KeyPackageTbs {
protocol_version: ProtocolVersion::default(),
ciphersuite,
init_key,
leaf_node,
extensions,
};
let key_package = key_package_tbs.sign(signer)?;
Ok((key_package, encryption_key_pair))
}
pub fn extensions(&self) -> &Extensions {
&self.payload.extensions
}
pub fn check_extension_support(
&self,
required_extensions: &[ExtensionType],
) -> Result<(), KeyPackageExtensionSupportError> {
for required_extension in required_extensions.iter() {
if !self.extensions().contains(*required_extension) {
return Err(KeyPackageExtensionSupportError::UnsupportedExtension);
}
}
Ok(())
}
pub fn hash_ref(&self, crypto: &impl OpenMlsCrypto) -> Result<KeyPackageRef, LibraryError> {
make_key_package_ref(
&self
.tls_serialize_detached()
.map_err(LibraryError::missing_bound_check)?,
self.payload.ciphersuite,
crypto,
)
.map_err(LibraryError::unexpected_crypto_error)
}
pub fn ciphersuite(&self) -> Ciphersuite {
self.payload.ciphersuite
}
pub fn leaf_node(&self) -> &LeafNode {
&self.payload.leaf_node
}
pub fn hpke_init_key(&self) -> &InitKey {
&self.payload.init_key
}
pub fn last_resort(&self) -> bool {
self.payload.extensions.contains(ExtensionType::LastResort)
}
}
impl KeyPackage {
pub(crate) fn protocol_version(&self) -> ProtocolVersion {
self.payload.protocol_version
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct KeyPackageBuilder {
key_package_lifetime: Option<Lifetime>,
key_package_extensions: Option<Extensions>,
leaf_node_capabilities: Option<Capabilities>,
leaf_node_extensions: Option<Extensions>,
last_resort: bool,
}
impl KeyPackageBuilder {
pub fn new() -> Self {
Self {
key_package_lifetime: None,
key_package_extensions: None,
leaf_node_capabilities: None,
leaf_node_extensions: None,
last_resort: false,
}
}
pub fn key_package_lifetime(mut self, lifetime: Lifetime) -> Self {
self.key_package_lifetime.replace(lifetime);
self
}
pub fn key_package_extensions(mut self, extensions: Extensions) -> Self {
self.key_package_extensions.replace(extensions);
self
}
pub fn mark_as_last_resort(mut self) -> Self {
self.last_resort = true;
self
}
pub fn leaf_node_capabilities(mut self, capabilities: Capabilities) -> Self {
self.leaf_node_capabilities.replace(capabilities);
self
}
pub fn leaf_node_extensions(mut self, extensions: Extensions) -> Self {
self.leaf_node_extensions.replace(extensions);
self
}
fn ensure_last_resort(&mut self) {
if self.last_resort {
let last_resort_extension = Extension::LastResort(LastResortExtension::default());
if let Some(extensions) = self.key_package_extensions.as_mut() {
extensions.add_or_replace(last_resort_extension);
} else {
self.key_package_extensions = Some(Extensions::single(last_resort_extension));
}
}
}
#[cfg(test)]
pub(crate) fn build_without_storage(
mut self,
ciphersuite: Ciphersuite,
provider: &impl OpenMlsProvider,
signer: &impl Signer,
credential_with_key: CredentialWithKey,
) -> Result<KeyPackageCreationResult, KeyPackageNewError> {
self.ensure_last_resort();
KeyPackage::create(
ciphersuite,
provider,
signer,
credential_with_key,
self.key_package_lifetime.unwrap_or_default(),
self.key_package_extensions.unwrap_or_default(),
self.leaf_node_capabilities.unwrap_or_default(),
self.leaf_node_extensions.unwrap_or_default(),
)
}
pub fn build(
mut self,
ciphersuite: Ciphersuite,
provider: &impl OpenMlsProvider,
signer: &impl Signer,
credential_with_key: CredentialWithKey,
) -> Result<KeyPackageBundle, KeyPackageNewError> {
self.ensure_last_resort();
let KeyPackageCreationResult {
key_package,
encryption_keypair,
init_private_key,
} = KeyPackage::create(
ciphersuite,
provider,
signer,
credential_with_key,
self.key_package_lifetime.unwrap_or_default(),
self.key_package_extensions.unwrap_or_default(),
self.leaf_node_capabilities.unwrap_or_default(),
self.leaf_node_extensions.unwrap_or_default(),
)?;
let full_kp = KeyPackageBundle {
key_package,
private_init_key: init_private_key,
private_encryption_key: encryption_keypair.private_key().clone(),
};
provider
.storage()
.write_key_package(&full_kp.key_package.hash_ref(provider.crypto())?, &full_kp)
.map_err(|_| KeyPackageNewError::StorageError)?;
Ok(full_kp)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyPackageBundle {
pub(crate) key_package: KeyPackage,
pub(crate) private_init_key: HpkePrivateKey,
pub(crate) private_encryption_key: EncryptionPrivateKey,
}
impl KeyPackageBundle {
pub fn key_package(&self) -> &KeyPackage {
&self.key_package
}
pub fn init_private_key(&self) -> &HpkePrivateKey {
&self.private_init_key
}
pub(crate) fn encryption_key_pair(&self) -> EncryptionKeyPair {
EncryptionKeyPair::from((
self.key_package.leaf_node().encryption_key().clone(),
self.private_encryption_key.clone(),
))
}
}
#[cfg(any(test, feature = "test-utils"))]
impl KeyPackageBundle {
pub fn new(
key_package: KeyPackage,
private_init_key: HpkePrivateKey,
private_encryption_key: EncryptionPrivateKey,
) -> Self {
Self {
key_package,
private_init_key,
private_encryption_key,
}
}
pub fn encryption_private_key(&self) -> &HpkePrivateKey {
self.private_encryption_key.key()
}
}
#[cfg(test)]
impl KeyPackageBundle {
pub(crate) fn generate(
provider: &impl OpenMlsProvider,
signer: &impl Signer,
ciphersuite: Ciphersuite,
credential_with_key: CredentialWithKey,
) -> Self {
KeyPackage::builder()
.build(ciphersuite, provider, signer, credential_with_key)
.unwrap()
}
}