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:
- Strong authenticated encryption using NaCl secretbox (XSalsa20-Poly1305)
- Identity-based encryption keys derived from VaultysID credentials
- Support for both local and remote key derivation
- Deterministic file format with versioning
- 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:
- Generate a PRF value based on the user's VaultysID and a random nonce
- 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β
- Generate a 32-byte random PRF nonce
- 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" - Verify the PRF is exactly 32 bytes
5.2. Key Derivationβ
- Derive the encryption key:
key = SHA-256(prf) - Securely erase the PRF value from memory
5.3. Whole-File Encryptionβ
- Generate a 24-byte random encryption nonce
- Encrypt the plaintext:
ciphertext = nacl.secretbox(plaintext, nonce, key) - Construct the encrypted file as specified in section 4.2
- Securely erase the key from memory
5.4. Chunked Encryptionβ
- Write the header and PRF nonce
- 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 - Securely erase the key from memory
6. Decryption Processβ
6.1. Header Verificationβ
- Read the 32-byte header
- Verify the magic string equals "vaultys/encryption"
- Extract the version number (current supported version is 1)
6.2. PRF Retrievalβ
- Read the 32-byte PRF nonce
- 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" - Verify the PRF is exactly 32 bytes
6.3. Key Derivationβ
- Derive the decryption key:
key = SHA-256(prf) - Securely erase the PRF value from memory
6.4. Whole-File Decryptionβ
- Read the 24-byte encryption nonce
- Read the encrypted data
- Decrypt:
plaintext = nacl.secretbox.open(ciphertext, nonce, key) - If decryption fails, abort and return an error
- Securely erase the key from memory
6.5. Chunked Decryptionβ
- 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 - Concatenate all plaintext chunks
- 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