mirror of
https://github.com/dkam/hsmr.git
synced 2025-12-28 00:34:52 +00:00
Split Key out to separate class
This commit is contained in:
28
Gemfile.lock
Normal file
28
Gemfile.lock
Normal file
@@ -0,0 +1,28 @@
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
diff-lcs (1.1.2)
|
||||
git (1.2.5)
|
||||
jeweler (1.5.2)
|
||||
bundler (~> 1.0.0)
|
||||
git (>= 1.2.5)
|
||||
rake
|
||||
rake (0.8.7)
|
||||
rcov (0.9.9)
|
||||
rspec (2.3.0)
|
||||
rspec-core (~> 2.3.0)
|
||||
rspec-expectations (~> 2.3.0)
|
||||
rspec-mocks (~> 2.3.0)
|
||||
rspec-core (2.3.1)
|
||||
rspec-expectations (2.3.0)
|
||||
diff-lcs (~> 1.1.2)
|
||||
rspec-mocks (2.3.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
bundler (~> 1.0.0)
|
||||
jeweler (~> 1.5.2)
|
||||
rcov
|
||||
rspec (~> 2.3.0)
|
||||
4
Rakefile
4
Rakefile
@@ -16,9 +16,11 @@ Jeweler::Tasks.new do |gem|
|
||||
gem.homepage = "http://github.com/dkam/hsmr"
|
||||
gem.license = "MIT"
|
||||
gem.summary = %Q{HSM Functions in Ruby}
|
||||
gem.description = %Q{TODO: longer description of your gem}
|
||||
gem.description = gem.summary
|
||||
gem.email = "d@nmilne.com"
|
||||
gem.authors = ["Dan Milne"]
|
||||
gem.version = '0.0.1'
|
||||
|
||||
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
||||
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
||||
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
||||
|
||||
247
lib/hsmr.rb
247
lib/hsmr.rb
@@ -1,10 +1,7 @@
|
||||
require 'openssl'
|
||||
|
||||
module HSMR
|
||||
VERSION = '1.0.0'
|
||||
#Decimalisation methods
|
||||
IBM=0
|
||||
VISA=1
|
||||
VERSION = '0.0.1'
|
||||
|
||||
# Key Lengths
|
||||
SINGLE=64
|
||||
@@ -38,14 +35,14 @@ module HSMR
|
||||
des = OpenSSL::Cipher::Cipher.new("des-cbc")
|
||||
des.encrypt
|
||||
des.key=key.key
|
||||
return HSMR::decimalise(des.update(validation_data).unpack('H*').first, IBM, dtable)[0...plength]
|
||||
return HSMR::decimalise(des.update(validation_data).unpack('H*').first, :ibm, dtable)[0...plength]
|
||||
|
||||
end
|
||||
|
||||
def self.decimalise(value, method=VISA, dtable="0123456789012345" )
|
||||
def self.decimalise(value, method=:visa, dtable="0123456789012345" )
|
||||
|
||||
result = []
|
||||
if method == IBM
|
||||
if method == :ibm
|
||||
##
|
||||
# The IBM method
|
||||
##
|
||||
@@ -53,7 +50,7 @@ module HSMR
|
||||
result << dtable[c.to_i(16),1].to_i
|
||||
end
|
||||
|
||||
elsif method == VISA
|
||||
elsif method == :visa
|
||||
|
||||
value.each_char do |c|
|
||||
result << c.to_i if numeric?(c)
|
||||
@@ -75,7 +72,7 @@ module HSMR
|
||||
des.encrypt
|
||||
des.key=key.key
|
||||
result = des.update(@tsp).unpack('H*').first.upcase
|
||||
decimalise(result, VISA)[0..3].join
|
||||
decimalise(result, :visa)[0..3].join
|
||||
end
|
||||
|
||||
def self.xor(component1, *rest)
|
||||
@@ -102,239 +99,9 @@ module HSMR
|
||||
## Method to determine if an object is a numeric type.
|
||||
true if Float(object) rescue false
|
||||
end
|
||||
|
||||
class Component
|
||||
attr_reader :component
|
||||
attr_reader :length
|
||||
attr_reader :parity
|
||||
|
||||
def initialize(component=nil, length=DOUBLE)
|
||||
## Should check for odd parity
|
||||
if component.nil?
|
||||
component = (0...(length/4)).collect { rand(16).to_s(16).upcase }.join
|
||||
else
|
||||
component=component.gsub(/ /,'')
|
||||
#raise TypeError, "Component argument expected" unless other.is_a? Component
|
||||
end
|
||||
@component = component.unpack('a2'*(component.length/2)).map{|x| x.hex}.pack('c'*(component.length/2))
|
||||
@length = @component.length
|
||||
end
|
||||
|
||||
def kcv()
|
||||
des = OpenSSL::Cipher::Cipher.new("des-cbc") if @component.length == 8
|
||||
des = OpenSSL::Cipher::Cipher.new("des-ede-cbc") if @component.length == 16
|
||||
des.encrypt
|
||||
des.key=@component
|
||||
des.update("\x00"*8).unpack('H*').first[0...6].upcase
|
||||
end
|
||||
|
||||
def xor(other)
|
||||
other = Component.new(other) unless other.is_a? Component
|
||||
raise TypeError, "Component argument expected" unless other.is_a? Component
|
||||
|
||||
@a = @component.unpack('C2'*(@component.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 odd_parity?
|
||||
# http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf
|
||||
#
|
||||
# The eight error detecting bits are set to make the parity of each 8-bit
|
||||
# byte of the key odd. That is, there is an odd number of "1"s in each 8-bit byte.
|
||||
|
||||
#3.to_s(2).count('1')
|
||||
#@key.unpack("H2").first.to_i(16).to_s(2)
|
||||
|
||||
working=@component.unpack('H2'*(@component.length))
|
||||
working.each do |o|
|
||||
freq = o.to_i(16).to_s(2).count('1').to_i
|
||||
if( freq%2 == 0)
|
||||
#puts "#{o} is #{o.to_i(16).to_s(2).count('1').to_i } - even"
|
||||
return false
|
||||
else
|
||||
return true
|
||||
#puts "#{o} is #{o.to_i(16).to_s(2).count('1').to_i } - odd"
|
||||
end
|
||||
end
|
||||
end
|
||||
def set_odd_parity
|
||||
return true if self.odd_parity? == true
|
||||
|
||||
working=@component.unpack('H2'*(@component.length))
|
||||
working.each_with_index do |o,i|
|
||||
freq = o.to_i(16).to_s(2).count('1').to_i
|
||||
if( freq%2 == 0)
|
||||
c1 = o[0].chr
|
||||
c2 = case o[1].chr
|
||||
when "0" then "1"
|
||||
when "1" then "0"
|
||||
when "2" then "3"
|
||||
when "3" then "2"
|
||||
when "4" then "5"
|
||||
when "5" then "4"
|
||||
when "6" then "7"
|
||||
when "7" then "6"
|
||||
when "8" then "9"
|
||||
when "9" then "8"
|
||||
when "a" then "b"
|
||||
when "b" then "a"
|
||||
when "c" then "d"
|
||||
when "d" then "c"
|
||||
when "e" then "f"
|
||||
when "f" then "e"
|
||||
end
|
||||
working[i]="#{c1}#{c2}"
|
||||
end
|
||||
end
|
||||
@component = working.join.unpack('a2'*(working.length)).map{|x| x.hex}.pack('c'*(working.length))
|
||||
end
|
||||
|
||||
def to_s
|
||||
@component.unpack('H4'*(@component.length/2)).join(" ").upcase
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
class Key
|
||||
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)
|
||||
|
||||
init = init.first if (init.is_a? Array) && (init.length == 1)
|
||||
|
||||
if init.is_a? Array
|
||||
init.collect! {|c| ( (c.is_a? HSMR::Component) ? c : HSMR::Component.new(c) ) }
|
||||
|
||||
raise TypeError, "Component argument expected" unless init.first.is_a? Component
|
||||
|
||||
@key=HSMR::xor(init.pop, init).key
|
||||
|
||||
elsif init.is_a? Component
|
||||
@key = init.component
|
||||
elsif init.is_a? String
|
||||
key=init.gsub(/ /,'')
|
||||
@key = key.unpack('a2'*(key.length/2)).map{|x| x.hex}.pack('c'*(key.length/2))
|
||||
elsif key.nil?
|
||||
key = (0...(length/4)).collect { rand(16).to_s(16).upcase }.join
|
||||
@key = key.unpack('a2'*(key.length/2)).map{|x| x.hex}.pack('c'*(key.length/2))
|
||||
end
|
||||
@length = @key.length
|
||||
end
|
||||
|
||||
def kcv()
|
||||
des = OpenSSL::Cipher::Cipher.new("des-cbc") if @key.length == 8
|
||||
des = OpenSSL::Cipher::Cipher.new("des-ede-cbc") if @key.length == 16
|
||||
des.encrypt
|
||||
des.key=@key
|
||||
des.update("\x00"*8).unpack('H*').first[0...6].upcase
|
||||
end
|
||||
|
||||
def encpin(pin)
|
||||
@pin = pin.unpack('a2'*(pin.length/2)).map{|x| x.hex}.pack('c'*(pin.length/2))
|
||||
des = OpenSSL::Cipher::Cipher.new("des-ede")
|
||||
des.encrypt
|
||||
des.key=@key
|
||||
return des.update(@pin).unpack('H*').first.upcase
|
||||
end
|
||||
|
||||
def decryptpin(pinblock)
|
||||
@pinblock = pinblock.unpack('a2'*(pinblock.length/2)).map{|x| x.hex}.pack('c'*(pinblock.length/2))
|
||||
des = OpenSSL::Cipher::Cipher.new("des-ede")
|
||||
des.decrypt
|
||||
des.padding=0
|
||||
des.key=@key
|
||||
result = des.update(@pinblock)
|
||||
result << des.final
|
||||
result.unpack('H*').first.upcase
|
||||
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 pvv(account, pvki, pin)
|
||||
HSMR::pvv(self, account, pvi, pin)
|
||||
end
|
||||
|
||||
def to_s
|
||||
@key.unpack('H4 '* (@length/2) ).join(" ").upcase
|
||||
end
|
||||
|
||||
def odd_parity?
|
||||
# http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf
|
||||
#
|
||||
# The eight error detecting bits are set to make the parity of each 8-bit
|
||||
# byte of the key odd. That is, there is an odd number of "1"s in each 8-bit byte.
|
||||
|
||||
#3.to_s(2).count('1')
|
||||
#@key.unpack("H2").first.to_i(16).to_s(2)
|
||||
|
||||
working=@key.unpack('H2'*(@key.length))
|
||||
working.each do |o|
|
||||
freq = o.to_i(16).to_s(2).count('1').to_i
|
||||
if( freq%2 == 0)
|
||||
#puts "#{o} is #{o.to_i(16).to_s(2).count('1').to_i } - even"
|
||||
return false
|
||||
else
|
||||
return true
|
||||
#puts "#{o} is #{o.to_i(16).to_s(2).count('1').to_i } - odd"
|
||||
end
|
||||
end
|
||||
end
|
||||
def set_odd_parity
|
||||
return true if self.odd_parity? == true
|
||||
|
||||
working=@key.unpack('H2'*(@key.length))
|
||||
working.each_with_index do |o,i|
|
||||
freq = o.to_i(16).to_s(2).count('1').to_i
|
||||
if( freq%2 == 0)
|
||||
c1 = o[0].chr
|
||||
c2 = case o[1].chr
|
||||
when "0" then "1"
|
||||
when "1" then "0"
|
||||
when "2" then "3"
|
||||
when "3" then "2"
|
||||
when "4" then "5"
|
||||
when "5" then "4"
|
||||
when "6" then "7"
|
||||
when "7" then "6"
|
||||
when "8" then "9"
|
||||
when "9" then "8"
|
||||
when "a" then "b"
|
||||
when "b" then "a"
|
||||
when "c" then "d"
|
||||
when "d" then "c"
|
||||
when "e" then "f"
|
||||
when "f" then "e"
|
||||
end
|
||||
working[i]="#{c1}#{c2}"
|
||||
end
|
||||
end
|
||||
@key = working.join.unpack('a2'*(working.length)).map{|x| x.hex}.pack('c'*(working.length))
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class String
|
||||
def xor(other)
|
||||
if other.empty?
|
||||
|
||||
112
lib/key.rb
Normal file
112
lib/key.rb
Normal file
@@ -0,0 +1,112 @@
|
||||
module HSMR
|
||||
class Key
|
||||
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)
|
||||
|
||||
init = init.first if (init.is_a? Array) && (init.length == 1)
|
||||
|
||||
if init.is_a? Array
|
||||
init.collect! {|c| ( (c.is_a? HSMR::Component) ? c : HSMR::Component.new(c) ) }
|
||||
|
||||
raise TypeError, "Component argument expected" unless init.first.is_a? Component
|
||||
|
||||
@key=HSMR::xor(init.pop, init).key
|
||||
|
||||
elsif init.is_a? Component
|
||||
@key = init.component
|
||||
elsif init.is_a? String
|
||||
key=init.gsub(/ /,'')
|
||||
@key = key.unpack('a2'*(key.length/2)).map{|x| x.hex}.pack('c'*(key.length/2))
|
||||
elsif key.nil?
|
||||
key = (0...(length/4)).collect { rand(16).to_s(16).upcase }.join
|
||||
@key = key.unpack('a2'*(key.length/2)).map{|x| x.hex}.pack('c'*(key.length/2))
|
||||
end
|
||||
@length = @key.length
|
||||
end
|
||||
|
||||
def kcv()
|
||||
des = OpenSSL::Cipher::Cipher.new("des-cbc") if @key.length == 8
|
||||
des = OpenSSL::Cipher::Cipher.new("des-ede-cbc") if @key.length == 16
|
||||
des.encrypt
|
||||
des.key=@key
|
||||
des.update("\x00"*8).unpack('H*').first[0...6].upcase
|
||||
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 odd_parity?
|
||||
# http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf
|
||||
#
|
||||
# The eight error detecting bits are set to make the parity of each 8-bit
|
||||
# byte of the key odd. That is, there is an odd number of "1"s in each 8-bit byte.
|
||||
|
||||
#3.to_s(2).count('1')
|
||||
#@key.unpack("H2").first.to_i(16).to_s(2)
|
||||
|
||||
working=@key.unpack('H2'*(@key.length))
|
||||
working.each do |o|
|
||||
freq = o.to_i(16).to_s(2).count('1').to_i
|
||||
if( freq%2 == 0)
|
||||
#puts "#{o} is #{o.to_i(16).to_s(2).count('1').to_i } - even"
|
||||
return false
|
||||
else
|
||||
return true
|
||||
#puts "#{o} is #{o.to_i(16).to_s(2).count('1').to_i } - odd"
|
||||
end
|
||||
end
|
||||
end
|
||||
def set_odd_parity
|
||||
return true if self.odd_parity? == true
|
||||
|
||||
working=@key.unpack('H2'*(@key.length))
|
||||
working.each_with_index do |o,i|
|
||||
freq = o.to_i(16).to_s(2).count('1').to_i
|
||||
if( freq%2 == 0)
|
||||
c1 = o[0].chr
|
||||
c2 = case o[1].chr
|
||||
when "0" then "1"
|
||||
when "1" then "0"
|
||||
when "2" then "3"
|
||||
when "3" then "2"
|
||||
when "4" then "5"
|
||||
when "5" then "4"
|
||||
when "6" then "7"
|
||||
when "7" then "6"
|
||||
when "8" then "9"
|
||||
when "9" then "8"
|
||||
when "a" then "b"
|
||||
when "b" then "a"
|
||||
when "c" then "d"
|
||||
when "d" then "c"
|
||||
when "e" then "f"
|
||||
when "f" then "e"
|
||||
end
|
||||
working[i]="#{c1}#{c2}"
|
||||
end
|
||||
end
|
||||
@key = working.join.unpack('a2'*(working.length)).map{|x| x.hex}.pack('c'*(working.length))
|
||||
end
|
||||
|
||||
def to_s
|
||||
@key.unpack('H4 '* (@length/2) ).join(" ").upcase
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user