forked from jwt/ruby-jwe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
aes_cbc_hs.rb
89 lines (71 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
module JWE
module Enc
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
cipher.encrypt
cipher.key = enc_key
cipher.iv = iv
ciphertext = cipher.update(cleartext) + cipher.final
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)
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
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')
end
cipher.decrypt
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')
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 ||= 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}.")
end
def tag
@tag || ''
end
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def available?
new.cipher
true
rescue JWE::NotImplementedError
false
end
end
end
end
end