-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Encryption support for the statement store #14440
Changes from 3 commits
553b32f
d0e0059
08e220f
26fc242
eecec32
72c16bf
b032859
57c8ea6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -196,6 +196,21 @@ impl Keystore for LocalKeystore { | |
self.sign::<ed25519::Pair>(key_type, public, msg) | ||
} | ||
|
||
fn with_ed25519_key( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already provide |
||
&self, | ||
key_type: KeyTypeId, | ||
public: &ed25519::Public, | ||
f: &mut dyn FnMut(&ed25519::Pair), | ||
) -> std::result::Result<bool, TraitError> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember doing a similar function in a branch. One possible issue is that this is maybe not appropriate for a future hardware keystore, so a specialized function may be more suitable, but at this point I don't know if it is worth doing (given this would mean putting eceis in keystore and I think it was a point that was not suitable from previous PR). Since it is explicitely a Statement store key that would not be possible to run on HSM, it may not be such an issue. |
||
let pair = self.0.read().key_pair_by_type::<ed25519::Pair>(public, key_type)?; | ||
Ok(if let Some(pair) = pair { | ||
f(&pair); | ||
true | ||
} else { | ||
false | ||
}) | ||
} | ||
|
||
fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa::Public> { | ||
self.public_keys::<ecdsa::Pair>(key_type) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,7 +56,8 @@ use parking_lot::RwLock; | |
use prometheus_endpoint::Registry as PrometheusRegistry; | ||
use sp_api::ProvideRuntimeApi; | ||
use sp_blockchain::HeaderBackend; | ||
use sp_core::{hexdisplay::HexDisplay, traits::SpawnNamed, Decode, Encode}; | ||
use sp_core::{crypto::UncheckedFrom, hexdisplay::HexDisplay, traits::SpawnNamed, Decode, Encode}; | ||
use sp_keystore::Keystore; | ||
use sp_runtime::traits::Block as BlockT; | ||
use sp_statement_store::{ | ||
runtime_api::{InvalidStatement, StatementSource, ValidStatement, ValidateStatement}, | ||
|
@@ -199,6 +200,7 @@ pub struct Store { | |
+ Send | ||
+ Sync, | ||
>, | ||
keystore: Arc<dyn Keystore>, | ||
// Used for testing | ||
time_override: Option<u64>, | ||
metrics: PrometheusMetrics, | ||
|
@@ -477,6 +479,7 @@ impl Store { | |
path: &std::path::Path, | ||
options: Options, | ||
client: Arc<Client>, | ||
keystore: Arc<dyn Keystore>, | ||
prometheus: Option<&PrometheusRegistry>, | ||
task_spawner: &dyn SpawnNamed, | ||
) -> Result<Arc<Store>> | ||
|
@@ -491,7 +494,7 @@ impl Store { | |
+ 'static, | ||
Client::Api: ValidateStatement<Block>, | ||
{ | ||
let store = Arc::new(Self::new(path, options, client.clone(), prometheus)?); | ||
let store = Arc::new(Self::new(path, options, client.clone(), keystore, prometheus)?); | ||
client.execution_extensions().register_statement_store(store.clone()); | ||
|
||
// Perform periodic statement store maintenance | ||
|
@@ -517,6 +520,7 @@ impl Store { | |
path: &std::path::Path, | ||
options: Options, | ||
client: Arc<Client>, | ||
keystore: Arc<dyn Keystore>, | ||
prometheus: Option<&PrometheusRegistry>, | ||
) -> Result<Store> | ||
where | ||
|
@@ -565,6 +569,7 @@ impl Store { | |
db, | ||
index: RwLock::new(Index::new(options)), | ||
validate_fn, | ||
keystore, | ||
time_override: None, | ||
metrics: PrometheusMetrics::new(prometheus), | ||
}; | ||
|
@@ -762,7 +767,35 @@ impl StatementStore for Store { | |
/// Return the decrypted data of all known statements whose decryption key is identified as | ||
/// `dest`. The key must be available to the client. | ||
fn posted_clear(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result<Vec<Vec<u8>>> { | ||
self.collect_statements(Some(dest), match_all_topics, |statement| statement.into_data()) | ||
self.collect_statements(Some(dest), match_all_topics, |statement| { | ||
if let (Some(key), Some(_)) = (statement.decryption_key(), statement.data()) { | ||
let public = UncheckedFrom::unchecked_from(key); | ||
let mut out = None; | ||
if let Err(e) = self.keystore.with_ed25519_key( | ||
sp_core::crypto::key_types::STATEMENT, | ||
&public, | ||
&mut |pair| match statement.decrypt_private(pair) { | ||
Ok(r) => out = r, | ||
Err(e) => log::debug!( | ||
target: LOG_TARGET, | ||
"Decryption error: {:?}, for statement {:?}", | ||
e, | ||
HexDisplay::from(&statement.hash()) | ||
), | ||
}, | ||
) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can have with_ed25519_key use a function that return an error as param, and potentially have posted clear report this in its list for results. |
||
log::debug!( | ||
target: LOG_TARGET, | ||
"Keystore error error: {:?}, for statement {:?}", | ||
e, | ||
HexDisplay::from(&statement.hash()) | ||
) | ||
} | ||
out | ||
} else { | ||
None | ||
} | ||
}) | ||
} | ||
|
||
/// Submit a statement to the store. Validates the statement and returns validation result. | ||
|
@@ -910,6 +943,7 @@ mod tests { | |
RuntimeApi { _inner: self.clone() }.into() | ||
} | ||
} | ||
|
||
sp_api::mock_impl_runtime_apis! { | ||
impl ValidateStatement<Block> for RuntimeApi { | ||
fn validate_statement( | ||
|
@@ -977,7 +1011,8 @@ mod tests { | |
let client = std::sync::Arc::new(TestClient); | ||
let mut path: std::path::PathBuf = temp_dir.path().into(); | ||
path.push("db"); | ||
let store = Store::new(&path, Default::default(), client, None).unwrap(); | ||
let keystore = std::sync::Arc::new(sp_keystore::testing::MemoryKeystore::new()); | ||
let store = Store::new(&path, Default::default(), client, keystore, None).unwrap(); | ||
(store, temp_dir) // return order is important. Store must be dropped before TempDir | ||
} | ||
|
||
|
@@ -1080,12 +1115,13 @@ mod tests { | |
assert_eq!(store.statements().unwrap().len(), 3); | ||
assert_eq!(store.broadcasts(&[]).unwrap().len(), 3); | ||
assert_eq!(store.statement(&statement1.hash()).unwrap(), Some(statement1.clone())); | ||
let keystore = store.keystore.clone(); | ||
drop(store); | ||
|
||
let client = std::sync::Arc::new(TestClient); | ||
let mut path: std::path::PathBuf = temp.path().into(); | ||
path.push("db"); | ||
let store = Store::new(&path, Default::default(), client, None).unwrap(); | ||
let store = Store::new(&path, Default::default(), client, keystore, None).unwrap(); | ||
assert_eq!(store.statements().unwrap().len(), 3); | ||
assert_eq!(store.broadcasts(&[]).unwrap().len(), 3); | ||
assert_eq!(store.statement(&statement1.hash()).unwrap(), Some(statement1)); | ||
|
@@ -1190,7 +1226,6 @@ mod tests { | |
statement(2, 4, None, 1000).hash(), | ||
statement(3, 4, Some(3), 300).hash(), | ||
statement(3, 5, None, 500).hash(), | ||
//statement(4, 6, None, 100).hash(), | ||
]; | ||
expected_statements.sort(); | ||
let mut statements: Vec<_> = | ||
|
@@ -1214,13 +1249,31 @@ mod tests { | |
store.set_time(DEFAULT_PURGE_AFTER_SEC + 1); | ||
store.maintain(); | ||
assert_eq!(store.index.read().expired.len(), 0); | ||
let keystore = store.keystore.clone(); | ||
drop(store); | ||
|
||
let client = std::sync::Arc::new(TestClient); | ||
let mut path: std::path::PathBuf = temp.path().into(); | ||
path.push("db"); | ||
let store = Store::new(&path, Default::default(), client, None).unwrap(); | ||
let store = Store::new(&path, Default::default(), client, keystore, None).unwrap(); | ||
assert_eq!(store.statements().unwrap().len(), 0); | ||
assert_eq!(store.index.read().expired.len(), 0); | ||
} | ||
|
||
#[test] | ||
fn posted_clear_decrypts() { | ||
let (store, _temp) = test_store(); | ||
let public = store | ||
.keystore | ||
.ed25519_generate_new(sp_core::crypto::key_types::STATEMENT, None) | ||
.unwrap(); | ||
let statement1 = statement(1, 1, None, 100); | ||
let mut statement2 = statement(1, 2, None, 0); | ||
let plain = b"The most valuable secret".to_vec(); | ||
statement2.encrypt(&plain, &public).unwrap(); | ||
store.submit(statement1, StatementSource::Network); | ||
store.submit(statement2, StatementSource::Network); | ||
let posted_clear = store.posted_clear(&[], public.into()).unwrap(); | ||
assert_eq!(posted_clear, vec![plain]); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.