1. Docs
  2. SaaS Shield
  3. Suite
  4. Tenant Security Client
  5. Deterministic Encryption
  1. Docs
  2. SaaS Shield
  3. Suite
  4. Tenant Security Client
  5. 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).

Java
DeterministicTenantSecurityClient detClient = DeterministicTenantSecurityClient.create(PROXY_DOMAIN, PROXY_API_KEY);
JavaScript
const detClient = new DeterministicTenantSecurityClient(PROXY_DOMAIN, PROXY_API_KEY);

Alternatively, you can create a deterministic client that inherits its configuration from an existing TenantSecurityClient.

Java
TenantSecurityClient client = TenantSecurityClient.create(PROXY_DOMAIN, PROXY_API_KEY); DeterministicTenantSecurityClient detClient = client.getDeterministicClient();
JavaScript
const 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.

Java
FieldMetadata 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.
JavaScript
const 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.

Java
FieldMetadata 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();
JavaScript
const 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.

Java
FieldMetadata 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.
JavaScript
const 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.

Java
FieldMetadata 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.
JavaScript
FieldMetadata 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.