lib/kms_encrypted/model.rb in kms_encrypted-0.1.4 vs lib/kms_encrypted/model.rb in kms_encrypted-0.2.0

- old
+ new

@@ -44,20 +44,39 @@ key_id: key_id, context: context } ActiveSupport::Notifications.instrument("generate_data_key.kms_encrypted", event) do if key_id == "insecure-test-key" - encrypted_key = "insecure-data-key-#{rand(1_000_000_000_000)}" plaintext_key = "00000000000000000000000000000000" + encrypted_key = "insecure-data-key-#{rand(1_000_000_000_000)}" + elsif key_id.start_with?("projects/") + # generate random AES-256 key + plaintext_key = OpenSSL::Random.random_bytes(32) + + # encrypt it + request = ::Google::Apis::CloudkmsV1::EncryptRequest.new( + plaintext: plaintext_key, + additional_authenticated_data: context.to_json + ) + response = KmsEncrypted::Google.kms_client.encrypt_crypto_key(key_id, request) + key_version = response.name + + # shorten key to save space + short_key_id = Base64.encode64(key_version.split("/").select.with_index { |p, i| i.odd? }.join("/")) + + # build encrypted key + # we reference the key in the field for easy rotation + encrypted_key = "$gc$#{short_key_id}$#{[response.ciphertext].pack(default_encoding)}" else + # generate data key from API resp = KmsEncrypted.kms_client.generate_data_key( key_id: key_id, encryption_context: context, key_spec: "AES_256" ) - encrypted_key = [resp.ciphertext_blob].pack(default_encoding) plaintext_key = resp.plaintext + encrypted_key = [resp.ciphertext_blob].pack(default_encoding) end end instance_variable_set(instance_var, plaintext_key) self.send("#{key_column}=", encrypted_key) @@ -70,11 +89,27 @@ event = { key_id: key_id, context: context } ActiveSupport::Notifications.instrument("decrypt_data_key.kms_encrypted", event) do - if key_id == "insecure-test-key" + if encrypted_key.start_with?("insecure-data-key-") plaintext_key = "00000000000000000000000000000000" + elsif encrypted_key.start_with?("$gc$") + _, _, short_key_id, ciphertext = encrypted_key.split("$", 4) + + # restore key, except for cryptoKeyVersion + stored_key_id = Base64.decode64(short_key_id).split("/")[0..3] + stored_key_id.insert(0, "projects") + stored_key_id.insert(2, "locations") + stored_key_id.insert(4, "keyRings") + stored_key_id.insert(6, "cryptoKeys") + stored_key_id = stored_key_id.join("/") + + request = ::Google::Apis::CloudkmsV1::DecryptRequest.new( + ciphertext: ciphertext.unpack(default_encoding).first, + additional_authenticated_data: context.to_json + ) + plaintext_key = KmsEncrypted::Google.kms_client.decrypt_crypto_key(stored_key_id, request).plaintext else plaintext_key = KmsEncrypted.kms_client.decrypt( ciphertext_blob: encrypted_key.unpack(default_encoding).first, encryption_context: context ).plaintext