sc_keystore/
local.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17//
18//! Local keystore implementation
19
20use parking_lot::RwLock;
21use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy};
22use sp_core::{
23	crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret},
24	ecdsa, ed25519, sr25519,
25};
26use sp_keystore::{Error as TraitError, Keystore, KeystorePtr};
27use std::{
28	collections::HashMap,
29	fs::{self, File},
30	io::Write,
31	path::PathBuf,
32	sync::Arc,
33};
34
35sp_keystore::bandersnatch_experimental_enabled! {
36use sp_core::bandersnatch;
37}
38
39sp_keystore::bls_experimental_enabled! {
40use sp_core::{bls381, ecdsa_bls381, KeccakHasher};
41}
42
43use crate::{Error, Result};
44
45/// A local based keystore that is either memory-based or filesystem-based.
46pub struct LocalKeystore(RwLock<KeystoreInner>);
47
48impl LocalKeystore {
49	/// Create a local keystore from filesystem.
50	///
51	/// The keystore will be created at `path`. The keystore optionally supports to encrypt/decrypt
52	/// the keys in the keystore using `password`.
53	///
54	/// NOTE: Even when passing a `password`, the keys on disk appear to look like normal secret
55	/// uris. However, without having the correct password the secret uri will not generate the
56	/// correct private key. See [`SecretUri`](sp_core::crypto::SecretUri) for more information.
57	pub fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<Self> {
58		let inner = KeystoreInner::open(path, password)?;
59		Ok(Self(RwLock::new(inner)))
60	}
61
62	/// Create a local keystore in memory.
63	pub fn in_memory() -> Self {
64		let inner = KeystoreInner::new_in_memory();
65		Self(RwLock::new(inner))
66	}
67
68	/// Get a key pair for the given public key.
69	///
70	/// Returns `Ok(None)` if the key doesn't exist, `Ok(Some(_))` if the key exists and
71	/// `Err(_)` when something failed.
72	pub fn key_pair<Pair: AppPair>(
73		&self,
74		public: &<Pair as AppCrypto>::Public,
75	) -> Result<Option<Pair>> {
76		self.0.read().key_pair::<Pair>(public)
77	}
78
79	fn public_keys<T: CorePair>(&self, key_type: KeyTypeId) -> Vec<T::Public> {
80		self.0
81			.read()
82			.raw_public_keys(key_type)
83			.map(|v| {
84				v.into_iter().filter_map(|k| T::Public::from_slice(k.as_slice()).ok()).collect()
85			})
86			.unwrap_or_default()
87	}
88
89	fn generate_new<T: CorePair>(
90		&self,
91		key_type: KeyTypeId,
92		seed: Option<&str>,
93	) -> std::result::Result<T::Public, TraitError> {
94		let pair = match seed {
95			Some(seed) => self.0.write().insert_ephemeral_from_seed_by_type::<T>(seed, key_type),
96			None => self.0.write().generate_by_type::<T>(key_type),
97		}
98		.map_err(|e| -> TraitError { e.into() })?;
99		Ok(pair.public())
100	}
101
102	fn sign<T: CorePair>(
103		&self,
104		key_type: KeyTypeId,
105		public: &T::Public,
106		msg: &[u8],
107	) -> std::result::Result<Option<T::Signature>, TraitError> {
108		let signature = self
109			.0
110			.read()
111			.key_pair_by_type::<T>(public, key_type)?
112			.map(|pair| pair.sign(msg));
113		Ok(signature)
114	}
115
116	fn vrf_sign<T: CorePair + VrfSecret>(
117		&self,
118		key_type: KeyTypeId,
119		public: &T::Public,
120		data: &T::VrfSignData,
121	) -> std::result::Result<Option<T::VrfSignature>, TraitError> {
122		let sig = self
123			.0
124			.read()
125			.key_pair_by_type::<T>(public, key_type)?
126			.map(|pair| pair.vrf_sign(data));
127		Ok(sig)
128	}
129
130	fn vrf_pre_output<T: CorePair + VrfSecret>(
131		&self,
132		key_type: KeyTypeId,
133		public: &T::Public,
134		input: &T::VrfInput,
135	) -> std::result::Result<Option<T::VrfPreOutput>, TraitError> {
136		let pre_output = self
137			.0
138			.read()
139			.key_pair_by_type::<T>(public, key_type)?
140			.map(|pair| pair.vrf_pre_output(input));
141		Ok(pre_output)
142	}
143}
144
145impl Keystore for LocalKeystore {
146	/// Insert a new secret key.
147	///
148	/// WARNING: if the secret keypair has been manually generated using a password
149	/// (e.g. using methods such as [`sp_core::crypto::Pair::from_phrase`]) then such
150	/// a password must match the one used to open the keystore via [`LocalKeystore::open`].
151	/// If the passwords doesn't match then the inserted key ends up being unusable under
152	/// the current keystore instance.
153	fn insert(
154		&self,
155		key_type: KeyTypeId,
156		suri: &str,
157		public: &[u8],
158	) -> std::result::Result<(), ()> {
159		self.0.write().insert(key_type, suri, public).map_err(|_| ())
160	}
161
162	fn keys(&self, key_type: KeyTypeId) -> std::result::Result<Vec<Vec<u8>>, TraitError> {
163		self.0.read().raw_public_keys(key_type).map_err(|e| e.into())
164	}
165
166	fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
167		public_keys
168			.iter()
169			.all(|(p, t)| self.0.read().key_phrase_by_type(p, *t).ok().flatten().is_some())
170	}
171
172	fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec<sr25519::Public> {
173		self.public_keys::<sr25519::Pair>(key_type)
174	}
175
176	/// Generate a new pair compatible with the 'ed25519' signature scheme.
177	///
178	/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
179	fn sr25519_generate_new(
180		&self,
181		key_type: KeyTypeId,
182		seed: Option<&str>,
183	) -> std::result::Result<sr25519::Public, TraitError> {
184		self.generate_new::<sr25519::Pair>(key_type, seed)
185	}
186
187	fn sr25519_sign(
188		&self,
189		key_type: KeyTypeId,
190		public: &sr25519::Public,
191		msg: &[u8],
192	) -> std::result::Result<Option<sr25519::Signature>, TraitError> {
193		self.sign::<sr25519::Pair>(key_type, public, msg)
194	}
195
196	fn sr25519_vrf_sign(
197		&self,
198		key_type: KeyTypeId,
199		public: &sr25519::Public,
200		data: &sr25519::vrf::VrfSignData,
201	) -> std::result::Result<Option<sr25519::vrf::VrfSignature>, TraitError> {
202		self.vrf_sign::<sr25519::Pair>(key_type, public, data)
203	}
204
205	fn sr25519_vrf_pre_output(
206		&self,
207		key_type: KeyTypeId,
208		public: &sr25519::Public,
209		input: &sr25519::vrf::VrfInput,
210	) -> std::result::Result<Option<sr25519::vrf::VrfPreOutput>, TraitError> {
211		self.vrf_pre_output::<sr25519::Pair>(key_type, public, input)
212	}
213
214	fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public> {
215		self.public_keys::<ed25519::Pair>(key_type)
216	}
217
218	/// Generate a new pair compatible with the 'sr25519' signature scheme.
219	///
220	/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
221	fn ed25519_generate_new(
222		&self,
223		key_type: KeyTypeId,
224		seed: Option<&str>,
225	) -> std::result::Result<ed25519::Public, TraitError> {
226		self.generate_new::<ed25519::Pair>(key_type, seed)
227	}
228
229	fn ed25519_sign(
230		&self,
231		key_type: KeyTypeId,
232		public: &ed25519::Public,
233		msg: &[u8],
234	) -> std::result::Result<Option<ed25519::Signature>, TraitError> {
235		self.sign::<ed25519::Pair>(key_type, public, msg)
236	}
237
238	fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa::Public> {
239		self.public_keys::<ecdsa::Pair>(key_type)
240	}
241
242	/// Generate a new pair compatible with the 'ecdsa' signature scheme.
243	///
244	/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
245	fn ecdsa_generate_new(
246		&self,
247		key_type: KeyTypeId,
248		seed: Option<&str>,
249	) -> std::result::Result<ecdsa::Public, TraitError> {
250		self.generate_new::<ecdsa::Pair>(key_type, seed)
251	}
252
253	fn ecdsa_sign(
254		&self,
255		key_type: KeyTypeId,
256		public: &ecdsa::Public,
257		msg: &[u8],
258	) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
259		self.sign::<ecdsa::Pair>(key_type, public, msg)
260	}
261
262	fn ecdsa_sign_prehashed(
263		&self,
264		key_type: KeyTypeId,
265		public: &ecdsa::Public,
266		msg: &[u8; 32],
267	) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
268		let sig = self
269			.0
270			.read()
271			.key_pair_by_type::<ecdsa::Pair>(public, key_type)?
272			.map(|pair| pair.sign_prehashed(msg));
273		Ok(sig)
274	}
275
276	sp_keystore::bandersnatch_experimental_enabled! {
277		fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec<bandersnatch::Public> {
278			self.public_keys::<bandersnatch::Pair>(key_type)
279		}
280
281		/// Generate a new pair compatible with the 'bandersnatch' signature scheme.
282		///
283		/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
284		fn bandersnatch_generate_new(
285			&self,
286			key_type: KeyTypeId,
287			seed: Option<&str>,
288		) -> std::result::Result<bandersnatch::Public, TraitError> {
289			self.generate_new::<bandersnatch::Pair>(key_type, seed)
290		}
291
292		fn bandersnatch_sign(
293			&self,
294			key_type: KeyTypeId,
295			public: &bandersnatch::Public,
296			msg: &[u8],
297		) -> std::result::Result<Option<bandersnatch::Signature>, TraitError> {
298			self.sign::<bandersnatch::Pair>(key_type, public, msg)
299		}
300
301		fn bandersnatch_vrf_sign(
302			&self,
303			key_type: KeyTypeId,
304			public: &bandersnatch::Public,
305			data: &bandersnatch::vrf::VrfSignData,
306		) -> std::result::Result<Option<bandersnatch::vrf::VrfSignature>, TraitError> {
307			self.vrf_sign::<bandersnatch::Pair>(key_type, public, data)
308		}
309
310		fn bandersnatch_vrf_pre_output(
311			&self,
312			key_type: KeyTypeId,
313			public: &bandersnatch::Public,
314			input: &bandersnatch::vrf::VrfInput,
315		) -> std::result::Result<Option<bandersnatch::vrf::VrfPreOutput>, TraitError> {
316			self.vrf_pre_output::<bandersnatch::Pair>(key_type, public, input)
317		}
318
319		fn bandersnatch_ring_vrf_sign(
320			&self,
321			key_type: KeyTypeId,
322			public: &bandersnatch::Public,
323			data: &bandersnatch::vrf::VrfSignData,
324			prover: &bandersnatch::ring_vrf::RingProver,
325		) -> std::result::Result<Option<bandersnatch::ring_vrf::RingVrfSignature>, TraitError> {
326			let sig = self
327				.0
328				.read()
329				.key_pair_by_type::<bandersnatch::Pair>(public, key_type)?
330				.map(|pair| pair.ring_vrf_sign(data, prover));
331			Ok(sig)
332		}
333	}
334
335	sp_keystore::bls_experimental_enabled! {
336		fn bls381_public_keys(&self, key_type: KeyTypeId) -> Vec<bls381::Public> {
337			self.public_keys::<bls381::Pair>(key_type)
338		}
339
340		/// Generate a new pair compatible with the 'bls381' signature scheme.
341		///
342		/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
343		fn bls381_generate_new(
344			&self,
345			key_type: KeyTypeId,
346			seed: Option<&str>,
347		) -> std::result::Result<bls381::Public, TraitError> {
348			self.generate_new::<bls381::Pair>(key_type, seed)
349		}
350
351		fn bls381_sign(
352			&self,
353			key_type: KeyTypeId,
354			public: &bls381::Public,
355			msg: &[u8],
356		) -> std::result::Result<Option<bls381::Signature>, TraitError> {
357			self.sign::<bls381::Pair>(key_type, public, msg)
358		}
359
360		fn ecdsa_bls381_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa_bls381::Public> {
361			self.public_keys::<ecdsa_bls381::Pair>(key_type)
362		}
363
364		/// Generate a new pair of paired-keys compatible with the '(ecdsa,bls381)' signature scheme.
365		///
366		/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
367		fn ecdsa_bls381_generate_new(
368			&self,
369			key_type: KeyTypeId,
370			seed: Option<&str>,
371		) -> std::result::Result<ecdsa_bls381::Public, TraitError> {
372			self.generate_new::<ecdsa_bls381::Pair>(key_type, seed)
373		}
374
375		fn ecdsa_bls381_sign(
376			&self,
377			key_type: KeyTypeId,
378			public: &ecdsa_bls381::Public,
379			msg: &[u8],
380		) -> std::result::Result<Option<ecdsa_bls381::Signature>, TraitError> {
381			self.sign::<ecdsa_bls381::Pair>(key_type, public, msg)
382		}
383
384		fn ecdsa_bls381_sign_with_keccak256(
385			&self,
386			key_type: KeyTypeId,
387			public: &ecdsa_bls381::Public,
388			msg: &[u8],
389		) -> std::result::Result<Option<ecdsa_bls381::Signature>, TraitError> {
390			 let sig = self.0
391			.read()
392			.key_pair_by_type::<ecdsa_bls381::Pair>(public, key_type)?
393			.map(|pair| pair.sign_with_hasher::<KeccakHasher>(msg));
394			Ok(sig)
395		}
396	}
397}
398
399impl Into<KeystorePtr> for LocalKeystore {
400	fn into(self) -> KeystorePtr {
401		Arc::new(self)
402	}
403}
404
405/// A local key store.
406///
407/// Stores key pairs in a file system store + short lived key pairs in memory.
408///
409/// Every pair that is being generated by a `seed`, will be placed in memory.
410struct KeystoreInner {
411	path: Option<PathBuf>,
412	/// Map over `(KeyTypeId, Raw public key)` -> `Key phrase/seed`
413	additional: HashMap<(KeyTypeId, Vec<u8>), String>,
414	password: Option<SecretString>,
415}
416
417impl KeystoreInner {
418	/// Open the store at the given path.
419	///
420	/// Optionally takes a password that will be used to encrypt/decrypt the keys.
421	fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<Self> {
422		let path = path.into();
423		fs::create_dir_all(&path)?;
424
425		Ok(Self { path: Some(path), additional: HashMap::new(), password })
426	}
427
428	/// Get the password for this store.
429	fn password(&self) -> Option<&str> {
430		self.password.as_ref().map(|p| p.expose_secret()).map(|p| p.as_str())
431	}
432
433	/// Create a new in-memory store.
434	fn new_in_memory() -> Self {
435		Self { path: None, additional: HashMap::new(), password: None }
436	}
437
438	/// Get the key phrase for the given public key and key type from the in-memory store.
439	fn get_additional_pair(&self, public: &[u8], key_type: KeyTypeId) -> Option<&String> {
440		let key = (key_type, public.to_vec());
441		self.additional.get(&key)
442	}
443
444	/// Insert the given public/private key pair with the given key type.
445	///
446	/// Does not place it into the file system store.
447	fn insert_ephemeral_pair<Pair: CorePair>(
448		&mut self,
449		pair: &Pair,
450		seed: &str,
451		key_type: KeyTypeId,
452	) {
453		let key = (key_type, pair.public().to_raw_vec());
454		self.additional.insert(key, seed.into());
455	}
456
457	/// Insert a new key with anonymous crypto.
458	///
459	/// Places it into the file system store, if a path is configured.
460	fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<()> {
461		if let Some(path) = self.key_file_path(public, key_type) {
462			Self::write_to_file(path, suri)?;
463		}
464
465		Ok(())
466	}
467
468	/// Generate a new key.
469	///
470	/// Places it into the file system store, if a path is configured. Otherwise insert
471	/// it into the memory cache only.
472	fn generate_by_type<Pair: CorePair>(&mut self, key_type: KeyTypeId) -> Result<Pair> {
473		let (pair, phrase, _) = Pair::generate_with_phrase(self.password());
474		if let Some(path) = self.key_file_path(pair.public().as_slice(), key_type) {
475			Self::write_to_file(path, &phrase)?;
476		} else {
477			self.insert_ephemeral_pair(&pair, &phrase, key_type);
478		}
479
480		Ok(pair)
481	}
482
483	/// Write the given `data` to `file`.
484	fn write_to_file(file: PathBuf, data: &str) -> Result<()> {
485		let mut file = File::create(file)?;
486
487		#[cfg(target_family = "unix")]
488		{
489			use std::os::unix::fs::PermissionsExt;
490			file.set_permissions(fs::Permissions::from_mode(0o600))?;
491		}
492
493		serde_json::to_writer(&file, data)?;
494		file.flush()?;
495		Ok(())
496	}
497
498	/// Create a new key from seed.
499	///
500	/// Does not place it into the file system store.
501	fn insert_ephemeral_from_seed_by_type<Pair: CorePair>(
502		&mut self,
503		seed: &str,
504		key_type: KeyTypeId,
505	) -> Result<Pair> {
506		let pair = Pair::from_string(seed, None).map_err(|_| Error::InvalidSeed)?;
507		self.insert_ephemeral_pair(&pair, seed, key_type);
508		Ok(pair)
509	}
510
511	/// Get the key phrase for a given public key and key type.
512	fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result<Option<String>> {
513		if let Some(phrase) = self.get_additional_pair(public, key_type) {
514			return Ok(Some(phrase.clone()))
515		}
516
517		let path = if let Some(path) = self.key_file_path(public, key_type) {
518			path
519		} else {
520			return Ok(None)
521		};
522
523		if path.exists() {
524			let file = File::open(path)?;
525
526			serde_json::from_reader(&file).map_err(Into::into).map(Some)
527		} else {
528			Ok(None)
529		}
530	}
531
532	/// Get a key pair for the given public key and key type.
533	fn key_pair_by_type<Pair: CorePair>(
534		&self,
535		public: &Pair::Public,
536		key_type: KeyTypeId,
537	) -> Result<Option<Pair>> {
538		let phrase = if let Some(p) = self.key_phrase_by_type(public.as_slice(), key_type)? {
539			p
540		} else {
541			return Ok(None)
542		};
543
544		let pair = Pair::from_string(&phrase, self.password()).map_err(|_| Error::InvalidPhrase)?;
545
546		if &pair.public() == public {
547			Ok(Some(pair))
548		} else {
549			Err(Error::PublicKeyMismatch)
550		}
551	}
552
553	/// Get the file path for the given public key and key type.
554	///
555	/// Returns `None` if the keystore only exists in-memory and there isn't any path to provide.
556	fn key_file_path(&self, public: &[u8], key_type: KeyTypeId) -> Option<PathBuf> {
557		let mut buf = self.path.as_ref()?.clone();
558		let key_type = array_bytes::bytes2hex("", &key_type.0);
559		let key = array_bytes::bytes2hex("", public);
560		buf.push(key_type + key.as_str());
561		Some(buf)
562	}
563
564	/// Returns a list of raw public keys filtered by `KeyTypeId`
565	fn raw_public_keys(&self, key_type: KeyTypeId) -> Result<Vec<Vec<u8>>> {
566		let mut public_keys: Vec<Vec<u8>> = self
567			.additional
568			.keys()
569			.into_iter()
570			.filter_map(|k| if k.0 == key_type { Some(k.1.clone()) } else { None })
571			.collect();
572
573		if let Some(path) = &self.path {
574			for entry in fs::read_dir(&path)? {
575				let entry = entry?;
576				let path = entry.path();
577
578				// skip directories and non-unicode file names (hex is unicode)
579				if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
580					match array_bytes::hex2bytes(name) {
581						Ok(ref hex) if hex.len() > 4 => {
582							if hex[0..4] != key_type.0 {
583								continue
584							}
585							let public = hex[4..].to_vec();
586							public_keys.push(public);
587						},
588						_ => continue,
589					}
590				}
591			}
592		}
593
594		Ok(public_keys)
595	}
596
597	/// Get a key pair for the given public key.
598	///
599	/// Returns `Ok(None)` if the key doesn't exist, `Ok(Some(_))` if the key exists or `Err(_)`
600	/// when something failed.
601	pub fn key_pair<Pair: AppPair>(
602		&self,
603		public: &<Pair as AppCrypto>::Public,
604	) -> Result<Option<Pair>> {
605		self.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID)
606			.map(|v| v.map(Into::into))
607	}
608}
609
610#[cfg(test)]
611mod tests {
612	use super::*;
613	use sp_application_crypto::{ed25519, sr25519, AppPublic};
614	use sp_core::{crypto::Ss58Codec, testing::SR25519, Pair};
615	use std::{fs, str::FromStr};
616	use tempfile::TempDir;
617
618	const TEST_KEY_TYPE: KeyTypeId = KeyTypeId(*b"test");
619
620	impl KeystoreInner {
621		fn insert_ephemeral_from_seed<Pair: AppPair>(&mut self, seed: &str) -> Result<Pair> {
622			self.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID)
623				.map(Into::into)
624		}
625
626		fn public_keys<Public: AppPublic>(&self) -> Result<Vec<Public>> {
627			self.raw_public_keys(Public::ID).map(|v| {
628				v.into_iter().filter_map(|k| Public::from_slice(k.as_slice()).ok()).collect()
629			})
630		}
631
632		fn generate<Pair: AppPair>(&mut self) -> Result<Pair> {
633			self.generate_by_type::<Pair::Generic>(Pair::ID).map(Into::into)
634		}
635	}
636
637	#[test]
638	fn basic_store() {
639		let temp_dir = TempDir::new().unwrap();
640		let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
641
642		assert!(store.public_keys::<ed25519::AppPublic>().unwrap().is_empty());
643
644		let key: ed25519::AppPair = store.generate().unwrap();
645		let key2: ed25519::AppPair = store.key_pair(&key.public()).unwrap().unwrap();
646
647		assert_eq!(key.public(), key2.public());
648
649		assert_eq!(store.public_keys::<ed25519::AppPublic>().unwrap()[0], key.public());
650	}
651
652	#[test]
653	fn has_keys_works() {
654		let temp_dir = TempDir::new().unwrap();
655		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
656
657		let key: ed25519::AppPair = store.0.write().generate().unwrap();
658		let key2 = ed25519::Pair::generate().0;
659
660		assert!(!store.has_keys(&[(key2.public().to_vec(), ed25519::AppPublic::ID)]));
661
662		assert!(!store.has_keys(&[
663			(key2.public().to_vec(), ed25519::AppPublic::ID),
664			(key.public().to_raw_vec(), ed25519::AppPublic::ID),
665		],));
666
667		assert!(store.has_keys(&[(key.public().to_raw_vec(), ed25519::AppPublic::ID)]));
668	}
669
670	#[test]
671	fn test_insert_ephemeral_from_seed() {
672		let temp_dir = TempDir::new().unwrap();
673		let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
674
675		let pair: ed25519::AppPair = store
676			.insert_ephemeral_from_seed(
677				"0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc",
678			)
679			.unwrap();
680		assert_eq!(
681			"5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HJCA",
682			pair.public().to_ss58check()
683		);
684
685		drop(store);
686		let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
687		// Keys generated from seed should not be persisted!
688		assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().is_none());
689	}
690
691	#[test]
692	fn password_being_used() {
693		let password = String::from("password");
694		let temp_dir = TempDir::new().unwrap();
695		let mut store = KeystoreInner::open(
696			temp_dir.path(),
697			Some(FromStr::from_str(password.as_str()).unwrap()),
698		)
699		.unwrap();
700
701		let pair: ed25519::AppPair = store.generate().unwrap();
702		assert_eq!(
703			pair.public(),
704			store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(),
705		);
706
707		// Without the password the key should not be retrievable
708		let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
709		assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).is_err());
710
711		let store = KeystoreInner::open(
712			temp_dir.path(),
713			Some(FromStr::from_str(password.as_str()).unwrap()),
714		)
715		.unwrap();
716		assert_eq!(
717			pair.public(),
718			store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(),
719		);
720	}
721
722	#[test]
723	fn public_keys_are_returned() {
724		let temp_dir = TempDir::new().unwrap();
725		let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
726
727		let mut keys = Vec::new();
728		for i in 0..10 {
729			keys.push(store.generate::<ed25519::AppPair>().unwrap().public());
730			keys.push(
731				store
732					.insert_ephemeral_from_seed::<ed25519::AppPair>(&format!(
733						"0x3d97c819d68f9bafa7d6e79cb991eebcd7{}d966c5334c0b94d9e1fa7ad0869dc",
734						i
735					))
736					.unwrap()
737					.public(),
738			);
739		}
740
741		// Generate a key of a different type
742		store.generate::<sr25519::AppPair>().unwrap();
743
744		keys.sort();
745		let mut store_pubs = store.public_keys::<ed25519::AppPublic>().unwrap();
746		store_pubs.sort();
747
748		assert_eq!(keys, store_pubs);
749	}
750
751	#[test]
752	fn store_unknown_and_extract_it() {
753		let temp_dir = TempDir::new().unwrap();
754		let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
755
756		let secret_uri = "//Alice";
757		let key_pair = sr25519::AppPair::from_string(secret_uri, None).expect("Generates key pair");
758
759		store
760			.insert(SR25519, secret_uri, key_pair.public().as_ref())
761			.expect("Inserts unknown key");
762
763		let store_key_pair = store
764			.key_pair_by_type::<sr25519::AppPair>(&key_pair.public(), SR25519)
765			.expect("Gets key pair from keystore")
766			.unwrap();
767
768		assert_eq!(key_pair.public(), store_key_pair.public());
769	}
770
771	#[test]
772	fn store_ignores_files_with_invalid_name() {
773		let temp_dir = TempDir::new().unwrap();
774		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
775
776		let file_name = temp_dir.path().join(array_bytes::bytes2hex("", &SR25519.0[..2]));
777		fs::write(file_name, "test").expect("Invalid file is written");
778
779		assert!(store.sr25519_public_keys(SR25519).is_empty());
780	}
781
782	#[test]
783	fn generate_with_seed_is_not_stored() {
784		let temp_dir = TempDir::new().unwrap();
785		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
786		let _alice_tmp_key = store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap();
787
788		assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1);
789
790		drop(store);
791		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
792		assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 0);
793	}
794
795	#[test]
796	fn generate_can_be_fetched_in_memory() {
797		let store = LocalKeystore::in_memory();
798		store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap();
799
800		assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1);
801		store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap();
802		assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 2);
803	}
804
805	#[test]
806	#[cfg(target_family = "unix")]
807	fn uses_correct_file_permissions_on_unix() {
808		use std::os::unix::fs::PermissionsExt;
809
810		let temp_dir = TempDir::new().unwrap();
811		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
812
813		let public = store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap();
814
815		let path = store.0.read().key_file_path(public.as_ref(), TEST_KEY_TYPE).unwrap();
816		let permissions = File::open(path).unwrap().metadata().unwrap().permissions();
817
818		assert_eq!(0o100600, permissions.mode());
819	}
820}