Merge pull request #78 from emmansun/20221015

Upgrade minimum supported golang version to 1.16
This commit is contained in:
Sun Yimin 2022-08-19 15:05:45 +08:00 committed by GitHub
commit f2245485bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 6501 additions and 7056 deletions

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
goVer: ['1.15', '1.16', '1.17']
goVer: ['1.16', '1.17', '1.18']
steps:
- name: Checkout Repo
uses: actions/checkout@v3

47
.github/workflows/ci_20221015.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: ci_20221015
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

View File

@ -6,7 +6,7 @@ jobs:
virt: vm
os: linux
dist: focal
go: 1.15.x
go: 1.16.x
group: edge
install:

View File

@ -8,8 +8,8 @@ import (
"errors"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/xor"
)
const (
@ -112,14 +112,14 @@ func (c *ccm) deriveCounter(counter *[ccmBlockSize]byte, nonce []byte) {
func (c *ccm) cmac(out, data []byte) {
for len(data) >= ccmBlockSize {
xor.XorBytes(out, out, data)
subtle.XORBytes(out, out, data)
c.cipher.Encrypt(out, out)
data = data[ccmBlockSize:]
}
if len(data) > 0 {
var block [ccmBlockSize]byte
copy(block[:], data)
xor.XorBytes(out, out, data)
subtle.XORBytes(out, out, data)
c.cipher.Encrypt(out, out)
}
}
@ -168,7 +168,7 @@ func (c *ccm) auth(nonce, plaintext, additionalData []byte, tagMask *[ccmBlockSi
if len(plaintext) > 0 {
c.cmac(out[:], plaintext)
}
xor.XorWords(out[:], out[:], tagMask[:])
subtle.XORBytes(out[:], out[:], tagMask[:])
return out[:c.tagSize]
}
@ -179,8 +179,8 @@ func (c *ccm) Seal(dst, nonce, plaintext, data []byte) []byte {
if uint64(len(plaintext)) > uint64(c.MaxLength()) {
panic("cipher: message too large for CCM")
}
ret, out := subtle.SliceForAppend(dst, len(plaintext)+c.tagSize)
if subtle.InexactOverlap(out, plaintext) {
ret, out := alias.SliceForAppend(dst, len(plaintext)+c.tagSize)
if alias.InexactOverlap(out, plaintext) {
panic("cipher: invalid buffer overlap")
}
@ -225,8 +225,8 @@ func (c *ccm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
c.deriveCounter(&counter, nonce)
c.cipher.Encrypt(tagMask[:], counter[:])
ret, out := subtle.SliceForAppend(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
ret, out := alias.SliceForAppend(dst, len(ciphertext))
if alias.InexactOverlap(out, ciphertext) {
panic("cipher: invalid buffer overlap")
}

View File

@ -6,8 +6,8 @@ import (
"errors"
"sync"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/xor"
)
const GF128_FDBK byte = 0x87
@ -89,7 +89,7 @@ func (c *xts) Encrypt(ciphertext, plaintext []byte, sectorNum uint64) {
if len(plaintext) < blockSize {
panic("xts: plaintext length is smaller than the block size")
}
if subtle.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
panic("xts: invalid buffer overlap")
}
@ -112,18 +112,18 @@ func (c *xts) Encrypt(ciphertext, plaintext []byte, sectorNum uint64) {
copy(tweaks[blockSize*i:], tweak[:])
mul2(tweak)
}
xor.XorBytes(ciphertext, plaintext, tweaks)
subtle.XORBytes(ciphertext, plaintext, tweaks)
concCipher.EncryptBlocks(ciphertext, ciphertext)
xor.XorBytes(ciphertext, ciphertext, tweaks)
subtle.XORBytes(ciphertext, ciphertext, tweaks)
plaintext = plaintext[batchSize:]
lastCiphertext = ciphertext[batchSize-blockSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(plaintext) >= blockSize {
xor.XorBytes(ciphertext, plaintext, tweak[:])
subtle.XORBytes(ciphertext, plaintext, tweak[:])
c.k1.Encrypt(ciphertext, ciphertext)
xor.XorBytes(ciphertext, ciphertext, tweak[:])
subtle.XORBytes(ciphertext, ciphertext, tweak[:])
plaintext = plaintext[blockSize:]
lastCiphertext = ciphertext
ciphertext = ciphertext[blockSize:]
@ -139,11 +139,11 @@ func (c *xts) Encrypt(ciphertext, plaintext []byte, sectorNum uint64) {
//Steal ciphertext to complete the block
copy(x[remain:], lastCiphertext[remain:blockSize])
//Merge the tweak into the input block
xor.XorBytes(x[:], x[:], tweak[:])
subtle.XORBytes(x[:], x[:], tweak[:])
//Encrypt the final block using K1
c.k1.Encrypt(x[:], x[:])
//Merge the tweak into the output block
xor.XorBytes(lastCiphertext, x[:], tweak[:])
subtle.XORBytes(lastCiphertext, x[:], tweak[:])
}
tweakPool.Put(tweak)
}
@ -158,7 +158,7 @@ func (c *xts) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) {
if len(ciphertext) < blockSize {
panic("xts: ciphertext length is smaller than the block size")
}
if subtle.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
panic("xts: invalid buffer overlap")
}
@ -179,18 +179,18 @@ func (c *xts) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) {
copy(tweaks[blockSize*i:], tweak[:])
mul2(tweak)
}
xor.XorBytes(plaintext, ciphertext, tweaks)
subtle.XORBytes(plaintext, ciphertext, tweaks)
concCipher.DecryptBlocks(plaintext, plaintext)
xor.XorBytes(plaintext, plaintext, tweaks)
subtle.XORBytes(plaintext, plaintext, tweaks)
plaintext = plaintext[batchSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(ciphertext) >= 2*blockSize {
xor.XorBytes(plaintext, ciphertext, tweak[:])
subtle.XORBytes(plaintext, ciphertext, tweak[:])
c.k1.Decrypt(plaintext, plaintext)
xor.XorBytes(plaintext, plaintext, tweak[:])
subtle.XORBytes(plaintext, plaintext, tweak[:])
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
@ -203,9 +203,9 @@ func (c *xts) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) {
var tt [blockSize]byte
copy(tt[:], tweak[:])
mul2(&tt)
xor.XorBytes(x[:], ciphertext, tt[:])
subtle.XORBytes(x[:], ciphertext, tt[:])
c.k1.Decrypt(x[:], x[:])
xor.XorBytes(plaintext, x[:], tt[:])
subtle.XORBytes(plaintext, x[:], tt[:])
//Retrieve the length of the final block
remain -= blockSize
@ -220,9 +220,9 @@ func (c *xts) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) {
//The last block contains exactly 128 bits
copy(x[:], ciphertext)
}
xor.XorBytes(x[:], x[:], tweak[:])
subtle.XORBytes(x[:], x[:], tweak[:])
c.k1.Decrypt(x[:], x[:])
xor.XorBytes(plaintext, x[:], tweak[:])
subtle.XORBytes(plaintext, x[:], tweak[:])
}
tweakPool.Put(tweak)

2
go.mod
View File

@ -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

View File

@ -0,0 +1,42 @@
package alias
import "testing"
var a, b [100]byte
var aliasingTests = []struct {
x, y []byte
anyOverlap, inexactOverlap bool
}{
{a[:], b[:], false, false},
{a[:], b[:0], false, false},
{a[:], b[:50], false, false},
{a[40:50], a[50:60], false, false},
{a[40:50], a[60:70], false, false},
{a[:51], a[50:], true, true},
{a[:], a[:], true, false},
{a[:50], a[:60], true, false},
{a[:], nil, false, false},
{nil, nil, false, false},
{a[:], a[:0], false, false},
{a[:10], a[:10:20], true, false},
{a[:10], a[5:10:20], true, true},
}
func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
any := AnyOverlap(x, y)
if any != anyOverlap {
t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
}
inexact := InexactOverlap(x, y)
if inexact != inexactOverlap {
t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
}
}
func TestAliasing(t *testing.T) {
for i, tt := range aliasingTests {
testAliasing(t, i, tt.x, tt.y, tt.anyOverlap, tt.inexactOverlap)
testAliasing(t, i, tt.y, tt.x, tt.anyOverlap, tt.inexactOverlap)
}
}

View File

@ -1,4 +1,4 @@
package subtle
package alias
import "unsafe"

View File

@ -0,0 +1,25 @@
package fiat_test
import (
"testing"
"github.com/emmansun/gmsm/internal/sm2ec/fiat"
)
func BenchmarkMul(b *testing.B) {
v := new(fiat.SM2P256Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Mul(v, v)
}
}
func BenchmarkSquare(b *testing.B) {
v := new(fiat.SM2P256Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Square(v)
}
}

View File

@ -24,15 +24,9 @@ import (
"os/exec"
"strings"
"text/template"
)
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
}
_sm2ec "github.com/emmansun/gmsm/sm2/sm2ec"
)
var curves = []struct {
P string
@ -43,15 +37,7 @@ var curves = []struct {
{
P: "SM2P256",
Element: "fiat.SM2P256Element",
Params: &elliptic.CurveParams{
Name: "sm2p256v1",
BitSize: 256,
P: bigFromHex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF"),
N: bigFromHex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"),
B: bigFromHex("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93"),
Gx: bigFromHex("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7"),
Gy: bigFromHex("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"),
},
Params: _sm2ec.P256().Params(),
BuildTags: "",
},
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
}

View 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)")
}
}

Binary file not shown.

View 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)
}
}
}

