kms_encrypted 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4755c4aff4e6bf830d6adb1fb1e81588b5c862de45ab4c72c43f2b95e9c6917
4
- data.tar.gz: dea7d38ed29b406a44f4e92d9a8059e1e517f6a36877718d7b7960a0aa4aedb2
3
+ metadata.gz: ff4007985837815789e565e18c75fa56cf140d9fe80de15116da853f9ac97b8a
4
+ data.tar.gz: d7107117b2b91d20e30bb3fae1ccb56f1c488b47e9be336db43dd76bef62c92d
5
5
  SHA512:
6
- metadata.gz: e8c5e7b8f0cbd544bd00dc1a786c391943fb41fbcc4ed454711b240e7abd365004170ced0b7dfe5604d16852482b6a72472aac16f32dbe5c070f4a5d864fa5be
7
- data.tar.gz: ff7e871de0c9ad6738a6d126991c38679916f17770d88c1132f5a6a3381db1e8db80f40c032cf00ea2b040692cdc64d29d45c08793e747a639ec939caf240b9e
6
+ metadata.gz: 36c756e49dabdce4f82b48d75bef9fceb8fbb91c2fc90b6137f9869d61d113464a9e10ff3db0ccd24a882b1746c19821113d64b23f3a9b5f27ecdd36f3c980c2
7
+ data.tar.gz: 94140b294489883bbb558a0f15d15c170193398ea0b629d7bf45d606b7de227d43afa6822c08490b388c8d4869016a009b58bc4f7ef1cd10c83ceffe9347e803
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 1.8.0 (2025-10-16)
2
+
3
+ - Added `client` option
4
+ - Dropped support for Rails < 7.2
5
+
1
6
  ## 1.7.0 (2025-04-03)
2
7
 
3
8
  - Dropped support for Ruby < 3.2 and Rails < 7.1
data/README.md CHANGED
@@ -10,7 +10,7 @@ With KMS Encrypted:
10
10
  - Decryption can be disabled if an attack is detected
11
11
  - It’s easy to rotate keys
12
12
 
