Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions app/services/encrypted_key_maker.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
class EncryptedKeyMaker
include Pii::Encodable

KEY_TYPE = {
KMS: 'KMSx',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there's only one value in KEY_TYPE for now, should we just make this KMS_KEY_TYPE = 'KMSx'? Or is this part of the plan to refactor EncryptedKeyMaker to allow locally-encrypted keys too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the latter. we wanted to provide structure for future refactor.

}.freeze

# Creates an encrypted encryption key.
#
# @param user_access_key [UserAccessKey]
Expand Down Expand Up @@ -34,7 +38,7 @@ def unlock(user_access_key, encryption_key)
private

def unlock_key(user_access_key, encryption_key)
if FeatureManagement.use_kms?
if FeatureManagement.use_kms? && looks_like_kms?(user_access_key, encryption_key)
unlock_kms(user_access_key, encryption_key)
else
unlock_local(user_access_key, encryption_key)
Expand All @@ -51,7 +55,7 @@ def make_kms(user_access_key)
key_id: Figaro.env.aws_kms_key_id,
plaintext: user_access_key.random_r
).ciphertext_blob
build_user_access_key(user_access_key, encrypted_key)
build_user_access_key(user_access_key, KEY_TYPE[:KMS] + encrypted_key)
end

def unlock_kms(user_access_key, encryption_key)
Expand All @@ -72,6 +76,10 @@ def unlock_local(user_access_key, encryption_key)
user_access_key.unlock(encryptor.decrypt(ciphertext, Figaro.env.password_pepper))
end

def looks_like_kms?(user_access_key, encryption_key)
user_access_key.xor(decode(encryption_key)).start_with?(KEY_TYPE[:KMS])
end

def aws_client
@_aws_client ||= Aws::KMS::Client.new(region: Figaro.env.aws_region)
end
Expand Down
9 changes: 9 additions & 0 deletions spec/services/encrypted_key_maker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@

expect(subject.unlock(user_access_key, encryption_key)).to eq hash_E
end

it 'recognizes locally-encrypted legacy keys' do
allow(FeatureManagement).to receive(:use_kms?).and_return(false)
subject.make(user_access_key)
encryption_key = user_access_key.encryption_key

allow(FeatureManagement).to receive(:use_kms?).and_return(true)
expect(subject.unlock(user_access_key, encryption_key)).to eq hash_E
end
end
end
end
8 changes: 5 additions & 3 deletions spec/services/pii/nist_encryption_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
allow(FeatureManagement).to receive(:use_kms?).and_return(true)
end

let(:kms_prefix) { '{}cH' } # XOR of 'KMSx' and '0000'

describe 'password hashing' do
it 'creates two substrings Z1 and Z2 via scrypt of password and salt' do
# Generate and store a 128-bit salt S.
Expand Down Expand Up @@ -61,7 +63,7 @@
encrypted_key_maker = EncryptedKeyMaker.new
encrypted_key_maker.make(user_access_key)

expect(user_access_key.encrypted_d).to eq encrypted_D
expect(user_access_key.encrypted_d).to eq(kms_prefix + encrypted_D)
expect(user_access_key.hash_e).to eq hash_E
end
end
Expand Down Expand Up @@ -92,7 +94,7 @@

encrypted_D = Base64.strict_decode64(user.encryption_key)

expect(user.user_access_key.xor(ciphered_R)).to eq encrypted_D
expect(kms_prefix + user.user_access_key.xor(ciphered_R)).to eq(encrypted_D)
end
end

Expand Down Expand Up @@ -121,7 +123,7 @@
# encrypted_payload is an envelope that contains C and D.
encrypted_key, encrypted_C = open_envelope(encrypted_payload)

expect(Base64.strict_decode64(encrypted_key)).to eq encrypted_D
expect(Base64.strict_decode64(encrypted_key)).to eq(kms_prefix + encrypted_D)

# unroll encrypted_C to verify it was encrypted with hash_E
cipher = Pii::Cipher.new
Expand Down