vta_cli_common/
local_keygen.rs1use ed25519_dalek::SigningKey;
18use rand::Rng;
19use vta_sdk::credentials::CredentialBundle;
20use vta_sdk::prelude::ed25519_multibase_pubkey;
21
22fn mint_ed25519_did_key() -> (String, String) {
29 let mut seed = [0u8; 32];
30 rand::rng().fill_bytes(&mut seed);
31 let signing_key = SigningKey::from_bytes(&seed);
32 let public_key = signing_key.verifying_key().to_bytes();
33 let multibase_pubkey = ed25519_multibase_pubkey(&public_key);
34 let did = format!("did:key:{multibase_pubkey}");
35 let private_key_multibase = multibase::encode(multibase::Base::Base58Btc, seed);
36 (did, private_key_multibase)
37}
38
39pub fn generate_admin_did_key(
45 vta_did: impl Into<String>,
46 vta_url: Option<String>,
47) -> (CredentialBundle, String) {
48 let (did, private_key_multibase) = mint_ed25519_did_key();
49 let bundle = CredentialBundle {
50 did: did.clone(),
51 private_key_multibase,
52 vta_did: vta_did.into(),
53 vta_url,
54 };
55 (bundle, did)
56}
57
58pub fn generate_unbound_admin_did_key() -> (String, String) {
69 mint_ed25519_did_key()
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn generated_did_is_did_key() {
78 let (bundle, did) = generate_admin_did_key("did:key:z6MkVTA", None);
79 assert_eq!(bundle.did, did);
80 assert!(did.starts_with("did:key:z"));
81 assert_eq!(bundle.vta_did, "did:key:z6MkVTA");
82 assert!(bundle.vta_url.is_none());
83 }
84
85 #[test]
86 fn generated_dids_are_unique() {
87 let a = generate_admin_did_key("did:key:z6MkVTA", None).1;
88 let b = generate_admin_did_key("did:key:z6MkVTA", None).1;
89 assert_ne!(a, b);
90 }
91
92 #[test]
93 fn private_key_multibase_roundtrips_to_seed() {
94 let (bundle, _) = generate_admin_did_key("did:key:z6MkVTA", None);
95 let (_, decoded) = multibase::decode(&bundle.private_key_multibase).unwrap();
96 assert_eq!(decoded.len(), 32, "Ed25519 seed must be 32 bytes");
97 }
98
99 #[test]
100 fn derived_did_matches_seed() {
101 let (bundle, did) = generate_admin_did_key("did:key:z6MkVTA", None);
102 let (_, seed_bytes) = multibase::decode(&bundle.private_key_multibase).unwrap();
104 let seed: [u8; 32] = seed_bytes.try_into().unwrap();
105 let signing_key = SigningKey::from_bytes(&seed);
106 let pubkey = signing_key.verifying_key().to_bytes();
107 let rederived = format!("did:key:{}", ed25519_multibase_pubkey(&pubkey));
108 assert_eq!(rederived, did);
109 }
110
111 #[test]
112 fn unbound_did_key_has_valid_shape() {
113 let (did, private_key_multibase) = generate_unbound_admin_did_key();
114 assert!(did.starts_with("did:key:z"));
115 let (_, decoded) = multibase::decode(&private_key_multibase).unwrap();
116 assert_eq!(decoded.len(), 32, "Ed25519 seed must be 32 bytes");
117 }
118
119 #[test]
120 fn unbound_dids_are_unique() {
121 let (a, _) = generate_unbound_admin_did_key();
122 let (b, _) = generate_unbound_admin_did_key();
123 assert_ne!(a, b);
124 }
125
126 #[test]
127 fn unbound_seed_round_trips_to_did() {
128 let (did, private_key_multibase) = generate_unbound_admin_did_key();
129 let (_, seed_bytes) = multibase::decode(&private_key_multibase).unwrap();
130 let seed: [u8; 32] = seed_bytes.try_into().unwrap();
131 let signing_key = SigningKey::from_bytes(&seed);
132 let pubkey = signing_key.verifying_key().to_bytes();
133 let rederived = format!("did:key:{}", ed25519_multibase_pubkey(&pubkey));
134 assert_eq!(rederived, did);
135 }
136}