#![cfg_attr(not(feature = "std"), no_std)]
#![deny(unsafe_code)]
#![warn(missing_docs)]
extern crate alloc;
pub mod canonical;
pub mod constants;
pub mod derive;
pub mod envelope;
pub mod error;
pub mod freshness;
pub mod identity_assertion;
pub mod keypackage;
pub mod prefixes;
pub use canonical::{
canonical_payload, control_signing_payload, control_signing_payload_v2,
gateway_address_proof_payload, CTRL_PK_META_KEY, CTRL_SIGN_DOMAIN, CTRL_SIGN_DOMAIN_V2,
CTRL_SIG_META_KEY, GATEWAY_ADDR_SIGN_DOMAIN,
};
pub use constants::{
ACK_FOR_KEY, LEAF_KEY_PACKAGE_LIFETIME, LEAF_KEY_PACKAGE_NOT_BEFORE_BACKDATE_SECONDS,
LEAF_SENDER_RATCHET_MAXIMUM_FORWARD_DISTANCE, MAX_ACCEPTED_KEY_PACKAGE_LIFETIME,
SENDER_RATCHET_MAXIMUM_FORWARD_DISTANCE, SENDER_RATCHET_OUT_OF_ORDER_TOLERANCE,
};
pub use derive::{derive_address, ED25519_PUBLIC_KEY_LEN};
pub use envelope::{EncryptedMessage, GroupId, MlsMessageType, WelcomeMessage};
pub use error::{Result, SealedError};
pub use freshness::{
control_frame_freshness, Freshness, CTRL_FRESHNESS_FUTURE_MS, CTRL_FRESHNESS_PAST_MS,
LEAF_CTRL_FRESHNESS_PAST_MS,
};
pub use identity_assertion::{
encode_identity_assertion, parse_identity_assertion, IdentityAssertion, ED25519_SIGNATURE_LEN,
IDENTITY_ASSERTION_MIN_LEN,
};
pub use keypackage::{
KeyPackagePayload, ACK_COALESCED_V1, ACK_UNDECRYPTABLE_V1, CTRL_SIGN_V2,
MLS_ENVELOPE_COMPACT_V1, SVC_SEALED_V1,
};
#[cfg(all(test, feature = "std"))]
mod manifest_guard_tests {
const LOCAL_DEPS: &[&str] = &[
"serde",
"serde_json",
"base64",
"thiserror",
"sha2",
"offline-protocol-core",
];
fn version_req(manifest: &str, name: &str) -> Option<String> {
let line = manifest
.lines()
.find(|l| l.starts_with(&format!("{name} = ")))?;
let rest = match line.find("version = \"") {
Some(i) => &line[i + "version = \"".len()..],
None => {
let i = line.find('"')?;
&line[i + 1..]
}
};
rest.find('"').map(|end| rest[..end].to_string())
}
#[test]
fn local_dep_versions_match_the_workspace_table() {
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let root = dir.join("../../Cargo.toml");
let Ok(root_manifest) = std::fs::read_to_string(&root) else {
return;
};
let own_manifest =
std::fs::read_to_string(dir.join("Cargo.toml")).expect("own manifest is readable");
for dep in LOCAL_DEPS {
let workspace = version_req(&root_manifest, dep)
.unwrap_or_else(|| panic!("`{dep}` not found in the workspace dependency table"));
let own = version_req(&own_manifest, dep)
.unwrap_or_else(|| panic!("`{dep}` not found in this crate's manifest"));
assert_eq!(
workspace, own,
"`{dep}` version drifted: workspace says {workspace:?}, \
offline-protocol-sealed says {own:?}. Update both together."
);
}
}
}
#[cfg(all(test, feature = "std"))]
mod interop_harness_guard_tests {
#[test]
fn the_interop_harness_uses_this_crate_rather_than_its_own_copies() {
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let harness = dir.join("../../tools/mls-interop");
let (Ok(main_rs), Ok(manifest)) = (
std::fs::read_to_string(harness.join("src/main.rs")),
std::fs::read_to_string(harness.join("Cargo.toml")),
) else {
eprintln!("tools/mls-interop not present, skipping harness copy check");
return;
};
assert!(
manifest.contains("offline-protocol-sealed"),
"the harness must depend on offline-protocol-sealed, not restate what it declares"
);
for symbol in [
"derive_address",
"SENDER_RATCHET_OUT_OF_ORDER_TOLERANCE",
"SENDER_RATCHET_MAXIMUM_FORWARD_DISTANCE",
"LEAF_SENDER_RATCHET_MAXIMUM_FORWARD_DISTANCE",
"LEAF_KEY_PACKAGE_LIFETIME",
"LEAF_KEY_PACKAGE_NOT_BEFORE_BACKDATE_SECONDS",
"MAX_ACCEPTED_KEY_PACKAGE_LIFETIME",
] {
assert!(
main_rs.contains(symbol),
"the harness no longer references `{symbol}`; if it stopped needing it, \
drop it from this list, but if it declared its own copy instead, do not"
);
}
for copy in [
"fn derive_address",
"const OUT_OF_ORDER_TOLERANCE",
"const MAXIMUM_FORWARD_DISTANCE",
"const LEAF_FORWARD_DISTANCE",
"const KEY_PACKAGE_LIFETIME",
"const NOT_BEFORE_BACKDATE",
"const MAX_ACCEPTED",
] {
assert!(
!main_rs.contains(copy),
"`{copy}` is back in tools/mls-interop/src/main.rs: the harness has to use \
offline-protocol-sealed's, or it stops testing the SDK's configuration"
);
}
}
}
#[cfg(all(test, feature = "std"))]
mod second_codec_guard_tests {
fn other_rust_sources() -> Vec<std::path::PathBuf> {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let mut out = Vec::new();
for group in ["crates", "tools"] {
let Ok(entries) = std::fs::read_dir(root.join(group)) else {
continue;
};
for entry in entries.flatten() {
let dir = entry.path();
if dir.file_name().and_then(|n| n.to_str()) == Some("offline-protocol-sealed") {
continue;
}
collect_rs(&dir.join("src"), &mut out);
}
}
out
}
fn collect_rs(dir: &std::path::Path, out: &mut Vec<std::path::PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_rs(&path, out);
} else if path.extension().and_then(|e| e.to_str()) == Some("rs") {
out.push(path);
}
}
}
#[test]
fn no_other_crate_declares_an_identity_assertion_codec() {
let sources = other_rust_sources();
if sources.is_empty() {
eprintln!("no sibling crates present, skipping the second-codec check");
return;
}
for path in sources {
let text = std::fs::read_to_string(&path).expect("source is readable");
for copy in [
"fn parse_identity_assertion",
"fn encode_identity_assertion",
] {
assert!(
!text.contains(copy),
"`{copy}` is declared in {}: the assertion layout has one home, \
offline-protocol-sealed, and everything else calls it",
path.display()
);
}
}
}
}