Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

SecretStore: generating and retrieving decryption keys via service contract #8029

Merged
merged 60 commits into from
Apr 3, 2018

Conversation

svyatonik
Copy link
Collaborator

@svyatonik svyatonik commented Mar 1, 2018

on top of #7887
Updated contract(s) PR: https://github.com/paritytech/contracts/pull/108

New service contract APIs

Server Key generation API has been changed

This is what already was a part of SecretStoreService.
But I've changed how it works to support wider range of usage cases:

  1. server key generation request is added via generateServerKey(key_id, threshold) method
  2. server key generation is reported via ServerKeyGenerated(key_id, key_public) event
  3. (new) server key generation can now fail if key has been already generated and error will be reported via ServerKeyGenerationError(key_id) event (previousy we were returning previously generated server key if generation of the same key has been requested)
  4. (new) every key server must confirm key generation (previously we required threshold+1 confirmations)
  5. (new) author of generation request is now important (you can only store document key for this server key by sending tx from the same address)

Server Key retrieval API has been splitted from Server key generation API

Retrieval was possible previously just by calling generation with the same key id. Now it is a separate API:

  1. retrieval request is added via retrieveServerKey(key_id) method
  2. retrieval is reported via ServerKeyRetrieved(key_id, key_public) event
  3. retrieval error is reported via ServerKeyRetrievalError(key_id) event
  4. since we do not know key threshold in service contract AND we do not want to blindly trust any key server that is reporting possibly-fake key_public:
    4.1) we're waiting for same-threshold confirmations from 50%+1 key servers
    4.2) once we know the threshold, we're waiting for threshold+1 confirmations of the same key_public

Document Key store API has been added

After server key is generated, the author of server key entry (the one who has sent generateServerKey transaction), can generate document key shadow using secretstore_generateDocumentKey RPC (see #7864). After generating, he can use service contract API to store generated key in SS:

  1. store request is added via storeDocumentKey(key_id, common_point, encrypted_point) method
  2. store is confirmed via DocumentKeyStored(key_id) event
  3. store error is reported via DocumentKeyStoreError(key_id) event
  4. we're waiting for store confirmations from all 100% key servers

Document Key shadow retrieval API has been added

  1. retrieval request is added via retrieveDocumentKeyShadow(key_id, requester_public) method (tx author must be the owner of requester_public key)
  2. retrieval is reported in two steps:
    2.1) we're waiting for same threshold and comon_point from 50%+1 key servers. Once there are enough confirmations these values are reported via DocumentKeyCommonRetrieved(key_id, requester_address, common_point, threshold)
    2.2) once requester sees DocumentKeyCommonRetrieved event, it must start waiting for threshold+1 DocumentKeyPersonalRetrieved(key_id, requester_address, decrypted_secret, shadow). After all data is collected, he can use secretstore_shadowDecrypt method to decrypt document
  3. at any time DocumentKeyShadowRetrievalError(key_id, requester_address) can be raised, signalling that retrieval has failed

