diff --git a/app/jobs/address_proofing_job.rb b/app/jobs/address_proofing_job.rb index e5c730ca1c6..55539ea373b 100644 --- a/app/jobs/address_proofing_job.rb +++ b/app/jobs/address_proofing_job.rb @@ -11,7 +11,7 @@ def perform(user_id:, issuer:, result_id:, encrypted_arguments:, trace_id:) raise_stale_job! if stale_job?(enqueued_at) decrypted_args = JSON.parse( - Encryption::Encryptors::SessionEncryptor.new.decrypt(encrypted_arguments), + Encryption::Encryptors::BackgroundProofingArgEncryptor.new.decrypt(encrypted_arguments), symbolize_names: true, ) diff --git a/app/jobs/document_proofing_job.rb b/app/jobs/document_proofing_job.rb index 60bb9f3d02d..4bdbc3d6091 100644 --- a/app/jobs/document_proofing_job.rb +++ b/app/jobs/document_proofing_job.rb @@ -22,7 +22,7 @@ def perform( user = dcs.user decrypted_args = JSON.parse( - Encryption::Encryptors::SessionEncryptor.new.decrypt(encrypted_arguments), + Encryption::Encryptors::BackgroundProofingArgEncryptor.new.decrypt(encrypted_arguments), symbolize_names: true, ) document_args = decrypted_args[:document_arguments] diff --git a/app/jobs/resolution_proofing_job.rb b/app/jobs/resolution_proofing_job.rb index fb2209b1801..9a1b1a217d2 100644 --- a/app/jobs/resolution_proofing_job.rb +++ b/app/jobs/resolution_proofing_job.rb @@ -19,7 +19,7 @@ def perform(result_id:, encrypted_arguments:, trace_id:, should_proof_state_id:, raise_stale_job! if stale_job?(enqueued_at) decrypted_args = JSON.parse( - Encryption::Encryptors::SessionEncryptor.new.decrypt(encrypted_arguments), + Encryption::Encryptors::BackgroundProofingArgEncryptor.new.decrypt(encrypted_arguments), symbolize_names: true, ) diff --git a/app/models/gpo_confirmation.rb b/app/models/gpo_confirmation.rb index d3567c49d03..63dc8aa8105 100644 --- a/app/models/gpo_confirmation.rb +++ b/app/models/gpo_confirmation.rb @@ -14,9 +14,6 @@ def entry private def encryptor - # This currently uses the SessionEncryptor, which is meant to be used to - # encrypt the session. When this code is changed to integrate a new mail - # vendor we should create a purpose built encryptor for that vendor - Encryption::Encryptors::SessionEncryptor.new + Encryption::Encryptors::BackgroundProofingArgEncryptor.new end end diff --git a/app/services/encrypted_redis_struct_storage.rb b/app/services/encrypted_redis_struct_storage.rb index c98a2f53161..2a185910895 100644 --- a/app/services/encrypted_redis_struct_storage.rb +++ b/app/services/encrypted_redis_struct_storage.rb @@ -22,7 +22,7 @@ def load(id, type:) ciphertext = REDIS_POOL.with { |client| client.get(key(id, type: type)) } return nil if ciphertext.blank? - json = Encryption::Encryptors::SessionEncryptor.new.decrypt(ciphertext) + json = Encryption::Encryptors::BackgroundProofingArgEncryptor.new.decrypt(ciphertext) data = JSON.parse(json, symbolize_names: true) type.new.tap do |struct| struct.id = id @@ -52,7 +52,7 @@ def store(struct, expires_in: 60) payload.transform_values!(&utf_8_encode_strs) struct_key = key(struct.id, type: struct.class) - ciphertext = Encryption::Encryptors::SessionEncryptor.new.encrypt(payload.to_json) + ciphertext = Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt(payload.to_json) REDIS_POOL.with do |client| client.setex(struct_key, expires_in, ciphertext) diff --git a/app/services/encryption/encryptors/background_proofing_arg_encryptor.rb b/app/services/encryption/encryptors/background_proofing_arg_encryptor.rb new file mode 100644 index 00000000000..d7364cf0ddf --- /dev/null +++ b/app/services/encryption/encryptors/background_proofing_arg_encryptor.rb @@ -0,0 +1,34 @@ +module Encryption + module Encryptors + class BackgroundProofingArgEncryptor + include Encodable + include ::NewRelic::Agent::MethodTracer + + def encrypt(plaintext) + aes_ciphertext = AesEncryptor.new.encrypt(plaintext, aes_encryption_key) + kms_ciphertext = KmsClient.new.encrypt(aes_ciphertext, 'context' => 'session-encryption') + encode(kms_ciphertext) + end + + def decrypt(ciphertext) + aes_ciphertext = KmsClient.new.decrypt( + decode(ciphertext), 'context' => 'session-encryption' + ) + aes_encryptor.decrypt(aes_ciphertext, aes_encryption_key) + end + + private + + def aes_encryptor + AesEncryptor.new + end + + def aes_encryption_key + IdentityConfig.store.session_encryption_key[0...32] + end + + add_method_tracer :encrypt, "Custom/#{name}/encrypt" + add_method_tracer :decrypt, "Custom/#{name}/decrypt" + end + end +end diff --git a/app/services/idv/agent.rb b/app/services/idv/agent.rb index 42e8ca58641..40a52232c96 100644 --- a/app/services/idv/agent.rb +++ b/app/services/idv/agent.rb @@ -9,7 +9,7 @@ def proof_resolution( ) document_capture_session.create_proofing_session - encrypted_arguments = Encryption::Encryptors::SessionEncryptor.new.encrypt( + encrypted_arguments = Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt( { applicant_pii: @applicant }.to_json, ) @@ -30,7 +30,7 @@ def proof_resolution( def proof_address(document_capture_session, user_id:, issuer:, trace_id:) document_capture_session.create_proofing_session - encrypted_arguments = Encryption::Encryptors::SessionEncryptor.new.encrypt( + encrypted_arguments = Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt( { applicant_pii: @applicant }.to_json, ) @@ -57,7 +57,7 @@ def proof_document( analytics_data:, flow_path: 'standard' ) - encrypted_arguments = Encryption::Encryptors::SessionEncryptor.new.encrypt( + encrypted_arguments = Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt( @applicant.to_json, ) diff --git a/spec/features/idv/doc_auth/document_capture_step_spec.rb b/spec/features/idv/doc_auth/document_capture_step_spec.rb index 8bb5cc04117..3c7d13e8cc2 100644 --- a/spec/features/idv/doc_auth/document_capture_step_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_step_spec.rb @@ -389,7 +389,7 @@ expect(page).to have_current_path(next_step, wait: 20) expect(DocumentProofingJob).to have_received(:perform_later) do |encrypted_arguments:, **| args = JSON.parse( - Encryption::Encryptors::SessionEncryptor.new.decrypt(encrypted_arguments), + Encryption::Encryptors::BackgroundProofingArgEncryptor.new.decrypt(encrypted_arguments), symbolize_names: true, )[:document_arguments] diff --git a/spec/jobs/address_proofing_job_spec.rb b/spec/jobs/address_proofing_job_spec.rb index 94c34e9eef0..90941b4673b 100644 --- a/spec/jobs/address_proofing_job_spec.rb +++ b/spec/jobs/address_proofing_job_spec.rb @@ -3,7 +3,7 @@ RSpec.describe AddressProofingJob, type: :job do let(:document_capture_session) { DocumentCaptureSession.new(result_id: SecureRandom.hex) } let(:encrypted_arguments) do - Encryption::Encryptors::SessionEncryptor.new.encrypt( + Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt( { applicant_pii: applicant_pii }.to_json, ) end diff --git a/spec/jobs/document_proofing_job_spec.rb b/spec/jobs/document_proofing_job_spec.rb index 10a3726d58a..62babf24ac8 100644 --- a/spec/jobs/document_proofing_job_spec.rb +++ b/spec/jobs/document_proofing_job_spec.rb @@ -36,7 +36,7 @@ end let(:encrypted_arguments) do - Encryption::Encryptors::SessionEncryptor.new.encrypt( + Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt( { document_arguments: { encryption_key: Base64.encode64(encryption_key), diff --git a/spec/jobs/resolution_proofing_job_spec.rb b/spec/jobs/resolution_proofing_job_spec.rb index 9c55a9b0af1..0980635a298 100644 --- a/spec/jobs/resolution_proofing_job_spec.rb +++ b/spec/jobs/resolution_proofing_job_spec.rb @@ -18,7 +18,7 @@ } end let(:encrypted_arguments) do - Encryption::Encryptors::SessionEncryptor.new.encrypt( + Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt( { applicant_pii: pii }.to_json, ) end diff --git a/spec/lib/identity_job_log_subscriber_spec.rb b/spec/lib/identity_job_log_subscriber_spec.rb index 1523ef49a06..bb176b6f1e1 100644 --- a/spec/lib/identity_job_log_subscriber_spec.rb +++ b/spec/lib/identity_job_log_subscriber_spec.rb @@ -19,7 +19,7 @@ end document_capture_session = DocumentCaptureSession.new(result_id: SecureRandom.hex) - encrypted_arguments = Encryption::Encryptors::SessionEncryptor.new.encrypt( + encrypted_arguments = Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt( { applicant_pii: { phone: Faker::PhoneNumber.cell_phone } }.to_json, ) diff --git a/spec/services/encryption/encryptors/background_proofing_arg_encryptor_spec.rb b/spec/services/encryption/encryptors/background_proofing_arg_encryptor_spec.rb new file mode 100644 index 00000000000..4983909ce6c --- /dev/null +++ b/spec/services/encryption/encryptors/background_proofing_arg_encryptor_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' + +describe Encryption::Encryptors::BackgroundProofingArgEncryptor do + let(:plaintext) { '{ "foo": "bar" }' } + + describe '#encrypt' do + it 'returns a KMS wrapped AES encrypted ciphertext' do + aes_encryptor = instance_double(Encryption::Encryptors::AesEncryptor) + kms_client = instance_double(Encryption::KmsClient) + allow(aes_encryptor).to receive(:encrypt). + with(plaintext, IdentityConfig.store.session_encryption_key[0...32]). + and_return('aes output') + allow(kms_client).to receive(:encrypt). + with('aes output', 'context' => 'session-encryption'). + and_return('kms output') + allow(Encryption::Encryptors::AesEncryptor).to receive(:new).and_return(aes_encryptor) + allow(Encryption::KmsClient).to receive(:new).and_return(kms_client) + + expected_ciphertext = Base64.strict_encode64('kms output') + + ciphertext = subject.encrypt(plaintext) + + expect(ciphertext).to eq(expected_ciphertext) + end + + it 'sets an encryption context' do + client = instance_double(Encryption::KmsClient) + expect(client).to receive(:encrypt).with( + instance_of(String), 'context' => 'session-encryption' + ).and_return('kms_ciphertext') + allow(Encryption::KmsClient).to receive(:new).and_return(client) + + subject.encrypt(plaintext) + end + end + + describe '#decrypt' do + let(:ciphertext) do + Encryption::Encryptors::BackgroundProofingArgEncryptor.new.encrypt(plaintext) + end + + it 'decrypts the ciphertext' do + expect(subject.decrypt(ciphertext)).to eq(plaintext) + end + end +end diff --git a/spec/services/gpo_confirmation_spec.rb b/spec/services/gpo_confirmation_spec.rb index ab67e551b1e..6b14559fe23 100644 --- a/spec/services/gpo_confirmation_spec.rb +++ b/spec/services/gpo_confirmation_spec.rb @@ -8,7 +8,7 @@ ssn: '123-456-7890', } end - let(:encryptor) { Encryption::Encryptors::SessionEncryptor.new } + let(:encryptor) { Encryption::Encryptors::BackgroundProofingArgEncryptor.new } subject { GpoConfirmation.create!(entry: attributes) }