Skip to content

Commit 9de48e3

Browse files
bwesterbarmfazh
authored andcommitted
Use plain X{25519,448} for PQ hybrids and reverse order
This should match more closely to what we expect will be adopted. The old CECPQ2 experiment uses this plain X25519 and order of shares.
1 parent 5170e38 commit 9de48e3

File tree

2 files changed

+212
-5
lines changed

2 files changed

+212
-5
lines changed

kem/hybrid/hybrid.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ package hybrid
3333
import (
3434
"errors"
3535

36-
"github.com/cloudflare/circl/hpke"
3736
"github.com/cloudflare/circl/internal/sha3"
3837
"github.com/cloudflare/circl/kem"
3938
"github.com/cloudflare/circl/kem/kyber/kyber1024"
@@ -57,26 +56,26 @@ func Kyber1024X448() kem.Scheme { return kyber1024X }
5756

5857
var kyber512X kem.Scheme = &scheme{
5958
"Kyber512-X25519",
59+
x25519Kem,
6060
kyber512.Scheme(),
61-
hpke.KEM_X25519_HKDF_SHA256.Scheme(),
6261
}
6362

6463
var kyber768X kem.Scheme = &scheme{
6564
"Kyber768-X25519",
65+
x25519Kem,
6666
kyber768.Scheme(),
67-
hpke.KEM_X25519_HKDF_SHA256.Scheme(),
6867
}
6968

7069
var kyber768X4 kem.Scheme = &scheme{
7170
"Kyber768-X448",
71+
x448Kem,
7272
kyber768.Scheme(),
73-
hpke.KEM_X448_HKDF_SHA512.Scheme(),
7473
}
7574

7675
var kyber1024X kem.Scheme = &scheme{
7776
"Kyber1024-X448",
77+
x448Kem,
7878
kyber1024.Scheme(),
79-
hpke.KEM_X448_HKDF_SHA512.Scheme(),
8079
}
8180

8281
// Public key of a hybrid KEM.

kem/hybrid/xkem.go

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package hybrid
2+
3+
import (
4+
"bytes"
5+
cryptoRand "crypto/rand"
6+
"crypto/subtle"
7+
8+
"github.com/cloudflare/circl/dh/x25519"
9+
"github.com/cloudflare/circl/dh/x448"
10+
"github.com/cloudflare/circl/internal/sha3"
11+
"github.com/cloudflare/circl/kem"
12+
)
13+
14+
type xPublicKey struct {
15+
scheme *xScheme
16+
key []byte
17+
}
18+
type xPrivateKey struct {
19+
scheme *xScheme
20+
key []byte
21+
}
22+
type xScheme struct {
23+
size int
24+
}
25+
26+
var (
27+
x25519Kem = &xScheme{x25519.Size}
28+
x448Kem = &xScheme{x448.Size}
29+
)
30+
31+
func (sch *xScheme) Name() string {
32+
switch sch.size {
33+
case x25519.Size:
34+
return "X25519"
35+
case x448.Size:
36+
return "X448"
37+
}
38+
panic(kem.ErrTypeMismatch)
39+
}
40+
41+
func (sch *xScheme) PublicKeySize() int { return sch.size }
42+
func (sch *xScheme) PrivateKeySize() int { return sch.size }
43+
func (sch *xScheme) SeedSize() int { return sch.size }
44+
func (sch *xScheme) SharedKeySize() int { return sch.size }
45+
func (sch *xScheme) CiphertextSize() int { return sch.size }
46+
func (sch *xScheme) EncapsulationSeedSize() int { return sch.size }
47+
48+
func (sk *xPrivateKey) Scheme() kem.Scheme { return sk.scheme }
49+
func (pk *xPublicKey) Scheme() kem.Scheme { return pk.scheme }
50+
51+
func (sk *xPrivateKey) MarshalBinary() ([]byte, error) {
52+
ret := make([]byte, len(sk.key))
53+
copy(ret, sk.key)
54+
return ret, nil
55+
}
56+
57+
func (sk *xPrivateKey) Equal(other kem.PrivateKey) bool {
58+
oth, ok := other.(*xPrivateKey)
59+
if !ok {
60+
return false
61+
}
62+
if oth.scheme != sk.scheme {
63+
return false
64+
}
65+
return subtle.ConstantTimeCompare(oth.key, sk.key) == 1
66+
}
67+
68+
func (sk *xPrivateKey) Public() kem.PublicKey {
69+
pk := xPublicKey{sk.scheme, make([]byte, sk.scheme.size)}
70+
switch sk.scheme.size {
71+
case x25519.Size:
72+
var sk2, pk2 x25519.Key
73+
copy(sk2[:], sk.key)
74+
x25519.KeyGen(&pk2, &sk2)
75+
copy(pk.key, pk2[:])
76+
case x448.Size:
77+
var sk2, pk2 x448.Key
78+
copy(sk2[:], sk.key)
79+
x448.KeyGen(&pk2, &sk2)
80+
copy(pk.key, pk2[:])
81+
}
82+
return &pk
83+
}
84+
85+
func (pk *xPublicKey) Equal(other kem.PublicKey) bool {
86+
oth, ok := other.(*xPublicKey)
87+
if !ok {
88+
return false
89+
}
90+
if oth.scheme != pk.scheme {
91+
return false
92+
}
93+
return bytes.Equal(oth.key, pk.key)
94+
}
95+
96+
func (pk *xPublicKey) MarshalBinary() ([]byte, error) {
97+
ret := make([]byte, pk.scheme.size)
98+
copy(ret, pk.key)
99+
return ret, nil
100+
}
101+
102+
func (sch *xScheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
103+
seed := make([]byte, sch.SeedSize())
104+
_, err := cryptoRand.Read(seed)
105+
if err != nil {
106+
return nil, nil, err
107+
}
108+
pk, sk := sch.DeriveKeyPair(seed)
109+
return pk, sk, nil
110+
}
111+
112+
func (sch *xScheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
113+
if len(seed) != sch.SeedSize() {
114+
panic(kem.ErrSeedSize)
115+
}
116+
sk := xPrivateKey{scheme: sch, key: make([]byte, sch.size)}
117+
118+
h := sha3.NewShake256()
119+
_, _ = h.Write(seed)
120+
_, _ = h.Read(sk.key)
121+
122+
return sk.Public(), &sk
123+
}
124+
125+
func (sch *xScheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) {
126+
seed := make([]byte, sch.EncapsulationSeedSize())
127+
_, err = cryptoRand.Read(seed)
128+
if err != nil {
129+
return
130+
}
131+
return sch.EncapsulateDeterministically(pk, seed)
132+
}
133+
134+
func (pk *xPublicKey) X(sk *xPrivateKey) []byte {
135+
if pk.scheme != sk.scheme {
136+
panic(kem.ErrTypeMismatch)
137+
}
138+
139+
switch pk.scheme.size {
140+
case x25519.Size:
141+
var ss2, pk2, sk2 x25519.Key
142+
copy(pk2[:], pk.key)
143+
copy(sk2[:], sk.key)
144+
x25519.Shared(&ss2, &sk2, &pk2)
145+
return ss2[:]
146+
case x448.Size:
147+
var ss2, pk2, sk2 x448.Key
148+
copy(pk2[:], pk.key)
149+
copy(sk2[:], sk.key)
150+
x448.Shared(&ss2, &sk2, &pk2)
151+
return ss2[:]
152+
}
153+
panic(kem.ErrTypeMismatch)
154+
}
155+
156+
func (sch *xScheme) EncapsulateDeterministically(
157+
pk kem.PublicKey, seed []byte,
158+
) (ct, ss []byte, err error) {
159+
if len(seed) != sch.EncapsulationSeedSize() {
160+
return nil, nil, kem.ErrSeedSize
161+
}
162+
pub, ok := pk.(*xPublicKey)
163+
if !ok || pub.scheme != sch {
164+
return nil, nil, kem.ErrTypeMismatch
165+
}
166+
167+
pk2, sk2 := sch.DeriveKeyPair(seed)
168+
ss = pub.X(sk2.(*xPrivateKey))
169+
ct, _ = pk2.MarshalBinary()
170+
return
171+
}
172+
173+
func (sch *xScheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) {
174+
if len(ct) != sch.CiphertextSize() {
175+
return nil, kem.ErrCiphertextSize
176+
}
177+
178+
priv, ok := sk.(*xPrivateKey)
179+
if !ok || priv.scheme != sch {
180+
return nil, kem.ErrTypeMismatch
181+
}
182+
183+
pk, err := sch.UnmarshalBinaryPublicKey(ct)
184+
if err != nil {
185+
return nil, err
186+
}
187+
188+
ss := pk.(*xPublicKey).X(priv)
189+
return ss, nil
190+
}
191+
192+
func (sch *xScheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) {
193+
if len(buf) != sch.PublicKeySize() {
194+
return nil, kem.ErrPubKeySize
195+
}
196+
ret := xPublicKey{sch, make([]byte, sch.size)}
197+
copy(ret.key, buf)
198+
return &ret, nil
199+
}
200+
201+
func (sch *xScheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) {
202+
if len(buf) != sch.PrivateKeySize() {
203+
return nil, kem.ErrPrivKeySize
204+
}
205+
ret := xPrivateKey{sch, make([]byte, sch.size)}
206+
copy(ret.key, buf)
207+
return &ret, nil
208+
}

0 commit comments

Comments
 (0)