From eea15c3e4c2069d9f153c340e86f3f4e986a4223 Mon Sep 17 00:00:00 2001 From: cliven Date: Sat, 13 Aug 2022 15:18:47 +0800 Subject: [PATCH 1/2] =?UTF-8?q?SM2=E5=AF=86=E9=92=A5=E4=BA=A4=E6=8D=A2?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E6=94=AF=E6=8C=81=E4=BA=86=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E6=97=B6=E6=B2=A1=E6=9C=89=E5=AF=B9=E7=AB=AF=E5=85=AC?= =?UTF-8?q?=E5=BC=80=E4=BF=A1=E6=81=AF=EF=BC=8C=E5=9C=A8=E5=90=8E=E7=BB=AD?= =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=85=AC=E5=BC=80=E4=BF=A1=E6=81=AF=E7=9A=84?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sm2/sm2_keyexchange.go | 43 ++++++++++++++++++++++++++++--- sm2/sm2_keyexchange_test.go | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/sm2/sm2_keyexchange.go b/sm2/sm2_keyexchange.go index c980349..5975c54 100644 --- a/sm2/sm2_keyexchange.go +++ b/sm2/sm2_keyexchange.go @@ -35,10 +35,14 @@ func (ke *KeyExchange) GetSharedKey() []byte { } // NewKeyExchange create one new KeyExchange object +// +// 在部分场景中,在初始 KeyExchange 时暂时没有对端的公开信息(如公钥、UID),这些信息可能需要在后续的交换中得到。 +// 这种情况下,可设置 peerPub、peerUID 参数为 nil,并在合适的时候通过 KeyExchange.SetPeerPub 方法配置相关参数。 +// 注意 KeyExchange.SetPeerPub 方法必须要在 KeyExchange.RepondKeyExchange 或 KeyExchange.RepondKeyExchange 方法调用。 func NewKeyExchange(priv *PrivateKey, peerPub *ecdsa.PublicKey, uid, peerUID []byte, keyLen int, genSignature bool) (ke *KeyExchange, err error) { ke = &KeyExchange{} ke.genSignature = genSignature - ke.peerPub = peerPub + ke.keyLength = keyLen ke.privateKey = priv w := (priv.Params().N.BitLen()+1)/2 - 1 @@ -48,24 +52,49 @@ func NewKeyExchange(priv *PrivateKey, peerPub *ecdsa.PublicKey, uid, peerUID []b x2minus1 := (&big.Int{}).Sub(x2, big.NewInt(1)) ke.w2Minus1 = x2minus1 + if len(uid) == 0 { + uid = defaultUID + } ke.z, err = calculateZA(&ke.privateKey.PublicKey, uid) if err != nil { return nil, err } - ke.peerZ, err = calculateZA(ke.peerPub, peerUID) + + err = ke.SetPeerPub(peerPub, peerUID) if err != nil { return nil, err } + ke.secret = &ecdsa.PublicKey{} ke.secret.Curve = priv.PublicKey.Curve - ke.peerSecret = &ecdsa.PublicKey{} - ke.peerSecret.Curve = peerPub.Curve + ke.v = &ecdsa.PublicKey{} ke.v.Curve = priv.PublicKey.Curve return } +// SetPeerPub 设置对端公开信息,该方法用于某些初期状态无法取得对端公开参数的场景。 +// 例如:在TLCP协议中,基于SM2算法ECDHE过程。 +func (ke *KeyExchange) SetPeerPub(peerPub *ecdsa.PublicKey, peerUID []byte) error { + if peerPub == nil { + return nil + } + if len(peerUID) == 0 { + peerUID = defaultUID + } + + var err error + ke.peerPub = peerPub + ke.peerZ, err = calculateZA(ke.peerPub, peerUID) + if err != nil { + return err + } + ke.peerSecret = &ecdsa.PublicKey{} + ke.peerSecret.Curve = peerPub.Curve + return nil +} + func initKeyExchange(ke *KeyExchange, r *big.Int) { ke.secret.X, ke.secret.Y = ke.privateKey.ScalarBaseMult(r.Bytes()) ke.r = r @@ -154,6 +183,9 @@ func respondKeyExchange(ke *KeyExchange, r *big.Int, rA *ecdsa.PublicKey) (*ecds // RepondKeyExchange when responder receive rA, for responder's step B1-B8 func (ke *KeyExchange) RepondKeyExchange(rand io.Reader, rA *ecdsa.PublicKey) (*ecdsa.PublicKey, []byte, error) { + if ke.peerPub == nil { + return nil, nil, errors.New("sm2: peer public not set, you probable need call KeyExchange.SetPeerPub") + } if !ke.privateKey.IsOnCurve(rA.X, rA.Y) { return nil, nil, errors.New("sm2: received invalid random from initiator") } @@ -167,6 +199,9 @@ func (ke *KeyExchange) RepondKeyExchange(rand io.Reader, rA *ecdsa.PublicKey) (* // ConfirmResponder for initiator's step A4-A10 func (ke *KeyExchange) ConfirmResponder(rB *ecdsa.PublicKey, sB []byte) ([]byte, error) { + if ke.peerPub == nil { + return nil, errors.New("sm2: peer public not set, you probable need call KeyExchange.SetPeerPub") + } if !ke.privateKey.IsOnCurve(rB.X, rB.Y) { return nil, errors.New("sm2: received invalid random from responder") } diff --git a/sm2/sm2_keyexchange_test.go b/sm2/sm2_keyexchange_test.go index bf14989..f4f4cf7 100644 --- a/sm2/sm2_keyexchange_test.go +++ b/sm2/sm2_keyexchange_test.go @@ -41,3 +41,53 @@ func TestKeyExchangeSample(t *testing.T) { t.Errorf("got different key") } } + +func TestKeyExchangeNoPeerPubInit(t *testing.T) { + priv1, _ := GenerateKey(rand.Reader) + priv2, _ := GenerateKey(rand.Reader) + uidA := []byte("Alice") + uidB := []byte("Bob") + + initiator, err := NewKeyExchange(priv1, nil, uidA, uidB, 32, true) + if err != nil { + t.Fatal(err) + } + responder, err := NewKeyExchange(priv2, nil, uidB, uidA, 32, true) + if err != nil { + t.Fatal(err) + } + + rA, err := initiator.InitKeyExchange(rand.Reader) + if err != nil { + t.Fatal(err) + } + + // 设置对端参数 + err = initiator.SetPeerPub(&priv2.PublicKey, uidB) + if err != nil { + t.Fatal(err) + } + err = responder.SetPeerPub(&priv1.PublicKey, uidA) + if err != nil { + t.Fatal(err) + } + + rB, s2, err := responder.RepondKeyExchange(rand.Reader, rA) + if err != nil { + t.Fatal(err) + } + + s1, err := initiator.ConfirmResponder(rB, s2) + if err != nil { + t.Fatal(err) + } + + err = responder.ConfirmInitiator(s1) + if err != nil { + t.Fatal(err) + } + + if hex.EncodeToString(initiator.key) != hex.EncodeToString(responder.key) { + t.Errorf("got different key") + } +} From a6172852fcda9ebe1440b15c288ad3cb5aa76611 Mon Sep 17 00:00:00 2001 From: cliven Date: Sun, 14 Aug 2022 11:10:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=AF=B9=E7=AB=AF=E5=8F=82=E6=95=B0=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BA=86=E9=98=B2=E6=AD=A2=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E8=AE=BE=E7=BD=AE=E7=9A=84=E6=A3=80=E6=9F=A5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sm2/sm2_keyexchange.go | 20 ++++--- sm2/sm2_keyexchange_test.go | 102 ++++++++++++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 10 deletions(-) diff --git a/sm2/sm2_keyexchange.go b/sm2/sm2_keyexchange.go index 5975c54..7c6fe49 100644 --- a/sm2/sm2_keyexchange.go +++ b/sm2/sm2_keyexchange.go @@ -37,8 +37,8 @@ func (ke *KeyExchange) GetSharedKey() []byte { // NewKeyExchange create one new KeyExchange object // // 在部分场景中,在初始 KeyExchange 时暂时没有对端的公开信息(如公钥、UID),这些信息可能需要在后续的交换中得到。 -// 这种情况下,可设置 peerPub、peerUID 参数为 nil,并在合适的时候通过 KeyExchange.SetPeerPub 方法配置相关参数。 -// 注意 KeyExchange.SetPeerPub 方法必须要在 KeyExchange.RepondKeyExchange 或 KeyExchange.RepondKeyExchange 方法调用。 +// 这种情况下,可设置 peerPub、peerUID 参数为 nil,并在合适的时候通过 KeyExchange.SetPeerParameters 方法配置相关参数。 +// 注意 KeyExchange.SetPeerParameters 方法必须要在 KeyExchange.RepondKeyExchange 或 KeyExchange.RepondKeyExchange 方法之前调用。 func NewKeyExchange(priv *PrivateKey, peerPub *ecdsa.PublicKey, uid, peerUID []byte, keyLen int, genSignature bool) (ke *KeyExchange, err error) { ke = &KeyExchange{} ke.genSignature = genSignature @@ -60,7 +60,7 @@ func NewKeyExchange(priv *PrivateKey, peerPub *ecdsa.PublicKey, uid, peerUID []b return nil, err } - err = ke.SetPeerPub(peerPub, peerUID) + err = ke.SetPeerParameters(peerPub, peerUID) if err != nil { return nil, err } @@ -74,15 +74,21 @@ func NewKeyExchange(priv *PrivateKey, peerPub *ecdsa.PublicKey, uid, peerUID []b return } -// SetPeerPub 设置对端公开信息,该方法用于某些初期状态无法取得对端公开参数的场景。 +// SetPeerParameters 设置对端公开信息,该方法用于某些初期状态无法取得对端公开参数的场景。 // 例如:在TLCP协议中,基于SM2算法ECDHE过程。 -func (ke *KeyExchange) SetPeerPub(peerPub *ecdsa.PublicKey, peerUID []byte) error { +// +// 注意该方法仅在 NewKeyExchange 没有提供 peerPub、peerUID参数时允许被调用, +// 且该方法只能调用一次不可重复调用,若多次调用或peerPub、peerUID已经存在则会发生错误。 +func (ke *KeyExchange) SetPeerParameters(peerPub *ecdsa.PublicKey, peerUID []byte) error { if peerPub == nil { return nil } if len(peerUID) == 0 { peerUID = defaultUID } + if ke.peerPub != nil { + return errors.New("sm2: 'peerPub' already exists, please do not set it") + } var err error ke.peerPub = peerPub @@ -184,7 +190,7 @@ func respondKeyExchange(ke *KeyExchange, r *big.Int, rA *ecdsa.PublicKey) (*ecds // RepondKeyExchange when responder receive rA, for responder's step B1-B8 func (ke *KeyExchange) RepondKeyExchange(rand io.Reader, rA *ecdsa.PublicKey) (*ecdsa.PublicKey, []byte, error) { if ke.peerPub == nil { - return nil, nil, errors.New("sm2: peer public not set, you probable need call KeyExchange.SetPeerPub") + return nil, nil, errors.New("sm2: peer public not set, you probable need call KeyExchange.SetPeerParameters") } if !ke.privateKey.IsOnCurve(rA.X, rA.Y) { return nil, nil, errors.New("sm2: received invalid random from initiator") @@ -200,7 +206,7 @@ func (ke *KeyExchange) RepondKeyExchange(rand io.Reader, rA *ecdsa.PublicKey) (* // ConfirmResponder for initiator's step A4-A10 func (ke *KeyExchange) ConfirmResponder(rB *ecdsa.PublicKey, sB []byte) ([]byte, error) { if ke.peerPub == nil { - return nil, errors.New("sm2: peer public not set, you probable need call KeyExchange.SetPeerPub") + return nil, errors.New("sm2: peer public not set, you probable need call KeyExchange.SetPeerParameters") } if !ke.privateKey.IsOnCurve(rB.X, rB.Y) { return nil, errors.New("sm2: received invalid random from responder") diff --git a/sm2/sm2_keyexchange_test.go b/sm2/sm2_keyexchange_test.go index f4f4cf7..99e232d 100644 --- a/sm2/sm2_keyexchange_test.go +++ b/sm2/sm2_keyexchange_test.go @@ -3,6 +3,7 @@ package sm2 import ( "crypto/rand" "encoding/hex" + "errors" "testing" ) @@ -42,7 +43,7 @@ func TestKeyExchangeSample(t *testing.T) { } } -func TestKeyExchangeNoPeerPubInit(t *testing.T) { +func TestSetPeerParameters(t *testing.T) { priv1, _ := GenerateKey(rand.Reader) priv2, _ := GenerateKey(rand.Reader) uidA := []byte("Alice") @@ -63,11 +64,11 @@ func TestKeyExchangeNoPeerPubInit(t *testing.T) { } // 设置对端参数 - err = initiator.SetPeerPub(&priv2.PublicKey, uidB) + err = initiator.SetPeerParameters(&priv2.PublicKey, uidB) if err != nil { t.Fatal(err) } - err = responder.SetPeerPub(&priv1.PublicKey, uidA) + err = responder.SetPeerParameters(&priv1.PublicKey, uidA) if err != nil { t.Fatal(err) } @@ -91,3 +92,98 @@ func TestKeyExchangeNoPeerPubInit(t *testing.T) { t.Errorf("got different key") } } + +func TestKeyExchange_SetPeerParameters(t *testing.T) { + priv1, _ := GenerateKey(rand.Reader) + priv2, _ := GenerateKey(rand.Reader) + uidA := []byte("Alice") + uidB := []byte("Bob") + + initiator, err := NewKeyExchange(priv1, nil, uidA, nil, 32, true) + if err != nil { + t.Fatal(err) + } + responder, err := NewKeyExchange(priv2, nil, uidB, nil, 32, true) + if err != nil { + t.Fatal(err) + } + + rA, err := initiator.InitKeyExchange(rand.Reader) + if err != nil { + t.Fatal(err) + } + + // 设置对端参数 + err = initiator.SetPeerParameters(&priv2.PublicKey, uidB) + if err != nil { + t.Fatal(err) + } + err = responder.SetPeerParameters(&priv1.PublicKey, uidA) + if err != nil { + t.Fatal(err) + } + + rB, s2, err := responder.RepondKeyExchange(rand.Reader, rA) + if err != nil { + t.Fatal(err) + } + + s1, err := initiator.ConfirmResponder(rB, s2) + if err != nil { + t.Fatal(err) + } + + err = responder.ConfirmInitiator(s1) + if err != nil { + t.Fatal(err) + } + + if hex.EncodeToString(initiator.key) != hex.EncodeToString(responder.key) { + t.Errorf("got different key") + } +} + +func TestKeyExchange_SetPeerParameters_ErrCase(t *testing.T) { + priv1, _ := GenerateKey(rand.Reader) + priv2, _ := GenerateKey(rand.Reader) + uidA := []byte("Alice") + uidB := []byte("Bob") + + initiator, err := NewKeyExchange(priv1, nil, uidA, nil, 32, true) + if err != nil { + t.Fatal(err) + } + responder, err := NewKeyExchange(priv2, &priv1.PublicKey, uidB, uidA, 32, true) + if err != nil { + t.Fatal(err) + } + + rA, err := initiator.InitKeyExchange(rand.Reader) + if err != nil { + t.Fatal(err) + } + rB, s2, err := responder.RepondKeyExchange(rand.Reader, rA) + if err != nil { + t.Fatal(err) + } + + _, err = initiator.ConfirmResponder(rB, s2) + if err == nil { + t.Fatal(errors.New("expect call ConfirmResponder got a error, but not")) + } + + err = initiator.SetPeerParameters(&priv2.PublicKey, uidB) + if err != nil { + t.Fatal(err) + } + + err = initiator.SetPeerParameters(&priv2.PublicKey, uidB) + if err == nil { + t.Fatal(errors.New("expect call SetPeerParameters repeat got a error, but not")) + } + + err = responder.SetPeerParameters(&priv1.PublicKey, uidA) + if err == nil { + t.Fatal(errors.New("expect responder call SetPeerParameters got a error, but not")) + } +}