13
- Supports [AWS KMS](https://aws.amazon.com/kms/), [Google Cloud KMS](https://cloud.google.com/kms/), and [Vault](https://www.vaultproject.io/)
13
+ Supports [AWS KMS](https://aws.amazon.com/kms/), [Google Cloud KMS](https://cloud.google.com/kms/), [Vault](https://developer.hashicorp.com/vault), and [OpenBao](https://openbao.org/)
14
14
 
15
15
  Check out [this post](https://ankane.org/sensitive-data-rails) for more info on securing sensitive data with Rails
16
16
 
@@ -36,7 +36,7 @@ And follow the instructions for your key management service:
36
36
 
37
37
  - [AWS KMS](#aws-kms)
38
38
  - [Google Cloud KMS](#google-cloud-kms)
39
- - [Vault](#vault)
39
+ - [Vault and OpenBao](#vault-and-openbao)
40
40
 
41
41
  ### AWS KMS
42
42
 
@@ -78,7 +78,7 @@ Create a [KMS key ring and key](https://console.cloud.google.com/iam-admin/kms)
78
78
  KMS_KEY_ID=projects/my-project/locations/global/keyRings/my-key-ring/cryptoKeys/my-key
79
79
  ```
80
80
 
81
- ### Vault
81
+ ### Vault and OpenBao
82
82
 
83
83
  Add this line to your application’s Gemfile:
84
84
 
@@ -86,19 +86,23 @@ Add this line to your application’s Gemfile:
86
86
  gem "vault"
87
87
  ```
88
88
 
89
- Enable the [transit](https://www.vaultproject.io/docs/secrets/transit/index.html) secrets engine
89
+ Enable the transit secrets engine for [Vault](https://developer.hashicorp.com/vault/docs/secrets/transit) or [OpenBao](https://openbao.org/docs/secrets/transit/)
90
90
 
91
91
  ```sh
92
92
  vault secrets enable transit
93
+ # or
94
+ bao secrets enable transit
93
95
  ```
94
96
 
95
97
  And create a key
96
98
 
97
99
  ```sh
98
100
  vault write -f transit/keys/my-key derived=true
101
+ # or
102
+ bao write -f transit/keys/my-key derived=true
99
103
  ```
100
104
 
101
- Set it in your environment along with your Vault credentials ([dotenv](https://github.com/bkeepers/dotenv) is great for this)
105
+ Set it in your environment along with your credentials ([dotenv](https://github.com/bkeepers/dotenv) is great for this)
102
106
 
103
107
  ```sh
104
108
  KMS_KEY_ID=vault/my-key
@@ -218,11 +222,11 @@ You should also use other tools to detect breaches, like an [IDS](https://www.al
218
222
 
219
223
  Follow the [instructions here](https://cloud.google.com/kms/docs/logging) to set up data access logging. There is not currently a way to see what data is being decrypted, since the additional authenticated data is not logged. For this reason, we recommend another KMS provider.
220
224
 
221
- ### Vault
225
+ ### Vault and OpenBao
222
226
 
223
- Follow the [instructions here](https://www.vaultproject.io/docs/audit/) to set up data access logging.
227
+ Follow the instructions for [Vault](https://developer.hashicorp.com/vault/docs/audit) or [OpenBao](https://openbao.org/docs/audit/) to set up data access logging.
224
228
 
225
- **Note:** Vault will only verify this value if `derived` was set to true when creating the key. If this is not done, the context cannot be trusted.
229
+ **Note:** Vault and OpenBao will only verify this value if `derived` was set to true when creating the key. If this is not done, the context cannot be trusted.
226
230
 
227
231
  Context will show up hashed in the audit logs. To get the hash for a record, use:
228
232
 
@@ -289,7 +293,7 @@ To decrypt the data, use an IAM policy with:
289
293
 
290
294
  todo: document
291
295
 
292
- ### Vault
296
+ ### Vault and OpenBao
293
297
 
294
298
  To encrypt the data, use a policy with:
295
299
 
@@ -313,12 +317,16 @@ Apply a policy with:
313
317
 
314
318
  ```sh
315
319
  vault policy write encrypt encrypt.hcl
320
+ # or
321
+ bao policy write encrypt encrypt.hcl
316
322
  ```
317
323
 
318
324
  And create a token with specific policies with:
319
325
 
320
326
  ```sh
321
327
  vault token create -policy=encrypt -policy=decrypt -no-default-policy
328
+ # or
329
+ bao token create -policy=encrypt -policy=decrypt -no-default-policy
322
330
  ```
323
331
 
324
332
  ## Testing
@@ -341,11 +349,8 @@ Key management services allow you to rotate the master key without any code chan
341
349
 
342
350
  - For AWS KMS, you can use [automatic key rotation](https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html)
343
351
  - For Google Cloud, use the Google Cloud Console or API
344
- - For Vault, use:
345
-
346
- ```sh
347
- vault write -f transit/keys/my-key/rotate
348
- ```
352
+ - For Vault, use `vault write -f transit/keys/my-key/rotate`
353
+ - For OpenBao, use `bao write -f transit/keys/my-key/rotate`
349
354
 
350
355
  New data will be encrypted with the new master key version. To encrypt existing data with new master key version, run:
351
356
 
@@ -1,17 +1,18 @@
1
1
  module KmsEncrypted
2
2
  class Box
3
- attr_reader :key_id, :version, :previous_versions
3
+ attr_reader :key_id, :version, :previous_versions, :client
4
4
 
5
- def initialize(key_id: nil, version: nil, previous_versions: nil)
5
+ def initialize(key_id: nil, version: nil, previous_versions: nil, client: nil)
6
6
  @key_id = key_id || KmsEncrypted.key_id
7
7
  @version = version || 1
8
8
  @previous_versions = previous_versions || {}
9
+ @client = client
9
10
  end
10
11
 
11
12
  def encrypt(plaintext, context: nil)
12
13
  context = version_context(context, version)
13
14
  key_id = version_key_id(version)
14
- ciphertext = KmsEncrypted::Client.new(key_id: key_id, data_key: true).encrypt(plaintext, context: context)
15
+ ciphertext = KmsEncrypted::Client.new(key_id: key_id, data_key: true, client: client).encrypt(plaintext, context: context)
15
16
  "v#{version}:#{encode64(ciphertext)}"
16
17
  end
17
18
 
@@ -43,11 +44,13 @@ module KmsEncrypted
43
44
  key_id ||= version_key_id(version)
44
45
  ciphertext = decode64(ciphertext)
45
46
  context = version_context(context, version)
47
+ client = version_client(version)
46
48
 
47
49
  KmsEncrypted::Client.new(
48
50
  key_id: key_id,
49
51
  data_key: true,
50
- legacy_context: legacy_context
52
+ legacy_context: legacy_context,
53
+ client: client
51
54
  ).decrypt(ciphertext, context: context)
52
55
  end
53
56
 
@@ -68,6 +71,10 @@ module KmsEncrypted
68
71
  key_id
69
72
  end
70
73
 
74
+ def version_client(version)
75
+ previous_versions.dig(version, :client) || self.client
76
+ end
77
+
71
78
  def version_context(context, version)
72
79
  if context.respond_to?(:call)
73
80
  if context.arity == 0
@@ -2,10 +2,11 @@ module KmsEncrypted
2
2
  class Client
3
3
  attr_reader :key_id, :data_key
4
4
 
5
- def initialize(key_id: nil, legacy_context: false, data_key: false)
5
+ def initialize(key_id: nil, legacy_context: false, data_key: false, client: nil)
6
6
  @key_id = key_id || KmsEncrypted.key_id
7
7
  @legacy_context = legacy_context
8
8
  @data_key = data_key
9
+ @service_client = client
9
10
  end
10
11
 
11
12
  def encrypt(plaintext, context: nil)
@@ -60,7 +61,7 @@ module KmsEncrypted
60
61
  KmsEncrypted::Clients::Aws
61
62
  end
62
63
 
63
- klass.new(key_id: key_id, legacy_context: @legacy_context)
64
+ klass.new(key_id: key_id, legacy_context: @legacy_context, client: @service_client)
64
65
  end
65
66
  end
66
67
  end
@@ -8,7 +8,7 @@ module KmsEncrypted
8
8
  }
9
9
  options[:encryption_context] = generate_context(context) if context
10
10
 
11
- KmsEncrypted.aws_client.encrypt(options).ciphertext_blob
11
+ client.encrypt(options).ciphertext_blob
12
12
  end
13
13
 
14
14
  def decrypt(ciphertext, context: nil)
@@ -18,7 +18,7 @@ module KmsEncrypted
18
18
  options[:encryption_context] = generate_context(context) if context
19
19
 
20
20
  begin
21
- KmsEncrypted.aws_client.decrypt(options).plaintext
21
+ client.decrypt(options).plaintext
22
22
  rescue ::Aws::KMS::Errors::InvalidCiphertextException
23
23
  decryption_failed!
24
24
  end
@@ -26,6 +26,10 @@ module KmsEncrypted
26
26
 
27
27
  private
28
28
 
29
+ def client
30
+ @client ||= KmsEncrypted.aws_client
31
+ end
32
+
29
33
  # make integers strings for convenience
30
34
  def generate_context(context)
31
35
  raise ArgumentError, "Context must be a hash" unless context.is_a?(Hash)
@@ -3,9 +3,10 @@ module KmsEncrypted
3
3
  class Base
4
4
  attr_reader :key_id
5
5
 
6
- def initialize(key_id: nil, legacy_context: false)
6
+ def initialize(key_id: nil, legacy_context: false, client: nil)
7
7
  @key_id = key_id
8
8
  @legacy_context = legacy_context
9
+ @client = client
9
10
  end
10
11
 
11
12
  protected
@@ -10,9 +10,9 @@ module KmsEncrypted
10
10
  options[:additional_authenticated_data] = generate_context(context) if context
11
11
 
12
12
  # ensure namespace gets loaded
13
- client = KmsEncrypted.google_client
13
+ client = self.client
14
14
 
15
- if defined?(::Google::Apis::CloudkmsV1::CloudKMSService) && KmsEncrypted.google_client.is_a?(::Google::Apis::CloudkmsV1::CloudKMSService)
15
+ if defined?(::Google::Apis::CloudkmsV1::CloudKMSService) && client.is_a?(::Google::Apis::CloudkmsV1::CloudKMSService)
16
16
  request = ::Google::Apis::CloudkmsV1::EncryptRequest.new(**options)
17
17
  response = client.encrypt_crypto_key(key_id, request)
18
18
  @last_key_version = response.name
@@ -32,9 +32,9 @@ module KmsEncrypted
32
32
  options[:additional_authenticated_data] = generate_context(context) if context
33
33
 
34
34
  # ensure namespace gets loaded
35
- client = KmsEncrypted.google_client
35
+ client = self.client
36
36
 
37
- if defined?(::Google::Apis::CloudkmsV1::CloudKMSService) && KmsEncrypted.google_client.is_a?(::Google::Apis::CloudkmsV1::CloudKMSService)
37
+ if defined?(::Google::Apis::CloudkmsV1::CloudKMSService) && client.is_a?(::Google::Apis::CloudkmsV1::CloudKMSService)
38
38
  request = ::Google::Apis::CloudkmsV1::DecryptRequest.new(**options)
39
39
  begin
40
40
  client.decrypt_crypto_key(key_id, request).plaintext
@@ -52,6 +52,12 @@ module KmsEncrypted
52
52
  end
53
53
  end
54
54
  end
55
+
56
+ private
57
+
58
+ def client
59
+ @client ||= KmsEncrypted.google_client
60
+ end
55
61
  end
56
62
  end
57
63
  end
@@ -7,7 +7,7 @@ module KmsEncrypted
7
7
  }
8
8
  options[:context] = generate_context(context) if context
9
9
 
10
- response = KmsEncrypted.vault_client.logical.write(
10
+ response = client.logical.write(
11
11
  "transit/encrypt/#{key_id.sub("vault/", "")}",
12
12
  options
13
13
  )
@@ -23,7 +23,7 @@ module KmsEncrypted
23
23
 
24
24
  response =
25
25
  begin
26
- KmsEncrypted.vault_client.logical.write(
26
+ client.logical.write(
27
27
  "transit/decrypt/#{key_id.sub("vault/", "")}",
28
28
  options
29
29
  )
@@ -42,6 +42,10 @@ module KmsEncrypted
42
42
 
43
43
  private
44
44
 
45
+ def client
46
+ @client ||= KmsEncrypted.vault_client
47
+ end
48
+
45
49
  # turn hash into json
46
50
  def generate_context(context)
47
51
  Base64.encode64(super)
@@ -20,6 +20,10 @@ module KmsEncrypted
20
20
  @previous_versions ||= evaluate_option(:previous_versions)
21
21
  end
22
22
 
23
+ def client
24
+ @client ||= evaluate_option(:client)
25
+ end
26
+
23
27
  def context(version)
24
28
  name = options[:name]
25
29
  context_method = name ? "kms_encryption_context_#{name}" : "kms_encryption_context"
@@ -36,7 +40,8 @@ module KmsEncrypted
36
40
  KmsEncrypted::Box.new(
37
41
  key_id: key_id,
38
42
  version: version,
39
- previous_versions: previous_versions
43
+ previous_versions: previous_versions,
44
+ client: client
40
45
  ).encrypt(plaintext, context: context)
41
46
  end
42
47
 
@@ -49,7 +54,8 @@ module KmsEncrypted
49
54
  KmsEncrypted::Box.new(
50
55
  key_id: key_id,
51
56
  version: version,
52
- previous_versions: previous_versions
57
+ previous_versions: previous_versions,
58
+ client: client
53
59
  ).decrypt(ciphertext, context: context)
54
60
  end
55
61
 
@@ -1,6 +1,6 @@
1
1
  module KmsEncrypted
2
2
  module Model
3
- def has_kms_key(name: nil, key_id: nil, eager_encrypt: false, version: 1, previous_versions: nil, upgrade_context: false)
3
+ def has_kms_key(name: nil, key_id: nil, eager_encrypt: false, version: 1, previous_versions: nil, upgrade_context: false, client: nil)
4
4
  key_id ||= KmsEncrypted.key_id
5
5
 
6
6
  key_method = name ? "kms_key_#{name}" : "kms_key"
@@ -28,7 +28,8 @@ module KmsEncrypted
28
28
  name: name,
29
29
  version: version,
30
30
  previous_versions: previous_versions,
31
- upgrade_context: upgrade_context
31
+ upgrade_context: upgrade_context,
32
+ client: client
32
33
  }
33
34
 
34
35
  if @kms_keys.size == 1
@@ -1,3 +1,3 @@
1
1
  module KmsEncrypted
2
- VERSION = "1.7.0"
2
+ VERSION = "1.8.0"
3
3
  end
data/lib/kms_encrypted.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # dependencies
2
2
  require "active_support"
3
3
  require "base64"
4
+
5
+ # stdlib
4
6
  require "json"
5
7
  require "securerandom"
6
8
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kms_encrypted
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-03 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '7.1'
18
+ version: '7.2'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '7.1'
25
+ version: '7.2'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: base64
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
77
  requirements: []
78
- rubygems_version: 3.6.2
78
+ rubygems_version: 3.6.9
79
79
  specification_version: 4
80
80
  summary: Simple, secure key management for Lockbox and attr_encrypted
81
81
  test_files: []