iota_sdk_types/crypto/
ed25519.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! Implementation of ed25519 public-key cryptogrophy.
6
7use crate::crypto::{PublicKeyExt, SignatureScheme};
8
9/// An ed25519 public key.
10///
11/// # BCS
12///
13/// The BCS serialized form for this type is defined by the following ABNF:
14///
15/// ```text
16/// ed25519-public-key = 32OCTECT
17/// ```
18#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
21#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
22pub struct Ed25519PublicKey(
23    #[cfg_attr(
24        feature = "serde",
25        serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable<super::Base64Array32>>")
26    )]
27    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::Base64"))]
28    [u8; Self::LENGTH],
29);
30
31impl Ed25519PublicKey {
32    /// The length of an ed25519 public key in bytes.
33    pub const LENGTH: usize = 32;
34
35    pub const fn new(bytes: [u8; Self::LENGTH]) -> Self {
36        Self(bytes)
37    }
38
39    #[cfg(feature = "rand")]
40    #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))]
41    pub fn generate<R>(mut rng: R) -> Self
42    where
43        R: rand_core::RngCore + rand_core::CryptoRng,
44    {
45        let mut buf: [u8; Self::LENGTH] = [0; Self::LENGTH];
46        rng.fill_bytes(&mut buf);
47        Self::new(buf)
48    }
49
50    /// Return the underlying byte array of an Ed25519PublicKey.
51    pub const fn into_inner(self) -> [u8; Self::LENGTH] {
52        self.0
53    }
54
55    pub const fn inner(&self) -> &[u8; Self::LENGTH] {
56        &self.0
57    }
58}
59
60impl PublicKeyExt for Ed25519PublicKey {
61    type FromBytesErr = std::array::TryFromSliceError;
62
63    /// Returns the public key as bytes.
64    fn as_bytes(&self) -> &[u8] {
65        &self.0
66    }
67
68    /// Tries to create an Ed25519PublicKey from bytes.
69    fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, Self::FromBytesErr> {
70        <[u8; Self::LENGTH]>::try_from(bytes.as_ref()).map(Self)
71    }
72
73    /// Returns the signature scheme for this public key.
74    fn scheme(&self) -> SignatureScheme {
75        SignatureScheme::Ed25519
76    }
77}
78
79impl std::str::FromStr for Ed25519PublicKey {
80    type Err = base64ct::Error;
81
82    fn from_str(s: &str) -> Result<Self, Self::Err> {
83        super::Base64FromStr32::from_str(s).map(|a| Self::new(a.0))
84    }
85}
86
87impl AsRef<[u8]> for Ed25519PublicKey {
88    fn as_ref(&self) -> &[u8] {
89        &self.0
90    }
91}
92
93impl AsRef<[u8; Self::LENGTH]> for Ed25519PublicKey {
94    fn as_ref(&self) -> &[u8; Self::LENGTH] {
95        &self.0
96    }
97}
98
99impl From<Ed25519PublicKey> for [u8; Ed25519PublicKey::LENGTH] {
100    fn from(public_key: Ed25519PublicKey) -> Self {
101        public_key.into_inner()
102    }
103}
104
105impl From<[u8; Self::LENGTH]> for Ed25519PublicKey {
106    fn from(public_key: [u8; Self::LENGTH]) -> Self {
107        Self::new(public_key)
108    }
109}
110
111impl std::fmt::Display for Ed25519PublicKey {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        std::fmt::Display::fmt(&super::Base64Display32(&self.0), f)
114    }
115}
116
117impl std::fmt::Debug for Ed25519PublicKey {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        f.debug_tuple("Ed25519PublicKey")
120            .field(&format_args!("\"{self}\""))
121            .finish()
122    }
123}
124
125/// An ed25519 signature.
126///
127/// # BCS
128///
129/// The BCS serialized form for this type is defined by the following ABNF:
130///
131/// ```text
132/// ed25519-signature = 64OCTECT
133/// ```
134#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
137#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
138pub struct Ed25519Signature(
139    #[cfg_attr(
140        feature = "serde",
141        serde(
142            with = "::serde_with::As::<::serde_with::IfIsHumanReadable<super::Base64Array64, [::serde_with::Same; 64]>>"
143        )
144    )]
145    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::Base64"))]
146    [u8; Self::LENGTH],
147);
148
149impl Ed25519Signature {
150    /// The length of an ed25519 signature key in bytes.
151    pub const LENGTH: usize = 64;
152
153    pub const fn new(bytes: [u8; Self::LENGTH]) -> Self {
154        Self(bytes)
155    }
156
157    #[cfg(feature = "rand")]
158    #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))]
159    pub fn generate<R>(mut rng: R) -> Self
160    where
161        R: rand_core::RngCore + rand_core::CryptoRng,
162    {
163        let mut buf: [u8; Self::LENGTH] = [0; Self::LENGTH];
164        rng.fill_bytes(&mut buf);
165        Self::new(buf)
166    }
167
168    /// Return the underlying byte array of an Ed25519Signature.
169    pub const fn into_inner(self) -> [u8; Self::LENGTH] {
170        self.0
171    }
172
173    pub const fn inner(&self) -> &[u8; Self::LENGTH] {
174        &self.0
175    }
176
177    pub const fn as_bytes(&self) -> &[u8] {
178        &self.0
179    }
180
181    pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, std::array::TryFromSliceError> {
182        <[u8; Self::LENGTH]>::try_from(bytes.as_ref()).map(Self)
183    }
184}
185
186impl std::str::FromStr for Ed25519Signature {
187    type Err = base64ct::Error;
188
189    fn from_str(s: &str) -> Result<Self, Self::Err> {
190        super::Base64FromStr64::from_str(s).map(|a| Self::new(a.0))
191    }
192}
193
194impl AsRef<[u8]> for Ed25519Signature {
195    fn as_ref(&self) -> &[u8] {
196        &self.0
197    }
198}
199
200impl AsRef<[u8; Self::LENGTH]> for Ed25519Signature {
201    fn as_ref(&self) -> &[u8; Self::LENGTH] {
202        &self.0
203    }
204}
205
206impl From<Ed25519Signature> for [u8; Ed25519Signature::LENGTH] {
207    fn from(signature: Ed25519Signature) -> Self {
208        signature.into_inner()
209    }
210}
211
212impl From<[u8; Self::LENGTH]> for Ed25519Signature {
213    fn from(signature: [u8; Self::LENGTH]) -> Self {
214        Self::new(signature)
215    }
216}
217
218impl std::fmt::Display for Ed25519Signature {
219    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220        std::fmt::Display::fmt(&super::Base64Display64(&self.0), f)
221    }
222}
223
224impl std::fmt::Debug for Ed25519Signature {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        f.debug_tuple("Ed25519Signature")
227            .field(&format_args!("\"{self}\""))
228            .finish()
229    }
230}