From e729dbabbe885be3f36441cbd7279c5815715aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 9 Jul 2019 17:09:14 +0200 Subject: [PATCH] [offchain] Support for sign & verify for crypto keys (#3023) * Implement sign & verify. * Use phrases and password. * Sign & verify with authority keys. * Fix tests. * WiP * WiP * Allow the caller to decide on 'CryptoKind'. * Remove TODO. * Make seed private back. * Fix non-std build and bump version. * Use Into instead of asses. * Add missing typedef. --- substrate/Cargo.lock | 1 + substrate/core/cli/src/lib.rs | 2 +- substrate/core/client/db/src/offchain.rs | 23 ++- substrate/core/client/src/backend.rs | 2 +- substrate/core/client/src/in_mem.rs | 30 ++- substrate/core/executor/src/wasm_executor.rs | 49 ++++- substrate/core/keystore/src/lib.rs | 16 +- substrate/core/offchain/src/api.rs | 201 +++++++++++++++++-- substrate/core/offchain/src/lib.rs | 81 ++++++-- substrate/core/offchain/src/testing.rs | 31 ++- substrate/core/primitives/Cargo.toml | 2 + substrate/core/primitives/src/crypto.rs | 59 +++++- substrate/core/primitives/src/offchain.rs | 85 +++++--- substrate/core/primitives/src/sr25519.rs | 1 + substrate/core/service/src/components.rs | 6 +- substrate/core/service/src/config.rs | 12 +- substrate/core/service/src/lib.rs | 73 +++++-- substrate/core/service/test/src/lib.rs | 2 +- substrate/core/sr-io/src/lib.rs | 13 +- substrate/core/sr-io/with_std.rs | 33 ++- substrate/core/sr-io/without_std.rs | 93 ++++++--- substrate/core/state-machine/src/lib.rs | 10 +- 22 files changed, 647 insertions(+), 178 deletions(-) diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 08cfbc63ae..2c38d96c12 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -4544,6 +4544,7 @@ dependencies = [ "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "twox-hash 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/substrate/core/cli/src/lib.rs b/substrate/core/cli/src/lib.rs index 39b98c092b..a22278b830 100644 --- a/substrate/core/cli/src/lib.rs +++ b/substrate/core/cli/src/lib.rs @@ -376,7 +376,7 @@ where let spec = load_spec(&cli.shared_params, spec_factory)?; let mut config = service::Configuration::default_with_spec(spec.clone()); if cli.interactive_password { - config.password = input_keystore_password()? + config.password = input_keystore_password()?.into() } config.impl_name = impl_name; diff --git a/substrate/core/client/db/src/offchain.rs b/substrate/core/client/db/src/offchain.rs index 1ec6e357e6..3cefdbf47a 100644 --- a/substrate/core/client/db/src/offchain.rs +++ b/substrate/core/client/db/src/offchain.rs @@ -79,7 +79,7 @@ impl client::backend::OffchainStorage for LocalStorage { &mut self, prefix: &[u8], item_key: &[u8], - old_value: &[u8], + old_value: Option<&[u8]>, new_value: &[u8], ) -> bool { let key: Vec = prefix.iter().chain(item_key).cloned().collect(); @@ -91,11 +91,10 @@ impl client::backend::OffchainStorage for LocalStorage { let is_set; { let _key_guard = key_lock.lock(); - is_set = self.db.get(columns::OFFCHAIN, &key) + let val = self.db.get(columns::OFFCHAIN, &key) .ok() - .and_then(|x| x) - .map(|v| &*v == old_value) - .unwrap_or(true); + .and_then(|x| x); + is_set = val.as_ref().map(|x| &**x) == old_value; if is_set { self.set(prefix, item_key, new_value) @@ -130,8 +129,20 @@ mod tests { storage.set(prefix, key, value); assert_eq!(storage.get(prefix, key), Some(value.to_vec())); - assert_eq!(storage.compare_and_set(prefix, key, value, b"asd"), true); + assert_eq!(storage.compare_and_set(prefix, key, Some(value), b"asd"), true); assert_eq!(storage.get(prefix, key), Some(b"asd".to_vec())); assert!(storage.locks.lock().is_empty(), "Locks map should be empty!"); } + + #[test] + fn should_compare_and_set_on_empty_field() { + let mut storage = LocalStorage::new_test(); + let prefix = b"prefix"; + let key = b"key"; + + assert_eq!(storage.compare_and_set(prefix, key, None, b"asd"), true); + assert_eq!(storage.get(prefix, key), Some(b"asd".to_vec())); + assert!(storage.locks.lock().is_empty(), "Locks map should be empty!"); + } + } diff --git a/substrate/core/client/src/backend.rs b/substrate/core/client/src/backend.rs index 110b7b4d44..79bc1b475b 100644 --- a/substrate/core/client/src/backend.rs +++ b/substrate/core/client/src/backend.rs @@ -213,7 +213,7 @@ pub trait OffchainStorage: Clone + Send + Sync { &mut self, prefix: &[u8], key: &[u8], - old_value: &[u8], + old_value: Option<&[u8]>, new_value: &[u8], ) -> bool; } diff --git a/substrate/core/client/src/in_mem.rs b/substrate/core/client/src/in_mem.rs index b6546a67bb..8a229470be 100644 --- a/substrate/core/client/src/in_mem.rs +++ b/substrate/core/client/src/in_mem.rs @@ -794,19 +794,23 @@ impl backend::OffchainStorage for OffchainStorage { &mut self, prefix: &[u8], key: &[u8], - old_value: &[u8], + old_value: Option<&[u8]>, new_value: &[u8], ) -> bool { + use std::collections::hash_map::Entry; let key = prefix.iter().chain(key).cloned().collect(); - let mut set = false; - self.storage.entry(key).and_modify(|val| { - if val.as_slice() == old_value { - *val = new_value.to_vec(); - set = true; - } - }); - set + match self.storage.entry(key) { + Entry::Vacant(entry) => if old_value.is_none() { + entry.insert(new_value.to_vec()); + true + } else { false }, + Entry::Occupied(ref mut entry) if Some(entry.get().as_slice()) == old_value => { + entry.insert(new_value.to_vec()); + true + }, + _ => false, + } } } @@ -845,9 +849,13 @@ mod tests { assert_eq!(storage.get(b"A", b"B"), Some(b"C".to_vec())); assert_eq!(storage.get(b"B", b"A"), None); - storage.compare_and_set(b"A", b"B", b"X", b"D"); + storage.compare_and_set(b"A", b"B", Some(b"X"), b"D"); assert_eq!(storage.get(b"A", b"B"), Some(b"C".to_vec())); - storage.compare_and_set(b"A", b"B", b"C", b"D"); + storage.compare_and_set(b"A", b"B", Some(b"C"), b"D"); assert_eq!(storage.get(b"A", b"B"), Some(b"D".to_vec())); + + assert!(!storage.compare_and_set(b"B", b"A", Some(b""), b"Y")); + assert!(storage.compare_and_set(b"B", b"A", None, b"X")); + assert_eq!(storage.get(b"B", b"A"), Some(b"X".to_vec())); } } diff --git a/substrate/core/executor/src/wasm_executor.rs b/substrate/core/executor/src/wasm_executor.rs index f7f60f0d5d..83f1cf5a4b 100644 --- a/substrate/core/executor/src/wasm_executor.rs +++ b/substrate/core/executor/src/wasm_executor.rs @@ -729,18 +729,26 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, .ok_or_else(|| "Calling unavailable API ext_new_crypto_key: wasm")?; match res { - Ok(key_id) => Ok(key_id.0 as u32), + Ok(key_id) => Ok(key_id.into()), Err(()) => Ok(u32::max_value()), } }, - ext_encrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8 => { + ext_encrypt( + key: u32, + kind: u32, + data: *const u8, + data_len: u32, + msg_len: *mut u32 + ) -> *mut u8 => { let key = u32_to_key(key) .map_err(|_| "Key OOB while ext_encrypt: wasm")?; + let kind = offchain::CryptoKind::try_from(kind) + .map_err(|_| "crypto kind OOB while ext_encrypt: wasm")?; let message = this.memory.get(data, data_len as usize) .map_err(|_| "OOB while ext_encrypt: wasm")?; let res = this.ext.offchain() - .map(|api| api.encrypt(key, &*message)) + .map(|api| api.encrypt(key, kind, &*message)) .ok_or_else(|| "Calling unavailable API ext_encrypt: wasm")?; let (offset,len) = match res { @@ -759,14 +767,22 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(offset) }, - ext_decrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8 => { + ext_decrypt( + key: u32, + kind: u32, + data: *const u8, + data_len: u32, + msg_len: *mut u32 + ) -> *mut u8 => { let key = u32_to_key(key) .map_err(|_| "Key OOB while ext_decrypt: wasm")?; + let kind = offchain::CryptoKind::try_from(kind) + .map_err(|_| "crypto kind OOB while ext_decrypt: wasm")?; let message = this.memory.get(data, data_len as usize) .map_err(|_| "OOB while ext_decrypt: wasm")?; let res = this.ext.offchain() - .map(|api| api.decrypt(key, &*message)) + .map(|api| api.decrypt(key, kind, &*message)) .ok_or_else(|| "Calling unavailable API ext_decrypt: wasm")?; let (offset,len) = match res { @@ -785,14 +801,22 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(offset) }, - ext_sign(key: u32, data: *const u8, data_len: u32, sig_data_len: *mut u32) -> *mut u8 => { + ext_sign( + key: u32, + kind: u32, + data: *const u8, + data_len: u32, + sig_data_len: *mut u32 + ) -> *mut u8 => { let key = u32_to_key(key) .map_err(|_| "Key OOB while ext_sign: wasm")?; + let kind = offchain::CryptoKind::try_from(kind) + .map_err(|_| "crypto kind OOB while ext_sign: wasm")?; let message = this.memory.get(data, data_len as usize) .map_err(|_| "OOB while ext_sign: wasm")?; let res = this.ext.offchain() - .map(|api| api.sign(key, &*message)) + .map(|api| api.sign(key, kind, &*message)) .ok_or_else(|| "Calling unavailable API ext_sign: wasm")?; let (offset,len) = match res { @@ -813,6 +837,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, }, ext_verify( key: u32, + kind: u32, msg: *const u8, msg_len: u32, signature: *const u8, @@ -820,13 +845,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, ) -> u32 => { let key = u32_to_key(key) .map_err(|_| "Key OOB while ext_verify: wasm")?; + let kind = offchain::CryptoKind::try_from(kind) + .map_err(|_| "crypto kind OOB while ext_verify: wasm")?; let message = this.memory.get(msg, msg_len as usize) .map_err(|_| "OOB while ext_verify: wasm")?; let signature = this.memory.get(signature, signature_len as usize) .map_err(|_| "OOB while ext_verify: wasm")?; let res = this.ext.offchain() - .map(|api| api.verify(key, &*message, &*signature)) + .map(|api| api.verify(key, kind, &*message, &*signature)) .ok_or_else(|| "Calling unavailable API ext_verify: wasm")?; match res { @@ -945,7 +972,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, .ok_or_else(|| "Calling unavailable API ext_http_request_start: wasm")?; if let Ok(id) = id { - Ok(id.0 as u32) + Ok(id.into()) } else { Ok(u32::max_value()) } @@ -996,7 +1023,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(match res { Ok(()) => 0, - Err(e) => e as u8 as u32, + Err(e) => e.into(), }) }, ext_http_response_wait( @@ -1074,7 +1101,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, read as u32 }, Err(err) => { - u32::max_value() - err as u8 as u32 + 1 + u32::max_value() - u32::from(err) + 1 } }) }, diff --git a/substrate/core/keystore/src/lib.rs b/substrate/core/keystore/src/lib.rs index 882807e36e..77106059f8 100644 --- a/substrate/core/keystore/src/lib.rs +++ b/substrate/core/keystore/src/lib.rs @@ -23,7 +23,7 @@ use std::path::PathBuf; use std::fs::{self, File}; use std::io::{self, Write}; -use substrate_primitives::crypto::{KeyTypeId, Pair, Public, TypedKey}; +use substrate_primitives::crypto::{KeyTypeId, Pair, Public}; /// Keystore error. #[derive(Debug, derive_more::Display, derive_more::From)] @@ -69,7 +69,7 @@ impl Store { Ok(Store { path, additional: HashMap::new() }) } - fn get_pair(&self, public: &TPair::Public) -> Result> { + fn get_pair(&self, public: &TPair::Public) -> Result> { let key = (TPair::KEY_TYPE, public.to_raw_vec()); if let Some(bytes) = self.additional.get(&key) { let pair = TPair::from_seed_slice(bytes) @@ -79,13 +79,13 @@ impl Store { Ok(None) } - fn insert_pair(&mut self, pair: &TPair) { + fn insert_pair(&mut self, pair: &TPair) { let key = (TPair::KEY_TYPE, pair.public().to_raw_vec()); self.additional.insert(key, pair.to_raw_vec()); } /// Generate a new key, placing it into the store. - pub fn generate(&self, password: &str) -> Result { + pub fn generate(&self, password: &str) -> Result { let (pair, phrase, _) = TPair::generate_with_phrase(Some(password)); let mut file = File::create(self.key_file_path::(&pair.public()))?; ::serde_json::to_writer(&file, &phrase)?; @@ -94,7 +94,7 @@ impl Store { } /// Create a new key from seed. Do not place it into the store. - pub fn generate_from_seed(&mut self, seed: &str) -> Result { + pub fn generate_from_seed(&mut self, seed: &str) -> Result { let pair = TPair::from_string(seed, None) .ok().ok_or(Error::InvalidSeed)?; self.insert_pair(&pair); @@ -102,7 +102,7 @@ impl Store { } /// Load a key file with given public key. - pub fn load(&self, public: &TPair::Public, password: &str) -> Result { + pub fn load(&self, public: &TPair::Public, password: &str) -> Result { if let Some(pair) = self.get_pair(public)? { return Ok(pair) } @@ -120,7 +120,7 @@ impl Store { } /// Get public keys of all stored keys. - pub fn contents(&self) -> Result> { + pub fn contents(&self) -> Result> { let mut public_keys: Vec = self.additional.keys() .filter_map(|(ty, public)| { if *ty != TPublic::KEY_TYPE { @@ -151,7 +151,7 @@ impl Store { Ok(public_keys) } - fn key_file_path(&self, public: &TPair::Public) -> PathBuf { + fn key_file_path(&self, public: &TPair::Public) -> PathBuf { let mut buf = self.path.clone(); let bytes: [u8; 4] = TPair::KEY_TYPE.to_le_bytes(); let key_type = hex::encode(bytes); diff --git a/substrate/core/offchain/src/api.rs b/substrate/core/offchain/src/api.rs index bb5db3daff..b6aba784b3 100644 --- a/substrate/core/offchain/src/api.rs +++ b/substrate/core/offchain/src/api.rs @@ -16,15 +16,18 @@ use std::sync::Arc; use client::backend::OffchainStorage; +use crate::AuthorityKeyProvider; use futures::{Stream, Future, sync::mpsc}; use log::{info, debug, warn, error}; -use parity_codec::Decode; +use parity_codec::{Encode, Decode}; use primitives::offchain::{ Timestamp, HttpRequestId, HttpRequestStatus, HttpError, Externalities as OffchainExt, CryptoKind, CryptoKeyId, StorageKind, }; +use primitives::crypto::{Pair, Protected}; +use primitives::{ed25519, sr25519}; use runtime_primitives::{ generic::BlockId, traits::{self, Extrinsic}, @@ -36,12 +39,26 @@ enum ExtMessage { SubmitExtrinsic(Vec), } +/// A persisted key seed. +#[derive(Encode, Decode)] +struct CryptoKey { + kind: CryptoKind, + phrase: String, +} + +enum Key { + Sr25519(sr25519::Pair), + Ed25519(ed25519::Pair), +} + /// Asynchronous offchain API. /// /// NOTE this is done to prevent recursive calls into the runtime (which are not supported currently). -pub(crate) struct Api { +pub(crate) struct Api { sender: mpsc::UnboundedSender, - db: S, + db: Storage, + keys_password: Protected, + key_provider: KeyProvider, } fn unavailable_yet(name: &str) -> R { @@ -52,8 +69,60 @@ fn unavailable_yet(name: &str) -> R { const LOCAL_DB: &str = "LOCAL (fork-aware) DB"; const STORAGE_PREFIX: &[u8] = b"storage"; +const KEYS_PREFIX: &[u8] = b"keys"; -impl OffchainExt for Api { +const NEXT_ID: &[u8] = b"crypto_key_id"; + +impl Api where + Storage: OffchainStorage, + KeyProvider: AuthorityKeyProvider, +{ + fn keypair(&self, phrase: &str) -> Result { + P::from_phrase(phrase, Some(self.keys_password.as_ref())) + .map_err(|e| { + warn!("Error recovering Offchain Worker key. Password invalid? {:?}", e); + () + }) + .map(|x| x.0) + } + + fn read_key(&self, id: Option, kind: CryptoKind) -> Result { + if let Some(id) = id { + let key = self.db.get(KEYS_PREFIX, &id.0.encode()) + .and_then(|key| CryptoKey::decode(&mut &*key)) + .ok_or(())?; + if key.kind != kind { + warn!( + "Invalid crypto kind (got: {:?}, expected: {:?}), when requesting key {:?}", + key.kind, + kind, + id + ); + return Err(()) + } + + Ok(match key.kind { + CryptoKind::Sr25519 => Key::Sr25519(self.keypair(&key.phrase)?), + CryptoKind::Ed25519 => Key::Ed25519(self.keypair(&key.phrase)?), + }) + } else { + let key = match kind { + CryptoKind::Sr25519 => self.key_provider.authority_key().map(Key::Sr25519), + CryptoKind::Ed25519 => self.key_provider.authority_key().map(Key::Ed25519), + }; + + key.ok_or_else(|| { + warn!("AuthorityKey is not configured, yet offchain worker tried to access it."); + () + }) + } + } +} + +impl OffchainExt for Api where + Storage: OffchainStorage, + KeyProvider: AuthorityKeyProvider, +{ fn submit_transaction(&mut self, ext: Vec) -> Result<(), ()> { self.sender .unbounded_send(ExtMessage::SubmitExtrinsic(ext)) @@ -61,29 +130,61 @@ impl OffchainExt for Api { .map_err(|_| ()) } - fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result { - unavailable_yet::<()>("new_crypto_key"); - Err(()) + fn new_crypto_key(&mut self, kind: CryptoKind) -> Result { + let phrase = match kind { + CryptoKind::Ed25519 => { + ed25519::Pair::generate_with_phrase(Some(self.keys_password.as_ref())).1 + }, + CryptoKind::Sr25519 => { + sr25519::Pair::generate_with_phrase(Some(self.keys_password.as_ref())).1 + }, + }; + + let (id, id_encoded) = loop { + let encoded = self.db.get(KEYS_PREFIX, NEXT_ID); + let encoded_slice = encoded.as_ref().map(|x| x.as_slice()); + let new_id = encoded_slice.and_then(|mut x| u16::decode(&mut x)).unwrap_or_default() + .checked_add(1) + .ok_or(())?; + let new_id_encoded = new_id.encode(); + + if self.db.compare_and_set(KEYS_PREFIX, NEXT_ID, encoded_slice, &new_id_encoded) { + break (new_id, new_id_encoded); + } + }; + + self.db.set(KEYS_PREFIX, &id_encoded, &CryptoKey { phrase, kind } .encode()); + + Ok(CryptoKeyId(id)) } - fn encrypt(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn encrypt(&mut self, _key: Option, _kind: CryptoKind, _data: &[u8]) -> Result, ()> { unavailable_yet::<()>("encrypt"); Err(()) } - fn decrypt(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn decrypt(&mut self, _key: Option, _kind: CryptoKind, _data: &[u8]) -> Result, ()> { unavailable_yet::<()>("decrypt"); Err(()) + } - fn sign(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { - unavailable_yet::<()>("sign"); - Err(()) + fn sign(&mut self, key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()> { + let key = self.read_key(key, kind)?; + + Ok(match key { + Key::Sr25519(pair) => pair.sign(data).0.to_vec(), + Key::Ed25519(pair) => pair.sign(data).0.to_vec(), + }) } - fn verify(&mut self, _key: Option, _msg: &[u8], _signature: &[u8]) -> Result { - unavailable_yet::<()>("verify"); - Err(()) + fn verify(&mut self, key: Option, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result { + let key = self.read_key(key, kind)?; + + Ok(match key { + Key::Sr25519(pair) => sr25519::Pair::verify_weak(signature, msg, pair.public()), + Key::Ed25519(pair) => ed25519::Pair::verify_weak(signature, msg, pair.public()), + }) } fn timestamp(&mut self) -> Timestamp { @@ -114,7 +215,7 @@ impl OffchainExt for Api { ) -> bool { match kind { StorageKind::PERSISTENT => { - self.db.compare_and_set(STORAGE_PREFIX, key, old_value, new_value) + self.db.compare_and_set(STORAGE_PREFIX, key, Some(old_value), new_value) }, StorageKind::LOCAL => unavailable_yet(LOCAL_DB), } @@ -195,16 +296,20 @@ pub(crate) struct AsyncApi { impl AsyncApi { /// Creates new Offchain extensions API implementation an the asynchronous processing part. - pub fn new( + pub fn new( transaction_pool: Arc>, db: S, + keys_password: Protected, + key_provider: P, at: BlockId, - ) -> (Api, AsyncApi) { + ) -> (Api, AsyncApi) { let (sender, rx) = mpsc::unbounded(); let api = Api { sender, db, + keys_password, + key_provider, }; let async_api = AsyncApi { @@ -251,8 +356,9 @@ impl AsyncApi { mod tests { use super::*; use client_db::offchain::LocalStorage; + use crate::tests::TestProvider; - fn offchain_api() -> (Api, AsyncApi) { + fn offchain_api() -> (Api, AsyncApi) { let _ = env_logger::try_init(); let db = LocalStorage::new_test(); let client = Arc::new(test_client::new()); @@ -260,7 +366,7 @@ mod tests { Pool::new(Default::default(), transaction_pool::ChainApi::new(client.clone())) ); - AsyncApi::new(pool, db, BlockId::Number(0)) + AsyncApi::new(pool, db, "pass".to_owned().into(), TestProvider::default(), BlockId::Number(0)) } #[test] @@ -294,4 +400,59 @@ mod tests { assert_eq!(api.local_storage_compare_and_set(kind, key, b"value", b"xxx"), true); assert_eq!(api.local_storage_get(kind, key), Some(b"xxx".to_vec())); } + + #[test] + fn should_create_a_new_key_and_sign_and_verify_stuff() { + let test = |kind: CryptoKind| { + // given + let mut api = offchain_api().0; + let msg = b"Hello world!"; + + // when + let key_id = api.new_crypto_key(kind).unwrap(); + let signature = api.sign(Some(key_id), kind, msg).unwrap(); + + // then + let res = api.verify(Some(key_id), kind, msg, &signature).unwrap(); + assert_eq!(res, true); + let res = api.verify(Some(key_id), kind, msg, &[]).unwrap(); + assert_eq!(res, false); + let res = api.verify(Some(key_id), kind, b"Different msg", &signature).unwrap(); + assert_eq!(res, false); + + assert_eq!( + api.verify(Some(key_id), CryptoKind::Sr25519, msg, &signature).is_err(), + kind != CryptoKind::Sr25519 + ); + + }; + + test(CryptoKind::Ed25519); + test(CryptoKind::Sr25519); + } + + #[test] + fn should_sign_and_verify_with_authority_key() { + // given + let mut api = offchain_api().0; + api.key_provider.ed_key = Some(ed25519::Pair::generate().0); + let msg = b"Hello world!"; + let kind = CryptoKind::Ed25519; + + // when + let signature = api.sign(None, kind, msg).unwrap(); + + // then + let res = api.verify(None, kind, msg, &signature).unwrap(); + assert_eq!(res, true); + let res = api.verify(None, kind, msg, &[]).unwrap(); + assert_eq!(res, false); + let res = api.verify(None, kind, b"Different msg", &signature).unwrap(); + assert_eq!(res, false); + + assert!( + api.verify(None, CryptoKind::Sr25519, msg, &signature).is_err(), + "Invalid kind should trigger a missing key error." + ); + } } diff --git a/substrate/core/offchain/src/lib.rs b/substrate/core/offchain/src/lib.rs index 88676e6ec8..de3a5da084 100644 --- a/substrate/core/offchain/src/lib.rs +++ b/substrate/core/offchain/src/lib.rs @@ -41,7 +41,10 @@ use std::{ use client::runtime_api::ApiExt; use log::{debug, warn}; -use primitives::ExecutionContext; +use primitives::{ + ExecutionContext, + crypto, +}; use runtime_primitives::{ generic::BlockId, traits::{self, ProvideRuntimeApi}, @@ -55,38 +58,71 @@ pub mod testing; pub use offchain_primitives::OffchainWorkerApi; +/// Provides currently configured authority key. +pub trait AuthorityKeyProvider: Clone + 'static { + /// Returns currently configured authority key. + fn authority_key(&self) -> Option; +} + /// An offchain workers manager. -pub struct OffchainWorkers { - client: Arc, - db: S, +pub struct OffchainWorkers< + Client, + Storage, + KeyProvider, + Block: traits::Block, +> { + client: Arc, + db: Storage, + authority_key: KeyProvider, + keys_password: crypto::Protected, _block: PhantomData, } -impl OffchainWorkers { +impl OffchainWorkers< + Client, + Storage, + KeyProvider, + Block, +> { /// Creates new `OffchainWorkers`. pub fn new( - client: Arc, - db: S, + client: Arc, + db: Storage, + authority_key: KeyProvider, + keys_password: crypto::Protected, ) -> Self { Self { client, db, + authority_key, + keys_password, _block: PhantomData, } } } -impl fmt::Debug for OffchainWorkers { +impl fmt::Debug for OffchainWorkers< + Client, + Storage, + KeyProvider, + Block, +> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("OffchainWorkers").finish() } } -impl OffchainWorkers where +impl OffchainWorkers< + Client, + Storage, + KeyProvider, + Block, +> where Block: traits::Block, - S: client::backend::OffchainStorage + 'static, - C: ProvideRuntimeApi, - C::Api: OffchainWorkerApi, + Client: ProvideRuntimeApi, + Client::Api: OffchainWorkerApi, + KeyProvider: AuthorityKeyProvider, + Storage: client::backend::OffchainStorage + 'static, { /// Start the offchain workers after given block. #[must_use] @@ -106,6 +142,8 @@ impl OffchainWorkers where let (api, runner) = api::AsyncApi::new( pool.clone(), self.db.clone(), + self.keys_password.clone(), + self.authority_key.clone(), at.clone(), ); debug!("Running offchain workers at {:?}", at); @@ -122,6 +160,23 @@ impl OffchainWorkers where mod tests { use super::*; use futures::Future; + use primitives::{ed25519, sr25519, crypto::{TypedKey, Pair}}; + + #[derive(Clone, Default)] + pub(crate) struct TestProvider { + pub(crate) sr_key: Option, + pub(crate) ed_key: Option, + } + + impl AuthorityKeyProvider for TestProvider { + fn authority_key(&self) -> Option { + TPair::from_seed_slice(&match TPair::KEY_TYPE { + sr25519::Pair::KEY_TYPE => self.sr_key.as_ref().map(|key| key.to_raw_vec()), + ed25519::Pair::KEY_TYPE => self.ed_key.as_ref().map(|key| key.to_raw_vec()), + _ => None, + }?).ok() + } + } #[test] fn should_call_into_runtime_and_produce_extrinsic() { @@ -133,7 +188,7 @@ mod tests { let db = client_db::offchain::LocalStorage::new_test(); // when - let offchain = OffchainWorkers::new(client, db); + let offchain = OffchainWorkers::new(client, db, TestProvider::default(), "".to_owned().into()); runtime.executor().spawn(offchain.on_block_imported(&0u64, &pool)); // then diff --git a/substrate/core/offchain/src/testing.rs b/substrate/core/offchain/src/testing.rs index 15e1a01ecb..2d8c690e9a 100644 --- a/substrate/core/offchain/src/testing.rs +++ b/substrate/core/offchain/src/testing.rs @@ -143,19 +143,40 @@ impl offchain::Externalities for TestOffchainExt { unimplemented!("not needed in tests so far") } - fn encrypt(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn encrypt( + &mut self, + _key: Option, + _kind: CryptoKind, + _data: &[u8], + ) -> Result, ()> { unimplemented!("not needed in tests so far") } - fn decrypt(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn decrypt( + &mut self, + _key: Option, + _kind: CryptoKind, + _data: &[u8], + ) -> Result, ()> { unimplemented!("not needed in tests so far") } - fn sign(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn sign( + &mut self, + _key: Option, + _kind: CryptoKind, + _data: &[u8], + ) -> Result, ()> { unimplemented!("not needed in tests so far") } - fn verify(&mut self, _key: Option, _msg: &[u8], _signature: &[u8]) -> Result { + fn verify( + &mut self, + _key: Option, + _kind: CryptoKind, + _msg: &[u8], + _signature: &[u8], + ) -> Result { unimplemented!("not needed in tests so far") } @@ -190,7 +211,7 @@ impl offchain::Externalities for TestOffchainExt { match kind { StorageKind::LOCAL => &mut state.local_storage, StorageKind::PERSISTENT => &mut state.persistent_storage, - }.compare_and_set(b"", key, old_value, new_value) + }.compare_and_set(b"", key, Some(old_value), new_value) } fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option> { diff --git a/substrate/core/primitives/Cargo.toml b/substrate/core/primitives/Cargo.toml index 106349906b..94dac26bec 100644 --- a/substrate/core/primitives/Cargo.toml +++ b/substrate/core/primitives/Cargo.toml @@ -27,6 +27,7 @@ tiny-bip39 = { version = "0.6.1", optional = true } hex = { version = "0.3", optional = true } regex = { version = "1.1", optional = true } num-traits = { version = "0.2", default-features = false } +zeroize = { version = "0.9.2", default-features = false } [dev-dependencies] substrate-serializer = { path = "../serializer" } @@ -72,4 +73,5 @@ std = [ "schnorrkel", "regex", "num-traits/std", + "zeroize/std" ] diff --git a/substrate/core/primitives/src/crypto.rs b/substrate/core/primitives/src/crypto.rs index 0d01da1b48..6aac4e08bc 100644 --- a/substrate/core/primitives/src/crypto.rs +++ b/substrate/core/primitives/src/crypto.rs @@ -28,6 +28,7 @@ use regex::Regex; use base58::{FromBase58, ToBase58}; #[cfg(feature = "std")] use std::hash::Hash; +use zeroize::Zeroize; /// The root phrase for our publicly known keys. pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; @@ -66,6 +67,43 @@ impl> UncheckedInto for S { } } +/// A store for sensitive data. +/// +/// Calls `Zeroize::zeroize` upon `Drop`. +#[derive(Clone)] +pub struct Protected(T); + +impl AsRef for Protected { + fn as_ref(&self) -> &T { + &self.0 + } +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for Protected { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "") + } +} + +impl From for Protected { + fn from(t: T) -> Self { + Protected(t) + } +} + +impl Zeroize for Protected { + fn zeroize(&mut self) { + self.0.zeroize() + } +} + +impl Drop for Protected { + fn drop(&mut self) { + self.zeroize() + } +} + /// An error with the interpretation of a secret. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg(feature = "std")] @@ -289,7 +327,7 @@ impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { } /// Trait suitable for typical cryptographic PKI key public type. -pub trait Public: PartialEq + Eq { +pub trait Public: TypedKey + PartialEq + Eq { /// A new instance from the given slice that should be 32 bytes long. /// /// NOTE: No checking goes on to ensure this is a real public key. Only use it if @@ -308,9 +346,8 @@ pub trait Public: PartialEq + Eq { /// /// For now it just specifies how to create a key from a phrase and derivation path. #[cfg(feature = "std")] -pub trait Pair: Sized + 'static -{ - /// TThe type which is used to encode a public key. +pub trait Pair: TypedKey + Sized + 'static { + /// The type which is used to encode a public key. type Public: Public + Hash; /// The type used to (minimally) encode the data required to securely create @@ -319,7 +356,7 @@ pub trait Pair: Sized + 'static /// The type used to represent a signature. Can be created from a key pair and a message /// and verified with the message and a public key. - type Signature; + type Signature: AsRef<[u8]>; /// Error returned from the `derive` function. type DeriveError; @@ -476,7 +513,7 @@ mod tests { #[derive(PartialEq, Eq, Hash)] struct TestPublic; impl Public for TestPublic { - fn from_slice(bytes: &[u8]) -> Self { + fn from_slice(_bytes: &[u8]) -> Self { Self } fn as_slice(&self) -> &[u8] { @@ -486,10 +523,13 @@ mod tests { vec![] } } + impl TypedKey for TestPublic { + const KEY_TYPE: u32 = 4242; + } impl Pair for TestPair { type Public = TestPublic; type Seed = [u8; 0]; - type Signature = (); + type Signature = [u8; 0]; type DeriveError = (); fn generate() -> (Self, ::Seed) { (TestPair::Generated, []) } @@ -510,7 +550,7 @@ mod tests { Err(()) } fn from_seed(_seed: &::Seed) -> Self { TestPair::Seed(vec![]) } - fn sign(&self, _message: &[u8]) -> Self::Signature { () } + fn sign(&self, _message: &[u8]) -> Self::Signature { [] } fn verify, M: AsRef<[u8]>>( _sig: &Self::Signature, _message: M, @@ -542,6 +582,9 @@ mod tests { vec![] } } + impl TypedKey for TestPair { + const KEY_TYPE: u32 = 4242; + } #[test] fn interpret_std_seed_should_work() { diff --git a/substrate/core/primitives/src/offchain.rs b/substrate/core/primitives/src/offchain.rs index 7c068ca886..2ea93423d9 100644 --- a/substrate/core/primitives/src/offchain.rs +++ b/substrate/core/primitives/src/offchain.rs @@ -16,11 +16,13 @@ //! Offchain workers types +use crate::crypto; +use parity_codec::{Encode, Decode}; use rstd::prelude::{Vec, Box}; use rstd::convert::TryFrom; /// A type of supported crypto. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug))] #[repr(C)] pub enum StorageKind { @@ -50,15 +52,21 @@ impl TryFrom for StorageKind { } } +impl From for u32 { + fn from(c: StorageKind) -> Self { + c as u8 as u32 + } +} + /// A type of supported crypto. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug))] #[repr(C)] pub enum CryptoKind { /// SR25519 crypto (Schnorrkel) - Sr25519 = 1, + Sr25519 = crypto::key_types::SR25519 as isize, /// ED25519 crypto (Edwards) - Ed25519 = 2, + Ed25519 = crypto::key_types::ED25519 as isize, } impl TryFrom for CryptoKind { @@ -66,23 +74,41 @@ impl TryFrom for CryptoKind { fn try_from(kind: u32) -> Result { match kind { - e if e == u32::from(CryptoKind::Sr25519 as u8) => Ok(CryptoKind::Sr25519), - e if e == u32::from(CryptoKind::Ed25519 as u8) => Ok(CryptoKind::Ed25519), + e if e == CryptoKind::Sr25519 as isize as u32 => Ok(CryptoKind::Sr25519), + e if e == CryptoKind::Ed25519 as isize as u32 => Ok(CryptoKind::Ed25519), _ => Err(()), } } } +impl From for u32 { + fn from(c: CryptoKind) -> Self { + c as isize as u32 + } +} + /// Opaque type for created crypto keys. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "std", derive(Debug))] pub struct CryptoKeyId(pub u16); +impl From for u32 { + fn from(c: CryptoKeyId) -> Self { + c.0 as u32 + } +} + /// Opaque type for offchain http requests. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "std", derive(Debug))] pub struct HttpRequestId(pub u16); +impl From for u32 { + fn from(c: HttpRequestId) -> Self { + c.0 as u32 + } +} + /// An error enum returned by some http methods. #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] @@ -106,6 +132,12 @@ impl TryFrom for HttpError { } } +impl From for u32 { + fn from(c: HttpError) -> Self { + c as u8 as u32 + } +} + /// Status of the HTTP request #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] @@ -216,31 +248,34 @@ pub trait Externalities { /// Encrypt a piece of data using given crypto key. /// - /// If `key` is `None`, it will attempt to use current authority key. + /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. /// - /// Returns an error if `key` is not available or does not exist. - fn encrypt(&mut self, key: Option, data: &[u8]) -> Result, ()>; + /// Returns an error if `key` is not available or does not exist, + /// or the expected `CryptoKind` does not match. + fn encrypt(&mut self, key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()>; /// Decrypt a piece of data using given crypto key. /// - /// If `key` is `None`, it will attempt to use current authority key. + /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. /// - /// Returns an error if data cannot be decrypted or the `key` is not available or does not exist. - fn decrypt(&mut self, key: Option, data: &[u8]) -> Result, ()>; + /// Returns an error if data cannot be decrypted or the `key` is not available or does not exist, + /// or the expected `CryptoKind` does not match. + fn decrypt(&mut self, key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()>; /// Sign a piece of data using given crypto key. /// - /// If `key` is `None`, it will attempt to use current authority key. + /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. /// - /// Returns an error if `key` is not available or does not exist. - fn sign(&mut self, key: Option, data: &[u8]) -> Result, ()>; + /// Returns an error if `key` is not available or does not exist, + /// or the expected `CryptoKind` does not match. + fn sign(&mut self, key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()>; /// Verifies that `signature` for `msg` matches given `key`. /// /// Returns an `Ok` with `true` in case it does, `false` in case it doesn't. /// Returns an error in case the key is not available or does not exist or the parameters - /// lengths are incorrect. - fn verify(&mut self, key: Option, msg: &[u8], signature: &[u8]) -> Result; + /// lengths are incorrect or `CryptoKind` does not match. + fn verify(&mut self, key: Option, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result; /// Returns current UNIX timestamp (in millis) fn timestamp(&mut self) -> Timestamp; @@ -359,20 +394,20 @@ impl Externalities for Box { (&mut **self).new_crypto_key(crypto) } - fn encrypt(&mut self, key: Option, data: &[u8]) -> Result, ()> { - (&mut **self).encrypt(key, data) + fn encrypt(&mut self, key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()> { + (&mut **self).encrypt(key, kind, data) } - fn decrypt(&mut self, key: Option, data: &[u8]) -> Result, ()> { - (&mut **self).decrypt(key, data) + fn decrypt(&mut self, key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()> { + (&mut **self).decrypt(key, kind, data) } - fn sign(&mut self, key: Option, data: &[u8]) -> Result, ()> { - (&mut **self).sign(key, data) + fn sign(&mut self, key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()> { + (&mut **self).sign(key, kind, data) } - fn verify(&mut self, key: Option, msg: &[u8], signature: &[u8]) -> Result { - (&mut **self).verify(key, msg, signature) + fn verify(&mut self, key: Option, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result { + (&mut **self).verify(key, kind, msg, signature) } fn timestamp(&mut self) -> Timestamp { diff --git a/substrate/core/primitives/src/sr25519.rs b/substrate/core/primitives/src/sr25519.rs index e92b9f0003..e01d989143 100644 --- a/substrate/core/primitives/src/sr25519.rs +++ b/substrate/core/primitives/src/sr25519.rs @@ -379,6 +379,7 @@ fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> Sec secret.hard_derive_mini_secret_key(Some(ChainCode(cc.clone())), b"").0.expand() } +/// The raw secret seed, which can be used to recreate the `Pair`. #[cfg(feature = "std")] type Seed = [u8; MINI_SECRET_KEY_LENGTH]; diff --git a/substrate/core/service/src/components.rs b/substrate/core/service/src/components.rs index fe72e2c280..228bfcc0e6 100644 --- a/substrate/core/service/src/components.rs +++ b/substrate/core/service/src/components.rs @@ -21,7 +21,7 @@ use serde::{Serialize, de::DeserializeOwned}; use crate::chain_spec::ChainSpec; use client_db; use client::{self, Client, runtime_api}; -use crate::{error, Service}; +use crate::{error, Service, AuthorityKeyProvider}; use consensus_common::{import_queue::ImportQueue, SelectChain}; use network::{self, OnDemand, FinalityProofProvider}; use substrate_executor::{NativeExecutor, NativeExecutionDispatch}; @@ -191,7 +191,7 @@ fn maintain_transaction_pool( client: &Client, transaction_pool: &TransactionPool, ) -> error::Result<()> where - Block: BlockT::Out>, + Block: BlockT::Out>, Backend: client::backend::Backend, Client: ProvideRuntimeApi, as ProvideRuntimeApi>::Api: runtime_api::TaggedTransactionQueue, @@ -231,6 +231,7 @@ pub trait OffchainWorker { offchain: &offchain::OffchainWorkers< ComponentClient, ComponentOffchainStorage, + AuthorityKeyProvider, ComponentBlock >, pool: &Arc>, @@ -246,6 +247,7 @@ impl OffchainWorker for C where offchain: &offchain::OffchainWorkers< ComponentClient, ComponentOffchainStorage, + AuthorityKeyProvider, ComponentBlock >, pool: &Arc>, diff --git a/substrate/core/service/src/config.rs b/substrate/core/service/src/config.rs index 8769f1dac9..5db9b756a1 100644 --- a/substrate/core/service/src/config.rs +++ b/substrate/core/service/src/config.rs @@ -16,12 +16,14 @@ //! Service configuration. -use std::{path::PathBuf, net::SocketAddr}; -use transaction_pool; -use crate::chain_spec::ChainSpec; pub use client::ExecutionStrategies; pub use client_db::PruningMode; pub use network::config::{ExtTransport, NetworkConfiguration, Roles}; + +use std::{path::PathBuf, net::SocketAddr}; +use transaction_pool; +use crate::chain_spec::ChainSpec; +use primitives::crypto::Protected; use runtime_primitives::BuildStorage; use serde::{Serialize, de::DeserializeOwned}; use target_info::Target; @@ -86,7 +88,7 @@ pub struct Configuration { /// Disable GRANDPA when running in validator mode pub disable_grandpa: bool, /// Node keystore's password - pub password: String, + pub password: Protected, } impl Configuration { @@ -120,7 +122,7 @@ impl Configuration { client: Arc>, - select_chain: Option<::SelectChain>, + select_chain: Option, network: Arc>, /// Sinks to propagate network status updates. network_status_sinks: Arc>, NetworkState )>>>>, transaction_pool: Arc>, - keystore: Option, + keystore: AuthorityKeyProvider, exit: ::exit_future::Exit, signal: Option, /// Sender for futures that must be spawned as background tasks. @@ -101,6 +101,7 @@ pub struct Service { _offchain_workers: Option, ComponentOffchainStorage, + AuthorityKeyProvider, ComponentBlock> >>, } @@ -192,7 +193,7 @@ impl Service { public_key = match keystore.contents::()?.get(0) { Some(public_key) => public_key.to_string(), None => { - let key: ed25519::Pair = keystore.generate(&config.password)?; + let key: ed25519::Pair = keystore.generate(&config.password.as_ref())?; let public_key = key.public(); info!("Generated a new keypair: {:?}", public_key); public_key.to_string() @@ -259,6 +260,12 @@ impl Service { let network = network_mut.service().clone(); let network_status_sinks = Arc::new(Mutex::new(Vec::new())); + let keystore_authority_key = AuthorityKeyProvider { + roles: config.roles, + password: config.password.clone(), + keystore: keystore.map(Arc::new), + }; + #[allow(deprecated)] let offchain_storage = client.backend().offchain_storage(); let offchain_workers = match (config.offchain_worker, offchain_storage) { @@ -266,6 +273,8 @@ impl Service { Some(Arc::new(offchain::OffchainWorkers::new( client.clone(), db, + keystore_authority_key.clone(), + config.password.clone(), ))) }, (true, None) => { @@ -469,7 +478,7 @@ impl Service { to_spawn_tx, to_spawn_rx, to_poll: Vec::new(), - keystore, + keystore: keystore_authority_key, config, exit, rpc_handlers, @@ -481,23 +490,10 @@ impl Service { } /// give the authority key, if we are an authority and have a key - pub fn authority_key(&self) -> Option - where - TPair: Pair + TypedKey, - ::Public: Public + TypedKey, - { - if self.config.roles != Roles::AUTHORITY { return None } - if let Some(keystore) = &self.keystore { - if let Ok(Some(Ok(key))) = keystore.contents::().map(|keys| keys.get(0) - .map(|k| keystore.load::(k, &self.config.password))) - { - Some(key) - } else { - None - } - } else { - None - } + pub fn authority_key(&self) -> Option { + use offchain::AuthorityKeyProvider; + + self.keystore.authority_key() } /// return a shared instance of Telemetry (if enabled) @@ -870,6 +866,39 @@ impl network::TransactionPool, ComponentBlock< } } +/// A provider of current authority key. +#[derive(Clone)] +pub struct AuthorityKeyProvider { + roles: Roles, + keystore: Option>, + password: crypto::Protected, +} + +impl offchain::AuthorityKeyProvider for AuthorityKeyProvider { + fn authority_key(&self) -> Option { + if self.roles != Roles::AUTHORITY { + return None + } + + let keystore = match self.keystore { + Some(ref keystore) => keystore, + None => return None + }; + + let loaded_key = keystore + .contents() + .map(|keys| keys.get(0) + .map(|k| keystore.load(k, self.password.as_ref())) + ); + + if let Ok(Some(Ok(key))) = loaded_key { + Some(key) + } else { + None + } + } +} + /// Constructs a service factory with the given name that implements the `ServiceFactory` trait. /// The required parameters are required to be given in the exact order. Some parameters are followed /// by `{}` blocks. These blocks are required and used to initialize the given parameter. diff --git a/substrate/core/service/test/src/lib.rs b/substrate/core/service/test/src/lib.rs index 48b310db5f..543f98d7ad 100644 --- a/substrate/core/service/test/src/lib.rs +++ b/substrate/core/service/test/src/lib.rs @@ -194,7 +194,7 @@ fn node_config ( offchain_worker: false, force_authoring: false, disable_grandpa: false, - password: "".to_string(), + password: "".to_string().into(), } } diff --git a/substrate/core/sr-io/src/lib.rs b/substrate/core/sr-io/src/lib.rs index 47df468d20..6721cc7ecd 100644 --- a/substrate/core/sr-io/src/lib.rs +++ b/substrate/core/sr-io/src/lib.rs @@ -249,28 +249,33 @@ export_api! { /// If `key` is `None`, it will attempt to use current authority key. /// /// Returns an error if `key` is not available or does not exist. - fn encrypt(key: Option, data: &[u8]) -> Result, ()>; + fn encrypt(key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()>; /// Decrypt a piece of data using given crypto key. /// /// If `key` is `None`, it will attempt to use current authority key. /// /// Returns an error if data cannot be decrypted or the `key` is not available or does not exist. - fn decrypt(key: Option, data: &[u8]) -> Result, ()>; + fn decrypt(key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()>; /// Sign a piece of data using given crypto key. /// /// If `key` is `None`, it will attempt to use current authority key. /// /// Returns an error if `key` is not available or does not exist. - fn sign(key: Option, data: &[u8]) -> Result, ()>; + fn sign(key: Option, kind: CryptoKind, data: &[u8]) -> Result, ()>; /// Verifies that `signature` for `msg` matches given `key`. /// /// Returns an `Ok` with `true` in case it does, `false` in case it doesn't. /// Returns an error in case the key is not available or does not exist or the parameters /// lengths are incorrect. - fn verify(key: Option, msg: &[u8], signature: &[u8]) -> Result; + fn verify( + key: Option, + kind: CryptoKind, + msg: &[u8], + signature: &[u8] + ) -> Result; /// Returns current UNIX timestamp (in millis) fn timestamp() -> Timestamp; diff --git a/substrate/core/sr-io/with_std.rs b/substrate/core/sr-io/with_std.rs index aee2dc1bc8..25be27046a 100644 --- a/substrate/core/sr-io/with_std.rs +++ b/substrate/core/sr-io/with_std.rs @@ -275,27 +275,44 @@ impl OffchainApi for () { }, "new_crypto_key can be called only in the offchain worker context") } - fn encrypt(key: Option, data: &[u8]) -> Result, ()> { + fn encrypt( + key: Option, + kind: offchain::CryptoKind, + data: &[u8], + ) -> Result, ()> { with_offchain(|ext| { - ext.encrypt(key, data) + ext.encrypt(key, kind, data) }, "encrypt can be called only in the offchain worker context") } - fn decrypt(key: Option, data: &[u8]) -> Result, ()> { + fn decrypt( + key: Option, + kind: offchain::CryptoKind, + data: &[u8], + ) -> Result, ()> { with_offchain(|ext| { - ext.decrypt(key, data) + ext.decrypt(key, kind, data) }, "decrypt can be called only in the offchain worker context") } - fn sign(key: Option, data: &[u8]) -> Result, ()> { + fn sign( + key: Option, + kind: offchain::CryptoKind, + data: &[u8], + ) -> Result, ()> { with_offchain(|ext| { - ext.sign(key, data) + ext.sign(key, kind, data) }, "sign can be called only in the offchain worker context") } - fn verify(key: Option, msg: &[u8], signature: &[u8]) -> Result { + fn verify( + key: Option, + kind: offchain::CryptoKind, + msg: &[u8], + signature: &[u8], + ) -> Result { with_offchain(|ext| { - ext.verify(key, msg, signature) + ext.verify(key, kind, msg, signature) }, "verify can be called only in the offchain worker context") } diff --git a/substrate/core/sr-io/without_std.rs b/substrate/core/sr-io/without_std.rs index ad1b26b8c1..5565f9ab38 100644 --- a/substrate/core/sr-io/without_std.rs +++ b/substrate/core/sr-io/without_std.rs @@ -395,18 +395,24 @@ pub mod ext { /// Encrypt a piece of data using given crypto key. /// - /// If `key` is `0`, it will attempt to use current authority key. + /// If `key` is `0`, it will attempt to use current authority key of given `kind`. /// /// # Returns /// /// - `0` in case the key is invalid, `msg_len` is set to `u32::max_value` /// - Otherwise, pointer to the encrypted message in memory, /// `msg_len` contains the length of the message. - fn ext_encrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8; + fn ext_encrypt( + key: u32, + kind: u32, + data: *const u8, + data_len: u32, + msg_len: *mut u32 + ) -> *mut u8; /// Decrypt a piece of data using given crypto key. /// - /// If `key `is `0`, it will attempt to use current authority key. + /// If `key` is `0`, it will attempt to use current authority key of given `kind`. /// /// # Returns /// @@ -414,11 +420,17 @@ pub mod ext { /// `msg_len` is set to `u32::max_value` /// - Otherwise, pointer to the decrypted message in memory, /// `msg_len` contains the length of the message. - fn ext_decrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8; + fn ext_decrypt( + key: u32, + kind: u32, + data: *const u8, + data_len: u32, + msg_len: *mut u32 + ) -> *mut u8; /// Sign a piece of data using given crypto key. /// - /// If `key` is `0`, it will attempt to use current authority key. + /// If `key` is `0`, it will attempt to use current authority key of given `kind`. /// /// # Returns /// @@ -426,11 +438,17 @@ pub mod ext { /// `sig_data_len` is set to `u32::max_value` /// - Otherwise, pointer to the signature in memory, /// `sig_data_len` contains the length of the signature. - fn ext_sign(key: u32, data: *const u8, data_len: u32, sig_data_len: *mut u32) -> *mut u8; + fn ext_sign( + key: u32, + kind: u32, + data: *const u8, + data_len: u32, + sig_data_len: *mut u32 + ) -> *mut u8; /// Verifies that `signature` for `msg` matches given `key`. /// - /// If `key` is `0`, it will attempt to use current authority key. + /// If `key` is `0`, it will attempt to use current authority key of given `kind`. /// /// # Returns /// - `0` in case the signature is correct @@ -438,6 +456,7 @@ pub mod ext { /// - `u32::max_value` if the key is invalid. fn ext_verify( key: u32, + kind: u32, msg: *const u8, msg_len: u32, signature: *const u8, @@ -870,7 +889,7 @@ impl OffchainApi for () { } fn new_crypto_key(crypto: offchain::CryptoKind) -> Result { - let crypto = crypto as u8 as u32; + let crypto = crypto.into(); let ret = unsafe { ext_new_crypto_key.get()(crypto) }; @@ -882,41 +901,63 @@ impl OffchainApi for () { } } - fn encrypt(key: Option, data: &[u8]) -> Result, ()> { - let key = key.map(|x| x.0 as u32).unwrap_or(0); + fn encrypt( + key: Option, + kind: offchain::CryptoKind, + data: &[u8], + ) -> Result, ()> { + let key = key.map(Into::into).unwrap_or(0); + let kind = kind.into(); let mut len = 0_u32; unsafe { - let ptr = ext_encrypt.get()(key, data.as_ptr(), data.len() as u32, &mut len); + let ptr = ext_encrypt.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len); from_raw_parts(ptr, len).ok_or(()) } } - fn decrypt(key: Option, data: &[u8]) -> Result, ()> { - let key = key.map(|x| x.0 as u32).unwrap_or(0); + fn decrypt( + key: Option, + kind: offchain::CryptoKind, + data: &[u8], + ) -> Result, ()> { + let key = key.map(Into::into).unwrap_or(0); + let kind = kind.into(); let mut len = 0_u32; unsafe { - let ptr = ext_decrypt.get()(key, data.as_ptr(), data.len() as u32, &mut len); + let ptr = ext_decrypt.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len); from_raw_parts(ptr, len).ok_or(()) } } - fn sign(key: Option, data: &[u8]) -> Result, ()> { - let key = key.map(|x| x.0 as u32).unwrap_or(0); + fn sign( + key: Option, + kind: offchain::CryptoKind, + data: &[u8], + ) -> Result, ()> { + let key = key.map(Into::into).unwrap_or(0); + let kind = kind.into(); let mut len = 0_u32; unsafe { - let ptr = ext_sign.get()(key, data.as_ptr(), data.len() as u32, &mut len); + let ptr = ext_sign.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len); from_raw_parts(ptr, len).ok_or(()) } } - fn verify(key: Option, msg: &[u8], signature: &[u8]) -> Result { - let key = key.map(|x| x.0 as u32).unwrap_or(0); + fn verify( + key: Option, + kind: offchain::CryptoKind, + msg: &[u8], + signature: &[u8], + ) -> Result { + let key = key.map(Into::into).unwrap_or(0); + let kind = kind.into(); let val = unsafe { ext_verify.get()( key, + kind, msg.as_ptr(), msg.len() as u32, signature.as_ptr(), @@ -954,7 +995,7 @@ impl OffchainApi for () { fn local_storage_set(kind: offchain::StorageKind, key: &[u8], value: &[u8]) { unsafe { ext_local_storage_set.get()( - kind as u8 as u32, + kind.into(), key.as_ptr(), key.len() as u32, value.as_ptr(), @@ -966,7 +1007,7 @@ impl OffchainApi for () { fn local_storage_compare_and_set(kind: offchain::StorageKind, key: &[u8], old_value: &[u8], new_value: &[u8]) -> bool { unsafe { ext_local_storage_compare_and_set.get()( - kind as u8 as u32, + kind.into(), key.as_ptr(), key.len() as u32, old_value.as_ptr(), @@ -981,7 +1022,7 @@ impl OffchainApi for () { let mut len = 0u32; unsafe { let ptr = ext_local_storage_get.get()( - kind as u8 as u32, + kind.into(), key.as_ptr(), key.len() as u32, &mut len, @@ -1019,7 +1060,7 @@ impl OffchainApi for () { let result = unsafe { ext_http_request_add_header.get()( - request_id.0 as u32, + request_id.into(), name.as_ptr(), name.len() as u32, value.as_ptr(), @@ -1041,7 +1082,7 @@ impl OffchainApi for () { ) -> Result<(), offchain::HttpError> { let res = unsafe { ext_http_request_write_body.get()( - request_id.0 as u32, + request_id.into(), chunk.as_ptr(), chunk.len() as u32, deadline.map_or(0, |x| x.unix_millis()), @@ -1084,7 +1125,7 @@ impl OffchainApi for () { let mut len = 0u32; let raw_result = unsafe { let ptr = ext_http_response_headers.get()( - request_id.0 as u32, + request_id.into(), &mut len, ); @@ -1101,7 +1142,7 @@ impl OffchainApi for () { ) -> Result { let res = unsafe { ext_http_response_read_body.get()( - request_id.0 as u32, + request_id.into(), buffer.as_mut_ptr(), buffer.len() as u32, deadline.map_or(0, |x| x.unix_millis()), diff --git a/substrate/core/state-machine/src/lib.rs b/substrate/core/state-machine/src/lib.rs index 7cff304675..f151fedaf5 100644 --- a/substrate/core/state-machine/src/lib.rs +++ b/substrate/core/state-machine/src/lib.rs @@ -250,6 +250,7 @@ impl offchain::Externalities for NeverOffchainExt { fn encrypt( &mut self, _key: Option, + _kind: offchain::CryptoKind, _data: &[u8], ) -> Result, ()> { unreachable!() @@ -258,18 +259,25 @@ impl offchain::Externalities for NeverOffchainExt { fn decrypt( &mut self, _key: Option, + _kind: offchain::CryptoKind, _data: &[u8], ) -> Result, ()> { unreachable!() } - fn sign(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn sign( + &mut self, + _key: Option, + _kind: offchain::CryptoKind, + _data: &[u8], + ) -> Result, ()> { unreachable!() } fn verify( &mut self, _key: Option, + _kind: offchain::CryptoKind, _msg: &[u8], _signature: &[u8], ) -> Result {