mirror of
https://github.com/logseq/logseq.git
synced 2026-02-01 22:47:36 +00:00
use salted PBKDF2 hash
This commit is contained in:
112
deps/publish/src/logseq/publish/common.cljs
vendored
112
deps/publish/src/logseq/publish/common.cljs
vendored
@@ -120,6 +120,89 @@
|
||||
digest (.digest js/crypto.subtle "SHA-256" data)]
|
||||
(to-hex digest)))
|
||||
|
||||
(def password-kdf-iterations 210000)
|
||||
|
||||
(defn bytes->base64url [bytes]
|
||||
(let [binary (apply str (map #(js/String.fromCharCode %) (array-seq bytes)))
|
||||
b64 (js/btoa binary)]
|
||||
(-> b64
|
||||
(string/replace #"\+" "-")
|
||||
(string/replace #"/" "_")
|
||||
(string/replace #"=+$" ""))))
|
||||
|
||||
(defn hash-password [password]
|
||||
(js-await [salt (doto (js/Uint8Array. 16)
|
||||
(js/crypto.getRandomValues))
|
||||
crypto-key (.importKey js/crypto.subtle
|
||||
"raw"
|
||||
(.encode text-encoder password)
|
||||
#js {:name "PBKDF2"}
|
||||
false
|
||||
#js ["deriveBits"])
|
||||
derived (.deriveBits js/crypto.subtle
|
||||
#js {:name "PBKDF2"
|
||||
:hash "SHA-256"
|
||||
:salt salt
|
||||
:iterations password-kdf-iterations}
|
||||
crypto-key
|
||||
256)
|
||||
derived-bytes (js/Uint8Array. derived)
|
||||
salt-encoded (bytes->base64url salt)
|
||||
hash-encoded (bytes->base64url derived-bytes)]
|
||||
(str "pbkdf2$sha256$"
|
||||
password-kdf-iterations
|
||||
"$"
|
||||
salt-encoded
|
||||
"$"
|
||||
hash-encoded)))
|
||||
|
||||
(defn base64url->uint8array [input]
|
||||
(let [pad (if (pos? (mod (count input) 4))
|
||||
(apply str (repeat (- 4 (mod (count input) 4)) "="))
|
||||
"")
|
||||
base64 (-> (str input pad)
|
||||
(string/replace "-" "+")
|
||||
(string/replace "_" "/"))
|
||||
raw (js/atob base64)
|
||||
data (js/Uint8Array. (.-length raw))]
|
||||
(dotimes [i (.-length raw)]
|
||||
(aset data i (.charCodeAt raw i)))
|
||||
data))
|
||||
|
||||
(defn verify-password [password stored-hash]
|
||||
(let [parts (when (string? stored-hash)
|
||||
(string/split stored-hash #"\$"))]
|
||||
(if-not (and (= 5 (count parts))
|
||||
(= "pbkdf2" (nth parts 0))
|
||||
(= "sha256" (nth parts 1)))
|
||||
false
|
||||
(js-await [iterations (js/parseInt (nth parts 2))
|
||||
salt (base64url->uint8array (nth parts 3))
|
||||
expected (base64url->uint8array (nth parts 4))
|
||||
crypto-key (.importKey js/crypto.subtle
|
||||
"raw"
|
||||
(.encode text-encoder password)
|
||||
#js {:name "PBKDF2"}
|
||||
false
|
||||
#js ["deriveBits"])
|
||||
derived (.deriveBits js/crypto.subtle
|
||||
#js {:name "PBKDF2"
|
||||
:hash "SHA-256"
|
||||
:salt salt
|
||||
:iterations iterations}
|
||||
crypto-key
|
||||
(* 8 (.-length expected)))
|
||||
derived-bytes (js/Uint8Array. derived)]
|
||||
(if (not= (.-length derived-bytes) (.-length expected))
|
||||
false
|
||||
(let [mismatch (reduce (fn [acc idx]
|
||||
(bit-or acc
|
||||
(bit-xor (aget derived-bytes idx)
|
||||
(aget expected idx))))
|
||||
0
|
||||
(range (.-length expected)))]
|
||||
(zero? mismatch)))))))
|
||||
|
||||
(defn hmac-sha256 [key message]
|
||||
(js-await [crypto-key (.importKey js/crypto.subtle
|
||||
"raw"
|
||||
@@ -194,22 +277,9 @@
|
||||
signed-query (str canonical-query "&X-Amz-Signature=" signature)]
|
||||
(str "https://" host canonical-uri "?" signed-query)))
|
||||
|
||||
(defn base64url->uint8array [input]
|
||||
(let [pad (if (pos? (mod (count input) 4))
|
||||
(apply str (repeat (- 4 (mod (count input) 4)) "="))
|
||||
"")
|
||||
base64 (-> (str input pad)
|
||||
(string/replace "-" "+")
|
||||
(string/replace "_" "/"))
|
||||
raw (js/atob base64)
|
||||
bytes (js/Uint8Array. (.-length raw))]
|
||||
(dotimes [i (.-length raw)]
|
||||
(aset bytes i (.charCodeAt raw i)))
|
||||
bytes))
|
||||
|
||||
(defn decode-jwt-part [part]
|
||||
(let [bytes (base64url->uint8array part)]
|
||||
(js/JSON.parse (.decode text-decoder bytes))))
|
||||
(let [data (base64url->uint8array part)]
|
||||
(js/JSON.parse (.decode text-decoder data))))
|
||||
|
||||
(defn import-rsa-key [jwk]
|
||||
(.importKey js/crypto.subtle
|
||||
@@ -254,17 +324,9 @@
|
||||
(when etag
|
||||
(string/replace etag #"\"" "")))
|
||||
|
||||
(defn bytes->base64url [bytes]
|
||||
(let [binary (apply str (map #(js/String.fromCharCode %) (array-seq bytes)))
|
||||
b64 (js/btoa binary)]
|
||||
(-> b64
|
||||
(string/replace #"\+" "-")
|
||||
(string/replace #"/" "_")
|
||||
(string/replace #"=+$" ""))))
|
||||
|
||||
(defn short-id-for-page [graph-uuid page-uuid]
|
||||
(js-await [payload (.encode text-encoder (str graph-uuid ":" page-uuid))
|
||||
digest (.digest js/crypto.subtle "SHA-256" payload)]
|
||||
(let [bytes (js/Uint8Array. digest)
|
||||
encoded (bytes->base64url bytes)]
|
||||
(let [data (js/Uint8Array. digest)
|
||||
encoded (bytes->base64url data)]
|
||||
(subs encoded 0 10))))
|
||||
|
||||
13
deps/publish/src/logseq/publish/routes.cljs
vendored
13
deps/publish/src/logseq/publish/routes.cljs
vendored
@@ -39,8 +39,8 @@
|
||||
{:allowed? true :provided? false}
|
||||
(let [provided (request-password request)]
|
||||
(if (string? provided)
|
||||
(js-await [hashed (publish-common/sha256-hex provided)]
|
||||
{:allowed? (= hashed stored-hash) :provided? true})
|
||||
(js-await [valid? (publish-common/verify-password provided stored-hash)]
|
||||
{:allowed? valid? :provided? true})
|
||||
{:allowed? false :provided? false})))))
|
||||
|
||||
(defn- auth-claims
|
||||
@@ -78,8 +78,8 @@
|
||||
(publish-model/entity->title (get payload-entities page-eid))))
|
||||
blocks (or (:blocks payload)
|
||||
(get payload "blocks"))
|
||||
page-password-hash (or (:page-password-hash payload)
|
||||
(get payload "page-password-hash"))
|
||||
page-password (or (:page-password payload)
|
||||
(get payload "page-password"))
|
||||
refs (when (and page-eid page-title)
|
||||
(publish-index/page-refs-from-payload payload page-eid page_uuid page-title graph))
|
||||
tagged-nodes (when (and page-eid page-title)
|
||||
@@ -113,12 +113,15 @@
|
||||
(throw (ex-info "owner sub or username is missing"
|
||||
{:owner-sub owner-sub
|
||||
:owner-username owner-username})))
|
||||
password-hash (when (and (string? page-password)
|
||||
(not (string/blank? page-password)))
|
||||
(publish-common/hash-password page-password))
|
||||
payload (bean/->js
|
||||
{:page_uuid page_uuid
|
||||
:page_title page-title
|
||||
:page_tags (when page-tags
|
||||
(js/JSON.stringify (clj->js page-tags)))
|
||||
:password_hash page-password-hash
|
||||
:password_hash password-hash
|
||||
:graph graph-uuid
|
||||
:schema_version schema_version
|
||||
:block_count block_count
|
||||
|
||||
@@ -321,9 +321,8 @@
|
||||
page-password (when (and (string? page-password)
|
||||
(not (string/blank? page-password)))
|
||||
page-password)
|
||||
page-password-hash (when page-password (<sha256-hex page-password))
|
||||
payload (cond-> payload
|
||||
page-password-hash (assoc :page-password-hash page-password-hash))
|
||||
page-password (assoc :page-password page-password))
|
||||
body (ldb/write-transit-str payload)
|
||||
content-hash (<sha256-hex body)
|
||||
graph-uuid (or (:graph-uuid payload)
|
||||
|
||||
Reference in New Issue
Block a user