Migration 11 is ready. It password protects the private key. Data storage functions encode and decode the private key and password.

This commit is contained in:
Robert C. Martin 2023-05-12 12:47:13 -05:00
parent eed87443dc
commit 9a49498f8e
6 changed files with 206 additions and 55 deletions

View File

@ -1,13 +1,17 @@
(ns more-speech.migrator-spec
(:require [speclj.core :refer :all]
[more-speech.migrator :refer :all]
(:require [clojure.java.io :as io]
[more-speech.bech32 :as bech32]
[more-speech.config :as config]
[clojure.java.io :as io]
[more-speech.user-configuration :as user-configuration]
[more-speech.data-storage :as data-storage]
[more-speech.db.gateway :as gateway]
[more-speech.db.in-memory :as in-memory]
[more-speech.mem :as mem]
[more-speech.migrator :refer :all]
[more-speech.nostr.elliptic-signature :as es]
[more-speech.nostr.util :as util]
[more-speech.user-configuration :as user-configuration]
[more-speech.util.files :refer :all]
[more-speech.nostr.util :as util]))
[speclj.core :refer :all]))
(defn change-to-tmp-files []
(when (file-exists? "tmp")
@ -279,9 +283,9 @@
renamed-dir (str @config/messages-directory ".migrated")]
(.mkdir (io/file "tmp/messages"))
(spit path1 [{:id 1 :content "c1"}
{:id 2 :content "c2"}])
{:id 2 :content "c2"}])
(spit path2 [{:id 3 :content "c3"}
{:id 4 :content "c4"}])
{:id 4 :content "c4"}])
(migration-10-load-events)
(should= {:id 1 :content "c1"} (gateway/get-event @db 1))
(should= {:id 2 :content "c2"} (gateway/get-event @db 2))
@ -295,4 +299,89 @@
(should (file-exists? (str renamed-dir f2 ".migrated")))
(prn 'got-here)))
)
(context "migration 11 password protect private key"
(with db (in-memory/get-db))
(before-all (config/set-db! :in-memory))
(before (in-memory/clear-db @db)
(mem/clear-mem))
(it "password protects the keys file"
(let [bytes-private-key (util/make-private-key)
private-key (util/hexify (util/bytes->num bytes-private-key))]
(spit @config/keys-filename {:private-key private-key})
(migration-11-password-for-private-key)
(let [keys (read-string (slurp @config/keys-filename))
encrypted-private-key (:private-key keys)
decoded-private-key (util/xor-string "password" (bech32/address->str encrypted-private-key))]
(should= private-key decoded-private-key)
(should= "password" (bech32/address->str (:password keys))))))
(context "data storage changes"
(it "reads the keys with no password"
(let [bytes-private-key (util/make-private-key)
hex-private-key (util/hexify (util/bytes->num bytes-private-key))
bytes-public-key (es/get-pub-key bytes-private-key)
public-key (util/bytes->num bytes-public-key)
hex-public-key (util/hexify public-key)]
(spit @config/keys-filename {:private-key hex-private-key
:public-key hex-public-key})
(data-storage/read-keys)
(should= hex-public-key (mem/get-mem [:keys :public-key]))
(should= hex-private-key (mem/get-mem [:keys :private-key]))
(should= nil (mem/get-mem [:keys :password]))
(should= public-key (mem/get-mem :pubkey))))
(it "reads the keys with a password"
(let [bytes-private-key (util/make-private-key)
hex-private-key (util/hexify (util/bytes->num bytes-private-key))
bytes-public-key (es/get-pub-key bytes-private-key)
public-key (util/bytes->num bytes-public-key)
hex-public-key (util/hexify public-key)
password (bech32/encode-str "pw" "password")
encoded-private-key (->> hex-private-key
(util/xor-string "password")
(bech32/encode-str "encoded"))
]
(spit @config/keys-filename {:private-key encoded-private-key
:public-key hex-public-key
:password password})
(data-storage/read-keys)
(should= hex-public-key (mem/get-mem [:keys :public-key]))
(should= hex-private-key (mem/get-mem [:keys :private-key]))
(should= "password" (mem/get-mem [:keys :password]))
(should= public-key (mem/get-mem :pubkey))))
(it "writes the keys with no password"
(let [bytes-private-key (util/make-private-key)
hex-private-key (util/hexify (util/bytes->num bytes-private-key))
bytes-public-key (es/get-pub-key bytes-private-key)
public-key (util/bytes->num bytes-public-key)
hex-public-key (util/hexify public-key)]
(data-storage/write-keys {:private-key hex-private-key
:public-key hex-public-key})
(should= {:private-key hex-private-key
:public-key hex-public-key
:password nil}
(read-string (slurp @config/keys-filename)))))
(it "writes the keys with a password"
(let [bytes-private-key (util/make-private-key)
hex-private-key (util/hexify (util/bytes->num bytes-private-key))
bytes-public-key (es/get-pub-key bytes-private-key)
public-key (util/bytes->num bytes-public-key)
hex-public-key (util/hexify public-key)
encoded-private-key (->> hex-private-key
(util/xor-string "password")
(bech32/encode-str "encoded"))]
(data-storage/write-keys {:private-key hex-private-key
:public-key hex-public-key
:password "password"})
(should= {:private-key encoded-private-key
:public-key hex-public-key
:password (bech32/encode-str "pw" "password")}
(read-string (slurp @config/keys-filename)))))
)
)
)

