rom_encrypted_attribute 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/lib/rom_encrypted_attribute/decryptor.rb +5 -7
- data/lib/rom_encrypted_attribute/encryptor.rb +2 -16
- data/lib/rom_encrypted_attribute/payload.rb +47 -0
- data/lib/rom_encrypted_attribute/version.rb +1 -1
- data/lib/rom_encrypted_attribute.rb +3 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a969ae5d8669ac0bba27f0ba1aeb8ad53b42962c4a63dea849033f3398ea0048
|
4
|
+
data.tar.gz: 752b299f84b9a569983e7b937fb8588f404572032d4fd58d64a022399ffbeb98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8a2e9a6dbebf654fff5349968e66b8a77909720ffedafeb1dfa9e7232d789c758123c36f078266fd6895fbacebed1d79a8aa78e439450bcb437cd6cbcdbfe80
|
7
|
+
data.tar.gz: 414fbbf17de5976ec4940f1d4244dc215f1b6ebfb3d4b98d80c2c72ba574c9ebe79a48d5db1b8960ef302f3320afcf1e6e19ee1845d228c81865c4b09587b14e
|
data/README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# RomEncryptedAttribute
|
2
2
|
|
3
|
-
This gem adds support for encrypted
|
3
|
+
This gem adds support for encrypted attributes to [ROM](https://rom-rb.org/).
|
4
4
|
|
5
|
-
Traditionally ROM suggested to put encryption logic in repository code (more precisely, in the mapper from database struct to an entity). I personally think this is not the greatest idea. Repository lies logically in application layer (or even domain layer), while encryption and decryption of data is a purely infrastructure concern. As such, it should be as low-level and hidden as possible.
|
5
|
+
Traditionally ROM team [suggested](https://discourse.rom-rb.org/t/question-encryption-support-thoughts/387) to put encryption logic in repository code (more precisely, in the mapper from database struct to an entity). I personally think this is not the greatest idea. Repository lies logically in the application layer (or even domain layer), while encryption and decryption of data is a purely infrastructure concern. As such, it should be as low-level and hidden as possible.
|
6
6
|
|
7
|
-
In ROM terms it means doing it in a relation.
|
7
|
+
In ROM terms it means doing it in a relation. The gem leverages custom types to achieve encryption and decryption.
|
8
8
|
|
9
|
-
The scheme is compatible with Rails' default settings for ActiveRecord encryption, so you can still read encrypted
|
9
|
+
The scheme is compatible with Rails' default settings for ActiveRecord encryption, so you can still read records encrypted with ActiveRecord from ROM (and vice versa) as long as you provide the same primary key and key derivation salt.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -27,7 +27,7 @@ class SecretNotes < ROM::Relation[:sql]
|
|
27
27
|
EncryptedString, EncryptedStringReader =
|
28
28
|
RomEncryptedAttribute.define_encrypted_attribute_types(
|
29
29
|
primary_key: ENV["ENCRYPTION_PRIMARY_KEY"],
|
30
|
-
|
30
|
+
key_derivation_salt: ENV["ENCRYPTION_KEY_DERIVATION_SALT"]
|
31
31
|
)
|
32
32
|
|
33
33
|
schema(:secret_notes, infer: true) do
|
@@ -42,7 +42,7 @@ Of course, you can define it somewhere else and just `include` in the relation o
|
|
42
42
|
|
43
43
|
* Due to a bug in `rom-sql`, reading unencrypted data is turned on by default
|
44
44
|
* The gem uses SHA256 for key derivation and it's currently not configurable
|
45
|
-
* Support for deterministic encryption from `ActiveRecord::
|
45
|
+
* Support for deterministic encryption from `ActiveRecord::Encryption` is not implemented
|
46
46
|
|
47
47
|
## Contributing
|
48
48
|
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "base64"
|
4
4
|
require "json"
|
5
5
|
require_relative "key_derivator"
|
6
|
+
require_relative "payload"
|
6
7
|
|
7
8
|
module RomEncryptedAttribute
|
8
9
|
class Decryptor
|
@@ -11,10 +12,7 @@ module RomEncryptedAttribute
|
|
11
12
|
end
|
12
13
|
|
13
14
|
def decrypt(message)
|
14
|
-
|
15
|
-
data = Base64.strict_decode64(message["p"])
|
16
|
-
iv = Base64.strict_decode64(message["h"]["iv"])
|
17
|
-
auth_tag = Base64.strict_decode64(message["h"]["at"])
|
15
|
+
payload = RomEncryptedAttribute::Payload.decode(message)
|
18
16
|
|
19
17
|
cipher = OpenSSL::Cipher.new("aes-256-gcm")
|
20
18
|
key = @derivator.derive(cipher.key_len)
|
@@ -22,10 +20,10 @@ module RomEncryptedAttribute
|
|
22
20
|
cipher.decrypt
|
23
21
|
cipher.padding = 0
|
24
22
|
cipher.key = key
|
25
|
-
cipher.iv =
|
26
|
-
cipher.auth_tag = auth_tag
|
23
|
+
cipher.iv = payload.initialization_vector
|
24
|
+
cipher.auth_tag = payload.auth_tag
|
27
25
|
cipher.auth_data = ""
|
28
|
-
cipher.update(
|
26
|
+
cipher.update(payload.message) + cipher.final
|
29
27
|
rescue JSON::ParserError
|
30
28
|
# we need to unconditionally support of reading unencrypted data due to a bug in rom-sql
|
31
29
|
# https://github.com/rom-rb/rom-sql/issues/423
|
@@ -4,6 +4,7 @@ require "base64"
|
|
4
4
|
require "json"
|
5
5
|
require "openssl"
|
6
6
|
require_relative "key_derivator"
|
7
|
+
require_relative "payload"
|
7
8
|
|
8
9
|
module RomEncryptedAttribute
|
9
10
|
class Encryptor
|
@@ -20,22 +21,7 @@ module RomEncryptedAttribute
|
|
20
21
|
cipher.key = key
|
21
22
|
cipher.iv = iv
|
22
23
|
encrypted = cipher.update(message) + cipher.final
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def serialize(encrypted, iv:, cipher:)
|
29
|
-
payload =
|
30
|
-
{
|
31
|
-
"p" => Base64.strict_encode64(encrypted),
|
32
|
-
"h" => {
|
33
|
-
"iv" => Base64.strict_encode64(iv),
|
34
|
-
"at" => Base64.strict_encode64(cipher.auth_tag)
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
JSON.dump(payload)
|
24
|
+
Payload.new(message: encrypted, initialization_vector: iv, auth_tag: cipher.auth_tag).encode
|
39
25
|
end
|
40
26
|
end
|
41
27
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types"
|
4
|
+
require "dry/struct"
|
5
|
+
require "base64"
|
6
|
+
|
7
|
+
class RomEncryptedAttribute::Payload < Dry::Struct
|
8
|
+
class Types
|
9
|
+
include Dry.Types()
|
10
|
+
end
|
11
|
+
|
12
|
+
attribute :message, Types::Strict::String
|
13
|
+
attribute :initialization_vector, Types::Strict::String
|
14
|
+
attribute :auth_tag, Types::Strict::String
|
15
|
+
|
16
|
+
def self.decode(database_value)
|
17
|
+
payload = JSON.parse(database_value)
|
18
|
+
new(
|
19
|
+
message: decode64(payload["p"]),
|
20
|
+
initialization_vector: decode64(payload.dig("h", "iv")),
|
21
|
+
auth_tag: decode64(payload.dig("h", "at"))
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.decode64(value)
|
26
|
+
Base64.strict_decode64(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def encode
|
30
|
+
payload =
|
31
|
+
{
|
32
|
+
"p" => encode64(message),
|
33
|
+
"h" => {
|
34
|
+
"iv" => encode64(initialization_vector),
|
35
|
+
"at" => encode64(auth_tag)
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
JSON.dump(payload)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def encode64(value)
|
45
|
+
Base64.strict_encode64(value)
|
46
|
+
end
|
47
|
+
end
|
@@ -7,13 +7,13 @@ require_relative "rom_encrypted_attribute/version"
|
|
7
7
|
require "dry/types"
|
8
8
|
|
9
9
|
module RomEncryptedAttribute
|
10
|
-
def self.define_encrypted_attribute_types(primary_key:,
|
10
|
+
def self.define_encrypted_attribute_types(primary_key:, key_derivation_salt:)
|
11
11
|
reader_type = Dry.Types.Constructor(String) do |value|
|
12
|
-
RomEncryptedAttribute::Decryptor.new(secret: primary_key, salt:
|
12
|
+
RomEncryptedAttribute::Decryptor.new(secret: primary_key, salt: key_derivation_salt).decrypt(value)
|
13
13
|
end
|
14
14
|
|
15
15
|
writer_type = Dry.Types.Constructor(String) do |value|
|
16
|
-
RomEncryptedAttribute::Encryptor.new(secret: primary_key, salt:
|
16
|
+
RomEncryptedAttribute::Encryptor.new(secret: primary_key, salt: key_derivation_salt).encrypt(value)
|
17
17
|
end
|
18
18
|
|
19
19
|
[writer_type, reader_type]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rom_encrypted_attribute
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paweł Świątkowski
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-types
|
@@ -82,6 +82,7 @@ files:
|
|
82
82
|
- lib/rom_encrypted_attribute/decryptor.rb
|
83
83
|
- lib/rom_encrypted_attribute/encryptor.rb
|
84
84
|
- lib/rom_encrypted_attribute/key_derivator.rb
|
85
|
+
- lib/rom_encrypted_attribute/payload.rb
|
85
86
|
- lib/rom_encrypted_attribute/version.rb
|
86
87
|
- rom_encrypted_attribute.gemspec
|
87
88
|
homepage:
|