1use std::cmp::{Eq, Ord, PartialEq, PartialOrd};
3use std::fmt;
4use std::io::{self, Cursor, Write};
5
6use crate::{skip_prefix, split_at_byte};
7use serde::{
8 de::{Deserialize, Deserializer, Error},
9 ser::{Serialize, Serializer},
10};
11
12use ssb_crypto::{AsBytes, Keypair, PublicKey, Signature};
13
14#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
16pub enum Multikey {
17 Ed25519(PublicKey),
19}
20
21impl Multikey {
22 pub fn from_ed25519(pk: &[u8; 32]) -> Multikey {
24 Multikey::Ed25519(PublicKey::from_slice(pk).unwrap())
25 }
26
27 pub fn from_ed25519_slice(pk: &[u8]) -> Multikey {
28 Multikey::Ed25519(PublicKey::from_slice(pk).unwrap())
29 }
30
31 pub fn into_ed25519_public_key(self) -> Option<PublicKey> {
32 match self {
33 Multikey::Ed25519(pk) => Some(pk),
34 }
35 }
36
37 pub fn from_legacy(mut s: &[u8]) -> Result<(Multikey, &[u8]), DecodeLegacyError> {
41 s = skip_prefix(s, b"@").ok_or(DecodeLegacyError::Sigil)?;
42
43 let (data, suffix) = split_at_byte(s, 0x2E).ok_or(DecodeLegacyError::NoDot)?;
44
45 let tail = skip_prefix(suffix, ED25519_SUFFIX).ok_or(DecodeLegacyError::UnknownSuffix)?;
46
47 if data.len() != ED25519_PK_BASE64_LEN {
48 return Err(DecodeLegacyError::Ed25519WrongSize);
49 }
50
51 if data[ED25519_PK_BASE64_LEN - 2] == b"="[0] {
52 return Err(DecodeLegacyError::Ed25519WrongSize);
53 }
54
55 if data[ED25519_PK_BASE64_LEN - 1] != b"="[0] {
56 return Err(DecodeLegacyError::Ed25519WrongSize);
57 }
58
59 let mut dec_data = [0_u8; 32];
60
61 base64::decode_config_slice(data, base64::STANDARD, &mut dec_data)
62 .map_err(|_| DecodeLegacyError::InvalidBase64)
63 .map(|_| (Multikey::from_ed25519(&dec_data), tail))
64 }
65
66 pub fn to_legacy<W: Write>(&self, w: &mut W) -> Result<(), io::Error> {
69 match self {
70 Multikey::Ed25519(ref pk) => {
71 w.write_all(b"@")?;
72
73 let data = pk.as_base64();
74 w.write_all(data.as_bytes())?;
75
76 w.write_all(b".")?;
77 w.write_all(ED25519_SUFFIX)
78 }
79 }
80 }
81
82 pub fn to_legacy_vec(&self) -> Vec<u8> {
85 let mut data = vec![];
86 self.to_legacy(&mut data).unwrap();
87 data
88 }
89
90 pub fn to_legacy_string(&self) -> String {
93 String::from_utf8(self.to_legacy_vec()).unwrap()
94 }
95
96 pub fn is_signature_correct(&self, data: &[u8], sig: &Multisig) -> bool {
98 match (&self, &sig.0) {
99 (Multikey::Ed25519(ref pk), _Multisig::Ed25519(ref sig)) => pk.verify(sig, data),
100 }
101 }
102}
103
104impl Serialize for Multikey {
105 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
106 where
107 S: Serializer,
108 {
109 serializer.serialize_str(&self.to_legacy_string())
110 }
111}
112
113impl<'de> Deserialize<'de> for Multikey {
114 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115 where
116 D: Deserializer<'de>,
117 {
118 let s = String::deserialize(deserializer)?;
119 Multikey::from_legacy(s.as_bytes())
120 .map(|(mk, _)| mk)
121 .map_err(|err| D::Error::custom(format!("Invalid multikey: {}", err)))
122 }
123}
124
125#[derive(Debug, PartialEq, Eq, Clone)]
127pub enum DecodeLegacyError {
128 Sigil,
130 NoDot,
132 InvalidUTF8,
134 InvalidBase64,
136 UnknownSuffix,
138 Ed25519WrongSize,
140}
141
142impl fmt::Display for DecodeLegacyError {
143 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 match self {
145 DecodeLegacyError::Sigil => write!(f, "Invalid sigil"),
146 DecodeLegacyError::InvalidUTF8 => write!(f, "Invalid utf8"),
147 DecodeLegacyError::InvalidBase64 => write!(f, "Invalid base64"),
148 DecodeLegacyError::NoDot => write!(f, "No dot"),
149 DecodeLegacyError::UnknownSuffix => write!(f, "Unknown suffix"),
150 DecodeLegacyError::Ed25519WrongSize => write!(f, "Data of wrong length"),
151 }
152 }
153}
154
155impl std::error::Error for DecodeLegacyError {}
156
157#[derive(Debug, Clone)]
159pub struct Multisecret(Keypair);
160
161impl Multisecret {
162 pub fn from_legacy(s: &[u8]) -> Result<(Multisecret, &[u8]), DecodeLegacyError> {
166 let (data, suffix) = split_at_byte(s, 0x2E).ok_or(DecodeLegacyError::NoDot)?;
167
168 let tail = skip_prefix(suffix, ED25519_SUFFIX).ok_or(DecodeLegacyError::UnknownSuffix)?;
169
170 let data_str = std::str::from_utf8(data).map_err(|_| DecodeLegacyError::InvalidUTF8)?;
171
172 let key_pair = Keypair::from_base64(data_str).ok_or(DecodeLegacyError::InvalidBase64)?;
173
174 Ok((Multisecret(key_pair), tail))
175 }
176
177 pub fn to_legacy<W: Write>(&self, w: &mut W) -> Result<(), io::Error> {
180 let data = self.0.as_base64();
181 w.write_all(data.as_bytes())?;
182 w.write_all(b".")?;
183 w.write_all(ED25519_SUFFIX)
184 }
185}
186
187impl Serialize for Multisecret {
188 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
189 where
190 S: Serializer,
191 {
192 let mut s = [0_u8; SSB_ED25519_SECRET_ENCODED_LEN];
193 self.to_legacy(&mut Cursor::new(&mut s[..])).unwrap();
194 serializer.serialize_str(std::str::from_utf8(&s).unwrap())
195 }
196}
197
198impl<'de> Deserialize<'de> for Multisecret {
199 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
200 where
201 D: Deserializer<'de>,
202 {
203 let s = String::deserialize(deserializer)?;
204 Multisecret::from_legacy(s.as_bytes())
205 .map(|(mk, _)| mk)
206 .map_err(|err| D::Error::custom(format!("Invalid multikey: {}", err)))
207 }
208}
209
210#[derive(Debug, PartialEq, Eq, Clone)]
212pub struct Multisig(_Multisig);
213
214#[derive(Clone)]
215enum _Multisig {
216 Ed25519(Signature),
218}
219
220impl fmt::Debug for _Multisig {
221 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222 match self {
223 _Multisig::Ed25519(data) => write!(f, "Ed25519 signature: {:?}", data.as_bytes()),
224 }
225 }
226}
227
228impl PartialEq for _Multisig {
229 fn eq(&self, other: &_Multisig) -> bool {
230 match (self, other) {
231 (_Multisig::Ed25519(ref a), _Multisig::Ed25519(ref b)) => a.as_bytes() == b.as_bytes(),
232 }
233 }
234}
235
236impl Eq for _Multisig {}
237
238impl Multikey {
239 pub fn sig_from_legacy<'a>(
241 &self,
242 s: &'a [u8],
243 ) -> Result<(Multisig, &'a [u8]), DecodeSignatureError> {
244 let (data, suffix) = split_at_byte(s, 0x2E).ok_or(DecodeSignatureError::NoDot)?;
245
246 let suffix = skip_prefix(suffix, b"sig").ok_or(DecodeSignatureError::NoDotSig)?;
247
248 match self {
249 Multikey::Ed25519(_) => {
250 let tail =
251 skip_prefix(suffix, b".ed25519").ok_or(DecodeSignatureError::UnknownSuffix)?;
252
253 if data.len() != ED25519_SIG_BASE64_LEN {
254 return Err(DecodeSignatureError::Ed25519WrongSize);
255 }
256
257 if data[ED25519_SIG_BASE64_LEN - 2] != b"="[0] {
258 return Err(DecodeSignatureError::Ed25519WrongSize);
259 }
260
261 let mut dec_data = [0_u8; 64];
262
263 base64::decode_config_slice(data, base64::STANDARD, &mut dec_data[..])
264 .map_err(DecodeSignatureError::InvalidBase64)
265 .map(|_| (Multisig::from_ed25519(&dec_data), tail))
266 }
267 }
268 }
269}
270
271impl Multisig {
272 pub fn from_ed25519(sig: &[u8; 64]) -> Multisig {
274 Multisig(_Multisig::Ed25519(Signature::from_slice(sig).unwrap()))
275 }
276
277 pub fn to_legacy<W: Write>(&self, w: &mut W) -> Result<(), io::Error> {
280 match self.0 {
281 _Multisig::Ed25519(ref sig) => {
282 let data = sig.as_base64();
283 w.write_all(data.as_bytes())?;
284 w.write_all(b".sig.ed25519")
285 }
286 }
287 }
288
289 pub fn to_legacy_vec(&self) -> Vec<u8> {
293 let mut data = vec![];
294 self.to_legacy(&mut data).unwrap();
295 data
296 }
297
298 pub fn to_legacy_string(&self) -> String {
302 String::from_utf8(self.to_legacy_vec()).unwrap()
303 }
304}
305
306#[derive(Debug, PartialEq, Eq, Clone)]
308pub enum DecodeSignatureError {
309 NoDot,
311 NoDotSig,
313 InvalidBase64(base64::DecodeError),
315 UnknownSuffix,
317 Ed25519WrongSize,
319}
320
321impl fmt::Display for DecodeSignatureError {
322 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
323 match self {
324 DecodeSignatureError::InvalidBase64(ref err) => write!(f, "{}", err),
325 DecodeSignatureError::NoDot => write!(f, "No dot"),
326 DecodeSignatureError::NoDotSig => write!(f, "No .sig"),
327 DecodeSignatureError::UnknownSuffix => write!(f, "Unknown suffix"),
328 DecodeSignatureError::Ed25519WrongSize => write!(f, "Data of wrong length"),
329 }
330 }
331}
332
333impl std::error::Error for DecodeSignatureError {}
334
335const ED25519_SUFFIX: &[u8] = b"ed25519";
337const ED25519_PK_BASE64_LEN: usize = 44;
339const ED25519_SIG_BASE64_LEN: usize = 88;
341const SSB_ED25519_SECRET_ENCODED_LEN: usize = 96;
343
344#[test]
345fn test_from_legacy() {
346 let valid_key = b"@zurF8X68ArfRM71dF3mKh36W0xDM8QmOnAS5bYOq8hA=.ed25519";
347 let (key, _) = Multikey::from_legacy(valid_key).unwrap();
348 let legacy_key = key.to_legacy_vec();
349
350 assert_eq!(legacy_key, valid_key);
351
352 assert!(
353 Multikey::from_legacy(b"@zurF8X68ArfRM71dF3mKh36W0xDM8QmOnAS5bYOq8hB=.ed25519").is_err()
354 );
355 assert!(
356 Multikey::from_legacy(b"&zurF8X68ArfRM71dF3mKh36W0xDM8QmOnAS5bYOq8hA=.ed25519").is_err()
357 );
358 assert!(
359 Multikey::from_legacy(b"@zurF8X68ArfRM71dF3mKh36W0xDM8QmOnAS5bYOq8hA=.dd25519").is_err()
360 );
361 assert!(
362 Multikey::from_legacy(b"@zurF8X68ArfRM71dF3mKh36W0xDM8QmOnAS5bYOq8hA=ed25519").is_err()
363 );
364 assert!(
365 Multikey::from_legacy(b"@zurF8X68ArfRM71dF3mKh36W0xDM8QmOnAS5bYOq8hA.ed25519").is_err()
366 );
367 assert!(
368 Multikey::from_legacy(b"@zurF8X68ArfRM71dF3mKh36W0xDM8QmOnAS5bYOq8hA==.ed25519").is_err()
369 );
370}