mesh_llm_identity/
keys.rs1use ed25519_dalek::{Signer, SigningKey, VerifyingKey};
2use sha2::{Digest, Sha256};
3
4use super::error::CryptoError;
5
6#[derive(Debug)]
8pub struct OwnerKeypair {
9 pub(crate) signing: SigningKey,
10 pub(crate) encryption: crypto_box::SecretKey,
11}
12
13impl OwnerKeypair {
14 pub fn generate() -> Self {
16 let signing = SigningKey::generate(&mut rand::rng());
19 let encryption = crypto_box::SecretKey::generate(&mut crypto_box::aead::OsRng);
20 Self {
21 signing,
22 encryption,
23 }
24 }
25
26 pub fn owner_id(&self) -> String {
30 owner_id_from_verifying_key(&self.signing.verifying_key())
31 }
32
33 pub fn verifying_key(&self) -> VerifyingKey {
35 self.signing.verifying_key()
36 }
37
38 pub fn encryption_public_key(&self) -> crypto_box::PublicKey {
40 self.encryption.public_key()
41 }
42
43 pub fn from_bytes(signing_bytes: &[u8], encryption_bytes: &[u8]) -> Result<Self, CryptoError> {
45 let signing_arr: [u8; 32] =
46 signing_bytes
47 .try_into()
48 .map_err(|_| CryptoError::InvalidKeyMaterial {
49 reason: "signing key must be 32 bytes".into(),
50 })?;
51 let encryption_arr: [u8; 32] =
52 encryption_bytes
53 .try_into()
54 .map_err(|_| CryptoError::InvalidKeyMaterial {
55 reason: "encryption key must be 32 bytes".into(),
56 })?;
57
58 let signing = SigningKey::from_bytes(&signing_arr);
59 let encryption = crypto_box::SecretKey::from(encryption_arr);
60
61 Ok(Self {
62 signing,
63 encryption,
64 })
65 }
66
67 pub fn signing_bytes(&self) -> &[u8; 32] {
69 self.signing.as_bytes()
70 }
71
72 pub fn encryption_bytes(&self) -> [u8; 32] {
74 self.encryption.to_bytes()
75 }
76
77 pub fn sign_bytes(&self, bytes: &[u8]) -> [u8; 64] {
79 self.signing.sign(bytes).to_bytes()
80 }
81}
82
83impl Clone for OwnerKeypair {
84 fn clone(&self) -> Self {
85 Self {
86 signing: self.signing.clone(),
87 encryption: crypto_box::SecretKey::from(self.encryption.to_bytes()),
88 }
89 }
90}
91
92pub fn owner_id_from_verifying_key(vk: &VerifyingKey) -> String {
96 let hash = Sha256::digest(vk.as_bytes());
97 hex::encode(hash)
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn key_generation_produces_valid_owner_id() {
106 let kp = OwnerKeypair::generate();
107 let id = kp.owner_id();
108 assert_eq!(id.len(), 64, "owner_id should be 64 hex chars");
109 assert!(
110 id.chars().all(|c| c.is_ascii_hexdigit()),
111 "owner_id should be hex"
112 );
113 }
114
115 #[test]
116 fn owner_id_is_deterministic_from_public_key() {
117 let kp = OwnerKeypair::generate();
118 let id1 = kp.owner_id();
119 let id2 = owner_id_from_verifying_key(&kp.verifying_key());
120 assert_eq!(id1, id2);
121 }
122
123 #[test]
124 fn different_keypairs_produce_different_owner_ids() {
125 let kp1 = OwnerKeypair::generate();
126 let kp2 = OwnerKeypair::generate();
127 assert_ne!(kp1.owner_id(), kp2.owner_id());
128 }
129
130 #[test]
131 fn round_trip_from_bytes() {
132 let kp = OwnerKeypair::generate();
133 let signing = kp.signing_bytes().to_vec();
134 let encryption = kp.encryption_bytes().to_vec();
135
136 let restored = OwnerKeypair::from_bytes(&signing, &encryption).unwrap();
137 assert_eq!(kp.owner_id(), restored.owner_id());
138 assert_eq!(
139 kp.encryption_public_key().as_bytes(),
140 restored.encryption_public_key().as_bytes()
141 );
142 }
143}