1#[derive(Debug, Clone)]
2pub struct OwnerKeypair(mesh_client::OwnerKeypair);
3
4impl OwnerKeypair {
5 const ENCODED_HEX_LEN: usize = 64 * 2;
6
7 pub fn generate() -> Self {
8 Self(mesh_client::OwnerKeypair::generate())
9 }
10
11 pub fn owner_id(&self) -> String {
12 self.0.owner_id()
13 }
14
15 pub fn from_bytes(signing_bytes: &[u8], encryption_bytes: &[u8]) -> Result<Self, String> {
16 mesh_client::OwnerKeypair::from_bytes(signing_bytes, encryption_bytes)
17 .map(Self)
18 .map_err(|err| err.to_string())
19 }
20
21 pub fn from_hex(encoded: &str) -> Result<Self, String> {
22 if encoded.len() != Self::ENCODED_HEX_LEN {
23 return Err(format!(
24 "owner keypair hex must be {} characters",
25 Self::ENCODED_HEX_LEN
26 ));
27 }
28
29 let bytes = hex::decode(encoded).map_err(|err| err.to_string())?;
30 let (signing_bytes, encryption_bytes) = bytes.split_at(32);
31 Self::from_bytes(signing_bytes, encryption_bytes)
32 }
33
34 pub fn to_hex(&self) -> String {
35 let mut bytes = Vec::with_capacity(64);
36 bytes.extend_from_slice(self.signing_bytes());
37 bytes.extend_from_slice(&self.encryption_bytes());
38 hex::encode(bytes)
39 }
40
41 pub fn signing_bytes(&self) -> &[u8; 32] {
42 self.0.signing_bytes()
43 }
44
45 pub fn encryption_bytes(&self) -> [u8; 32] {
46 self.0.encryption_bytes()
47 }
48
49 pub(crate) fn into_inner(self) -> mesh_client::OwnerKeypair {
50 self.0
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::OwnerKeypair;
57
58 #[test]
59 fn owner_keypair_hex_roundtrip() {
60 let keypair = OwnerKeypair::generate();
61 let encoded = keypair.to_hex();
62 let restored = OwnerKeypair::from_hex(&encoded).expect("hex roundtrip");
63 assert_eq!(keypair.owner_id(), restored.owner_id());
64 assert_eq!(keypair.to_hex(), restored.to_hex());
65 }
66
67 #[test]
68 fn owner_keypair_hex_requires_full_key_material() {
69 let err = OwnerKeypair::from_hex("deadbeef").expect_err("short hex must fail");
70 assert!(err.contains("128"));
71 }
72}