noise.go 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package nebula
  2. import (
  3. "crypto/cipher"
  4. "encoding/binary"
  5. "errors"
  6. "github.com/flynn/noise"
  7. )
  8. type endianness interface {
  9. PutUint64(b []byte, v uint64)
  10. }
  11. var noiseEndianness endianness = binary.BigEndian
  12. // NonceSize is the AEAD nonce length used by all ciphers nebula supports
  13. // today (AES-GCM and ChaCha20-Poly1305 both use 96-bit nonces). Encrypt-
  14. // and DecryptDanger lay out the nonce as 4 zero bytes followed by an 8-byte
  15. // big-endian counter; if a future cipher with a different nonce size is
  16. // added, this constant and those layouts must change together.
  17. const NonceSize = 12
  18. // AEADOverhead is the AEAD authentication tag length the ciphers nebula
  19. // supports append to ciphertext. Both AES-GCM and ChaCha20-Poly1305 use
  20. // 128-bit tags. NebulaCipherState.Overhead() returns this dynamically from
  21. // the cipher; the constant is for sizing buffers at construction time.
  22. const AEADOverhead = 16
  23. type NebulaCipherState struct {
  24. c cipher.AEAD
  25. }
  26. func NewNebulaCipherState(s *noise.CipherState) *NebulaCipherState {
  27. x := s.Cipher()
  28. return &NebulaCipherState{c: x.(cipher.AEAD)}
  29. }
  30. // EncryptDanger encrypts and authenticates a given payload.
  31. //
  32. // out is a destination slice to hold the output of the EncryptDanger operation.
  33. // - ad is additional data, which will be authenticated and appended to out, but not encrypted.
  34. // - plaintext is encrypted, authenticated and appended to out.
  35. // - n is a nonce value which must never be re-used with this key.
  36. // - nb is a buffer used for temporary storage in the implementation of this call, which should
  37. // be re-used by callers to minimize garbage collection.
  38. func (s *NebulaCipherState) EncryptDanger(out, ad, plaintext []byte, n uint64, nb []byte) ([]byte, error) {
  39. if s != nil {
  40. // TODO: Is this okay now that we have made messageCounter atomic?
  41. // Alternative may be to split the counter space into ranges
  42. //if n <= s.n {
  43. // return nil, errors.New("CRITICAL: a duplicate counter value was used")
  44. //}
  45. //s.n = n
  46. nb[0] = 0
  47. nb[1] = 0
  48. nb[2] = 0
  49. nb[3] = 0
  50. noiseEndianness.PutUint64(nb[4:], n)
  51. out = s.c.Seal(out, nb, plaintext, ad)
  52. //l.Debugf("Encryption: outlen: %d, nonce: %d, ad: %s, plainlen %d", len(out), n, ad, len(plaintext))
  53. return out, nil
  54. } else {
  55. return nil, errors.New("no cipher state available to encrypt")
  56. }
  57. }
  58. func (s *NebulaCipherState) DecryptDanger(out, ad, ciphertext []byte, n uint64, nb []byte) ([]byte, error) {
  59. if s != nil {
  60. nb[0] = 0
  61. nb[1] = 0
  62. nb[2] = 0
  63. nb[3] = 0
  64. noiseEndianness.PutUint64(nb[4:], n)
  65. return s.c.Open(out, nb, ciphertext, ad)
  66. } else {
  67. return []byte{}, nil
  68. }
  69. }
  70. func (s *NebulaCipherState) Overhead() int {
  71. if s != nil {
  72. return s.c.Overhead()
  73. }
  74. return 0
  75. }