1use parking_lot::RwLock;
10use std::{
11 collections::HashMap,
12 fs::{self, File},
13 io::Write,
14 path::PathBuf,
15 sync::Arc,
16};
17use subsoil::application_crypto::{AppCrypto, AppPair, IsWrappedBy};
18use subsoil::core::{
19 crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret},
20 ecdsa, ed25519, sr25519,
21};
22use subsoil::keystore::{Error as TraitError, Keystore, KeystorePtr};
23
24subsoil::keystore::bandersnatch_experimental_enabled! {
25use subsoil::core::bandersnatch;
26}
27
28subsoil::keystore::bls_experimental_enabled! {
29use subsoil::core::{bls381, ecdsa_bls381, KeccakHasher, proof_of_possession::ProofOfPossessionGenerator};
30}
31
32use super::{Error, Result};
33
34pub struct LocalKeystore(RwLock<KeystoreInner>);
36
37impl LocalKeystore {
38 pub fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<Self> {
47 let inner = KeystoreInner::open(path, password)?;
48 Ok(Self(RwLock::new(inner)))
49 }
50
51 pub fn in_memory() -> Self {
53 let inner = KeystoreInner::new_in_memory();
54 Self(RwLock::new(inner))
55 }
56
57 pub fn key_pair<Pair: AppPair>(
62 &self,
63 public: &<Pair as AppCrypto>::Public,
64 ) -> Result<Option<Pair>> {
65 self.0.read().key_pair::<Pair>(public)
66 }
67
68 fn public_keys<T: CorePair>(&self, key_type: KeyTypeId) -> Vec<T::Public> {
69 self.0
70 .read()
71 .raw_public_keys(key_type)
72 .map(|v| {
73 v.into_iter().filter_map(|k| T::Public::from_slice(k.as_slice()).ok()).collect()
74 })
75 .unwrap_or_default()
76 }
77
78 fn generate_new<T: CorePair>(
79 &self,
80 key_type: KeyTypeId,
81 seed: Option<&str>,
82 ) -> std::result::Result<T::Public, TraitError> {
83 let pair = match seed {
84 Some(seed) => self.0.write().insert_ephemeral_from_seed_by_type::<T>(seed, key_type),
85 None => self.0.write().generate_by_type::<T>(key_type),
86 }
87 .map_err(|e| -> TraitError { e.into() })?;
88 Ok(pair.public())
89 }
90
91 fn sign<T: CorePair>(
92 &self,
93 key_type: KeyTypeId,
94 public: &T::Public,
95 msg: &[u8],
96 ) -> std::result::Result<Option<T::Signature>, TraitError> {
97 let signature = self
98 .0
99 .read()
100 .key_pair_by_type::<T>(public, key_type)?
101 .map(|pair| pair.sign(msg));
102 Ok(signature)
103 }
104
105 fn vrf_sign<T: CorePair + VrfSecret>(
106 &self,
107 key_type: KeyTypeId,
108 public: &T::Public,
109 data: &T::VrfSignData,
110 ) -> std::result::Result<Option<T::VrfSignature>, TraitError> {
111 let sig = self
112 .0
113 .read()
114 .key_pair_by_type::<T>(public, key_type)?
115 .map(|pair| pair.vrf_sign(data));
116 Ok(sig)
117 }
118
119 fn vrf_pre_output<T: CorePair + VrfSecret>(
120 &self,
121 key_type: KeyTypeId,
122 public: &T::Public,
123 input: &T::VrfInput,
124 ) -> std::result::Result<Option<T::VrfPreOutput>, TraitError> {
125 let pre_output = self
126 .0
127 .read()
128 .key_pair_by_type::<T>(public, key_type)?
129 .map(|pair| pair.vrf_pre_output(input));
130 Ok(pre_output)
131 }
132
133 subsoil::keystore::bls_experimental_enabled! {
134 fn generate_proof_of_possession<T: CorePair + ProofOfPossessionGenerator>(
135 &self,
136 key_type: KeyTypeId,
137 public: &T::Public,
138 owner: &[u8],
139 ) -> std::result::Result<Option<T::ProofOfPossession>, TraitError> {
140 let proof_of_possession = self
141 .0
142 .read()
143 .key_pair_by_type::<T>(public, key_type)?
144 .map(|mut pair| pair.generate_proof_of_possession(owner));
145 Ok(proof_of_possession)
146 }
147 }
148}
149
150impl Keystore for LocalKeystore {
151 fn insert(
159 &self,
160 key_type: KeyTypeId,
161 suri: &str,
162 public: &[u8],
163 ) -> std::result::Result<(), ()> {
164 self.0.write().insert(key_type, suri, public).map_err(|_| ())
165 }
166
167 fn keys(&self, key_type: KeyTypeId) -> std::result::Result<Vec<Vec<u8>>, TraitError> {
168 self.0.read().raw_public_keys(key_type).map_err(|e| e.into())
169 }
170
171 fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
172 public_keys
173 .iter()
174 .all(|(p, t)| self.0.read().key_phrase_by_type(p, *t).ok().flatten().is_some())
175 }
176
177 fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec<sr25519::Public> {
178 self.public_keys::<sr25519::Pair>(key_type)
179 }
180
181 fn sr25519_generate_new(
185 &self,
186 key_type: KeyTypeId,
187 seed: Option<&str>,
188 ) -> std::result::Result<sr25519::Public, TraitError> {
189 self.generate_new::<sr25519::Pair>(key_type, seed)
190 }
191
192 fn sr25519_sign(
193 &self,
194 key_type: KeyTypeId,
195 public: &sr25519::Public,
196 msg: &[u8],
197 ) -> std::result::Result<Option<sr25519::Signature>, TraitError> {
198 self.sign::<sr25519::Pair>(key_type, public, msg)
199 }
200
201 fn sr25519_vrf_sign(
202 &self,
203 key_type: KeyTypeId,
204 public: &sr25519::Public,
205 data: &sr25519::vrf::VrfSignData,
206 ) -> std::result::Result<Option<sr25519::vrf::VrfSignature>, TraitError> {
207 self.vrf_sign::<sr25519::Pair>(key_type, public, data)
208 }
209
210 fn sr25519_vrf_pre_output(
211 &self,
212 key_type: KeyTypeId,
213 public: &sr25519::Public,
214 input: &sr25519::vrf::VrfInput,
215 ) -> std::result::Result<Option<sr25519::vrf::VrfPreOutput>, TraitError> {
216 self.vrf_pre_output::<sr25519::Pair>(key_type, public, input)
217 }
218
219 fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public> {
220 self.public_keys::<ed25519::Pair>(key_type)
221 }
222
223 fn ed25519_generate_new(
227 &self,
228 key_type: KeyTypeId,
229 seed: Option<&str>,
230 ) -> std::result::Result<ed25519::Public, TraitError> {
231 self.generate_new::<ed25519::Pair>(key_type, seed)
232 }
233
234 fn ed25519_sign(
235 &self,
236 key_type: KeyTypeId,
237 public: &ed25519::Public,
238 msg: &[u8],
239 ) -> std::result::Result<Option<ed25519::Signature>, TraitError> {
240 self.sign::<ed25519::Pair>(key_type, public, msg)
241 }
242
243 fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa::Public> {
244 self.public_keys::<ecdsa::Pair>(key_type)
245 }
246
247 fn ecdsa_generate_new(
251 &self,
252 key_type: KeyTypeId,
253 seed: Option<&str>,
254 ) -> std::result::Result<ecdsa::Public, TraitError> {
255 self.generate_new::<ecdsa::Pair>(key_type, seed)
256 }
257
258 fn ecdsa_sign(
259 &self,
260 key_type: KeyTypeId,
261 public: &ecdsa::Public,
262 msg: &[u8],
263 ) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
264 self.sign::<ecdsa::Pair>(key_type, public, msg)
265 }
266
267 fn ecdsa_sign_prehashed(
268 &self,
269 key_type: KeyTypeId,
270 public: &ecdsa::Public,
271 msg: &[u8; 32],
272 ) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
273 let sig = self
274 .0
275 .read()
276 .key_pair_by_type::<ecdsa::Pair>(public, key_type)?
277 .map(|pair| pair.sign_prehashed(msg));
278 Ok(sig)
279 }
280
281 subsoil::keystore::bandersnatch_experimental_enabled! {
282 fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec<bandersnatch::Public> {
283 self.public_keys::<bandersnatch::Pair>(key_type)
284 }
285
286 fn bandersnatch_generate_new(
290 &self,
291 key_type: KeyTypeId,
292 seed: Option<&str>,
293 ) -> std::result::Result<bandersnatch::Public, TraitError> {
294 self.generate_new::<bandersnatch::Pair>(key_type, seed)
295 }
296
297 fn bandersnatch_sign(
298 &self,
299 key_type: KeyTypeId,
300 public: &bandersnatch::Public,
301 msg: &[u8],
302 ) -> std::result::Result<Option<bandersnatch::Signature>, TraitError> {
303 self.sign::<bandersnatch::Pair>(key_type, public, msg)
304 }
305
306 fn bandersnatch_vrf_sign(
307 &self,
308 key_type: KeyTypeId,
309 public: &bandersnatch::Public,
310 data: &bandersnatch::vrf::VrfSignData,
311 ) -> std::result::Result<Option<bandersnatch::vrf::VrfSignature>, TraitError> {
312 self.vrf_sign::<bandersnatch::Pair>(key_type, public, data)
313 }
314
315 fn bandersnatch_vrf_pre_output(
316 &self,
317 key_type: KeyTypeId,
318 public: &bandersnatch::Public,
319 input: &bandersnatch::vrf::VrfInput,
320 ) -> std::result::Result<Option<bandersnatch::vrf::VrfPreOutput>, TraitError> {
321 self.vrf_pre_output::<bandersnatch::Pair>(key_type, public, input)
322 }
323
324 fn bandersnatch_ring_vrf_sign(
325 &self,
326 key_type: KeyTypeId,
327 public: &bandersnatch::Public,
328 data: &bandersnatch::vrf::VrfSignData,
329 prover: &bandersnatch::ring_vrf::RingProver,
330 ) -> std::result::Result<Option<bandersnatch::ring_vrf::RingVrfSignature>, TraitError> {
331 let sig = self
332 .0
333 .read()
334 .key_pair_by_type::<bandersnatch::Pair>(public, key_type)?
335 .map(|pair| pair.ring_vrf_sign(data, prover));
336 Ok(sig)
337 }
338 }
339
340 subsoil::keystore::bls_experimental_enabled! {
341 fn bls381_public_keys(&self, key_type: KeyTypeId) -> Vec<bls381::Public> {
342 self.public_keys::<bls381::Pair>(key_type)
343 }
344
345 fn bls381_generate_new(
349 &self,
350 key_type: KeyTypeId,
351 seed: Option<&str>,
352 ) -> std::result::Result<bls381::Public, TraitError> {
353 self.generate_new::<bls381::Pair>(key_type, seed)
354 }
355
356 fn bls381_sign(
357 &self,
358 key_type: KeyTypeId,
359 public: &bls381::Public,
360 msg: &[u8],
361 ) -> std::result::Result<Option<bls381::Signature>, TraitError> {
362 self.sign::<bls381::Pair>(key_type, public, msg)
363 }
364
365 fn bls381_generate_proof_of_possession(
366 &self,
367 key_type: KeyTypeId,
368 public: &bls381::Public,
369 owner: &[u8],
370 ) -> std::result::Result<Option<bls381::ProofOfPossession>, TraitError> {
371 self.generate_proof_of_possession::<bls381::Pair>(key_type, public, owner)
372 }
373
374 fn ecdsa_bls381_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa_bls381::Public> {
375 self.public_keys::<ecdsa_bls381::Pair>(key_type)
376 }
377
378 fn ecdsa_bls381_generate_new(
382 &self,
383 key_type: KeyTypeId,
384 seed: Option<&str>,
385 ) -> std::result::Result<ecdsa_bls381::Public, TraitError> {
386 let pubkey = self.generate_new::<ecdsa_bls381::Pair>(key_type, seed)?;
387
388 let s = self
389 .0
390 .read()
391 .additional
392 .get(&(key_type, pubkey.to_vec()))
393 .map(|s| s.to_string())
394 .expect("Can retrieve seed");
395
396 self.generate_new::<ecdsa::Pair>(key_type, Some(&*s)).expect("seed slice is valid");
400 self.generate_new::<bls381::Pair>(key_type, Some(&*s)).expect("seed slice is valid");
401
402 Ok(pubkey)
403 }
404
405 fn ecdsa_bls381_sign(
406 &self,
407 key_type: KeyTypeId,
408 public: &ecdsa_bls381::Public,
409 msg: &[u8],
410 ) -> std::result::Result<Option<ecdsa_bls381::Signature>, TraitError> {
411 self.sign::<ecdsa_bls381::Pair>(key_type, public, msg)
412 }
413
414 fn ecdsa_bls381_sign_with_keccak256(
415 &self,
416 key_type: KeyTypeId,
417 public: &ecdsa_bls381::Public,
418 msg: &[u8],
419 ) -> std::result::Result<Option<ecdsa_bls381::Signature>, TraitError> {
420 let sig = self.0
421 .read()
422 .key_pair_by_type::<ecdsa_bls381::Pair>(public, key_type)?
423 .map(|pair| pair.sign_with_hasher::<KeccakHasher>(msg));
424 Ok(sig)
425 }
426 }
427}
428
429impl Into<KeystorePtr> for LocalKeystore {
430 fn into(self) -> KeystorePtr {
431 Arc::new(self)
432 }
433}
434
435struct KeystoreInner {
441 path: Option<PathBuf>,
442 additional: HashMap<(KeyTypeId, Vec<u8>), String>,
444 password: Option<SecretString>,
445}
446
447impl KeystoreInner {
448 fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<Self> {
452 let path = path.into();
453 fs::create_dir_all(&path)?;
454
455 Ok(Self { path: Some(path), additional: HashMap::new(), password })
456 }
457
458 fn password(&self) -> Option<&str> {
460 self.password.as_ref().map(|p| p.expose_secret()).map(|p| p.as_str())
461 }
462
463 fn new_in_memory() -> Self {
465 Self { path: None, additional: HashMap::new(), password: None }
466 }
467
468 fn get_additional_pair(&self, public: &[u8], key_type: KeyTypeId) -> Option<&String> {
470 let key = (key_type, public.to_vec());
471 self.additional.get(&key)
472 }
473
474 fn insert_ephemeral_pair<Pair: CorePair>(
478 &mut self,
479 pair: &Pair,
480 seed: &str,
481 key_type: KeyTypeId,
482 ) {
483 let key = (key_type, pair.public().to_raw_vec());
484 self.additional.insert(key, seed.into());
485 }
486
487 fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<()> {
491 if let Some(path) = self.key_file_path(public, key_type) {
492 Self::write_to_file(path, suri)?;
493 }
494
495 Ok(())
496 }
497
498 fn generate_by_type<Pair: CorePair>(&mut self, key_type: KeyTypeId) -> Result<Pair> {
503 let (pair, phrase, _) = Pair::generate_with_phrase(self.password());
504 if let Some(path) = self.key_file_path(pair.public().as_slice(), key_type) {
505 Self::write_to_file(path, &phrase)?;
506 } else {
507 self.insert_ephemeral_pair(&pair, &phrase, key_type);
508 }
509
510 Ok(pair)
511 }
512
513 fn write_to_file(file: PathBuf, data: &str) -> Result<()> {
515 let mut file = File::create(file)?;
516
517 #[cfg(target_family = "unix")]
518 {
519 use std::os::unix::fs::PermissionsExt;
520 file.set_permissions(fs::Permissions::from_mode(0o600))?;
521 }
522
523 serde_json::to_writer(&file, data)?;
524 file.flush()?;
525 Ok(())
526 }
527
528 fn insert_ephemeral_from_seed_by_type<Pair: CorePair>(
532 &mut self,
533 seed: &str,
534 key_type: KeyTypeId,
535 ) -> Result<Pair> {
536 let pair = Pair::from_string(seed, None).map_err(|_| Error::InvalidSeed)?;
537 self.insert_ephemeral_pair(&pair, seed, key_type);
538 Ok(pair)
539 }
540
541 fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result<Option<String>> {
543 if let Some(phrase) = self.get_additional_pair(public, key_type) {
544 return Ok(Some(phrase.clone()));
545 }
546
547 let path = if let Some(path) = self.key_file_path(public, key_type) {
548 path
549 } else {
550 return Ok(None);
551 };
552
553 if path.exists() {
554 let file = File::open(path)?;
555
556 serde_json::from_reader(&file).map_err(Into::into).map(Some)
557 } else {
558 Ok(None)
559 }
560 }
561
562 fn key_pair_by_type<Pair: CorePair>(
564 &self,
565 public: &Pair::Public,
566 key_type: KeyTypeId,
567 ) -> Result<Option<Pair>> {
568 let phrase = if let Some(p) = self.key_phrase_by_type(public.as_slice(), key_type)? {
569 p
570 } else {
571 return Ok(None);
572 };
573
574 let pair = Pair::from_string(&phrase, self.password()).map_err(|_| Error::InvalidPhrase)?;
575
576 if &pair.public() == public {
577 Ok(Some(pair))
578 } else {
579 Err(Error::PublicKeyMismatch)
580 }
581 }
582
583 fn key_file_path(&self, public: &[u8], key_type: KeyTypeId) -> Option<PathBuf> {
587 let mut buf = self.path.as_ref()?.clone();
588 let key_type = array_bytes::bytes2hex("", &key_type.0);
589 let key = array_bytes::bytes2hex("", public);
590 buf.push(key_type + key.as_str());
591 Some(buf)
592 }
593
594 fn raw_public_keys(&self, key_type: KeyTypeId) -> Result<Vec<Vec<u8>>> {
596 let mut public_keys: Vec<Vec<u8>> = self
597 .additional
598 .keys()
599 .into_iter()
600 .filter_map(|k| if k.0 == key_type { Some(k.1.clone()) } else { None })
601 .collect();
602
603 if let Some(path) = &self.path {
604 for entry in fs::read_dir(&path)? {
605 let entry = entry?;
606 let path = entry.path();
607
608 if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
610 match array_bytes::hex2bytes(name) {
611 Ok(ref hex) if hex.len() > 4 => {
612 if hex[0..4] != key_type.0 {
613 continue;
614 }
615 let public = hex[4..].to_vec();
616 public_keys.push(public);
617 },
618 _ => continue,
619 }
620 }
621 }
622 }
623
624 Ok(public_keys)
625 }
626
627 pub fn key_pair<Pair: AppPair>(
632 &self,
633 public: &<Pair as AppCrypto>::Public,
634 ) -> Result<Option<Pair>> {
635 self.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID)
636 .map(|v| v.map(Into::into))
637 }
638}
639
640#[cfg(test)]
641mod tests {
642 use super::*;
643 use std::{fs, str::FromStr};
644 use subsoil::application_crypto::{ed25519, sr25519, AppPublic};
645 use subsoil::core::{crypto::Ss58Codec, testing::SR25519, Pair};
646 use tempfile::TempDir;
647
648 const TEST_KEY_TYPE: KeyTypeId = KeyTypeId(*b"test");
649
650 impl KeystoreInner {
651 fn insert_ephemeral_from_seed<Pair: AppPair>(&mut self, seed: &str) -> Result<Pair> {
652 self.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID)
653 .map(Into::into)
654 }
655
656 fn public_keys<Public: AppPublic>(&self) -> Result<Vec<Public>> {
657 self.raw_public_keys(Public::ID).map(|v| {
658 v.into_iter().filter_map(|k| Public::from_slice(k.as_slice()).ok()).collect()
659 })
660 }
661
662 fn generate<Pair: AppPair>(&mut self) -> Result<Pair> {
663 self.generate_by_type::<Pair::Generic>(Pair::ID).map(Into::into)
664 }
665 }
666
667 #[test]
668 fn basic_store() {
669 let temp_dir = TempDir::new().unwrap();
670 let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
671
672 assert!(store.public_keys::<ed25519::AppPublic>().unwrap().is_empty());
673
674 let key: ed25519::AppPair = store.generate().unwrap();
675 let key2: ed25519::AppPair = store.key_pair(&key.public()).unwrap().unwrap();
676
677 assert_eq!(key.public(), key2.public());
678
679 assert_eq!(store.public_keys::<ed25519::AppPublic>().unwrap()[0], key.public());
680 }
681
682 #[test]
683 fn has_keys_works() {
684 let temp_dir = TempDir::new().unwrap();
685 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
686
687 let key: ed25519::AppPair = store.0.write().generate().unwrap();
688 let key2 = ed25519::Pair::generate().0;
689
690 assert!(!store.has_keys(&[(key2.public().to_vec(), ed25519::AppPublic::ID)]));
691
692 assert!(!store.has_keys(&[
693 (key2.public().to_vec(), ed25519::AppPublic::ID),
694 (key.public().to_raw_vec(), ed25519::AppPublic::ID),
695 ],));
696
697 assert!(store.has_keys(&[(key.public().to_raw_vec(), ed25519::AppPublic::ID)]));
698 }
699
700 #[test]
701 fn test_insert_ephemeral_from_seed() {
702 let temp_dir = TempDir::new().unwrap();
703 let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
704
705 let pair: ed25519::AppPair = store
706 .insert_ephemeral_from_seed(
707 "0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc",
708 )
709 .unwrap();
710 assert_eq!(
711 "5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HJCA",
712 pair.public().to_ss58check()
713 );
714
715 drop(store);
716 let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
717 assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().is_none());
719 }
720
721 #[test]
722 fn password_being_used() {
723 let password = String::from("password");
724 let temp_dir = TempDir::new().unwrap();
725 let mut store = KeystoreInner::open(
726 temp_dir.path(),
727 Some(FromStr::from_str(password.as_str()).unwrap()),
728 )
729 .unwrap();
730
731 let pair: ed25519::AppPair = store.generate().unwrap();
732 assert_eq!(
733 pair.public(),
734 store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(),
735 );
736
737 let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
739 assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).is_err());
740
741 let store = KeystoreInner::open(
742 temp_dir.path(),
743 Some(FromStr::from_str(password.as_str()).unwrap()),
744 )
745 .unwrap();
746 assert_eq!(
747 pair.public(),
748 store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(),
749 );
750 }
751
752 #[test]
753 fn public_keys_are_returned() {
754 let temp_dir = TempDir::new().unwrap();
755 let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
756
757 let mut keys = Vec::new();
758 for i in 0..10 {
759 keys.push(store.generate::<ed25519::AppPair>().unwrap().public());
760 keys.push(
761 store
762 .insert_ephemeral_from_seed::<ed25519::AppPair>(&format!(
763 "0x3d97c819d68f9bafa7d6e79cb991eebcd7{}d966c5334c0b94d9e1fa7ad0869dc",
764 i
765 ))
766 .unwrap()
767 .public(),
768 );
769 }
770
771 store.generate::<sr25519::AppPair>().unwrap();
773
774 keys.sort();
775 let mut store_pubs = store.public_keys::<ed25519::AppPublic>().unwrap();
776 store_pubs.sort();
777
778 assert_eq!(keys, store_pubs);
779 }
780
781 #[test]
782 fn store_unknown_and_extract_it() {
783 let temp_dir = TempDir::new().unwrap();
784 let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
785
786 let secret_uri = "//Alice";
787 let key_pair = sr25519::AppPair::from_string(secret_uri, None).expect("Generates key pair");
788
789 store
790 .insert(SR25519, secret_uri, key_pair.public().as_ref())
791 .expect("Inserts unknown key");
792
793 let store_key_pair = store
794 .key_pair_by_type::<sr25519::AppPair>(&key_pair.public(), SR25519)
795 .expect("Gets key pair from keystore")
796 .unwrap();
797
798 assert_eq!(key_pair.public(), store_key_pair.public());
799 }
800
801 #[test]
802 fn store_ignores_files_with_invalid_name() {
803 let temp_dir = TempDir::new().unwrap();
804 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
805
806 let file_name = temp_dir.path().join(array_bytes::bytes2hex("", &SR25519.0[..2]));
807 fs::write(file_name, "test").expect("Invalid file is written");
808
809 assert!(store.sr25519_public_keys(SR25519).is_empty());
810 }
811
812 #[test]
813 fn generate_with_seed_is_not_stored() {
814 let temp_dir = TempDir::new().unwrap();
815 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
816 let _alice_tmp_key = store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap();
817
818 assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1);
819
820 drop(store);
821 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
822 assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 0);
823 }
824
825 #[test]
826 fn generate_can_be_fetched_in_memory() {
827 let store = LocalKeystore::in_memory();
828 store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap();
829
830 assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1);
831 store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap();
832 assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 2);
833 }
834
835 #[test]
836 #[cfg(target_family = "unix")]
837 fn uses_correct_file_permissions_on_unix() {
838 use std::os::unix::fs::PermissionsExt;
839
840 let temp_dir = TempDir::new().unwrap();
841 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
842
843 let public = store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap();
844
845 let path = store.0.read().key_file_path(public.as_ref(), TEST_KEY_TYPE).unwrap();
846 let permissions = File::open(path).unwrap().metadata().unwrap().permissions();
847
848 assert_eq!(0o100600, permissions.mode());
849 }
850
851 #[test]
852 #[cfg(feature = "bls-experimental")]
853 fn ecdsa_bls381_generate_with_none_works() {
854 use subsoil::core::testing::ECDSA_BLS381;
855
856 let store = LocalKeystore::in_memory();
857 let ecdsa_bls381_key =
858 store.ecdsa_bls381_generate_new(ECDSA_BLS381, None).expect("Cant generate key");
859
860 let ecdsa_keys = store.ecdsa_public_keys(ECDSA_BLS381);
861 let bls381_keys = store.bls381_public_keys(ECDSA_BLS381);
862 let ecdsa_bls381_keys = store.ecdsa_bls381_public_keys(ECDSA_BLS381);
863
864 assert_eq!(ecdsa_keys.len(), 1);
865 assert_eq!(bls381_keys.len(), 1);
866 assert_eq!(ecdsa_bls381_keys.len(), 1);
867
868 let ecdsa_key = ecdsa_keys[0];
869 let bls381_key = bls381_keys[0];
870
871 let mut combined_key_raw = [0u8; ecdsa_bls381::PUBLIC_KEY_LEN];
872 combined_key_raw[..ecdsa::PUBLIC_KEY_SERIALIZED_SIZE].copy_from_slice(ecdsa_key.as_ref());
873 combined_key_raw[ecdsa::PUBLIC_KEY_SERIALIZED_SIZE..].copy_from_slice(bls381_key.as_ref());
874 let combined_key = ecdsa_bls381::Public::from_raw(combined_key_raw);
875
876 assert_eq!(combined_key, ecdsa_bls381_key);
877 }
878
879 #[test]
880 #[cfg(feature = "bls-experimental")]
881 fn ecdsa_bls381_generate_with_seed_works() {
882 use subsoil::core::testing::ECDSA_BLS381;
883
884 let store = LocalKeystore::in_memory();
885 let ecdsa_bls381_key = store
886 .ecdsa_bls381_generate_new(ECDSA_BLS381, Some("//Alice"))
887 .expect("Cant generate key");
888
889 let ecdsa_keys = store.ecdsa_public_keys(ECDSA_BLS381);
890 let bls381_keys = store.bls381_public_keys(ECDSA_BLS381);
891 let ecdsa_bls381_keys = store.ecdsa_bls381_public_keys(ECDSA_BLS381);
892
893 assert_eq!(ecdsa_keys.len(), 1);
894 assert_eq!(bls381_keys.len(), 1);
895 assert_eq!(ecdsa_bls381_keys.len(), 1);
896
897 let ecdsa_key = ecdsa_keys[0];
898 let bls381_key = bls381_keys[0];
899
900 let mut combined_key_raw = [0u8; ecdsa_bls381::PUBLIC_KEY_LEN];
901 combined_key_raw[..ecdsa::PUBLIC_KEY_SERIALIZED_SIZE].copy_from_slice(ecdsa_key.as_ref());
902 combined_key_raw[ecdsa::PUBLIC_KEY_SERIALIZED_SIZE..].copy_from_slice(bls381_key.as_ref());
903 let combined_key = ecdsa_bls381::Public::from_raw(combined_key_raw);
904
905 assert_eq!(combined_key, ecdsa_bls381_key);
906 }
907}