Class: CMAC
- Inherits:
-
Object
- Object
- CMAC
- Defined in:
- lib/cmac.rb,
lib/cmac/version.rb,
lib/cmac/exception.rb
Defined Under Namespace
Classes: Exception
Constant Summary collapse
- ZeroBlock =
"\0" * 16
- ConstantBlock =
("\0" * 15) + "\x87"
- VERSION =
'0.1.0'
Instance Method Summary collapse
- #_derive_key(key) ⇒ Object
- #_encrypt_block(key, block) ⇒ Object
- #_generate_subkeys(key) ⇒ Object
- #_leftshift(input) ⇒ Object
- #_needs_padding?(message) ⇒ Boolean
- #_next_key(key) ⇒ Object
- #_pad_message(message) ⇒ Object
- #_secure_compare?(a, b) ⇒ Boolean
- #_xor(a, b) ⇒ Object
-
#initialize(key) ⇒ CMAC
constructor
A new instance of CMAC.
- #sign(message, truncate = 16) ⇒ Object (also: #encrypt)
- #valid_message?(tag, message) ⇒ Boolean
Constructor Details
#initialize(key) ⇒ CMAC
Returns a new instance of CMAC.
10 11 12 13 14 |
# File 'lib/cmac.rb', line 10 def initialize(key) key.force_encoding('BINARY') if key.respond_to?(:force_encoding) @key = _derive_key(key) @key1, @key2 = _generate_subkeys(@key) end |
Instance Method Details
#_derive_key(key) ⇒ Object
50 51 52 53 54 55 56 57 |
# File 'lib/cmac.rb', line 50 def _derive_key(key) if key.length == 16 key else cmac = CMAC.new(ZeroBlock) cmac.encrypt(key) end end |
#_encrypt_block(key, block) ⇒ Object
59 60 61 62 63 64 65 |
# File 'lib/cmac.rb', line 59 def _encrypt_block(key, block) cipher = OpenSSL::Cipher.new('AES-128-ECB') cipher.encrypt cipher.padding = 0 cipher.key = key cipher.update(block) + cipher.final end |
#_generate_subkeys(key) ⇒ Object
67 68 69 70 71 72 |
# File 'lib/cmac.rb', line 67 def _generate_subkeys(key) key0 = _encrypt_block(key, ZeroBlock) key1 = _next_key(key0) key2 = _next_key(key1) [key1, key2] end |
#_leftshift(input) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/cmac.rb', line 86 def _leftshift(input) overflow = 0 words = input.unpack('N4').reverse words = words.map do |word| new_word = (word << 1) & 0xFFFFFFFF new_word |= overflow overflow = (word & 0x80000000) >= 0x80000000 ? 1 : 0 new_word end words.reverse.pack('N4') end |
#_needs_padding?(message) ⇒ Boolean
74 75 76 |
# File 'lib/cmac.rb', line 74 def _needs_padding?() .length == 0 || .length % 16 != 0 end |
#_next_key(key) ⇒ Object
78 79 80 81 82 83 84 |
# File 'lib/cmac.rb', line 78 def _next_key(key) if key[0].ord < 0x80 _leftshift(key) else _xor(_leftshift(key), ConstantBlock) end end |
#_pad_message(message) ⇒ Object
98 99 100 101 102 |
# File 'lib/cmac.rb', line 98 def () padded_length = .length + 16 - (.length % 16) = + "\x80" .ljust(padded_length, "\0") end |
#_secure_compare?(a, b) ⇒ Boolean
104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/cmac.rb', line 104 def _secure_compare?(a, b) return false unless a.bytesize == b.bytesize bytes = a.unpack("C#{a.bytesize}") result = 0 b.each_byte do |byte| result |= byte ^ bytes.shift end result == 0 end |
#_xor(a, b) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/cmac.rb', line 116 def _xor(a, b) a.force_encoding('BINARY') if a.respond_to?(:force_encoding) b.force_encoding('BINARY') if b.respond_to?(:force_encoding) output = '' length = [a.length, b.length].min length.times do |i| output << (a[i].ord ^ b[i].ord).chr end output end |
#sign(message, truncate = 16) ⇒ Object Also known as: encrypt
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/cmac.rb', line 16 def sign(, truncate = 16) raise CMAC::Exception.new('Tag cannot be greater than maximum (16 bytes)') if truncate > 16 raise CMAC::Exception.new('Tag cannot be less than minimum (8 bytes)') if truncate < 8 .force_encoding('BINARY') if .respond_to?(:force_encoding) if _needs_padding?() = () final_block = @key2 else final_block = @key1 end last_ciphertext = ZeroBlock count = .length / 16 range = Range.new(0, count - 1) blocks = range.map { |i| .slice(16 * i, 16) } blocks.each_with_index do |block, i| if i == range.last block = _xor(final_block, block) end block = _xor(block, last_ciphertext) last_ciphertext = _encrypt_block(@key, block) end last_ciphertext.slice(0, truncate) end |
#valid_message?(tag, message) ⇒ Boolean
45 46 47 48 |
# File 'lib/cmac.rb', line 45 def (tag, ) other_tag = sign() _secure_compare?(tag, other_tag) end |