Compare commits

11 Commits

Author SHA1 Message Date
590f14d101 cleaning temporary files 2025-12-02 23:06:35 +01:00
d824b3ef72 cleaning and gui improvemenet 2025-11-24 00:48:16 +01:00
5b1c375934 Remove compiled classes 2025-11-23 22:25:44 +01:00
7080ea74fb Pure clojure import from proto 2025-11-23 22:09:34 +01:00
97d1d6e59b len type 2025-11-19 15:00:49 +01:00
9a858c8993 Array of bytes with varints to seq of ints 2025-11-19 09:52:50 +01:00
e003288004 implementing TLVs 2025-11-18 16:08:02 +01:00
d2c97bd5e5 pack TLV 2025-11-18 09:55:01 +01:00
1ec2db9583 Code cleaning 2025-11-17 15:50:18 +01:00
b8cd76b481 decimal to base uses euclidean division 2025-11-17 14:21:15 +01:00
7380362280 swap parameters order 2025-11-14 13:52:23 +01:00
38 changed files with 2516 additions and 3137 deletions

View File

@@ -1 +1,11 @@
~/.sdkman/candidates/java/21.0.2-graalce/bin/java -agentlib:native-image-agent=config-output-dir=META-INF/native-image -jar target/clj-topt-1.0.32-standalone.jar g TUGOBTEHPSCMUCYAT6UPELNWGE -c #!/usr/bin/env sh
JAVA_CMD=~/.sdkman/candidates/java/21.0.2-graalce/bin/java
UBERJAR=$(realpath --relative-to=. target/clj-totp-cli-*-standalone.jar)
echo "Using uberjar $UBERJAR"
$JAVA_CMD -agentlib:native-image-agent=config-output-dir=target/native-image\
-jar $UBERJAR import "deleteme" "otpauth-migration://offline?data=CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA"
java -jar $UBERJAR delete "deleteme"

View File

@@ -1,4 +0,0 @@
#!/bin/env sh
protoc --java_out projects/core/java/protoc/ projects/core/resources/proto/otpauth-migration.proto
#javac -cp resources/protobuf-java-3.25.8.jar -d target/classes/proto src/OtpauthMigration.java

View File

@@ -64,5 +64,11 @@
:uber/cli {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}} :uber/cli {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
:ns-default build :ns-default build
:exec-fn uber :exec-fn uber
:exec-args {:subproj "cli" :main-ns "totp.app"}}}} :exec-args {:subproj "cli" :main-ns "totp.app"}}
;; Build uber jar for CLI app
:uber/gui {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
:ns-default build
:exec-fn uber
:exec-args {:subproj "gui" :main-ns "totp.app"}}}}

View File

@@ -4,8 +4,10 @@ NATIVE=~/.sdkman/candidates/java/21.0.2-graalce/bin/native-image
BIN_FILE=totp BIN_FILE=totp
echo "Creating uberjar" echo "Creating uberjar"
clojure -T:build uber #clojure -T:uber/cli
UBERJAR=$(realpath --relative-to=target target/clj-totp-*-standalone.jar) UBERJAR=$(realpath --relative-to=target target/clj-totp-cli-*-standalone.jar)
echo "Using uberjar $UBERJAR"
echo "Creating native image" echo "Creating native image"
$NATIVE -jar target/$UBERJAR -o target/$BIN_FILE\ $NATIVE -jar target/$UBERJAR -o target/$BIN_FILE\
@@ -17,10 +19,13 @@ $NATIVE -jar target/$UBERJAR -o target/$BIN_FILE\
--strict-image-heap\ --strict-image-heap\
-march=native\ -march=native\
-R:MaxHeapSize=10m\ -R:MaxHeapSize=10m\
--trace-object-instantiation=java.lang.Thread\
--initialize-at-build-time=org.fusesource.jansi.Ansi\ --initialize-at-build-time=org.fusesource.jansi.Ansi\
--initialize-at-build-time='org.fusesource.jansi.Ansi$Color'\ --initialize-at-build-time='org.fusesource.jansi.Ansi$Color'\
--initialize-at-build-time='org.fusesource.jansi.Ansi$Attribute'\ --initialize-at-build-time='org.fusesource.jansi.Ansi$Attribute'\
'--initialize-at-build-time=org.fusesource.jansi.Ansi$1' --initialize-at-build-time='org.fusesource.jansi.Ansi$1'\
--initialize-at-run-time=cljc_long.constants__init
echo "Executable created on target/$BIN_FILE" echo "Executable created on target/$BIN_FILE"
cp target/$BIN_FILE ~/bin cp target/$BIN_FILE ~/bin

View File

