hsmr 0.0.1
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.
- data/.gitignore +44 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +11 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +20 -0
- data/README.md +67 -0
- data/Rakefile +13 -0
- data/hsmr.gemspec +28 -0
- data/lib/hsmr.rb +267 -0
- data/lib/hsmr/component.rb +38 -0
- data/lib/hsmr/key.rb +56 -0
- data/lib/hsmr/version.rb +3 -0
- data/spec/hsmr_spec.rb +181 -0
- data/spec/spec_helper.rb +12 -0
- data/test/hsmr_test.rb +182 -0
- data/test/test_helper.rb +2 -0
- metadata +113 -0
data/.gitignore
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
|
4
|
+
# rdoc generated
|
5
|
+
rdoc
|
6
|
+
|
7
|
+
# yard generated
|
8
|
+
doc
|
9
|
+
.yardoc
|
10
|
+
|
11
|
+
# bundler
|
12
|
+
.bundle
|
13
|
+
Gemfile.lock
|
14
|
+
|
15
|
+
# jeweler generated
|
16
|
+
pkg
|
17
|
+
|
18
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
19
|
+
#
|
20
|
+
# * Create a file at ~/.gitignore
|
21
|
+
# * Include files you want ignored
|
22
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
23
|
+
#
|
24
|
+
# After doing this, these files will be ignored in all your git projects,
|
25
|
+
# saving you from having to 'pollute' every project you touch with them
|
26
|
+
#
|
27
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
28
|
+
#
|
29
|
+
# For MacOS:
|
30
|
+
#
|
31
|
+
#.DS_Store
|
32
|
+
#
|
33
|
+
# For TextMate
|
34
|
+
#*.tmproj
|
35
|
+
#tmtags
|
36
|
+
#
|
37
|
+
# For emacs:
|
38
|
+
#*~
|
39
|
+
#\#*
|
40
|
+
#.\#*
|
41
|
+
#
|
42
|
+
# For vim:
|
43
|
+
*.swp
|
44
|
+
*.un~
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
## v0.0.1
|
2
|
+
|
3
|
+
* Initial release
|
4
|
+
* Key / Componet features
|
5
|
+
* Create Keys and Components
|
6
|
+
* XOR Keys and Components together
|
7
|
+
* Find Key Check Values (kcv)
|
8
|
+
* Determine and change key parity
|
9
|
+
* Encrypt / Decrypt PIN Blocks
|
10
|
+
* Generate IBM3624 PINs
|
11
|
+
* Generate PVV, CVV & CVV2 values
|
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in hsmr.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
#group :development do
|
7
|
+
# gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i
|
8
|
+
# gem 'growl', :require => false if RUBY_PLATFORM =~ /darwin/i
|
9
|
+
# gem 'guard-test'
|
10
|
+
# gem 'factory_girl'
|
11
|
+
#end
|
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Dan Milne
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
HSMR [](http://travis-ci.org/dkam/hsmr)
|
2
|
+
===========
|
3
|
+
|
4
|
+
HSMR is a collection of cryptographic commands usually implemented on a HSM (Hardware Security Module). These
|
5
|
+
are implemented for your education or for testing purposes and should not be used to replace an actual HSM.
|
6
|
+
|
7
|
+
Installation
|
8
|
+
-------------
|
9
|
+
|
10
|
+
One day I'll learn how to make gems. Until then, git clone is the only way to use this softawre.
|
11
|
+
|
12
|
+
Usage
|
13
|
+
---------
|
14
|
+
|
15
|
+
require './lib/hsmr'
|
16
|
+
require './lib/key'
|
17
|
+
require './lib/component'
|
18
|
+
|
19
|
+
# Create a Key
|
20
|
+
> key=HSMR::Key.new("4CA2161637D0133E5E151AEA45DA2A12")
|
21
|
+
=> 4CA2 1616 37D0 133E 5E15 1AEA 45DA 2A12
|
22
|
+
> key.key
|
23
|
+
=> "L\xA2\x16\x167\xD0\x13>^\x15\x1A\xEAE\xDA*\x12"
|
24
|
+
> key.to_s
|
25
|
+
=> "4CA2 1616 37D0 133E 5E15 1AEA 45DA 2A12"
|
26
|
+
> key.kcv
|
27
|
+
=> "7B0898"
|
28
|
+
> key.parity
|
29
|
+
=> "odd"
|
30
|
+
|
31
|
+
# Generate a PVV
|
32
|
+
|
33
|
+
pan="5999997890123412"
|
34
|
+
pin="1234"
|
35
|
+
pvki="1"
|
36
|
+
pvv = HSMR.pvv(key, pan, pvki, pin)
|
37
|
+
=> "0798"
|
38
|
+
|
39
|
+
Features
|
40
|
+
---------
|
41
|
+
|
42
|
+
* Key / Componet features
|
43
|
+
* Create Keys and Components
|
44
|
+
* XOR Keys and Components together
|
45
|
+
* Find Key Check Values (kcv)
|
46
|
+
* Determine and change key parity
|
47
|
+
* Encrypt / Decrypt PIN Blocks
|
48
|
+
* Generate IBM3624 PINs
|
49
|
+
* Generate PVV, CVV & CVV2 values
|
50
|
+
|
51
|
+
|
52
|
+
Development
|
53
|
+
-----------
|
54
|
+
|
55
|
+
* Source hosted on GitHub.
|
56
|
+
* Report issues on GitHub Issues.
|
57
|
+
* Pull requests are awesome! Please include test::unit tests
|
58
|
+
|
59
|
+
Author
|
60
|
+
==========
|
61
|
+
|
62
|
+
Dan Milne, http://da.nmilne.com, http://booko.com.au
|
63
|
+
|
64
|
+
Copyright
|
65
|
+
----------
|
66
|
+
|
67
|
+
Copyright (c) 2010 Dan Milne. See LICENSE for details.
|
data/Rakefile
ADDED
data/hsmr.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "hsmr/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "hsmr"
|
7
|
+
s.version = HSMR::VERSION
|
8
|
+
s.authors = ["Dan Milne"]
|
9
|
+
s.email = ["d@nmilne.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{HSM commands in Ruby}
|
12
|
+
s.description = %q{A collection of methods usually implemented in a HSM (Hardware Security Module)}
|
13
|
+
|
14
|
+
s.rubyforge_project = "hsmr"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
|
25
|
+
s.add_development_dependency "rake"
|
26
|
+
s.add_development_dependency "guard-test"
|
27
|
+
s.add_development_dependency "factory_girl"
|
28
|
+
end
|
data/lib/hsmr.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'hsmr/component'
|
3
|
+
require 'hsmr/key'
|
4
|
+
|
5
|
+
module HSMR
|
6
|
+
# Key Lengths
|
7
|
+
SINGLE=64
|
8
|
+
DOUBLE=128
|
9
|
+
TRIPLE=192
|
10
|
+
|
11
|
+
## Mixin functionality
|
12
|
+
|
13
|
+
def kcv()
|
14
|
+
des = OpenSSL::Cipher::Cipher.new("des-cbc") if @key.length == 8
|
15
|
+
des = OpenSSL::Cipher::Cipher.new("des-ede-cbc") if @key.length == 16
|
16
|
+
des.encrypt
|
17
|
+
des.key=@key
|
18
|
+
des.update("\x00"*8).unpack('H*').first[0...6].upcase
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate(length)
|
22
|
+
(0...(length/4)).collect { rand(16).to_s(16).upcase }.join
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
@key.unpack('H4'*(@key.length/2)).join(" ").upcase
|
28
|
+
end
|
29
|
+
|
30
|
+
def parity
|
31
|
+
'even' unless odd_parity?
|
32
|
+
'odd'
|
33
|
+
end
|
34
|
+
|
35
|
+
def odd_parity?
|
36
|
+
# http://www.cryptosys.net/3des.html
|
37
|
+
# http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf
|
38
|
+
#
|
39
|
+
# The eight error detecting bits are set to make the parity of each 8-bit
|
40
|
+
# byte of the key odd. That is, there is an odd number of "1"s in each 8-bit byte.
|
41
|
+
|
42
|
+
#3.to_s(2).count('1')
|
43
|
+
#@key.unpack("H2").first.to_i(16).to_s(2)
|
44
|
+
|
45
|
+
working=@key.unpack('H2'*(@key.length))
|
46
|
+
working.each do |o|
|
47
|
+
freq = o.to_i(16).to_s(2).count('1').to_i
|
48
|
+
if( freq%2 == 0)
|
49
|
+
#puts "#{o} is #{o.to_i(16).to_s(2).count('1').to_i } - even"
|
50
|
+
return false
|
51
|
+
else
|
52
|
+
return true
|
53
|
+
#puts "#{o} is #{o.to_i(16).to_s(2).count('1').to_i } - odd"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.encrypt(data, key)
|
59
|
+
unless key.length == 8 || key.length == 16 || key.length ==24
|
60
|
+
raise TypeError, "key length should be 8, 16 or 24 bytes"
|
61
|
+
end
|
62
|
+
des = OpenSSL::Cipher::Cipher.new("des-cbc") if key.length == 8
|
63
|
+
des = OpenSSL::Cipher::Cipher.new("des-ede-cbc") if key.length == 16
|
64
|
+
des = OpenSSL::Cipher::Cipher.new("des-ede3-cbc") if key.length == 24
|
65
|
+
|
66
|
+
des.encrypt
|
67
|
+
des.key=key.key
|
68
|
+
to_hex( des.update(to_binary(data)) )
|
69
|
+
end
|
70
|
+
|
71
|
+
def set_odd_parity
|
72
|
+
return self if self.odd_parity? == true
|
73
|
+
|
74
|
+
working=@key.unpack('H2'*(@key.length))
|
75
|
+
working.each_with_index do |o,i|
|
76
|
+
freq = o.to_i(16).to_s(2).count('1').to_i
|
77
|
+
if( freq%2 == 0)
|
78
|
+
c1 = o[0].chr
|
79
|
+
c2 = case o[1].chr
|
80
|
+
when "0" then "1"
|
81
|
+
when "1" then "0"
|
82
|
+
when "2" then "3"
|
83
|
+
when "3" then "2"
|
84
|
+
when "4" then "5"
|
85
|
+
when "5" then "4"
|
86
|
+
when "6" then "7"
|
87
|
+
when "7" then "6"
|
88
|
+
when "8" then "9"
|
89
|
+
when "9" then "8"
|
90
|
+
when "a" then "b"
|
91
|
+
when "b" then "a"
|
92
|
+
when "c" then "d"
|
93
|
+
when "d" then "c"
|
94
|
+
when "e" then "f"
|
95
|
+
when "f" then "e"
|
96
|
+
end
|
97
|
+
working[i]="#{c1}#{c2}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
@key = working.join.unpack('a2'*(working.length)).map{|x| x.hex}.pack('c'*(working.length))
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
|
104
|
+
def xor(other)
|
105
|
+
other=Component.new(other) if other.is_a? String
|
106
|
+
#other=Component.new(other.to_s) if other.is_a? Key
|
107
|
+
|
108
|
+
unless (other.is_a? Component ) or ( other.is_a? Key )
|
109
|
+
raise TypeError, "Component argument expected"
|
110
|
+
end
|
111
|
+
|
112
|
+
@a = @key.unpack('C2'*(@key.length/2))
|
113
|
+
@b = other.key.unpack('C2'*(other.length/2))
|
114
|
+
|
115
|
+
resultant = Key.new( @a.zip(@b).
|
116
|
+
map {|x,y| x^y}.
|
117
|
+
map {|z| z.to_s(16) }.
|
118
|
+
map {|c| c.length == 1 ? '0'+c : c }.
|
119
|
+
join.upcase )
|
120
|
+
resultant
|
121
|
+
end
|
122
|
+
|
123
|
+
def xor!(_key)
|
124
|
+
@key = xor(_key).key
|
125
|
+
end
|
126
|
+
|
127
|
+
## Module Methods
|
128
|
+
|
129
|
+
def self.to_binary(data)
|
130
|
+
data.unpack('a2'*(data.length/2)).map{|x| x.hex}.pack('c'*(data.length/2))
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.to_hex(data)
|
134
|
+
data.unpack('H*').first.upcase
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.encrypt_pin(key, pin)
|
138
|
+
@pin = pin.unpack('a2'*(pin.length/2)).map{|x| x.hex}.pack('c'*(pin.length/2))
|
139
|
+
des = OpenSSL::Cipher::Cipher.new("des-ede")
|
140
|
+
des.encrypt
|
141
|
+
des.key=key.key
|
142
|
+
return des.update(@pin).unpack('H*').first.upcase
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.decrypt_pin(key, pinblock)
|
146
|
+
@pinblock = pinblock.unpack('a2'*(pinblock.length/2)).map{|x| x.hex}.pack('c'*(pinblock.length/2))
|
147
|
+
des = OpenSSL::Cipher::Cipher.new("des-ede")
|
148
|
+
des.decrypt
|
149
|
+
des.padding=0
|
150
|
+
des.key=key.key
|
151
|
+
result = des.update(@pinblock)
|
152
|
+
result << des.final
|
153
|
+
result.unpack('H*').first.upcase
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.ibm3624(key, account, plength=4, dtable="0123456789012345" )
|
157
|
+
|
158
|
+
validation_data = account.unpack('a2'*(account.length/2)).map{|x| x.hex}.pack('c'*(account.length/2))
|
159
|
+
|
160
|
+
#des = OpenSSL::Cipher::Cipher.new("des-ede-cbc")
|
161
|
+
des = OpenSSL::Cipher::Cipher.new("des-cbc")
|
162
|
+
des.encrypt
|
163
|
+
des.key=key.key
|
164
|
+
return HSMR::decimalise(des.update(validation_data).unpack('H*').first, :ibm, dtable)[0...plength]
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.decimalise(value, method=:visa, dtable="0123456789012345" )
|
169
|
+
|
170
|
+
result = []
|
171
|
+
if method == :ibm
|
172
|
+
##
|
173
|
+
# The IBM method
|
174
|
+
##
|
175
|
+
value.each_char do |c|
|
176
|
+
result << dtable[c.to_i(16),1].to_i
|
177
|
+
end
|
178
|
+
|
179
|
+
elsif method == :visa
|
180
|
+
|
181
|
+
value.each_char do |c|
|
182
|
+
result << c.to_i if numeric?(c)
|
183
|
+
end
|
184
|
+
|
185
|
+
value.upcase.each_char do |c|
|
186
|
+
result << dtable[c.to_i(16),1].to_i unless numeric?(c)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
return result
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.pvv(key, account, pvki, pin)
|
195
|
+
tsp = account.to_s[4,11] + pvki.to_s + pin.to_s
|
196
|
+
@tsp = tsp.unpack('a2'*(tsp.length/2)).map{|x| x.hex}.pack('c'*(tsp.length/2))
|
197
|
+
des = OpenSSL::Cipher::Cipher.new("des-ede")
|
198
|
+
des.encrypt
|
199
|
+
des.key=key.key
|
200
|
+
result = des.update(@tsp).unpack('H*').first.upcase
|
201
|
+
decimalise(result, :visa)[0..3].join
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.cvv(key_a, key_b, pan, exp, svc)
|
205
|
+
# http://www.m-sinergi.com/hairi/doc.html
|
206
|
+
# For CVV2 use SVC 000
|
207
|
+
# For CVV3 SVC is 502
|
208
|
+
# For iCVV use SVC of 999
|
209
|
+
#
|
210
|
+
#raise ArgumentError "PAN"
|
211
|
+
|
212
|
+
data1 = pan
|
213
|
+
data2 = "#{exp}#{svc}".ljust(16, '0')
|
214
|
+
|
215
|
+
result = encrypt(data1, key_a)
|
216
|
+
result = result.xor(data2)
|
217
|
+
|
218
|
+
result1 = encrypt(result, HSMR::Key.new(key_a.to_s + key_b.to_s) )
|
219
|
+
|
220
|
+
return HSMR.decimalise( result1 )[0,3].join
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.xor(component1, *rest)
|
224
|
+
return if rest.length == 0
|
225
|
+
|
226
|
+
component1 = Component.new(component1) unless component1.is_a? Component
|
227
|
+
raise TypeError, "Component argument expected" unless component1.is_a? Component
|
228
|
+
|
229
|
+
#@components=[]
|
230
|
+
#rest.each {|c| components << ((c.is_a? HSMR::Component) ? c : HSMR::Component.new(c) ) }
|
231
|
+
#components.each {|c| raise TypeError, "Component argument expected" unless c.is_a? Component }
|
232
|
+
#resultant = component1.xor(components.pop)
|
233
|
+
#components.each {|c| resultant.xor!(c) }
|
234
|
+
|
235
|
+
rest.collect! {|c| ( (c.is_a? HSMR::Component) ? c : HSMR::Component.new(c) ) }
|
236
|
+
rest.each {|c| raise TypeError, "Component argument expected" unless c.is_a? HSMR::Component }
|
237
|
+
resultant = component1.xor(rest.pop)
|
238
|
+
rest.each {|c| resultant.xor!(c) }
|
239
|
+
|
240
|
+
return(resultant)
|
241
|
+
end
|
242
|
+
|
243
|
+
def self.numeric?(object)
|
244
|
+
## Method to determine if an object is a numeric type.
|
245
|
+
true if Float(object) rescue false
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
class String
|
251
|
+
def xor(other)
|
252
|
+
if other.empty?
|
253
|
+
self
|
254
|
+
else
|
255
|
+
a1 = self.unpack("a2"*(self.length/2)).map {|x| x.hex }
|
256
|
+
a2 = other.unpack("a2"*(other.length/2)).map {|x| x.hex }
|
257
|
+
#a2 *= 2 while a2.length < a1.length
|
258
|
+
|
259
|
+
#a1.zip(a2).collect{|c1,c2| c1^c2}.pack("C*")
|
260
|
+
a1.zip(a2).
|
261
|
+
map {|x,y| x^y}.
|
262
|
+
map {|z| z.to_s(16) }.
|
263
|
+
map {|c| c.length == 1 ? '0'+c : c }.
|
264
|
+
join.upcase
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module HSMR
|
2
|
+
class Component
|
3
|
+
include HSMR
|
4
|
+
attr_reader :key
|
5
|
+
attr_reader :length
|
6
|
+
|
7
|
+
def component
|
8
|
+
@key
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(key=nil, length=DOUBLE)
|
12
|
+
## Should check for odd parity
|
13
|
+
if key.nil?
|
14
|
+
key = generate(length)
|
15
|
+
else
|
16
|
+
key=key.gsub(/ /,'')
|
17
|
+
#raise TypeError, "Component argument expected" unless other.is_a? Component
|
18
|
+
end
|
19
|
+
@key = key.unpack('a2'*(key.length/2)).map{|x| x.hex}.pack('c'*(key.length/2))
|
20
|
+
@length = @key.length
|
21
|
+
@key = @key
|
22
|
+
end
|
23
|
+
|
24
|
+
# def xor(other)
|
25
|
+
# other = Component.new(other) unless other.is_a? Component
|
26
|
+
# raise TypeError, "Component argument expected" unless other.is_a? Component
|
27
|
+
#
|
28
|
+
# @a = @key.unpack('C2'*(@key.length/2))
|
29
|
+
# @b = other.component.unpack('C2'*(other.component.length/2))
|
30
|
+
#
|
31
|
+
# #result = @a.zip(@b).map {|x,y| x^y}.map {|z| z.to_s(16) }.join.upcase
|
32
|
+
# result = @a.zip(@b).map {|x,y| x^y}.map {|z| z.to_s(16) }.map {|c| c.length == 1 ? '0'+c : c }.join.upcase
|
33
|
+
#
|
34
|
+
# Key.new(result)
|
35
|
+
# end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/hsmr/key.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module HSMR
|
2
|
+
class Key
|
3
|
+
include HSMR
|
4
|
+
|
5
|
+
attr_reader :key
|
6
|
+
attr_reader :length
|
7
|
+
|
8
|
+
def initialize(init=nil, length=DOUBLE)
|
9
|
+
return nil if (init.is_a? Array ) && (init.length == 0)
|
10
|
+
|
11
|
+
init = init.first if (init.is_a? Array) && (init.length == 1)
|
12
|
+
|
13
|
+
if init.is_a? Array
|
14
|
+
init.collect! {|c| ( (c.is_a? HSMR::Component) ? c : HSMR::Component.new(c) ) }
|
15
|
+
|
16
|
+
raise TypeError, "Component argument expected" unless init.first.is_a? Component
|
17
|
+
|
18
|
+
@key=HSMR::xor(init.pop, init).key
|
19
|
+
|
20
|
+
elsif init.is_a? Component
|
21
|
+
@key = init.component
|
22
|
+
elsif init.is_a? String
|
23
|
+
key=init.gsub(/ /,'')
|
24
|
+
@key = key.unpack('a2'*(key.length/2)).map{|x| x.hex}.pack('c'*(key.length/2))
|
25
|
+
elsif key.nil?
|
26
|
+
key = generate(length)
|
27
|
+
@key = key.unpack('a2'*(key.length/2)).map{|x| x.hex}.pack('c'*(key.length/2))
|
28
|
+
end
|
29
|
+
@length = @key.length
|
30
|
+
end
|
31
|
+
|
32
|
+
#def xor(other)
|
33
|
+
# other=Component.new(other) if other.is_a? String
|
34
|
+
# other=Component.new(other.to_s) if other.is_a? Key
|
35
|
+
#
|
36
|
+
# puts "other is #{other.class} - #{other.key}"
|
37
|
+
#
|
38
|
+
# raise TypeError, "Component argument expected" unless other.is_a? Component
|
39
|
+
#
|
40
|
+
# @a = @key.unpack('C2'*(@key.length/2))
|
41
|
+
# @b = other.component.unpack('C2'*(other.length/2))
|
42
|
+
#
|
43
|
+
# resultant = Key.new( @a.zip(@b).
|
44
|
+
# map {|x,y| x^y}.
|
45
|
+
# map {|z| z.to_s(16) }.
|
46
|
+
# map {|c| c.length == 1 ? '0'+c : c }.
|
47
|
+
# join.upcase )
|
48
|
+
# resultant
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# def xor!(_key)
|
52
|
+
# @key = xor(_key).key
|
53
|
+
# end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
data/lib/hsmr/version.rb
ADDED
data/spec/hsmr_spec.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Generate Component" do
|
4
|
+
it "should generate a component" do
|
5
|
+
component_1 = HSMR::Component.new(nil, HSMR::SINGLE)
|
6
|
+
component_2 = HSMR::Component.new(nil, HSMR::DOUBLE)
|
7
|
+
component_3 = HSMR::Component.new(nil, HSMR::TRIPLE)
|
8
|
+
|
9
|
+
component_1.length.should == 8
|
10
|
+
component_2.length.should == 16
|
11
|
+
component_3.length.should == 24
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "Calculate component KCV" do
|
16
|
+
|
17
|
+
it "should calculate single length component KCV values" do
|
18
|
+
component_1 = HSMR::Component.new("6DBF C180 4A01 5BAD")
|
19
|
+
component_1.kcv.should == "029E60"
|
20
|
+
|
21
|
+
component_2 = HSMR::Component.new("5D80 0497 B319 8591")
|
22
|
+
component_2.kcv.should == "3B86C3"
|
23
|
+
|
24
|
+
component_3 = HSMR::Component.new("B0C7 7CDC 7354 97C7")
|
25
|
+
component_3.kcv.should == "7A77BC"
|
26
|
+
|
27
|
+
component_4 = HSMR::Component.new("DAE0 86FE D6EA 0BEA")
|
28
|
+
component_4.kcv.should == "2E6191"
|
29
|
+
|
30
|
+
component_5 = HSMR::Component.new("682C 8315 F4BF FBC1")
|
31
|
+
component_5.kcv.should == "62B336"
|
32
|
+
|
33
|
+
component_6 = HSMR::Component.new("5715 F289 04BC B62F")
|
34
|
+
component_6.kcv.should == "3CBA88"
|
35
|
+
|
36
|
+
component_7 = HSMR::Component.new("D0C4 29AE C4A8 02B5")
|
37
|
+
component_7.kcv.should == "33AF02"
|
38
|
+
|
39
|
+
component_8 = HSMR::Component.new("7049 D0F7 4A97 15B6")
|
40
|
+
component_8.kcv.should == "AC1399"
|
41
|
+
|
42
|
+
component_9 = HSMR::Component.new("BC91 D698 157A A4E5")
|
43
|
+
component_9.kcv.should == "295491"
|
44
|
+
|
45
|
+
component_10 = HSMR::Component.new("64AB 8568 7A0E 322F")
|
46
|
+
component_10.kcv.should == "D9F7B3"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should calculate double length component KCV values" do
|
50
|
+
component_1 = HSMR::Component.new("ADE3 9B38 0DBC DF38 AE02 AECE 64B3 4373")
|
51
|
+
component_1.kcv.should == "3002D5"
|
52
|
+
|
53
|
+
component_2 = HSMR::Component.new("B64A EF86 460D DF5B 57B3 D53D AD37 52A1")
|
54
|
+
component_2.kcv.should == "1F7C07"
|
55
|
+
|
56
|
+
component_3 = HSMR::Component.new("5B89 6E29 76EC 9745 15B5 238C 8CFE 3D23")
|
57
|
+
component_3.kcv.should == "DE78F2"
|
58
|
+
|
59
|
+
component_4 = HSMR::Component.new("5E61 CB20 D540 1FC7 58EC CDC8 B558 E9B9")
|
60
|
+
component_4.kcv.should == "FED957"
|
61
|
+
|
62
|
+
component_5 = HSMR::Component.new("23DF CEB9 BF94 ADA8 91E9 580B 8C8F 5BEF")
|
63
|
+
component_5.kcv.should == "902085"
|
64
|
+
|
65
|
+
component_6 = HSMR::Component.new("DFEF 61C8 2037 3DA4 CE9B 92CD 89E9 B334")
|
66
|
+
component_6.kcv.should == "E45EB7"
|
67
|
+
|
68
|
+
component_7 = HSMR::Component.new("6746 9E4C FE83 F831 F23E 9D9E 9D9E 9DB3")
|
69
|
+
component_7.kcv.should == "813B7B"
|
70
|
+
|
71
|
+
component_8 = HSMR::Component.new("23E5 496E DF94 0BD5 9734 B07A BF26 B9E6")
|
72
|
+
component_8.kcv.should == "E7C48F"
|
73
|
+
|
74
|
+
component_9 = HSMR::Component.new("974F 26BC CB2A ECD5 434F 1CDC 64DF A275")
|
75
|
+
component_9.kcv.should == "E27250"
|
76
|
+
|
77
|
+
component_10 = HSMR::Component.new("E57A DF5B CEA7 F42A DFD9 E554 07A2 F891")
|
78
|
+
component_10.kcv.should == "50E3F8"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "Calculate key KCV" do
|
83
|
+
|
84
|
+
it "should calculate single length key KCV values" do
|
85
|
+
key_1 = HSMR::Key.new("6DBF C180 4A01 5BAD")
|
86
|
+
key_1.kcv.should == "029E60"
|
87
|
+
|
88
|
+
key_2 = HSMR::Key.new("5D80 0497 B319 8591")
|
89
|
+
key_2.kcv.should == "3B86C3"
|
90
|
+
|
91
|
+
key_3 = HSMR::Key.new("B0C7 7CDC 7354 97C7")
|
92
|
+
key_3.kcv.should == "7A77BC"
|
93
|
+
|
94
|
+
key_4 = HSMR::Key.new("DAE0 86FE D6EA 0BEA")
|
95
|
+
key_4.kcv.should == "2E6191"
|
96
|
+
|
97
|
+
key_5 = HSMR::Key.new("682C 8315 F4BF FBC1")
|
98
|
+
key_5.kcv.should == "62B336"
|
99
|
+
|
100
|
+
key_6 = HSMR::Key.new("5715 F289 04BC B62F")
|
101
|
+
key_6.kcv.should == "3CBA88"
|
102
|
+
|
103
|
+
key_7 = HSMR::Key.new("D0C4 29AE C4A8 02B5")
|
104
|
+
key_7.kcv.should == "33AF02"
|
105
|
+
|
106
|
+
key_8 = HSMR::Key.new("7049 D0F7 4A97 15B6")
|
107
|
+
key_8.kcv.should == "AC1399"
|
108
|
+
|
109
|
+
key_9 = HSMR::Key.new("BC91 D698 157A A4E5")
|
110
|
+
key_9.kcv.should == "295491"
|
111
|
+
|
112
|
+
key_10 = HSMR::Key.new("64AB 8568 7A0E 322F")
|
113
|
+
key_10.kcv.should == "D9F7B3"
|
114
|
+
end
|
115
|
+
it "should calculate double length key KCV values" do
|
116
|
+
key_1 = HSMR::Key.new("ADE3 9B38 0DBC DF38 AE02 AECE 64B3 4373")
|
117
|
+
key_1.kcv.should == "3002D5"
|
118
|
+
|
119
|
+
key_2 = HSMR::Key.new("B64A EF86 460D DF5B 57B3 D53D AD37 52A1")
|
120
|
+
key_2.kcv.should == "1F7C07"
|
121
|
+
|
122
|
+
key_3 = HSMR::Key.new("5B89 6E29 76EC 9745 15B5 238C 8CFE 3D23")
|
123
|
+
key_3.kcv.should == "DE78F2"
|
124
|
+
|
125
|
+
key_4 = HSMR::Key.new("5E61 CB20 D540 1FC7 58EC CDC8 B558 E9B9")
|
126
|
+
key_4.kcv.should == "FED957"
|
127
|
+
|
128
|
+
key_5 = HSMR::Key.new("23DF CEB9 BF94 ADA8 91E9 580B 8C8F 5BEF")
|
129
|
+
key_5.kcv.should == "902085"
|
130
|
+
|
131
|
+
key_6 = HSMR::Key.new("DFEF 61C8 2037 3DA4 CE9B 92CD 89E9 B334")
|
132
|
+
key_6.kcv.should == "E45EB7"
|
133
|
+
|
134
|
+
key_7 = HSMR::Key.new("6746 9E4C FE83 F831 F23E 9D9E 9D9E 9DB3")
|
135
|
+
key_7.kcv.should == "813B7B"
|
136
|
+
|
137
|
+
key_8 = HSMR::Key.new("23E5 496E DF94 0BD5 9734 B07A BF26 B9E6")
|
138
|
+
key_8.kcv.should == "E7C48F"
|
139
|
+
|
140
|
+
key_9 = HSMR::Key.new("974F 26BC CB2A ECD5 434F 1CDC 64DF A275")
|
141
|
+
key_9.kcv.should == "E27250"
|
142
|
+
|
143
|
+
key_10 = HSMR::Key.new("E57A DF5B CEA7 F42A DFD9 E554 07A2 F891")
|
144
|
+
key_10.kcv.should == "50E3F8"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "Calculate parity" do
|
149
|
+
it "should detect odd_parity in a key" do
|
150
|
+
odd_key = HSMR::Key.new("41A2AC14A90C583741A2AC14A90C5837")
|
151
|
+
odd_key.odd_parity?.should == false
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should set double length key parity to odd" do
|
155
|
+
odd_key = HSMR::Key.new("41A2AC14A90C583741A2AC14A90C5837")
|
156
|
+
|
157
|
+
odd_key.set_odd_parity
|
158
|
+
|
159
|
+
even_key = HSMR::Key.new("40A2AD15A80D583740A2AD15A80D5837")
|
160
|
+
|
161
|
+
odd_key.key.should == even_key.key
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should detect odd_parity in a component" do
|
166
|
+
odd_component = HSMR::Component.new("41A2AC14A90C583741A2AC14A90C5837")
|
167
|
+
odd_component.odd_parity?.should == false
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should set double length component parity to odd" do
|
171
|
+
odd_component = HSMR::Component.new("41A2AC14A90C583741A2AC14A90C5837")
|
172
|
+
|
173
|
+
odd_component.set_odd_parity
|
174
|
+
|
175
|
+
even_component = HSMR::Component.new("40A2AD15A80D583740A2AD15A80D5837")
|
176
|
+
|
177
|
+
odd_component.component.should == even_component.component
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'hsmr'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
data/test/hsmr_test.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TestHSMR < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_generate_a_component
|
6
|
+
component_1 = HSMR::Component.new(nil, HSMR::SINGLE)
|
7
|
+
component_2 = HSMR::Component.new(nil, HSMR::DOUBLE)
|
8
|
+
component_3 = HSMR::Component.new(nil, HSMR::TRIPLE)
|
9
|
+
|
10
|
+
assert_equal 8, component_1.length
|
11
|
+
assert_equal 16, component_2.length
|
12
|
+
assert_equal 24, component_3.length
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_caclulation_of_single_length_component_KCV_values
|
16
|
+
component_1 = HSMR::Component.new("6DBF C180 4A01 5BAD")
|
17
|
+
assert_equal "029E60", component_1.kcv
|
18
|
+
|
19
|
+
component_2 = HSMR::Component.new("5D80 0497 B319 8591")
|
20
|
+
assert_equal "3B86C3", component_2.kcv
|
21
|
+
|
22
|
+
component_3 = HSMR::Component.new("B0C7 7CDC 7354 97C7")
|
23
|
+
assert_equal "7A77BC", component_3.kcv
|
24
|
+
|
25
|
+
component_4 = HSMR::Component.new("DAE0 86FE D6EA 0BEA")
|
26
|
+
assert_equal "2E6191", component_4.kcv
|
27
|
+
|
28
|
+
component_5 = HSMR::Component.new("682C 8315 F4BF FBC1")
|
29
|
+
assert_equal "62B336", component_5.kcv
|
30
|
+
|
31
|
+
component_6 = HSMR::Component.new("5715 F289 04BC B62F")
|
32
|
+
assert_equal "3CBA88", component_6.kcv
|
33
|
+
|
34
|
+
component_7 = HSMR::Component.new("D0C4 29AE C4A8 02B5")
|
35
|
+
assert_equal "33AF02", component_7.kcv
|
36
|
+
|
37
|
+
component_8 = HSMR::Component.new("7049 D0F7 4A97 15B6")
|
38
|
+
assert_equal "AC1399", component_8.kcv
|
39
|
+
|
40
|
+
component_9 = HSMR::Component.new("BC91 D698 157A A4E5")
|
41
|
+
assert_equal "295491", component_9.kcv
|
42
|
+
|
43
|
+
component_10 = HSMR::Component.new("64AB 8568 7A0E 322F")
|
44
|
+
assert_equal "D9F7B3", component_10.kcv
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_calculate_double_length_key_KCV_values
|
48
|
+
key_1 = HSMR::Key.new("ADE3 9B38 0DBC DF38 AE02 AECE 64B3 4373")
|
49
|
+
assert_equal "3002D5", key_1.kcv
|
50
|
+
|
51
|
+
key_2 = HSMR::Key.new("B64A EF86 460D DF5B 57B3 D53D AD37 52A1")
|
52
|
+
assert_equal "1F7C07", key_2.kcv
|
53
|
+
|
54
|
+
key_3 = HSMR::Key.new("5B89 6E29 76EC 9745 15B5 238C 8CFE 3D23")
|
55
|
+
assert_equal "DE78F2", key_3.kcv
|
56
|
+
|
57
|
+
key_4 = HSMR::Key.new("5E61 CB20 D540 1FC7 58EC CDC8 B558 E9B9")
|
58
|
+
assert_equal "FED957", key_4.kcv
|
59
|
+
|
60
|
+
key_5 = HSMR::Key.new("23DF CEB9 BF94 ADA8 91E9 580B 8C8F 5BEF")
|
61
|
+
assert_equal "902085", key_5.kcv
|
62
|
+
|
63
|
+
key_6 = HSMR::Key.new("DFEF 61C8 2037 3DA4 CE9B 92CD 89E9 B334")
|
64
|
+
assert_equal "E45EB7", key_6.kcv
|
65
|
+
|
66
|
+
key_7 = HSMR::Key.new("6746 9E4C FE83 F831 F23E 9D9E 9D9E 9DB3")
|
67
|
+
assert_equal "813B7B", key_7.kcv
|
68
|
+
|
69
|
+
key_8 = HSMR::Key.new("23E5 496E DF94 0BD5 9734 B07A BF26 B9E6")
|
70
|
+
assert_equal "E7C48F", key_8.kcv
|
71
|
+
|
72
|
+
key_9 = HSMR::Key.new("974F 26BC CB2A ECD5 434F 1CDC 64DF A275")
|
73
|
+
assert_equal "E27250", key_9.kcv
|
74
|
+
|
75
|
+
key_10 = HSMR::Key.new("E57A DF5B CEA7 F42A DFD9 E554 07A2 F891")
|
76
|
+
assert_equal "50E3F8", key_10.kcv
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_should_detect_odd_parity_in_a_key
|
80
|
+
parity=[]
|
81
|
+
# Odd Even
|
82
|
+
parity << %W{ 0123456789ABCDEF 0022446688AACCEE }
|
83
|
+
parity << %W{ FEDCBA9876543210 FFDDBB9977553311 }
|
84
|
+
parity << %W{ 89ABCDEF01234567 88AACCEE00224466 }
|
85
|
+
parity << %W{ 40A2AD15A80D583740A2AD15A80D5837 41A2AC14A90C583741A2AC14A90C5837 }
|
86
|
+
|
87
|
+
# Test determining the parity
|
88
|
+
parity.each do |pair|
|
89
|
+
assert_equal true, HSMR::Key.new(pair[0]).odd_parity?
|
90
|
+
assert_equal false, HSMR::Key.new(pair[1]).odd_parity?
|
91
|
+
|
92
|
+
assert_equal true, HSMR::Component.new(pair[0]).odd_parity?
|
93
|
+
assert_equal false, HSMR::Component.new(pair[1]).odd_parity?
|
94
|
+
end
|
95
|
+
|
96
|
+
# Test converting even to odd parity
|
97
|
+
parity.each do |pair|
|
98
|
+
odd_key = HSMR::Key.new(pair[0])
|
99
|
+
even_key = HSMR::Key.new(pair[0])
|
100
|
+
|
101
|
+
even_key.set_odd_parity
|
102
|
+
assert_equal odd_key, odd_key
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_converting_string_to_ascii_works
|
107
|
+
key_string = "E57A DF5B CEA7 F42A DFD9 E554 07A2 F891"
|
108
|
+
key = HSMR::Key.new(key_string)
|
109
|
+
|
110
|
+
assert_equal key.to_s, key_string
|
111
|
+
|
112
|
+
key_string = "E57A DF5B CEA7 F42A DFD9 E554 07A2 F891"
|
113
|
+
comp = HSMR::Component.new(key_string)
|
114
|
+
|
115
|
+
assert_equal comp.to_s, key_string
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_CVC_CVC2_calculations
|
119
|
+
cases = []
|
120
|
+
# Component 1 Component 2 PAN EXP SCode CVC
|
121
|
+
cases << %W{ 1234567890ABCDEF FEDCBA1234567890 5656565656565656 1010 ___ 922 }
|
122
|
+
cases << %W{ 1234567890ABCDEF FEDCBA1234567890 5656565656565656 1010 000 922 }
|
123
|
+
cases << %W{ 1234567890ABCDEF FEDCBA1234567890 5683739237489383838 1010 000 367 }
|
124
|
+
cases << %W{ 1234567890ABCDEF FEDCBA1234567890 568367393472639 1010 000 067 }
|
125
|
+
cases << %W{ 1234567890ABCDEF FEDCBA1234567890 5683673934726394 1010 000 409 }
|
126
|
+
cases << %W{ 1234567890ABCDEF FEDCBA1234567890 5683673934726394 1010 050 CVV248 or CVC409 }
|
127
|
+
cases << %W{ 1234567890ABCDEF FEDCBA1234567890 5683673934726394 1010 101 CVV501 or CVC409 }
|
128
|
+
cases << %W{ 1234567890ABCDEF FEDCBA1234567890 5683673934726394 1010 102 CVV206 or CVC409 }
|
129
|
+
|
130
|
+
kl = "0123456789ABCDEF"
|
131
|
+
kr = "FEDCBA1234567890"
|
132
|
+
|
133
|
+
|
134
|
+
#HSMR.cvv(kl, kr, "4509494222049051", "0907", "1010")
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_PIN_PVV_CVV_and_CVV2_generation
|
138
|
+
cases=[]
|
139
|
+
# Account Exp PIN PVV CVV2 CVV PGK1 PGK2 PVKI PVK1 PVK2 CVKA CVKB DEC
|
140
|
+
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13
|
141
|
+
cases << %W{ 5560501200002101 1010 4412 6183 134 317 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345}
|
142
|
+
cases << %W{ 5560501200002111 1010 4784 0931 561 924 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345}
|
143
|
+
cases << %W{ 5560501200002121 1010 1040 4895 462 673 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345}
|
144
|
+
cases << %W{ 5560501200002131 1010 3680 6373 826 267 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345}
|
145
|
+
cases << %W{ 5560501200002101 1110 4412 6183 900 155 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345}
|
146
|
+
cases << %W{ 5560501200002111 1110 4784 0931 363 513 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345}
|
147
|
+
cases << %W{ 5560501200002121 1110 1040 4895 952 937 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345}
|
148
|
+
cases << %W{ 5560501200002131 1110 3680 6373 667 522 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345}
|
149
|
+
cases << %W{ 5560501200002101 1010 9907 7527 777 473 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345}
|
150
|
+
cases << %W{ 5560501200002111 1010 2345 0658 638 553 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345}
|
151
|
+
cases << %W{ 5560501200002121 1010 8245 8196 085 480 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345}
|
152
|
+
cases << %W{ 5560501200002131 1010 3812 2948 591 546 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345}
|
153
|
+
cases << %W{ 5560501200002101 1110 9907 7527 349 994 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345}
|
154
|
+
cases << %W{ 5560501200002111 1110 2345 0658 245 266 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345}
|
155
|
+
cases << %W{ 5560501200002121 1110 8245 8196 441 115 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345}
|
156
|
+
cases << %W{ 5560501200002131 1110 3812 2948 126 768 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345}
|
157
|
+
|
158
|
+
cases.each do |c|
|
159
|
+
ibm1=HSMR::Component.new(c[6])
|
160
|
+
ibm2=HSMR::Component.new(c[7])
|
161
|
+
ibm=ibm1.xor(ibm2)
|
162
|
+
|
163
|
+
pvk=HSMR::Key.new(c[9]+c[10])
|
164
|
+
|
165
|
+
pin = HSMR::ibm3624(ibm, c[0], 4, c[13]).join
|
166
|
+
pvv = HSMR::pvv(pvk, c[0], c[8], pin)
|
167
|
+
|
168
|
+
assert_equal pin, c[2]
|
169
|
+
assert_equal pin.to_i, c[2].to_i
|
170
|
+
assert_equal pvv, c[3]
|
171
|
+
assert_equal pvv.to_i, c[3].to_i
|
172
|
+
|
173
|
+
cvv = HSMR::cvv(HSMR::Key.new(c[11]), HSMR::Key.new(c[12]), c[0], c[1], '0')
|
174
|
+
cvv2 = HSMR::cvv(HSMR::Key.new(c[11]), HSMR::Key.new(c[12]), c[0], c[1], '101')
|
175
|
+
|
176
|
+
assert_equal c[4].to_i, cvv2.to_i
|
177
|
+
assert_equal c[5].to_i, cvv.to_i
|
178
|
+
|
179
|
+
#puts "#{pin} == #{c[2]} ? #{pin.to_i == c[2].to_i} | #{pvv} == #{c[3]} ? #{pvv.to_i == c[3].to_i}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hsmr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dan Milne
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2012-01-15 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: guard-test
|
28
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: factory_girl
|
39
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *id003
|
48
|
+
description: A collection of methods usually implemented in a HSM (Hardware Security Module)
|
49
|
+
email:
|
50
|
+
- d@nmilne.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- .travis.yml
|
60
|
+
- CHANGELOG.md
|
61
|
+
- Gemfile
|
62
|
+
- Gemfile.lock
|
63
|
+
- Guardfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- hsmr.gemspec
|
68
|
+
- lib/hsmr.rb
|
69
|
+
- lib/hsmr/component.rb
|
70
|
+
- lib/hsmr/key.rb
|
71
|
+
- lib/hsmr/version.rb
|
72
|
+
- spec/hsmr_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- test/hsmr_test.rb
|
75
|
+
- test/test_helper.rb
|
76
|
+
homepage: ""
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: -754850866914746297
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: -754850866914746297
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
version: "0"
|
102
|
+
requirements: []
|
103
|
+
|
104
|
+
rubyforge_project: hsmr
|
105
|
+
rubygems_version: 1.8.8
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: HSM commands in Ruby
|
109
|
+
test_files:
|
110
|
+
- spec/hsmr_spec.rb
|
111
|
+
- spec/spec_helper.rb
|
112
|
+
- test/hsmr_test.rb
|
113
|
+
- test/test_helper.rb
|