lib/jwe/enc/aes_cbc_hs.rb in jwe-0.3.0 vs lib/jwe/enc/aes_cbc_hs.rb in jwe-0.3.1

- old
+ new

@@ -1,7 +1,10 @@ +require 'jwe/enc/cipher' + module JWE module Enc + # Abstract AES in Block cipher mode, with message signature for different key sizes. module AesCbcHs attr_accessor :cek attr_accessor :iv attr_accessor :tag @@ -11,43 +14,47 @@ end def encrypt(cleartext, authenticated_data) raise JWE::BadCEK.new("The supplied key is invalid. Required length: #{key_length}") if cek.length != key_length - cipher.encrypt - cipher.key = enc_key - cipher.iv = iv + ciphertext = cipher_round(:encrypt, iv, cleartext) - ciphertext = cipher.update(cleartext) + cipher.final - length = [authenticated_data.length * 8].pack('Q>') # 64bit big endian + signature = generate_tag(authenticated_data, iv, ciphertext) + self.tag = signature - to_sign = authenticated_data + iv + ciphertext + length - signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new(hash_name), mac_key, to_sign) - self.tag = signature[0...mac_key.length] - ciphertext end def decrypt(ciphertext, authenticated_data) - raise JWE::BadCEK.new("The supplied key is invalid. Required length: #{key_length}") if cek.length != key_length + raise JWE::BadCEK, "The supplied key is invalid. Required length: #{key_length}" if cek.length != key_length - length = [authenticated_data.length * 8].pack('Q>') # 64bit big endian - to_sign = authenticated_data + iv + ciphertext + length - signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new(hash_name), mac_key, to_sign) - if signature[0...mac_key.length] != tag - raise JWE::InvalidData.new('Authentication tag verification failed') + signature = generate_tag(authenticated_data, iv, ciphertext) + if signature != tag + raise JWE::InvalidData, 'Authentication tag verification failed' end - cipher.decrypt + cipher_round(:decrypt, iv, ciphertext) + rescue OpenSSL::Cipher::CipherError + raise JWE::InvalidData, 'Invalid ciphertext or authentication tag' + end + + def cipher_round(direction, iv, data) + cipher.send(direction) cipher.key = enc_key cipher.iv = iv - cipher.update(ciphertext) + cipher.final - rescue OpenSSL::Cipher::CipherError - raise JWE::InvalidData.new('Invalid ciphertext or authentication tag') + cipher.update(data) + cipher.final end + def generate_tag(authenticated_data, iv, ciphertext) + length = [authenticated_data.length * 8].pack('Q>') # 64bit big endian + to_sign = authenticated_data + iv + ciphertext + length + signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new(hash_name), mac_key, to_sign) + + signature[0...mac_key.length] + end + def iv @iv ||= SecureRandom.random_bytes(16) end def cek @@ -61,22 +68,21 @@ def enc_key cek[key_length / 2..-1] 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 ||= Cipher.for(cipher_name) end def tag @tag || '' end def self.included(base) base.extend(ClassMethods) end + # Provides availability checks for Key Encryption algorithms module ClassMethods def available? new.cipher true rescue JWE::NotImplementedError