1use crate::{
21 crypto::{
22 ByteArray, CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair,
23 PublicBytes, SecretStringError, SignatureBytes,
24 },
25 proof_of_possession::NonAggregatable,
26};
27
28use ed25519_zebra::{SigningKey, VerificationKey};
29
30use alloc::vec::Vec;
31
32pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ed25");
34
35pub const PUBLIC_KEY_SERIALIZED_SIZE: usize = 32;
37
38pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
40
41type Seed = [u8; 32];
45
46#[doc(hidden)]
47pub struct Ed25519Tag;
48
49pub type Public = PublicBytes<PUBLIC_KEY_SERIALIZED_SIZE, Ed25519Tag>;
51
52pub type Signature = SignatureBytes<SIGNATURE_SERIALIZED_SIZE, Ed25519Tag>;
54
55pub type ProofOfPossession = Signature;
57
58#[derive(Copy, Clone)]
60pub struct Pair {
61 public: VerificationKey,
62 secret: SigningKey,
63}
64
65fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed {
67 use codec::Encode;
68 ("Ed25519HDKD", secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256)
69}
70
71impl TraitPair for Pair {
72 type Public = Public;
73 type Seed = Seed;
74 type Signature = Signature;
75 type ProofOfPossession = ProofOfPossession;
76
77 fn from_seed_slice(seed_slice: &[u8]) -> Result<Pair, SecretStringError> {
82 let secret =
83 SigningKey::try_from(seed_slice).map_err(|_| SecretStringError::InvalidSeedLength)?;
84 let public = VerificationKey::from(&secret);
85 Ok(Pair { secret, public })
86 }
87
88 fn derive<Iter: Iterator<Item = DeriveJunction>>(
90 &self,
91 path: Iter,
92 _seed: Option<Seed>,
93 ) -> Result<(Pair, Option<Seed>), DeriveError> {
94 let mut acc = self.secret.into();
95 for j in path {
96 match j {
97 DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath),
98 DeriveJunction::Hard(cc) => acc = derive_hard_junction(&acc, &cc),
99 }
100 }
101 Ok((Self::from_seed(&acc), Some(acc)))
102 }
103
104 fn public(&self) -> Public {
106 Public::from_raw(self.public.into())
107 }
108
109 #[cfg(feature = "full_crypto")]
111 fn sign(&self, message: &[u8]) -> Signature {
112 Signature::from_raw(self.secret.sign(message).into())
113 }
114
115 fn verify<M: AsRef<[u8]>>(sig: &Signature, message: M, public: &Public) -> bool {
119 let Ok(public) = VerificationKey::try_from(public.as_slice()) else { return false };
120 let Ok(signature) = ed25519_zebra::Signature::try_from(sig.as_slice()) else {
121 return false
122 };
123 public.verify(&signature, message.as_ref()).is_ok()
124 }
125
126 fn to_raw_vec(&self) -> Vec<u8> {
128 self.seed().to_vec()
129 }
130}
131
132impl Pair {
133 pub fn seed(&self) -> Seed {
135 self.secret.into()
136 }
137
138 #[cfg(feature = "std")]
141 pub fn from_legacy_string(s: &str, password_override: Option<&str>) -> Pair {
142 Self::from_string(s, password_override).unwrap_or_else(|_| {
143 let mut padded_seed: Seed = [b' '; 32];
144 let len = s.len().min(32);
145 padded_seed[..len].copy_from_slice(&s.as_bytes()[..len]);
146 Self::from_seed(&padded_seed)
147 })
148 }
149}
150
151impl CryptoType for Public {
152 type Pair = Pair;
153}
154
155impl CryptoType for Signature {
156 type Pair = Pair;
157}
158
159impl CryptoType for Pair {
160 type Pair = Pair;
161}
162
163impl NonAggregatable for Pair {}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 #[cfg(feature = "serde")]
169 use crate::crypto::Ss58Codec;
170 use crate::{
171 crypto::DEV_PHRASE,
172 proof_of_possession::{ProofOfPossessionGenerator, ProofOfPossessionVerifier},
173 };
174 use serde_json;
175
176 #[test]
177 fn default_phrase_should_be_used() {
178 assert_eq!(
179 Pair::from_string("//Alice///password", None).unwrap().public(),
180 Pair::from_string(&format!("{}//Alice", DEV_PHRASE), Some("password"))
181 .unwrap()
182 .public(),
183 );
184 }
185
186 #[test]
187 fn seed_and_derive_should_work() {
188 let seed = array_bytes::hex2array_unchecked(
189 "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
190 );
191 let pair = Pair::from_seed(&seed);
192 assert_eq!(pair.seed(), seed);
193 let path = vec![DeriveJunction::Hard([0u8; 32])];
194 let derived = pair.derive(path.into_iter(), None).ok().unwrap().0;
195 assert_eq!(
196 derived.seed(),
197 array_bytes::hex2array_unchecked::<_, 32>(
198 "ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c"
199 )
200 );
201 }
202
203 #[test]
204 fn generate_with_phrase_should_be_recoverable_with_from_string() {
205 let (pair, phrase, seed) = Pair::generate_with_phrase(None);
206 let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid");
207 assert_eq!(pair.public(), repair_seed.public());
208 assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
209 let (repair_phrase, reseed) =
210 Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid");
211 assert_eq!(seed, reseed);
212 assert_eq!(pair.public(), repair_phrase.public());
213 assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
214 let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid");
215 assert_eq!(pair.public(), repair_string.public());
216 assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
217 }
218
219 #[test]
220 fn test_vector_should_work() {
221 let pair = Pair::from_seed(&array_bytes::hex2array_unchecked(
222 "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
223 ));
224 let public = pair.public();
225 assert_eq!(
226 public,
227 Public::from_raw(array_bytes::hex2array_unchecked(
228 "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"
229 ))
230 );
231 let message = b"";
232 let signature = array_bytes::hex2array_unchecked("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
233 let signature = Signature::from_raw(signature);
234 assert!(pair.sign(&message[..]) == signature);
235 assert!(Pair::verify(&signature, &message[..], &public));
236 }
237
238 #[test]
239 fn test_vector_by_string_should_work() {
240 let pair = Pair::from_string(
241 "0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
242 None,
243 )
244 .unwrap();
245 let public = pair.public();
246 assert_eq!(
247 public,
248 Public::from_raw(array_bytes::hex2array_unchecked(
249 "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"
250 ))
251 );
252 let message = b"";
253 let signature = array_bytes::hex2array_unchecked("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
254 let signature = Signature::from_raw(signature);
255 assert!(pair.sign(&message[..]) == signature);
256 assert!(Pair::verify(&signature, &message[..], &public));
257 }
258
259 #[test]
260 fn generated_pair_should_work() {
261 let (pair, _) = Pair::generate();
262 let public = pair.public();
263 let message = b"Something important";
264 let signature = pair.sign(&message[..]);
265 assert!(Pair::verify(&signature, &message[..], &public));
266 assert!(!Pair::verify(&signature, b"Something else", &public));
267 }
268
269 #[test]
270 fn seeded_pair_should_work() {
271 let pair = Pair::from_seed(b"12345678901234567890123456789012");
272 let public = pair.public();
273 assert_eq!(
274 public,
275 Public::from_raw(array_bytes::hex2array_unchecked(
276 "2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee"
277 ))
278 );
279 let message = array_bytes::hex2bytes_unchecked("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000");
280 let signature = pair.sign(&message[..]);
281 println!("Correct signature: {:?}", signature);
282 assert!(Pair::verify(&signature, &message[..], &public));
283 assert!(!Pair::verify(&signature, "Other message", &public));
284 }
285
286 #[test]
287 fn generate_with_phrase_recovery_possible() {
288 let (pair1, phrase, _) = Pair::generate_with_phrase(None);
289 let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
290
291 assert_eq!(pair1.public(), pair2.public());
292 }
293
294 #[test]
295 fn generate_with_password_phrase_recovery_possible() {
296 let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password"));
297 let (pair2, _) = Pair::from_phrase(&phrase, Some("password")).unwrap();
298
299 assert_eq!(pair1.public(), pair2.public());
300 }
301
302 #[test]
303 fn password_does_something() {
304 let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password"));
305 let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
306
307 assert_ne!(pair1.public(), pair2.public());
308 assert_ne!(pair1.to_raw_vec(), pair2.to_raw_vec());
309 }
310
311 #[test]
312 fn ss58check_roundtrip_works() {
313 let pair = Pair::from_seed(b"12345678901234567890123456789012");
314 let public = pair.public();
315 let s = public.to_ss58check();
316 println!("Correct: {}", s);
317 let cmp = Public::from_ss58check(&s).unwrap();
318 assert_eq!(cmp, public);
319 }
320
321 #[test]
322 fn signature_serialization_works() {
323 let pair = Pair::from_seed(b"12345678901234567890123456789012");
324 let message = b"Something important";
325 let signature = pair.sign(&message[..]);
326 let serialized_signature = serde_json::to_string(&signature).unwrap();
327 assert_eq!(serialized_signature.len(), 130);
329 let signature = serde_json::from_str(&serialized_signature).unwrap();
330 assert!(Pair::verify(&signature, &message[..], &pair.public()));
331 }
332
333 #[test]
334 fn signature_serialization_doesnt_panic() {
335 fn deserialize_signature(text: &str) -> Result<Signature, serde_json::error::Error> {
336 serde_json::from_str(text)
337 }
338 assert!(deserialize_signature("Not valid json.").is_err());
339 assert!(deserialize_signature("\"Not an actual signature.\"").is_err());
340 assert!(deserialize_signature("\"abc123\"").is_err());
342 }
343
344 #[test]
345 fn good_proof_of_possession_should_work_bad_proof_of_possession_should_fail() {
346 let owner = b"owner";
347 let not_owner = b"not owner";
348
349 let mut pair = Pair::from_seed(b"12345678901234567890123456789012");
350 let other_pair = Pair::from_seed(b"23456789012345678901234567890123");
351 let proof_of_possession = pair.generate_proof_of_possession(owner);
352 assert!(Pair::verify_proof_of_possession(owner, &proof_of_possession, &pair.public()));
353 assert_eq!(
354 Pair::verify_proof_of_possession(owner, &proof_of_possession, &other_pair.public()),
355 false
356 );
357 assert!(!Pair::verify_proof_of_possession(not_owner, &proof_of_possession, &pair.public()));
358 }
359}