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.
@@ -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~
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
@@ -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
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :test do
5
+ watch(%r{lib/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
6
+ watch(%r{test/.+_test\.rb})
7
+ watch('test/test_helper.rb') { "test" }
8
+ end
@@ -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.
@@ -0,0 +1,67 @@
1
+ HSMR [![Build Status](https://secure.travis-ci.org/dkam/hsmr.png)](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.
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/*_test.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :test
12
+
13
+ task :default => :test
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module HSMR
2
+ VERSION = "0.0.1"
3
+ end
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,2 @@
1
+ require "test/unit"
2
+ require "hsmr"
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