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