View File

@ -64,4 +64,19 @@
pw gen-string]
(= source (->> source (xor-string pw) (xor-string pw)))
))))
(it "can write and read back xord strings"
(should-be
:result
(tc/quick-check
100
(prop/for-all
[source gen-string
pw gen-string]
(spit "xor-test.txt" (xor-string pw source))
(let [text (slurp "xor-test.txt")
decoded-source (xor-string pw text)]
(= source decoded-source)))))
(more-speech.util.files/delete-file "xor-test.txt")
)
)

View File

@ -25,7 +25,7 @@
(with-redefs [event-composers/compose-and-send-metadata-event (stub :compose)
protocol/request-profiles-and-contacts-for (stub :request)
data-storage/write-keys (stub :write-keys)]
(let [private-key (rand-int 1000000000)
(let [private-key (util/bytes->num (util/make-private-key))
hex-private-key (util/hexify private-key)
pubkey (->> private-key (util/num->bytes 32) es/get-pub-key util/bytes->num)
hex-pubkey (util/hexify pubkey)]
@ -48,7 +48,7 @@
(with-redefs [event-composers/compose-and-send-metadata-event (stub :compose)
protocol/request-profiles-and-contacts-for (stub :request)
data-storage/write-keys (stub :write-keys)]
(let [private-key (rand-int 1000000000)
(let [private-key (util/bytes->num (util/make-private-key))
hex-private-key (util/hexify private-key)
nsec-private-key (bech32/encode "nsec" private-key)
pubkey (->> private-key (util/num->bytes 32) es/get-pub-key util/bytes->num)

View File

@ -1,5 +1,6 @@
(ns more-speech.data-storage
(:require [clojure.string :as string]
[more-speech.bech32 :as bech32]
[more-speech.config :as config :refer [get-db]]
[more-speech.db.gateway :as gateway]
[more-speech.db.in-memory :as in-memory]
@ -24,16 +25,28 @@
(clojure.pprint/pprint (relays/relays-for-writing))))))
(defn write-keys [keys]
(let [keys-string (with-out-str (clojure.pprint/pprint keys))]
(let [private-key (:private-key keys)
password (:password keys)
encoded-password (if (empty? password)
password
(bech32/encode-str "pw" password))
private-key (if (empty? password)
private-key
(->> private-key
(util/xor-string password)
(bech32/encode-str "encoded")))
keys (assoc keys :private-key private-key
:password encoded-password)
keys-string (with-out-str (clojure.pprint/pprint keys))]
(if (config/is-test-run?)
(log-pr 2 `write-keys (dissoc keys :private-key))
(spit @config/keys-filename keys-string))))
(defn write-tabs []
(log-pr 2 'writing-tabs)
(spit @config/tabs-list-filename
(with-out-str
(clojure.pprint/pprint (get-mem :tabs-list)))))
(spit @config/tabs-list-filename
(with-out-str
(clojure.pprint/pprint (get-mem :tabs-list)))))
(defn write-configuration []
(log-pr 2 'writing-relays)
@ -59,21 +72,34 @@
(read-string (slurp @config/contact-lists-filename))
{}))
(defn load-configuration []
(defn read-keys []
(let [keys (read-string (slurp @config/keys-filename))
pubkey (util/hex-string->num (:public-key keys))
tabs-list (tabs/ensure-tab-list-has-all
pw (:password keys)
pw (if (empty? pw) nil
(bech32/address->str pw))
private-key (:private-key keys)
private-key (if (some? pw)
(util/xor-string pw (bech32/address->str private-key))
private-key)
]
(set-mem :keys (assoc keys :private-key private-key
:password pw))
(set-mem :pubkey pubkey)
)
)
(defn load-configuration []
(let [tabs-list (tabs/ensure-tab-list-has-all
(read-string (slurp @config/tabs-list-filename)))
user-configuration (user-configuration/validate
(read-string (slurp @config/user-configuration-filename)))
profiles (read-profiles)
contact-lists (read-contact-lists)
]
contact-lists (read-contact-lists)]
(read-keys)
(when (= :in-memory @config/db-type)
(swap! in-memory/db assoc :contact-lists contact-lists)
(swap! in-memory/db assoc :profiles profiles))
(set-mem :keys keys)
(set-mem :pubkey pubkey)
(set-mem :tabs-list tabs-list)
(set-mem :user-configuration user-configuration)
(set-mem :event-history [])
@ -258,9 +284,9 @@
(log-pr 1 'ingested n 'contact-lists))
(recur (rest contacts-in) (conj contacts-out (first contact-list)) (inc n)))))))
(defn put-events [db events]
(log-pr 1 'putting (count events) 'events)
(xt/submit-tx db (map #(vector ::xt/put %) events)))
(defn put-events [db events]
(log-pr 1 'putting (count events) 'events)
(xt/submit-tx db (map #(vector ::xt/put %) events)))
(defn put-profiles [db profiles]
(log-pr 1 'putting (count profiles) 'profiles)
@ -275,19 +301,19 @@
(log-pr 1 'putting (count contacts) 'contacts)
(xt/submit-tx db (map #(vector ::xt/put (fix-contact-list %)) contacts)))
(defn compress []
(log-pr 1 'Compressing)
(let [now (util/get-now)
event-since (- now (* 14 86400))
profiles-since (- now (* 90 86400))
contacts-since (- now (* 90 86400))
prod-db (xtdb/start-xtdb! config/prod-db)
temp-db (xtdb/start-xtdb! config/temp-db)]
(put-events temp-db (get-events-since prod-db event-since))
(put-profiles temp-db (get-profiles-since prod-db profiles-since))
(put-contacts temp-db (get-contacts-since prod-db contacts-since))
(xt/sync temp-db)
(log-pr 1 'renaming config/prod-db (str config/prod-db "-old"))
(rename-file config/prod-db (str config/prod-db "-old"))
(rename-file config/temp-db config/prod-db)))
(defn compress []
(log-pr 1 'Compressing)
(let [now (util/get-now)
event-since (- now (* 14 86400))
profiles-since (- now (* 90 86400))
contacts-since (- now (* 90 86400))
prod-db (xtdb/start-xtdb! config/prod-db)
temp-db (xtdb/start-xtdb! config/temp-db)]
(put-events temp-db (get-events-since prod-db event-since))
(put-profiles temp-db (get-profiles-since prod-db profiles-since))
(put-contacts temp-db (get-contacts-since prod-db contacts-since))
(xt/sync temp-db)
(log-pr 1 'renaming config/prod-db (str config/prod-db "-old"))
(rename-file config/prod-db (str config/prod-db "-old"))
(rename-file config/temp-db config/prod-db)))

View File

@ -1,18 +1,20 @@
(ns more-speech.migrator
(:require [more-speech.logger.default :refer [log-pr]]
[clojure.java.io :as io]
[clojure.set :as set]
[more-speech.config :refer [migration-filename]]
[more-speech.config :as config]
[more-speech.nostr
[util :as util]
[elliptic-signature :as ecc]
[event-dispatcher :as handlers]]
[more-speech.data-storage :as data-storage]
[more-speech.user-configuration :as user-configuration]
[more-speech.db.gateway :as gateway]
[more-speech.util.files :refer :all]
[more-speech.initial-contact-list :as initial-contact-list]))
(:require
[more-speech.bech32 :as bech32]
[more-speech.logger.default :refer [log-pr]]
[clojure.java.io :as io]
[clojure.set :as set]
[more-speech.config :refer [migration-filename]]
[more-speech.config :as config]
[more-speech.nostr
[util :as util]
[elliptic-signature :as ecc]
[event-dispatcher :as handlers]]
[more-speech.data-storage :as data-storage]
[more-speech.user-configuration :as user-configuration]
[more-speech.db.gateway :as gateway]
[more-speech.util.files :refer :all]
[more-speech.initial-contact-list :as initial-contact-list]))
;---The Migrations
@ -162,7 +164,19 @@
(migration-10-load-contacts)
(migration-10-load-events))
;--- Migration 11 password protect private key
(defn migration-11-password-for-private-key []
(let [keys (read-string (slurp @config/keys-filename))
private-key (:private-key keys)
encoded-private-key (->> private-key
(util/xor-string "password")
(bech32/encode-str "encoded"))
encoded-keys (assoc keys :private-key encoded-private-key
:password (bech32/encode-str "pw" "password"))]
(spit @config/keys-filename (with-out-str (clojure.pprint/pprint encoded-keys)))
)
)
;---------- The Migrations List -------

View File

@ -177,13 +177,20 @@
:size [800 :by 20])]
(left-right-split the-label the-field)))
(defn- valid-password? []
(if (empty? (get-mem [:keys :password]))
true
(= (get-mem [:keys :password]) (input "Enter password"))))
(defn show-private-key [profile-frame _e]
(let [private-key-field (select profile-frame [:#private-key-field])
show-box (select profile-frame [:#show-box])
show? (config show-box :selected?)]
(config! private-key-field :text (if show?
(get-mem [:keys :private-key])
""))))
show? (config show-box :selected?)
allowed? (and show? (valid-password?))]
(if allowed?
(config! private-key-field :text (get-mem [:keys :private-key]))
(do (config! private-key-field :text "")
(config! show-box :selected? false)))))
(defn make-private-key-panel [profile-frame]
(let [the-label (label :text "Private key:" :size [150 :by 20])