chacha20.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // chacha20.go - RFC 7539 ChaCha20
  2. //
  3. // To the extent possible under law, Yawning Angel has waived all copyright
  4. // and related or neighboring rights to the software, 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 hs1siv
  8. import (
  9. "encoding/binary"
  10. "math/bits"
  11. )
  12. const (
  13. sigma0 = 0x61707865
  14. sigma1 = 0x3320646e
  15. sigma2 = 0x79622d32
  16. sigma3 = 0x6b206574
  17. chachaKeySize = 32
  18. chachaNonceSize = 12
  19. chachaStateSize = 16
  20. chachaBlockSize = 64
  21. chachaRounds = 20 // Parameter r
  22. )
  23. type chachaState [chachaStateSize]uint32
  24. func chacha20(key, nonce, in, out []byte, initialCounter uint32) {
  25. if len(key) != chachaKeySize {
  26. panic("hs1siv: invalid chacha key size")
  27. }
  28. if len(nonce) != chachaNonceSize {
  29. panic("hs1siv: invalid chacha nonce size")
  30. }
  31. if len(in) == 0 {
  32. return
  33. }
  34. if len(out) < len(in) {
  35. in = in[:len(out)]
  36. }
  37. _, _ = key[31], nonce[11] // Bounds check elimination.
  38. s := &chachaState{
  39. sigma0, sigma1, sigma2, sigma3,
  40. binary.LittleEndian.Uint32(key[0:4]),
  41. binary.LittleEndian.Uint32(key[4:8]),
  42. binary.LittleEndian.Uint32(key[8:12]),
  43. binary.LittleEndian.Uint32(key[12:16]),
  44. binary.LittleEndian.Uint32(key[16:20]),
  45. binary.LittleEndian.Uint32(key[20:24]),
  46. binary.LittleEndian.Uint32(key[24:28]),
  47. binary.LittleEndian.Uint32(key[28:32]),
  48. initialCounter,
  49. binary.LittleEndian.Uint32(nonce[0:4]),
  50. binary.LittleEndian.Uint32(nonce[4:8]),
  51. binary.LittleEndian.Uint32(nonce[8:12]),
  52. }
  53. chachaXORKeyStream(s, in, out)
  54. // Purge the state off the stack.
  55. burnUint32s(s[:])
  56. }
  57. func chachaXORKeyStreamRef(s *chachaState, in, out []byte) {
  58. // Process full blocks.
  59. off, inLen := 0, len(in)
  60. if fullBlocks := inLen / chachaBlockSize; fullBlocks > 0 {
  61. chachaBlocksRef(s, in, out, fullBlocks)
  62. off += fullBlocks * chachaBlockSize
  63. inLen -= fullBlocks * chachaBlockSize
  64. }
  65. // Process the partial block, if any.
  66. if inLen > 0 {
  67. var partial [chachaBlockSize]byte
  68. copy(partial[:], in[off:])
  69. chachaBlocksRef(s, partial[:], partial[:], 1)
  70. copy(out[off:], partial[:])
  71. }
  72. }
  73. func chachaBlocksRef(s *chachaState, in, out []byte, nrBlocks int) {
  74. s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15 := s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]
  75. for nrBlocks > 0 {
  76. x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15
  77. for i := chachaRounds; i > 0; i -= 2 {
  78. x0 += x4
  79. x12 ^= x0
  80. x12 = bits.RotateLeft32(x12, 16)
  81. x8 += x12
  82. x4 ^= x8
  83. x4 = bits.RotateLeft32(x4, 12)
  84. x0 += x4
  85. x12 ^= x0
  86. x12 = bits.RotateLeft32(x12, 8)
  87. x8 += x12
  88. x4 ^= x8
  89. x4 = bits.RotateLeft32(x4, 7)
  90. x1 += x5
  91. x13 ^= x1
  92. x13 = bits.RotateLeft32(x13, 16)
  93. x9 += x13
  94. x5 ^= x9
  95. x5 = bits.RotateLeft32(x5, 12)
  96. x1 += x5
  97. x13 ^= x1
  98. x13 = bits.RotateLeft32(x13, 8)
  99. x9 += x13
  100. x5 ^= x9
  101. x5 = bits.RotateLeft32(x5, 7)
  102. x2 += x6
  103. x14 ^= x2
  104. x14 = bits.RotateLeft32(x14, 16)
  105. x10 += x14
  106. x6 ^= x10
  107. x6 = bits.RotateLeft32(x6, 12)
  108. x2 += x6
  109. x14 ^= x2
  110. x14 = bits.RotateLeft32(x14, 8)
  111. x10 += x14
  112. x6 ^= x10
  113. x6 = bits.RotateLeft32(x6, 7)
  114. x3 += x7
  115. x15 ^= x3
  116. x15 = bits.RotateLeft32(x15, 16)
  117. x11 += x15
  118. x7 ^= x11
  119. x7 = bits.RotateLeft32(x7, 12)
  120. x3 += x7
  121. x15 ^= x3
  122. x15 = bits.RotateLeft32(x15, 8)
  123. x11 += x15
  124. x7 ^= x11
  125. x7 = bits.RotateLeft32(x7, 7)
  126. x0 += x5
  127. x15 ^= x0
  128. x15 = bits.RotateLeft32(x15, 16)
  129. x10 += x15
  130. x5 ^= x10
  131. x5 = bits.RotateLeft32(x5, 12)
  132. x0 += x5
  133. x15 ^= x0
  134. x15 = bits.RotateLeft32(x15, 8)
  135. x10 += x15
  136. x5 ^= x10
  137. x5 = bits.RotateLeft32(x5, 7)
  138. x1 += x6
  139. x12 ^= x1
  140. x12 = bits.RotateLeft32(x12, 16)
  141. x11 += x12
  142. x6 ^= x11
  143. x6 = bits.RotateLeft32(x6, 12)
  144. x1 += x6
  145. x12 ^= x1
  146. x12 = bits.RotateLeft32(x12, 8)
  147. x11 += x12
  148. x6 ^= x11
  149. x6 = bits.RotateLeft32(x6, 7)
  150. x2 += x7
  151. x13 ^= x2
  152. x13 = bits.RotateLeft32(x13, 16)
  153. x8 += x13
  154. x7 ^= x8
  155. x7 = bits.RotateLeft32(x7, 12)
  156. x2 += x7
  157. x13 ^= x2
  158. x13 = bits.RotateLeft32(x13, 8)
  159. x8 += x13
  160. x7 ^= x8
  161. x7 = bits.RotateLeft32(x7, 7)
  162. x3 += x4
  163. x14 ^= x3
  164. x14 = bits.RotateLeft32(x14, 16)
  165. x9 += x14
  166. x4 ^= x9
  167. x4 = bits.RotateLeft32(x4, 12)
  168. x3 += x4
  169. x14 ^= x3
  170. x14 = bits.RotateLeft32(x14, 8)
  171. x9 += x14
  172. x4 ^= x9
  173. x4 = bits.RotateLeft32(x4, 7)
  174. }
  175. _, _ = in[chachaBlockSize-1], out[chachaBlockSize-1] // Bounds check elimination.
  176. binary.LittleEndian.PutUint32(out[0:4], binary.LittleEndian.Uint32(in[0:4])^(x0+s0))
  177. binary.LittleEndian.PutUint32(out[4:8], binary.LittleEndian.Uint32(in[4:8])^(x1+s1))
  178. binary.LittleEndian.PutUint32(out[8:12], binary.LittleEndian.Uint32(in[8:12])^(x2+s2))
  179. binary.LittleEndian.PutUint32(out[12:16], binary.LittleEndian.Uint32(in[12:16])^(x3+s3))
  180. binary.LittleEndian.PutUint32(out[16:20], binary.LittleEndian.Uint32(in[16:20])^(x4+s4))
  181. binary.LittleEndian.PutUint32(out[20:24], binary.LittleEndian.Uint32(in[20:24])^(x5+s5))
  182. binary.LittleEndian.PutUint32(out[24:28], binary.LittleEndian.Uint32(in[24:28])^(x6+s6))
  183. binary.LittleEndian.PutUint32(out[28:32], binary.LittleEndian.Uint32(in[28:32])^(x7+s7))
  184. binary.LittleEndian.PutUint32(out[32:36], binary.LittleEndian.Uint32(in[32:36])^(x8+s8))
  185. binary.LittleEndian.PutUint32(out[36:40], binary.LittleEndian.Uint32(in[36:40])^(x9+s9))
  186. binary.LittleEndian.PutUint32(out[40:44], binary.LittleEndian.Uint32(in[40:44])^(x10+s10))
  187. binary.LittleEndian.PutUint32(out[44:48], binary.LittleEndian.Uint32(in[44:48])^(x11+s11))
  188. binary.LittleEndian.PutUint32(out[48:52], binary.LittleEndian.Uint32(in[48:52])^(x12+s12))
  189. binary.LittleEndian.PutUint32(out[52:56], binary.LittleEndian.Uint32(in[52:56])^(x13+s13))
  190. binary.LittleEndian.PutUint32(out[56:60], binary.LittleEndian.Uint32(in[56:60])^(x14+s14))
  191. binary.LittleEndian.PutUint32(out[60:64], binary.LittleEndian.Uint32(in[60:64])^(x15+s15))
  192. in, out = in[chachaBlockSize:], out[chachaBlockSize:]
  193. s12, nrBlocks = s12+1, nrBlocks-1
  194. }
  195. s[12] = s12
  196. }