From 23914a86c3561f84b0ab53dbcfc574c77a3009df Mon Sep 17 00:00:00 2001 From: Sun Yimin Date: Fri, 17 Jun 2022 16:58:26 +0800 Subject: [PATCH] SM2: key exchange --- sm2/sm2_keyexchange.go | 255 ++++++++++++++++++++++++++++++++++++ sm2/sm2_keyexchange_test.go | 38 ++++++ sm9/sm9.go | 9 +- 3 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 sm2/sm2_keyexchange.go create mode 100644 sm2/sm2_keyexchange_test.go diff --git a/sm2/sm2_keyexchange.go b/sm2/sm2_keyexchange.go new file mode 100644 index 0000000..e000d55 --- /dev/null +++ b/sm2/sm2_keyexchange.go @@ -0,0 +1,255 @@ +package sm2 + +import ( + "crypto/ecdsa" + goSubtle "crypto/subtle" + "errors" + "io" + "math/big" + + "github.com/emmansun/gmsm/sm3" +) + +// Point represent point on curve +type Point struct { + X *big.Int + Y *big.Int +} + +// KeyExchange key exchange struct, include internal stat in whole key exchange flow. +// Initiator's flow will be: NewKeyExchange -> InitKeyExchange -> transmission -> ConfirmResponder +// Responder's flow will be: NewKeyExchange -> waiting ... -> RepondKeyExchange -> transmission -> ConfirmInitiator +type KeyExchange struct { + genSignature bool // control the optional sign/verify step triggered by responsder + keyLength int // key length + privateKey *PrivateKey // owner's encryption private key + uid []byte // owner uid + peerUID []byte // peer uid + peerPub *ecdsa.PublicKey // peer public key + r *big.Int // random which will be used to compute secret + secret Point // generated secret which will be passed to peer + peerSecret Point // received peer's secret + w2 *big.Int // internal state which will be used when compute the key and signature + w2Minus1 *big.Int // internal state which will be used when compute the key and signature + v Point // internal state which will be used when compute the key and signature + key []byte // key will be used after key agreement +} + +// GetKey return key after key agreement +func (ke *KeyExchange) GetKey() []byte { + return ke.key +} + +// NewKeyExchange create one new KeyExchange object +func NewKeyExchange(priv *PrivateKey, peerPub *ecdsa.PublicKey, uid, peerUID []byte, keyLen int, genSignature bool) *KeyExchange { + ke := &KeyExchange{} + ke.genSignature = genSignature + ke.peerPub = peerPub + ke.keyLength = keyLen + ke.privateKey = priv + ke.uid = uid + ke.peerUID = peerUID + w := (priv.Params().N.BitLen()+1)/2 - 1 + x2 := big.NewInt(2) + ke.w2 = x2 + x2.Lsh(x2, uint(w)) + x2minus1 := (&big.Int{}).Sub(x2, big.NewInt(1)) + ke.w2Minus1 = x2minus1 + + return ke +} + +func initKeyExchange(ke *KeyExchange, r *big.Int) { + ke.secret.X, ke.secret.Y = ke.privateKey.ScalarBaseMult(r.Bytes()) + ke.r = r +} + +// InitKeyExchange generate random with responder uid, for initiator's step A1-A3 +func (ke *KeyExchange) InitKeyExchange(rand io.Reader) (*Point, error) { + r, err := randFieldElement(ke.privateKey, rand) + if err != nil { + return nil, err + } + initKeyExchange(ke, r) + return &ke.secret, nil +} + +func respondKeyExchange(ke *KeyExchange, r *big.Int, rA *Point) (*Point, []byte, error) { + ke.secret.X, ke.secret.Y = ke.privateKey.ScalarBaseMult(r.Bytes()) + ke.r = r + + t := (&big.Int{}).And(ke.w2Minus1, ke.secret.X) + t.Add(ke.w2, t) + t.Mul(t, ke.r) + t.Add(t, ke.privateKey.D) + t.Mod(t, ke.privateKey.Params().N) + + x1 := (&big.Int{}).And(ke.w2Minus1, ke.peerSecret.X) + x1.Add(ke.w2, x1) + + x3, y3 := ke.privateKey.ScalarMult(ke.peerSecret.X, ke.peerSecret.Y, x1.Bytes()) + x3, y3 = ke.privateKey.Add(ke.peerPub.X, ke.peerPub.Y, x3, y3) + ke.v.X, ke.v.Y = ke.privateKey.ScalarMult(x3, y3, t.Bytes()) + if ke.v.X.Sign() == 0 && ke.v.Y.Sign() == 0 { + return nil, nil, errors.New("sm2: key exchange fail") + } + + var buffer []byte + zA, err := calculateZA(ke.peerPub, ke.peerUID) + if err != nil { + return nil, nil, err + } + zB, err := calculateZA(&ke.privateKey.PublicKey, ke.uid) + if err != nil { + return nil, nil, err + } + buffer = append(buffer, toBytes(ke.privateKey, ke.v.X)...) + buffer = append(buffer, toBytes(ke.privateKey, ke.v.Y)...) + buffer = append(buffer, zA...) + buffer = append(buffer, zB...) + key, _ := sm3.Kdf(buffer, ke.keyLength) + ke.key = key + + if !ke.genSignature { + return &ke.secret, nil, nil + } + + hash := sm3.New() + hash.Write(toBytes(ke.privateKey, ke.v.X)) + hash.Write(zA) + hash.Write(zB) + hash.Write(toBytes(ke.privateKey, ke.peerSecret.X)) + hash.Write(toBytes(ke.privateKey, ke.peerSecret.Y)) + hash.Write(toBytes(ke.privateKey, ke.secret.X)) + hash.Write(toBytes(ke.privateKey, ke.secret.Y)) + buffer = hash.Sum(nil) + hash.Reset() + hash.Write([]byte{0x02}) + hash.Write(toBytes(ke.privateKey, ke.v.Y)) + hash.Write(buffer) + buffer = hash.Sum(nil) + + return &ke.secret, buffer, nil +} + +// RepondKeyExchange when responder receive rA, for responder's step B1-B8 +func (ke *KeyExchange) RepondKeyExchange(rand io.Reader, rA *Point) (*Point, []byte, error) { + if !ke.privateKey.IsOnCurve(rA.X, rA.Y) { + return nil, nil, errors.New("sm2: received invalid random from initiator") + } + ke.peerSecret = *rA + r, err := randFieldElement(ke.privateKey, rand) + if err != nil { + return nil, nil, err + } + return respondKeyExchange(ke, r, rA) +} + +// ConfirmResponder for initiator's step A4-A10 +func (ke *KeyExchange) ConfirmResponder(rB *Point, sB []byte) ([]byte, error) { + if !ke.privateKey.IsOnCurve(rB.X, rB.Y) { + return nil, errors.New("sm2: received invalid random from responder") + } + hash := sm3.New() + + ke.peerSecret = *rB + + t := (&big.Int{}).And(ke.w2Minus1, ke.secret.X) + t.Add(ke.w2, t) + t.Mul(t, ke.r) + t.Add(t, ke.privateKey.D) + t.Mod(t, ke.privateKey.Params().N) + + x2 := (&big.Int{}).And(ke.w2Minus1, ke.peerSecret.X) + x2.Add(ke.w2, x2) + + x3, y3 := ke.privateKey.ScalarMult(ke.peerSecret.X, ke.peerSecret.Y, x2.Bytes()) + x3, y3 = ke.privateKey.Add(ke.peerPub.X, ke.peerPub.Y, x3, y3) + ke.v.X, ke.v.Y = ke.privateKey.ScalarMult(x3, y3, t.Bytes()) + + if ke.v.X.Sign() == 0 && ke.v.Y.Sign() == 0 { + return nil, errors.New("sm2: key exchange fail") + } + + var buffer []byte + zA, err := calculateZA(&ke.privateKey.PublicKey, ke.uid) + if err != nil { + return nil, err + } + zB, err := calculateZA(ke.peerPub, ke.peerUID) + if err != nil { + return nil, err + } + buffer = append(buffer, toBytes(ke.privateKey, ke.v.X)...) + buffer = append(buffer, toBytes(ke.privateKey, ke.v.Y)...) + buffer = append(buffer, zA...) + buffer = append(buffer, zB...) + key, _ := sm3.Kdf(buffer, ke.keyLength) + ke.key = key + + if len(sB) > 0 { + hash.Write(toBytes(ke.privateKey, ke.v.X)) + hash.Write(zA) + hash.Write(zB) + hash.Write(toBytes(ke.privateKey, ke.secret.X)) + hash.Write(toBytes(ke.privateKey, ke.secret.Y)) + hash.Write(toBytes(ke.privateKey, ke.peerSecret.X)) + hash.Write(toBytes(ke.privateKey, ke.peerSecret.Y)) + buffer = hash.Sum(nil) + hash.Reset() + hash.Write([]byte{0x02}) + hash.Write(toBytes(ke.privateKey, ke.v.Y)) + hash.Write(buffer) + buffer = hash.Sum(nil) + hash.Reset() + if goSubtle.ConstantTimeCompare(buffer, sB) != 1 { + return nil, errors.New("sm2: verify responder's signature fail") + } + } + hash.Write(toBytes(ke.privateKey, ke.v.X)) + hash.Write(zA) + hash.Write(zB) + hash.Write(toBytes(ke.privateKey, ke.secret.X)) + hash.Write(toBytes(ke.privateKey, ke.secret.Y)) + hash.Write(toBytes(ke.privateKey, ke.peerSecret.X)) + hash.Write(toBytes(ke.privateKey, ke.peerSecret.Y)) + buffer = hash.Sum(nil) + hash.Reset() + hash.Write([]byte{0x03}) + hash.Write(toBytes(ke.privateKey, ke.v.Y)) + hash.Write(buffer) + buffer = hash.Sum(nil) + + return buffer, nil +} + +// ConfirmInitiator for responder's step B10 +func (ke *KeyExchange) ConfirmInitiator(s1 []byte) error { + hash := sm3.New() + var buffer []byte + zB, err := calculateZA(&ke.privateKey.PublicKey, ke.uid) + if err != nil { + return err + } + zA, err := calculateZA(ke.peerPub, ke.peerUID) + if err != nil { + return err + } + hash.Write(toBytes(ke.privateKey, ke.v.X)) + hash.Write(zA) + hash.Write(zB) + hash.Write(toBytes(ke.privateKey, ke.peerSecret.X)) + hash.Write(toBytes(ke.privateKey, ke.peerSecret.Y)) + hash.Write(toBytes(ke.privateKey, ke.secret.X)) + hash.Write(toBytes(ke.privateKey, ke.secret.Y)) + buffer = hash.Sum(nil) + hash.Reset() + hash.Write([]byte{0x03}) + hash.Write(toBytes(ke.privateKey, ke.v.Y)) + hash.Write(buffer) + buffer = hash.Sum(nil) + if goSubtle.ConstantTimeCompare(buffer, s1) != 1 { + return errors.New("sm2: verify initiator's signature fail") + } + return nil +} diff --git a/sm2/sm2_keyexchange_test.go b/sm2/sm2_keyexchange_test.go new file mode 100644 index 0000000..b489df0 --- /dev/null +++ b/sm2/sm2_keyexchange_test.go @@ -0,0 +1,38 @@ +package sm2 + +import ( + "crypto/rand" + "encoding/hex" + "testing" +) + +func TestKeyExchangeSample(t *testing.T) { + priv1, _ := GenerateKey(rand.Reader) + priv2, _ := GenerateKey(rand.Reader) + initiator := NewKeyExchange(priv1, &priv2.PublicKey, []byte("Alice"), []byte("Bob"), 32, true) + responsder := NewKeyExchange(priv2, &priv1.PublicKey, []byte("Bob"), []byte("Alice"), 32, true) + + rA, err := initiator.InitKeyExchange(rand.Reader) + if err != nil { + t.Fatal(err) + } + + rB, s2, err := responsder.RepondKeyExchange(rand.Reader, rA) + if err != nil { + t.Fatal(err) + } + + s1, err := initiator.ConfirmResponder(rB, s2) + if err != nil { + t.Fatal(err) + } + + err = responsder.ConfirmInitiator(s1) + if err != nil { + t.Fatal(err) + } + + if hex.EncodeToString(initiator.key) != hex.EncodeToString(responsder.key) { + t.Errorf("got different key") + } +} diff --git a/sm9/sm9.go b/sm9/sm9.go index cd4ed46..8b4add7 100644 --- a/sm9/sm9.go +++ b/sm9/sm9.go @@ -549,7 +549,7 @@ func (ke *KeyExchange) InitKeyExchange(rand io.Reader, hid byte) (*bn256.G1, err func respondKeyExchange(ke *KeyExchange, hid byte, r *big.Int, rA *bn256.G1) (*bn256.G1, []byte, error) { if !rA.IsOnCurve() { - return nil, nil, errors.New("SM9: received invalid random from initiator") + return nil, nil, errors.New("sm9: received invalid random from initiator") } ke.peerSecret = rA pubA := ke.privateKey.GenerateUserPublicKey(ke.peerUID, hid) @@ -605,6 +605,9 @@ func (ke *KeyExchange) RepondKeyExchange(rand io.Reader, hid byte, rA *bn256.G1) // ConfirmResponder for initiator's step A5-A7 func (ke *KeyExchange) ConfirmResponder(rB *bn256.G1, sB []byte) ([]byte, error) { + if !rB.IsOnCurve() { + return nil, errors.New("sm9: received invalid random from responder") + } hash := sm3.New() // step 5 ke.peerSecret = rB @@ -630,7 +633,7 @@ func (ke *KeyExchange) ConfirmResponder(rB *bn256.G1, sB []byte) ([]byte, error) signature := hash.Sum(nil) hash.Reset() if goSubtle.ConstantTimeCompare(signature, sB) != 1 { - return nil, errors.New("sm9: verify signature fail") + return nil, errors.New("sm9: verify responder's signature fail") } } buffer = append(buffer, ke.uid...) @@ -668,7 +671,7 @@ func (ke *KeyExchange) ConfirmInitiator(s1 []byte) error { hash.Write(buffer) buffer = hash.Sum(nil) if goSubtle.ConstantTimeCompare(buffer, s1) != 1 { - return errors.New("sm9: verify signature fail") + return errors.New("sm9: verify initiator's signature fail") } return nil }