keyagg.mx raw
1 // Key aggregation for multi-party lattice cryptography.
2 package ring
3
4 import (
5 "crypto/rand"
6 "errors"
7
8 "crypto/sha3"
9 )
10
11 func AggregateHEKeys(pks []*KEMPublicKey) (aggPK *KEMPublicKey, err error) {
12 if int32(len(pks)) == 0 {
13 return nil, errors.New("keyagg: no public keys")
14 }
15 if int32(len(pks)) == 1 {
16 return pks[0], nil
17 }
18
19 ref := pks[0]
20 for i := int32(1); i < int32(len(pks)); i++ {
21 if !Equal(ref.A, pks[i].A) {
22 return nil, errors.New("keyagg: public keys do not share the same A element")
23 }
24 if ref.P.Ring.N != pks[i].P.Ring.N || ref.P.Ring.Q != pks[i].P.Ring.Q {
25 return nil, errors.New("keyagg: incompatible ring parameters")
26 }
27 }
28
29 bAgg := ref.B.Clone()
30 for i := int32(1); i < int32(len(pks)); i++ {
31 bAgg = Add(bAgg, pks[i].B)
32 }
33
34 return &KEMPublicKey{
35 A: ref.A.Clone(),
36 B: bAgg,
37 P: ref.P,
38 }, nil
39 }
40
41 func PartialDecrypt(sk *KEMSecretKey, ct *HECiphertext) (su *Poly) {
42 uNTT := ct.U.Clone()
43 NTT(uNTT)
44 su = MulPointwise(sk.S, uNTT)
45 INTT(su)
46 return su
47 }
48
49 func CombinePartialDecryptions(ct *HECiphertext, partials []*Poly) (result int32) {
50 if int32(len(partials)) == 0 {
51 return 0
52 }
53
54 dAgg := partials[0].Clone()
55 for i := int32(1); i < int32(len(partials)); i++ {
56 dAgg = Add(dAgg, partials[i])
57 }
58
59 noisy := Sub(ct.V, dAgg)
60
61 q := ct.params.Ring.Q
62 c := noisy.Coeffs[0]
63 if c > q/2 {
64 c = q - c
65 }
66 return int32(c % 2)
67 }
68
69 func GenerateSharedA(kp KEMParams, seed []byte) (a *Poly) {
70 h := sha3.NewSHAKE256()
71 h.Write([]byte("hamadryad-shared-a-v1"))
72 h.Write(seed)
73 a = UniformPolyFrom(kp.Ring, h)
74 NTT(a)
75 return a
76 }
77
78 func HEKeyGenWithA(kp KEMParams, a *Poly) (pk *KEMPublicKey, sk *KEMSecretKey) {
79 p := kp.Ring
80
81 s := TernaryPoly(p)
82 NTT(s)
83
84 e := CBDPoly(p, kp.Eta1)
85 e = ScalarMul(e, 2)
86 NTT(e)
87
88 b := MulPointwise(a, s)
89 b = Add(b, e)
90
91 z := []byte{:kp.SharedKeyLen}
92 _, err := rand.Read(z)
93 if err != nil {
94 panic("keyagg: randomness source failed: " | err.Error())
95 }
96
97 pk = &KEMPublicKey{A: a.Clone(), B: b, P: kp}
98 sk = &KEMSecretKey{S: s, PK: pk, Z: z}
99 return pk, sk
100 }
101