Skip to content

How does an encrypted library work?

Logan Guo edited this page May 16, 2016 · 7 revisions

How does an encrypted library work?

  1. When you create an encrypted library, you'll provide a password for it. All the data in that library will be encrypted with the password before uploading to the server.
  2. The file encryption/decryption is performed in the client-side.
  3. Pay very much attention to key derivation algorithm and data encryption algorithm, they are a lot different though they were discussed together. Key derivation algorithm is PBKDF2WithHmacSHA256 and data encryption algorithm is AES 256/CBC (PKCS7Padding).
  4. Unfortunately, Android SDK supports neither PBKDF2 nor AES 256/CBC. So we use a third party library Bouncy Castle.
  5. Bouncy Castle doesn't support PBKDF2WithHmacSHA256 by default, so use PKCS5S2ParametersGenerator with SHA256Digest instead. That depends on Bouncy Castle jars, which could be downloaded here.
  6. But this patch uses gradle dependencies, as you can find it in the build.gradle.
  7. If you use version 1.47 or higher of SpongyCastle, you can invoke PBKDF2WithHmacSHA256 directly. In versions of BC < 1.47, you could not specify SHA256 digest and it defaulted to SHA1.

Server side encryption

As background knowledge

magic token

  1. When you create the library, a "magic token" is derived from the password and library id.
  2. This token is stored with the library on the server side.
  3. The magic token is generated by PBKDF2 algorithm with 1000 iterations of SHA256 hash.

Encrypted file key (random key)

  1. Generate a 32-byte long cryptographically strong random number. This will be used as the file encryption key ("file key").
  2. Encrypt the file key with the user provided password. We first use PBKDF2 algorithm (1000 iterations of SHA256) to derive a key/iv pair from the password, then use AES 256/CBC to encrypt the file key. The result is called the "encrypted file key". This encrypted file key will be stored on the server. When you need to access the data, you can decrypt the file key from the encrypted file key.
  3. All file data is encrypted by the file key with AES 256/CBC. We use PBKDF2 algorithm (1000 iterations of SHA256) to derive key/iv pair from the file key. After encryption, the data is uploaded to the server.
  4. The flexibility to use password to encrypt real secret key rather than encrypt data is you can change password later. When you change password, decrypt secret key from the old password, then encrypt by the new password, magic token should be also calculated.
  • Internally " encrypted file key" is also called "random key".*

Client side decryption

Verify password

  1. When you view an encrypted library, the client needs to verify your password.
  2. The client use "magic token" to check whether your password is correct before you sync the library.
  3. Client side uses the same algorithm to compute local magic token, then compare with the one within the repo.
  4. If equals, client decrypt the "file key" from "random key" and derive "encKey" and "encIv" from the "file key". To avoid decrypt "random key" repeatedly, client should persist the "encKey" and "encIv" for each repo for later use.
  5. Persist these "encKey"s and "encIv"s in local storage.

Data decryption

Client side decryption simply reverses the encryption procedure on server side

  1. Get the encKey and encIv from local storage, no expired time restriction.
  2. file data is encrypted/decrypted by the encKey and encIv with AES/CBC/PKCS7Padding

File upload

Firstly, chunk file into 2M blocks. Blockid is sha1 of the block content. Internally file blocks (chunks) were managed by FileBlocks.

Then get file server link from seahub

Seahub server URL: /api2/repos/<repo_id>/upload-blks-link/

Then upload all blocks to file server at the same time.

File server URL: http://server-address:8082/upload-blks-api/<token>

Parameters to upload blocks:

  • parent_dir: parent directory path
  • file_name: file name
  • file_size: file size in bytes
  • replace: whether overwrite file with the same name

The client sends the blocks with a POST request to the file server. The content of the request is in multipart form-data format.

multi-part form example

 ------SeafileAndroidBound$_$
 Content-Disposition: form-data; name="parent_dir"

 /
 ------SeafileAndroidBound$_$
 Content-Disposition: form-data; name="file_name"

 IMG_20160114_163406.jpg
 ------SeafileAndroidBound$_$
 Content-Disposition: form-data; name="file_size"

 2262770
 ------SeafileAndroidBound$_$--

response

response: {
    "id": fileid
}

File download

Getting block list of a file from Seahub: url: /api2/repos/<repo_id>/file/?p=<filepath>&op=downloadblks

The response contains the file_id, block id list. Then the client downloads each block separately from file server.

Dowbload a block

First get file server link for downloading a block:

url: /api2/repos/<repo_id>/files/<file_id>/blks/<blk_id>/download-link/

Then download the block from file server Parameters to generate token:

  • op: 'downloadblks'
  • obj_id: SHA1 id of the file object
  • use_onetime: True

File server URL: http://server-address:8082/blks/<token>/<block_id>

Then assemble all blocks into the file in order.

notify uploading progress

exactly the same with file uploading

cache management

related to file_id