Module: EMV::Crypto
- Defined in:
- lib/emv/crypto/crypto.rb
Class Method Summary collapse
- .check_and_convert(mes, data, length_in_bytes) ⇒ Object
-
.encrypt_cbc(key, data) ⇒ Object
encrypt cbc accroding to CPS 5.5.2.
-
.encrypt_ecb(key, data) ⇒ Object
encrypt ecb according to CPS 5.5.1.
-
.generate_session_key(ic_card_key, seq, type) ⇒ Object
Generate Session keys according to CPS 5.3.
-
.mac_for_personalization(key, input) ⇒ Object
Mac calculation according to CPS 5.4.1.
- .pad(input) ⇒ Object
-
.retail_mac(key, data) ⇒ Object
calculate ISO 9797-1 “Retail Mac” (MAC Algorithm 3 with output transformation 3, without truncation) : DES with final TDES.
Class Method Details
.check_and_convert(mes, data, length_in_bytes) ⇒ Object
98 99 100 101 102 103 104 105 |
# File 'lib/emv/crypto/crypto.rb', line 98 def self.check_and_convert mes, data, length_in_bytes raise "`#{mes}` may not be nil" unless data unless data.length == length_in_bytes || data.length == length_in_bytes*2 raise "invalid length for '#{mes}'. Should be #{length_in_bytes}" end data = ISO7816.s2b(data) if data.length == length_in_bytes*2 data end |
.encrypt_cbc(key, data) ⇒ Object
encrypt cbc accroding to CPS 5.5.2
37 38 39 40 41 42 |
# File 'lib/emv/crypto/crypto.rb', line 37 def self.encrypt_cbc key, data cipher = OpenSSL::Cipher::Cipher.new("des-ede-cbc").encrypt cipher.key = key data = pad(data) cipher.update data end |
.encrypt_ecb(key, data) ⇒ Object
encrypt ecb according to CPS 5.5.1
30 31 32 33 34 |
# File 'lib/emv/crypto/crypto.rb', line 30 def self.encrypt_ecb key, data cipher = OpenSSL::Cipher::Cipher.new("des-ede").encrypt cipher.key = key cipher.update(data) end |
.generate_session_key(ic_card_key, seq, type) ⇒ Object
Generate Session keys according to CPS 5.3
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/emv/crypto/crypto.rb', line 7 def self.generate_session_key ic_card_key, seq, type seq = check_and_convert("seq", seq, 2) ic_card_key = check_and_convert("ic_card_key", ic_card_key, 16) derivation_data = case type when :enc "\x01\x82" when :mac "\x01\x01" when :dek "\x01\x81" else raise "invalid type of key: #{type}" end derivation_data += seq + ISO7816.s2b("000000000000000000000000") cipher = OpenSSL::Cipher::Cipher.new("des-ede-cbc").encrypt cipher.key = ic_card_key cipher.update derivation_data end |
.mac_for_personalization(key, input) ⇒ Object
Mac calculation according to CPS 5.4.1.
45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/emv/crypto/crypto.rb', line 45 def self.mac_for_personalization key, input key = check_and_convert "key", key, 16 input = pad(input) cipher = OpenSSL::Cipher::Cipher.new("des-ede-cbc").encrypt cipher.key = key mac = "" input.scan(/.{8}/m) {|block| mac = cipher.update block } mac end |
.pad(input) ⇒ Object
89 90 91 92 93 94 95 96 |
# File 'lib/emv/crypto/crypto.rb', line 89 def self.pad input input += "\x80" while (input.length % 8) != 0 input << 0x00 end input end |
.retail_mac(key, data) ⇒ Object
calculate ISO 9797-1 “Retail Mac” (MAC Algorithm 3 with output transformation 3, without truncation) : DES with final TDES.
Padding is added if data is not a multiple of 8
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/emv/crypto/crypto.rb', line 63 def self.retail_mac key, data cipher = OpenSSL::Cipher::Cipher.new("des-cbc").encrypt cipher.key = key[0,8] data = pad(data) single_data = data[0,data.length-8] # Single DES with XOR til the last block if single_data && single_data.length > 0 mac_ = cipher.update(single_data) mac_ = mac_[mac_.length-8, 8] else # length of data was <= 8 mac_ = "\x00"*8 end triple_data = data[data.length-8, 8] mac = "" 0.upto(7) { |i| mac << (mac_[i] ^ triple_data[i]) } # Final Round of TDES cipher = OpenSSL::Cipher::Cipher.new("des-ede").encrypt cipher.key = key cipher.update(mac) end |