Encrypted Payload Creation

All API v1 endpoints require encrypted payloads using XSalsa20-Poly1305 (NaCl SecretBox). This page provides working code examples for different languages.

Encryption Format

The encrypted payload format is:

base64( 0x02 + nonce(24 bytes) + ciphertext )
  • 0x02 — format version byte
  • nonce — 24 random bytes (XSalsa20 nonce)
  • ciphertext — encrypted data with Poly1305 MAC
  • Key derivation — SHA256(api_secret)

Request Code Samples

Python
import base64
import hashlib
import os
import json
from nacl.secret import SecretBox

FORMAT_VERSION_NACL = 0x02


class SayferWebNacl:
    def __init__(self, password: str):
        if len(password) < 8:
            raise ValueError("Password is too short")

        self.password = password.encode()
        key = hashlib.sha256(self.password).digest()
        self._box = SecretBox(key)

    def encrypt(self, data: str | bytes) -> str:
        """Encrypt string or bytes. Returns base64 encoded result."""
        if isinstance(data, str):
            data = data.encode()

        nonce = os.urandom(SecretBox.NONCE_SIZE)  # 24 bytes
        ciphertext = self._box.encrypt(data, nonce).ciphertext

        # Format: version_byte + nonce + ciphertext
        combined = bytes([FORMAT_VERSION_NACL]) + nonce + ciphertext
        return base64.b64encode(combined).decode()

    def decrypt(self, key_field: str) -> str:
        """Decrypt base64 encoded string."""
        combined = base64.b64decode(key_field)

        if combined[0] == FORMAT_VERSION_NACL:
            nonce = combined[1:25]
            ciphertext = combined[25:]
            plaintext = self._box.decrypt(ciphertext, nonce)
            return plaintext.decode()
        else:
            raise ValueError("Unsupported format")


# Usage example
if __name__ == "__main__":
    api_secret = "your_api_secret_key_here"
    sayfer = SayferWebNacl(api_secret)

    # Create payload
    payload = {
        "request_timestamp": 1700000000000000,
        "card_id": "card_abc123"
    }

    # Encrypt
    encrypted = sayfer.encrypt(json.dumps(payload))
    print(f"Encrypted: {encrypted}")

    # Decrypt
    decrypted = json.loads(sayfer.decrypt(encrypted))
    print(f"Decrypted: {decrypted}")

Dependencies

LanguagePackageInstall Command
Pythonpynaclpip install pynacl
Gogolang.org/x/cryptogo get golang.org/x/crypto/nacl/secretbox
JavaScripttweetnaclnpm install tweetnacl
TypeScripttweetnaclnpm install tweetnacl @types/tweetnacl

Payload Structure

Every encrypted payload must include request_timestamp for replay protection:

{
  "request_timestamp": 1700000000000000,  // Unix timestamp in microseconds
  "card_id": "card_abc123",               // Your request fields...
  // ... other fields
}

The timestamp must be within 30 seconds of the server time to prevent replay attacks.