You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.2 KiB

  1. // Package hkdfchacha20poly1305 creates an AEAD using hkdf, chacha20, and poly1305
  2. // When sealing and opening, the hkdf is used to obtain the nonce and subkey for
  3. // chacha20. Other than the change for the how the subkey and nonce for chacha
  4. // are obtained, this is the same as chacha20poly1305
  5. package hkdfchacha20poly1305
  6. import (
  7. "crypto/cipher"
  8. "crypto/sha256"
  9. "errors"
  10. "io"
  11. "golang.org/x/crypto/chacha20poly1305"
  12. "golang.org/x/crypto/hkdf"
  13. )
  14. type hkdfchacha20poly1305 struct {
  15. key [KeySize]byte
  16. }
  17. const (
  18. // KeySize is the size of the key used by this AEAD, in bytes.
  19. KeySize = 32
  20. // NonceSize is the size of the nonce used with this AEAD, in bytes.
  21. NonceSize = 24
  22. // TagSize is the size added from poly1305
  23. TagSize = 16
  24. // MaxPlaintextSize is the max size that can be passed into a single call of Seal
  25. MaxPlaintextSize = (1 << 38) - 64
  26. // MaxCiphertextSize is the max size that can be passed into a single call of Open,
  27. // this differs from plaintext size due to the tag
  28. MaxCiphertextSize = (1 << 38) - 48
  29. // HkdfInfo is the parameter used internally for Hkdf's info parameter.
  30. HkdfInfo = "TENDERMINT_SECRET_CONNECTION_FRAME_KEY_DERIVE"
  31. )
  32. //New xChaChapoly1305 AEAD with 24 byte nonces
  33. func New(key []byte) (cipher.AEAD, error) {
  34. if len(key) != KeySize {
  35. return nil, errors.New("chacha20poly1305: bad key length")
  36. }
  37. ret := new(hkdfchacha20poly1305)
  38. copy(ret.key[:], key)
  39. return ret, nil
  40. }
  41. func (c *hkdfchacha20poly1305) NonceSize() int {
  42. return NonceSize
  43. }
  44. func (c *hkdfchacha20poly1305) Overhead() int {
  45. return TagSize
  46. }
  47. func (c *hkdfchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
  48. if len(nonce) != NonceSize {
  49. panic("hkdfchacha20poly1305: bad nonce length passed to Seal")
  50. }
  51. if uint64(len(plaintext)) > MaxPlaintextSize {
  52. panic("hkdfchacha20poly1305: plaintext too large")
  53. }
  54. subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce)
  55. aead, err := chacha20poly1305.New(subKey[:])
  56. if err != nil {
  57. panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305")
  58. }
  59. return aead.Seal(dst, chachaNonce[:], plaintext, additionalData)
  60. }
  61. func (c *hkdfchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
  62. if len(nonce) != NonceSize {
  63. return nil, errors.New("hkdfchacha20poly1305: bad nonce length passed to Open")
  64. }
  65. if uint64(len(ciphertext)) > MaxCiphertextSize {
  66. return nil, errors.New("hkdfchacha20poly1305: ciphertext too large")
  67. }
  68. subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce)
  69. aead, err := chacha20poly1305.New(subKey[:])
  70. if err != nil {
  71. panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305")
  72. }
  73. return aead.Open(dst, chachaNonce[:], ciphertext, additionalData)
  74. }
  75. func getSubkeyAndChachaNonceFromHkdf(cKey *[32]byte, nonce *[]byte) (
  76. subKey [KeySize]byte, chachaNonce [chacha20poly1305.NonceSize]byte) {
  77. hash := sha256.New
  78. hkdf := hkdf.New(hash, (*cKey)[:], *nonce, []byte(HkdfInfo))
  79. _, err := io.ReadFull(hkdf, subKey[:])
  80. if err != nil {
  81. panic("hkdfchacha20poly1305: failed to read subkey from hkdf")
  82. }
  83. _, err = io.ReadFull(hkdf, chachaNonce[:])
  84. if err != nil {
  85. panic("hkdfchacha20poly1305: failed to read chachaNonce from hkdf")
  86. }
  87. return
  88. }