secretbox.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. Package secretbox encrypts and authenticates small messages.
  6. Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with
  7. secret-key cryptography. The length of messages is not hidden.
  8. It is the caller's responsibility to ensure the uniqueness of nonces—for
  9. example, by using nonce 1 for the first message, nonce 2 for the second
  10. message, etc. Nonces are long enough that randomly generated nonces have
  11. negligible risk of collision.
  12. This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
  13. */
  14. package secretbox // import "golang.org/x/crypto/nacl/secretbox"
  15. import (
  16. "golang.org/x/crypto/poly1305"
  17. "golang.org/x/crypto/salsa20/salsa"
  18. )
  19. // Overhead is the number of bytes of overhead when boxing a message.
  20. const Overhead = poly1305.TagSize
  21. // setup produces a sub-key and Salsa20 counter given a nonce and key.
  22. func setup(subKey *[32]byte, counter *[16]byte, nonce *[24]byte, key *[32]byte) {
  23. // We use XSalsa20 for encryption so first we need to generate a
  24. // key and nonce with HSalsa20.
  25. var hNonce [16]byte
  26. copy(hNonce[:], nonce[:])
  27. salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma)
  28. // The final 8 bytes of the original nonce form the new nonce.
  29. copy(counter[:], nonce[16:])
  30. }
  31. // sliceForAppend takes a slice and a requested number of bytes. It returns a
  32. // slice with the contents of the given slice followed by that many bytes and a
  33. // second slice that aliases into it and contains only the extra bytes. If the
  34. // original slice has sufficient capacity then no allocation is performed.
  35. func sliceForAppend(in []byte, n int) (head, tail []byte) {
  36. if total := len(in) + n; cap(in) >= total {
  37. head = in[:total]
  38. } else {
  39. head = make([]byte, total)
  40. copy(head, in)
  41. }
  42. tail = head[len(in):]
  43. return
  44. }
  45. // Seal appends an encrypted and authenticated copy of message to out, which
  46. // must not overlap message. The key and nonce pair must be unique for each
  47. // distinct message and the output will be Overhead bytes longer than message.
  48. func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
  49. var subKey [32]byte
  50. var counter [16]byte
  51. setup(&subKey, &counter, nonce, key)
  52. // The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
  53. // Salsa20 works with 64-byte blocks, we also generate 32 bytes of
  54. // keystream as a side effect.
  55. var firstBlock [64]byte
  56. salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
  57. var poly1305Key [32]byte
  58. copy(poly1305Key[:], firstBlock[:])
  59. ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
  60. // We XOR up to 32 bytes of message with the keystream generated from
  61. // the first block.
  62. firstMessageBlock := message
  63. if len(firstMessageBlock) > 32 {
  64. firstMessageBlock = firstMessageBlock[:32]
  65. }
  66. tagOut := out
  67. out = out[poly1305.TagSize:]
  68. for i, x := range firstMessageBlock {
  69. out[i] = firstBlock[32+i] ^ x
  70. }
  71. message = message[len(firstMessageBlock):]
  72. ciphertext := out
  73. out = out[len(firstMessageBlock):]
  74. // Now encrypt the rest.
  75. counter[8] = 1
  76. salsa.XORKeyStream(out, message, &counter, &subKey)
  77. var tag [poly1305.TagSize]byte
  78. poly1305.Sum(&tag, ciphertext, &poly1305Key)
  79. copy(tagOut, tag[:])
  80. return ret
  81. }
  82. // Open authenticates and decrypts a box produced by Seal and appends the
  83. // message to out, which must not overlap box. The output will be Overhead
  84. // bytes smaller than box.
  85. func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
  86. if len(box) < Overhead {
  87. return nil, false
  88. }
  89. var subKey [32]byte
  90. var counter [16]byte
  91. setup(&subKey, &counter, nonce, key)
  92. // The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
  93. // Salsa20 works with 64-byte blocks, we also generate 32 bytes of
  94. // keystream as a side effect.
  95. var firstBlock [64]byte
  96. salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
  97. var poly1305Key [32]byte
  98. copy(poly1305Key[:], firstBlock[:])
  99. var tag [poly1305.TagSize]byte
  100. copy(tag[:], box)
  101. if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) {
  102. return nil, false
  103. }
  104. ret, out := sliceForAppend(out, len(box)-Overhead)
  105. // We XOR up to 32 bytes of box with the keystream generated from
  106. // the first block.
  107. box = box[Overhead:]
  108. firstMessageBlock := box
  109. if len(firstMessageBlock) > 32 {
  110. firstMessageBlock = firstMessageBlock[:32]
  111. }
  112. for i, x := range firstMessageBlock {
  113. out[i] = firstBlock[32+i] ^ x
  114. }
  115. box = box[len(firstMessageBlock):]
  116. out = out[len(firstMessageBlock):]
  117. // Now decrypt the rest.
  118. counter[8] = 1
  119. salsa.XORKeyStream(out, box, &counter, &subKey)
  120. return ret, true
  121. }