Skip to main content

ac_keystore/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) 2019-2021 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. This file is from substrate but was copied here to have
19//! access to the private stuff.
20//! Original file: https://github.com/paritytech/polkadot-sdk/blob/76a6d478e07b2cdf9e5b87a2a840e92bea8d9e12/substrate/client/keystore/src/local.rs
21
22use array_bytes::{Dehexify, Hexify};
23use parking_lot::RwLock;
24use sc_keystore::{Error, Result};
25use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy};
26use sp_core::{
27	crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret},
28	ecdsa, ed25519, sr25519,
29};
30use std::{
31	collections::HashMap,
32	fs::{self, File},
33	io::Write,
34	path::PathBuf,
35	sync::Arc,
36};
37
38mod keystore_ext;
39pub use keystore_ext::KeystoreExt;
40pub use sp_keystore::{Error as TraitError, Keystore, KeystorePtr};
41
42sp_keystore::bandersnatch_experimental_enabled! {
43use sp_core::bandersnatch;
44}
45
46sp_keystore::bls_experimental_enabled! {
47use sp_core::{bls381, ecdsa_bls381, KeccakHasher};
48}
49
50/// A local based keystore that is either memory-based or filesystem-based.
51pub struct LocalKeystore(RwLock<KeystoreInner>);
52
53impl LocalKeystore {
54	/// Create a local keystore from filesystem.
55	///
56	/// The keystore will be created at `path`. The keystore optionally supports to encrypt/decrypt
57	/// the keys in the keystore using `password`.
58	///
59	/// NOTE: Even when passing a `password`, the keys on disk appear to look like normal secret
60	/// uris. However, without having the correct password the secret uri will not generate the
61	/// correct private key. See [`SecretUri`](sp_core::crypto::SecretUri) for more information.
62	pub fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<Self> {
63		let inner = KeystoreInner::open(path, password)?;
64		Ok(Self(RwLock::new(inner)))
65	}
66
67	/// Create a local keystore in memory.
68	pub fn in_memory() -> Self {
69		let inner = KeystoreInner::new_in_memory();
70		Self(RwLock::new(inner))
71	}
72
73	/// Get a key pair for the given public key.
74	///
75	/// Returns `Ok(None)` if the key doesn't exist, `Ok(Some(_))` if the key exists and
76	/// `Err(_)` when something failed.
77	pub fn key_pair<Pair: AppPair>(
78		&self,
79		public: &<Pair as AppCrypto>::Public,
80	) -> Result<Option<Pair>> {
81		self.0.read().key_pair::<Pair>(public)
82	}
83
84	fn public_keys<T: CorePair>(&self, key_type: KeyTypeId) -> Vec<T::Public> {
85		self.0
86			.read()
87			.raw_public_keys(key_type)
88			.map(|v| {
89				v.into_iter().filter_map(|k| T::Public::from_slice(k.as_slice()).ok()).collect()
90			})
91			.unwrap_or_default()
92	}
93
94	fn generate_new<T: CorePair>(
95		&self,
96		key_type: KeyTypeId,
97		seed: Option<&str>,
98	) -> std::result::Result<T::Public, TraitError> {
99		let pair = match seed {
100			Some(seed) => self.0.write().insert_ephemeral_from_seed_by_type::<T>(seed, key_type),
101			None => self.0.write().generate_by_type::<T>(key_type),
102		}
103		.map_err(|e| -> TraitError { e.into() })?;
104		Ok(pair.public())
105	}
106
107	fn sign<T: CorePair>(
108		&self,
109		key_type: KeyTypeId,
110		public: &T::Public,
111		msg: &[u8],
112	) -> std::result::Result<Option<T::Signature>, TraitError> {
113		let signature = self
114			.0
115			.read()
116			.key_pair_by_type::<T>(public, key_type)?
117			.map(|pair| pair.sign(msg));
118		Ok(signature)
119	}
120
121	fn vrf_sign<T: CorePair + VrfSecret>(
122		&self,
123		key_type: KeyTypeId,
124		public: &T::Public,
125		data: &T::VrfSignData,
126	) -> std::result::Result<Option<T::VrfSignature>, TraitError> {
127		let sig = self
128			.0
129			.read()
130			.key_pair_by_type::<T>(public, key_type)?
131			.map(|pair| pair.vrf_sign(data));
132		Ok(sig)
133	}
134
135	fn vrf_pre_output<T: CorePair + VrfSecret>(
136		&self,
137		key_type: KeyTypeId,
138		public: &T::Public,
139		input: &T::VrfInput,
140	) -> std::result::Result<Option<T::VrfPreOutput>, TraitError> {
141		let pre_output = self
142			.0
143			.read()
144			.key_pair_by_type::<T>(public, key_type)?
145			.map(|pair| pair.vrf_pre_output(input));
146		Ok(pre_output)
147	}
148}
149
150impl Keystore for LocalKeystore {
151	/// Insert a new secret key.
152	///
153	/// WARNING: if the secret keypair has been manually generated using a password
154	/// (e.g. using methods such as [`sp_core::crypto::Pair::from_phrase`]) then such
155	/// a password must match the one used to open the keystore via [`LocalKeystore::open`].
156	/// If the passwords doesn't match then the inserted key ends up being unusable under
157	/// the current keystore instance.
158	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	/// Generate a new pair compatible with the 'ed25519' signature scheme.
182	///
183	/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
184	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	/// Generate a new pair compatible with the 'sr25519' signature scheme.
224	///
225	/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
226	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	/// Generate a new pair compatible with the 'ecdsa' signature scheme.
248	///
249	/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
250	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	sp_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		/// Generate a new pair compatible with the 'bandersnatch' signature scheme.
287		///
288		/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
289		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	sp_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		/// Generate a new pair compatible with the 'bls381' signature scheme.
346		///
347		/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
348		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 ecdsa_bls381_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa_bls381::Public> {
366			self.public_keys::<ecdsa_bls381::Pair>(key_type)
367		}
368
369		/// Generate a new pair of paired-keys compatible with the '(ecdsa,bls381)' signature scheme.
370		///
371		/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
372		fn ecdsa_bls381_generate_new(
373			&self,
374			key_type: KeyTypeId,
375			seed: Option<&str>,
376		) -> std::result::Result<ecdsa_bls381::Public, TraitError> {
377			self.generate_new::<ecdsa_bls381::Pair>(key_type, seed)
378		}
379
380		fn ecdsa_bls381_sign(
381			&self,
382			key_type: KeyTypeId,
383			public: &ecdsa_bls381::Public,
384			msg: &[u8],
385		) -> std::result::Result<Option<ecdsa_bls381::Signature>, TraitError> {
386			self.sign::<ecdsa_bls381::Pair>(key_type, public, msg)
387		}
388
389		fn ecdsa_bls381_sign_with_keccak256(
390			&self,
391			key_type: KeyTypeId,
392			public: &ecdsa_bls381::Public,
393			msg: &[u8],
394		) -> std::result::Result<Option<ecdsa_bls381::Signature>, TraitError> {
395			 let sig = self.0
396			.read()
397			.key_pair_by_type::<ecdsa_bls381::Pair>(public, key_type)?
398			.map(|pair| pair.sign_with_hasher::<KeccakHasher>(msg));
399			Ok(sig)
400		}
401	}
402}
403
404impl From<LocalKeystore> for KeystorePtr {
405	fn from(val: LocalKeystore) -> Self {
406		Arc::new(val)
407	}
408}
409
410/// A local key store.
411///
412/// Stores key pairs in a file system store + short lived key pairs in memory.
413///
414/// Every pair that is being generated by a `seed`, will be placed in memory.
415struct KeystoreInner {
416	path: Option<PathBuf>,
417	/// Map over `(KeyTypeId, Raw public key)` -> `Key phrase/seed`
418	additional: HashMap<(KeyTypeId, Vec<u8>), String>,
419	password: Option<SecretString>,
420}
421
422impl KeystoreInner {
423	/// Open the store at the given path.
424	///
425	/// Optionally takes a password that will be used to encrypt/decrypt the keys.
426	fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<Self> {
427		let path = path.into();
428		fs::create_dir_all(&path)?;
429
430		Ok(Self { path: Some(path), additional: HashMap::new(), password })
431	}
432
433	/// Get the password for this store.
434	fn password(&self) -> Option<&str> {
435		self.password.as_ref().map(|p| p.expose_secret()).map(|p| p.as_str())
436	}
437
438	/// Create a new in-memory store.
439	fn new_in_memory() -> Self {
440		Self { path: None, additional: HashMap::new(), password: None }
441	}
442
443	/// Get the key phrase for the given public key and key type from the in-memory store.
444	fn get_additional_pair(&self, public: &[u8], key_type: KeyTypeId) -> Option<&String> {
445		let key = (key_type, public.to_vec());
446		self.additional.get(&key)
447	}
448
449	/// Insert the given public/private key pair with the given key type.
450	///
451	/// Does not place it into the file system store.
452	fn insert_ephemeral_pair<Pair: CorePair>(
453		&mut self,
454		pair: &Pair,
455		seed: &str,
456		key_type: KeyTypeId,
457	) {
458		let key = (key_type, pair.public().to_raw_vec());
459		self.additional.insert(key, seed.into());
460	}
461
462	/// Insert a new key with anonymous crypto.
463	///
464	/// Places it into the file system store, if a path is configured.
465	fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<()> {
466		if let Some(path) = self.key_file_path(public, key_type) {
467			Self::write_to_file(path, suri)?;
468		}
469
470		Ok(())
471	}
472
473	/// Generate a new key.
474	///
475	/// Places it into the file system store, if a path is configured. Otherwise insert
476	/// it into the memory cache only.
477	fn generate_by_type<Pair: CorePair>(&mut self, key_type: KeyTypeId) -> Result<Pair> {
478		let (pair, phrase, _) = Pair::generate_with_phrase(self.password());
479		if let Some(path) = self.key_file_path(pair.public().as_slice(), key_type) {
480			Self::write_to_file(path, &phrase)?;
481		} else {
482			self.insert_ephemeral_pair(&pair, &phrase, key_type);
483		}
484
485		Ok(pair)
486	}
487
488	/// Write the given `data` to `file`.
489	fn write_to_file(file: PathBuf, data: &str) -> Result<()> {
490		let mut file = File::create(file)?;
491
492		#[cfg(target_family = "unix")]
493		{
494			use std::os::unix::fs::PermissionsExt;
495			file.set_permissions(fs::Permissions::from_mode(0o600))?;
496		}
497
498		serde_json::to_writer(&file, data)?;
499		file.flush()?;
500		Ok(())
501	}
502
503	/// Create a new key from seed.
504	///
505	/// Does not place it into the file system store.
506	fn insert_ephemeral_from_seed_by_type<Pair: CorePair>(
507		&mut self,
508		seed: &str,
509		key_type: KeyTypeId,
510	) -> Result<Pair> {
511		let pair = Pair::from_string(seed, None).map_err(|_| Error::InvalidSeed)?;
512		self.insert_ephemeral_pair(&pair, seed, key_type);
513		Ok(pair)
514	}
515
516	/// Get the key phrase for a given public key and key type.
517	fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result<Option<String>> {
518		if let Some(phrase) = self.get_additional_pair(public, key_type) {
519			return Ok(Some(phrase.clone()))
520		}
521
522		let path = if let Some(path) = self.key_file_path(public, key_type) {
523			path
524		} else {
525			return Ok(None)
526		};
527
528		if path.exists() {
529			let file = File::open(path)?;
530
531			serde_json::from_reader(&file).map_err(Into::into).map(Some)
532		} else {
533			Ok(None)
534		}
535	}
536
537	/// Get a key pair for the given public key and key type.
538	fn key_pair_by_type<Pair: CorePair>(
539		&self,
540		public: &Pair::Public,
541		key_type: KeyTypeId,
542	) -> Result<Option<Pair>> {
543		let phrase = if let Some(p) = self.key_phrase_by_type(public.as_slice(), key_type)? {
544			p
545		} else {
546			return Ok(None)
547		};
548
549		let pair = Pair::from_string(&phrase, self.password()).map_err(|_| Error::InvalidPhrase)?;
550
551		if &pair.public() == public {
552			Ok(Some(pair))
553		} else {
554			Err(Error::PublicKeyMismatch)
555		}
556	}
557
558	/// Get the file path for the given public key and key type.
559	///
560	/// Returns `None` if the keystore only exists in-memory and there isn't any path to provide.
561	fn key_file_path(&self, public: &[u8], key_type: KeyTypeId) -> Option<PathBuf> {
562		let mut buf = self.path.as_ref()?.clone();
563		let key_type = key_type.0.hexify();
564		let key = public.hexify();
565		buf.push(key_type + key.as_str());
566		Some(buf)
567	}
568
569	/// Returns a list of raw public keys filtered by `KeyTypeId`
570	fn raw_public_keys(&self, key_type: KeyTypeId) -> Result<Vec<Vec<u8>>> {
571		let mut public_keys: Vec<Vec<u8>> = self
572			.additional
573			.keys()
574			.filter_map(|k| if k.0 == key_type { Some(k.1.clone()) } else { None })
575			.collect();
576
577		if let Some(path) = &self.path {
578			for entry in fs::read_dir(path)? {
579				let entry = entry?;
580				let path = entry.path();
581
582				// skip directories and non-unicode file names (hex is unicode)
583				if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
584					match <Vec<u8>>::dehexify(name) {
585						Ok(ref hex) if hex.len() > 4 => {
586							if hex[0..4] != key_type.0 {
587								continue
588							}
589							let public = hex[4..].to_vec();
590							public_keys.push(public);
591						},
592						_ => continue,
593					}
594				}
595			}
596		}
597
598		Ok(public_keys)
599	}
600
601	/// Get a key pair for the given public key.
602	///
603	/// Returns `Ok(None)` if the key doesn't exist, `Ok(Some(_))` if the key exists or `Err(_)`
604	/// when something failed.
605	pub fn key_pair<Pair: AppPair>(
606		&self,
607		public: &<Pair as AppCrypto>::Public,
608	) -> Result<Option<Pair>> {
609		self.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID)
610			.map(|v| v.map(Into::into))
611	}
612}
613
614#[cfg(test)]
615mod tests {
616	use super::*;
617	use sp_application_crypto::{ed25519, sr25519, AppPublic};
618	use sp_core::{crypto::Ss58Codec, testing::SR25519, Pair};
619	use std::{fs, str::FromStr};
620	use tempfile::TempDir;
621
622	const TEST_KEY_TYPE: KeyTypeId = KeyTypeId(*b"test");
623
624	impl KeystoreInner {
625		fn insert_ephemeral_from_seed<Pair: AppPair>(&mut self, seed: &str) -> Result<Pair> {
626			self.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID)
627				.map(Into::into)
628		}
629
630		fn public_keys<Public: AppPublic>(&self) -> Result<Vec<Public>> {
631			self.raw_public_keys(Public::ID).map(|v| {
632				v.into_iter().filter_map(|k| Public::from_slice(k.as_slice()).ok()).collect()
633			})
634		}
635
636		fn generate<Pair: AppPair>(&mut self) -> Result<Pair> {
637			self.generate_by_type::<Pair::Generic>(Pair::ID).map(Into::into)
638		}
639	}
640
641	#[test]
642	fn basic_store() {
643		let temp_dir = TempDir::new().unwrap();
644		let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
645
646		assert!(store.public_keys::<ed25519::AppPublic>().unwrap().is_empty());
647
648		let key: ed25519::AppPair = store.generate().unwrap();
649		let key2: ed25519::AppPair = store.key_pair(&key.public()).unwrap().unwrap();
650
651		assert_eq!(key.public(), key2.public());
652
653		assert_eq!(store.public_keys::<ed25519::AppPublic>().unwrap()[0], key.public());
654	}
655
656	#[test]
657	fn has_keys_works() {
658		let temp_dir = TempDir::new().unwrap();
659		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
660
661		let key: ed25519::AppPair = store.0.write().generate().unwrap();
662		let key2 = ed25519::Pair::generate().0;
663
664		assert!(!store.has_keys(&[(key2.public().to_vec(), ed25519::AppPublic::ID)]));
665
666		assert!(!store.has_keys(&[
667			(key2.public().to_vec(), ed25519::AppPublic::ID),
668			(key.public().to_raw_vec(), ed25519::AppPublic::ID),
669		],));
670
671		assert!(store.has_keys(&[(key.public().to_raw_vec(), ed25519::AppPublic::ID)]));
672	}
673
674	#[test]
675	fn test_insert_ephemeral_from_seed() {
676		let temp_dir = TempDir::new().unwrap();
677		let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
678
679		let pair: ed25519::AppPair = store
680			.insert_ephemeral_from_seed(
681				"0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc",
682			)
683			.unwrap();
684		assert_eq!(
685			"5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HJCA",
686			pair.public().to_ss58check()
687		);
688
689		drop(store);
690		let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
691		// Keys generated from seed should not be persisted!
692		assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().is_none());
693	}
694
695	#[test]
696	fn password_being_used() {
697		let password = String::from("password");
698		let temp_dir = TempDir::new().unwrap();
699		let mut store = KeystoreInner::open(
700			temp_dir.path(),
701			Some(FromStr::from_str(password.as_str()).unwrap()),
702		)
703		.unwrap();
704
705		let pair: ed25519::AppPair = store.generate().unwrap();
706		assert_eq!(
707			pair.public(),
708			store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(),
709		);
710
711		// Without the password the key should not be retrievable
712		let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
713		assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).is_err());
714
715		let store = KeystoreInner::open(
716			temp_dir.path(),
717			Some(FromStr::from_str(password.as_str()).unwrap()),
718		)
719		.unwrap();
720		assert_eq!(
721			pair.public(),
722			store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(),
723		);
724	}
725
726	#[test]
727	fn public_keys_are_returned() {
728		let temp_dir = TempDir::new().unwrap();
729		let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
730
731		let mut keys = Vec::new();
732		for i in 0..10 {
733			keys.push(store.generate::<ed25519::AppPair>().unwrap().public());
734			keys.push(
735				store
736					.insert_ephemeral_from_seed::<ed25519::AppPair>(&format!(
737						"0x3d97c819d68f9bafa7d6e79cb991eebcd7{}d966c5334c0b94d9e1fa7ad0869dc",
738						i
739					))
740					.unwrap()
741					.public(),
742			);
743		}
744
745		// Generate a key of a different type
746		store.generate::<sr25519::AppPair>().unwrap();
747
748		keys.sort();
749		let mut store_pubs = store.public_keys::<ed25519::AppPublic>().unwrap();
750		store_pubs.sort();
751
752		assert_eq!(keys, store_pubs);
753	}
754
755	#[test]
756	fn store_unknown_and_extract_it() {
757		let temp_dir = TempDir::new().unwrap();
758		let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
759
760		let secret_uri = "//Alice";
761		let key_pair = sr25519::AppPair::from_string(secret_uri, None).expect("Generates key pair");
762
763		store
764			.insert(SR25519, secret_uri, key_pair.public().as_ref())
765			.expect("Inserts unknown key");
766
767		let store_key_pair = store
768			.key_pair_by_type::<sr25519::AppPair>(&key_pair.public(), SR25519)
769			.expect("Gets key pair from keystore")
770			.unwrap();
771
772		assert_eq!(key_pair.public(), store_key_pair.public());
773	}
774
775	#[test]
776	fn store_ignores_files_with_invalid_name() {
777		let temp_dir = TempDir::new().unwrap();
778		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
779
780		let file_name = temp_dir.path().join(&SR25519.0[..2].hexify());
781		fs::write(file_name, "test").expect("Invalid file is written");
782
783		assert!(store.sr25519_public_keys(SR25519).is_empty());
784	}
785
786	#[test]
787	fn generate_with_seed_is_not_stored() {
788		let temp_dir = TempDir::new().unwrap();
789		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
790		let _alice_tmp_key = store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap();
791
792		assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1);
793
794		drop(store);
795		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
796		assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 0);
797	}
798
799	#[test]
800	fn generate_can_be_fetched_in_memory() {
801		let store = LocalKeystore::in_memory();
802		store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap();
803
804		assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1);
805		store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap();
806		assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 2);
807	}
808
809	#[test]
810	#[cfg(target_family = "unix")]
811	fn uses_correct_file_permissions_on_unix() {
812		use std::os::unix::fs::PermissionsExt;
813
814		let temp_dir = TempDir::new().unwrap();
815		let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
816
817		let public = store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap();
818
819		let path = store.0.read().key_file_path(public.as_ref(), TEST_KEY_TYPE).unwrap();
820		let permissions = File::open(path).unwrap().metadata().unwrap().permissions();
821
822		assert_eq!(0o100600, permissions.mode());
823	}
824}