github.com/cloudflare/circl@v1.5.0/dh/sidh/sike.go (about) 1 package sidh 2 3 import ( 4 "crypto/subtle" 5 "errors" 6 "io" 7 8 "github.com/cloudflare/circl/dh/sidh/internal/common" 9 "github.com/cloudflare/circl/internal/sha3" 10 ) 11 12 // SIKE KEM interface. 13 // 14 // Deprecated: not cryptographically secure. 15 type KEM struct { 16 allocated bool 17 rng io.Reader 18 msg []byte 19 secretBytes []byte 20 params *common.SidhParams 21 shake sha3.State 22 } 23 24 // NewSike434 instantiates SIKE/p434 KEM. 25 // 26 // Deprecated: not cryptographically secure. 27 func NewSike434(rng io.Reader) *KEM { 28 var c KEM 29 c.Allocate(Fp434, rng) 30 return &c 31 } 32 33 // NewSike503 instantiates SIKE/p503 KEM. 34 // 35 // Deprecated: not cryptographically secure. 36 func NewSike503(rng io.Reader) *KEM { 37 var c KEM 38 c.Allocate(Fp503, rng) 39 return &c 40 } 41 42 // NewSike751 instantiates SIKE/p751 KEM. 43 // 44 // Deprecated: not cryptographically secure. 45 func NewSike751(rng io.Reader) *KEM { 46 var c KEM 47 c.Allocate(Fp751, rng) 48 return &c 49 } 50 51 // Allocate allocates KEM object for multiple SIKE operations. The rng 52 // must be cryptographically secure PRNG. 53 func (c *KEM) Allocate(id uint8, rng io.Reader) { 54 c.rng = rng 55 c.params = common.Params(id) 56 c.msg = make([]byte, c.params.MsgLen) 57 c.secretBytes = make([]byte, c.params.A.SecretByteLen) 58 c.shake = sha3.NewShake256() 59 c.allocated = true 60 } 61 62 // Encapsulate receives the public key and generates SIKE ciphertext and shared secret. 63 // The generated ciphertext is used for authentication. 64 // Error is returned in case PRNG fails. Function panics in case wrongly formatted 65 // input was provided. 66 func (c *KEM) Encapsulate(ciphertext, secret []byte, pub *PublicKey) error { 67 if !c.allocated { 68 panic("KEM unallocated") 69 } 70 71 if KeyVariantSike != pub.keyVariant { 72 panic("Wrong type of public key") 73 } 74 75 if len(secret) < c.SharedSecretSize() { 76 panic("shared secret buffer to small") 77 } 78 79 if len(ciphertext) < c.CiphertextSize() { 80 panic("ciphertext buffer to small") 81 } 82 83 // Generate ephemeral value 84 _, err := io.ReadFull(c.rng, c.msg[:]) 85 if err != nil { 86 return err 87 } 88 89 var buf [3 * common.MaxSharedSecretBsz]byte 90 skA := PrivateKey{ 91 key: key{ 92 params: c.params, 93 keyVariant: KeyVariantSidhA, 94 }, 95 Scalar: c.secretBytes, 96 } 97 pkA := NewPublicKey(c.params.ID, KeyVariantSidhA) 98 99 pub.Export(buf[:]) 100 c.shake.Reset() 101 _, _ = c.shake.Write(c.msg) 102 _, _ = c.shake.Write(buf[:3*c.params.SharedSecretSize]) 103 _, _ = c.shake.Read(skA.Scalar) 104 105 // Ensure bitlength is not bigger then to 2^e2-1 106 skA.Scalar[len(skA.Scalar)-1] &= (1 << (c.params.A.SecretBitLen % 8)) - 1 107 skA.GeneratePublicKey(pkA) 108 c.generateCiphertext(ciphertext, &skA, pkA, pub, c.msg[:]) 109 110 // K = H(msg||(c0||c1)) 111 c.shake.Reset() 112 _, _ = c.shake.Write(c.msg) 113 _, _ = c.shake.Write(ciphertext) 114 _, _ = c.shake.Read(secret[:c.SharedSecretSize()]) 115 return nil 116 } 117 118 // Decapsulate given the keypair and ciphertext as inputs, Decapsulate outputs a shared 119 // secret if plaintext verifies correctly, otherwise function outputs random value. 120 // Decapsulation may panic in case input is wrongly formatted, in particular, size of 121 // the 'ciphertext' must be exactly equal to c.CiphertextSize(). 122 func (c *KEM) Decapsulate(secret []byte, prv *PrivateKey, pub *PublicKey, ciphertext []byte) error { 123 if !c.allocated { 124 panic("KEM unallocated") 125 } 126 127 if KeyVariantSike != pub.keyVariant { 128 panic("Wrong type of public key") 129 } 130 131 if pub.keyVariant != prv.keyVariant { 132 panic("Public and private key are of different type") 133 } 134 135 if len(secret) < c.SharedSecretSize() { 136 panic("shared secret buffer to small") 137 } 138 139 if len(ciphertext) != c.CiphertextSize() { 140 panic("ciphertext buffer to small") 141 } 142 143 var m [common.MaxMsgBsz]byte 144 var r [common.MaxSidhPrivateKeyBsz]byte 145 var pkBytes [3 * common.MaxSharedSecretBsz]byte 146 skA := PrivateKey{ 147 key: key{ 148 params: c.params, 149 keyVariant: KeyVariantSidhA, 150 }, 151 Scalar: c.secretBytes, 152 } 153 pkA := NewPublicKey(c.params.ID, KeyVariantSidhA) 154 c1Len, err := c.decrypt(m[:], prv, ciphertext) 155 if err != nil { 156 return err 157 } 158 159 // r' = G(m'||pub) 160 pub.Export(pkBytes[:]) 161 c.shake.Reset() 162 _, _ = c.shake.Write(m[:c1Len]) 163 _, _ = c.shake.Write(pkBytes[:3*c.params.SharedSecretSize]) 164 _, _ = c.shake.Read(r[:c.params.A.SecretByteLen]) 165 // Ensure bitlength is not bigger than 2^e2-1 166 r[c.params.A.SecretByteLen-1] &= (1 << (c.params.A.SecretBitLen % 8)) - 1 167 168 err = skA.Import(r[:c.params.A.SecretByteLen]) 169 if err != nil { 170 return err 171 } 172 skA.GeneratePublicKey(pkA) 173 pkA.Export(pkBytes[:]) 174 175 // S is chosen at random when generating a key and unknown to other party. It is 176 // important that S is unpredictable to the other party. Without this check, would 177 // be possible to recover a secret, by providing series of invalid ciphertexts. 178 // 179 // See more details in "On the security of supersingular isogeny cryptosystems" 180 // (S. Galbraith, et al., 2016, ePrint #859). 181 mask := subtle.ConstantTimeCompare(pkBytes[:c.params.PublicKeySize], ciphertext[:pub.params.PublicKeySize]) 182 common.Cpick(mask, m[:c1Len], m[:c1Len], prv.S) 183 c.shake.Reset() 184 _, _ = c.shake.Write(m[:c1Len]) 185 _, _ = c.shake.Write(ciphertext) 186 _, _ = c.shake.Read(secret[:c.SharedSecretSize()]) 187 return nil 188 } 189 190 // Resets internal state of KEM. Function should be used 191 // after Allocate and between subsequent calls to Encapsulate 192 // and/or Decapsulate. 193 func (c *KEM) Reset() { 194 for i := range c.msg { 195 c.msg[i] = 0 196 } 197 198 for i := range c.secretBytes { 199 c.secretBytes[i] = 0 200 } 201 } 202 203 // Returns size of resulting ciphertext. 204 func (c *KEM) CiphertextSize() int { 205 return c.params.CiphertextSize 206 } 207 208 // Returns size of resulting shared secret. 209 func (c *KEM) SharedSecretSize() int { 210 return c.params.KemSize 211 } 212 213 // PublicKeySize returns size of the public key in bytes. 214 func (c *KEM) PublicKeySize() int { 215 return c.params.PublicKeySize 216 } 217 218 // Size returns size of the private key in bytes. 219 func (c *KEM) PrivateKeySize() int { 220 return int(c.params.B.SecretByteLen) + c.params.MsgLen 221 } 222 223 func (c *KEM) generateCiphertext(ctext []byte, skA *PrivateKey, pkA, pkB *PublicKey, ptext []byte) { 224 var n [common.MaxMsgBsz]byte 225 var j [common.MaxSharedSecretBsz]byte 226 ptextLen := skA.params.MsgLen 227 228 skA.DeriveSecret(j[:], pkB) 229 c.shake.Reset() 230 _, _ = c.shake.Write(j[:skA.params.SharedSecretSize]) 231 _, _ = c.shake.Read(n[:ptextLen]) 232 for i := range ptext { 233 n[i] ^= ptext[i] 234 } 235 236 pkA.Export(ctext) 237 copy(ctext[pkA.Size():], n[:ptextLen]) 238 } 239 240 // encrypt uses SIKE public key to encrypt plaintext. Requires cryptographically secure 241 // PRNG. Returns ciphertext in case encryption succeeds. Returns error in case PRNG fails 242 // or wrongly formated input was provided. 243 func (c *KEM) encrypt(ctext []byte, rng io.Reader, pub *PublicKey, ptext []byte) error { 244 ptextLen := len(ptext) 245 // c1 must be security level + 64 bits (see [SIKE] 1.4 and 4.3.3) 246 if ptextLen != pub.params.KemSize { 247 return errors.New("unsupported message length") 248 } 249 250 skA := NewPrivateKey(pub.params.ID, KeyVariantSidhA) 251 pkA := NewPublicKey(pub.params.ID, KeyVariantSidhA) 252 err := skA.Generate(rng) 253 if err != nil { 254 return err 255 } 256 257 skA.GeneratePublicKey(pkA) 258 c.generateCiphertext(ctext, skA, pkA, pub, ptext) 259 return nil 260 } 261 262 // decrypt uses SIKE private key to decrypt ciphertext. Returns plaintext in case 263 // decryption succeeds or error in case unexpected input was provided. 264 // Constant time. 265 func (c *KEM) decrypt(n []byte, prv *PrivateKey, ctext []byte) (int, error) { 266 var c1Len int 267 var j [common.MaxSharedSecretBsz]byte 268 pkLen := prv.params.PublicKeySize 269 270 // ctext is a concatenation of (ciphertext = pubkey_A || c1) 271 // it must be security level + 64 bits (see [SIKE] 1.4 and 4.3.3) 272 // Lengths has been already checked by Decapsulate() 273 c1Len = len(ctext) - pkLen 274 c0 := NewPublicKey(prv.params.ID, KeyVariantSidhA) 275 err := c0.Import(ctext[:pkLen]) 276 prv.DeriveSecret(j[:], c0) 277 c.shake.Reset() 278 _, _ = c.shake.Write(j[:prv.params.SharedSecretSize]) 279 _, _ = c.shake.Read(n[:c1Len]) 280 for i := range n[:c1Len] { 281 n[i] ^= ctext[pkLen+i] 282 } 283 return c1Len, err 284 }