Cryptography in the cryptutils

Cryptography in the cryptutils

The common subpackage contains the cryptographic packages.


These are the basic principles guiding the cryptography in these programs.

  1. NaCl is used for most cryptographic primitives.
  2. Ed25519 should be used for digital signatures.
  3. SHA-256 may be used to generate an identifier for data, though this is only used in the experimental public key programs as a key identifier.
  4. Random nonces should be generated for each new message. The nonce is prepended to the ciphertext.
  5. Scrypt should be used to derive symmetric keys from passphrases. Passphrases are UTF-8 encoded octet strings. The salt is a randomly-generated 32-byte octet string; the salt will be prepended to any data encrypted using a passphrase. The Scrypt parameters are N=1048576, r=8, p=4 to provide a high cost for deriving symmetric keys.
  6. The operating system’s PRNG (on Linux, this is /dev/urandom) is used as the source of randomness.
  7. Cryptographic operations return boolean success indicators: the cause of the error is not returned.

Symmetric cryptography

Symmetric cryptography uses NaCl’s secretbox, which means XSalsa20 and Poly1305. Plaintext is encrypted as such:

  1. A 24-byte nonce for the message is randomly generated.
  2. The message is encrypted with the key.
  3. The resulting ciphertext is appended to the nonce.

Files are encrypted similarly, but the package only provides functions for encrypting and decrypting using passphrases.

  1. The user presents a file path, a UTF-8-encoded passphrase, and an octet string containing the plaintext.
  2. A random, 32-byte salt is generated.
  3. This newly-generated salt and the passphrase are sent to Scrypt (N=1048576, r=8, p=4) to generate a 32-byte secretbox key.
  4. The plaintext is encrypted as per the previous section.
  5. The ciphertext is appended to the salt, and the resulting octet string is written to disk to the path requested.

Public-key cryptography

The public-key cryptography used in these programs employ the following key structure:

type PrivateKey struct {
    // Curve25519
    D   *[32]byte // Decryption key (encryption private key).
    // Ed25519
    S   *[64]byte // Signature key (signing private key).

type PublicKey struct {
    // Curve25519
    E   *[32]byte // Encryption key (encryption public key).
    // Ed25519
    V   *[32]byte // Verification key (signing public key).

These keys are passed around as octet strings, where the private key packs the D, S, E, and V values in; public keys pack in the E and V values in. Packing is done by appending the values to each other.

Encryption is done as follows:

  1. An ephemeral Curve25519 key pair is generated.
  2. The ephemeral key pair is used to perform a key exchange with the peer’s public key. The private key is then discarded.
  3. The key exchange produces a symmetric key, and the plaintext is encrypted as in the symmetric section.
  4. The resulting ciphertext is appended to the ephemeral public key.

There is an encrypt-and-verify operation that expects a signature to be appended to the plaintext. There is currently no way to distinguish between a signed and unsigned message. The encrypt-and-sign is done exactly as encryption, except that the message is signed prior to encryption and this signature is appended to the message. Signatures are a fixed size (64 bytes), so the decrypt-and-verify decrypts the message, then verifies the plaintext is large enough to have a signature and uses the final 64 bytes as the signature.