BTFS Encrypted Storage Protocol

Background

In some scenarios, users would like their files stored on BTFS to be "private" and visible to themselves or designated users only. So to store and send private files on the publicly-accessible BTFS, we have created an encrypted file storage protocol, which leverages the sophisticated and efficient hybrid encryption technology for encrypted file storage and sharing.

Structure of Encrypted File

Encrypted files are made up of three parts: HeaderLength, Header, and Payload.

681
  • HeaderLength: Length of the Header that follows the declaration (always 8 bytes); big-endian uint64.
  • Header: Header describes the way the file is encrypted; start from the 8th byte of the file.
  • Payload: Content of the file, encrypted in the way described in the Header.

Structure of Encrypted File Header

The Header is mainly used to declare the algorithm, secret, etc., used for file encryption. The recipient of a file decrypts the file content in the way declared in the Header.

  • Format: JSON
  • Structure:
{
    "version": "1.0",
    "secret_encryption_algorithm": "RSA",
    "secret": "<Secret>",
    "encryption_algorithm": "AES",
    "encoding": "gzip",
    "public_info": {}
}
FieldTypeDescription
versionstringVersion of the protocol
secret_encryption_algorithmstringSecret encryption algorithm (asymmetric): The file publisher selects an asymmetric public key encryption algorithm to encrypt the symmetric secret of the file content using the public key provided by the file recipient; algorithm options include RSA, ECC, and DH
secretstringSecret: The secret for the file content, encrypted with asymmetric encryption; the file recipient can decrypt the secret using the corresponding private key, and then use the decrypted secret to decrypt the file content
encryption_algorithmstringContent encryption algorithm: The file publisher selects a symmetric encryption protocol and encrypt file content using the original secret; algorithm options include AES, DES, and RC4
encodingstringContent encoding: Encoding format of the decrypted content; gzip is recommended
public_infostringPublic information: Publicly-available information customized by the file publisher

Encrypted File Extension

.bte

Encryption

811
  • Pack and compress the original file or directory; TAR & GZIP are recommended (this step can be skipped)
  • Generate a random string as the original secret; the string length is recommended to be at least 16 characters
  • Use the original secret to encrypt the packed file with symmetric encryption; AES is recommended
  • Use the decryptor's public key to encrypt the original secret with asymmetric encryption; RSA is recommended
  • Generate JSON data of the Header, and write the protocol version, the above algorithms, and the secret to the data
  • Calculate the HeaderLength
  • Write the Header and HeaderLength into the front of the encrypted file's content, and save the file as xxx.bte
  • Upload the encrypted file to BTFS to get a CID
  • Share the CID with the recipient
  • Pseudocode sample:
filesContent = ReadDir(dirPath)
payload = GZIP(TAR(filesContent))
key = RANDOM()
encryptPayload = EncryptByAES(payload, key)
encryptKey = EncryptByRSA(key, publicKey)
header = JSON(
    {
        version: 1.0, 
        secret_encryption_algorithm: "RSA", 
        secret: $encryptKey, 
        encryption_algorithm: "AES", 
        encoding: "tar/gzip", 
        public_info: {}
    }
)
headerLen = Len(header)
content = headerLen + header + encryptPayload
cid = UploadToBTFS("demo.bte", content)

Decryption

621
  • Download the encrypted file from BTFS with the specified CID
  • Read 8 bytes from the file content and convert them to int64 to get the HeaderLength
  • Continue to read bytes, the number of which equals the HeaderLength, from the file content to get the Header content
  • Parse the Header based on its protocol structure to get the encrypted version of the file, secret, secret encryption algorithm, and content encryption algorithm
  • Verify the protocol version
  • Decrypt the secret with the private key and the secret encryption algorithm specified in the Header to get the file secret
  • Decrypt the rest of the file content with the file secret and the content encryption algorithm specified in the Header to get the original packed file
  • Unpack the file according to the protocol specified in the Header to get the original file
  • Pseudocode sample:
content = DownloadFromBTFS(cid)
headerLen = int64(content[:8])
header = JSON(content[8:8+headerLen])
encryptPayload = content[8+headerLen:]
encryptKey = header.secret
key = DecryptByRSA(encryptKey, privateKey)
payload = DecryptByAES(encryptPayload, key)
filesContent = UNTAR(UNGZIP(payload))
WriteDir(dirPath, filesContent)

Implementation Notes

  • "version" in the Header is a fixed field; the position and type of this field should be the same in any version of the Header
  • Header structures vary with the versions of encryption protocols; the encryptor should inform the recipient of the structure in time
  • An encrypted file only has one recipient; to share a file with multiple recipients, the encryptor has to generate an encrypted file individually
  • The recipient always needs an asymmetric public/private key pair, and the public key should be passed to the encryptor (any way is allowed)
  • The encryptor must use a reliable encryption algorithm and provide the same recipient with a random, new secret each time
  • The encryptor can write public information into the Header
  • The encryptor and the recipient can be the same person; in this case, BTFS functions as an encrypted file storage network