Class: WorkOS::Vault
- Inherits:
-
Object
- Object
- WorkOS::Vault
- Defined in:
- lib/workos/vault.rb
Overview
WorkOS Vault: KV secret storage, server-managed key wrapping, and client-side AES-GCM encrypt/decrypt.
Defined Under Namespace
Classes: DataKey, DataKeyPair, ObjectDigest, ObjectMetadata, ObjectUpdateBy, ObjectVersion, VaultObject
Constant Summary collapse
- DEFAULT_RESPONSE_LIMIT =
10
Instance Method Summary collapse
-
#create_data_key(key_context:, request_options: {}) ⇒ DataKeyPair
Generate a data key for local encryption.
-
#create_object(name:, value:, key_context:, request_options: {}) ⇒ Object
Create a new Vault encrypted object.
-
#decrypt(encrypted_data:, associated_data: nil) ⇒ Object
Decrypt data previously encrypted by ‘encrypt`.
-
#decrypt_data_key(keys:, request_options: {}) ⇒ DataKey
Decrypt encrypted data keys previously generated by create_data_key.
-
#delete_object(object_id:, request_options: {}) ⇒ Object
Permanently delete a Vault encrypted object.
-
#encrypt(data:, key_context:, associated_data: nil) ⇒ Object
Encrypt data locally using AES-GCM with a data key derived from the context.
-
#get_object_metadata(object_id:, request_options: {}) ⇒ Object
Get a Vault object’s metadata without decrypting the value.
-
#initialize(client) ⇒ Vault
constructor
A new instance of Vault.
-
#list_object_versions(object_id:, request_options: {}) ⇒ Array<ObjectVersion>
List versions for a specific Vault object.
-
#list_objects(limit: DEFAULT_RESPONSE_LIMIT, before: nil, after: nil, request_options: {}) ⇒ Array<ObjectDigest>
List encrypted Vault objects.
-
#read_object(object_id:, request_options: {}) ⇒ Object
Get a Vault object with the value decrypted.
-
#read_object_by_name(name:, request_options: {}) ⇒ Object
Get a Vault object by name with the value decrypted.
-
#update_object(object_id:, value:, version_check: nil, request_options: {}) ⇒ Object
Update an existing Vault object.
Constructor Details
#initialize(client) ⇒ Vault
Returns a new instance of Vault.
88 89 90 |
# File 'lib/workos/vault.rb', line 88 def initialize(client) @client = client end |
Instance Method Details
#create_data_key(key_context:, request_options: {}) ⇒ DataKeyPair
Generate a data key for local encryption.
153 154 155 156 157 |
# File 'lib/workos/vault.rb', line 153 def create_data_key(key_context:, request_options: {}) body = {"context" => key_context} response = @client.request(method: :post, path: "/vault/v1/keys/data-key", auth: true, body: body, request_options: ) DataKeyPair.from_response(JSON.parse(response.body)) end |
#create_object(name:, value:, key_context:, request_options: {}) ⇒ Object
Create a new Vault encrypted object.
130 131 132 133 134 |
# File 'lib/workos/vault.rb', line 130 def create_object(name:, value:, key_context:, request_options: {}) body = {"name" => name, "value" => value, "key_context" => key_context} response = @client.request(method: :post, path: "/vault/v1/kv", auth: true, body: body, request_options: ) ObjectMetadata.from_hash(JSON.parse(response.body)) end |
#decrypt(encrypted_data:, associated_data: nil) ⇒ Object
Decrypt data previously encrypted by ‘encrypt`.
181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/workos/vault.rb', line 181 def decrypt(encrypted_data:, associated_data: nil) payload = Base64.decode64(encrypted_data) iv = payload.byteslice(0, 12) tag = payload.byteslice(12, 16) key_len, leb_len = decode_u32_leb128(payload.byteslice(28, payload.bytesize - 28)) keys_index = 28 + leb_len key_blob = payload.byteslice(keys_index, key_len) ciphertext = payload.byteslice(keys_index + key_len, payload.bytesize - (keys_index + key_len)) data_key = decrypt_data_key(keys: Base64.strict_encode64(key_blob)) key = Base64.decode64(data_key.key) aes_gcm_decrypt(ciphertext, key, iv, tag, associated_data&.b) end |
#decrypt_data_key(keys:, request_options: {}) ⇒ DataKey
Decrypt encrypted data keys previously generated by create_data_key.
161 162 163 164 165 |
# File 'lib/workos/vault.rb', line 161 def decrypt_data_key(keys:, request_options: {}) body = {"keys" => keys} response = @client.request(method: :post, path: "/vault/v1/keys/decrypt", auth: true, body: body, request_options: ) DataKey.from_response(JSON.parse(response.body)) end |
#delete_object(object_id:, request_options: {}) ⇒ Object
Permanently delete a Vault encrypted object.
144 145 146 147 |
# File 'lib/workos/vault.rb', line 144 def delete_object(object_id:, request_options: {}) @client.request(method: :delete, path: "/vault/v1/kv/#{WorkOS::Util.encode_path(object_id)}", auth: true, request_options: ) nil end |
#encrypt(data:, key_context:, associated_data: nil) ⇒ Object
Encrypt data locally using AES-GCM with a data key derived from the context. Returns base64(IV || TAG || LEB128(len(keyBlob)) || keyBlob || ciphertext).
171 172 173 174 175 176 177 178 |
# File 'lib/workos/vault.rb', line 171 def encrypt(data:, key_context:, associated_data: nil) pair = create_data_key(key_context: key_context) key = Base64.decode64(pair.data_key.key) key_blob = Base64.decode64(pair.encrypted_keys) prefix = encode_u32_leb128(key_blob.bytesize) iv, ciphertext, tag = aes_gcm_encrypt(data.b, key, associated_data&.b) Base64.strict_encode64(iv + tag + prefix + key_blob + ciphertext) end |
#get_object_metadata(object_id:, request_options: {}) ⇒ Object
Get a Vault object’s metadata without decrypting the value.
107 108 109 110 |
# File 'lib/workos/vault.rb', line 107 def (object_id:, request_options: {}) response = @client.request(method: :get, path: "/vault/v1/kv/#{WorkOS::Util.encode_path(object_id)}/metadata", auth: true, request_options: ) VaultObject.from_hash(JSON.parse(response.body)) end |
#list_object_versions(object_id:, request_options: {}) ⇒ Array<ObjectVersion>
List versions for a specific Vault object.
123 124 125 126 127 |
# File 'lib/workos/vault.rb', line 123 def list_object_versions(object_id:, request_options: {}) response = @client.request(method: :get, path: "/vault/v1/kv/#{WorkOS::Util.encode_path(object_id)}/versions", auth: true, request_options: ) parsed = JSON.parse(response.body) (parsed["data"] || []).map { |item| ObjectVersion.from_hash(item) } end |
#list_objects(limit: DEFAULT_RESPONSE_LIMIT, before: nil, after: nil, request_options: {}) ⇒ Array<ObjectDigest>
List encrypted Vault objects.
114 115 116 117 118 119 |
# File 'lib/workos/vault.rb', line 114 def list_objects(limit: DEFAULT_RESPONSE_LIMIT, before: nil, after: nil, request_options: {}) params = {"limit" => limit, "before" => before, "after" => after}.compact response = @client.request(method: :get, path: "/vault/v1/kv", auth: true, params: params, request_options: ) parsed = JSON.parse(response.body) (parsed["data"] || []).map { |item| ObjectDigest.from_hash(item) } end |
#read_object(object_id:, request_options: {}) ⇒ Object
Get a Vault object with the value decrypted.
95 96 97 98 |
# File 'lib/workos/vault.rb', line 95 def read_object(object_id:, request_options: {}) response = @client.request(method: :get, path: "/vault/v1/kv/#{WorkOS::Util.encode_path(object_id)}", auth: true, request_options: ) VaultObject.from_hash(JSON.parse(response.body)) end |
#read_object_by_name(name:, request_options: {}) ⇒ Object
Get a Vault object by name with the value decrypted.
101 102 103 104 |
# File 'lib/workos/vault.rb', line 101 def read_object_by_name(name:, request_options: {}) response = @client.request(method: :get, path: "/vault/v1/kv/name/#{WorkOS::Util.encode_path(name)}", auth: true, request_options: ) VaultObject.from_hash(JSON.parse(response.body)) end |
#update_object(object_id:, value:, version_check: nil, request_options: {}) ⇒ Object
Update an existing Vault object.
137 138 139 140 141 |
# File 'lib/workos/vault.rb', line 137 def update_object(object_id:, value:, version_check: nil, request_options: {}) body = {"value" => value, "version_check" => version_check}.compact response = @client.request(method: :put, path: "/vault/v1/kv/#{WorkOS::Util.encode_path(object_id)}", auth: true, body: body, request_options: ) VaultObject.from_hash(JSON.parse(response.body)) end |