Skip to content

File encryption

To encrypt & decrypt files, you can use:

For use of the EncryptionSession#encryptFile and EncryptionSession#decryptFile methods, refer to the dedicated section of the EncryptionSession guide.

Format of an encrypted file

A file encrypted with Seald consists of two parts:

  • a header containing the identifier of the EncryptionSession;
  • the result of the encryption with the key of the EncryptionSession of a tar containing the file to be encrypted.

Thus the encrypted file is slightly larger than the original file, by about 2 kB.

Function description

encryptFile

The encryptFile method has 4 arguments: clearFile, filename, recipients and opts.

It returns a Promise which resolves to the same type as used for clearFile.

clearFile: the file to encrypt

encryptFile supports several types for clearFile and returns the result with the same type:

Examples of use with these different types are available at the end of this page.

filename: the name of the file

This is a String which is used to:

  1. be the default metadata of the EncryptionSession that will be created;
  2. be registered as the name of the file to be encrypted in the tar described above.

TIP

The filename is not available at decryption, it will be in a future update.

recipients: users authorized to decrypt

This is an object with properties describing the users authorized to decrypt:

  • sealdIds: an array containing the Seald IDs of users allowed to decrypt. This is the recommended way to list allowed user.
  • connectors: an array containing connectors referencing users of your app, as declared during the generation of identities.
  • userIds: an array containing user IDs in your application as declared during the generation of identities. This option is deprecated. It is not advised to use it in new code.

TIP

It is possible to encrypt for a connector that is not yet assigned to a Seald identity if opts.allowUnregisteredUsers is set to true.

In this case, it is not end-to-end encryption: the plaintext encryption key is put in escrow on the Seald servers until a Seald identity is created and associated to this connector.

opts: options

This is an object with several optional properties. Three of them are of interest (to see the full list, see the reference):

  • fileSize: size of the file to be encrypted, required for use with the ReadableStream and Readable types.
  • metadata: the metadata of the EncryptionSession that will be created (the default is the value of filename).
  • encryptForSelf: if you are encrypting for a group of which the user is a member, you can set it to false so that you do not encrypt directly for their own identities. The user will still be able to decrypt the data thanks to their group membership. This can allow you to improve the performance of the encryption.

decryptFile

The decryptFile method has a single argument: encryptedFile, the file to be decrypted.

decryptFile supports the same types for encryptedFile as encryptFile does for clearFile.

It returns a Promise which resolves to an Object with the following properties:

  • data: the decrypted file, is of the same type as used for encryptedFile;
  • type: one of 'string', 'buffer', 'node-stream', 'blob', 'readable-stream';
  • filename: the original file name;
  • size: the file size in bytes;
  • sessionId: the EncryptionSession's identifier.

Examples

With String

The simplest signature to encrypt a file is with a String:

js
const encryptedString = await seald.encryptFile(
  'Secret file content',
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] }
)
const encryptedString = await seald.encryptFile(
  'Secret file content',
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] }
)

In this case, the output type will be a base64 encoded String of the encrypted file. It will then be possible to decrypt it as follows:

js
const {
  data, // 'Secret file content'
  type, // 'string'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await seald.decryptFile(encryptedString)
console.log(data) // This will log 'Secret file content'
const {
  data, // 'Secret file content'
  type, // 'string'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await seald.decryptFile(encryptedString)
console.log(data) // This will log 'Secret file content'

info

The content of clearFile is interpreted as utf-8.

TIP

If only text is to be encrypted, it is recommended to use the other encryption format: encryptMessage / decryptMessage which is much lighter.

WARNING

The output of encryptFile used with a String is about 33% heavier than the original file due to the base64 encoding.

With Buffer

The SDK can use Buffer in the sense of Node.js (which are polyfilled outside of a Node.js environment):

js
const encryptedBuffer = await seald.encryptFile(
  Buffer.from('Secret file content', 'utf8'),
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] }
)
const encryptedBuffer = await seald.encryptFile(
  Buffer.from('Secret file content', 'utf8'),
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] }
)

In this case, the output type will be a Buffer. It will then be possible to decrypt it in the following way:

js
const {
  data, // An instance of Buffer containing the clear text data
  type, // 'buffer'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await seald.decryptFile(encryptedBuffer)

console.log(data.toString('utf8')) // This will log 'Secret file content'
const {
  data, // An instance of Buffer containing the clear text data
  type, // 'buffer'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await seald.decryptFile(encryptedBuffer)

console.log(data.toString('utf8')) // This will log 'Secret file content'

With Blob

In a browser, the SDK can use Blob:

js
const encryptedBlob = await seald.encryptFile(
  new Blob(['Secret file content']),
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] }
)
const encryptedBlob = await seald.encryptFile(
  new Blob(['Secret file content']),
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] }
)

In this case, the output type will be a Blob. It will then be possible to decrypt it in the following way:

