The NaCl (pronounced "salt") packages provide high-level, easy-to-use cryptographic operations based on Daniel J. Bernstein's Networking and Cryptography library. These packages offer authenticated encryption, message authentication, and digital signatures with a focus on simplicity and security.
Public-key authenticated encryption using Curve25519, XSalsa20, and Poly1305. Box provides end-to-end encryption between two parties.
const (
// Overhead is the number of bytes added by encryption.
Overhead = secretbox.Overhead
// AnonymousOverhead is the number of bytes added by anonymous encryption.
AnonymousOverhead = Overhead + 32
)// GenerateKey generates a new public/private key pair suitable for use with box.
func GenerateKey(rand io.Reader) (publicKey, privateKey *[32]byte, err error)
// Seal appends an encrypted and authenticated message to out.
// The nonce must be unique for each message with the same key pair.
// The message is encrypted and authenticated with Curve25519, XSalsa20, and Poly1305.
func Seal(out, message []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) []byte
// Open authenticates and decrypts a box produced by Seal.
// The message is only returned if authentication succeeds.
func Open(out, box []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) ([]byte, bool)
// Precompute calculates the shared key between peersPublicKey and privateKey
// and writes it to sharedKey. The shared key can be used with SealAfterPrecomputation
// and OpenAfterPrecomputation for improved performance when encrypting multiple messages.
func Precompute(sharedKey, peersPublicKey, privateKey *[32]byte)
// SealAfterPrecomputation encrypts a message using a precomputed shared key.
func SealAfterPrecomputation(out, message []byte, nonce *[24]byte, sharedKey *[32]byte) []byte
// OpenAfterPrecomputation decrypts a message using a precomputed shared key.
func OpenAfterPrecomputation(out, box []byte, nonce *[24]byte, sharedKey *[32]byte) ([]byte, bool)
// SealAnonymous encrypts a message to a recipient's public key.
// Unlike Seal, the sender's identity is not included in the message.
// The recipient must try each of their private keys to decrypt.
func SealAnonymous(out, message []byte, recipient *[32]byte, rand io.Reader) ([]byte, error)
// OpenAnonymous decrypts a message encrypted with SealAnonymous.
func OpenAnonymous(out, box []byte, publicKey, privateKey *[32]byte) (message []byte, ok bool)package main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/nacl/box"
)
func main() {
// Generate key pairs for Alice and Bob
alicePub, alicePriv, _ := box.GenerateKey(rand.Reader)
bobPub, bobPriv, _ := box.GenerateKey(rand.Reader)
// Alice encrypts a message to Bob
message := []byte("Hello, Bob!")
var nonce [24]byte
rand.Read(nonce[:])
encrypted := box.Seal(nil, message, &nonce, bobPub, alicePriv)
fmt.Printf("Encrypted: %x\n", encrypted)
// Bob decrypts the message from Alice
decrypted, ok := box.Open(nil, encrypted, &nonce, alicePub, bobPriv)
if !ok {
panic("decryption failed")
}
fmt.Printf("Decrypted: %s\n", decrypted)
}package main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/nacl/box"
)
func main() {
// Generate key pairs
alicePub, alicePriv, _ := box.GenerateKey(rand.Reader)
bobPub, bobPriv, _ := box.GenerateKey(rand.Reader)
// Precompute shared keys (do this once)
var aliceShared, bobShared [32]byte
box.Precompute(&aliceShared, bobPub, alicePriv)
box.Precompute(&bobShared, alicePub, bobPriv)
// Alice encrypts multiple messages efficiently
message1 := []byte("First message")
message2 := []byte("Second message")
var nonce1, nonce2 [24]byte
rand.Read(nonce1[:])
rand.Read(nonce2[:])
encrypted1 := box.SealAfterPrecomputation(nil, message1, &nonce1, &aliceShared)
encrypted2 := box.SealAfterPrecomputation(nil, message2, &nonce2, &aliceShared)
// Bob decrypts efficiently
decrypted1, _ := box.OpenAfterPrecomputation(nil, encrypted1, &nonce1, &bobShared)
decrypted2, _ := box.OpenAfterPrecomputation(nil, encrypted2, &nonce2, &bobShared)
fmt.Printf("Message 1: %s\n", decrypted1)
fmt.Printf("Message 2: %s\n", decrypted2)
}package main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/nacl/box"
)
func main() {
// Generate Bob's key pair
bobPub, bobPriv, _ := box.GenerateKey(rand.Reader)
// Anonymous sender encrypts to Bob
message := []byte("Anonymous message")
encrypted, _ := box.SealAnonymous(nil, message, bobPub, rand.Reader)
fmt.Printf("Encrypted: %x\n", encrypted)
// Bob decrypts (doesn't know who sent it)
decrypted, ok := box.OpenAnonymous(nil, encrypted, bobPub, bobPriv)
if !ok {
panic("decryption failed")
}
fmt.Printf("Decrypted: %s\n", decrypted)
}Secret-key authenticated encryption using XSalsa20 and Poly1305. Secretbox provides symmetric authenticated encryption.
const Overhead = poly1305.TagSize // 16 bytes// Seal appends an encrypted and authenticated message to out.
// The nonce must be unique for each distinct message with the same key.
// The key must be 32 bytes.
func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte
// Open authenticates and decrypts a box produced by Seal.
// The message is only returned if authentication succeeds.
func Open(out, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool)package main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/nacl/secretbox"
)
func main() {
// Generate a random key
var key [32]byte
rand.Read(key[:])
// Encrypt a message
message := []byte("Secret message")
var nonce [24]byte
rand.Read(nonce[:])
encrypted := secretbox.Seal(nil, message, &nonce, &key)
fmt.Printf("Encrypted: %x\n", encrypted)
// Decrypt the message
decrypted, ok := secretbox.Open(nil, encrypted, &nonce, &key)
if !ok {
panic("decryption failed")
}
fmt.Printf("Decrypted: %s\n", decrypted)
// Tampered ciphertext fails authentication
encrypted[0] ^= 1
_, ok = secretbox.Open(nil, encrypted, &nonce, &key)
fmt.Printf("Tampered decryption failed: %v\n", !ok)
}Message authentication using HMAC-SHA-512 truncated to 32 bytes.
const (
Size = 32 // Authenticator size in bytes
KeySize = 32 // Key size in bytes
)// Sum generates an authenticator for message m using key.
func Sum(m []byte, key *[KeySize]byte) *[Size]byte
// Verify reports whether digest is a valid authenticator for message m.
func Verify(digest []byte, m []byte, key *[KeySize]byte) boolpackage main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/nacl/auth"
)
func main() {
// Generate a random key
var key [32]byte
rand.Read(key[:])
// Authenticate a message
message := []byte("Message to authenticate")
mac := auth.Sum(message, &key)
fmt.Printf("MAC: %x\n", mac)
// Verify the MAC
valid := auth.Verify(mac[:], message, &key)
fmt.Printf("Valid: %v\n", valid)
// Wrong message fails verification
wrongValid := auth.Verify(mac[:], []byte("Wrong message"), &key)
fmt.Printf("Wrong valid: %v\n", wrongValid)
// Tampered MAC fails verification
mac[0] ^= 1
tamperedValid := auth.Verify(mac[:], message, &key)
fmt.Printf("Tampered valid: %v\n", tamperedValid)
}Message signing using Ed25519 signatures.
const Overhead = 64 // Signature overhead in bytes// GenerateKey generates a new public/private key pair for signing.
func GenerateKey(rand io.Reader) (publicKey *[32]byte, privateKey *[64]byte, err error)
// Sign appends a signed message to out.
// The signed message includes both the signature and the message itself.
func Sign(out, message []byte, privateKey *[64]byte) []byte
// Open verifies a signed message and returns the message if the signature is valid.
// The message is only returned if the signature is valid.
func Open(out, signedMessage []byte, publicKey *[32]byte) ([]byte, bool)package main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/nacl/sign"
)
func main() {
// Generate key pair
publicKey, privateKey, _ := sign.GenerateKey(rand.Reader)
// Sign a message
message := []byte("Important message")
signedMessage := sign.Sign(nil, message, privateKey)
fmt.Printf("Signed message length: %d bytes\n", len(signedMessage))
fmt.Printf("Signature: %x...\n", signedMessage[:32])
// Verify and extract message
extractedMessage, ok := sign.Open(nil, signedMessage, publicKey)
if !ok {
panic("verification failed")
}
fmt.Printf("Extracted: %s\n", extractedMessage)
// Invalid signature fails
signedMessage[0] ^= 1
_, ok = sign.Open(nil, signedMessage, publicKey)
fmt.Printf("Tampered verification failed: %v\n", !ok)
}| Feature | Box (nacl/box) | Secretbox (nacl/secretbox) |
|---|---|---|
| Encryption | Public-key (asymmetric) | Secret-key (symmetric) |
| Key Exchange | Built-in (Curve25519) | Manual key distribution |
| Sender Auth | Yes (sender's private key) | No (shared secret only) |
| Use Case | Peer-to-peer messaging | Encrypted storage, VPN |
| Key Size | 32 bytes (public + private) | 32 bytes (shared secret) |
| Feature | Auth (nacl/auth) | Sign (nacl/sign) |
|---|---|---|
| Type | MAC (symmetric) | Digital signature (asymmetric) |
| Verification | Shared secret required | Public key only |
| Non-repudiation | No | Yes |
| Use Case | Message integrity | Publicly verifiable signatures |
| Output Size | 32 bytes | 64 bytes |
// Generate ephemeral key pairs for each session
alicePub, alicePriv, _ := box.GenerateKey(rand.Reader)
bobPub, bobPriv, _ := box.GenerateKey(rand.Reader)
// Exchange public keys
// ... send alicePub to Bob, receive bobPub from Bob
// Precompute for session
var shared [32]byte
box.Precompute(&shared, bobPub, alicePriv)
// Use shared key with secretbox for messages
// (more efficient than repeated box operations)// Generate random key
var key [32]byte
rand.Read(key[:])
// Encrypt file contents
var nonce [24]byte
rand.Read(nonce[:])
encrypted := secretbox.Seal(nonce[:], fileData, &nonce, &key)
// Store encrypted with key encrypted by password-derived key
// (use argon2.IDKey from hashing.md to derive key from password)// Producer and consumer share auth key
var authKey [32]byte
// ... distribute securely
// Producer creates authenticated message
message := []byte("queue message")
mac := auth.Sum(message, &authKey)
signedMessage := append(message, mac[:]...)
// Consumer verifies and processes
receivedMessage := signedMessage[:len(signedMessage)-32]
receivedMAC := signedMessage[len(signedMessage)-32:]
if auth.Verify(receivedMAC, receivedMessage, &authKey) {
// Process message
}If migrating from NaCl to other packages: