PyNaCl Documentation

repository·main·Indexed 22 days ago

https://github.com/pyca/pynacl

Python binding to libsodium, a modern software library for essential cryptographic operations including encryption, digital signatures, and password hashing. PyNaCl provides APIs for hashing (SHA-2, BLAKE2b, SipHash), key derivation using scrypt and argon2id, password hashing and verification via nacl.pwhash, and a flexible encoding system for transforming cryptographic keys and messages.

Tokens
16.8K
Snippets
53
Records
75
Agent score
78%

What's inside PyNaCl

  1. Overview of libsodium cryptographic capabilities

    main

    libsodium (the underlying library for PyNaCl) is a software library providing a wide range of cryptographic operations. It is a portable, cross-compilable fork of NaCl that maintains API compatibility while extending functionality for better usability.

    Key capabilities include:

    • Encryption & Decryption: Securely handling data with modern algorithms.
    • Digital Signatures: Creating and verifying signatures for data authenticity.
    • Secure Password Hashing: Protecting user credentials.
    • Cross-Platform Support: Works on Windows (MinGW, Visual Studio, x86, x64, arm64), iOS, Android, JavaScript, and WebAssembly.
  2. Overview of PyNaCl features

    main

    PyNaCl is a Python binding to libsodium. It is designed to improve usability, security, and speed for cryptographic operations.

    Key features include:

    • Digital signatures
    • Secret-key encryption
    • Public-key encryption
    • Hashing and message authentication
    • Password based key derivation and password hashing
  3. Overview of Ed25519 digital signatures

    main

    Ed25519 is a high-performance public-key signature system used for digital signatures in PyNaCl. It is designed to be fast for signing, single-signature verification, and batch verification, while maintaining a high security level (2^128 target).

    Key characteristics include:

    • Small footprint: Signatures are 64 bytes (512 bits) and public keys are 32 bytes (256 bits).
    • Side-channel resistance: Immune to cache-timing and branch-prediction attacks because it avoids secret-dependent memory access and branching.
    • Deterministic signing: Unlike (EC)DSA, it does not rely on an external entropy source for nonces during the signing process, which prevents private key compromise due to poor entropy.
    • Collision resilience: The system is resilient against hash-function collisions.
  4. Use nacl.public.SealedBox for untraceable encryption

    main

    The nacl.public.SealedBox class allows a sender to encrypt a message for a recipient without the sender needing to maintain a long-term identity or provide proof of authorship. It uses an ephemeral key pair for the encryption process, which is discarded immediately after.

    Key characteristics:

    • Untraceable: The recipient cannot trace the ciphertext to a specific sender because the sender's key pair is ephemeral.
    • One-way: The sender cannot decrypt the message they just created.
    • Usage: The sender initializes SealedBox(recipient_public_key). The recipient decrypts using SealedBox(recipient_private_key).
    from nacl.public import PrivateKey, SealedBox
    
    # Recipient setup
    skbob = PrivateKey.generate()
    pkbob = skbob.public_key
    
    # Sender (Alice) encrypts for Bob
    sealed_box = SealedBox(pkbob)
    message = b"Kill all kittens"
    encrypted = sealed_box.encrypt(message)
    
    # Recipient (Bob) decrypts
    unseal_box = SealedBox(skbob)
    plaintext = unseal_box.decrypt(encrypted)
    print(plaintext.decode('utf-8'))
  5. Handle PyNaCl cryptographic errors using CryptoError

    main

    All exceptions raised by PyNaCl methods and functions are subclasses of nacl.exceptions.CryptoError. To safely handle any error originating from cryptographic operations (such as signature verification failures or key mismatches), you should wrap your code in a try...except nacl.exceptions.CryptoError block. This allows you to catch all library-specific errors in a single block for cleanup or error reporting.

    import nacl.exceptions
    
    try:
        # cryptographic operations
        pass
    except nacl.exceptions.CryptoError:
        # cleanup after any kind of exception
        # raised from cryptographic-related operations
        pass
  6. Use EncryptedMessage to handle combined nonce and ciphertext

    main

    The EncryptedMessage class is a bytes subclass used to represent a message that has been encrypted by nacl.secret.SecretBox or nacl.public.Box. It encapsulates both the nonce and the ciphertext into a single object. The full content of the EncryptedMessage object is the concatenation of the nonce and the ciphertext.

    # EncryptedMessage is a bytes subclass
    # It contains both nonce and ciphertext
    msg = EncryptedMessage(nonce=some_nonce, ciphertext=some_ciphertext)
    print(msg.nonce)
    print(msg.ciphertext)
  7. Manage nonces for SecretBox and Aead

    main

    A nonce (Number used once) is a 24-byte value used during encryption.

    Critical Security Requirement: A nonce MUST NEVER be reused with the same key. Reusing a nonce can allow an attacker to decrypt messages or forge new ones.

    • Visibility: Nonces are not secret. They can be transmitted or stored in plaintext alongside the ciphertext.
    • Generation: You can use nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE) for SecretBox or nacl.secret.Aead.NONCE_SIZE for Aead.
    • Explicit Nonce Usage: If you need to provide your own nonce (e.g., a counter), pass it as the second argument to .encrypt().
    • Extracting Ciphertext without Nonce: If you are transmitting the nonce via a different channel, you can access the ciphertext attribute of the nacl.utils.EncryptedMessage object returned by .encrypt(). This attribute contains only the message and the MAC (length: len(message) + box.MACBYTES).
  8. Compare SecretBox and Aead

    main

    Choose between SecretBox and Aead based on your data requirements:

    FeatureSecretBoxAead
    Primary UseStandard symmetric encryptionEncryption with authenticated metadata (AAD)
    AlgorithmXSalsa20 + Poly1305XChaCha20 + Poly1305 (IETF construction)
    AAD SupportNoYes (via aad parameter)
    Nonce Size24 bytes24 bytes (XChaCha20)

    Both provide authenticated encryption, meaning they detect tampering, but Aead allows you to bind unencrypted context to the ciphertext.

  9. Define a custom Encoder

    main

    To create a custom encoder, define a class with two static methods: encode(data) and decode(data). This allows you to integrate custom serialization formats into PyNaCl's API by passing your class as the encoder argument.

    import binascii
    
    class HexEncoder(object):
        @staticmethod
        def encode(data):
            return binascii.hexlify(data)
    
        @staticmethod
        def decode(data):
            return binascii.unhexlify(data)
  10. Use nacl.public.Box for mutual authentication

    main

    The nacl.public.Box class provides authenticated encryption between two parties using their respective public and private keys. It derives a shared secret from a pair of keys (one private, one public), ensuring that only the intended recipient can decrypt the message and that the message's authenticity is verified.

    To use Box, both parties must exchange public keys. The sender creates a Box using their own PrivateKey and the recipient's PublicKey. The recipient creates a Box using their own PrivateKey and the sender's PublicKey to decrypt.

    import nacl.utils
    from nacl.public import PrivateKey, Box
    
    # Setup keys
    skbob = PrivateKey.generate()
    pkbob = skbob.public_key
    skalice = PrivateKey.generate()
    pkalice = skalice.public_key
    
    # Bob sends to Alice
    bob_box = Box(skbob, pkalice)
    message = b"Kill all humans"
    encrypted = bob_box.encrypt(message)
    
    # Alice receives from Bob
    alice_box = Box(skalice, pkbob)
    plaintext = alice_box.decrypt(encrypted)
    print(plaintext.decode('utf-8'))
  11. Understand libsodium versioning and release types

    main

    libsodium uses a two-tier release system to manage updates and security:

    • Point releases (e.g., 1.0.19, 1.0.20): These are tagged when new features are added or significant changes occur.
    • Stable releases: Frequent maintenance updates between point releases. These fix minor issues and security vulnerabilities without introducing new features or breaking changes.

    Note for developers: If your application depends on a specific point release, applying stable updates is safe as they remain fully compatible with their parent point release.

  12. Understand the two scrypt API styles in PyNaCl

    main

    PyNaCl (via libsodium) provides two ways to interact with the scrypt Key Derivation Function (KDF):

    1. Simplified API: Uses opslimit (CPU load) and memlimit (memory load) parameters. This is a libsodium-specific implementation designed for ease of use.
    2. Traditional API: Uses the standard (N, r, p) parameter triple (cost, block size, and parallelization factor). This follows the specification found in RFC 7914.

    You can use nacl.bindings.nacl_bindings_pick_scrypt_params to translate libsodium's opslimit and memlimit into the traditional (N, r, p) values.