Distributed Public Key Infrastructure (DPKI)¶
Like most other distributed systems, Holochain fundamentally relies on public key cryptography. Among other uses, Holochain nodes are identified by their public keys, and thus provenance of messages from nodes can be checked simply by checking a message signature against the node's identifier. This fundamental use requires nodes to keep the private key secret, lest the node's very agency be compromised. Keeping such secrets in the digital age is a non-trivial problem. Additionally, distributed systems imply an abundance of nodes with public keys, many of which may wish to be identified as being under the control of a single actor. Solutions to this need create yet another layer of keys which sign other keys, and the management of all this can become quite complex.
Addressing these needs is the function of Public Key Infrastructure, and in our case, because we use the power of Holochain itself to do this, a Distributed Public Key Infrastructure: DPKI.
DPKI needs to fulfill at least the following design requirements:
- Provide a way to create new keys for nodes.
- Provide a way to revoke compromised keys and re-issue keys for a node.
- Provide a way to verify the provenance of keys by grouping them as originating from a single actor.
- Securely manage the private keys.
- Reliably distribute and make available information about public keys.
In designing a solution for DPKI we recognize that this is a complex and difficult enough problem that any solution will need to evolve, and in fact there will be multiple solutions necessary for different contexts. Thus, we have built into the Holochain conductor a simple interface for the fundamental needed functions, e.g. creating new keys when installing a DNA for the first time, that can then be implemented by specialized DPKI applications. Furthermore we've implemented a reference implementation of a Holochain based DPKI application, which we call DeepKey.
To deliver on the basics of Distributed Public Key Infrastructure, we need a way to generate keys of various types (revocation, identity, encryption, signing) from seeds, and we need to be able to generate such seeds from primary seeds, so that a human agent can create related "device agents" provably under their control.
After studying a number of uses cases, including initial sign-up, key revocation, etc, the central insight we came to was the need to create a Hierarchical Deterministic Key generation system, based on a Primary Seed, from which additional seeds can be generated which then are in turn used to actually generate many key-pairs. This allows us, by-convention, to use the first seed generated by the Primary seed as the seed for revocation keys, and subsequent seeds as seeds for keys of separate Holochain devices that can be proven to be under the control of the holder of Primary Seed.
TODO: merge the various docs we developed to explain DeepKey here. - https://medium.com/holochain/part-2-holochain-holo-accounts-cryptographic-key-management-and-deepkey-bf32ee91af65 - https://hackmd.io/UbfvwQdJRKaAHI9Xa7F3VA?view - https://hackmd.io/8c8rZCyaTTqH_7TIBVtEUQ - https://hackmd.io/oobu0sKMSMadLXza4rHY_g
DPKI Implementation Technical Details¶
For each Holochain DNA instance, the Conductor maintains a Keystore, which holds "secrets" (seeds and keys) needed for cryptographic signing and encrypting. Each of the secrets in the Keystore is associated with a string which is a handle needed when using that secret for some cryptographic operation. Our cryptographic implementation is based on libsodium, and the seeds use their notions of context and index for key derivation paths. This implementation allows DNA developers to securely call cryptographic functions from WASM which will be executed in the conductor's secure memory space when actually doing the cryptographic processing.
Decrypting a secret from the keystore invokes a passphrase manager service, which is used to collect the passphrase from some end-user. This service is implementation specific. Currently we have implementations for command-line passphrase collection for use in the
hc keygen command, and also a command-line implementation within the conductor.
If the Conductor config does not include a DPKI section, then the conductor assumes it's in standalone mode and takes responsibility for adding generating new agent secrets when it receives
admin/add_agent requests through the admin interface. This mode is also used by the
hc command-line tool.
If the Conductor config specifies a DPKI instance, then the conductor will initially bootstrap the DPKI instance, and also delegate any
admin/add_agent requests to the DPKI app for processing. Note that the Conductor does assume that basic agent key will be created by the DPKI app so that it can actually create the agent keystore file on behalf of the DPKI app.
The Holochain conductor expects the following exposed functions to exist in any DPKI application as a trait so that it can call them at various times to manage keys and identity.
init(params): Called during bootstrap with initialization parameters retrieved from the DPKI configuration. This function is only called if a prior call to
is_initialized()-> bool : Should return a boolean value if the DPKI DNA has been initialized or not
create_agent_key(agent_name): Called any time the conductor creates a new DNA instance. Should create a keystore record for the instance.
TODO: add more functions for the trait.