Ensure that db encryption keys have proper bytesize
This commit is contained in:
parent
8a235c0c05
commit
777b6713bb
|
@ -95,6 +95,14 @@ class Settings < Settingslogic
|
|||
Gitlab::Application.secrets.db_key_base[0..31]
|
||||
end
|
||||
|
||||
def attr_encrypted_db_key_base_32
|
||||
Gitlab::Utils.ensure_utf8_size(attr_encrypted_db_key_base, bytes: 32.bytes)
|
||||
end
|
||||
|
||||
def attr_encrypted_db_key_base_12
|
||||
Gitlab::Utils.ensure_utf8_size(attr_encrypted_db_key_base, bytes: 12.bytes)
|
||||
end
|
||||
|
||||
# This should be used for :per_attribute_salt_and_iv mode. There is no
|
||||
# need to truncate the key because the encryptor will use the salt to
|
||||
# generate a hash of the password:
|
||||
|
|
|
@ -6,8 +6,8 @@ module Gitlab
|
|||
|
||||
AES256_GCM_OPTIONS = {
|
||||
algorithm: 'aes-256-gcm',
|
||||
key: Settings.attr_encrypted_db_key_base_truncated,
|
||||
iv: Settings.attr_encrypted_db_key_base_truncated[0..11]
|
||||
key: Settings.attr_encrypted_db_key_base_32,
|
||||
iv: Settings.attr_encrypted_db_key_base_12
|
||||
}.freeze
|
||||
|
||||
def sha256(value)
|
||||
|
|
|
@ -16,6 +16,20 @@ module Gitlab
|
|||
str.force_encoding(Encoding::UTF_8)
|
||||
end
|
||||
|
||||
def ensure_utf8_size(str, bytes:)
|
||||
raise ArgumentError if str.empty? || bytes.negative?
|
||||
|
||||
truncated = str.each_char.each_with_object(String.new) do |char, object|
|
||||
if object.bytesize + char.bytesize > bytes
|
||||
break object
|
||||
else
|
||||
object.concat(char)
|
||||
end
|
||||
end
|
||||
|
||||
truncated + ("\0" * (bytes - truncated.bytesize))
|
||||
end
|
||||
|
||||
# Append path to host, making sure there's one single / in between
|
||||
def append_path(host, path)
|
||||
"#{host.to_s.sub(%r{\/+$}, '')}/#{path.to_s.sub(%r{^\/+}, '')}"
|
||||
|
|
|
@ -6,4 +6,102 @@ describe Settings do
|
|||
expect(described_class.omniauth.enabled).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '.attr_encrypted_db_key_base_truncated' do
|
||||
it 'is a string with maximum 32 bytes size' do
|
||||
expect(described_class.attr_encrypted_db_key_base_truncated.bytesize)
|
||||
.to be <= 32
|
||||
end
|
||||
end
|
||||
|
||||
describe '.attr_encrypted_db_key_base_12' do
|
||||
context 'when db key base secret is less than 12 bytes' do
|
||||
before do
|
||||
allow(described_class)
|
||||
.to receive(:attr_encrypted_db_key_base)
|
||||
.and_return('a' * 10)
|
||||
end
|
||||
|
||||
it 'expands db key base secret to 12 bytes' do
|
||||
expect(described_class.attr_encrypted_db_key_base_12)
|
||||
.to eq ('a' * 10) + ("\0" * 2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when key has multiple multi-byte UTF chars exceeding 12 bytes' do
|
||||
before do
|
||||
allow(described_class)
|
||||
.to receive(:attr_encrypted_db_key_base)
|
||||
.and_return('❤' * 18)
|
||||
end
|
||||
|
||||
it 'does not use more than 32 bytes' do
|
||||
db_key_base = described_class.attr_encrypted_db_key_base_12
|
||||
|
||||
expect(db_key_base).to eq ('❤' * 4)
|
||||
expect(db_key_base.bytesize).to eq 12
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.attr_encrypted_db_key_base_32' do
|
||||
context 'when db key base secret is less than 32 bytes' do
|
||||
before do
|
||||
allow(described_class)
|
||||
.to receive(:attr_encrypted_db_key_base)
|
||||
.and_return('a' * 10)
|
||||
end
|
||||
|
||||
it 'expands db key base secret to 32 bytes' do
|
||||
expanded_key_base = ('a' * 10) + ("\0" * 22)
|
||||
|
||||
expect(expanded_key_base.bytesize).to eq 32
|
||||
expect(described_class.attr_encrypted_db_key_base_32)
|
||||
.to eq expanded_key_base
|
||||
end
|
||||
end
|
||||
|
||||
context 'when db key base secret is 32 bytes' do
|
||||
before do
|
||||
allow(described_class)
|
||||
.to receive(:attr_encrypted_db_key_base)
|
||||
.and_return('a' * 32)
|
||||
end
|
||||
|
||||
it 'returns original value' do
|
||||
expect(described_class.attr_encrypted_db_key_base_32)
|
||||
.to eq 'a' * 32
|
||||
end
|
||||
end
|
||||
|
||||
context 'when db key base contains multi-byte UTF character' do
|
||||
before do
|
||||
allow(described_class)
|
||||
.to receive(:attr_encrypted_db_key_base)
|
||||
.and_return('❤' * 6)
|
||||
end
|
||||
|
||||
it 'does not use more than 32 bytes' do
|
||||
db_key_base = described_class.attr_encrypted_db_key_base_32
|
||||
|
||||
expect(db_key_base).to eq '❤❤❤❤❤❤' + ("\0" * 14)
|
||||
expect(db_key_base.bytesize).to eq 32
|
||||
end
|
||||
end
|
||||
|
||||
context 'when db key base multi-byte UTF chars exceeding 32 bytes' do
|
||||
before do
|
||||
allow(described_class)
|
||||
.to receive(:attr_encrypted_db_key_base)
|
||||
.and_return('❤' * 18)
|
||||
end
|
||||
|
||||
it 'does not use more than 32 bytes' do
|
||||
db_key_base = described_class.attr_encrypted_db_key_base_32
|
||||
|
||||
expect(db_key_base).to eq ('❤' * 10) + ("\0" * 2)
|
||||
expect(db_key_base.bytesize).to eq 32
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -127,4 +127,42 @@ describe Gitlab::Utils do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.ensure_utf8_size' do
|
||||
context 'string is has less bytes than expected' do
|
||||
it 'backfills string with null characters' do
|
||||
transformed = described_class.ensure_utf8_size('a' * 10, bytes: 32)
|
||||
|
||||
expect(transformed.bytesize).to eq 32
|
||||
expect(transformed).to eq ('a' * 10) + ("\0" * 22)
|
||||
end
|
||||
end
|
||||
|
||||
context 'string size is exactly the one that is expected' do
|
||||
it 'returns original value' do
|
||||
transformed = described_class.ensure_utf8_size('a' * 32, bytes: 32)
|
||||
|
||||
expect(transformed).to eq 'a' * 32
|
||||
expect(transformed.bytesize).to eq 32
|
||||
end
|
||||
end
|
||||
|
||||
context 'when string contains a few multi-byte UTF characters' do
|
||||
it 'backfills string with null characters' do
|
||||
transformed = described_class.ensure_utf8_size('❤' * 6, bytes: 32)
|
||||
|
||||
expect(transformed).to eq '❤❤❤❤❤❤' + ("\0" * 14)
|
||||
expect(transformed.bytesize).to eq 32
|
||||
end
|
||||
end
|
||||
|
||||
context 'when string has multiple multi-byte UTF chars exceeding 32 bytes' do
|
||||
it 'truncates string to 32 characters and backfills it if needed' do
|
||||
transformed = described_class.ensure_utf8_size('❤' * 18, bytes: 32)
|
||||
|
||||
expect(transformed).to eq ('❤' * 10) + ("\0" * 2)
|
||||
expect(transformed.bytesize).to eq 32
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue