stellar_strkey/
ed25519.rs

1use crate::{
2    convert::{decode, encode},
3    error::DecodeError,
4    version,
5};
6
7use alloc::{format, string::String, vec, vec::Vec};
8use core::{
9    fmt::{Debug, Display},
10    str::FromStr,
11};
12
13#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
14#[cfg_attr(
15    feature = "serde",
16    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
17)]
18pub struct PrivateKey(pub [u8; 32]);
19
20impl Debug for PrivateKey {
21    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22        write!(f, "PrivateKey(")?;
23        write!(
24            f,
25            "{}",
26            &self
27                .0
28                .iter()
29                .map(|b| format!("{b:02x}"))
30                .collect::<String>()
31        )?;
32        write!(f, ")")?;
33        Ok(())
34    }
35}
36
37impl PrivateKey {
38    pub fn to_string(&self) -> String {
39        encode(version::PRIVATE_KEY_ED25519, &self.0)
40    }
41
42    pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
43        match payload.try_into() {
44            Ok(ed25519) => Ok(Self(ed25519)),
45            Err(_) => Err(DecodeError::Invalid),
46        }
47    }
48
49    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
50        let (ver, payload) = decode(s)?;
51        match ver {
52            version::PRIVATE_KEY_ED25519 => Self::from_payload(&payload),
53            _ => Err(DecodeError::Invalid),
54        }
55    }
56}
57
58impl Display for PrivateKey {
59    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60        write!(f, "{}", self.to_string())
61    }
62}
63
64impl FromStr for PrivateKey {
65    type Err = DecodeError;
66
67    fn from_str(s: &str) -> Result<Self, Self::Err> {
68        PrivateKey::from_string(s)
69    }
70}
71
72#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
73#[cfg_attr(
74    feature = "serde",
75    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
76)]
77pub struct PublicKey(pub [u8; 32]);
78
79impl Debug for PublicKey {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        write!(f, "PublicKey(")?;
82        write!(
83            f,
84            "{}",
85            &self
86                .0
87                .iter()
88                .map(|b| format!("{b:02x}"))
89                .collect::<String>()
90        )?;
91        write!(f, ")")?;
92        Ok(())
93    }
94}
95
96impl PublicKey {
97    pub fn to_string(&self) -> String {
98        encode(version::PUBLIC_KEY_ED25519, &self.0)
99    }
100
101    pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
102        match payload.try_into() {
103            Ok(ed25519) => Ok(Self(ed25519)),
104            Err(_) => Err(DecodeError::Invalid),
105        }
106    }
107
108    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
109        let (ver, payload) = decode(s)?;
110        match ver {
111            version::PUBLIC_KEY_ED25519 => Self::from_payload(&payload),
112            _ => Err(DecodeError::Invalid),
113        }
114    }
115}
116
117impl Display for PublicKey {
118    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119        write!(f, "{}", self.to_string())
120    }
121}
122
123impl FromStr for PublicKey {
124    type Err = DecodeError;
125
126    fn from_str(s: &str) -> Result<Self, Self::Err> {
127        PublicKey::from_string(s)
128    }
129}
130
131#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
132#[cfg_attr(
133    feature = "serde",
134    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
135)]
136pub struct MuxedAccount {
137    pub ed25519: [u8; 32],
138    pub id: u64,
139}
140
141impl Debug for MuxedAccount {
142    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
143        write!(f, "MuxedAccount(")?;
144        write!(
145            f,
146            "{}",
147            &self
148                .ed25519
149                .iter()
150                .map(|b| format!("{b:02x}"))
151                .collect::<String>()
152        )?;
153        write!(f, ", ")?;
154        write!(f, "{}", self.id)?;
155        write!(f, ")")?;
156        Ok(())
157    }
158}
159
160impl MuxedAccount {
161    pub fn to_string(&self) -> String {
162        let mut payload: [u8; 40] = [0; 40];
163        let (ed25519, id) = payload.split_at_mut(32);
164        ed25519.copy_from_slice(&self.ed25519);
165        id.copy_from_slice(&self.id.to_be_bytes());
166        encode(version::MUXED_ACCOUNT_ED25519, &payload)
167    }
168
169    pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
170        if payload.len() < 40 {
171            return Err(DecodeError::Invalid);
172        }
173        let (ed25519, id) = payload.split_at(32);
174        Ok(Self {
175            ed25519: ed25519.try_into().map_err(|_| DecodeError::Invalid)?,
176            id: u64::from_be_bytes(id.try_into().map_err(|_| DecodeError::Invalid)?),
177        })
178    }
179
180    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
181        let (ver, payload) = decode(s)?;
182        match ver {
183            version::MUXED_ACCOUNT_ED25519 => Self::from_payload(&payload),
184            _ => Err(DecodeError::Invalid),
185        }
186    }
187}
188
189impl Display for MuxedAccount {
190    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
191        write!(f, "{}", self.to_string())
192    }
193}
194
195impl FromStr for MuxedAccount {
196    type Err = DecodeError;
197
198    fn from_str(s: &str) -> Result<Self, Self::Err> {
199        MuxedAccount::from_string(s)
200    }
201}
202
203/// Stores a signed payload ed25519 signer.
204///
205/// The payload must not have a size larger than u32::MAX.
206#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
207#[cfg_attr(
208    feature = "serde",
209    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
210)]
211pub struct SignedPayload {
212    pub ed25519: [u8; 32],
213    pub payload: Vec<u8>,
214}
215
216impl Debug for SignedPayload {
217    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
218        write!(f, "MuxedAccount(")?;
219        write!(
220            f,
221            "{}",
222            &self
223                .ed25519
224                .iter()
225                .map(|b| format!("{b:02x}"))
226                .collect::<String>()
227        )?;
228        write!(f, ", ")?;
229        write!(
230            f,
231            "{}",
232            &self
233                .payload
234                .iter()
235                .map(|b| format!("{b:02x}"))
236                .collect::<String>()
237        )?;
238        write!(f, ")")?;
239        Ok(())
240    }
241}
242
243impl SignedPayload {
244    /// Returns the strkey string for the signed payload signer.
245    ///
246    /// ### Panics
247    ///
248    /// When the payload is larger than u32::MAX.
249    pub fn to_string(&self) -> String {
250        let inner_payload_len = self.payload.len();
251        let payload_len = 32 + 4 + inner_payload_len + (4 - inner_payload_len % 4) % 4;
252
253        let inner_payload_len_u32: u32 = inner_payload_len
254            .try_into()
255            .expect("payload length larger than u32::MAX");
256
257        let mut payload = vec![0; payload_len];
258        payload[..32].copy_from_slice(&self.ed25519);
259        payload[32..32 + 4].copy_from_slice(&(inner_payload_len_u32).to_be_bytes());
260        payload[32 + 4..32 + 4 + inner_payload_len].copy_from_slice(&self.payload);
261
262        encode(version::SIGNED_PAYLOAD_ED25519, &payload)
263    }
264
265    pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
266        // 32-byte for the signer, 4-byte for the payload size, then either 4-byte for the
267        // min or 64-byte for the max payload
268        const MAX_INNER_PAYLOAD_LENGTH: u32 = 64;
269        const MIN_LENGTH: usize = 32 + 4 + 4;
270        const MAX_LENGTH: usize = 32 + 4 + (MAX_INNER_PAYLOAD_LENGTH as usize);
271        let payload_len = payload.len();
272        if !(MIN_LENGTH..=MAX_LENGTH).contains(&payload_len) {
273            return Err(DecodeError::Invalid);
274        }
275
276        // Decode ed25519 public key. 32 bytes.
277        let mut offset = 0;
278        let ed25519: [u8; 32] = payload
279            .get(offset..offset + 32)
280            .ok_or(DecodeError::Invalid)?
281            .try_into()
282            .map_err(|_| DecodeError::Invalid)?;
283        offset += 32;
284
285        // Decode inner payload length. 4 bytes.
286        let inner_payload_len = u32::from_be_bytes(
287            payload
288                .get(offset..offset + 4)
289                .ok_or(DecodeError::Invalid)?
290                .try_into()
291                .map_err(|_| DecodeError::Invalid)?,
292        );
293        offset += 4;
294
295        // Check inner payload length is inside accepted range.
296        if inner_payload_len > MAX_INNER_PAYLOAD_LENGTH {
297            return Err(DecodeError::Invalid);
298        }
299
300        // Decode inner payload.
301        let inner_payload = payload
302            .get(offset..offset + inner_payload_len as usize)
303            .ok_or(DecodeError::Invalid)?;
304        offset += inner_payload_len as usize;
305
306        // Calculate padding at end of inner payload. 0-3 bytes.
307        let padding_len = (4 - inner_payload_len % 4) % 4;
308
309        // Decode padding.
310        let padding = payload
311            .get(offset..offset + padding_len as usize)
312            .ok_or(DecodeError::Invalid)?;
313        offset += padding_len as usize;
314
315        // Check padding is all zeros.
316        if padding.iter().any(|b| *b != 0) {
317            return Err(DecodeError::Invalid);
318        }
319
320        // Check that entire payload consumed.
321        if offset != payload_len {
322            return Err(DecodeError::Invalid);
323        }
324
325        Ok(Self {
326            ed25519,
327            payload: inner_payload.to_vec(),
328        })
329    }
330
331    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
332        let (ver, payload) = decode(s)?;
333        match ver {
334            version::SIGNED_PAYLOAD_ED25519 => Self::from_payload(&payload),
335            _ => Err(DecodeError::Invalid),
336        }
337    }
338}
339
340impl Display for SignedPayload {
341    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
342        write!(f, "{}", self.to_string())
343    }
344}
345
346impl FromStr for SignedPayload {
347    type Err = DecodeError;
348
349    fn from_str(s: &str) -> Result<Self, Self::Err> {
350        SignedPayload::from_string(s)
351    }
352}