1package noise 2 3import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/rand" 7 "crypto/sha256" 8 "crypto/sha512" 9 "encoding/binary" 10 "hash" 11 "io" 12 13 "golang.org/x/crypto/blake2b" 14 "golang.org/x/crypto/blake2s" 15 "golang.org/x/crypto/chacha20poly1305" 16 "golang.org/x/crypto/curve25519" 17) 18 19// A DHKey is a keypair used for Diffie-Hellman key agreement. 20type DHKey struct { 21 Private []byte 22 Public []byte 23} 24 25// A DHFunc implements Diffie-Hellman key agreement. 26type DHFunc interface { 27 // GenerateKeypair generates a new keypair using random as a source of 28 // entropy. 29 GenerateKeypair(random io.Reader) (DHKey, error) 30 31 // DH performs a Diffie-Hellman calculation between the provided private and 32 // public keys and returns the result. 33 DH(privkey, pubkey []byte) []byte 34 35 // DHLen is the number of bytes returned by DH. 36 DHLen() int 37 38 // DHName is the name of the DH function. 39 DHName() string 40} 41 42// A HashFunc implements a cryptographic hash function. 43type HashFunc interface { 44 // Hash returns a hash state. 45 Hash() hash.Hash 46 47 // HashName is the name of the hash function. 48 HashName() string 49} 50 51// A CipherFunc implements an AEAD symmetric cipher. 52type CipherFunc interface { 53 // Cipher initializes the algorithm with the provided key and returns a Cipher. 54 Cipher(k [32]byte) Cipher 55 56 // CipherName is the name of the cipher. 57 CipherName() string 58} 59 60// A Cipher is a AEAD cipher that has been initialized with a key. 61type Cipher interface { 62 // Encrypt encrypts the provided plaintext with a nonce and then appends the 63 // ciphertext to out along with an authentication tag over the ciphertext 64 // and optional authenticated data. 65 Encrypt(out []byte, n uint64, ad, plaintext []byte) []byte 66 67 // Decrypt authenticates the ciphertext and optional authenticated data and 68 // then decrypts the provided ciphertext using the provided nonce and 69 // appends it to out. 70 Decrypt(out []byte, n uint64, ad, ciphertext []byte) ([]byte, error) 71} 72 73// A CipherSuite is a set of cryptographic primitives used in a Noise protocol. 74// It should be constructed with NewCipherSuite. 75type CipherSuite interface { 76 DHFunc 77 CipherFunc 78 HashFunc 79 Name() []byte 80} 81 82// NewCipherSuite returns a CipherSuite constructed from the specified 83// primitives. 84func NewCipherSuite(dh DHFunc, c CipherFunc, h HashFunc) CipherSuite { 85 return ciphersuite{ 86 DHFunc: dh, 87 CipherFunc: c, 88 HashFunc: h, 89 name: []byte(dh.DHName() + "_" + c.CipherName() + "_" + h.HashName()), 90 } 91} 92 93type ciphersuite struct { 94 DHFunc 95 CipherFunc 96 HashFunc 97 name []byte 98} 99 100func (s ciphersuite) Name() []byte { return s.name } 101 102// DH25519 is the Curve25519 ECDH function. 103var DH25519 DHFunc = dh25519{} 104 105type dh25519 struct{} 106 107func (dh25519) GenerateKeypair(rng io.Reader) (DHKey, error) { 108 var pubkey, privkey [32]byte 109 if rng == nil { 110 rng = rand.Reader 111 } 112 if _, err := io.ReadFull(rng, privkey[:]); err != nil { 113 return DHKey{}, err 114 } 115 curve25519.ScalarBaseMult(&pubkey, &privkey) 116 return DHKey{Private: privkey[:], Public: pubkey[:]}, nil 117} 118 119func (dh25519) DH(privkey, pubkey []byte) []byte { 120 var dst, in, base [32]byte 121 copy(in[:], privkey) 122 copy(base[:], pubkey) 123 curve25519.ScalarMult(&dst, &in, &base) 124 return dst[:] 125} 126 127func (dh25519) DHLen() int { return 32 } 128func (dh25519) DHName() string { return "25519" } 129 130type cipherFn struct { 131 fn func([32]byte) Cipher 132 name string 133} 134 135func (c cipherFn) Cipher(k [32]byte) Cipher { return c.fn(k) } 136func (c cipherFn) CipherName() string { return c.name } 137 138// CipherAESGCM is the AES256-GCM AEAD cipher. 139var CipherAESGCM CipherFunc = cipherFn{cipherAESGCM, "AESGCM"} 140 141func cipherAESGCM(k [32]byte) Cipher { 142 c, err := aes.NewCipher(k[:]) 143 if err != nil { 144 panic(err) 145 } 146 gcm, err := cipher.NewGCM(c) 147 if err != nil { 148 panic(err) 149 } 150 return aeadCipher{ 151 gcm, 152 func(n uint64) []byte { 153 var nonce [12]byte 154 binary.BigEndian.PutUint64(nonce[4:], n) 155 return nonce[:] 156 }, 157 } 158} 159 160// CipherChaChaPoly is the ChaCha20-Poly1305 AEAD cipher construction. 161var CipherChaChaPoly CipherFunc = cipherFn{cipherChaChaPoly, "ChaChaPoly"} 162 163func cipherChaChaPoly(k [32]byte) Cipher { 164 c, err := chacha20poly1305.New(k[:]) 165 if err != nil { 166 panic(err) 167 } 168 return aeadCipher{ 169 c, 170 func(n uint64) []byte { 171 var nonce [12]byte 172 binary.LittleEndian.PutUint64(nonce[4:], n) 173 return nonce[:] 174 }, 175 } 176} 177 178type aeadCipher struct { 179 cipher.AEAD 180 nonce func(uint64) []byte 181} 182 183func (c aeadCipher) Encrypt(out []byte, n uint64, ad, plaintext []byte) []byte { 184 return c.Seal(out, c.nonce(n), plaintext, ad) 185} 186 187func (c aeadCipher) Decrypt(out []byte, n uint64, ad, ciphertext []byte) ([]byte, error) { 188 return c.Open(out, c.nonce(n), ciphertext, ad) 189} 190 191type hashFn struct { 192 fn func() hash.Hash 193 name string 194} 195 196func (h hashFn) Hash() hash.Hash { return h.fn() } 197func (h hashFn) HashName() string { return h.name } 198 199// HashSHA256 is the SHA-256 hash function. 200var HashSHA256 HashFunc = hashFn{sha256.New, "SHA256"} 201 202// HashSHA512 is the SHA-512 hash function. 203var HashSHA512 HashFunc = hashFn{sha512.New, "SHA512"} 204 205func blake2bNew() hash.Hash { 206 h, err := blake2b.New512(nil) 207 if err != nil { 208 panic(err) 209 } 210 return h 211} 212 213// HashBLAKE2b is the BLAKE2b hash function. 214var HashBLAKE2b HashFunc = hashFn{blake2bNew, "BLAKE2b"} 215 216func blake2sNew() hash.Hash { 217 h, err := blake2s.New256(nil) 218 if err != nil { 219 panic(err) 220 } 221 return h 222} 223 224// HashBLAKE2s is the BLAKE2s hash function. 225var HashBLAKE2s HashFunc = hashFn{blake2sNew, "BLAKE2s"} 226