use std::collections::BTreeMap;
use pg_core::api::{KeyResponse, Parameters};
use pg_core::artifacts::{PublicKey, SigningKey, SigningKeyExt, UserSecretKey, VerifyingKey};
use pg_core::client::rust::stream::SealerStreamConfig;
use pg_core::client::rust::SealerMemoryConfig;
use pg_core::client::Sealer;
use pg_core::consts::{SYMMETRIC_CRYPTO_DEFAULT_CHUNK, VERSION_2};
use pg_core::identity::{Attribute, EncryptionPolicy, Policy};
use pg_core::kem::cgw_kv::CGWKV;
use pg_core::kem::IBKEM;
use irma::{ProofStatus, SessionStatus};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
pub const SEED: [u8; 32] = *b"postguard-wire-compat-sample-set";
pub const SCHEMA_VERSION: u32 = 1;
const TIMESTAMP: u64 = 1_704_067_200;
const SENDER: &str = "sender@sample.test";
pub const MANIFEST: &str = "manifest.json";
struct Recipient {
id: &'static str,
usk_file: &'static str,
policy: Policy,
}
pub struct Case {
pub name: &'static str,
pub mode: &'static str,
pub file: &'static str,
pub plaintext_file: &'static str,
pub private_signing: bool,
pub plaintext: Vec<u8>,
}
fn multi_segment_payload() -> Vec<u8> {
let len = SYMMETRIC_CRYPTO_DEFAULT_CHUNK as usize + 4096;
(0..len).map(|i| (i % 251) as u8).collect()
}
pub fn cases() -> Vec<Case> {
vec![
Case {
name: "mem",
mode: "memory",
file: "mem.bin",
plaintext_file: "mem.plain",
private_signing: false,
plaintext: b"postguard wire-compat sample: in-memory container".to_vec(),
},
Case {
name: "mem-privsig",
mode: "memory",
file: "mem-privsig.bin",
plaintext_file: "mem-privsig.plain",
private_signing: true,
plaintext: b"postguard wire-compat sample: in-memory container, private signature"
.to_vec(),
},
Case {
name: "stream",
mode: "stream",
file: "stream.bin",
plaintext_file: "stream.plain",
private_signing: false,
plaintext: b"postguard wire-compat sample: streaming container".to_vec(),
},
Case {
name: "stream-privsig",
mode: "stream",
file: "stream-privsig.bin",
plaintext_file: "stream-privsig.plain",
private_signing: true,
plaintext: b"postguard wire-compat sample: streaming container, private signature"
.to_vec(),
},
Case {
name: "stream-multi-segment",
mode: "stream",
file: "stream-multi-segment.bin",
plaintext_file: "stream-multi-segment.plain",
private_signing: false,
plaintext: multi_segment_payload(),
},
]
}
fn recipients() -> Vec<Recipient> {
vec![
Recipient {
id: "alice",
usk_file: "usk-alice.json",
policy: Policy {
timestamp: TIMESTAMP,
con: vec![Attribute::new(
"pbdf.sidn-pbdf.email.email",
Some("alice@sample.test"),
)],
},
},
Recipient {
id: "bob",
usk_file: "usk-bob.json",
policy: Policy {
timestamp: TIMESTAMP,
con: vec![
Attribute::new("pbdf.gemeente.personalData.fullname", Some("Bob")),
Attribute::new("pbdf.sidn-pbdf.email.email", Some("bob@sample.test")),
],
},
},
]
}
fn public_sender_policy() -> Policy {
Policy {
timestamp: TIMESTAMP,
con: vec![Attribute::new("pbdf.sidn-pbdf.email.email", Some(SENDER))],
}
}
fn private_sender_policy() -> Policy {
Policy {
timestamp: TIMESTAMP,
con: vec![Attribute::new(
"pbdf.gemeente.personalData.fullname",
Some("Sample Sender"),
)],
}
}
pub fn build() -> BTreeMap<String, Vec<u8>> {
let mut rng = ChaCha20Rng::from_seed(SEED);
let (ibe_pk, ibe_msk) = CGWKV::setup(&mut rng);
let (ibs_pk, ibs_sk) = ibs::gg::setup(&mut rng);
let mpk = PublicKey::<CGWKV>(ibe_pk);
let vk = VerifyingKey(ibs_pk);
let recipients = recipients();
let mut out = BTreeMap::new();
let enc_policy: EncryptionPolicy = recipients
.iter()
.map(|r| (r.id.to_string(), r.policy.clone()))
.collect();
for r in &recipients {
let derived = r.policy.derive_kem::<CGWKV>().unwrap();
let usk = UserSecretKey::<CGWKV>(CGWKV::extract_usk(
Some(&mpk.0),
&ibe_msk,
&derived,
&mut rng,
));
let response = KeyResponse {
status: SessionStatus::Done,
proof_status: Some(ProofStatus::Valid),
key: Some(usk),
};
out.insert(r.usk_file.to_string(), json(&response));
}
let public = public_sender_policy();
let private = private_sender_policy();
let pub_sign_key = SigningKeyExt {
key: SigningKey(ibs::gg::keygen(
&ibs_sk,
&public.derive_ibs().unwrap(),
&mut rng,
)),
policy: public.clone(),
};
let priv_sign_key = SigningKeyExt {
key: SigningKey(ibs::gg::keygen(
&ibs_sk,
&private.derive_ibs().unwrap(),
&mut rng,
)),
policy: private.clone(),
};
out.insert(
"vk.json".to_string(),
json(&Parameters {
format_version: 0,
public_key: vk,
}),
);
let cases = cases();
for case in &cases {
let bytes = match case.mode {
"memory" => {
let sealer = Sealer::<_, SealerMemoryConfig>::new(
&mpk,
&enc_policy,
&pub_sign_key,
&mut rng,
)
.unwrap();
let sealer = if case.private_signing {
sealer.with_priv_signing_key(priv_sign_key.clone())
} else {
sealer
};
sealer.seal(&case.plaintext).unwrap()
}
"stream" => {
let mut sealer = Sealer::<_, SealerStreamConfig>::new(
&mpk,
&enc_policy,
&pub_sign_key,
&mut rng,
)
.unwrap();
if case.private_signing {
sealer = sealer.with_priv_signing_key(priv_sign_key.clone());
}
if case.plaintext.len() > SYMMETRIC_CRYPTO_DEFAULT_CHUNK as usize {
let len = case.plaintext.len() as u64;
sealer = sealer.with_size_hint((len, Some(len)));
}
let mut buf = Vec::new();
futures::executor::block_on(async {
let mut input = futures::io::Cursor::new(&case.plaintext);
sealer.seal(&mut input, &mut buf).await.unwrap();
});
buf
}
other => panic!("unknown mode {other}"),
};
out.insert(case.file.to_string(), bytes);
out.insert(case.plaintext_file.to_string(), case.plaintext.clone());
}
out.insert(MANIFEST.to_string(), manifest(&cases, &recipients));
out
}
fn manifest(cases: &[Case], recipients: &[Recipient]) -> Vec<u8> {
let recipients: Vec<_> = recipients
.iter()
.map(|r| {
serde_json::json!({
"id": r.id,
"usk": r.usk_file,
})
})
.collect();
let cases: Vec<_> = cases
.iter()
.map(|c| {
serde_json::json!({
"name": c.name,
"mode": c.mode,
"ciphertext": c.file,
"plaintext": c.plaintext_file,
"privateSigning": c.private_signing,
"recipients": recipients,
})
})
.collect();
let manifest = serde_json::json!({
"schemaVersion": SCHEMA_VERSION,
"sealedBy": {
"crate": "pg-core",
"version": env!("CARGO_PKG_VERSION"),
},
"wireVersion": VERSION_2,
"verifyingKey": "vk.json",
"sender": {
"public": public_sender_policy(),
"private": private_sender_policy(),
},
"cases": cases,
});
json(&manifest)
}
fn json<T: serde::Serialize>(value: &T) -> Vec<u8> {
let mut buf = serde_json::to_vec_pretty(value).unwrap();
buf.push(b'\n');
buf
}