Migrating into new Gem repo

This commit is contained in:
Dan Milne
2011-01-07 22:15:14 +11:00
parent a2aecfd44b
commit 8620a66d29
4 changed files with 561 additions and 3 deletions

7
CHANGES.md Normal file
View File

@@ -0,0 +1,7 @@
History
=========
1.0.0
-------
Initial release.

28
README.md Normal file
View File

@@ -0,0 +1,28 @@
HSMR
===========
HSMR is a collection of cryptographic commands usually implemented on a HSM (Hardware Security Module). These
are implemented for your education or for testing purposes and should not be used to replace an actual HSM.
Installation
-------------
You install it just like any other Ruby gem:
gem install hsmr
Usage
---------
require 'hsmr'
Author
==========
Dan Milne, http://da.nmilne.com
Copyright
----------
Copyright (c) 2010 Dan Milne. See LICENSE for details.

View File

@@ -0,0 +1,349 @@
require 'openssl'
module HSMR
VERSION = '1.0.0'
#Decimalisation methods
IBM=0
VISA=1
# Key Lengths
SINGLE=64
DOUBLE=128
TRIPLE=192
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")
des.encrypt
des.key=key.key
return des.update(@pin).unpack('H*').first.upcase
end
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::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
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::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
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
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::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
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
def self.numeric?(object)
## 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?
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*")
end
end
end

View File

