sm2: support ecdh mqv

This commit is contained in:
Sun Yimin 2022-09-02 14:03:51 +08:00 committed by GitHub
parent 6a556b26d3
commit 8f7a7626ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 2141 additions and 59 deletions

View File

@ -23,7 +23,10 @@ type Curve interface {
ECDH(local *PrivateKey, remote *PublicKey) ([]byte, error)
// SM2MQV performs a SM2 specific style ECMQV exchange and return the shared secret.
//SM2MQV(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error)
SM2MQV(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error)
// SM2SharedKey performs SM2 key derivation to generate shared keying data, the uv was generated by SM2MQV.
SM2SharedKey(isResponder bool, kenLen int, uv, sPub, sRemote *PublicKey, uid []byte, remoteUID []byte) ([]byte, error)
// GenerateKey generates a new PrivateKey from rand.
GenerateKey(rand io.Reader) (*PrivateKey, error)

View File

@ -5,6 +5,7 @@ import (
"crypto"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"testing"
@ -23,6 +24,14 @@ var _ interface {
Equal(x crypto.PrivateKey) bool
} = &ecdh.PrivateKey{}
func hexDecode(t *testing.T, s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
t.Fatal("invalid hex string:", s)
}
return b
}
func TestECDH(t *testing.T) {
aliceKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
@ -69,6 +78,180 @@ func TestECDH(t *testing.T) {
}
}
func TestSM2MQV(t *testing.T) {
aliceSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSecret, err := ecdh.P256().SM2MQV(bobSKey, bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := ecdh.P256().SM2MQV(aliceSKey, aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
}
func TestSM2SharedKey(t *testing.T) {
aliceSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
bobSecret, err := ecdh.P256().SM2MQV(bobSKey, bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := ecdh.P256().SM2MQV(aliceSKey, aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
bobKey, err := ecdh.P256().SM2SharedKey(true, 48, bobSecret, bobSKey.PublicKey(), aliceSKey.PublicKey(), []byte("Bob"), []byte("Alice"))
if err != nil {
t.Fatal(err)
}
aliceKey, err := ecdh.P256().SM2SharedKey(false, 48, aliceSecret, aliceSKey.PublicKey(), bobSKey.PublicKey(), []byte("Alice"), []byte("Bob"))
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bobKey, aliceKey) {
t.Error("two SM2SharedKey computations came out different")
}
}
var vectors = []struct {
LocalStaticPriv, LocalEphemeralPriv string
RemoteStaticPriv, RemoteEphemeralPriv string
SharedSecret, Key string
}{
{
"e04c3fd77408b56a648ad439f673511a2ae248def3bab26bdfc9cdbd0ae9607e",
"6fe0bac5b09d3ab10f724638811c34464790520e4604e71e6cb0e5310623b5b1",
"7a1136f60d2c5531447e5a3093078c2a505abf74f33aefed927ac0a5b27e7dd7",
"d0233bdbb0b8a7bfe1aab66132ef06fc4efaedd5d5000692bc21185242a31f6f",
"046ab5c9709277837cedc515730d04751ef81c71e81e0e52357a98cf41796ab560508da6e858b40c6264f17943037434174284a847f32c4f54104a98af5148d89f",
"1ad809ebc56ddda532020c352e1e60b121ebeb7b4e632db4dd90a362cf844f8bba85140e30984ddb581199bf5a9dda22",
},
{
"cb5ac204b38d0e5c9fc38a467075986754018f7dbb7cbbc5b4c78d56a88a8ad8",
"1681a66c02b67fdadfc53cba9b417b9499d0159435c86bb8760c3a03ae157539",
"4f54b10e0d8e9e2fe5cc79893e37fd0fd990762d1372197ed92dde464b2773ef",
"a2fe43dea141e9acc88226eaba8908ad17e81376c92102cb8186e8fef61a8700",
"04677d055355a1dcc9de4df00d3a80b6daa76bdf54ff7e0a3a6359fcd0c6f1e4b4697fffc41bbbcc3a28ea3aa1c6c380d1e92f142233afa4b430d02ab4cebc43b2",
"7a103ae61a30ed9df573a5febb35a9609cbed5681bcb98a8545351bf7d6824cc4635df5203712ea506e2e3c4ec9b12e7",
},
{
"ee690a34a779ab48227a2f68b062a80f92e26d82835608dd01b7452f1e4fb296",
"2046c6cee085665e9f3abeba41fd38e17a26c08f2f5e8f0e1007afc0bf6a2a5d",
"8ef49ea427b13cc31151e1c96ae8a48cb7919063f2d342560fb7eaaffb93d8fe",
"9baf8d602e43fbae83fedb7368f98c969d378b8a647318f8cafb265296ae37de",
"04f7e9f1447968b284ff43548fcec3752063ea386b48bfabb9baf2f9c1caa05c2fb12c2cca37326ce27e68f8cc6414c2554895519c28da1ca21e61890d0bc525c4",
"b18e78e5072f301399dc1f4baf2956c0ed2d5f52f19abb1705131b0865b079031259ee6c629b4faed528bcfa1c5d2cbc",
},
}
func TestSM2SharedKeyVectors(t *testing.T) {
initiator := []byte("Alice")
responder := []byte("Bob")
kenLen := 48
for i, v := range vectors {
aliceSKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.LocalStaticPriv))
if err != nil {
t.Fatal(err)
}
aliceEKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.LocalEphemeralPriv))
if err != nil {
t.Fatal(err)
}
bobSKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.RemoteStaticPriv))
if err != nil {
t.Fatal(err)
}
bobEKey, err := ecdh.P256().NewPrivateKey(hexDecode(t, v.RemoteEphemeralPriv))
if err != nil {
t.Fatal(err)
}
bobSecret, err := ecdh.P256().SM2MQV(bobSKey, bobEKey, aliceSKey.PublicKey(), aliceEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
aliceSecret, err := ecdh.P256().SM2MQV(aliceSKey, aliceEKey, bobSKey.PublicKey(), bobEKey.PublicKey())
if err != nil {
t.Fatal(err)
}
if !aliceSecret.Equal(bobSecret) {
t.Error("two SM2MQV computations came out different")
}
if !bytes.Equal(aliceSecret.Bytes(), hexDecode(t, v.SharedSecret)) {
t.Errorf("%v shared secret is not expected.", i)
}
bobKey, err := ecdh.P256().SM2SharedKey(true, kenLen, bobSecret, bobSKey.PublicKey(), aliceSKey.PublicKey(), responder, initiator)
if err != nil {
t.Fatal(err)
}
aliceKey, err := ecdh.P256().SM2SharedKey(false, kenLen, aliceSecret, aliceSKey.PublicKey(), bobSKey.PublicKey(), initiator, responder)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bobKey, aliceKey) {
t.Error("two SM2SharedKey computations came out different")
}
if !bytes.Equal(bobKey, hexDecode(t, v.Key)) {
t.Errorf("%v keying data is not expected.", i)
}
}
}
type countingReader struct {
r io.Reader
n int

View File

@ -3,18 +3,24 @@ package ecdh
import (
"encoding/binary"
"errors"
"hash"
"io"
"math/bits"
"github.com/emmansun/gmsm/internal/randutil"
sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/kdf"
"github.com/emmansun/gmsm/sm3"
)
type sm2Curve struct {
name string
newPoint func() *sm2ec.SM2P256Point
scalarOrder []byte
constantA []byte
constantB []byte
generator []byte
}
func (c *sm2Curve) String() string {
@ -107,6 +113,91 @@ func (c *sm2Curve) ECDH(local *PrivateKey, remote *PublicKey) ([]byte, error) {
return p.BytesX()
}
func (c *sm2Curve) sm2avf(secret *PublicKey) []byte {
bytes := secret.publicKey[1:33]
var result [32]byte
copy(result[16:], bytes[16:])
result[16] = (result[16] & 0x7f) | 0x80
return result[:]
}
func (c *sm2Curve) SM2MQV(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error) {
// implicitSig: (sLocal + avf(eLocal.Pub) * ePriv) mod N
x2 := c.sm2avf(eLocal.PublicKey())
t, err := sm2ec.ImplicitSig(sLocal.privateKey, eLocal.privateKey, x2)
if err != nil {
return nil, err
}
// new base point: peerPub + [x1](peerSecret)
x1 := c.sm2avf(eRemote)
p2, err := c.newPoint().SetBytes(eRemote.publicKey)
if err != nil {
return nil, err
}
if _, err := p2.ScalarMult(p2, x1); err != nil {
return nil, err
}
p1, err := c.newPoint().SetBytes(sRemote.publicKey)
if err != nil {
return nil, err
}
p2.Add(p1, p2)
if _, err := p2.ScalarMult(p2, t); err != nil {
return nil, err
}
return c.NewPublicKey(p2.Bytes())
}
func (c *sm2Curve) SM2SharedKey(isResponder bool, kenLen int, uv, sPub, sRemote *PublicKey, uid []byte, remoteUID []byte) ([]byte, error) {
var buffer [128]byte
copy(buffer[:], uv.publicKey[1:])
peerZ, err := c.sm2za(sm3.New(), sRemote, remoteUID)
if err != nil {
return nil, err
}
z, err := c.sm2za(sm3.New(), sPub, uid)
if err != nil {
return nil, err
}
if isResponder {
copy(buffer[64:], peerZ)
copy(buffer[96:], z)
} else {
copy(buffer[64:], z)
copy(buffer[96:], peerZ)
}
return kdf.Kdf(sm3.New(), buffer[:], kenLen), nil
}
var defaultUID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}
// CalculateZA ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
// Compliance with GB/T 32918.2-2016 5.5
func (c *sm2Curve) sm2za(md hash.Hash, pub *PublicKey, uid []byte) ([]byte, error) {
if len(uid) == 0 {
uid = defaultUID
}
uidLen := len(uid)
if uidLen >= 0x2000 {
return nil, errors.New("ecdh: the uid is too long")
}
entla := uint16(uidLen) << 3
md.Write([]byte{byte(entla >> 8), byte(entla)})
if uidLen > 0 {
md.Write(uid)
}
md.Write(c.constantA)
md.Write(c.constantB)
md.Write(c.generator)
md.Write(pub.publicKey[1:])
return md.Sum(nil), nil
}
// P256 returns a Curve which implements SM2, also known as sm2p256v1
//
// Multiple invocations of this function will return the same value, so it can
@ -117,9 +208,15 @@ var sm2P256 = &sm2Curve{
name: "sm2p256v1",
newPoint: sm2ec.NewSM2P256Point,
scalarOrder: sm2P256Order,
generator: sm2Generator,
constantA: sm2ConstantA,
constantB: sm2ConstantB,
}
var sm2P256Order = []byte{0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b, 0x53, 0xbb, 0xf4, 0x09, 0x39, 0xd5, 0x41, 0x23}
var sm2Generator = []byte{0x32, 0xc4, 0xae, 0x2c, 0x1f, 0x19, 0x81, 0x19, 0x5f, 0x99, 0x4, 0x46, 0x6a, 0x39, 0xc9, 0x94, 0x8f, 0xe3, 0xb, 0xbf, 0xf2, 0x66, 0xb, 0xe1, 0x71, 0x5a, 0x45, 0x89, 0x33, 0x4c, 0x74, 0xc7, 0xbc, 0x37, 0x36, 0xa2, 0xf4, 0xf6, 0x77, 0x9c, 0x59, 0xbd, 0xce, 0xe3, 0x6b, 0x69, 0x21, 0x53, 0xd0, 0xa9, 0x87, 0x7c, 0xc6, 0x2a, 0x47, 0x40, 0x2, 0xdf, 0x32, 0xe5, 0x21, 0x39, 0xf0, 0xa0}
var sm2ConstantA = []byte{0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc}
var sm2ConstantB = []byte{0x28, 0xe9, 0xfa, 0x9e, 0x9d, 0x9f, 0x5e, 0x34, 0x4d, 0x5a, 0x9e, 0x4b, 0xcf, 0x65, 0x09, 0xa7, 0xf3, 0x97, 0x89, 0xf5, 0x15, 0xab, 0x8f, 0x92, 0xdd, 0xbc, 0xbd, 0x41, 0x4d, 0x94, 0x0e, 0x93}
// isLess returns whether a < b, where a and b are big-endian buffers of the
// same length and shorter than 72 bytes.

4
go.mod
View File

@ -3,6 +3,6 @@ module github.com/emmansun/gmsm
go 1.16
require (
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
)

8
go.sum
View File

@ -1,11 +1,11 @@
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,136 @@
package fiat_test
import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"math/big"
"testing"
"github.com/emmansun/gmsm/internal/sm2ec/fiat"
)
var ordN *big.Int
func init() {
// n=115792089210356248756420345214020892766061623724957744567843809356293439045923
// p-n=188730266966446886577384576996245946076
ordN, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
}
var testValues = [20]string{
"e576e1aefe41c42a634a6937982dd8ea60654c4d406ef141018072b8a8ee10ff",
"374bf8d3ed1a35a109ccc73276e4fa3697d942eafcd514a82a985d0820f02645",
"d62fd995bdc9ed6d405cad6a5cd48e0b92b465c2c8fbb7b14cc86e16e6dba6e8",
"a8c28fe4b2c4abad3759ac3cb97c23eb0440273277f8d8be794eea0a2561357d",
"f3bcfff783d0eb4de34bffd0c6290f75381bf715a1bc2b02ffbb58cc794ef1b7",
"a08b119bb9bf49b2cda951de57df6e95f413a609aefa51eefa554a4906963942",
"1b767aabebdf28a447de4c37b18d8c86e431c70acbb6d05eab459180e3731075",
"40616625f9dd4e7c396106e539ed7891636acfb3ba7f80e72dc305b8cb2955d8",
"3246e27330be55dc574e97a9e0c5ab6a476bb2b5422e8c47b2248a40504fc8a0",
"aa54dec0a14ee69417186ff2711e59282d5badc3faa1528c4171e14baa525865",
"408817dd964bd439aec08c3ebda707dc8ff969d25aef0ec0ba6085bc8da6996f",
"99ed1792abdda9f0e43fd50c59a57b7f9c3c60d69c8046c71b67a1a71d9f7d55",
"455705f9823bd5ba6f58c2a4dbdf6f10a0de1947a82c2653b00833ea39e26b5d",
"b43fdba6043be8524bcc4cd6ab7d71534fcaf42869ab838e98608d5e9d801cf9",
"c97498821b3b4db41239d1a3d47d49754e5e6b7bb7ae21d4eb0826bd5c0aeed6",
"c0213f02d06c935b798594c9c3b4feaebea881205733a21484a48df4643fbde7",
"313c9f7129eb1a09c385dc755aab9d88fcab79a7e4deaca68dd08d93fd68d252",
"eb7b96f239402bd494dc258672cd4a1643ae9fe092ddaaca54f9e909548eaa90",
"24567a167761a040aed80ea4655616b5aae5a0548b2a2a39a99bd4a6d7791610",
"c79886c5cd9de1f2a0deee1c76cd8c38da7dcd401f59ec4bebbaf815006f2f71",
}
func TestGenerateValues(t *testing.T) {
p, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
for i := 0; i < 20; i++ {
k, _ := rand.Int(rand.Reader, p)
if k.Sign() > 0 {
fmt.Printf("%v\n", hex.EncodeToString(k.Bytes()))
}
}
}
func p256OrderMulTest(t *testing.T, x, y, n *big.Int) {
var scalar1 [32]byte
var scalar2 [32]byte
var scalar [32]byte
x1 := new(big.Int).Mod(x, n)
y1 := new(big.Int).Mod(y, n)
ax := new(fiat.SM2P256OrderElement)
ay := new(fiat.SM2P256OrderElement)
res := new(fiat.SM2P256OrderElement)
x1.FillBytes(scalar1[:])
y1.FillBytes(scalar2[:])
_, err := ax.SetBytes(scalar1[:])
if err != nil {
t.Error(err)
}
if !bytes.Equal(scalar1[:], ax.Bytes()) {
t.Errorf("x SetBytes/Bytes error, expected %v, got %v\n", hex.EncodeToString(scalar1[:]), hex.EncodeToString(ax.Bytes()))
}
_, err = ay.SetBytes(scalar2[:])
if err != nil {
t.Error(err)
}
if !bytes.Equal(scalar2[:], ay.Bytes()) {
t.Errorf("y SetBytes/Bytes error, expected %v, got %v\n", hex.EncodeToString(scalar2[:]), hex.EncodeToString(ay.Bytes()))
}
res = res.Mul(ax, ay)
expected := new(big.Int).Mul(x1, y1)
expected = expected.Mod(expected, n)
expected.FillBytes(scalar[:])
if !bytes.Equal(res.Bytes(), scalar[:]) {
t.Errorf("expected %v, got %v\n", hex.EncodeToString(scalar[:]), hex.EncodeToString(res.Bytes()))
}
}
func TestP256Mul(t *testing.T) {
for i := 0; i < 20; i += 2 {
x, _ := new(big.Int).SetString(testValues[i], 16)
y, _ := new(big.Int).SetString(testValues[i+1], 16)
p256OrderMulTest(t, x, y, ordN)
}
}
func TestP256Square(t *testing.T) {
var scalar [32]byte
for i := 0; i < 20; i++ {
x, _ := new(big.Int).SetString(testValues[i], 16)
ax := new(fiat.SM2P256OrderElement)
ax.SetBytes(x.Bytes())
res := new(fiat.SM2P256OrderElement)
res.Square(ax)
expected := new(big.Int).Mul(x, x)
expected = expected.Mod(expected, ordN)
expected.FillBytes(scalar[:])
if !bytes.Equal(res.Bytes(), scalar[:]) {
t.Errorf("expected %v, got %v\n", hex.EncodeToString(scalar[:]), hex.EncodeToString(res.Bytes()))
}
}
}
func TestP256Add(t *testing.T) {
var scalar [32]byte
for i := 0; i < 20; i += 2 {
x, _ := new(big.Int).SetString(testValues[i], 16)
y, _ := new(big.Int).SetString(testValues[i+1], 16)
expected := new(big.Int).Add(x, y)
expected = expected.Mod(expected, ordN)
expected.FillBytes(scalar[:])
ax := new(fiat.SM2P256OrderElement)
ax.SetBytes(x.Bytes())
ay := new(fiat.SM2P256OrderElement)
ay.SetBytes(y.Bytes())
res := new(fiat.SM2P256OrderElement)
res.Add(ax, ay)
if !bytes.Equal(res.Bytes(), scalar[:]) {
t.Errorf("expected %v, got %v\n", hex.EncodeToString(scalar[:]), hex.EncodeToString(res.Bytes()))
}
}
}

