github.com/cloudflare/circl@v1.5.0/kem/hybrid/ckem.go (about)

     1  package hybrid
     2  
     3  // TODO move over to crypto/ecdh once we can assume Go 1.20.
     4  
     5  import (
     6  	"crypto/elliptic"
     7  	cryptoRand "crypto/rand"
     8  	"crypto/subtle"
     9  	"math/big"
    10  
    11  	"github.com/cloudflare/circl/kem"
    12  	"github.com/cloudflare/circl/xof"
    13  )
    14  
    15  type cPublicKey struct {
    16  	scheme *cScheme
    17  	x, y   *big.Int
    18  }
    19  type cPrivateKey struct {
    20  	scheme *cScheme
    21  	key    []byte
    22  }
    23  type cScheme struct {
    24  	curve elliptic.Curve
    25  }
    26  
    27  var p256Kem = &cScheme{elliptic.P256()}
    28  
    29  func (sch *cScheme) scSize() int {
    30  	return (sch.curve.Params().N.BitLen() + 7) / 8
    31  }
    32  
    33  func (sch *cScheme) ptSize() int {
    34  	return (sch.curve.Params().BitSize + 7) / 8
    35  }
    36  
    37  func (sch *cScheme) Name() string {
    38  	return sch.curve.Params().Name
    39  }
    40  
    41  func (sch *cScheme) PublicKeySize() int {
    42  	return 2*sch.ptSize() + 1
    43  }
    44  
    45  func (sch *cScheme) PrivateKeySize() int {
    46  	return sch.scSize()
    47  }
    48  
    49  func (sch *cScheme) SeedSize() int {
    50  	return sch.PrivateKeySize()
    51  }
    52  
    53  func (sch *cScheme) SharedKeySize() int {
    54  	return sch.ptSize()
    55  }
    56  
    57  func (sch *cScheme) CiphertextSize() int {
    58  	return sch.PublicKeySize()
    59  }
    60  
    61  func (sch *cScheme) EncapsulationSeedSize() int {
    62  	return sch.SeedSize()
    63  }
    64  
    65  func (sk *cPrivateKey) Scheme() kem.Scheme { return sk.scheme }
    66  func (pk *cPublicKey) Scheme() kem.Scheme  { return pk.scheme }
    67  
    68  func (sk *cPrivateKey) MarshalBinary() ([]byte, error) {
    69  	ret := make([]byte, len(sk.key))
    70  	copy(ret, sk.key)
    71  	return ret, nil
    72  }
    73  
    74  func (sk *cPrivateKey) Equal(other kem.PrivateKey) bool {
    75  	oth, ok := other.(*cPrivateKey)
    76  	if !ok {
    77  		return false
    78  	}
    79  	if oth.scheme != sk.scheme {
    80  		return false
    81  	}
    82  	return subtle.ConstantTimeCompare(oth.key, sk.key) == 1
    83  }
    84  
    85  func (sk *cPrivateKey) Public() kem.PublicKey {
    86  	x, y := sk.scheme.curve.ScalarBaseMult(sk.key)
    87  	return &cPublicKey{
    88  		sk.scheme,
    89  		x,
    90  		y,
    91  	}
    92  }
    93  
    94  func (pk *cPublicKey) Equal(other kem.PublicKey) bool {
    95  	oth, ok := other.(*cPublicKey)
    96  	if !ok {
    97  		return false
    98  	}
    99  	if oth.scheme != pk.scheme {
   100  		return false
   101  	}
   102  	return oth.x.Cmp(pk.x) == 0 && oth.y.Cmp(pk.y) == 0
   103  }
   104  
   105  func (pk *cPublicKey) MarshalBinary() ([]byte, error) {
   106  	return elliptic.Marshal(pk.scheme.curve, pk.x, pk.y), nil
   107  }
   108  
   109  func (sch *cScheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
   110  	seed := make([]byte, sch.SeedSize())
   111  	_, err := cryptoRand.Read(seed)
   112  	if err != nil {
   113  		return nil, nil, err
   114  	}
   115  	pk, sk := sch.DeriveKeyPair(seed)
   116  	return pk, sk, nil
   117  }
   118  
   119  func (sch *cScheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
   120  	if len(seed) != sch.SeedSize() {
   121  		panic(kem.ErrSeedSize)
   122  	}
   123  	h := xof.SHAKE256.New()
   124  	_, _ = h.Write(seed)
   125  	key, x, y, err := elliptic.GenerateKey(sch.curve, h)
   126  	if err != nil {
   127  		panic(err)
   128  	}
   129  
   130  	sk := cPrivateKey{scheme: sch, key: key}
   131  	pk := cPublicKey{scheme: sch, x: x, y: y}
   132  
   133  	return &pk, &sk
   134  }
   135  
   136  func (sch *cScheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) {
   137  	seed := make([]byte, sch.EncapsulationSeedSize())
   138  	_, err = cryptoRand.Read(seed)
   139  	if err != nil {
   140  		return
   141  	}
   142  	return sch.EncapsulateDeterministically(pk, seed)
   143  }
   144  
   145  func (pk *cPublicKey) X(sk *cPrivateKey) []byte {
   146  	if pk.scheme != sk.scheme {
   147  		panic(kem.ErrTypeMismatch)
   148  	}
   149  
   150  	sharedKey := make([]byte, pk.scheme.SharedKeySize())
   151  	xShared, _ := pk.scheme.curve.ScalarMult(pk.x, pk.y, sk.key)
   152  	xShared.FillBytes(sharedKey)
   153  	return sharedKey
   154  }
   155  
   156  func (sch *cScheme) EncapsulateDeterministically(
   157  	pk kem.PublicKey, seed []byte,
   158  ) (ct, ss []byte, err error) {
   159  	if len(seed) != sch.EncapsulationSeedSize() {
   160  		return nil, nil, kem.ErrSeedSize
   161  	}
   162  	pub, ok := pk.(*cPublicKey)
   163  	if !ok || pub.scheme != sch {
   164  		return nil, nil, kem.ErrTypeMismatch
   165  	}
   166  
   167  	pk2, sk2 := sch.DeriveKeyPair(seed)
   168  	ss = pub.X(sk2.(*cPrivateKey))
   169  	ct, _ = pk2.MarshalBinary()
   170  	return
   171  }
   172  
   173  func (sch *cScheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) {
   174  	if len(ct) != sch.CiphertextSize() {
   175  		return nil, kem.ErrCiphertextSize
   176  	}
   177  
   178  	priv, ok := sk.(*cPrivateKey)
   179  	if !ok || priv.scheme != sch {
   180  		return nil, kem.ErrTypeMismatch
   181  	}
   182  
   183  	pk, err := sch.UnmarshalBinaryPublicKey(ct)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	ss := pk.(*cPublicKey).X(priv)
   189  	return ss, nil
   190  }
   191  
   192  func (sch *cScheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) {
   193  	if len(buf) != sch.PublicKeySize() {
   194  		return nil, kem.ErrPubKeySize
   195  	}
   196  	x, y := elliptic.Unmarshal(sch.curve, buf)
   197  	return &cPublicKey{sch, x, y}, nil
   198  }
   199  
   200  func (sch *cScheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) {
   201  	if len(buf) != sch.PrivateKeySize() {
   202  		return nil, kem.ErrPrivKeySize
   203  	}
   204  	ret := cPrivateKey{sch, make([]byte, sch.PrivateKeySize())}
   205  	copy(ret.key, buf)
   206  	return &ret, nil
   207  }