ciphersuite.go raw
1 package mls
2
3 import (
4 "crypto"
5 "crypto/ecdsa"
6 "crypto/ed25519"
7 "crypto/elliptic"
8 "crypto/hmac"
9 "crypto/rand"
10 _ "crypto/sha256"
11 _ "crypto/sha512"
12 "fmt"
13 "math/big"
14
15 "github.com/cloudflare/circl/hpke"
16 "github.com/cloudflare/circl/sign/ed448"
17 "golang.org/x/crypto/cryptobyte"
18 )
19
20 // A CipherSuite defines the cryptographic primitives to be used in group key
21 // computations: HPKE parameters (KEM, KDF and AEAD), hash, MAC and signature.
22 //
23 // MLS cipher suites are listed at:
24 // https://www.iana.org/assignments/mls/mls.xhtml#mls-ciphersuites
25 type CipherSuite uint16
26
27 const (
28 CipherSuiteMLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 CipherSuite = 0x0001
29 CipherSuiteMLS_128_DHKEMP256_AES128GCM_SHA256_P256 CipherSuite = 0x0002
30 CipherSuiteMLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 CipherSuite = 0x0003
31 CipherSuiteMLS_256_DHKEMX448_AES256GCM_SHA512_Ed448 CipherSuite = 0x0004
32 CipherSuiteMLS_256_DHKEMP521_AES256GCM_SHA512_P521 CipherSuite = 0x0005
33 CipherSuiteMLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 CipherSuite = 0x0006
34 CipherSuiteMLS_256_DHKEMP384_AES256GCM_SHA384_P384 CipherSuite = 0x0007
35 )
36
37 // String returns the name of the cipher suite.
38 func (cs CipherSuite) String() string {
39 switch cs {
40 case CipherSuiteMLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519:
41 return "MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519"
42 case CipherSuiteMLS_128_DHKEMP256_AES128GCM_SHA256_P256:
43 return "MLS_128_DHKEMP256_AES128GCM_SHA256_P256"
44 case CipherSuiteMLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519:
45 return "MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519"
46 case CipherSuiteMLS_256_DHKEMX448_AES256GCM_SHA512_Ed448:
47 return "MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448"
48 case CipherSuiteMLS_256_DHKEMP521_AES256GCM_SHA512_P521:
49 return "MLS_256_DHKEMP521_AES256GCM_SHA512_P521"
50 case CipherSuiteMLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448:
51 return "MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448"
52 case CipherSuiteMLS_256_DHKEMP384_AES256GCM_SHA384_P384:
53 return "MLS_256_DHKEMP384_AES256GCM_SHA384_P384"
54 }
55 return fmt.Sprintf("<%d>", cs)
56 }
57
58 // Supported checks whether a cipher suite is supported by the library.
59 func (cs CipherSuite) Supported() bool {
60 if _, ok := cipherSuiteDescriptions[cs]; !ok {
61 return false
62 }
63
64 // TODO: drop the seed size check, see:
65 // https://github.com/cloudflare/circl/issues/486
66 kem, kdf, _ := cs.hpke().Params()
67 if kem.Scheme().SeedSize() != kdf.ExtractSize() {
68 return false
69 }
70
71 return true
72 }
73
74 func (cs CipherSuite) hash() crypto.Hash {
75 desc, ok := cipherSuiteDescriptions[cs]
76 if !ok {
77 panic(fmt.Errorf("mls: invalid cipher suite %d", cs))
78 }
79 return desc.hash
80 }
81
82 func (cs CipherSuite) hpke() hpke.Suite {
83 desc, ok := cipherSuiteDescriptions[cs]
84 if !ok {
85 panic(fmt.Errorf("mls: invalid cipher suite %d", cs))
86 }
87 return desc.hpke
88 }
89
90 func (cs CipherSuite) signatureScheme() signatureScheme {
91 desc, ok := cipherSuiteDescriptions[cs]
92 if !ok {
93 panic(fmt.Errorf("mls: invalid cipher suite %d", cs))
94 }
95 return desc.sig
96 }
97
98 type cipherSuiteDescription struct {
99 hash crypto.Hash
100 hpke hpke.Suite
101 sig signatureScheme
102 }
103
104 var cipherSuiteDescriptions = map[CipherSuite]cipherSuiteDescription{
105 CipherSuiteMLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519: {
106 hash: crypto.SHA256,
107 hpke: hpke.NewSuite(hpke.KEM_X25519_HKDF_SHA256, hpke.KDF_HKDF_SHA256, hpke.AEAD_AES128GCM),
108 sig: ed25519SignatureScheme{},
109 },
110 CipherSuiteMLS_128_DHKEMP256_AES128GCM_SHA256_P256: {
111 hash: crypto.SHA256,
112 hpke: hpke.NewSuite(hpke.KEM_P256_HKDF_SHA256, hpke.KDF_HKDF_SHA256, hpke.AEAD_AES128GCM),
113 sig: ecdsaSignatureScheme{elliptic.P256(), crypto.SHA256},
114 },
115 CipherSuiteMLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519: {
116 hash: crypto.SHA256,
117 hpke: hpke.NewSuite(hpke.KEM_X25519_HKDF_SHA256, hpke.KDF_HKDF_SHA256, hpke.AEAD_ChaCha20Poly1305),
118 sig: ed25519SignatureScheme{},
119 },
120 CipherSuiteMLS_256_DHKEMX448_AES256GCM_SHA512_Ed448: {
121 hash: crypto.SHA512,
122 hpke: hpke.NewSuite(hpke.KEM_X448_HKDF_SHA512, hpke.KDF_HKDF_SHA512, hpke.AEAD_AES256GCM),
123 sig: ed448SignatureScheme{},
124 },
125 CipherSuiteMLS_256_DHKEMP521_AES256GCM_SHA512_P521: {
126 hash: crypto.SHA512,
127 hpke: hpke.NewSuite(hpke.KEM_P521_HKDF_SHA512, hpke.KDF_HKDF_SHA512, hpke.AEAD_AES256GCM),
128 sig: ecdsaSignatureScheme{elliptic.P521(), crypto.SHA512},
129 },
130 CipherSuiteMLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448: {
131 hash: crypto.SHA512,
132 hpke: hpke.NewSuite(hpke.KEM_X448_HKDF_SHA512, hpke.KDF_HKDF_SHA512, hpke.AEAD_ChaCha20Poly1305),
133 sig: ed448SignatureScheme{},
134 },
135 CipherSuiteMLS_256_DHKEMP384_AES256GCM_SHA384_P384: {
136 hash: crypto.SHA384,
137 hpke: hpke.NewSuite(hpke.KEM_P384_HKDF_SHA384, hpke.KDF_HKDF_SHA384, hpke.AEAD_AES256GCM),
138 sig: ecdsaSignatureScheme{elliptic.P384(), crypto.SHA384},
139 },
140 }
141
142 func (cs CipherSuite) signMAC(key, message []byte) []byte {
143 // All cipher suites use HMAC
144 mac := hmac.New(cs.hash().New, key)
145 mac.Write(message)
146 return mac.Sum(nil)
147 }
148
149 func (cs CipherSuite) verifyMAC(key, message, tag []byte) bool {
150 return hmac.Equal(tag, cs.signMAC(key, message))
151 }
152
153 func (cs CipherSuite) refHash(label, value []byte) ([]byte, error) {
154 var b cryptobyte.Builder
155 writeOpaqueVec(&b, label)
156 writeOpaqueVec(&b, value)
157 in, err := b.Bytes()
158 if err != nil {
159 return nil, err
160 }
161
162 h := cs.hash().New()
163 h.Write(in)
164 return h.Sum(nil), nil
165 }
166
167 func (cs CipherSuite) expandWithLabel(secret, label, context []byte, length uint16) ([]byte, error) {
168 label = append([]byte("MLS 1.0 "), label...)
169
170 var b cryptobyte.Builder
171 b.AddUint16(length)
172 writeOpaqueVec(&b, label)
173 writeOpaqueVec(&b, context)
174 kdfLabel, err := b.Bytes()
175 if err != nil {
176 return nil, err
177 }
178
179 _, kdf, _ := cs.hpke().Params()
180 return kdf.Expand(secret, kdfLabel, uint(length)), nil
181 }
182
183 func (cs CipherSuite) deriveSecret(secret, label []byte) ([]byte, error) {
184 _, kdf, _ := cs.hpke().Params()
185 return cs.expandWithLabel(secret, label, nil, uint16(kdf.ExtractSize()))
186 }
187
188 func (cs CipherSuite) signWithLabel(signKey signaturePrivateKey, label, content []byte) ([]byte, error) {
189 signContent, err := marshalSignContent(label, content)
190 if err != nil {
191 return nil, err
192 }
193
194 return cs.signatureScheme().Sign(signKey, signContent)
195 }
196
197 func (cs CipherSuite) verifyWithLabel(verifKey signaturePublicKey, label, content, signValue []byte) bool {
198 signContent, err := marshalSignContent(label, content)
199 if err != nil {
200 return false
201 }
202
203 return cs.signatureScheme().Verify(verifKey, signContent, signValue)
204 }
205
206 func (cs CipherSuite) encryptWithLabel(publicKey hpkePublicKey, label, context, plaintext []byte) (kemOutput, ciphertext []byte, err error) {
207 encryptContext, err := marshalEncryptContext(label, context)
208 if err != nil {
209 return nil, nil, err
210 }
211
212 hpke := cs.hpke()
213 kem, _, _ := hpke.Params()
214 pub, err := kem.Scheme().UnmarshalBinaryPublicKey(publicKey)
215 if err != nil {
216 return nil, nil, err
217 }
218
219 sender, err := hpke.NewSender(pub, encryptContext)
220 if err != nil {
221 return nil, nil, err
222 }
223
224 kemOutput, sealer, err := sender.Setup(rand.Reader)
225 if err != nil {
226 return nil, nil, err
227 }
228
229 ciphertext, err = sealer.Seal(plaintext, nil)
230 return kemOutput, ciphertext, err
231 }
232
233 func (cs CipherSuite) decryptWithLabel(privateKey hpkePrivateKey, label, context, kemOutput, ciphertext []byte) ([]byte, error) {
234 encryptContext, err := marshalEncryptContext(label, context)
235 if err != nil {
236 return nil, err
237 }
238
239 hpke := cs.hpke()
240 kem, _, _ := hpke.Params()
241 priv, err := kem.Scheme().UnmarshalBinaryPrivateKey(privateKey)
242 if err != nil {
243 return nil, err
244 }
245
246 receiver, err := hpke.NewReceiver(priv, encryptContext)
247 if err != nil {
248 return nil, err
249 }
250
251 opener, err := receiver.Setup(kemOutput)
252 if err != nil {
253 return nil, err
254 }
255
256 return opener.Open(ciphertext, nil)
257 }
258
259 func (cs CipherSuite) generateEncryptionKeyPair() (hpkePublicKey, hpkePrivateKey, error) {
260 kem, _, _ := cs.hpke().Params()
261 pub, priv, err := kem.Scheme().GenerateKeyPair()
262 if err != nil {
263 return nil, nil, err
264 }
265
266 rawPub, err := pub.MarshalBinary()
267 if err != nil {
268 return nil, nil, err
269 }
270
271 rawPriv, err := priv.MarshalBinary()
272 if err != nil {
273 return nil, nil, err
274 }
275
276 return rawPub, rawPriv, nil
277 }
278
279 func (cs CipherSuite) deriveEncryptionKeyPair(seed []byte) (hpkePublicKey, hpkePrivateKey, error) {
280 kem, _, _ := cs.hpke().Params()
281 pub, priv := kem.Scheme().DeriveKeyPair(seed)
282
283 rawPub, err := pub.MarshalBinary()
284 if err != nil {
285 return nil, nil, err
286 }
287
288 rawPriv, err := priv.MarshalBinary()
289 if err != nil {
290 return nil, nil, err
291 }
292
293 return rawPub, rawPriv, nil
294 }
295
296 func marshalSignContent(label, content []byte) ([]byte, error) {
297 label = append([]byte("MLS 1.0 "), label...)
298
299 var b cryptobyte.Builder
300 writeOpaqueVec(&b, label)
301 writeOpaqueVec(&b, content)
302 return b.Bytes()
303 }
304
305 func marshalEncryptContext(label, context []byte) ([]byte, error) {
306 label = append([]byte("MLS 1.0 "), label...)
307
308 var b cryptobyte.Builder
309 writeOpaqueVec(&b, label)
310 writeOpaqueVec(&b, context)
311 return b.Bytes()
312 }
313
314 type signatureScheme interface {
315 Sign(signKey, message []byte) ([]byte, error)
316 Verify(publicKey, message, sig []byte) bool
317 GenerateKeyPair() (publicKey, privateKey []byte, err error)
318 }
319
320 type ed25519SignatureScheme struct{}
321
322 func (ed25519SignatureScheme) Sign(signKey, message []byte) ([]byte, error) {
323 if len(signKey) != ed25519.SeedSize {
324 return nil, fmt.Errorf("mls: invalid Ed25519 private key size")
325 }
326 priv := ed25519.NewKeyFromSeed(signKey)
327 return ed25519.Sign(priv, message), nil
328 }
329
330 func (ed25519SignatureScheme) Verify(publicKey, message, sig []byte) bool {
331 if len(publicKey) != ed25519.PublicKeySize {
332 return false
333 }
334 return ed25519.Verify(ed25519.PublicKey(publicKey), message, sig)
335 }
336
337 func (ed25519SignatureScheme) GenerateKeyPair() (publicKey, privateKey []byte, err error) {
338 pub, priv, err := ed25519.GenerateKey(rand.Reader)
339 if err != nil {
340 return nil, nil, err
341 }
342 return []byte(pub), priv.Seed(), nil
343 }
344
345 type ecdsaSignatureScheme struct {
346 curve elliptic.Curve
347 hash crypto.Hash
348 }
349
350 func (scheme ecdsaSignatureScheme) hashSum(message []byte) []byte {
351 h := scheme.hash.New()
352 h.Write(message)
353 return h.Sum(nil)
354 }
355
356 func (scheme ecdsaSignatureScheme) Sign(signKey, message []byte) ([]byte, error) {
357 d := new(big.Int).SetBytes(signKey)
358 x, y := scheme.curve.ScalarBaseMult(signKey)
359 priv := &ecdsa.PrivateKey{
360 PublicKey: ecdsa.PublicKey{Curve: scheme.curve, X: x, Y: y},
361 D: d,
362 }
363 return ecdsa.SignASN1(rand.Reader, priv, scheme.hashSum(message))
364 }
365
366 func (scheme ecdsaSignatureScheme) Verify(publicKey, message, sig []byte) bool {
367 x, y := elliptic.Unmarshal(scheme.curve, publicKey)
368 pub := &ecdsa.PublicKey{Curve: scheme.curve, X: x, Y: y}
369 return ecdsa.VerifyASN1(pub, scheme.hashSum(message), sig)
370 }
371
372 func (scheme ecdsaSignatureScheme) GenerateKeyPair() (publicKey, privateKey []byte, err error) {
373 priv, err := ecdsa.GenerateKey(scheme.curve, rand.Reader)
374 if err != nil {
375 return nil, nil, err
376 }
377 pub := priv.PublicKey
378 return elliptic.Marshal(pub.Curve, pub.X, pub.Y), priv.D.Bytes(), nil
379 }
380
381 type ed448SignatureScheme struct{}
382
383 func (ed448SignatureScheme) Sign(signKey, message []byte) ([]byte, error) {
384 if len(signKey) != ed448.SeedSize {
385 return nil, fmt.Errorf("mls: invalid Ed448 private key size")
386 }
387 priv := ed448.NewKeyFromSeed(signKey)
388 return ed448.Sign(priv, message, ""), nil
389 }
390
391 func (ed448SignatureScheme) Verify(publicKey, message, sig []byte) bool {
392 if len(publicKey) != ed448.PublicKeySize {
393 return false
394 }
395 return ed448.Verify(ed448.PublicKey(publicKey), message, sig, "")
396 }
397
398 func (ed448SignatureScheme) GenerateKeyPair() (publicKey, privateKey []byte, err error) {
399 pub, priv, err := ed448.GenerateKey(rand.Reader)
400 if err != nil {
401 return nil, nil, err
402 }
403 return []byte(pub), priv.Seed(), nil
404 }
405