go-iden3-crypto/babyjub/eddsa.go
babyjub/eddsa.goRaw
1package babyjub
2
3import (
4 "crypto/rand"
5
6 "github.com/iden3/go-iden3-crypto/mimc7"
7 "github.com/iden3/go-iden3-crypto/poseidon"
8 "github.com/iden3/go-iden3-crypto/utils"
9
10 "math/big"
11)
12
13// pruneBuffer prunes the buffer during key generation according to RFC 8032.
14// https://tools.ietf.org/html/rfc8032#page-13
15func pruneBuffer(buf *[32]byte) *[32]byte {
16 buf[0] = buf[0] & 0xF8
17 buf[31] = buf[31] & 0x7F
18 buf[31] = buf[31] | 0x40
19 return buf
20}
21
22// PrivateKey is an EdDSA private key, which is a 32byte buffer.
23type PrivateKey [32]byte
24
25// NewRandPrivKey generates a new random private key (using cryptographically
26// secure randomness).
27func NewRandPrivKey() PrivateKey {
28 var k PrivateKey
29 _, err := rand.Read(k[:])
30 if err != nil {
31 panic(err)
32 }
33 return k
34}
35
36// Scalar converts a private key into the scalar value s following the EdDSA
37// standard, and using blake-512 hash.
38func (k *PrivateKey) Scalar() *PrivKeyScalar {
39 sBuf := Blake512(k[:])
40 sBuf32 := [32]byte{}
41 copy(sBuf32[:], sBuf[:32])
42 pruneBuffer(&sBuf32)
43 s := new(big.Int)
44 utils.SetBigIntFromLEBytes(s, sBuf32[:])
45 s.Rsh(s, 3)
46 return NewPrivKeyScalar(s)
47}
48
49// Pub returns the public key corresponding to a private key.
50func (k *PrivateKey) Public() *PublicKey {
51 return k.Scalar().Public()
52}
53
54// PrivKeyScalar represents the scalar s output of a private key
55type PrivKeyScalar big.Int
56
57// NewPrivKeyScalar creates a new PrivKeyScalar from a big.Int
58func NewPrivKeyScalar(s *big.Int) *PrivKeyScalar {
59 sk := PrivKeyScalar(*s)
60 return &sk
61}
62
63// Pub returns the public key corresponding to the scalar value s of a private
64// key.
65func (s *PrivKeyScalar) Public() *PublicKey {
66 p := NewPoint().Mul((*big.Int)(s), B8)
67 pk := PublicKey(*p)
68 return &pk
69}
70
71// BigInt returns the big.Int corresponding to a PrivKeyScalar.
72func (s *PrivKeyScalar) BigInt() *big.Int {
73 return (*big.Int)(s)
74}
75
76// PublicKey represents an EdDSA public key, which is a curve point.
77type PublicKey Point
78
79func (pk PublicKey) MarshalText() ([]byte, error) {
80 pkc := pk.Compress()
81 return utils.Hex(pkc[:]).MarshalText()
82}
83
84func (pk PublicKey) String() string {
85 pkc := pk.Compress()
86 return utils.Hex(pkc[:]).String()
87}
88
89func (pk *PublicKey) UnmarshalText(h []byte) error {
90 var pkc PublicKeyComp
91 if err := utils.HexDecodeInto(pkc[:], h); err != nil {
92 return err
93 }
94 pkd, err := pkc.Decompress()
95 if err != nil {
96 return err
97 }
98 *pk = *pkd
99 return nil
100}
101
102// Point returns the Point corresponding to a PublicKey.
103func (p *PublicKey) Point() *Point {
104 return (*Point)(p)
105}
106
107// PublicKeyComp represents a compressed EdDSA Public key; it's a compressed curve
108// point.
109type PublicKeyComp [32]byte
110
111func (buf PublicKeyComp) MarshalText() ([]byte, error) { return utils.Hex(buf[:]).MarshalText() }
112func (buf PublicKeyComp) String() string { return utils.Hex(buf[:]).String() }
113func (buf *PublicKeyComp) UnmarshalText(h []byte) error { return utils.HexDecodeInto(buf[:], h) }
114
115func (p *PublicKey) Compress() PublicKeyComp {
116 return PublicKeyComp((*Point)(p).Compress())
117}
118
119func (p *PublicKeyComp) Decompress() (*PublicKey, error) {
120 point, err := NewPoint().Decompress(*p)
121 if err != nil {
122 return nil, err
123 }
124 pk := PublicKey(*point)
125 return &pk, nil
126}
127
128// Signature represents an EdDSA uncompressed signature.
129type Signature struct {
130 R8 *Point
131 S *big.Int
132}
133
134// SignatureComp represents a compressed EdDSA signature.
135type SignatureComp [64]byte
136
137func (buf SignatureComp) MarshalText() ([]byte, error) { return utils.Hex(buf[:]).MarshalText() }
138func (buf SignatureComp) String() string { return utils.Hex(buf[:]).String() }
139func (buf *SignatureComp) UnmarshalText(h []byte) error { return utils.HexDecodeInto(buf[:], h) }
140
141// Compress an EdDSA signature by concatenating the compression of
142// the point R8 and the Little-Endian encoding of S.
143func (s *Signature) Compress() SignatureComp {
144 R8p := s.R8.Compress()
145 Sp := utils.BigIntLEBytes(s.S)
146 buf := [64]byte{}
147 copy(buf[:32], R8p[:])
148 copy(buf[32:], Sp[:])
149 return SignatureComp(buf)
150}
151
152// Decompress a compressed signature into s, and also returns the decompressed
153// signature. Returns error if the Point decompression fails.
154func (s *Signature) Decompress(buf [64]byte) (*Signature, error) {
155 R8p := [32]byte{}
156 copy(R8p[:], buf[:32])
157 var err error
158 if s.R8, err = NewPoint().Decompress(R8p); err != nil {
159 return nil, err
160 }
161 s.S = utils.SetBigIntFromLEBytes(new(big.Int), buf[32:])
162 return s, nil
163}
164
165// Decompress a compressed signature. Returns error if the Point decompression
166// fails.
167func (s *SignatureComp) Decompress() (*Signature, error) {
168 return new(Signature).Decompress(*s)
169}
170
171// SignMimc7 signs a message encoded as a big.Int in Zq using blake-512 hash
172// for buffer hashing and mimc7 for big.Int hashing.
173func (k *PrivateKey) SignMimc7(msg *big.Int) *Signature {
174 h1 := Blake512(k[:])
175 msgBuf := utils.BigIntLEBytes(msg)
176 msgBuf32 := [32]byte{}
177 copy(msgBuf32[:], msgBuf[:])
178 rBuf := Blake512(append(h1[32:], msgBuf32[:]...))
179 r := utils.SetBigIntFromLEBytes(new(big.Int), rBuf) // r = H(H_{32..63}(k), msg)
180 r.Mod(r, SubOrder)
181 R8 := NewPoint().Mul(r, B8) // R8 = r * 8 * B
182 A := k.Public().Point()
183 hmInput, err := mimc7.BigIntsToRElems([]*big.Int{R8.X, R8.Y, A.X, A.Y, msg})
184 if err != nil {
185 panic(err)
186 }
187 hm := mimc7.Hash(hmInput, nil) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
188 S := new(big.Int).Lsh(k.Scalar().BigInt(), 3)
189 S = S.Mul(hm, S)
190 S.Add(r, S)
191 S.Mod(S, SubOrder) // S = r + hm * 8 * s
192
193 return &Signature{R8: R8, S: S}
194}
195
196// VerifyMimc7 verifies the signature of a message encoded as a big.Int in Zq
197// using blake-512 hash for buffer hashing and mimc7 for big.Int hashing.
198func (p *PublicKey) VerifyMimc7(msg *big.Int, sig *Signature) bool {
199 hmInput, err := mimc7.BigIntsToRElems([]*big.Int{sig.R8.X, sig.R8.Y, p.X, p.Y, msg})
200 if err != nil {
201 panic(err)
202 }
203 hm := mimc7.Hash(hmInput, nil) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
204
205 left := NewPoint().Mul(sig.S, B8) // left = s * 8 * B
206 r1 := big.NewInt(8)
207 r1.Mul(r1, hm)
208 right := NewPoint().Mul(r1, p.Point())
209 right.Add(sig.R8, right) // right = 8 * R + 8 * hm * A
210 return (left.X.Cmp(right.X) == 0) && (left.Y.Cmp(right.Y) == 0)
211}
212
213// SignPoseidon signs a message encoded as a big.Int in Zq using blake-512 hash
214// for buffer hashing and Poseidon for big.Int hashing.
215func (k *PrivateKey) SignPoseidon(msg *big.Int) *Signature {
216 h1 := Blake512(k[:])
217 msgBuf := utils.BigIntLEBytes(msg)
218 msgBuf32 := [32]byte{}
219 copy(msgBuf32[:], msgBuf[:])
220 rBuf := Blake512(append(h1[32:], msgBuf32[:]...))
221 r := utils.SetBigIntFromLEBytes(new(big.Int), rBuf) // r = H(H_{32..63}(k), msg)
222 r.Mod(r, SubOrder)
223 R8 := NewPoint().Mul(r, B8) // R8 = r * 8 * B
224 A := k.Public().Point()
225 hmInput := []*big.Int{R8.X, R8.Y, A.X, A.Y, msg}
226 hm, err := poseidon.Hash(hmInput) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
227 if err != nil {
228 panic(err)
229 }
230 S := new(big.Int).Lsh(k.Scalar().BigInt(), 3)
231 S = S.Mul(hm, S)
232 S.Add(r, S)
233 S.Mod(S, SubOrder) // S = r + hm * 8 * s
234
235 return &Signature{R8: R8, S: S}
236}
237
238// VerifyPoseidon verifies the signature of a message encoded as a big.Int in Zq
239// using blake-512 hash for buffer hashing and Poseidon for big.Int hashing.
240func (p *PublicKey) VerifyPoseidon(msg *big.Int, sig *Signature) bool {
241 hmInput := []*big.Int{sig.R8.X, sig.R8.Y, p.X, p.Y, msg}
242 hm, err := poseidon.Hash(hmInput) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
243 if err != nil {
244 panic(err)
245 }
246
247 left := NewPoint().Mul(sig.S, B8) // left = s * 8 * B
248 r1 := big.NewInt(8)
249 r1.Mul(r1, hm)
250 right := NewPoint().Mul(r1, p.Point())
251 right.Add(sig.R8, right) // right = 8 * R + 8 * hm * A
252 return (left.X.Cmp(right.X) == 0) && (left.Y.Cmp(right.Y) == 0)
253}