aead.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // aead.go - An AEAD_CHACHA20_POLY1305 implementation.
  2. //
  3. // To the extent possible under law, Yawning Angel has waived all copyright
  4. // and related or neighboring rights to chacha20poly1305, 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 chacha20poly1305 implemnets the RFC 7539 AEAD_CHACHA20_POLY1305
  8. // construct. It depends on my ChaCha20 and Poly1305 libraries (and not
  9. // golang.org/x/crypto for the latter), and attempts to be correct and easy to
  10. // read over fast.
  11. //
  12. // When the golang.org/x/crypto maintainers feel like providing a sane
  13. // interface to the Poly1305 code, this will switch to using that, but not
  14. // before then.
  15. package chacha20poly1305
  16. import (
  17. "crypto/cipher"
  18. "crypto/subtle"
  19. "encoding/binary"
  20. "errors"
  21. "git.schwanenlied.me/yawning/chacha20.git"
  22. "git.schwanenlied.me/yawning/poly1305.git"
  23. )
  24. const (
  25. // KeySize is the key length in bytes (32 bytes, 256 bits).
  26. KeySize = chacha20.KeySize
  27. // NonceSize is the nonce (IV) length in bytes (12 bytes, 96 bits).
  28. NonceSize = chacha20.INonceSize
  29. // Overhead is the tag length in bytes (16 bytes, 128 bits).
  30. Overhead = poly1305.Size
  31. )
  32. var (
  33. // ErrOpen is the error returned when an Open fails.
  34. ErrOpen = errors.New("chacha20poly1305: message authentication failed")
  35. paddingBytes [16]byte
  36. )
  37. // ChaCha20Poly1305 is an AEAD_CHACHA20_POLY1305 instance.
  38. type ChaCha20Poly1305 struct {
  39. key [KeySize]byte
  40. }
  41. // NonceSize returns the size of the nonce that must be passed to Seal
  42. // and Open.
  43. func (a *ChaCha20Poly1305) NonceSize() int {
  44. return NonceSize
  45. }
  46. // Overhead returns the maximum difference between the lengths of a
  47. // plaintext and its ciphertext.
  48. func (a *ChaCha20Poly1305) Overhead() int {
  49. return Overhead
  50. }
  51. func (a *ChaCha20Poly1305) init(nonce []byte) (*chacha20.Cipher, *poly1305.Poly1305) {
  52. if len(nonce) != a.NonceSize() {
  53. panic("chacha20poly1305: len(nonce) != NonceSize()")
  54. }
  55. // First, a Poly1305 one-time key is generated from the 256-bit key
  56. // and nonce using the procedure described in Section 2.6.
  57. var polyKey [32]byte
  58. defer memwipe(polyKey[:])
  59. c, err := chacha20.NewCipher(a.key[:], nonce)
  60. if err != nil {
  61. panic("chacha20poly1305: failed to initialize chacha20: " + err.Error())
  62. }
  63. c.KeyStream(polyKey[:])
  64. c.Seek(1) // Set the initial counter to 1 in preparation for payload.
  65. m, err := poly1305.New(polyKey[:])
  66. if err != nil {
  67. panic("chacha20poly1305: failed to initialize poly1305: " + err.Error())
  68. }
  69. return c, m
  70. }
  71. // Seal encrypts and authenticates plaintext, authenticates the
  72. // additional data and appends the result to dst, returning the updated
  73. // slice. The nonce must be NonceSize() bytes long and unique for all
  74. // time, for a given key.
  75. func (a *ChaCha20Poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
  76. c, m := a.init(nonce)
  77. defer c.Reset()
  78. defer m.Clear()
  79. // Next, the ChaCha20 encryption function is called to encrypt the
  80. // plaintext, using the same key and nonce, and with the initial
  81. // counter set to 1.
  82. retLen := len(plaintext) + Overhead
  83. ret := make([]byte, len(plaintext), retLen)
  84. c.XORKeyStream(ret, plaintext)
  85. // Finally, the Poly1305 function is called with the Poly1305 key
  86. // calculated above, and a message constructed as a concatenation of
  87. // the following:
  88. // The AAD
  89. m.Write(additionalData)
  90. // padding1 -- the padding is up to 15 zero bytes, and it brings
  91. // the total length so far to an integral multiple of 16. If the
  92. // length of the AAD was already an integral multiple of 16 bytes,
  93. // this field is zero-length.
  94. padding1 := (16 - (len(additionalData) & 0x0f)) & 0x0f
  95. m.Write(paddingBytes[:padding1])
  96. // The ciphertext
  97. m.Write(ret)
  98. // padding2 -- the padding is up to 15 zero bytes, and it brings
  99. // the total length so far to an integral multiple of 16. If the
  100. // length of the ciphertext was already an integral multiple of 16
  101. // bytes, this field is zero-length.
  102. padding2 := (16 - (len(plaintext) & 0x0f)) & 0x0f
  103. m.Write(paddingBytes[:padding2])
  104. // The length of the additional data in octets (as a 64-bit
  105. // little-endian integer).
  106. var lenBuf [8]byte
  107. binary.LittleEndian.PutUint64(lenBuf[0:], uint64(len(additionalData)))
  108. m.Write(lenBuf[:])
  109. // The length of the ciphertext in octets (as a 64-bit little-
  110. // endian integer).
  111. binary.LittleEndian.PutUint64(lenBuf[0:], uint64(len(plaintext)))
  112. m.Write(lenBuf[:])
  113. // Return `dst | ciphertext | tag.
  114. ret = m.Sum(ret)
  115. return append(dst, ret...)
  116. }
  117. // Open decrypts and authenticates ciphertext, authenticates the
  118. // additional data and, if successful, appends the resulting plaintext
  119. // to dst, returning the updated slice. The nonce must be NonceSize()
  120. // bytes long and both it and the additional data must match the
  121. // value passed to Seal.
  122. //
  123. // Even if the function fails, the contents of dst, up to its capacity,
  124. // may be overwritten.
  125. func (a *ChaCha20Poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
  126. if len(ciphertext) < Overhead {
  127. return nil, ErrOpen
  128. }
  129. ctLen := len(ciphertext) - Overhead
  130. c, m := a.init(nonce)
  131. defer c.Reset()
  132. defer m.Clear()
  133. // Derive the tag based on the data received, and validate.
  134. m.Write(additionalData)
  135. padding1 := (16 - (len(additionalData) & 0x0f)) & 0x0f
  136. m.Write(paddingBytes[:padding1])
  137. m.Write(ciphertext[:ctLen])
  138. padding2 := (16 - (ctLen & 0x0f)) & 0x0f
  139. m.Write(paddingBytes[:padding2])
  140. var lenBuf [8]byte
  141. binary.LittleEndian.PutUint64(lenBuf[0:], uint64(len(additionalData)))
  142. m.Write(lenBuf[:])
  143. binary.LittleEndian.PutUint64(lenBuf[0:], uint64(ctLen))
  144. m.Write(lenBuf[:])
  145. derivedTag := m.Sum(nil)
  146. if subtle.ConstantTimeCompare(ciphertext[ctLen:], derivedTag[:]) != 1 {
  147. memwipe(dst)
  148. return nil, ErrOpen
  149. }
  150. // Decrypt and return.
  151. ret := make([]byte, ctLen)
  152. c.XORKeyStream(ret, ciphertext[:ctLen])
  153. return append(dst, ret...), nil
  154. }
  155. // Reset clears all sensitive cryptographic material from a given instance
  156. // so that it is no longer resident in memory.
  157. func (a *ChaCha20Poly1305) Reset() {
  158. memwipe(a.key[:])
  159. }
  160. // New returns a new ChaCha20Poly1305 instance, keyed with a given key.
  161. func New(key []byte) (*ChaCha20Poly1305, error) {
  162. if len(key) != KeySize {
  163. return nil, chacha20.ErrInvalidKey
  164. }
  165. a := &ChaCha20Poly1305{}
  166. copy(a.key[:], key)
  167. return a, nil
  168. }
  169. func memwipe(buf []byte) {
  170. for i := range buf {
  171. buf[i] = 0
  172. }
  173. }
  174. var _ cipher.AEAD = (*ChaCha20Poly1305)(nil)