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.

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