js
const {
  data, // An instance of Blob containing the clear text data
  type, // 'blob'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await seald.decryptFile(encryptedBlob)

console.log(await data.text()) // This will log 'Secret file content'
const {
  data, // An instance of Blob containing the clear text data
  type, // 'blob'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await seald.decryptFile(encryptedBlob)

console.log(await data.text()) // This will log 'Secret file content'

TIP

The files obtained through fields <input type="file"> are of type File which inherits from Blob. They are therefore treated in the same way by the SDK.

The decrypted Blob has no Blob#type set but it can be inferred from the file extension (using the mime-types package for example).

You can also re-create an instance of File using its constructor from the decrypted Blob and the filename (and optionally the inferred mime type).

WARNING

Since React-Native does not implement the Blob#arrayBuffer or Blob#stream methods, even though they are standard, it is not possible to use the Blob type in a React-Native environment. It is necessary to use another type (like Buffer using a polyfill or String).

With ReadableStream

In a browser, the SDK can use ReadableStream. For example, with the method Blob#stream:

WARNING

In this mode, the encryptFile function must be given the size of the plaintext file via the options.fileSize parameter.

js
const blob = new Blob(['Secret file content'])

const encryptedReadableStream = await seald.encryptFile(
  blob.stream(),
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] },
  { fileSize: blob.size }
)
const blob = new Blob(['Secret file content'])

const encryptedReadableStream = await seald.encryptFile(
  blob.stream(),
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] },
  { fileSize: blob.size }
)

In this case, the output type will be a ReadableStream. It will then be possible to decrypt it in the following way:

js
const {
  data, // An instance of ReadableStream which will stream the clear text data
  type, // 'readable-stream'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await sealdSession.decryptFile(encryptedReadableStream)
let result = ''
const reader = data.getReader()

while (true) {
  const read = await reader.read()
  if (read.done) break
  else result += read.value
}
console.log(result) // This will log 'Secret file content'
const {
  data, // An instance of ReadableStream which will stream the clear text data
  type, // 'readable-stream'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await sealdSession.decryptFile(encryptedReadableStream)
let result = ''
const reader = data.getReader()

while (true) {
  const read = await reader.read()
  if (read.done) break
  else result += read.value
}
console.log(result) // This will log 'Secret file content'

Stream a file upload

Except on Chrome since version 95, it is not possible to use fetch with a ReadableStream as a request body.

To work around this, a solution is to split the encrypted file on the client side into chunks of e.g. 5MB, send them individually to the server, and the server will be responsible for reconstructing the different chunks in order.

With Node.js' Readable streams

The SDK can use Readable streams from Node.js. In a browser, this also works if you use a polyfill. For example, to read & encrypt a file then write it:

WARNING

In this mode, the encryptFile function must be given the size of the plaintext file via the options.fileSize parameter.

js
const { createReadStream, createWriteStream, promises: fsPromises } = require('fs')
const { stat } = fsPromises

const inputFile = './path/to/fileToEncrypt'
const outputFile = './path/to/encryptedFile'

const readable = createReadStream(inputFile)
const writable = createWriteStream(outputFile)

const encryptedStream = await seald.encryptFile(
  readable,
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] },
  { fileSize: (await stat(inputFile)).size }
)

encryptedStream
  .on('error', () => { console.error('errored') })
  .pipe(writable)
  .on('error', () => { console.error('errored') })
  .on('end', () => { console.log('finished') })
const { createReadStream, createWriteStream, promises: fsPromises } = require('fs')
const { stat } = fsPromises

const inputFile = './path/to/fileToEncrypt'
const outputFile = './path/to/encryptedFile'

const readable = createReadStream(inputFile)
const writable = createWriteStream(outputFile)

const encryptedStream = await seald.encryptFile(
  readable,
  'SecretFile.txt',
  { sealdIds: [mySealdId, user2SealdId] },
  { fileSize: (await stat(inputFile)).size }
)

encryptedStream
  .on('error', () => { console.error('errored') })
  .pipe(writable)
  .on('error', () => { console.error('errored') })
  .on('end', () => { console.log('finished') })

It will then be possible to decrypt it in the following way:

js
const { createReadStream, createWriteStream, promises: fsPromises } = require('fs')
const { stat } = fsPromises

const inputFile = './path/to/encryptedFile'
const outputFile = './path/to/decryptedFile'

const readable = createReadStream(inputFile)
const writable = createWriteStream(outputFile)

const {
  data, // An instance of Readable which will stream the clear text data
  type, // 'node-stream'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await seald.decryptFile(readable)

data
  .on('error', () => { console.error('errored') })
  .pipe(writable)
  .on('error', () => { console.error('errored') })
  .on('end', () => { console.log('finished') })
const { createReadStream, createWriteStream, promises: fsPromises } = require('fs')
const { stat } = fsPromises

const inputFile = './path/to/encryptedFile'
const outputFile = './path/to/decryptedFile'

const readable = createReadStream(inputFile)
const writable = createWriteStream(outputFile)

const {
  data, // An instance of Readable which will stream the clear text data
  type, // 'node-stream'
  size, // 19
  sessionId, // the encryption session ID
  filename // 'SecretFile.txt'
} = await seald.decryptFile(readable)

data
  .on('error', () => { console.error('errored') })
  .pipe(writable)
  .on('error', () => { console.error('errored') })
  .on('end', () => { console.log('finished') })