id-kit / src / ardegazu / id / selftest.cljs
  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
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
;; ported-from: src/selftest.ts @ v1.1.0
;;
;; Dev-only self-test: determinism, binding, certs, wraps, profile clamping.
(ns ardegazu.id.selftest
  (:require [ardegazu.id.bridge-host :as host]
            [ardegazu.id.crypto :as c]
            [ardegazu.id.identity :as identity]
            [ardegazu.id.profile :as profile]
            [ardegazu.id.protocol :as protocol]
            [ardegazu.id.xkey :as xkey]
            [shadow.cljs.modern :refer (js-await)]))

(defn- check [cond what]
  (when-not cond
    (throw (js/Error. (str "id-kit self-test FAILED: " what)))))

(defn- identity-part []
  ;; identity: seed shape, determinism, binding assertion domain separation
  (let [seed (identity/new-seed)]
    (check (.test protocol/SEED-RE seed) "seed shape")
    (js-await [id1 (identity/from-seed-impl seed)]
      (js-await [id2 (identity/from-seed-impl seed)]
        (check (identical? (unchecked-get id1 "publicKeyB64")
                           (unchecked-get id2 "publicKeyB64"))
               "identity deterministic from seed")
        (js-await [fp (identity/fingerprint-of (unchecked-get id1 "publicKeyB64"))]
          (check (and (identical? 64 (.-length (unchecked-get fp "hex")))
                      (pos? (.-length (unchecked-get fp "emoji"))))
                 "fingerprint shape")
          (js-await [sig (.assert ^js id1 "selftest/v1" "room" "peer")]
            (js-await [v1 (identity/verify-assertion "selftest/v1" "room" "peer"
                                                     (unchecked-get id1 "publicKeyB64") sig)]
              (check (some? v1) "assertion verifies")
              (js-await [v2 (identity/verify-assertion "selftest/v1" "room" "other"
                                                       (unchecked-get id1 "publicKeyB64") sig)]
                (check (nil? v2) "bound to peerId")
                (js-await [v3 (identity/verify-assertion "selftest/v2" "room" "peer"
                                                         (unchecked-get id1 "publicKeyB64") sig)]
                  (check (nil? v3) "bound to salt")
                  (js-obj "seed" seed "id1" id1))))))))))

(defn- xkey-part [seed id1]
  ;; xkey: deterministic, salt-separated, certs verify, wraps roundtrip
  (js-await [xa (xkey/derive-suite-x-key-pair seed)]
    (js-await [xb (xkey/derive-suite-x-key-pair seed)]
      (check (identical? (unchecked-get xa "pubB64") (unchecked-get xb "pubB64"))
             "x25519 deterministic from seed")
      (js-await [x-other (xkey/derive-x-key-pair seed "someapp/v1")]
        (check (not (identical? (unchecked-get x-other "pubB64") (unchecked-get xa "pubB64")))
               "x25519 salt-separated")
        (js-await [cert (xkey/sign-x-cert id1 xkey/SUITE-SALT (unchecked-get xa "pubB64"))]
          (js-await [cv (xkey/verify-x-cert xkey/SUITE-SALT (unchecked-get id1 "publicKeyB64")
                                            (unchecked-get xa "pubB64") cert)]
            (check cv "x cert verifies")
            (js-await [cv2 (xkey/verify-x-cert "someapp/v1" (unchecked-get id1 "publicKeyB64")
                                               (unchecked-get xa "pubB64") cert)]
              (check (not cv2) "x cert bound to salt")
              (let [payload (c/utf8 "hello friend")]
                (js-await [w (xkey/wrap-to xkey/SUITE-SALT "ctx-1"
                                           (unchecked-get id1 "publicKeyB64")
                                           (unchecked-get xa "pubB64") payload)]
                  (js-await [opened (xkey/unwrap xkey/SUITE-SALT "ctx-1" w xa)]
                    (check (and (some? opened)
                                (identical? (.decode (js/TextDecoder.) opened) "hello friend"))
                           "wrap roundtrip")
                    (js-await [wrong-ctx (xkey/unwrap xkey/SUITE-SALT "ctx-2" w xa)]
                      (check (nil? wrong-ctx) "wrap bound to ctx")
                      (js-await [stranger (xkey/derive-suite-x-key-pair (identity/new-seed))]
                        (js-await [stolen (xkey/unwrap xkey/SUITE-SALT "ctx-1" w stranger)]
                          (check (nil? stolen) "wrap sealed to recipient")
                          nil)))))))))))))

(defn- sync-part [seed id1]
  ;; profile: hue determinism + clamping of hostile input
  (check (identical? (profile/hue-of-pub (unchecked-get id1 "publicKeyB64"))
                     (profile/hue-of-pub (unchecked-get id1 "publicKeyB64")))
         "hue deterministic")
  (let [hostile (js/Object.assign (js/Object.create (js-obj "evil" 1))
                                  (js-obj "name" (.repeat "x" 99) "hue" 720.5 "glyph" "🦊🦊"))
        clamped (profile/clamp-profile hostile)]
    (check (and (identical? 32 (.-length (unchecked-get clamped "name")))
                (nil? (unchecked-get clamped "hue"))
                (identical? (unchecked-get clamped "glyph") "🦊"))
           "profile clamped")
    (check (not (js-in "evil" clamped)) "no prototype pollution"))

  ;; record sanitation
  (check (nil? (host/sanitize-record (js-obj "seed" "short"))) "bad seed rejected")
  (let [rec (host/sanitize-record (js-obj "seed" seed "name" 42 "hue" "red" "createdAt" -5))]
    (check (and (some? rec)
                (identical? (unchecked-get rec "name") "")
                (nil? (unchecked-get rec "hue"))
                (> (unchecked-get rec "createdAt") 0))
           "record normalized"))
  nil)

(defn run-id-kit-self-test []
  (js-await [ids (identity-part)]
    (let [seed (unchecked-get ids "seed")
          id1 (unchecked-get ids "id1")]
      (js-await [_ (xkey-part seed id1)]
        (sync-part seed id1)
        (js/console.info "✅ id-kit self-test passed")
        js/undefined))))

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/id-kit.git