Skip to main content

ENCRYPTION

Request for Comments: VaultysID Encryption Protocolโ€‹

1. Introductionโ€‹

This document specifies the VaultysID Encryption Protocol, a secure method for encrypting and decrypting data using the NaCl secretbox authenticated encryption scheme. The protocol is designed to provide confidentiality, integrity, and authenticity for file encryption while supporting both local and remote key derivation.

1.1. Terminologyโ€‹

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

1.2. Abbreviationsโ€‹

  • PRF: Pseudo-Random Function
  • HMAC: Hash-based Message Authentication Code
  • VID: VaultysID

2. Protocol Overviewโ€‹

The VaultysID Encryption Protocol provides secure file encryption with the following features:

  1. Strong authenticated encryption using NaCl secretbox (XSalsa20-Poly1305)
  2. Identity-based encryption keys derived from VaultysID credentials
  3. Support for both local and remote key derivation
  4. Deterministic file format with versioning
  5. Support for both whole-file and streaming encryption modes

3. Cryptographic Componentsโ€‹

3.1. Encryption Methodโ€‹

The protocol uses NaCl secretbox, which provides authenticated encryption using:

  • XSalsa20 stream cipher for encryption
  • Poly1305 for authentication

This combination provides confidentiality, integrity, and authenticity.

3.2. Key Derivationโ€‹

Encryption keys are derived through a two-step process:

  1. Generate a PRF value based on the user's VaultysID and a random nonce
  2. Apply SHA-256 to the PRF to create a 32-byte encryption key

This approach ensures the encryption key is deterministically derived from the user's identity while remaining unique for each encryption operation.

4. File Format Specificationโ€‹

4.1. Header Formatโ€‹

Every encrypted file begins with a fixed 32-byte header with the following structure:

+----------------+----------------+----------------+
| Magic String | Version Number | Reserved |
| (18 bytes) | (8 bytes) | (6 bytes) |
+----------------+----------------+----------------+
  • Magic String: The ASCII string "vaultys/encryption" (18 bytes)
  • Version Number: 64-bit unsigned big-endian integer (8 bytes)
  • Reserved: Zero-filled bytes reserved for future use (6 bytes)

The full header in hexadecimal for version 1 is:

7661756c7479732f656e6372797074696f6e00000000000000010000000000000000

4.2. Whole-File Encryption Formatโ€‹

The complete encrypted file structure is:

+----------------+----------------+----------------+----------------+----------------+
| Header | PRF Nonce | Encryption | Encrypted Data | Authentication |
| (32 bytes) | (32 bytes) | Nonce (24 B) | (variable) | Tag (16 bytes) |
+----------------+----------------+----------------+----------------+----------------+
  • Header: As defined in section 4.1
  • PRF Nonce: 32-byte random value used for PRF derivation
  • Encryption Nonce: 24-byte nonce used by NaCl secretbox
  • Encrypted Data: The encrypted content
  • Authentication Tag: Poly1305 authentication tag (implicitly included in NaCl secretbox output)

4.3. Chunked Encryption Formatโ€‹

For streaming or chunked encryption, the format is:

+----------------+----------------+----------------+
| Header | PRF Nonce | Chunks... |
| (32 bytes) | (32 bytes) | (variable) |
+----------------+----------------+----------------+

Where each chunk has the format:
+----------------+----------------+----------------+
| Encryption | Encrypted Data | Authentication |
| Nonce (24 B) | (variable) | Tag (16 bytes) |
+----------------+----------------+----------------+
  • Each chunk contains its own encryption nonce
  • The recommended chunk size is 64 KB of plaintext data
  • The final chunk MAY be smaller than the recommended chunk size

5. Encryption Processโ€‹

5.1. PRF Generationโ€‹

  1. Generate a 32-byte random PRF nonce
  2. Derive the PRF by one of two methods: a. Local: prf = HMAC(VID_key, "encryption/prf|" + prf_nonce_hex + "|prf/encryption") b. Remote: Request PRF via secure channel using "encryption/" + prf_nonce_hex + "/encryption"
  3. Verify the PRF is exactly 32 bytes

5.2. Key Derivationโ€‹

  1. Derive the encryption key: key = SHA-256(prf)
  2. Securely erase the PRF value from memory

5.3. Whole-File Encryptionโ€‹

  1. Generate a 24-byte random encryption nonce
  2. Encrypt the plaintext: ciphertext = nacl.secretbox(plaintext, nonce, key)
  3. Construct the encrypted file as specified in section 4.2
  4. Securely erase the key from memory

5.4. Chunked Encryptionโ€‹

  1. Write the header and PRF nonce
  2. For each chunk: a. Generate a unique encryption nonce (MAY be derived from chunk number) b. Encrypt the chunk: encrypted_chunk = nacl.secretbox(chunk, nonce, key) c. Write the nonce and encrypted chunk
  3. Securely erase the key from memory

