Module: Crypto
Overview
This class contains the implementation of cryptographics utilities and functions.
All keys used to communicate with the tag (Triple DES keys) are always represented as 16 bytes long byte-strings. Byte-strings is what the OpenSSL library use, so it makes sense to use that. But often, it is useful to manipulate those as array of bytes. The functions String.unpack() and Array.pack() ((un)pack into(from) a byte string) are very useful for this, especially with ‘C*’ as argument .
The card is only able to perform TDES encryption (not decryption) with 2 keys (keying option 2, aka 2TDEA). When the two keys are identical, the result is the same as plain DES. We elected to use only TDES on the user end (which is presumably also the case on the tag’s end). Therefore all TDES and DES keys are 16-byte key. A 16 byte DES key is formed by concatening the 8 byte DES key to itself.
AES encryption is used to encrypt the credentials stored on the tag.
OpenSSL modes:
DES-EDE-CBC = Triple DES (Encrypt Decrypt Encrypt) in the regular CBC mode (send for encryption, receive for decryption). The IV is zeroed by default.
AES-256-CBC = AES with 256 bit keys in the regular CBC mode. The IV is zeroed by default.
Constant Summary collapse
- DES_BLEN =
Block length for DES.
8- ZERO_IV =
An IV of
DES_BLENzeroes. Array.new(DES_BLEN, 0).pack("C*")
- HMAC_LEN =
Size of the HMAC (SHA-256) digest.
32
Instance Method Summary collapse
-
#aes_decipher(bytes, key) ⇒ Object
Deciphers using AES with 256 bits key in CBC (receive) mode.
-
#aes_encipher(bytes, key) ⇒ Object
Enciphers using AES with 256 bits key in CBC (send) mode.
-
#decipher_receive(bytes, key) ⇒ Object
Deciphers a byte string using 3DES in CBC receive mode.
-
#decipher_send(bytes, key) ⇒ Object
Deciphers a byte string using 3DES in CBC send mode.
-
#derive_desfire_key(pass, salt) ⇒ Object
Derive a DESFire Triple DES key.
-
#derive_key(pass, salt, key_size) ⇒ Object
Derive a key of the given size using the PBKDF2 derivation function, from given pass and salt.
-
#encipher_receive(bytes, key) ⇒ Object
Enciphers a byte string using 3DES in CBC receive mode.
-
#encipher_send(bytes, key) ⇒ Object
Enciphers a byte string using 3DES in CBC send mode.
-
#hmac(bytes, key) ⇒ Object
Computes a HMAC digest on given data using given key.
Methods included from BytesManipulation
#bs_rotate, #bs_xor, #desfire_crc, #to_hex_array, #to_le_array
Instance Method Details
#aes_decipher(bytes, key) ⇒ Object
Deciphers using AES with 256 bits key in CBC (receive) mode.
131 132 133 134 135 136 137 |
# File 'lib/crypto.rb', line 131 def aes_decipher(bytes, key) aes = OpenSSL::Cipher.new('AES-256-CBC') aes.decrypt aes.key = key aes.padding = 0 # no padding return aes.update(bytes) + aes.final end |
#aes_encipher(bytes, key) ⇒ Object
Enciphers using AES with 256 bits key in CBC (send) mode.
122 123 124 125 126 127 128 |
# File 'lib/crypto.rb', line 122 def aes_encipher(bytes, key) aes = OpenSSL::Cipher.new('AES-256-CBC') aes.encrypt aes.key = key aes.padding = 0 # no padding return aes.update(bytes) + aes.final end |
#decipher_receive(bytes, key) ⇒ Object
Deciphers a byte string using 3DES in CBC receive mode. Returns a byte string.
45 46 47 48 49 50 51 |
# File 'lib/crypto.rb', line 45 def decipher_receive(bytes, key) des = OpenSSL::Cipher.new('DES-EDE-CBC') des.decrypt des.key = key des.padding = 0 # no padding return des.update(bytes) + des.final end |
#decipher_send(bytes, key) ⇒ Object
Deciphers a byte string using 3DES in CBC send mode.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/crypto.rb', line 54 def decipher_send(bytes, key) mix = ZERO_IV partial_block_count = (bytes.bytesize % DES_BLEN > 0) ? 1 : 0 nblocks = bytes.length / DES_BLEN + partial_block_count out = Array.new(nblocks) out.each_index do |i| block = bytes.byteslice(i*DES_BLEN, DES_BLEN) block = bs_xor(block, mix) block = decipher_receive(block, key) out[i] = block mix = block end return out.reduce("", :+) end |
#derive_desfire_key(pass, salt) ⇒ Object
Derive a DESFire Triple DES key. This is similar to derive_key() with key_size = 16, excepted that we overwrite the parity bits of the key. Anyone can ask to get those bits from the tag (“key versionning” feature). As such, keeping the orignal parity bits makes the key less secure.
114 115 116 117 118 119 |
# File 'lib/crypto.rb', line 114 def derive_desfire_key(pass, salt) key = derive_key(pass, salt, 16) array = key.unpack('C*') array.map! { |e| e & 0xFE } return array.pack('C*') end |
#derive_key(pass, salt, key_size) ⇒ Object
Derive a key of the given size using the PBKDF2 derivation function, from given pass and salt. 50k iterations of SHA-256 are used.
103 104 105 106 107 |
# File 'lib/crypto.rb', line 103 def derive_key(pass, salt, key_size) sha256_d = OpenSSL::Digest::SHA256.new # (pass, salt, iter, keylength, digest) return OpenSSL::PKCS5::pbkdf2_hmac(pass, salt, 50000, key_size, sha256_d) end |
#encipher_receive(bytes, key) ⇒ Object
Enciphers a byte string using 3DES in CBC receive mode. Returns a byte string. Unused.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/crypto.rb', line 85 def encipher_receive(bytes, key) mix = ZERO_IV partial_block_count = (bytes.bytesize % DES_BLEN > 0) ? 1 : 0 nblocks = bytes.length / DES_BLEN + partial_block_count out = Array.new(nblocks) out.each_index do |i| block1 = bytes.byteslice(i*DES_BLEN, DES_BLEN) block = encipher_send(block1, key) block = bs_xor(block, mix) out[i] = block mix = block1 end return out.reduce("", :+) end |
#encipher_send(bytes, key) ⇒ Object
Enciphers a byte string using 3DES in CBC send mode. Returns a byte string. Unused.
75 76 77 78 79 80 81 |
# File 'lib/crypto.rb', line 75 def encipher_send(bytes, key) des = OpenSSL::Cipher.new('DES-EDE-CBC') des.encrypt des.key = key des.padding = 0 # no padding return des.update(bytes) + des.final end |
#hmac(bytes, key) ⇒ Object
Computes a HMAC digest on given data using given key. Uses SHA-1 as digest.
141 142 143 144 |
# File 'lib/crypto.rb', line 141 def hmac(bytes, key) sha256_d = sha256_d = OpenSSL::Digest::SHA256.new return OpenSSL::HMAC.digest(sha256_d, key, bytes) end |