stellar_strkey/
ed25519.rs

1use crate::{
2    convert::{binary_len, decode, encode, encode_len},
3    error::DecodeError,
4    version,
5};
6
7use core::{
8    fmt::{Debug, Display},
9    str::FromStr,
10};
11use heapless::{String, Vec};
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        for b in &self.0 {
24            write!(f, "{b:02x}")?;
25        }
26        write!(f, ")")
27    }
28}
29
30impl PrivateKey {
31    pub(crate) const PAYLOAD_LEN: usize = 32;
32    pub(crate) const BINARY_LEN: usize = binary_len(Self::PAYLOAD_LEN);
33    pub(crate) const ENCODED_LEN: usize = encode_len(Self::BINARY_LEN);
34    const _ASSERTS: () = {
35        assert!(Self::BINARY_LEN == 35);
36        assert!(Self::ENCODED_LEN == 56);
37    };
38
39    pub fn to_string(&self) -> String<{ Self::ENCODED_LEN }> {
40        encode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }, { Self::ENCODED_LEN }>(
41            version::PRIVATE_KEY_ED25519,
42            &self.0,
43        )
44    }
45
46    pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
47        match payload.try_into() {
48            Ok(ed25519) => Ok(Self(ed25519)),
49            Err(_) => Err(DecodeError::Invalid),
50        }
51    }
52
53    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
54        Self::from_slice(s.as_bytes())
55    }
56
57    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
58        let (ver, payload) = decode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }>(s)?;
59        match ver {
60            version::PRIVATE_KEY_ED25519 => Self::from_payload(&payload),
61            _ => Err(DecodeError::Invalid),
62        }
63    }
64}
65
66impl Display for PrivateKey {
67    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68        write!(f, "{}", self.to_string())
69    }
70}
71
72impl FromStr for PrivateKey {
73    type Err = DecodeError;
74
75    fn from_str(s: &str) -> Result<Self, Self::Err> {
76        PrivateKey::from_string(s)
77    }
78}
79
80#[cfg(feature = "serde-decoded")]
81mod private_key_decoded_serde_impl {
82    use super::*;
83    use crate::decoded_json_format::Decoded;
84    use serde::{Deserialize, Deserializer, Serialize, Serializer};
85    use serde_with::serde_as;
86
87    #[serde_as]
88    #[derive(Serialize)]
89    #[serde(transparent)]
90    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
91
92    #[serde_as]
93    #[derive(Deserialize)]
94    #[serde(transparent)]
95    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
96
97    impl Serialize for Decoded<&PrivateKey> {
98        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
99            let Self(PrivateKey(bytes)) = self;
100            DecodedBorrowed(bytes).serialize(serializer)
101        }
102    }
103
104    impl<'de> Deserialize<'de> for Decoded<PrivateKey> {
105        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
106            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
107            Ok(Decoded(PrivateKey(bytes)))
108        }
109    }
110}
111
112#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
113#[cfg_attr(
114    feature = "serde",
115    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
116)]
117pub struct PublicKey(pub [u8; 32]);
118
119impl Debug for PublicKey {
120    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121        write!(f, "PublicKey(")?;
122        for b in &self.0 {
123            write!(f, "{b:02x}")?;
124        }
125        write!(f, ")")
126    }
127}
128
129impl PublicKey {
130    pub(crate) const PAYLOAD_LEN: usize = 32;
131    pub(crate) const BINARY_LEN: usize = binary_len(Self::PAYLOAD_LEN);
132    pub(crate) const ENCODED_LEN: usize = encode_len(Self::BINARY_LEN);
133    const _ASSERTS: () = {
134        assert!(Self::BINARY_LEN == 35);
135        assert!(Self::ENCODED_LEN == 56);
136    };
137
138    pub fn to_string(&self) -> String<{ Self::ENCODED_LEN }> {
139        encode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }, { Self::ENCODED_LEN }>(
140            version::PUBLIC_KEY_ED25519,
141            &self.0,
142        )
143    }
144
145    pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
146        match payload.try_into() {
147            Ok(ed25519) => Ok(Self(ed25519)),
148            Err(_) => Err(DecodeError::Invalid),
149        }
150    }
151
152    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
153        Self::from_slice(s.as_bytes())
154    }
155
156    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
157        let (ver, payload) = decode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }>(s)?;
158        match ver {
159            version::PUBLIC_KEY_ED25519 => Self::from_payload(&payload),
160            _ => Err(DecodeError::Invalid),
161        }
162    }
163}
164
165impl Display for PublicKey {
166    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
167        write!(f, "{}", self.to_string())
168    }
169}
170
171impl FromStr for PublicKey {
172    type Err = DecodeError;
173
174    fn from_str(s: &str) -> Result<Self, Self::Err> {
175        PublicKey::from_string(s)
176    }
177}
178
179#[cfg(feature = "serde-decoded")]
180mod public_key_decoded_serde_impl {
181    use super::*;
182    use crate::decoded_json_format::Decoded;
183    use serde::{Deserialize, Deserializer, Serialize, Serializer};
184    use serde_with::serde_as;
185
186    #[serde_as]
187    #[derive(Serialize)]
188    #[serde(transparent)]
189    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
190
191    #[serde_as]
192    #[derive(Deserialize)]
193    #[serde(transparent)]
194    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
195
196    impl Serialize for Decoded<&PublicKey> {
197        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
198            let Self(PublicKey(bytes)) = self;
199            DecodedBorrowed(bytes).serialize(serializer)
200        }
201    }
202
203    impl<'de> Deserialize<'de> for Decoded<PublicKey> {
204        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
205            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
206            Ok(Decoded(PublicKey(bytes)))
207        }
208    }
209}
210
211#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
212#[cfg_attr(
213    feature = "serde",
214    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
215)]
216pub struct MuxedAccount {
217    pub ed25519: [u8; 32],
218    pub id: u64,
219}
220
221impl Debug for MuxedAccount {
222    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
223        write!(f, "MuxedAccount(")?;
224        for b in &self.ed25519 {
225            write!(f, "{b:02x}")?;
226        }
227        write!(f, ", {}", self.id)?;
228        write!(f, ")")
229    }
230}
231
232impl MuxedAccount {
233    pub(crate) const PAYLOAD_LEN: usize = 32 + 8; // ed25519 + id
234    pub(crate) const BINARY_LEN: usize = binary_len(Self::PAYLOAD_LEN);
235    pub(crate) const ENCODED_LEN: usize = encode_len(Self::BINARY_LEN);
236    const _ASSERTS: () = {
237        assert!(Self::BINARY_LEN == 43);
238        assert!(Self::ENCODED_LEN == 69);
239    };
240
241    pub fn to_string(&self) -> String<{ Self::ENCODED_LEN }> {
242        let mut payload: [u8; Self::PAYLOAD_LEN] = [0; Self::PAYLOAD_LEN];
243        let (ed25519, id) = payload.split_at_mut(32);
244        ed25519.copy_from_slice(&self.ed25519);
245        id.copy_from_slice(&self.id.to_be_bytes());
246        encode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }, { Self::ENCODED_LEN }>(
247            version::MUXED_ACCOUNT_ED25519,
248            &payload,
249        )
250    }
251
252    pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
253        if payload.len() < 40 {
254            return Err(DecodeError::Invalid);
255        }
256        let (ed25519, id) = payload.split_at(32);
257        Ok(Self {
258            ed25519: ed25519.try_into().map_err(|_| DecodeError::Invalid)?,
259            id: u64::from_be_bytes(id.try_into().map_err(|_| DecodeError::Invalid)?),
260        })
261    }
262
263    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
264        Self::from_slice(s.as_bytes())
265    }
266
267    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
268        let (ver, payload) = decode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }>(s)?;
269        match ver {
270            version::MUXED_ACCOUNT_ED25519 => Self::from_payload(&payload),
271            _ => Err(DecodeError::Invalid),
272        }
273    }
274}
275
276impl Display for MuxedAccount {
277    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
278        write!(f, "{}", self.to_string())
279    }
280}
281
282impl FromStr for MuxedAccount {
283    type Err = DecodeError;
284
285    fn from_str(s: &str) -> Result<Self, Self::Err> {
286        MuxedAccount::from_string(s)
287    }
288}
289
290#[cfg(feature = "serde-decoded")]
291mod muxed_account_decoded_serde_impl {
292    use super::*;
293    use crate::decoded_json_format::Decoded;
294    use serde::{Deserialize, Deserializer, Serialize, Serializer};
295    use serde_with::serde_as;
296
297    #[serde_as]
298    #[derive(Serialize)]
299    struct DecodedBorrowed<'a> {
300        #[serde_as(as = "serde_with::hex::Hex")]
301        ed25519: &'a [u8; 32],
302        id: u64,
303    }
304
305    #[serde_as]
306    #[derive(Deserialize)]
307    struct DecodedOwned {
308        #[serde_as(as = "serde_with::hex::Hex")]
309        ed25519: [u8; 32],
310        id: u64,
311    }
312
313    impl Serialize for Decoded<&MuxedAccount> {
314        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
315            let Self(MuxedAccount { ed25519, id }) = self;
316            DecodedBorrowed { ed25519, id: *id }.serialize(serializer)
317        }
318    }
319
320    impl<'de> Deserialize<'de> for Decoded<MuxedAccount> {
321        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
322            let DecodedOwned { ed25519, id } = DecodedOwned::deserialize(deserializer)?;
323            Ok(Decoded(MuxedAccount { ed25519, id }))
324        }
325    }
326}
327
328/// Stores a signed payload ed25519 signer.
329///
330/// The payload must not have a size larger than 64 bytes.
331#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
332#[cfg_attr(
333    feature = "serde",
334    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
335)]
336pub struct SignedPayload {
337    pub ed25519: [u8; 32],
338    pub payload: Vec<u8, 64>,
339}
340
341impl Debug for SignedPayload {
342    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
343        write!(f, "SignedPayload(")?;
344        for b in &self.ed25519 {
345            write!(f, "{b:02x}")?;
346        }
347        write!(f, ", ")?;
348        for b in &self.payload {
349            write!(f, "{b:02x}")?;
350        }
351        write!(f, ")")
352    }
353}
354
355impl SignedPayload {
356    // Max payload: 32 ed25519 + 4 len + 64 inner payload = 100
357    pub(crate) const MAX_PAYLOAD_LEN: usize = 32 + 4 + 64;
358    pub(crate) const MAX_BINARY_LEN: usize = binary_len(Self::MAX_PAYLOAD_LEN);
359    pub(crate) const MAX_ENCODED_LEN: usize = encode_len(Self::MAX_BINARY_LEN);
360    const _ASSERTS: () = {
361        assert!(Self::MAX_PAYLOAD_LEN == 100);
362        assert!(Self::MAX_BINARY_LEN == 103);
363        assert!(Self::MAX_ENCODED_LEN == 165);
364    };
365
366    /// Returns the strkey string for the signed payload signer.
367    pub fn to_string(&self) -> String<{ Self::MAX_ENCODED_LEN }> {
368        let inner_payload_len = self.payload.len();
369        let payload_len = 32 + 4 + inner_payload_len + (4 - inner_payload_len % 4) % 4;
370
371        let inner_payload_len_u32: u32 = inner_payload_len as u32;
372
373        // Max payload_len is 100 (32 + 4 + 64), use fixed array
374        let mut payload = [0u8; Self::MAX_PAYLOAD_LEN];
375        payload[..32].copy_from_slice(&self.ed25519);
376        payload[32..32 + 4].copy_from_slice(&(inner_payload_len_u32).to_be_bytes());
377        payload[32 + 4..32 + 4 + inner_payload_len].copy_from_slice(&self.payload);
378
379        encode::<{ Self::MAX_PAYLOAD_LEN }, { Self::MAX_BINARY_LEN }, { Self::MAX_ENCODED_LEN }>(
380            version::SIGNED_PAYLOAD_ED25519,
381            &payload[..payload_len],
382        )
383    }
384
385    /// Decodes a signed payload from raw bytes.
386    ///
387    /// ### Errors
388    ///
389    /// If the payload is larger than 64 bytes.
390    pub fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
391        // 32-byte for the signer, 4-byte for the payload size, then either 4-byte for the
392        // min or 64-byte for the max payload
393        const MAX_INNER_PAYLOAD_LENGTH: u32 = 64;
394        const MIN_LENGTH: usize = 32 + 4 + 4;
395        const MAX_LENGTH: usize = 32 + 4 + (MAX_INNER_PAYLOAD_LENGTH as usize);
396        let payload_len = payload.len();
397        if !(MIN_LENGTH..=MAX_LENGTH).contains(&payload_len) {
398            return Err(DecodeError::Invalid);
399        }
400
401        // Decode ed25519 public key. 32 bytes.
402        let mut offset = 0;
403        let ed25519: [u8; 32] = payload
404            .get(offset..offset + 32)
405            .ok_or(DecodeError::Invalid)?
406            .try_into()
407            .map_err(|_| DecodeError::Invalid)?;
408        offset += 32;
409
410        // Decode inner payload length. 4 bytes.
411        let inner_payload_len = u32::from_be_bytes(
412            payload
413                .get(offset..offset + 4)
414                .ok_or(DecodeError::Invalid)?
415                .try_into()
416                .map_err(|_| DecodeError::Invalid)?,
417        );
418        offset += 4;
419
420        // Check inner payload length is inside accepted range.
421        if inner_payload_len > MAX_INNER_PAYLOAD_LENGTH {
422            return Err(DecodeError::Invalid);
423        }
424
425        // Decode inner payload.
426        let inner_payload = payload
427            .get(offset..offset + inner_payload_len as usize)
428            .ok_or(DecodeError::Invalid)?;
429        offset += inner_payload_len as usize;
430
431        // Calculate padding at end of inner payload. 0-3 bytes.
432        let padding_len = (4 - inner_payload_len % 4) % 4;
433
434        // Decode padding.
435        let padding = payload
436            .get(offset..offset + padding_len as usize)
437            .ok_or(DecodeError::Invalid)?;
438        offset += padding_len as usize;
439
440        // Check padding is all zeros.
441        if padding.iter().any(|b| *b != 0) {
442            return Err(DecodeError::Invalid);
443        }
444
445        // Check that entire payload consumed.
446        if offset != payload_len {
447            return Err(DecodeError::Invalid);
448        }
449
450        let mut payload = Vec::new();
451        payload
452            .extend_from_slice(inner_payload)
453            .map_err(|_| DecodeError::Invalid)?;
454        Ok(Self { ed25519, payload })
455    }
456
457    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
458        Self::from_slice(s.as_bytes())
459    }
460
461    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
462        let (ver, payload) = decode::<{ Self::MAX_PAYLOAD_LEN }, { Self::MAX_BINARY_LEN }>(s)?;
463        match ver {
464            version::SIGNED_PAYLOAD_ED25519 => Self::from_payload(&payload),
465            _ => Err(DecodeError::Invalid),
466        }
467    }
468}
469
470impl Display for SignedPayload {
471    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
472        write!(f, "{}", self.to_string())
473    }
474}
475
476impl FromStr for SignedPayload {
477    type Err = DecodeError;
478
479    fn from_str(s: &str) -> Result<Self, Self::Err> {
480        SignedPayload::from_string(s)
481    }
482}
483
484#[cfg(feature = "serde-decoded")]
485mod signed_payload_decoded_serde_impl {
486    use super::SignedPayload;
487    use crate::decoded_json_format::Decoded;
488    use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
489    use serde_with::serde_as;
490
491    #[serde_as]
492    #[derive(Serialize)]
493    struct DecodedBorrowed<'a> {
494        #[serde_as(as = "serde_with::hex::Hex")]
495        ed25519: &'a [u8; 32],
496        #[serde_as(as = "serde_with::hex::Hex")]
497        payload: &'a [u8],
498    }
499
500    #[serde_as]
501    #[derive(Deserialize)]
502    struct DecodedOwned {
503        #[serde_as(as = "serde_with::hex::Hex")]
504        ed25519: [u8; 32],
505        #[serde_as(as = "serde_with::hex::Hex")]
506        payload: alloc::vec::Vec<u8>,
507    }
508
509    impl Serialize for Decoded<&SignedPayload> {
510        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
511            let Self(SignedPayload { ed25519, payload }) = self;
512            DecodedBorrowed { ed25519, payload }.serialize(serializer)
513        }
514    }
515
516    impl<'de> Deserialize<'de> for Decoded<SignedPayload> {
517        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
518            let DecodedOwned { ed25519, payload } = DecodedOwned::deserialize(deserializer)?;
519            Ok(Decoded(SignedPayload {
520                ed25519,
521                payload: payload
522                    .as_slice()
523                    .try_into()
524                    .map_err(|_| de::Error::custom("payload too large"))?,
525            }))
526        }
527    }
528}