From ac248234bb171487c0c40be95f5aeb6f7f97e879 Mon Sep 17 00:00:00 2001 From: Dan Milne Date: Fri, 25 Nov 2011 22:30:30 +1100 Subject: [PATCH] CVC & CVC2 working --- lib/component.rb | 22 +++++++------ lib/hsmr.rb | 82 +++++++++++++++++++++++++++++++++++++++++++---- lib/key.rb | 38 +++++++++++++--------- test/hsmr_test.rb | 61 ++++++++++++++++++++++++++++++++++- 4 files changed, 170 insertions(+), 33 deletions(-) diff --git a/lib/component.rb b/lib/component.rb index ca3cc63..bd97c43 100644 --- a/lib/component.rb +++ b/lib/component.rb @@ -3,7 +3,6 @@ module HSMR include HSMR attr_reader :key attr_reader :length - attr_reader :parity def component @key @@ -22,15 +21,18 @@ module HSMR @key = @key end - def xor(other) - other = Component.new(other) unless other.is_a? Component - raise TypeError, "Component argument expected" unless other.is_a? Component - - @a = @key.unpack('C2'*(@key.length/2)) - @b = other.component.unpack('C2'*(other.component.length/2)) - result = @a.zip(@b).map {|x,y| x^y}.map {|z| z.to_s(16) }.join.upcase - Key.new(result) - end +# def xor(other) +# other = Component.new(other) unless other.is_a? Component +# raise TypeError, "Component argument expected" unless other.is_a? Component +# +# @a = @key.unpack('C2'*(@key.length/2)) +# @b = other.component.unpack('C2'*(other.component.length/2)) +# +# #result = @a.zip(@b).map {|x,y| x^y}.map {|z| z.to_s(16) }.join.upcase +# 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 +# +# Key.new(result) +# end end end diff --git a/lib/hsmr.rb b/lib/hsmr.rb index b7a00f9..d372cb6 100644 --- a/lib/hsmr.rb +++ b/lib/hsmr.rb @@ -27,6 +27,11 @@ module HSMR @key.unpack('H4'*(@key.length/2)).join(" ").upcase end + def parity + 'even' unless odd_parity? + 'odd' + end + def odd_parity? # http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf # @@ -49,6 +54,19 @@ module HSMR end end + def self.encrypt(data, key) + unless key.length == 8 || key.length == 16 || key.length ==24 + raise TypeError, "key length should be 8, 16 or 24 bytes" + end + des = OpenSSL::Cipher::Cipher.new("des-cbc") if key.length == 8 + des = OpenSSL::Cipher::Cipher.new("des-ede-cbc") if key.length == 16 + des = OpenSSL::Cipher::Cipher.new("des-ede3-cbc") if key.length == 24 + + des.encrypt + des.key=key.key + to_hex( des.update(to_binary(data)) ) + end + def set_odd_parity return true if self.odd_parity? == true @@ -81,8 +99,39 @@ module HSMR @key = working.join.unpack('a2'*(working.length)).map{|x| x.hex}.pack('c'*(working.length)) end + def xor(other) + other=Component.new(other) if other.is_a? String + #other=Component.new(other.to_s) if other.is_a? Key + + unless (other.is_a? Component ) or ( other.is_a? Key ) + raise TypeError, "Component argument expected" + end + + @a = @key.unpack('C2'*(@key.length/2)) + @b = other.key.unpack('C2'*(other.length/2)) + + resultant = Key.new( @a.zip(@b). + map {|x,y| x^y}. + map {|z| z.to_s(16) }. + map {|c| c.length == 1 ? '0'+c : c }. + join.upcase ) + resultant + end + + def xor!(_key) + @key = xor(_key).key + end + ## Module Methods + def self.to_binary(data) + data.unpack('a2'*(data.length/2)).map{|x| x.hex}.pack('c'*(data.length/2)) + end + + def self.to_hex(data) + data.unpack('H*').first.upcase + end + def self.encrypt_pin(key, pin) @pin = pin.unpack('a2'*(pin.length/2)).map{|x| x.hex}.pack('c'*(pin.length/2)) des = OpenSSL::Cipher::Cipher.new("des-ede") @@ -150,8 +199,23 @@ module HSMR decimalise(result, :visa)[0..3].join end - def self.cvv(key_left, key_right, account, exp, service_code) - + def self.cvv(key_a, key_b, pan, exp, svc) + # http://www.m-sinergi.com/hairi/doc.html + # For CVV2 use SVC 000 + # For CVV3 SVC is 502 + # For iCVV use SVC of 999 + # + #raise ArgumentError "PAN" + + data1 = pan + data2 = "#{exp}#{svc}".ljust(16, '0') + + result = encrypt(data1, key_a) + result = result.xor(data2) + + result1 = encrypt(result, HSMR::Key.new(key_a.to_s + key_b.to_s) ) + + return HSMR.decimalise( result1 )[0,3].join end def self.xor(component1, *rest) @@ -186,10 +250,16 @@ class String if other.empty? self else - a1 = self.unpack("c*") - a2 = other.unpack("c*") - a2 *= 2 while a2.length < a1.length - a1.zip(a2).collect{|c1,c2| c1^c2}.pack("c*") + a1 = self.unpack("a2"*(self.length/2)).map {|x| x.hex } + a2 = other.unpack("a2"*(other.length/2)).map {|x| x.hex } + #a2 *= 2 while a2.length < a1.length + + #a1.zip(a2).collect{|c1,c2| c1^c2}.pack("C*") + a1.zip(a2). + map {|x,y| x^y}. + map {|z| z.to_s(16) }. + map {|c| c.length == 1 ? '0'+c : c }. + join.upcase end end end diff --git a/lib/key.rb b/lib/key.rb index 56ca49d..4920a87 100644 --- a/lib/key.rb +++ b/lib/key.rb @@ -4,7 +4,6 @@ module HSMR attr_reader :key attr_reader :length - attr_reader :parity def initialize(init=nil, length=DOUBLE) return nil if (init.is_a? Array ) && (init.length == 0) @@ -30,21 +29,28 @@ module HSMR @length = @key.length end - def xor(other) - other=Component.new(other) if other.is_a? String - other=Component.new(other.key) if other.is_a? Key - - raise TypeError, "Component argument expected" unless other.is_a? Component - - @a = @key.unpack('C2'*(@key.length/2)) - @b = other.component.unpack('C2'*(@key.length/2)) - - resultant = Key.new( @a.zip(@b).map {|x,y| x^y}.map {|z| z.to_s(16) }.join.upcase ) - end - - def xor!(_key) - @key = xor(_key).key - end + #def xor(other) + # other=Component.new(other) if other.is_a? String + # other=Component.new(other.to_s) if other.is_a? Key +# +# puts "other is #{other.class} - #{other.key}" +# +# raise TypeError, "Component argument expected" unless other.is_a? Component +# +# @a = @key.unpack('C2'*(@key.length/2)) +# @b = other.component.unpack('C2'*(other.length/2)) +# +# resultant = Key.new( @a.zip(@b). +# map {|x,y| x^y}. +# map {|z| z.to_s(16) }. +# map {|c| c.length == 1 ? '0'+c : c }. +# join.upcase ) +# resultant +# end +# +# def xor!(_key) +# @key = xor(_key).key +# end end end diff --git a/test/hsmr_test.rb b/test/hsmr_test.rb index f88cc89..bc9fc13 100644 --- a/test/hsmr_test.rb +++ b/test/hsmr_test.rb @@ -131,7 +131,66 @@ class TestHSMR < Test::Unit::TestCase kl = "0123456789ABCDEF" kr = "FEDCBA1234567890" - HSMR.cvv(kl, kr, "4509494222049051", "0907", "1010") + #HSMR.cvv(kl, kr, "4509494222049051", "0907", "1010") end + test "Test PIN, PVV, CVV and CVV2 generation" do + cases=[] + # Account Exp PIN PVV CVV2 CVV PGK1 PGK2 PVKI PVK1 PVK2 CVKA CVKB DEC + # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + cases << %W{ 5560501200002101 1010 4412 6183 134 317 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345} + cases << %W{ 5560501200002111 1010 4784 0931 561 924 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345} + cases << %W{ 5560501200002121 1010 1040 4895 462 673 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345} + cases << %W{ 5560501200002131 1010 3680 6373 826 267 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345} + cases << %W{ 5560501200002101 1110 4412 6183 900 155 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345} + cases << %W{ 5560501200002111 1110 4784 0931 363 513 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345} + cases << %W{ 5560501200002121 1110 1040 4895 952 937 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345} + cases << %W{ 5560501200002131 1110 3680 6373 667 522 3737373737373737 0000000000000000 2 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0123456789012345} + cases << %W{ 5560501200002101 1010 9907 7527 777 473 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345} + cases << %W{ 5560501200002111 1010 2345 0658 638 553 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345} + cases << %W{ 5560501200002121 1010 8245 8196 085 480 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345} + cases << %W{ 5560501200002131 1010 3812 2948 591 546 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345} + cases << %W{ 5560501200002101 1110 9907 7527 349 994 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345} + cases << %W{ 5560501200002111 1110 2345 0658 245 266 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345} + cases << %W{ 5560501200002121 1110 8245 8196 441 115 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345} + cases << %W{ 5560501200002131 1110 3812 2948 126 768 0123456789ABCDEF FEDCBA9876543210 2 7BB19E3D56A1237E 29F7C8FA379EE25C 007A5048DB9531B3 0322DA78AB2F85E1 0123456789012345} + + cases.each do |c| + ibm1=HSMR::Component.new(c[6]) + ibm2=HSMR::Component.new(c[7]) + ibm=ibm1.xor(ibm2) + + pvk=HSMR::Key.new(c[9]+c[10]) + + pin = HSMR::ibm3624(ibm, c[0], 4, c[13]).join + pvv = HSMR::pvv(pvk, c[0], c[8], pin) + + assert_equal pin, c[2] + assert_equal pin.to_i, c[2].to_i + assert_equal pvv, c[3] + assert_equal pvv.to_i, c[3].to_i + + cvv = HSMR::cvv(HSMR::Key.new(c[11]), HSMR::Key.new(c[12]), c[0], c[1], '0') + cvv2 = HSMR::cvv(HSMR::Key.new(c[11]), HSMR::Key.new(c[12]), c[0], c[1], '101') + + assert_equal c[4].to_i, cvv2.to_i + assert_equal c[5].to_i, cvv.to_i + + #puts "#{pin} == #{c[2]} ? #{pin.to_i == c[2].to_i} | #{pvv} == #{c[3]} ? #{pvv.to_i == c[3].to_i}" + # + end + end + + #test "cvv generation" do + # keya=HSMR::Key.new("0123456789ABCDEF") + # keyb=HSMR::Key.new("FEDCBA1234567890") + # key =HSMR::Key.new("0123456789ABCDEFFEDCBA1234567890") +# +# pan="4509494222049051" +# exp="0907" +# svc="101000000000" +# res="271" +# r=HSMR::cvv(keya, keyb, pan, exp, svc) +# assert_equal res, r +# end end