Skip to content

Commit 688b7bb

Browse files
authored
Merge pull request #109 from btoews/po-fix
Remove padding oracle
2 parents 3c163df + 5fd19cb commit 688b7bb

File tree

2 files changed

+63
-15
lines changed

2 files changed

+63
-15
lines changed

lib/json/jwe.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,12 @@ def decrypt!(private_key_or_secret, algorithms = nil, encryption_methods = nil)
4343
raise UnexpectedAlgorithm.new('Unexpected alg header') unless algorithms.blank? || Array(algorithms).include?(alg)
4444
raise UnexpectedAlgorithm.new('Unexpected enc header') unless encryption_methods.blank? || Array(encryption_methods).include?(enc)
4545
self.private_key_or_secret = with_jwk_support private_key_or_secret
46-
cipher.decrypt
4746
self.content_encryption_key = decrypt_content_encryption_key
4847
self.mac_key, self.encryption_key = derive_encryption_and_mac_keys
48+
49+
verify_cbc_authentication_tag! if cbc?
50+
51+
cipher.decrypt
4952
cipher.key = encryption_key
5053
cipher.iv = iv # NOTE: 'iv' has to be set after 'key' for GCM
5154
if gcm?
@@ -54,8 +57,15 @@ def decrypt!(private_key_or_secret, algorithms = nil, encryption_methods = nil)
5457
cipher.auth_tag = authentication_tag
5558
cipher.auth_data = auth_data
5659
end
57-
self.plain_text = cipher.update(cipher_text) + cipher.final
58-
verify_cbc_authentication_tag! if cbc?
60+
61+
begin
62+
self.plain_text = cipher.update(cipher_text) + cipher.final
63+
rescue OpenSSL::OpenSSLError
64+
# Ensure that the same error is raised for invalid PKCS7 padding
65+
# as for invalid signatures. This prevents padding-oracle attacks.
66+
raise DecryptionFailed
67+
end
68+
5969
self
6070
end
6171

@@ -244,7 +254,7 @@ def verify_cbc_authentication_tag!
244254
sha_digest, mac_key, secured_input
245255
)[0, sha_size / 2 / 8]
246256
unless secure_compare(authentication_tag, expected_authentication_tag)
247-
raise DecryptionFailed.new('Invalid authentication tag')
257+
raise DecryptionFailed
248258
end
249259
end
250260

spec/json/jwe_spec.rb

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,60 @@
9191
end
9292

9393
shared_examples_for :verify_cbc_authentication_tag do
94-
let(:jwe_string) do
94+
let(:jwe_parts) do
9595
_jwe_ = JSON::JWE.new plain_text
9696
_jwe_.alg, _jwe_.enc = alg, enc
9797
_jwe_.encrypt! key
98-
hdr, extra, iv, cipher_text, _ = _jwe_.to_s.split '.'
99-
[hdr, extra, iv, cipher_text, ''].join '.'
98+
_jwe_.to_s.split '.'
10099
end
101100

102-
it do
103-
# fetching those variables outside of exception block to make sure
104-
# we intercept exception in decrypt! and not in other place
105-
j = jwe
106-
k = key
107-
expect do
108-
j.decrypt! k
109-
end.to raise_error JSON::JWE::DecryptionFailed
101+
let(:hdr) { jwe_parts[0] }
102+
let(:extra) { jwe_parts[1] }
103+
let(:iv) { jwe_parts[2] }
104+
let(:cipher_text) { jwe_parts[3] }
105+
let(:signature) { jwe_parts[4] }
106+
107+
let(:jwe_string) { [hdr, extra, iv, cipher_text, signature].join '.' }
108+
109+
shared_examples_for :signature_verification_failure do
110+
it do
111+
# fetching those variables outside of exception block to make sure
112+
# we intercept exception in decrypt! and not in other place
113+
j = jwe
114+
k = key
115+
expect do
116+
j.decrypt! k
117+
end.to raise_error JSON::JWE::DecryptionFailed
118+
end
119+
end
120+
121+
describe "with missing signature" do
122+
let(:signature) { "" }
123+
it_behaves_like :signature_verification_failure
124+
end
125+
126+
describe "with good pkcs7 padding and bad signature" do
127+
let(:iv) do
128+
good_padding_byte = 16 - (plain_text.bytesize % 16)
129+
bad_padding_byte = 1
130+
iv_bytes = Base64.urlsafe_decode64(super()).bytes
131+
iv_bytes[-1] = iv_bytes[-1] ^ good_padding_byte ^ bad_padding_byte
132+
Base64.urlsafe_encode64(iv_bytes.pack("C*"), padding: false)
133+
end
134+
135+
it_behaves_like :signature_verification_failure
136+
end
137+
138+
describe "with bad pkcs7 padding and bad signature" do
139+
let(:iv) do
140+
good_padding_byte = 16 - (plain_text.bytesize % 16)
141+
bad_padding_byte = good_padding_byte - 1
142+
iv_bytes = Base64.urlsafe_decode64(super()).bytes
143+
iv_bytes[-1] = iv_bytes[-1] ^ good_padding_byte ^ bad_padding_byte
144+
Base64.urlsafe_encode64(iv_bytes.pack("C*"), padding: false)
145+
end
146+
147+
it_behaves_like :signature_verification_failure
110148
end
111149
end
112150

0 commit comments

Comments
 (0)