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