6. Decryption Processโ€‹

6.1. Header Verificationโ€‹

  1. Read the 32-byte header
  2. Verify the magic string equals "vaultys/encryption"
  3. Extract the version number (current supported version is 1)

6.2. PRF Retrievalโ€‹

  1. Read the 32-byte PRF nonce
  2. Retrieve the PRF by one of two methods: a. Local: prf = HMAC(VID_key, "encryption/prf|" + prf_nonce_hex + "|prf/encryption") b. Remote: Request PRF via secure channel using "encryption/" + prf_nonce_hex + "/encryption"
  3. Verify the PRF is exactly 32 bytes

6.3. Key Derivationโ€‹

  1. Derive the decryption key: key = SHA-256(prf)
  2. Securely erase the PRF value from memory

6.4. Whole-File Decryptionโ€‹

  1. Read the 24-byte encryption nonce
  2. Read the encrypted data
  3. Decrypt: plaintext = nacl.secretbox.open(ciphertext, nonce, key)
  4. If decryption fails, abort and return an error
  5. Securely erase the key from memory

6.5. Chunked Decryptionโ€‹

  1. For each chunk: a. Read the 24-byte encryption nonce b. Read the encrypted chunk c. Decrypt: plaintext_chunk = nacl.secretbox.open(encrypted_chunk, nonce, key) d. If decryption fails, abort and return an error
  2. Concatenate all plaintext chunks
  3. Securely erase the key from memory

7. Security Considerationsโ€‹

7.1. Nonce Uniquenessโ€‹

The security of XSalsa20 depends on nonce uniqueness. The protocol ensures this by:

  • Using random 24-byte nonces for whole-file encryption
  • Using deterministic nonces derived from chunk numbers for chunked encryption
  • Using 32-byte PRF nonces to ensure unique keys for each encryption operation

7.2. Key Managementโ€‹

  • Encryption keys MUST be 32 bytes long
  • Keys MUST be securely erased from memory after use
  • The PRF value MUST be securely erased after key derivation

7.3. Error Handlingโ€‹

  • Implementations MUST NOT reveal information about decryption failures beyond indicating that an error occurred
  • Any tampering with the ciphertext will cause authentication to fail, and implementations MUST reject such data

7.4. Forward Secrecyโ€‹

This protocol does not provide forward secrecy. If a VaultysID is compromised, all files encrypted with that identity may be decrypted.

8. Implementation Considerationsโ€‹

8.1. Streaming vs. Whole-Fileโ€‹

  • Whole-file encryption is simpler but requires keeping the entire file in memory
  • Streaming encryption is more complex but allows handling files larger than available memory
  • Implementations SHOULD support both modes where appropriate

8.2. Progress Reportingโ€‹

  • Implementations SHOULD provide progress reporting for large files
  • For chunked encryption/decryption, progress can be reported per chunk

8.3. Error Propagationโ€‹

  • Stream-based implementations MUST properly propagate errors to the application
  • If one chunk fails to decrypt, the entire file SHOULD be considered compromised

9. Examplesโ€‹

9.1. Example Header (Hexadecimal)โ€‹

For version 1:

7661756c7479732f656e6372797074696f6e00000000000000010000000000000000

For version 2:

7661756c7479732f656e6372797074696f6e00000000000000020000000000000000

9.2. PRF Derivation String Examplesโ€‹

Local PRF derivation string for nonce "a1b2c3d4...":

encryption/prf|a1b2c3d4...|prf/encryption

Remote PRF request path for nonce "a1b2c3d4...":

encryption/a1b2c3d4.../encryption

10. Interoperability Considerationsโ€‹

10.1. Versioningโ€‹

  • Implementations MUST reject files with unsupported version numbers
  • Future versions MAY add additional fields or change encryption methods, but MUST maintain the same header format

10.2. Cross-Platform Issuesโ€‹

  • Implementations MUST handle binary data correctly, especially when transmitting between platforms with different endianness
  • All multi-byte integers MUST be encoded in big-endian format

11. Referencesโ€‹

11.1. Normative Referencesโ€‹

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[NACL] Bernstein, D.J., et al., "Networking and Cryptography library", https://nacl.cr.yp.to/

11.2. Informative Referencesโ€‹

[XSALSA20] Bernstein, D.J., "Extending the Salsa20 nonce", https://cr.yp.to/snuffle/xsalsa-20110204.pdf

[POLY1305] Bernstein, D.J., "The Poly1305-AES message-authentication code", https://cr.yp.to/mac/poly1305-20050329.pdf

Appendix A: Test Vectorsโ€‹

[TODO]

Appendix B: Code Examplesโ€‹

[TODO]

Authors' Addressesโ€‹

Franรงois-Xavier Thoorens fx.thoorens@vaultys.com