module HSMR

Constants

DOUBLE
SINGLE

Key Lengths

TRIPLE
VERSION

Public Class Methods

cvv(key_a, key_b, pan, exp, svc) click to toggle source
# File lib/hsmr.rb, line 204
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
decimalise(value, method=:visa, dtable="0123456789012345" ) click to toggle source
# File lib/hsmr.rb, line 168
def self.decimalise(value, method=:visa, dtable="0123456789012345" )

  result = []
  if method == :ibm  
    ##
    # The IBM method
    ##
    value.each_char do |c| 
      result << dtable[c.to_i(16),1].to_i
    end
    
  elsif method == :visa
    
    value.each_char do |c|
      result << c.to_i if numeric?(c)
    end
  
    value.upcase.each_char do |c|
      result << dtable[c.to_i(16),1].to_i unless numeric?(c)
    end
    
  end
  
  return result
end
decrypt_pin(key, pinblock) click to toggle source
# File lib/hsmr.rb, line 145
def self.decrypt_pin(key, pinblock)
  @pinblock = pinblock.unpack('a2'*(pinblock.length/2)).map{|x| x.hex}.pack('c'*(pinblock.length/2))
  des = OpenSSL::Cipher.new("des-ede")
  des.decrypt
  des.padding=0
  des.key=key.key
  result = des.update(@pinblock)
  result << des.final
  result.unpack('H*').first.upcase
end
encrypt(data, key) click to toggle source
# File lib/hsmr.rb, line 58
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.new("des-cbc") if key.length == 8
  des = OpenSSL::Cipher.new("des-ede-cbc") if key.length == 16
  des = OpenSSL::Cipher.new("des-ede3-cbc") if key.length == 24

  des.encrypt
  des.key=key.key
  to_hex( des.update(to_binary(data)) )
end
encrypt_pin(key, pin) click to toggle source
# File lib/hsmr.rb, line 137
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.new("des-ede")
  des.encrypt
  des.key=key.key
  return des.update(@pin).unpack('H*').first.upcase
end
ibm3624(key, account, plength=4, dtable="0123456789012345" ) click to toggle source
# File lib/hsmr.rb, line 156
def self.ibm3624(key, account, plength=4, dtable="0123456789012345" )
  
  validation_data = account.unpack('a2'*(account.length/2)).map{|x| x.hex}.pack('c'*(account.length/2))

  #des = OpenSSL::Cipher::Cipher.new("des-ede-cbc")
  des = OpenSSL::Cipher.new("des-cbc")
  des.encrypt
  des.key=key.key
  return HSMR::decimalise(des.update(validation_data).unpack('H*').first, :ibm, dtable)[0...plength]
  
end
numeric?(object) click to toggle source
# File lib/hsmr.rb, line 243
def self.numeric?(object)
  ## Method to determine if an object is a numeric type.
  true if Float(object) rescue false
end
pvv(key, account, pvki, pin) click to toggle source
# File lib/hsmr.rb, line 194
def self.pvv(key, account, pvki, pin)
  tsp = account.to_s[4,11] + pvki.to_s + pin.to_s
  @tsp = tsp.unpack('a2'*(tsp.length/2)).map{|x| x.hex}.pack('c'*(tsp.length/2))
  des = OpenSSL::Cipher.new("des-ede")
  des.encrypt
  des.key=key.key
  result = des.update(@tsp).unpack('H*').first.upcase
  decimalise(result, :visa)[0..3].join
end
to_binary(data) click to toggle source

Module Methods

# File lib/hsmr.rb, line 129
def self.to_binary(data)
  data.unpack('a2'*(data.length/2)).map{|x| x.hex}.pack('c'*(data.length/2))
end
to_hex(data) click to toggle source
# File lib/hsmr.rb, line 133
def self.to_hex(data)
  data.unpack('H*').first.upcase
end
xor(component1, *rest) click to toggle source
# File lib/hsmr.rb, line 223
def self.xor(component1, *rest)
  return if rest.length == 0
  
  component1 = Component.new(component1) unless component1.is_a? Component
  raise TypeError, "Component argument expected" unless component1.is_a? Component
  
  #@components=[]
  #rest.each {|c| components << ((c.is_a? HSMR::Component) ? c : HSMR::Component.new(c) ) }
  #components.each {|c| raise TypeError, "Component argument expected" unless c.is_a? Component }
  #resultant = component1.xor(components.pop)
  #components.each {|c| resultant.xor!(c) }

  rest.collect! {|c| ( (c.is_a? HSMR::Component) ? c : HSMR::Component.new(c) ) }
  rest.each {|c| raise TypeError, "Component argument expected" unless c.is_a? HSMR::Component }
  resultant = component1.xor(rest.pop)
  rest.each {|c| resultant.xor!(c) }

  return(resultant)
end

Public Instance Methods

generate(length) click to toggle source
# File lib/hsmr.rb, line 21
def generate(length)
  (0...(length/4)).collect { rand(16).to_s(16).upcase }.join
end
kcv() click to toggle source

Mixin functionality

# File lib/hsmr.rb, line 13
def kcv()
  des = OpenSSL::Cipher.new("des-cbc") if @key.length == 8
  des = OpenSSL::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
odd_parity?() click to toggle source
# File lib/hsmr.rb, line 35
def odd_parity?
  # http://www.cryptosys.net/3des.html
  # 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
parity() click to toggle source
# File lib/hsmr.rb, line 30
def parity
  'even' unless odd_parity?
  'odd'
end
set_odd_parity() click to toggle source
# File lib/hsmr.rb, line 71
def set_odd_parity
  return self 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))
  return self
end
to_s() click to toggle source
# File lib/hsmr.rb, line 26
def to_s
  @key.unpack('H4'*(@key.length/2)).join(" ").upcase
end
xor(other) click to toggle source
# File lib/hsmr.rb, line 104
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
xor!(_key) click to toggle source
# File lib/hsmr.rb, line 123
def xor!(_key)
  @key = xor(_key).key
end