mirror of
https://github.com/emmansun/gmsm.git
synced 2025-05-12 03:56:17 +08:00
Merge pull request #78 from emmansun/20221015
Upgrade minimum supported golang version to 1.16
This commit is contained in:
commit
f2245485bc
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -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
47
.github/workflows/ci_20221015.yml
vendored
Normal 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
|
@ -6,7 +6,7 @@ jobs:
|
||||
virt: vm
|
||||
os: linux
|
||||
dist: focal
|
||||
go: 1.15.x
|
||||
go: 1.16.x
|
||||
group: edge
|
||||
|
||||
install:
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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
2
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/emmansun/gmsm
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
|
42
internal/alias/alias_test.go
Normal file
42
internal/alias/alias_test.go
Normal 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)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package subtle
|
||||
package alias
|
||||
|
||||
import "unsafe"
|
||||
|
25
internal/sm2ec/fiat/fiat_test.go
Normal file
25
internal/sm2ec/fiat/fiat_test.go
Normal 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)
|
||||
}
|
||||
}
|
@ -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
109
internal/sm2ec/p256_asm_ordinv.go
Normal file
109
internal/sm2ec/p256_asm_ordinv.go
Normal file
@ -0,0 +1,109 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import "errors"
|
||||
|
||||
// Montgomery multiplication modulo org(G). Sets res = in1 * in2 * R⁻¹.
|
||||
//
|
||||
//go:noescape
|
||||
func p256OrdMul(res, in1, in2 *p256OrdElement)
|
||||
|
||||
// Montgomery square modulo org(G), repeated n times (n >= 1).
|
||||
//
|
||||
//go:noescape
|
||||
func p256OrdSqr(res, in *p256OrdElement, n int)
|
||||
|
||||
// P256OrdInverse, sets out to in⁻¹ mod org(G). If in is zero, out will be zero.
|
||||
// n-2 =
|
||||
// 1111111111111111111111111111111011111111111111111111111111111111
|
||||
// 1111111111111111111111111111111111111111111111111111111111111111
|
||||
// 0111001000000011110111110110101100100001110001100000010100101011
|
||||
// 0101001110111011111101000000100100111001110101010100000100100001
|
||||
//
|
||||
func P256OrdInverse(k []byte) ([]byte, error) {
|
||||
if len(k) != 32 {
|
||||
return nil, errors.New("invalid scalar length")
|
||||
}
|
||||
x := new(p256OrdElement)
|
||||
p256OrdBigToLittle(x, toElementArray(k))
|
||||
|
||||
// Inversion is implemented as exponentiation by n - 2, per Fermat's little theorem.
|
||||
//
|
||||
// The sequence of 43 multiplications and 254 squarings is derived from
|
||||
// https://briansmith.org/ecc-inversion-addition-chains-01#p256_scalar_inversion
|
||||
_1 := new(p256OrdElement)
|
||||
_11 := new(p256OrdElement)
|
||||
_101 := new(p256OrdElement)
|
||||
_111 := new(p256OrdElement)
|
||||
_1111 := new(p256OrdElement)
|
||||
_10101 := new(p256OrdElement)
|
||||
_101111 := new(p256OrdElement)
|
||||
t := new(p256OrdElement)
|
||||
m := new(p256OrdElement)
|
||||
|
||||
// This code operates in the Montgomery domain where R = 2²⁵⁶ mod n and n is
|
||||
// the order of the scalar field. Elements in the Montgomery domain take the
|
||||
// form a×R and p256OrdMul calculates (a × b × R⁻¹) mod n. RR is R in the
|
||||
// domain, or R×R mod n, thus p256OrdMul(x, RR) gives x×R, i.e. converts x
|
||||
// into the Montgomery domain.
|
||||
RR := &p256OrdElement{0x901192af7c114f20, 0x3464504ade6fa2fa, 0x620fc84c3affe0d4, 0x1eb5e412a22b3d3b}
|
||||
|
||||
p256OrdMul(_1, x, RR) // _1 , 2^0
|
||||
p256OrdSqr(m, _1, 1) // _10, 2^1
|
||||
p256OrdMul(_11, m, _1) // _11, 2^1 + 2^0
|
||||
p256OrdMul(_101, m, _11) // _101, 2^2 + 2^0
|
||||
p256OrdMul(_111, m, _101) // _111, 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(x, _101, 1) // _1010, 2^3 + 2^1
|
||||
p256OrdMul(_1111, _101, x) // _1111, 2^3 + 2^2 + 2^1 + 2^0
|
||||
|
||||
p256OrdSqr(t, x, 1) // _10100, 2^4 + 2^2
|
||||
p256OrdMul(_10101, t, _1) // _10101, 2^4 + 2^2 + 2^0
|
||||
p256OrdSqr(x, _10101, 1) // _101010, 2^5 + 2^3 + 2^1
|
||||
p256OrdMul(_101111, _101, x) // _101111, 2^5 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdMul(x, _10101, x) // _111111 = x6, 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(t, x, 2) // _11111100, 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2
|
||||
|
||||
p256OrdMul(m, t, m) // _11111110 = x8, , 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1
|
||||
p256OrdMul(t, t, _11) // _11111111 = x8, , 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(x, t, 8) // _ff00, 2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8
|
||||
p256OrdMul(m, x, m) // _fffe
|
||||
p256OrdMul(x, x, t) // _ffff = x16, 2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8 + 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
|
||||
p256OrdSqr(t, x, 16) // _ffff0000, 2^31 + 2^30 + 2^29 + 2^28 + 2^27 + 2^26 + 2^25 + 2^24 + 2^23 + 2^22 + 2^21 + 2^20 + 2^19 + 2^18 + 2^17 + 2^16
|
||||
p256OrdMul(m, t, m) // _fffffffe
|
||||
p256OrdMul(t, t, x) // _ffffffff = x32
|
||||
|
||||
p256OrdSqr(x, m, 32) // _fffffffe00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffff
|
||||
p256OrdSqr(x, x, 32) // _fffffffeffffffff00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffffffffffff
|
||||
p256OrdSqr(x, x, 32) // _fffffffeffffffffffffffff00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffffffffffffffffffff
|
||||
|
||||
sqrs := []uint8{
|
||||
4, 3, 11, 5, 3, 5, 1,
|
||||
3, 7, 5, 9, 7, 5, 5,
|
||||
4, 5, 2, 2, 7, 3, 5,
|
||||
5, 6, 2, 6, 3, 5,
|
||||
}
|
||||
muls := []*p256OrdElement{
|
||||
_111, _1, _1111, _1111, _101, _10101, _1,
|
||||
_1, _111, _11, _101, _10101, _10101, _111,
|
||||
_111, _1111, _11, _1, _1, _1, _111,
|
||||
_111, _10101, _1, _1, _1, _1}
|
||||
|
||||
for i, s := range sqrs {
|
||||
p256OrdSqr(x, x, int(s))
|
||||
p256OrdMul(x, x, muls[i])
|
||||
}
|
||||
// Montgomery multiplication by R⁻¹, or 1 outside the domain as R⁻¹×R = 1,
|
||||
// converts a Montgomery value out of the domain.
|
||||
one := &p256OrdElement{1}
|
||||
p256OrdMul(x, x, one)
|
||||
|
||||
var xOut [32]byte
|
||||
p256OrdLittleToBig(&xOut, x)
|
||||
return xOut[:], nil
|
||||
}
|
92
internal/sm2ec/p256_asm_ordinv_test.go
Normal file
92
internal/sm2ec/p256_asm_ordinv_test.go
Normal file
@ -0,0 +1,92 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/sm2ec"
|
||||
elliptic "github.com/emmansun/gmsm/sm2/sm2ec"
|
||||
)
|
||||
|
||||
func TestP256OrdInverse(t *testing.T) {
|
||||
N := elliptic.P256().Params().N
|
||||
|
||||
// inv(0) is expected to be 0.
|
||||
zero := make([]byte, 32)
|
||||
out, err := sm2ec.P256OrdInverse(zero)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, zero) {
|
||||
t.Error("unexpected output for inv(0)")
|
||||
}
|
||||
|
||||
// inv(N) is also 0 mod N.
|
||||
input := make([]byte, 32)
|
||||
N.FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, zero) {
|
||||
t.Error("unexpected output for inv(N)")
|
||||
}
|
||||
if !bytes.Equal(input, N.Bytes()) {
|
||||
t.Error("input was modified")
|
||||
}
|
||||
|
||||
// Check inv(1) and inv(N+1) against math/big
|
||||
exp := new(big.Int).ModInverse(big.NewInt(1), N).FillBytes(make([]byte, 32))
|
||||
big.NewInt(1).FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(1)")
|
||||
}
|
||||
new(big.Int).Add(N, big.NewInt(1)).FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(N+1)")
|
||||
}
|
||||
|
||||
// Check inv(20) and inv(N+20) against math/big
|
||||
exp = new(big.Int).ModInverse(big.NewInt(20), N).FillBytes(make([]byte, 32))
|
||||
big.NewInt(20).FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(20)")
|
||||
}
|
||||
new(big.Int).Add(N, big.NewInt(20)).FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(N+20)")
|
||||
}
|
||||
|
||||
// Check inv(2^256-1) against math/big
|
||||
bigInput := new(big.Int).Lsh(big.NewInt(1), 256)
|
||||
bigInput.Sub(bigInput, big.NewInt(1))
|
||||
exp = new(big.Int).ModInverse(bigInput, N).FillBytes(make([]byte, 32))
|
||||
bigInput.FillBytes(input)
|
||||
out, err = sm2ec.P256OrdInverse(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, exp) {
|
||||
t.Error("unexpected output for inv(2^256-1)")
|
||||
}
|
||||
}
|
BIN
internal/sm2ec/p256_asm_table.bin
Normal file
BIN
internal/sm2ec/p256_asm_table.bin
Normal file
Binary file not shown.
46
internal/sm2ec/p256_asm_table_test.go
Normal file
46
internal/sm2ec/p256_asm_table_test.go
Normal file
@ -0,0 +1,46 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestP256PrecomputedTable(t *testing.T) {
|
||||
base := NewP256Point().SetGenerator()
|
||||
|
||||
for i := 0; i < 43; i++ {
|
||||
t.Run(fmt.Sprintf("table[%d]", i), func(t *testing.T) {
|
||||
testP256AffineTable(t, base, &p256Precomputed[i])
|
||||
})
|
||||
|
||||
for k := 0; k < 6; k++ {
|
||||
base.Double(base)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testP256AffineTable(t *testing.T, base *P256Point, table *p256AffineTable) {
|
||||
p := NewP256Point()
|
||||
zInv := new(p256Element)
|
||||
zInvSq := new(p256Element)
|
||||
|
||||
for j := 0; j < 32; j++ {
|
||||
p.Add(p, base)
|
||||
|
||||
// Convert p to affine coordinates.
|
||||
p256Inverse(zInv, &p.z)
|
||||
p256Sqr(zInvSq, zInv, 1)
|
||||
p256Mul(zInv, zInv, zInvSq)
|
||||
|
||||
p256Mul(&p.x, &p.x, zInvSq)
|
||||
p256Mul(&p.y, &p.y, zInv)
|
||||
p.z = p256One
|
||||
|
||||
if p256Equal(&table[j].x, &p.x) != 1 || p256Equal(&table[j].y, &p.y) != 1 {
|
||||
t.Fatalf("incorrect table entry at index %d", j)
|
||||
}
|
||||
}
|
||||
}
|
882
internal/sm2ec/sm2p256_asm.go
Normal file
882
internal/sm2ec/sm2p256_asm.go
Normal file
@ -0,0 +1,882 @@
|
||||
// It is by standing on the shoulders of giants.
|
||||
|
||||
// This file contains the Go wrapper for the constant-time, 64-bit assembly
|
||||
// implementation of P256. The optimizations performed here are described in
|
||||
// detail in:
|
||||
// S.Gueron and V.Krasnov, "Fast prime field elliptic-curve cryptography with
|
||||
// 256-bit primes"
|
||||
// https://link.springer.com/article/10.1007%2Fs13389-014-0090-x
|
||||
// https://eprint.iacr.org/2013/816.pdf
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// p256Element is a P-256 base field element in [0, P-1] in the Montgomery
|
||||
// domain (with R 2²⁵⁶) as four limbs in little-endian order value.
|
||||
type p256Element [4]uint64
|
||||
|
||||
// p256One is one in the Montgomery domain.
|
||||
var p256One = p256Element{0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000}
|
||||
|
||||
var p256Zero = p256Element{}
|
||||
|
||||
// p256P is 2^256 - 2^224 - 2^96 + 2^64 - 1.
|
||||
var p256P = p256Element{0xffffffffffffffff, 0xffffffff00000000,
|
||||
0xffffffffffffffff, 0xfffffffeffffffff}
|
||||
|
||||
// P256Point is a P-256 point. The zero value should not be assumed to be valid
|
||||
// (although it is in this implementation).
|
||||
type P256Point struct {
|
||||
// (X:Y:Z) are Jacobian coordinates where x = X/Z² and y = Y/Z³. The point
|
||||
// at infinity can be represented by any set of coordinates with Z = 0.
|
||||
x, y, z p256Element
|
||||
}
|
||||
|
||||
// NewP256Point returns a new P256Point representing the point at infinity.
|
||||
func NewP256Point() *P256Point {
|
||||
return &P256Point{
|
||||
x: p256One, y: p256One, z: p256Zero,
|
||||
}
|
||||
}
|
||||
|
||||
// SetGenerator sets p to the canonical generator and returns p.
|
||||
func (p *P256Point) SetGenerator() *P256Point {
|
||||
p.x = p256Element{0x61328990f418029e, 0x3e7981eddca6c050,
|
||||
0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
p.y = p256Element{0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa,
|
||||
0x8d4cfb066e2a48f8, 0x63cd65d481d735bd}
|
||||
p.z = p256One
|
||||
return p
|
||||
}
|
||||
|
||||
// Set sets p = q and returns p.
|
||||
func (p *P256Point) Set(q *P256Point) *P256Point {
|
||||
p.x, p.y, p.z = q.x, q.y, q.z
|
||||
return p
|
||||
}
|
||||
|
||||
const p256ElementLength = 32
|
||||
const p256UncompressedLength = 1 + 2*p256ElementLength
|
||||
const p256CompressedLength = 1 + p256ElementLength
|
||||
|
||||
// toElementArray, convert slice of bytes to pointer to [32]byte.
|
||||
// This function is required for low version of golang, can type cast directly
|
||||
// since golang 1.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)
|
||||
}
|
135
internal/sm2ec/sm2p256_asm_test.go
Normal file
135
internal/sm2ec/sm2p256_asm_test.go
Normal file
@ -0,0 +1,135 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// fromBig converts a *big.Int into a format used by this code.
|
||||
func fromBig(out *p256Element, big *big.Int) {
|
||||
for i := range out {
|
||||
out[i] = 0
|
||||
}
|
||||
|
||||
for i, v := range big.Bits() {
|
||||
out[i] = uint64(v)
|
||||
}
|
||||
}
|
||||
|
||||
func toBigInt(in *p256Element) *big.Int {
|
||||
var valBytes [32]byte
|
||||
p256LittleToBig(&valBytes, in)
|
||||
return new(big.Int).SetBytes(valBytes[:])
|
||||
}
|
||||
|
||||
func p256MulTest(t *testing.T, x, y, p, r *big.Int) {
|
||||
x1 := new(big.Int).Mul(x, r)
|
||||
x1 = x1.Mod(x1, p)
|
||||
y1 := new(big.Int).Mul(y, r)
|
||||
y1 = y1.Mod(y1, p)
|
||||
ax := new(p256Element)
|
||||
ay := new(p256Element)
|
||||
res := new(p256Element)
|
||||
res2 := new(p256Element)
|
||||
fromBig(ax, x1)
|
||||
fromBig(ay, y1)
|
||||
p256Mul(res2, ax, ay)
|
||||
p256FromMont(res, res2)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
expected := new(big.Int).Mul(x, y)
|
||||
expected = expected.Mod(expected, p)
|
||||
if resInt.Cmp(expected) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyP256Mul(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
var scalar1 [32]byte
|
||||
var scalar2 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
io.ReadFull(rand.Reader, scalar2[:])
|
||||
x := new(big.Int).SetBytes(scalar1[:])
|
||||
y := new(big.Int).SetBytes(scalar2[:])
|
||||
p256MulTest(t, x, y, p, r)
|
||||
}
|
||||
}
|
||||
|
||||
func p256SqrTest(t *testing.T, x, p, r *big.Int) {
|
||||
x1 := new(big.Int).Mul(x, r)
|
||||
x1 = x1.Mod(x1, p)
|
||||
ax := new(p256Element)
|
||||
res := new(p256Element)
|
||||
res2 := new(p256Element)
|
||||
fromBig(ax, x1)
|
||||
p256Sqr(res2, ax, 1)
|
||||
p256FromMont(res, res2)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
expected := new(big.Int).Mul(x, x)
|
||||
expected = expected.Mod(expected, p)
|
||||
if resInt.Cmp(expected) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyP256Sqr(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
var scalar1 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
x := new(big.Int).SetBytes(scalar1[:])
|
||||
p256SqrTest(t, x, p, r)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256Inverse(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
gx := &p256Element{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
res := new(p256Element)
|
||||
p256Inverse(res, gx)
|
||||
resInt := toBigInt(res)
|
||||
xInv := new(big.Int).ModInverse(x, p)
|
||||
xInv = new(big.Int).Mul(xInv, r)
|
||||
xInv = new(big.Int).Mod(xInv, p)
|
||||
if resInt.Cmp(xInv) != 0 {
|
||||
t.Errorf("expected %v, got %v", hex.EncodeToString(xInv.Bytes()), hex.EncodeToString(resInt.Bytes()))
|
||||
}
|
||||
}
|
28
internal/subtle/xor_amd64.go
Normal file
28
internal/subtle/xor_amd64.go
Normal 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)
|
@ -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
|
29
internal/subtle/xor_arm64.go
Normal file
29
internal/subtle/xor_arm64.go
Normal 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)
|
@ -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
|
@ -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))
|
||||
}
|
||||
}
|
95
internal/subtle/xor_test.go
Normal file
95
internal/subtle/xor_test.go
Normal 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()
|
||||
}
|
@ -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)
|
@ -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)
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = elliptic.P256()
|
||||
|
||||
func TestFuzz(t *testing.T) {
|
||||
p256 := P256()
|
||||
p256Generic := p256.Params()
|
||||
|
||||
var scalar1 [32]byte
|
||||
var scalar2 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
io.ReadFull(rand.Reader, scalar2[:])
|
||||
|
||||
x, y := p256.ScalarBaseMult(scalar1[:])
|
||||
x2, y2 := p256Generic.ScalarBaseMult(scalar1[:])
|
||||
|
||||
xx, yy := p256.ScalarMult(x, y, scalar2[:])
|
||||
xx2, yy2 := p256Generic.ScalarMult(x2, y2, scalar2[:])
|
||||
|
||||
if x.Cmp(x2) != 0 || y.Cmp(y2) != 0 {
|
||||
t.Fatalf("ScalarBaseMult does not match reference result with scalar: %x, please report this error to https://github.com/emmansun/gmsm/issues", scalar1)
|
||||
}
|
||||
|
||||
if xx.Cmp(xx2) != 0 || yy.Cmp(yy2) != 0 {
|
||||
t.Fatalf("ScalarMult does not match reference result with scalars: %x and %x, please report this error to https://github.com/emmansun/gmsm/issues", scalar1, scalar2)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/format"
|
||||
)
|
||||
|
||||
func GenTables() {
|
||||
buf := new(bytes.Buffer)
|
||||
fmt.Fprint(buf, `
|
||||
// Generated by gen_p256_table.go. DO NOT EDIT.
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
`[1:])
|
||||
|
||||
// Generate precomputed p256 tables.
|
||||
var pre [43][32 * 8]uint64
|
||||
basePoint := []uint64{
|
||||
0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05,
|
||||
0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd,
|
||||
0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000,
|
||||
}
|
||||
t1 := make([]uint64, 12)
|
||||
t2 := make([]uint64, 12)
|
||||
copy(t2, basePoint)
|
||||
zInv := make([]uint64, 4)
|
||||
zInvSq := make([]uint64, 4)
|
||||
for j := 0; j < 32; j++ {
|
||||
copy(t1, t2)
|
||||
for i := 0; i < 43; i++ {
|
||||
// The window size is 6 so we need to double 6 times.
|
||||
if i != 0 {
|
||||
for k := 0; k < 6; k++ {
|
||||
p256PointDoubleAsm(t1, t1)
|
||||
}
|
||||
}
|
||||
// Convert the point to affine form. (Its values are
|
||||
// still in Montgomery form however.)
|
||||
p256Inverse(zInv, t1[8:12])
|
||||
p256Sqr(zInvSq, zInv, 1)
|
||||
p256Mul(zInv, zInv, zInvSq)
|
||||
p256Mul(t1[:4], t1[:4], zInvSq)
|
||||
p256Mul(t1[4:8], t1[4:8], zInv)
|
||||
copy(t1[8:12], basePoint[8:12])
|
||||
// Update the table entry
|
||||
copy(pre[i][j*8:], t1[:8])
|
||||
}
|
||||
if j == 0 {
|
||||
p256PointDoubleAsm(t2, basePoint)
|
||||
} else {
|
||||
p256PointAddAsm(t2, t2, basePoint)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, "const p256Precomputed = \"\" +\n\n")
|
||||
|
||||
// Dump the precomputed tables, flattened, little-endian.
|
||||
// These tables are used directly by assembly on little-endian platforms.
|
||||
// Putting the data in a const string lets it be stored readonly.
|
||||
for i := range &pre {
|
||||
for j, v := range &pre[i] {
|
||||
fmt.Fprintf(buf, "\"")
|
||||
var u8 [8]byte
|
||||
binary.LittleEndian.PutUint64(u8[:], v)
|
||||
for _, b := range &u8 {
|
||||
fmt.Fprintf(buf, "\\x%02x", b)
|
||||
}
|
||||
fmt.Fprintf(buf, "\"")
|
||||
if i < len(pre)-1 || j < len(pre[i])-1 {
|
||||
fmt.Fprint(buf, "+")
|
||||
}
|
||||
if j%8 == 7 {
|
||||
fmt.Fprint(buf, "\n")
|
||||
}
|
||||
}
|
||||
fmt.Fprint(buf, "\n")
|
||||
}
|
||||
|
||||
src := buf.Bytes()
|
||||
fmtsrc, fmterr := format.Source(src)
|
||||
// If formatting failed, keep the original source for debugging.
|
||||
if fmterr == nil {
|
||||
src = fmtsrc
|
||||
}
|
||||
fmt.Println(string(src))
|
||||
}
|
579
sm2/p256_asm.go
579
sm2/p256_asm.go
@ -1,579 +0,0 @@
|
||||
// It is by standing on the shoulders of giants.
|
||||
|
||||
// This file contains the Go wrapper for the constant-time, 64-bit assembly
|
||||
// implementation of P256. The optimizations performed here are described in
|
||||
// detail in:
|
||||
// S.Gueron and V.Krasnov, "Fast prime field elliptic-curve cryptography with
|
||||
// 256-bit primes"
|
||||
// https://link.springer.com/article/10.1007%2Fs13389-014-0090-x
|
||||
// https://eprint.iacr.org/2013/816.pdf
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type (
|
||||
p256Curve struct {
|
||||
*elliptic.CurveParams
|
||||
}
|
||||
|
||||
p256Point struct {
|
||||
xyz [12]uint64
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
p256 p256Curve
|
||||
)
|
||||
|
||||
func initP256() {
|
||||
// 2**256 - 2**224 - 2**96 + 2**64 - 1
|
||||
p256.CurveParams = &elliptic.CurveParams{Name: "sm2p256v1"}
|
||||
p256.P, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
p256.N, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
p256.B, _ = new(big.Int).SetString("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
|
||||
p256.Gx, _ = new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
p256.Gy, _ = new(big.Int).SetString("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)
|
||||
p256.BitSize = 256
|
||||
}
|
||||
|
||||
func (curve p256Curve) Params() *elliptic.CurveParams {
|
||||
return curve.CurveParams
|
||||
}
|
||||
|
||||
// Functions implemented in p256_asm_*64.s
|
||||
// Montgomery multiplication modulo P256
|
||||
//go:noescape
|
||||
func p256Mul(res, in1, in2 []uint64)
|
||||
|
||||
// Montgomery square modulo P256, repeated n times (n >= 1)
|
||||
//go:noescape
|
||||
func p256Sqr(res, in []uint64, n int)
|
||||
|
||||
// Montgomery multiplication by 1
|
||||
//go:noescape
|
||||
func p256FromMont(res, in []uint64)
|
||||
|
||||
// iff cond == 1 val <- -val
|
||||
//go:noescape
|
||||
func p256NegCond(val []uint64, cond int)
|
||||
|
||||
// if cond == 0 res <- b; else res <- a
|
||||
//go:noescape
|
||||
func p256MovCond(res, a, b []uint64, cond int)
|
||||
|
||||
// Endianness swap
|
||||
//go:noescape
|
||||
func p256BigToLittle(res []uint64, in []byte)
|
||||
|
||||
//go:noescape
|
||||
func p256LittleToBig(res []byte, in []uint64)
|
||||
|
||||
// Constant time table access
|
||||
//go:noescape
|
||||
func p256Select(point, table []uint64, idx int)
|
||||
|
||||
//go:noescape
|
||||
func p256SelectBase(point *[12]uint64, table string, idx int)
|
||||
|
||||
// Montgomery multiplication modulo Ord(G)
|
||||
//go:noescape
|
||||
func p256OrdMul(res, in1, in2 []uint64)
|
||||
|
||||
// Montgomery square modulo Ord(G), repeated n times
|
||||
//go:noescape
|
||||
func p256OrdSqr(res, in []uint64, n int)
|
||||
|
||||
// Point add with in2 being affine point
|
||||
// If sign == 1 -> in2 = -in2
|
||||
// If sel == 0 -> res = in1
|
||||
// if zero == 0 -> res = in2
|
||||
//go:noescape
|
||||
func p256PointAddAffineAsm(res, in1, in2 []uint64, sign, sel, zero int)
|
||||
|
||||
// Point add. Returns one if the two input points were equal and zero
|
||||
// otherwise. (Note that, due to the way that the equations work out, some
|
||||
// representations of ∞ are considered equal to everything by this function.)
|
||||
//go:noescape
|
||||
func p256PointAddAsm(res, in1, in2 []uint64) int
|
||||
|
||||
// Point double
|
||||
//go:noescape
|
||||
func p256PointDoubleAsm(res, in []uint64)
|
||||
|
||||
var p256one = []uint64{0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000}
|
||||
|
||||
// Inverse, implements invertible interface, used by Sign()
|
||||
// n-2 =
|
||||
// 1111111111111111111111111111111011111111111111111111111111111111
|
||||
// 1111111111111111111111111111111111111111111111111111111111111111
|
||||
// 0111001000000011110111110110101100100001110001100000010100101011
|
||||
// 0101001110111011111101000000100100111001110101010100000100100001
|
||||
//
|
||||
func (curve p256Curve) Inverse(k *big.Int) *big.Int {
|
||||
if k.Sign() < 0 {
|
||||
// This should never happen.
|
||||
k = new(big.Int).Neg(k)
|
||||
}
|
||||
|
||||
if k.Cmp(p256.N) >= 0 {
|
||||
// This should never happen.
|
||||
k = new(big.Int).Mod(k, p256.N)
|
||||
}
|
||||
|
||||
// table will store precomputed powers of x.
|
||||
var table [4 * 10]uint64
|
||||
var (
|
||||
_1 = table[4*0 : 4*1]
|
||||
_11 = table[4*1 : 4*2]
|
||||
_101 = table[4*2 : 4*3]
|
||||
_111 = table[4*3 : 4*4]
|
||||
_1111 = table[4*4 : 4*5]
|
||||
_10101 = table[4*5 : 4*6]
|
||||
_101111 = table[4*6 : 4*7]
|
||||
x = table[4*7 : 4*8]
|
||||
t = table[4*8 : 4*9]
|
||||
m = table[4*9 : 4*10]
|
||||
)
|
||||
|
||||
fromBig(x[:], k)
|
||||
// This code operates in the Montgomery domain where R = 2^256 mod n
|
||||
// and n is the order of the scalar field. (See initP256 for the
|
||||
// value.) Elements in the Montgomery domain take the form a×R and
|
||||
// multiplication of x and y in the calculates (x × y × R^-1) mod n. RR
|
||||
// is R×R mod n thus the Montgomery multiplication x and RR gives x×R,
|
||||
// i.e. converts x into the Montgomery domain.
|
||||
// Window values borrowed from https://briansmith.org/ecc-inversion-addition-chains-01#p256_scalar_inversion
|
||||
RR := []uint64{0x901192af7c114f20, 0x3464504ade6fa2fa, 0x620fc84c3affe0d4, 0x1eb5e412a22b3d3b}
|
||||
|
||||
p256OrdMul(_1, x, RR) // _1 , 2^0
|
||||
p256OrdSqr(m, _1, 1) // _10, 2^1
|
||||
p256OrdMul(_11, m, _1) // _11, 2^1 + 2^0
|
||||
p256OrdMul(_101, m, _11) // _101, 2^2 + 2^0
|
||||
p256OrdMul(_111, m, _101) // _111, 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(x, _101, 1) // _1010, 2^3 + 2^1
|
||||
p256OrdMul(_1111, _101, x) // _1111, 2^3 + 2^2 + 2^1 + 2^0
|
||||
|
||||
p256OrdSqr(t, x, 1) // _10100, 2^4 + 2^2
|
||||
p256OrdMul(_10101, t, _1) // _10101, 2^4 + 2^2 + 2^0
|
||||
p256OrdSqr(x, _10101, 1) // _101010, 2^5 + 2^3 + 2^1
|
||||
p256OrdMul(_101111, _101, x) // _101111, 2^5 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdMul(x, _10101, x) // _111111 = x6, 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(t, x, 2) // _11111100, 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2
|
||||
|
||||
p256OrdMul(m, t, m) // _11111110 = x8, , 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1
|
||||
p256OrdMul(t, t, _11) // _11111111 = x8, , 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
p256OrdSqr(x, t, 8) // _ff00, 2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8
|
||||
p256OrdMul(m, x, m) // _fffe
|
||||
p256OrdMul(x, x, t) // _ffff = x16, 2^15 + 2^14 + 2^13 + 2^12 + 2^11 + 2^10 + 2^9 + 2^8 + 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
|
||||
|
||||
p256OrdSqr(t, x, 16) // _ffff0000, 2^31 + 2^30 + 2^29 + 2^28 + 2^27 + 2^26 + 2^25 + 2^24 + 2^23 + 2^22 + 2^21 + 2^20 + 2^19 + 2^18 + 2^17 + 2^16
|
||||
p256OrdMul(m, t, m) // _fffffffe
|
||||
p256OrdMul(t, t, x) // _ffffffff = x32
|
||||
|
||||
p256OrdSqr(x, m, 32) // _fffffffe00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffff
|
||||
p256OrdSqr(x, x, 32) // _fffffffeffffffff00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffffffffffff
|
||||
p256OrdSqr(x, x, 32) // _fffffffeffffffffffffffff00000000
|
||||
p256OrdMul(x, x, t) // _fffffffeffffffffffffffffffffffff
|
||||
|
||||
sqrs := []uint8{
|
||||
4, 3, 11, 5, 3, 5, 1,
|
||||
3, 7, 5, 9, 7, 5, 5,
|
||||
4, 5, 2, 2, 7, 3, 5,
|
||||
5, 6, 2, 6, 3, 5,
|
||||
}
|
||||
muls := [][]uint64{
|
||||
_111, _1, _1111, _1111, _101, _10101, _1,
|
||||
_1, _111, _11, _101, _10101, _10101, _111,
|
||||
_111, _1111, _11, _1, _1, _1, _111,
|
||||
_111, _10101, _1, _1, _1, _1}
|
||||
|
||||
for i, s := range sqrs {
|
||||
p256OrdSqr(x, x, int(s))
|
||||
p256OrdMul(x, x, muls[i])
|
||||
}
|
||||
|
||||
// Multiplying by one in the Montgomery domain converts a Montgomery
|
||||
// value out of the domain.
|
||||
one := []uint64{1, 0, 0, 0}
|
||||
p256OrdMul(x, x, one)
|
||||
|
||||
xOut := make([]byte, 32)
|
||||
p256LittleToBig(xOut, x)
|
||||
return new(big.Int).SetBytes(xOut)
|
||||
}
|
||||
|
||||
// fromBig converts a *big.Int into a format used by this code.
|
||||
func fromBig(out []uint64, big *big.Int) {
|
||||
for i := range out {
|
||||
out[i] = 0
|
||||
}
|
||||
|
||||
for i, v := range big.Bits() {
|
||||
out[i] = uint64(v)
|
||||
}
|
||||
}
|
||||
|
||||
// p256GetScalar endian-swaps the big-endian scalar value from in and writes it
|
||||
// to out. If the scalar is equal or greater than the order of the group, it's
|
||||
// reduced modulo that order.
|
||||
func p256GetScalar(out []uint64, in []byte) {
|
||||
n := new(big.Int).SetBytes(in)
|
||||
|
||||
if n.Cmp(p256.N) >= 0 {
|
||||
n.Mod(n, p256.N)
|
||||
}
|
||||
fromBig(out, n)
|
||||
}
|
||||
|
||||
// p256Mul operates in a Montgomery domain with R = 2^256 mod p, where p is the
|
||||
// underlying field of the curve. (See initP256 for the value.) Thus rr here is
|
||||
// R×R mod p. See comment in Inverse about how this is used.
|
||||
var rr = []uint64{0x200000003, 0x2ffffffff, 0x100000001, 0x400000002}
|
||||
|
||||
func maybeReduceModP(in *big.Int) *big.Int {
|
||||
if in.Cmp(p256.P) < 0 {
|
||||
return in
|
||||
}
|
||||
return new(big.Int).Mod(in, p256.P)
|
||||
}
|
||||
|
||||
func (curve p256Curve) CombinedMult(bigX, bigY *big.Int, baseScalar, scalar []byte) (x, y *big.Int) {
|
||||
scalarReversed := make([]uint64, 4)
|
||||
var r1, r2 p256Point
|
||||
p256GetScalar(scalarReversed, baseScalar)
|
||||
r1IsInfinity := scalarIsZero(scalarReversed)
|
||||
r1.p256BaseMult(scalarReversed)
|
||||
|
||||
p256GetScalar(scalarReversed, scalar)
|
||||
r2IsInfinity := scalarIsZero(scalarReversed)
|
||||
fromBig(r2.xyz[0:4], maybeReduceModP(bigX))
|
||||
fromBig(r2.xyz[4:8], maybeReduceModP(bigY))
|
||||
p256Mul(r2.xyz[0:4], r2.xyz[0:4], rr[:])
|
||||
p256Mul(r2.xyz[4:8], r2.xyz[4:8], rr[:])
|
||||
|
||||
// This sets r2's Z value to 1, in the Montgomery domain.
|
||||
r2.xyz[8] = p256one[0]
|
||||
r2.xyz[9] = p256one[1]
|
||||
r2.xyz[10] = p256one[2]
|
||||
r2.xyz[11] = p256one[3]
|
||||
|
||||
r2.p256ScalarMult(scalarReversed)
|
||||
|
||||
var sum, double p256Point
|
||||
pointsEqual := p256PointAddAsm(sum.xyz[:], r1.xyz[:], r2.xyz[:])
|
||||
p256PointDoubleAsm(double.xyz[:], r1.xyz[:])
|
||||
sum.CopyConditional(&double, pointsEqual)
|
||||
sum.CopyConditional(&r1, r2IsInfinity)
|
||||
sum.CopyConditional(&r2, r1IsInfinity)
|
||||
|
||||
return sum.p256PointToAffine()
|
||||
}
|
||||
|
||||
func (curve p256Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
|
||||
scalarReversed := make([]uint64, 4)
|
||||
p256GetScalar(scalarReversed, scalar)
|
||||
|
||||
var r p256Point
|
||||
r.p256BaseMult(scalarReversed)
|
||||
return r.p256PointToAffine()
|
||||
}
|
||||
|
||||
func (curve p256Curve) ScalarMult(bigX, bigY *big.Int, scalar []byte) (x, y *big.Int) {
|
||||
scalarReversed := make([]uint64, 4)
|
||||
p256GetScalar(scalarReversed, scalar)
|
||||
|
||||
var r p256Point
|
||||
fromBig(r.xyz[0:4], maybeReduceModP(bigX))
|
||||
fromBig(r.xyz[4:8], maybeReduceModP(bigY))
|
||||
p256Mul(r.xyz[0:4], r.xyz[0:4], rr[:])
|
||||
p256Mul(r.xyz[4:8], r.xyz[4:8], rr[:])
|
||||
// This sets r2's Z value to 1, in the Montgomery domain.
|
||||
r.xyz[8] = p256one[0]
|
||||
r.xyz[9] = p256one[1]
|
||||
r.xyz[10] = p256one[2]
|
||||
r.xyz[11] = p256one[3]
|
||||
|
||||
r.p256ScalarMult(scalarReversed)
|
||||
return r.p256PointToAffine()
|
||||
}
|
||||
|
||||
// uint64IsZero returns 1 if x is zero and zero otherwise.
|
||||
func uint64IsZero(x uint64) int {
|
||||
x = ^x
|
||||
x &= x >> 32
|
||||
x &= x >> 16
|
||||
x &= x >> 8
|
||||
x &= x >> 4
|
||||
x &= x >> 2
|
||||
x &= x >> 1
|
||||
return int(x & 1)
|
||||
}
|
||||
|
||||
// scalarIsZero returns 1 if scalar represents the zero value, and zero
|
||||
// otherwise.
|
||||
func scalarIsZero(scalar []uint64) int {
|
||||
return uint64IsZero(scalar[0] | scalar[1] | scalar[2] | scalar[3])
|
||||
}
|
||||
|
||||
func (p *p256Point) p256PointToAffine() (x, y *big.Int) {
|
||||
zInv := make([]uint64, 4)
|
||||
zInvSq := make([]uint64, 4)
|
||||
p256Inverse(zInv, p.xyz[8:12])
|
||||
p256Sqr(zInvSq, zInv, 1)
|
||||
p256Mul(zInv, zInv, zInvSq)
|
||||
|
||||
p256Mul(zInvSq, p.xyz[0:4], zInvSq)
|
||||
p256Mul(zInv, p.xyz[4:8], zInv)
|
||||
|
||||
p256FromMont(zInvSq, zInvSq)
|
||||
p256FromMont(zInv, zInv)
|
||||
|
||||
xOut := make([]byte, 32)
|
||||
yOut := make([]byte, 32)
|
||||
p256LittleToBig(xOut, zInvSq)
|
||||
p256LittleToBig(yOut, zInv)
|
||||
|
||||
return new(big.Int).SetBytes(xOut), new(big.Int).SetBytes(yOut)
|
||||
}
|
||||
|
||||
// CopyConditional copies overwrites p with src if v == 1, and leaves p
|
||||
// unchanged if v == 0.
|
||||
func (p *p256Point) CopyConditional(src *p256Point, v int) {
|
||||
pMask := uint64(v) - 1
|
||||
srcMask := ^pMask
|
||||
|
||||
for i, n := range p.xyz {
|
||||
p.xyz[i] = (n & pMask) | (src.xyz[i] & srcMask)
|
||||
}
|
||||
}
|
||||
|
||||
// p256Inverse sets out to in^-1 mod p.
|
||||
func p256Inverse(out, in []uint64) {
|
||||
// Inversion is calculated through exponentiation by p - 2, per Fermat's
|
||||
// little theorem.
|
||||
//
|
||||
// The sequence of 14 multiplications and 255 squarings is derived from the
|
||||
// following addition chain generated with github.com/mmcloughlin/addchain
|
||||
// v0.4.0.
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _110 = 2*_11
|
||||
// _111 = 1 + _110
|
||||
// _111000 = _111 << 3
|
||||
// _111111 = _111 + _111000
|
||||
// _1111110 = 2*_111111
|
||||
// _1111111 = 1 + _1111110
|
||||
// x12 = _1111110 << 5 + _111111
|
||||
// x24 = x12 << 12 + x12
|
||||
// x31 = x24 << 7 + _1111111
|
||||
// i39 = x31 << 2
|
||||
// i68 = i39 << 29
|
||||
// x62 = x31 + i68
|
||||
// i71 = i68 << 2
|
||||
// x64 = i39 + i71 + _11
|
||||
// i265 = ((i71 << 32 + x64) << 64 + x64) << 94
|
||||
// return (x62 + i265) << 2 + 1
|
||||
var stack [3 * 4]uint64
|
||||
t0 := stack[4*0 : 4*0+4]
|
||||
t1 := stack[4*1 : 4*1+4]
|
||||
t2 := stack[4*2 : 4*2+4]
|
||||
|
||||
p256Sqr(out, in, 1)
|
||||
p256Mul(t0, in, out)
|
||||
p256Sqr(out, t0, 1)
|
||||
p256Mul(out, in, out)
|
||||
p256Sqr(t1, out, 3)
|
||||
p256Mul(t1, out, t1)
|
||||
p256Sqr(t2, t1, 1)
|
||||
p256Mul(out, in, t2)
|
||||
p256Sqr(t2, t2, 5)
|
||||
p256Mul(t1, t1, t2)
|
||||
p256Sqr(t2, t1, 12)
|
||||
p256Mul(t1, t1, t2)
|
||||
p256Sqr(t1, t1, 7)
|
||||
p256Mul(out, out, t1)
|
||||
p256Sqr(t2, out, 2)
|
||||
p256Sqr(t1, t2, 29)
|
||||
p256Mul(out, out, t1)
|
||||
p256Sqr(t1, t1, 2)
|
||||
p256Mul(t2, t2, t1)
|
||||
p256Mul(t0, t0, t2)
|
||||
p256Sqr(t1, t1, 32)
|
||||
p256Mul(t1, t0, t1)
|
||||
p256Sqr(t1, t1, 64)
|
||||
p256Mul(t0, t0, t1)
|
||||
p256Sqr(t0, t0, 94)
|
||||
p256Mul(out, out, t0)
|
||||
p256Sqr(out, out, 2)
|
||||
p256Mul(out, in, out)
|
||||
}
|
||||
|
||||
func (p *p256Point) p256StorePoint(r *[16 * 4 * 3]uint64, index int) {
|
||||
copy(r[index*12:], p.xyz[:])
|
||||
}
|
||||
|
||||
// This function takes those six bits as an integer (0 .. 63), writing the
|
||||
// recoded digit to *sign (0 for positive, 1 for negative) and *digit (absolute
|
||||
// value, in the range 0 .. 16). Note that this integer essentially provides
|
||||
// the input bits "shifted to the left" by one position: for example, the input
|
||||
// to compute the least significant recoded digit, given that there's no bit
|
||||
// b_-1, has to be b_4 b_3 b_2 b_1 b_0 0.
|
||||
//
|
||||
// Reference:
|
||||
// https://github.com/openssl/openssl/blob/master/crypto/ec/ecp_nistputil.c
|
||||
// https://github.com/google/boringssl/blob/master/crypto/fipsmodule/ec/util.c
|
||||
func boothW5(in uint) (int, int) {
|
||||
var s uint = ^((in >> 5) - 1) // sets all bits to MSB(in), 'in' seen as 6-bit value
|
||||
var d uint = (1 << 6) - in - 1 // d = 63 - in, or d = ^in & 0x3f
|
||||
d = (d & s) | (in & (^s)) // d = in if in < 2^5; otherwise, d = 63 - in
|
||||
d = (d >> 1) + (d & 1) // d = (d + 1) / 2
|
||||
return int(d), int(s & 1)
|
||||
}
|
||||
|
||||
func boothW6(in uint) (int, int) {
|
||||
var s uint = ^((in >> 6) - 1)
|
||||
var d uint = (1 << 7) - in - 1
|
||||
d = (d & s) | (in & (^s))
|
||||
d = (d >> 1) + (d & 1)
|
||||
return int(d), int(s & 1)
|
||||
}
|
||||
|
||||
func (p *p256Point) p256BaseMult(scalar []uint64) {
|
||||
wvalue := (scalar[0] << 1) & 0x7f
|
||||
sel, sign := boothW6(uint(wvalue))
|
||||
p256SelectBase(&p.xyz, p256Precomputed, sel)
|
||||
p256NegCond(p.xyz[4:8], sign)
|
||||
|
||||
// (This is one, in the Montgomery domain.)
|
||||
p.xyz[8] = p256one[0]
|
||||
p.xyz[9] = p256one[1]
|
||||
p.xyz[10] = p256one[2]
|
||||
p.xyz[11] = p256one[3]
|
||||
|
||||
var t0 p256Point
|
||||
// (This is one, in the Montgomery domain.)
|
||||
t0.xyz[8] = p256one[0]
|
||||
t0.xyz[9] = p256one[1]
|
||||
t0.xyz[10] = p256one[2]
|
||||
t0.xyz[11] = p256one[3]
|
||||
|
||||
index := uint(5)
|
||||
zero := sel
|
||||
|
||||
for i := 1; i < 43; i++ {
|
||||
if index < 192 {
|
||||
wvalue = ((scalar[index/64] >> (index % 64)) + (scalar[index/64+1] << (64 - (index % 64)))) & 0x7f
|
||||
} else {
|
||||
wvalue = (scalar[index/64] >> (index % 64)) & 0x7f
|
||||
}
|
||||
index += 6
|
||||
sel, sign = boothW6(uint(wvalue))
|
||||
p256SelectBase(&t0.xyz, p256Precomputed[i*32*8*8:], sel)
|
||||
p256PointAddAffineAsm(p.xyz[0:12], p.xyz[0:12], t0.xyz[0:8], sign, sel, zero)
|
||||
zero |= sel
|
||||
}
|
||||
}
|
||||
|
||||
func (p *p256Point) p256ScalarMult(scalar []uint64) {
|
||||
// precomp is a table of precomputed points that stores powers of p
|
||||
// from p^1 to p^16.
|
||||
var precomp [16 * 4 * 3]uint64
|
||||
var t0, t1, t2, t3 p256Point
|
||||
|
||||
// Prepare the table
|
||||
p.p256StorePoint(&precomp, 0) // 1
|
||||
|
||||
p256PointDoubleAsm(t0.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(t1.xyz[:], t0.xyz[:])
|
||||
p256PointDoubleAsm(t2.xyz[:], t1.xyz[:])
|
||||
p256PointDoubleAsm(t3.xyz[:], t2.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 1) // 2
|
||||
t1.p256StorePoint(&precomp, 3) // 4
|
||||
t2.p256StorePoint(&precomp, 7) // 8
|
||||
t3.p256StorePoint(&precomp, 15) // 16
|
||||
|
||||
p256PointAddAsm(t0.xyz[:], t0.xyz[:], p.xyz[:])
|
||||
p256PointAddAsm(t1.xyz[:], t1.xyz[:], p.xyz[:])
|
||||
p256PointAddAsm(t2.xyz[:], t2.xyz[:], p.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 2) // 3
|
||||
t1.p256StorePoint(&precomp, 4) // 5
|
||||
t2.p256StorePoint(&precomp, 8) // 9
|
||||
|
||||
p256PointDoubleAsm(t0.xyz[:], t0.xyz[:])
|
||||
p256PointDoubleAsm(t1.xyz[:], t1.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 5) // 6
|
||||
t1.p256StorePoint(&precomp, 9) // 10
|
||||
|
||||
p256PointAddAsm(t2.xyz[:], t0.xyz[:], p.xyz[:])
|
||||
p256PointAddAsm(t1.xyz[:], t1.xyz[:], p.xyz[:])
|
||||
t2.p256StorePoint(&precomp, 6) // 7
|
||||
t1.p256StorePoint(&precomp, 10) // 11
|
||||
|
||||
p256PointDoubleAsm(t0.xyz[:], t0.xyz[:])
|
||||
p256PointDoubleAsm(t2.xyz[:], t2.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 11) // 12
|
||||
t2.p256StorePoint(&precomp, 13) // 14
|
||||
|
||||
p256PointAddAsm(t0.xyz[:], t0.xyz[:], p.xyz[:])
|
||||
p256PointAddAsm(t2.xyz[:], t2.xyz[:], p.xyz[:])
|
||||
t0.p256StorePoint(&precomp, 12) // 13
|
||||
t2.p256StorePoint(&precomp, 14) // 15
|
||||
|
||||
// Start scanning the window from top bit
|
||||
index := uint(254)
|
||||
var sel, sign int
|
||||
|
||||
wvalue := (scalar[index/64] >> (index % 64)) & 0x3f
|
||||
sel, _ = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(p.xyz[0:12], precomp[0:], sel)
|
||||
zero := sel
|
||||
|
||||
for index > 4 {
|
||||
index -= 5
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
|
||||
if index < 192 {
|
||||
wvalue = ((scalar[index/64] >> (index % 64)) + (scalar[index/64+1] << (64 - (index % 64)))) & 0x3f
|
||||
} else {
|
||||
wvalue = (scalar[index/64] >> (index % 64)) & 0x3f
|
||||
}
|
||||
|
||||
sel, sign = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(t0.xyz[0:], precomp[0:], sel)
|
||||
p256NegCond(t0.xyz[4:8], sign)
|
||||
p256PointAddAsm(t1.xyz[:], p.xyz[:], t0.xyz[:])
|
||||
p256MovCond(t1.xyz[0:12], t1.xyz[0:12], p.xyz[0:12], sel)
|
||||
p256MovCond(p.xyz[0:12], t1.xyz[0:12], t0.xyz[0:12], zero)
|
||||
zero |= sel
|
||||
}
|
||||
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
p256PointDoubleAsm(p.xyz[:], p.xyz[:])
|
||||
|
||||
wvalue = (scalar[0] << 1) & 0x3f
|
||||
sel, sign = boothW5(uint(wvalue))
|
||||
|
||||
p256Select(t0.xyz[0:], precomp[0:], sel)
|
||||
p256NegCond(t0.xyz[4:8], sign)
|
||||
p256PointAddAsm(t1.xyz[:], p.xyz[:], t0.xyz[:])
|
||||
p256MovCond(t1.xyz[0:12], t1.xyz[0:12], p.xyz[0:12], sel)
|
||||
p256MovCond(p.xyz[0:12], t1.xyz[0:12], t0.xyz[0:12], zero)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,65 +0,0 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestP256PrecomputedTable(t *testing.T) {
|
||||
|
||||
basePoint := []uint64{
|
||||
0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05,
|
||||
0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd,
|
||||
0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000,
|
||||
}
|
||||
t1 := make([]uint64, 12)
|
||||
t2 := make([]uint64, 12)
|
||||
copy(t2, basePoint)
|
||||
|
||||
zInv := make([]uint64, 4)
|
||||
zInvSq := make([]uint64, 4)
|
||||
for j := 0; j < 32; j++ {
|
||||
copy(t1, t2)
|
||||
for i := 0; i < 43; i++ {
|
||||
// The window size is 6 so we need to double 6 times.
|
||||
if i != 0 {
|
||||
for k := 0; k < 6; k++ {
|
||||
p256PointDoubleAsm(t1, t1)
|
||||
}
|
||||
}
|
||||
// Convert the point to affine form. (Its values are
|
||||
// still in Montgomery form however.)
|
||||
p256Inverse(zInv, t1[8:12])
|
||||
p256Sqr(zInvSq, zInv, 1)
|
||||
p256Mul(zInv, zInv, zInvSq)
|
||||
|
||||
p256Mul(t1[:4], t1[:4], zInvSq)
|
||||
p256Mul(t1[4:8], t1[4:8], zInv)
|
||||
|
||||
copy(t1[8:12], basePoint[8:12])
|
||||
|
||||
buf := make([]byte, 8*8)
|
||||
for i, u := range t1[:8] {
|
||||
binary.LittleEndian.PutUint64(buf[i*8:i*8+8], u)
|
||||
}
|
||||
start := i*32*8*8 + j*8*8
|
||||
if got, want := p256Precomputed[start:start+64], string(buf); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("Unexpected table entry at [%d][%d:%d]: got %v, want %v", i, j*8, (j*8)+8, got, want)
|
||||
}
|
||||
}
|
||||
if j == 0 {
|
||||
p256PointDoubleAsm(t2, basePoint)
|
||||
} else {
|
||||
p256PointAddAsm(t2, t2, basePoint)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,392 +0,0 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func toBigInt(in []uint64) *big.Int {
|
||||
var valBytes = make([]byte, 32)
|
||||
p256LittleToBig(valBytes, in)
|
||||
return new(big.Int).SetBytes(valBytes)
|
||||
}
|
||||
|
||||
// ordk0 = -n^(-1) mod 2^64
|
||||
func Test_p256ordk0(t *testing.T) {
|
||||
n, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
p, _ := new(big.Int).SetString("10000000000000000", 16) // 2^64
|
||||
n = n.ModInverse(n, p)
|
||||
n = n.Neg(n)
|
||||
n = n.Mod(n, p)
|
||||
if "327f9e8872350975" != hex.EncodeToString(n.Bytes()) {
|
||||
t.Failed()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256NegCond(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
var val = []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
bigVal := toBigInt(val)
|
||||
|
||||
p256NegCond(val, 0)
|
||||
bigVal1 := toBigInt(val)
|
||||
if bigVal.Cmp(bigVal1) != 0 {
|
||||
t.Fatal("should be same")
|
||||
}
|
||||
p256NegCond(val, 1)
|
||||
bigVal1 = toBigInt(val)
|
||||
if bigVal.Cmp(bigVal1) == 0 {
|
||||
t.Fatal("should be different")
|
||||
}
|
||||
bigVal2 := new(big.Int).Sub(p, bigVal)
|
||||
if bigVal2.Cmp(bigVal1) != 0 {
|
||||
t.Fatal("should be same")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256FromMont(t *testing.T) {
|
||||
res := make([]uint64, 4)
|
||||
p256FromMont(res, []uint64{0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000})
|
||||
res1 := (res[0] ^ 0x0000000000000001) | res[1] | res[2] | res[3]
|
||||
if res1 != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
x1 := make([]uint64, 4)
|
||||
p256BigToLittle(x1, x.Bytes())
|
||||
|
||||
p256FromMont(res, []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05})
|
||||
if (res[0]^x1[0])|(res[1]^x1[1])|(res[2]^x1[2])|(res[3]^x1[3]) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256Sqr(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
one := []uint64{0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000}
|
||||
res := make([]uint64, 4)
|
||||
p256Sqr(res, one, 2)
|
||||
if (res[0]^one[0])|(res[1]^one[1])|(res[2]^one[2])|(res[3]^one[3]) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
gx := []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
p256Sqr(res, gx, 2)
|
||||
resInt := toBigInt(res)
|
||||
gxsqr := new(big.Int).Mul(x, x)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, p)
|
||||
gxsqr = new(big.Int).Mul(gxsqr, gxsqr)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, p)
|
||||
gxsqr = new(big.Int).Mul(gxsqr, r)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, p)
|
||||
if resInt.Cmp(gxsqr) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256Mul(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
y, _ := new(big.Int).SetString("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)
|
||||
res := make([]uint64, 4)
|
||||
gx := []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
gy := []uint64{0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd}
|
||||
|
||||
p256Mul(res, gx, gy)
|
||||
resInt := toBigInt(res)
|
||||
xmy := new(big.Int).Mul(x, y)
|
||||
xmy = new(big.Int).Mod(xmy, p)
|
||||
xmy = new(big.Int).Mul(xmy, r)
|
||||
xmy = new(big.Int).Mod(xmy, p)
|
||||
if resInt.Cmp(xmy) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func p256SqrTest(t *testing.T, x, p, r *big.Int) {
|
||||
x1 := new(big.Int).Mul(x, r)
|
||||
x1 = x1.Mod(x1, p)
|
||||
ax := make([]uint64, 4)
|
||||
res := make([]uint64, 4)
|
||||
res2 := make([]uint64, 4)
|
||||
fromBig(ax, x1)
|
||||
p256Sqr(res2, ax, 1)
|
||||
p256FromMont(res, res2)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
expected := new(big.Int).Mul(x, x)
|
||||
expected = expected.Mod(expected, p)
|
||||
if resInt.Cmp(expected) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyP256Sqr(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
var scalar1 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
x := new(big.Int).SetBytes(scalar1[:])
|
||||
p256SqrTest(t, x, p, r)
|
||||
}
|
||||
}
|
||||
|
||||
func p256MulTest(t *testing.T, x, y, p, r *big.Int) {
|
||||
x1 := new(big.Int).Mul(x, r)
|
||||
x1 = x1.Mod(x1, p)
|
||||
y1 := new(big.Int).Mul(y, r)
|
||||
y1 = y1.Mod(y1, p)
|
||||
ax := make([]uint64, 4)
|
||||
ay := make([]uint64, 4)
|
||||
res := make([]uint64, 4)
|
||||
res2 := make([]uint64, 4)
|
||||
fromBig(ax, x1)
|
||||
fromBig(ay, y1)
|
||||
p256Mul(res2, ax, ay)
|
||||
p256FromMont(res, res2)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
expected := new(big.Int).Mul(x, y)
|
||||
expected = expected.Mod(expected, p)
|
||||
if resInt.Cmp(expected) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzyP256Mul(t *testing.T) {
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
var scalar1 [32]byte
|
||||
var scalar2 [32]byte
|
||||
var timeout *time.Timer
|
||||
|
||||
if testing.Short() {
|
||||
timeout = time.NewTimer(10 * time.Millisecond)
|
||||
} else {
|
||||
timeout = time.NewTimer(2 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
return
|
||||
default:
|
||||
}
|
||||
io.ReadFull(rand.Reader, scalar1[:])
|
||||
io.ReadFull(rand.Reader, scalar2[:])
|
||||
x := new(big.Int).SetBytes(scalar1[:])
|
||||
y := new(big.Int).SetBytes(scalar2[:])
|
||||
p256MulTest(t, x, y, p, r)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256MulSqr(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
res := make([]uint64, 4)
|
||||
gx := []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
|
||||
p256Sqr(res, gx, 32)
|
||||
resInt := toBigInt(res)
|
||||
|
||||
p256Mul(res, gx, gx)
|
||||
for i := 0; i < 31; i++ {
|
||||
p256Mul(res, res, res)
|
||||
}
|
||||
resInt1 := toBigInt(res)
|
||||
|
||||
resInt2 := new(big.Int).Mod(x, p)
|
||||
|
||||
for i := 0; i < 32; i++ {
|
||||
resInt2 = new(big.Int).Mul(resInt2, resInt2)
|
||||
resInt2 = new(big.Int).Mod(resInt2, p)
|
||||
}
|
||||
resInt2 = new(big.Int).Mul(resInt2, r)
|
||||
resInt2 = new(big.Int).Mod(resInt2, p)
|
||||
|
||||
if resInt.Cmp(resInt2) != 0 || resInt1.Cmp(resInt2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256OrdSqr(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
n, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
gx := make([]uint64, 4)
|
||||
res := make([]uint64, 4)
|
||||
xm := new(big.Int).Mul(x, r)
|
||||
xm = new(big.Int).Mod(xm, n)
|
||||
p256BigToLittle(gx, xm.Bytes())
|
||||
p256OrdMul(res, gx, gx)
|
||||
resInt := toBigInt(res)
|
||||
gxsqr := new(big.Int).Mul(x, x)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, n)
|
||||
gxsqr = new(big.Int).Mul(gxsqr, r)
|
||||
gxsqr = new(big.Int).Mod(gxsqr, n)
|
||||
if resInt.Cmp(gxsqr) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
p256OrdSqr(res, gx, 1)
|
||||
resInt = toBigInt(res)
|
||||
if resInt.Cmp(gxsqr) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256Inverse(t *testing.T) {
|
||||
r, _ := new(big.Int).SetString("10000000000000000000000000000000000000000000000000000000000000000", 16)
|
||||
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
gx := []uint64{0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05}
|
||||
res := make([]uint64, 4)
|
||||
p256Inverse(res, gx)
|
||||
resInt := toBigInt(res)
|
||||
xInv := new(big.Int).ModInverse(x, p)
|
||||
xInv = new(big.Int).Mul(xInv, r)
|
||||
xInv = new(big.Int).Mod(xInv, p)
|
||||
if resInt.Cmp(xInv) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256PointAddAsm_basepoint(t *testing.T) {
|
||||
curve1 := P256()
|
||||
params := curve1.Params()
|
||||
basePoint := []uint64{
|
||||
0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05,
|
||||
0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd,
|
||||
0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000,
|
||||
}
|
||||
in := make([]uint64, 12)
|
||||
res := make([]uint64, 12)
|
||||
copy(in, basePoint)
|
||||
p256PointDoubleAsm(res, in)
|
||||
p256PointAddAsm(res, res, in)
|
||||
var r p256Point
|
||||
copy(r.xyz[:], res)
|
||||
x1, y1 := r.p256PointToAffine()
|
||||
x2, y2 := params.Double(params.Gx, params.Gy)
|
||||
x2, y2 = params.Add(params.Gx, params.Gy, x2, y2)
|
||||
if x1.Cmp(x2) != 0 || y1.Cmp(y2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256PointDoubleAsm(t *testing.T) {
|
||||
basePoint := []uint64{
|
||||
0x61328990f418029e, 0x3e7981eddca6c050, 0xd6a1ed99ac24c3c3, 0x91167a5ee1c13b05,
|
||||
0xc1354e593c2d0ddd, 0xc1f5e5788d3295fa, 0x8d4cfb066e2a48f8, 0x63cd65d481d735bd,
|
||||
0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000,
|
||||
}
|
||||
t1 := make([]uint64, 12)
|
||||
copy(t1, basePoint)
|
||||
for i := 0; i < 16; i++ {
|
||||
p256PointDoubleAsm(t1, t1)
|
||||
}
|
||||
var r p256Point
|
||||
copy(r.xyz[:], t1)
|
||||
x1, y1 := r.p256PointToAffine()
|
||||
curve1 := P256()
|
||||
params := curve1.Params()
|
||||
x2, y2 := params.Double(params.Gx, params.Gy)
|
||||
for i := 0; i < 15; i++ {
|
||||
x2, y2 = params.Double(x2, y2)
|
||||
}
|
||||
if x1.Cmp(x2) != 0 || y1.Cmp(y2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ScalarBaseMult(t *testing.T) {
|
||||
scalar := big.NewInt(0xffffffff)
|
||||
curve1 := P256()
|
||||
x1, y1 := curve1.ScalarBaseMult(scalar.Bytes())
|
||||
params := curve1.Params()
|
||||
x2, y2 := params.ScalarBaseMult(scalar.Bytes())
|
||||
if x1.Cmp(x2) != 0 || y1.Cmp(y2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_p256PointAddAsm(t *testing.T) {
|
||||
curve1 := P256()
|
||||
params := curve1.Params()
|
||||
k1, _ := randFieldElement(params, rand.Reader)
|
||||
x1, y1 := params.ScalarBaseMult(k1.Bytes())
|
||||
k2, _ := randFieldElement(params, rand.Reader)
|
||||
x2, y2 := params.ScalarBaseMult(k2.Bytes())
|
||||
x3, y3 := params.Add(x1, y1, x2, y2)
|
||||
var in1, in2, rp p256Point
|
||||
fromBig(in1.xyz[0:4], maybeReduceModP(x1))
|
||||
fromBig(in1.xyz[4:8], maybeReduceModP(y1))
|
||||
fromBig(in2.xyz[0:4], maybeReduceModP(x2))
|
||||
fromBig(in2.xyz[4:8], maybeReduceModP(y2))
|
||||
in1.xyz[8] = 0x0000000000000001
|
||||
in1.xyz[9] = 0x00000000ffffffff
|
||||
in1.xyz[10] = 0x0000000000000000
|
||||
in1.xyz[11] = 0x0000000100000000
|
||||
in2.xyz[8] = 0x0000000000000001
|
||||
in2.xyz[9] = 0x00000000ffffffff
|
||||
in2.xyz[10] = 0x0000000000000000
|
||||
in2.xyz[11] = 0x0000000100000000
|
||||
p256Mul(in1.xyz[0:4], in1.xyz[0:4], rr[:])
|
||||
p256Mul(in1.xyz[4:8], in1.xyz[4:8], rr[:])
|
||||
p256Mul(in2.xyz[0:4], in2.xyz[0:4], rr[:])
|
||||
p256Mul(in2.xyz[4:8], in2.xyz[4:8], rr[:])
|
||||
res := make([]uint64, 12)
|
||||
n := p256PointAddAsm(res, in1.xyz[:], in2.xyz[:])
|
||||
copy(rp.xyz[:], res)
|
||||
x4, y4 := rp.p256PointToAffine()
|
||||
if n == 0 && (x3.Cmp(x4) != 0 || y3.Cmp(y4) != 0) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ScalarMult_basepoint(t *testing.T) {
|
||||
scalar := big.NewInt(0xffffffff)
|
||||
curve1 := P256()
|
||||
x1, y1 := curve1.ScalarMult(curve1.Params().Gx, curve1.Params().Gy, scalar.Bytes())
|
||||
params := curve1.Params()
|
||||
x2, y2 := params.ScalarMult(curve1.Params().Gx, curve1.Params().Gy, scalar.Bytes())
|
||||
if x1.Cmp(x2) != 0 || y1.Cmp(y2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Inverse(t *testing.T) {
|
||||
n, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
x, _ := new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
nm2 := new(big.Int).Sub(n, big.NewInt(2))
|
||||
nm2a := make([]uint64, 4)
|
||||
fromBig(nm2a, nm2)
|
||||
xInv1 := fermatInverse(x, n)
|
||||
_ = P256()
|
||||
xInv2 := p256.Inverse(x)
|
||||
|
||||
if xInv1.Cmp(xInv2) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
40
sm2/sm2.go
40
sm2/sm2.go
@ -21,10 +21,10 @@ import (
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/randutil"
|
||||
"github.com/emmansun/gmsm/internal/xor"
|
||||
"github.com/emmansun/gmsm/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()
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
62
sm2/sm2ec/elliptic.go
Normal 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
348
sm2/sm2ec/elliptic_test.go
Normal file
@ -0,0 +1,348 @@
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var _ = elliptic.P256() // force NIST P curves init, avoid panic when we invoke generic implementation's method
|
||||
|
||||
// genericParamsForCurve returns the dereferenced CurveParams for
|
||||
// the specified curve. This is used to avoid the logic for
|
||||
// upgrading a curve to its specific implementation, forcing
|
||||
// usage of the generic implementation.
|
||||
func genericParamsForCurve(c elliptic.Curve) *elliptic.CurveParams {
|
||||
d := *(c.Params())
|
||||
return &d
|
||||
}
|
||||
|
||||
func testAllCurves(t *testing.T, f func(*testing.T, elliptic.Curve)) {
|
||||
tests := []struct {
|
||||
name string
|
||||
curve elliptic.Curve
|
||||
}{
|
||||
{"SM2P256", P256()},
|
||||
{"SM2P256/Params", genericParamsForCurve(P256())},
|
||||
}
|
||||
if testing.Short() {
|
||||
tests = tests[:1]
|
||||
}
|
||||
for _, test := range tests {
|
||||
curve := test.curve
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
f(t, curve)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnCurve(t *testing.T) {
|
||||
testAllCurves(t, func(t *testing.T, curve elliptic.Curve) {
|
||||
if !curve.IsOnCurve(curve.Params().Gx, curve.Params().Gy) {
|
||||
t.Error("basepoint is not on the curve")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestOffCurve(t *testing.T) {
|
||||
testAllCurves(t, func(t *testing.T, curve elliptic.Curve) {
|
||||
x, y := new(big.Int).SetInt64(1), new(big.Int).SetInt64(1)
|
||||
if curve.IsOnCurve(x, y) {
|
||||
t.Errorf("point off curve is claimed to be on the curve")
|
||||
}
|
||||
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
b := make([]byte, 1+2*byteLen)
|
||||
b[0] = 4 // uncompressed point
|
||||
x.FillBytes(b[1 : 1+byteLen])
|
||||
y.FillBytes(b[1+byteLen : 1+2*byteLen])
|
||||
|
||||
x1, y1 := 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
198
sm2/sm2ec/sm2p256_asm_ec.go
Normal file
@ -0,0 +1,198 @@
|
||||
//go:build (amd64 && !generic) || (arm64 && !generic)
|
||||
// +build amd64,!generic arm64,!generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
_sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
|
||||
)
|
||||
|
||||
// TODO: will merge it with sm2p256_generic.go from golang 1.18 with generic support.
|
||||
type sm2Curve struct {
|
||||
newPoint func() *_sm2ec.P256Point
|
||||
params *elliptic.CurveParams
|
||||
}
|
||||
|
||||
var sm2p256 = &sm2Curve{newPoint: _sm2ec.NewP256Point}
|
||||
|
||||
func initSM2P256() {
|
||||
sm2p256.params = sm2Params
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Params() *elliptic.CurveParams {
|
||||
return curve.params
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
// IsOnCurve is documented to reject (0, 0), the conventional point at
|
||||
// infinity, which however is accepted by pointFromAffine.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := curve.pointFromAffine(x, y)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointFromAffine(x, y *big.Int) (p *_sm2ec.P256Point, err error) {
|
||||
p = curve.newPoint()
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates. See Issue 37294.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return p, nil
|
||||
}
|
||||
// Reject values that would not get correctly encoded.
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return p, errors.New("negative coordinate")
|
||||
}
|
||||
if x.BitLen() > curve.params.BitSize || y.BitLen() > curve.params.BitSize {
|
||||
return p, errors.New("overflowing coordinate")
|
||||
}
|
||||
// Encode the coordinates and let SetBytes reject invalid points.
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
buf := make([]byte, 1+2*byteLen)
|
||||
buf[0] = 4 // uncompressed point
|
||||
x.FillBytes(buf[1 : 1+byteLen])
|
||||
y.FillBytes(buf[1+byteLen : 1+2*byteLen])
|
||||
return p.SetBytes(buf)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointToAffine(p *_sm2ec.P256Point) (x, y *big.Int) {
|
||||
out := p.Bytes()
|
||||
if len(out) == 1 && out[0] == 0 {
|
||||
// This is the encoding of the point at infinity, which the affine
|
||||
// coordinates API represents as (0, 0) by convention.
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(out[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(out[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
p1, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
p2, err := curve.pointFromAffine(x2, y2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p1.Add(p1, p2))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Double was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p.Double(p))
|
||||
}
|
||||
|
||||
// normalizeScalar brings the scalar within the byte size of the order of the
|
||||
// curve, as expected by the nistec scalar multiplication functions.
|
||||
func (curve *sm2Curve) normalizeScalar(scalar []byte) []byte {
|
||||
byteSize := (curve.params.N.BitLen() + 7) / 8
|
||||
if len(scalar) == byteSize {
|
||||
return scalar
|
||||
}
|
||||
s := new(big.Int).SetBytes(scalar)
|
||||
if len(scalar) > byteSize {
|
||||
s.Mod(s, curve.params.N)
|
||||
}
|
||||
out := make([]byte, byteSize)
|
||||
return s.FillBytes(out)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(Bx, By)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: ScalarMult was called on an invalid point")
|
||||
}
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err = p.ScalarMult(p, scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err := curve.newPoint().ScalarBaseMult(scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
// CombinedMult returns [s1]G + [s2]P where G is the generator. It's used
|
||||
// through an interface upgrade in crypto/ecdsa.
|
||||
func (curve *sm2Curve) CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int) {
|
||||
s1 = curve.normalizeScalar(s1)
|
||||
q, err := curve.newPoint().ScalarBaseMult(s1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
p, err := curve.pointFromAffine(Px, Py)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: CombinedMult was called on an invalid point")
|
||||
}
|
||||
s2 = curve.normalizeScalar(s2)
|
||||
p, err = p.ScalarMult(p, s2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p.Add(p, q))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Unmarshal(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || data[0] != 4 {
|
||||
return nil, nil
|
||||
}
|
||||
// Use SetBytes to check that data encodes a valid point.
|
||||
_, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// We don't use pointToAffine because it involves an expensive field
|
||||
// inversion to convert from Jacobian to affine coordinates, which we
|
||||
// already have.
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(data[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) UnmarshalCompressed(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || (data[0] != 2 && data[0] != 3) {
|
||||
return nil, nil
|
||||
}
|
||||
p, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
// Inverse, implements invertible interface, used by Sign()
|
||||
func (curve *sm2Curve) Inverse(k *big.Int) *big.Int {
|
||||
if k.Sign() < 0 {
|
||||
// This should never happen.
|
||||
k = new(big.Int).Neg(k)
|
||||
}
|
||||
if k.Cmp(curve.params.N) >= 0 {
|
||||
// This should never happen.
|
||||
k = new(big.Int).Mod(k, curve.params.N)
|
||||
}
|
||||
scalar := k.FillBytes(make([]byte, 32))
|
||||
inverse, err := _sm2ec.P256OrdInverse(scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return new(big.Int).SetBytes(inverse)
|
||||
}
|
@ -1,186 +1,180 @@
|
||||
//go:build !amd64 && !arm64 || generic
|
||||
// +build !amd64,!arm64 generic
|
||||
|
||||
package sm2
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/sm2ec"
|
||||
)
|
||||
|
||||
// TODO: will merge it with sm2p256_asm_ec.go from golang 1.18 with generic support.
|
||||
type sm2Curve struct {
|
||||
newPoint func() *sm2ec.SM2P256Point
|
||||
CurveParams *elliptic.CurveParams
|
||||
}
|
||||
|
||||
var p256 = &sm2Curve{newPoint: sm2ec.NewSM2P256Point}
|
||||
|
||||
func initP256() {
|
||||
p256.CurveParams = &elliptic.CurveParams{Name: "sm2p256v1"}
|
||||
p256.CurveParams.P, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
|
||||
p256.CurveParams.N, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
|
||||
p256.CurveParams.B, _ = new(big.Int).SetString("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
|
||||
p256.CurveParams.Gx, _ = new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
|
||||
p256.CurveParams.Gy, _ = new(big.Int).SetString("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)
|
||||
p256.CurveParams.BitSize = 256
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Params() *elliptic.CurveParams {
|
||||
return curve.CurveParams
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
// IsOnCurve is documented to reject (0, 0), the conventional point at
|
||||
// infinity, which however is accepted by pointFromAffine.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := curve.pointFromAffine(x, y)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointFromAffine(x, y *big.Int) (p *sm2ec.SM2P256Point, err error) {
|
||||
p = curve.newPoint()
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates. See Issue 37294.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return p, nil
|
||||
}
|
||||
// Reject values that would not get correctly encoded.
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return p, errors.New("negative coordinate")
|
||||
}
|
||||
if x.BitLen() > curve.CurveParams.BitSize || y.BitLen() > curve.CurveParams.BitSize {
|
||||
return p, errors.New("overflowing coordinate")
|
||||
}
|
||||
// Encode the coordinates and let SetBytes reject invalid points.
|
||||
byteLen := (curve.CurveParams.BitSize + 7) / 8
|
||||
buf := make([]byte, 1+2*byteLen)
|
||||
buf[0] = 4 // uncompressed point
|
||||
x.FillBytes(buf[1 : 1+byteLen])
|
||||
y.FillBytes(buf[1+byteLen : 1+2*byteLen])
|
||||
return p.SetBytes(buf)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointToAffine(p *sm2ec.SM2P256Point) (x, y *big.Int) {
|
||||
out := p.Bytes()
|
||||
if len(out) == 1 && out[0] == 0 {
|
||||
// This is the encoding of the point at infinity, which the affine
|
||||
// coordinates API represents as (0, 0) by convention.
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
byteLen := (curve.CurveParams.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(out[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(out[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
p1, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
p2, err := curve.pointFromAffine(x2, y2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p1.Add(p1, p2))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Double was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p.Double(p))
|
||||
}
|
||||
|
||||
// normalizeScalar brings the scalar within the byte size of the order of the
|
||||
// curve, as expected by the nistec scalar multiplication functions.
|
||||
func (curve *sm2Curve) normalizeScalar(scalar []byte) []byte {
|
||||
byteSize := (curve.CurveParams.N.BitLen() + 7) / 8
|
||||
if len(scalar) == byteSize {
|
||||
return scalar
|
||||
}
|
||||
s := new(big.Int).SetBytes(scalar)
|
||||
if len(scalar) > byteSize {
|
||||
s.Mod(s, curve.CurveParams.N)
|
||||
}
|
||||
out := make([]byte, byteSize)
|
||||
return s.FillBytes(out)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(Bx, By)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: ScalarMult was called on an invalid point")
|
||||
}
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err = p.ScalarMult(p, scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err := curve.newPoint().ScalarBaseMult(scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
// CombinedMult returns [s1]G + [s2]P where G is the generator. It's used
|
||||
// through an interface upgrade in crypto/ecdsa.
|
||||
func (curve *sm2Curve) CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int) {
|
||||
s1 = curve.normalizeScalar(s1)
|
||||
q, err := curve.newPoint().ScalarBaseMult(s1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
p, err := curve.pointFromAffine(Px, Py)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: CombinedMult was called on an invalid point")
|
||||
}
|
||||
s2 = curve.normalizeScalar(s2)
|
||||
p, err = p.ScalarMult(p, s2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p.Add(p, q))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Unmarshal(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || data[0] != 4 {
|
||||
return nil, nil
|
||||
}
|
||||
// Use SetBytes to check that data encodes a valid point.
|
||||
_, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// We don't use pointToAffine because it involves an expensive field
|
||||
// inversion to convert from Jacobian to affine coordinates, which we
|
||||
// already have.
|
||||
byteLen := (curve.CurveParams.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(data[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) UnmarshalCompressed(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || (data[0] != 2 && data[0] != 3) {
|
||||
return nil, nil
|
||||
}
|
||||
p, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
//go:build !amd64 && !arm64 || generic
|
||||
// +build !amd64,!arm64 generic
|
||||
|
||||
package sm2ec
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
_sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
|
||||
)
|
||||
|
||||
// TODO: will merge it with sm2p256_asm_ec.go from golang 1.18 with generic support.
|
||||
type sm2Curve struct {
|
||||
newPoint func() *_sm2ec.SM2P256Point
|
||||
params *elliptic.CurveParams
|
||||
}
|
||||
|
||||
var sm2p256 = &sm2Curve{newPoint: _sm2ec.NewSM2P256Point}
|
||||
|
||||
func initSM2P256() {
|
||||
sm2p256.params = sm2Params
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Params() *elliptic.CurveParams {
|
||||
return curve.params
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
// IsOnCurve is documented to reject (0, 0), the conventional point at
|
||||
// infinity, which however is accepted by pointFromAffine.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := curve.pointFromAffine(x, y)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointFromAffine(x, y *big.Int) (p *_sm2ec.SM2P256Point, err error) {
|
||||
p = curve.newPoint()
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates. See Issue 37294.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return p, nil
|
||||
}
|
||||
// Reject values that would not get correctly encoded.
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return p, errors.New("negative coordinate")
|
||||
}
|
||||
if x.BitLen() > curve.params.BitSize || y.BitLen() > curve.params.BitSize {
|
||||
return p, errors.New("overflowing coordinate")
|
||||
}
|
||||
// Encode the coordinates and let SetBytes reject invalid points.
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
buf := make([]byte, 1+2*byteLen)
|
||||
buf[0] = 4 // uncompressed point
|
||||
x.FillBytes(buf[1 : 1+byteLen])
|
||||
y.FillBytes(buf[1+byteLen : 1+2*byteLen])
|
||||
return p.SetBytes(buf)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) pointToAffine(p *_sm2ec.SM2P256Point) (x, y *big.Int) {
|
||||
out := p.Bytes()
|
||||
if len(out) == 1 && out[0] == 0 {
|
||||
// This is the encoding of the point at infinity, which the affine
|
||||
// coordinates API represents as (0, 0) by convention.
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(out[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(out[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
p1, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
p2, err := curve.pointFromAffine(x2, y2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Add was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p1.Add(p1, p2))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(x1, y1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: Double was called on an invalid point")
|
||||
}
|
||||
return curve.pointToAffine(p.Double(p))
|
||||
}
|
||||
|
||||
// normalizeScalar brings the scalar within the byte size of the order of the
|
||||
// curve, as expected by the nistec scalar multiplication functions.
|
||||
func (curve *sm2Curve) normalizeScalar(scalar []byte) []byte {
|
||||
byteSize := (curve.params.N.BitLen() + 7) / 8
|
||||
if len(scalar) == byteSize {
|
||||
return scalar
|
||||
}
|
||||
s := new(big.Int).SetBytes(scalar)
|
||||
if len(scalar) > byteSize {
|
||||
s.Mod(s, curve.params.N)
|
||||
}
|
||||
out := make([]byte, byteSize)
|
||||
return s.FillBytes(out)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||
p, err := curve.pointFromAffine(Bx, By)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: ScalarMult was called on an invalid point")
|
||||
}
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err = p.ScalarMult(p, scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
|
||||
scalar = curve.normalizeScalar(scalar)
|
||||
p, err := curve.newPoint().ScalarBaseMult(scalar)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
||||
|
||||
// CombinedMult returns [s1]G + [s2]P where G is the generator. It's used
|
||||
// through an interface upgrade in crypto/ecdsa.
|
||||
func (curve *sm2Curve) CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int) {
|
||||
s1 = curve.normalizeScalar(s1)
|
||||
q, err := curve.newPoint().ScalarBaseMult(s1)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
p, err := curve.pointFromAffine(Px, Py)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: CombinedMult was called on an invalid point")
|
||||
}
|
||||
s2 = curve.normalizeScalar(s2)
|
||||
p, err = p.ScalarMult(p, s2)
|
||||
if err != nil {
|
||||
panic("sm2/elliptic: sm2 rejected normalized scalar")
|
||||
}
|
||||
return curve.pointToAffine(p.Add(p, q))
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) Unmarshal(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || data[0] != 4 {
|
||||
return nil, nil
|
||||
}
|
||||
// Use SetBytes to check that data encodes a valid point.
|
||||
_, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// We don't use pointToAffine because it involves an expensive field
|
||||
// inversion to convert from Jacobian to affine coordinates, which we
|
||||
// already have.
|
||||
byteLen := (curve.params.BitSize + 7) / 8
|
||||
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(data[1+byteLen:])
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve *sm2Curve) UnmarshalCompressed(data []byte) (x, y *big.Int) {
|
||||
if len(data) == 0 || (data[0] != 2 && data[0] != 3) {
|
||||
return nil, nil
|
||||
}
|
||||
p, err := curve.newPoint().SetBytes(data)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return curve.pointToAffine(p)
|
||||
}
|
@ -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 {
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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[:])
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
20
sm9/sm9.go
20
sm9/sm9.go
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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, "/")
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user