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 }