Reversibility
To anticipate situations where you may need to stop using the Seald SDK and retrieve your application's data in a decrypted state, we provide a reversibility tool.
This tool uses an export of your application's Seald data from our API and SSKS servers, as well as an export of certain data from your own backend, to decrypt data that was encrypted with the Seald SDK.
Please note, using this tool breaks end-to-end encryption. It is intended for exceptional situations where you need to stop using the Seald SDK, and is not designed for regular or routine use.
If you are interested in this tool, we strongly recommend that you test the procedure on your Staging environment in advance.
WARNING
This reversibility procedure requires you have access to the identities of each user. This is not always possible, for example if you use only the password protection only, with a password that is only known to the users.
If you use the 2-man-rule mechanism to store the users' identities, you should be able to follow this procedure and decrypt the user's identities with the Seald SSKS backups plus the export from your server.
If you do not use 2-man-rule, you may have to push some client-side code to your users to retrieve their identities.
Exporting Seald Data
To obtain this export, you must contact Seald support, who will provide you with the necessary files.
We can also set up a regular export of this data to an S3-compatible Object Storage under your control, if desired. In this case, we will periodically export the data to your servers.
The data is encrypted for a private key. This private key can be under your control; in that case, we recommend storing it offline, on one or more physical media (USB key, hard drive, etc.) stored in a secure location. The private key may also be given in escrow to a trusted third party, such as the Agence de Protection des Programmes (APP), who can provide it to you in case of contract termination or contractual default by Seald (additional escrow fees may apply).
Exporting Your Backend Data
You must also export certain data from your backend in order to decrypt data encrypted with the Seald SDK. This data is optional, depending on which Seald SDK features you use in your application.
This data, and the expected export format, are described in the section Using the Reversibility Tool below.
Of course, you must also ensure that you have access to the encrypted data that needs to be decrypted.
Installing the Reversibility Tool
The reversibility tool is a JavaScript library, available as an npm package: https://www.npmjs.com/package/@seald-io/reversibility-tools.
You can install it in your project with the following command:
npm install @seald-io/reversibility-toolsYou can then use it in your JavaScript code.
Using the Reversibility Tool
This tool is a Node.JS library, intended to be used in .js scripts that a developer writes to do this process.
This process is done in 3 steps:
- Getting a database of decrypted identities
- Getting a database of decrypted session keys
- Decrypting the data itself
Getting a database of decrypted identities
A database of decrypted identities (the output of this step) is a JSONLines file, containing:
{ sealdId: string, deviceId: string, identity: string }The identity field here contains the Base64 encoding of the identity buffer.
This file can be constructed in multiple ways.
First, it can be done directly by the developer, by calling sealdSdk.exportIdentity() on their clients, then sending these identities back to their server, and constructing this file themselves.
Also, this database can be obtained by using the decryptSsksExport(ssksExportPath, privateKey, outputPath, { appId, ssksTmrKeysPath, ssksStrictPasswordsPath }) function of the reversibility tools.
In this function call:
ssksExportPathis the path to the export file for SSKS provided by SealdprivateKeyis an SSCrypto instance of your private key for which the exports were encryptedoutputPathis the path to which to write the output file (the directory must already exist). If the file already exists, it will be overwritten.appIdis your Seald App IDssksTmrKeysPathis an optional argument, if you want to retrieve the identities from SSKS 2MR. It is the path to a file that is exported from your database, containing thetwoManRuleKeyorrawTwoManRuleKeyfor each user of your app, stored in a JSONLines file, in the following format:
{ userId: string, sealdId?: string, deviceId?: string, rawTwoManRuleKey?: string, twoManRuleKey?: string }Each line must contain one, and only one, of rawTwoManRuleKey or twoManRuleKey. The fields sealdId and deviceId are optional, and are used to check that the decrypted identity indeed corresponds to what is expected.
ssksStrictPasswordsPathis an optional argument, if you want to retrieve the identities from SSKS Password. It is the path to a file that is exported from your database, containing thepasswordorrawEncryptionKeyused to store their identity on SSKS Password for each user of your app, stored in a JSONLines file, in the following format:
{ userId: string, sealdId?: string, deviceId?: string, password?: string, rawEncryptionKey?: string }Each line must contain one, and only one, of password or rawEncryptionKey. The fields sealdId and deviceId are optional, and are used to check that the decrypted identity indeed corresponds to what is expected.
Getting a database of decrypted session keys
Once you have the database of decrypted identities, you can use the decryptApiExport(apiExportPath, decryptedIdentitiesPath, privateKey, outputPath, { tmrAccessesOverEncryptionKeysPath?, symEncKeysPath?, appId? }) function of the reversibility tools.
In this function call:
apiExportPathis the path to the export file for the main Seald API provided by SealddecryptedIdentitiesPathis the path to the file that is the result of the previous stepprivateKeyis an SSCrypto instance of your private key for which the exports were encryptedtmrAccessesOverEncryptionKeysPathis an optional argument, if you want to retrieve session keys from TMR Accesses. This should be the path to a file that is exported from your database, containing therawOverEncryptionKeyfor each TMR Access, stored in a JSONLines file, in the following format:
{ sessionId: string, tmrAccessId: string, rawOverEncryptionKey: string }groupTmrTemporaryKeysOverEncryptionKeysPathis an optional argument, if you want to retrieve session keys from Group TMR temporary keys. This should be the path to a file that is exported from your database, containing therawOverEncryptionKeyfor each Group TMR Temporary key, stored in a JSONLines file, in the following format:
{ gTMRTKId: string, rawOverEncryptionKey: string }symEncKeysPathis an optional argument, if you want to retrieve session keys from SymEncKeys. This should be the path to a file that is exported from your database, containing the Sym Enc Keys, stored in a JSONLines file, in the following format:
{ sessionId: string, symEncKeyId: string, rawSecret?: string, rawSymKey?: string, password?: string }Each line must contain one, and only one, of password or the pair rawSecret and rawSymKey.
appIdis your Seald App ID. It is required only if you passsymEncKeysPath.
This will result in a JSONLines file in the following format:
{ sessionId: string, key: string }Decrypting the data itself
Once you have the database of decrypted session keys, you can use the decryptMessage(encryptedMessage, decryptedSessionKeysPath, { raw , sessionId }) and decryptFile(encryptedFileStream, decryptedSessionKeysPath, { sessionId }) functions to decrypt messages and files respectively.
In the decryptMessage function call:
encryptedMessageis the encrypted message to decryptdecryptedSessionKeysPathis the path to the file that is the result of the previous steprawis an optional boolean that defaults tofalse, and that you should set totrueif the message was encrypted as a raw messagesessionIdis the session ID to which this message corresponds. Optional if the message is not raw. Required if the message is raw.
This returns the content of the decrypted message as a string.
In the decryptFile function call:
encryptedFileStreamis a Readable stream of the file to decryptdecryptedSessionKeysPathis the path to the file that is the result of the previous stepsessionIdis the session ID to which this file corresponds. Optional.
This returns:
stream, which is aReadablestream which contains the content of the decrypted filepromise, which resolves as soon as the parsing is finished and which contains the file sizesize, the file namefilename, the protocol versionprotocol, and the parsed session IDmid
Example Usage
A complete example of how to use the reversibility tool is available on
GitHub at https://github.com/seald/seald-reversibility-tools-demo.
Here is also a basic example of how to use the reversibility tool:
const { node: sscrypto } = require('sscrypto')
const fs = require('node:fs')
const {
decryptApiExport,
decryptFile,
decryptMessage,
decryptSsksExport
} = require('@seald-io/reversibility-tools')
// Your Seald Application Id
const appId = 'YOUR_APP_ID'
const databaseReversibilityDemo = async () => {
// Create an output directory to write clear files
await fs.promises.mkdir('./clear-files', { recursive: true })
// Load and instantiate a SSCrypto.PrivateKey instance
// This is the private key used to encrypt the Seald exports
const privateKeyB64 = await fs.promises.readFile('./seald-database-exports/private_key', 'utf8')
const privateKeyInstance = sscrypto.PrivateKey.fromB64(privateKeyB64)
// First, decrypt the identities export
// It will create a file containing SealdIds and their associated private keys
await decryptSsksExport(
'./seald-database-exports/test-ssks.backup',
privateKeyInstance,
'./clear-files/identities.jsonl',
{
appId,
ssksTmrKeysPath: './exports/ssks-tmr-keys.jsonl',
ssksStrictPasswordsPath: './exports/ssks-strict-passwords.jsonl'
}
)
// Second, decrypt the API export
// This will create a file associating message ids and the corresponding encryption symmetric key.
await decryptApiExport(
'./seald-database-exports/api.backup',
'./clear-files/identities.jsonl',
privateKeyInstance,
'./clear-files/sessions.jsonl',
{
tmrAccessesOverEncryptionKeysPath: './exports/tmr-accesses-over-encryption-keys.jsonl',
tmrTemporaryKeysOverEncryptionKeysPath: './exports/group-tmr-temp-key-over-encryption-key.jsonl',
symEncKeysPath: './exports/sym-enc-keys.jsonl',
appId
}
)
// Now that the seald database has been fully decrypted, we can decrypt any message
// Decrypt file
const res = decryptFile(
fs.createReadStream('./encrypted-files/test.txt.seald'),
'./clear-files/sessions.jsonl'
)
await fs.promises.writeFile('./clear-files/test.txt', res.stream)
// Decrypt message
const encrypted = await fs.promises.readFile('./encrypted-files/test-message.bin', 'utf8')
const decryptedMessage = await decryptMessage(encrypted, './clear-files/sessions.jsonl')
await fs.promises.writeFile('./clear-files/test-message.txt', decryptedMessage)
}
databaseReversibilityDemo()