# ssb-keyring

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

Latest version **7.0.0** (published 2023-10-12) · LGPL-3.0 license · 0 weekly downloads

## Install

```sh
npm install ssb-keyring
pnpm add ssb-keyring
yarn add ssb-keyring
bun add ssb-keyring
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 7.0.0 |
| Published | 2023-10-12 |
| First published | 2021-09-01 |
| Weekly downloads | 0 |
| License | LGPL-3.0 |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 10 |
| Unpacked size | 63.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | mixmix |
| Maintainers | powersource, arj03, staltz, mixmix, cel |
| Keywords | scuttlebutt, keys, encryption |

## Links

- npm: https://www.npmjs.com/package/ssb-keyring
- Repository: https://gitlab.com/ahau/ssb-keyring
- Homepage: https://gitlab.com/ahau/ssb-keyring#readme
- Issues: https://gitlab.com/ahau/ssb-keyring/issues
- npm.io page: https://npm.io/package/ssb-keyring

## Dependencies (10)

- [level](https://npm.io/package/level.md) ^6.0.1
- [mkdirp](https://npm.io/package/mkdirp.md) ^1.0.4
- [ssb-ref](https://npm.io/package/ssb-ref.md) ^2.16.0
- [charwise](https://npm.io/package/charwise.md) ^3.0.1
- [ssb-uri2](https://npm.io/package/ssb-uri2.md) ^2.4.1
- [pull-level](https://npm.io/package/pull-level.md) ^2.0.4
- [lodash.find](https://npm.io/package/lodash.find.md) ^4.6.0
- [pull-stream](https://npm.io/package/pull-stream.md) ^3.7.0
- [private-group-spec](https://npm.io/package/private-group-spec.md) ^8.0.0
- [ssb-private-group-keys](https://npm.io/package/ssb-private-group-keys.md) ^1.1.2

## Alternatives

- [@gemini-wallet/core](https://npm.io/package/@gemini-wallet/core.md) — 515.6K weekly downloads
- [utility](https://npm.io/package/utility.md) — 416.6K weekly downloads
- [@primno/dpapi](https://npm.io/package/@primno/dpapi.md) — 7.2K weekly downloads
- [pi-readseek](https://npm.io/package/pi-readseek.md) — 3.7K weekly downloads
- [@emilia-protocol/verify](https://npm.io/package/@emilia-protocol/verify.md) — 1.1K weekly downloads

## Recent versions

- 7.0.0 (latest) — 2023-10-12
- 6.0.0 — 2023-10-12
- 5.6.0 — 2023-09-19
- 5.5.0 — 2023-05-10
- 5.4.0 — 2023-05-10
- 5.3.3 — 2023-05-08
- 1.1.1 — 2023-04-28
- 5.3.2 — 2023-04-27
- 5.3.1 — 2023-04-25
- 5.3.0 — 2023-04-20
- 5.2.0 — 2023-04-13
- 5.0.0 — 2023-03-07
- 4.1.0 — 2023-02-03
- 4.0.0 — 2023-02-03
- 3.1.0 — 2023-02-03
- … 18 more at https://npm.io/package/ssb-keyring/versions

## README

# 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

---
_Source: https://npm.io/package/ssb-keyring · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
