- Docs
- SaaS Shield
- Suite
- Tenant Security Client
- Deterministic Encryption
Deterministic Encryption API Methods
The Tenant Security Client (TSC) provides the following methods to support deterministic encryption of data.
Create
The deterministic encryption methods in the TSC are all provided by a separate DeterministicTenantSecurityClient.
This client is created similarly to the TenantSecurityClient, using the same PROXY_DOMAIN
and PROXY_API_KEY
parameters (described in more detail here).
JavaDeterministicTenantSecurityClient detClient = DeterministicTenantSecurityClient.create(PROXY_DOMAIN, PROXY_API_KEY);
JavaScriptconst detClient = new DeterministicTenantSecurityClient(PROXY_DOMAIN, PROXY_API_KEY);
Alternatively, you can create a deterministic client that inherits its configuration from an existing TenantSecurityClient.
JavaTenantSecurityClient client = TenantSecurityClient.create(PROXY_DOMAIN, PROXY_API_KEY); DeterministicTenantSecurityClient detClient = client.getDeterministicClient();
JavaScriptconst client = new TenantSecurityClient(PROXY_DOMAIN, PROXY_API_KEY); const detClient = client.deterministicClient;
EncryptField
This encrypt operation takes a value for a particular field in your data store (a column in a table, property in an object, etc.) as bytes, along with the secret path and derivation path that together uniquely identify the field, and encrypts the data using AES-SIV and a key that is unique to that data path and tenant. The caller must include the metadata associated with the field, which is the same structure as the metadata used in the encrypt call.
JavaFieldMetadata metadata = new FieldMetadata("TENANT_ID", "serviceOrUserId", "data label"); byte[] fieldBytes = "Jim Bridger".getBytes("UTF-8"); DeterministicPlaintextField field = new DeterministicPlaintextField(fieldBytes, "secretPath", "derivPath"); DeterministicEncryptedField encrypted = detClient.encryptField(field, metadata).get(); byte[] encryptedBytes = encrypted.getEncryptedField(); // Store encrypted bytes in your persistence layer. // You will need the derivation path and secret path when decrypting.
JavaScriptconst metadata = new FieldMetadata("TENANT_ID", "serviceOrUserId", "data label"); const fieldBytes = Buffer.from("Jim Bridger", "utf-8"); const field = { plaintextField: fieldBytes, secretPath: "secretPath", derivationPath: "derivPath" }; const encrypted = await detClient.encryptField(field, metadata); const encryptedBytes = encrypted.encryptedField; // Store encrypted bytes in your persistence layer. // You will need the derivation path and secret path when decrypting.
The operation is asynchronous, and when it resolves, it returns the encrypted data with the secret ID as an array of six bytes prepended. It also returns the search path and derivation path that were used. You can choose whether you persist the paths along with the encrypted data, but the identical values will need to be provided to decrypt the value.
DecryptField
This decrypt operation takes the encrypted value and the secret and derivation paths, plus the same metadata structure. The tenant ID in the metadata and the paths must be identical to the values used to encrypt the data in order to decrypt it successfully. The TSC will extract the secret ID that was prepended to the encrypted data, use it and the paths to get a key from the TSP, and decrypt the encrypted data with that key.
JavaFieldMetadata metadata = new FieldMetadata("TENANT_ID", "serviceOrUserId", "data label"); byte[] encryptedBytes = /* encrypted bytes from your persistence layer */; String secretPath = /* secret path used during encryption */; String derivPath = /* derivation path used during encryption */; DeterministicEncryptedField recreated = new DeterministicEncryptedField(encryptedBytes, secretPath, derivPath); DeterministicPlaintextField decrypted = detClient.decryptField(recreated, metadata).get(); byte[] decryptedBytes = decrypted.getPlaintextField();
JavaScriptconst metadata = new FieldMetadata("TENANT_ID", "serviceOrUserId", "data label"); const recreated = { encryptedField: /* encrypted bytes from your persistence layer */, secretPath: /* secret path used during encryption */, derivationPath: /* derivation path used during encryption */ }; const decrypted = await detClient.decryptField(recreated, metadata); const decryptedBytes = decrypted.plaintextField;
The operation is asynchronous, and when it resolves, it returns the decrypted data, along with the search and derivation paths that were specified.
RotateField
This method is used to reencrypt a value when the secret that was used to encrypt it is being rotated to a new secret.
You pass it the same encrypted field and metadata you would pass to decryptField
. This operation is also asynchronous,
and when it resolves, it returns a response that is the same as the response from encryptField
.
JavaFieldMetadata metadata = new FieldMetadata("TENANT_ID", "serviceOrUserId", "data label"); byte[] encryptedBytes = /* encrypted bytes from your persistence layer */; String secretPath = /* secret path used during encryption */; String derivPath = /* derivation path used during encryption */; DeterministicEncryptedField recreated = new DeterministicEncryptedField(encryptedBytes, secretPath, derivPath); DeterministicEncryptedField rotated = detClient.rotateField(recreated, metadata).get(); byte[] rotatedBytes = rotated.getEncryptedField(); // Store rotated bytes in your persistence layer in place of the previous bytes. // You will need the derivation path and secret path when decrypting.
JavaScriptconst metadata = new FieldMetadata("TENANT_ID", "serviceOrUserId", "data label"); const recreated = { encryptedField: /* encrypted bytes from your persistence layer */, secretPath: /* secret path used during encryption */, derivationPath: /* derivation path used during encryption */ }; const rotated = await detClient.rotateField(recreated, metadata); const rotatedBytes = rotated.encryptedField; // Store rotated bytes in your persistence layer in place of the previous bytes. // You will need the derivation path and secret path when decrypting.
GenerateSearchTerms
This method is used if you want to search your data store for a particular value. The input is the same as the input to
encryptField
. The method is asynchronous, and when it resolves it returns an array of values that are each the same type as
the return value from an encryptField
operation. The array will have one entry if key rotation is not ongoing for the
specified tenant, or two entries if key rotation is in progress. If you get two entries in the response, you are responsible
for searching for matches for either of those values in your data store to properly respond to the search request.
JavaFieldMetadata metadata = new FieldMetadata("TENANT_ID", "serviceOrUserId", "data label"); byte[] fieldBytes = "Jim Bridger".getBytes("UTF-8"); String secretPath = /* secret path used during encryption */; String derivPath = /* derivation path used during encryption */; DeterministicPlaintextField field = new DeterministicPlaintextField(fieldBytes, secretPath, derivPath); DeterministicEncryptedField[] searchTerms = detClient.generateSearchTerms(field, metadata).get(); // searchTerms can contain up to two results, and the bytes contained within should be // used in combination when searching for this value.
JavaScriptFieldMetadata metadata = new FieldMetadata("TENANT_ID", "serviceOrUserId", "data label"); byte[] fieldBytes = "Jim Bridger".getBytes("UTF-8"); const field = { encryptedField: fieldBytes, secretPath: /* secret path used during encryption */, derivationPath: /* derivation path used during encryption */ }; const searchTerms = await detClient.generateSearchTerms(field, metadata); // searchTerms can contain up to two results, and the bytes contained within should be // used in combination when searching for this value.
Batch Methods
There are batch equivalents each of these methods that take a map from ID to the respective function’s input type and a single metadata record and return a response that has a map from ID to the respective function’s return type and a map from ID to failures when they resolve.