Skip to content

Commit 5e88046

Browse files
committed
mayo: make key expansion part of keygen
1 parent 29d480e commit 5e88046

File tree

13 files changed

+544
-482
lines changed

13 files changed

+544
-482
lines changed

sign/mayo/mode1/internal/mayo.go

+63-64
Original file line numberDiff line numberDiff line change
@@ -12,60 +12,70 @@ import (
1212
"github.com/cloudflare/circl/internal/sha3"
1313
)
1414

15-
type (
16-
PrivateKey [PrivateKeySize]byte
17-
PublicKey [PublicKeySize]byte
18-
)
15+
type PublicKey struct {
16+
seed [PublicKeySeedSize]byte
17+
p3 [P3Size / 8]uint64
1918

20-
func (pk *PublicKey) Equal(other *PublicKey) bool {
21-
return *pk == *other
19+
// P1 and P2 are expanded from seed
20+
p1 [P1Size / 8]uint64
21+
p2 [P2Size / 8]uint64
2222
}
2323

24-
func (sk *PrivateKey) Equal(other *PrivateKey) bool {
25-
return subtle.ConstantTimeCompare((*sk)[:], (*other)[:]) == 1
24+
type PrivateKey struct {
25+
seed [KeySeedSize]byte
26+
27+
p1 [P1Size / 8]uint64
28+
o [V * O]byte
29+
l [M * V * O / 16]uint64
30+
}
31+
32+
func (pk *PublicKey) Equal(other *PublicKey) bool {
33+
return pk.seed == other.seed && pk.p3 == other.p3
2634
}
2735

28-
type ExpandedPublicKey struct {
29-
p1 [P1Size]byte
30-
p2 [P2Size]byte
31-
p3 [P3Size]byte
36+
func (sk *PrivateKey) Equal(other *PrivateKey) bool {
37+
return subtle.ConstantTimeCompare(sk.seed[:], other.seed[:]) == 1
3238
}
3339

34-
type ExpandedPrivateKey struct {
35-
seed [KeySeedSize]byte
36-
o [V * O]byte
37-
p1 [M * V * V / 16]uint64
38-
l [M * V * O / 16]uint64
40+
// Packs the public key into buf.
41+
func (pk *PublicKey) Pack(buf *[PublicKeySize]byte) {
42+
copy(buf[:PublicKeySeedSize], pk.seed[:])
43+
copyUint64SliceToBytesLE(buf[PublicKeySeedSize:], pk.p3[:])
3944
}
4045

41-
func (pk *PublicKey) Expand() *ExpandedPublicKey {
42-
seedPk := pk[:PublicKeySeedSize]
46+
// Sets pk to the public key encoded in buf.
47+
func (pk *PublicKey) Unpack(buf *[PublicKeySize]byte) {
48+
copy(pk.seed[:], buf[:PublicKeySeedSize])
4349

4450
var nonce [16]byte
4551
// TODO there are unnecessary allocations
46-
block, _ := aes.NewCipher(seedPk[:])
52+
block, _ := aes.NewCipher(pk.seed[:])
4753
ctr := cipher.NewCTR(block, nonce[:])
4854

49-
epk := ExpandedPublicKey{}
50-
ctr.XORKeyStream(epk.p1[:], epk.p1[:])
51-
ctr.XORKeyStream(epk.p2[:], epk.p2[:])
52-
53-
copy(epk.p3[:], pk[PublicKeySeedSize:])
55+
var p1 [P1Size]byte
56+
var p2 [P2Size]byte
57+
ctr.XORKeyStream(p1[:], p1[:])
58+
ctr.XORKeyStream(p2[:], p2[:])
5459

55-
return &epk
60+
copyBytesToUint64SliceLE(pk.p1[:], p1[:])
61+
copyBytesToUint64SliceLE(pk.p2[:], p2[:])
62+
copyBytesToUint64SliceLE(pk.p3[:], buf[PublicKeySeedSize:])
5663
}
5764

58-
func (sk *PrivateKey) Expand() *ExpandedPrivateKey {
59-
var esk ExpandedPrivateKey
65+
// Packs the private key into buf.
66+
func (sk *PrivateKey) Pack(buf *[PrivateKeySize]byte) {
67+
copy(buf[:], sk.seed[:])
68+
}
6069

61-
seed := (*sk)[:KeySeedSize]
62-
copy(esk.seed[:], seed)
70+
// Sets sk to the private key encoded in buf.
71+
func (sk *PrivateKey) Unpack(buf *[PrivateKeySize]byte) {
72+
copy(sk.seed[:], buf[:])
6373

6474
var seedPk [PublicKeySeedSize]byte
6575
var o [OSize]byte
6676

6777
h := sha3.NewShake256()
68-
_, _ = h.Write(seed[:])
78+
_, _ = h.Write(sk.seed[:])
6979
_, _ = h.Read(seedPk[:])
7080
_, _ = h.Read(o[:])
7181

@@ -77,15 +87,13 @@ func (sk *PrivateKey) Expand() *ExpandedPrivateKey {
7787
var p12 [P1Size + P2Size]byte
7888
ctr.XORKeyStream(p12[:], p12[:])
7989

80-
decode(esk.o[:], o[:])
90+
decode(sk.o[:], o[:])
8191

82-
copyBytesToUint64SliceLE(esk.p1[:P1Size/8], p12[:P1Size])
83-
copyBytesToUint64SliceLE(esk.l[:], p12[P1Size:])
92+
copyBytesToUint64SliceLE(sk.p1[:P1Size/8], p12[:P1Size])
93+
copyBytesToUint64SliceLE(sk.l[:], p12[P1Size:])
8494

8595
// compute L_i = (P1 + P1^t)*O + P2
86-
mulAddMUpperTriangularWithTransposeMatXMat(esk.l[:], esk.p1[:], esk.o[:], V, O)
87-
88-
return &esk
96+
mulAddMUpperTriangularWithTransposeMatXMat(sk.l[:], sk.p1[:], sk.o[:], V, O)
8997
}
9098

9199
// decode unpacks N bytes from src to N*2 nibbles of dst.
@@ -103,7 +111,7 @@ func decode(dst []byte, src []byte) {
103111
}
104112
}
105113

106-
// encode packs N=length low nibbles from src to (N+1)/2 bytes in dst.
114+
// encode packs N=length low nibbles from src to ceil(N/2) bytes in dst.
107115
func encode(dst []byte, src []byte, length int) {
108116
var i int
109117
for i = 0; i+1 < length; i += 2 {
@@ -147,51 +155,49 @@ func GenerateKey(rand io.Reader) (*PublicKey, *PrivateKey, error) {
147155

148156
func NewKeyFromSeed(seed [KeySeedSize]byte) (*PublicKey, *PrivateKey) {
149157
var sk PrivateKey
150-
copy(sk[:], seed[:])
158+
sk.Unpack(&seed)
151159

152160
return sk.Public(), &sk
153161
}
154162

155163
func (sk *PrivateKey) Public() *PublicKey {
156164
var pk PublicKey
157-
seedPk := pk[:PublicKeySeedSize]
158165
var o [OSize]byte
159166

160167
h := sha3.NewShake256()
161-
_, _ = h.Write(sk[:])
162-
_, _ = h.Read(seedPk[:])
168+
_, _ = h.Write(sk.seed[:])
169+
_, _ = h.Read(pk.seed[:])
163170
_, _ = h.Read(o[:])
164171

165172
var nonce [16]byte
166173
// TODO there are unnecessary allocations
167-
block, _ := aes.NewCipher(seedPk[:])
174+
block, _ := aes.NewCipher(pk.seed[:])
168175
ctr := cipher.NewCTR(block, nonce[:])
169176

170-
var p12 [P1Size + P2Size]byte
171-
ctr.XORKeyStream(p12[:], p12[:])
177+
var p1 [P1Size]byte
178+
var p2 [P2Size]byte
179+
ctr.XORKeyStream(p1[:], p1[:])
180+
ctr.XORKeyStream(p2[:], p2[:])
181+
182+
copyBytesToUint64SliceLE(pk.p1[:], p1[:])
183+
copyBytesToUint64SliceLE(pk.p2[:], p2[:])
172184

173185
var oo [V * O]byte
174186
decode(oo[:], o[:])
175187

176-
var p1Tri [P1Size / 8]uint64
177188
var p1OP2 [P2Size / 8]uint64
178-
copyBytesToUint64SliceLE(p1Tri[:], p12[:P1Size])
179-
copyBytesToUint64SliceLE(p1OP2[:], p12[P1Size:])
189+
copy(p1OP2[:], pk.p2[:])
180190

181191
var p3full [M * O * O / 16]uint64
182-
var p3 [P3Size / 8]uint64
183-
184-
mulAddMUpperTriangularMatXMat(p1OP2[:], p1Tri[:], oo[:], V, O)
192+
mulAddMUpperTriangularMatXMat(p1OP2[:], pk.p1[:], oo[:], V, O)
185193
mulAddMatTransXMMat(p3full[:], oo[:], p1OP2[:], V, O, O)
186194

187-
upper(p3full[:], p3[:], O)
188-
189-
copyUint64SliceToBytesLE(pk[PublicKeySeedSize:], p3[:])
195+
upper(p3full[:], pk.p3[:], O)
190196

191197
return &pk
192198
}
193199

194-
func Sign(msg []byte, sk *ExpandedPrivateKey, rand io.Reader) ([]byte, error) {
200+
func Sign(msg []byte, sk *PrivateKey, rand io.Reader) ([]byte, error) {
195201
if rand == nil {
196202
rand = cryptoRand.Reader
197203
}
@@ -622,7 +628,7 @@ func transpose16x16Nibbles(m []uint64) {
622628
}
623629
}
624630

625-
func Verify(epk *ExpandedPublicKey, msg []byte, sig []byte) bool {
631+
func Verify(pk *PublicKey, msg []byte, sig []byte) bool {
626632
if len(sig) != SignatureSize {
627633
return false
628634
}
@@ -649,13 +655,6 @@ func Verify(epk *ExpandedPublicKey, msg []byte, sig []byte) bool {
649655
var s [K * N]byte
650656
decode(s[:], senc[:])
651657

652-
var P1 [P1Size / 8]uint64
653-
var P2 [P2Size / 8]uint64
654-
var P3 [P3Size / 8]uint64
655-
copyBytesToUint64SliceLE(P1[:], epk.p1[:])
656-
copyBytesToUint64SliceLE(P2[:], epk.p2[:])
657-
copyBytesToUint64SliceLE(P3[:], epk.p3[:])
658-
659658
// Note: the variable time approach is overall about 30% faster
660659
// compute P * S^t = [ P1 P2 ] * [S1] = [P1*S1 + P2*S2]
661660
// [ 0 P3 ] [S2] [ P3*S2]
@@ -665,7 +664,7 @@ func Verify(epk *ExpandedPublicKey, msg []byte, sig []byte) bool {
665664
// mulAddMMatXMatTrans(pst[:], P2, s[V:], V, O, K, N, false)
666665
// mulAddMMatXMatTrans(pst[M*V*K/16:], P3, s[V:], O, O, K, N, true)
667666
// Variable time approach with table access where index depends on input:
668-
calculatePStVarTime(pst[:], P1[:], P2[:], P3[:], s[:])
667+
calculatePStVarTime(pst[:], pk.p1[:], pk.p2[:], pk.p3[:], s[:])
669668

670669
// compute S * PST
671670
var sps [M * K * K / 16]uint64

sign/mayo/mode1/internal/mayo_test.go

+18-39
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ func TestNewKey(t *testing.T) {
3030

3131
pk, sk := NewKeyFromSeed(seed)
3232

33-
pk2 := [PublicKeySize]byte(*pk)
34-
sk2 := [PrivateKeySize]byte(*sk)
33+
var pk2 [PublicKeySize]byte
34+
var sk2 [PrivateKeySize]byte
35+
pk.Pack(&pk2)
36+
sk.Pack(&sk2)
3537

3638
if hex.EncodeToString(pk2[:]) != tc.expectedPk {
3739
t.Fatal()
@@ -67,14 +69,13 @@ func TestVerify(t *testing.T) {
6769
s, _ := hex.DecodeString(tc.signature)
6870

6971
pk, _ := NewKeyFromSeed(seed)
70-
epk := pk.Expand()
7172

72-
if !Verify(epk, m, s) {
73+
if !Verify(pk, m, s) {
7374
t.Fatal("should verify")
7475
}
7576

76-
epk.p1[0] ^= 1
77-
if Verify(epk, m, s) {
77+
pk.p1[0] ^= 1
78+
if Verify(pk, m, s) {
7879
t.Fatal("should not verify")
7980
}
8081
})
@@ -97,12 +98,12 @@ func TestSampleSolution(t *testing.T) {
9798
t.Fatal()
9899
}
99100

100-
sig, err := Sign(msg[:], sk.Expand(), &g)
101+
sig, err := Sign(msg[:], sk, &g)
101102
if err != nil {
102103
t.Fatal()
103104
}
104105

105-
if !Verify(pk.Expand(), msg[:], sig[:]) {
106+
if !Verify(pk, msg[:], sig[:]) {
106107
t.Fatal()
107108
}
108109
}
@@ -145,21 +146,23 @@ func TestPQCgenKATSign(t *testing.T) {
145146
_, _ = g2.Read(eseed[:])
146147
pk, sk := NewKeyFromSeed(eseed)
147148

148-
pk2 := [PublicKeySize]byte(*pk)
149-
sk2 := [PrivateKeySize]byte(*sk)
149+
var pk2 [PublicKeySize]byte
150+
var sk2 [PrivateKeySize]byte
151+
pk.Pack(&pk2)
152+
sk.Pack(&sk2)
150153

151154
fmt.Fprintf(f, "pk = %X\n", pk2)
152155
fmt.Fprintf(f, "sk = %X\n", sk2)
153156
fmt.Fprintf(f, "smlen = %d\n", mlen+SignatureSize)
154157

155-
sig, err := Sign(msg[:], sk.Expand(), &g2)
158+
sig, err := Sign(msg[:], sk, &g2)
156159
if err != nil {
157160
t.Fatal()
158161
}
159162

160163
fmt.Fprintf(f, "sm = %X%X\n\n", sig, msg)
161164

162-
if !Verify(pk.Expand(), msg[:], sig) {
165+
if !Verify(pk, msg[:], sig) {
163166
t.Fatal()
164167
}
165168
}
@@ -196,22 +199,10 @@ func BenchmarkVerify(b *testing.B) {
196199
var seed [KeySeedSize]byte
197200
var msg [8]byte
198201
pk, sk := NewKeyFromSeed(seed)
199-
sig, _ := Sign(msg[:], sk.Expand(), nil)
202+
sig, _ := Sign(msg[:], sk, nil)
200203
b.ResetTimer()
201204
for i := 0; i < b.N; i++ {
202-
_ = Verify(pk.Expand(), msg[:], sig[:])
203-
}
204-
}
205-
206-
func BenchmarkVerifyExpandedKey(b *testing.B) {
207-
var seed [KeySeedSize]byte
208-
var msg [8]byte
209-
pk, sk := NewKeyFromSeed(seed)
210-
sig, _ := Sign(msg[:], sk.Expand(), nil)
211-
epk := pk.Expand()
212-
b.ResetTimer()
213-
for i := 0; i < b.N; i++ {
214-
_ = Verify(epk, msg[:], sig[:])
205+
_ = Verify(pk, msg[:], sig[:])
215206
}
216207
}
217208

@@ -231,18 +222,6 @@ func BenchmarkSign(b *testing.B) {
231222
b.ResetTimer()
232223
for i := 0; i < b.N; i++ {
233224
binary.LittleEndian.PutUint64(msg[:], uint64(i))
234-
_, _ = Sign(msg[:], sk.Expand(), zeroReader{})
235-
}
236-
}
237-
238-
func BenchmarkSignExpandedKey(b *testing.B) {
239-
var seed [KeySeedSize]byte
240-
var msg [8]byte
241-
_, sk := NewKeyFromSeed(seed)
242-
esk := sk.Expand()
243-
b.ResetTimer()
244-
for i := 0; i < b.N; i++ {
245-
binary.LittleEndian.PutUint64(msg[:], uint64(i))
246-
_, _ = Sign(msg[:], esk, zeroReader{})
225+
_, _ = Sign(msg[:], sk, zeroReader{})
247226
}
248227
}

0 commit comments

Comments
 (0)