Gost
Bouncycastle wrapper for Clojure to work with GOST algorithms
Install / Use
/learn @redstarssystems/GostREADME
= GOST library :git: https://git-scm.com[git] :clojure-deps-cli: https://clojure.org/guides/getting_started[clojure deps cli] :tools-build: https://clojure.org/guides/tools_build[tools-build] :deps-new: https://github.com/seancorfield/deps-new[deps-new] :build-clj: https://github.com/seancorfield/build-clj[build-clj] :babashka: https://github.com/babashka/babashka[babashka] :toc: macro :toclevels: 4
image:https://img.shields.io/github/license/redstarssystems/gost[license,link=LICENSE] image:https://img.shields.io/clojars/v/org.rssys/gost.svg[clojars,link=https://clojars.org/org.rssys/gost]
== Intro
This is a thin wrapper for https://bouncycastle.org[Bouncycastle library] to work with GOST algorithms - Russian cryptographic standards.
The library provides:
- encryption, mac using GOST 28147-89, GOST 3412-2015;
- digest, hmac using GOST3411-94/2012 256 and 512 bits;
- signature using GOST3410-2012 256 and 512 bit key length;
- GOST key generation: secret keys, public/private keys, password based keys;
- compression + encryption + mac / decryption + check mac + decompression;
- encrypting with GOST EC public keys / decrypting with EC private keys;
- save/load GOST keys (secret, private, public) to PEM format;
- X.509 GOST certificates: generate, sign, save/load.
toc::[]
== Usage
Add dependency to the deps.edn:
[source,clojure]
:deps { org.rssys/gost {:mvn/version "0.4.0"}}
Require necessary namespaces:
[source,clojure]
(require '[org.rssys.gost.encrypt :as e]) (require '[org.rssys.gost.digest :as d]) (require '[org.rssys.gost.common :as common])
== Secret key generation
This is high-level functions.
[source, clojure]
;; To generate a secret key for the GOST3412-2015 use generate-secret-key function.
;; This will return a 256-bit random secret key as a SecretKeySpec object.
;; The algorithm is set to GOST3412-2015
(def secret-key-2015 (e/generate-secret-key))
(e/algo-name secret-key-2015) ;; => GOST3412-2015
;; To generate a secret key GOST28147-89 use generate-secret-key function with a parameter.
;; This will return a 256-bit random secret key as a SecretKeySpec object.
;; The algorithm is set to GOST28147.
(def secret-key-89 (e/generate-secret-key e/gost28147))
(e/algo-name secret-key-89) ;; => GOST28147
;; To convert a SecretKeySpec to a byte array: (e/secret-key->byte-array secret-key-2015) ;; => [B ;; [-38, -86, 71, -42, -69, 73, -33, 53, 72, 80, 38, 26, 57, 69, -114, -1, ;; -119, 13, 113, -84, -31, 54, -128, 114, -79, -55, 85, 126, 105, -96, ;; -37, -128]
;; To convert a byte array to SecretKeySpec: (e/byte-array->secret-key (byte-array [-38, -86, 71, -42, -69, 73, -33, 53, 72, 80, 38, 26, 57, 69, -114, -1, -119, 13, 113, -84, -31, 54, -128, 114, -79, -55, 85, 126, 105, -96, -37, -128])) ;; => #object[javax.crypto.spec.SecretKeySpec
;; We can generate a secret key bytes from a password. ;; This function always return the same bytes value from the same String password. ;; By default, it uses min 10000 iterations of PBKDF2WITHHMACGOST3411 algorithm, recommended by NIST (e/generate-secret-bytes-from-password "qwerty12345") ;; => [B ;;[-113, 62, 87, -90, 116, -44, -20, -98, 4, -108, 77, -59, -22, 25, -73, ;; 20, -31, 62, -86, 19, 103, 81, -64, 32, 74, 81, -32, -97, -78, 123, ;; -82, -70]
;; To convert it to SecretKeySpec (e/byte-array->secret-key (e/generate-secret-bytes-from-password "qwerty12345")) ;; => #object[javax.crypto.spec.SecretKeySpec
== Encryption
This is high-level functions.
[source,clojure]
(def message "This text has length = 32 bytes.")
;; To encrypt a byte array (any binary content) in a most secured way just use protect-bytes function.
;; The encryption algorithm GOST3412-2015 or GOST28147-89 is already set in SecretKeySpec.
;; This function calculates Mac for plain data, then
;; compress a plain data to hide information structure, then
;; encrypts data and Mac in CFB mode with always random IV.
;; The encrypted bytes from the same message and same key are always different!
(def encrypted-message (e/protect-bytes secret-key-2015 (.getBytes message))) ;; Returns bytes array with structure:
;; [random(IV), encrypted(Mac), encrypted(compressed-data)]
;; To decrypt and restore a plain text just use unprotect-bytes function.
;; The decryption algorithm GOST3412-2015 or GOST28147-89 is already set in SecretKeySpec.
;; This function decrypts Mac and data, then
;; decompress data, then calculate Mac for decompressed data, then
;; compare Mac from a message and Mac calculated.
;; If Macs are the same then return plain data, otherwise throw an Exception.
(def decrypted-message (e/unprotect-bytes secret-key-2015 encrypted-message))
(= message (String. ^bytes decrypted-message)) ;; => true
;; To encrypt a file (any binary content) in a most secured way just use protect-file function.
;; The encryption algorithm GOST3412-2015 or GOST28147-89 is already set in SecretKeySpec.
;; This function calculates Mac for plain file, then
;; compress a plain file to hide information structure, then
;; encrypts data and Mac in CFB mode with always random IV.
;; The encrypted bytes from the same message and same key are always different!
(e/protect-file secret-key-2015 "dev/src/examples/plain32.txt" "target/plain32.enc") ;; Encrypted file has structure:
;; random(IV), encrypted(Mac), encrypted(compressed-data).
;; To decrypt a file just use unprotect-file function.
;; The decryption algorithm GOST3412-2015 or GOST28147-89 is already set in SecretKeySpec.
;; This function decrypts Mac and data, then
;; decompress data in a file, then calculate Mac for decompressed data, then
;; compare Mac from the message and Mac calculated.
;; If Macs are the same then return output file name as String, otherwise throw an Exception.
(e/unprotect-file secret-key-2015 "target/plain32.enc" "target/plain32.txt")
(= (slurp "dev/src/examples/plain32.txt") (slurp "target/plain32.txt")) ;; => true
== Mac generation
This is high-level functions.
[source,clojure]
;; To calculate Mac for a file (any binary file) use mac-stream function.
;; The encryption algorithm GOST3412-2015 or GOST28147-89 is already set in SecretKeySpec.
;; Mac value from the same data and same SecretKeySpec is always the same.
(e/mac-stream secret-key-2015 "dev/src/examples/plain32.txt") ;; => [B
;; [-111, 125, 10, -34, -109, -109, 41, 115, 81, 61, -90, -80, 16, 71, -108, 91]
;; To calculate Mac for a byte array (any binary file) use the same mac-stream function.
;; The encryption algorithm GOST3412-2015 or GOST28147-89 is already set in SecretKeySpec.
;; Mac value from the same data and same SecretKeySpec is always the same.
(e/mac-stream secret-key-2015 (.getBytes message)) ;; => [B
;; [-111, 125, 10, -34, -109, -109, 41, 115, 81, 61, -90, -80, 16, 71, -108, 91]
== Digest
This is high-level functions.
[source,clojure] .digest.clj
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; High-level functions
(require '[org.rssys.gost.digest :as d]) (require '[org.rssys.gost.common :as common])
(def message "The quick brown fox jumps over the lazy dog")
;; To generate GOST3411-94 digest from byte array use digest-3411-94 function
(def d1 (d/digest-3411-94 (.getBytes message)))
(common/bytes-to-hex d1) ;; => ;; "9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76"
;; To generate GOST3411-94 digest from file use the same digest-3411-94 function
(def d2 (d/digest-3411-94 "dev/src/examples/plain32.txt"))
(common/bytes-to-hex d2) ;; => ;; "94ca6fc62ae26d3bb0109c16e6a5749c291bbdd0cdf5231e3f4073679227b9fb"
;; To generate GOST3411-2012-256 digest from byte array use digest-2012-256 function
(def d3 (d/digest-2012-256 (.getBytes message)))
(common/bytes-to-hex d3) ;; => ;; "3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4"
;; To generate GOST3411-2012-256 digest from file use the same GOST3411-2012-256 function
(def d4 (d/digest-2012-256 "dev/src/examples/plain32.txt"))
(common/bytes-to-hex d4) ;; => ;; "ee363d5e40c1ff1965ee308beef1ca153c1d56d377a63be29924731732f2c697"
;; To generate GOST3411-2012-512 digest from byte array use digest-2012-512 function
(def d5 (d/digest-2012-512 (.getBytes message)))
(common/bytes-to-hex d5) ;; => ;; "d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe"
;; To generate GOST3411-2012-512 digest from file use the same GOST3411-2012-512 function
(def d6 (d/digest-2012-512 "dev/src/examples/plain32.txt"))
(common/bytes-to-hex d6) ;; => ;; "7f75cf439c41420b25a3964ab0608af592c9af44e852dcbc18ae9fcfa0c2d7e3edda83715d23d30e5d3dc521290c66980695faa69adc7c5854ced01f0af6f0e9"
== HMAC
This is high-level functions.
[source,clojure] .hmac.clj
(require '[org.rssys.gost.digest :as d]) (require '[org.rssys.gost.encrypt :as e])
(def message "The quick brown fox jumps over the lazy dog")
;; generate secret key bytes from password (def secret-key (e/generate-secret-bytes-from-password "12345678"))
;; Generate HMAC using GOST3411-94 and secret-key bytes (def h1 (d/hmac-3411-94 (.getBytes message) secret-key))
(common/bytes-to-hex h1) ;; => ;; "1ffb045ab775c674b5809d6f5c180c73be459223e93951e8c19cc1e0ed559b20"
;; Generate HMAC using GOST3411-2012-256 and secret-key bytes (def h2 (d/hmac-2012-256 (.getBytes message) secret-key))
(common/bytes-to-hex h2) ;; => ;; "405854baba2cc90661f1ff08e40c2cd0fb36869a5a32f655f51ea6fd577c6d84"
;; Genera
