hybridkem.go raw

   1  package hpke
   2  
   3  // This file implements a hybrid KEM for HPKE using a simple concatenation
   4  // combiner.
   5  //
   6  // WARNING It is not safe to combine arbitrary KEMs using this combiner.
   7  // See the draft specification for more details:
   8  // https://bwesterb.github.io/draft-westerbaan-cfrg-hpke-xyber768d00/draft-westerbaan-cfrg-hpke-xyber768d00.html#name-security-considerations
   9  
  10  import (
  11  	"crypto/rand"
  12  
  13  	"github.com/cloudflare/circl/kem"
  14  )
  15  
  16  type hybridKEM struct {
  17  	kemBase
  18  	kemA kem.Scheme
  19  	kemB kem.Scheme
  20  }
  21  
  22  func (h hybridKEM) PrivateKeySize() int { return h.kemA.PrivateKeySize() + h.kemB.PrivateKeySize() }
  23  func (h hybridKEM) SeedSize() int       { return 32 }
  24  func (h hybridKEM) CiphertextSize() int { return h.kemA.CiphertextSize() + h.kemB.CiphertextSize() }
  25  func (h hybridKEM) PublicKeySize() int  { return h.kemA.PublicKeySize() + h.kemB.PublicKeySize() }
  26  func (h hybridKEM) EncapsulationSeedSize() int {
  27  	return h.kemA.EncapsulationSeedSize() + h.kemB.EncapsulationSeedSize()
  28  }
  29  func (h hybridKEM) SharedKeySize() int { return h.kemA.SharedKeySize() + h.kemB.SharedKeySize() }
  30  func (h hybridKEM) Name() string       { return h.name }
  31  
  32  func (h hybridKEM) AuthDecapsulate(skR kem.PrivateKey,
  33  	ct []byte,
  34  	pkS kem.PublicKey,
  35  ) ([]byte, error) {
  36  	panic("AuthDecapsulate is not supported for this KEM")
  37  }
  38  
  39  func (h hybridKEM) AuthEncapsulate(pkr kem.PublicKey, sks kem.PrivateKey) (
  40  	ct []byte, ss []byte, err error,
  41  ) {
  42  	panic("AuthEncapsulate is not supported for this KEM")
  43  }
  44  
  45  func (h hybridKEM) AuthEncapsulateDeterministically(pkr kem.PublicKey, sks kem.PrivateKey, seed []byte) (ct, ss []byte, err error) {
  46  	panic("AuthEncapsulateDeterministically is not supported for this KEM")
  47  }
  48  
  49  func (h hybridKEM) Encapsulate(pkr kem.PublicKey) (
  50  	ct []byte, ss []byte, err error,
  51  ) {
  52  	panic("Encapsulate is not implemented")
  53  }
  54  
  55  func (h hybridKEM) Decapsulate(skr kem.PrivateKey, ct []byte) ([]byte, error) {
  56  	hybridSk := skr.(*hybridKEMPrivKey)
  57  	ssA, err := h.kemA.Decapsulate(hybridSk.privA, ct[0:h.kemA.CiphertextSize()])
  58  	if err != nil {
  59  		return nil, err
  60  	}
  61  	ssB, err := h.kemB.Decapsulate(hybridSk.privB, ct[h.kemA.CiphertextSize():])
  62  	if err != nil {
  63  		return nil, err
  64  	}
  65  
  66  	ss := append(ssA, ssB...)
  67  
  68  	return ss, nil
  69  }
  70  
  71  func (h hybridKEM) EncapsulateDeterministically(
  72  	pkr kem.PublicKey, seed []byte,
  73  ) (ct, ss []byte, err error) {
  74  	hybridPk := pkr.(*hybridKEMPubKey)
  75  	encA, ssA, err := h.kemA.EncapsulateDeterministically(hybridPk.pubA, seed[0:h.kemA.EncapsulationSeedSize()])
  76  	if err != nil {
  77  		return nil, nil, err
  78  	}
  79  	encB, ssB, err := h.kemB.EncapsulateDeterministically(hybridPk.pubB, seed[h.kemA.EncapsulationSeedSize():])
  80  	if err != nil {
  81  		return nil, nil, err
  82  	}
  83  
  84  	ct = append(encA, encB...)
  85  	ss = append(ssA, ssB...)
  86  
  87  	return ct, ss, nil
  88  }
  89  
  90  type hybridKEMPrivKey struct {
  91  	scheme kem.Scheme
  92  	privA  kem.PrivateKey
  93  	privB  kem.PrivateKey
  94  }
  95  
  96  func (k *hybridKEMPrivKey) Scheme() kem.Scheme {
  97  	return k.scheme
  98  }
  99  
 100  func (k *hybridKEMPrivKey) MarshalBinary() ([]byte, error) {
 101  	skA, err := k.privA.MarshalBinary()
 102  	if err != nil {
 103  		return nil, err
 104  	}
 105  	skB, err := k.privB.MarshalBinary()
 106  	if err != nil {
 107  		return nil, err
 108  	}
 109  	return append(skA, skB...), nil
 110  }
 111  
 112  func (k *hybridKEMPrivKey) Equal(sk kem.PrivateKey) bool {
 113  	k1, ok := sk.(*hybridKEMPrivKey)
 114  	return ok &&
 115  		k.privA.Equal(k1.privA) &&
 116  		k.privB.Equal(k1.privB)
 117  }
 118  
 119  func (k *hybridKEMPrivKey) Public() kem.PublicKey {
 120  	return &hybridKEMPubKey{
 121  		scheme: k.scheme,
 122  		pubA:   k.privA.Public(),
 123  		pubB:   k.privB.Public(),
 124  	}
 125  }
 126  
 127  type hybridKEMPubKey struct {
 128  	scheme kem.Scheme
 129  	pubA   kem.PublicKey
 130  	pubB   kem.PublicKey
 131  }
 132  
 133  func (k *hybridKEMPubKey) Scheme() kem.Scheme {
 134  	return k.scheme
 135  }
 136  
 137  func (k hybridKEMPubKey) MarshalBinary() ([]byte, error) {
 138  	pkA, err := k.pubA.MarshalBinary()
 139  	if err != nil {
 140  		return nil, err
 141  	}
 142  	pkB, err := k.pubB.MarshalBinary()
 143  	if err != nil {
 144  		return nil, err
 145  	}
 146  	return append(pkA, pkB...), nil
 147  }
 148  
 149  func (k *hybridKEMPubKey) Equal(pk kem.PublicKey) bool {
 150  	k1, ok := pk.(*hybridKEMPubKey)
 151  	return ok &&
 152  		k.pubA.Equal(k1.pubA) &&
 153  		k.pubB.Equal(k1.pubB)
 154  }
 155  
 156  // Deterministically derives a keypair from a seed. If you're unsure,
 157  // you're better off using GenerateKey().
 158  //
 159  // Panics if seed is not of length SeedSize().
 160  func (h hybridKEM) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
 161  	// Implementation based on
 162  	// https://www.ietf.org/archive/id/draft-irtf-cfrg-hpke-07.html#name-derivekeypair
 163  	if len(seed) != h.SeedSize() {
 164  		panic(kem.ErrSeedSize)
 165  	}
 166  
 167  	outputSeedSize := h.kemA.SeedSize() + h.kemB.SeedSize()
 168  	dkpPrk := h.labeledExtract([]byte(""), []byte("dkp_prk"), seed)
 169  	bytes := h.labeledExpand(
 170  		dkpPrk,
 171  		[]byte("sk"),
 172  		nil,
 173  		uint16(outputSeedSize),
 174  	)
 175  	seedA := bytes[0:h.kemA.SeedSize()]
 176  	seedB := bytes[h.kemA.SeedSize():]
 177  	pubA, privA := h.kemA.DeriveKeyPair(seedA)
 178  	pubB, privB := h.kemB.DeriveKeyPair(seedB)
 179  
 180  	privKey := &hybridKEMPrivKey{
 181  		privA: privA,
 182  		privB: privB,
 183  	}
 184  	pubKey := &hybridKEMPubKey{
 185  		pubA: pubA,
 186  		pubB: pubB,
 187  	}
 188  
 189  	return pubKey, privKey
 190  }
 191  
 192  func (h hybridKEM) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
 193  	seed := make([]byte, h.SeedSize())
 194  	_, err := rand.Read(seed)
 195  	if err != nil {
 196  		return nil, nil, err
 197  	}
 198  	pk, sk := h.DeriveKeyPair(seed)
 199  	return pk, sk, nil
 200  }
 201  
 202  func (h hybridKEM) UnmarshalBinaryPrivateKey(data []byte) (kem.PrivateKey, error) {
 203  	skA, err := h.kemA.UnmarshalBinaryPrivateKey(data[0:h.kemA.PrivateKeySize()])
 204  	if err != nil {
 205  		return nil, err
 206  	}
 207  	skB, err := h.kemB.UnmarshalBinaryPrivateKey(data[h.kemA.PrivateKeySize():])
 208  	if err != nil {
 209  		return nil, err
 210  	}
 211  
 212  	return &hybridKEMPrivKey{
 213  		privA: skA,
 214  		privB: skB,
 215  	}, nil
 216  }
 217  
 218  func (h hybridKEM) UnmarshalBinaryPublicKey(data []byte) (kem.PublicKey, error) {
 219  	pkA, err := h.kemA.UnmarshalBinaryPublicKey(data[0:h.kemA.PublicKeySize()])
 220  	if err != nil {
 221  		return nil, err
 222  	}
 223  	pkB, err := h.kemB.UnmarshalBinaryPublicKey(data[h.kemA.PublicKeySize():])
 224  	if err != nil {
 225  		return nil, err
 226  	}
 227  
 228  	return &hybridKEMPubKey{
 229  		pubA: pkA,
 230  		pubB: pkB,
 231  	}, nil
 232  }
 233