xkem.go raw

   1  package hpke
   2  
   3  import (
   4  	"bytes"
   5  	"crypto/rand"
   6  	"crypto/subtle"
   7  	"fmt"
   8  	"io"
   9  
  10  	"github.com/cloudflare/circl/dh/x25519"
  11  	"github.com/cloudflare/circl/dh/x448"
  12  	"github.com/cloudflare/circl/kem"
  13  )
  14  
  15  type xKEM struct {
  16  	dhKemBase
  17  	size int
  18  }
  19  
  20  func (x xKEM) PrivateKeySize() int        { return x.size }
  21  func (x xKEM) SeedSize() int              { return x.size }
  22  func (x xKEM) CiphertextSize() int        { return x.size }
  23  func (x xKEM) PublicKeySize() int         { return x.size }
  24  func (x xKEM) EncapsulationSeedSize() int { return x.size }
  25  
  26  func (x xKEM) sizeDH() int { return x.size }
  27  func (x xKEM) calcDH(dh []byte, sk kem.PrivateKey, pk kem.PublicKey) error {
  28  	PK := pk.(*xKEMPubKey)
  29  	SK := sk.(*xKEMPrivKey)
  30  	switch x.size {
  31  	case x25519.Size:
  32  		var ss, sKey, pKey x25519.Key
  33  		copy(sKey[:], SK.priv)
  34  		copy(pKey[:], PK.pub)
  35  		if !x25519.Shared(&ss, &sKey, &pKey) {
  36  			return ErrInvalidKEMSharedSecret
  37  		}
  38  		copy(dh, ss[:])
  39  	case x448.Size:
  40  		var ss, sKey, pKey x448.Key
  41  		copy(sKey[:], SK.priv)
  42  		copy(pKey[:], PK.pub)
  43  		if !x448.Shared(&ss, &sKey, &pKey) {
  44  			return ErrInvalidKEMSharedSecret
  45  		}
  46  		copy(dh, ss[:])
  47  	}
  48  	return nil
  49  }
  50  
  51  // Deterministically derives a keypair from a seed. If you're unsure,
  52  // you're better off using GenerateKey().
  53  //
  54  // Panics if seed is not of length SeedSize().
  55  func (x xKEM) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
  56  	// Implementation based on
  57  	// https://www.ietf.org/archive/id/draft-irtf-cfrg-hpke-07.html#name-derivekeypair
  58  	if len(seed) != x.SeedSize() {
  59  		panic(kem.ErrSeedSize)
  60  	}
  61  	sk := &xKEMPrivKey{scheme: x, priv: make([]byte, x.size)}
  62  	dkpPrk := x.labeledExtract([]byte(""), []byte("dkp_prk"), seed)
  63  	bytes := x.labeledExpand(
  64  		dkpPrk,
  65  		[]byte("sk"),
  66  		nil,
  67  		uint16(x.PrivateKeySize()),
  68  	)
  69  	copy(sk.priv, bytes)
  70  	return sk.Public(), sk
  71  }
  72  
  73  func (x xKEM) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
  74  	sk := &xKEMPrivKey{scheme: x, priv: make([]byte, x.PrivateKeySize())}
  75  	_, err := io.ReadFull(rand.Reader, sk.priv)
  76  	if err != nil {
  77  		return nil, nil, err
  78  	}
  79  	return sk.Public(), sk, nil
  80  }
  81  
  82  func (x xKEM) UnmarshalBinaryPrivateKey(data []byte) (kem.PrivateKey, error) {
  83  	l := x.PrivateKeySize()
  84  	if len(data) < l {
  85  		return nil, ErrInvalidKEMPrivateKey
  86  	}
  87  	sk := &xKEMPrivKey{x, make([]byte, l), nil}
  88  	copy(sk.priv, data[:l])
  89  	if !sk.validate() {
  90  		return nil, ErrInvalidKEMPrivateKey
  91  	}
  92  	return sk, nil
  93  }
  94  
  95  func (x xKEM) UnmarshalBinaryPublicKey(data []byte) (kem.PublicKey, error) {
  96  	l := x.PublicKeySize()
  97  	if len(data) < l {
  98  		return nil, ErrInvalidKEMPublicKey
  99  	}
 100  	pk := &xKEMPubKey{x, make([]byte, l)}
 101  	copy(pk.pub, data[:l])
 102  	if !pk.validate() {
 103  		return nil, ErrInvalidKEMPublicKey
 104  	}
 105  	return pk, nil
 106  }
 107  
 108  type xKEMPubKey struct {
 109  	scheme xKEM
 110  	pub    []byte
 111  }
 112  
 113  func (k *xKEMPubKey) String() string     { return fmt.Sprintf("%x", k.pub) }
 114  func (k *xKEMPubKey) Scheme() kem.Scheme { return k.scheme }
 115  func (k *xKEMPubKey) MarshalBinary() ([]byte, error) {
 116  	return append(make([]byte, 0, k.scheme.PublicKeySize()), k.pub...), nil
 117  }
 118  
 119  func (k *xKEMPubKey) Equal(pk kem.PublicKey) bool {
 120  	k1, ok := pk.(*xKEMPubKey)
 121  	return ok &&
 122  		k.scheme.id == k1.scheme.id &&
 123  		bytes.Equal(k.pub, k1.pub)
 124  }
 125  func (k *xKEMPubKey) validate() bool { return len(k.pub) == k.scheme.PublicKeySize() }
 126  
 127  type xKEMPrivKey struct {
 128  	scheme xKEM
 129  	priv   []byte
 130  	pub    *xKEMPubKey
 131  }
 132  
 133  func (k *xKEMPrivKey) String() string     { return fmt.Sprintf("%x", k.priv) }
 134  func (k *xKEMPrivKey) Scheme() kem.Scheme { return k.scheme }
 135  func (k *xKEMPrivKey) MarshalBinary() ([]byte, error) {
 136  	return append(make([]byte, 0, k.scheme.PrivateKeySize()), k.priv...), nil
 137  }
 138  
 139  func (k *xKEMPrivKey) Equal(pk kem.PrivateKey) bool {
 140  	k1, ok := pk.(*xKEMPrivKey)
 141  	return ok &&
 142  		k.scheme.id == k1.scheme.id &&
 143  		subtle.ConstantTimeCompare(k.priv, k1.priv) == 1
 144  }
 145  
 146  func (k *xKEMPrivKey) Public() kem.PublicKey {
 147  	if k.pub == nil {
 148  		k.pub = &xKEMPubKey{scheme: k.scheme, pub: make([]byte, k.scheme.size)}
 149  		switch k.scheme.size {
 150  		case x25519.Size:
 151  			var sk, pk x25519.Key
 152  			copy(sk[:], k.priv)
 153  			x25519.KeyGen(&pk, &sk)
 154  			copy(k.pub.pub, pk[:])
 155  		case x448.Size:
 156  			var sk, pk x448.Key
 157  			copy(sk[:], k.priv)
 158  			x448.KeyGen(&pk, &sk)
 159  			copy(k.pub.pub, pk[:])
 160  		}
 161  	}
 162  	return k.pub
 163  }
 164  func (k *xKEMPrivKey) validate() bool { return len(k.priv) == k.scheme.PrivateKeySize() }
 165