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

     1  // Package hpke implements the Hybrid Public Key Encryption (HPKE) standard
     2  // specified by draft-irtf-cfrg-hpke-07.
     3  //
     4  // HPKE works for any combination of a public-key encapsulation mechanism
     5  // (KEM), a key derivation function (KDF), and an authenticated encryption
     6  // scheme with additional data (AEAD).
     7  //
     8  // Specification in
     9  // https://datatracker.ietf.org/doc/draft-irtf-cfrg-hpke
    10  //
    11  // BUG(cjpatton): This package does not implement the "Export-Only" mode of the
    12  // HPKE context. In particular, it does not recognize the AEAD codepoint
    13  // reserved for this purpose (0xFFFF).
    14  package hpke
    15  
    16  import (
    17  	"crypto/rand"
    18  	"encoding"
    19  	"errors"
    20  	"io"
    21  
    22  	"github.com/cloudflare/circl/kem"
    23  )
    24  
    25  const versionLabel = "HPKE-v1"
    26  
    27  // Context defines the capabilities of an HPKE context.
    28  type Context interface {
    29  	encoding.BinaryMarshaler
    30  	// Export takes a context string exporterContext and a desired length (in
    31  	// bytes), and produces a secret derived from the internal exporter secret
    32  	// using the corresponding KDF Expand function. It panics if length is
    33  	// greater than 255*N bytes, where N is the size (in bytes) of the KDF's
    34  	// output.
    35  	Export(exporterContext []byte, length uint) []byte
    36  	// Suite returns the cipher suite corresponding to this context.
    37  	Suite() Suite
    38  }
    39  
    40  // Sealer encrypts a plaintext using an AEAD encryption.
    41  type Sealer interface {
    42  	Context
    43  	// Seal takes a plaintext and associated data to produce a ciphertext.
    44  	// The nonce is handled by the Sealer and incremented after each call.
    45  	Seal(pt, aad []byte) (ct []byte, err error)
    46  }
    47  
    48  // Opener decrypts a ciphertext using an AEAD encryption.
    49  type Opener interface {
    50  	Context
    51  	// Open takes a ciphertext and associated data to recover, if successful,
    52  	// the plaintext. The nonce is handled by the Opener and incremented after
    53  	// each call.
    54  	Open(ct, aad []byte) (pt []byte, err error)
    55  }
    56  
    57  // modeID represents an HPKE variant.
    58  type modeID = uint8
    59  
    60  const (
    61  	// modeBase to enable encryption to the holder of a given KEM private key.
    62  	modeBase modeID = 0x00
    63  	// modePSK extends the base mode by allowing the Receiver to authenticate
    64  	// that the sender possessed a given pre-shared key (PSK).
    65  	modePSK modeID = 0x01
    66  	// modeAuth extends the base mode by allowing the Receiver to authenticate
    67  	// that the sender possessed a given KEM private key.
    68  	modeAuth modeID = 0x02
    69  	// modeAuthPSK provides a combination of the PSK and Auth modes.
    70  	modeAuthPSK modeID = 0x03
    71  )
    72  
    73  // Suite is an HPKE cipher suite consisting of a KEM, KDF, and AEAD algorithm.
    74  type Suite struct {
    75  	kemID  KEM
    76  	kdfID  KDF
    77  	aeadID AEAD
    78  }
    79  
    80  // NewSuite builds a Suite from a specified set of algorithms. Panics
    81  // if an algorithm identifier is not valid.
    82  func NewSuite(kemID KEM, kdfID KDF, aeadID AEAD) Suite {
    83  	s := Suite{kemID, kdfID, aeadID}
    84  	if !s.isValid() {
    85  		panic(ErrInvalidHPKESuite)
    86  	}
    87  	return s
    88  }
    89  
    90  type state struct {
    91  	Suite
    92  	modeID modeID
    93  	skS    kem.PrivateKey
    94  	pkS    kem.PublicKey
    95  	psk    []byte
    96  	pskID  []byte
    97  	info   []byte
    98  }
    99  
   100  // Sender performs hybrid public-key encryption.
   101  type Sender struct {
   102  	state
   103  	pkR kem.PublicKey
   104  }
   105  
   106  // NewSender creates a Sender with knowledge of the receiver's public-key.
   107  func (suite Suite) NewSender(pkR kem.PublicKey, info []byte) (*Sender, error) {
   108  	return &Sender{
   109  		state: state{Suite: suite, info: info},
   110  		pkR:   pkR,
   111  	}, nil
   112  }
   113  
   114  // Setup generates a new HPKE context used for Base Mode encryption.
   115  // Returns the Sealer and corresponding encapsulated key.
   116  func (s *Sender) Setup(rnd io.Reader) (enc []byte, seal Sealer, err error) {
   117  	s.modeID = modeBase
   118  	return s.allSetup(rnd)
   119  }
   120  
   121  // SetupAuth generates a new HPKE context used for Auth Mode encryption.
   122  // Returns the Sealer and corresponding encapsulated key.
   123  func (s *Sender) SetupAuth(rnd io.Reader, skS kem.PrivateKey) (
   124  	enc []byte, seal Sealer, err error,
   125  ) {
   126  	s.modeID = modeAuth
   127  	s.state.skS = skS
   128  	return s.allSetup(rnd)
   129  }
   130  
   131  // SetupPSK generates a new HPKE context used for PSK Mode encryption.
   132  // Returns the Sealer and corresponding encapsulated key.
   133  func (s *Sender) SetupPSK(rnd io.Reader, psk, pskID []byte) (
   134  	enc []byte, seal Sealer, err error,
   135  ) {
   136  	s.modeID = modePSK
   137  	s.state.psk = psk
   138  	s.state.pskID = pskID
   139  	return s.allSetup(rnd)
   140  }
   141  
   142  // SetupAuthPSK generates a new HPKE context used for Auth-PSK Mode encryption.
   143  // Returns the Sealer and corresponding encapsulated key.
   144  func (s *Sender) SetupAuthPSK(rnd io.Reader, skS kem.PrivateKey, psk, pskID []byte) (
   145  	enc []byte, seal Sealer, err error,
   146  ) {
   147  	s.modeID = modeAuthPSK
   148  	s.state.skS = skS
   149  	s.state.psk = psk
   150  	s.state.pskID = pskID
   151  	return s.allSetup(rnd)
   152  }
   153  
   154  // Receiver performs hybrid public-key decryption.
   155  type Receiver struct {
   156  	state
   157  	skR kem.PrivateKey
   158  	enc []byte
   159  }
   160  
   161  // NewReceiver creates a Receiver with knowledge of a private key.
   162  func (suite Suite) NewReceiver(skR kem.PrivateKey, info []byte) (
   163  	*Receiver, error,
   164  ) {
   165  	return &Receiver{state: state{Suite: suite, info: info}, skR: skR}, nil
   166  }
   167  
   168  // Setup generates a new HPKE context used for Base Mode encryption.
   169  // Setup takes an encapsulated key and returns an Opener.
   170  func (r *Receiver) Setup(enc []byte) (Opener, error) {
   171  	r.modeID = modeBase
   172  	r.enc = enc
   173  	return r.allSetup()
   174  }
   175  
   176  // SetupAuth generates a new HPKE context used for Auth Mode encryption.
   177  // SetupAuth takes an encapsulated key and a public key, and returns an Opener.
   178  func (r *Receiver) SetupAuth(enc []byte, pkS kem.PublicKey) (Opener, error) {
   179  	r.modeID = modeAuth
   180  	r.enc = enc
   181  	r.state.pkS = pkS
   182  	return r.allSetup()
   183  }
   184  
   185  // SetupPSK generates a new HPKE context used for PSK Mode encryption.
   186  // SetupPSK takes an encapsulated key, and a pre-shared key; and returns an
   187  // Opener.
   188  func (r *Receiver) SetupPSK(enc, psk, pskID []byte) (Opener, error) {
   189  	r.modeID = modePSK
   190  	r.enc = enc
   191  	r.state.psk = psk
   192  	r.state.pskID = pskID
   193  	return r.allSetup()
   194  }
   195  
   196  // SetupAuthPSK generates a new HPKE context used for Auth-PSK Mode encryption.
   197  // SetupAuthPSK takes an encapsulated key, a public key, and a pre-shared key;
   198  // and returns an Opener.
   199  func (r *Receiver) SetupAuthPSK(
   200  	enc, psk, pskID []byte, pkS kem.PublicKey,
   201  ) (Opener, error) {
   202  	r.modeID = modeAuthPSK
   203  	r.enc = enc
   204  	r.state.psk = psk
   205  	r.state.pskID = pskID
   206  	r.state.pkS = pkS
   207  	return r.allSetup()
   208  }
   209  
   210  func (s *Sender) allSetup(rnd io.Reader) ([]byte, Sealer, error) {
   211  	scheme := s.kemID.Scheme()
   212  
   213  	if rnd == nil {
   214  		rnd = rand.Reader
   215  	}
   216  	seed := make([]byte, scheme.EncapsulationSeedSize())
   217  	_, err := io.ReadFull(rnd, seed)
   218  	if err != nil {
   219  		return nil, nil, err
   220  	}
   221  
   222  	var enc, ss []byte
   223  	switch s.modeID {
   224  	case modeBase, modePSK:
   225  		enc, ss, err = scheme.EncapsulateDeterministically(s.pkR, seed)
   226  	case modeAuth, modeAuthPSK:
   227  		enc, ss, err = scheme.AuthEncapsulateDeterministically(s.pkR, s.skS, seed)
   228  	}
   229  	if err != nil {
   230  		return nil, nil, err
   231  	}
   232  
   233  	ctx, err := s.keySchedule(ss, s.info, s.psk, s.pskID)
   234  	if err != nil {
   235  		return nil, nil, err
   236  	}
   237  
   238  	return enc, &sealContext{ctx}, nil
   239  }
   240  
   241  func (r *Receiver) allSetup() (Opener, error) {
   242  	var err error
   243  	var ss []byte
   244  	scheme := r.kemID.Scheme()
   245  	switch r.modeID {
   246  	case modeBase, modePSK:
   247  		ss, err = scheme.Decapsulate(r.skR, r.enc)
   248  	case modeAuth, modeAuthPSK:
   249  		ss, err = scheme.AuthDecapsulate(r.skR, r.enc, r.pkS)
   250  	}
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	ctx, err := r.keySchedule(ss, r.info, r.psk, r.pskID)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  	return &openContext{ctx}, nil
   260  }
   261  
   262  var (
   263  	ErrInvalidHPKESuite       = errors.New("hpke: invalid HPKE suite")
   264  	ErrInvalidKDF             = errors.New("hpke: invalid KDF identifier")
   265  	ErrInvalidKEM             = errors.New("hpke: invalid KEM identifier")
   266  	ErrInvalidAEAD            = errors.New("hpke: invalid AEAD identifier")
   267  	ErrInvalidKEMPublicKey    = errors.New("hpke: invalid KEM public key")
   268  	ErrInvalidKEMPrivateKey   = errors.New("hpke: invalid KEM private key")
   269  	ErrInvalidKEMSharedSecret = errors.New("hpke: invalid KEM shared secret")
   270  	ErrAEADSeqOverflows       = errors.New("hpke: AEAD sequence number overflows")
   271  )