@@ -1,7 +1,181 @@
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Hsmr" do
it "fails" do
fail "hey buddy, you should probably rename this file and start specing for real"
describe "Generate Component" do
it "should generate a component" do
component_1 = HSMR::Component.new(nil, HSMR::SINGLE)
component_2 = HSMR::Component.new(nil, HSMR::DOUBLE)
component_3 = HSMR::Component.new(nil, HSMR::TRIPLE)
component_1.length.should == 8
component_2.length.should == 16
component_3.length.should == 24
end
end
describe "Calculate component KCV" do
it "should calculate single length component KCV values" do
component_1 = HSMR::Component.new("6DBF C180 4A01 5BAD")
component_1.kcv.should == "029E60"
component_2 = HSMR::Component.new("5D80 0497 B319 8591")
component_2.kcv.should == "3B86C3"
component_3 = HSMR::Component.new("B0C7 7CDC 7354 97C7")
component_3.kcv.should == "7A77BC"
component_4 = HSMR::Component.new("DAE0 86FE D6EA 0BEA")
component_4.kcv.should == "2E6191"
component_5 = HSMR::Component.new("682C 8315 F4BF FBC1")
component_5.kcv.should == "62B336"
component_6 = HSMR::Component.new("5715 F289 04BC B62F")
component_6.kcv.should == "3CBA88"
component_7 = HSMR::Component.new("D0C4 29AE C4A8 02B5")
component_7.kcv.should == "33AF02"
component_8 = HSMR::Component.new("7049 D0F7 4A97 15B6")
component_8.kcv.should == "AC1399"
component_9 = HSMR::Component.new("BC91 D698 157A A4E5")
component_9.kcv.should == "295491"
component_10 = HSMR::Component.new("64AB 8568 7A0E 322F")
component_10.kcv.should == "D9F7B3"
end
it "should calculate double length component KCV values" do
component_1 = HSMR::Component.new("ADE3 9B38 0DBC DF38 AE02 AECE 64B3 4373")
component_1.kcv.should == "3002D5"
component_2 = HSMR::Component.new("B64A EF86 460D DF5B 57B3 D53D AD37 52A1")
component_2.kcv.should == "1F7C07"
component_3 = HSMR::Component.new("5B89 6E29 76EC 9745 15B5 238C 8CFE 3D23")
component_3.kcv.should == "DE78F2"
component_4 = HSMR::Component.new("5E61 CB20 D540 1FC7 58EC CDC8 B558 E9B9")
component_4.kcv.should == "FED957"
component_5 = HSMR::Component.new("23DF CEB9 BF94 ADA8 91E9 580B 8C8F 5BEF")
component_5.kcv.should == "902085"
component_6 = HSMR::Component.new("DFEF 61C8 2037 3DA4 CE9B 92CD 89E9 B334")
component_6.kcv.should == "E45EB7"
component_7 = HSMR::Component.new("6746 9E4C FE83 F831 F23E 9D9E 9D9E 9DB3")
component_7.kcv.should == "813B7B"
component_8 = HSMR::Component.new("23E5 496E DF94 0BD5 9734 B07A BF26 B9E6")
component_8.kcv.should == "E7C48F"
component_9 = HSMR::Component.new("974F 26BC CB2A ECD5 434F 1CDC 64DF A275")
component_9.kcv.should == "E27250"
component_10 = HSMR::Component.new("E57A DF5B CEA7 F42A DFD9 E554 07A2 F891")
component_10.kcv.should == "50E3F8"
end
end
describe "Calculate key KCV" do
it "should calculate single length key KCV values" do
key_1 = HSMR::Key.new("6DBF C180 4A01 5BAD")
key_1.kcv.should == "029E60"
key_2 = HSMR::Key.new("5D80 0497 B319 8591")
key_2.kcv.should == "3B86C3"
key_3 = HSMR::Key.new("B0C7 7CDC 7354 97C7")
key_3.kcv.should == "7A77BC"
key_4 = HSMR::Key.new("DAE0 86FE D6EA 0BEA")
key_4.kcv.should == "2E6191"
key_5 = HSMR::Key.new("682C 8315 F4BF FBC1")
key_5.kcv.should == "62B336"
key_6 = HSMR::Key.new("5715 F289 04BC B62F")
key_6.kcv.should == "3CBA88"
key_7 = HSMR::Key.new("D0C4 29AE C4A8 02B5")
key_7.kcv.should == "33AF02"
key_8 = HSMR::Key.new("7049 D0F7 4A97 15B6")
key_8.kcv.should == "AC1399"
key_9 = HSMR::Key.new("BC91 D698 157A A4E5")
key_9.kcv.should == "295491"
key_10 = HSMR::Key.new("64AB 8568 7A0E 322F")
key_10.kcv.should == "D9F7B3"
end
it "should calculate double length key KCV values" do
key_1 = HSMR::Key.new("ADE3 9B38 0DBC DF38 AE02 AECE 64B3 4373")
key_1.kcv.should == "3002D5"
key_2 = HSMR::Key.new("B64A EF86 460D DF5B 57B3 D53D AD37 52A1")
key_2.kcv.should == "1F7C07"
key_3 = HSMR::Key.new("5B89 6E29 76EC 9745 15B5 238C 8CFE 3D23")
key_3.kcv.should == "DE78F2"
key_4 = HSMR::Key.new("5E61 CB20 D540 1FC7 58EC CDC8 B558 E9B9")
key_4.kcv.should == "FED957"
key_5 = HSMR::Key.new("23DF CEB9 BF94 ADA8 91E9 580B 8C8F 5BEF")
key_5.kcv.should == "902085"
key_6 = HSMR::Key.new("DFEF 61C8 2037 3DA4 CE9B 92CD 89E9 B334")
key_6.kcv.should == "E45EB7"
key_7 = HSMR::Key.new("6746 9E4C FE83 F831 F23E 9D9E 9D9E 9DB3")
key_7.kcv.should == "813B7B"
key_8 = HSMR::Key.new("23E5 496E DF94 0BD5 9734 B07A BF26 B9E6")
key_8.kcv.should == "E7C48F"
key_9 = HSMR::Key.new("974F 26BC CB2A ECD5 434F 1CDC 64DF A275")
key_9.kcv.should == "E27250"
key_10 = HSMR::Key.new("E57A DF5B CEA7 F42A DFD9 E554 07A2 F891")
key_10.kcv.should == "50E3F8"
end
end
describe "Calculate parity" do
it "should detect odd_parity in a key" do
odd_key = HSMR::Key.new("41A2AC14A90C583741A2AC14A90C5837")
odd_key.odd_parity?.should == false
end
it "should set double length key parity to odd" do
odd_key = HSMR::Key.new("41A2AC14A90C583741A2AC14A90C5837")
odd_key.set_odd_parity
even_key = HSMR::Key.new("40A2AD15A80D583740A2AD15A80D5837")
odd_key.key.should == even_key.key
end
it "should detect odd_parity in a component" do
odd_component = HSMR::Component.new("41A2AC14A90C583741A2AC14A90C5837")
odd_component.odd_parity?.should == false
end
it "should set double length component parity to odd" do
odd_component = HSMR::Component.new("41A2AC14A90C583741A2AC14A90C5837")
odd_component.set_odd_parity
even_component = HSMR::Component.new("40A2AD15A80D583740A2AD15A80D5837")
odd_component.component.should == even_component.component
end
end