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

@@ -53,7 +53,7 @@
:ns-default build
:exec-fn jar
:exec-args {:subproj "gui"}}
;; Build the three libraries
:build/all {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
:replace-deps {clj-totp/core {:local/root "projects/core"}}
@@ -64,5 +64,11 @@
:uber/cli {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
:ns-default build
: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
echo "Creating uberjar"
clojure -T:build uber
UBERJAR=$(realpath --relative-to=target target/clj-totp-*-standalone.jar)
#clojure -T:uber/cli
UBERJAR=$(realpath --relative-to=target target/clj-totp-cli-*-standalone.jar)
echo "Using uberjar $UBERJAR"
echo "Creating native image"
$NATIVE -jar target/$UBERJAR -o target/$BIN_FILE\
@@ -17,10 +19,13 @@ $NATIVE -jar target/$UBERJAR -o target/$BIN_FILE\
--strict-image-heap\
-march=native\
-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$Color'\
--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"
cp target/$BIN_FILE ~/bin

View File

@@ -3,7 +3,8 @@
org.clojure/clojure {:mvn/version "1.12.1"}
cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic
;; 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
;: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
;; Protobuf for java
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

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
(ns totp.data
(:require [clojure.edn :as e]
(:require [totp.otp-proto :as proto]
[clojure.edn :as e]
[clojure.string :as str]
[clojure.java.io :as io]
[clojure.pprint :as pp]
[alphabase.base64 :as b64]
[alphabase.base32 :as b32])
(:import [protoc OtpauthMigration$MigrationPayload]))
[alphabase.base32 :as b32]))
(defn join-path
"Joins several subpaths using system's path separator (/ un *NIX and \\ in windows)"
@@ -207,28 +207,26 @@
(when (some? url)
(let [b64-data (second (str/split url #"=" -1))
data-b (b64/decode b64-data)
parsed (OtpauthMigration$MigrationPayload/parseFrom data-b)
payload (bean (.getOtpParameters parsed 0))
;{:keys [name secret name issuer digitsValue algorithmValue typeValue]} payload
secret-b (:secret payload)
secret (b32/encode (.toByteArray secret-b))
payload (proto/parse-data data-b)
;{:keys [name secret name issuer digits algorithm type]} payload
secret (:secret payload)
user (:name payload)
issuer (:issuer payload)
algorithm (case (:algorithmValuei payload) 2 "sha256" 3 "sha512" "sha1")
digits (case (:digitsValue payload) 2 8 6)
valid-type (= 2 (:typeValue payload))
algorithm (:algorithm payload)
digits (:digits 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
(create-app name secret user issuer algorithm digits 30)
(println "Invalid OTP type" (:typeValue payload)))
(println "Invalid OTP type" (:type payload)))
)))
(comment
(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
;; 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
(let [encoded "CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA"
decoded (b64/decode encoded)]
(print (hex/encode decoded))
(print (b/to-string decoded))
)
)
(print (b/to-string decoded))))
(defn to-fancy-bin
@@ -44,42 +50,89 @@
digits = ceil ( log_n ( x + 1 ) )
"
[x n]
[n x]
(case x
nil 0 ;; nill is encoded with zero bytes
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))
(long (m/ceil (/ (m/log (inc x)) (m/log (m/pow 2 n))))))))
(comment
(len-bits 513 8)
(len-bits Long/MAX_VALUE 8)
(len-bits (dec Long/MAX_VALUE) 8)
)
(len-bits 8 513)
(len-bits 8 Long/MAX_VALUE)
(len-bits 8 (dec Long/MAX_VALUE)))
(defn len-bytes
"How may bytes are needed to encode this number?"
[x]
(len-bits x 8))
(len-bits 8 x))
(defn integer>bytes
"Converts an integer to a byte array"
[x]
(when x (let [len (len-bytes x)
(when x (let [len (len-bytes x)
hex-len (* 2 len)]
(hex/decode (String/format (str "%0" hex-len "x") (into-array [x]))))))
(comment
(integer>bytes 513)
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
integer with value between 0 and base-1.
@@ -99,41 +152,81 @@
1 / 5 = 0 rem 1
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]
(decimal-to-base n base true))
([n base reverse?]
(if (or (nil? n) (nil? base) (zero? n) (zero? base)) ;; Allways [0] for base zero or number zero
[0]
([base n]
(decimal->base base n true))
([base n reverse?]
(if (or (nil? n) (nil? base) (zero? n) (zero? base))
[0] ;; Allways [0] for base zero or number zero
(loop [acc []
x n]
(if (zero? x)
(vec (if reverse? (reverse acc) acc)) ;; When x is zero, we have finished
(recur
(conj acc (rem x base)) ;; Accumulate the remainder
(quot x base))))))) ;; Pass the quotient to the next step
x (if (and (< n 0) (> base 0)) (abs n) n)] ;; If n < 0 and b > 0 => |n|, else n
(let [q (euclidean-quot x base)
r (euclidean-rem x base)]
;;(printf "Acc: %s Calculating: %s/%s -> %s rem %s%n" acc x base q r)
(if (or (zero? x) (> (count acc) (inc Long/SIZE))) ;; max digits is 65
(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
(decimal-to-base 8 2)
(decimal->base 2 8)
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"
[n base]
[base n]
(if (or (nil? n) (nil? base) (zero? (count n)))
0
(long (reduce-kv
(fn [acc k v]
(+ acc (* v (m/pow base k))))
0 (vec (reverse n))))))
(fn [acc k v]
(+ acc (* v (m/pow base k))))
0 (vec (reverse n))))))
(comment
(reduce-kv #(+ %1 (* %3 (m/pow 2 %2))) 0 [1 0 0 0]) ;; backwards!
(base-to-decimal [1 0 0 0] 2)
)
(base->decimal 2 [1 0 0 0]))
(defn int->varint
@@ -151,7 +244,7 @@
The result is a byte array.
"
[x]
(let [b128 (decimal-to-base x 128 false)]
(let [b128 (decimal->base 128 x false)]
(byte-array (conj
(vec (map #(bit-or 2r10000000 %) (butlast b128)))
(bit-and 2r01111111 (peek b128))))))
@@ -174,7 +267,8 @@
00000000 10010110 -> fill the bytes
"
[x]
(base-to-decimal (reverse (map #(bit-and 2r01111111 %) x)) 128))
(base->decimal 128 (reverse (map #(bit-and 2r01111111 %) x))))
(comment
;; 150
@@ -183,38 +277,109 @@
(to-fancy-bin (reverse (map #(bit-and 2r01111111 %) [-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 [-105, 1])
(to-fancy-bin (int->varint Long/MAX_VALUE))
(to-fancy-bin (int->varint (dec Long/MAX_VALUE)))
;; 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])
[2r10010110 2r00000001]
[2r0010110 2r0000001]
(comment
(bit-and 2r10000000 2r10000001)
(bit-and 2r10000000 2r00000001)
( 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
(testing "Check required number of blocks to encode a number in n bits"
(is (nil? (len-bits 10 nil)))
(is (= 0 (len-bits nil 10)))
(is (= 1 (len-bits 1 2)))
(is (= 2 (len-bits 10 2)))
(is (= 2 (len-bits 15 2)))
(is (= 3 (len-bits 16 2)))
(is (= 1 (len-bits 255 8)))
(is (= 2 (len-bits 255 7)))
(is (nil? (len-bits nil 10)))
(is (= 0 (len-bits 10 nil)))
(is (= 1 (len-bits 2 1)))
(is (= 2 (len-bits 2 10)))
(is (= 2 (len-bits 2 15)))
(is (= 3 (len-bits 2 16)))
(is (= 1 (len-bits 8 255)))
(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"
(is (= [0] (decimal-to-base 10 nil)))
(is (= [0] (decimal-to-base nil 10)))
(is (= [0] (decimal-to-base 0 2)))
(is (= [0] (decimal-to-base 2 0)))
(is (= [1 0 0 0] (decimal-to-base 8 2)))
(is (= [2 2] (decimal-to-base 8 3)))
(is (= [2 0] (decimal-to-base 8 4)))
(is (= [1 3] (decimal-to-base 8 5)))
(is (= [3 1] (decimal-to-base 8 5 false)))
(is (= [0] (decimal-to-base 0 5)))
)
(is (= [0] (decimal->base nil 10)))
(is (= [0] (decimal->base 10 nil)))
(is (= [0] (decimal->base 2 0)))
(is (= [0] (decimal->base 0 2)))
(is (= [1 0 0 0] (decimal->base 2 8)))
(is (= [2 2] (decimal->base 3 8)))
(is (= [2 0] (decimal->base 4 8)))
(is (= [1 3] (decimal->base 5 8)))
(is (= [3 1] (decimal->base 5 8 false)))
(is (= [0] (decimal->base 5 0)))
)
)
(deftest base-to-decimal-test
(deftest base->decimal-test
(testing "Convert from arbitrary base to decimal"
(is (zero? (base-to-decimal nil 2)))
(is (zero? (base-to-decimal [] 2)))
(is (zero? (base-to-decimal [1] nil)))
(is (= 8 (base-to-decimal [1 0 0 0] 2)))
(is (= 8 (base-to-decimal [2 2] 3)))
(is (= 8 (base-to-decimal [2 0] 4)))
(is (= 8 (base-to-decimal [1 3] 5)))
)
(is (zero? (base->decimal 2 nil)))
(is (zero? (base->decimal 2 [])))
(is (zero? (base->decimal nil [1])))
(is (= 8 (base->decimal 2 [1 0 0 0])))
(is (= 8 (base->decimal 3 [2 2])))
(is (= 8 (base->decimal 4 [2 0])))
(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"
(is (= 8 (base-to-decimal (decimal-to-base 8 2) 2)))
(is (= 127 (base-to-decimal (decimal-to-base 127 2) 2)))
(is (= 417 (base-to-decimal (decimal-to-base 417 13) 13)))
))
(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)))
(is (= 8 (base->decimal 2 (decimal->base 2 8))))
(is (= 127 (base->decimal 2 (decimal->base 2 127))))
(is (= 417 (base->decimal 13 (decimal->base 13 417))))
)
)
@@ -120,5 +111,39 @@
(testing "Convert from int to varint and back to int"
(is (= 150 (varint->int (int->varint 150))))
(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"}
cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic
;; 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.clipboard :as cp]
[seesaw.dev :refer :all])
(:import [java.util Date TimerTask Timer]))
(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 ""))]]))
(:import [java.util Date TimerTask Timer])
(:gen-class))
(defn copy-handler
@@ -37,36 +16,34 @@
[field-id e]
(let [b-name (str "#" field-id)
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)
(cp/contents! b-text)
))
(cp/contents! b-text)))
(defn make-otp-list
"Make panel with OTPs"
[]
(mig-panel
:constraints ["wrap 3"
"[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]
(let [{:keys [name secret algorithm digits period]} a
field-id (str "field-totp-" name)]
(-> acc
(conj [name])
(conj [(text :text (get-otp secret algorithm digits period)
:editable? false
:id field-id)])
(conj [(action :name "copy"
:handler (partial copy-handler field-id)
:command (str "cmd-" name))]))))
[] apps))))
(scrollable
(mig-panel
:constraints ["wrap 3"
"[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]
(let [{:keys [name secret algorithm digits period]} a
field-id (str "field-totp-" name)]
(-> acc
(conj [name])
(conj [(text :text (get-otp secret algorithm digits period)
:editable? false
:id field-id)])
(conj [(action :name "copy"
:handler (partial copy-handler field-id)
:command (str "cmd-" name))]))))
[] apps)))))
(comment
(make-otp-list)
)
(make-otp-list))
(defn make-time-bar
@@ -76,21 +53,47 @@
:hgap 5
:center (progress-bar :id "timer-bar"
:value init-val
:max 30)
:max 30
:paint-string? true)
:east (text :id "timer-text"
:text init-val
:editable? false
: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
[]
(border-panel :hgap 10 :vgap 10
:center (make-otp-list)
:north (make-time-bar (int(/ (calculate-offset-millis 30) 1000)))
;:south "SOUTH"
:north (make-time-bar (int (/ (calculate-offset-millis 30) 1000)))
:south (make-main-buttons)
;:east "EAST"
;:west "WEST"
))
@@ -106,6 +109,7 @@
:on-close :dispose))
(defn update-totps
"Update all totps"
[root]
@@ -115,7 +119,7 @@
field-id (str "field-totp-" name)
field (select root [(keyword (str "#" field-id))])
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)))))
@@ -124,13 +128,12 @@
(let [time-bar (select root [:#timer-bar])
time-text (select root [:#timer-text])
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-text :text offset)
(when (= 1 offset)
(println "update TOTP")
(update-totps root))
))
;;(println "update TOTP")
(update-totps root))))
(defn start-updater
@@ -142,8 +145,8 @@
(proxy [TimerTask] []
(run [] (update-progress root)))
delay 1000))
(println "Now" now "Delay" delay)
))
(println "Now" now "Delay" delay)))
(defn -main [& args]
@@ -152,6 +155,7 @@
(-> (make-frame)
pack!
show!
(make-add-frame)
start-updater))
(println "Gui started"))
@@ -163,4 +167,6 @@
(show-options (frame))
(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"
}
]