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 96 97 98 99
|
From 3393f394f271c87bd42ec23c300727b4437d1638 Mon Sep 17 00:00:00 2001
From: Bart de Water <bartdewater@gmail.com>
Date: Mon, 30 Apr 2018 10:57:16 -0400
Subject: [PATCH] Verify the GCM auth tag length
As described in https://github.com/ruby/openssl/issues/63 without this check, only a single byte needs to be supplied to make the authentication pass. This means that an attacker needs at most 256 attempts in order to forge a valid authentication tag.
The JWE spec example prescribes 128 bits (16 bytes) for the tag: https://tools.ietf.org/html/rfc7516#section-3.3
---
lib/json/jwe.rb | 2 ++
spec/json/jwe_spec.rb | 24 ++++++++++++++++++++++++
2 files changed, 26 insertions(+)
--- a/lib/json/jwe.rb
+++ b/lib/json/jwe.rb
@@ -259,6 +259,8 @@
cipher.key = encryption_key
cipher.iv = iv # NOTE: 'iv' has to be set after 'key' for GCM
if gcm?
+ # https://github.com/ruby/openssl/issues/63
+ raise DecryptionFailed.new('Invalid authentication tag') if authentication_tag.length < 16
cipher.auth_tag = authentication_tag
cipher.auth_data = auth_data
end
--- a/spec/json/jwe_spec.rb
+++ b/spec/json/jwe_spec.rb
@@ -162,6 +162,24 @@
end
end
+ shared_examples_for :verify_gcm_authentication_tag do
+ let(:jwe_string) do
+ _jwe_ = JSON::JWE.new plain_text
+ _jwe_.alg, _jwe_.enc = alg, enc
+ _jwe_.encrypt! key
+ header, key, iv, cipher_text, auth_tag = _jwe_.to_s.split('.')
+ truncated_auth_tag = Base64.urlsafe_decode64(auth_tag).slice(0..-2)
+ truncated_auth_tag = Base64.urlsafe_encode64(truncated_auth_tag, padding: false)
+ [header, key, iv, cipher_text, truncated_auth_tag].join('.')
+ end
+
+ it do
+ expect do
+ jwe.decrypt! key
+ end.to raise_error JSON::JWE::DecryptionFailed
+ end
+ end
+
shared_examples_for :unexpected_algorithm_for_decryption do
it do
expect do
@@ -186,6 +204,7 @@
let(:enc) { :A128GCM }
if gcm_supported?
it_behaves_like :decryptable
+ it_behaves_like :verify_gcm_authentication_tag
else
it_behaves_like :gcm_decryption_unsupported
end
@@ -195,6 +214,7 @@
let(:enc) { :A256GCM }
if gcm_supported?
it_behaves_like :decryptable
+ it_behaves_like :verify_gcm_authentication_tag
else
it_behaves_like :gcm_decryption_unsupported
end
@@ -219,6 +239,7 @@
let(:enc) { :A128GCM }
if gcm_supported?
it_behaves_like :decryptable
+ it_behaves_like :verify_gcm_authentication_tag
else
it_behaves_like :gcm_decryption_unsupported
end
@@ -228,6 +249,7 @@
let(:enc) { :A256GCM }
if gcm_supported?
it_behaves_like :decryptable
+ it_behaves_like :verify_gcm_authentication_tag
else
it_behaves_like :gcm_decryption_unsupported
end
@@ -254,6 +276,7 @@
let(:enc) { :A128GCM }
if gcm_supported?
it_behaves_like :decryptable
+ it_behaves_like :verify_gcm_authentication_tag
else
it_behaves_like :gcm_decryption_unsupported
end
@@ -263,6 +286,7 @@
let(:enc) { :A256GCM }
if gcm_supported?
it_behaves_like :decryptable
+ it_behaves_like :verify_gcm_authentication_tag
else
it_behaves_like :gcm_decryption_unsupported
end
|