View File

@ -15,6 +15,13 @@ func p256OrdMul(res, in1, in2 *p256OrdElement)
//go:noescape
func p256OrdSqr(res, in *p256OrdElement, n int)
// 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.
var RR = &p256OrdElement{0x901192af7c114f20, 0x3464504ade6fa2fa, 0x620fc84c3affe0d4, 0x1eb5e412a22b3d3b}
// P256OrdInverse, sets out to in⁻¹ mod org(G). If in is zero, out will be zero.
// n-2 =
// 1111111111111111111111111111111011111111111111111111111111111111
@ -43,13 +50,6 @@ func P256OrdInverse(k []byte) ([]byte, error) {
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
@ -98,12 +98,35 @@ func P256OrdInverse(k []byte) ([]byte, error) {
p256OrdSqr(x, x, int(s))
p256OrdMul(x, x, muls[i])
}
return p256OrderFromMont(x), nil
}
// P256OrdMul multiplication modulo org(G).
func P256OrdMul(in1, in2 []byte) ([]byte, error) {
if len(in1) != 32 || len(in2) != 32 {
return nil, errors.New("invalid scalar length")
}
x1 := new(p256OrdElement)
p256OrdBigToLittle(x1, toElementArray(in1))
p256OrdMul(x1, x1, RR)
x2 := new(p256OrdElement)
p256OrdBigToLittle(x2, toElementArray(in2))
p256OrdMul(x2, x2, RR)
res := new(p256OrdElement)
p256OrdMul(res, x1, x2)
return p256OrderFromMont(res), nil
}
func p256OrderFromMont(in *p256OrdElement) []byte {
// 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)
p256OrdMul(in, in, one)
var xOut [32]byte
p256OrdLittleToBig(&xOut, x)
return xOut[:], nil
p256OrdLittleToBig(&xOut, in)
return xOut[:]
}

