chacha20.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // chacha20.go - A ChaCha stream cipher implementation.
  2. //
  3. // To the extent possible under law, Yawning Angel has waived all copyright
  4. // and related or neighboring rights to chacha20, using the Creative
  5. // Commons "CC0" public domain dedication. See LICENSE or
  6. // <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
  7. package chacha20
  8. import (
  9. "crypto/cipher"
  10. "encoding/binary"
  11. "errors"
  12. "math"
  13. "runtime"
  14. )
  15. const (
  16. // KeySize is the ChaCha20 key size in bytes.
  17. KeySize = 32
  18. // NonceSize is the ChaCha20 nonce size in bytes.
  19. NonceSize = 8
  20. // INonceSize is the IETF ChaCha20 nonce size in bytes.
  21. INonceSize = 12
  22. // XNonceSize is the XChaCha20 nonce size in bytes.
  23. XNonceSize = 24
  24. // HNonceSize is the HChaCha20 nonce size in bytes.
  25. HNonceSize = 16
  26. // BlockSize is the ChaCha20 block size in bytes.
  27. BlockSize = 64
  28. stateSize = 16
  29. chachaRounds = 20
  30. // The constant "expand 32-byte k" as little endian uint32s.
  31. sigma0 = uint32(0x61707865)
  32. sigma1 = uint32(0x3320646e)
  33. sigma2 = uint32(0x79622d32)
  34. sigma3 = uint32(0x6b206574)
  35. )
  36. var (
  37. // ErrInvalidKey is the error returned when the key is invalid.
  38. ErrInvalidKey = errors.New("key length must be KeySize bytes")
  39. // ErrInvalidNonce is the error returned when the nonce is invalid.
  40. ErrInvalidNonce = errors.New("nonce length must be NonceSize/INonceSize/XNonceSize bytes")
  41. // ErrInvalidCounter is the error returned when the counter is invalid.
  42. ErrInvalidCounter = errors.New("block counter is invalid (out of range)")
  43. useUnsafe = false
  44. usingVectors = false
  45. blocksFn = blocksRef
  46. )
  47. // A Cipher is an instance of ChaCha20/XChaCha20 using a particular key and
  48. // nonce.
  49. type Cipher struct {
  50. state [stateSize]uint32
  51. buf [BlockSize]byte
  52. off int
  53. ietf bool
  54. }
  55. // Reset zeros the key data so that it will no longer appear in the process's
  56. // memory.
  57. func (c *Cipher) Reset() {
  58. for i := range c.state {
  59. c.state[i] = 0
  60. }
  61. for i := range c.buf {
  62. c.buf[i] = 0
  63. }
  64. }
  65. // XORKeyStream sets dst to the result of XORing src with the key stream. Dst
  66. // and src may be the same slice but otherwise should not overlap.
  67. func (c *Cipher) XORKeyStream(dst, src []byte) {
  68. if len(dst) < len(src) {
  69. src = src[:len(dst)]
  70. }
  71. for remaining := len(src); remaining > 0; {
  72. // Process multiple blocks at once.
  73. if c.off == BlockSize {
  74. nrBlocks := remaining / BlockSize
  75. directBytes := nrBlocks * BlockSize
  76. if nrBlocks > 0 {
  77. blocksFn(&c.state, src, dst, nrBlocks, c.ietf)
  78. remaining -= directBytes
  79. if remaining == 0 {
  80. return
  81. }
  82. dst = dst[directBytes:]
  83. src = src[directBytes:]
  84. }
  85. // If there's a partial block, generate 1 block of keystream into
  86. // the internal buffer.
  87. blocksFn(&c.state, nil, c.buf[:], 1, c.ietf)
  88. c.off = 0
  89. }
  90. // Process partial blocks from the buffered keystream.
  91. toXor := BlockSize - c.off
  92. if remaining < toXor {
  93. toXor = remaining
  94. }
  95. if toXor > 0 {
  96. for i, v := range src[:toXor] {
  97. dst[i] = v ^ c.buf[c.off+i]
  98. }
  99. dst = dst[toXor:]
  100. src = src[toXor:]
  101. remaining -= toXor
  102. c.off += toXor
  103. }
  104. }
  105. }
  106. // KeyStream sets dst to the raw keystream.
  107. func (c *Cipher) KeyStream(dst []byte) {
  108. for remaining := len(dst); remaining > 0; {
  109. // Process multiple blocks at once.
  110. if c.off == BlockSize {
  111. nrBlocks := remaining / BlockSize
  112. directBytes := nrBlocks * BlockSize
  113. if nrBlocks > 0 {
  114. blocksFn(&c.state, nil, dst, nrBlocks, c.ietf)
  115. remaining -= directBytes
  116. if remaining == 0 {
  117. return
  118. }
  119. dst = dst[directBytes:]
  120. }
  121. // If there's a partial block, generate 1 block of keystream into
  122. // the internal buffer.
  123. blocksFn(&c.state, nil, c.buf[:], 1, c.ietf)
  124. c.off = 0
  125. }
  126. // Process partial blocks from the buffered keystream.
  127. toCopy := BlockSize - c.off
  128. if remaining < toCopy {
  129. toCopy = remaining
  130. }
  131. if toCopy > 0 {
  132. copy(dst[:toCopy], c.buf[c.off:c.off+toCopy])
  133. dst = dst[toCopy:]
  134. remaining -= toCopy
  135. c.off += toCopy
  136. }
  137. }
  138. }
  139. // ReKey reinitializes the ChaCha20/XChaCha20 instance with the provided key
  140. // and nonce.
  141. func (c *Cipher) ReKey(key, nonce []byte) error {
  142. if len(key) != KeySize {
  143. return ErrInvalidKey
  144. }
  145. switch len(nonce) {
  146. case NonceSize:
  147. case INonceSize:
  148. case XNonceSize:
  149. var subkey [KeySize]byte
  150. var subnonce [HNonceSize]byte
  151. copy(subnonce[:], nonce[0:16])
  152. HChaCha(key, &subnonce, &subkey)
  153. key = subkey[:]
  154. nonce = nonce[16:24]
  155. defer func() {
  156. for i := range subkey {
  157. subkey[i] = 0
  158. }
  159. }()
  160. default:
  161. return ErrInvalidNonce
  162. }
  163. c.Reset()
  164. c.state[0] = sigma0
  165. c.state[1] = sigma1
  166. c.state[2] = sigma2
  167. c.state[3] = sigma3
  168. c.state[4] = binary.LittleEndian.Uint32(key[0:4])
  169. c.state[5] = binary.LittleEndian.Uint32(key[4:8])
  170. c.state[6] = binary.LittleEndian.Uint32(key[8:12])
  171. c.state[7] = binary.LittleEndian.Uint32(key[12:16])
  172. c.state[8] = binary.LittleEndian.Uint32(key[16:20])
  173. c.state[9] = binary.LittleEndian.Uint32(key[20:24])
  174. c.state[10] = binary.LittleEndian.Uint32(key[24:28])
  175. c.state[11] = binary.LittleEndian.Uint32(key[28:32])
  176. c.state[12] = 0
  177. if len(nonce) == INonceSize {
  178. c.state[13] = binary.LittleEndian.Uint32(nonce[0:4])
  179. c.state[14] = binary.LittleEndian.Uint32(nonce[4:8])
  180. c.state[15] = binary.LittleEndian.Uint32(nonce[8:12])
  181. c.ietf = true
  182. } else {
  183. c.state[13] = 0
  184. c.state[14] = binary.LittleEndian.Uint32(nonce[0:4])
  185. c.state[15] = binary.LittleEndian.Uint32(nonce[4:8])
  186. c.ietf = false
  187. }
  188. c.off = BlockSize
  189. return nil
  190. }
  191. // Seek sets the block counter to a given offset.
  192. func (c *Cipher) Seek(blockCounter uint64) error {
  193. if c.ietf {
  194. if blockCounter > math.MaxUint32 {
  195. return ErrInvalidCounter
  196. }
  197. c.state[12] = uint32(blockCounter)
  198. } else {
  199. c.state[12] = uint32(blockCounter)
  200. c.state[13] = uint32(blockCounter >> 32)
  201. }
  202. c.off = BlockSize
  203. return nil
  204. }
  205. // NewCipher returns a new ChaCha20/XChaCha20 instance.
  206. func NewCipher(key, nonce []byte) (*Cipher, error) {
  207. c := new(Cipher)
  208. if err := c.ReKey(key, nonce); err != nil {
  209. return nil, err
  210. }
  211. return c, nil
  212. }
  213. // HChaCha is the HChaCha20 hash function used to make XChaCha.
  214. func HChaCha(key []byte, nonce *[HNonceSize]byte, out *[32]byte) {
  215. var x [stateSize]uint32 // Last 4 slots unused, sigma hardcoded.
  216. x[0] = binary.LittleEndian.Uint32(key[0:4])
  217. x[1] = binary.LittleEndian.Uint32(key[4:8])
  218. x[2] = binary.LittleEndian.Uint32(key[8:12])
  219. x[3] = binary.LittleEndian.Uint32(key[12:16])
  220. x[4] = binary.LittleEndian.Uint32(key[16:20])
  221. x[5] = binary.LittleEndian.Uint32(key[20:24])
  222. x[6] = binary.LittleEndian.Uint32(key[24:28])
  223. x[7] = binary.LittleEndian.Uint32(key[28:32])
  224. x[8] = binary.LittleEndian.Uint32(nonce[0:4])
  225. x[9] = binary.LittleEndian.Uint32(nonce[4:8])
  226. x[10] = binary.LittleEndian.Uint32(nonce[8:12])
  227. x[11] = binary.LittleEndian.Uint32(nonce[12:16])
  228. hChaChaRef(&x, out)
  229. }
  230. func init() {
  231. switch runtime.GOARCH {
  232. case "386", "amd64":
  233. // Abuse unsafe to skip calling binary.LittleEndian.PutUint32
  234. // in the critical path. This is a big boost on systems that are
  235. // little endian and not overly picky about alignment.
  236. useUnsafe = true
  237. }
  238. }
  239. var _ cipher.Stream = (*Cipher)(nil)