Skip to main content

defuse_crypto/
ed25519.rs

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