Compare commits
51 Commits
v1.0
...
76510be028
| Author | SHA1 | Date | |
|---|---|---|---|
| 76510be028 | |||
| a1fec08cc4 | |||
| 556cc85cde | |||
| 2ebac1676f | |||
| b6749bdb29 | |||
| 1b141173cc | |||
| 06174de597 | |||
| e6523e0a7b | |||
| 78da3c37c0 | |||
| 3d305a0d70 | |||
| 6166e930fe | |||
| 5edbfa4ce4 | |||
| 1071d9e5ee | |||
| 017291f784 | |||
| 17a7a09ab0 | |||
| 3a6fd107c0 | |||
| 4c31950a88 | |||
| aa71cb1d76 | |||
| c746675045 | |||
| 4052995ba8 | |||
| 44f48fced8 | |||
| c78e89a94b | |||
| 01842dbc8d | |||
| cec35fc16b | |||
| e5fb6e7231 | |||
| 6c017b3262 | |||
| 2e64c26a0a | |||
| c8b9556bcd | |||
| 5c93f4e570 | |||
| dfc3d4e579 | |||
| e7b2683d2c | |||
| 3dd79af7de | |||
| d86054f3a3 | |||
| 547e143f0c | |||
| 48478c49bc | |||
| ba58c7d744 | |||
| 32cf9cb581 | |||
| 29a1061d18 | |||
| 13be73f7e2 | |||
| 38586187e9 | |||
| af987008b0 | |||
| ba393ec55a | |||
| a85fc61e16 | |||
| 7b629b4b0d | |||
| 72923a34ff | |||
| fd012eea00 | |||
| 2aeef9925d | |||
| 75075b81fb | |||
| 0e88cddc24 | |||
| 6dc9100b45 | |||
| 1863e82595 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
/.clj-kondo/
|
/.clj-kondo/
|
||||||
/.cpcache/
|
/**/.cpcache/
|
||||||
/.lsp/
|
/.lsp/
|
||||||
/target/
|
/target/
|
||||||
.nrepl-port
|
.nrepl-port
|
||||||
|
|||||||
170
README.md
170
README.md
@@ -1,6 +1,7 @@
|
|||||||
# clj-totp
|
# clj-totp
|
||||||
|
|
||||||
TOTP (Time-based One Time Password) in clojure. It can be used in the command line, web API o simple embedded web.
|
TOTP (Time-based One Time Password) in clojure. It supports several digest algorithms and length.
|
||||||
|
|
||||||
|
|
||||||
## What is TOPT
|
## What is TOPT
|
||||||
|
|
||||||
@@ -14,27 +15,182 @@ You can read more about the algorithm here:
|
|||||||
- HOTP RFC: https://www.ietf.org/rfc/rfc4226.txt
|
- HOTP RFC: https://www.ietf.org/rfc/rfc4226.txt
|
||||||
|
|
||||||
|
|
||||||
## The inside
|
## How to use
|
||||||
|
|
||||||
This project is done 100% in clojure. It uses `deps.edn` for configuring the project and `build.clj` for compiling.
|

