Skip to main content

soil_client/keystore/
local.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Local keystore implementation
8
9use 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
34/// A local based keystore that is either memory-based or filesystem-based.
35pub struct LocalKeystore(RwLock<KeystoreInner>);
36
37impl LocalKeystore {
38	/// Create a local keystore from filesystem.
39	///
40	/// The keystore will be created at `path`. The keystore optionally supports to encrypt/decrypt
41	/// the keys in the keystore using `password`.
42	///
43	/// NOTE: Even when passing a `password`, the keys on disk appear to look like normal secret
44	/// uris. However, without having the correct password the secret uri will not generate the
45	/// correct private key. See [`SecretUri`](subsoil::core::crypto::SecretUri) for more information.
46	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	/// Create a local keystore in memory.
52	pub fn in_memory() -> Self {
53		let inner = KeystoreInner::new_in_memory();
54		Self(RwLock::new(inner))
55	}
56
57	/// Get a key pair for the given public key.
58	///
59	/// Returns `Ok(None)` if the key doesn't exist, `Ok(Some(_))` if the key exists and
60	/// `Err(_)` when something failed.
61	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	/// 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 [`subsoil::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	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		/// 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	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		/// 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 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		/// Generate a new pair of paired-keys compatible with the '(ecdsa,bls381)' signature scheme.
379		///
380		/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
381		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			// This is done to give the keystore access to individual keys, this is necessary to avoid
397			// unnecessary host functions for paired keys and re-use host functions implemented for each
398			// element of the pair.
399			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
435/// A local key store.
436///
437/// Stores key pairs in a file system store + short lived key pairs in memory.
438///
439/// Every pair that is being generated by a `seed`, will be placed in memory.
440struct KeystoreInner {
441	path: Option<PathBuf>,
442	/// Map over `(KeyTypeId, Raw public key)` -> `Key phrase/seed`
443	additional: HashMap<(KeyTypeId, Vec<u8>), String>,
444	password: Option<SecretString>,
445}
446
447impl KeystoreInner {
448	/// Open the store at the given path.
449	///
450	/// Optionally takes a password that will be used to encrypt/decrypt the keys.
451	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	/// Get the password for this store.
459	fn password(&self) -> Option<&str> {
460		self.password.as_ref().map(|p| p.expose_secret()).map(|p| p.as_str())
461	}
462
463	/// Create a new in-memory store.
464	fn new_in_memory() -> Self {
465		Self { path: None, additional: HashMap::new(), password: None }
466	}
467
468	/// Get the key phrase for the given public key and key type from the in-memory store.
469	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	/// Insert the given public/private key pair with the given key type.
475	///
476	/// Does not place it into the file system store.
477	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	/// Insert a new key with anonymous crypto.
488	///
489	/// Places it into the file system store, if a path is configured.
490	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	/// Generate a new key.
499	///
500	/// Places it into the file system store, if a path is configured. Otherwise insert
501	/// it into the memory cache only.
502	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	/// Write the given `data` to `file`.
514	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	/// Create a new key from seed.
529	///
530	/// Does not place it into the file system store.
531	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	/// Get the key phrase for a given public key and key type.
542	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	/// Get a key pair for the given public key and key type.
563	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	/// Get the file path for the given public key and key type.
584	///
585	/// Returns `None` if the keystore only exists in-memory and there isn't any path to provide.
586	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	/// Returns a list of raw public keys filtered by `KeyTypeId`
595	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				// skip directories and non-unicode file names (hex is unicode)
609				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	/// Get a key pair for the given public key.
628	///
629	/// Returns `Ok(None)` if the key doesn't exist, `Ok(Some(_))` if the key exists or `Err(_)`
630	/// when something failed.
631	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		// Keys generated from seed should not be persisted!
718		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		// Without the password the key should not be retrievable
738		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		// Generate a key of a different type
772		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}