diff --git a/sm9/README.md b/sm9/README.md index bc88904..51846e1 100644 --- a/sm9/README.md +++ b/sm9/README.md @@ -1,4 +1,40 @@ This part codes mainly refer two projects: 1. [bn256](https://github.com/cloudflare/bn256), 主要是基域运算 -2. [gmssl sm9](https://github.com/guanzhi/GmSSL/blob/develop/src/sm9_alg.c),主要是2-4-12塔式扩域,以及r-ate等 \ No newline at end of file +2. [gmssl sm9](https://github.com/guanzhi/GmSSL/blob/develop/src/sm9_alg.c),主要是2-4-12塔式扩域,以及r-ate等 + + +**SM9 Sign Benchmark** + + goos: windows + goarch: amd64 + pkg: github.com/emmansun/gmsm/sm9 + cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz + BenchmarkSign-6 1344 871597 ns/op 35870 B/op 1013 allocs/op + + +**SM9 Verify Benchmark** + + goos: windows + goarch: amd64 + pkg: github.com/emmansun/gmsm/sm9 + cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz + BenchmarkVerify-6 352 3331673 ns/op 237676 B/op 6283 allocs/op + +**SM9 Encrypt(XOR) Benchmark** + + goos: windows + goarch: amd64 + pkg: github.com/emmansun/gmsm/sm9 + cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz + BenchmarkEncrypt-6 1120 971188 ns/op 38125 B/op 1036 allocs/op + +**SM9 Decrypt(XOR) Benchmark** + + goos: windows + goarch: amd64 + pkg: github.com/emmansun/gmsm/sm9 + cpu: Intel(R) Core(TM) i5-9500 CPU @ 3.00GHz + BenchmarkDecrypt-6 507 2345492 ns/op 202360 B/op 5228 allocs/op + +To further improve `Verify()/Decrypt()` performance, need to improve `Pair()` method performance. diff --git a/sm9/gfp12.go b/sm9/gfp12.go index 3346960..0ef3b48 100644 --- a/sm9/gfp12.go +++ b/sm9/gfp12.go @@ -367,3 +367,11 @@ func (e *gfP12) Conjugate(a *gfP12) *gfP12 { e.x.Conjugate(&a.x) return e } + +// Select sets q to p1 if cond == 1, and to p2 if cond == 0. +func (q *gfP12) Select(p1, p2 *gfP12, cond int) *gfP12 { + q.x.Select(&p1.x, &p2.x, cond) + q.y.Select(&p1.y, &p2.y, cond) + q.z.Select(&p1.z, &p2.z, cond) + return q +} diff --git a/sm9/gfp4.go b/sm9/gfp4.go index 94a1b4b..5f4a99e 100644 --- a/sm9/gfp4.go +++ b/sm9/gfp4.go @@ -241,3 +241,10 @@ func (e *gfP4) FrobeniusP3(a *gfP4) *gfP4 { return e } + +// Select sets q to p1 if cond == 1, and to p2 if cond == 0. +func (q *gfP4) Select(p1, p2 *gfP4, cond int) *gfP4 { + q.x.Select(&p1.x, &p2.x, cond) + q.y.Select(&p1.y, &p2.y, cond) + return q +} diff --git a/sm9/gt.go b/sm9/gt.go index f51de78..eac5b48 100644 --- a/sm9/gt.go +++ b/sm9/gt.go @@ -1,6 +1,7 @@ package sm9 import ( + "crypto/subtle" "errors" "io" "math/big" @@ -85,6 +86,15 @@ func (e *GT) Set(a *GT) *GT { return e } +// Set sets e to one and then returns e. +func (e *GT) SetOne() *GT { + if e.p == nil { + e.p = &gfP12{} + } + e.p.SetOne() + return e +} + // Finalize is a linear function from F_p^12 to GT. func (e *GT) Finalize() *GT { ret := finalExponentiation(e.p) @@ -197,3 +207,21 @@ func (e *GT) Unmarshal(m []byte) ([]byte, error) { return m[12*numBytes:], nil } + +// A gtPointTable holds the first 15 Exp of a value at offset -1, so P +// is at table[0], P^15 is at table[14], and P^0 is implicitly the identity +// point. +type gtTable [15]*GT + +// Select selects the n-th multiple of the table base point into p. It works in +// constant time by iterating over every entry of the table. n must be in [0, 15]. +func (table *gtTable) Select(p *GT, n uint8) { + if n >= 16 { + panic("sm9: internal error: gtTable called with out-of-bounds value") + } + p.p.SetOne() + for i := uint8(1); i < 16; i++ { + cond := subtle.ConstantTimeByteEq(i, n) + p.p.Select(table[i-1].p, p.p, cond) + } +} diff --git a/sm9/sm9.go b/sm9/sm9.go index 2e91fd1..ace9023 100644 --- a/sm9/sm9.go +++ b/sm9/sm9.go @@ -91,17 +91,68 @@ func (pub *SignMasterPublicKey) Pair() *GT { return pub.basePoint } +func (pub *SignMasterPublicKey) generatorTable() *[32 * 2]gtTable { + pub.tableGenOnce.Do(func() { + pub.table = new([32 * 2]gtTable) + base := >{} + base.Set(pub.Pair()) + for i := 0; i < 32*2; i++ { + pub.table[i][0] = >{} + pub.table[i][0].Set(base) + for j := 1; j < 15; j += 2 { + pub.table[i][j] = >{} + pub.table[i][j].p = &gfP12{} + pub.table[i][j].p.Square(pub.table[i][j/2].p) + pub.table[i][j+1] = >{} + pub.table[i][j+1].p = &gfP12{} + pub.table[i][j+1].Add(pub.table[i][j], base) + } + base.p.Square(base.p) + base.p.Square(base.p) + base.p.Square(base.p) + base.p.Square(base.p) + } + }) + return pub.table +} + +func (pub *SignMasterPublicKey) ScalarBaseMult(r *big.Int) *GT { + scalar := normalizeScalar(r.Bytes()) + tables := pub.generatorTable() + // This is also a scalar multiplication with a four-bit window like in + // ScalarMult, but in this case the doublings are precomputed. The value + // [windowValue]G added at iteration k would normally get doubled + // (totIterations-k)×4 times, but with a larger precomputation we can + // instead add [2^((totIterations-k)×4)][windowValue]G and avoid the + // doublings between iterations. + e, t := >{}, >{} + tableIndex := len(tables) - 1 + e.SetOne() + t.SetOne() + for _, byte := range scalar { + windowValue := byte >> 4 + tables[tableIndex].Select(t, windowValue) + e.Add(e, t) + tableIndex-- + windowValue = byte & 0b1111 + tables[tableIndex].Select(t, windowValue) + e.Add(e, t) + tableIndex-- + } + return e +} + // Sign signs a hash (which should be the result of hashing a larger message) // using the user dsa key. It returns the signature as a pair of h and s. func Sign(rand io.Reader, priv *SignPrivateKey, hash []byte) (h *big.Int, s *G1, err error) { - g := priv.Pair() var r *big.Int for { r, err = randFieldElement(rand) if err != nil { return } - w := new(GT).ScalarMult(g, r) + + w := priv.SignMasterPublicKey.ScalarBaseMult(r) var buffer []byte buffer = append(buffer, hash...) @@ -157,9 +208,8 @@ func Verify(pub *SignMasterPublicKey, uid []byte, hid byte, hash []byte, h *big. if !s.p.IsOnCurve() { return false } - g := pub.Pair() - t := new(GT).ScalarMult(g, h) + t := pub.ScalarBaseMult(h) // user sign public key p generation p := pub.GenerateUserPublicKey(uid, hid) @@ -227,6 +277,57 @@ func (pub *EncryptMasterPublicKey) Pair() *GT { return pub.basePoint } +func (pub *EncryptMasterPublicKey) generatorTable() *[32 * 2]gtTable { + pub.tableGenOnce.Do(func() { + pub.table = new([32 * 2]gtTable) + base := >{} + base.Set(pub.Pair()) + for i := 0; i < 32*2; i++ { + pub.table[i][0] = >{} + pub.table[i][0].Set(base) + for j := 1; j < 15; j += 2 { + pub.table[i][j] = >{} + pub.table[i][j].p = &gfP12{} + pub.table[i][j].p.Square(pub.table[i][j/2].p) + pub.table[i][j+1] = >{} + pub.table[i][j+1].p = &gfP12{} + pub.table[i][j+1].Add(pub.table[i][j], base) + } + base.p.Square(base.p) + base.p.Square(base.p) + base.p.Square(base.p) + base.p.Square(base.p) + } + }) + return pub.table +} + +func (pub *EncryptMasterPublicKey) ScalarBaseMult(r *big.Int) *GT { + scalar := normalizeScalar(r.Bytes()) + tables := pub.generatorTable() + // This is also a scalar multiplication with a four-bit window like in + // ScalarMult, but in this case the doublings are precomputed. The value + // [windowValue]G added at iteration k would normally get doubled + // (totIterations-k)×4 times, but with a larger precomputation we can + // instead add [2^((totIterations-k)×4)][windowValue]G and avoid the + // doublings between iterations. + e, t := >{}, >{} + tableIndex := len(tables) - 1 + e.SetOne() + t.SetOne() + for _, byte := range scalar { + windowValue := byte >> 4 + tables[tableIndex].Select(t, windowValue) + e.Add(e, t) + tableIndex-- + windowValue = byte & 0b1111 + tables[tableIndex].Select(t, windowValue) + e.Add(e, t) + tableIndex-- + } + return e +} + // WrappKey generate and wrapp key wtih reciever's uid and system hid func WrappKey(rand io.Reader, pub *EncryptMasterPublicKey, uid []byte, hid byte, kLen int) (key []byte, cipher *G1, err error) { q := pub.GenerateUserPublicKey(uid, hid) @@ -240,8 +341,7 @@ func WrappKey(rand io.Reader, pub *EncryptMasterPublicKey, uid []byte, hid byte, cipher = new(G1).ScalarMult(q, r) - g := pub.Pair() - w := new(GT).ScalarMult(g, r) + w := pub.ScalarBaseMult(r) var buffer []byte buffer = append(buffer, cipher.Marshal()...) diff --git a/sm9/sm9_key.go b/sm9/sm9_key.go index b3761d5..946ddf7 100644 --- a/sm9/sm9_key.go +++ b/sm9/sm9_key.go @@ -18,6 +18,8 @@ type SignMasterPublicKey struct { MasterPublicKey *G2 pairOnce sync.Once basePoint *GT + tableGenOnce sync.Once + table *[32 * 2]gtTable } type SignPrivateKey struct { @@ -34,6 +36,8 @@ type EncryptMasterPublicKey struct { MasterPublicKey *G1 pairOnce sync.Once basePoint *GT + tableGenOnce sync.Once + table *[32 * 2]gtTable } type EncryptPrivateKey struct {