Other changes to service contract

  1. there are currently several service contracts, instead of one that has been there previously. The reason is that even uncompleted all-in-one service contract takes >4mln gas to deploy. So there are 1 option for configuring every API described above (service_contract_srv_gen, service_contract_srv_retr, service_contract_doc_store, service_contract_doc_sretr) and also 'backup' option for all-in-one contract (service_contract)
  2. extracted important portions of every contract in an interface so that every contract now have 3 kinds of public methods/events: for 'clients' of these contract (ClientApi interfaces), for key servers (KeyServerApi) and administrative methods (they're here for test period and possbily be changed/removed later).
  3. service contracts are now tightly connected to KeyServerSet contracts (i.e. it is incorrect to use config-file key server set along with service contract)
  4. to make gas cost of calling ServiceContract/KeyServerSet methods limited to some finite value, we now support only 256 key servers at most (when configured via contracts)
  5. to be able to deal with service contract changes, single important value is added as a characteristic of KeyServerSet' current set - the number of block when this set has been changed last. When response cames from KeyServer and this number differs from values of previous responses, all responses are reset && KeyServers mut resend these. Also every request in service contract is considered as non-responded request to make KeyServers able to retry it in next Retry interval.

Important TODOs left

  1. all errors that can occur in SecretStore should be splitted in two sets - internal errors (for example - db error, connectivity error, ...) and external errors (access denied, no such key, ...). Error must be reprted to contract only if it is an external error. If it is an internal error - just try to process request later.

@svyatonik svyatonik added A0-pleasereview 🤓 Pull request needs code review. and removed A3-inprogress ⏳ Pull request is in progress. No review needed at this stage. labels Mar 12, 2018
@svyatonik svyatonik changed the title SecretStore: generating and retrieving decryption keys via service contract [WIP] SecretStore: generating and retrieving decryption keys via service contract Mar 12, 2018
@svyatonik
Copy link
Collaborator Author

Found some issue with shutdown => marking as gotissues

@svyatonik svyatonik added A4-gotissues 💥 Pull request is reviewed and has significant issues which must be addressed. and removed A0-pleasereview 🤓 Pull request needs code review. labels Mar 12, 2018
@svyatonik
Copy link
Collaborator Author

SS-related issue is fixed in previous commit, but there's still some issue, which results in failed to join thread: Resource deadlock avoided (os error 35) error. It seems related to io worker threads - I'll try to investigate this later.

@svyatonik svyatonik added A0-pleasereview 🤓 Pull request needs code review. and removed A4-gotissues 💥 Pull request is reviewed and has significant issues which must be addressed. labels Mar 12, 2018
Copy link
Contributor

@folsen folsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have only reviewed this for style/readability and that I can understand roughly what's going on. Have not reviewed any of the logic in-depth. But at this point I think that's the best anyone can do since I don't think anyone understands this system in depth except the author ^^

I will be digging into this code more to try to understand it at this level but I will need some time and I'm fine with merging this in the meantime since it's behind a compiler flag anyway.

Box::new(request_logs.into_iter()
.filter_map(|log| {
let raw_log: RawLog = (log.entry.topics.into_iter().map(|t| t.0.into()).collect(), log.entry.data).into();
if raw_log.topics[0] == *SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a match statement for better readability?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure :) These are not actually a constant expressions, but Deref calls (it is lazy_static 'consts'). I could replace with match topics[0] { x if x == *SERVER_KEY... }, but that's definitely not better :)

// ignore result - the only thing that we can do is to log the error
let session_id = session.id();
let server_key_id = session_id.id;
if let Some(requester) = session.requester().and_then(|r| r.address(&server_key_id).ok()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you concatenate all the 3 lines below into 1 if let statement? Since they don't depend on the previous value. So like if let (Some(x), Some(y)) = (session.requester()[..], session.origin()) ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged two lines into 1. Not sure this improves readability, though :) 3rd line has side effects => do not want to depend on evaluation order => left separate if let

@@ -1405,7 +1405,7 @@ pub mod tests {
// run session to completion
let session_id = SessionId::default();
let session = clusters[0].client().new_generation_session(session_id, Default::default(), Default::default(), threshold).unwrap();
loop_until(&mut core, time::Duration::from_millis(1000), || session.joint_public_and_secret().is_some());
loop_until(&mut core, Duration::from_millis(1000), || session.joint_public_and_secret().is_some());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to const?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done :)

@5chdn 5chdn added A8-looksgood 🦄 Pull request is reviewed well. and removed A0-pleasereview 🤓 Pull request needs code review. labels Apr 3, 2018
@5chdn 5chdn merged commit ec96091 into master Apr 3, 2018
@5chdn 5chdn deleted the secretstore_doc_key_via_contract branch April 3, 2018 14:54
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A8-looksgood 🦄 Pull request is reviewed well. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants