chacha20.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // Copryright (C) 2019 Yawning Angel
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as
  5. // published by the Free Software Foundation, either version 3 of the
  6. // License, or (at your option) any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU Affero General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU Affero General Public License
  14. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. // Package chacha20 implements the ChaCha20 stream cipher.
  16. package chacha20 // import "gitlab.com/yawning/chacha20.git"
  17. import (
  18. "crypto/cipher"
  19. "encoding/binary"
  20. "errors"
  21. "math"
  22. "gitlab.com/yawning/chacha20.git/internal/api"
  23. "gitlab.com/yawning/chacha20.git/internal/hardware"
  24. "gitlab.com/yawning/chacha20.git/internal/ref"
  25. )
  26. const (
  27. // KeySize is the ChaCha20 key size in bytes.
  28. KeySize = 32
  29. // NonceSize is the ChaCha20 nonce size in bytes.
  30. NonceSize = 8
  31. // INonceSize is the IETF ChaCha20 nonce size in bytes.
  32. INonceSize = 12
  33. // XNonceSize is the XChaCha20 nonce size in bytes.
  34. XNonceSize = 24
  35. // HNonceSize is the HChaCha20 nonce size in bytes.
  36. HNonceSize = 16
  37. )
  38. var (
  39. // ErrInvalidKey is the error returned when the key is invalid.
  40. ErrInvalidKey = errors.New("chacha20: key length must be KeySize bytes")
  41. // ErrInvalidNonce is the error returned when the nonce is invalid.
  42. ErrInvalidNonce = errors.New("chacha20: nonce length must be NonceSize/INonceSize/XNonceSize bytes")
  43. // ErrInvalidCounter is the error returned when the counter is invalid.
  44. ErrInvalidCounter = errors.New("chacha20: block counter is invalid (out of range)")
  45. supportedImpls []api.Implementation
  46. activeImpl api.Implementation
  47. _ cipher.Stream = (*Cipher)(nil)
  48. )
  49. // Cipher is an instance of ChaCha20/XChaCha20 using a particular key and nonce.
  50. type Cipher struct {
  51. state [api.StateSize]uint32
  52. buf [api.BlockSize]byte
  53. off int
  54. ietf bool
  55. }
  56. // Reset zeros the key data so that it will no longer appear in the process's
  57. // memory.
  58. func (c *Cipher) Reset() {
  59. for i := range c.state {
  60. c.state[i] = 0
  61. }
  62. for i := range c.buf {
  63. c.buf[i] = 0
  64. }
  65. }
  66. // Seek sets the block counter to a given offset.
  67. func (c *Cipher) Seek(blockCounter uint64) error {
  68. if c.ietf {
  69. if blockCounter > math.MaxUint32 {
  70. return ErrInvalidCounter
  71. }
  72. c.state[12] = uint32(blockCounter)
  73. } else {
  74. c.state[12] = uint32(blockCounter)
  75. c.state[13] = uint32(blockCounter >> 32)
  76. }
  77. c.off = api.BlockSize
  78. return nil
  79. }
  80. // ReKey reinitializes the ChaCha20/XChaCha20 instance with the provided key
  81. // and nonce.
  82. func (c *Cipher) ReKey(key, nonce []byte) error {
  83. c.Reset()
  84. return c.doReKey(key, nonce)
  85. }
  86. func (c *Cipher) doReKey(key, nonce []byte) error {
  87. if len(key) != KeySize {
  88. return ErrInvalidKey
  89. }
  90. var subKey []byte
  91. switch len(nonce) {
  92. case NonceSize, INonceSize:
  93. case XNonceSize:
  94. subKey = c.buf[:KeySize]
  95. activeImpl.HChaCha(key, nonce, subKey)
  96. key = subKey
  97. nonce = nonce[16:24]
  98. default:
  99. return ErrInvalidNonce
  100. }
  101. _ = key[31] // Force bounds check elimination.
  102. c.state[0] = api.Sigma0
  103. c.state[1] = api.Sigma1
  104. c.state[2] = api.Sigma2
  105. c.state[3] = api.Sigma3
  106. c.state[4] = binary.LittleEndian.Uint32(key[0:4])
  107. c.state[5] = binary.LittleEndian.Uint32(key[4:8])
  108. c.state[6] = binary.LittleEndian.Uint32(key[8:12])
  109. c.state[7] = binary.LittleEndian.Uint32(key[12:16])
  110. c.state[8] = binary.LittleEndian.Uint32(key[16:20])
  111. c.state[9] = binary.LittleEndian.Uint32(key[20:24])
  112. c.state[10] = binary.LittleEndian.Uint32(key[24:28])
  113. c.state[11] = binary.LittleEndian.Uint32(key[28:32])
  114. c.state[12] = 0
  115. if len(nonce) == INonceSize {
  116. _ = nonce[11] // Force bounds check elimination.
  117. c.state[13] = binary.LittleEndian.Uint32(nonce[0:4])
  118. c.state[14] = binary.LittleEndian.Uint32(nonce[4:8])
  119. c.state[15] = binary.LittleEndian.Uint32(nonce[8:12])
  120. c.ietf = true
  121. } else {
  122. _ = nonce[7] // Force bounds check elimination.
  123. c.state[13] = 0
  124. c.state[14] = binary.LittleEndian.Uint32(nonce[0:4])
  125. c.state[15] = binary.LittleEndian.Uint32(nonce[4:8])
  126. c.ietf = false
  127. }
  128. c.off = api.BlockSize
  129. if subKey != nil {
  130. for i := range subKey {
  131. subKey[i] = 0
  132. }
  133. }
  134. return nil
  135. }
  136. // New returns a new ChaCha20/XChaCha20 instance.
  137. func New(key, nonce []byte) (*Cipher, error) {
  138. var c Cipher
  139. if err := c.doReKey(key, nonce); err != nil {
  140. return nil, err
  141. }
  142. return &c, nil
  143. }
  144. // HChaCha is the HChaCha20 hash function used to make XChaCha.
  145. func HChaCha(key, nonce []byte, dst *[32]byte) {
  146. activeImpl.HChaCha(key, nonce, dst[:])
  147. }
  148. // XORKeyStream sets dst to the result of XORing src with the key stream. Dst
  149. // and src may be the same slice but otherwise should not overlap.
  150. func (c *Cipher) XORKeyStream(dst, src []byte) {
  151. if len(dst) < len(src) {
  152. src = src[:len(dst)]
  153. }
  154. for remaining := len(src); remaining > 0; {
  155. // Process multiple blocks at once.
  156. if c.off == api.BlockSize {
  157. nrBlocks := remaining / api.BlockSize
  158. directBytes := nrBlocks * api.BlockSize
  159. if nrBlocks > 0 {
  160. c.doBlocks(dst, src, nrBlocks)
  161. remaining -= directBytes
  162. if remaining == 0 {
  163. return
  164. }
  165. dst = dst[directBytes:]
  166. src = src[directBytes:]
  167. }
  168. // If there's a partial block, generate 1 block of keystream into
  169. // the internal buffer.
  170. c.doBlocks(c.buf[:], nil, 1)
  171. c.off = 0
  172. }
  173. // Process partial blocks from the buffered keystream.
  174. toXor := api.BlockSize - c.off
  175. if remaining < toXor {
  176. toXor = remaining
  177. }
  178. if toXor > 0 {
  179. // The inliner doesn't want to inline this function, but my
  180. // attempts to force BCE don't seem to work with manual
  181. // inlining.
  182. //
  183. // Taking the extra function call overhead here appears to be
  184. // worth it.
  185. c.xorBufBytes(dst, src, toXor)
  186. dst = dst[toXor:]
  187. src = src[toXor:]
  188. remaining -= toXor
  189. }
  190. }
  191. }
  192. func (c *Cipher) xorBufBytes(dst, src []byte, n int) {
  193. // Force bounds check elimination.
  194. buf := c.buf[c.off:]
  195. _ = buf[n-1]
  196. _ = dst[n-1]
  197. _ = src[n-1]
  198. for i := 0; i < n; i++ {
  199. dst[i] = buf[i] ^ src[i]
  200. }
  201. c.off += n
  202. }
  203. // KeyStream sets dst to the raw keystream.
  204. func (c *Cipher) KeyStream(dst []byte) {
  205. for remaining := len(dst); remaining > 0; {
  206. // Process multiple blocks at once.
  207. if c.off == api.BlockSize {
  208. nrBlocks := remaining / api.BlockSize
  209. directBytes := nrBlocks * api.BlockSize
  210. if nrBlocks > 0 {
  211. c.doBlocks(dst, nil, nrBlocks)
  212. remaining -= directBytes
  213. if remaining == 0 {
  214. return
  215. }
  216. dst = dst[directBytes:]
  217. }
  218. // If there's a partial block, generate 1 block of keystream into
  219. // the internal buffer.
  220. c.doBlocks(c.buf[:], nil, 1)
  221. c.off = 0
  222. }
  223. // Process partial blocks from the buffered keystream.
  224. toCopy := api.BlockSize - c.off
  225. if remaining < toCopy {
  226. toCopy = remaining
  227. }
  228. if toCopy > 0 {
  229. copy(dst[:toCopy], c.buf[c.off:c.off+toCopy])
  230. dst = dst[toCopy:]
  231. remaining -= toCopy
  232. c.off += toCopy
  233. }
  234. }
  235. }
  236. func (c *Cipher) doBlocks(dst, src []byte, nrBlocks int) {
  237. if c.ietf {
  238. ctr := uint64(c.state[12])
  239. if ctr+uint64(nrBlocks) > math.MaxUint32 {
  240. panic("chacha20: will exceed key stream per nonce limit")
  241. }
  242. }
  243. activeImpl.Blocks(&c.state, dst, src, nrBlocks)
  244. }
  245. func init() {
  246. supportedImpls = hardware.Register(supportedImpls)
  247. supportedImpls = ref.Register(supportedImpls)
  248. activeImpl = supportedImpls[0]
  249. }