@@ -3,7 +3,8 @@
org.clojure/clojure {:mvn/version "1.12.1"} org.clojure/clojure {:mvn/version "1.12.1"}
cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic
;; Progress bar ;; Progress bar
com.github.pmonks/spinner {:mvn/version "2.0.284"}} com.github.pmonks/spinner {:mvn/version "2.0.284"}
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"}}
:aliases {;; Execute the app :aliases {;; Execute the app
;:run {:main-opts ["-m" "totp.app"]} ;:run {:main-opts ["-m" "totp.app"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
-m
kaocha.runner

View File

@@ -3,6 +3,8 @@
mvxcvi/alphabase {:mvn/version "3.0.185"} ;; https://github.com/greglook/alphabase mvxcvi/alphabase {:mvn/version "3.0.185"} ;; https://github.com/greglook/alphabase
;; Protobuf for java ;; Protobuf for java
com.google.protobuf/protobuf-java {:mvn/version "3.25.8"} com.google.protobuf/protobuf-java {:mvn/version "3.25.8"}
;; Dynamic protobuf
com.github.s-expresso/clojobuf {:mvn/version "0.2.1"}
} }
:aliases {;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha :aliases {;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
(ns totp.data (ns totp.data
(:require [clojure.edn :as e] (:require [totp.otp-proto :as proto]
[clojure.edn :as e]
[clojure.string :as str] [clojure.string :as str]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.pprint :as pp] [clojure.pprint :as pp]
[alphabase.base64 :as b64] [alphabase.base64 :as b64]
[alphabase.base32 :as b32]) [alphabase.base32 :as b32]))
(:import [protoc OtpauthMigration$MigrationPayload]))
(defn join-path (defn join-path
"Joins several subpaths using system's path separator (/ un *NIX and \\ in windows)" "Joins several subpaths using system's path separator (/ un *NIX and \\ in windows)"
@@ -207,28 +207,26 @@
(when (some? url) (when (some? url)
(let [b64-data (second (str/split url #"=" -1)) (let [b64-data (second (str/split url #"=" -1))
data-b (b64/decode b64-data) data-b (b64/decode b64-data)
parsed (OtpauthMigration$MigrationPayload/parseFrom data-b) payload (proto/parse-data data-b)
payload (bean (.getOtpParameters parsed 0)) ;{:keys [name secret name issuer digits algorithm type]} payload
;{:keys [name secret name issuer digitsValue algorithmValue typeValue]} payload secret (:secret payload)
secret-b (:secret payload)
secret (b32/encode (.toByteArray secret-b))
user (:name payload) user (:name payload)
issuer (:issuer payload) issuer (:issuer payload)
algorithm (case (:algorithmValuei payload) 2 "sha256" 3 "sha512" "sha1") algorithm (:algorithm payload)
digits (case (:digitsValue payload) 2 8 6) digits (:digits payload)
valid-type (= 2 (:typeValue payload)) valid-type (= 2 (:type payload))
] ]
(println "name:" name "user:" user "issuer:" issuer "digitsValue:" digits "algorithm:" algorithm "valid type?" valid-type) ;(println "name:" name "user:" user "issuer:" issuer "digits:" digits "algorithm:" algorithm "valid type?" valid-type)
(if valid-type (if valid-type
(create-app name secret user issuer algorithm digits 30) (create-app name secret user issuer algorithm digits 30)
(println "Invalid OTP type" (:typeValue payload))) (println "Invalid OTP type" (:type payload)))
))) )))
(comment (comment
(url-export->app "test" (url-export->app "test"
"otpauth-migration://offline?data=CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA" "otpauth-migration://offline?data=CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA"
)
) )
)

View File

@@ -9,13 +9,19 @@
;; Original description of the export protocol uses Google's Protocol Buffers ;; Original description of the export protocol uses Google's Protocol Buffers
;; https://protobuf.dev/ ;; https://protobuf.dev/
(def WIRE_TYPES {:varint 0 ;; Integeres of variable length encoding
:i64 1 ;; Fixed 64 bits numbers (integer or decimal)
:len 2 ;; Block of bytes with a predefined length
:sgroup 3 ;; Group end (deprecated)
:egroup 4 ;; Group start (deprecated)
:i32 5 ;; Fixed 32 bits number (integer or decimal)
})
(comment (comment
(let [encoded "CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA" (let [encoded "CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA"
decoded (b64/decode encoded)] decoded (b64/decode encoded)]
(print (hex/encode decoded)) (print (hex/encode decoded))
(print (b/to-string decoded)) (print (b/to-string decoded))))
)
)
(defn to-fancy-bin (defn to-fancy-bin
@@ -44,25 +50,24 @@
digits = ceil ( log_n ( x + 1 ) ) digits = ceil ( log_n ( x + 1 ) )
" "
[x n] [n x]
(case x (case x
nil 0 ;; nill is encoded with zero bytes nil 0 ;; nill is encoded with zero bytes
0 1 ;; One block to zero 0 1 ;; One block to zero
9223372036854775807 (len-bits (dec x) n) ;; Beware the overflow!! it's best to lose some precision 9223372036854775807 (len-bits n (dec x)) ;; Beware the overflow!! it's best to lose some precision
(when (and (>= x 0) (some? n) (> n 0)) (when (and (>= x 0) (some? n) (> n 0))
(long (m/ceil (/ (m/log (inc x)) (m/log (m/pow 2 n)))))))) (long (m/ceil (/ (m/log (inc x)) (m/log (m/pow 2 n))))))))
(comment (comment
(len-bits 513 8) (len-bits 8 513)
(len-bits Long/MAX_VALUE 8) (len-bits 8 Long/MAX_VALUE)
(len-bits (dec Long/MAX_VALUE) 8) (len-bits 8 (dec Long/MAX_VALUE)))
)
(defn len-bytes (defn len-bytes
"How may bytes are needed to encode this number?" "How may bytes are needed to encode this number?"
[x] [x]
(len-bits x 8)) (len-bits 8 x))
(defn integer>bytes (defn integer>bytes
@@ -75,11 +80,59 @@
(comment (comment
(integer>bytes 513) (integer>bytes 513)
10r3 10r3
3r10 3r10)
)
(defn decimal-to-base (defn euclidean-quot
"Euclidean division for integers
More info: https://en.wikipedia.org/wiki/Euclidean_division
"
[a b]
(when (zero? b)
(throw (IllegalArgumentException. "You can't divide by zero!")))
(let [b-abs (Math/abs b)
r (mod a b-abs)] ;; 0 <= r < |b|
(quot (- a r) b) ;; adjusts quotient with the positive remainder
))
(defn euclidean-rem
"Modulus for euclidean division for integers
More info: https://en.wikipedia.org/wiki/Euclidean_division
"
[a b]
(when (zero? b)
(throw (IllegalArgumentException. "You can't divide by zero!")))
(let [b-abs (Math/abs b)]
(mod a b-abs);; 0 <= r < |b|
))
(defn euclid-div
"Euclidean division. Returns quotient and remainder as a map. The remainder is
allways a positive number.
More info: https://en.wikipedia.org/wiki/Euclidean_division"
[a b]
(when (zero? b)
(throw (IllegalArgumentException. "You can't divide by zero!")))
(let [b-abs (Math/abs b)
r (mod a b-abs) ;; 0 <= r < |b|
q (quot (- a r) b)] ;; adjusts quotient with the positive remainder
{:q q :r r}))
(comment
(euclid-div 7 3)
(euclid-div -7 3)
(euclid-div 7 -3)
(euclid-div -7 -3))
(defn decimal->base
"Converts a decimal number to an arbitrary base. Each digit is encoded as an "Converts a decimal number to an arbitrary base. Each digit is encoded as an
integer with value between 0 and base-1. integer with value between 0 and base-1.
@@ -99,41 +152,81 @@
1 / 5 = 0 rem 1 1 / 5 = 0 rem 1
so, 127 in base 5 is 1002. so, 127 in base 5 is 1002.
The maximum digits this method can return is 65. Max size for a long is base 2
is 64, so if you obtain a result with 65 digits is very probable that the
conversion has failed.
If the number is negative and the base is possitive, we return the conversion
of the absolute value. You must take care of the sign in your implementation.
There are negative bases as -2 that can encode possitive and negative numbers.
" "
([n base] ([base n]
(decimal-to-base n base true)) (decimal->base base n true))
([n base reverse?] ([base n reverse?]
(if (or (nil? n) (nil? base) (zero? n) (zero? base)) ;; Allways [0] for base zero or number zero (if (or (nil? n) (nil? base) (zero? n) (zero? base))
[0] [0] ;; Allways [0] for base zero or number zero
(loop [acc [] (loop [acc []
x n] x (if (and (< n 0) (> base 0)) (abs n) n)] ;; If n < 0 and b > 0 => |n|, else n
(if (zero? x) (let [q (euclidean-quot x base)
(vec (if reverse? (reverse acc) acc)) ;; When x is zero, we have finished r (euclidean-rem x base)]
(recur ;;(printf "Acc: %s Calculating: %s/%s -> %s rem %s%n" acc x base q r)
(conj acc (rem x base)) ;; Accumulate the remainder (if (or (zero? x) (> (count acc) (inc Long/SIZE))) ;; max digits is 65
(quot x base))))))) ;; Pass the quotient to the next step (vec (if reverse? (reverse acc) acc)) ;; When x is zero, we have finished
(recur
(conj acc r) ;; Accumulate the remainder
q ;; Pass the quotient to the next step
)))))))
(comment (comment
(decimal-to-base 8 2) (decimal->base 2 8)
2r1000 2r1000
) (decimal->base 2 3)
(letfn [(step [x b name q r]
(printf "%s -> %d/%d = %d rem %d%n" name x b q r))
(examples [x b exp-q exp-r]
(step x b "EXPECTED " exp-q exp-r)
(step x b "IEEE " (int (m/IEEE-remainder x b)) (rem x b))
(step x b "With rem " (quot x b) (rem x b))
(step x b "With mod " (quot x b) (mod x b))
(step x b "floor " (m/floor-div x b) (m/floor-mod x b))
(step x b "Manual 1 " (int (clojure.math/round (double (/ x b)))) (rem x b))
(step x b "Manual 2 " (int (clojure.math/rint (double (/ x b)))) (rem x b))
(step x b "Unchecked " (unchecked-divide-int x b) (unchecked-remainder-int x b))
(step x b "Euclidean " (euclidean-quot x b) (euclidean-rem x b)))]
(examples -3 -2 2 1)
(println)
(examples 2 -2 -1 0)
(println)
(examples -1 -2 1 1)
(println)
(examples 1 -2 0 1)
(println)
(println)
(examples 7 3 2 1)
(println)
(examples -7 3 -3 2)
(println)
(examples 7 -3 -2 1)
(println)
(examples -7 -3 3 2)))
(defn base-to-decimal (defn base->decimal
"Converts from an array with values in an arbitrary base into decimal values" "Converts from an array with values in an arbitrary base into decimal values"
[n base] [base n]
(if (or (nil? n) (nil? base) (zero? (count n))) (if (or (nil? n) (nil? base) (zero? (count n)))
0 0
(long (reduce-kv (long (reduce-kv
(fn [acc k v] (fn [acc k v]
(+ acc (* v (m/pow base k)))) (+ acc (* v (m/pow base k))))
0 (vec (reverse n)))))) 0 (vec (reverse n))))))
(comment (comment
(reduce-kv #(+ %1 (* %3 (m/pow 2 %2))) 0 [1 0 0 0]) ;; backwards! (reduce-kv #(+ %1 (* %3 (m/pow 2 %2))) 0 [1 0 0 0]) ;; backwards!
(base->decimal 2 [1 0 0 0]))
(base-to-decimal [1 0 0 0] 2)
)
(defn int->varint (defn int->varint
@@ -151,7 +244,7 @@
The result is a byte array. The result is a byte array.
" "
[x] [x]
(let [b128 (decimal-to-base x 128 false)] (let [b128 (decimal->base 128 x false)]
(byte-array (conj (byte-array (conj
(vec (map #(bit-or 2r10000000 %) (butlast b128))) (vec (map #(bit-or 2r10000000 %) (butlast b128)))
(bit-and 2r01111111 (peek b128)))))) (bit-and 2r01111111 (peek b128))))))
@@ -174,7 +267,8 @@
00000000 10010110 -> fill the bytes 00000000 10010110 -> fill the bytes
" "
[x] [x]
(base-to-decimal (reverse (map #(bit-and 2r01111111 %) x)) 128)) (base->decimal 128 (reverse (map #(bit-and 2r01111111 %) x))))
(comment (comment
;; 150 ;; 150
@@ -183,38 +277,109 @@
(to-fancy-bin (reverse (map #(bit-and 2r01111111 %) [-106, 1]))) (to-fancy-bin (reverse (map #(bit-and 2r01111111 %) [-106, 1])))
(varint->int [-106, 1]) (varint->int [-106, 1])
;; Long/MAX_VALUE
(to-fancy-bin (map #(bit-and 2r01111111 %) [-1, -1, -1, -1, -1, -1, -1, -1, 128]))
(varint->int [-2, -1, -1, -1, -1, -1, -1, -1, 127])
)
(comment
(hex/decode (String/format "%08x" (into-array [1023])))
(to-fancy-bin [150])
(to-fancy-bin (int->varint 150))
(to-fancy-bin (int->varint 151))
(to-fancy-hex (int->varint 150))
(to-fancy-hex (int->varint 151) 4)
(varint->int [-106, 1]) (varint->int [-106, 1])
(varint->int [-105, 1]) (varint->int [-105, 1])
(to-fancy-bin (int->varint Long/MAX_VALUE)) ;; Long/MAX_VALUE
(to-fancy-bin (int->varint (dec Long/MAX_VALUE))) (to-fancy-bin (map #(bit-and 2r01111111 %) [-1, -1, -1, -1, -1, -1, -1, -1, 128]))
(varint->int [-2, -1, -1, -1, -1, -1, -1, -1, 127]))
(to-fancy-hex (int->varint (dec Long/MAX_VALUE)))
(varint->int [-2, -1, -1, -1, -1, -1, -1, -1, 127]) (defn more-blocks?
"True if the MSB bit is 1"
[b]
(when b
(< 0 (bit-and 2r10000000 b))))
(to-fancy-bin [2r10010110 2r00000001]) (comment
(bit-and 2r10000000 2r10000001)
[2r10010110 2r00000001] (bit-and 2r10000000 2r00000001)
[2r0010110 2r0000001] ( more-blocks? 2r10000001)
(more-blocks? 2r00000001)
) )
(defn extract-varint-blocks
"Group varints in a byte array"
[bytes]
(loop [acc [] ;; Groups of varints
group [] ;; Current varint
r bytes] ;; Current tested byte
(if (empty? r) ;; Final condition: no more bytes to test
(if ((complement empty?) group)
(conj acc group) ;; Return acc with the last group added if not empty
acc) ;; if empty, return acc
(if (more-blocks? (first r))
;; If more blocks remains:
(recur acc
(conj group (first r)) ;; add current byte to current group
(next r))
;; If it's last in varint:
(recur (->> (first r)
(conj group) ;; add byte to group
(conj acc)) ;; add group to accumulator
[] ;; and start a new empty group
(next r))))))
(comment
(extract-varint-blocks [2r11111111 2r01111111, 2r10000001 2r10101001 2r00000001, 2r00000111])
)
(defn extract-varints
"Group varints in an byte array and convert them to decimal"
[bytes]
(map varint->int (extract-varint-blocks bytes)))
(comment
(extract-varints [2r11111111 2r01111111, 2r10000001 2r10101001 2r00000001, 2r00000111])
(varint->int [2r11111111 2r01111111])
(varint->int [2r10000001 2r10101001 2r00000001])
(varint->int [2r00000111])
)
(defn bytes->len-type
"Prepend byte array with length in varint format"
[bytes]
(concat (int->varint (count bytes))
bytes))
(comment
(to-fancy-hex (bytes->len-type (.getBytes "testing")))
)
(defn pack-bytes-as-tlv
"Pack the value as TLV.
Type can one of those 6 IDs:
ID Name Used For
0 VARINT int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 I64 fixed64, sfixed64, double
2 LEN string, bytes, embedded messages, packed repeated fields
3 SGROUP group start (deprecated)
4 EGROUP group end (deprecated)
5 I32 fixed32, sfixed32, float
"
([{:keys [field type value]}]
(pack-bytes-as-tlv field type value))
([field-number type value]
(let [field-displaced (bit-shift-left field-number 3)
tag (int->varint (bit-or field-displaced type))]
(concat tag value))))
(defn unpack-tlv-bytes
"Returns a map with 3 pairs:
- :field is the field number
- :type is one of the values of WIRE_TYPES
- :value is the byte array with the payload"
[packed]
(let [first-byte (bit-and 2r01111111 (first packed)) ;; bye bye, MSB
type (bit-and 2r00000111 first-byte)
field-number (bit-shift-right first-byte 3)]
{:field field-number :type type :value (rest packed)}))

View File

@@ -0,0 +1,45 @@
(ns totp.otp-proto
(:require [clojobuf.core :refer [protoc decode]]
[alphabase.base64 :as b64]
[alphabase.base32 :as b32]))
;; Where lookup for proto files
(def registry (protoc ["resourcse/proto/"
"projects/core/resources/proto/"]
["otpauth-migration.proto"]))
(defn parse-data
[binary-data]
(let [decoded (decode registry :MigrationPayload binary-data)
msg (get-in decoded [:otp_parameters 0])]
;(println "Decoded:" msg)
(when msg
{:secret (b32/encode (:secret msg))
:name (:name msg)
:issuer (:issuer msg)
:algorithm (case (:algorithm msg)
:ALGORITHM_SHA256 "sha256"
:ALGORITHM_SHA512 "sha512"
:ALGORITHM_MD5 "md5"
"sha1") ;; sha1 by default
:digits (case (:digits msg)
:DIGIT_COUNT_EIGHT 8
6) ;; 6 digits by default
:type (case (:type msg)
:OTP_TYPE_UNSPECIFIED 0
:OTP_TYPE_HOTP 1
:OTP_TYPE_TOTP 2) ;; Only TOTP is supported
})))
(comment
(let [b64-data "CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA"
bin-data (b64/decode b64-data)
decoded (decode registry :MigrationPayload bin-data)]
;(get-in decoded [:otp_parameters 0])
(parse-data bin-data)
)
)

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env sh
JAVA_EXECUTABLE=java
UBER_JAR=$(realpath clj-totp-*-standalone.jar)
OPTS="-Xms256m -Xmx256m -client -Dclojure.spec.skip-macros=true"
$JAVA_EXECUTABLE $OPTS -jar $UBER_JAR $@

View File

@@ -11,14 +11,14 @@
(deftest len-bits-test (deftest len-bits-test
(testing "Check required number of blocks to encode a number in n bits" (testing "Check required number of blocks to encode a number in n bits"
(is (nil? (len-bits 10 nil))) (is (nil? (len-bits nil 10)))
(is (= 0 (len-bits nil 10))) (is (= 0 (len-bits 10 nil)))
(is (= 1 (len-bits 1 2))) (is (= 1 (len-bits 2 1)))
(is (= 2 (len-bits 10 2))) (is (= 2 (len-bits 2 10)))
(is (= 2 (len-bits 15 2))) (is (= 2 (len-bits 2 15)))
(is (= 3 (len-bits 16 2))) (is (= 3 (len-bits 2 16)))
(is (= 1 (len-bits 255 8))) (is (= 1 (len-bits 8 255)))
(is (= 2 (len-bits 255 7))) (is (= 2 (len-bits 7 255)))
) )
) )
@@ -49,49 +49,40 @@
) )
(deftest decimal-to-base-test (deftest decimal->base-test
(testing "Convert from decimal base to an arbitrary base" (testing "Convert from decimal base to an arbitrary base"
(is (= [0] (decimal-to-base 10 nil))) (is (= [0] (decimal->base nil 10)))
(is (= [0] (decimal-to-base nil 10))) (is (= [0] (decimal->base 10 nil)))
(is (= [0] (decimal-to-base 0 2))) (is (= [0] (decimal->base 2 0)))
(is (= [0] (decimal-to-base 2 0))) (is (= [0] (decimal->base 0 2)))
(is (= [1 0 0 0] (decimal-to-base 8 2))) (is (= [1 0 0 0] (decimal->base 2 8)))
(is (= [2 2] (decimal-to-base 8 3))) (is (= [2 2] (decimal->base 3 8)))
(is (= [2 0] (decimal-to-base 8 4))) (is (= [2 0] (decimal->base 4 8)))
(is (= [1 3] (decimal-to-base 8 5))) (is (= [1 3] (decimal->base 5 8)))
(is (= [3 1] (decimal-to-base 8 5 false))) (is (= [3 1] (decimal->base 5 8 false)))
(is (= [0] (decimal-to-base 0 5))) (is (= [0] (decimal->base 5 0)))
)
) )
)
(deftest base-to-decimal-test (deftest base->decimal-test
(testing "Convert from arbitrary base to decimal" (testing "Convert from arbitrary base to decimal"
(is (zero? (base-to-decimal nil 2))) (is (zero? (base->decimal 2 nil)))
(is (zero? (base-to-decimal [] 2))) (is (zero? (base->decimal 2 [])))
(is (zero? (base-to-decimal [1] nil))) (is (zero? (base->decimal nil [1])))
(is (= 8 (base-to-decimal [1 0 0 0] 2))) (is (= 8 (base->decimal 2 [1 0 0 0])))
(is (= 8 (base-to-decimal [2 2] 3))) (is (= 8 (base->decimal 3 [2 2])))
(is (= 8 (base-to-decimal [2 0] 4))) (is (= 8 (base->decimal 4 [2 0])))
(is (= 8 (base-to-decimal [1 3] 5))) (is (= 8 (base->decimal 5 [1 3])))
)
) )
)
(deftest decimal-base-decimal-test (deftest decimal->base-decimal-test
(testing "Check if convert from decimal to a base and back preserves the original number" (testing "Check if convert from decimal to a base and back preserves the original number"
(is (= 8 (base-to-decimal (decimal-to-base 8 2) 2))) (is (= 8 (base->decimal 2 (decimal->base 2 8))))
(is (= 127 (base-to-decimal (decimal-to-base 127 2) 2))) (is (= 127 (base->decimal 2 (decimal->base 2 127))))
(is (= 417 (base-to-decimal (decimal-to-base 417 13) 13))) (is (= 417 (base->decimal 13 (decimal->base 13 417))))
))
(deftest int->varint-test
(testing "Convert from integer number (int, long, byte, etc) to varint"
(is (Arrays/equals (byte-array [0]) (int->varint nil)))
(is (Arrays/equals (byte-array [0]) (int->varint 0)))
(is (Arrays/equals (byte-array [2r10010110 2r00000001]) (int->varint 150)))
) )
) )
@@ -120,5 +111,39 @@
(testing "Convert from int to varint and back to int" (testing "Convert from int to varint and back to int"
(is (= 150 (varint->int (int->varint 150)))) (is (= 150 (varint->int (int->varint 150))))
(is (= 151 (varint->int (int->varint 151)))) (is (= 151 (varint->int (int->varint 151))))
) )
) )
(deftest more-blocks?-test
(testing "Check if there are more blocks in a varint type"
(is (nil? (more-blocks? nil)))
(is (true? (more-blocks? 2r10000001)))
(is (true? (more-blocks? 2r11111111)))
(is (false? (more-blocks? 2r00000001)))
(is (false? (more-blocks? 2r01111111)))
)
)
(deftest extract-varint-blocks-test
(testing "Group varints"
(is (empty? (extract-varint-blocks nil)))
(is (empty? (extract-varint-blocks [])))
(is (= [[2r00000111]] (extract-varint-blocks [2r00000111])))
(is (= [[2r00000111] [2r00000111]] (extract-varint-blocks [2r00000111 2r00000111])))
(is (= [[2r11111111 2r01111111] [2r10000001 2r10101001 2r00000001] [2r00000111]] (extract-varint-blocks [2r11111111 2r01111111, 2r10000001 2r10101001 2r00000001, 2r00000111])))
(is (= [[2r11111111 2r01111111] [2r10000001 2r10101001 2r00000001] [2r10000111]] (extract-varint-blocks [2r11111111 2r01111111, 2r10000001 2r10101001 2r00000001, 2r10000111])))
)
)
(deftest extract-varints-test
(testing "bytes with varints to decimal"
(is (empty? (extract-varints nil)))
(is (empty? (extract-varints [])))
(is (= [7] (extract-varints [2r00000111])))
(is (= [7 7] (extract-varints [2r00000111 2r00000111])))
(is (= [16383 21633 7] (extract-varints [2r11111111 2r01111111, 2r10000001 2r10101001 2r00000001, 2r00000111])))
(is (= [16383 21633 7] (extract-varints [2r11111111 2r01111111, 2r10000001 2r10101001 2r00000001, 2r10000111])))
)
)

View File

@@ -2,6 +2,7 @@
:deps {;;org.clojure/clojure {:mvn/version "1.12.1"} :deps {;;org.clojure/clojure {:mvn/version "1.12.1"}
cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic
;; GUI ;; GUI
seesaw/seesaw {:mvn/version "1.5.0"}} seesaw/seesaw {:mvn/version "1.5.0"}
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"}}
} }

View File

@@ -7,29 +7,8 @@
[seesaw.mig :refer :all] [seesaw.mig :refer :all]
[seesaw.clipboard :as cp] [seesaw.clipboard :as cp]
[seesaw.dev :refer :all]) [seesaw.dev :refer :all])
(:import [java.util Date TimerTask Timer])) (:import [java.util Date TimerTask Timer])
(:gen-class))
(defn content-test
[]
(let [choose (fn [e] (alert "I should open a file chooser"))]
(flow-panel
:items ["File" [:fill-h 5]
(text (System/getProperty "user.dir")) [:fill-h 5]
(action :handler choose :name "...")])))
(defn content-test2
[name category date comment]
(mig-panel
:constraints ["wrap 2"
"[shrink 0]20px[200, grow, fill]"
"[shrink 0]5px[]"]
:items [["name:"] [(text (or name ""))]
["category:"] [(text (or category ""))]
["date:"] [(text (or date ""))]
["comment:"] [(text (or comment ""))]]))
(defn copy-handler (defn copy-handler
@@ -39,34 +18,32 @@
b-obj (select (to-root e) [(keyword b-name)]) b-obj (select (to-root e) [(keyword b-name)])
b-text (config b-obj :text)] b-text (config b-obj :text)]
(println "Copying text value: " b-text) (println "Copying text value: " b-text)
(cp/contents! b-text) (cp/contents! b-text)))
))
(defn make-otp-list (defn make-otp-list
"Make panel with OTPs" "Make panel with OTPs"
[] []
(mig-panel (scrollable
:constraints ["wrap 3" (mig-panel
"[shrink 0]20px[200, grow, fill]10px[shrink 0]"] :constraints ["wrap 3"
:items (let [apps (with-config (filter some? #_{:clj-kondo/ignore [:unresolved-symbol]} cfg)) "[shrink 0]20px[200, grow, fill]10px[shrink 0]"]
] :items (let [apps (with-config (filter some? #_{:clj-kondo/ignore [:unresolved-symbol]} cfg))]
(reduce (fn [acc a] (reduce (fn [acc a]
(let [{:keys [name secret algorithm digits period]} a (let [{:keys [name secret algorithm digits period]} a
field-id (str "field-totp-" name)] field-id (str "field-totp-" name)]
(-> acc (-> acc
(conj [name]) (conj [name])
(conj [(text :text (get-otp secret algorithm digits period) (conj [(text :text (get-otp secret algorithm digits period)
:editable? false :editable? false
:id field-id)]) :id field-id)])
(conj [(action :name "copy" (conj [(action :name "copy"
:handler (partial copy-handler field-id) :handler (partial copy-handler field-id)
:command (str "cmd-" name))])))) :command (str "cmd-" name))]))))
[] apps)))) [] apps)))))
(comment (comment
(make-otp-list) (make-otp-list))
)
(defn make-time-bar (defn make-time-bar
@@ -76,21 +53,47 @@
:hgap 5 :hgap 5
:center (progress-bar :id "timer-bar" :center (progress-bar :id "timer-bar"
:value init-val :value init-val
:max 30) :max 30
:paint-string? true)
:east (text :id "timer-text" :east (text :id "timer-text"
:text init-val :text init-val
:editable? false :editable? false
:columns 2 :columns 2
:halign :right) :halign :right)))
))
(defn make-add-frame
[parent]
(frame :title "Add new TOTP"
:minimum-size [320 :by 200]
:size [320 :by 220]
:on-close :dispose
:content (border-panel
:center (mig-panel
:constraints ["wrap 2"
"[shrink 0]20px[200, grow, fill]"]
:items [["Name"] [(text :id "add-name" :columns 32)]
["Secret (B32)"] [(text :id "add-secret" :columns 32)]
["User (optional)"] [(text :id "add-user" :columns 32)]
["Issuer (optional)"] [(text :id "add-issuer" :columns 32)]
["Algorithm"] [(combobox :model ["sha1" "sha256" "sha512"])]
["Digits"] [(combobox :model ["6" "8"])]])
:south (flow-panel :align :right :items [(action :name "Cancel")
(action :name "Add")]))))
(defn make-main-buttons
[]
(flow-panel :align :right
:items ["button a" "button b"]))
(defn make-frame-content (defn make-frame-content
[] []
(border-panel :hgap 10 :vgap 10 (border-panel :hgap 10 :vgap 10
:center (make-otp-list) :center (make-otp-list)
:north (make-time-bar (int(/ (calculate-offset-millis 30) 1000))) :north (make-time-bar (int (/ (calculate-offset-millis 30) 1000)))
;:south "SOUTH" :south (make-main-buttons)
;:east "EAST" ;:east "EAST"
;:west "WEST" ;:west "WEST"
)) ))
@@ -106,6 +109,7 @@
:on-close :dispose)) :on-close :dispose))
(defn update-totps (defn update-totps
"Update all totps" "Update all totps"
[root] [root]
@@ -115,7 +119,7 @@
field-id (str "field-totp-" name) field-id (str "field-totp-" name)
field (select root [(keyword (str "#" field-id))]) field (select root [(keyword (str "#" field-id))])
current-otp (get-otp secret algorithm digits period)] current-otp (get-otp secret algorithm digits period)]
(println "Updating" field-id "with otp" current-otp) ;;(println "Updating" field-id "with otp" current-otp)
(config! field :text current-otp))))) (config! field :text current-otp)))))
@@ -124,13 +128,12 @@
(let [time-bar (select root [:#timer-bar]) (let [time-bar (select root [:#timer-bar])
time-text (select root [:#timer-text]) time-text (select root [:#timer-text])
offset (inc (int (/ (calculate-offset-millis 30) 1000)))] offset (inc (int (/ (calculate-offset-millis 30) 1000)))]
(println "Updating at at" (System/currentTimeMillis)) ;;(println "Updating at at" (System/currentTimeMillis))
(config! time-bar :value offset) (config! time-bar :value offset)
(config! time-text :text offset) (config! time-text :text offset)
(when (= 1 offset) (when (= 1 offset)
(println "update TOTP") ;;(println "update TOTP")
(update-totps root)) (update-totps root))))
))
(defn start-updater (defn start-updater
@@ -142,8 +145,8 @@
(proxy [TimerTask] [] (proxy [TimerTask] []
(run [] (update-progress root))) (run [] (update-progress root)))
delay 1000)) delay 1000))
(println "Now" now "Delay" delay) (println "Now" now "Delay" delay)))
))
(defn -main [& args] (defn -main [& args]
@@ -152,6 +155,7 @@
(-> (make-frame) (-> (make-frame)
pack! pack!
show! show!
(make-add-frame)
start-updater)) start-updater))
(println "Gui started")) (println "Gui started"))
@@ -163,4 +167,6 @@
(show-options (frame)) (show-options (frame))
(show-options (text)) (show-options (text))
) (-> (make-add-frame nil)
(pack!)
(show!)))

