owned this note
owned this note
Published
Linked with GitHub
# DeepKey HDK API
The DeepKey app will need some crypto support. This is an investigation into what HDK support we would need in order to write the Zome code for the DeepKey App.
## Conductor KeyStore
We cannot pass secrets across the HDK barrier. They will no longer be memory protected when in WASM. We need a method for referencing secrets inside zome code without exposing those secrets.
Keystore entries will be encrypted / decrypted via a passphrase (the same passphrase as conductor is run with?) Passphrase should time-out... that is, the following apis should require user input of the passphrase for the first one, then NOT require entry for a specified time, after which any further requests should again require passphrase entry.
> [name=Arthur Brock] We need a secure way to write private keys into a multiple key keystore. At the moment, they're using separate key files for each key which makes starting the conductor very painful (type the password for every keyfile).
>
## Initialization
### Keystore Level Initialization:
All DNA keystores are initialized with their agent private keypair using the id_string: the same as AGENT_ID (i.e. the base32 public key)
Special Case: DeepKey app also has a seed that it can use for generating future DNA keypairs identified with a well known string: "device_seed"
### Conductor level initialization:
1. HC Admin sends Conductor:
- List of core apps to install
- The device seed for generating key pairs (encrypted)
- (in the case of second device initialization, the DeepKey Root ID (not the seed) which identifies the keyset)
2. Conductor initializes DeepKey with the seed and it's keypair, if this is device zero, gets back the DeepKey Root ID which it returns to HC Admin
3. [Conductor asks DeepKey to generate agent keys for other apps, which publishes the public part to the DHT (along with the DeepKey Root ID) and returns.]
3. or Conductor generates the keypair and passes the public key into DeepKey for authorizing & publishing
5. Conductor initializes the remaining apps.
## Persistence
## API functions
### keystore_list
`keystore_list () -> [ id_strs ... ]`
List the ids of all secrets stored in this keystore for this app.
### keystore_new_random (`add_random_seed`)
`keystore_new_random ( id_str, usize )`
Generate a new cryptographically secure random number of bytes suitable for seeds. If id_str is already in the store, return an error.
### keystore_derive (`add_seed_from_seed`)
`keystore_derive ( src_id_str, dest_id_str, context, index )`
Apply a seed derivation, store the results in at a new id_str.
## Signature Functions
### sign_seed_to_keypair (`add_key_from_seed`)
`sign_seed_to_keypair ( seed_id_str, priv_id_str ) -> base32pubkey`
Use 32 byte seed stored at `seed_id_str` to generate a signing keypair. Store the private key at `priv_id_str`, return the base32 encoded public key.
### sign
`sign ( priv_id_str, base64payload ) -> ( base64signature )`
Generate the signature with the privkey stored in the keystore at `priv_id_str`.
### sign_verify
returns true if signature matches pubkey for given payload.
### sign_one_time
`sign_one_time ( Vec<base64payloads> ) -> ( base32pubkey, Vec<base64signatures> )`
This call will generate a pure entropy signing key pair. sign the given data, shred / forget the private key, and send back the base32 encoded pubkey id and signature data.
## Example Usage
On first run of a newly installed conductor, the "installation" workflow would cause a bunch of things to happen, including generation of the initial set of keys and entries to publish on the DeepKey DHT.
This example shows what might be needed for an already running Conductor that wants to install a new app, but has no additional pre-generated app keypairs, so needs to sign new ones into the DeepKey keyset.
```rust
// say we have 11 other apps,
// first we need a new application seed for index 12.
keystore_derive("device:0:pin:app:root", "device:0:pin:app:seed:12", "HCAPPLIC", 12);
// now turn that seed into an application keypair
let agentId = sign_seed_to_keypair("device:0:pin:app:seed:12", "device:0:pin:app:priv:12");
// now we know the agentId of the new app pair, generate a key entry for the dpki app
let new_entry = /* generate new "add key" entry with agentId */;
// we need to sign this addition with our AUTH privkey... this is a special key that lets us add new pubkeys to our DeepKey keyset.
sign("device:0:pin:auth:priv", new_entry);
// now we can commit this to the DeepKey DHT, and inform the conductor about the availability of a new agentId for the new install.
```
## HDK vs Conductor Websocket API
Another options would be to make the keystore functions available over the conductor websocket api, exposing them to "ui"s (or things like the interCeptr). The "ui" would send the passphrase over the wire, and gain access to e.g. making signatures that could be passed into zome functions by inclusion as parameters in zome functions.
In the interest of maximizing utility, I think we need to go the HDK route. Two particular use-cases spring to mind:
- "Receive" callback - if an app would like to do custom signature / key management within the "Receive" side of a send/receive call, we cannot contact the ui for a signature, this needs to happen inline in the zome code.
- bridging - again, if an app wants to expose custom cryptography logic via bridging, it will need to have access within the zome code.
This could, of course, be worked around with multiple entry types and asynchronous behavior that requires user interaction with a ui in the middle... But why would we choose to require this limitation when going the HDK route allows both options to app developers?
This route also allows finer grained access control through the capabilites on the zome functions.
As an implementation aside, I can't think of *any* reason we would expose keys created by other app dna code. We may not even want to expose the agent keys for *this* app, since we'll have `hdk::sign` available for that. We need these keys to be secured by a single conductor-level passphrase, but beyond that they should be entirely sandboxed by dna hash.