 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defmethod must-update? :default
-  [{:keys [algorithm iterations]}]
-  (let [desired-iterations (get *default-iterations* algorithm)]
+  [{:keys [alg iterations]}]
+  (let [desired-iterations (get *default-iterations* alg)]
     (and desired-iterations (> desired-iterations iterations))))
 
+(defmethod must-update? :bcrypt+sha512
+  [{:keys [alg iterations]}]
+  true)
+
 (defmethod must-update? :scrypt
-  [{:keys [algorithm memcost cpucost]}]
+  [{:keys [alg memcost cpucost]}]
   (let [desired-memcost (get-in *default-iterations* [:scrypt :memcost])
         desired-cpucost (get-in *default-iterations* [:scrypt :cpucost])]
     (and desired-cpucost

        (first)
        (keyword))))

(defmulti derive-password
  "Derive key depending on algorithm."
  :algorithm)

(defmulti check-password
  "Password verification implementation."
  :algorithm)

(defmulti format-password
  "Format password depending on algorithm."
  :algorithm)

(defmulti must-update?
  "Check if the current password configuration
  is succeptible to be updatable."
  :algorithm)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Key Derivation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmethod derive-password :pbkdf2+sha1
  [{:keys [algorithm password salt iterations] :as pwdparams}]
  (let [salt (->byte-array (or salt (nonce/random-bytes 12)))
        iterations (or iterations (get *default-iterations* algorithm))
        pgen (doto (PKCS5S2ParametersGenerator. (SHA1Digest.))
               (.init password salt iterations))
        password (.getKey (.generateDerivedParameters pgen 160))]
    {:algorithm algorithm
     :password password
     :salt salt
     :iterations iterations}))

(defmethod derive-password :pbkdf2+sha256
  [{:keys [algorithm password salt iterations] :as pwdparams}]
  (let [salt (->byte-array (or salt (nonce/random-bytes 12)))
        iterations (or iterations (get *default-iterations* algorithm))
        pgen (doto (PKCS5S2ParametersGenerator. (SHA256Digest.))
               (.init password salt iterations))
        password (.getKey (.generateDerivedParameters pgen 160))]
    {:algorithm algorithm
     :password password
     :salt salt
     :iterations iterations}))

(defmethod derive-password :pbkdf2+sha3_256
  [{:keys [algorithm password salt iterations] :as pwdparams}]
  (let [salt (->byte-array (or salt (nonce/random-bytes 12)))
        iterations (or iterations (get *default-iterations* algorithm))
        pgen (doto (PKCS5S2ParametersGenerator. (SHA3Digest. 256))
               (.init password salt iterations))
        password (.getKey (.generateDerivedParameters pgen 256))]
    {:algorithm algorithm
     :password password
     :salt salt
     :iterations iterations}))

(defmethod derive-password :bcrypt+sha512
  [{:keys [algorithm password salt iterations] :as pwdparams}]
  (let [salt (->byte-array (or salt (nonce/random-bytes 12)))
        iterations (or iterations (get *default-iterations* algorithm))
        iv (BCrypt/gensalt iterations)
        pwd (-> password
                (bytes/concat salt)

    (format "scrypt$%s$%s$%s$%s$%s" salt cpucost memcost parallelism password)))

(defmethod format-password :default
  [{:keys [alg password salt iterations]}]
  (let [algname (name alg)
        salt (bytes->hex salt)
        password (bytes->hex password)]
    (if (nil? iterations)
      (format "%s$%s$%s" algname salt password)
      (format "%s$%s$%s$%s" algname salt iterations password))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Key Parsing
