github.com/cloudflare/circl@v1.5.0/hpke/hybridkem.go (about)

     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  }