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