Skip to main content

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