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-tools
You 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:
ssksExportPath
is the path to the export file for SSKS provided by SealdprivateKey
is an SSCrypto instance of your private key for which the exports were encryptedoutputPath
is the path to which to write the output file (the directory must already exist). If the file already exists, it will be overwritten.appId
is your Seald App IDssksTmrKeysPath
is 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 thetwoManRuleKey
orrawTwoManRuleKey
for 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.
ssksStrictPasswordsPath
is 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 thepassword
orrawEncryptionKey
used 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:
apiExportPath
is the path to the export file for the main Seald API provided by SealddecryptedIdentitiesPath
is the path to the file that is the result of the previous stepprivateKey
is an SSCrypto instance of your private key for which the exports were encryptedtmrAccessesOverEncryptionKeysPath
is 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 therawOverEncryptionKey
for each TMR Access, stored in a JSONLines file, in the following format:
{ sessionId: string, tmrAccessId: string, rawOverEncryptionKey: string }
groupTmrTemporaryKeysOverEncryptionKeysPath
is 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 therawOverEncryptionKey
for each Group TMR Temporary key, stored in a JSONLines file, in the following format:
{ gTMRTKId: string, rawOverEncryptionKey: string }
symEncKeysPath
is 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
.
appId
is 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:
encryptedMessage
is the encrypted message to decryptdecryptedSessionKeysPath
is the path to the file that is the result of the previous stepraw
is an optional boolean that defaults tofalse
, and that you should set totrue
if the message was encrypted as a raw messagesessionId
is 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:
encryptedFileStream
is a Readable stream of the file to decryptdecryptedSessionKeysPath
is the path to the file that is the result of the previous stepsessionId
is the session ID to which this file corresponds. Optional.
This returns:
stream
, which is aReadable
stream 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()