stream.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. package crypto
  2. import (
  3. "bytes"
  4. "crypto/rand"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "golang.org/x/crypto/chacha20"
  9. "golang.org/x/crypto/chacha20poly1305"
  10. "golang.org/x/crypto/poly1305"
  11. )
  12. // public constants
  13. const (
  14. //TagMessage the most common tag, that doesn't add any information about the nature of the message.
  15. TagMessage = 0
  16. // TagPush indicates that the message marks the end of a set of messages,
  17. // but not the end of the stream. For example, a huge JSON string sent as multiple chunks can use this tag to indicate to the application that the string is complete and that it can be decoded. But the stream itself is not closed, and more data may follow.
  18. TagPush = 0x01
  19. // TagRekey "forget" the key used to encrypt this message and the previous ones, and derive a new secret key.
  20. TagRekey = 0x02
  21. // TagFinal indicates that the message marks the end of the stream, and erases the secret key used to encrypt the previous sequence.
  22. TagFinal = TagPush | TagRekey
  23. StreamKeyBytes = chacha20poly1305.KeySize
  24. StreamHeaderBytes = chacha20poly1305.NonceSizeX
  25. // XChaCha20Poly1305IetfABYTES links to crypto_secretstream_xchacha20poly1305_ABYTES
  26. XChaCha20Poly1305IetfABYTES = 16 + 1
  27. )
  28. const cryptoCoreHchacha20InputBytes = 16
  29. /* const crypto_secretstream_xchacha20poly1305_INONCEBYTES = 8 */
  30. const cryptoSecretStreamXchacha20poly1305Counterbytes = 4
  31. var pad0 [16]byte
  32. var invalidKey = errors.New("invalid key")
  33. var invalidInput = errors.New("invalid input")
  34. var cryptoFailure = errors.New("crypto failed")
  35. // crypto_secretstream_xchacha20poly1305_state
  36. type streamState struct {
  37. k [StreamKeyBytes]byte
  38. nonce [chacha20poly1305.NonceSize]byte
  39. pad [8]byte
  40. }
  41. func (s *streamState) reset() {
  42. for i := range s.nonce {
  43. s.nonce[i] = 0
  44. }
  45. s.nonce[0] = 1
  46. }
  47. type Encryptor interface {
  48. Push(m []byte, tag byte) ([]byte, error)
  49. }
  50. type Decryptor interface {
  51. Pull(m []byte) ([]byte, byte, error)
  52. }
  53. type encryptor struct {
  54. streamState
  55. }
  56. type decryptor struct {
  57. streamState
  58. }
  59. func NewStreamKey() []byte {
  60. k := make([]byte, chacha20poly1305.KeySize)
  61. _, _ = rand.Read(k)
  62. return k
  63. }
  64. func NewEncryptor(key []byte) (Encryptor, []byte, error) {
  65. if len(key) != StreamKeyBytes {
  66. return nil, nil, invalidKey
  67. }
  68. header := make([]byte, StreamHeaderBytes)
  69. _, _ = rand.Read(header)
  70. stream := &encryptor{}
  71. k, err := chacha20.HChaCha20(key[:], header[:16])
  72. if err != nil {
  73. //fmt.Printf("error: %v", err)
  74. return nil, nil, err
  75. }
  76. copy(stream.k[:], k)
  77. stream.reset()
  78. for i := range stream.pad {
  79. stream.pad[i] = 0
  80. }
  81. for i, b := range header[cryptoCoreHchacha20InputBytes:] {
  82. stream.nonce[i+cryptoSecretStreamXchacha20poly1305Counterbytes] = b
  83. }
  84. // fmt.Printf("stream: %+v\n", stream.streamState)
  85. return stream, header, nil
  86. }
  87. func (s *encryptor) Push(plain []byte, tag byte) ([]byte, error) {
  88. var err error
  89. //crypto_onetimeauth_poly1305_state poly1305_state;
  90. var poly *poly1305.MAC
  91. //unsigned char block[64U];
  92. var block [64]byte
  93. //unsigned char slen[8U];
  94. var slen [8]byte
  95. //unsigned char *c;
  96. //unsigned char *mac;
  97. //
  98. //if (outlen_p != NULL) {
  99. //*outlen_p = 0U;
  100. //}
  101. mlen := len(plain)
  102. //if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
  103. //sodium_misuse();
  104. //}
  105. out := make([]byte, mlen+XChaCha20Poly1305IetfABYTES)
  106. chacha, err := chacha20.NewUnauthenticatedCipher(s.k[:], s.nonce[:])
  107. if err != nil {
  108. return nil, err
  109. }
  110. //crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
  111. chacha.XORKeyStream(block[:], block[:])
  112. //crypto_onetimeauth_poly1305_init(&poly1305_state, block);
  113. var poly_init [32]byte
  114. copy(poly_init[:], block[:])
  115. poly = poly1305.New(&poly_init)
  116. // TODO add support for add data
  117. //sodium_memzero(block, sizeof block);
  118. //crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
  119. //crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
  120. //(0x10 - adlen) & 0xf);
  121. //memset(block, 0, sizeof block);
  122. //block[0] = tag;
  123. memZero(block[:])
  124. block[0] = tag
  125. //
  126. //crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, state->nonce, 1U, state->k);
  127. //crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
  128. //out[0] = block[0];
  129. chacha.XORKeyStream(block[:], block[:])
  130. _, _ = poly.Write(block[:])
  131. out[0] = block[0]
  132. //
  133. //c = out + (sizeof tag);
  134. c := out[1:]
  135. //crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
  136. //crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
  137. //crypto_onetimeauth_poly1305_update (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
  138. chacha.XORKeyStream(c, plain)
  139. _, _ = poly.Write(c[:mlen])
  140. padlen := (0x10 - len(block) + mlen) & 0xf
  141. _, _ = poly.Write(pad0[:padlen])
  142. //
  143. //STORE64_LE(slen, (uint64_t) adlen);
  144. //crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
  145. binary.LittleEndian.PutUint64(slen[:], uint64(0))
  146. _, _ = poly.Write(slen[:])
  147. //STORE64_LE(slen, (sizeof block) + mlen);
  148. //crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
  149. binary.LittleEndian.PutUint64(slen[:], uint64(len(block)+mlen))
  150. _, _ = poly.Write(slen[:])
  151. //
  152. //mac = c + mlen;
  153. //crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
  154. mac := c[mlen:]
  155. copy(mac, poly.Sum(nil))
  156. //sodium_memzero(&poly1305_state, sizeof poly1305_state);
  157. //
  158. //XOR_BUF(STATE_INONCE(state), mac, crypto_secretstream_xchacha20poly1305_INONCEBYTES);
  159. //sodium_increment(STATE_COUNTER(state), crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
  160. xorBuf(s.nonce[cryptoSecretStreamXchacha20poly1305Counterbytes:], mac)
  161. bufInc(s.nonce[:cryptoSecretStreamXchacha20poly1305Counterbytes])
  162. // TODO
  163. //if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
  164. //sodium_is_zero(STATE_COUNTER(state),
  165. //crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
  166. //crypto_secretstream_xchacha20poly1305_rekey(state);
  167. //}
  168. //if (outlen_p != NULL) {
  169. //*outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
  170. //}
  171. //return 0;
  172. return out, nil
  173. }
  174. func NewDecryptor(key, header []byte) (Decryptor, error) {
  175. stream := &decryptor{}
  176. //crypto_core_hchacha20(state->k, in, k, NULL);
  177. k, err := chacha20.HChaCha20(key, header[:16])
  178. if err != nil {
  179. fmt.Printf("error: %v", err)
  180. return nil, err
  181. }
  182. copy(stream.k[:], k)
  183. //_crypto_secretstream_xchacha20poly1305_counter_reset(state);
  184. stream.reset()
  185. //memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
  186. // crypto_secretstream_xchacha20poly1305_INONCEBYTES);
  187. copy(stream.nonce[cryptoSecretStreamXchacha20poly1305Counterbytes:],
  188. header[cryptoCoreHchacha20InputBytes:])
  189. //memset(state->_pad, 0, sizeof state->_pad);
  190. copy(stream.pad[:], pad0[:])
  191. //fmt.Printf("decryptor: %+v\n", stream.streamState)
  192. return stream, nil
  193. }
  194. func (s *decryptor) Pull(cipher []byte) ([]byte, byte, error) {
  195. cipherLen := len(cipher)
  196. //crypto_onetimeauth_poly1305_state poly1305_state;
  197. var poly1305State [32]byte
  198. //unsigned char block[64U];
  199. var block [64]byte
  200. //unsigned char slen[8U];
  201. var slen [8]byte
  202. //unsigned char mac[crypto_onetimeauth_poly1305_BYTES];
  203. //const unsigned char *c;
  204. //const unsigned char *stored_mac;
  205. //unsigned long long mlen; // length of the returned message
  206. //unsigned char tag; // for the return value
  207. //
  208. //if (mlen_p != NULL) {
  209. //*mlen_p = 0U;
  210. //}
  211. //if (tag_p != NULL) {
  212. //*tag_p = 0xff;
  213. //}
  214. /*
  215. if (inlen < crypto_secretstream_xchacha20poly1305_ABYTES) {
  216. return -1;
  217. }
  218. mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
  219. */
  220. if cipherLen < XChaCha20Poly1305IetfABYTES {
  221. return nil, 0, invalidInput
  222. }
  223. mlen := cipherLen - XChaCha20Poly1305IetfABYTES
  224. //if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
  225. //sodium_misuse();
  226. //}
  227. //crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
  228. chacha, err := chacha20.NewUnauthenticatedCipher(s.k[:], s.nonce[:])
  229. if err != nil {
  230. return nil, 0, err
  231. }
  232. chacha.XORKeyStream(block[:], block[:])
  233. //crypto_onetimeauth_poly1305_init(&poly1305_state, block);
  234. copy(poly1305State[:], block[:])
  235. poly := poly1305.New(&poly1305State)
  236. // TODO
  237. //sodium_memzero(block, sizeof block);
  238. //crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
  239. //crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
  240. //(0x10 - adlen) & 0xf);
  241. //
  242. //memset(block, 0, sizeof block);
  243. //block[0] = in[0];
  244. //crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, state->nonce, 1U, state->k);
  245. memZero(block[:])
  246. block[0] = cipher[0]
  247. chacha.XORKeyStream(block[:], block[:])
  248. //tag = block[0];
  249. //block[0] = in[0];
  250. //crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
  251. tag := block[0]
  252. block[0] = cipher[0]
  253. if _, err = poly.Write(block[:]); err != nil {
  254. return nil, 0, err
  255. }
  256. //c = in + (sizeof tag);
  257. //crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
  258. //crypto_onetimeauth_poly1305_update (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
  259. c := cipher[1:]
  260. if _, err = poly.Write(c[:mlen]); err != nil {
  261. return nil, 0, err
  262. }
  263. padLen := (0x10 - len(block) + mlen) & 0xf
  264. if _, err = poly.Write(pad0[:padLen]); err != nil {
  265. return nil, 0, err
  266. }
  267. //
  268. //STORE64_LE(slen, (uint64_t) adlen);
  269. //crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
  270. binary.LittleEndian.PutUint64(slen[:], uint64(0))
  271. if _, err = poly.Write(slen[:]); err != nil {
  272. return nil, 0, err
  273. }
  274. //STORE64_LE(slen, (sizeof block) + mlen);
  275. //crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
  276. binary.LittleEndian.PutUint64(slen[:], uint64(len(block)+mlen))
  277. if _, err = poly.Write(slen[:]); err != nil {
  278. return nil, 0, err
  279. }
  280. //
  281. //crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
  282. //sodium_memzero(&poly1305_state, sizeof poly1305_state);
  283. mac := poly.Sum(nil)
  284. memZero(poly1305State[:])
  285. //stored_mac = c + mlen;
  286. //if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
  287. //sodium_memzero(mac, sizeof mac);
  288. //return -1;
  289. //}
  290. storedMac := c[mlen:]
  291. if !bytes.Equal(mac, storedMac) {
  292. memZero(mac)
  293. return nil, 0, cryptoFailure
  294. }
  295. //crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
  296. //XOR_BUF(STATE_INONCE(state), mac, crypto_secretstream_xchacha20poly1305_INONCEBYTES);
  297. //sodium_increment(STATE_COUNTER(state), crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
  298. m := make([]byte, mlen)
  299. chacha.XORKeyStream(m, c[:mlen])
  300. xorBuf(s.nonce[cryptoSecretStreamXchacha20poly1305Counterbytes:], mac)
  301. bufInc(s.nonce[:cryptoSecretStreamXchacha20poly1305Counterbytes])
  302. // TODO
  303. //if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
  304. //sodium_is_zero(STATE_COUNTER(state),
  305. //crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
  306. //crypto_secretstream_xchacha20poly1305_rekey(state);
  307. //}
  308. //if (mlen_p != NULL) {
  309. //*mlen_p = mlen;
  310. //}
  311. //if (tag_p != NULL) {
  312. //*tag_p = tag;
  313. //}
  314. //return 0;
  315. return m, tag, nil
  316. }