:deps { org.rssys/gost {:mvn/version "0.2.0"}}
This is a thin wrapper for 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;
key generation: secret keys, public/private keys, password based keys;
compression + encryption + mac / decryption + check mac + decompression
encrypting with EC public keys / decrypting with EC private keys
save/load keys (secret, private, public) to PEM format.
Add dependency to the deps.edn
:
:deps { org.rssys/gost {:mvn/version "0.2.0"}}
Require necessary namespaces:
(require '[org.rssys.gost.encrypt :as e])
(require '[org.rssys.gost.digest :as d])
(require '[org.rssys.gost.common :as common])
This is high-level functions.
;; 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
This is high-level functions.
(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
This is high-level functions.
;; 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]
This is high-level functions.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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"
This is high-level functions.
(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"
;; Generate HMAC using GOST3411-2012-512 and secret-key bytes
(def h3 (d/hmac-2012-512 (.getBytes message) secret-key))
(common/bytes-to-hex h3) ;; =>
;; "14923d761858aa272028855999c0bd3f37964e98bb3bb163825ecfbcd049e10f612566053031bec01611bc9584ef24aa80073cecc51d125fe989a973dd1f6813"
;; To generate GOST3411-2012-256 HMAC from file use the same `hmac-2012-256` function
(def h4 (d/hmac-2012-256 "dev/src/examples/plain32.txt" secret-key))
(common/bytes-to-hex h4) ;; =>
;; "2c36afad546eb7026b1bfd92dc83a6e6cfd20f301a786fed41fd3c2213214d43"
This is high-level functions.
(require '[org.rssys.gost.sign :as s])
(def message "This is a message.")
;; Generate public and private keypair 256 bit length
(def kp-256 (s/gen-keypair-256))
(def public-key-256 (.getPublic kp-256))
(def private-key-256 (.getPrivate kp-256))
;; Generate signature for byte array.
;; Digest GOST3411-2012 256-bit length for message will be calculated automatically.
(def signature-256 (s/sign-256 private-key-256 (.getBytes message)))
;; Check signature length
(alength signature-256) ;; => 64
;; Check signature using public key
(s/verify-256 public-key-256 (.getBytes message) signature-256) ;; => true
;; Generate public and private keypair 512 bit length
(def kp-512 (s/gen-keypair-512))
(def public-key-512 (.getPublic kp-512))
(def private-key-512 (.getPrivate kp-512))
;; Generate signature for a file.
;; Digest GOST3411-2012 512 bit length for file content will be calculated automatically.
(def signature-512 (s/sign-512 private-key-512 "test/data/big.txt"))
;; Check signature length
(alength signature-512) ;; => 128
;; Check signature of file using public key
(s/verify-512 public-key-512 "test/data/big.txt" signature-512) ;; => true
This is high-level functions.
(require '[org.rssys.gost.sign :as s])
(require '[org.rssys.gost.pem :as p])
(require '[org.rssys.gost.encrypt :as e])
;; Generate secret key
(def secret-key-2015 (e/generate-secret-key))
;; Convert SecretKeySpec to encrypted PEM string
;; Secret key will be encrypted with key derived from PBKDF2(`password`) using GOST3412-2015-CBC"
(def encrypted-pem-secret-key (p/secret-key->encrypted-pem secret-key-2015 "123456"))
;; Restore secret key from encrypted PEM
;; Secret key will be decrypted with key derived from PBKDF2(`password`) using GOST3412-2015-CBC"
(def restored-encrypted-secret-key-2015 (p/encrypted-pem->secret-key encrypted-pem-secret-key "123456"))
;; Check that keys are equal
(= restored-encrypted-secret-key-2015 secret-key-2015)
;; PEM string with plain secret key
;; Warning: PEM string will be not encrypted!
(def pem-secret-key-2015 (p/secret-key->pem secret-key-2015))
;; Restore secret key from plain PEM
(def restored-secret-key-2015 (p/pem->secret-key pem-secret-key-2015))
;; Check that keys are equal
(= restored-secret-key-2015 secret-key-2015)
;; Generate public and private keypair 256 bit length
(def kp-256 (s/gen-keypair-256))
(def public-key-256 (.getPublic kp-256))
(def private-key-256 (.getPrivate kp-256))
;; To save private key to encrypted PEM (PKCS8) string use `private-key->encrypted-pem`
;; Private key will be encrypted with AES-256-CBC in `openssl` format.
(p/private-key->encrypted-pem private-key-256 "123456") ;;=>
;; "-----BEGIN ENCRYPTED PRIVATE KEY-----
;;MIGpMFUGCSqGSIb3DQEFDTBIMCcGCSqGSIb3DQEFDDAaBBSMtRpFQ6n7RgZTriGG
;;bFr8JJeKiQICBAAwHQYJYIZIAWUDBAEqBBB0XmFK1rvMBnC4t7BSGndLBFDiON0S
;;e1iGKb80u/lLXti1+7x9QKCGZtVIJw62YIQWAxy7zK5vZ1xAQxSRNjssfi0niroL
;;0ZqRJpFb6czeCFyq2HBzUvNH2rRdAiRv91KDgg==
;;-----END ENCRYPTED PRIVATE KEY-----
;;"
;; To restore private key from encrypted PEM (PKCS8) string use `encrypted-pem->private-key`
(p/encrypted-pem->private-key (slurp "test/data/test-encrypted-private-key.pem") "123456") ;;=>
;;#object[org.bouncycastle.jcajce.provider.asymmetric.ecgost12.BCECGOST3410_2012PrivateKey 0x3776cb5 "ECGOST3410-2012
;; Private Key [55:07:ef:03:d1:7f:ea:e7:53:ca:58:6d:0e:da:0a:6f:e2:93:4b:b4]
;; X: df0679d81ec2156f062b507918c10fb9e680848be92ec69af6be9f32ffd8669e
;; Y: 2234280a15135e723579ef96544742f6cc06f8d59cccd88fd4b377f818ce9f95
;;"]
;; Convert PrivateKey to PEM string
;; Warning: PEM string will be not encrypted!
(def private-pem-256 (p/private-key->pem private-key-256))
;; Convert PublicKey to PEM string
(def public-pem-256 (p/public-key->pem public-key-256))
;; Convert PEM string to a PrivateKey
(def restored-private-256 (p/pem->private-key private-pem-256))
;; check that keys are equal
(= restored-private-256 private-key-256)
;; Convert PEM string to a PublicKey
(def restored-public-256 (p/pem->public-key public-pem-256))
;; check that keys are equal
(= restored-public-256 public-key-256)
;; You can write to PEM format arbitrary byte array
(p/write-bytes-to-pem "MESSAGE" (.getBytes "Hello"))
;; You can read from PEM arbitrary byte array
(String. (p/read-bytes-from-pem "-----BEGIN MESSAGE-----\nSGVsbG8=\n-----END MESSAGE-----\n"))
;; You can write structured data to PEM format
(p/write-struct-to-pem {:data (.getBytes "Hello") :type "MESSAGE" :headers {:status "unencrypted" :date "01-01-2022"}} ) ;; =>
;; "-----BEGIN MESSAGE-----
;;status: unencrypted
;;date: 01-01-2022
;;
;;SGVsbG8=
;;-----END MESSAGE-----
;;"
;; You can read structured data from PEM string
(p/read-struct-from-pem (slurp "test/data/test-plain-with-headers.pem")) ;; =>
;;{:data [72, 101, 108, 108, 111],
;; :type "MESSAGE",
;; :headers {"status" "unencrypted", "date" "01-01-2022"}}
Also, you can use openssl with GOST support to read PEM private key.
Download openssl with GOST from here: docker run --rm -i -t rnix/openssl-gost bash
.
openssl pkey -in test/data/test-private-key.pem -noout -text
Private key: DC03D4325299B33F75DFE365E3018330B72FE4FE227A00509D92EDC43034D3106F98F6E8B9CA71D2620DE1DF329549860688713DF97D7FE3CA118C7BB74290CB
Public key:
X:583E506A840B067967A9C66AC5DE4E55F9C179E723E7D5FD9E5B3C474413416CA9EBB62202433A4DE92DE8B711619AC69F18ED35559563D2563F451C13128C2E
Y:192D0CE9E4A62EF00CF4E523C429B7A18EB55CF52DC6F1D76FCF6F5599F2C112D7650BC7501B2C4E4D51E4A597B9B9C157B9F2C71098D9F3A8348A17F0769488
Parameter set: id-tc26-gost-3410-2012-512-paramSetA
Digest Algorithm: GOST R 34-11-2012 (512 bit)
openssl pkey -pubin -in test/data/test-public-key.pem -text
-----BEGIN PUBLIC KEY-----
MIGqMCEGCCqFAwcBAQECMBUGCSqFAwcBAgECAQYIKoUDBwEBAgMDgYQABIGALowS
ExxFP1bSY5VVNe0Yn8aaYRG36C3pTTpDAiK266lsQRNERzxbnv3V5yPnecH5VU7e
xWrGqWd5BguEalA+WIiUdvAXijSo89mYEMfyuVfBubmXpeRRTU4sG1DHC2XXEsHy
mVVvz2/X8cYt9Vy1jqG3KcQj5fQM8C6m5OkMLRk=
-----END PUBLIC KEY-----
Public key:
X:583E506A840B067967A9C66AC5DE4E55F9C179E723E7D5FD9E5B3C474413416CA9EBB62202433A4DE92DE8B711619AC69F18ED35559563D2563F451C13128C2E
Y:192D0CE9E4A62EF00CF4E523C429B7A18EB55CF52DC6F1D76FCF6F5599F2C112D7650BC7501B2C4E4D51E4A597B9B9C157B9F2C71098D9F3A8348A17F0769488
Parameter set: id-tc26-gost-3410-2012-512-paramSetA
Digest Algorithm: GOST R 34-11-2012 (512 bit)
This is high-level functions.
(require '[org.rssys.gost.armor :as a])
(require '[org.rssys.gost.pem :as p])
(require '[org.rssys.gost.sign :as s])
;; Read test public and private keys
(def public-key-256 (p/pem->public-key (slurp "test/data/test-public-key-256.pem")))
(def private-key-256 (p/pem->private-key (slurp "test/data/test-private-key-256.pem")))
;; Read plain text
(def plain-32-message (slurp "test/data/plain32.txt"))
;; Sign message + time and produce armored message: plain text + time + signature
(def armored-message (a/sign-message private-key-256 plain-32-message))
"-----[ START DATA ]-----
This is message, length=32 bytes
-----[ END DATA ]-----
-----BEGIN SIGNATURE-----
time: 2022-02-24T01:23:16.246966
Ow1R7KEKvFjbOg33LZt390NqPX2d6vOQi3k0lNWDMJ8aJGlOhLvYxfZlk8FnpbKI
eUsdGvkNc4yzwM5x5VEwRQ==
-----END SIGNATURE-----
"
;; Verify signature for message + time and if signature is valid return message
(def restored-message (a/verify-message public-key-256 armored-message))
;; Sign message with headers data
(def armored-message-with-headers
(a/sign-message private-key-256 plain-32-message
:headers {:issuer "Certification Authority" :address "Moscow"}))
"-----[ START DATA ]-----
This is message, length=32 bytes
-----[ END DATA ]-----
-----BEGIN SIGNATURE-----
issuer: Certification Authority
address: Moscow
time: 2022-02-24T01:38:20.397299
IiCHULQELZsdRFcsbjE9Xm0c24fvXplOiUO1mL4ze+wcPASsRX2ouA7L52xb7gZe
WLxZ2OKplo+0JHVyXQCXRQ==
-----END SIGNATURE-----
"
;; Sign message with headers data and custom date-time formatter
(def armored-message-with-headers-custom-formatter
(a/sign-message private-key-256 plain-32-message
:headers {:issuer "Certification Authority" :address "Moscow"}
:datetime-formatter (DateTimeFormatter/ofLocalizedDateTime FormatStyle/LONG)))
"-----[ START DATA ]-----
This is message, length=32 bytes
-----[ END DATA ]-----
-----BEGIN SIGNATURE-----
issuer: Certification Authority
address: Moscow
time: 24 февраля 2022 г., 23:26:26 MSK
OFVM4pfFJ4hpC0p7Scr9XNAj1JDvf3meDGWFTNIhWsgyKCIfGJc7FGyDsmFPzvlY
OgkYN1rBj8kgIQDvrM+W7A==
-----END SIGNATURE-----
"
;; Verify message with headers
(a/verify-message public-key-256 (slurp "test/data/armored-plain32-with-headers.pem"))
Use these functions carefully. If you are not sure use high-level functions only! |
;; IV length depends on encryption mode and algorithm
(e/iv-length-by-algo-mode e/gost3412-2015 :cfb-mode) ;; => 16
(e/iv-length-by-algo-mode e/gost3412-2015 :cbc-mode) ;; => 16
(e/iv-length-by-algo-mode e/gost3412-2015 :ctr-mode) ;; => 8 !!
(e/iv-length-by-algo-mode e/gost28147 :cfb-mode) ;; => 8
(e/iv-length-by-algo-mode e/gost28147 :cbc-mode) ;; => 8
(e/iv-length-by-algo-mode e/gost28147 :ctr-mode) ;; => 8
;; Mac length
(e/mac-length-by-algo e/gost3412-2015) ;; => 16
(e/mac-length-by-algo e/gost28147) ;; => 4
This is for low-level functions.
Available encryption modes for GOST3412-2015:
:cfb-mode
is "GOST3412-2015/CFB/NoPadding"
:ctr-mode
is "GOST3412-2015/CTR/NoPadding"
:cbc-mode
is "GOST3412-2015/CBC/PKCS7Padding"
Available encryption modes for GOST28147:
:cfb-mode
is "GOST28147/CFB/NoPadding"
:ctr-mode
is "GOST28147/CTR/NoPadding"
:cbc-mode
is "GOST28147/CBC/PKCS7Padding"
;; Init Cipher for GOST28147 in CFB, CTR, CBC mode
(def cipher1 (e/init-cipher-mode e/gost28147 :cfb-mode))
(def cipher2 (e/init-cipher-mode e/gost28147 :ctr-mode))
(def cipher3 (e/init-cipher-mode e/gost28147 :cbc-mode))
;; Init Cipher for GOST3412-2015 in CFB, CTR, CBC mode
(def cipher4 (e/init-cipher-mode e/gost3412-2015 :cfb-mode))
(def cipher5 (e/init-cipher-mode e/gost3412-2015 :ctr-mode))
(def cipher6 (e/init-cipher-mode e/gost3412-2015 :cbc-mode))
This is low-level functions.
;; Random IV generation
(e/new-iv-8) ;; => [B
;; [25, 117, -36, -32, -87, -128, -25, 23]
(e/new-iv-16) ;;=> [B
;; [29, -49, 83, 120, -125, 95, 41, -54, -11, -37, -2, -19, 123, -122,
;; -21, 6]
;; Also we can generate IV depend on cipher mode and algorithm name
(e/new-iv e/gost28147 :cfb-mode) ;; => [B
;; [-101, 29, 29, 55, 112, 14, 55, 104]
(e/new-iv e/gost3412-2015 :cbc-mode) ;; => [B
;; [6, 87, 96, -83, -128, 25, -57, -70, -54, 51, 9, -26, 73, -103, 64, 67]
;; Warning! IV for :ctr-mode is always 8 bytes length for any algorithm
(e/new-iv e/gost3412-2015 :ctr-mode) ;; => [B => [45, -71, 116, -67, 9, -39, -101, -51]
(e/new-iv e/gost28147 :ctr-mode) ;; => [B => [8, 39, -126, -5, 122, -120, 1, -108]
This is low-level functions.
The GOST28147-89 has several named S-box
parameters:
"E-A" - Gost28147_89_CryptoPro_A_ParamSet (most used);
"E-B" - Gost28147_89_CryptoPro_B_ParamSet (most used);
"E-C" - Gost28147_89_CryptoPro_C_ParamSet;
"E-D" - Gost28147_89_CryptoPro_D_ParamSet;
"Param-Z" - tc26_gost_28147_param_Z.
(def secret-key (e/generate-secret-key e/gost28147)) ;; generate secret key
(def iv-8 (e/new-iv (e/algo-name secret-key) :cfb-mode)) ;; generate new random IV
(def algo-param-spec (e/init-gost-named-params (e/algo-name secret-key) iv-8 "E-A")) ;; Init GOST with "E-A" parameters
The GOST28147-89 has several OID S-box
parameters
OID params table
;; Init GOST with OID parameters
;; See https://cpdn.cryptopro.ru/content/csp40/html/group___pro_c_s_p_ex_CP_PARAM_OIDS.html
(e/init-gost-oid-params e/gost28147 iv-8 (org.bouncycastle.asn1.ASN1ObjectIdentifier. "1.2.643.2.2.31.1"))
The GOST28147-89 may be initialized with 'S-boxes' as bytes array:
;; Init GOST 28147 with S-box as binary array
;; https://datatracker.ietf.org/doc/html/rfc4357
;; id-Gost28147-89-CryptoPro-A-ParamSet
(def ^:const s-box-crypto-pro-a
[9 6 3 2 8 11 1 7 10 4 14 15 12 0 13 5
3 7 14 9 8 10 15 0 5 2 6 12 11 4 13 1
14 4 6 2 11 3 13 8 12 15 5 10 0 7 1 9
14 7 10 12 13 1 3 9 0 2 11 4 15 8 5 6
11 5 1 9 8 13 15 0 14 4 2 3 12 7 10 6
3 10 13 12 1 2 0 11 7 5 9 4 8 15 14 6
1 13 2 9 7 10 6 0 8 12 4 5 15 3 11 14
11 10 15 5 0 12 14 8 6 2 3 9 1 7 13 4])
(e/init-gost-sbox-binary-params e/gost28147 iv-8 (byte-array s-box-crypto-pro-a))
The new-encryption-cipher
is a low-level function.
;; Init cipher for GOST3412-2015, generate random IV automatically
(def cipher-2015 (e/new-encryption-cipher secret-key-2015 :cfb-mode))
;; extract IV
(.getIV cipher-2015) ;; => [B
;;[105, 13, 115, 71, 2, -23, 6, 82, -30, -13, 113, -12, -34, 69, -6, 27]
;; Init cipher for GOST28147, generate random IV automatically
(def cipher-89 (e/new-encryption-cipher secret-key-89 :cfb-mode))
;; extract IV
(.getIV cipher-89) ;; => [-84, -116, -60, -99, 89, 43, -107, 127]
;; Init cipher for GOST3412-2015, with AlgoParamsSpec, IV should be always random
(def cipher-2015 (e/new-encryption-cipher secret-key-2015 :cfb-mode
(javax.crypto.spec.IvParameterSpec. (e/new-iv-16))))
;; Init cipher for GOST28147, with AlgoParamsSpec, IV should be always random
(def cipher-89 (e/new-encryption-cipher secret-key-89 :cfb-mode
(e/init-gost-named-params (e/algo-name secret-key-89) (e/new-iv-8) "E-A")))
The new-decryption-cipher
is a low-level function.
;; Init decryption cipher for GOST3412-2015
(def iv-16 (.getIV cipher-2015)) ;; we should use the same IV which was used in encryption phase
(def decryption-cipher-2015 (e/new-decryption-cipher secret-key-2015 :cfb-mode
(javax.crypto.spec.IvParameterSpec. iv-16)))
;; Init decryption cipher for GOST28147
;; we should use the same IV and S-boxes which were used in encryption phase
(def iv-8 (.getIV cipher-89))
(def decryption-cipher-89 (e/new-decryption-cipher secret-key-89 :cfb-mode
(e/init-gost-named-params (e/algo-name secret-key-89) iv-8 "E-A")))
This is low-level functions.
;; Init cipher for GOST3412-2015, generate random IV automatically
(def cipher-2015 (e/new-encryption-cipher secret-key-2015 :cfb-mode))
(def iv-16 (.getIV cipher-2015))
(def decryption-cipher-2015 (e/new-decryption-cipher secret-key-2015 :cfb-mode (javax.crypto.spec.IvParameterSpec. iv-16)))
;; To encrypt bytes use `encrypt-bytes` function and Cipher initialized with
;; secret key and random IV in encryption mode
(def e1 (e/encrypt-bytes cipher-2015 (.getBytes message))) ;; => [B
;;[79, 67, 111, -67, 4, 99, 92, -68, 66, -35, 77, -6, 115, 56, 108, 47,
;; -124, -82, 107, -18, -95, -125, -18, 106, -53, -21, 0, -108, -48, 41,
;; -86, -84]
;; Remember, you should know IV which was used during encryption to decrypt it.
;; To decrypt bytes use `decrypt-bytes` function and Cipher initialized with
;; the same secret key and the same IV in decryption mode
(String. ^bytes (e/decrypt-bytes decryption-cipher-2015 e1)) ;; => "This text has length = 32 bytes."
;; To encrypt file use `encrypt-stream` function and Cipher initialized with
;; secret key and random IV in encryption mode
(e/encrypt-stream cipher-2015 "dev/src/examples/plain32.txt" "target/plain32.enc")
;; Remember, you should know IV which was used during encryption to decrypt it.
;; To decrypt file use `decrypt-stream` function and Cipher initialized with
;; the same secret key and the same IV in decryption mode
(e/decrypt-stream decryption-cipher-2015 "target/plain32.enc" "target/plain32.txt") ;; => "This text has length = 32 bytes."
(slurp "target/plain32.txt") ;; => "This text has length = 32 bytes."
This is low-level functions.
;; To compress plain bytes to hide its internal structure before encryption use `compress-bytes` function
(def cb (e/compress-bytes (.getBytes message))) ;; => [B
;;[120, -38, 11, -55, -56, 44, 86, 40, 73, -83, 40, 81, -56, 72, 44, 86,
;; -56, 73, -51, 75, 47, -55, 80, -80, 85, 48, 54, 82, 72, -86, 44, 73,
;; 45, -42, 3, 0, -71, 112, 10, -45]
;; To decompress plain bytes use `decompress-bytes` function
(String. (e/decompress-bytes cb)) ;; => "This text has length = 32 bytes."
;; To compress file to hide its internal structure before encryption use `compress-stream` function
(e/compress-stream "dev/src/examples/plain32.txt" "target/plain32.gz")
;; To decompress file use `decompress-stream` function
(e/decompress-stream "target/plain32.gz" "target/plain32.txt")
(slurp "target/plain32.txt") ;; => "This text has length = 32 bytes."
This is low-level functions.
;; Init cipher for GOST3412-2015, generate random IV automatically
(def cipher-2015 (e/new-encryption-cipher secret-key-2015 :cfb-mode))
(def iv-16 (.getIV cipher-2015))
(def decryption-cipher-2015 (e/new-decryption-cipher secret-key-2015 :cfb-mode (javax.crypto.spec.IvParameterSpec. iv-16)))
(def baos (ByteArrayOutputStream.))
;; To compress and encrypt plain bytes use `compress-and-encrypt-stream` function
(e/compress-and-encrypt-stream cipher-2015 (.getBytes message) baos)
(def ceb (.toByteArray baos)) ;; => [B
;;[-14, -43, -92, -4, -79, 85, 72, -50, 77, -102, -24, 21, -119, 81,
;; -100, -98, 39, -79, -56, 61, -95, 118, 28, -126, 39, -65, 10, -15, 21,
;; -33, 23, -44, 60, 52, 76, 35, 69, 119, -96, 50]
(def baosd (ByteArrayOutputStream.))
;; Remember, you should know IV which was used during encryption to decrypt it.
;; To decompress and decrypt use `decrypt-and-decompress-stream` function
(e/decrypt-and-decompress-stream decryption-cipher-2015 ceb baosd)
(String. (.toByteArray baosd)) ;; => "This text has length = 32 bytes."
;; To compress and encrypt file use `compress-and-encrypt-stream` function
(e/compress-and-encrypt-stream cipher-2015 "dev/src/examples/plain32.txt" "target/plain32.egz")
;; Remember, you should know IV which was used during encryption to decrypt it.
;; To decompress and decrypt file use `decrypt-and-decompress-stream` function
(e/decrypt-and-decompress-stream decryption-cipher-2015 "target/plain32.egz" "target/plain32.txt")
(slurp "target/plain32.txt") ;; => "This text has length = 32 bytes."
Project org.rssys/gost generated from Library template.
All these tools you need to install only once.
Install clojure deps cli tools version 1.10.3.1069+
MacOS
brew install clojure/tools/clojure
Linux
Ensure that the following dependencies are installed in OS: bash
, curl
, rlwrap
, and Java
.
curl -O https://download.clojure.org/install/linux-install-1.10.3.1069.sh
chmod +x linux-install-1.10.3.1069.sh
sudo ./linux-install-1.10.3.1069.sh
Install latest deps-new
clojure -Ttools install io.github.seancorfield/deps-new '{:git/tag "v0.4.9"}' :as new
Tool will be installed in ~/.gitlibs/libs/
Install babashka v0.4.0+
MacOS
brew install borkdude/brew/babashka
Linux
sudo bash < <(curl -s https://raw.githubusercontent.com/babashka/babashka/master/install)
Run once:
bb requirements
to install other necessary tools (MacOS only, for Linux manual instruction).
To configure project workflow scripts use bb.edn
and build.clj
files.
Run bb tasks
to show help for project workflow. The following tasks are available:
clean Clean target folder
build Build deployable jar file for this project
install Install deployable jar locally (requires the pom.xml file)
deploy Deploy this library to Clojars
test Run tests
repl Run Clojure repl
outdated Check for outdated dependencies
outdated:fix Check for outdated dependencies and fix
format Format source code
lint Lint source code
requirements Install project requirements
Copyright © 2022 Mike Ananev
Distributed under the Eclipse Public License 1.0 or (at your option) any later version.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close