UNPKG

ssb-keyring

Version:

A persistence store for encryption keys for scuttlebutt. It's purpose is to make easy to answer box2 encryption/ decryption questions.

245 lines (160 loc) 9.13 kB
# ssb-keyring A persistence store for encryption keys for scuttlebutt. It's purpose is to make easy to answer box2 encryption/ decryption questions. _This module was extracted from `ssb-tribes`_ ## Usage ```js const Keyring = require('ssb-keyring') const ssbKeys = require('ssb-keys') const keys = ssbKeys.generate() Keyring(`/tmp/keyring-demo-${Date.now()}`, (err, keyring) => { keyring.signing.add(keys.id, keys) // ... }) ``` ## API This module is responsible for recording key details: - persisting encryption keys (async) - persisting various signing keys (async) - persisting who has access to keys (async) Overview (methods most people should use): ```js /* SETUP */ Keyring(dbPath, cb) /* REGISTERING */ keyring.signing.add(keys.id, keys) keyring.dm.add(myLeafFeedId, theirLeafFeedId, myDHKeys, theirDHKeys, cb) keyring.group.add(groupId, keyInfo, cb) /* QUERYING */ keyring.signing.get(keys.id) keyring.dm.get(myLeafFeedId, theirLeafFeedId) keyring.group.get(groupId) keys.close(cb) ``` All querying methods are synchronous, so that encryption / decryption never has to wait for IO to access keys. All registering methods: - synchronously return a boolean indicating whether the key is new - all querying is done on an in-memory cache, which new items are added to - allow a callback argument at the end if you want to know when the keyring database persisted the key - the only asynchronous aspect is for persistence ### `Keyring(path, cb)` where - `path` *String* to location your keys will be persisted to on disk - `cb` *function* calls back with `(err, keyring)` (where `keyring` is the keyring API) - if `cb` not provided, function returns a Promise --- ### `keyring.signing.add(ssbKeys, cb) => Boolean` Takes some signing keys `ssbKeys` (made by `ssb-keys` or similar), and adds them as keys you can use for signing messages or other content. - `ssbKeys` *Object* containing ed25519 keys - `ssbKeys.id` *String* base64 encoded "ID" resembling the public part - `ssbKeys.curve` *String* name of the cryptographic elliptic curve used - `ssbKeys.public` *String* base64 encoded public part of the keypair - `ssbKeys.private` *String* base64 encoded private part of the keypair - `cb` *function* callback with signature `(err) => {}`, called after persistence Returns `true` if this `ssbKeys` is new (not yet in the database), or returns `false` if it was already in the database. ### `keyring.signing.addNamed(name, ssbKeys, cb) => Boolean` Similar to `keyring.signing.add`, but uses a free-form `name` string to identify the keys. This `name` can be used in `keyring.signing.get(name)` to retrieve the keys. ### `keyring.signing.has(id) => Boolean` Returns `true` if the keyring has a signing key for `id`, and `false` otherwise. ### `keyring.signing.get(id) => Object | null` Returns the signing keys for `id`, or `null` if the keyring doesn't have any. Alternatively, you can pass a `name` instead of `id` if you used `keyring.signing.addNamed`. --- ### `keyring.dm.add(myId, theirId, myDHKeys, theirDHKeys, cb) => Boolean` Takes a pair of Diffie-Hellman keys `myDHKeys` and `theirDHKeys` (made with `ssb-private-group-keys`), and their identifiers `myId` and `theirId`, then performs Diffie-Hellman key exchange to derive a shared secret, and adds it to the keyring. - `myId` *String* identifier for the local peer, usually a feed SSB URI - `theirId` *String* identifier for the remote peer, usually a feed SSB URI - `myDHKeys` *DHKeys* class instance (from `ssb-private-group-keys`) - `theirDHKeys` *DHKeys* class instance (from `ssb-private-group-keys`) - `cb` *function* callback with signature `(err) => {}`, called after persistence Returns `true` if the shared secret is new (not yet in the database), or returns `false` if it was already in the database. ### `keyring.dm.has(myId, theirId) => Boolean` Returns `true` if the keyring has a shared secret for the pair of `myId` and `theirId`, and `false` otherwise. ### `keyring.dm.get(myId, theirId) => Object | null` Returns the shared secret for the Diffie-Hellman key exchange between `myId` and `theirId`, or `null` if the keyring doesn't have any. ### `keyring.dm.addTriangle(xRootId, xLeafId, yLeafId, cb) => Boolean` Takes the identifiers for a "triangle" of feed IDs in two metafeed trees, and registers these in the keyring database. - `xRootId` *String* identifier for the root feed of the first metafeed tree - `xLeafId` *String* identifier for the leaf feed of the first metafeed tree - `yLeafId` *String* identifier for the leaf feed of the second metafeed tree - `cb` *function* callback with signature `(err) => {}`, called after persistence Returns `true` if the triangle is new (not yet in the database), or returns `false` if it was already in the database. ### `keyring.dm.triangulate(xRootId, yLeafId) => String | null` Returns `xLeafId` if the keyring has a triangle between `xRootId` and `yLeafId`. --- ### `keyring.self.set(keyInfo, cb)` Your keyring will always intialize and check if a self-key has been set. You can use this method to override that default. - `keyInfo` *Object* contains symmetric key - `info.key` *String* base64 encoded symmetric key - `info.scheme` *string* (optional) - `cb` *function* callback with signature `(err)` ### `keyring.self.get() => keyInfo` Returns the keyInfo for the self-key. --- ### `keyring.group.add(groupId, addInfo, cb)` Adds a group key to the keyring, where - `groupId` *String* a cloaked message Id which identifies the group - `addInfo` *Object*: - `addInfo.key` *Buffer* - the group encryption key (needed if `addInfo.scheme` is set) - `addInfo.scheme` *String* - scheme of that encryption key (optional, there is only one option at the moment which we default to) - `addInfo.root` *MessageId* the id of the `group/init` message (optional) - `cb` *function* callback with signature `(err) => {}` called after persistence Can be called multiple times to add more read keys. The first time you add a key it will be automatically picked as the write key. If you call `group.add` with `addInfo.key` on an excluded group, the group will automatically be un-excluded. ### `keyring.group.pickWriteKey(groupId, pickedKey, cb) Allows you to pick one of the group's current `readKeys` and promote it to being the current `writeKey` that will be available in the object returned by `keyring.group.get`. `pickedKey` needs to exactly match one of the `readKeys`. - `pickedKey` *Object*: - `pickedKey.key` *Buffer* - a group encryption key - `pickedKey.scheme` *String* - scheme of that encryption key - `cb` *function* callback with signature `(err) => {}` called after persistence ### `keyring.group.has(groupId) => Boolean` Returns `true` if the keyring has a group key for `groupId`, and `false` otherwise, if you haven't been in the group or if you've been excluded. ### `keyring.group.get(groupId) => groupInfo` Returns the `groupInfo` that you've added with `keyring.group.add` for `groupId`, or `null` if the keyring doesn't have any. It has the format - `groupInfo` *Object`: - `groupInfo.writeKey` *GroupKey* the currently selected key for writing. Is always one of `groupInfo.readKeys`. - `groupInfo.readKeys` *Array\<GroupKey\>* all keys that you've added to this `groupId` - `groupInfo.root` *MessageId* the id of the `group/init` message. where - `groupKey` *Object*: - `groupKey.key` *Buffer* - a group encryption key - `groupKey.scheme` *String* - scheme of that encryption key ### `keyring.group.getUpdates(groupId) => PullStream<groupInfo>` Like `keyring.group.get` but instead as a live pull stream, that outputs whenever there's been a change to that group's info. Note, this includes all "old" updates and then continues to emit new updates. ### `keyring.group.exclude(groupId, cb)` Marks group you've been in as excluded and removes its writeKey. This is useful if you or someone else excludes you from a group. The group info will lose the `writeKey` field and get an `excluded` field set to `true`. ### `keyring.group.list({ live, excluded }) => PullStream<groupId>` Returns an pull stream of all the groupIds in the keyring. If `live` is true then it outputs all existing group ids but also all new ones added. If `excluded` is true (default false) then it only returns excluded groups instead of only non-excluded groups. ### `keyring.group.listSync({ excluded }) => [groupId]` Returns an array of all the groupIds in the keyring If `excluded` is true (default false) then it only returns excluded groups instead of only non-excluded groups. --- ### `keyring.poBox.add(poBoxId, info, cb)` where - `poBoxId` *String* is an SSB-URI for a P.O. Box - `info` *Object* - `info.key` *Buffer* - the private part of a diffie-hellman key - `info.scheme` *String* the scheme associated with that key (currently optional) ### `keyring.poBox.has(poBoxId) => Boolean` ### `keyring.poBox.get(poBoxId) => keyInfo` ### `keyring.poBox.list(poBoxId) => [poBoxId]` --- ### `keys.close(cb)` Closes the keyring database