1pub use ed25519_dalek;
4use ed25519_dalek::{Signature, VerifyingKey};
5
6use crate::Curve;
7
8pub struct Ed25519;
10
11impl Curve for Ed25519 {
12 type PublicKey = VerifyingKey;
13
14 type Signature = Signature;
15
16 #[inline]
19 fn verify(public_key: &Self::PublicKey, msg: &[u8], signature: &Self::Signature) -> bool {
20 if public_key.is_weak() {
21 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#[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 #[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#[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 #[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}