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
13
14
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
25
26
27 func NewSike434(rng io.Reader) *KEM {
28 var c KEM
29 c.Allocate(Fp434, rng)
30 return &c
31 }
32
33
34
35
36 func NewSike503(rng io.Reader) *KEM {
37 var c KEM
38 c.Allocate(Fp503, rng)
39 return &c
40 }
41
42
43
44
45 func NewSike751(rng io.Reader) *KEM {
46 var c KEM
47 c.Allocate(Fp751, rng)
48 return &c
49 }
50
51
52
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
63
64
65
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
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
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
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
119
120
121
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
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
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
176
177
178
179
180
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
191
192
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
204 func (c *KEM) CiphertextSize() int {
205 return c.params.CiphertextSize
206 }
207
208
209 func (c *KEM) SharedSecretSize() int {
210 return c.params.KemSize
211 }
212
213
214 func (c *KEM) PublicKeySize() int {
215 return c.params.PublicKeySize
216 }
217
218
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
241
242
243 func (c *KEM) encrypt(ctext []byte, rng io.Reader, pub *PublicKey, ptext []byte) error {
244 ptextLen := len(ptext)
245
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
263
264
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
271
272
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 }
285
View as plain text