Class: MIFARE::Key

Inherits:
Object
  • Object
show all
Defined in:
lib/mifare/key.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, key, version = 0x00) ⇒ Key

Returns a new instance of Key.


8
9
10
11
12
13
# File 'lib/mifare/key.rb', line 8

def initialize(type, key, version = 0x00)
  @type = type
  set_key_data(type, key, version)
  clear_iv
  init_cipher
end

Instance Attribute Details

#cipher_suiteObject (readonly)

Returns the value of attribute cipher_suite.


4
5
6
# File 'lib/mifare/key.rb', line 4

def cipher_suite
  @cipher_suite
end

#key_sizeObject (readonly)

Returns the value of attribute key_size.


5
6
7
# File 'lib/mifare/key.rb', line 5

def key_size
  @key_size
end

#typeObject (readonly)

Returns the value of attribute type.


3
4
5
# File 'lib/mifare/key.rb', line 3

def type
  @type
end

#versionObject (readonly)

Returns the value of attribute version.


6
7
8
# File 'lib/mifare/key.rb', line 6

def version
  @version
end

Instance Method Details

#calculate_cmac(data) ⇒ Object


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/mifare/key.rb', line 81

def calculate_cmac(data)
  if @cmac_subkey1.nil? || @cmac_subkey2.nil?
    raise UsageError, 'Generate subkeys before calculating CMAC'
  end

  # Separate from input object
  data = data.dup

  if data.size == 0 || data.size % @block_size != 0
    # padding with byte: 0x80, 0x00, 0x00.....
    data << 0x80
    until data.size % @block_size == 0
      data << 0x00
    end

    key = @cmac_subkey2
  else
    key = @cmac_subkey1
  end

  # XOR last data block with selected CMAC subkey
  data = data[0...-@block_size] + data[-@block_size..-1].zip(key).map{|x, y| x ^ y }
  encrypt(data)

  @cipher_iv.bytes
end

#clear_ivObject


63
64
65
# File 'lib/mifare/key.rb', line 63

def clear_iv
  @cipher_iv = "\x00" * @block_size
end

#decrypt(data, cbc_mode: :receive, data_length: nil) ⇒ Object


42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/mifare/key.rb', line 42

def decrypt(data, cbc_mode: :receive, data_length: nil)
  @cipher.decrypt

  data = cbc_crypt(data, cbc_mode)
  if @padding_mode == 1
    data[0...data_length]
  elsif @padding_mode == 2
    str = data.pack('C*')
    str.sub! /#{0x80.chr}#{0x00.chr}*\z/, ''
    str.bytes
  else
    raise UsageError, 'Padding mode not set'
  end
end

#encrypt(data, cbc_mode: :send, data_length: nil) ⇒ Object


27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/mifare/key.rb', line 27

def encrypt(data, cbc_mode: :send, data_length: nil)
  @cipher.encrypt

  # Add padding if not a complete block
  if data.size % @block_size != 0
    raise UsageError, 'Padding mode not set' unless @padding_mode
    data << 0x80 if @padding_mode == 2
    until data.size % @block_size == 0
      data << 0x00
    end
  end
  
  cbc_crypt(data, cbc_mode)
end

#generate_cmac_subkeysObject


67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/mifare/key.rb', line 67

def generate_cmac_subkeys
  r = (@block_size == 8) ? 0x1B : 0x87
  data = Array.new(@block_size, 0)

  clear_iv
  data = encrypt(data, cbc_mode: :receive)

  @cmac_subkey1 = bit_shift_left(data)
  @cmac_subkey1[-1] ^= r if data[0] & 0x80 != 0

  @cmac_subkey2 = bit_shift_left(@cmac_subkey1)
  @cmac_subkey2[-1] ^= r if @cmac_subkey1[0] & 0x80 != 0
end

#keyObject


23
24
25
# File 'lib/mifare/key.rb', line 23

def key
  @key.bytes
end

#padding_mode(mode) ⇒ Object

Padding Method according to ISO-9797


16
17
18
19
20
21
# File 'lib/mifare/key.rb', line 16

def padding_mode(mode)
  if mode != 1 && mode != 2
    raise UsageError, 'Unknown padding mode'
  end
  @padding_mode = mode
end

#set_iv(iv) ⇒ Object

Raises:


57
58
59
60
61
# File 'lib/mifare/key.rb', line 57

def set_iv(iv)
  iv = iv.pack('C*')
  raise UsageError, 'Incorrect IV length' if iv.size != @block_size
  @cipher_iv = iv
end