1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
6 Package secretbox encrypts and authenticates small messages.
8 Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with
9 secret-key cryptography. The length of messages is not hidden.
11 It is the caller's responsibility to ensure the uniqueness of nonces—for
12 example, by using nonce 1 for the first message, nonce 2 for the second
13 message, etc. Nonces are long enough that randomly generated nonces have
14 negligible risk of collision.
16 This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
18 package secretbox // import "golang.org/x/crypto/nacl/secretbox"
21 "golang.org/x/crypto/poly1305"
22 "golang.org/x/crypto/salsa20/salsa"
25 // Overhead is the number of bytes of overhead when boxing a message.
26 const Overhead = poly1305.TagSize
28 // setup produces a sub-key and Salsa20 counter given a nonce and key.
29 func setup(subKey *[32]byte, counter *[16]byte, nonce *[24]byte, key *[32]byte) {
30 // We use XSalsa20 for encryption so first we need to generate a
31 // key and nonce with HSalsa20.
33 copy(hNonce[:], nonce[:])
34 salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma)
36 // The final 8 bytes of the original nonce form the new nonce.
37 copy(counter[:], nonce[16:])
40 // sliceForAppend takes a slice and a requested number of bytes. It returns a
41 // slice with the contents of the given slice followed by that many bytes and a
42 // second slice that aliases into it and contains only the extra bytes. If the
43 // original slice has sufficient capacity then no allocation is performed.
44 func sliceForAppend(in []byte, n int) (head, tail []byte) {
45 if total := len(in) + n; cap(in) >= total {
48 head = make([]byte, total)
55 // Seal appends an encrypted and authenticated copy of message to out, which
56 // must not overlap message. The key and nonce pair must be unique for each
57 // distinct message and the output will be Overhead bytes longer than message.
58 func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
61 setup(&subKey, &counter, nonce, key)
63 // The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
64 // Salsa20 works with 64-byte blocks, we also generate 32 bytes of
65 // keystream as a side effect.
66 var firstBlock [64]byte
67 salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
69 var poly1305Key [32]byte
70 copy(poly1305Key[:], firstBlock[:])
72 ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
74 // We XOR up to 32 bytes of message with the keystream generated from
76 firstMessageBlock := message
77 if len(firstMessageBlock) > 32 {
78 firstMessageBlock = firstMessageBlock[:32]
82 out = out[poly1305.TagSize:]
83 for i, x := range firstMessageBlock {
84 out[i] = firstBlock[32+i] ^ x
86 message = message[len(firstMessageBlock):]
88 out = out[len(firstMessageBlock):]
90 // Now encrypt the rest.
92 salsa.XORKeyStream(out, message, &counter, &subKey)
94 var tag [poly1305.TagSize]byte
95 poly1305.Sum(&tag, ciphertext, &poly1305Key)
101 // Open authenticates and decrypts a box produced by Seal and appends the
102 // message to out, which must not overlap box. The output will be Overhead
103 // bytes smaller than box.
104 func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
105 if len(box) < Overhead {
111 setup(&subKey, &counter, nonce, key)
113 // The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
114 // Salsa20 works with 64-byte blocks, we also generate 32 bytes of
115 // keystream as a side effect.
116 var firstBlock [64]byte
117 salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
119 var poly1305Key [32]byte
120 copy(poly1305Key[:], firstBlock[:])
121 var tag [poly1305.TagSize]byte
124 if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) {
128 ret, out := sliceForAppend(out, len(box)-Overhead)
130 // We XOR up to 32 bytes of box with the keystream generated from
133 firstMessageBlock := box
134 if len(firstMessageBlock) > 32 {
135 firstMessageBlock = firstMessageBlock[:32]
137 for i, x := range firstMessageBlock {
138 out[i] = firstBlock[32+i] ^ x
141 box = box[len(firstMessageBlock):]
142 out = out[len(firstMessageBlock):]
144 // Now decrypt the rest.
146 salsa.XORKeyStream(out, box, &counter, &subKey)