(ns rosetta.brute-force (:require [clojure.math.combinatorics :refer [selections]]) ;; https://github.com/clojure/math.combinatorics (:import [java.util Arrays] [java.security MessageDigest])) ;;https://rosettacode.org/wiki/Parallel_Brute_Force (def targets ;; length = 5 ["1115dd800feaacefdf481f1f9070374a2a81e27880f187396db67958b207cbad" "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b" "74e1bb62f8dabb8125a58852b63bdf6eaef667cb56ac7f7cdba6d7305c50a22f"]) ;; HELPER/UTIL fns ;;================= (defn digest "Given a byte-array returns its hash (also a byte-array)." ^bytes [^MessageDigest md ^bytes bs] (.digest md bs)) (defn char-range "Helper fn for easily producing character ranges." [start end] (map char (range (int start) (inc (int end))))) (def low-case-eng-bytes "Our search-space (all lower case english characters converted to bytes)." (map byte (char-range \a \z))) (defn hex->bytes "Converts a hex string to a byte-array." ^bytes [^String hex] (let [len (.length hex) ret (byte-array (/ len 2))] (run! (fn [i] (aset ret (/ i 2) ^byte (unchecked-add-int (bit-shift-left (Character/digit (.charAt hex i) 16) 4) (Character/digit (.charAt hex (inc i)) 16)))) (range 0 len 2)) ret)) (defn bytes->hex "Converts a byte-array to a hex string." [^bytes bs] (.toString ^StringBuilder (areduce bs idx ret (StringBuilder.) (doto ret (.append (format "%02x" (aget bs idx))))))) ;; MAIN LOGIC ;;=========== (defn check-candidate "Checks whether the SHA256 hash of (a list of 5 bytes), matches . If it does, returns that hash as a hex-encoded String. Otherwise returns nil." [^bytes target sha256 candidate] (let [candidate-bytes (byte-array candidate) ^bytes candidate-hash (sha256 candidate-bytes)] (when (Arrays/equals target candidate-hash) (let [answer (String. candidate-bytes)] (println "Answer found for:" (bytes->hex candidate-hash) "=>" answer) answer)))) (defn sha256-brute-force "Top level function. Returns a list with the 3 answers." [space hex-hashes] (->> hex-hashes (map hex->bytes) ;; convert the hex strings to bytes (pmap ;; parallel map the checker-fn (fn [target-bytes] (let [message-digest (MessageDigest/getInstance "SHA-256") ;; new digest instance per thread sha256 (partial digest message-digest)] (some (partial check-candidate target-bytes sha256) (selections space 5)))))))