Skip to main content

defuse_crypto/
ed25519.rs

1//! Ed25519 Digital Signature Algorithm
2
3pub use ed25519_dalek;
4use ed25519_dalek::{Signature, VerifyingKey};
5
6use crate::Curve;
7
8/// Ed25519 Digital Signature Algorithm
9pub struct Ed25519;
10
11impl Curve for Ed25519 {
12    type PublicKey = VerifyingKey;
13
14    type Signature = Signature;
15
16    /// Verify ed25519 signature over given message (of arbitrary length)
17    /// for given public key. Hashing the
18    #[inline]
19    fn verify(public_key: &Self::PublicKey, msg: &[u8], signature: &Self::Signature) -> bool {
20        if public_key.is_weak() {
21            // prevent using weak (i.e. low order) public keys, see
22            // https://github.com/dalek-cryptography/ed25519-dalek#weak-key-forgery-and-verify_strict
23            return false;
24        }
25
26        cfg_select! {
27            near => {
28                ::near_sdk::env::ed25519_verify(
29                    &signature.to_bytes(),
30                    msg,
31                    public_key.as_bytes(),
32                )
33            }
34            _ => {{
35                use ed25519_dalek::Verifier;
36
37                public_key.verify(msg, signature).is_ok()
38            }}
39        }
40    }
41}
42
43#[cfg(feature = "signing")]
44const _: () = {
45    use core::convert::Infallible;
46
47    use ed25519_dalek::SigningKey;
48    use futures_util::future;
49
50    use crate::Signer;
51
52    impl Signer<Ed25519> for SigningKey {
53        type Error = Infallible;
54
55        #[inline]
56        fn public_key(&self) -> <Ed25519 as Curve>::PublicKey {
57            self.verifying_key()
58        }
59
60        #[inline]
61        fn sign(
62            &self,
63            msg: &[u8],
64        ) -> impl Future<Output = Result<<Ed25519 as Curve>::Signature, Self::Error>> + Send
65        {
66            future::ok(ed25519_dalek::Signer::sign(self, msg))
67        }
68    }
69};
70
71/// Ed25519 public key
72#[cfg_attr(
73    feature = "serde",
74    derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
75    cfg_attr(
76        feature = "schemars-v0_8",
77        derive(::schemars::JsonSchema),
78        schemars(example = "Self::example")
79    )
80)]
81#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
82#[cfg_attr(
83    feature = "borsh",
84    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
85    cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
86)]
87#[derive(
88    Debug,
89    Clone,
90    Copy,
91    PartialEq,
92    Eq,
93    PartialOrd,
94    Ord,
95    Hash,
96    derive_more::AsRef,
97    derive_more::From,
98    derive_more::Into,
99)]
100#[as_ref([u8], [u8; 32])]
101#[into(owned, ref)]
102#[repr(transparent)]
103pub struct Ed25519PublicKey(
104    // schemars@0.8 ignores `with` at struct level for newtypes; must be on the field
105    #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 32],
106);
107
108impl Ed25519PublicKey {
109    #[cfg(feature = "schemars-v0_8")]
110    const fn example() -> Self {
111        Self(hex_literal::hex!(
112            "8565df94b8caab08f28cdd2ee014b800915741d4694fa840e50cca02ae5c6466"
113        ))
114    }
115}
116
117impl From<VerifyingKey> for Ed25519PublicKey {
118    #[inline]
119    fn from(value: VerifyingKey) -> Self {
120        (&value).into()
121    }
122}
123
124impl From<&VerifyingKey> for Ed25519PublicKey {
125    #[inline]
126    fn from(value: &VerifyingKey) -> Self {
127        Self(value.to_bytes())
128    }
129}
130
131impl TryFrom<Ed25519PublicKey> for VerifyingKey {
132    type Error = ed25519_dalek::ed25519::Error;
133
134    #[inline]
135    fn try_from(value: Ed25519PublicKey) -> Result<Self, Self::Error> {
136        (&value).try_into()
137    }
138}
139
140impl TryFrom<&Ed25519PublicKey> for VerifyingKey {
141    type Error = ed25519_dalek::ed25519::Error;
142
143    #[inline]
144    fn try_from(value: &Ed25519PublicKey) -> Result<Self, Self::Error> {
145        Self::from_bytes(&value.0)
146    }
147}
148
149/// Ed25519 signature
150#[cfg_attr(
151    feature = "serde",
152    derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
153    cfg_attr(
154        feature = "schemars-v0_8",
155        derive(::schemars::JsonSchema),
156        schemars(example = "Self::example"),
157    )
158)]
159#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
160#[cfg_attr(
161    feature = "borsh",
162    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
163    cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
164)]
165#[derive(
166    Debug,
167    Clone,
168    Copy,
169    PartialEq,
170    Eq,
171    PartialOrd,
172    Ord,
173    Hash,
174    derive_more::AsRef,
175    derive_more::From,
176    derive_more::Into,
177)]
178#[as_ref([u8], [u8; 64])]
179#[into(owned, ref)]
180#[repr(transparent)]
181pub struct Ed25519Signature(
182    // schemars@0.8 ignores `with` at struct level for newtypes; must be on the field
183    #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 64],
184);
185
186impl Ed25519Signature {
187    #[cfg(feature = "schemars-v0_8")]
188    const fn example() -> Self {
189        Self(hex_literal::hex!(
190            "e4822e15e5988bf08c80b72f2d1292b7229029f342d42bb9dfe4e230c66c10a6c4a86a47ddc58b1446baedf2f1312294d59638c812082a0124e513d4eb16c40e"
191        ))
192    }
193}
194
195impl From<Signature> for Ed25519Signature {
196    #[inline]
197    fn from(value: Signature) -> Self {
198        (&value).into()
199    }
200}
201
202impl From<&Signature> for Ed25519Signature {
203    #[inline]
204    fn from(value: &Signature) -> Self {
205        Self(value.to_bytes())
206    }
207}
208
209impl From<Ed25519Signature> for Signature {
210    #[inline]
211    fn from(value: Ed25519Signature) -> Self {
212        (&value).into()
213    }
214}
215
216impl From<&Ed25519Signature> for Signature {
217    #[inline]
218    fn from(value: &Ed25519Signature) -> Self {
219        Self::from_bytes(&value.0)
220    }
221}
222
223#[cfg(feature = "fmt")]
224const _: () = {
225    use core::{
226        fmt::{self, Display},
227        str::FromStr,
228    };
229
230    use crate::fmt::{ParseCurveError, TypedCurve};
231
232    impl TypedCurve for Ed25519 {
233        const CURVE_TYPE: &str = "ed25519";
234    }
235
236    impl Display for Ed25519PublicKey {
237        #[inline]
238        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239            f.write_str(&Ed25519::to_base58(self.0))
240        }
241    }
242
243    impl FromStr for Ed25519PublicKey {
244        type Err = ParseCurveError;
245
246        #[inline]
247        fn from_str(s: &str) -> Result<Self, Self::Err> {
248            Ed25519::parse_base58(s).map(Self)
249        }
250    }
251
252    impl Display for Ed25519Signature {
253        #[inline]
254        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255            f.write_str(&Ed25519::to_base58(self.0))
256        }
257    }
258
259    impl FromStr for Ed25519Signature {
260        type Err = ParseCurveError;
261
262        #[inline]
263        fn from_str(s: &str) -> Result<Self, Self::Err> {
264            Ed25519::parse_base58(s).map(Self)
265        }
266    }
267};
268
269#[cfg(test)]
270mod tests {
271    use ed25519_dalek::SigningKey;
272    use hex_literal::hex;
273    use rand::{rand_core::UnwrapErr, rngs::SysRng};
274    use rstest::rstest;
275
276    use crate::tests::test_sign_verify;
277
278    use super::*;
279
280    #[rstest]
281    #[case(
282        hex!("8565df94b8caab08f28cdd2ee014b800915741d4694fa840e50cca02ae5c6466"),
283        hex!("060fab6e0fa2ea8913ef80f6f4c6fd8c0c24c2ac044d73b837d887b1e6f378fa"),
284        hex!("e4822e15e5988bf08c80b72f2d1292b7229029f342d42bb9dfe4e230c66c10a6c4a86a47ddc58b1446baedf2f1312294d59638c812082a0124e513d4eb16c40e"),
285    )]
286    #[case(
287        hex!("5231b8bba197e888c447ff6617d33dbb7fa571cdbbfb93f0b845c2293c86a3f0"),
288        hex!("15401eb21a14a1f9b21277cd65e4e985e094a465c2939c13b39a56b4043a2cdc"),
289        hex!("024068f38742fa99be08b9779745562ba10ce336de5865497fc5442353e355d90c1eb986d04fb70d1031b0a7f7cfe80946e0cd3979316b522fbdb8ed35028f0f"),
290    )]
291    fn verify_ok(
292        #[case] public_key: impl Into<Ed25519PublicKey>,
293        #[case] msg: impl AsRef<[u8]>,
294        #[case] signature: impl Into<Ed25519Signature>,
295    ) {
296        assert!(
297            Ed25519::verify(
298                &public_key.into().try_into().unwrap(),
299                msg.as_ref(),
300                &signature.into().into(),
301            ),
302            "signature is invalid",
303        );
304    }
305
306    #[rstest]
307    #[case(
308        hex!("8565df94b8caab08f28cdd2ee014b800915741d4694fa840e50cca02ae5c6466"),
309        hex!("94fde20581344b29a34224eadd55ceff65afc94148f550255d36e1de9ec064d0"),
310        hex!("e4822e15e5988bf08c80b72f2d1292b7229029f342d42bb9dfe4e230c66c10a6c4a86a47ddc58b1446baedf2f1312294d59638c812082a0124e513d4eb16c40e"),
311    )]
312    #[case(
313        hex!("5231b8bba197e888c447ff6617d33dbb7fa571cdbbfb93f0b845c2293c86a3f0"),
314        hex!("060fab6e0fa2ea8913ef80f6f4c6fd8c0c24c2ac044d73b837d887b1e6f378fa"),
315        hex!("e4822e15e5988bf08c80b72f2d1292b7229029f342d42bb9dfe4e230c66c10a6c4a86a47ddc58b1446baedf2f1312294d59638c812082a0124e513d4eb16c40e"),
316    )]
317    #[case(
318        hex!("8565df94b8caab08f28cdd2ee014b800915741d4694fa840e50cca02ae5c6466"),
319        hex!("060fab6e0fa2ea8913ef80f6f4c6fd8c0c24c2ac044d73b837d887b1e6f378fa"),
320        hex!("024068f38742fa99be08b9779745562ba10ce336de5865497fc5442353e355d90c1eb986d04fb70d1031b0a7f7cfe80946e0cd3979316b522fbdb8ed35028f0f"),
321    )]
322    fn verify_fail(
323        #[case] public_key: impl Into<Ed25519PublicKey>,
324        #[case] msg: impl AsRef<[u8]>,
325        #[case] signature: impl Into<Ed25519Signature>,
326    ) {
327        assert!(
328            !Ed25519::verify(
329                &public_key.into().try_into().unwrap(),
330                msg.as_ref(),
331                &signature.into().into(),
332            ),
333            "invalid signature passed verification",
334        );
335    }
336
337    #[rstest]
338    #[case("")]
339    #[case("test")]
340    #[case(
341        hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"),
342    )]
343    #[tokio::test]
344    async fn sign_verify(#[case] msg: impl AsRef<[u8]>) {
345        test_sign_verify(SigningKey::generate(&mut UnwrapErr(SysRng)), msg).await;
346    }
347}