dukpt 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -20,6 +20,9 @@ Or install it yourself as:
20
20
 
21
21
  # Instantiate a decrypter with your base derivation key (BDK)
22
22
  decrypter = DUKPT::Decrypter.new("0123456789ABCDEFFEDCBA9876543210")
23
+
24
+ # You can specify whether you want to use "ecb" cipher mode if needed. The default is "cbc".
25
+ decrypter = DUKPT::Decrypter.new("0123456789ABCDEFFEDCBA9876543210", "ecb")
23
26
 
24
27
  # Pass the ciphertext and the current Key Serial Number (KSN), as hex encoded strings, to the decryptor to get back the plaintext
25
28
  ksn = "FFFF9876543210E00008"
@@ -3,8 +3,10 @@ module DUKPT
3
3
  include Encryption
4
4
 
5
5
  attr_reader :bdk
6
- def initialize(bdk)
6
+
7
+ def initialize(bdk, mode=nil)
7
8
  @bdk = bdk
9
+ self.cipher_mode = mode.nil? ? 'cbc' : mode
8
10
  end
9
11
 
10
12
  def decrypt(cryptogram, ksn)
@@ -10,7 +10,18 @@ module DUKPT
10
10
  KEY_MASK = 0xC0C0C0C000000000C0C0C0C000000000
11
11
  PEK_MASK = 0x00000000000000FF00000000000000FF
12
12
  KSN_MASK = 0xFFFFFFFFFFFFFFE00000
13
-
13
+ DEK_MASK = 0x0000000000FF00000000000000FF0000 # Used by IDTECH reader
14
+
15
+ def cipher_mode=(cipher_type)
16
+ if cipher_type == "ecb"
17
+ @cipher_type_des = "des-ecb"
18
+ @cipher_type_tdes = "des-ede"
19
+ else
20
+ @cipher_type_des = "des-cbc"
21
+ @cipher_type_tdes = "des-ede-cbc"
22
+ end
23
+ end
24
+
14
25
  def derive_key(ipek, ksn)
15
26
  ksn_current = ksn.to_i(16)
16
27
 
@@ -51,10 +62,33 @@ module DUKPT
51
62
  hex_string_from_val((key.to_i(16) ^ PEK_MASK), 16)
52
63
  end
53
64
 
65
+ def dek_from_key(key)
66
+ key = key.to_i(16)
67
+
68
+ key = key ^ DEK_MASK
69
+
70
+ left = (key & MS16_MASK) >> 64
71
+ right = (key & LS16_MASK)
72
+
73
+ invariant_key_hex = hex_string_from_val(key, 16)
74
+
75
+ left = triple_des_encrypt(invariant_key_hex, hex_string_from_val(left, 8))
76
+ right = triple_des_encrypt(invariant_key_hex, hex_string_from_val(right, 8))
77
+
78
+ left = hex_string_from_val(left.to_i(16), 8)
79
+ right = hex_string_from_val(right.to_i(16), 8)
80
+
81
+ [left, right].join
82
+ end
83
+
54
84
  def derive_PEK(ipek, ksn)
55
85
  pek_from_key(derive_key(ipek, ksn))
56
86
  end
57
87
 
88
+ def derive_DEK(ipek, ksn)
89
+ dek_from_key(derive_key(ipek, ksn))
90
+ end
91
+
58
92
  def derive_IPEK(bdk, ksn)
59
93
  ksn_cleared_count = (ksn.to_i(16) & KSN_MASK) >> 16
60
94
  left_half_of_ipek = triple_des_encrypt(bdk, hex_string_from_val(ksn_cleared_count, 8))
@@ -63,23 +97,36 @@ module DUKPT
63
97
  ipek_derived = left_half_of_ipek + right_half_of_ipek
64
98
  end
65
99
 
100
+ def aes_decrypt(key, message)
101
+ openssl_encrypt("aes-128-cbc", key, message, false)
102
+ end
103
+
66
104
  def triple_des_decrypt(key, message)
67
- openssl_encrypt("des-ede-cbc", key, message, false)
105
+ openssl_encrypt(cipher_type_tdes, key, message, false)
68
106
  end
69
107
 
70
108
  def triple_des_encrypt(key, message)
71
- openssl_encrypt("des-ede-cbc", key, message, true)
109
+ openssl_encrypt(cipher_type_tdes, key, message, true)
72
110
  end
73
-
111
+
74
112
  def des_encrypt(key, message)
75
- openssl_encrypt("des-cbc", key, message, true)
113
+ openssl_encrypt(cipher_type_des, key, message, true)
76
114
  end
