p2p-db / src / ird / p2p / db / identity.cljs
439 words
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
(ns ird.p2p.db.identity
  "The OrbitDB identity every holder of the secret shares. Write access to
  the database is possession of the secret: the database's access list
  names exactly one OrbitDB identity, and that identity is derived from the
  secret, so whoever holds the secret can produce it and nobody else can.

  An OrbitDB identity is two keys. The *identity key* gives the identity its
  id (the lowercase hex of its public key) and signs the binding of the
  second key; the *signing key* signs every entry. Both are Ed25519 keys
  whose 32-byte seeds come from the key schedule (ird.p2p.db.crypto):

    identity key  seed = HKDF(\"db|<name>|identity\")
    signing key   seed = HKDF(\"db|<name>|identity|sign\")

  so the same secret, app salt and name always yield the same identity,
  on every peer, with nothing exchanged. The keys reach OrbitDB's keystore
  as its raw Ed25519 form, seed ‖ public key (64 bytes), filed under
  `keystore-label` (the identity key) and under the identity's id (the
  signing key) — exactly where OrbitDB's `publickey` identity provider
  looks for them. Everything here is strings: seeds and public keys as
  URL-safe base64, raw keystore records as standard base64."
  (:require [cljs.core.async :as async :refer [go <!]]
            [ird.p2p.async :as a :refer [go-try <?]]
            [ird.p2p.db.crypto :as crypto]
            [ird.p2p.db.errors :as err]
            [ird.p2p.impl.bytes :as bytes]
            [ird.p2p.impl.webcrypto :as wc]))

(def keystore-label
  "The keystore id under which the identity key is filed — the `id` this
  library asks OrbitDB's identity provider for."
  "ird-p2p-db")

(defn- hex-of [b64url] (bytes/b64->hex (bytes/b64url->b64 b64url)))

(defn from-secret
  "The shared identity of the database: {:orbit-id <64 lowercase hex, the
  identity key's public key> :signing-key <64 lowercase hex, the signing
  key's public key> :id-seed :id-public-key :sign-seed :sign-public-key}
  (seeds and keys as URL-safe base64, 32 bytes each). Channel; a config
  error when the secret, salt or name is invalid."
  [{:keys [secret app-salt name]}]
  (go-try
    (crypto/check-secret secret)
    (when-not (string? app-salt)
      (throw (err/config-error "app salt must be a string" {:app-salt app-salt})))
    (crypto/check-name name)
    (let [id-seed (<! (crypto/identity-seed secret app-salt name))
          sign-seed (<! (crypto/signing-seed secret app-salt name))
          id-pub (when id-seed (<! (wc/ed25519-public-key id-seed)))
          sign-pub (when sign-seed (<! (wc/ed25519-public-key sign-seed)))]
      (when-not (and id-pub sign-pub)
        (throw (err/config-error "identity derivation failed" {})))
      {:orbit-id (hex-of id-pub)
       :signing-key (hex-of sign-pub)
       :id-seed id-seed
       :id-public-key id-pub
       :sign-seed sign-seed
       :sign-public-key sign-pub})))

(defn raw-key
  "OrbitDB's raw Ed25519 private key form, seed ‖ public key (64 bytes), as
  standard base64."
  [seed-b64url public-key-b64url]
  (bytes/concat-b64 (bytes/b64url->b64 seed-b64url) (bytes/b64url->b64 public-key-b64url)))

(defn keystore-records
  "The two keystore records the impl files before asking OrbitDB for the
  identity: [{:id <keystore id> :private-key <raw key, standard base64>} ...]
  — the identity key under `keystore-label`, the signing key under the
  identity's id."
  [{:keys [orbit-id id-seed id-public-key sign-seed sign-public-key]}]
  [{:id keystore-label :private-key (raw-key id-seed id-public-key)}
   {:id orbit-id :private-key (raw-key sign-seed sign-public-key)}])

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/p2p-db.git