|
||||||
|
|
||||||
## Implementation timeline
|
First, you must have installed a Java Runtime Environment. Check https://adoptium.net/es if you are
|
||||||
|
unsure how to install.
|
||||||
|
|
||||||
|
The project is distributed as a jar file with all dependencies included (a.k.a. "uberjar"), and uses
|
||||||
|
a simple script to launch the program. Script and uberjar must be in the same directory.
|
||||||
|
|
||||||
|
Execute without parameters to show main commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clj-totp.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
A quick description of each command:
|
||||||
|
|
||||||
|
- `generate`: Show a TOTP with a given secret and parameters, not stored in config.
|
||||||
|
- `config`: Manage configuration file
|
||||||
|
- `add`: Store a new TOTP configuration
|
||||||
|
- `delete`: Delete an stored configuration
|
||||||
|
- `list`: Shows a list of TOPT stored configurations
|
||||||
|
- `get`: Generate a TOTP previously added
|
||||||
|
- `import`: Import a URL with the TOTP configuration
|
||||||
|
|
||||||
|
|
||||||
|
### Quick and simple generation
|
||||||
|
|
||||||
|
If you want to quickly generate a TOPT, you only need to suministrate the secret in B32 format:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clj-totp.sh generate <B32 secret>
|
||||||
|
```
|
||||||
|
|
||||||
|
The OTP changes every 30 seconds, you can print every change with `-c` option:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clj-totp.sh generate -c <B32 secret>
|
||||||
|
```
|
||||||
|
|
||||||
|
It will update the TOTP every 30 seconds, until you press `<Enter>` or `<ctrl+c>`.
|
||||||
|
|
||||||
|
|
||||||
|
### Store your configurations
|
||||||
|
|
||||||
|
Writing the B32 secret each time can be a bit tedious, but you can store secrets for your applications.
|
||||||
|
Be carefull, this version **don't encrypt passwords**, secrets are saved in plain text in a file in
|
||||||
|
your home dir.
|
||||||
|
|
||||||
|
With the `config` command you can check your configuration file. Now, let's explorer the subcommands:
|
||||||
|
|
||||||
|
Check if the config file exists, and show the full path:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clj-totp.sh info config
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new config file, if the file exists, it will prompt you if you want to overwrite it:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clj-totp.sh info init
|
||||||
|
```
|
||||||
|
|
||||||
|
Show all data contained in the config file, as a table:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clj-totp.sh info show
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Use stored configurations
|
||||||
|
|
||||||
|
If you have a valid configuration file, it's time to configure some applications.
|
||||||
|
|
||||||
|
To add a new configured application, you can use the `add` command. The simplest way to add a new
|
||||||
|
configuration is to specify an alias and the secret in B32:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clj-totp.sh add <alias> <b32 secret>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `add` subcommand has a lot of configuration options, you can explore them wit the `-?` param.
|
||||||
|
|
||||||
|
To list all added configurations, use the `list` command:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clj-totp.sh list
|
||||||
|
```
|
||||||
|
|
||||||
|
If you made a mistake, you can delete a configured app with the `delete` command:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clj-totp.sh delete <alias>
|
||||||
|
```
|
||||||
|
|
||||||
|
When you have some configured apps, it's time to use them, with the `get` command. To generate a
|
||||||
|
single TOTP for some app you can simple pass a list of alias:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clj-totp.sh get <alias1> <alias2> <aliasN>
|
||||||
|
```
|
||||||
|
|
||||||
|
It will show the TOTP value at the current time, but TOTPs changes every 30 seconds, to show
|
||||||
|
the value when it changes, add the `-c` param. It will update the TOTP for each alias until you
|
||||||
|
press enter key (or <Ctrl-C>):
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clj-totp.sh get <alias1> <alias2> <aliasN> -c
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, this program has an `import` command, that can import from a decoded QR or exported data
|
||||||
|
from Google Autenticator:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
clj-totp.sh import <alias> "<url>"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Project's plan
|
||||||
|
|
||||||
### v1.0
|
### v1.0
|
||||||
- [x] Functional TOTP generation
|
- [x] Functional TOTP generation
|
||||||
- [x] Get TOTP from command line
|
- [x] Get TOTP from command line
|
||||||
- [x] Continuous generation
|
- [x] Continuous generation
|
||||||
- [ ] Store configuration in a properties file or simple DB
|
|
||||||
|
|
||||||
### v1.1
|
### v1.1
|
||||||
|
- [x] Store configuration in a properties file or simple DB
|
||||||
|
- [x] Import from `otpauth` and `otpauth-migration` protocols
|
||||||
|
- [x] Show several OTPs at once
|
||||||
|
|
||||||
|
### v1.2
|
||||||
|
- [x] Show progress bar
|
||||||
|
- [x] Styles for progress bar
|
||||||
|
- [x] Native compilation script corrections
|
||||||
|
|
||||||
|
### v2
|
||||||
|
- [x] Restructurate as a multiproject
|
||||||
- [ ] REST API
|
- [ ] REST API
|
||||||
- [ ] User management
|
- [ ] User management
|
||||||
- [ ] Robust BD backend (H2, datomic, or similar)
|
- [ ] Robust BD backend (H2, datomic, or similar)
|
||||||
|
|
||||||
### v1.2
|
### v3
|
||||||
- [ ] Simple web connected to REST API
|
- [ ] Simple web connected to REST API
|
||||||
|
|
||||||
|
|
||||||
## Ideas
|
## Ideas
|
||||||
- Import from google auth URL: https://github.com/dim13/otpauth
|
|
||||||
|
Some ideas for future versions:
|
||||||
|
|
||||||
- Store passwords securely: https://github.com/weavejester/crypto-password
|
- Store passwords securely: https://github.com/weavejester/crypto-password
|
||||||
|
|
||||||
|
|
||||||
|
## Building the project
|
||||||
|
|
||||||
|
This project is done 100% in clojure. It uses `deps.edn` for configuring the project and `build.clj`
|
||||||
|
for defining compilation tasks.
|
||||||
|
|
||||||
|
The first step is to install Java JDK, version 11 or newer (version 21 recommended).
|
||||||
|
|
||||||
|
To execute manually the main function, simple use the `:run` alias:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clojure -M:run/cli <commands and parameters>
|
||||||
|
```
|
||||||
|
|
||||||
|
To build the uberjar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clojure -T:build :uber/cli
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a utility script to build a native executable using Graal VM. Please, edit the script and
|
||||||
|
check the path to your Graal installation. Use it at your own risk.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
native.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
189
build.clj
189
build.clj
@@ -1,25 +1,174 @@
|
|||||||
(ns build
|
(ns build
|
||||||
(:require [clojure.tools.build.api :as b]))
|
(:refer-clojure :exclude [test])
|
||||||
|
(:require [clojure.tools.build.api :as b]
|
||||||
|
[clojure.java.io :as io]
|
||||||
|
[clojure.pprint :as pp]
|
||||||
|
[clojure.java.basis :as basis]))
|
||||||
|
|
||||||
(def lib 'es.rcorral/clj-topt)
|
(def lib-group "es.rcorral")
|
||||||
(def version (format "1.0.%s" (b/git-count-revs nil)))
|
(def artifact-prefix "clj-totp")
|
||||||
(def class-dir "target/classes")
|
(def subprojs-base "projects")
|
||||||
(def uber-file (format "target/%s-%s-standalone.jar" (name lib) version))
|
(def curr-version (format "2.0.%s" (b/git-count-revs nil)))
|
||||||
|
|
||||||
;; delay to defer side effects (artifact downloads)
|
|
||||||
(def basis (delay (b/create-basis {:project "deps.edn"})))
|
|
||||||
|
|
||||||
(defn clean [_]
|
;; Builds artifact's full descriptor for each subproject
|
||||||
(b/delete {:path "target"}))
|
(defn lib [subproj]
|
||||||
|
(symbol (str lib-group "/" artifact-prefix "-" subproj )))
|
||||||
|
|
||||||
(defn uber [_]
|
|
||||||
(clean nil)
|
;; Basis for each subproject, using their own deps.edn
|
||||||
(b/copy-dir {:src-dirs ["src" "resources"]
|
;; Injects :extra-deps from :build as additional dependencies
|
||||||
:target-dir class-dir})
|
(defn basis [subproj]
|
||||||
(b/compile-clj {:basis @basis
|
(delay (b/create-basis {:project (str subprojs-base "/" subproj "/deps.edn")
|
||||||
:ns-compile '[totp.app]
|
;; Inject extra deps as deps
|
||||||
:class-dir class-dir})
|
:extra {:deps (get-in (basis/initial-basis) [:aliases :build :extra-deps])}
|
||||||
(b/uber {:class-dir class-dir
|
})))
|
||||||
:uber-file uber-file
|
|
||||||
:basis @basis
|
|
||||||
:main 'totp.app}))
|
;; Show basis generated for a subproject
|
||||||
|
#_{:clojure-lsp/ignore [:clojure-lsp/unused-public-var]}
|
||||||
|
(defn show-basis [subproj]
|
||||||
|
(println (with-out-str
|
||||||
|
(pp/pprint
|
||||||
|
@(basis subproj)
|
||||||
|
;(basis/initial-basis)
|
||||||
|
))))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(pp/pprint (keys (basis/initial-basis)))
|
||||||
|
(pp/pprint (:deps (basis/initial-basis)))
|
||||||
|
(pp/pprint (:libs (basis/initial-basis)))
|
||||||
|
(pp/pprint (sort (keys (:aliases (basis/initial-basis)))))
|
||||||
|
(get-in (basis/initial-basis) [:aliases :build :extra-deps])
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Target dir for each subproject
|
||||||
|
(defn target-dir [subproj]
|
||||||
|
(str "target/" subproj))
|
||||||
|
|
||||||
|
|
||||||
|
;; Path for compiled classes
|
||||||
|
(defn class-dir [subproj]
|
||||||
|
(str (target-dir subproj) "/" "classes"))
|
||||||
|
|
||||||
|
|
||||||
|
;; Jar file for each subproject. :uber type adds -standalone suffix
|
||||||
|
(defn jar-file [subproj version type]
|
||||||
|
(format "target/%s-%s-%s%s.jar" artifact-prefix subproj version
|
||||||
|
(if (= type :uber) "-standalone" "")))
|
||||||
|
|
||||||
|
|
||||||
|
;; Clean target dir for subproject
|
||||||
|
(defn clean [{:keys [subproj]}]
|
||||||
|
(b/delete {:path (target-dir subproj)})
|
||||||
|
(println "Project" subproj "cleaned"))
|
||||||
|
|
||||||
|
|
||||||
|
;; Compile java classes, only if java subdir exists
|
||||||
|
(defn compile-java [subproj]
|
||||||
|
(let [java-dir (str subprojs-base "/" subproj "/java")]
|
||||||
|
(when (.exists (io/file java-dir))
|
||||||
|
(println "Compiling java code for" subproj)
|
||||||
|
(b/javac {:src-dirs [java-dir]
|
||||||
|
:class-dir (class-dir subproj)
|
||||||
|
:basis @(basis subproj)
|
||||||
|
:javac-opts ["-source" "11" "--target" "11" "-proc:none"]}))))
|
||||||
|
|
||||||
|
|
||||||
|
;; Create a jar file
|
||||||
|
(defn jar
|
||||||
|
"Build a simple jar file, with no dependencies included."
|
||||||
|
[{:keys [subproj version]
|
||||||
|
:or {version curr-version}}]
|
||||||
|
(let [target-dir (target-dir subproj)
|
||||||
|
class-dir (class-dir subproj)
|
||||||
|
src-dir (str subprojs-base "/" subproj "/src")
|
||||||
|
resources-dir (str subprojs-base "/" subproj "/resources")
|
||||||
|
basis (basis subproj)
|
||||||
|
jar-file (jar-file subproj version :plain)]
|
||||||
|
;; Clean only class dir
|
||||||
|
(b/delete {:path class-dir})
|
||||||
|
;; Copy code
|
||||||
|
(b/copy-dir {:src-dirs [src-dir]
|
||||||
|
:target-dir class-dir})
|
||||||
|
;; Copy resources
|
||||||
|
(b/copy-dir {:src-dirs [resources-dir]
|
||||||
|
:target-dir target-dir})
|
||||||
|
;; Compile java code, if exists
|
||||||
|
(compile-java subproj)
|
||||||
|
;; Build jar
|
||||||
|
(b/jar {:class-dir class-dir
|
||||||
|
:basis @basis
|
||||||
|
:jar-file jar-file
|
||||||
|
:lib (lib subproj)
|
||||||
|
:version version})
|
||||||
|
(println "Generated jar file:" jar-file)))
|
||||||
|
|
||||||
|
|
||||||
|
;; Create an uber jar, with all dependencies inside
|
||||||
|
#_{:clojure-lsp/ignore [:clojure-lsp/unused-public-var]}
|
||||||
|
(defn uber
|
||||||
|
"Build a uberjar with all dependencies included"
|
||||||
|
[{:keys [subproj version main-ns]
|
||||||
|
:or {version curr-version}}]
|
||||||
|
(let [target-dir (target-dir subproj)
|
||||||
|
basis (basis subproj)
|
||||||
|
class-dir (class-dir subproj)
|
||||||
|
src-dir (str subprojs-base "/" subproj "/src")
|
||||||
|
resources-dir (str subprojs-base "/" subproj "/resources")
|
||||||
|
uber-file (jar-file subproj version :uber)]
|
||||||
|
;(println "Using basis: ")(show-basis subproj)
|
||||||
|
(b/delete {:path class-dir})
|
||||||
|
(b/copy-dir {:src-dirs [src-dir]
|
||||||
|
:target-dir class-dir})
|
||||||
|
(b/copy-dir {:src-dirs [resources-dir]
|
||||||
|
:target-dir target-dir})
|
||||||
|
(compile-java subproj)
|
||||||
|
(b/compile-clj {:basis @basis
|
||||||
|
:src-dirs [src-dir] :class-dir class-dir})
|
||||||
|
(b/uber {:class-dir class-dir
|
||||||
|
:uber-file uber-file
|
||||||
|
:basis @basis
|
||||||
|
:main main-ns})
|
||||||
|
(println "Generated uberjar executable:" uber-file)))
|
||||||
|
|
||||||
|
|
||||||
|
;; Multimethod to get the name of all subdirs in a dir.
|
||||||
|
;; Accepts strings or files
|
||||||
|
(defmulti get-subdirs type)
|
||||||
|
|
||||||
|
(defmethod get-subdirs
|
||||||
|
java.lang.String [dir]
|
||||||
|
(get-subdirs (io/file dir)))
|
||||||
|
|
||||||
|
(defmethod get-subdirs
|
||||||
|
java.io.File [dir]
|
||||||
|
(if (.isDirectory dir)
|
||||||
|
(filter #(.isDirectory %) (.listFiles dir))
|
||||||
|
(println "Directory" subprojs-base "doesn't exists!")))
|
||||||
|
|
||||||
|
|
||||||
|
;; Get the name of all subdir in a given directory
|
||||||
|
(defn get-subdir-names
|
||||||
|
"Get a list projects in the 'projects' directory"
|
||||||
|
[dir-name]
|
||||||
|
(map #(.getName %) (get-subdirs dir-name)))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(get-subdirs "projects")
|
||||||
|
(get-subdirs (io/file "projects"))
|
||||||
|
(get-subdir-names "projects")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
;; Generate jar files for all projects
|
||||||
|
(defn jar-all
|
||||||
|
"Build jar files for all projects"
|
||||||
|
[& {:keys [version]
|
||||||
|
:or {version curr-version}}]
|
||||||
|
(dorun (map #(jar {:subproj % :version version}) (get-subdir-names subprojs-base))))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(jar-all )
|
||||||
|
|
||||||
|
)
|
||||||
42
clj-totp.iml
42
clj-totp.iml
@@ -1,42 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="JAVA_MODULE" version="4">
|
|
||||||
<component name="BuildSystem">
|
|
||||||
<option name="buildSystemId" value="CLOJURE_DEPS" />
|
|
||||||
<option name="displayName" value="clj-totp" />
|
|
||||||
</component>
|
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
|
||||||
<exclude-output />
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
<orderEntry type="library" name="Deps: org.clojure/clojure:1.12.1" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: lambdaisland/deep-diff2:2.11.216" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: org.clojure/core.specs.alpha:0.4.74" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: lambdaisland/kaocha:1.91.1392" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: expound:0.9.0" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: org.clojure/spec.alpha:0.5.238" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: org.clojure/tools.cli:1.1.230" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: lambdaisland/clj-diff:1.4.78" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: net.incongru.watchservice/barbary-watchservice:1.0" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: slingshot:0.12.2" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: fipp:0.6.26" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: com.nextjournal/beholder:1.0.2" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: aero:1.1.6" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: lambdaisland/tools.namespace:0.3.256" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: mvxcvi/arrangement:2.1.0" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: io.methvin/directory-watcher:0.17.3" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: progrock:0.1.2" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: org.clojure/java.classpath:1.0.0" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: clojure.java-time:1.4.3" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: org.clojure/core.rrb-vector:0.1.2" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: net.java.dev.jna/jna:5.12.1" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: org.clojure/tools.reader:1.3.6" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: org.tcrawley/dynapath:1.1.0" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: org.slf4j/slf4j-api:1.7.36" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: hawk:0.2.11" level="project" />
|
|
||||||
<orderEntry type="library" name="Deps: meta-merge:1.0.0" level="project" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
4
compile_proto.sh
Executable file
4
compile_proto.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/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
|
||||||
76
deps.edn
76
deps.edn
@@ -1,24 +1,68 @@
|
|||||||
{:paths ["src"]
|
{:paths ["src" "resources" "target/classes"]
|
||||||
:deps {org.clojure/clojure {:mvn/version "1.12.1"}
|
:deps {org.clojure/clojure {:mvn/version "1.12.1"}
|
||||||
io.github.clojure/tools.build {:mvn/version "0.10.10"}
|
;; Native image (GraalVM). Tutorial: https://shagunagrawal.me/posts/setup-clojure-with-graalvm-for-native-image/
|
||||||
mvxcvi/alphabase {:mvn/version "3.0.185"} ;; https://github.com/greglook/alphabase
|
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"}
|
||||||
cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic
|
;; Local subprojects
|
||||||
;; For SQLite
|
clj-totp/core {:local/root "projects/core"}
|
||||||
com.github.seancorfield/next.jdbc {:mvn/version "1.3.1048"}
|
clj-totp/cli {:local/root "projects/cli"}
|
||||||
org.xerial/sqlite-jdbc {:mvn/version "3.50.3.0"}
|
clj-totp/web {:local/root "projects/web"}
|
||||||
;; For Datomic local
|
clj-totp/gui {:local/root "projects/gui"}
|
||||||
com.datomic/local {:mvn/version "1.0.291"};; https://docs.datomic.com/datomic-local.html
|
}
|
||||||
;; Native image (GraalVM)
|
|
||||||
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"}};; Tutorial: https://shagunagrawal.me/posts/setup-clojure-with-graalvm-for-native-image/
|
|
||||||
|
|
||||||
:aliases {;; Execute the app
|
:aliases {;; Execute the app.
|
||||||
:run {:main-opts ["-m" "totp.app"]}
|
:run {:main-opts ["-m" "totp.app"]}
|
||||||
|
;:run {:exec-fn totp.app/-main}
|
||||||
|
|
||||||
|
;; Execute the app (prepared for more subprojects)
|
||||||
|
:run/cli {:main-opts ["-m" "totp.app"]}
|
||||||
|
|
||||||
|
:run/gui {:main-opts ["-m" "totp.gui"]}
|
||||||
|
|
||||||
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
||||||
:test {:extra-paths ["test"] ;; https://cljdoc.org/d/uberdeps/uberdeps/1.4.0/doc/readme
|
;; Check test.edn for kaocha runner's config
|
||||||
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
|
:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}
|
||||||
|
lambdaisland/kaocha-cloverage {:mvn/version "1.1.89"}}
|
||||||
:main-opts ["-m" "kaocha.runner"]}
|
:main-opts ["-m" "kaocha.runner"]}
|
||||||
|
|
||||||
;; Run with clj -T:build function-in-build
|
;; Run with clj -T:build function-in-build
|
||||||
:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
||||||
:ns-default build}}}
|
;; Used by all compilations
|
||||||
|
:extra-deps {clj-totp/core {:local/root "projects/core"}}
|
||||||
|
:ns-default build}
|
||||||
|
|
||||||
|
;; Aliases for easy building
|
||||||
|
:build/core {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
||||||
|
:ns-default build
|
||||||
|
:exec-fn jar
|
||||||
|
:exec-args {:subproj "core"}}
|
||||||
|
|
||||||
|
:build/cli {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}
|
||||||
|
clj-totp/core {:local/root "projects/core"}}
|
||||||
|
:ns-default build
|
||||||
|
:exec-fn jar
|
||||||
|
:exec-args {:subproj "cli"}}
|
||||||
|
|
||||||
|
:build/web {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
||||||
|
:replace-deps {clj-totp/core {:local/root "projects/core"}}
|
||||||
|
:ns-default build
|
||||||
|
:exec-fn jar
|
||||||
|
:exec-args {:subproj "web"}}
|
||||||
|
|
||||||
|
:build/gui {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
||||||
|
:replace-deps {clj-totp/core {:local/root "projects/core"}}
|
||||||
|
: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"}}
|
||||||
|
:ns-default build
|
||||||
|
:exec-fn jar-all}
|
||||||
|
|
||||||
|
;; Build uber jar for CLI app
|
||||||
|
: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"}}}}
|
||||||
|
|
||||||
|
|||||||
33
native.cmd
Normal file
33
native.cmd
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
REM YOUR LOCAL GRAAL VM INSTALLATION
|
||||||
|
set JAVA_HOME=D:\programas\graalvm-jdk-21.0.7+8.1
|
||||||
|
REM generated file
|
||||||
|
set BIN_FILE=totp
|
||||||
|
|
||||||
|
set DEST_DIR=C:\Users\rubencj\util
|
||||||
|
|
||||||
|
set PATH=%JAVA_HOME%\bin;%CLOJURE_HOME%;%PATH%
|
||||||
|
set NATIVE=%JAVA_HOME%\bin\native-image.cmd
|
||||||
|
|
||||||
|
|
||||||
|
echo Using GraalVM native compiler: %NATIVE%
|
||||||
|
|
||||||
|
echo Creating uberjar
|
||||||
|
clojure -T:build uber
|
||||||
|
|
||||||
|
set UBERJAR=
|
||||||
|
for /f "delims=" %%a in ('dir /b /s target\clj-totp-*-standalone.jar') do @set UBERJAR=%%a
|
||||||
|
echo Created uberjar: %UBERJAR%
|
||||||
|
|
||||||
|
echo "Creating native image"
|
||||||
|
cmd /c %NATIVE% -jar %UBERJAR% -o target\%BIN_FILE% -H:+ReportExceptionStackTraces --features=clj_easy.graal_build_time.InitClojureClasses --report-unsupported-elements-at-runtime --verbose --no-fallback -H:ReflectionConfigurationFiles=reflect_config.json -H:-CheckToolchain --initialize-at-build-time=org.fusesource.jansi.Ansi
|
||||||
|
|
||||||
|
|
||||||
|
echo Executable created: target\%BIN_FILE%.exe
|
||||||
|
|
||||||
|
copy target\%BIN_FILE%.exe %DEST_DIR%
|
||||||
|
echo Native image copied to %DEST_DIR%\%BIN_FILE%.exe
|
||||||
22
native.sh
22
native.sh
@@ -1,14 +1,30 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
NATIVE=~/.sdkman/candidates/java/21.0.2-graalce/bin/native-image
|
NATIVE=~/.sdkman/candidates/java/21.0.2-graalce/bin/native-image
|
||||||
UBERJAR=clj-topt-1.0.32-standalone.jar
|
|
||||||
BIN_FILE=totp
|
BIN_FILE=totp
|
||||||
|
|
||||||
echo "Creating uberjar"
|
echo "Creating uberjar"
|
||||||
clojure -T:build uber
|
clojure -T:build uber
|
||||||
|
UBERJAR=$(realpath --relative-to=target target/clj-totp-*-standalone.jar)
|
||||||
|
|
||||||
echo "Creating native image"
|
echo "Creating native image"
|
||||||
|
$NATIVE -jar target/$UBERJAR -o target/$BIN_FILE\
|
||||||
$NATIVE -jar target/$UBERJAR -o target/$BIN_FILE -H:+ReportExceptionStackTraces --features=clj_easy.graal_build_time.InitClojureClasses --report-unsupported-elements-at-runtime --verbose --no-fallback -H:ReflectionConfigurationFiles=./reflect_config.json
|
-H:+ReportExceptionStackTraces\
|
||||||
|
-H:ReflectionConfigurationFiles=./reflect_config.json\
|
||||||
|
--verbose --no-fallback\
|
||||||
|
--features=clj_easy.graal_build_time.InitClojureClasses\
|
||||||
|
--report-unsupported-elements-at-runtime\
|
||||||
|
--strict-image-heap\
|
||||||
|
-march=native\
|
||||||
|
-R:MaxHeapSize=10m\
|
||||||
|
--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'
|
||||||
|
|
||||||
echo "Executable created on target/$BIN_FILE"
|
echo "Executable created on target/$BIN_FILE"
|
||||||
|
cp target/$BIN_FILE ~/bin
|
||||||
|
echo "Copied to ~/bin/$BIN_FILE"
|
||||||
|
|
||||||
|
echo "Compress executable for distribution"
|
||||||
|
xz -fv target/$BIN_FILE
|
||||||
|
|||||||
15
projects/cli/deps.edn
Executable file
15
projects/cli/deps.edn
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
{:paths ["src" "resources" "target/classes"]
|
||||||
|
:deps {;clj-totp/core {:local/root "../core"}
|
||||||
|
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"}}
|
||||||
|
|
||||||
|
:aliases {;; Execute the app
|
||||||
|
;:run {:main-opts ["-m" "totp.app"]}
|
||||||
|
|
||||||
|
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
||||||
|
:test {:extra-paths ["test"]
|
||||||
|
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
|
||||||
|
:main-opts ["-m" "kaocha.runner"]}}}
|
||||||
|
|
||||||
371
projects/cli/src/totp/app.clj
Normal file
371
projects/cli/src/totp/app.clj
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
(ns totp.app
|
||||||
|
#_{:clj-kondo/ignore [:refer-all]}
|
||||||
|
(:require [totp.core :refer :all]
|
||||||
|
[totp.data :refer :all]
|
||||||
|
[cli-matic.core :refer [run-cmd]]
|
||||||
|
[clojure.pprint :as pp]
|
||||||
|
[clojure.string :as str]
|
||||||
|
[progress.determinate :as pd])
|
||||||
|
(:import [java.util TimerTask Timer])
|
||||||
|
(:gen-class))
|
||||||
|
|
||||||
|
(def DEFAULT_BAR_STYLE :coloured-ascii-boxes)
|
||||||
|
|
||||||
|
(defn print-timer
|
||||||
|
([] (print-timer 1 30 DEFAULT_BAR_STYLE))
|
||||||
|
([bar-style] (print-timer 1 30 bar-style))
|
||||||
|
([start period bar-style]
|
||||||
|
(let [a (atom start)]
|
||||||
|
(pd/animate! a :opts {:total period
|
||||||
|
;:line 1
|
||||||
|
:label "Next TOTP: "
|
||||||
|
;:redraw-rate 60 ;; updates per second
|
||||||
|
:style (get pd/styles bar-style)}
|
||||||
|
;(println)
|
||||||
|
(run! (fn [_] (Thread/sleep 1000) (swap! a inc)) (range start (inc period)))
|
||||||
|
;(println)
|
||||||
|
))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- print-confinuous
|
||||||
|
([secret] (print-confinuous secret "sha1" 6 30 true DEFAULT_BAR_STYLE))
|
||||||
|
([secret algorithm digits period bar bar-style]
|
||||||
|
(let [step-millis (* 1000 period)
|
||||||
|
now (System/currentTimeMillis)
|
||||||
|
delay (int (- step-millis (rem now step-millis)))
|
||||||
|
delay-sec (int (/ delay 1000))
|
||||||
|
fn-show (fn [s] (println (format "%n[%d] %s%n"
|
||||||
|
(System/currentTimeMillis)
|
||||||
|
(get-otp s algorithm digits period))))
|
||||||
|
task (proxy [TimerTask] []
|
||||||
|
(run [] (println) (fn-show secret)))
|
||||||
|
task-bar (proxy [TimerTask] []
|
||||||
|
(run [] (print-timer bar-style)))
|
||||||
|
task-init (proxy [TimerTask] []
|
||||||
|
(run [] (print-timer (- period delay-sec) period bar-style)))]
|
||||||
|
(println "\n <Generating continuosly, press enter to stop>\n")
|
||||||
|
;; (println "Now:" now ", Delay:" delay ", Next execution: " (+ now delay))
|
||||||
|
(println "Refresing in" delay-sec "seconds")
|
||||||
|
(fn-show secret)
|
||||||
|
;; Now, start the tasks
|
||||||
|
(when bar
|
||||||
|
(. (new Timer) (schedule task-init 0))
|
||||||
|
(. (new Timer) (scheduleAtFixedRate task-bar delay step-millis)))
|
||||||
|
(. (new Timer) (scheduleAtFixedRate task delay step-millis)))
|
||||||
|
;; Waits for a key press
|
||||||
|
(read-line)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn cmd-generate
|
||||||
|
[& {:keys [secret continuous algorithm digits period bar bar-style]}]
|
||||||
|
;;(pp/pprint opts)
|
||||||
|
(if continuous
|
||||||
|
(print-confinuous secret algorithm digits period bar bar-style)
|
||||||
|
(println (get-otp secret algorithm digits period))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn- print-app
|
||||||
|
[app]
|
||||||
|
(let [{:keys [name secret algorithm digits period]
|
||||||
|
:or {algorithm "sha1"
|
||||||
|
digits 6
|
||||||
|
period 30}} app]
|
||||||
|
(println (format "[%d] %12s -> %s" (System/currentTimeMillis) name (get-otp secret algorithm digits period)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- print-app-continuous
|
||||||
|
([period apps]
|
||||||
|
(let [step-millis (* 1000 period)
|
||||||
|
now (System/currentTimeMillis)
|
||||||
|
delay (int (- step-millis (rem now step-millis)))
|
||||||
|
delay-sec (int (/ delay 1000))
|
||||||
|
fn-show (fn [s]
|
||||||
|
(println "\n")
|
||||||
|
(dorun (map print-app s))
|
||||||
|
(println) ;; Separate each
|
||||||
|
)
|
||||||
|
task (proxy [TimerTask] []
|
||||||
|
(run [] (fn-show apps)))]
|
||||||
|
(println "\n <Generating continuosly, press enter to stop>\n")
|
||||||
|
;; (println "Now:" now ", Delay:" delay ", Next execution: " (+ now delay))
|
||||||
|
(println "Refresing in" delay-sec "seconds")
|
||||||
|
(fn-show apps)
|
||||||
|
;; Now, start the tasks
|
||||||
|
(. (new Timer) (scheduleAtFixedRate task delay step-millis)))
|
||||||
|
)) ;; Waits for a key press
|
||||||
|
|
||||||
|
|
||||||
|
(defn cmd-get-multi
|
||||||
|
[& {:keys [continuous bar bar-style _arguments]}]
|
||||||
|
;(pp/pprint opts)
|
||||||
|
(with-config
|
||||||
|
(let [apps (filter some? #_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||||
|
(map #(get-app cfg %) _arguments))]
|
||||||
|
;(println "found apps: " apps)
|
||||||
|
(if continuous
|
||||||
|
(let [period 30
|
||||||
|
step-millis (* 1000 period)
|
||||||
|
now (System/currentTimeMillis)
|
||||||
|
delay (int (- step-millis (rem now step-millis)))
|
||||||
|
delay-sec (int (/ delay 1000))
|
||||||
|
task-bar (proxy [TimerTask] []
|
||||||
|
(run [] (print-timer bar-style)))
|
||||||
|
task-init (proxy [TimerTask] []
|
||||||
|
(run [] (print-timer (- period delay-sec) period bar-style)))]
|
||||||
|
(print-app-continuous period apps)
|
||||||
|
(when bar
|
||||||
|
(. (new Timer) (schedule task-init 0))
|
||||||
|
(. (new Timer) (scheduleAtFixedRate task-bar delay step-millis)))
|
||||||
|
(read-line))
|
||||||
|
(dorun (map #(print-app %) apps))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn cmd-config
|
||||||
|
[& {:keys [command]}]
|
||||||
|
;;(pp/pprint opts)
|
||||||
|
(case command
|
||||||
|
"info" (println "Configuration file:"
|
||||||
|
(if (exists-config)
|
||||||
|
cfg-file
|
||||||
|
(str "not found. Expected location: " cfg-file)))
|
||||||
|
"init" (if (exists-config)
|
||||||
|
(do
|
||||||
|
(println "Configuration already exists, this will delete it. Are you sure? [N/y]")
|
||||||
|
(case (read-line)
|
||||||
|
"y" (create-cfg-file)
|
||||||
|
"Y" (create-cfg-file)
|
||||||
|
(println "Cancelling operation.")))
|
||||||
|
(create-cfg-file))
|
||||||
|
"show" (do
|
||||||
|
(println "Config file:\n")
|
||||||
|
;(println (slurp cfg-file))
|
||||||
|
(pp/print-table (load-config)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn cmd-list
|
||||||
|
[& {:keys [sorted mode]}]
|
||||||
|
(println "List mode:" mode)
|
||||||
|
(if (exists-config)
|
||||||
|
(case mode
|
||||||
|
"list" (let [apps-list (list-apps (load-config))
|
||||||
|
s-list (if sorted (sort apps-list) apps-list)]
|
||||||
|
(dorun (map #(println %) s-list)))
|
||||||
|
"table" (pp/print-table (load-config))
|
||||||
|
)
|
||||||
|
(println "Config file not found.")))
|
||||||
|
|
||||||
|
|
||||||
|
(defn cmd-add
|
||||||
|
[& {:keys [name secret user issuer algorithm digits period update] }]
|
||||||
|
;;(pp/pprint opts)
|
||||||
|
(when (not (exists-config))
|
||||||
|
(println "Config not found. Creating new config")
|
||||||
|
(create-cfg-file))
|
||||||
|
(let [cfg (load-config)]
|
||||||
|
(if (or update (nil? (get-app cfg name))) ;; get-app returns nil if app don't exists
|
||||||
|
(do
|
||||||
|
(store-config (add-app cfg name secret user issuer algorithm digits period))
|
||||||
|
(println "App" name "added or updated."))
|
||||||
|
(println "App" name "already exists.\nUse --update if you want to overwrite"))))
|
||||||
|
|
||||||
|
|
||||||
|
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||||
|
(defn cmd-import
|
||||||
|
[& {:keys [name url update] }]
|
||||||
|
(with-config
|
||||||
|
(if (or update (nil? (get-app cfg name)))
|
||||||
|
(cond
|
||||||
|
(str/starts-with? url "otpauth-migration")
|
||||||
|
(do
|
||||||
|
(store-config (import-from-url-export cfg name url))
|
||||||
|
(println "Import successful"))
|
||||||
|
(str/starts-with? url "otpauth")
|
||||||
|
(do
|
||||||
|
(store-config (import-from-url-create cfg name url))
|
||||||
|
(println "Import successful"))
|
||||||
|
:else (println "URL type not supported"))
|
||||||
|
(println "App" name "already exists.\nUse --update if you want to overwrite"))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn cmd-delete
|
||||||
|
[& {:keys [name force]}]
|
||||||
|
;;(pp/pprint opts)
|
||||||
|
(if (exists-config)
|
||||||
|
(let [cfg (load-config)]
|
||||||
|
(when (or force (some? (get-app cfg name)))
|
||||||
|
(store-config (delete-app cfg name))))
|
||||||
|
(println "Config file not found.")))
|
||||||
|
|
||||||
|
|
||||||
|
(def cli-options
|
||||||
|
{:app {:command "totp"
|
||||||
|
:version "1.2"
|
||||||
|
:description ["Generate a TOTP"]}
|
||||||
|
|
||||||
|
:commands [;; Generate a TOTP with given params
|
||||||
|
{:command "generate"
|
||||||
|
:description "Generate one TOTP for a BASE32 secret, ignoring configured apps"
|
||||||
|
:examples ["Generate one TOTP for a provided BASE32 secret:"
|
||||||
|
" totp generate ABCD1234"
|
||||||
|
"Generate one TOTP and refresh it continuosly:"
|
||||||
|
" totp generate -c ABCD1234"]
|
||||||
|
:opts [{:option "secret" :short 0
|
||||||
|
:as "Secret encoded in BASE32"
|
||||||
|
:type :string
|
||||||
|
:default :present}
|
||||||
|
{:option "continuous" :short "c"
|
||||||
|
:as "Contiuous mode"
|
||||||
|
:type :with-flag
|
||||||
|
:default false}
|
||||||
|
{:option "algorithm" :short "a"
|
||||||
|
:as "Algorithm used for the key generation"
|
||||||
|
:type #{"sha1" "sha256" "sha512"}
|
||||||
|
:default "sha1"}
|
||||||
|
{:option "digits" :short "d"
|
||||||
|
:as "Number of digits for OTP. Usually 6 or 8"
|
||||||
|
:type :int
|
||||||
|
:default 6}
|
||||||
|
{:option "period" :short "p"
|
||||||
|
:as "Validity time in seconds"
|
||||||
|
:type :int
|
||||||
|
:default 30}
|
||||||
|
{:option "bar" :short "b"
|
||||||
|
:as "Show progress bar"
|
||||||
|
:type :with-flag
|
||||||
|
:default true}
|
||||||
|
{:option "bar-style" :short "s"
|
||||||
|
:as "Progress bar style"
|
||||||
|
:type #{:ascii-basic :ascii-boxes :coloured-ascii-boxes :emoji-circles :emoji-boxes}
|
||||||
|
:default :coloured-ascii-boxes}]
|
||||||
|
:runs cmd-generate}
|
||||||
|
;; Generate a TOTP for a configured app
|
||||||
|
{:command "get" :short "g"
|
||||||
|
:description "Generate one TOTP for a configured app"
|
||||||
|
:examples ["Generate one TOTP for a provided app:"
|
||||||
|
" totp get app1"
|
||||||
|
"Generate one TOTP and refresh it continuosly:"
|
||||||
|
" totp get -c app1"]
|
||||||
|
:opts [{:option "name" :short 0
|
||||||
|
:as "Name of the previous configured app"
|
||||||
|
:type :string
|
||||||
|
:default :present}
|
||||||
|
{:option "continuous" :short "c"
|
||||||
|
:as "Contiuous mode"
|
||||||
|
:type :with-flag
|
||||||
|
:default false}
|
||||||
|
{:option "bar" :short "b"
|
||||||
|
:as "Show progress bar"
|
||||||
|
:type :with-flag
|
||||||
|
:default true}
|
||||||
|
{:option "bar-style" :short "s"
|
||||||
|
:as "Progress bar style"
|
||||||
|
:type #{:ascii-basic :ascii-boxes :coloured-ascii-boxes :emoji-circles :emoji-boxes}
|
||||||
|
:default :coloured-ascii-boxes}]
|
||||||
|
:runs cmd-get-multi}
|
||||||
|
;; Check and init your config file
|
||||||
|
{:command "config" :short "c"
|
||||||
|
:description "Manage configuration"
|
||||||
|
:examples ["Show location for the configuration file:"
|
||||||
|
" totp config info"
|
||||||
|
"Recreate config (warning: it will delete the existing config file):"
|
||||||
|
" totp config init"
|
||||||
|
"Show configuration:"
|
||||||
|
" totp config show"]
|
||||||
|
:opts [{:option "command" :short 0
|
||||||
|
:as "Command to execute. See examples"
|
||||||
|
:type #{"info" "init" "show"}
|
||||||
|
:default :present}]
|
||||||
|
:runs cmd-config}
|
||||||
|
;; List available apps
|
||||||
|
{:command "list" :short "l"
|
||||||
|
:description "List existing apps"
|
||||||
|
:examples ["List apps:"
|
||||||
|
" totp list"
|
||||||
|
"List apps sorted by name:"
|
||||||
|
" totp list --sorted"]
|
||||||
|
:opts [{:option "sorted" :short "s"
|
||||||
|
:as "If provided, the list will be sorted by name"
|
||||||
|
:type :with-flag
|
||||||
|
:default false}
|
||||||
|
{:option "mode" :short "m"
|
||||||
|
:as "How to show the list of configured apps"
|
||||||
|
:type #{"list" "table"}
|
||||||
|
:default "list"}]
|
||||||
|
:runs cmd-list}
|
||||||
|
;; Adds a new app to the configuration
|
||||||
|
{:command "add" :short "a"
|
||||||
|
:description "Add a new application with an unique name"
|
||||||
|
:examples ["Add a new application named 'app1' with a BASE32 secred, with defaults:"
|
||||||
|
" totp add app1 \"MJXW42LBORXQ====\""
|
||||||
|
"Add an application, with all posible configuration params:"
|
||||||
|
" topt add app2 \"MJXW42LBORXQ====\" -u \"user1@server\" -i my_provider -a sha1 -d 6 -p 30 --update"]
|
||||||
|
:opts [{:option "name" :short 0
|
||||||
|
:as "Unique name (alias) for the application"
|
||||||
|
:type :string
|
||||||
|
:default :present}
|
||||||
|
{:option "secret" :short 1
|
||||||
|
:as "Secret encoded in BASE32"
|
||||||
|
:type :string
|
||||||
|
:default :present}
|
||||||
|
{:option "user" :short "u"
|
||||||
|
:as "Username in the format <user>@<server>"
|
||||||
|
:type :string}
|
||||||
|
{:option "issuer" :short "i"
|
||||||
|
:as "The issuer (provider) of the service"
|
||||||
|
:type :string}
|
||||||
|
{:option "algorithm" :short "a"
|
||||||
|
:as "Algorithm used for the key generation"
|
||||||
|
:type #{"sha1" "sha256" "sha512"}
|
||||||
|
:default "sha1"}
|
||||||
|
{:option "digits" :short "d"
|
||||||
|
:as "Number of digits for OTP. Usually 6 or 8"
|
||||||
|
:type :int
|
||||||
|
:default 6}
|
||||||
|
{:option "period" :short "p"
|
||||||
|
:as "Validity time in seconds"
|
||||||
|
:type :int
|
||||||
|
:default 30}
|
||||||
|
{:option "update"
|
||||||
|
:as "Update an app with the same name if exists"
|
||||||
|
:type :with-flag
|
||||||
|
:default false}]
|
||||||
|
:runs cmd-add}
|
||||||
|
;; Import from URL
|
||||||
|
{:command "import" :short "i"
|
||||||
|
:description "Import a TOTP config from a URL"
|
||||||
|
:examples ["Import from a QR for creation"
|
||||||
|
" totp import app1 \"otpauth://totp/<label>?issuer=<issuer>&secret=<base32 secret>\""
|
||||||
|
"Import from a QR for exportation"
|
||||||
|
" totp import app2 \"otpauth-migration://offline?data=<exported data>\""]
|
||||||
|
:opts [{:option "name" :short 0
|
||||||
|
:as "Unique name of the application"
|
||||||
|
:type :string
|
||||||
|
:default :present}
|
||||||
|
{:option "url" :short 1
|
||||||
|
:as "Imported URL"
|
||||||
|
:type :string
|
||||||
|
:default :present}
|
||||||
|
{:option "update"
|
||||||
|
:as "Update an app with the same name if exists"
|
||||||
|
:type :with-flag
|
||||||
|
:default false}]
|
||||||
|
:runs cmd-import}
|
||||||
|
;; Deletes an existing app from configuration
|
||||||
|
{:command "delete" :short "d"
|
||||||
|
:description "Removes an existing application by it's unique name"
|
||||||
|
:examples ["Remove the application name app1"
|
||||||
|
" totp remove app1"]
|
||||||
|
:opts [{:option "name" :short 0
|
||||||
|
:as "Unique name of the application"
|
||||||
|
:type :string
|
||||||
|
:default :present}
|
||||||
|
{:option "force" :short "f"
|
||||||
|
:as "Don't ask, just remove"
|
||||||
|
:type :with-flag
|
||||||
|
:default false}]
|
||||||
|
:runs cmd-delete}]})
|
||||||
|
|
||||||
|
|
||||||
|
(defn -main [& args]
|
||||||
|
(run-cmd args cli-options))
|
||||||
1
projects/cli/tests.edn
Normal file
1
projects/cli/tests.edn
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#kaocha/v1 {}
|
||||||
1
projects/core/.cpcache/4091673994.basis
Normal file
1
projects/core/.cpcache/4091673994.basis
Normal file
File diff suppressed because one or more lines are too long
1
projects/core/.cpcache/4091673994.cp
Normal file
1
projects/core/.cpcache/4091673994.cp
Normal file
File diff suppressed because one or more lines are too long
1
projects/core/.cpcache/425892293.basis
Normal file
1
projects/core/.cpcache/425892293.basis
Normal file
File diff suppressed because one or more lines are too long
1
projects/core/.cpcache/425892293.cp
Normal file
1
projects/core/.cpcache/425892293.cp
Normal file
File diff suppressed because one or more lines are too long
2
projects/core/.cpcache/425892293.main
Normal file
2
projects/core/.cpcache/425892293.main
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-m
|
||||||
|
kaocha.runner
|
||||||
12
projects/core/deps.edn
Executable file
12
projects/core/deps.edn
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
{:paths ["src" "resources" "target/classes"]
|
||||||
|
:deps {org.clojure/clojure {:mvn/version "1.12.1"}
|
||||||
|
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"}
|
||||||
|
}
|
||||||
|
|
||||||
|
:aliases {;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
||||||
|
:test {:extra-paths ["test"]
|
||||||
|
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
|
||||||
|
:main-opts ["-m" "kaocha.runner"]}}}
|
||||||
|
|
||||||
2913
projects/core/java/protoc/OtpauthMigration.java
Normal file
2913
projects/core/java/protoc/OtpauthMigration.java
Normal file
File diff suppressed because it is too large
Load Diff
8
projects/core/resources/clj-totp.sh
Executable file
8
projects/core/resources/clj-totp.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/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 $@
|
||||||
39
projects/core/resources/proto/otpauth-migration.proto
Normal file
39
projects/core/resources/proto/otpauth-migration.proto
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
message MigrationPayload {
|
||||||
|
enum Algorithm {
|
||||||
|
ALGORITHM_UNSPECIFIED = 0;
|
||||||
|
ALGORITHM_SHA1 = 1;
|
||||||
|
ALGORITHM_SHA256 = 2;
|
||||||
|
ALGORITHM_SHA512 = 3;
|
||||||
|
ALGORITHM_MD5 = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DigitCount {
|
||||||
|
DIGIT_COUNT_UNSPECIFIED = 0;
|
||||||
|
DIGIT_COUNT_SIX = 1;
|
||||||
|
DIGIT_COUNT_EIGHT = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OtpType {
|
||||||
|
OTP_TYPE_UNSPECIFIED = 0;
|
||||||
|
OTP_TYPE_HOTP = 1;
|
||||||
|
OTP_TYPE_TOTP = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OtpParameters {
|
||||||
|
bytes secret = 1;
|
||||||
|
string name = 2;
|
||||||
|
string issuer = 3;
|
||||||
|
Algorithm algorithm = 4;
|
||||||
|
DigitCount digits = 5;
|
||||||
|
OtpType type = 6;
|
||||||
|
int64 counter = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
repeated OtpParameters otp_parameters = 1;
|
||||||
|
int32 version = 2;
|
||||||
|
int32 batch_size = 3;
|
||||||
|
int32 batch_index = 4;
|
||||||
|
int32 batch_id = 5;
|
||||||
|
}
|
||||||
127
projects/core/src/totp/core.clj
Normal file
127
projects/core/src/totp/core.clj
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
(ns totp.core
|
||||||
|
(:require [alphabase.base32 :as b32]
|
||||||
|
[clojure.math :as m])
|
||||||
|
(:import (javax.crypto Mac)
|
||||||
|
(javax.crypto.spec SecretKeySpec)
|
||||||
|
(java.util Base64 Arrays)
|
||||||
|
(java.nio ByteBuffer)))
|
||||||
|
|
||||||
|
(def ^:private byte-array-type (type (.getBytes "")))
|
||||||
|
|
||||||
|
(defn timestamp->steps
|
||||||
|
"Converts from UNIX timestamp in milliseconds to a number os steps of 's' seconds of duration"
|
||||||
|
[time, step-size]
|
||||||
|
(if (or (nil? time) (nil? step-size) (zero? step-size))
|
||||||
|
0
|
||||||
|
(int (quot time (* 1000 step-size)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn bytes-array?
|
||||||
|
"Return true if x is a byte[]"
|
||||||
|
[x]
|
||||||
|
(= byte-array-type (type x)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn get-alg
|
||||||
|
[alg]
|
||||||
|
(case alg
|
||||||
|
"sha1" "HmacSHA1"
|
||||||
|
"sha256" "HmacSHA256"
|
||||||
|
"sha512" "HmacSHA512"
|
||||||
|
""))
|
||||||
|
|
||||||
|
|
||||||
|
(defmulti hmac
|
||||||
|
"Generates an HMAC. Algorithms supported: sha1, sha256, sha512.
|
||||||
|
The key and the message can be (both) string or array of bytes, nil otherwise"
|
||||||
|
(fn [algorithm key message]
|
||||||
|
(cond
|
||||||
|
(and (string? key) (string? message) (some? (get-alg algorithm))) :string
|
||||||
|
(and (bytes-array? key) (bytes-array? message) (some? (get-alg algorithm))) :byte
|
||||||
|
:else :nil)))
|
||||||
|
|
||||||
|
;; By default
|
||||||
|
(defmethod hmac :nil [_ _ _]
|
||||||
|
nil)
|
||||||
|
|
||||||
|
;; When key and message are strings
|
||||||
|
(defmethod hmac :string [algorithm key message]
|
||||||
|
(if (or (empty? key) (empty? message))
|
||||||
|
""
|
||||||
|
(let [mac (doto (Mac/getInstance (get-alg algorithm)) (.init (SecretKeySpec. (.getBytes key) (get-alg algorithm))))
|
||||||
|
hmac-bytes (.doFinal mac (.getBytes message))]
|
||||||
|
;; Return the Base64 encoded HMAC
|
||||||
|
(.encodeToString (Base64/getEncoder) hmac-bytes))))
|
||||||
|
|
||||||
|
;; When key and message are arrays of bytes
|
||||||
|
(defmethod hmac :byte [algorithm key message]
|
||||||
|
(if (nil? message)
|
||||||
|
(bytes (byte-array 0))
|
||||||
|
(let [mac (doto (Mac/getInstance (get-alg algorithm)) (.init (SecretKeySpec. key (get-alg algorithm))))
|
||||||
|
hmac-bytes (.doFinal mac message)]
|
||||||
|
;; Return the Base64 encoded HMAC
|
||||||
|
(Base64/getEncoder) hmac-bytes)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn long->bytes
|
||||||
|
"Converts a long to an array of 8 bytes"
|
||||||
|
[l]
|
||||||
|
;;Java equivalent: ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(someLong).array();
|
||||||
|
(when (integer? l)
|
||||||
|
(-> (ByteBuffer/allocate (/ Long/SIZE Byte/SIZE))
|
||||||
|
(.putLong l)
|
||||||
|
(.array))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn bytes->int
|
||||||
|
"Converts an array of 4 bytes to an integer"
|
||||||
|
[bytes]
|
||||||
|
;;Java equivalent: ByteBuffer.wrap(data).getInt()
|
||||||
|
(when (some? bytes)
|
||||||
|
(.getInt (ByteBuffer/wrap bytes))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn get-otp
|
||||||
|
"Generate an OTP with the given secret (in base32) for the specified timestep"
|
||||||
|
([secret algorithm digits period] ;;algorithm digits period
|
||||||
|
(when (and secret period)
|
||||||
|
(let [step (timestamp->steps (System/currentTimeMillis) period)
|
||||||
|
k (b32/decode secret)
|
||||||
|
c (long->bytes step)
|
||||||
|
hs (hmac algorithm k c)
|
||||||
|
offset (bit-and (get hs (dec (count hs))) 0x0f) ;; int offset = hs[hs.length-1] & 0xf;
|
||||||
|
chunk (Arrays/copyOfRange hs offset (+ offset 4)) ;(take 4 (drop offset hs)) ;; byte[] chunk = Arrays.copyOfRange(hs, offset, offset+4)
|
||||||
|
]
|
||||||
|
(format (str "%0" digits "d")
|
||||||
|
(-> chunk
|
||||||
|
(bytes->int)
|
||||||
|
(bit-and 0x7fffffff)
|
||||||
|
(rem (int (m/pow 10 digits))))))))
|
||||||
|
([secret]
|
||||||
|
(get-otp secret "sha1" 6 30)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn calculate-offset-millis
|
||||||
|
[period]
|
||||||
|
(let [step-millis (* 1000 period)
|
||||||
|
now (System/currentTimeMillis)]
|
||||||
|
(int (rem now step-millis))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn calculate-delay-millis
|
||||||
|
[period]
|
||||||
|
(let [step-millis (* 1000 period)]
|
||||||
|
(- step-millis (calculate-offset-millis period))))
|
||||||
|
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(let [now (System/currentTimeMillis)
|
||||||
|
off (calculate-offset-millis 30)
|
||||||
|
delay (calculate-delay-millis 30)
|
||||||
|
]
|
||||||
|
(println "Now: " (int (/ now 1000)) "secs" now "millis")
|
||||||
|
(println "Offset:" (int (/ off 1000)) "secs" off "millis")
|
||||||
|
(println "Delay: " (int (/ delay 1000)) "secs" delay "millis")
|
||||||
|
(println "Total: " (+ (int (/ off 1000)) (int (/ delay 1000))) "secs" (+ delay off) "millis")
|
||||||
|
)
|
||||||
|
)
|
||||||
251
projects/core/src/totp/data.clj
Normal file
251
projects/core/src/totp/data.clj
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
(ns totp.data
|
||||||
|
(:require [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]))
|
||||||
|
|
||||||
|
(defn join-path
|
||||||
|
"Joins several subpaths using system's path separator (/ un *NIX and \\ in windows)"
|
||||||
|
[& col]
|
||||||
|
(str/join java.io.File/separator col))
|
||||||
|
|
||||||
|
|
||||||
|
(def cfg-path (join-path (System/getProperty "user.home") ".config" "totp"))
|
||||||
|
|
||||||
|
(def cfg-file (join-path cfg-path "apps.edn"))
|
||||||
|
|
||||||
|
(def cfg-header ";; clj-totp configuration file
|
||||||
|
;; This file contents a list of maps with :name and :secret entries
|
||||||
|
;; Secrets must be encoded in BASE32
|
||||||
|
|
||||||
|
")
|
||||||
|
|
||||||
|
|
||||||
|
(defn exists-config
|
||||||
|
"Checks if the config file exists"
|
||||||
|
[]
|
||||||
|
(.exists (io/file cfg-file)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn create-cfg-file
|
||||||
|
"Creates the config file"
|
||||||
|
[]
|
||||||
|
(println "Creating " cfg-file)
|
||||||
|
(io/delete-file cfg-file true)
|
||||||
|
(io/make-parents cfg-file)
|
||||||
|
(spit cfg-file cfg-header)
|
||||||
|
true)
|
||||||
|
|
||||||
|
|
||||||
|
(defn create-cfg?
|
||||||
|
"Create configuration file if not exists. Overridable with allways = true"
|
||||||
|
([] (create-cfg? false))
|
||||||
|
([allways]
|
||||||
|
(if (or allways (not (exists-config)))
|
||||||
|
(create-cfg-file)
|
||||||
|
false)))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(exists-config)
|
||||||
|
(create-cfg?))
|
||||||
|
|
||||||
|
|
||||||
|
(defn load-config
|
||||||
|
"Loads configuration from file"
|
||||||
|
[]
|
||||||
|
(e/read-string (slurp cfg-file)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn store-config
|
||||||
|
"Store configuration to file"
|
||||||
|
[cfg]
|
||||||
|
(when cfg
|
||||||
|
(spit cfg-file (str cfg-header (with-out-str
|
||||||
|
(binding [pp/*print-right-margin* 50]
|
||||||
|
(pp/pprint cfg)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn delete-app
|
||||||
|
[cfg name]
|
||||||
|
(filter #(not= name (:name %)) cfg))
|
||||||
|
|
||||||
|
|
||||||
|
(defn create-app
|
||||||
|
([name secret] (create-app name secret nil nil "sha1" 6 30))
|
||||||
|
([name secret user issuer] (create-app name secret user issuer "sha1" 6 30))
|
||||||
|
([name secret user issuer algorithm digits period]
|
||||||
|
{:name name :secret secret :user user :issuer issuer :algorithm algorithm :digits digits :period period}))
|
||||||
|
|
||||||
|
|
||||||
|
(defn add-app
|
||||||
|
([cfg app-map] (apply add-app (cons cfg (vals app-map))))
|
||||||
|
([cfg name secret] (add-app cfg name secret nil nil "sha1" 6 30))
|
||||||
|
([cfg name secret user issuer algorithm digits period]
|
||||||
|
(conj (delete-app cfg name) (create-app name secret user issuer algorithm digits period))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn list-apps
|
||||||
|
[cfg]
|
||||||
|
(->> cfg
|
||||||
|
(filter #(contains? % :name))
|
||||||
|
(map :name)))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(list-apps (load-config))
|
||||||
|
(with-config #_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||||
|
(list-apps cfg))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(defn get-app
|
||||||
|
[cfg name]
|
||||||
|
(let [app (first (filter #(= name (:name %)) cfg))]
|
||||||
|
(if app
|
||||||
|
app
|
||||||
|
(println "App" name "not found"))))
|
||||||
|
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(exists-config)
|
||||||
|
(create-cfg?)
|
||||||
|
(load-config)
|
||||||
|
|
||||||
|
(get-app [{:name "abc" :secret "def"} {:name "my-app" :secret "abc123"} {:name "another app" :secret "ABCDEF1234"}] "my-app2")
|
||||||
|
|
||||||
|
(with-out-str
|
||||||
|
(binding [pp/*print-right-margin* 50]
|
||||||
|
(pp/pprint [{:name "abc" :secret "def"} {:name "my-app" :secret "abc123"}])))
|
||||||
|
|
||||||
|
(store-config [{:name "abc" :secret "def"} {:name "my-app" :secret "abc123"} {:name "another app" :secret "ABCDEF1234"}])
|
||||||
|
|
||||||
|
(-> nil
|
||||||
|
(add-app "app1" "abc123abc123")
|
||||||
|
(add-app "app2" "abc123abc123")
|
||||||
|
(add-app "app1" "123456789012")
|
||||||
|
(store-config))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro with-config
|
||||||
|
"Loads config file and stores it in a cfg binding.
|
||||||
|
You can use the cfg var the inner code.
|
||||||
|
|
||||||
|
Next example will print config data:
|
||||||
|
(with-config (println cfg))
|
||||||
|
|
||||||
|
Be cafefull: dont use a binding called cfg in any module.
|
||||||
|
"
|
||||||
|
[form]
|
||||||
|
(let [cfg (symbol "cfg")] ;; This symbol will prevent error with qualified in the inner let
|
||||||
|
`(if (exists-config)
|
||||||
|
(let [~cfg (load-config)] ;; This is the problematic let binding. See: https://stackoverflow.com/a/15122414
|
||||||
|
(if (some? ~cfg)
|
||||||
|
(do ~form)))
|
||||||
|
(println "Config file not found"))))
|
||||||
|
|
||||||
|
|
||||||
|
(comment
|
||||||
|
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||||
|
(with-config (first cfg))
|
||||||
|
|
||||||
|
(macroexpand-1 '(with-config (first cfg)))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn url-create->app
|
||||||
|
"Import data from url using the protocol otpauth://
|
||||||
|
|
||||||
|
Example: otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
|
||||||
|
|
||||||
|
https://github.com/google/google-authenticator/wiki/Key-Uri-Format"
|
||||||
|
[name url]
|
||||||
|
(when (str/starts-with? url "otpauth://")
|
||||||
|
(let [parts (str/split url #"\?")
|
||||||
|
meta-parts (str/split (first parts) #"/" -1)
|
||||||
|
data-parts (str/split (second parts) #"&" -1)
|
||||||
|
otp-type (nth meta-parts 2)]
|
||||||
|
(if (not= "totp" otp-type) ;; Only totp is supported
|
||||||
|
(println "Invalid protocol OTP type:" otp-type)
|
||||||
|
(let [user-data (str/split (nth meta-parts 3) #":" -1)
|
||||||
|
issuer (first user-data)
|
||||||
|
user (second user-data)
|
||||||
|
;data-map (apply hash-map (flatten (map #(str/split % #"=") data-parts)))
|
||||||
|
data-map (reduce (fn [acc v] ;; From array to map
|
||||||
|
(let [[k v] (str/split v #"=")]
|
||||||
|
(assoc acc (keyword k) v)))
|
||||||
|
{} data-parts)
|
||||||
|
secret (:secret data-map)
|
||||||
|
;;issuer2 (:issuer data-map)
|
||||||
|
]
|
||||||
|
(create-app name secret user issuer))))))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(url-create->app "app1" "otpauth://totp/Reddit:errepunto?issuer=Reddit&secret=3RR2")
|
||||||
|
|
||||||
|
(url-create->app "app2" "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example")
|
||||||
|
|
||||||
|
(add-app [{:a 1 :b 2}] (url-create->app "app2" "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"))
|
||||||
|
)
|
||||||
|
|
||||||
|
(defn import-from-url-create
|
||||||
|
[cfg name url]
|
||||||
|
(add-app cfg (url-create->app name url)))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||||
|
(with-config (import-from-url-create cfg "app1" "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn url-export->app
|
||||||
|
[name url]
|
||||||
|
(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))
|
||||||
|
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))
|
||||||
|
]
|
||||||
|
(println "name:" name "user:" user "issuer:" issuer "digitsValue:" 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)))
|
||||||
|
)))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(url-export->app "test"
|
||||||
|
"otpauth-migration://offline?data=CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn import-from-url-export
|
||||||
|
"Import data from url using the protocol otpauth-migration://
|
||||||
|
|
||||||
|
https://alexbakker.me/post/parsing-google-auth-export-qr-code.html"
|
||||||
|
[cfg name url]
|
||||||
|
(add-app cfg (url-export->app name url)))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||||
|
(with-config (import-from-url-export
|
||||||
|
cfg
|
||||||
|
"app2"
|
||||||
|
"otpauth-migration://offline?data=CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
186
projects/core/src/totp/otp_import.clj
Normal file
186
projects/core/src/totp/otp_import.clj
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
(ns totp.otp-import
|
||||||
|
(:require [alphabase.bytes :as b]
|
||||||
|
[alphabase.base16 :as hex]
|
||||||
|
[alphabase.base64 :as b64]
|
||||||
|
[alphabase.base2 :as b2]
|
||||||
|
[clojure.math :as m]
|
||||||
|
[clojure.string :as s]))
|
||||||
|
|
||||||
|
;; Original description of the export protocol uses Google's Protocol Buffers
|
||||||
|
;; https://protobuf.dev/
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(let [encoded "CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA"
|
||||||
|
decoded (b64/decode encoded)]
|
||||||
|
(print (hex/encode decoded))
|
||||||
|
(print (b/to-string decoded))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defn len-bits
|
||||||
|
"How may blocks of n bits are needed to encode this number?
|
||||||
|
|
||||||
|
We use the following formula, for calculating the number of digits required
|
||||||
|
to encode the number n in the base b:
|
||||||
|
|
||||||
|
digits = ceil ( log_n ( x + 1 ) )
|
||||||
|
"
|
||||||
|
[x n]
|
||||||
|
(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
|
||||||
|
(when (and (>= x 0) (some? n) (> n 0))
|
||||||
|
(int (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)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(defn len-bytes
|
||||||
|
"How may bytes are needed to encode this number?"
|
||||||
|
[x]
|
||||||
|
(len-bits x 8))
|
||||||
|
|
||||||
|
|
||||||
|
(defn integer>bytes
|
||||||
|
"Converts an integer to a byte array"
|
||||||
|
[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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(defn decimal-to-base
|
||||||
|
"Converts a decimal number to an arbitrary base. Each digit is encoded as an
|
||||||
|
integer with value between 0 and base-1.
|
||||||
|
|
||||||
|
For example, 10 in base 4 will be encoded as:
|
||||||
|
[2 0 2]
|
||||||
|
, 127 in base 126 will be:
|
||||||
|
[1 1]
|
||||||
|
and so on.
|
||||||
|
|
||||||
|
We will use the sucessive division method: do a integer division of the number
|
||||||
|
to the base until the quotient is zero, and take the reminders backwards. For
|
||||||
|
example, if we want to converto 127 to base 5:
|
||||||
|
|
||||||
|
127 / 5 = 25 rem 2
|
||||||
|
25 / 5 = 5 rem 0
|
||||||
|
5 / 5 = 1 rem 0
|
||||||
|
1 / 5 = 0 rem 1
|
||||||
|
|
||||||
|
so, 127 in base 5 is 1002.
|
||||||
|
"
|
||||||
|
([n base]
|
||||||
|
(decimal-to-base n base true))
|
||||||
|
([n base reverse?]
|
||||||
|
(when (and n base)
|
||||||
|
(if (or (nil? n) (nil? base) (zero? n) (zero? base)) ;; Allways [0] for base zero or number zero
|
||||||
|
[0]
|
||||||
|
(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
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(decimal-to-base 8 2)
|
||||||
|
2r1000
|
||||||
|
)
|
||||||
|
|
||||||
|
(defn base-to-decimal
|
||||||
|
"Converts from an array with values in an arbitrary base into decimal values"
|
||||||
|
[n base]
|
||||||
|
(if (or (nil? n) (nil? base) (zero? (count n)))
|
||||||
|
0
|
||||||
|
(int (reduce-kv
|
||||||
|
(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])
|
||||||
|
|
||||||
|
(reduce-kv
|
||||||
|
(fn [acc k v]
|
||||||
|
(+ acc (* v (m/pow 2 k))))
|
||||||
|
0 (vec (reverse [1 0 0 0])))
|
||||||
|
|
||||||
|
(base-to-decimal [1 0 0 0] 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defn int->varint
|
||||||
|
"Converts a integer value to a varint, that is encoded in 7 bits, where the first
|
||||||
|
bit is used to indicate if there are more bytes.
|
||||||
|
|
||||||
|
For example, the number 255 is usually encoded in a byte as follows:
|
||||||
|
11111111
|
||||||
|
but in a varint it will turn to:
|
||||||
|
10000001 01111111
|
||||||
|
|
||||||
|
First byte has the MSB set to 1, because there is another byte after it. The
|
||||||
|
second byte is the last one, so the MSB is set to 0.
|
||||||
|
|
||||||
|
The result is a byte array.
|
||||||
|
"
|
||||||
|
[x]
|
||||||
|
(let [b128 (decimal-to-base x 128 false)]
|
||||||
|
(byte-array (conj
|
||||||
|
(vec (map #(bit-or 2r10000000 %) (butlast b128)))
|
||||||
|
(bit-and 2r01111111 (peek b128))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn to-fancy-bin
|
||||||
|
"Return a string with bits from number in a fancy manner"
|
||||||
|
[x]
|
||||||
|
(s/join " "
|
||||||
|
(map s/join
|
||||||
|
(partition 8 (b2/encode (byte-array (map #(b/to-byte %) x)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn to-fancy-hex
|
||||||
|
"Return a string with hex values from number in a fancy manner"
|
||||||
|
([x]
|
||||||
|
(to-fancy-hex x 2))
|
||||||
|
([x group-size]
|
||||||
|
(s/join " "
|
||||||
|
(map s/join
|
||||||
|
(partition group-size (hex/encode (byte-array (map #(b/to-byte %) x))))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(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)
|
||||||
|
|
||||||
|
|
||||||
|
(to-fancy-bin (int->varint Long/MAX_VALUE))
|
||||||
|
(to-fancy-bin (int->varint (dec Long/MAX_VALUE)))
|
||||||
|
|
||||||
|
(to-fancy-hex (int->varint (dec Long/MAX_VALUE)))
|
||||||
|
|
||||||
|
(to-fancy-bin [2r10010110 2r00000001])
|
||||||
|
|
||||||
|
[2r10010110 2r00000001]
|
||||||
|
[2r0010110 2r0000001]
|
||||||
|
)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
projects/core/target/classes/protoc/OtpauthMigration.class
Normal file
BIN
projects/core/target/classes/protoc/OtpauthMigration.class
Normal file
Binary file not shown.
BIN
projects/core/target/clj-totp-core-1.2.69-standalone.jar
Normal file
BIN
projects/core/target/clj-totp-core-1.2.69-standalone.jar
Normal file
Binary file not shown.
BIN
projects/core/target/clj-totp-core-1.2.69.jar
Normal file
BIN
projects/core/target/clj-totp-core-1.2.69.jar
Normal file
Binary file not shown.
8
projects/core/target/clj-totp.sh
Executable file
8
projects/core/target/clj-totp.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/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 $@
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
(ns totp.core-test
|
(ns totp.core-test
|
||||||
|
#_{:clj-kondo/ignore [:refer-all]}
|
||||||
(:require [clojure.test :refer :all]
|
(:require [clojure.test :refer :all]
|
||||||
[totp.core :refer :all]
|
[totp.core :refer :all]
|
||||||
[alphabase.base64 :as b64])
|
[alphabase.base64 :as b64])
|
||||||
@@ -25,21 +26,21 @@
|
|||||||
(is (= true (bytes-array? (.getBytes ""))))
|
(is (= true (bytes-array? (.getBytes ""))))
|
||||||
(is (= true (bytes-array? (bytes (byte-array [0 0 0 0 0 0 0 0])))))))
|
(is (= true (bytes-array? (bytes (byte-array [0 0 0 0 0 0 0 0])))))))
|
||||||
|
|
||||||
(deftest hmac-sha1-test
|
(deftest hmac-test
|
||||||
(testing "border cases"
|
(testing "border cases"
|
||||||
(is (= nil (hmac-sha1 nil nil)))
|
(is (= nil (hmac nil nil nil)))
|
||||||
(is (= nil (hmac-sha1 "" nil)))
|
(is (= nil (hmac nil "" nil)))
|
||||||
(is (= nil (hmac-sha1 nil "")))
|
(is (= nil (hmac nil nil "")))
|
||||||
(is (= nil (hmac-sha1 (.getBytes "") nil)))
|
(is (= nil (hmac nil (.getBytes "") nil)))
|
||||||
(is (= nil (hmac-sha1 nil (.getBytes ""))))
|
(is (= nil (hmac nil nil (.getBytes ""))))
|
||||||
(is (= "" (hmac-sha1 "" ""))))
|
(is (= "" (hmac "" "" ""))))
|
||||||
(testing "String params"
|
(testing "String params"
|
||||||
(is (= "63h3K4sN+c3NDEl3EGeA23jq/EY=" (hmac-sha1 "12345" "this is a message")))
|
(is (= "63h3K4sN+c3NDEl3EGeA23jq/EY=" (hmac "sha1" "12345" "this is a message")))
|
||||||
(is (= "MA+ieo7t7MeQfyZR/X52dB1aXDI=" (hmac-sha1 "12345" "this is a longer message
|
(is (= "MA+ieo7t7MeQfyZR/X52dB1aXDI=" (hmac "sha1" "12345" "this is a longer message
|
||||||
with some lines"))))
|
with some lines"))))
|
||||||
(testing "byte[] params"
|
(testing "byte[] params"
|
||||||
(is (Arrays/equals (b64/decode "63h3K4sN+c3NDEl3EGeA23jq/EY=") (hmac-sha1 (.getBytes "12345") (.getBytes "this is a message"))))
|
(is (Arrays/equals (b64/decode "63h3K4sN+c3NDEl3EGeA23jq/EY=") (hmac "sha1" (.getBytes "12345") (.getBytes "this is a message"))))
|
||||||
(is (Arrays/equals (b64/decode "MA+ieo7t7MeQfyZR/X52dB1aXDI=") (hmac-sha1 (.getBytes "12345") (.getBytes "this is a longer message
|
(is (Arrays/equals (b64/decode "MA+ieo7t7MeQfyZR/X52dB1aXDI=") (hmac "sha1" (.getBytes "12345") (.getBytes "this is a longer message
|
||||||
with some lines"))))))
|
with some lines"))))))
|
||||||
|
|
||||||
|
|
||||||
@@ -62,10 +63,10 @@
|
|||||||
|
|
||||||
(deftest get-otp-test
|
(deftest get-otp-test
|
||||||
(testing "Border cases"
|
(testing "Border cases"
|
||||||
(is (nil? (get-otp nil nil)))
|
(is (nil? (get-otp nil nil nil nil)))
|
||||||
(is (nil? (get-otp "" nil)))
|
(is (nil? (get-otp "" nil nil nil)))
|
||||||
(is (nil? (get-otp nil "")))
|
(is (nil? (get-otp nil "" nil nil)))
|
||||||
(is (nil? (get-otp nil 1000))))
|
(is (nil? (get-otp nil 1000 nil nil))))
|
||||||
(testing "Common usage"
|
(testing "Common usage"
|
||||||
(is (= "837552" (get-otp "MJXW42LBORXQ====" 10000)))
|
(is (= 6 (count (get-otp "MJXW42LBORXQ====" "sha1" 6 10000))))
|
||||||
(is (= 6 (count (get-otp "MJXW42LBORXQ===="))))))
|
(is (= 6 (count (get-otp "MJXW42LBORXQ===="))))))
|
||||||
83
projects/core/test/totp/otp_import_test.clj
Normal file
83
projects/core/test/totp/otp_import_test.clj
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
(ns totp.otp-import-test
|
||||||
|
#_{:clj-kondo/ignore [:refer-all]}
|
||||||
|
(:require [clojure.test :refer :all]
|
||||||
|
[totp.otp-import :refer :all]
|
||||||
|
[alphabase.bytes :as b]
|
||||||
|
[alphabase.base16 :as hex]
|
||||||
|
[alphabase.base64 :as b64]
|
||||||
|
[alphabase.base32 :as b32]))
|
||||||
|
|
||||||
|
|
||||||
|
(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)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(deftest len-bytes-test
|
||||||
|
(testing "Check required number of bytes to encode a number"
|
||||||
|
(is (= 0 (len-bytes nil)))
|
||||||
|
(is (= 1 (len-bytes 0)))
|
||||||
|
(is (= 1 (len-bytes 1)))
|
||||||
|
(is (= 1 (len-bytes 255)))
|
||||||
|
(is (= 2 (len-bytes 256)))
|
||||||
|
(is (= 2 (len-bytes 65535)))
|
||||||
|
(is (= 3 (len-bytes 65536)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(deftest integer>bytes-test
|
||||||
|
(testing "Convert several ints to a byte array"
|
||||||
|
(is (nil? (integer>bytes nil)))
|
||||||
|
(is (b/bytes= (b/init-bytes [0]) (integer>bytes 0)))
|
||||||
|
(is (b/bytes= (b/init-bytes [1]) (integer>bytes 1)))
|
||||||
|
(is (b/bytes= (b/init-bytes [-1]) (integer>bytes 255)))
|
||||||
|
(is (b/bytes= (b/init-bytes [1 0]) (integer>bytes 256)))
|
||||||
|
(is (b/bytes= (b/init-bytes [-1 -1]) (integer>bytes 65535)))
|
||||||
|
(is (b/bytes= (b/init-bytes [127 -1 -1 -1 -1 -1 -1 -1]) (integer>bytes Long/MAX_VALUE)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(deftest decimal-to-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)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(deftest base-to-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)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(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)))
|
||||||
|
))
|
||||||
1
projects/core/tests.edn
Normal file
1
projects/core/tests.edn
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#kaocha/v1 {}
|
||||||
7
projects/gui/deps.edn
Executable file
7
projects/gui/deps.edn
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
{:paths ["src" "target/classes"]
|
||||||
|
: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"}}
|
||||||
|
}
|
||||||
|
|
||||||
166
projects/gui/src/totp/gui.clj
Normal file
166
projects/gui/src/totp/gui.clj
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
(ns totp.gui
|
||||||
|
#_{:clj-kondo/ignore [:refer-all]}
|
||||||
|
(:require [totp.core :refer :all]
|
||||||
|
[totp.data :refer :all]
|
||||||
|
[clojure.pprint :as pp]
|
||||||
|
[seesaw.core :refer :all]
|
||||||
|
[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 ""))]]))
|
||||||
|
|
||||||
|
|
||||||
|
(defn copy-handler
|
||||||
|
"Copies TOTP from text with id field-id, to system clipboard"
|
||||||
|
[field-id e]
|
||||||
|
(let [b-name (str "#" field-id)
|
||||||
|
b-obj (select (to-root e) [(keyword b-name)])
|
||||||
|
b-text (config b-obj :text)]
|
||||||
|
(println "Copying text value: " 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))))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(make-otp-list)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(defn make-time-bar
|
||||||
|
"Make the progress bar with the remaining time"
|
||||||
|
[init-val]
|
||||||
|
(border-panel
|
||||||
|
:hgap 5
|
||||||
|
:center (progress-bar :id "timer-bar"
|
||||||
|
:value init-val
|
||||||
|
:max 30)
|
||||||
|
:east (text :id "timer-text"
|
||||||
|
:text init-val
|
||||||
|
:editable? false
|
||||||
|
:columns 2
|
||||||
|
:halign :right)
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(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"
|
||||||
|
;:east "EAST"
|
||||||
|
;:west "WEST"
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(defn make-frame
|
||||||
|
"Make main frame's content"
|
||||||
|
[]
|
||||||
|
(frame :title "TOTP",
|
||||||
|
:content (make-frame-content)
|
||||||
|
;:minimum-size [320 :by 240]
|
||||||
|
;;:on-close :exit
|
||||||
|
:on-close :dispose))
|
||||||
|
|
||||||
|
|
||||||
|
(defn update-totps
|
||||||
|
"Update all totps"
|
||||||
|
[root]
|
||||||
|
(let [apps (with-config (filter some? #_{:clj-kondo/ignore [:unresolved-symbol]} cfg))]
|
||||||
|
(doseq [app apps]
|
||||||
|
(let [{:keys [name secret algorithm digits period]} app
|
||||||
|
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)
|
||||||
|
(config! field :text current-otp)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn update-progress
|
||||||
|
[root]
|
||||||
|
(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))
|
||||||
|
(config! time-bar :value offset)
|
||||||
|
(config! time-text :text offset)
|
||||||
|
(when (= 1 offset)
|
||||||
|
(println "update TOTP")
|
||||||
|
(update-totps root))
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(defn start-updater
|
||||||
|
[root]
|
||||||
|
(let [now (System/currentTimeMillis)
|
||||||
|
now-seconds (int (/ now 1000))
|
||||||
|
delay (- 1000 (- now (* 1000 now-seconds)))]
|
||||||
|
(. (new Timer) (scheduleAtFixedRate
|
||||||
|
(proxy [TimerTask] []
|
||||||
|
(run [] (update-progress root)))
|
||||||
|
delay 1000))
|
||||||
|
(println "Now" now "Delay" delay)
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(defn -main [& args]
|
||||||
|
(native!)
|
||||||
|
(invoke-later
|
||||||
|
(-> (make-frame)
|
||||||
|
pack!
|
||||||
|
show!
|
||||||
|
start-updater))
|
||||||
|
(println "Gui started"))
|
||||||
|
|
||||||
|
|
||||||
|
(comment
|
||||||
|
;; This kills your REPL connection
|
||||||
|
(-main)
|
||||||
|
|
||||||
|
(show-options (frame))
|
||||||
|
(show-options (text))
|
||||||
|
|
||||||
|
)
|
||||||
18
projects/web/deps.edn
Executable file
18
projects/web/deps.edn
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
{:paths ["src" "resources" "target/classes"]
|
||||||
|
:deps {;clj-totp/core {:local/root "../core"}
|
||||||
|
org.clojure/clojure {:mvn/version "1.12.1"}
|
||||||
|
;; For SQLite
|
||||||
|
com.github.seancorfield/next.jdbc {:mvn/version "1.3.1048"}
|
||||||
|
org.xerial/sqlite-jdbc {:mvn/version "3.50.3.0"}
|
||||||
|
;; For Datomic local
|
||||||
|
com.datomic/local {:mvn/version "1.0.291"};; https://docs.datomic.com/datomic-local.html
|
||||||
|
}
|
||||||
|
|
||||||
|
:aliases {;; Execute the app
|
||||||
|
;:run {:main-opts ["-m" "totp.app"]}
|
||||||
|
|
||||||
|
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
||||||
|
:test {:extra-paths ["test"]
|
||||||
|
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
|
||||||
|
:main-opts ["-m" "kaocha.runner"]}}}
|
||||||
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
(ns totp.db.datomic
|
(ns totp.db.datomic
|
||||||
(:require [datomic.client.api :as d]))
|
(:require [totp.data :as data]
|
||||||
|
[datomic.client.api :as d]))
|
||||||
|
|
||||||
|
|
||||||
|
(def cfg-path (data/join-path (System/getProperty "user.home") ".config" "totp"))
|
||||||
(def db-path (str cfg-path java.io.File/separator "data"))
|
(def db-path (str cfg-path java.io.File/separator "data"))
|
||||||
|
|
||||||
(def cfg {:server-type :datomic-local
|
(def cfg {:server-type :datomic-local
|
||||||
1
projects/web/tests.edn
Normal file
1
projects/web/tests.edn
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#kaocha/v1 {}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
(ns totp.app
|
|
||||||
(:require [totp.core :refer :all]
|
|
||||||
[totp.data :refer :all]
|
|
||||||
[cli-matic.core :refer [run-cmd]]
|
|
||||||
[cli-matic.utils :as U]
|
|
||||||
[clojure.pprint :as pp])
|
|
||||||
(:import [java.util TimerTask Timer])
|
|
||||||
(:gen-class))
|
|
||||||
|
|
||||||
|
|
||||||
(defn- print-confinuous
|
|
||||||
([secret] (print-confinuous secret 30))
|
|
||||||
([secret step]
|
|
||||||
(let [step-millis (* 1000 step)
|
|
||||||
now (System/currentTimeMillis)
|
|
||||||
delay (int (- step-millis (rem now step-millis)))
|
|
||||||
fn-show (fn [s] (println (System/currentTimeMillis) "-> "(get-otp s)))
|
|
||||||
task (proxy [TimerTask] []
|
|
||||||
(run [] (fn-show secret)))]
|
|
||||||
(println "\n <Generating continuosly, press enter to stop>\n")
|
|
||||||
;; (println "Now:" now ", Delay:" delay ", Next execution: " (+ now delay))
|
|
||||||
(fn-show secret)
|
|
||||||
(. (new Timer) (scheduleAtFixedRate task delay step-millis)))
|
|
||||||
(read-line))) ;; Waits for a key press
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(print get-otp "MJXW42LBORXQ====")
|
|
||||||
(print-confinuous "MJXW42LBORXQ====")
|
|
||||||
)
|
|
||||||
|
|
||||||
(defn cmd-generate
|
|
||||||
[& {:keys [secret continuous] :as otps}]
|
|
||||||
;(pp/pprint otps)
|
|
||||||
(if continuous
|
|
||||||
(print-confinuous secret)
|
|
||||||
(println (get-otp secret))
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
(def cli-options
|
|
||||||
{:app {:command "totp"
|
|
||||||
:version "1.0.0"
|
|
||||||
:description ["Generate a TOTP"]}
|
|
||||||
|
|
||||||
:commands [{:command "generate" :short "g"
|
|
||||||
:description "Generate one TOTP with a given secret in BASE32"
|
|
||||||
:examples ["totp generate \"MJXW42LBORXQ====\""
|
|
||||||
"totp g \"MJXW42LBORXQ====\""]
|
|
||||||
:opts [{:option "secret"
|
|
||||||
:short 0
|
|
||||||
:as "Secret codified in BASE32"
|
|
||||||
:type :string
|
|
||||||
:default :present}
|
|
||||||
{:option "continuous"
|
|
||||||
:short "c"
|
|
||||||
:type :with-flag
|
|
||||||
:as "Contiuous mode"
|
|
||||||
:default false}]
|
|
||||||
:runs cmd-generate}]})
|
|
||||||
|
|
||||||
|
|
||||||
(defn -main [& args]
|
|
||||||
(run-cmd args cli-options))
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
(ns totp.core
|
|
||||||
(:require [alphabase.base32 :as b32])
|
|
||||||
(:import (javax.crypto Mac)
|
|
||||||
(javax.crypto.spec SecretKeySpec)
|
|
||||||
(java.util Base64 Arrays)
|
|
||||||
(java.nio ByteBuffer)))
|
|
||||||
|
|
||||||
(def ^:private byte-array-type (type (.getBytes "")))
|
|
||||||
|
|
||||||
(defn timestamp->steps
|
|
||||||
"Converts from UNIX timestamp in milliseconds to a number os steps of 's' seconds of duration"
|
|
||||||
[time, step-size]
|
|
||||||
(if (or (nil? time) (nil? step-size) (zero? step-size))
|
|
||||||
0
|
|
||||||
(int (quot time (* 1000 step-size)))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn bytes-array?
|
|
||||||
"Return true if x is a byte[]"
|
|
||||||
[x]
|
|
||||||
(= byte-array-type (type x)))
|
|
||||||
|
|
||||||
|
|
||||||
(defmulti hmac-sha1
|
|
||||||
"Generates an HMAC-SHA1. The key and the message can be (both) string or array of bytes, nil otherwise"
|
|
||||||
(fn [key message]
|
|
||||||
(cond
|
|
||||||
(and (string? key) (string? message)) :string
|
|
||||||
(and (bytes-array? key) (bytes-array? message)) :byte
|
|
||||||
:else :nil)))
|
|
||||||
|
|
||||||
;; By default
|
|
||||||
(defmethod hmac-sha1 :nil [_ _]
|
|
||||||
nil)
|
|
||||||
|
|
||||||
;; When key and message are strings
|
|
||||||
(defmethod hmac-sha1 :string [key message]
|
|
||||||
(if (or (empty? key) (empty? message))
|
|
||||||
""
|
|
||||||
(let [mac (doto (Mac/getInstance "HmacSHA1") (.init (SecretKeySpec. (.getBytes key) "HmacSHA1")))
|
|
||||||
hmac-bytes (.doFinal mac (.getBytes message))]
|
|
||||||
;; Return the Base64 encoded HMAC
|
|
||||||
(.encodeToString (Base64/getEncoder) hmac-bytes))))
|
|
||||||
|
|
||||||
;; When key and message are arrays of bytes
|
|
||||||
(defmethod hmac-sha1 :byte [key message]
|
|
||||||
(if (nil? message)
|
|
||||||
(bytes (byte-array 0))
|
|
||||||
(let [mac (doto (Mac/getInstance "HmacSHA1") (.init (SecretKeySpec. key "HmacSHA1")))
|
|
||||||
hmac-bytes (.doFinal mac message)]
|
|
||||||
;; Return the Base64 encoded HMAC
|
|
||||||
(Base64/getEncoder) hmac-bytes)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn long->bytes
|
|
||||||
"Converts a long to an array of 8 bytes"
|
|
||||||
[l]
|
|
||||||
;;Java equivalent: ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(someLong).array();
|
|
||||||
(when (integer? l)
|
|
||||||
(-> (ByteBuffer/allocate (/ Long/SIZE Byte/SIZE))
|
|
||||||
(.putLong l)
|
|
||||||
(.array))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn bytes->int
|
|
||||||
"Converts an array of 4 bytes to an integer"
|
|
||||||
[bytes]
|
|
||||||
;;Java equivalent: ByteBuffer.wrap(data).getInt()
|
|
||||||
(when (some? bytes)
|
|
||||||
(.getInt (ByteBuffer/wrap bytes))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn get-otp
|
|
||||||
"Generate an OTP with the given secret (in base32) for the specified timestep"
|
|
||||||
([secret step]
|
|
||||||
(when (and secret step)
|
|
||||||
(let [k (b32/decode secret)
|
|
||||||
c (long->bytes step)
|
|
||||||
hs (hmac-sha1 k c)
|
|
||||||
offset (bit-and (get hs (dec (count hs))) 0x0f) ;; int offset = hs[hs.length-1] & 0xf;
|
|
||||||
chunk (Arrays/copyOfRange hs offset (+ offset 4)) ;(take 4 (drop offset hs)) ;; byte[] chunk = Arrays.copyOfRange(hs, offset, offset+4)
|
|
||||||
]
|
|
||||||
(format "%06d" (-> chunk
|
|
||||||
(bytes->int)
|
|
||||||
(bit-and 0x7fffffff)
|
|
||||||
(rem 1000000))))))
|
|
||||||
([secret]
|
|
||||||
(get-otp secret (timestamp->steps (System/currentTimeMillis) 30))))
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
(ns totp.data
|
|
||||||
(:require [clojure.edn :as e]
|
|
||||||
[clojure.string :as str]
|
|
||||||
[clojure.java.io :as io]
|
|
||||||
[clojure.pprint :as pp]))
|
|
||||||
|
|
||||||
(defn join-path
|
|
||||||
"Joins several subpaths using system's path separator (/ un *NIX and \\ in windows)"
|
|
||||||
[& col]
|
|
||||||
(str/join java.io.File/separator col))
|
|
||||||
|
|
||||||
|
|
||||||
(def cfg-path (join-path (System/getProperty "user.home") ".config" "totp"))
|
|
||||||
|
|
||||||
(def cfg-file (join-path cfg-path "apps.edn"))
|
|
||||||
|
|
||||||
(def cfg-header ";; clj-totp configuration file
|
|
||||||
;; This file contents a list of maps with :name and :secret entries
|
|
||||||
;; Secrets must be encoded in BASE32
|
|
||||||
|
|
||||||
")
|
|
||||||
|
|
||||||
|
|
||||||
(defn exists-config
|
|
||||||
"Checks if the config file exists"
|
|
||||||
[]
|
|
||||||
(.exists (io/file cfg-file)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn create-cfg-file
|
|
||||||
"Creates the config file"
|
|
||||||
[]
|
|
||||||
(println "Creating " cfg-file)
|
|
||||||
(io/delete-file cfg-file true)
|
|
||||||
(io/make-parents cfg-file)
|
|
||||||
(spit cfg-file cfg-header)
|
|
||||||
true)
|
|
||||||
|
|
||||||
|
|
||||||
(defn create-cfg?
|
|
||||||
"Create configuration file if not exists. Overridable with allways = true"
|
|
||||||
([] (create-cfg? false))
|
|
||||||
([allways]
|
|
||||||
(if (or allways (not (exists-config)))
|
|
||||||
(create-cfg-file)
|
|
||||||
false)))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(exists-config)
|
|
||||||
(create-cfg?)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(defn load-config
|
|
||||||
"Loads configuration from file"
|
|
||||||
[]
|
|
||||||
(e/read-string (slurp cfg-file)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn store-config
|
|
||||||
"Store configuration to file"
|
|
||||||
[cfg]
|
|
||||||
(when cfg
|
|
||||||
(spit cfg-file (str cfg-header (with-out-str
|
|
||||||
(binding [pp/*print-right-margin* 50]
|
|
||||||
(pp/pprint cfg)))))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn delete-app
|
|
||||||
[cfg name]
|
|
||||||
(filter #(not= name (:name %)) cfg))
|
|
||||||
|
|
||||||
|
|
||||||
(defn add-app
|
|
||||||
[cfg name secret]
|
|
||||||
(conj (delete-app cfg name) {:name name :secret secret}))
|
|
||||||
|
|
||||||
|
|
||||||
(defn list-apps
|
|
||||||
[cfg]
|
|
||||||
(map :name cfg))
|
|
||||||
|
|
||||||
|
|
||||||
(defn get-app
|
|
||||||
[cfg name]
|
|
||||||
(first (filter #(= name (:name %)) cfg)))
|
|
||||||
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(exists-config)
|
|
||||||
(create-cfg?)
|
|
||||||
(load-config)
|
|
||||||
|
|
||||||
(with-out-str
|
|
||||||
(binding [pp/*print-right-margin* 50]
|
|
||||||
(pp/pprint [{:name "abc" :secret "def"} {:name "my-app" :secret "abc123"}])))
|
|
||||||
|
|
||||||
(store-config [{:name "abc" :secret "def"} {:name "my-app" :secret "abc123"}])
|
|
||||||
|
|
||||||
(-> nil
|
|
||||||
(add-app "app1" "abc123abc123")
|
|
||||||
(add-app "app2" "abc123abc123")
|
|
||||||
(add-app "app1" "123456789012")
|
|
||||||
(store-config))
|
|
||||||
|
|
||||||
)
|
|
||||||
@@ -1 +1,6 @@
|
|||||||
#kaocha/v1 {}
|
#kaocha/v1
|
||||||
|
{:tests [{:test-paths ["projects/core/src" "projects/core/test"]}]
|
||||||
|
:plugins [:kaocha.plugin/cloverage]
|
||||||
|
:cloverage/opts {:src-ns-path ["projects/core/src" "projects/core/test"]
|
||||||
|
:ns-regex ["totp\\..*(?<!test)$"] ;; All starting with "totp" but not ending by "test"
|
||||||
|
}}
|
||||||
812
use_example.cast
Normal file
812
use_example.cast
Normal file
@@ -0,0 +1,812 @@
|
|||||||
|
{"version": 2, "width": 130, "height": 40, "timestamp": 1759268913, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
|
||||||
|
[0.176989, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:48 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[1.762001, "o", "#"]
|
||||||
|
[3.322002, "o", " "]
|
||||||
|
[4.343358, "o", "L"]
|
||||||
|
[5.091238, "o", "\b\u001b[K"]
|
||||||
|
[5.587452, "o", "S"]
|
||||||
|
[5.746296, "o", "h"]
|
||||||
|
[5.797527, "o", "o"]
|
||||||
|
[5.877536, "o", "w"]
|
||||||
|
[6.021713, "o", " "]
|
||||||
|
[6.138177, "o", "a"]
|
||||||
|
[6.262353, "o", "l"]
|
||||||
|
[6.439976, "o", "l"]
|
||||||
|
[6.559555, "o", " "]
|
||||||
|
[6.698153, "o", "o"]
|
||||||
|
[6.854063, "o", "p"]
|
||||||
|
[6.949519, "o", "t"]
|
||||||
|
[7.743221, "o", "i"]
|
||||||
|
[7.810171, "o", "o"]
|
||||||
|
[8.002195, "o", "n"]
|
||||||
|
[8.102527, "o", "s"]
|
||||||
|
[9.149834, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[9.164586, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:48 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[13.250329, "o", "\u001b[7mtotp\u001b[27m"]
|
||||||
|
[14.043554, "o", "\r\u001b[C\u001b[Ctotp\r\n\u001b[?2004l\r"]
|
||||||
|
[14.056176, "o", "** ERROR: **\r\nNo sub-command specified.\r\n\r\n\r\n"]
|
||||||
|
[14.056426, "o", "NAME:\r\n totp - Generate a TOTP\r\n\r\nUSAGE:\r\n totp [global-options] command [command options] [arguments...]\r\n\r\nVERSION:\r\n 1.2\r\n\r\nCOMMANDS:\r\n generate Generate one TOTP for a BASE32 secret, ignoring configured apps\r\n get, g Generate one TOTP for a configured app\r\n config, c Manage configuration\r\n list, l List existing apps\r\n add, a Add a new application with an unique name\r\n import, i Import a TOTP config from a URL\r\n delete, d Removes an existing application by it's unique name\r\n\r\nGLOBAL OPTIONS:\r\n -?, --help\r\n\r\n"]
|
||||||
|
[14.075216, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:48 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;204;36;29m\u001b[0m "]
|
||||||
|
[17.138979, "o", "#"]
|
||||||
|
[17.583635, "o", " "]
|
||||||
|
[17.929988, "o", "g"]
|
||||||
|
[18.069244, "o", "e"]
|
||||||
|
[18.198196, "o", "n"]
|
||||||
|
[18.270251, "o", "e"]
|
||||||
|
[18.318014, "o", "r"]
|
||||||
|
[18.417589, "o", "a"]
|
||||||
|
[18.574066, "o", "t"]
|
||||||
|
[18.649313, "o", "e"]
|
||||||
|
[18.850539, "o", " "]
|
||||||
|
[19.006152, "o", "a"]
|
||||||
|
[19.126994, "o", " "]
|
||||||
|
[19.34653, "o", "s"]
|
||||||
|
[19.430616, "o", "i"]
|
||||||
|
[19.583429, "o", "m"]
|
||||||
|
[19.675425, "o", "p"]
|
||||||
|
[20.261871, "o", "l"]
|
||||||
|
[20.398368, "o", "e"]
|
||||||
|
[20.567488, "o", " "]
|
||||||
|
[20.886076, "o", "t"]
|
||||||
|
[21.001264, "o", "o"]
|
||||||
|
[21.125369, "o", "t"]
|
||||||
|
[21.241288, "o", "p"]
|
||||||
|
[22.32187, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[22.336755, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:48 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;204;36;29m\u001b[0m "]
|
||||||
|
[34.302699, "o", "\u001b[7mtotp\u001b[27m"]
|
||||||
|
[35.866271, "o", "\r\u001b[C\u001b[C\u001b[1Ptot"]
|
||||||
|
[37.185679, "o", "t"]
|
||||||
|
[37.617081, "o", "\b\u001b[K"]
|
||||||
|
[37.989199, "o", "p"]
|
||||||
|
[38.081063, "o", " "]
|
||||||
|
[43.738456, "o", "\u001b[7mgenerate JBSWY3DPEHPK3PXP\u001b[27m"]
|
||||||
|
[44.621944, "o", "\r\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[C\u001b[Cgenerate JBSWY3DPEHPK3PXP\r\n\u001b[?2004l\r"]
|
||||||
|
[44.635244, "o", "159410\r\n"]
|
||||||
|
[44.654801, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:49 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[47.843229, "o", "#"]
|
||||||
|
[48.209818, "o", " "]
|
||||||
|
[48.413046, "o", "n"]
|
||||||
|
[48.505043, "o", "i"]
|
||||||
|
[48.877725, "o", "c"]
|
||||||
|
[49.098866, "o", "e"]
|
||||||
|
[49.457645, "o", ","]
|
||||||
|
[49.516937, "o", " "]
|
||||||
|
[49.864976, "o", "l"]
|
||||||
|
[50.525075, "o", "\b\u001b[K"]
|
||||||
|
[50.945054, "o", "n"]
|
||||||
|
[51.000961, "o", "o"]
|
||||||
|
[51.133706, "o", "w"]
|
||||||
|
[51.465572, "o", ","]
|
||||||
|
[51.537081, "o", " "]
|
||||||
|
[53.649628, "o", "g"]
|
||||||
|
[53.816856, "o", "e"]
|
||||||
|
[53.921891, "o", "n"]
|
||||||
|
[53.969913, "o", "e"]
|
||||||
|
[54.630876, "o", "r"]
|
||||||
|
[54.725743, "o", "a"]
|
||||||
|
[54.861157, "o", "t"]
|
||||||
|
[54.932831, "o", "e"]
|
||||||
|
[55.074689, "o", " "]
|
||||||
|
[55.6697, "o", "*"]
|
||||||
|
[55.909582, "o", "a"]
|
||||||
|
[56.0267, "o", "n"]
|
||||||
|
[56.065065, "o", "d"]
|
||||||
|
[56.37366, "o", "*"]
|
||||||
|
[56.521083, "o", " "]
|
||||||
|
[56.734656, "o", "u"]
|
||||||
|
[56.921009, "o", "p"]
|
||||||
|
[57.025878, "o", "d"]
|
||||||
|
[57.105928, "o", "a"]
|
||||||
|
[57.221608, "o", "t"]
|
||||||
|
[57.29692, "o", "e"]
|
||||||
|
[58.005352, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[58.020059, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:49 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[64.138603, "o", "\u001b[7mtotp generate -c JBSWY3DPEHPK3PXP\u001b[27m"]
|
||||||
|
[65.09814, "o", "\r\u001b[C\u001b[Ctotp generate -c JBSWY3DPEHPK3PXP\r\n\u001b[?2004l\r"]
|
||||||
|
[65.110699, "o", "\r\n <Generating continuosly, press enter to stop>\r\n\r\n"]
|
||||||
|
[65.111, "o", "Refresing in 21 seconds\r\n"]
|
||||||
|
[65.111575, "o", "\r\n[1759268978747] 908667\r\n\r\n"]
|
||||||
|
[65.112729, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 9/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[66.114963, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 10/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[67.117234, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 11/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[68.12148, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 12/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[69.125246, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 13/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[70.129928, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 14/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[70.141157, "o", "\r\n"]
|
||||||
|
[70.161973, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:49 \u001b[0m\u001b[38;2;60;56;54m \u001b[0mtook \u001b[1;33m5s\u001b[0m \r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[72.341232, "o", "#"]
|
||||||
|
[73.225094, "o", " "]
|
||||||
|
[76.03286, "o", "l"]
|
||||||
|
[76.176549, "o", "e"]
|
||||||
|
[76.540956, "o", "\b\u001b[K"]
|
||||||
|
[76.692529, "o", "\b\u001b[K"]
|
||||||
|
[77.10447, "o", "w"]
|
||||||
|
[77.220492, "o", "i"]
|
||||||
|
[77.384462, "o", " "]
|
||||||
|
[77.744525, "o", "\b\u001b[K"]
|
||||||
|
[77.912457, "o", "\b\u001b[K"]
|
||||||
|
[78.08847, "o", "\b\u001b[K"]
|
||||||
|
[80.10545, "o", "w"]
|
||||||
|
[80.201343, "o", "e"]
|
||||||
|
[80.376923, "o", " "]
|
||||||
|
[80.797294, "o", "w"]
|
||||||
|
[80.86962, "o", "i"]
|
||||||
|
[81.096974, "o", "l"]
|
||||||
|
[81.256371, "o", "l"]
|
||||||
|
[81.352347, "o", " "]
|
||||||
|
[81.740674, "o", "s"]
|
||||||
|
[81.992616, "o", "t"]
|
||||||
|
[82.12436, "o", "o"]
|
||||||
|
[82.240303, "o", "r"]
|
||||||
|
[82.297093, "o", "e"]
|
||||||
|
[82.421136, "o", " "]
|
||||||
|
[84.272464, "o", "t"]
|
||||||
|
[84.372248, "o", "h"]
|
||||||
|
[84.496211, "o", "i"]
|
||||||
|
[84.622221, "o", "s"]
|
||||||
|
[84.746303, "o", " "]
|
||||||
|
[84.921847, "o", "c"]
|
||||||
|
[84.9407, "o", "o"]
|
||||||
|
[85.02135, "o", "n"]
|
||||||
|
[85.105464, "o", "f"]
|
||||||
|
[85.216459, "o", "i"]
|
||||||
|
[85.301386, "o", "g"]
|
||||||
|
[85.432453, "o", "u"]
|
||||||
|
[85.602839, "o", "r"]
|
||||||
|
[85.950362, "o", "\b\u001b[K"]
|
||||||
|
[86.096569, "o", "\b\u001b[K"]
|
||||||
|
[86.252261, "o", "\b\u001b[K"]
|
||||||
|
[86.384954, "o", "\b\u001b[K"]
|
||||||
|
[86.504287, "o", "\b\u001b[K"]
|
||||||
|
[86.636345, "o", "\b\u001b[K"]
|
||||||
|
[86.768411, "o", "\b\u001b[K"]
|
||||||
|
[87.220431, "o", "\b\u001b[K"]
|
||||||
|
[87.448385, "o", "d"]
|
||||||
|
[87.562457, "o", "a"]
|
||||||
|
[87.684574, "o", "t"]
|
||||||
|
[87.776284, "o", "e"]
|
||||||
|
[88.772657, "o", "\b\u001b[K"]
|
||||||
|
[88.896276, "o", "a"]
|
||||||
|
[90.472592, "o", " "]
|
||||||
|
[90.652377, "o", "u"]
|
||||||
|
[90.776294, "o", "s"]
|
||||||
|
[90.920295, "o", "i"]
|
||||||
|
[91.084256, "o", "n"]
|
||||||
|
[91.184256, "o", "g"]
|
||||||
|
[91.292257, "o", " "]
|
||||||
|
[91.380229, "o", "l"]
|
||||||
|
[91.560883, "o", "o"]
|
||||||
|
[91.648839, "o", "c"]
|
||||||
|
[91.765128, "o", "a"]
|
||||||
|
[91.864932, "o", "l"]
|
||||||
|
[91.992656, "o", " "]
|
||||||
|
[92.120836, "o", "c"]
|
||||||
|
[92.160275, "o", "o"]
|
||||||
|
[92.236934, "o", "n"]
|
||||||
|
[92.317385, "o", "f"]
|
||||||
|
[92.405098, "o", "i"]
|
||||||
|
[92.509016, "o", "g"]
|
||||||
|
[92.572204, "o", "u"]
|
||||||
|
[92.689152, "o", "r"]
|
||||||
|
[92.773425, "o", "a"]
|
||||||
|
[92.896795, "o", "t"]
|
||||||
|
[92.960195, "o", "i"]
|
||||||
|
[93.016197, "o", "o"]
|
||||||
|
[93.056162, "o", "n"]
|
||||||
|
[93.684574, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[93.699621, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:50 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[95.700291, "o", "#"]
|
||||||
|
[96.017982, "o", "f"]
|
||||||
|
[96.14066, "o", "i"]
|
||||||
|
[96.260872, "o", "r"]
|
||||||
|
[96.388124, "o", "s"]
|
||||||
|
[96.548141, "o", "t"]
|
||||||
|
[96.748096, "o", " "]
|
||||||
|
[97.684556, "o", "\b\u001b[K"]
|
||||||
|
[97.960221, "o", ","]
|
||||||
|
[98.068189, "o", " "]
|
||||||
|
[98.41609, "o", "w"]
|
||||||
|
[98.877924, "o", "\b\u001b[K"]
|
||||||
|
[100.213156, "o", "w"]
|
||||||
|
[100.341245, "o", "h"]
|
||||||
|
[100.430133, "o", "e"]
|
||||||
|
[100.536017, "o", "r"]
|
||||||
|
[100.635989, "o", "e"]
|
||||||
|
[100.793695, "o", " "]
|
||||||
|
[100.997081, "o", "i"]
|
||||||
|
[101.073151, "o", "s"]
|
||||||
|
[101.236518, "o", " "]
|
||||||
|
[101.372497, "o", "t"]
|
||||||
|
[101.472003, "o", "h"]
|
||||||
|
[101.551994, "o", "e"]
|
||||||
|
[101.684209, "o", " "]
|
||||||
|
[102.634076, "o", "c"]
|
||||||
|
[102.716541, "o", "o"]
|
||||||
|
[102.809801, "o", "n"]
|
||||||
|
[102.897178, "o", "f"]
|
||||||
|
[103.033125, "o", "i"]
|
||||||
|
[103.116694, "o", "g"]
|
||||||
|
[103.231916, "o", "u"]
|
||||||
|
[103.3456, "o", "r"]
|
||||||
|
[103.401105, "o", "a"]
|
||||||
|
[103.552214, "o", "t"]
|
||||||
|
[103.607996, "o", "i"]
|
||||||
|
[103.671962, "o", "o"]
|
||||||
|
[103.731929, "o", "n"]
|
||||||
|
[103.82512, "o", " "]
|
||||||
|
[103.968845, "o", "f"]
|
||||||
|
[104.065441, "o", "i"]
|
||||||
|
[104.245532, "o", "l"]
|
||||||
|
[104.308966, "o", "e"]
|
||||||
|
[104.592765, "o", "?"]
|
||||||
|
[104.949829, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[104.966403, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:50 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[110.735684, "o", "\u001b[7mtotp config info\u001b[27m"]
|
||||||
|
[111.585646, "o", "\r\u001b[C\u001b[Ctotp config info\r\n\u001b[?2004l\r"]
|
||||||
|
[111.599116, "o", "Configuration file: not found. Expected location: /home/ruben/.config/totp/apps.edn\r\n"]
|
||||||
|
[111.61847, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:50 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[117.168242, "o", "n"]
|
||||||
|
[117.215739, "o", "o"]
|
||||||
|
[117.355702, "o", " "]
|
||||||
|
[117.733603, "o", "\b\u001b[K"]
|
||||||
|
[117.908862, "o", "\b\u001b[K"]
|
||||||
|
[118.077682, "o", "\b\u001b[K"]
|
||||||
|
[118.444922, "o", "#"]
|
||||||
|
[118.693748, "o", " "]
|
||||||
|
[118.885488, "o", "n"]
|
||||||
|
[118.944353, "o", "o"]
|
||||||
|
[119.044626, "o", " "]
|
||||||
|
[119.212729, "o", "f"]
|
||||||
|
[119.325414, "o", "o"]
|
||||||
|
[119.384939, "o", "n"]
|
||||||
|
[119.640383, "o", "g"]
|
||||||
|
[119.936985, "o", "\b\u001b[K"]
|
||||||
|
[120.072503, "o", "\b\u001b[K"]
|
||||||
|
[120.216486, "o", "\b\u001b[K"]
|
||||||
|
[120.36447, "o", "\b\u001b[K"]
|
||||||
|
[120.471851, "o", "c"]
|
||||||
|
[120.584431, "o", "o"]
|
||||||
|
[120.65574, "o", "n"]
|
||||||
|
[120.731969, "o", "f"]
|
||||||
|
[120.848454, "o", "i"]
|
||||||
|
[120.924386, "o", "g"]
|
||||||
|
[121.027572, "o", "u"]
|
||||||
|
[121.112001, "o", "r"]
|
||||||
|
[121.189835, "o", "a"]
|
||||||
|
[121.343833, "o", "t"]
|
||||||
|
[121.451598, "o", "i"]
|
||||||
|
[121.483569, "o", "o"]
|
||||||
|
[121.544645, "o", "n"]
|
||||||
|
[121.693602, "o", " "]
|
||||||
|
[121.79612, "o", "f"]
|
||||||
|
[122.761645, "o", "o"]
|
||||||
|
[122.901749, "o", "r"]
|
||||||
|
[123.145145, "o", " "]
|
||||||
|
[123.304789, "o", "n"]
|
||||||
|
[123.372733, "o", "o"]
|
||||||
|
[123.473625, "o", "w"]
|
||||||
|
[124.049661, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[124.065701, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:50 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[130.128615, "o", "#"]
|
||||||
|
[130.692546, "o", " "]
|
||||||
|
[130.93938, "o", "l"]
|
||||||
|
[131.043615, "o", "e"]
|
||||||
|
[131.13983, "o", "s"]
|
||||||
|
[131.555945, "o", "\b\u001b[K"]
|
||||||
|
[131.663469, "o", "t"]
|
||||||
|
[131.723532, "o", "s"]
|
||||||
|
[131.843418, "o", " "]
|
||||||
|
[131.987492, "o", "a"]
|
||||||
|
[132.096224, "o", "d"]
|
||||||
|
[132.25656, "o", "d"]
|
||||||
|
[132.352497, "o", " "]
|
||||||
|
[132.572509, "o", "s"]
|
||||||
|
[132.692485, "o", "o"]
|
||||||
|
[132.796747, "o", "m"]
|
||||||
|
[132.92812, "o", "e"]
|
||||||
|
[133.040381, "o", " "]
|
||||||
|
[133.21634, "o", "c"]
|
||||||
|
[133.268543, "o", "o"]
|
||||||
|
[133.316484, "o", "n"]
|
||||||
|
[133.396408, "o", "f"]
|
||||||
|
[133.496, "o", "i"]
|
||||||
|
[133.584045, "o", "g"]
|
||||||
|
[134.036184, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[134.051459, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:50 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[135.546399, "o", "\u001b[7mtotp add test JBSWY3DPEHPK3PXP\u001b[27m"]
|
||||||
|
[136.875925, "o", "\r\u001b[C\u001b[Ctotp add test JBSWY3DPEHPK3PXP\r\n\u001b[?2004l\r"]
|
||||||
|
[136.889149, "o", "Config not found. Creating new config\r\nCreating /home/ruben/.config/totp/apps.edn\r\n"]
|
||||||
|
[136.890105, "o", "App test not found\r\n"]
|
||||||
|
[136.893221, "o", "App test added or updated.\r\n"]
|
||||||
|
[136.912416, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:50 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[140.795747, "o", "t"]
|
||||||
|
[140.88717, "o", "o"]
|
||||||
|
[141.015232, "o", "t"]
|
||||||
|
[141.139247, "o", "p"]
|
||||||
|
[141.299209, "o", " "]
|
||||||
|
[141.447423, "o", "l"]
|
||||||
|
[141.540391, "o", "i"]
|
||||||
|
[141.563636, "o", "s"]
|
||||||
|
[141.647284, "o", "t"]
|
||||||
|
[142.235768, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[142.248069, "o", "List mode: list\r\n"]
|
||||||
|
[142.248867, "o", "test\r\n"]
|
||||||
|
[142.267845, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:50 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[147.531311, "o", "#"]
|
||||||
|
[147.883753, "o", " "]
|
||||||
|
[148.339198, "o", "n"]
|
||||||
|
[148.407112, "o", "o"]
|
||||||
|
[148.519068, "o", "e"]
|
||||||
|
[148.56398, "o", "w"]
|
||||||
|
[149.175509, "o", "\b\u001b[K"]
|
||||||
|
[149.323071, "o", "\b\u001b[K"]
|
||||||
|
[149.431036, "o", "w"]
|
||||||
|
[149.739772, "o", ","]
|
||||||
|
[149.835045, "o", " "]
|
||||||
|
[150.267209, "o", "l"]
|
||||||
|
[150.395079, "o", "e"]
|
||||||
|
[150.467278, "o", "t"]
|
||||||
|
[150.719059, "o", "'"]
|
||||||
|
[150.931756, "o", "s"]
|
||||||
|
[151.184173, "o", " "]
|
||||||
|
[151.611112, "o", "u"]
|
||||||
|
[151.762964, "o", "s"]
|
||||||
|
[151.844066, "o", "e"]
|
||||||
|
[152.024966, "o", " "]
|
||||||
|
[152.295203, "o", "i"]
|
||||||
|
[152.347119, "o", "t"]
|
||||||
|
[153.263554, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[153.278788, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:51 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[155.303403, "o", "t"]
|
||||||
|
[156.036697, "o", "\b\u001b[K"]
|
||||||
|
[161.713757, "o", "\u001b[7mtotp get test\u001b[27m"]
|
||||||
|
[164.459119, "o", "\r\u001b[C\u001b[Ctotp get test\r\n\u001b[?2004l\r"]
|
||||||
|
[164.47357, "o", "[1759269078109] test -> 538187\r\n"]
|
||||||
|
[164.494047, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:51 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[167.643612, "o", "n"]
|
||||||
|
[167.743908, "o", "i"]
|
||||||
|
[167.907667, "o", "c"]
|
||||||
|
[168.11663, "o", "e"]
|
||||||
|
[168.679846, "o", "\b\u001b[K"]
|
||||||
|
[168.835568, "o", "\b\u001b[K"]
|
||||||
|
[168.987573, "o", "\b\u001b[K"]
|
||||||
|
[169.134704, "o", "\b\u001b[K"]
|
||||||
|
[169.664678, "o", "#"]
|
||||||
|
[169.944556, "o", "n"]
|
||||||
|
[170.036654, "o", "i"]
|
||||||
|
[170.655785, "o", "c"]
|
||||||
|
[170.720607, "o", "c"]
|
||||||
|
[170.871841, "o", "e"]
|
||||||
|
[171.087914, "o", ","]
|
||||||
|
[171.176657, "o", " "]
|
||||||
|
[171.728478, "o", "\b\u001b[K"]
|
||||||
|
[171.880337, "o", "\b\u001b[K"]
|
||||||
|
[172.028366, "o", "\b\u001b[K"]
|
||||||
|
[172.163433, "o", "\b\u001b[K"]
|
||||||
|
[172.307877, "o", "e"]
|
||||||
|
[172.80039, "o", ","]
|
||||||
|
[172.875822, "o", " "]
|
||||||
|
[173.263396, "o", "b"]
|
||||||
|
[173.32745, "o", "u"]
|
||||||
|
[173.592525, "o", "t"]
|
||||||
|
[173.744595, "o", " "]
|
||||||
|
[175.150913, "o", "i"]
|
||||||
|
[175.275145, "o", "t"]
|
||||||
|
[175.370513, "o", " "]
|
||||||
|
[175.680036, "o", "c"]
|
||||||
|
[175.780515, "o", "a"]
|
||||||
|
[175.87158, "o", "n"]
|
||||||
|
[175.971182, "o", " "]
|
||||||
|
[176.163563, "o", "b"]
|
||||||
|
[176.404005, "o", "e"]
|
||||||
|
[176.496397, "o", " "]
|
||||||
|
[176.77961, "o", "b"]
|
||||||
|
[177.07975, "o", "e"]
|
||||||
|
[177.539027, "o", "t"]
|
||||||
|
[177.682551, "o", "t"]
|
||||||
|
[177.827595, "o", "e"]
|
||||||
|
[177.924439, "o", "r"]
|
||||||
|
[178.038649, "o", ","]
|
||||||
|
[178.132454, "o", " "]
|
||||||
|
[178.272441, "o", "l"]
|
||||||
|
[178.351211, "o", "e"]
|
||||||
|
[178.471602, "o", "s"]
|
||||||
|
[178.971113, "o", "\b\u001b[K"]
|
||||||
|
[179.0676, "o", "t"]
|
||||||
|
[179.123609, "o", "s"]
|
||||||
|
[179.247413, "o", " "]
|
||||||
|
[180.082803, "o", "u"]
|
||||||
|
[180.2324, "o", "p"]
|
||||||
|
[180.394597, "o", "d"]
|
||||||
|
[180.482853, "o", "a"]
|
||||||
|
[180.648252, "o", "t"]
|
||||||
|
[180.723541, "o", "e"]
|
||||||
|
[181.03104, "o", " "]
|
||||||
|
[182.038726, "o", "\b\u001b[K"]
|
||||||
|
[182.669551, "o", "\b\u001b[K"]
|
||||||
|
[182.708688, "o", "\b\u001b[K"]
|
||||||
|
[182.748425, "o", "\b\u001b[K"]
|
||||||
|
[182.788279, "o", "\b\u001b[K"]
|
||||||
|
[182.829023, "o", "\b\u001b[K"]
|
||||||
|
[182.868831, "o", "\b\u001b[K"]
|
||||||
|
[182.908891, "o", "\b\u001b[K"]
|
||||||
|
[182.948819, "o", "\b\u001b[K"]
|
||||||
|
[183.134335, "o", "\b\u001b[K"]
|
||||||
|
[183.31039, "o", "\b\u001b[K"]
|
||||||
|
[183.462279, "o", "\b\u001b[K"]
|
||||||
|
[183.626415, "o", "\b\u001b[K"]
|
||||||
|
[184.226378, "o", " "]
|
||||||
|
[184.548189, "o", "w"]
|
||||||
|
[184.64351, "o", "i"]
|
||||||
|
[184.842896, "o", "t"]
|
||||||
|
[184.95423, "o", "h"]
|
||||||
|
[185.182882, "o", " "]
|
||||||
|
[185.410352, "o", "a"]
|
||||||
|
[185.531177, "o", "u"]
|
||||||
|
[185.626453, "o", "t"]
|
||||||
|
[185.71036, "o", "o"]
|
||||||
|
[185.798217, "o", " "]
|
||||||
|
[185.939366, "o", "u"]
|
||||||
|
[186.087955, "o", "p"]
|
||||||
|
[186.264704, "o", "d"]
|
||||||
|
[186.311424, "o", "a"]
|
||||||
|
[186.483026, "o", "t"]
|
||||||
|
[186.555381, "o", "e"]
|
||||||
|
[186.888118, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[186.904273, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:51 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[194.832903, "o", "\u001b[7mtotp get test -c -s ascii-basic\u001b[27m"]
|
||||||
|
[195.895065, "o", "\r\u001b[C\u001b[Ctotp get test -c -s ascii-basic\r\n\u001b[?2004l\r"]
|
||||||
|
[195.907922, "o", "\r\n <Generating continuosly, press enter to stop>\r\n\r\n"]
|
||||||
|
[195.908159, "o", "Refresing in 10 seconds\r\n\r\n\r\n"]
|
||||||
|
[195.908736, "o", "[1759269109544] test -> 993001\r\n\r\n"]
|
||||||
|
[195.910021, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m###################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 20/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[196.913521, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m#####################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 21/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[197.914821, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m#######################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 22/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[198.916125, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m########################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 23/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[199.917646, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m##########################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 24/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[200.918974, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m############################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 25/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[201.920305, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m##############################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 26/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[202.921624, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m###############################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 27/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[203.922865, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m#################################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 28/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[204.924224, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m###################################################\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 29/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[205.925493, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m####################################################\u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 30/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[206.365098, "o", "\r\n\r\n"]
|
||||||
|
[206.365278, "o", "[1759269120001] test -> 539013\r\n\r\n"]
|
||||||
|
[206.365606, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m##\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 1/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[206.910784, "o", "\r\u001b[K"]
|
||||||
|
[207.367214, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m####\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 2/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[208.368552, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m######\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 3/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[209.369936, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[39m[\u001b[m\u001b[m\u001b[49m\u001b[39m#######\u001b[m\u001b[m\u001b[49m\u001b[39m \u001b[m\u001b[m\u001b[49m\u001b[39m]\u001b[m\u001b[m\u001b[49m\u001b[39m 4/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[209.410133, "o", "\r\n"]
|
||||||
|
[209.430893, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:52 \u001b[0m\u001b[38;2;60;56;54m \u001b[0mtook \u001b[1;33m13s\u001b[0m \r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[210.699351, "o", "#"]
|
||||||
|
[211.038494, "o", " "]
|
||||||
|
[211.309807, "o", "s"]
|
||||||
|
[211.882298, "o", "\b\u001b[K"]
|
||||||
|
[211.949712, "o", "a"]
|
||||||
|
[211.99763, "o", "s"]
|
||||||
|
[212.105601, "o", " "]
|
||||||
|
[212.410208, "o", "y"]
|
||||||
|
[212.489736, "o", "o"]
|
||||||
|
[212.653977, "o", "u"]
|
||||||
|
[212.993745, "o", " "]
|
||||||
|
[213.370889, "o", "c"]
|
||||||
|
[213.470001, "o", "a"]
|
||||||
|
[213.53479, "o", "n"]
|
||||||
|
[213.663549, "o", " "]
|
||||||
|
[213.763578, "o", "s"]
|
||||||
|
[214.043568, "o", "e"]
|
||||||
|
[214.173681, "o", "e"]
|
||||||
|
[214.466255, "o", ","]
|
||||||
|
[214.549634, "o", " "]
|
||||||
|
[216.30996, "o", "t"]
|
||||||
|
[216.461652, "o", "e"]
|
||||||
|
[216.493704, "o", "h"]
|
||||||
|
[217.06972, "o", "\b\u001b[K"]
|
||||||
|
[217.205766, "o", "\b\u001b[K"]
|
||||||
|
[217.413748, "o", "h"]
|
||||||
|
[217.481597, "o", "e"]
|
||||||
|
[217.594439, "o", "r"]
|
||||||
|
[217.698256, "o", "e"]
|
||||||
|
[217.823511, "o", " "]
|
||||||
|
[217.934876, "o", "a"]
|
||||||
|
[218.234185, "o", "r"]
|
||||||
|
[218.325626, "o", "e"]
|
||||||
|
[218.521534, "o", " "]
|
||||||
|
[220.314138, "o", "s"]
|
||||||
|
[220.377731, "o", "o"]
|
||||||
|
[220.585578, "o", "m"]
|
||||||
|
[220.733803, "o", "e"]
|
||||||
|
[220.781393, "o", " "]
|
||||||
|
[221.510396, "o", "s"]
|
||||||
|
[221.665754, "o", "t"]
|
||||||
|
[221.857486, "o", "y"]
|
||||||
|
[222.083464, "o", "l"]
|
||||||
|
[222.206997, "o", "e"]
|
||||||
|
[222.262264, "o", "s"]
|
||||||
|
[222.330128, "o", " "]
|
||||||
|
[222.497639, "o", "f"]
|
||||||
|
[222.58273, "o", "o"]
|
||||||
|
[222.686721, "o", "r"]
|
||||||
|
[222.821483, "o", " "]
|
||||||
|
[222.913548, "o", "t"]
|
||||||
|
[223.033543, "o", "h"]
|
||||||
|
[223.149439, "o", "e"]
|
||||||
|
[223.20627, "o", " "]
|
||||||
|
[223.335623, "o", "p"]
|
||||||
|
[223.418267, "o", "r"]
|
||||||
|
[223.534662, "o", "o"]
|
||||||
|
[223.654181, "o", "g"]
|
||||||
|
[223.838228, "o", "r"]
|
||||||
|
[223.90657, "o", "e"]
|
||||||
|
[223.965747, "o", "s"]
|
||||||
|
[224.122669, "o", "s"]
|
||||||
|
[224.269633, "o", " "]
|
||||||
|
[224.434395, "o", "b"]
|
||||||
|
[224.574579, "o", "a"]
|
||||||
|
[224.64739, "o", "r"]
|
||||||
|
[225.626113, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[225.641031, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:52 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[233.457421, "o", "#"]
|
||||||
|
[235.829555, "o", " "]
|
||||||
|
[236.685245, "o", "l"]
|
||||||
|
[238.381942, "o", "e"]
|
||||||
|
[238.613414, "o", "t"]
|
||||||
|
[239.501198, "o", "'"]
|
||||||
|
[239.585226, "o", "s"]
|
||||||
|
[239.701157, "o", " "]
|
||||||
|
[239.879208, "o", "d"]
|
||||||
|
[240.37912, "o", "e"]
|
||||||
|
[240.5226, "o", "l"]
|
||||||
|
[240.650743, "o", "e"]
|
||||||
|
[240.789392, "o", "t"]
|
||||||
|
[240.869225, "o", "e"]
|
||||||
|
[240.973015, "o", " "]
|
||||||
|
[241.305866, "o", "t"]
|
||||||
|
[241.416959, "o", "h"]
|
||||||
|
[241.577133, "o", "e"]
|
||||||
|
[241.69785, "o", " "]
|
||||||
|
[242.019033, "o", "c"]
|
||||||
|
[242.033506, "o", "o"]
|
||||||
|
[242.137762, "o", "n"]
|
||||||
|
[242.209209, "o", "f"]
|
||||||
|
[242.341133, "o", "i"]
|
||||||
|
[242.433165, "o", "g"]
|
||||||
|
[242.573055, "o", "u"]
|
||||||
|
[242.641041, "o", "r"]
|
||||||
|
[242.713105, "o", "a"]
|
||||||
|
[242.83762, "o", "t"]
|
||||||
|
[242.917079, "o", "i"]
|
||||||
|
[242.969032, "o", "o"]
|
||||||
|
[243.013048, "o", "n"]
|
||||||
|
[243.138504, "o", " "]
|
||||||
|
[243.473234, "o", "\""]
|
||||||
|
[243.685267, "o", "t"]
|
||||||
|
[243.760932, "o", "e"]
|
||||||
|
[243.869004, "o", "s"]
|
||||||
|
[243.973005, "o", "t"]
|
||||||
|
[244.373051, "o", "\""]
|
||||||
|
[244.654483, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[244.669815, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:52 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[251.917944, "o", "\u001b[7mtotp delete test\u001b[27m"]
|
||||||
|
[252.989446, "o", "\r\u001b[C\u001b[Ctotp delete test\r\n\u001b[?2004l\r"]
|
||||||
|
[253.02292, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:52 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[255.301036, "o", "t"]
|
||||||
|
[255.504806, "o", "o"]
|
||||||
|
[255.65279, "o", "t"]
|
||||||
|
[255.800825, "o", "p"]
|
||||||
|
[257.001238, "o", " "]
|
||||||
|
[257.224701, "o", "l"]
|
||||||
|
[257.304734, "o", "i"]
|
||||||
|
[257.324649, "o", "s"]
|
||||||
|
[257.436758, "o", "t"]
|
||||||
|
[257.757304, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[257.769018, "o", "List mode: list\r\n"]
|
||||||
|
[257.788948, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:52 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[258.944879, "o", "#"]
|
||||||
|
[259.517298, "o", " "]
|
||||||
|
[259.784678, "o", "n"]
|
||||||
|
[259.840851, "o", "o"]
|
||||||
|
[259.932589, "o", " "]
|
||||||
|
[260.278845, "o", "c"]
|
||||||
|
[260.3467, "o", "o"]
|
||||||
|
[260.425825, "o", "n"]
|
||||||
|
[260.493876, "o", "f"]
|
||||||
|
[260.598613, "o", "i"]
|
||||||
|
[260.677442, "o", "g"]
|
||||||
|
[260.768652, "o", "u"]
|
||||||
|
[260.880472, "o", "r"]
|
||||||
|
[260.953382, "o", "a"]
|
||||||
|
[261.060749, "o", "t"]
|
||||||
|
[261.112634, "o", "i"]
|
||||||
|
[261.180594, "o", "o"]
|
||||||
|
[261.204656, "o", "n"]
|
||||||
|
[261.348611, "o", "s"]
|
||||||
|
[261.554583, "o", " "]
|
||||||
|
[261.789793, "o", "s"]
|
||||||
|
[261.861763, "o", "o"]
|
||||||
|
[262.198226, "o", " "]
|
||||||
|
[262.341632, "o", "f"]
|
||||||
|
[262.492809, "o", "a"]
|
||||||
|
[262.593517, "o", "r"]
|
||||||
|
[263.625065, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[263.640423, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:52 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[267.84483, "o", "w"]
|
||||||
|
[268.512968, "o", "\b\u001b[K"]
|
||||||
|
[268.896408, "o", "#"]
|
||||||
|
[269.113218, "o", " "]
|
||||||
|
[269.372569, "o", "w"]
|
||||||
|
[269.500297, "o", "e"]
|
||||||
|
[269.661237, "o", " "]
|
||||||
|
[269.854302, "o", "c"]
|
||||||
|
[269.989274, "o", "a"]
|
||||||
|
[270.078315, "o", "n"]
|
||||||
|
[270.152714, "o", " "]
|
||||||
|
[270.309658, "o", "i"]
|
||||||
|
[270.485438, "o", "m"]
|
||||||
|
[270.570015, "o", "p"]
|
||||||
|
[270.596809, "o", "o"]
|
||||||
|
[270.641587, "o", "r"]
|
||||||
|
[270.832613, "o", "t"]
|
||||||
|
[270.952398, "o", " "]
|
||||||
|
[271.408861, "o", "f"]
|
||||||
|
[271.621126, "o", "r"]
|
||||||
|
[271.696271, "o", "o"]
|
||||||
|
[271.752437, "o", "m"]
|
||||||
|
[271.85841, "o", " "]
|
||||||
|
[272.000574, "o", "a"]
|
||||||
|
[272.124981, "o", " "]
|
||||||
|
[272.305481, "o", "d"]
|
||||||
|
[272.938453, "o", "\b\u001b[K"]
|
||||||
|
[273.452832, "o", "d"]
|
||||||
|
[273.613687, "o", "e"]
|
||||||
|
[273.833281, "o", "c"]
|
||||||
|
[274.154276, "o", "o"]
|
||||||
|
[274.253164, "o", "d"]
|
||||||
|
[274.432922, "o", "e"]
|
||||||
|
[274.588231, "o", "d"]
|
||||||
|
[274.733447, "o", " "]
|
||||||
|
[274.981022, "o", "Q"]
|
||||||
|
[275.050288, "o", "R"]
|
||||||
|
[275.362007, "o", " "]
|
||||||
|
[277.496889, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[277.512098, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:53 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[285.370066, "o", "\u001b[7mtotp import test2 \"otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example\"\u001b[27m"]
|
||||||
|
[287.308788, "o", "\r\u001b[C\u001b[Ctotp import test2 \"otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example\"\r\n\u001b[?2004l\r"]
|
||||||
|
[287.322431, "o", "App test2 not found\r\n"]
|
||||||
|
[287.326283, "o", "Import successful\r\n"]
|
||||||
|
[287.346185, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:53 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[288.769428, "o", "#"]
|
||||||
|
[289.141148, "o", " "]
|
||||||
|
[290.424621, "o", "b"]
|
||||||
|
[290.725468, "o", "e"]
|
||||||
|
[290.868228, "o", " "]
|
||||||
|
[291.173075, "o", "c"]
|
||||||
|
[291.297181, "o", "a"]
|
||||||
|
[291.426014, "o", "r"]
|
||||||
|
[291.548835, "o", "e"]
|
||||||
|
[291.900446, "o", "f"]
|
||||||
|
[292.025023, "o", "u"]
|
||||||
|
[292.278039, "o", "l"]
|
||||||
|
[292.432302, "o", "l"]
|
||||||
|
[292.548875, "o", " "]
|
||||||
|
[293.284385, "o", "\b\u001b[K"]
|
||||||
|
[293.531889, "o", ","]
|
||||||
|
[293.631761, "o", " "]
|
||||||
|
[294.360849, "o", "e"]
|
||||||
|
[295.140282, "o", "\b\u001b[K"]
|
||||||
|
[295.475851, "o", "u"]
|
||||||
|
[295.54373, "o", "s"]
|
||||||
|
[295.612843, "o", "e"]
|
||||||
|
[295.760867, "o", " "]
|
||||||
|
[297.521913, "o", "\""]
|
||||||
|
[298.656149, "o", " "]
|
||||||
|
[300.597656, "o", "w"]
|
||||||
|
[300.749634, "o", "h"]
|
||||||
|
[300.892144, "o", "e"]
|
||||||
|
[301.180181, "o", "n"]
|
||||||
|
[301.296973, "o", " "]
|
||||||
|
[302.500014, "o", "p"]
|
||||||
|
[302.615858, "o", "a"]
|
||||||
|
[302.67969, "o", "s"]
|
||||||
|
[302.849466, "o", "s"]
|
||||||
|
[302.961937, "o", "i"]
|
||||||
|
[303.101489, "o", "n"]
|
||||||
|
[303.180088, "o", "g"]
|
||||||
|
[303.35366, "o", " "]
|
||||||
|
[303.48116, "o", "a"]
|
||||||
|
[303.604602, "o", " "]
|
||||||
|
[303.816902, "o", "u"]
|
||||||
|
[303.976617, "o", "r"]
|
||||||
|
[304.101316, "o", "l"]
|
||||||
|
[307.059903, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[307.075511, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:53 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[308.748767, "o", "w"]
|
||||||
|
[308.912618, "o", "e"]
|
||||||
|
[309.091714, "o", " "]
|
||||||
|
[309.255696, "o", "c"]
|
||||||
|
[309.36445, "o", "a"]
|
||||||
|
[309.464001, "o", "n"]
|
||||||
|
[309.528542, "o", " "]
|
||||||
|
[309.688527, "o", "u"]
|
||||||
|
[309.731301, "o", "s"]
|
||||||
|
[309.829634, "o", "e"]
|
||||||
|
[310.060886, "o", " "]
|
||||||
|
[310.572134, "o", "t"]
|
||||||
|
[310.651382, "o", "h"]
|
||||||
|
[310.755376, "o", "e"]
|
||||||
|
[310.873195, "o", " "]
|
||||||
|
[311.636149, "o", "i"]
|
||||||
|
[311.815418, "o", "m"]
|
||||||
|
[311.884555, "o", "p"]
|
||||||
|
[311.944949, "o", "o"]
|
||||||
|
[311.984584, "o", "r"]
|
||||||
|
[312.220074, "o", "t"]
|
||||||
|
[312.283313, "o", "e"]
|
||||||
|
[312.508213, "o", " "]
|
||||||
|
[312.600614, "o", "c"]
|
||||||
|
[312.953066, "o", "\b\u001b[K"]
|
||||||
|
[313.088559, "o", "\b\u001b[K"]
|
||||||
|
[313.281026, "o", "d"]
|
||||||
|
[313.459927, "o", " "]
|
||||||
|
[313.621497, "o", "c"]
|
||||||
|
[313.676178, "o", "o"]
|
||||||
|
[313.716444, "o", "n"]
|
||||||
|
[313.807705, "o", "f"]
|
||||||
|
[313.90908, "o", "i"]
|
||||||
|
[313.99701, "o", "g"]
|
||||||
|
[314.111396, "o", "u"]
|
||||||
|
[314.208486, "o", "r"]
|
||||||
|
[314.277257, "o", "a"]
|
||||||
|
[314.407495, "o", "t"]
|
||||||
|
[314.47123, "o", "i"]
|
||||||
|
[314.527248, "o", "o"]
|
||||||
|
[314.584277, "o", "n"]
|
||||||
|
[315.059961, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[315.190478, "o", "we: no se encontró la orden\r\n"]
|
||||||
|
[315.214549, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:53 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;204;36;29m\u001b[0m "]
|
||||||
|
[315.32342, "o", "t"]
|
||||||
|
[315.379208, "o", "o"]
|
||||||
|
[315.556049, "o", "t"]
|
||||||
|
[315.631347, "o", "p"]
|
||||||
|
[315.823791, "o", " "]
|
||||||
|
[316.115847, "o", "l"]
|
||||||
|
[316.167385, "o", "i"]
|
||||||
|
[316.212386, "o", "s"]
|
||||||
|
[316.323384, "o", "t"]
|
||||||
|
[316.652722, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[316.66479, "o", "List mode: list\r\n"]
|
||||||
|
[316.665415, "o", "test2\r\n"]
|
||||||
|
[316.684175, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:53 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[323.48764, "o", "\u001b[7mtotp get test2 -c\u001b[27m"]
|
||||||
|
[324.439742, "o", "\r\u001b[C\u001b[Ctotp get test2 -c\r\n\u001b[?2004l\r"]
|
||||||
|
[324.452596, "o", "\r\n <Generating continuosly, press enter to stop>\r\n\r\n"]
|
||||||
|
[324.452846, "o", "Refresing in 1 seconds\r\n\r\n\r\n"]
|
||||||
|
[324.453445, "o", "[1759269238089] test2 -> 957725\r\n\r\n"]
|
||||||
|
[324.454681, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉\u001b[m\u001b[m\u001b[49m\u001b[39m 29/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[325.456238, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[39m 30/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[326.364838, "o", "\r\n\r\n"]
|
||||||
|
[326.364948, "o", "[1759269240001] test2 -> 227087\r\n\r\n"]
|
||||||
|
[326.365295, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 1/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[326.45472, "o", "\r\u001b[K"]
|
||||||
|
[327.366772, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 2/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[328.368886, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 3/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[329.370693, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 4/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[330.372132, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 5/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[331.373589, "o", "\u001b[25l\r\u001b[49m\u001b[39mNext TOTP: \u001b[m\u001b[m\u001b[49m\u001b[97m▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[93m▉\u001b[m\u001b[m\u001b[49m\u001b[90m▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉\u001b[m\u001b[m\u001b[49m\u001b[39m 6/30\u001b[m\u001b[m\u001b[K\u001b[25h"]
|
||||||
|
[331.57506, "o", "\r\n"]
|
||||||
|
[331.594921, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:54 \u001b[0m\u001b[38;2;60;56;54m \u001b[0mtook \u001b[1;33m7s\u001b[0m \r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[332.515102, "o", "#"]
|
||||||
|
[333.135521, "o", " "]
|
||||||
|
[333.803007, "o", "h"]
|
||||||
|
[333.882813, "o", "a"]
|
||||||
|
[334.21893, "o", "v"]
|
||||||
|
[334.403614, "o", "e"]
|
||||||
|
[334.52687, "o", " "]
|
||||||
|
[334.871466, "o", "f"]
|
||||||
|
[334.951931, "o", "u"]
|
||||||
|
[335.148189, "o", "n"]
|
||||||
|
[335.488484, "o", "!"]
|
||||||
|
[336.103143, "o", "\r\n\u001b[?2004l\r"]
|
||||||
|
[336.118171, "o", "\u001b[?2004h\r\n\u001b[38;2;214;93;14m\u001b[48;2;214;93;14;38;2;251;241;199m ruben\u001b[48;2;215;153;33;38;2;214;93;14m\u001b[38;2;251;241;199m …/clj-totp \u001b[48;2;104;157;106;38;2;215;153;33m\u001b[38;2;251;241;199m main ? \u001b[48;2;69;133;136;38;2;104;157;106m\u001b[38;2;251;241;199m v21.0.8 \u001b[48;2;102;92;84;38;2;69;133;136m\u001b[48;2;60;56;54;38;2;102;92;84m\u001b[38;2;251;241;199m 23:54 \u001b[0m\u001b[38;2;60;56;54m \u001b[0m\r\n\u001b[1;38;2;152;151;26m\u001b[0m "]
|
||||||
|
[336.90336, "o", "\u001b[?2004l\r\r\nexit\r\n"]
|
||||||
BIN
use_example.gif
Normal file
BIN
use_example.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
Reference in New Issue
Block a user