File diff suppressed because it is too large Load Diff

270
reflect_config.json.bak Normal file
View File

@@ -0,0 +1,270 @@
[
{
"name": "com.sun.crypto.provider.HmacSHA1",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "java.lang.reflect.Method",
"methods": [
{
"name": "canAccess",
"parameterTypes": [
"java.lang.Object"
]
}
]
},
{
"name": "java.util.Arrays",
"allDeclaredClasses": true,
"allPublicClasses": true,
"queryAllPublicMethods": true,
"methods": [
{
"name": "copyOfRange",
"parameterTypes": [
"byte[]",
"int",
"int"
]
}
]
},
{
"name": "java.util.Timer",
"queryAllPublicMethods": true,
"methods": [
{
"name": "scheduleAtFixedRate",
"parameterTypes": [
"java.util.TimerTask",
"long",
"long"
]
}
]
},
{
"name": "java.util.concurrent.locks.Lock"
},
{
"name": "java.util.concurrent.locks.ReentrantLock"
},
{
"name": "rubberbuf.ast_postprocess$eval194",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_postprocess$eval209",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_postprocess$eval224",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_postprocess$eval242",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_postprocess$eval331",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_postprocess__init"
},
{
"name": "rubberbuf.ast_preprocess$eval148",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval15",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval179",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval254",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval265",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval279",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval290",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval301",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval314",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess$eval321",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"name": "rubberbuf.ast_preprocess__init"
},
{
"name": "rubberbuf.ast_util__init"
},
{
"name": "rubberbuf.core__init"
},
{
"name": "rubberbuf.ebnf__init"
},
{
"name": "rubberbuf.parse__init"
},
{
"name": "rubberbuf.parse_textformat__init"
},
{
"name": "rubberbuf.util__init"
},
{
"name":"clojobuf.constant__init"
},
{
"name":"clojobuf.core__init"
},
{
"name":"clojobuf.decode__init"
},
{
"name":"clojobuf.encode__init"
},
{
"name":"clojobuf.schema$eval367",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"clojobuf.schema$eval396",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"clojobuf.schema__init"
},
{
"name":"clojobuf.util__init"
},
{
"name":"clojobuf_codec.decode__init"
},
{
"name":"clojobuf_codec.deserialize__init"
},
{
"name":"clojobuf_codec.encode__init"
},
{
"name":"clojobuf_codec.io.reader.ByteReader"
},
{
"name":"clojobuf_codec.io.reader__init"
},
{
"name":"clojobuf_codec.io.writer.ByteWriter"
},
{
"name":"clojobuf_codec.io.writer__init"
},
{
"name":"clojobuf_codec.serialize__init"
},
{
"name":"clojobuf_codec.util__init"
}
]