openssl-cmac 1.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +12 -0
- data/.yardopts +4 -0
- data/Gemfile +8 -0
- data/LICENSE +21 -0
- data/README.md +43 -0
- data/Rakefile +31 -0
- data/lib/openssl/cmac/version.rb +5 -0
- data/lib/openssl/cmac.rb +171 -0
- data/test/test_cmac.rb +167 -0
- metadata +156 -23
- data/lib/openssl_cmac.rb +0 -69
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 16491a689fe7f5d899159d3762cd7618f3150bc2a1ecede264731ef308a5d876
|
4
|
+
data.tar.gz: b4df90a8d51cdba9649101217df4978181a93ea680ac67c3b6cd0b15939e55e7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0b26a6dd073ebdef048598ccec4a44ab0fe7510a785a5dc656cc28760dfa596b7f7e358abd3d1940709687dc1e4ad730348198a0bd9174df166636055715b2fc
|
7
|
+
data.tar.gz: 82292fbd9b159ca48b8062093c8a6c093960b52813717729a38648f880ad84c2b9843e91b5d42782e91ee51a38dd05288ea76b3bdbe4e8b4552667144a540d5b
|
data/.rubocop.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014, Maxim Chechel, Lars Schmertmann <SmallLars@t-online.de>
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
[](http://badge.fury.io/rb/openssl-cmac)
|
2
|
+
[](https://gemnasium.com/SmallLars/openssl-cmac)
|
3
|
+
[](https://travis-ci.org/SmallLars/openssl-cmac)
|
4
|
+
[](https://coveralls.io/r/SmallLars/openssl-cmac)
|
5
|
+
[](https://codeclimate.com/github/SmallLars/openssl-cmac)
|
6
|
+
[](http://inch-ci.org/github/smalllars/openssl-cmac)
|
7
|
+
|
8
|
+
# openssl-cmac
|
9
|
+
|
10
|
+
Ruby Gem for
|
11
|
+
* [RFC 4493 - The AES-CMAC Algorithm](http://tools.ietf.org/html/rfc4493)
|
12
|
+
* [RFC 4494 - The AES-CMAC-96 Algorithm and Its Use with IPsec](http://tools.ietf.org/html/rfc4494)
|
13
|
+
* [RFC 4615 - The Advanced Encryption Standard-Cipher-based Message Authentication Code-Pseudo-Random Function-128 (AES-CMAC-PRF-128) Algorithm for the Internet Key Exchange Protocol (IKE)](http://tools.ietf.org/html/rfc4615)
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
gem 'openssl-cmac'
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install openssl-cmac
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
Example 1:
|
32
|
+
|
33
|
+
require 'openssl/cmac'
|
34
|
+
mac = OpenSSL::CMAC.digest('AES', 'message', 'key')
|
35
|
+
|
36
|
+
Example 2:
|
37
|
+
|
38
|
+
require 'openssl/cmac'
|
39
|
+
cmac = OpenSSL::CMAC.new('AES', 'key')
|
40
|
+
cmac.update('message chunk 1')
|
41
|
+
...
|
42
|
+
cmac.update('message chunk n')
|
43
|
+
mac = cmac.digest
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require './lib/openssl/cmac/version'
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
task :default => :build
|
6
|
+
|
7
|
+
desc "Run tests"
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << 'test'
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Create documentation"
|
13
|
+
task :doc do
|
14
|
+
sh "gem rdoc --rdoc openssl-cmac"
|
15
|
+
sh "yardoc"
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Uninstall and clean documentation"
|
19
|
+
task :clean do
|
20
|
+
sh "gem uninstall openssl-cmac"
|
21
|
+
begin; sh "rm -R ./coverage"; rescue; end
|
22
|
+
begin; sh "rm -R ./.yardoc"; rescue; end
|
23
|
+
begin; sh "rm -R ./doc"; rescue; end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Development Dependencies"
|
27
|
+
task (:devinst) { sh "gem install --dev ./pkg/openssl-cmac-#{OpenSSL::CMAC::VERSION}.gem" }
|
28
|
+
|
29
|
+
desc "Bundle install"
|
30
|
+
task (:bundle) { sh "bundle install" }
|
31
|
+
|
data/lib/openssl/cmac.rb
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module OpenSSL
|
4
|
+
# CMACError used for wrong parameter resonse.
|
5
|
+
class CMACError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
# Abstract from http://tools.ietf.org/html/rfc4493:
|
9
|
+
#
|
10
|
+
# The National Institute of Standards and Technology (NIST) has
|
11
|
+
# recently specified the Cipher-based Message Authentication Code
|
12
|
+
# (CMAC), which is equivalent to the One-Key CBC MAC1 (OMAC1) submitted
|
13
|
+
# by Iwata and Kurosawa. This memo specifies an authentication
|
14
|
+
# algorithm based on CMAC with the 128-bit Advanced Encryption Standard
|
15
|
+
# (AES). This new authentication algorithm is named AES-CMAC. The
|
16
|
+
# purpose of this document is to make the AES-CMAC algorithm
|
17
|
+
# conveniently available to the Internet Community.
|
18
|
+
#
|
19
|
+
# http://tools.ietf.org/html/rfc4494
|
20
|
+
# reduces the length of the result from 16 to 12 Byte.
|
21
|
+
#
|
22
|
+
# http://tools.ietf.org/html/rfc4615
|
23
|
+
# allows to use variable key sizes.
|
24
|
+
class CMAC
|
25
|
+
# Searches for supported algorithms within OpenSSL
|
26
|
+
#
|
27
|
+
# @return [[String]] supported algorithms
|
28
|
+
def self.ciphers
|
29
|
+
@ciphers ||= OpenSSL::Cipher.ciphers.select { |c| c.match(/-128-CBC$/i) }.map { |e| e[0..-9].upcase }.uniq
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the authentication code as a binary string. The cipher parameter
|
33
|
+
# must be an entry of OpenSSL::CMAC.ciphers.
|
34
|
+
#
|
35
|
+
# @param cipher [String] entry of OpenSSL::CMAC.ciphers
|
36
|
+
# @param key [String] binary key string
|
37
|
+
# @param data [String] binary data string
|
38
|
+
# @param length [Number] length of the authentication code
|
39
|
+
#
|
40
|
+
# @return [String] authentication code
|
41
|
+
def self.digest(cipher, key, data, length = 16)
|
42
|
+
CMAC.new(cipher, key).update(data).digest(length)
|
43
|
+
end
|
44
|
+
|
45
|
+
public
|
46
|
+
|
47
|
+
# Returns an instance of OpenSSL::CMAC set with the cipher algorithm and
|
48
|
+
# key to be used. The instance represents the initial state of the message
|
49
|
+
# authentication code before any data has been processed. To process data
|
50
|
+
# with it, use the instance method update with your data as an argument.
|
51
|
+
#
|
52
|
+
# @param cipher [String] entry of OpenSSL::CMAC.ciphers
|
53
|
+
# @param key [String] binary key string
|
54
|
+
#
|
55
|
+
# @return [Object] the new CMAC object
|
56
|
+
def initialize(cipher, key = '')
|
57
|
+
unless CMAC.ciphers.include?(cipher.upcase)
|
58
|
+
fail CMACError, "unsupported cipher algorithm (#{cipher})"
|
59
|
+
end
|
60
|
+
|
61
|
+
@keys = []
|
62
|
+
@buffer = ''.force_encoding('ASCII-8BIT')
|
63
|
+
@cipher = OpenSSL::Cipher.new("#{cipher.upcase}-128-CBC")
|
64
|
+
|
65
|
+
self.key = key unless key == ''
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns self as it was when it was first initialized with new key,
|
69
|
+
# with all processed data cleared from it.
|
70
|
+
#
|
71
|
+
# @param key [String] binary key string
|
72
|
+
#
|
73
|
+
# @return [Object] self with initial state and new key
|
74
|
+
def key=(key)
|
75
|
+
reset
|
76
|
+
key = CMAC.digest('AES', "\x00" * 16, key, 16) unless key.b.length == 16
|
77
|
+
|
78
|
+
@keys[0] = key.dup
|
79
|
+
@cipher.key = @keys[0]
|
80
|
+
|
81
|
+
cipher = OpenSSL::Cipher.new(@cipher.name)
|
82
|
+
cipher.encrypt
|
83
|
+
cipher.key = @keys[0]
|
84
|
+
k = (cipher.update("\x00" * 16) + cipher.final).bytes[0...16]
|
85
|
+
1.upto(2) do |i|
|
86
|
+
k = k.pack('C*').unpack('B*')[0]
|
87
|
+
msb = k.slice!(0)
|
88
|
+
k = [k, '0'].pack('B*').bytes
|
89
|
+
k[15] ^= 0x87 if msb == '1'
|
90
|
+
@keys[i] = k.dup
|
91
|
+
end
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
# Alias for: update
|
96
|
+
def <<(data)
|
97
|
+
update(data)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the block length of the used cipher algorithm.
|
101
|
+
#
|
102
|
+
# @return [Number] length of the used cipher algorithm
|
103
|
+
def block_length
|
104
|
+
16
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns the maximum length of the resulting digest.
|
108
|
+
#
|
109
|
+
# @return [Number] maximum length of the resulting digest
|
110
|
+
def digest_max_length
|
111
|
+
16
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the name of the used authentication code algorithm.
|
115
|
+
#
|
116
|
+
# @return [String] name of the used authentication code algorithm
|
117
|
+
def name
|
118
|
+
"CMAC with #{@cipher.name[0..-9]}"
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns self as it was when it was first initialized,
|
122
|
+
# with all processed data cleared from it.
|
123
|
+
#
|
124
|
+
# @return [Object] self with initial state
|
125
|
+
def reset
|
126
|
+
@keys.clear
|
127
|
+
@buffer.clear
|
128
|
+
@cipher.reset unless @keys[0].nil?
|
129
|
+
@cipher.iv = "\x00" * 16
|
130
|
+
@cipher.encrypt
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns self updated with the message to be authenticated.
|
135
|
+
# Can be called repeatedly with chunks of the message.
|
136
|
+
#
|
137
|
+
# @param data [String] binary data string
|
138
|
+
#
|
139
|
+
# @return [Object] self with new state
|
140
|
+
def update(data)
|
141
|
+
fail CMACError, 'no key is set' if @keys[0].nil?
|
142
|
+
|
143
|
+
@buffer += data
|
144
|
+
@cipher.update(@buffer.slice!(0...16)) while @buffer.length > 16
|
145
|
+
self
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns the authentication code an instance represents as a binary string.
|
149
|
+
#
|
150
|
+
# @param length [Number] length of the authentication code
|
151
|
+
def digest(length = 16)
|
152
|
+
fail CMACError, 'no key is set' if @keys[0].nil?
|
153
|
+
fail CMACError, 'no key is set' unless length.between?(1, 16)
|
154
|
+
|
155
|
+
block = @buffer.bytes
|
156
|
+
@buffer.clear
|
157
|
+
k = @keys[block.length == 16 ? 1 : 2].dup
|
158
|
+
i = block.length.times { |t| k[t] ^= block[t] }
|
159
|
+
k[i] ^= 0x80 if i < 16
|
160
|
+
mac = @cipher.update(k.pack('C*')) + @cipher.final
|
161
|
+
@cipher.reset
|
162
|
+
@cipher.encrypt
|
163
|
+
@cipher.key = @keys[0]
|
164
|
+
@cipher.iv = "\x00" * 16
|
165
|
+
# Each block is 16-bytes and the last block will always be PKCS#7 padding
|
166
|
+
# which we want to discard. Take the last block prior to the padding for
|
167
|
+
# the MAC.
|
168
|
+
mac[-32...(-32 + length)]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/test/test_cmac.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
3
|
+
require 'test/unit'
|
4
|
+
require 'openssl/cmac'
|
5
|
+
|
6
|
+
# Testclass with Test Vectors from RFC's
|
7
|
+
class CMACTest < Test::Unit::TestCase
|
8
|
+
# http://tools.ietf.org/html/rfc4493#section-4
|
9
|
+
KEY = ['2b7e151628aed2a6abf7158809cf4f3c'].pack('H*')
|
10
|
+
DATA = [[''].pack('H*'),
|
11
|
+
['6bc1bee22e409f96e93d7e117393172a'].pack('H*'),
|
12
|
+
['6bc1bee22e409f96e93d7e117393172a'\
|
13
|
+
'ae2d8a571e03ac9c9eb76fac45af8e51'\
|
14
|
+
'30c81c46a35ce411'].pack('H*'),
|
15
|
+
['6bc1bee22e409f96e93d7e117393172a'\
|
16
|
+
'ae2d8a571e03ac9c9eb76fac45af8e51'\
|
17
|
+
'30c81c46a35ce411e5fbc1191a0a52ef'\
|
18
|
+
'f69f2445df4f9b17ad2b417be66c3710'].pack('H*')]
|
19
|
+
MAC = %w(bb1d6929e95937287fa37d129b756746
|
20
|
+
070a16b46b4d4144f79bdd9dd04a287c
|
21
|
+
dfa66747de9ae63030ca32611497c827
|
22
|
+
51f0bebf7e3b9d92fc49741779363cfe)
|
23
|
+
|
24
|
+
# http://tools.ietf.org/html/rfc4615#section-4
|
25
|
+
PRF_KEYS = [['000102030405060708090a0b0c0d0e0fedcb'].pack('H*'),
|
26
|
+
['000102030405060708090a0b0c0d0e0f'].pack('H*'),
|
27
|
+
['00010203040506070809'].pack('H*')]
|
28
|
+
PRF_DATA = ['000102030405060708090a0b0c0d0e0f10111213'].pack('H*')
|
29
|
+
PRF_OUTS = %w(84a348a4a45d235babfffc0d2b4da09a
|
30
|
+
980ae87b5f4c9c5214f5b6a8455e4c2d
|
31
|
+
290d9e112edb09ee141fcf64c0b72f3d)
|
32
|
+
|
33
|
+
def test_cmac_keys
|
34
|
+
cmac = OpenSSL::CMAC.new('AES')
|
35
|
+
cmac.key = KEY
|
36
|
+
check_keys(cmac)
|
37
|
+
|
38
|
+
cmac = OpenSSL::CMAC.new('AES', KEY)
|
39
|
+
check_keys(cmac)
|
40
|
+
|
41
|
+
assert(cmac.instance_variable_get(:@buffer).empty?, 'Wrong buffer')
|
42
|
+
cmac.update(DATA[2])
|
43
|
+
assert(cmac.instance_variable_get(:@buffer).length == 8, 'Wrong buffer')
|
44
|
+
cmac.update(DATA[2])
|
45
|
+
assert(cmac.instance_variable_get(:@buffer).length == 16, 'Wrong buffer')
|
46
|
+
|
47
|
+
cmac.reset
|
48
|
+
assert(cmac.instance_variable_get(:@keys)[0].nil?, 'Reset fail')
|
49
|
+
assert(cmac.instance_variable_get(:@keys)[1].nil?, 'Reset fail')
|
50
|
+
assert(cmac.instance_variable_get(:@keys)[2].nil?, 'Reset fail')
|
51
|
+
assert_equal('', cmac.instance_variable_get(:@buffer), 'Reset fail')
|
52
|
+
|
53
|
+
assert_raise(OpenSSL::CMACError) { cmac.update(DATA[2]) }
|
54
|
+
assert_raise(OpenSSL::CMACError) { cmac.digest }
|
55
|
+
|
56
|
+
cmac.key = KEY
|
57
|
+
check_keys(cmac)
|
58
|
+
|
59
|
+
m = cmac.update(DATA[2]).digest.unpack('H*')[0]
|
60
|
+
assert_equal(MAC[2], m)
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_keys(cmac)
|
64
|
+
assert_equal(
|
65
|
+
'2b7e151628aed2a6abf7158809cf4f3c',
|
66
|
+
cmac.instance_variable_get(:@keys)[0].unpack('H*')[0],
|
67
|
+
'Key ERROR'
|
68
|
+
)
|
69
|
+
assert_equal(
|
70
|
+
'fbeed618357133667c85e08f7236a8de',
|
71
|
+
cmac.instance_variable_get(:@keys)[1].pack('C*').unpack('H*')[0],
|
72
|
+
'SubKey 1 ERROR'
|
73
|
+
)
|
74
|
+
|
75
|
+
assert_equal(
|
76
|
+
'f7ddac306ae266ccf90bc11ee46d513b',
|
77
|
+
cmac.instance_variable_get(:@keys)[2].pack('C*').unpack('H*')[0],
|
78
|
+
'SubKey 2 ERROR'
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_cmac_vars
|
83
|
+
cmac = OpenSSL::CMAC.new('AES')
|
84
|
+
assert_equal(16, cmac.block_length)
|
85
|
+
assert_equal(16, cmac.digest_max_length)
|
86
|
+
assert_equal('CMAC with AES', cmac.name)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_cmac_update
|
90
|
+
for cipher in ['aes', 'AES']
|
91
|
+
# Test with 1 call of update and new CCM object for each test.
|
92
|
+
DATA.length.times do |i|
|
93
|
+
cmac = OpenSSL::CMAC.new(cipher, KEY)
|
94
|
+
m = cmac.update(DATA[i]).digest.unpack('H*')[0]
|
95
|
+
assert_equal(MAC[i], m, "Test: 1, Vector: #{i + 1}")
|
96
|
+
end
|
97
|
+
|
98
|
+
# Test with 1 call of update and same CCM object for each test.
|
99
|
+
# There is no reset, because it should be possible to calculate
|
100
|
+
# a new mac after digest without reset.
|
101
|
+
cmac = OpenSSL::CMAC.new(cipher, KEY)
|
102
|
+
DATA.length.times do |i|
|
103
|
+
m = cmac.update(DATA[i]).digest.unpack('H*')[0]
|
104
|
+
assert_equal(MAC[i], m, "Test: 2, Vector: #{i + 1}")
|
105
|
+
end
|
106
|
+
|
107
|
+
# Test with multiple calls of update and new CCM object for each test
|
108
|
+
1.upto(DATA.length - 1) do |i|
|
109
|
+
1.upto(17) do |c|
|
110
|
+
cmac = OpenSSL::CMAC.new(cipher, KEY)
|
111
|
+
DATA[i].bytes.each_slice(c) { |w| cmac.update(w.pack('C*')) }
|
112
|
+
m = cmac.digest.unpack('H*')[0]
|
113
|
+
assert_equal(MAC[i], m, "Test: 3, Vector: #{i + 1}, Tokenlen: #{c}")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Test with multiple calls of update and same CCM object for each test
|
118
|
+
cmac = OpenSSL::CMAC.new(cipher, KEY)
|
119
|
+
1.upto(DATA.length - 1) do |i|
|
120
|
+
1.upto(17) do |c|
|
121
|
+
DATA[i].bytes.each_slice(c) { |w| cmac.update(w.pack('C*')) }
|
122
|
+
m = cmac.digest.unpack('H*')[0]
|
123
|
+
assert_equal(MAC[i], m, "Test: 4, Vector: #{i + 1}, Tokenlen: #{c}")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Test for Operator <<
|
129
|
+
DATA[3].bytes.each_slice(5) { |w| cmac << w.pack('C*') }
|
130
|
+
m = cmac.digest.unpack('H*')[0]
|
131
|
+
assert_equal(MAC[3], m, 'Test: 5, Vector: 4, Tokenlen: 5')
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_cmac_digest
|
135
|
+
for cipher in ['aes', 'AES']
|
136
|
+
cmac = OpenSSL::CMAC.new(cipher, KEY)
|
137
|
+
m = cmac.update(DATA[3]).digest.unpack('H*')[0]
|
138
|
+
assert_equal(MAC[3], m, 'Digest with no update')
|
139
|
+
|
140
|
+
cmac.update(DATA[3].b[0...20])
|
141
|
+
m = cmac.update(DATA[3].b[20...64]).digest.unpack('H*')[0]
|
142
|
+
assert_equal(MAC[3], m, 'Digest after update')
|
143
|
+
|
144
|
+
cmac.update(DATA[3])
|
145
|
+
m = cmac.update('').digest.unpack('H*')[0]
|
146
|
+
assert_equal(MAC[3], m, 'Empty digest')
|
147
|
+
|
148
|
+
DATA.length.times do |i|
|
149
|
+
m = OpenSSL::CMAC.digest(cipher, KEY, DATA[i]).unpack('H*')[0]
|
150
|
+
assert_equal(MAC[i], m, "Vector: #{i + 1}")
|
151
|
+
|
152
|
+
m = OpenSSL::CMAC.digest(cipher, KEY, DATA[i], 12).unpack('H*')[0]
|
153
|
+
assert_equal(24, m.length, "Vector: #{i + 1} - length")
|
154
|
+
assert_equal(MAC[i][0...24], m, "Vector: #{i + 1} - 12")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_cmac_prf
|
160
|
+
cmac = OpenSSL::CMAC.new('AES')
|
161
|
+
3.times do |i|
|
162
|
+
cmac.key = PRF_KEYS[i]
|
163
|
+
m = cmac.update(PRF_DATA).digest.unpack('H*')[0]
|
164
|
+
assert_equal(PRF_OUTS[i], m, "Vector: #{i + 1}")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
metadata
CHANGED
@@ -1,46 +1,179 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openssl-cmac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Maxim M. Chechel
|
9
|
-
|
8
|
+
- Lars Schmertmann
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
|
15
|
-
|
12
|
+
date: 2022-07-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '12.3'
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 12.3.2
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '12.3'
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 12.3.2
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rdoc
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.3'
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 4.3.0
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - "~>"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '4.3'
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 4.3.0
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: yard
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.9'
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.9.16
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0.9'
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 0.9.16
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: rubocop
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0.50'
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 0.50.0
|
84
|
+
type: :development
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0.50'
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.50.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: test-unit
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '3.2'
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 3.2.9
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.2'
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 3.2.9
|
114
|
+
- !ruby/object:Gem::Dependency
|
115
|
+
name: coveralls
|
116
|
+
requirement: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - "~>"
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0.8'
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 0.8.22
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0.8'
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 0.8.22
|
134
|
+
description: Ruby Gem for RFC 4493, 4494, 4615 - The AES-CMAC Algorithm
|
135
|
+
email:
|
136
|
+
- maximchick@gmail.com
|
137
|
+
- SmallLars@t-online.de
|
16
138
|
executables: []
|
17
139
|
extensions: []
|
18
|
-
extra_rdoc_files:
|
140
|
+
extra_rdoc_files:
|
141
|
+
- README.md
|
142
|
+
- LICENSE
|
19
143
|
files:
|
20
|
-
-
|
21
|
-
|
144
|
+
- ".rubocop.yml"
|
145
|
+
- ".yardopts"
|
146
|
+
- Gemfile
|
147
|
+
- LICENSE
|
148
|
+
- README.md
|
149
|
+
- Rakefile
|
150
|
+
- lib/openssl/cmac.rb
|
151
|
+
- lib/openssl/cmac/version.rb
|
152
|
+
- test/test_cmac.rb
|
153
|
+
homepage: https://github.com/smalllars/openssl-cmac
|
22
154
|
licenses:
|
23
155
|
- MIT
|
24
|
-
|
25
|
-
|
156
|
+
metadata: {}
|
157
|
+
post_install_message: Thanks for installing!
|
158
|
+
rdoc_options:
|
159
|
+
- "-x"
|
160
|
+
- test/data_*
|
26
161
|
require_paths:
|
27
162
|
- lib
|
28
163
|
required_ruby_version: !ruby/object:Gem::Requirement
|
29
|
-
none: false
|
30
164
|
requirements:
|
31
|
-
- -
|
165
|
+
- - ">="
|
32
166
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
167
|
+
version: 2.0.0
|
34
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
35
|
-
none: false
|
36
169
|
requirements:
|
37
|
-
- -
|
170
|
+
- - ">="
|
38
171
|
- !ruby/object:Gem::Version
|
39
172
|
version: '0'
|
40
173
|
requirements: []
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
174
|
+
rubygems_version: 3.3.15
|
175
|
+
signing_key:
|
176
|
+
specification_version: 4
|
177
|
+
summary: RFC 4493, 4494, 4615 - CMAC
|
178
|
+
test_files:
|
179
|
+
- test/test_cmac.rb
|
data/lib/openssl_cmac.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
# This is an implementation of AES-CMAC Algorithm:
|
2
|
-
# http://tools.ietf.org/html/rfc4493
|
3
|
-
#
|
4
|
-
# OpenSSL version > 1.0.1 already has a native implementation of CMAC
|
5
|
-
# but there are no corresponding bindings in Ruby OpenSSL standard library
|
6
|
-
|
7
|
-
require 'openssl'
|
8
|
-
|
9
|
-
module OpenSSL
|
10
|
-
class CMAC
|
11
|
-
CONST_ZERO = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".force_encoding('ASCII-8BIT')
|
12
|
-
CONST_RB = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87]
|
13
|
-
|
14
|
-
# key - base 128 bit AES key
|
15
|
-
def initialize(key)
|
16
|
-
@key = key
|
17
|
-
@k1, @k2 = CMAC.gen_subkeys(@key)
|
18
|
-
end
|
19
|
-
|
20
|
-
def generate(data)
|
21
|
-
data8 = data.dup.force_encoding('ASCII-8BIT')
|
22
|
-
|
23
|
-
xor_key = @k1
|
24
|
-
unless data8.size > 0 && 0 == data8.size % 16
|
25
|
-
xor_key = @k2
|
26
|
-
padding = "\x80"
|
27
|
-
padding << "\x00" * (15 - data8.size % 16)
|
28
|
-
data8 << padding
|
29
|
-
end
|
30
|
-
|
31
|
-
data8[-16, 16].unpack('C*').each_with_index do |e, i|
|
32
|
-
data8[data8.size - 16 + i] = (e ^ xor_key[i]).chr
|
33
|
-
end
|
34
|
-
|
35
|
-
cipher = Cipher::AES.new(128, :CBC)
|
36
|
-
cipher.encrypt
|
37
|
-
cipher.key = @key
|
38
|
-
|
39
|
-
cipher.update(data8)[-16, 16]
|
40
|
-
end
|
41
|
-
|
42
|
-
def verify(data, cmac)
|
43
|
-
generate(data) == cmac
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.gen_subkeys(key)
|
47
|
-
cipher = Cipher::AES.new(128, :ECB)
|
48
|
-
cipher.encrypt
|
49
|
-
cipher.key = key
|
50
|
-
|
51
|
-
k1 = (cipher.update(CONST_ZERO)).unpack('C*')
|
52
|
-
xor_flag = k1[0] >= 0x80
|
53
|
-
|
54
|
-
k2 = Array.new(16)
|
55
|
-
|
56
|
-
k1.each_with_index {|e, i|
|
57
|
-
lsb = i == 15 ? 0 : (k1[i+1] & 0x80) / 0x80
|
58
|
-
k1[i] = (k1[i] << 1) % 256 | lsb
|
59
|
-
k1[i] ^= CONST_RB[i] if xor_flag
|
60
|
-
|
61
|
-
lsb = i == 15 ? 0 : (k1[i+1] << 1 & 0x80) / 0x80
|
62
|
-
k2[i] = (k1[i] << 1) % 256 | lsb
|
63
|
-
k2[i] ^= CONST_RB[i] if k1[0] >= 0x80
|
64
|
-
}
|
65
|
-
|
66
|
-
[k1, k2]
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|