github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/crypto/nacl/secretbox/secretbox.go (about)

     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.
     4  
     5  /*
     6  Package secretbox encrypts and authenticates small messages.
     7  
     8  Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with
     9  secret-key cryptography. The length of messages is not hidden.
    10  
    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.
    15  
    16  This package is interoperable with NaCl: http://nacl.cr.yp.to/secretbox.html.
    17  */
    18  package secretbox // import "golang.org/x/crypto/nacl/secretbox"
    19  
    20  import (
    21  	"golang.org/x/crypto/poly1305"
    22  	"golang.org/x/crypto/salsa20/salsa"
    23  )
    24  
    25  // Overhead is the number of bytes of overhead when boxing a message.
    26  const Overhead = poly1305.TagSize
    27  
    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.
    32  	var hNonce [16]byte
    33  	copy(hNonce[:], nonce[:])
    34  	salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma)
    35  
    36  	// The final 8 bytes of the original nonce form the new nonce.
    37  	copy(counter[:], nonce[16:])
    38  }
    39  
    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 {
    46  		head = in[:total]
    47  	} else {
    48  		head = make([]byte, total)
    49  		copy(head, in)
    50  	}
    51  	tail = head[len(in):]
    52  	return
    53  }
    54  
    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 {
    59  	var subKey [32]byte
    60  	var counter [16]byte
    61  	setup(&subKey, &counter, nonce, key)
    62  
    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)
    68  
    69  	var poly1305Key [32]byte
    70  	copy(poly1305Key[:], firstBlock[:])
    71  
    72  	ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
    73  
    74  	// We XOR up to 32 bytes of message with the keystream generated from
    75  	// the first block.
    76  	firstMessageBlock := message
    77  	if len(firstMessageBlock) > 32 {
    78  		firstMessageBlock = firstMessageBlock[:32]
    79  	}
    80  
    81  	tagOut := out
    82  	out = out[poly1305.TagSize:]
    83  	for i, x := range firstMessageBlock {
    84  		out[i] = firstBlock[32+i] ^ x
    85  	}
    86  	message = message[len(firstMessageBlock):]
    87  	ciphertext := out
    88  	out = out[len(firstMessageBlock):]
    89  
    90  	// Now encrypt the rest.
    91  	counter[8] = 1
    92  	salsa.XORKeyStream(out, message, &counter, &subKey)
    93  
    94  	var tag [poly1305.TagSize]byte
    95  	poly1305.Sum(&tag, ciphertext, &poly1305Key)
    96  	copy(tagOut, tag[:])
    97  
    98  	return ret
    99  }
   100  
   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 {
   106  		return nil, false
   107  	}
   108  
   109  	var subKey [32]byte
   110  	var counter [16]byte
   111  	setup(&subKey, &counter, nonce, key)
   112  
   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)
   118  
   119  	var poly1305Key [32]byte
   120  	copy(poly1305Key[:], firstBlock[:])
   121  	var tag [poly1305.TagSize]byte
   122  	copy(tag[:], box)
   123  
   124  	if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) {
   125  		return nil, false
   126  	}
   127  
   128  	ret, out := sliceForAppend(out, len(box)-Overhead)
   129  
   130  	// We XOR up to 32 bytes of box with the keystream generated from
   131  	// the first block.
   132  	box = box[Overhead:]
   133  	firstMessageBlock := box
   134  	if len(firstMessageBlock) > 32 {
   135  		firstMessageBlock = firstMessageBlock[:32]
   136  	}
   137  	for i, x := range firstMessageBlock {
   138  		out[i] = firstBlock[32+i] ^ x
   139  	}
   140  
   141  	box = box[len(firstMessageBlock):]
   142  	out = out[len(firstMessageBlock):]
   143  
   144  	// Now decrypt the rest.
   145  	counter[8] = 1
   146  	salsa.XORKeyStream(out, box, &counter, &subKey)
   147  
   148  	return ret, true
   149  }