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