1use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
9use rand_core::{OsRng, RngCore};
10use serde::{Deserialize, Serialize};
11use zeroize::Zeroizing;
12
13use crate::{codec, Error, Result};
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub struct UserId(#[serde(with = "serde_bytes")] pub Vec<u8>);
18
19impl UserId {
20 pub fn from_pubkey(pk: &VerifyingKey) -> Self {
21 UserId(codec::sha256(pk.as_bytes()).to_vec())
22 }
23 pub fn as_hex(&self) -> String {
24 hex::encode(&self.0)
25 }
26}
27
28pub struct Identity {
30 user_id: UserId,
31 signing: SigningKey,
32}
33
34impl std::fmt::Debug for Identity {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 f.debug_struct("Identity")
37 .field("user_id", &self.user_id.as_hex())
38 .finish()
39 }
40}
41
42impl Identity {
43 pub fn generate() -> Self {
44 let mut seed = [0u8; 32];
45 OsRng.fill_bytes(&mut seed);
46 let signing = SigningKey::from_bytes(&seed);
47 let user_id = UserId::from_pubkey(&signing.verifying_key());
48 Identity { user_id, signing }
49 }
50
51 pub fn user_id(&self) -> &UserId {
52 &self.user_id
53 }
54 pub fn public_key(&self) -> VerifyingKey {
55 self.signing.verifying_key()
56 }
57
58 pub fn sign_device_binding(&self, device_id: &[u8]) -> Vec<u8> {
60 let mut buf = Vec::with_capacity(self.user_id.0.len() + device_id.len());
61 buf.extend_from_slice(&self.user_id.0);
62 buf.extend_from_slice(device_id);
63 self.signing.sign(&buf).to_bytes().to_vec()
64 }
65
66 pub fn verify_device_binding(
67 user_pk: &VerifyingKey,
68 user_id: &UserId,
69 device_id: &[u8],
70 sig: &[u8],
71 ) -> Result<()> {
72 let sig: [u8; 64] = sig
73 .try_into()
74 .map_err(|_| Error::Identity("bad signature length".into()))?;
75 let signature = Signature::from_bytes(&sig);
76 let mut buf = Vec::with_capacity(user_id.0.len() + device_id.len());
77 buf.extend_from_slice(&user_id.0);
78 buf.extend_from_slice(device_id);
79 user_pk
80 .verify(&buf, &signature)
81 .map_err(|e| Error::Identity(format!("signature verify failed: {e}")))
82 }
83
84 pub fn export(&self) -> Zeroizing<Vec<u8>> {
87 #[derive(Serialize)]
88 struct Export<'a> {
89 v: u8,
90 #[serde(with = "serde_bytes")]
91 seed: &'a [u8],
92 }
93 let bytes = codec::encode(&Export {
94 v: 1,
95 seed: self.signing.as_bytes(),
96 })
97 .expect("identity export cannot fail");
98 Zeroizing::new(bytes)
99 }
100
101 pub fn import(bytes: &[u8]) -> Result<Self> {
102 #[derive(Deserialize)]
103 struct Export {
104 v: u8,
105 #[serde(with = "serde_bytes")]
106 seed: Vec<u8>,
107 }
108 let imported: Export = codec::decode(bytes)?;
109 if imported.v != 1 {
110 return Err(Error::Identity(format!(
111 "unknown export version {}",
112 imported.v
113 )));
114 }
115 let seed: [u8; 32] = imported
116 .seed
117 .as_slice()
118 .try_into()
119 .map_err(|_| Error::Identity("bad seed length".into()))?;
120 let signing = SigningKey::from_bytes(&seed);
121 let user_id = UserId::from_pubkey(&signing.verifying_key());
122 Ok(Identity { user_id, signing })
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn export_roundtrip() {
132 let id = Identity::generate();
133 let exported = id.export();
134 let restored = Identity::import(&exported).unwrap();
135 assert_eq!(id.user_id(), restored.user_id());
136 assert_eq!(id.public_key().as_bytes(), restored.public_key().as_bytes());
137 }
138
139 #[test]
140 fn device_binding_verifies() {
141 let id = Identity::generate();
142 let device_id = b"device-1";
143 let sig = id.sign_device_binding(device_id);
144 Identity::verify_device_binding(&id.public_key(), id.user_id(), device_id, &sig).unwrap();
145 }
146}