View 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.17.
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)
}

View 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()))
}
}

View File

@ -0,0 +1,28 @@
// Copyright 2018 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
// +build amd64,!generic
package subtle
// XORBytes xors the bytes in a and b. The destination should have enough
// space, otherwise XORBytes will panic. Returns the number of bytes xor'd.
func XORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
if n == 0 {
return 0
}
if n > len(dst) {
panic("subtle.XORBytes: dst too short")
}
xorBytes(&dst[0], &a[0], &b[0], n) // amd64 must have SSE2
return n
}
//go:noescape
func xorBytes(dst, a, b *byte, n int)

View File

@ -1,13 +1,14 @@
// Copyright 2018 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
// +build amd64,!generic
#include "textflag.h"
// func xorBytesSSE2(dst, a, b *byte, n int)
TEXT ·xorBytesSSE2(SB), NOSPLIT, $0
// func xorBytes(dst, a, b *byte, n int)
TEXT ·xorBytes(SB), NOSPLIT, $0
MOVQ dst+0(FP), BX
MOVQ a+8(FP), SI
MOVQ b+16(FP), CX

View File

@ -0,0 +1,29 @@
// Copyright 2018 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 arm64 && !generic
// +build arm64,!generic
package subtle
// XORBytes xors the bytes in a and b. The destination should have enough
// space, otherwise XORBytes will panic. Returns the number of bytes xor'd.
func XORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
if n == 0 {
return 0
}
if n > len(dst) {
panic("subtle.XORBytes: dst too short")
}
xorBytes(&dst[0], &a[0], &b[0], n)
return n
}
//go:noescape
func xorBytes(dst, a, b *byte, n int)

View File

@ -1,13 +1,14 @@
// Copyright 2020 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 arm64 && !generic
// +build arm64,!generic
#include "textflag.h"
// func xorBytesARM64(dst, a, b *byte, n int)
TEXT ·xorBytesARM64(SB), NOSPLIT|NOFRAME, $0
// func xorBytes(dst, a, b *byte, n int)
TEXT ·xorBytes(SB), NOSPLIT|NOFRAME, $0
MOVD dst+0(FP), R0
MOVD a+8(FP), R1
MOVD b+16(FP), R2

View File

@ -1,19 +1,27 @@
// Copyright 2013 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 && !arm64 || generic
// +build !amd64,!arm64 generic
package xor
package subtle
import (
"runtime"
"unsafe"
)
// xorBytes xors the bytes in a and b. The destination should have enough
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
func XorBytes(dst, a, b []byte) int {
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" ||
runtime.GOARCH == "ppc64" ||
runtime.GOARCH == "ppc64le" ||
runtime.GOARCH == "s390x"
// XORBytes xors the bytes in a and b. The destination should have enough
// space, otherwise XORBytes will panic. Returns the number of bytes xor'd.
func XORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
@ -21,6 +29,9 @@ func XorBytes(dst, a, b []byte) int {
if n == 0 {
return 0
}
if n > len(dst) {
panic("subtle.XORBytes: dst too short")
}
switch {
case supportsUnaligned:
@ -36,16 +47,10 @@ func XorBytes(dst, a, b []byte) int {
return n
}
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
// fastXORBytes xors in bulk. It only works on architectures that
// support unaligned read/writes.
// n needs to be smaller or equal than the length of a and b.
func fastXORBytes(dst, a, b []byte, n int) {
// Assert dst has enough space
_ = dst[n-1]
w := n / wordSize
if w > 0 {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
@ -67,25 +72,3 @@ func safeXORBytes(dst, a, b []byte, n int) {
dst[i] = a[i] ^ b[i]
}
}
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
// The arguments are assumed to be of equal length.
func fastXORWords(dst, a, b []byte) {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
n := len(b) / wordSize
for i := 0; i < n; i++ {
dw[i] = aw[i] ^ bw[i]
}
}
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
// The slice arguments a and b are assumed to be of equal length.
func XorWords(dst, a, b []byte) {
if supportsUnaligned {
fastXORWords(dst, a, b)
} else {
safeXORBytes(dst, a, b, len(b))
}
}

View File

@ -0,0 +1,95 @@
package subtle_test
import (
"bytes"
"crypto/rand"
"fmt"
"io"
"testing"
"github.com/emmansun/gmsm/internal/subtle"
)
func TestXORBytes(t *testing.T) {
for n := 1; n <= 1024; n++ {
if n > 16 && testing.Short() {
n += n >> 3
}
for alignP := 0; alignP < 8; alignP++ {
for alignQ := 0; alignQ < 8; alignQ++ {
for alignD := 0; alignD < 8; alignD++ {
p := make([]byte, alignP+n, alignP+n+10)[alignP:]
q := make([]byte, alignQ+n, alignQ+n+10)[alignQ:]
if n&1 != 0 {
p = p[:n]
} else {
q = q[:n]
}
if _, err := io.ReadFull(rand.Reader, p); err != nil {
t.Fatal(err)
}
if _, err := io.ReadFull(rand.Reader, q); err != nil {
t.Fatal(err)
}
d := make([]byte, alignD+n, alignD+n+10)
for i := range d {
d[i] = 0xdd
}
want := make([]byte, len(d), cap(d))
copy(want[:cap(want)], d[:cap(d)])
for i := 0; i < n; i++ {
want[alignD+i] = p[i] ^ q[i]
}
if subtle.XORBytes(d[alignD:], p, q); !bytes.Equal(d, want) {
t.Fatalf("n=%d alignP=%d alignQ=%d alignD=%d:\n\tp = %x\n\tq = %x\n\td = %x\n\twant %x\n", n, alignP, alignQ, alignD, p, q, d, want)
}
}
}
}
}
}
func TestXorBytesPanic(t *testing.T) {
mustPanic(t, "subtle.XORBytes: dst too short", func() {
subtle.XORBytes(nil, make([]byte, 1), make([]byte, 1))
})
mustPanic(t, "subtle.XORBytes: dst too short", func() {
subtle.XORBytes(make([]byte, 1), make([]byte, 2), make([]byte, 3))
})
}
func BenchmarkXORBytes(b *testing.B) {
dst := make([]byte, 1<<15)
data0 := make([]byte, 1<<15)
data1 := make([]byte, 1<<15)
sizes := []int64{1 << 3, 1 << 7, 1 << 11, 1 << 15}
for _, size := range sizes {
b.Run(fmt.Sprintf("%dBytes", size), func(b *testing.B) {
s0 := data0[:size]
s1 := data1[:size]
b.SetBytes(int64(size))
for i := 0; i < b.N; i++ {
subtle.XORBytes(dst, s0, s1)
}
})
}
}
func mustPanic(t *testing.T, expected string, f func()) {
t.Helper()
defer func() {
switch msg := recover().(type) {
case nil:
t.Errorf("expected panic(%q), but did not panic", expected)
case string:
if msg != expected {
t.Errorf("expected panic(%q), but got panic(%q)", expected, msg)
}
default:
t.Errorf("expected panic(%q), but got panic(%T%v)", expected, msg, msg)
}
}()
f()
}

View File

@ -1,29 +0,0 @@
// Copyright 2018 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
// +build amd64,!generic
package xor
// XorBytes xors the bytes in a and b. The destination should have enough
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
func XorBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
if n == 0 {
return 0
}
_ = dst[n-1]
xorBytesSSE2(&dst[0], &a[0], &b[0], n) // amd64 must have SSE2
return n
}
func XorWords(dst, a, b []byte) {
XorBytes(dst, a, b)
}
//go:noescape
func xorBytesSSE2(dst, a, b *byte, n int)

View File

@ -1,31 +0,0 @@
// Copyright 2018 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 arm64 && !generic
// +build arm64,!generic
package xor
// xorBytes xors the bytes in a and b. The destination should have enough
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
func XorBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
if n == 0 {
return 0
}
// make sure dst has enough space
_ = dst[n-1]
xorBytesARM64(&dst[0], &a[0], &b[0], n)
return n
}
func XorWords(dst, a, b []byte) {
XorBytes(dst, a, b)
}
//go:noescape
func xorBytesARM64(dst, a, b *byte, n int)

View File

@ -3,7 +3,7 @@ package padding
import (
"errors"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/alias"
)
// https://www.ibm.com/docs/en/linux-on-systems?topic=processes-ansi-x923-cipher-block-chaining
@ -15,7 +15,7 @@ func (pad ansiX923Padding) BlockSize() int {
func (pad ansiX923Padding) Pad(src []byte) []byte {
overhead := pad.BlockSize() - len(src)%pad.BlockSize()
ret, out := subtle.SliceForAppend(src, overhead)
ret, out := alias.SliceForAppend(src, overhead)
out[overhead-1] = byte(overhead)
for i := 0; i < overhead-1; i++ {
out[i] = 0

View File

@ -4,7 +4,7 @@ package padding
import (
"errors"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/alias"
)
// https://datatracker.ietf.org/doc/html/rfc5652#section-6.3
@ -16,7 +16,7 @@ func (pad pkcs7Padding) BlockSize() int {
func (pad pkcs7Padding) Pad(src []byte) []byte {
overhead := pad.BlockSize() - len(src)%pad.BlockSize()
ret, out := subtle.SliceForAppend(src, overhead)
ret, out := alias.SliceForAppend(src, overhead)
for i := 0; i < overhead; i++ {
out[i] = byte(overhead)
}

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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()
}
}

View File

@ -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/internal/subtle"
"github.com/emmansun/gmsm/sm2/sm2ec"
"github.com/emmansun/gmsm/sm3"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
@ -136,7 +136,7 @@ func bytes2Point(curve elliptic.Curve, bytes []byte) (*big.Int, *big.Int, int, e
data := make([]byte, 1+byteLen*2)
data[0] = uncompressed
copy(data[1:], bytes[1:1+byteLen*2])
x, y := elliptic.Unmarshal(curve, data)
x, y := sm2ec.Unmarshal(curve, data)
if x == nil || y == nil {
return nil, nil, 0, fmt.Errorf("sm2: point is not on curve %s", curve.Params().Name)
}
@ -146,9 +146,9 @@ 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])
x, y := sm2ec.UnmarshalCompressed(curve, bytes[:1+byteLen])
if x == nil || y == nil {
return nil, nil, 0, fmt.Errorf("sm2: point is not on curve %s", curve.Params().Name)
}
@ -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) {
@ -352,7 +345,7 @@ func Encrypt(random io.Reader, pub *ecdsa.PublicKey, msg []byte, opts *Encrypter
}
//A6, C2 = M + t;
xor.XorBytes(c2, msg, c2)
subtle.XORBytes(c2, msg, c2)
//A7, C3 = hash(x2||M||y2)
c3 := calculateC3(curve, x2, y2, msg)
@ -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
@ -409,7 +402,7 @@ func rawDecrypt(priv *PrivateKey, x1, y1 *big.Int, c2, c3 []byte) ([]byte, error
}
//B5, calculate msg = c2 ^ t
xor.XorBytes(msg, c2, msg)
subtle.XORBytes(msg, c2, msg)
u := calculateC3(curve, x2, y2, msg)
for i := 0; i < sm3.Size; i++ {
@ -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()
}

View File

@ -93,6 +93,10 @@ func (ke *KeyExchange) SetPeerParameters(peerPub *ecdsa.PublicKey, peerUID []byt
return errors.New("sm2: 'peerPub' already exists, please do not set it")
}
if peerPub.Curve != ke.privateKey.Curve {
return errors.New("sm2: peer public key is not expected/supported")
}
var err error
ke.peerPub = peerPub
ke.peerZ, err = calculateZA(ke.peerPub, peerUID)

View File

@ -1,6 +1,8 @@
package sm2
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"errors"
@ -83,6 +85,7 @@ func TestKeyExchangeSimplest(t *testing.T) {
func TestSetPeerParameters(t *testing.T) {
priv1, _ := GenerateKey(rand.Reader)
priv2, _ := GenerateKey(rand.Reader)
priv3, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
uidA := []byte("Alice")
uidB := []byte("Bob")
@ -101,6 +104,11 @@ func TestSetPeerParameters(t *testing.T) {
}
// 设置对端参数
err = initiator.SetPeerParameters(&priv3.PublicKey, uidB)
if err == nil {
t.Errorf("should be failed")
}
err = initiator.SetPeerParameters(&priv2.PublicKey, uidB)
if err != nil {
t.Fatal(err)

View File

@ -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")
}

62
sm2/sm2ec/elliptic.go Normal file
View File

@ -0,0 +1,62 @@
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
}
// Since golang 1.19
// unmarshaler is implemented by curves with their own constant-time Unmarshal.
// There isn't an equivalent interface for Marshal/MarshalCompressed because
// that doesn't involve any mathematical operations, only FillBytes and Bit.
type unmarshaler interface {
Unmarshal([]byte) (x, y *big.Int)
UnmarshalCompressed([]byte) (x, y *big.Int)
}
func Unmarshal(curve elliptic.Curve, data []byte) (x, y *big.Int) {
if c, ok := curve.(unmarshaler); ok {
return c.Unmarshal(data)
}
return elliptic.Unmarshal(curve, data)
}
// UnmarshalCompressed converts a point, serialized by MarshalCompressed, into
// an x, y pair. It is an error if the point is not in compressed form, is not
// on the curve, or is the point at infinity. On error, x = nil.
func UnmarshalCompressed(curve elliptic.Curve, data []byte) (x, y *big.Int) {
if c, ok := curve.(unmarshaler); ok {
return c.UnmarshalCompressed(data)
}
return elliptic.UnmarshalCompressed(curve, data)
}

348
sm2/sm2ec/elliptic_test.go Normal file
View 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 := 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 := 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 := 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 := 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 := 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 := 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 := 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 := 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 := 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
View 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)
}

View File

@ -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)
}

View File

@ -1,10 +1,7 @@
package sm2
package sm2ec
import (
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"fmt"
"math/big"
"testing"
)
@ -142,11 +139,6 @@ var p256MultTests = []scalarMultTest{
},
}
func genericParamsForCurve(c elliptic.Curve) *elliptic.CurveParams {
d := *(c.Params())
return &d
}
func TestP256BaseMult(t *testing.T) {
p256 := P256()
p256Generic := genericParamsForCurve(p256)
@ -177,6 +169,7 @@ func TestP256BaseMult(t *testing.T) {
}
}
/*
func generateP256MultTests() {
p256 := P256()
p256Generic := genericParamsForCurve(p256)
@ -198,7 +191,7 @@ func generateP256MultTests() {
fmt.Printf("%s\n", hex.EncodeToString(y2.Bytes()))
}
}
*/
func TestP256Mult(t *testing.T) {
p256 := P256()
for i, e := range p256MultTests {

View File

@ -6,8 +6,8 @@ package sm4
import (
"crypto/cipher"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/xor"
)
// Assert that sm4CipherAsm implements the cbcEncAble and cbcDecAble interfaces.
@ -56,7 +56,7 @@ func (x *cbc) CryptBlocks(dst, src []byte) {
if len(dst) < len(src) {
panic("cipher: output smaller than input")
}
if subtle.InexactOverlap(dst[:len(src)], src) {
if alias.InexactOverlap(dst[:len(src)], src) {
panic("cipher: invalid buffer overlap")
}
if len(src) == 0 {
@ -79,7 +79,7 @@ func (x *cbc) CryptBlocks(dst, src []byte) {
for start > 0 {
x.b.DecryptBlocks(temp, src[start:end])
copy(batchSrc, src[start-BlockSize:])
xor.XorBytes(dst[start:], temp, batchSrc)
subtle.XORBytes(dst[start:], temp, batchSrc)
end = start
start -= x.b.blocksSize
}
@ -88,7 +88,7 @@ func (x *cbc) CryptBlocks(dst, src []byte) {
copy(batchSrc[BlockSize:], src[:end])
x.b.DecryptBlocks(temp, batchSrc[BlockSize:])
copy(batchSrc, x.iv)
xor.XorBytes(dst, temp[:end], batchSrc)
subtle.XORBytes(dst, temp[:end], batchSrc)
// Set the new iv to the first block we copied earlier.
x.iv, x.tmp = x.tmp, x.iv

View File

@ -5,7 +5,7 @@ import (
"crypto/cipher"
"fmt"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/alias"
)
// BlockSize the sm4 block size in bytes.
@ -49,7 +49,7 @@ func (c *sm4Cipher) Encrypt(dst, src []byte) {
if len(dst) < BlockSize {
panic("sm4: output not full block")
}
if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlockGo(c.enc, dst, src)
@ -62,7 +62,7 @@ func (c *sm4Cipher) Decrypt(dst, src []byte) {
if len(dst) < BlockSize {
panic("sm4: output not full block")
}
if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("sm4: invalid buffer overlap")
}
decryptBlockGo(c.dec, dst, src)

View File

@ -6,7 +6,7 @@ package sm4
import (
"crypto/cipher"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/alias"
"golang.org/x/sys/cpu"
)
@ -65,7 +65,7 @@ func (c *sm4CipherAsm) Encrypt(dst, src []byte) {
if len(dst) < BlockSize {
panic("sm4: output not full block")
}
if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlockAsm(&c.enc[0], &dst[0], &src[0], INST_AES)
@ -78,7 +78,7 @@ func (c *sm4CipherAsm) EncryptBlocks(dst, src []byte) {
if len(dst) < c.blocksSize {
panic("sm4: output not full blocks")
}
if subtle.InexactOverlap(dst[:c.blocksSize], src[:c.blocksSize]) {
if alias.InexactOverlap(dst[:c.blocksSize], src[:c.blocksSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlocksAsm(&c.enc[0], dst, src, INST_AES)
@ -91,7 +91,7 @@ func (c *sm4CipherAsm) Decrypt(dst, src []byte) {
if len(dst) < BlockSize {
panic("sm4: output not full block")
}
if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlockAsm(&c.dec[0], &dst[0], &src[0], INST_AES)
@ -104,7 +104,7 @@ func (c *sm4CipherAsm) DecryptBlocks(dst, src []byte) {
if len(dst) < c.blocksSize {
panic("sm4: output not full blocks")
}
if subtle.InexactOverlap(dst[:c.blocksSize], src[:c.blocksSize]) {
if alias.InexactOverlap(dst[:c.blocksSize], src[:c.blocksSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlocksAsm(&c.dec[0], dst, src, INST_AES)

View File

@ -6,7 +6,7 @@ package sm4
import (
"crypto/cipher"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/alias"
)
type sm4CipherNI struct {
@ -29,7 +29,7 @@ func (c *sm4CipherNI) Encrypt(dst, src []byte) {
if len(dst) < BlockSize {
panic("sm4: output not full block")
}
if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlockAsm(&c.enc[0], &dst[0], &src[0], INST_SM4)
@ -42,7 +42,7 @@ func (c *sm4CipherNI) Decrypt(dst, src []byte) {
if len(dst) < BlockSize {
panic("sm4: output not full block")
}
if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlockAsm(&c.dec[0], &dst[0], &src[0], INST_SM4)

View File

@ -6,8 +6,8 @@ package sm4
import (
"crypto/cipher"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/xor"
)
// Assert that sm4CipherAsm implements the ctrAble interface.
@ -83,14 +83,14 @@ func (x *ctr) XORKeyStream(dst, src []byte) {
if len(dst) < len(src) {
panic("cipher: output smaller than input")
}
if subtle.InexactOverlap(dst[:len(src)], src) {
if alias.InexactOverlap(dst[:len(src)], src) {
panic("cipher: invalid buffer overlap")
}
for len(src) > 0 {
if x.outUsed >= len(x.out)-BlockSize {
x.refill()
}
n := xor.XorBytes(dst, src, x.out[x.outUsed:])
n := subtle.XORBytes(dst, src, x.out[x.outUsed:])
dst = dst[n:]
src = src[n:]
x.outUsed += n

View File

@ -9,8 +9,8 @@ import (
"encoding/binary"
"errors"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/xor"
)
// Assert that sm4CipherAsm implements the gcmAble interface.
@ -86,8 +86,8 @@ func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
panic("cipher: message too large for GCM")
}
ret, out := subtle.SliceForAppend(dst, len(plaintext)+g.tagSize)
if subtle.InexactOverlap(out, plaintext) {
ret, out := alias.SliceForAppend(dst, len(plaintext)+g.tagSize)
if alias.InexactOverlap(out, plaintext) {
panic("cipher: invalid buffer overlap")
}
@ -137,8 +137,8 @@ func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
var expectedTag [gcmTagSize]byte
g.auth(expectedTag[:], ciphertext, data, &tagMask)
ret, out := subtle.SliceForAppend(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
ret, out := alias.SliceForAppend(dst, len(ciphertext))
if alias.InexactOverlap(out, ciphertext) {
panic("cipher: invalid buffer overlap")
}
@ -274,7 +274,7 @@ func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
gcmInc32(counter)
}
g.cipher.EncryptBlocks(mask, counters)
xor.XorWords(out, in, mask[:])
subtle.XORBytes(out, in, mask[:])
out = out[g.cipher.blocksSize:]
in = in[g.cipher.blocksSize:]
}
@ -286,7 +286,7 @@ func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
gcmInc32(counter)
}
g.cipher.EncryptBlocks(mask, counters)
xor.XorBytes(out, in, mask[:blocks*gcmBlockSize])
subtle.XORBytes(out, in, mask[:blocks*gcmBlockSize])
}
}
@ -328,5 +328,5 @@ func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]
binary.BigEndian.PutUint64(out, y.low)
binary.BigEndian.PutUint64(out[8:], y.high)
xor.XorWords(out, out, tagMask[:])
subtle.XORBytes(out, out, tagMask[:])
}

View File

@ -5,9 +5,9 @@ package sm4
import (
"crypto/cipher"
goSubtle "crypto/subtle"
"crypto/subtle"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/alias"
)
// sm4CipherGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM
@ -86,8 +86,8 @@ func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
var tagOut [gcmTagSize]byte
gcmSm4Data(&g.bytesProductTable, data, &tagOut)
ret, out := subtle.SliceForAppend(dst, len(plaintext)+g.tagSize)
if subtle.InexactOverlap(out[:len(plaintext)], plaintext) {
ret, out := alias.SliceForAppend(dst, len(plaintext)+g.tagSize)
if alias.InexactOverlap(out[:len(plaintext)], plaintext) {
panic("cipher: invalid buffer overlap")
}
@ -140,8 +140,8 @@ func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
var expectedTag [gcmTagSize]byte
gcmSm4Data(&g.bytesProductTable, data, &expectedTag)
ret, out := subtle.SliceForAppend(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
ret, out := alias.SliceForAppend(dst, len(ciphertext))
if alias.InexactOverlap(out, ciphertext) {
panic("cipher: invalid buffer overlap")
}
if len(ciphertext) > 0 {
@ -149,7 +149,7 @@ func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
}
gcmSm4Finish(&g.bytesProductTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data)))
if goSubtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
for i := range out {
out[i] = 0
}

View File

@ -5,9 +5,9 @@ package sm4
import (
"crypto/cipher"
goSubtle "crypto/subtle"
"crypto/subtle"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/alias"
)
//go:noescape
@ -79,8 +79,8 @@ func (g *gcmNI) Seal(dst, nonce, plaintext, data []byte) []byte {
var tagOut [gcmTagSize]byte
gcmSm4Data(&g.bytesProductTable, data, &tagOut)
ret, out := subtle.SliceForAppend(dst, len(plaintext)+g.tagSize)
if subtle.InexactOverlap(out[:len(plaintext)], plaintext) {
ret, out := alias.SliceForAppend(dst, len(plaintext)+g.tagSize)
if alias.InexactOverlap(out[:len(plaintext)], plaintext) {
panic("cipher: invalid buffer overlap")
}
@ -133,8 +133,8 @@ func (g *gcmNI) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
var expectedTag [gcmTagSize]byte
gcmSm4Data(&g.bytesProductTable, data, &expectedTag)
ret, out := subtle.SliceForAppend(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
ret, out := alias.SliceForAppend(dst, len(ciphertext))
if alias.InexactOverlap(out, ciphertext) {
panic("cipher: invalid buffer overlap")
}
if len(ciphertext) > 0 {
@ -142,7 +142,7 @@ func (g *gcmNI) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
}
gcmSm4Finish(&g.bytesProductTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data)))
if goSubtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
for i := range out {
out[i] = 0
}

View File

@ -3,14 +3,14 @@ package sm9
import (
"crypto"
"crypto/subtle"
goSubtle "crypto/subtle"
"encoding/binary"
"errors"
"fmt"
"io"
"math/big"
"github.com/emmansun/gmsm/internal/xor"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/sm3"
"github.com/emmansun/gmsm/sm9/bn256"
"golang.org/x/crypto/cryptobyte"
@ -325,7 +325,7 @@ func Encrypt(rand io.Reader, pub *EncryptMasterPublicKey, uid []byte, hid byte,
if err != nil {
return nil, err
}
xor.XorBytes(key, key[:len(plaintext)], plaintext)
subtle.XORBytes(key, key[:len(plaintext)], plaintext)
hash := sm3.New()
hash.Write(key)
@ -349,7 +349,7 @@ func (pub *EncryptMasterPublicKey) Encrypt(rand io.Reader, uid []byte, hid byte,
if err != nil {
return nil, err
}
xor.XorBytes(key, key[:len(plaintext)], plaintext)
subtle.XORBytes(key, key[:len(plaintext)], plaintext)
hash := sm3.New()
hash.Write(key)
@ -385,11 +385,11 @@ func Decrypt(priv *EncryptPrivateKey, uid, ciphertext []byte) ([]byte, error) {
hash.Write(key[len(c2):])
c32 := hash.Sum(nil)
if subtle.ConstantTimeCompare(c3[:sm3.Size], c32) != 1 {
if goSubtle.ConstantTimeCompare(c3[:sm3.Size], c32) != 1 {
return nil, errors.New("sm9: invalid mac value")
}
xor.XorBytes(key, c2, key[:len(c2)])
subtle.XORBytes(key, c2, key[:len(c2)])
return key[:len(c2)], nil
}
@ -437,10 +437,10 @@ func DecryptASN1(priv *EncryptPrivateKey, uid, ciphertext []byte) ([]byte, error
hash.Write(key[len(c2Bytes):])
c32 := hash.Sum(nil)
if subtle.ConstantTimeCompare(c3Bytes, c32) != 1 {
if goSubtle.ConstantTimeCompare(c3Bytes, c32) != 1 {
return nil, errors.New("sm9: invalid mac value")
}
xor.XorBytes(key, c2Bytes, key[:len(c2Bytes)])
subtle.XORBytes(key, c2Bytes, key[:len(c2Bytes)])
return key[:len(c2Bytes)], nil
}
@ -597,7 +597,7 @@ func (ke *KeyExchange) ConfirmResponder(rB *bn256.G1, sB []byte) ([]byte, error)
// step 6, verify signature
if len(sB) > 0 {
signature := ke.sign(false, 0x82)
if subtle.ConstantTimeCompare(signature, sB) != 1 {
if goSubtle.ConstantTimeCompare(signature, sB) != 1 {
return nil, errors.New("sm9: invalid responder's signature")
}
}
@ -611,7 +611,7 @@ func (ke *KeyExchange) ConfirmResponder(rB *bn256.G1, sB []byte) ([]byte, error)
// ConfirmInitiator for responder's step B8
func (ke *KeyExchange) ConfirmInitiator(s1 []byte) error {
buffer := ke.sign(true, 0x83)
if subtle.ConstantTimeCompare(buffer, s1) != 1 {
if goSubtle.ConstantTimeCompare(buffer, s1) != 1 {
return errors.New("sm9: invalid initiator's signature")
}
return nil

View File

@ -6,7 +6,7 @@ import (
"math/big"
"testing"
"github.com/emmansun/gmsm/internal/xor"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/sm3"
"github.com/emmansun/gmsm/sm9/bn256"
)
@ -493,7 +493,7 @@ func TestEncryptSM9Sample(t *testing.T) {
if hex.EncodeToString(key) != expectedKey {
t.Errorf("not expected key")
}
xor.XorBytes(key, key[:len(plaintext)], plaintext)
subtle.XORBytes(key, key[:len(plaintext)], plaintext)
hash := sm3.New()
hash.Write(key)

View File

@ -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)
}
@ -76,28 +78,29 @@ func loadSystemRoots() (*CertPool, error) {
return nil, firstErr
}
// readUniqueDirectoryEntries is like ioutil.ReadDir but omits
// readUniqueDirectoryEntries is like os.ReadDir but omits
// symlinks that point within the directory.
func readUniqueDirectoryEntries(dir string) ([]os.FileInfo, error) {
fis, err := ioutil.ReadDir(dir)
func readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) {
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
}
// 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, "/")
}

View File

@ -4,8 +4,8 @@ import (
"crypto/cipher"
"encoding/binary"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/internal/xor"
)
const RoundWords = 32
@ -35,7 +35,7 @@ func xorKeyStreamGeneric(c *zucState32, dst, src []byte) {
for j := 0; j < RoundWords; j++ {
binary.BigEndian.PutUint32(keyBytes[j*4:], keyWords[j])
}
xor.XorBytes(dst, src, keyBytes[:])
subtle.XORBytes(dst, src, keyBytes[:])
dst = dst[RoundWords*4:]
src = src[RoundWords*4:]
}
@ -44,7 +44,7 @@ func xorKeyStreamGeneric(c *zucState32, dst, src []byte) {
for j := 0; j < words-rounds*RoundWords; j++ {
binary.BigEndian.PutUint32(keyBytes[j*4:], keyWords[j])
}
xor.XorBytes(dst, src, keyBytes[:])
subtle.XORBytes(dst, src, keyBytes[:])
}
}
@ -52,7 +52,7 @@ func (c *zucState32) XORKeyStream(dst, src []byte) {
if len(dst) < len(src) {
panic("zuc: output smaller than input")
}
if subtle.InexactOverlap(dst[:len(src)], src) {
if alias.InexactOverlap(dst[:len(src)], src) {
panic("zuc: invalid buffer overlap")
}
xorKeyStream(c, dst, src)

View File

@ -4,7 +4,7 @@
package zuc
import (
"github.com/emmansun/gmsm/internal/xor"
"github.com/emmansun/gmsm/internal/subtle"
)
//go:noescape
@ -17,13 +17,13 @@ func xorKeyStream(c *zucState32, dst, src []byte) {
if words > 0 {
dstWords := dst[:words*4]
genKeyStreamRev32Asm(dstWords, c)
xor.XorBytes(dst, src, dstWords)
subtle.XORBytes(dst, src, dstWords)
}
// handle remain bytes
if words*4 < len(src) {
var singleWord [4]byte
genKeyStreamRev32Asm(singleWord[:], c)
xor.XorBytes(dst[words*4:], src[words*4:], singleWord[:])
subtle.XORBytes(dst[words*4:], src[words*4:], singleWord[:])
}
} else {
xorKeyStreamGeneric(c, dst, src)