1use std::io::{self};
2
3use agile::{agile_gen_keypair, AeadAlg, KemAlg};
4
5use bech32::{ToBase32, Variant};
6use internal::{Identity, Recipient};
7use rand::{rngs::StdRng, SeedableRng};
8
9use crate::internal::{IdentityPlugin, RecipientPlugin};
10
11pub mod agile;
12mod internal;
13
14const PLUGIN_RECIPIENT_PREFIX: &str = "age1";
16const PLUGIN_IDENTITY_PREFIX: &str = "age-plugin-";
17
18pub fn run_state_machine(plugin_name: &str, state_machine: &str) -> io::Result<()> {
19 age_plugin::run_state_machine(
20 state_machine,
21 || RecipientPlugin::new(plugin_name),
22 || IdentityPlugin::new(plugin_name),
23 )
24}
25
26pub fn new_identity(kem: KemAlg, aead: AeadAlg, associated_data: &str) -> (Vec<u8>, Vec<u8>) {
27 let mut csprng = StdRng::from_entropy();
28 let keypair = agile_gen_keypair(kem.clone(), &mut csprng);
29 let identity = Identity::new(
30 kem.clone(),
31 aead.clone(),
32 kem.kdf_alg(),
33 keypair.private_key(),
34 associated_data.as_bytes(),
35 );
36 let recipient = Recipient::new(
37 kem.clone(),
38 aead,
39 kem.kdf_alg(),
40 keypair.public_key(),
41 associated_data.as_bytes(),
42 );
43
44 (identity.to_bytes(), recipient.to_bytes())
45}
46
47pub fn new_identity_to_string(plugin_name: &str, identity: &[u8], recipient: &[u8]) -> String {
48 format!(
49 "# created: {}
50# recipient: {}
51{}
52",
53 chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
54 recipient_to_string(plugin_name, recipient),
55 identity_to_string(plugin_name, identity),
56 )
57}
58
59pub fn identity_to_string(plugin_name: &str, identity: &[u8]) -> String {
60 bech32::encode(
61 &format!("{}{}-", PLUGIN_IDENTITY_PREFIX, plugin_name),
62 identity.to_base32(),
63 Variant::Bech32,
64 )
65 .expect("HRP is valid")
66 .to_uppercase()
67}
68
69pub fn identity_from_string(identity: &str) -> Vec<u8> {
70 use bech32::FromBase32;
71
72 let mut identity = identity.trim();
73 while identity.starts_with('#') {
74 identity = identity
75 .find('\n')
76 .map(|i| identity.split_at(i + 1))
77 .map(|(_, rest)| rest)
78 .unwrap_or("")
79 .trim();
80 }
81 let (_, identity_decoded, _) = bech32::decode(identity).unwrap();
82 Vec::from_base32(&identity_decoded).unwrap()
83}
84
85pub fn recipient_to_string(plugin_name: &str, recipient: &[u8]) -> String {
86 bech32::encode(
87 &format!("{}{}", PLUGIN_RECIPIENT_PREFIX, plugin_name),
88 recipient.to_base32(),
89 Variant::Bech32,
90 )
91 .expect("HRP is valid")
92}
93
94pub fn convert_identity_to_recipient(identity: &[u8]) -> Vec<u8> {
95 let recipient: Recipient = Identity::from_bytes(identity).into();
96 recipient.to_bytes()
97}