gost_magma 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cd350e44bcc1d1a5292bb8c55bc4b3c823642a17
4
+ data.tar.gz: a5cd6e5ad70cf28dc9b488b4cd6bd19f0adaa5d8
5
+ SHA512:
6
+ metadata.gz: e4123d9e2d4b88bbae6a8e2803eb9994cecccfc602dab301e90a439e5b17cdd2fe1bf0adb780a39914c002d56ce577ee19c837f9e36765f58d29ccf6a3cd26e9
7
+ data.tar.gz: 2cc20c75e00a0b34d88082c161c279fbb1a9be4d8cda253c718e4d94707562a00580ea3246498c0b6c8e326d63700e77a1b243cd5cf6a5f19a2c7853db84a873
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.4.5
7
+ before_install: gem install bundler -v 2.0.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gost_magma.gemspec
4
+ gemspec
5
+
6
+ gem 'minitest-reporters', '1.2.0'
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gost_magma (0.1.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ansi (1.5.0)
10
+ builder (3.2.3)
11
+ minitest (5.11.3)
12
+ minitest-reporters (1.2.0)
13
+ ansi
14
+ builder
15
+ minitest (>= 5.0)
16
+ ruby-progressbar
17
+ rake (10.5.0)
18
+ ruby-progressbar (1.10.0)
19
+
20
+ PLATFORMS
21
+ x64-mingw32
22
+
23
+ DEPENDENCIES
24
+ bundler (~> 2.0)
25
+ gost_magma!
26
+ minitest (~> 5.0)
27
+ minitest-reporters (= 1.2.0)
28
+ rake (~> 10.0)
29
+
30
+ BUNDLED WITH
31
+ 2.0.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 vblazhnov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,151 @@
1
+ # GostMagma
2
+
3
+ GOST R 34.12/13-2015 (Magma) block cipher algorithms for ECB, CBC, CTR, OFB, CFB, OMAC, CTR-ACPKM, OMAC-ACPKM modes and key export/import algorithms.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'gost_magma'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install gost_magma
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ require 'gost_magma'
25
+ include GostMagma
26
+
27
+ BlockSize = Magma::BlockLengthInBytes
28
+
29
+ # GOST R 34.13-2015 Magma test data
30
+ SelfTestGostMMasterKeyData = [
31
+ 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
32
+ 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
33
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
34
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
35
+ ].pack('C*').freeze
36
+ key = SelfTestGostMMasterKeyData
37
+
38
+ SelfTestGostMPlainText = [
39
+ 0x92, 0xde, 0xf0, 0x6b, 0x3c, 0x13, 0x0a, 0x59,
40
+ 0xdb, 0x54, 0xc7, 0x04, 0xf8, 0x18, 0x9d, 0x20,
41
+ 0x4a, 0x98, 0xfb, 0x2e, 0x67, 0xa8, 0x02, 0x4c,
42
+ 0x89, 0x12, 0x40, 0x9b, 0x17, 0xb5, 0x7e, 0x41,
43
+ ].pack('C*').freeze
44
+ plain_text = SelfTestGostMPlainText
45
+
46
+ # ECB mode
47
+ SelfTestGostMEcbEncText = [
48
+ 0x2b, 0x07, 0x3f, 0x04, 0x94, 0xf3, 0x72, 0xa0,
49
+ 0xde, 0x70, 0xe7, 0x15, 0xd3, 0x55, 0x6e, 0x48,
50
+ 0x11, 0xd8, 0xd9, 0xe9, 0xea, 0xcf, 0xbc, 0x1e,
51
+ 0x7c, 0x68, 0x26, 0x09, 0x96, 0xc6, 0x7e, 0xfb
52
+ ].pack('C*').freeze
53
+ encrypted_test = SelfTestGostMEcbEncText
54
+
55
+ encrypted_text = MagmaEcb.new(key).encrypt(plain_text)
56
+ puts "ECB encrypted_text == encrypted_test: #{encrypted_text == encrypted_test}"
57
+
58
+ decrypted_text = MagmaEcb.new(key).decrypt(encrypted_test)
59
+ puts "ECB decrypted_text == plain_text: #{decrypted_text == plain_text}"
60
+
61
+ # OMAC mode
62
+ SelfTestGostMMacValue = [
63
+ 0x15, 0x4e, 0x72, 0x10, 0x20, 0x30, 0xc5, 0xbb
64
+ ].pack('C*').freeze
65
+ mac_test = SelfTestGostMMacValue
66
+
67
+ mac = MagmaOmac.new(key, mac_test.length).update(plain_text).final
68
+ puts "OMAC mac == mac_test: #{mac == mac_test}"
69
+
70
+ # CTR mode
71
+ SelfTestGostMCtrSV = [
72
+ 0x12, 0x34, 0x56, 0x78
73
+ ].pack('C*').freeze
74
+ iv = SelfTestGostMCtrSV
75
+
76
+ SelfTestGostMCtrEncText = [
77
+ 0x4e, 0x98, 0x11, 0x0c, 0x97, 0xb7, 0xb9, 0x3c,
78
+ 0x3e, 0x25, 0x0d, 0x93, 0xd6, 0xe8, 0x5d, 0x69,
79
+ 0x13, 0x6d, 0x86, 0x88, 0x07, 0xb2, 0xdb, 0xef,
80
+ 0x56, 0x8e, 0xb6, 0x80, 0xab, 0x52, 0xa1, 0x2d
81
+ ].pack('C*').freeze
82
+ encrypted_test = SelfTestGostMCtrEncText
83
+
84
+ encrypted_text = MagmaCtr.new(key, iv, BlockSize).encrypt(plain_text)
85
+ puts "CTR encrypted_text == encrypted_test: #{encrypted_text == encrypted_test}"
86
+
87
+ # CTR multi-part usage
88
+ text_len = plain_text.length
89
+ ctx = MagmaCtr.new(key, iv, BlockSize)
90
+ decrypted_text = ctx.decrypt(encrypted_test[0...text_len/3]) +
91
+ ctx.decrypt(encrypted_test[text_len/3..-1])
92
+ puts "CTR decrypted_text == plain_text: #{decrypted_text == plain_text}"
93
+
94
+ # Key export/import (TC 26 KExp15/KImp15)
95
+ TC26_K = [
96
+ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
97
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
98
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
99
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
100
+ ].pack('C*').freeze
101
+ TC26_Kmac = [
102
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
103
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
104
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
105
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
106
+ ].pack('C*').freeze
107
+ TC26_Kenc = [
108
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
109
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
110
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
111
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
112
+ ].pack('C*').freeze
113
+ TC26_IV_M = [
114
+ 0x67, 0xBE, 0xD6, 0x54
115
+ ].pack('C*').freeze
116
+ TC26_Kexp_M = [
117
+ 0xCF, 0xD5, 0xA1, 0x2D, 0x5B, 0x81, 0xB6, 0xE1,
118
+ 0xE9, 0x9C, 0x91, 0x6D, 0x07, 0x90, 0x0C, 0x6A,
119
+ 0xC1, 0x27, 0x03, 0xFB, 0x3A, 0xBD, 0xED, 0x55,
120
+ 0x56, 0x7B, 0xF3, 0x74, 0x2C, 0x89, 0x9C, 0x75,
121
+ 0x5D, 0xAF, 0xE7, 0xB4, 0x2E, 0x3A, 0x8B, 0xD9
122
+ ].pack('C*').freeze
123
+
124
+ key = TC26_K
125
+ key_mac = TC26_Kmac
126
+ key_enc = TC26_Kenc
127
+ iv = TC26_IV_M
128
+ key_exp_test = TC26_Kexp_M
129
+
130
+ key_exp = MagmaKeyExpImp::export(key, key_mac, key_enc, iv)
131
+ puts "Key Export key_exp == key_exp_test: #{key_exp == key_exp_test}"
132
+
133
+ imp_key = MagmaKeyExpImp::import(key_exp_test, key_mac, key_enc, iv)
134
+ puts "Key Import imp_key == key: #{imp_key == key}"
135
+ ```
136
+
137
+ For other cipher modes see test samples in /test/gost_magma_test.rb please.
138
+
139
+ ## Development
140
+
141
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
142
+
143
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
144
+
145
+ ## Contributing
146
+
147
+ Bug reports and pull requests are welcome on GitHub at https://github.com/vblazhnovgit/gost_magma.
148
+
149
+ ## License
150
+
151
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gost_magma"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,38 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "gost_magma/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gost_magma"
8
+ spec.version = GostMagma::VERSION
9
+ spec.authors = ["vblazhnovgit"]
10
+ spec.email = ["vblazhnov@yandex.ru"]
11
+
12
+ spec.summary = %q{Magma block ciphers}
13
+ spec.description = %q{GOST R 34.12/13-2015 (Magma) block cipher algorithms for ECB, CBC, CTR, OFB, CFB, OMAC, CTR-ACPKM and OMAC-ACPKM modes.}
14
+ spec.homepage = "https://github.com/vblazhnovgit/gost_magma.git"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ # Specify which files should be added to the gem when it is released.
27
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
28
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
29
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ spec.add_development_dependency "bundler", "~> 2.0"
36
+ spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "minitest", "~> 5.0"
38
+ end
@@ -0,0 +1,15 @@
1
+ require "gost_magma/version"
2
+
3
+ module GostMagma
4
+ require "gost_magma/magma"
5
+ require "gost_magma/magma_tables"
6
+ require "gost_magma/magma_ecb"
7
+ require "gost_magma/magma_omac"
8
+ require "gost_magma/magma_ctr"
9
+ require "gost_magma/magma_ofb"
10
+ require "gost_magma/magma_cfb"
11
+ require "gost_magma/magma_cbc"
12
+ require "gost_magma/magma_ctr_acpkm"
13
+ require "gost_magma/magma_omac_acpkm"
14
+ require "gost_magma/magma_key_exp_imp"
15
+ end
@@ -0,0 +1,165 @@
1
+ module GostMagma
2
+ # Base abstract class
3
+ class Magma
4
+ # class constants
5
+ BlockLengthInBytes = 8
6
+ KeyLengthInBytes = 32
7
+
8
+ # 's' stands for native-endian byte order but 'n' stands for network (big-endian) byte order
9
+ BigEndian = [1].pack('s') == [1].pack('n')
10
+
11
+ protected
12
+
13
+ def self.printBytes(bytes, line_size = 16)
14
+ bytes.unpack('H*')[0].scan(/.{1,#{line_size}}/).each{|s| puts(s)}
15
+ end
16
+
17
+ def self.zeroBytes(n)
18
+ ("\x00"*n).force_encoding('BINARY')
19
+ end
20
+
21
+ def self.zeroBlock
22
+ ("\x00"*BlockLengthInBytes).force_encoding('BINARY')
23
+ end
24
+
25
+ # Unload 32-bit number to 8-byte string
26
+ # (big-endian, adding leading zeroes)
27
+ def self.uint32ToUint8BE(n)
28
+ str = n.to_s(16) # big-endian
29
+ len = str.length
30
+ # add leading zeroes
31
+ str.insert(0, '0'*(8 - len)) if len < 8
32
+ # To byte string
33
+ bytes = [str].pack('H*')
34
+ end
35
+
36
+ # Unload 32-bit number to 8-byte string
37
+ # (native-endian, adding leading zeroes)
38
+ def self.uint32ToUint8(n)
39
+ bytes = uint32ToUint8BE(n)
40
+ bytes.reverse! unless BigEndian
41
+ bytes
42
+ end
43
+
44
+ # Unpacks 8-byte string to 32-bit number
45
+ # (native-endian)
46
+ def self.uint8ToUint32(bytes)
47
+ bytes.unpack('L*')[0]
48
+ end
49
+
50
+ def self.encryptRound(right, left, key1, key2)
51
+ t = (key1 + right) & 0xffffffff
52
+ left ^= TzTable[0][t & 0xff] ^ TzTable[1][(t >> 8) & 0xff] ^
53
+ TzTable[2][(t >> 16) & 0xff] ^ TzTable[3][t >> 24 & 0xff]
54
+ t = (key2 + left) & 0xffffffff
55
+ right ^= TzTable[0][t & 0xff] ^ TzTable[1][(t >> 8) & 0xff] ^
56
+ TzTable[2][(t >> 16) & 0xff] ^ TzTable[3][(t >> 24) & 0xff]
57
+ [right, left]
58
+ end
59
+
60
+ def self.encryptCycle(right, left, keys)
61
+ right, left = encryptRound(right, left, keys[0], keys[1])
62
+ right, left = encryptRound(right, left, keys[2], keys[3])
63
+ right, left = encryptRound(right, left, keys[4], keys[5])
64
+ right, left = encryptRound(right, left, keys[6], keys[7])
65
+ right, left = encryptRound(right, left, keys[0], keys[1])
66
+ right, left = encryptRound(right, left, keys[2], keys[3])
67
+ right, left = encryptRound(right, left, keys[4], keys[5])
68
+ right, left = encryptRound(right, left, keys[6], keys[7])
69
+ right, left = encryptRound(right, left, keys[0], keys[1])
70
+ right, left = encryptRound(right, left, keys[2], keys[3])
71
+ right, left = encryptRound(right, left, keys[4], keys[5])
72
+ right, left = encryptRound(right, left, keys[6], keys[7])
73
+ right, left = encryptRound(right, left, keys[7], keys[6])
74
+ right, left = encryptRound(right, left, keys[5], keys[4])
75
+ right, left = encryptRound(right, left, keys[3], keys[2])
76
+ right, left = encryptRound(right, left, keys[1], keys[0])
77
+ [right, left]
78
+ end
79
+
80
+ def self.decryptCycle(right, left, keys)
81
+ right, left = encryptRound(right, left, keys[0], keys[1])
82
+ right, left = encryptRound(right, left, keys[2], keys[3])
83
+ right, left = encryptRound(right, left, keys[4], keys[5])
84
+ right, left = encryptRound(right, left, keys[6], keys[7])
85
+ right, left = encryptRound(right, left, keys[7], keys[6])
86
+ right, left = encryptRound(right, left, keys[5], keys[4])
87
+ right, left = encryptRound(right, left, keys[3], keys[2])
88
+ right, left = encryptRound(right, left, keys[1], keys[0])
89
+ right, left = encryptRound(right, left, keys[7], keys[6])
90
+ right, left = encryptRound(right, left, keys[5], keys[4])
91
+ right, left = encryptRound(right, left, keys[3], keys[2])
92
+ right, left = encryptRound(right, left, keys[1], keys[0])
93
+ right, left = encryptRound(right, left, keys[7], keys[6])
94
+ right, left = encryptRound(right, left, keys[5], keys[4])
95
+ right, left = encryptRound(right, left, keys[3], keys[2])
96
+ right, left = encryptRound(right, left, keys[1], keys[0])
97
+ [right, left]
98
+ end
99
+
100
+ def self.encryptBlockUintKey(input, keys)
101
+ right = uint8ToUint32(input[0..3])
102
+ left = uint8ToUint32(input[4..-1])
103
+ right, left = encryptCycle(right, left, keys)
104
+ output = uint32ToUint8(left) + uint32ToUint8(right)
105
+ end
106
+
107
+ def self.decryptBlockUintKey(input, keys)
108
+ right = uint8ToUint32(input[0..3])
109
+ left = uint8ToUint32(input[4..-1])
110
+ right, left = decryptCycle(right, left, keys)
111
+ output = uint32ToUint8(left) + uint32ToUint8(right)
112
+ end
113
+
114
+ def self.encryptBlock(input, keys)
115
+ tmp_input = input.reverse
116
+ tmp_output = encryptBlockUintKey(tmp_input, keys)
117
+ output = tmp_output.reverse
118
+ end
119
+
120
+ def self.decryptBlock(input, keys)
121
+ tmp_input = input.reverse
122
+ tmp_output = decryptBlockUintKey(tmp_input, keys)
123
+ output = tmp_output.reverse
124
+ end
125
+
126
+ # Increment CTR counter
127
+ def self.incrementModulo(counter, size)
128
+ lastIndex = size - 1
129
+ (0...size).each do |i|
130
+ if counter[lastIndex - i].ord > 0xfe then
131
+ counter[lastIndex - i] = (counter[lastIndex - i].ord - 0xff).chr
132
+ else
133
+ counter[lastIndex - i] = (counter[lastIndex - i].ord + 1).chr
134
+ break
135
+ end
136
+ end
137
+ counter
138
+ end
139
+
140
+ # block - byte string
141
+ def self.shiftLeftOne(block)
142
+ (0...(BlockLengthInBytes-1)).each do |i|
143
+ ri1 = block[i+1].ord
144
+ ri = block[i].ord << 1
145
+ ri &= 0xfe
146
+ ri |= (ri1 >> 7) & 0x1
147
+ block[i] = ri.chr
148
+ end
149
+ ri = block[BlockLengthInBytes-1].ord << 1
150
+ block[BlockLengthInBytes-1] = (ri & 0xfe).chr
151
+ block
152
+ end
153
+
154
+ def self.padd(incomplete_block)
155
+ padding_len = BlockLengthInBytes - (incomplete_block.length % BlockLengthInBytes)
156
+ padded_block = incomplete_block.dup
157
+ padded_block += 0x80.chr
158
+ padding_len -= 1
159
+ if padding_len > 0 then
160
+ padded_block += 0.chr * padding_len
161
+ end
162
+ padded_block
163
+ end
164
+ end
165
+ end