Skip to content

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:

bash
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 Seald
  • privateKey is an SSCrypto instance of your private key for which the exports were encrypted
  • outputPath 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 ID
  • ssksTmrKeysPath 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 the twoManRuleKey or rawTwoManRuleKey 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 the password or rawEncryptionKey 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 Seald
  • decryptedIdentitiesPath is the path to the file that is the result of the previous step
  • privateKey is an SSCrypto instance of your private key for which the exports were encrypted
  • tmrAccessesOverEncryptionKeysPath 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 the rawOverEncryptionKey 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 the rawOverEncryptionKey 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 pass symEncKeysPath.

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 decrypt
  • decryptedSessionKeysPath is the path to the file that is the result of the previous step
  • raw is an optional boolean that defaults to false, and that you should set to true if the message was encrypted as a raw message
  • sessionId 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 decrypt
  • decryptedSessionKeysPath is the path to the file that is the result of the previous step
  • sessionId is the session ID to which this file corresponds. Optional.

This returns:

  • stream, which is a Readable stream which contains the content of the decrypted file
  • promise, which resolves as soon as the parsing is finished and which contains the file size size , the file name filename, the protocol version protocol, and the parsed session ID mid

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:

javascript
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()