-
Notifications
You must be signed in to change notification settings - Fork 14
/
aes_cbc_hs.rb
95 lines (74 loc) · 2.4 KB
/
aes_cbc_hs.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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
def initialize(cek = nil, iv = nil)
self.iv = iv
self.cek = cek
end
def encrypt(cleartext, authenticated_data)
raise JWE::BadCEK.new("The supplied key is invalid. Required length: #{key_length}") if cek.length != key_length
ciphertext = cipher_round(:encrypt, iv, cleartext)
signature = generate_tag(authenticated_data, iv, ciphertext)
self.tag = signature
ciphertext
end
def decrypt(ciphertext, authenticated_data)
raise JWE::BadCEK, "The supplied key is invalid. Required length: #{key_length}" if cek.length != key_length
signature = generate_tag(authenticated_data, iv, ciphertext)
if signature != tag
raise JWE::InvalidData, 'Authentication tag verification failed'
end
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(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
@cek ||= SecureRandom.random_bytes(key_length)
end
def mac_key
cek[0...key_length / 2]
end
def enc_key
cek[key_length / 2..-1]
end
def cipher
@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
false
end
end
end
end
end