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 )