77
115
 
78
116
  private
117
+
118
+ def cipher_type_des
119
+ @cipher_type_des || "des-cbc"
120
+ end
121
+
122
+ def cipher_type_tdes
123
+ @cipher_type_tdes || "des-ede-cbc"
124
+ end
79
125
 
80
126
  def hex_string_from_val val, bytes
81
127
  val.to_s(16).rjust(bytes * 2, "0")
82
128
  end
129
+
83
130
  def encrypt_register(curkey, reg_8)
84
131
  left_key_half = (curkey & MS16_MASK) >> 64
85
132
  right_key_half = curkey & LS16_MASK
@@ -96,7 +143,6 @@ module DUKPT
96
143
  is_encrypt ? cipher.encrypt : cipher.decrypt
97
144
  cipher.padding = 0
98
145
  cipher.key = [key].pack('H*')
99
- # No Initial Vector is used in the process.
100
146
  cipher_result = ""
101
147
  cipher_result << cipher.update([message].pack('H*'))
102
148
  cipher_result << cipher.final
@@ -1,3 +1,3 @@
1
1
  module DUKPT
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -10,7 +10,7 @@ class DUKPT::DecrypterTest < Test::Unit::TestCase
10
10
  ciphertext = "C25C1D1197D31CAA87285D59A892047426D9182EC11353C051ADD6D0F072A6CB3436560B3071FC1FD11D9F7E74886742D9BEE0CFD1EA1064C213BB55278B2F12"
11
11
  plaintext = "%B5452300551227189^HOGAN/PAUL ^08043210000000725000000?\x00\x00\x00\x00"
12
12
 
13
- decrypter = DUKPT::Decrypter.new(bdk)
13
+ decrypter = DUKPT::Decrypter.new(bdk, "cbc")
14
14
  assert_equal plaintext, decrypter.decrypt(ciphertext, ksn)
15
15
  end
16
16
 
@@ -84,10 +84,35 @@ def test_derive_pek_counter_EFF800
84
84
  assert_equal '2542353435323330303535313232373138395e484f47414e2f5041554c2020202020205e30383034333231303030303030303732353030303030303f00000000', data_decrypted
85
85
  end
86
86
 
87
+ def test_triple_des_decrypt_with_ecb
88
+ self.cipher_mode = "ecb"
89
+ ciphertext = "C25C1D1197D31CAA87285D59A892047426D9182EC11353C051ADD6D0F072A6CB3436560B3071FC1FD11D9F7E74886742D9BEE0CFD1EA1064C213BB55278B2F12"
90
+ data_decrypted = triple_des_decrypt('27f66d5244ff621eaa6f6120edeb427f', ciphertext)
91
+ assert_equal '2542353435323330f2692820a5e12b9bbf110311e7d5453a0989597b8d3373e0718df68ec04a96ff0704673b0041cc2fe12da84c41b85772e98ed0f0d1ea1064', data_decrypted
92
+ end
93
+
87
94
  def test_unpacking_decrypted_data
88
95
  data_decrypted = '2542353435323330303535313232373138395e484f47414e2f5041554c2020202020205e30383034333231303030303030303732353030303030303f00000000'
89
96
  expected = "%B5452300551227189^HOGAN/PAUL ^08043210000000725000000?\x00\x00\x00\x00"
90
97
  assert_equal expected, [data_decrypted].pack('H*')
91
98
  end
92
99
 
100
+ def test_cipher_mode_ecb
101
+ self.cipher_mode = "ecb"
102
+ assert_equal cipher_type_des, "des-ecb"
103
+ assert_equal cipher_type_tdes, "des-ede"
104
+ end
105
+
106
+ def test_dek_from_key
107
+ key = "27F66D5244FF62E1AA6F6120EDEB4280"
108
+ dek = dek_from_key(key)
109
+ assert_equal "C39B2778B058AC376FB18DC906F75CBA", dek.upcase
110
+ end
111
+
112
+ def test_derive_dek_counter_13
113
+ ksn = "FFFF9876543210E00013"
114
+ dek = derive_DEK('6ac292faa1315b4d858ab3a3d7d5933a', ksn)
115
+ assert_equal '44893E3434ABDD6A817CE2841825E1FD', dek.upcase
116
+ end
117
+
93
118
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dukpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-03-18 00:00:00.000000000 Z
13
+ date: 2013-05-21 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: Implements a Derived Unique Key Per Transaction (DUKPT) decrypter
16
16
  email: