lib/jwe/alg/aes_kw.rb in jwe-0.3.0 vs lib/jwe/alg/aes_kw.rb in jwe-0.3.1

- old
+ new

@@ -1,7 +1,10 @@ +require 'jwe/enc/cipher' + module JWE module Alg + # Generic AES Key Wrapping algorithm for any key size. module AesKw attr_accessor :key attr_accessor :iv def initialize(key = nil, iv = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6") @@ -9,55 +12,63 @@ self.key = key.force_encoding('ASCII-8BIT') end def encrypt(cek) a = iv - r = cek.scan(/.{8}/m) + r = cek.force_encoding('ASCII-8BIT').scan(/.{8}/m) 6.times do |j| - r.length.times do |i| - b = encrypt_round(a + r[i]) - - a = b.chars.first(8).join - r[i] = b.chars.last(8).join - - t = (r.length * j) + i + 1 - a = xor(a, t) - end + a, r = kw_encrypt_round(j, a, r) end ([a] + r).join end - def decrypt(encrypted_cek) - c = encrypted_cek.scan(/.{8}/m) - a = c[0] + def kw_encrypt_round(j, a, r) + r.length.times do |i| + b = encrypt_round(a + r[i]).chars - r = c[1..c.length] + a, r[i] = a_ri(b) - 5.downto(0) do |j| - r.length.downto(1) do |i| - t = (r.length * j) + i - a = xor(a, t) + a = xor(a, (r.length * j) + i + 1) + end - b = decrypt_round(a + r[i - 1]) + [a, r] + end - a = b.chars.first(8).join - r[i - 1] = b.chars.last(8).join - end + def decrypt(encrypted_cek) + c = encrypted_cek.force_encoding('ASCII-8BIT').scan(/.{8}/m) + a, *r = c + + 5.downto(0) do |j| + a, r = kw_decrypt_round(j, a, r) end if a != iv raise StandardError.new('The encrypted key has been tampered. Do not use this key.') end r.join end + def kw_decrypt_round(j, a, r) + r.length.downto(1) do |i| + a = xor(a, (r.length * j) + i) + + b = decrypt_round(a + r[i - 1]).chars + + a, r[i - 1] = a_ri(b) + end + + [a, r] + end + + def a_ri(b) + [b.first(8).join, b.last(8).join] + end + def cipher - @cipher ||= OpenSSL::Cipher.new(cipher_name) - rescue RuntimeError - raise JWE::NotImplementedError.new("The version of OpenSSL linked to your Ruby does not support the cipher #{cipher_name}.") + @cipher ||= Enc::Cipher.for(cipher_name) end def encrypt_round(data) cipher.encrypt cipher.key = key