View File

@ -1,6 +1,3 @@
//go:build (amd64 && !generic) || (arm64 && !generic)
// +build amd64,!generic arm64,!generic
package sm2ec_test
import (

View File

@ -74,6 +74,10 @@ func TestSM2P256MontgomeryDomainN(t *testing.T) {
in string
out string
}{
{ // One
"01",
"010000000000000000000000008dfc2094de39fad4ac440bf6c62abedd",
},
{ // R
"010000000000000000000000000000000000000000000000000000000000000000",
"1eb5e412a22b3d3b620fc84c3affe0d43464504ade6fa2fa901192af7c114f20",

View File

@ -0,0 +1,78 @@
package sm2ec
import (
"encoding/binary"
"errors"
"math/bits"
)
var p256Order = [4]uint64{0x53bbf40939d54123, 0x7203df6b21c6052b,
0xffffffffffffffff, 0xfffffffeffffffff}
func fromBytes(bytes []byte) (*[4]uint64, error) {
if len(bytes) != 32 {
return nil, errors.New("invalid scalar length")
}
var t [4]uint64
t[0] = binary.BigEndian.Uint64(bytes[24:])
t[1] = binary.BigEndian.Uint64(bytes[16:])
t[2] = binary.BigEndian.Uint64(bytes[8:])
t[3] = binary.BigEndian.Uint64(bytes)
return &t, nil
}
func toBytes(t *[4]uint64) []byte {
var bytes [32]byte
binary.BigEndian.PutUint64(bytes[:], t[3])
binary.BigEndian.PutUint64(bytes[8:], t[2])
binary.BigEndian.PutUint64(bytes[16:], t[1])
binary.BigEndian.PutUint64(bytes[24:], t[0])
return bytes[:]
}
// p256OrdAdd sets res = x + y.
func p256OrdAdd(res, x, y *[4]uint64) {
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], p256Order[0], 0)
t2[1], b = bits.Sub64(t1[1], p256Order[1], b)
t2[2], b = bits.Sub64(t1[2], p256Order[2], b)
t2[3], b = bits.Sub64(t1[3], p256Order[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)
}
func ImplicitSig(sPriv, ePriv, t []byte) ([]byte, error) {
mulRes, err := P256OrdMul(ePriv, t)
if err != nil {
return nil, err
}
t1, err := fromBytes(mulRes)
if err != nil {
return nil, err
}
t2, err := fromBytes(sPriv)
if err != nil {
return nil, err
}
var t3 [4]uint64
p256OrdAdd(&t3, t1, t2)
return toBytes(&t3), nil
}

View File

@ -0,0 +1,48 @@
package sm2ec_test
import (
"bytes"
"crypto/rand"
"encoding/hex"
"io"
"math/big"
"testing"
"github.com/emmansun/gmsm/internal/sm2ec"
)
func randomK(r io.Reader, ord *big.Int) (k *big.Int, err error) {
for {
k, err = rand.Int(r, ord)
if k.Sign() > 0 || err != nil {
return
}
}
}
func TestImplicitSig(t *testing.T) {
n, _ := new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
sPriv, err := randomK(rand.Reader, n)
if err != nil {
t.Fatal(err)
}
ePriv, err := randomK(rand.Reader, n)
if err != nil {
t.Fatal(err)
}
k, err := randomK(rand.Reader, n)
if err != nil {
t.Fatal(err)
}
res1, err := sm2ec.ImplicitSig(sPriv.Bytes(), ePriv.Bytes(), k.Bytes())
if err != nil {
t.Fatal(err)
}
res2 := new(big.Int)
res2.Mul(ePriv, k)
res2.Add(res2, sPriv)
res2.Mod(res2, n)
if !bytes.Equal(res1, res2.Bytes()) {
t.Errorf("expected %s, got %s", hex.EncodeToString(res1), hex.EncodeToString(res2.Bytes()))
}
}

View File

@ -0,0 +1,128 @@
//go:build (!amd64 && !arm64) || generic
// +build !amd64,!arm64 generic
package sm2ec
import (
"errors"
"github.com/emmansun/gmsm/internal/sm2ec/fiat"
)
// 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(fiat.SM2P256OrderElement)
_1 := new(fiat.SM2P256OrderElement)
_, err := _1.SetBytes(k)
if err != nil {
return nil, err
}
_11 := new(fiat.SM2P256OrderElement)
_101 := new(fiat.SM2P256OrderElement)
_111 := new(fiat.SM2P256OrderElement)
_1111 := new(fiat.SM2P256OrderElement)
_10101 := new(fiat.SM2P256OrderElement)
_101111 := new(fiat.SM2P256OrderElement)
t := new(fiat.SM2P256OrderElement)
m := new(fiat.SM2P256OrderElement)
m.Square(_1)
_11.Mul(m, _1)
_101.Mul(m, _11)
_111.Mul(m, _101)
x.Square(_101)
_1111.Mul(_101, x)
t.Square(x)
_10101.Mul(t, _1)
x.Square(_10101)
_101111.Mul(x, _101)
x.Mul(_10101, x)
t.Square(x)
t.Square(t)
m.Mul(t, m)
t.Mul(t, _11)
x.Square(t)
for i := 1; i < 8; i++ {
x.Square(x)
}
m.Mul(x, m)
x.Mul(x, t)
t.Square(x)
for i := 1; i < 16; i++ {
t.Square(t)
}
m.Mul(t, m)
t.Mul(t, x)
x.Square(m)
for i := 1; i < 32; i++ {
x.Square(x)
}
x.Mul(x, t)
for i := 0; i < 32; i++ {
x.Square(x)
}
x.Mul(x, t)
for i := 0; i < 32; i++ {
x.Square(x)
}
x.Mul(x, t)
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 := []*fiat.SM2P256OrderElement{
_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 {
for j := 0; j < int(s); j++ {
x.Square(x)
}
x.Mul(x, muls[i])
}
return x.Bytes(), nil
}
// P256OrdMul multiplication modulo org(G).
func P256OrdMul(in1, in2 []byte) ([]byte, error) {
if len(in1) != 32 || len(in2) != 32 {
return nil, errors.New("invalid scalar length")
}
ax := new(fiat.SM2P256OrderElement)
ay := new(fiat.SM2P256OrderElement)
res := new(fiat.SM2P256OrderElement)
_, err := ax.SetBytes(in1)
if err != nil {
return nil, err
}
_, err = ay.SetBytes(in2)
if err != nil {
return nil, err
}
res = res.Mul(ax, ay)
return res.Bytes(), nil
}

View File

@ -214,7 +214,14 @@ func (ke *KeyExchange) mqv() {
ke.v.X, ke.v.Y = ke.privateKey.ScalarMult(x, y, t.Bytes())
}
func respondKeyExchange(ke *KeyExchange, r *big.Int) (*ecdsa.PublicKey, []byte, error) {
func respondKeyExchange(ke *KeyExchange, rA *ecdsa.PublicKey, r *big.Int) (*ecdsa.PublicKey, []byte, error) {
if ke.peerPub == nil {
return nil, nil, errors.New("sm2: no peer public key given")
}
if !ke.privateKey.IsOnCurve(rA.X, rA.Y) {
return nil, nil, errors.New("sm2: invalid initiator's ephemeral public key")
}
ke.peerSecret = rA
// secret = RB = [r]G
ke.secret.X, ke.secret.Y = ke.privateKey.ScalarBaseMult(r.Bytes())
ke.r = r
@ -236,18 +243,11 @@ func respondKeyExchange(ke *KeyExchange, r *big.Int) (*ecdsa.PublicKey, []byte,
//
// It will check if there are peer's public key and validate the peer's Ephemeral Public Key.
func (ke *KeyExchange) RepondKeyExchange(rand io.Reader, rA *ecdsa.PublicKey) (*ecdsa.PublicKey, []byte, error) {
if ke.peerPub == nil {
return nil, nil, errors.New("sm2: no peer public key given")
}
if !ke.privateKey.IsOnCurve(rA.X, rA.Y) {
return nil, nil, errors.New("sm2: invalid initiator's ephemeral public key")
}
ke.peerSecret = rA
r, err := randFieldElement(ke.privateKey, rand)
if err != nil {
return nil, nil, err
}
return respondKeyExchange(ke, r)
return respondKeyExchange(ke, rA, r)
}
// ConfirmResponder for initiator's step A4-A10, returns keying data and optional signature.

View File

@ -1,22 +1,118 @@
package sm2
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"errors"
"math/big"
"testing"
)
var vectors = []struct {
LocalStaticPriv, LocalEphemeralPriv string
RemoteStaticPriv, RemoteEphemeralPriv string
SharedSecret, Key string
}{
{
"e04c3fd77408b56a648ad439f673511a2ae248def3bab26bdfc9cdbd0ae9607e",
"6fe0bac5b09d3ab10f724638811c34464790520e4604e71e6cb0e5310623b5b1",
"7a1136f60d2c5531447e5a3093078c2a505abf74f33aefed927ac0a5b27e7dd7",
"d0233bdbb0b8a7bfe1aab66132ef06fc4efaedd5d5000692bc21185242a31f6f",
"046ab5c9709277837cedc515730d04751ef81c71e81e0e52357a98cf41796ab560508da6e858b40c6264f17943037434174284a847f32c4f54104a98af5148d89f",
"1ad809ebc56ddda532020c352e1e60b121ebeb7b4e632db4dd90a362cf844f8bba85140e30984ddb581199bf5a9dda22",
},
{
"cb5ac204b38d0e5c9fc38a467075986754018f7dbb7cbbc5b4c78d56a88a8ad8",
"1681a66c02b67fdadfc53cba9b417b9499d0159435c86bb8760c3a03ae157539",
"4f54b10e0d8e9e2fe5cc79893e37fd0fd990762d1372197ed92dde464b2773ef",
"a2fe43dea141e9acc88226eaba8908ad17e81376c92102cb8186e8fef61a8700",
"04677d055355a1dcc9de4df00d3a80b6daa76bdf54ff7e0a3a6359fcd0c6f1e4b4697fffc41bbbcc3a28ea3aa1c6c380d1e92f142233afa4b430d02ab4cebc43b2",
"7a103ae61a30ed9df573a5febb35a9609cbed5681bcb98a8545351bf7d6824cc4635df5203712ea506e2e3c4ec9b12e7",
},
{
"ee690a34a779ab48227a2f68b062a80f92e26d82835608dd01b7452f1e4fb296",
"2046c6cee085665e9f3abeba41fd38e17a26c08f2f5e8f0e1007afc0bf6a2a5d",
"8ef49ea427b13cc31151e1c96ae8a48cb7919063f2d342560fb7eaaffb93d8fe",
"9baf8d602e43fbae83fedb7368f98c969d378b8a647318f8cafb265296ae37de",
"04f7e9f1447968b284ff43548fcec3752063ea386b48bfabb9baf2f9c1caa05c2fb12c2cca37326ce27e68f8cc6414c2554895519c28da1ca21e61890d0bc525c4",
"b18e78e5072f301399dc1f4baf2956c0ed2d5f52f19abb1705131b0865b079031259ee6c629b4faed528bcfa1c5d2cbc",
},
}
func hexDecode(t *testing.T, s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
t.Fatal("invalid hex string:", s)
}
return b
}
func TestKeyExchangeSample(t *testing.T) {
priv1, _ := GenerateKey(rand.Reader)
priv2, _ := GenerateKey(rand.Reader)
initiator, err := NewKeyExchange(priv1, &priv2.PublicKey, []byte("Alice"), []byte("Bob"), 32, true)
initiatorUID := []byte("Alice")
responderUID := []byte("Bob")
kenLen := 48
for i, v := range vectors {
priv1 := new(PrivateKey)
priv1.D, _ = new(big.Int).SetString(v.LocalStaticPriv, 16)
priv1.Curve = P256()
priv1.X, priv1.Y = priv1.Curve.ScalarBaseMult(priv1.D.Bytes())
priv2 := new(PrivateKey)
priv2.D, _ = new(big.Int).SetString(v.RemoteStaticPriv, 16)
priv2.Curve = P256()
priv2.X, priv2.Y = priv1.Curve.ScalarBaseMult(priv2.D.Bytes())
initiator, err := NewKeyExchange(priv1, &priv2.PublicKey, initiatorUID, responderUID, kenLen, true)
if err != nil {
t.Fatal(err)
}
responder, err := NewKeyExchange(priv2, &priv1.PublicKey, []byte("Bob"), []byte("Alice"), 32, true)
responder, err := NewKeyExchange(priv2, &priv1.PublicKey, responderUID, initiatorUID, 48, true)
if err != nil {
t.Fatal(err)
}
defer func() {
initiator.Destroy()
responder.Destroy()
}()
rA, _ := new(big.Int).SetString(v.LocalEphemeralPriv, 16)
initKeyExchange(initiator, rA)
rB, _ := new(big.Int).SetString(v.RemoteEphemeralPriv, 16)
RB, s2, _ := respondKeyExchange(responder, initiator.secret, rB)
key1, s1, err := initiator.ConfirmResponder(RB, s2)
if err != nil {
t.Fatal(err)
}
key2, err := responder.ConfirmInitiator(s1)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(key1, key2) {
t.Errorf("got different key")
}
if !bytes.Equal(key1, hexDecode(t, v.Key)) {
t.Errorf("case %v got unexpected keying data", i)
}
if !bytes.Equal(elliptic.Marshal(initiator.v.Curve, initiator.v.X, initiator.v.Y), hexDecode(t, v.SharedSecret)) {
t.Errorf("case %v got unexpected shared key", i)
}
}
}
func TestKeyExchange(t *testing.T) {
priv1, _ := GenerateKey(rand.Reader)
priv2, _ := GenerateKey(rand.Reader)
initiator, err := NewKeyExchange(priv1, &priv2.PublicKey, []byte("Alice"), []byte("Bob"), 48, true)
if err != nil {
t.Fatal(err)
}
responder, err := NewKeyExchange(priv2, &priv1.PublicKey, []byte("Bob"), []byte("Alice"), 48, true)
if err != nil {
t.Fatal(err)
}

View File

@ -174,3 +174,21 @@ func (curve *sm2Curve) UnmarshalCompressed(data []byte) (x, y *big.Int) {
}
return curve.pointToAffine(p)
}
// Inverse, implements invertible interface, used by Sign()
func (curve *sm2Curve) Inverse(k *big.Int) *big.Int {
if k.Sign() < 0 {
// This should never happen.
k = new(big.Int).Neg(k)
}
if k.Cmp(curve.params.N) >= 0 {
// This should never happen.
k = new(big.Int).Mod(k, curve.params.N)
}
scalar := k.FillBytes(make([]byte, 32))
inverse, err := _sm2ec.P256OrdInverse(scalar)
if err != nil {
panic("sm2/elliptic: sm2 rejected normalized scalar")
}
return new(big.Int).SetBytes(inverse)
}

View File

@ -1,28 +0,0 @@
//go:build (amd64 && !generic) || (arm64 && !generic)
// +build amd64,!generic arm64,!generic
package sm2ec
import (
"math/big"
_sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
)
// 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)
}