mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-27 12:46:18 +08:00
sync code from sm2fiat and branch 1.16
This commit is contained in:
parent
c88bad8c7d
commit
93dca77af8
47
.github/ci_20221015.yml
vendored
Normal file
47
.github/ci_20221015.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '20221015' ]
|
||||
pull_request:
|
||||
branches: [ '20221015' ]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goVer: ['1.16', '1.17', '1.18']
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ matrix.goVer }}
|
||||
|
||||
- name: Setup Environment
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Module cache
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: go-mod-cache
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }}
|
||||
|
||||
- name: Test with Coverage
|
||||
run: go test -coverpkg=./... -v -short -coverprofile=coverage1.txt -covermode=atomic ./...
|
||||
|
||||
- name: Test Generic
|
||||
run: go test -coverpkg=./... -v -short -tags generic -coverprofile=coverage2.txt -covermode=atomic ./...
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage1.txt,./coverage2.txt
|
@ -6,7 +6,7 @@ jobs:
|
||||
virt: vm
|
||||
os: linux
|
||||
dist: focal
|
||||
go: 1.15.x
|
||||
go: 1.16.x
|
||||
group: edge
|
||||
|
||||
install:
|
||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/emmansun/gmsm
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
109
internal/sm2ec/p256_asm_ordinv.go
Normal file
109
internal/sm2ec/p256_asm_ordinv.go
Normal file
@ -0,0 +1,109 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import "errors"
|
||||
|
||||
// Montgomery multiplication modulo org(G). Sets res = in1 * in2 * R⁻¹.
|
||||
//
|
||||
//go:noescape
|
||||
func p256OrdMul(res, in1, in2 *p256OrdElement)
|
||||
|
||||
// Montgomery square modulo org(G), repeated n times (n >= 1).
|
||||
//
|
||||
//go:noescape
|
||||
func p256OrdSqr(res, in *p256OrdElement, n int)
|
||||
|
||||
// P256OrdInverse, sets out to in⁻¹ mod org(G). If in is zero, out will be zero.
|
||||
// n-2 =
|
||||
// 1111111111111111111111111111111011111111111111111111111111111111
|
||||
// 1111111111111111111111111111111111111111111111111111111111111111
|
||||
// 0111001000000011110111110110101100100001110001100000010100101011
|
||||
// 0101001110111011111101000000100100111001110101010100000100100001
|
||||
//
|
||||
func P256OrdInverse(k []byte) ([]byte, error) {
|
||||
if len(k) != 32 {
|
||||
return nil, errors.New("invalid scalar length")
|
||||
}
|
||||
x := new(p256OrdElement)
|
||||
p256OrdBigToLittle(x, toElementArray(k))
|
||||
|
||||
// Inversion is implemented as exponentiation by n - 2, per Fermat's little theorem.
|
||||
//
|
||||
// The sequence of 43 multiplications and 254 squarings is derived from
|
||||
// https://briansmith.org/ecc-inversion-addition-chains-01#p256_scalar_inversion
|
||||
_1 := new(p256OrdElement)
|
||||
_11 := new(p256OrdElement)
|
||||
_101 := new(p256OrdElement)
|
||||
_111 := new(p256OrdElement)
|
||||
_1111 := new(p256OrdElement)
|
||||
_10101 := new(p256OrdElement)
|
||||
_101111 := new(p256OrdElement)
|
||||
t := new(p256OrdElement)
|
||||
m := new(p256OrdElement)
|
||||
|
||||
// This code operates in the Montgomery domain where R = 2²⁵⁶ mod n and n is
|
||||
// the order of the scalar field. Elements in the Montgomery domain take the
|
||||
// form a×R and p256OrdMul calculates (a × b × R⁻¹) mod n. RR is R in the
|
||||
// domain, or R×R mod n, thus p256OrdMul(x, RR) gives x×R, i.e. converts x
|
||||
// into the Montgomery domain.
|
||||
RR := &p256OrdElement{0x901192af7c114f20, 0x3464504ade6fa2fa, 0x620fc84c3affe0d4, 0x1eb5e412a22b3d3b}
|
||||
|
||||
p256OrdMul(_1, x, RR) // _1 , 2^0
|
||||
p256OrdSqr(m, _1, 1) // _10, 2^1
|
||||
p256OrdMul(_11, m, _1) // _11, 2^1 + 2^0
|
||||
p256OrdMul(_101, m, _11) // _101, 2^2 + 2^0
|
||||
p256OrdMul(_111, m, _101) // _111, 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(x, _101, 1) // _1010, 2^3 + 2^1
|
||||
p256OrdMul(_1111, _101, x) // _1111, 2^3 + 2^2 + 2^1 + 2^0
|
||||
|
||||
p256OrdSqr(t, x, 1) // _10100, 2^4 + 2^2
|
||||
p256OrdMul(_10101, t, _1) // _10101, 2^4 + 2^2 + 2^0
|
||||
p256OrdSqr(x, _10101, 1) // _101010, 2^5 + 2^3 + 2^1
|
||||
p256OrdMul(_101111, _101, x) // _101111, 2^5 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdMul(x, _10101, x) // _111111 = x6, 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(t, x, 2) // _11111100, 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2
|
||||
|
||||
p256OrdMul(m, t, m) // _11111110 = x8, , 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1
|
||||
p256OrdMul(t, t, _11) // _11111111 = x8, , 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(x, t, 8) // _ff00, 2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8
|
||||
p256OrdMul(m, x, m) // _fffe
|
||||
p256OrdMul(x, x, t) // _ffff = x16, 2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8 + 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
|
||||
p256OrdSqr(t, x, 16) // _ffff0000, 2^31 + 2^30 + 2^29 + 2^28 + 2^27 + 2^26 + 2^25 + 2^24 + 2^23 + 2^22 + 2^21 + 2^20 + 2^19 + 2^18 + 2^17 + 2^16
|
||||
p256OrdMul(m, t, m) // _fffffffe
|
||||
p256OrdMul(t, t, x) // _ffffffff = x32
|
||||
|
||||
p256OrdSqr(x, m, 32) // _fffffffe00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffff
|
||||
p256OrdSqr(x, x, 32) // _fffffffeffffffff00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffffffffffff
|
||||
p256OrdSqr(x, x, 32) // _fffffffeffffffffffffffff00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffffffffffffffffffff
|
||||
|
||||
sqrs := []uint8{
|
||||
4, 3, 11, 5, 3, 5, 1,
|
||||
3, 7, 5, 9, 7, 5, 5,
|
||||
4, 5, 2, 2, 7, 3, 5,
|
||||
5, 6, 2, 6, 3, 5,
|
||||
}
|
||||
muls := []*p256OrdElement{
|
||||
_111, _1, _1111, _1111, _101, _10101, _1,
|
||||
_1, _111, _11, _101, _10101, _10101, _111,
|
||||
_111, _1111, _11, _1, _1, _1, _111,
|
||||
_111, _10101, _1, _1, _1, _1}
|
||||
|
||||
for i, s := range sqrs {
|
||||
p256OrdSqr(x, x, int(s))
|
||||
p256OrdMul(x, x, muls[i])
|
||||
}
|
||||
// Montgomery multiplication by R⁻¹, or 1 outside the domain as R⁻¹×R = 1,
|
||||
// converts a Montgomery value out of the domain.
|
||||
one := &p256OrdElement{1}
|
||||
p256OrdMul(x, x, one)
|
||||
|
||||
var xOut [32]byte
|
||||
p256OrdLittleToBig(&xOut, x)
|
||||
return xOut[:], nil
|
||||
}
|
92
internal/sm2ec/p256_asm_ordinv_test.go
Normal file
92
internal/sm2ec/p256_asm_ordinv_test.go
Normal file
@ -0,0 +1,92 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/sm2ec"
|
||||
elliptic "github.com/emmansun/gmsm/sm2/sm2ec"
|
||||
)
|
||||
|
||||
func TestP256OrdInverse(t *testing.T) {
|
||||
N := elliptic.P256().Params().N
|
||||
|
||||
// inv(0) is expected to be 0.
|
||||
zero := make([]byte, 32)
|
||||
out, err := sm2ec.P256OrdInverse(zero)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, zero) {
|
||||
t.Error("unexpected output for inv(0)")
|
||||
}
|
||||
|
||||
// inv(N) is also 0 mod N.
|
||||
input := make([]byte, 32)
|
||||
N.FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, zero) {
|
||||
t.Error("unexpected output for inv(N)")
|
||||
}
|
||||
if !bytes.Equal(input, N.Bytes()) {
|
||||
t.Error("input was modified")
|
||||
}
|
||||
|
||||
// Check inv(1) and inv(N+1) against math/big
|
||||
exp := new(big.Int).ModInverse(big.NewInt(1), N).FillBytes(make([]byte, 32))
|
||||
big.NewInt(1).FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(1)")
|
||||
}
|
||||
new(big.Int).Add(N, big.NewInt(1)).FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(N+1)")
|
||||
}
|
||||
|
||||
// Check inv(20) and inv(N+20) against math/big
|
||||
exp = new(big.Int).ModInverse(big.NewInt(20), N).FillBytes(make([]byte, 32))
|
||||
big.NewInt(20).FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(20)")
|
||||
}
|
||||
new(big.Int).Add(N, big.NewInt(20)).FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(N+20)")
|
||||
}
|
||||
|
||||
// Check inv(2^256-1) against math/big
|
||||
bigInput := new(big.Int).Lsh(big.NewInt(1), 256)
|
||||
bigInput.Sub(bigInput, big.NewInt(1))
|
||||
exp = new(big.Int).ModInverse(bigInput, N).FillBytes(make([]byte, 32))
|
||||
bigInput.FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(2^256-1)")
|
||||
}
|
||||
}
|
BIN
internal/sm2ec/p256_asm_table.bin
Normal file
BIN
internal/sm2ec/p256_asm_table.bin
Normal file
Binary file not shown.
46
internal/sm2ec/p256_asm_table_test.go
Normal file
46
internal/sm2ec/p256_asm_table_test.go
Normal file
@ -0,0 +1,46 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestP256PrecomputedTable(t *testing.T) {
|
||||
base := NewP256Point().SetGenerator()
|
||||
|
||||
for i := 0; i < 43; i++ {
|
||||
t.Run(fmt.Sprintf("table[%d]", i), func(t *testing.T) {
|
||||
testP256AffineTable(t, base, &p256Precomputed[i])
|
||||
})
|
||||
|
||||
for k := 0; k < 6; k++ {
|
||||
base.Double(base)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testP256AffineTable(t *testing.T, base *P256Point, table *p256AffineTable) {
|
||||
p := NewP256Point()
|
||||
zInv := new(p256Element)
|
||||
zInvSq := new(p256Element)
|
||||
|
||||
for j := 0; j < 32; j++ {
|
||||
p.Add(p, base)
|
||||
|
||||
// Convert p to affine coordinates.
|
||||
p256Inverse(zInv, &p.z)
|
||||
p256Sqr(zInvSq, zInv, 1)
|
||||
p256Mul(zInv, zInv, zInvSq)
|
||||
|
||||
p256Mul(&p.x, &p.x, zInvSq)
|
||||
p256Mul(&p.y, &p.y, zInv)
|
||||
p.z = p256One
|
||||
|
||||
if p256Equal(&table[j].x, &p.x) != 1 || p256Equal(&table[j].y, &p.y) != 1 {
|
||||
t.Fatalf("incorrect table entry at index %d", j)
|
||||
}
|
||||
}
|
||||
}
|
882
internal/sm2ec/sm2p256_asm.go
Normal file
882
internal/sm2ec/sm2p256_asm.go
Normal file
@ -0,0 +1,882 @@
|
||||
// It is by standing on the shoulders of giants.
|
||||
|
||||
// This file contains the Go wrapper for the constant-time, 64-bit assembly
|
||||
// implementation of P256. The optimizations performed here are described in
|
||||
// detail in:
|
||||
// S.Gueron and V.Krasnov, "Fast prime field elliptic-curve cryptography with
|
||||
// 256-bit primes"
|
||||
// https://link.springer.com/article/10.1007%2Fs13389-014-0090-x
|
||||
// https://eprint.iacr.org/2013/816.pdf
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// p256Element is a P-256 base field element in [0, P-1] in the Montgomery
|
||||
// domain (with R 2²⁵⁶) as four limbs in little-endian order value.
|
||||
type p256Element [4]uint64
|
||||
|
||||
// p256One is one in the Montgomery domain.
|
||||
var p256One = p256Element{0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000}
|
||||
|
||||
var p256Zero = p256Element{}
|
||||
|
||||
// p256P is 2^256 - 2^224 - 2^96 + 2^64 - 1.
|
||||
var p256P = p256Element{0xffffffffffffffff, 0xffffffff00000000,
|
||||
0xffffffffffffffff, 0xfffffffeffffffff}
|
||||
|
||||
// P256Point is a P-256 point. The zero value should not be assumed to be valid
|
||||
// (although it is in this implementation).
|
||||
type P256Point struct {
|
||||
// (X:Y:Z) are Jacobian coordinates where x = X/Z² and y = Y/Z³. The point
|
||||
// at infinity can be represented by any set of coordinates with Z = 0.
|
||||
x, y, z p256Element
|
||||
}
|
||||
|
||||
// NewP256Point returns a new P256Point representing the point at infinity.
|
||||
func NewP256Point() *P256Point {
|
||||
return &P256Point{
|
||||
x: p256One, y: p256One, z: p256Zero,
|
||||
}
|
||||
}
|
||||
|
||||
// SetGenerator sets p to the canonical generator and returns p.
|
||||
func (p *P256Point) SetGenerator() *P256Point {
|
||||
p.x = p256Element{0x61328990f418029e, 0x3e7981eddca6c050,
|
||||
0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
p.y = p256Element{0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa,
|
||||
0x8d4cfb066e2a48f8, 0x63cd65d481d735bd}
|
||||
p.z = p256One
|
||||
return p
|
||||
}
|
||||
|
||||
// Set sets p = q and returns p.
|
||||
func (p *P256Point) Set(q *P256Point) *P256Point {
|
||||
p.x, p.y, p.z = q.x, q.y, q.z
|
||||
return p
|
||||
}
|
||||
|
||||
const p256ElementLength = 32
|
||||
const p256UncompressedLength = 1 + 2*p256ElementLength
|
||||
const p256CompressedLength = 1 + p256ElementLength
|
||||
|
||||
// toElementArray, convert slice of bytes to pointer to [32]byte.
|
||||
// This function is required for low version of golang, can type cast directly
|
||||
// since golang 1.18.
|
||||
func toElementArray(b []byte) *[32]byte {
|
||||
tmpPtr := (*unsafe.Pointer)(unsafe.Pointer(&b))
|
||||
return (*[32]byte)(*tmpPtr)
|
||||
}
|
||||
|
||||
// SetBytes sets p to the compressed, uncompressed, or infinity value encoded in
|
||||
// b, as specified in SEC 1, Version 2.0, Section 2.3.4. If the point is not on
|
||||
// the curve, it returns nil and an error, and the receiver is unchanged.
|
||||
// Otherwise, it returns p.
|
||||
func (p *P256Point) SetBytes(b []byte) (*P256Point, error) {
|
||||
// p256Mul operates in the Montgomery domain with R = 2²⁵⁶ mod p. Thus rr
|
||||
// here is R in the Montgomery domain, or R×R mod p. See comment in
|
||||
// P256OrdInverse about how this is used.
|
||||
rr := p256Element{0x0000000200000003, 0x00000002ffffffff,
|
||||
0x0000000100000001, 0x0000000400000002}
|
||||
|
||||
switch {
|
||||
// Point at infinity.
|
||||
case len(b) == 1 && b[0] == 0:
|
||||
return p.Set(NewP256Point()), nil
|
||||
|
||||
// Uncompressed form.
|
||||
case len(b) == p256UncompressedLength && b[0] == 4:
|
||||
var r P256Point
|
||||
p256BigToLittle(&r.x, toElementArray(b[1:33]))
|
||||
p256BigToLittle(&r.y, toElementArray(b[33:65]))
|
||||
if p256LessThanP(&r.x) == 0 || p256LessThanP(&r.y) == 0 {
|
||||
return nil, errors.New("invalid P256 element encoding")
|
||||
}
|
||||
p256Mul(&r.x, &r.x, &rr)
|
||||
p256Mul(&r.y, &r.y, &rr)
|
||||
if err := p256CheckOnCurve(&r.x, &r.y); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.z = p256One
|
||||
return p.Set(&r), nil
|
||||
|
||||
// Compressed form.
|
||||
case len(b) == p256CompressedLength && (b[0] == 2 || b[0] == 3):
|
||||
var r P256Point
|
||||
p256BigToLittle(&r.x, toElementArray(b[1:33]))
|
||||
if p256LessThanP(&r.x) == 0 {
|
||||
return nil, errors.New("invalid P256 element encoding")
|
||||
}
|
||||
p256Mul(&r.x, &r.x, &rr)
|
||||
|
||||
// y² = x³ - 3x + b
|
||||
p256Polynomial(&r.y, &r.x)
|
||||
if !p256Sqrt(&r.y, &r.y) {
|
||||
return nil, errors.New("invalid P256 compressed point encoding")
|
||||
}
|
||||
|
||||
// Select the positive or negative root, as indicated by the least
|
||||
// significant bit, based on the encoding type byte.
|
||||
yy := new(p256Element)
|
||||
p256FromMont(yy, &r.y)
|
||||
cond := int(yy[0]&1) ^ int(b[0]&1)
|
||||
p256NegCond(&r.y, cond)
|
||||
|
||||
r.z = p256One
|
||||
return p.Set(&r), nil
|
||||
|
||||
default:
|
||||
return nil, errors.New("invalid P256 point encoding")
|
||||
}
|
||||
}
|
||||
|
||||
// p256Polynomial sets y2 to x³ - 3x + b, and returns y2.
|
||||
func p256Polynomial(y2, x *p256Element) *p256Element {
|
||||
x3 := new(p256Element)
|
||||
p256Sqr(x3, x, 1)
|
||||
p256Mul(x3, x3, x)
|
||||
|
||||
threeX := new(p256Element)
|
||||
p256Add(threeX, x, x)
|
||||
p256Add(threeX, threeX, x)
|
||||
p256NegCond(threeX, 1)
|
||||
|
||||
p256B := &p256Element{0x90d230632bc0dd42, 0x71cf379ae9b537ab,
|
||||
0x527981505ea51c3c, 0x240fe188ba20e2c8}
|
||||
|
||||
p256Add(x3, x3, threeX)
|
||||
p256Add(x3, x3, p256B)
|
||||
|
||||
*y2 = *x3
|
||||
return y2
|
||||
}
|
||||
|
||||
func p256CheckOnCurve(x, y *p256Element) error {
|
||||
// y² = x³ - 3x + b
|
||||
rhs := p256Polynomial(new(p256Element), x)
|
||||
lhs := new(p256Element)
|
||||
p256Sqr(lhs, y, 1)
|
||||
if p256Equal(lhs, rhs) != 1 {
|
||||
return errors.New("point not on SM2 P256 curve")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// p256LessThanP returns 1 if x < p, and 0 otherwise. Note that a p256Element is
|
||||
// not allowed to be equal to or greater than p, so if this function returns 0
|
||||
// then x is invalid.
|
||||
func p256LessThanP(x *p256Element) int {
|
||||
var b uint64
|
||||
_, b = bits.Sub64(x[0], p256P[0], b)
|
||||
_, b = bits.Sub64(x[1], p256P[1], b)
|
||||
_, b = bits.Sub64(x[2], p256P[2], b)
|
||||
_, b = bits.Sub64(x[3], p256P[3], b)
|
||||
return int(b)
|
||||
}
|
||||
|
||||
// p256Add sets res = x + y.
|
||||
func p256Add(res, x, y *p256Element) {
|
||||
var c, b uint64
|
||||
t1 := make([]uint64, 4)
|
||||
t1[0], c = bits.Add64(x[0], y[0], 0)
|
||||
t1[1], c = bits.Add64(x[1], y[1], c)
|
||||
t1[2], c = bits.Add64(x[2], y[2], c)
|
||||
t1[3], c = bits.Add64(x[3], y[3], c)
|
||||
t2 := make([]uint64, 4)
|
||||
t2[0], b = bits.Sub64(t1[0], p256P[0], 0)
|
||||
t2[1], b = bits.Sub64(t1[1], p256P[1], b)
|
||||
t2[2], b = bits.Sub64(t1[2], p256P[2], b)
|
||||
t2[3], b = bits.Sub64(t1[3], p256P[3], b)
|
||||
// Three options:
|
||||
// - a+b < p
|
||||
// then c is 0, b is 1, and t1 is correct
|
||||
// - p <= a+b < 2^256
|
||||
// then c is 0, b is 0, and t2 is correct
|
||||
// - 2^256 <= a+b
|
||||
// then c is 1, b is 1, and t2 is correct
|
||||
t2Mask := (c ^ b) - 1
|
||||
res[0] = (t1[0] & ^t2Mask) | (t2[0] & t2Mask)
|
||||
res[1] = (t1[1] & ^t2Mask) | (t2[1] & t2Mask)
|
||||
res[2] = (t1[2] & ^t2Mask) | (t2[2] & t2Mask)
|
||||
res[3] = (t1[3] & ^t2Mask) | (t2[3] & t2Mask)
|
||||
}
|
||||
|
||||
// p256Sqrt sets e to a square root of x. If x is not a square, p256Sqrt returns
|
||||
// false and e is unchanged. e and x can overlap.
|
||||
func p256Sqrt(e, x *p256Element) (isSquare bool) {
|
||||
z, t0, t1, t2, t3, t4 := new(p256Element), new(p256Element), new(p256Element), new(p256Element), new(p256Element), new(p256Element)
|
||||
|
||||
// Since p = 3 mod 4, exponentiation by (p + 1) / 4 yields a square root candidate.
|
||||
//
|
||||
// The sequence of 13 multiplications and 253 squarings is derived from the
|
||||
// following addition chain generated with github.com/mmcloughlin/addchain v0.4.0.
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _110 = 2*_11
|
||||
// _111 = 1 + _110
|
||||
// _1110 = 2*_111
|
||||
// _1111 = 1 + _1110
|
||||
// _11110 = 2*_1111
|
||||
// _111100 = 2*_11110
|
||||
// _1111000 = 2*_111100
|
||||
// i19 = (_1111000 << 3 + _111100) << 5 + _1111000
|
||||
// x31 = (i19 << 2 + _11110) << 14 + i19 + _111
|
||||
// i42 = x31 << 4
|
||||
// i73 = i42 << 31
|
||||
// i74 = i42 + i73
|
||||
// i171 = (i73 << 32 + i74) << 62 + i74 + _1111
|
||||
// return (i171 << 32 + 1) << 62
|
||||
//
|
||||
p256Sqr(z, x, 1) // z.Square(x)
|
||||
p256Mul(z, x, z) // z.Mul(x, z)
|
||||
p256Sqr(z, z, 1) // z.Square(z)
|
||||
p256Mul(t0, x, z) // t0.Mul(x, z)
|
||||
p256Sqr(z, t0, 1) // z.Square(t0)
|
||||
p256Mul(z, x, z) // z.Mul(x, z)
|
||||
p256Sqr(t2, z, 1) // t2.Square(z)
|
||||
p256Sqr(t3, t2, 1) // t3.Square(t2)
|
||||
p256Sqr(t1, t3, 1) // t1.Square(t3)
|
||||
// t4.Square(t1)
|
||||
//for s := 1; s < 3; s++ {
|
||||
// t4.Square(t4)
|
||||
//}
|
||||
p256Sqr(t4, t1, 3)
|
||||
p256Mul(t3, t3, t4) // t3.Mul(t3, t4)
|
||||
//for s := 0; s < 5; s++ {
|
||||
// t3.Square(t3)
|
||||
//}
|
||||
p256Sqr(t3, t3, 5)
|
||||
p256Mul(t1, t1, t3) // t1.Mul(t1, t3)
|
||||
//t3.Square(t1)
|
||||
//for s := 1; s < 2; s++ {
|
||||
// t3.Square(t3)
|
||||
//}
|
||||
p256Sqr(t3, t1, 2)
|
||||
p256Mul(t2, t2, t3) // t2.Mul(t2, t3)
|
||||
//for s := 0; s < 14; s++ {
|
||||
// t2.Square(t2)
|
||||
//}
|
||||
p256Sqr(t2, t2, 14)
|
||||
p256Mul(t1, t1, t2) // t1.Mul(t1, t2)
|
||||
|
||||
p256Mul(t0, t0, t1) // t0.Mul(t0, t1)
|
||||
//for s := 0; s < 4; s++ {
|
||||
// t0.Square(t0)
|
||||
//}
|
||||
p256Sqr(t0, t0, 4)
|
||||
//t1.Square(t0)
|
||||
//for s := 1; s < 31; s++ {
|
||||
// t1.Square(t1)
|
||||
//}
|
||||
p256Sqr(t1, t0, 31)
|
||||
p256Mul(t0, t0, t1) //t0.Mul(t0, t1)
|
||||
//for s := 0; s < 32; s++ {
|
||||
// t1.Square(t1)
|
||||
//}
|
||||
p256Sqr(t1, t1, 32)
|
||||
|
||||
p256Mul(t1, t0, t1) //t1.Mul(t0, t1)
|
||||
//for s := 0; s < 62; s++ {
|
||||
// t1.Square(t1)
|
||||
//}
|
||||
p256Sqr(t1, t1, 62)
|
||||
p256Mul(t0, t0, t1) //t0.Mul(t0, t1)
|
||||
p256Mul(z, z, t0) //z.Mul(z, t0)
|
||||
//for s := 0; s < 32; s++ {
|
||||
// e.Square(e)
|
||||
//}
|
||||
p256Sqr(z, z, 32)
|
||||
p256Mul(z, z, x) // z.Mul(x, z)
|
||||
//for s := 0; s < 62; s++ {
|
||||
// z.Square(z)
|
||||
//}
|
||||
p256Sqr(z, z, 62)
|
||||
|
||||
p256Sqr(t1, z, 1)
|
||||
if p256Equal(t1, x) != 1 {
|
||||
return false
|
||||
}
|
||||
*e = *z
|
||||
return true
|
||||
}
|
||||
|
||||
// The following assembly functions are implemented in p256_asm_*.s
|
||||
|
||||
// Montgomery multiplication. Sets res = in1 * in2 * R⁻¹ mod p.
|
||||
//
|
||||
//go:noescape
|
||||
func p256Mul(res, in1, in2 *p256Element)
|
||||
|
||||
// Montgomery square, repeated n times (n >= 1).
|
||||
//
|
||||
//go:noescape
|
||||
func p256Sqr(res, in *p256Element, n int)
|
||||
|
||||
// Montgomery multiplication by R⁻¹, or 1 outside the domain.
|
||||
// Sets res = in * R⁻¹, bringing res out of the Montgomery domain.
|
||||
//
|
||||
//go:noescape
|
||||
func p256FromMont(res, in *p256Element)
|
||||
|
||||
// If cond is not 0, sets val = -val mod p.
|
||||
//
|
||||
//go:noescape
|
||||
func p256NegCond(val *p256Element, cond int)
|
||||
|
||||
// If cond is 0, sets res = b, otherwise sets res = a.
|
||||
//
|
||||
//go:noescape
|
||||
func p256MovCond(res, a, b *P256Point, cond int)
|
||||
|
||||
//go:noescape
|
||||
func p256BigToLittle(res *p256Element, in *[32]byte)
|
||||
|
||||
//go:noescape
|
||||
func p256LittleToBig(res *[32]byte, in *p256Element)
|
||||
|
||||
//go:noescape
|
||||
func p256OrdBigToLittle(res *p256OrdElement, in *[32]byte)
|
||||
|
||||
//go:noescape
|
||||
func p256OrdLittleToBig(res *[32]byte, in *p256OrdElement)
|
||||
|
||||
// p256Table is a table of the first 16 multiples of a point. Points are stored
|
||||
// at an index offset of -1 so [8]P is at index 7, P is at 0, and [16]P is at 15.
|
||||
// [0]P is the point at infinity and it's not stored.
|
||||
type p256Table [16]P256Point
|
||||
|
||||
// p256Select sets res to the point at index idx in the table.
|
||||
// idx must be in [0, 15]. It executes in constant time.
|
||||
//
|
||||
//go:noescape
|
||||
func p256Select(res *P256Point, table *p256Table, idx int)
|
||||
|
||||
// p256AffinePoint is a point in affine coordinates (x, y). x and y are still
|
||||
// Montgomery domain elements. The point can't be the point at infinity.
|
||||
type p256AffinePoint struct {
|
||||
x, y p256Element
|
||||
}
|
||||
|
||||
// p256AffineTable is a table of the first 32 multiples of a point. Points are
|
||||
// stored at an index offset of -1 like in p256Table, and [0]P is not stored.
|
||||
type p256AffineTable [32]p256AffinePoint
|
||||
|
||||
// p256Precomputed is a series of precomputed multiples of G, the canonical
|
||||
// generator. The first p256AffineTable contains multiples of G. The second one
|
||||
// multiples of [2⁶]G, the third one of [2¹²]G, and so on, where each successive
|
||||
// table is the previous table doubled six times. Six is the width of the
|
||||
// sliding window used in p256ScalarMult, and having each table already
|
||||
// pre-doubled lets us avoid the doublings between windows entirely. This table
|
||||
// MUST NOT be modified, as it aliases into p256PrecomputedEmbed below.
|
||||
var p256Precomputed *[43]p256AffineTable
|
||||
|
||||
//go:embed p256_asm_table.bin
|
||||
var p256PrecomputedEmbed string
|
||||
|
||||
func init() {
|
||||
p256PrecomputedPtr := (*unsafe.Pointer)(unsafe.Pointer(&p256PrecomputedEmbed))
|
||||
p256Precomputed = (*[43]p256AffineTable)(*p256PrecomputedPtr)
|
||||
}
|
||||
|
||||
// p256SelectAffine sets res to the point at index idx in the table.
|
||||
// idx must be in [0, 31]. It executes in constant time.
|
||||
//
|
||||
//go:noescape
|
||||
func p256SelectAffine(res *p256AffinePoint, table *p256AffineTable, idx int)
|
||||
|
||||
// Point addition with an affine point and constant time conditions.
|
||||
// If zero is 0, sets res = in2. If sel is 0, sets res = in1.
|
||||
// If sign is not 0, sets res = in1 + -in2. Otherwise, sets res = in1 + in2
|
||||
//
|
||||
//go:noescape
|
||||
func p256PointAddAffineAsm(res, in1 *P256Point, in2 *p256AffinePoint, sign, sel, zero int)
|
||||
|
||||
// Point addition. Sets res = in1 + in2. Returns one if the two input points
|
||||
// were equal and zero otherwise. If in1 or in2 are the point at infinity, res
|
||||
// and the return value are undefined.
|
||||
//
|
||||
//go:noescape
|
||||
func p256PointAddAsm(res, in1, in2 *P256Point) int
|
||||
|
||||
// Point doubling. Sets res = in + in. in can be the point at infinity.
|
||||
//
|
||||
//go:noescape
|
||||
func p256PointDoubleAsm(res, in *P256Point)
|
||||
|
||||
// p256OrdElement is a P-256 scalar field element in [0, ord(G)-1] in the
|
||||
// Montgomery domain (with R 2²⁵⁶) as four uint64 limbs in little-endian order.
|
||||
type p256OrdElement [4]uint64
|
||||
|
||||
// Add sets q = p1 + p2, and returns q. The points may overlap.
|
||||
func (q *P256Point) Add(r1, r2 *P256Point) *P256Point {
|
||||
var sum, double P256Point
|
||||
r1IsInfinity := r1.isInfinity()
|
||||
r2IsInfinity := r2.isInfinity()
|
||||
pointsEqual := p256PointAddAsm(&sum, r1, r2)
|
||||
p256PointDoubleAsm(&double, r1)
|
||||
p256MovCond(&sum, &double, &sum, pointsEqual)
|
||||
p256MovCond(&sum, r1, &sum, r2IsInfinity)
|
||||
p256MovCond(&sum, r2, &sum, r1IsInfinity)
|
||||
return q.Set(&sum)
|
||||
}
|
||||
|
||||
// Double sets q = p + p, and returns q. The points may overlap.
|
||||
func (q *P256Point) Double(p *P256Point) *P256Point {
|
||||
var double P256Point
|
||||
p256PointDoubleAsm(&double, p)
|
||||
return q.Set(&double)
|
||||
}
|
||||
|
||||
// ScalarBaseMult sets r = scalar * generator, where scalar is a 32-byte big
|
||||
// endian value, and returns r. If scalar is not 32 bytes long, ScalarBaseMult
|
||||
// returns an error and the receiver is unchanged.
|
||||
func (r *P256Point) ScalarBaseMult(scalar []byte) (*P256Point, error) {
|
||||
if len(scalar) != 32 {
|
||||
return nil, errors.New("invalid scalar length")
|
||||
}
|
||||
scalarReversed := new(p256OrdElement)
|
||||
p256OrdBigToLittle(scalarReversed, toElementArray(scalar))
|
||||
|
||||
r.p256BaseMult(scalarReversed)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// ScalarMult sets r = scalar * q, where scalar is a 32-byte big endian value,
|
||||
// and returns r. If scalar is not 32 bytes long, ScalarBaseMult returns an
|
||||
// error and the receiver is unchanged.
|
||||
func (r *P256Point) ScalarMult(q *P256Point, scalar []byte) (*P256Point, error) {
|
||||
if len(scalar) != 32 {
|
||||
return nil, errors.New("invalid scalar length")
|
||||
}
|
||||
scalarReversed := new(p256OrdElement)
|
||||
p256OrdBigToLittle(scalarReversed, toElementArray(scalar))
|
||||
|
||||
r.Set(q).p256ScalarMult(scalarReversed)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// uint64IsZero returns 1 if x is zero and zero otherwise.
|
||||
func uint64IsZero(x uint64) int {
|
||||
x = ^x
|
||||
x &= x >> 32
|
||||
x &= x >> 16
|
||||
x &= x >> 8
|
||||
x &= x >> 4
|
||||
x &= x >> 2
|
||||
x &= x >> 1
|
||||
return int(x & 1)
|
||||
}
|
||||
|
||||
// p256Equal returns 1 if a and b are equal and 0 otherwise.
|
||||
func p256Equal(a, b *p256Element) int {
|
||||
var acc uint64
|
||||
for i := range a {
|
||||
acc |= a[i] ^ b[i]
|
||||
}
|
||||
return uint64IsZero(acc)
|
||||
}
|
||||
|
||||
// isInfinity returns 1 if p is the point at infinity and 0 otherwise.
|
||||
func (p *P256Point) isInfinity() int {
|
||||
return p256Equal(&p.z, &p256Zero)
|
||||
}
|
||||
|
||||
// Bytes returns the uncompressed or infinity encoding of p, as specified in
|
||||
// SEC 1, Version 2.0, Section 2.3.3. Note that the encoding of the point at
|
||||
// infinity is shorter than all other encodings.
|
||||
func (p *P256Point) Bytes() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var out [p256UncompressedLength]byte
|
||||
return p.bytes(&out)
|
||||
}
|
||||
|
||||
func (p *P256Point) bytes(out *[p256UncompressedLength]byte) []byte {
|
||||
// The proper representation of the point at infinity is a single zero byte.
|
||||
if p.isInfinity() == 1 {
|
||||
return append(out[:0], 0)
|
||||
}
|
||||
|
||||
x, y := new(p256Element), new(p256Element)
|
||||
p.affineFromMont(x, y)
|
||||
|
||||
out[0] = 4 // Uncompressed form.
|
||||
p256LittleToBig(toElementArray(out[1:33]), x)
|
||||
p256LittleToBig(toElementArray(out[33:65]), y)
|
||||
|
||||
return out[:]
|
||||
}
|
||||
|
||||
// affineFromMont sets (x, y) to the affine coordinates of p, converted out of the
|
||||
// Montgomery domain.
|
||||
func (p *P256Point) affineFromMont(x, y *p256Element) {
|
||||
p256Inverse(y, &p.z)
|
||||
p256Sqr(x, y, 1)
|
||||
p256Mul(y, y, x)
|
||||
|
||||
p256Mul(x, &p.x, x)
|
||||
p256Mul(y, &p.y, y)
|
||||
|
||||
p256FromMont(x, x)
|
||||
p256FromMont(y, y)
|
||||
}
|
||||
|
||||
// BytesCompressed returns the compressed or infinity encoding of p, as
|
||||
// specified in SEC 1, Version 2.0, Section 2.3.3. Note that the encoding of the
|
||||
// point at infinity is shorter than all other encodings.
|
||||
func (p *P256Point) BytesCompressed() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var out [p256CompressedLength]byte
|
||||
return p.bytesCompressed(&out)
|
||||
}
|
||||
|
||||
func (p *P256Point) bytesCompressed(out *[p256CompressedLength]byte) []byte {
|
||||
if p.isInfinity() == 1 {
|
||||
return append(out[:0], 0)
|
||||
}
|
||||
|
||||
x, y := new(p256Element), new(p256Element)
|
||||
p.affineFromMont(x, y)
|
||||
|
||||
out[0] = 2 | byte(y[0]&1)
|
||||
p256LittleToBig(toElementArray(out[1:33]), x)
|
||||
|
||||
return out[:]
|
||||
}
|
||||
|
||||
// Select sets q to p1 if cond == 1, and to p2 if cond == 0.
|
||||
func (q *P256Point) Select(p1, p2 *P256Point, cond int) *P256Point {
|
||||
p256MovCond(q, p1, p2, cond)
|
||||
return q
|
||||
}
|
||||
|
||||
// p256Inverse sets out to in⁻¹ mod p. If in is zero, out will be zero.
|
||||
func p256Inverse(out, in *p256Element) {
|
||||
// Inversion is calculated through exponentiation by p - 2, per Fermat's
|
||||
// little theorem.
|
||||
//
|
||||
// The sequence of 14 multiplications and 255 squarings is derived from the
|
||||
// following addition chain generated with github.com/mmcloughlin/addchain
|
||||
// v0.4.0.
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _110 = 2*_11
|
||||
// _111 = 1 + _110
|
||||
// _111000 = _111 << 3
|
||||
// _111111 = _111 + _111000
|
||||
// _1111110 = 2*_111111
|
||||
// _1111111 = 1 + _1111110
|
||||
// x12 = _1111110 << 5 + _111111
|
||||
// x24 = x12 << 12 + x12
|
||||
// x31 = x24 << 7 + _1111111
|
||||
// i39 = x31 << 2
|
||||
// i68 = i39 << 29
|
||||
// x62 = x31 + i68
|
||||
// i71 = i68 << 2
|
||||
// x64 = i39 + i71 + _11
|
||||
// i265 = ((i71 << 32 + x64) << 64 + x64) << 94
|
||||
// return (x62 + i265) << 2 + 1
|
||||
// Allocate Temporaries.
|
||||
var (
|
||||
t0 = new(p256Element)
|
||||
t1 = new(p256Element)
|
||||
t2 = new(p256Element)
|
||||
)
|
||||
// Step 1: z = x^0x2
|
||||
//z.Sqr(x)
|
||||
p256Sqr(out, in, 1)
|
||||
|
||||
// Step 2: t0 = x^0x3
|
||||
// t0.Mul(x, z)
|
||||
p256Mul(t0, in, out)
|
||||
|
||||
// Step 3: z = x^0x6
|
||||
// z.Sqr(t0)
|
||||
p256Sqr(out, t0, 1)
|
||||
|
||||
// Step 4: z = x^0x7
|
||||
// z.Mul(x, z)
|
||||
p256Mul(out, in, out)
|
||||
|
||||
// Step 7: t1 = x^0x38
|
||||
//t1.Sqr(z)
|
||||
//for s := 1; s < 3; s++ {
|
||||
// t1.Sqr(t1)
|
||||
//}
|
||||
p256Sqr(t1, out, 3)
|
||||
|
||||
// Step 8: t1 = x^0x3f
|
||||
//t1.Mul(z, t1)
|
||||
p256Mul(t1, out, t1)
|
||||
|
||||
// Step 9: t2 = x^0x7e
|
||||
//t2.Sqr(t1)
|
||||
p256Sqr(t2, t1, 1)
|
||||
|
||||
// Step 10: z = x^0x7f
|
||||
//z.Mul(x, t2)
|
||||
p256Mul(out, in, t2)
|
||||
|
||||
// Step 15: t2 = x^0xfc0
|
||||
//for s := 0; s < 5; s++ {
|
||||
// t2.Sqr(t2)
|
||||
//}
|
||||
p256Sqr(t2, t2, 5)
|
||||
|
||||
// Step 16: t1 = x^0xfff
|
||||
//t1.Mul(t1, t2)
|
||||
p256Mul(t1, t1, t2)
|
||||
|
||||
// Step 28: t2 = x^0xfff000
|
||||
//t2.Sqr(t1)
|
||||
//for s := 1; s < 12; s++ {
|
||||
// t2.Sqr(t2)
|
||||
//}
|
||||
p256Sqr(t2, t1, 12)
|
||||
|
||||
// Step 29: t1 = x^0xffffff
|
||||
//t1.Mul(t1, t2)
|
||||
p256Mul(t1, t1, t2)
|
||||
|
||||
// Step 36: t1 = x^0x7fffff80
|
||||
//for s := 0; s < 7; s++ {
|
||||
// t1.Sqr(t1)
|
||||
//}
|
||||
p256Sqr(t1, t1, 7)
|
||||
|
||||
// Step 37: z = x^0x7fffffff
|
||||
//z.Mul(z, t1)
|
||||
p256Mul(out, out, t1)
|
||||
|
||||
// Step 39: t2 = x^0x1fffffffc
|
||||
//t2.Sqr(z)
|
||||
//for s := 1; s < 2; s++ {
|
||||
// t2.Sqr(t2)
|
||||
//}
|
||||
p256Sqr(t2, out, 2)
|
||||
|
||||
// Step 68: t1 = x^0x3fffffff80000000
|
||||
//t1.Sqr(t2)
|
||||
//for s := 1; s < 29; s++ {
|
||||
// t1.Sqr(t1)
|
||||
//}
|
||||
p256Sqr(t1, t2, 29)
|
||||
|
||||
// Step 69: z = x^0x3fffffffffffffff
|
||||
//z.Mul(z, t1)
|
||||
p256Mul(out, out, t1)
|
||||
|
||||
// Step 71: t1 = x^0xfffffffe00000000
|
||||
//for s := 0; s < 2; s++ {
|
||||
// t1.Sqr(t1)
|
||||
//}
|
||||
p256Sqr(t1, t1, 2)
|
||||
|
||||
// Step 72: t2 = x^0xfffffffffffffffc
|
||||
//t2.Mul(t2, t1)
|
||||
p256Mul(t2, t2, t1)
|
||||
|
||||
// Step 73: t0 = x^0xffffffffffffffff
|
||||
//t0.Mul(t0, t2)
|
||||
p256Mul(t0, t0, t2)
|
||||
|
||||
// Step 105: t1 = x^0xfffffffe0000000000000000
|
||||
//for s := 0; s < 32; s++ {
|
||||
// t1.Sqr(t1)
|
||||
//}
|
||||
p256Sqr(t1, t1, 32)
|
||||
|
||||
// Step 106: t1 = x^0xfffffffeffffffffffffffff
|
||||
//t1.Mul(t0, t1)
|
||||
p256Mul(t1, t0, t1)
|
||||
|
||||
// Step 170: t1 = x^0xfffffffeffffffffffffffff0000000000000000
|
||||
//for s := 0; s < 64; s++ {
|
||||
// t1.Sqr(t1)
|
||||
//}
|
||||
p256Sqr(t1, t1, 64)
|
||||
|
||||
// Step 171: t0 = x^0xfffffffeffffffffffffffffffffffffffffffff
|
||||
//t0.Mul(t0, t1)
|
||||
p256Mul(t0, t0, t1)
|
||||
|
||||
// Step 265: t0 = x^0x3fffffffbfffffffffffffffffffffffffffffffc00000000000000000000000
|
||||
//for s := 0; s < 94; s++ {
|
||||
// t0.Sqr(t0)
|
||||
//}
|
||||
p256Sqr(t0, t0, 94)
|
||||
|
||||
// Step 266: z = x^0x3fffffffbfffffffffffffffffffffffffffffffc00000003fffffffffffffff
|
||||
//z.Mul(z, t0)
|
||||
p256Mul(out, out, t0)
|
||||
|
||||
// Step 268: z = x^0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc
|
||||
//for s := 0; s < 2; s++ {
|
||||
// z.Sqr(z)
|
||||
//}
|
||||
p256Sqr(out, out, 2)
|
||||
|
||||
// Step 269: z = x^0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffd
|
||||
//z.Mul(x, z)
|
||||
p256Mul(out, in, out)
|
||||
}
|
||||
|
||||
// This function takes those six bits as an integer (0 .. 63), writing the
|
||||
// recoded digit to *sign (0 for positive, 1 for negative) and *digit (absolute
|
||||
// value, in the range 0 .. 16). Note that this integer essentially provides
|
||||
// the input bits "shifted to the left" by one position: for example, the input
|
||||
// to compute the least significant recoded digit, given that there's no bit
|
||||
// b_-1, has to be b_4 b_3 b_2 b_1 b_0 0.
|
||||
//
|
||||
// Reference:
|
||||
// https://github.com/openssl/openssl/blob/master/crypto/ec/ecp_nistputil.c
|
||||
// https://github.com/google/boringssl/blob/master/crypto/fipsmodule/ec/util.c
|
||||
func boothW5(in uint) (int, int) {
|
||||
var s uint = ^((in >> 5) - 1) // sets all bits to MSB(in), 'in' seen as 6-bit value
|
||||
var d uint = (1 << 6) - in - 1 // d = 63 - in, or d = ^in & 0x3f
|
||||
d = (d & s) | (in & (^s)) // d = in if in < 2^5; otherwise, d = 63 - in
|
||||
d = (d >> 1) + (d & 1) // d = (d + 1) / 2
|
||||
return int(d), int(s & 1)
|
||||
}
|
||||
|
||||
func boothW6(in uint) (int, int) {
|
||||
var s uint = ^((in >> 6) - 1)
|
||||
var d uint = (1 << 7) - in - 1
|
||||
d = (d & s) | (in & (^s))
|
||||
d = (d >> 1) + (d & 1)
|
||||
return int(d), int(s & 1)
|
||||
}
|
||||
|
||||
func (p *P256Point) p256BaseMult(scalar *p256OrdElement) {
|
||||
var t0 p256AffinePoint
|
||||
|
||||
wvalue := (scalar[0] << 1) & 0x7f
|
||||
sel, sign := boothW6(uint(wvalue))
|
||||
p256SelectAffine(&t0, &p256Precomputed[0], sel)
|
||||
p.x, p.y, p.z = t0.x, t0.y, p256One
|
||||
p256NegCond(&p.y, sign)
|
||||
|
||||
index := uint(5)
|
||||
zero := sel
|
||||
|
||||
for i := 1; i < 43; i++ {
|
||||
if index < 192 {
|
||||
wvalue = ((scalar[index/64] >> (index % 64)) + (scalar[index/64+1] << (64 - (index % 64)))) & 0x7f
|
||||
} else {
|
||||
wvalue = (scalar[index/64] >> (index % 64)) & 0x7f
|
||||
}
|
||||
index += 6
|
||||
sel, sign = boothW6(uint(wvalue))
|
||||
p256SelectAffine(&t0, &p256Precomputed[i], sel)
|
||||
p256PointAddAffineAsm(p, p, &t0, sign, sel, zero)
|
||||
zero |= sel
|
||||
}
|
||||
|
||||
// If the whole scalar was zero, set to the point at infinity.
|
||||
p256MovCond(p, p, NewP256Point(), zero)
|
||||
}
|
||||
|
||||
func (p *P256Point) p256ScalarMult(scalar *p256OrdElement) {
|
||||
// precomp is a table of precomputed points that stores powers of p
|
||||
// from p^1 to p^16.
|
||||
var precomp p256Table
|
||||
var t0, t1, t2, t3 P256Point
|
||||
|
||||
// Prepare the table
|
||||
precomp[0] = *p // 1
|
||||
|
||||
p256PointDoubleAsm(&t0, p)
|
||||
p256PointDoubleAsm(&t1, &t0)
|
||||
p256PointDoubleAsm(&t2, &t1)
|
||||
p256PointDoubleAsm(&t3, &t2)
|
||||
precomp[1] = t0 // 2
|
||||
precomp[3] = t1 // 4
|
||||
precomp[7] = t2 // 8
|
||||
precomp[15] = t3 // 16
|
||||
|
||||
p256PointAddAsm(&t0, &t0, p)
|
||||
p256PointAddAsm(&t1, &t1, p)
|
||||
p256PointAddAsm(&t2, &t2, p)
|
||||
precomp[2] = t0 // 3
|
||||
precomp[4] = t1 // 5
|
||||
precomp[8] = t2 // 9
|
||||
|
||||
p256PointDoubleAsm(&t0, &t0)
|
||||
p256PointDoubleAsm(&t1, &t1)
|
||||
precomp[5] = t0 // 6
|
||||
precomp[9] = t1 // 10
|
||||
|
||||
p256PointAddAsm(&t2, &t0, p)
|
||||
p256PointAddAsm(&t1, &t1, p)
|
||||
precomp[6] = t2 // 7
|
||||
precomp[10] = t1 // 11
|
||||
|
||||
p256PointDoubleAsm(&t0, &t0)
|
||||
p256PointDoubleAsm(&t2, &t2)
|
||||
precomp[11] = t0 // 12
|
||||
precomp[13] = t2 // 14
|
||||
|
||||
p256PointAddAsm(&t0, &t0, p)
|
||||
p256PointAddAsm(&t2, &t2, p)
|
||||
precomp[12] = t0 // 13
|
||||
precomp[14] = t2 // 15
|
||||
|
||||
// Start scanning the window from top bit
|
||||
index := uint(254)
|
||||
var sel, sign int
|
||||
|
||||
wvalue := (scalar[index/64] >> (index % 64)) & 0x3f
|
||||
sel, _ = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(p, &precomp, sel)
|
||||
zero := sel
|
||||
|
||||
for index > 4 {
|
||||
index -= 5
|
||||
p256PointDoubleAsm(p, p)
|
||||
p256PointDoubleAsm(p, p)
|
||||
p256PointDoubleAsm(p, p)
|
||||
p256PointDoubleAsm(p, p)
|
||||
p256PointDoubleAsm(p, p)
|
||||
|
||||
if index < 192 {
|
||||
wvalue = ((scalar[index/64] >> (index % 64)) + (scalar[index/64+1] << (64 - (index % 64)))) & 0x3f
|
||||
} else {
|
||||
wvalue = (scalar[index/64] >> (index % 64)) & 0x3f
|
||||
}
|
||||
|
||||
sel, sign = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(&t0, &precomp, sel)
|
||||
p256NegCond(&t0.y, sign)
|
||||
p256PointAddAsm(&t1, p, &t0)
|
||||
p256MovCond(&t1, &t1, p, sel)
|
||||
p256MovCond(p, &t1, &t0, zero)
|
||||
zero |= sel
|
||||
}
|
||||
|
||||
p256PointDoubleAsm(p, p)
|
||||
p256PointDoubleAsm(p, p)
|
||||
p256PointDoubleAsm(p, p)
|
||||
p256PointDoubleAsm(p, p)
|
||||
p256PointDoubleAsm(p, p)
|
||||
|
||||
wvalue = (scalar[0] << 1) & 0x3f
|
||||
sel, sign = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(&t0, &precomp, sel)
|
||||
p256NegCond(&t0.y, sign)
|
||||
p256PointAddAsm(&t1, p, &t0)
|
||||
p256MovCond(&t1, &t1, p, sel)
|
||||
p256MovCond(p, &t1, &t0, zero)
|
||||
}
|
135
internal/sm2ec/sm2p256_asm_test.go
Normal file
135
internal/sm2ec/sm2p256_asm_test.go
Normal file
@ -0,0 +1,135 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// fromBig converts a *big.Int into a format used by this code.
|
||||
func fromBig(out *p256Element, big *big.Int) {
|
||||
for i := range out {
|
||||
out[i] = 0
|
||||
}
|
||||
|
||||
for i, v := range big.Bits() {
|
||||
out[i] = uint64(v)
|
||||
}
|
||||
}
|
||||
|
||||
func toBigInt(in *p256Element) *big.Int {
|
||||
var valBytes [32]byte
|
||||
p256LittleToBig(&valBytes, in)
|
||||
return new(big.Int).SetBytes(valBytes[:])
|
||||
}
|
||||
|
||||
func p256MulTest(t *testing.T, x, y, p, r *big.Int) {
|
||||
x1 := new(big.Int).Mul(x, r)
|
||||
x1 = x1.Mod(x1, p)
|
||||
y1 := new(big.Int).Mul(y, r)
|
||||
y1 = y1.Mod(y1, p)
|
||||
ax := new(p256Element)
|
||||
ay := new(p256Element)
|
||||
res := new(p256Element)
|
||||
res2 := new(p256Element)
|
||||
fromBig(ax, x1)
|
||||
fromBig(ay, y1)
|
||||
p256Mul(res2, ax, ay)
|
||||
p256FromMont(res, res2)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
expected := new(big.Int).Mul(x, y)
|
||||
expected = expected.Mod(expected, p)
|
||||
if resInt.Cmp(expected) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyP256Mul(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
var scalar1 [32]byte
|
||||
var scalar2 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
io.ReadFull(rand.Reader, scalar2[:])
|
||||
x := new(big.Int).SetBytes(scalar1[:])
|
||||
y := new(big.Int).SetBytes(scalar2[:])
|
||||
p256MulTest(t, x, y, p, r)
|
||||
}
|
||||
}
|
||||
|
||||
func p256SqrTest(t *testing.T, x, p, r *big.Int) {
|
||||
x1 := new(big.Int).Mul(x, r)
|
||||
x1 = x1.Mod(x1, p)
|
||||
ax := new(p256Element)
|
||||
res := new(p256Element)
|
||||
res2 := new(p256Element)
|
||||
fromBig(ax, x1)
|
||||
p256Sqr(res2, ax, 1)
|
||||
p256FromMont(res, res2)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
expected := new(big.Int).Mul(x, x)
|
||||
expected = expected.Mod(expected, p)
|
||||
if resInt.Cmp(expected) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyP256Sqr(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
var scalar1 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
x := new(big.Int).SetBytes(scalar1[:])
|
||||
p256SqrTest(t, x, p, r)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256Inverse(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
gx := &p256Element{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
res := new(p256Element)
|
||||
p256Inverse(res, gx)
|
||||
resInt := toBigInt(res)
|
||||
xInv := new(big.Int).ModInverse(x, p)
|
||||
xInv = new(big.Int).Mul(xInv, r)
|
||||
xInv = new(big.Int).Mod(xInv, p)
|
||||
if resInt.Cmp(xInv) != 0 {
|
||||
t.Errorf("expected %v, got %v", hex.EncodeToString(xInv.Bytes()), hex.EncodeToString(resInt.Bytes()))
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = elliptic.P256()
|
||||
|
||||
func TestFuzz(t *testing.T) {
|
||||
p256 := P256()
|
||||
p256Generic := p256.Params()
|
||||
|
||||
var scalar1 [32]byte
|
||||
var scalar2 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
io.ReadFull(rand.Reader, scalar2[:])
|
||||
|
||||
x, y := p256.ScalarBaseMult(scalar1[:])
|
||||
x2, y2 := p256Generic.ScalarBaseMult(scalar1[:])
|
||||
|
||||
xx, yy := p256.ScalarMult(x, y, scalar2[:])
|
||||
xx2, yy2 := p256Generic.ScalarMult(x2, y2, scalar2[:])
|
||||
|
||||
if x.Cmp(x2) != 0 || y.Cmp(y2) != 0 {
|
||||
t.Fatalf("ScalarBaseMult does not match reference result with scalar: %x, please report this error to https://github.com/emmansun/gmsm/issues", scalar1)
|
||||
}
|
||||
|
||||
if xx.Cmp(xx2) != 0 || yy.Cmp(yy2) != 0 {
|
||||
t.Fatalf("ScalarMult does not match reference result with scalars: %x and %x, please report this error to https://github.com/emmansun/gmsm/issues", scalar1, scalar2)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/format"
|
||||
)
|
||||
|
||||
func GenTables() {
|
||||
buf := new(bytes.Buffer)
|
||||
fmt.Fprint(buf, `
|
||||
// Generated by gen_p256_table.go. DO NOT EDIT.
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
`[1:])
|
||||
|
||||
// Generate precomputed p256 tables.
|
||||
var pre [43][32 * 8]uint64
|
||||
basePoint := []uint64{
|
||||
0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05,
|
||||
0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd,
|
||||
0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000,
|
||||
}
|
||||
t1 := make([]uint64, 12)
|
||||
t2 := make([]uint64, 12)
|
||||
copy(t2, basePoint)
|
||||
zInv := make([]uint64, 4)
|
||||
zInvSq := make([]uint64, 4)
|
||||
for j := 0; j < 32; j++ {
|
||||
copy(t1, t2)
|
||||
for i := 0; i < 43; i++ {
|
||||
// The window size is 6 so we need to double 6 times.
|
||||
if i != 0 {
|
||||
for k := 0; k < 6; k++ {
|
||||
p256PointDoubleAsm(t1, t1)
|
||||
}
|
||||
}
|
||||
// Convert the point to affine form. (Its values are
|
||||
// still in Montgomery form however.)
|
||||
p256Inverse(zInv, t1[8:12])
|
||||
p256Sqr(zInvSq, zInv, 1)
|
||||
p256Mul(zInv, zInv, zInvSq)
|
||||
p256Mul(t1[:4], t1[:4], zInvSq)
|
||||
p256Mul(t1[4:8], t1[4:8], zInv)
|
||||
copy(t1[8:12], basePoint[8:12])
|
||||
// Update the table entry
|
||||
copy(pre[i][j*8:], t1[:8])
|
||||
}
|
||||
if j == 0 {
|
||||
p256PointDoubleAsm(t2, basePoint)
|
||||
} else {
|
||||
p256PointAddAsm(t2, t2, basePoint)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, "const p256Precomputed = \"\" +\n\n")
|
||||
|
||||
// Dump the precomputed tables, flattened, little-endian.
|
||||
// These tables are used directly by assembly on little-endian platforms.
|
||||
// Putting the data in a const string lets it be stored readonly.
|
||||
for i := range &pre {
|
||||
for j, v := range &pre[i] {
|
||||
fmt.Fprintf(buf, "\"")
|
||||
var u8 [8]byte
|
||||
binary.LittleEndian.PutUint64(u8[:], v)
|
||||
for _, b := range &u8 {
|
||||
fmt.Fprintf(buf, "\\x%02x", b)
|
||||
}
|
||||
fmt.Fprintf(buf, "\"")
|
||||
if i < len(pre)-1 || j < len(pre[i])-1 {
|
||||
fmt.Fprint(buf, "+")
|
||||
}
|
||||
if j%8 == 7 {
|
||||
fmt.Fprint(buf, "\n")
|
||||
}
|
||||
}
|
||||
fmt.Fprint(buf, "\n")
|
||||
}
|
||||
|
||||
src := buf.Bytes()
|
||||
fmtsrc, fmterr := format.Source(src)
|
||||
// If formatting failed, keep the original source for debugging.
|
||||
if fmterr == nil {
|
||||
src = fmtsrc
|
||||
}
|
||||
fmt.Println(string(src))
|
||||
}
|
579
sm2/p256_asm.go
579
sm2/p256_asm.go
@ -1,579 +0,0 @@
|
||||
// It is by standing on the shoulders of giants.
|
||||
|
||||
// This file contains the Go wrapper for the constant-time, 64-bit assembly
|
||||
// implementation of P256. The optimizations performed here are described in
|
||||
// detail in:
|
||||
// S.Gueron and V.Krasnov, "Fast prime field elliptic-curve cryptography with
|
||||
// 256-bit primes"
|
||||
// https://link.springer.com/article/10.1007%2Fs13389-014-0090-x
|
||||
// https://eprint.iacr.org/2013/816.pdf
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type (
|
||||
p256Curve struct {
|
||||
*elliptic.CurveParams
|
||||
}
|
||||
|
||||
p256Point struct {
|
||||
xyz [12]uint64
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
p256 p256Curve
|
||||
)
|
||||
|
||||
func initP256() {
|
||||
// 2**256 - 2**224 - 2**96 + 2**64 - 1
|
||||
p256.CurveParams = &elliptic.CurveParams{Name: "sm2p256v1"}
|
||||
p256.P, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
p256.N, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
p256.B, _ = new(big.Int).SetString("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
|
||||
p256.Gx, _ = new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
p256.Gy, _ = new(big.Int).SetString("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)
|
||||
p256.BitSize = 256
|
||||
}
|
||||
|
||||
func (curve p256Curve) Params() *elliptic.CurveParams {
|
||||
return curve.CurveParams
|
||||
}
|
||||
|
||||
// Functions implemented in p256_asm_*64.s
|
||||
// Montgomery multiplication modulo P256
|
||||
//go:noescape
|
||||
func p256Mul(res, in1, in2 []uint64)
|
||||
|
||||
// Montgomery square modulo P256, repeated n times (n >= 1)
|
||||
//go:noescape
|
||||
func p256Sqr(res, in []uint64, n int)
|
||||
|
||||
// Montgomery multiplication by 1
|
||||
//go:noescape
|
||||
func p256FromMont(res, in []uint64)
|
||||
|
||||
// iff cond == 1 val <- -val
|
||||
//go:noescape
|
||||
func p256NegCond(val []uint64, cond int)
|
||||
|
||||
// if cond == 0 res <- b; else res <- a
|
||||
//go:noescape
|
||||
func p256MovCond(res, a, b []uint64, cond int)
|
||||
|
||||
// Endianness swap
|
||||
//go:noescape
|
||||
func p256BigToLittle(res []uint64, in []byte)
|
||||
|
||||
//go:noescape
|
||||
func p256LittleToBig(res []byte, in []uint64)
|
||||
|
||||
// Constant time table access
|
||||
//go:noescape
|
||||
func p256Select(point, table []uint64, idx int)
|
||||
|
||||
//go:noescape
|
||||
func p256SelectBase(point *[12]uint64, table string, idx int)
|
||||
|
||||
// Montgomery multiplication modulo Ord(G)
|
||||
//go:noescape
|
||||
func p256OrdMul(res, in1, in2 []uint64)
|
||||
|
||||
// Montgomery square modulo Ord(G), repeated n times
|
||||
//go:noescape
|
||||
func p256OrdSqr(res, in []uint64, n int)
|
||||
|
||||
// Point add with in2 being affine point
|
||||
// If sign == 1 -> in2 = -in2
|
||||
// If sel == 0 -> res = in1
|
||||
// if zero == 0 -> res = in2
|
||||
//go:noescape
|
||||
func p256PointAddAffineAsm(res, in1, in2 []uint64, sign, sel, zero int)
|
||||
|
||||
// Point add. Returns one if the two input points were equal and zero
|
||||
// otherwise. (Note that, due to the way that the equations work out, some
|
||||
// representations of ∞ are considered equal to everything by this function.)
|
||||
//go:noescape
|
||||
func p256PointAddAsm(res, in1, in2 []uint64) int
|
||||
|
||||
// Point double
|
||||
//go:noescape
|
||||
func p256PointDoubleAsm(res, in []uint64)
|
||||
|
||||
var p256one = []uint64{0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000}
|
||||
|
||||
// Inverse, implements invertible interface, used by Sign()
|
||||
// n-2 =
|
||||
// 1111111111111111111111111111111011111111111111111111111111111111
|
||||
// 1111111111111111111111111111111111111111111111111111111111111111
|
||||
// 0111001000000011110111110110101100100001110001100000010100101011
|
||||
// 0101001110111011111101000000100100111001110101010100000100100001
|
||||
//
|
||||
func (curve p256Curve) Inverse(k *big.Int) *big.Int {
|
||||
if k.Sign() < 0 {
|
||||
// This should never happen.
|
||||
k = new(big.Int).Neg(k)
|
||||
}
|
||||
|
||||
if k.Cmp(p256.N) >= 0 {
|
||||
// This should never happen.
|
||||
k = new(big.Int).Mod(k, p256.N)
|
||||
}
|
||||
|
||||
// table will store precomputed powers of x.
|
||||
var table [4 * 10]uint64
|
||||
var (
|
||||
_1 = table[4*0 : 4*1]
|
||||
_11 = table[4*1 : 4*2]
|
||||
_101 = table[4*2 : 4*3]
|
||||
_111 = table[4*3 : 4*4]
|
||||
_1111 = table[4*4 : 4*5]
|
||||
_10101 = table[4*5 : 4*6]
|
||||
_101111 = table[4*6 : 4*7]
|
||||
x = table[4*7 : 4*8]
|
||||
t = table[4*8 : 4*9]
|
||||
m = table[4*9 : 4*10]
|
||||
)
|
||||
|
||||
fromBig(x[:], k)
|
||||
// This code operates in the Montgomery domain where R = 2^256 mod n
|
||||
// and n is the order of the scalar field. (See initP256 for the
|
||||
// value.) Elements in the Montgomery domain take the form a×R and
|
||||
// multiplication of x and y in the calculates (x × y × R^-1) mod n. RR
|
||||
// is R×R mod n thus the Montgomery multiplication x and RR gives x×R,
|
||||
// i.e. converts x into the Montgomery domain.
|
||||
// Window values borrowed from https://briansmith.org/ecc-inversion-addition-chains-01#p256_scalar_inversion
|
||||
RR := []uint64{0x901192af7c114f20, 0x3464504ade6fa2fa, 0x620fc84c3affe0d4, 0x1eb5e412a22b3d3b}
|
||||
|
||||
p256OrdMul(_1, x, RR) // _1 , 2^0
|
||||
p256OrdSqr(m, _1, 1) // _10, 2^1
|
||||
p256OrdMul(_11, m, _1) // _11, 2^1 + 2^0
|
||||
p256OrdMul(_101, m, _11) // _101, 2^2 + 2^0
|
||||
p256OrdMul(_111, m, _101) // _111, 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(x, _101, 1) // _1010, 2^3 + 2^1
|
||||
p256OrdMul(_1111, _101, x) // _1111, 2^3 + 2^2 + 2^1 + 2^0
|
||||
|
||||
p256OrdSqr(t, x, 1) // _10100, 2^4 + 2^2
|
||||
p256OrdMul(_10101, t, _1) // _10101, 2^4 + 2^2 + 2^0
|
||||
p256OrdSqr(x, _10101, 1) // _101010, 2^5 + 2^3 + 2^1
|
||||
p256OrdMul(_101111, _101, x) // _101111, 2^5 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdMul(x, _10101, x) // _111111 = x6, 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(t, x, 2) // _11111100, 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2
|
||||
|
||||
p256OrdMul(m, t, m) // _11111110 = x8, , 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1
|
||||
p256OrdMul(t, t, _11) // _11111111 = x8, , 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(x, t, 8) // _ff00, 2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8
|
||||
p256OrdMul(m, x, m) // _fffe
|
||||
p256OrdMul(x, x, t) // _ffff = x16, 2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8 + 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
|
||||
p256OrdSqr(t, x, 16) // _ffff0000, 2^31 + 2^30 + 2^29 + 2^28 + 2^27 + 2^26 + 2^25 + 2^24 + 2^23 + 2^22 + 2^21 + 2^20 + 2^19 + 2^18 + 2^17 + 2^16
|
||||
p256OrdMul(m, t, m) // _fffffffe
|
||||
p256OrdMul(t, t, x) // _ffffffff = x32
|
||||
|
||||
p256OrdSqr(x, m, 32) // _fffffffe00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffff
|
||||
p256OrdSqr(x, x, 32) // _fffffffeffffffff00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffffffffffff
|
||||
p256OrdSqr(x, x, 32) // _fffffffeffffffffffffffff00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffffffffffffffffffff
|
||||
|
||||
sqrs := []uint8{
|
||||
4, 3, 11, 5, 3, 5, 1,
|
||||
3, 7, 5, 9, 7, 5, 5,
|
||||
4, 5, 2, 2, 7, 3, 5,
|
||||
5, 6, 2, 6, 3, 5,
|
||||
}
|
||||
muls := [][]uint64{
|
||||
_111, _1, _1111, _1111, _101, _10101, _1,
|
||||
_1, _111, _11, _101, _10101, _10101, _111,
|
||||
_111, _1111, _11, _1, _1, _1, _111,
|
||||
_111, _10101, _1, _1, _1, _1}
|
||||
|
||||
for i, s := range sqrs {
|
||||
p256OrdSqr(x, x, int(s))
|
||||
p256OrdMul(x, x, muls[i])
|
||||
}
|
||||
|
||||
// Multiplying by one in the Montgomery domain converts a Montgomery
|
||||
// value out of the domain.
|
||||
one := []uint64{1, 0, 0, 0}
|
||||
p256OrdMul(x, x, one)
|
||||
|
||||
xOut := make([]byte, 32)
|
||||
p256LittleToBig(xOut, x)
|
||||
return new(big.Int).SetBytes(xOut)
|
||||
}
|
||||
|
||||
// fromBig converts a *big.Int into a format used by this code.
|
||||
func fromBig(out []uint64, big *big.Int) {
|
||||
for i := range out {
|
||||
out[i] = 0
|
||||
}
|
||||
|
||||
for i, v := range big.Bits() {
|
||||
out[i] = uint64(v)
|
||||
}
|
||||
}
|
||||
|
||||
// p256GetScalar endian-swaps the big-endian scalar value from in and writes it
|
||||
// to out. If the scalar is equal or greater than the order of the group, it's
|
||||
// reduced modulo that order.
|
||||
func p256GetScalar(out []uint64, in []byte) {
|
||||
n := new(big.Int).SetBytes(in)
|
||||
|
||||
if n.Cmp(p256.N) >= 0 {
|
||||
n.Mod(n, p256.N)
|
||||
}
|
||||
fromBig(out, n)
|
||||
}
|
||||
|
||||
// p256Mul operates in a Montgomery domain with R = 2^256 mod p, where p is the
|
||||
// underlying field of the curve. (See initP256 for the value.) Thus rr here is
|
||||
// R×R mod p. See comment in Inverse about how this is used.
|
||||
var rr = []uint64{0x200000003, 0x2ffffffff, 0x100000001, 0x400000002}
|
||||
|
||||
func maybeReduceModP(in *big.Int) *big.Int {
|
||||
if in.Cmp(p256.P) < 0 {
|
||||
return in
|
||||
}
|
||||
return new(big.Int).Mod(in, p256.P)
|
||||
}
|
||||
|
||||
func (curve p256Curve) CombinedMult(bigX, bigY *big.Int, baseScalar, scalar []byte) (x, y *big.Int) {
|
||||
scalarReversed := make([]uint64, 4)
|
||||
var r1, r2 p256Point
|
||||
p256GetScalar(scalarReversed, baseScalar)
|
||||
r1IsInfinity := scalarIsZero(scalarReversed)
|
||||
r1.p256BaseMult(scalarReversed)
|
||||
|
||||
p256GetScalar(scalarReversed, scalar)
|
||||
r2IsInfinity := scalarIsZero(scalarReversed)
|
||||
fromBig(r2.xyz[0:4], maybeReduceModP(bigX))
|
||||
fromBig(r2.xyz[4:8], maybeReduceModP(bigY))
|
||||
p256Mul(r2.xyz[0:4], r2.xyz[0:4], rr[:])
|
||||
p256Mul(r2.xyz[4:8], r2.xyz[4:8], rr[:])
|
||||
|
||||
// This sets r2's Z value to 1, in the Montgomery domain.
|
||||
r2.xyz[8] = p256one[0]
|
||||
r2.xyz[9] = p256one[1]
|
||||
r2.xyz[10] = p256one[2]
|
||||
r2.xyz[11] = p256one[3]
|
||||
|
||||
r2.p256ScalarMult(scalarReversed)
|
||||
|
||||
var sum, double p256Point
|
||||
pointsEqual := p256PointAddAsm(sum.xyz[:], r1.xyz[:], r2.xyz[:])
|
||||
p256PointDoubleAsm(double.xyz[:], r1.xyz[:])
|
||||
sum.CopyConditional(&double, pointsEqual)
|
||||
sum.CopyConditional(&r1, r2IsInfinity)
|
||||
sum.CopyConditional(&r2, r1IsInfinity)
|
||||
|
||||
return sum.p256PointToAffine()
|
||||
}
|
||||
|
||||
func (curve p256Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
|
||||
scalarReversed := make([]uint64, 4)
|
||||
p256GetScalar(scalarReversed, scalar)
|
||||
|
||||
var r p256Point
|
||||
r.p256BaseMult(scalarReversed)
|
||||
return r.p256PointToAffine()
|
||||
}
|
||||
|
||||
func (curve p256Curve) ScalarMult(bigX, bigY *big.Int, scalar []byte) (x, y *big.Int) {
|
||||
scalarReversed := make([]uint64, 4)
|
||||
p256GetScalar(scalarReversed, scalar)
|
||||
|
||||
var r p256Point
|
||||
fromBig(r.xyz[0:4], maybeReduceModP(bigX))
|
||||
fromBig(r.xyz[4:8], maybeReduceModP(bigY))
|
||||
p256Mul(r.xyz[0:4], r.xyz[0:4], rr[:])
|
||||
p256Mul(r.xyz[4:8], r.xyz[4:8], rr[:])
|
||||
// This sets r2's Z value to 1, in the Montgomery domain.
|
||||
r.xyz[8] = p256one[0]
|
||||
r.xyz[9] = p256one[1]
|
||||
r.xyz[10] = p256one[2]
|
||||
r.xyz[11] = p256one[3]
|
||||
|
||||
r.p256ScalarMult(scalarReversed)
|
||||
return r.p256PointToAffine()
|
||||
}
|
||||
|
||||
// uint64IsZero returns 1 if x is zero and zero otherwise.
|
||||
func uint64IsZero(x uint64) int {
|
||||
x = ^x
|
||||
x &= x >> 32
|
||||
x &= x >> 16
|
||||
x &= x >> 8
|
||||
x &= x >> 4
|
||||
x &= x >> 2
|
||||
x &= x >> 1
|
||||
return int(x & 1)
|
||||
}
|
||||
|
||||
// scalarIsZero returns 1 if scalar represents the zero value, and zero
|
||||
// otherwise.
|
||||
func scalarIsZero(scalar []uint64) int {
|
||||
return uint64IsZero(scalar[0] | scalar[1] | scalar[2] | scalar[3])
|
||||
}
|
||||
|
||||
func (p *p256Point) p256PointToAffine() (x, y *big.Int) {
|
||||
zInv := make([]uint64, 4)
|
||||
zInvSq := make([]uint64, 4)
|
||||
p256Inverse(zInv, p.xyz[8:12])
|
||||
p256Sqr(zInvSq, zInv, 1)
|
||||
p256Mul(zInv, zInv, zInvSq)
|
||||
|
||||
p256Mul(zInvSq, p.xyz[0:4], zInvSq)
|
||||
p256Mul(zInv, p.xyz[4:8], zInv)
|
||||
|
||||
p256FromMont(zInvSq, zInvSq)
|
||||
p256FromMont(zInv, zInv)
|
||||
|
||||
xOut := make([]byte, 32)
|
||||
yOut := make([]byte, 32)
|
||||
p256LittleToBig(xOut, zInvSq)
|
||||
p256LittleToBig(yOut, zInv)
|
||||
|
||||
return new(big.Int).SetBytes(xOut), new(big.Int).SetBytes(yOut)
|
||||
}
|
||||
|
||||
// CopyConditional copies overwrites p with src if v == 1, and leaves p
|
||||
// unchanged if v == 0.
|
||||
func (p *p256Point) CopyConditional(src *p256Point, v int) {
|
||||
pMask := uint64(v) - 1
|
||||
srcMask := ^pMask
|
||||
|
||||
for i, n := range p.xyz {
|
||||
p.xyz[i] = (n & pMask) | (src.xyz[i] & srcMask)
|
||||
}
|
||||
}
|
||||
|
||||
// p256Inverse sets out to in^-1 mod p.
|
||||
func p256Inverse(out, in []uint64) {
|
||||
// Inversion is calculated through exponentiation by p - 2, per Fermat's
|
||||
// little theorem.
|
||||
//
|
||||
// The sequence of 14 multiplications and 255 squarings is derived from the
|
||||
// following addition chain generated with github.com/mmcloughlin/addchain
|
||||
// v0.4.0.
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _110 = 2*_11
|
||||
// _111 = 1 + _110
|
||||
// _111000 = _111 << 3
|
||||
// _111111 = _111 + _111000
|
||||
// _1111110 = 2*_111111
|
||||
// _1111111 = 1 + _1111110
|
||||
// x12 = _1111110 << 5 + _111111
|
||||
// x24 = x12 << 12 + x12
|
||||
// x31 = x24 << 7 + _1111111
|
||||
// i39 = x31 << 2
|
||||
// i68 = i39 << 29
|
||||
// x62 = x31 + i68
|
||||
// i71 = i68 << 2
|
||||
// x64 = i39 + i71 + _11
|
||||
// i265 = ((i71 << 32 + x64) << 64 + x64) << 94
|
||||
// return (x62 + i265) << 2 + 1
|
||||
var stack [3 * 4]uint64
|
||||
t0 := stack[4*0 : 4*0+4]
|
||||
t1 := stack[4*1 : 4*1+4]
|
||||
t2 := stack[4*2 : 4*2+4]
|
||||
|
||||
p256Sqr(out, in, 1)
|
||||
p256Mul(t0, in, out)
|
||||
p256Sqr(out, t0, 1)
|
||||
p256Mul(out, in, out)
|
||||
p256Sqr(t1, out, 3)
|
||||
p256Mul(t1, out, t1)
|
||||
p256Sqr(t2, t1, 1)
|
||||
p256Mul(out, in, t2)
|
||||
p256Sqr(t2, t2, 5)
|
||||
p256Mul(t1, t1, t2)
|
||||
p256Sqr(t2, t1, 12)
|
||||
p256Mul(t1, t1, t2)
|
||||
p256Sqr(t1, t1, 7)
|
||||
p256Mul(out, out, t1)
|
||||
p256Sqr(t2, out, 2)
|
||||
p256Sqr(t1, t2, 29)
|
||||
p256Mul(out, out, t1)
|
||||
p256Sqr(t1, t1, 2)
|
||||
p256Mul(t2, t2, t1)
|
||||
p256Mul(t0, t0, t2)
|
||||
p256Sqr(t1, t1, 32)
|
||||
p256Mul(t1, t0, t1)
|
||||
p256Sqr(t1, t1, 64)
|
||||
p256Mul(t0, t0, t1)
|
||||
p256Sqr(t0, t0, 94)
|
||||
p256Mul(out, out, t0)
|
||||
p256Sqr(out, out, 2)
|
||||
p256Mul(out, in, out)
|
||||
}
|
||||
|
||||
func (p *p256Point) p256StorePoint(r *[16 * 4 * 3]uint64, index int) {
|
||||
copy(r[index*12:], p.xyz[:])
|
||||
}
|
||||
|
||||
// This function takes those six bits as an integer (0 .. 63), writing the
|
||||
// recoded digit to *sign (0 for positive, 1 for negative) and *digit (absolute
|
||||
// value, in the range 0 .. 16). Note that this integer essentially provides
|
||||
// the input bits "shifted to the left" by one position: for example, the input
|
||||
// to compute the least significant recoded digit, given that there's no bit
|
||||
// b_-1, has to be b_4 b_3 b_2 b_1 b_0 0.
|
||||
//
|
||||
// Reference:
|
||||
// https://github.com/openssl/openssl/blob/master/crypto/ec/ecp_nistputil.c
|
||||
// https://github.com/google/boringssl/blob/master/crypto/fipsmodule/ec/util.c
|
||||
func boothW5(in uint) (int, int) {
|
||||
var s uint = ^((in >> 5) - 1) // sets all bits to MSB(in), 'in' seen as 6-bit value
|
||||
var d uint = (1 << 6) - in - 1 // d = 63 - in, or d = ^in & 0x3f
|
||||
d = (d & s) | (in & (^s)) // d = in if in < 2^5; otherwise, d = 63 - in
|
||||
d = (d >> 1) + (d & 1) // d = (d + 1) / 2
|
||||
return int(d), int(s & 1)
|
||||
}
|
||||
|
||||
func boothW6(in uint) (int, int) {
|
||||
var s uint = ^((in >> 6) - 1)
|
||||
var d uint = (1 << 7) - in - 1
|
||||
d = (d & s) | (in & (^s))
|
||||
d = (d >> 1) + (d & 1)
|
||||
return int(d), int(s & 1)
|
||||
}
|
||||
|
||||
func (p *p256Point) p256BaseMult(scalar []uint64) {
|
||||
wvalue := (scalar[0] << 1) & 0x7f
|
||||
sel, sign := boothW6(uint(wvalue))
|
||||
p256SelectBase(&p.xyz, p256Precomputed, sel)
|
||||
p256NegCond(p.xyz[4:8], sign)
|
||||
|
||||
// (This is one, in the Montgomery domain.)
|
||||
p.xyz[8] = p256one[0]
|
||||
p.xyz[9] = p256one[1]
|
||||
p.xyz[10] = p256one[2]
|
||||
p.xyz[11] = p256one[3]
|
||||
|
||||
var t0 p256Point
|
||||
// (This is one, in the Montgomery domain.)
|
||||
t0.xyz[8] = p256one[0]
|
||||
t0.xyz[9] = p256one[1]
|
||||
t0.xyz[10] = p256one[2]
|
||||
t0.xyz[11] = p256one[3]
|
||||
|
||||
index := uint(5)
|
||||
zero := sel
|
||||
|
||||
for i := 1; i < 43; i++ {
|
||||
if index < 192 {
|
||||
wvalue = ((scalar[index/64] >> (index % 64)) + (scalar[index/64+1] << (64 - (index % 64)))) & 0x7f
|
||||
} else {
|
||||
wvalue = (scalar[index/64] >> (index % 64)) & 0x7f
|
||||
}
|
||||
index += 6
|
||||
sel, sign = boothW6(uint(wvalue))
|
||||
p256SelectBase(&t0.xyz, p256Precomputed[i*32*8*8:], sel)
|
||||
p256PointAddAffineAsm(p.xyz[0:12], p.xyz[0:12], t0.xyz[0:8], sign, sel, zero)
|
||||
zero |= sel
|
||||
}
|
||||
}
|
||||
|
||||
func (p *p256Point) p256ScalarMult(scalar []uint64) {
|
||||
// precomp is a table of precomputed points that stores powers of p
|
||||
// from p^1 to p^16.
|
||||
var precomp [16 * 4 * 3]uint64
|
||||
var t0, t1, t2, t3 p256Point
|
||||
|
||||
// Prepare the table
|
||||
p.p256StorePoint(&precomp, 0) // 1
|
||||
|
||||
p256PointDoubleAsm(t0.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(t1.xyz[:], t0.xyz[:])
|
||||
p256PointDoubleAsm(t2.xyz[:], t1.xyz[:])
|
||||
p256PointDoubleAsm(t3.xyz[:], t2.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 1) // 2
|
||||
t1.p256StorePoint(&precomp, 3) // 4
|
||||
t2.p256StorePoint(&precomp, 7) // 8
|
||||
t3.p256StorePoint(&precomp, 15) // 16
|
||||
|
||||
p256PointAddAsm(t0.xyz[:], t0.xyz[:], p.xyz[:])
|
||||
p256PointAddAsm(t1.xyz[:], t1.xyz[:], p.xyz[:])
|
||||
p256PointAddAsm(t2.xyz[:], t2.xyz[:], p.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 2) // 3
|
||||
t1.p256StorePoint(&precomp, 4) // 5
|
||||
t2.p256StorePoint(&precomp, 8) // 9
|
||||
|
||||
p256PointDoubleAsm(t0.xyz[:], t0.xyz[:])
|
||||
p256PointDoubleAsm(t1.xyz[:], t1.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 5) // 6
|
||||
t1.p256StorePoint(&precomp, 9) // 10
|
||||
|
||||
p256PointAddAsm(t2.xyz[:], t0.xyz[:], p.xyz[:])
|
||||
p256PointAddAsm(t1.xyz[:], t1.xyz[:], p.xyz[:])
|
||||
t2.p256StorePoint(&precomp, 6) // 7
|
||||
t1.p256StorePoint(&precomp, 10) // 11
|
||||
|
||||
p256PointDoubleAsm(t0.xyz[:], t0.xyz[:])
|
||||
p256PointDoubleAsm(t2.xyz[:], t2.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 11) // 12
|
||||
t2.p256StorePoint(&precomp, 13) // 14
|
||||
|
||||
p256PointAddAsm(t0.xyz[:], t0.xyz[:], p.xyz[:])
|
||||
p256PointAddAsm(t2.xyz[:], t2.xyz[:], p.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 12) // 13
|
||||
t2.p256StorePoint(&precomp, 14) // 15
|
||||
|
||||
// Start scanning the window from top bit
|
||||
index := uint(254)
|
||||
var sel, sign int
|
||||
|
||||
wvalue := (scalar[index/64] >> (index % 64)) & 0x3f
|
||||
sel, _ = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(p.xyz[0:12], precomp[0:], sel)
|
||||
zero := sel
|
||||
|
||||
for index > 4 {
|
||||
index -= 5
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
|
||||
if index < 192 {
|
||||
wvalue = ((scalar[index/64] >> (index % 64)) + (scalar[index/64+1] << (64 - (index % 64)))) & 0x3f
|
||||
} else {
|
||||
wvalue = (scalar[index/64] >> (index % 64)) & 0x3f
|
||||
}
|
||||
|
||||
sel, sign = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(t0.xyz[0:], precomp[0:], sel)
|
||||
p256NegCond(t0.xyz[4:8], sign)
|
||||
p256PointAddAsm(t1.xyz[:], p.xyz[:], t0.xyz[:])
|
||||
p256MovCond(t1.xyz[0:12], t1.xyz[0:12], p.xyz[0:12], sel)
|
||||
p256MovCond(p.xyz[0:12], t1.xyz[0:12], t0.xyz[0:12], zero)
|
||||
zero |= sel
|
||||
}
|
||||
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
|
||||
wvalue = (scalar[0] << 1) & 0x3f
|
||||
sel, sign = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(t0.xyz[0:], precomp[0:], sel)
|
||||
p256NegCond(t0.xyz[4:8], sign)
|
||||
p256PointAddAsm(t1.xyz[:], p.xyz[:], t0.xyz[:])
|
||||
p256MovCond(t1.xyz[0:12], t1.xyz[0:12], p.xyz[0:12], sel)
|
||||
p256MovCond(p.xyz[0:12], t1.xyz[0:12], t0.xyz[0:12], zero)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,65 +0,0 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestP256PrecomputedTable(t *testing.T) {
|
||||
|
||||
basePoint := []uint64{
|
||||
0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05,
|
||||
0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd,
|
||||
0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000,
|
||||
}
|
||||
t1 := make([]uint64, 12)
|
||||
t2 := make([]uint64, 12)
|
||||
copy(t2, basePoint)
|
||||
|
||||
zInv := make([]uint64, 4)
|
||||
zInvSq := make([]uint64, 4)
|
||||
for j := 0; j < 32; j++ {
|
||||
copy(t1, t2)
|
||||
for i := 0; i < 43; i++ {
|
||||
// The window size is 6 so we need to double 6 times.
|
||||
if i != 0 {
|
||||
for k := 0; k < 6; k++ {
|
||||
p256PointDoubleAsm(t1, t1)
|
||||
}
|
||||
}
|
||||
// Convert the point to affine form. (Its values are
|
||||
// still in Montgomery form however.)
|
||||
p256Inverse(zInv, t1[8:12])
|
||||
p256Sqr(zInvSq, zInv, 1)
|
||||
p256Mul(zInv, zInv, zInvSq)
|
||||
|
||||
p256Mul(t1[:4], t1[:4], zInvSq)
|
||||
p256Mul(t1[4:8], t1[4:8], zInv)
|
||||
|
||||
copy(t1[8:12], basePoint[8:12])
|
||||
|
||||
buf := make([]byte, 8*8)
|
||||
for i, u := range t1[:8] {
|
||||
binary.LittleEndian.PutUint64(buf[i*8:i*8+8], u)
|
||||
}
|
||||
start := i*32*8*8 + j*8*8
|
||||
if got, want := p256Precomputed[start:start+64], string(buf); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("Unexpected table entry at [%d][%d:%d]: got %v, want %v", i, j*8, (j*8)+8, got, want)
|
||||
}
|
||||
}
|
||||
if j == 0 {
|
||||
p256PointDoubleAsm(t2, basePoint)
|
||||
} else {
|
||||
p256PointAddAsm(t2, t2, basePoint)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,392 +0,0 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func toBigInt(in []uint64) *big.Int {
|
||||
var valBytes = make([]byte, 32)
|
||||
p256LittleToBig(valBytes, in)
|
||||
return new(big.Int).SetBytes(valBytes)
|
||||
}
|
||||
|
||||
// ordk0 = -n^(-1) mod 2^64
|
||||
func Test_p256ordk0(t *testing.T) {
|
||||
n, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
p, _ := new(big.Int).SetString("10000000000000000", 16) // 2^64
|
||||
n = n.ModInverse(n, p)
|
||||
n = n.Neg(n)
|
||||
n = n.Mod(n, p)
|
||||
if "327f9e8872350975" != hex.EncodeToString(n.Bytes()) {
|
||||
t.Failed()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256NegCond(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
var val = []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
bigVal := toBigInt(val)
|
||||
|
||||
p256NegCond(val, 0)
|
||||
bigVal1 := toBigInt(val)
|
||||
if bigVal.Cmp(bigVal1) != 0 {
|
||||
t.Fatal("should be same")
|
||||
}
|
||||
p256NegCond(val, 1)
|
||||
bigVal1 = toBigInt(val)
|
||||
if bigVal.Cmp(bigVal1) == 0 {
|
||||
t.Fatal("should be different")
|
||||
}
|
||||
bigVal2 := new(big.Int).Sub(p, bigVal)
|
||||
if bigVal2.Cmp(bigVal1) != 0 {
|
||||
t.Fatal("should be same")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256FromMont(t *testing.T) {
|
||||
res := make([]uint64, 4)
|
||||
p256FromMont(res, []uint64{0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000})
|
||||
res1 := (res[0] ^ 0x0000000000000001) | res[1] | res[2] | res[3]
|
||||
if res1 != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
x1 := make([]uint64, 4)
|
||||
p256BigToLittle(x1, x.Bytes())
|
||||
|
||||
p256FromMont(res, []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05})
|
||||
if (res[0]^x1[0])|(res[1]^x1[1])|(res[2]^x1[2])|(res[3]^x1[3]) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256Sqr(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
one := []uint64{0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000}
|
||||
res := make([]uint64, 4)
|
||||
p256Sqr(res, one, 2)
|
||||
if (res[0]^one[0])|(res[1]^one[1])|(res[2]^one[2])|(res[3]^one[3]) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
gx := []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
p256Sqr(res, gx, 2)
|
||||
resInt := toBigInt(res)
|
||||
gxsqr := new(big.Int).Mul(x, x)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, p)
|
||||
gxsqr = new(big.Int).Mul(gxsqr, gxsqr)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, p)
|
||||
gxsqr = new(big.Int).Mul(gxsqr, r)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, p)
|
||||
if resInt.Cmp(gxsqr) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256Mul(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
y, _ := new(big.Int).SetString("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)
|
||||
res := make([]uint64, 4)
|
||||
gx := []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
gy := []uint64{0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd}
|
||||
|
||||
p256Mul(res, gx, gy)
|
||||
resInt := toBigInt(res)
|
||||
xmy := new(big.Int).Mul(x, y)
|
||||
xmy = new(big.Int).Mod(xmy, p)
|
||||
xmy = new(big.Int).Mul(xmy, r)
|
||||
xmy = new(big.Int).Mod(xmy, p)
|
||||
if resInt.Cmp(xmy) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func p256SqrTest(t *testing.T, x, p, r *big.Int) {
|
||||
x1 := new(big.Int).Mul(x, r)
|
||||
x1 = x1.Mod(x1, p)
|
||||
ax := make([]uint64, 4)
|
||||
res := make([]uint64, 4)
|
||||
res2 := make([]uint64, 4)
|
||||
fromBig(ax, x1)
|
||||
p256Sqr(res2, ax, 1)
|
||||
p256FromMont(res, res2)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
expected := new(big.Int).Mul(x, x)
|
||||
expected = expected.Mod(expected, p)
|
||||
if resInt.Cmp(expected) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyP256Sqr(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
var scalar1 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
x := new(big.Int).SetBytes(scalar1[:])
|
||||
p256SqrTest(t, x, p, r)
|
||||
}
|
||||
}
|
||||
|
||||
func p256MulTest(t *testing.T, x, y, p, r *big.Int) {
|
||||
x1 := new(big.Int).Mul(x, r)
|
||||
x1 = x1.Mod(x1, p)
|
||||
y1 := new(big.Int).Mul(y, r)
|
||||
y1 = y1.Mod(y1, p)
|
||||
ax := make([]uint64, 4)
|
||||
ay := make([]uint64, 4)
|
||||
res := make([]uint64, 4)
|
||||
res2 := make([]uint64, 4)
|
||||
fromBig(ax, x1)
|
||||
fromBig(ay, y1)
|
||||
p256Mul(res2, ax, ay)
|
||||
p256FromMont(res, res2)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
expected := new(big.Int).Mul(x, y)
|
||||
expected = expected.Mod(expected, p)
|
||||
if resInt.Cmp(expected) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyP256Mul(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
var scalar1 [32]byte
|
||||
var scalar2 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
io.ReadFull(rand.Reader, scalar2[:])
|
||||
x := new(big.Int).SetBytes(scalar1[:])
|
||||
y := new(big.Int).SetBytes(scalar2[:])
|
||||
p256MulTest(t, x, y, p, r)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256MulSqr(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
res := make([]uint64, 4)
|
||||
gx := []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
|
||||
p256Sqr(res, gx, 32)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
p256Mul(res, gx, gx)
|
||||
for i := 0; i < 31; i++ {
|
||||
p256Mul(res, res, res)
|
||||
}
|
||||
resInt1 := toBigInt(res)
|
||||
|
||||
resInt2 := new(big.Int).Mod(x, p)
|
||||
|
||||
for i := 0; i < 32; i++ {
|
||||
resInt2 = new(big.Int).Mul(resInt2, resInt2)
|
||||
resInt2 = new(big.Int).Mod(resInt2, p)
|
||||
}
|
||||
resInt2 = new(big.Int).Mul(resInt2, r)
|
||||
resInt2 = new(big.Int).Mod(resInt2, p)
|
||||
|
||||
if resInt.Cmp(resInt2) != 0 || resInt1.Cmp(resInt2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256OrdSqr(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
n, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
gx := make([]uint64, 4)
|
||||
res := make([]uint64, 4)
|
||||
xm := new(big.Int).Mul(x, r)
|
||||
xm = new(big.Int).Mod(xm, n)
|
||||
p256BigToLittle(gx, xm.Bytes())
|
||||
p256OrdMul(res, gx, gx)
|
||||
resInt := toBigInt(res)
|
||||
gxsqr := new(big.Int).Mul(x, x)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, n)
|
||||
gxsqr = new(big.Int).Mul(gxsqr, r)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, n)
|
||||
if resInt.Cmp(gxsqr) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
p256OrdSqr(res, gx, 1)
|
||||
resInt = toBigInt(res)
|
||||
if resInt.Cmp(gxsqr) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256Inverse(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
gx := []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
res := make([]uint64, 4)
|
||||
p256Inverse(res, gx)
|
||||
resInt := toBigInt(res)
|
||||
xInv := new(big.Int).ModInverse(x, p)
|
||||
xInv = new(big.Int).Mul(xInv, r)
|
||||
xInv = new(big.Int).Mod(xInv, p)
|
||||
if resInt.Cmp(xInv) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256PointAddAsm_basepoint(t *testing.T) {
|
||||
curve1 := P256()
|
||||
params := curve1.Params()
|
||||
basePoint := []uint64{
|
||||
0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05,
|
||||
0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd,
|
||||
0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000,
|
||||
}
|
||||
in := make([]uint64, 12)
|
||||
res := make([]uint64, 12)
|
||||
copy(in, basePoint)
|
||||
p256PointDoubleAsm(res, in)
|
||||
p256PointAddAsm(res, res, in)
|
||||
var r p256Point
|
||||
copy(r.xyz[:], res)
|
||||
x1, y1 := r.p256PointToAffine()
|
||||
x2, y2 := params.Double(params.Gx, params.Gy)
|
||||
x2, y2 = params.Add(params.Gx, params.Gy, x2, y2)
|
||||
if x1.Cmp(x2) != 0 || y1.Cmp(y2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256PointDoubleAsm(t *testing.T) {
|
||||
basePoint := []uint64{
|
||||
0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05,
|
||||
0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd,
|
||||
0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000,
|
||||
}
|
||||
t1 := make([]uint64, 12)
|
||||
copy(t1, basePoint)
|
||||
for i := 0; i < 16; i++ {
|
||||
p256PointDoubleAsm(t1, t1)
|
||||
}
|
||||
var r p256Point
|
||||
copy(r.xyz[:], t1)
|
||||
x1, y1 := r.p256PointToAffine()
|
||||
curve1 := P256()
|
||||
params := curve1.Params()
|
||||
x2, y2 := params.Double(params.Gx, params.Gy)
|
||||
for i := 0; i < 15; i++ {
|
||||
x2, y2 = params.Double(x2, y2)
|
||||
}
|
||||
if x1.Cmp(x2) != 0 || y1.Cmp(y2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ScalarBaseMult(t *testing.T) {
|
||||
scalar := big.NewInt(0xffffffff)
|
||||
curve1 := P256()
|
||||
x1, y1 := curve1.ScalarBaseMult(scalar.Bytes())
|
||||
params := curve1.Params()
|
||||
x2, y2 := params.ScalarBaseMult(scalar.Bytes())
|
||||
if x1.Cmp(x2) != 0 || y1.Cmp(y2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256PointAddAsm(t *testing.T) {
|
||||
curve1 := P256()
|
||||
params := curve1.Params()
|
||||
k1, _ := randFieldElement(params, rand.Reader)
|
||||
x1, y1 := params.ScalarBaseMult(k1.Bytes())
|
||||
k2, _ := randFieldElement(params, rand.Reader)
|
||||
x2, y2 := params.ScalarBaseMult(k2.Bytes())
|
||||
x3, y3 := params.Add(x1, y1, x2, y2)
|
||||
var in1, in2, rp p256Point
|
||||
fromBig(in1.xyz[0:4], maybeReduceModP(x1))
|
||||
fromBig(in1.xyz[4:8], maybeReduceModP(y1))
|
||||
fromBig(in2.xyz[0:4], maybeReduceModP(x2))
|
||||
fromBig(in2.xyz[4:8], maybeReduceModP(y2))
|
||||
in1.xyz[8] = 0x0000000000000001
|
||||
in1.xyz[9] = 0x00000000ffffffff
|
||||
in1.xyz[10] = 0x0000000000000000
|
||||
in1.xyz[11] = 0x0000000100000000
|
||||
in2.xyz[8] = 0x0000000000000001
|
||||
in2.xyz[9] = 0x00000000ffffffff
|
||||
in2.xyz[10] = 0x0000000000000000
|
||||
in2.xyz[11] = 0x0000000100000000
|
||||
p256Mul(in1.xyz[0:4], in1.xyz[0:4], rr[:])
|
||||
p256Mul(in1.xyz[4:8], in1.xyz[4:8], rr[:])
|
||||
p256Mul(in2.xyz[0:4], in2.xyz[0:4], rr[:])
|
||||
p256Mul(in2.xyz[4:8], in2.xyz[4:8], rr[:])
|
||||
res := make([]uint64, 12)
|
||||
n := p256PointAddAsm(res, in1.xyz[:], in2.xyz[:])
|
||||
copy(rp.xyz[:], res)
|
||||
x4, y4 := rp.p256PointToAffine()
|
||||
if n == 0 && (x3.Cmp(x4) != 0 || y3.Cmp(y4) != 0) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ScalarMult_basepoint(t *testing.T) {
|
||||
scalar := big.NewInt(0xffffffff)
|
||||
curve1 := P256()
|
||||
x1, y1 := curve1.ScalarMult(curve1.Params().Gx, curve1.Params().Gy, scalar.Bytes())
|
||||
params := curve1.Params()
|
||||
x2, y2 := params.ScalarMult(curve1.Params().Gx, curve1.Params().Gy, scalar.Bytes())
|
||||
if x1.Cmp(x2) != 0 || y1.Cmp(y2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Inverse(t *testing.T) {
|
||||
n, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
nm2 := new(big.Int).Sub(n, big.NewInt(2))
|
||||
nm2a := make([]uint64, 4)
|
||||
fromBig(nm2a, nm2)
|
||||
xInv1 := fermatInverse(x, n)
|
||||
_ = P256()
|
||||
xInv2 := p256.Inverse(x)
|
||||
|
||||
if xInv1.Cmp(xInv2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
293
sm2/p256_test.go
293
sm2/p256_test.go
@ -1,293 +0,0 @@
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type baseMultTest struct {
|
||||
k string
|
||||
}
|
||||
|
||||
var baseMultTests = []baseMultTest{
|
||||
{
|
||||
"112233445566778899",
|
||||
},
|
||||
{
|
||||
"112233445566778899112233445566778899",
|
||||
},
|
||||
{
|
||||
"6950511619965839450988900688150712778015737983940691968051900319680",
|
||||
},
|
||||
{
|
||||
"13479972933410060327035789020509431695094902435494295338570602119423",
|
||||
},
|
||||
{
|
||||
"13479971751745682581351455311314208093898607229429740618390390702079",
|
||||
},
|
||||
{
|
||||
"13479972931865328106486971546324465392952975980343228160962702868479",
|
||||
},
|
||||
{
|
||||
"11795773708834916026404142434151065506931607341523388140225443265536",
|
||||
},
|
||||
{
|
||||
"784254593043826236572847595991346435467177662189391577090",
|
||||
},
|
||||
{
|
||||
"13479767645505654746623887797783387853576174193480695826442858012671",
|
||||
},
|
||||
{
|
||||
"205688069665150753842126177372015544874550518966168735589597183",
|
||||
},
|
||||
{
|
||||
"13479966930919337728895168462090683249159702977113823384618282123295",
|
||||
},
|
||||
{
|
||||
"50210731791415612487756441341851895584393717453129007497216",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368041",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368042",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368043",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368044",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368045",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368046",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368047",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368048",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368049",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368050",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368051",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368052",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368053",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368054",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368055",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368056",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368057",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368058",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368059",
|
||||
},
|
||||
{
|
||||
"26959946667150639794667015087019625940457807714424391721682722368060",
|
||||
},
|
||||
}
|
||||
|
||||
type scalarMultTest struct {
|
||||
k string
|
||||
xIn, yIn string
|
||||
xOut, yOut string
|
||||
}
|
||||
|
||||
var p256MultTests = []scalarMultTest{
|
||||
{
|
||||
"9e9e0dfa7b29bd78a381e5ad3c3ef3154080bf8198b4f6d4dc4b13a04e49a979",
|
||||
"0a5351c475d8f8c5dab77b688b17fa1d6f2a9aed187b3a6cb670647c1a1b2369",
|
||||
"aba5ace91a313f0d4468a44f66617e7f497f3508c6f2c0273dc6c133c9a59df0",
|
||||
"f64634b9eb2b0feb5bfdcb882a365041437da717dfb4156e7b3f3b22784889a9",
|
||||
"84d36430a453396b047494e6a74c43abf193c13ce17dd60b614b22de97139d09",
|
||||
},
|
||||
{
|
||||
"dd242eb66c7be62f2d3173185b6875f66d0d0bc75df8900c69d48630ef60faff",
|
||||
"0f5e36b3eaa03868bccfd0f7e5f0189ee5d58b0946420ee0672797620f4856df",
|
||||
"35032c2d743a0df6d838b01034402db85d3ad4b07f316612cfc8902434dedd29",
|
||||
"1ffe871e928012e14dfad0ec1d54a8198c6830dd283703a42c21f2367c72d10f",
|
||||
"fbf401d0729d2b38a925d2d2b750293239ea74065a28279710e5fc8a7c86b3b7",
|
||||
},
|
||||
{
|
||||
"38f2411d1cad8c1b026e731a85dcc2eca79f472369233ae204aa5d6a2f6542f1",
|
||||
"1fb1c5de8ef2fdecf9a729ed4eb9ce0f363e75fed95400dbd25c333c26393bc3",
|
||||
"e9250c58d7200783aa9ec9814c13f252ba368bf52d6fd8f2e9397e603972e55d",
|
||||
"b5301fcc9818019651e8f56a265fb254ad864d9001b21ebd6b1a6ec0e6f6e07a",
|
||||
"2e29cd8f8697360d0b60d730d073793d41bc3c99f00c99875f5d22ed0b32ea6a",
|
||||
},
|
||||
}
|
||||
|
||||
func genericParamsForCurve(c elliptic.Curve) *elliptic.CurveParams {
|
||||
d := *(c.Params())
|
||||
return &d
|
||||
}
|
||||
|
||||
func TestP256BaseMult(t *testing.T) {
|
||||
p256 := P256()
|
||||
p256Generic := genericParamsForCurve(p256)
|
||||
|
||||
scalars := make([]*big.Int, 0, len(baseMultTests)+1)
|
||||
for i := 1; i <= 20; i++ {
|
||||
k := new(big.Int).SetInt64(int64(i))
|
||||
scalars = append(scalars, k)
|
||||
}
|
||||
for _, e := range baseMultTests {
|
||||
k, _ := new(big.Int).SetString(e.k, 10)
|
||||
scalars = append(scalars, k)
|
||||
}
|
||||
k := new(big.Int).SetInt64(1)
|
||||
k.Lsh(k, 500)
|
||||
scalars = append(scalars, k)
|
||||
|
||||
for i, k := range scalars {
|
||||
x, y := p256.ScalarBaseMult(k.Bytes())
|
||||
x2, y2 := p256Generic.ScalarBaseMult(k.Bytes())
|
||||
if x.Cmp(x2) != 0 || y.Cmp(y2) != 0 {
|
||||
t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, x, y, x2, y2)
|
||||
}
|
||||
|
||||
if testing.Short() && i > 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateP256MultTests() {
|
||||
p256 := P256()
|
||||
p256Generic := genericParamsForCurve(p256)
|
||||
for i := 0; i < 3; i++ {
|
||||
k1, err := randFieldElement(p256Generic, rand.Reader)
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
x1, y1 := p256Generic.ScalarBaseMult(k1.Bytes())
|
||||
k2, err := randFieldElement(p256Generic, rand.Reader)
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
x2, y2 := p256Generic.ScalarMult(x1, y1, k2.Bytes())
|
||||
fmt.Printf("%s\n", hex.EncodeToString(k2.Bytes()))
|
||||
fmt.Printf("%s\n", hex.EncodeToString(x1.Bytes()))
|
||||
fmt.Printf("%s\n", hex.EncodeToString(y1.Bytes()))
|
||||
fmt.Printf("%s\n", hex.EncodeToString(x2.Bytes()))
|
||||
fmt.Printf("%s\n", hex.EncodeToString(y2.Bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestP256Mult(t *testing.T) {
|
||||
p256 := P256()
|
||||
for i, e := range p256MultTests {
|
||||
x, _ := new(big.Int).SetString(e.xIn, 16)
|
||||
y, _ := new(big.Int).SetString(e.yIn, 16)
|
||||
k, _ := new(big.Int).SetString(e.k, 16)
|
||||
expectedX, _ := new(big.Int).SetString(e.xOut, 16)
|
||||
expectedY, _ := new(big.Int).SetString(e.yOut, 16)
|
||||
|
||||
xx, yy := p256.ScalarMult(x, y, k.Bytes())
|
||||
if xx.Cmp(expectedX) != 0 || yy.Cmp(expectedY) != 0 {
|
||||
t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, xx, yy, expectedX, expectedY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type synthCombinedMult struct {
|
||||
elliptic.Curve
|
||||
}
|
||||
|
||||
func (s synthCombinedMult) CombinedMult(bigX, bigY *big.Int, baseScalar, scalar []byte) (x, y *big.Int) {
|
||||
x1, y1 := s.ScalarBaseMult(baseScalar)
|
||||
x2, y2 := s.ScalarMult(bigX, bigY, scalar)
|
||||
return s.Add(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
func TestP256CombinedMult(t *testing.T) {
|
||||
type combinedMult interface {
|
||||
elliptic.Curve
|
||||
CombinedMult(bigX, bigY *big.Int, baseScalar, scalar []byte) (x, y *big.Int)
|
||||
}
|
||||
|
||||
p256, ok := P256().(combinedMult)
|
||||
if !ok {
|
||||
p256 = &synthCombinedMult{P256()}
|
||||
}
|
||||
|
||||
gx := p256.Params().Gx
|
||||
gy := p256.Params().Gy
|
||||
|
||||
zero := make([]byte, 32)
|
||||
one := make([]byte, 32)
|
||||
one[31] = 1
|
||||
two := make([]byte, 32)
|
||||
two[31] = 2
|
||||
|
||||
// 0×G + 0×G = ∞
|
||||
x, y := p256.CombinedMult(gx, gy, zero, zero)
|
||||
if x.Sign() != 0 || y.Sign() != 0 {
|
||||
t.Errorf("0×G + 0×G = (%d, %d), should be ∞", x, y)
|
||||
}
|
||||
|
||||
// 1×G + 0×G = G
|
||||
x, y = p256.CombinedMult(gx, gy, one, zero)
|
||||
if x.Cmp(gx) != 0 || y.Cmp(gy) != 0 {
|
||||
t.Errorf("1×G + 0×G = (%d, %d), should be (%d, %d)", x, y, gx, gy)
|
||||
}
|
||||
|
||||
// 0×G + 1×G = G
|
||||
x, y = p256.CombinedMult(gx, gy, zero, one)
|
||||
if x.Cmp(gx) != 0 || y.Cmp(gy) != 0 {
|
||||
t.Errorf("0×G + 1×G = (%d, %d), should be (%d, %d)", x, y, gx, gy)
|
||||
}
|
||||
|
||||
// 1×G + 1×G = 2×G
|
||||
x, y = p256.CombinedMult(gx, gy, one, one)
|
||||
ggx, ggy := p256.ScalarBaseMult(two)
|
||||
if x.Cmp(ggx) != 0 || y.Cmp(ggy) != 0 {
|
||||
t.Errorf("1×G + 1×G = (%d, %d), should be (%d, %d)", x, y, ggx, ggy)
|
||||
}
|
||||
|
||||
minusOne := new(big.Int).Sub(p256.Params().N, big.NewInt(1))
|
||||
// 1×G + (-1)×G = ∞
|
||||
x, y = p256.CombinedMult(gx, gy, one, minusOne.Bytes())
|
||||
if x.Sign() != 0 || y.Sign() != 0 {
|
||||
t.Errorf("1×G + (-1)×G = (%d, %d), should be ∞", x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue52075(t *testing.T) {
|
||||
Gx, Gy := P256().Params().Gx, P256().Params().Gy
|
||||
scalar := make([]byte, 33)
|
||||
scalar[32] = 1
|
||||
x, y := P256().ScalarBaseMult(scalar)
|
||||
if x.Cmp(Gx) != 0 || y.Cmp(Gy) != 0 {
|
||||
t.Errorf("unexpected output (%v,%v)", x, y)
|
||||
}
|
||||
x, y = P256().ScalarMult(Gx, Gy, scalar)
|
||||
if x.Cmp(Gx) != 0 || y.Cmp(Gy) != 0 {
|
||||
t.Errorf("unexpected output (%v,%v)", x, y)
|
||||
}
|
||||
}
|
30
sm2/sm2.go
30
sm2/sm2.go
@ -21,10 +21,10 @@ import (
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/randutil"
|
||||
"github.com/emmansun/gmsm/internal/xor"
|
||||
"github.com/emmansun/gmsm/sm2/sm2ec"
|
||||
"github.com/emmansun/gmsm/sm3"
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
"golang.org/x/crypto/cryptobyte/asn1"
|
||||
@ -146,7 +146,7 @@ func bytes2Point(curve elliptic.Curve, bytes []byte) (*big.Int, *big.Int, int, e
|
||||
return nil, nil, 0, fmt.Errorf("sm2: invalid point compressed form bytes length %d", len(bytes))
|
||||
}
|
||||
// Make sure it's NIST curve or SM2 P-256 curve
|
||||
if strings.HasPrefix(curve.Params().Name, "P-") || strings.EqualFold(curve.Params().Name, p256.CurveParams.Name) {
|
||||
if strings.HasPrefix(curve.Params().Name, "P-") || strings.EqualFold(curve.Params().Name, sm2ec.P256().Params().Name) {
|
||||
// y² = x³ - 3x + b, prime curves
|
||||
x, y := elliptic.UnmarshalCompressed(curve, bytes[:1+byteLen])
|
||||
if x == nil || y == nil {
|
||||
@ -201,7 +201,7 @@ func (*SM2SignerOption) HashFunc() crypto.Hash {
|
||||
|
||||
// FromECPrivateKey convert an ecdsa private key to SM2 private key.
|
||||
func (priv *PrivateKey) FromECPrivateKey(key *ecdsa.PrivateKey) (*PrivateKey, error) {
|
||||
if key.Curve != P256() {
|
||||
if key.Curve != sm2ec.P256() {
|
||||
return nil, errors.New("sm2: it's NOT a sm2 curve private key")
|
||||
}
|
||||
priv.PrivateKey = *key
|
||||
@ -259,16 +259,9 @@ func (priv *PrivateKey) Decrypt(rand io.Reader, msg []byte, opts crypto.Decrypte
|
||||
}
|
||||
|
||||
var (
|
||||
one = new(big.Int).SetInt64(1)
|
||||
initonce sync.Once
|
||||
one = new(big.Int).SetInt64(1)
|
||||
)
|
||||
|
||||
// P256 init and return the singleton.
|
||||
func P256() elliptic.Curve {
|
||||
initonce.Do(initP256)
|
||||
return p256
|
||||
}
|
||||
|
||||
// randFieldElement returns a random element of the order of the given
|
||||
// curve using the procedure given in FIPS 186-4, Appendix B.5.1.
|
||||
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
|
||||
@ -372,7 +365,7 @@ func Encrypt(random io.Reader, pub *ecdsa.PublicKey, msg []byte, opts *Encrypter
|
||||
|
||||
// GenerateKey generates a public and private key pair.
|
||||
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
||||
c := P256()
|
||||
c := sm2ec.P256()
|
||||
k, err := randFieldElement(c, rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -483,7 +476,7 @@ func ASN1Ciphertext2Plain(ciphertext []byte, opts *EncrypterOpts) ([]byte, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curve := P256()
|
||||
curve := sm2ec.P256()
|
||||
c1 := opts.PointMarshalMode.mashal(curve, x1, y1)
|
||||
if opts.CiphertextSplicingOrder == C1C3C2 {
|
||||
// c1 || c3 || c2
|
||||
@ -498,7 +491,7 @@ func PlainCiphertext2ASN1(ciphertext []byte, from ciphertextSplicingOrder) ([]by
|
||||
if ciphertext[0] == 0x30 {
|
||||
return nil, errors.New("sm2: invalid plain encoding ciphertext")
|
||||
}
|
||||
curve := P256()
|
||||
curve := sm2ec.P256()
|
||||
ciphertextLen := len(ciphertext)
|
||||
if ciphertextLen <= 1+(curve.Params().BitSize/8)+sm3.Size {
|
||||
return nil, errors.New("sm2: invalid ciphertext length")
|
||||
@ -523,7 +516,7 @@ func PlainCiphertext2ASN1(ciphertext []byte, from ciphertextSplicingOrder) ([]by
|
||||
|
||||
// AdjustCiphertextSplicingOrder utility method to change c2 c3 order
|
||||
func AdjustCiphertextSplicingOrder(ciphertext []byte, from, to ciphertextSplicingOrder) ([]byte, error) {
|
||||
curve := P256()
|
||||
curve := sm2ec.P256()
|
||||
if from == to {
|
||||
return ciphertext, nil
|
||||
}
|
||||
@ -868,5 +861,10 @@ var zeroReader = &zr{}
|
||||
// IsSM2PublicKey check if given public key is a SM2 public key or not
|
||||
func IsSM2PublicKey(publicKey interface{}) bool {
|
||||
pub, ok := publicKey.(*ecdsa.PublicKey)
|
||||
return ok && pub.Curve == P256()
|
||||
return ok && pub.Curve == sm2ec.P256()
|
||||
}
|
||||
|
||||
// P256 return sm2 curve signleton, this function is for backward compatibility.
|
||||
func P256() elliptic.Curve {
|
||||
return sm2ec.P256()
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/sm2/sm2ec"
|
||||
"github.com/emmansun/gmsm/sm3"
|
||||
)
|
||||
|
||||
@ -438,7 +439,7 @@ func BenchmarkLessThan32_P256(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkLessThan32_SM2(b *testing.B) {
|
||||
benchmarkEncrypt(b, P256(), "encryption standard")
|
||||
benchmarkEncrypt(b, sm2ec.P256(), "encryption standard")
|
||||
}
|
||||
|
||||
func BenchmarkMoreThan32_P256(b *testing.B) {
|
||||
@ -446,5 +447,5 @@ func BenchmarkMoreThan32_P256(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMoreThan32_SM2(b *testing.B) {
|
||||
benchmarkEncrypt(b, P256(), "encryption standard encryption standard encryption standard encryption standard encryption standard encryption standard encryption standard")
|
||||
benchmarkEncrypt(b, sm2ec.P256(), "encryption standard encryption standard encryption standard encryption standard encryption standard encryption standard encryption standard")
|
||||
}
|
||||
|
36
sm2/sm2ec/elliptic.go
Normal file
36
sm2/sm2ec/elliptic.go
Normal file
@ -0,0 +1,36 @@
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var initonce sync.Once
|
||||
|
||||
var sm2Params = &elliptic.CurveParams{
|
||||
Name: "sm2p256v1",
|
||||
BitSize: 256,
|
||||
P: bigFromHex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF"),
|
||||
N: bigFromHex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"),
|
||||
B: bigFromHex("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93"),
|
||||
Gx: bigFromHex("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7"),
|
||||
Gy: bigFromHex("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"),
|
||||
}
|
||||
|
||||
func bigFromHex(s string) *big.Int {
|
||||
b, ok := new(big.Int).SetString(s, 16)
|
||||
if !ok {
|
||||
panic("sm2/elliptic: internal error: invalid encoding")
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func initAll() {
|
||||
initSM2P256()
|
||||
}
|
||||
|
||||
func P256() elliptic.Curve {
|
||||
initonce.Do(initAll)
|
||||
return sm2p256
|
||||
}
|
348
sm2/sm2ec/elliptic_test.go
Normal file
348
sm2/sm2ec/elliptic_test.go
Normal file
@ -0,0 +1,348 @@
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var _ = elliptic.P256() // force NIST P curves init, avoid panic when we invoke generic implementation's method
|
||||
|
||||
// genericParamsForCurve returns the dereferenced CurveParams for
|
||||
// the specified curve. This is used to avoid the logic for
|
||||
// upgrading a curve to its specific implementation, forcing
|
||||
// usage of the generic implementation.
|
||||
func genericParamsForCurve(c elliptic.Curve) *elliptic.CurveParams {
|
||||
d := *(c.Params())
|
||||
return &d
|
||||
}
|
||||
|
||||
func testAllCurves(t *testing.T, f func(*testing.T, elliptic.Curve)) {
|
||||
tests := []struct {
|
||||
name string
|
||||
curve elliptic.Curve
|
||||
}{
|
||||
{"SM2P256", P256()},
|
||||
{"SM2P256/Params", genericParamsForCurve(P256())},
|
||||
}
|
||||
if testing.Short() {
|
||||
tests = tests[:1]
|
||||
}
|
||||
for _, test := range tests {
|
||||
curve := test.curve
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
f(t, curve)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnCurve(t *testing.T) {
|
||||
testAllCurves(t, func(t *testing.T, curve elliptic.Curve) {
|
||||
if !curve.IsOnCurve(curve.Params().Gx, curve.Params().Gy) {
|
||||
t.Error("basepoint is not on the curve")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestOffCurve(t *testing.T) {
|
||||
testAllCurves(t, func(t *testing.T, curve elliptic.Curve) {
|
||||
x, y := new(big.Int).SetInt64(1), new(big.Int).SetInt64(1)
|
||||
if curve.IsOnCurve(x, y) {
|
||||
t.Errorf("point off curve is claimed to be on the curve")
|
||||
}
|
||||
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
b := make([]byte, 1+2*byteLen)
|
||||
b[0] = 4 // uncompressed point
|
||||
x.FillBytes(b[1 : 1+byteLen])
|
||||
y.FillBytes(b[1+byteLen : 1+2*byteLen])
|
||||
|
||||
x1, y1 := elliptic.Unmarshal(curve, b)
|
||||
if x1 != nil || y1 != nil {
|
||||
t.Errorf("unmarshaling a point not on the curve succeeded")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfinity(t *testing.T) {
|
||||
testAllCurves(t, testInfinity)
|
||||
}
|
||||
|
||||
func isInfinity(x, y *big.Int) bool {
|
||||
return x.Sign() == 0 && y.Sign() == 0
|
||||
}
|
||||
|
||||
func testInfinity(t *testing.T, curve elliptic.Curve) {
|
||||
x0, y0 := new(big.Int), new(big.Int)
|
||||
xG, yG := curve.Params().Gx, curve.Params().Gy
|
||||
|
||||
if !isInfinity(curve.ScalarMult(xG, yG, curve.Params().N.Bytes())) {
|
||||
t.Errorf("x^q != ∞")
|
||||
}
|
||||
if !isInfinity(curve.ScalarMult(xG, yG, []byte{0})) {
|
||||
t.Errorf("x^0 != ∞")
|
||||
}
|
||||
|
||||
if !isInfinity(curve.ScalarMult(x0, y0, []byte{1, 2, 3})) {
|
||||
t.Errorf("∞^k != ∞")
|
||||
}
|
||||
if !isInfinity(curve.ScalarMult(x0, y0, []byte{0})) {
|
||||
t.Errorf("∞^0 != ∞")
|
||||
}
|
||||
|
||||
if !isInfinity(curve.ScalarBaseMult(curve.Params().N.Bytes())) {
|
||||
t.Errorf("b^q != ∞")
|
||||
}
|
||||
if !isInfinity(curve.ScalarBaseMult([]byte{0})) {
|
||||
t.Errorf("b^0 != ∞")
|
||||
}
|
||||
|
||||
if !isInfinity(curve.Double(x0, y0)) {
|
||||
t.Errorf("2∞ != ∞")
|
||||
}
|
||||
// There is no other point of order two on the NIST curves (as they have
|
||||
// cofactor one), so Double can't otherwise return the point at infinity.
|
||||
|
||||
nMinusOne := new(big.Int).Sub(curve.Params().N, big.NewInt(1))
|
||||
x, y := curve.ScalarMult(xG, yG, nMinusOne.Bytes())
|
||||
x, y = curve.Add(x, y, xG, yG)
|
||||
if !isInfinity(x, y) {
|
||||
t.Errorf("x^(q-1) + x != ∞")
|
||||
}
|
||||
x, y = curve.Add(xG, yG, x0, y0)
|
||||
if x.Cmp(xG) != 0 || y.Cmp(yG) != 0 {
|
||||
t.Errorf("x+∞ != x")
|
||||
}
|
||||
x, y = curve.Add(x0, y0, xG, yG)
|
||||
if x.Cmp(xG) != 0 || y.Cmp(yG) != 0 {
|
||||
t.Errorf("∞+x != x")
|
||||
}
|
||||
|
||||
if curve.IsOnCurve(x0, y0) {
|
||||
t.Errorf("IsOnCurve(∞) == true")
|
||||
}
|
||||
|
||||
if xx, yy := elliptic.Unmarshal(curve, elliptic.Marshal(curve, x0, y0)); xx != nil || yy != nil {
|
||||
t.Errorf("Unmarshal(Marshal(∞)) did not return an error")
|
||||
}
|
||||
// We don't test UnmarshalCompressed(MarshalCompressed(∞)) because there are
|
||||
// two valid points with x = 0.
|
||||
if xx, yy := elliptic.Unmarshal(curve, []byte{0x00}); xx != nil || yy != nil {
|
||||
t.Errorf("Unmarshal(∞) did not return an error")
|
||||
}
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
buf := make([]byte, byteLen*2+1)
|
||||
buf[0] = 4 // Uncompressed format.
|
||||
if xx, yy := elliptic.Unmarshal(curve, buf); xx != nil || yy != nil {
|
||||
t.Errorf("Unmarshal((0,0)) did not return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
testAllCurves(t, func(t *testing.T, curve elliptic.Curve) {
|
||||
_, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
serialized := elliptic.Marshal(curve, x, y)
|
||||
xx, yy := elliptic.Unmarshal(curve, serialized)
|
||||
if xx == nil {
|
||||
t.Fatal("failed to unmarshal")
|
||||
}
|
||||
if xx.Cmp(x) != 0 || yy.Cmp(y) != 0 {
|
||||
t.Fatal("unmarshal returned different values")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestInvalidCoordinates tests big.Int values that are not valid field elements
|
||||
// (negative or bigger than P). They are expected to return false from
|
||||
// IsOnCurve, all other behavior is undefined.
|
||||
func TestInvalidCoordinates(t *testing.T) {
|
||||
testAllCurves(t, testInvalidCoordinates)
|
||||
}
|
||||
|
||||
func testInvalidCoordinates(t *testing.T, curve elliptic.Curve) {
|
||||
checkIsOnCurveFalse := func(name string, x, y *big.Int) {
|
||||
if curve.IsOnCurve(x, y) {
|
||||
t.Errorf("IsOnCurve(%s) unexpectedly returned true", name)
|
||||
}
|
||||
}
|
||||
|
||||
p := curve.Params().P
|
||||
_, x, y, _ := elliptic.GenerateKey(curve, rand.Reader)
|
||||
xx, yy := new(big.Int), new(big.Int)
|
||||
|
||||
// Check if the sign is getting dropped.
|
||||
xx.Neg(x)
|
||||
checkIsOnCurveFalse("-x, y", xx, y)
|
||||
yy.Neg(y)
|
||||
checkIsOnCurveFalse("x, -y", x, yy)
|
||||
|
||||
// Check if negative values are reduced modulo P.
|
||||
xx.Sub(x, p)
|
||||
checkIsOnCurveFalse("x-P, y", xx, y)
|
||||
yy.Sub(y, p)
|
||||
checkIsOnCurveFalse("x, y-P", x, yy)
|
||||
|
||||
// Check if positive values are reduced modulo P.
|
||||
xx.Add(x, p)
|
||||
checkIsOnCurveFalse("x+P, y", xx, y)
|
||||
yy.Add(y, p)
|
||||
checkIsOnCurveFalse("x, y+P", x, yy)
|
||||
|
||||
// Check if the overflow is dropped.
|
||||
xx.Add(x, new(big.Int).Lsh(big.NewInt(1), 535))
|
||||
checkIsOnCurveFalse("x+2⁵³⁵, y", xx, y)
|
||||
yy.Add(y, new(big.Int).Lsh(big.NewInt(1), 535))
|
||||
checkIsOnCurveFalse("x, y+2⁵³⁵", x, yy)
|
||||
|
||||
// Check if P is treated like zero (if possible).
|
||||
// y^2 = x^3 - 3x + B
|
||||
// y = mod_sqrt(x^3 - 3x + B)
|
||||
// y = mod_sqrt(B) if x = 0
|
||||
// If there is no modsqrt, there is no point with x = 0, can't test x = P.
|
||||
if yy := new(big.Int).ModSqrt(curve.Params().B, p); yy != nil {
|
||||
if !curve.IsOnCurve(big.NewInt(0), yy) {
|
||||
t.Fatal("(0, mod_sqrt(B)) is not on the curve?")
|
||||
}
|
||||
checkIsOnCurveFalse("P, y", p, yy)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalCompressed(t *testing.T) {
|
||||
t.Run("P-256/03", func(t *testing.T) {
|
||||
data, _ := hex.DecodeString("031b5709a068f5c1d05d0a61c0c70a13310df2d3a6c2ca9c9bba53337ea3e10de3")
|
||||
x, _ := new(big.Int).SetString("1b5709a068f5c1d05d0a61c0c70a13310df2d3a6c2ca9c9bba53337ea3e10de3", 16)
|
||||
y, _ := new(big.Int).SetString("a7ac81d1fdd4fcd224bbd95183136f948861812594ef24bd867c23d955fee3bb", 16)
|
||||
testMarshalCompressed(t, P256(), x, y, data)
|
||||
})
|
||||
t.Run("P-256/02", func(t *testing.T) {
|
||||
data, _ := hex.DecodeString("0258f9a2efca4139f2b07662b937439a719ea3bf59d7de346c365db7c85d4bc32a")
|
||||
x, _ := new(big.Int).SetString("58f9a2efca4139f2b07662b937439a719ea3bf59d7de346c365db7c85d4bc32a", 16)
|
||||
y, _ := new(big.Int).SetString("02680fbe48b1d8cf023d0b7c1d9ab9b56535384729db5fcb8db29ec72c7fc9ca", 16)
|
||||
testMarshalCompressed(t, P256(), x, y, data)
|
||||
})
|
||||
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
data, _ := hex.DecodeString("02fd4bf61763b46581fd9174d623516cf3c81edd40e29ffa2777fb6cb0ae3ce535")
|
||||
X, Y := elliptic.UnmarshalCompressed(P256(), data)
|
||||
if X != nil || Y != nil {
|
||||
t.Error("expected an error for invalid encoding")
|
||||
}
|
||||
})
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("skipping other curves on short test")
|
||||
}
|
||||
|
||||
testAllCurves(t, func(t *testing.T, curve elliptic.Curve) {
|
||||
_, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testMarshalCompressed(t, curve, x, y, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func testMarshalCompressed(t *testing.T, curve elliptic.Curve, x, y *big.Int, want []byte) {
|
||||
if !curve.IsOnCurve(x, y) {
|
||||
t.Fatal("invalid test point")
|
||||
}
|
||||
got := elliptic.MarshalCompressed(curve, x, y)
|
||||
if want != nil && !bytes.Equal(got, want) {
|
||||
t.Errorf("got unexpected MarshalCompressed result: got %x, want %x", got, want)
|
||||
}
|
||||
|
||||
X, Y := elliptic.UnmarshalCompressed(curve, got)
|
||||
if X == nil || Y == nil {
|
||||
t.Fatalf("UnmarshalCompressed failed unexpectedly")
|
||||
}
|
||||
|
||||
if !curve.IsOnCurve(X, Y) {
|
||||
t.Error("UnmarshalCompressed returned a point not on the curve")
|
||||
}
|
||||
if X.Cmp(x) != 0 || Y.Cmp(y) != 0 {
|
||||
t.Errorf("point did not round-trip correctly: got (%v, %v), want (%v, %v)", X, Y, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLargeIsOnCurve(t *testing.T) {
|
||||
testAllCurves(t, func(t *testing.T, curve elliptic.Curve) {
|
||||
large := big.NewInt(1)
|
||||
large.Lsh(large, 1000)
|
||||
if curve.IsOnCurve(large, large) {
|
||||
t.Errorf("(2^1000, 2^1000) is reported on the curve")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmarkAllCurves(b *testing.B, f func(*testing.B, elliptic.Curve)) {
|
||||
tests := []struct {
|
||||
name string
|
||||
curve elliptic.Curve
|
||||
}{
|
||||
{"SM2P256", P256()},
|
||||
}
|
||||
for _, test := range tests {
|
||||
curve := test.curve
|
||||
b.Run(test.name, func(b *testing.B) {
|
||||
f(b, curve)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarBaseMult(b *testing.B) {
|
||||
benchmarkAllCurves(b, func(b *testing.B, curve elliptic.Curve) {
|
||||
priv, _, _, _ := elliptic.GenerateKey(curve, rand.Reader)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
x, _ := curve.ScalarBaseMult(priv)
|
||||
// Prevent the compiler from optimizing out the operation.
|
||||
priv[0] ^= byte(x.Bits()[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkScalarMult(b *testing.B) {
|
||||
benchmarkAllCurves(b, func(b *testing.B, curve elliptic.Curve) {
|
||||
_, x, y, _ := elliptic.GenerateKey(curve, rand.Reader)
|
||||
priv, _, _, _ := elliptic.GenerateKey(curve, rand.Reader)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
x, y = curve.ScalarMult(x, y, priv)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkMarshalUnmarshal(b *testing.B) {
|
||||
benchmarkAllCurves(b, func(b *testing.B, curve elliptic.Curve) {
|
||||
_, x, y, _ := elliptic.GenerateKey(curve, rand.Reader)
|
||||
b.Run("Uncompressed", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf := elliptic.Marshal(curve, x, y)
|
||||
xx, yy := elliptic.Unmarshal(curve, buf)
|
||||
if xx.Cmp(x) != 0 || yy.Cmp(y) != 0 {
|
||||
b.Error("Unmarshal output different from Marshal input")
|
||||
}
|
||||
}
|
||||
})
|
||||
b.Run("Compressed", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf := elliptic.MarshalCompressed(curve, x, y)
|
||||
xx, yy := elliptic.UnmarshalCompressed(curve, buf)
|
||||
if xx.Cmp(x) != 0 || yy.Cmp(y) != 0 {
|
||||
b.Error("Unmarshal output different from Marshal input")
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
198
sm2/sm2ec/sm2p256_asm_ec.go
Normal file
198
sm2/sm2ec/sm2p256_asm_ec.go
Normal file
@ -0,0 +1,198 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
_sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
|
||||
)
|
||||
|
||||
// TODO: will merge it with sm2p256_generic.go from golang 1.18 with generic support.
|
||||
type sm2Curve struct {
|
||||
newPoint func() *_sm2ec.P256Point
|
||||
params *elliptic.CurveParams
|
||||
}
|
||||
|
||||
var sm2p256 = &sm2Curve{newPoint: _sm2ec.NewP256Point}
|
||||
|
||||
func initSM2P256() {
|
||||
sm2p256.params = sm2Params
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Params() *elliptic.CurveParams {
|
||||
return curve.params
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
// IsOnCurve is documented to reject (0, 0), the conventional point at
|
||||
// infinity, which however is accepted by pointFromAffine.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := curve.pointFromAffine(x, y)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointFromAffine(x, y *big.Int) (p *_sm2ec.P256Point, err error) {
|
||||
p = curve.newPoint()
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates. See Issue 37294.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return p, nil
|
||||
}
|
||||
// Reject values that would not get correctly encoded.
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return p, errors.New("negative coordinate")
|
||||
}
|
||||
if x.BitLen() > curve.params.BitSize || y.BitLen() > curve.params.BitSize {
|
||||
return p, errors.New("overflowing coordinate")
|
||||
}
|
||||
// Encode the coordinates and let SetBytes reject invalid points.
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
buf := make([]byte, 1+2*byteLen)
|
||||
buf[0] = 4 // uncompressed point
|
||||
x.FillBytes(buf[1 : 1+byteLen])
|
||||
y.FillBytes(buf[1+byteLen : 1+2*byteLen])
|
||||
return p.SetBytes(buf)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointToAffine(p *_sm2ec.P256Point) (x, y *big.Int) {
|
||||
out := p.Bytes()
|
||||
if len(out) == 1 && out[0] == 0 {
|
||||
// This is the encoding of the point at infinity, which the affine
|
||||
// coordinates API represents as (0, 0) by convention.
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(out[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(out[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
p1, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
p2, err := curve.pointFromAffine(x2, y2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p1.Add(p1, p2))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Double was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p.Double(p))
|
||||
}
|
||||
|
||||
// normalizeScalar brings the scalar within the byte size of the order of the
|
||||
// curve, as expected by the nistec scalar multiplication functions.
|
||||
func (curve *sm2Curve) normalizeScalar(scalar []byte) []byte {
|
||||
byteSize := (curve.params.N.BitLen() + 7) / 8
|
||||
if len(scalar) == byteSize {
|
||||
return scalar
|
||||
}
|
||||
s := new(big.Int).SetBytes(scalar)
|
||||
if len(scalar) > byteSize {
|
||||
s.Mod(s, curve.params.N)
|
||||
}
|
||||
out := make([]byte, byteSize)
|
||||
return s.FillBytes(out)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(Bx, By)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: ScalarMult was called on an invalid point")
|
||||
}
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err = p.ScalarMult(p, scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err := curve.newPoint().ScalarBaseMult(scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
// CombinedMult returns [s1]G + [s2]P where G is the generator. It's used
|
||||
// through an interface upgrade in crypto/ecdsa.
|
||||
func (curve *sm2Curve) CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int) {
|
||||
s1 = curve.normalizeScalar(s1)
|
||||
q, err := curve.newPoint().ScalarBaseMult(s1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
p, err := curve.pointFromAffine(Px, Py)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: CombinedMult was called on an invalid point")
|
||||
}
|
||||
s2 = curve.normalizeScalar(s2)
|
||||
p, err = p.ScalarMult(p, s2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p.Add(p, q))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Unmarshal(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || data[0] != 4 {
|
||||
return nil, nil
|
||||
}
|
||||
// Use SetBytes to check that data encodes a valid point.
|
||||
_, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// We don't use pointToAffine because it involves an expensive field
|
||||
// inversion to convert from Jacobian to affine coordinates, which we
|
||||
// already have.
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(data[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) UnmarshalCompressed(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || (data[0] != 2 && data[0] != 3) {
|
||||
return nil, nil
|
||||
}
|
||||
p, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
// Inverse, implements invertible interface, used by Sign()
|
||||
func (curve *sm2Curve) Inverse(k *big.Int) *big.Int {
|
||||
if k.Sign() < 0 {
|
||||
// This should never happen.
|
||||
k = new(big.Int).Neg(k)
|
||||
}
|
||||
if k.Cmp(curve.params.N) >= 0 {
|
||||
// This should never happen.
|
||||
k = new(big.Int).Mod(k, curve.params.N)
|
||||
}
|
||||
scalar := k.FillBytes(make([]byte, 32))
|
||||
inverse, err := _sm2ec.P256OrdInverse(scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return new(big.Int).SetBytes(inverse)
|
||||
}
|
@ -1,186 +1,180 @@
|
||||
//go:build !amd64 && !arm64 || generic
|
||||
// +build !amd64,!arm64 generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/sm2ec"
|
||||
)
|
||||
|
||||
// TODO: will merge it with sm2p256_asm_ec.go from golang 1.18 with generic support.
|
||||
type sm2Curve struct {
|
||||
newPoint func() *sm2ec.SM2P256Point
|
||||
CurveParams *elliptic.CurveParams
|
||||
}
|
||||
|
||||
var p256 = &sm2Curve{newPoint: sm2ec.NewSM2P256Point}
|
||||
|
||||
func initP256() {
|
||||
p256.CurveParams = &elliptic.CurveParams{Name: "sm2p256v1"}
|
||||
p256.CurveParams.P, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
p256.CurveParams.N, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
p256.CurveParams.B, _ = new(big.Int).SetString("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
|
||||
p256.CurveParams.Gx, _ = new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
p256.CurveParams.Gy, _ = new(big.Int).SetString("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)
|
||||
p256.CurveParams.BitSize = 256
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Params() *elliptic.CurveParams {
|
||||
return curve.CurveParams
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
// IsOnCurve is documented to reject (0, 0), the conventional point at
|
||||
// infinity, which however is accepted by pointFromAffine.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := curve.pointFromAffine(x, y)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointFromAffine(x, y *big.Int) (p *sm2ec.SM2P256Point, err error) {
|
||||
p = curve.newPoint()
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates. See Issue 37294.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return p, nil
|
||||
}
|
||||
// Reject values that would not get correctly encoded.
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return p, errors.New("negative coordinate")
|
||||
}
|
||||
if x.BitLen() > curve.CurveParams.BitSize || y.BitLen() > curve.CurveParams.BitSize {
|
||||
return p, errors.New("overflowing coordinate")
|
||||
}
|
||||
// Encode the coordinates and let SetBytes reject invalid points.
|
||||
byteLen := (curve.CurveParams.BitSize + 7) / 8
|
||||
buf := make([]byte, 1+2*byteLen)
|
||||
buf[0] = 4 // uncompressed point
|
||||
x.FillBytes(buf[1 : 1+byteLen])
|
||||
y.FillBytes(buf[1+byteLen : 1+2*byteLen])
|
||||
return p.SetBytes(buf)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointToAffine(p *sm2ec.SM2P256Point) (x, y *big.Int) {
|
||||
out := p.Bytes()
|
||||
if len(out) == 1 && out[0] == 0 {
|
||||
// This is the encoding of the point at infinity, which the affine
|
||||
// coordinates API represents as (0, 0) by convention.
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
byteLen := (curve.CurveParams.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(out[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(out[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
p1, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
p2, err := curve.pointFromAffine(x2, y2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p1.Add(p1, p2))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Double was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p.Double(p))
|
||||
}
|
||||
|
||||
// normalizeScalar brings the scalar within the byte size of the order of the
|
||||
// curve, as expected by the nistec scalar multiplication functions.
|
||||
func (curve *sm2Curve) normalizeScalar(scalar []byte) []byte {
|
||||
byteSize := (curve.CurveParams.N.BitLen() + 7) / 8
|
||||
if len(scalar) == byteSize {
|
||||
return scalar
|
||||
}
|
||||
s := new(big.Int).SetBytes(scalar)
|
||||
if len(scalar) > byteSize {
|
||||
s.Mod(s, curve.CurveParams.N)
|
||||
}
|
||||
out := make([]byte, byteSize)
|
||||
return s.FillBytes(out)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(Bx, By)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: ScalarMult was called on an invalid point")
|
||||
}
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err = p.ScalarMult(p, scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err := curve.newPoint().ScalarBaseMult(scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
// CombinedMult returns [s1]G + [s2]P where G is the generator. It's used
|
||||
// through an interface upgrade in crypto/ecdsa.
|
||||
func (curve *sm2Curve) CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int) {
|
||||
s1 = curve.normalizeScalar(s1)
|
||||
q, err := curve.newPoint().ScalarBaseMult(s1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
p, err := curve.pointFromAffine(Px, Py)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: CombinedMult was called on an invalid point")
|
||||
}
|
||||
s2 = curve.normalizeScalar(s2)
|
||||
p, err = p.ScalarMult(p, s2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p.Add(p, q))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Unmarshal(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || data[0] != 4 {
|
||||
return nil, nil
|
||||
}
|
||||
// Use SetBytes to check that data encodes a valid point.
|
||||
_, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// We don't use pointToAffine because it involves an expensive field
|
||||
// inversion to convert from Jacobian to affine coordinates, which we
|
||||
// already have.
|
||||
byteLen := (curve.CurveParams.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(data[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) UnmarshalCompressed(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || (data[0] != 2 && data[0] != 3) {
|
||||
return nil, nil
|
||||
}
|
||||
p, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
//go:build !amd64 && !arm64 || generic
|
||||
// +build !amd64,!arm64 generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
_sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
|
||||
)
|
||||
|
||||
// TODO: will merge it with sm2p256_asm_ec.go from golang 1.18 with generic support.
|
||||
type sm2Curve struct {
|
||||
newPoint func() *_sm2ec.SM2P256Point
|
||||
params *elliptic.CurveParams
|
||||
}
|
||||
|
||||
var sm2p256 = &sm2Curve{newPoint: _sm2ec.NewSM2P256Point}
|
||||
|
||||
func initSM2P256() {
|
||||
sm2p256.params = sm2Params
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Params() *elliptic.CurveParams {
|
||||
return curve.params
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
// IsOnCurve is documented to reject (0, 0), the conventional point at
|
||||
// infinity, which however is accepted by pointFromAffine.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := curve.pointFromAffine(x, y)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointFromAffine(x, y *big.Int) (p *_sm2ec.SM2P256Point, err error) {
|
||||
p = curve.newPoint()
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates. See Issue 37294.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return p, nil
|
||||
}
|
||||
// Reject values that would not get correctly encoded.
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return p, errors.New("negative coordinate")
|
||||
}
|
||||
if x.BitLen() > curve.params.BitSize || y.BitLen() > curve.params.BitSize {
|
||||
return p, errors.New("overflowing coordinate")
|
||||
}
|
||||
// Encode the coordinates and let SetBytes reject invalid points.
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
buf := make([]byte, 1+2*byteLen)
|
||||
buf[0] = 4 // uncompressed point
|
||||
x.FillBytes(buf[1 : 1+byteLen])
|
||||
y.FillBytes(buf[1+byteLen : 1+2*byteLen])
|
||||
return p.SetBytes(buf)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointToAffine(p *_sm2ec.SM2P256Point) (x, y *big.Int) {
|
||||
out := p.Bytes()
|
||||
if len(out) == 1 && out[0] == 0 {
|
||||
// This is the encoding of the point at infinity, which the affine
|
||||
// coordinates API represents as (0, 0) by convention.
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(out[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(out[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
p1, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
p2, err := curve.pointFromAffine(x2, y2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p1.Add(p1, p2))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Double was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p.Double(p))
|
||||
}
|
||||
|
||||
// normalizeScalar brings the scalar within the byte size of the order of the
|
||||
// curve, as expected by the nistec scalar multiplication functions.
|
||||
func (curve *sm2Curve) normalizeScalar(scalar []byte) []byte {
|
||||
byteSize := (curve.params.N.BitLen() + 7) / 8
|
||||
if len(scalar) == byteSize {
|
||||
return scalar
|
||||
}
|
||||
s := new(big.Int).SetBytes(scalar)
|
||||
if len(scalar) > byteSize {
|
||||
s.Mod(s, curve.params.N)
|
||||
}
|
||||
out := make([]byte, byteSize)
|
||||
return s.FillBytes(out)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(Bx, By)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: ScalarMult was called on an invalid point")
|
||||
}
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err = p.ScalarMult(p, scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err := curve.newPoint().ScalarBaseMult(scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
// CombinedMult returns [s1]G + [s2]P where G is the generator. It's used
|
||||
// through an interface upgrade in crypto/ecdsa.
|
||||
func (curve *sm2Curve) CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int) {
|
||||
s1 = curve.normalizeScalar(s1)
|
||||
q, err := curve.newPoint().ScalarBaseMult(s1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
p, err := curve.pointFromAffine(Px, Py)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: CombinedMult was called on an invalid point")
|
||||
}
|
||||
s2 = curve.normalizeScalar(s2)
|
||||
p, err = p.ScalarMult(p, s2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p.Add(p, q))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Unmarshal(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || data[0] != 4 {
|
||||
return nil, nil
|
||||
}
|
||||
// Use SetBytes to check that data encodes a valid point.
|
||||
_, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// We don't use pointToAffine because it involves an expensive field
|
||||
// inversion to convert from Jacobian to affine coordinates, which we
|
||||
// already have.
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(data[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) UnmarshalCompressed(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || (data[0] != 2 && data[0] != 3) {
|
||||
return nil, nil
|
||||
}
|
||||
p, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
package smx509
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -17,6 +17,8 @@ const (
|
||||
|
||||
// certDirEnv is the environment variable which identifies which directory
|
||||
// to check for SSL certificate files. If set this overrides the system default.
|
||||
// It is a colon separated list of directories.
|
||||
// See https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html.
|
||||
certDirEnv = "SSL_CERT_DIR"
|
||||
)
|
||||
|
||||
@ -34,7 +36,7 @@ func loadSystemRoots() (*CertPool, error) {
|
||||
|
||||
var firstErr error
|
||||
for _, file := range files {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
data, err := os.ReadFile(file)
|
||||
if err == nil {
|
||||
roots.AppendCertsFromPEM(data)
|
||||
break
|
||||
@ -62,7 +64,7 @@ func loadSystemRoots() (*CertPool, error) {
|
||||
continue
|
||||
}
|
||||
for _, fi := range fis {
|
||||
data, err := ioutil.ReadFile(directory + "/" + fi.Name())
|
||||
data, err := os.ReadFile(directory + "/" + fi.Name())
|
||||
if err == nil {
|
||||
roots.AppendCertsFromPEM(data)
|
||||
}
|
||||
@ -79,14 +81,14 @@ func loadSystemRoots() (*CertPool, error) {
|
||||
// readUniqueDirectoryEntries is like ioutil.ReadDir but omits
|
||||
// symlinks that point within the directory.
|
||||
func readUniqueDirectoryEntries(dir string) ([]os.FileInfo, error) {
|
||||
fis, err := ioutil.ReadDir(dir)
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uniq := fis[:0]
|
||||
for _, fi := range fis {
|
||||
if !isSameDirSymlink(fi, dir) {
|
||||
uniq = append(uniq, fi)
|
||||
uniq := files[:0]
|
||||
for _, f := range files {
|
||||
if !isSameDirSymlink(f, dir) {
|
||||
uniq = append(uniq, f)
|
||||
}
|
||||
}
|
||||
return uniq, nil
|
||||
@ -94,10 +96,10 @@ func readUniqueDirectoryEntries(dir string) ([]os.FileInfo, error) {
|
||||
|
||||
// isSameDirSymlink reports whether fi in dir is a symlink with a
|
||||
// target not containing a slash.
|
||||
func isSameDirSymlink(fi os.FileInfo, dir string) bool {
|
||||
if fi.Mode()&os.ModeSymlink == 0 {
|
||||
func isSameDirSymlink(f fs.DirEntry, dir string) bool {
|
||||
if f.Type()&fs.ModeSymlink == 0 {
|
||||
return false
|
||||
}
|
||||
target, err := os.Readlink(filepath.Join(dir, fi.Name()))
|
||||
target, err := os.Readlink(filepath.Join(dir, f.Name()))
|
||||
return err == nil && !strings.Contains(target, "/")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user