SkillAgentSearch skills...

Gost

Bouncycastle wrapper for Clojure to work with GOST algorithms

Install / Use

/learn @redstarssystems/Gost
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

= 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

View on GitHub
GitHub Stars6
CategoryDevelopment
Updated2mo ago
Forks0

Languages

Clojure

Security Score

85/100

Audited on Jan 11, 2026

No findings