init: v1.0.0

This commit is contained in:
yaole
2026-05-27 23:03:00 +08:00
commit 8d97f750eb
466 changed files with 80067 additions and 0 deletions
+192
View File
@@ -0,0 +1,192 @@
package sm2a
import (
"bytes"
"encoding/hex"
"testing"
"xdx.jelly/xgcl/sm/sm2"
)
var signStdData = struct {
e string
clientKey string
serverKey string
publicKey string
sig string
k1 string
k2 string
}{
"5D1D20948D88FC76CF1AB994AFA484AF1603A61920D860635EABAA9D518848B0",
"5F16B93817200830863BB55A523E131563C639880DA8D5F663C9CA32E872C621",
"5749BD354348F66F9905254E784C97BDE700DB7968219829F2DB5EC80D0AB0DF",
"C31ED61795626AA8D8D26BB17359160F3CCB63786D50DF2C350C9DD27539DBDA5C2D7FAE4D9360CC77F9C0F7E66DB80ED35CF9969E68A0496BE1120020A4396A",
"B5E3ADF54DFAAB5E383BD01B1CDFBCAB93EFBA28D38BD0596ADE4BF2E14315E8EF337B8EAA38A82D2674ED1E6481B49DDDB556086938120C0C0EBE146C3CBEE8",
"29D9F41BBE10C7A06064AFE3ADB5E23393FBA3981ADDBF146F87C7F7CA8645C6",
"29D9F41BBE10C7A06064AFE3ADB5E23393FBA3981ADDBF146F87C7F7CA8645C6",
}
func SignStdTest() bool {
dc := sm2.NewPrivateKey()
dc.SetString(signStdData.clientKey, 16)
ds := sm2.NewPrivateKey()
ds.SetString(signStdData.serverKey, 16)
// 协同计算签名
e, _ := hex.DecodeString(signStdData.e)
buf, _ := hex.DecodeString(signStdData.publicKey)
pk := sm2.NewPublicKey()
pk.SetBytes(buf)
k1, _ := hex.DecodeString(signStdData.k1)
clientSign := NewClientSignContext(pk, bytes.NewReader(k1))
buf, _ = clientSign.Initial(e)
k2, _ := hex.DecodeString(signStdData.k2)
buf, _ = ServerSign(ds, pk, buf, bytes.NewReader(k2))
sig, _ := clientSign.Final(dc, buf)
buf, _ = hex.DecodeString(signStdData.sig)
if !sm2.Verify(e, pk, sig) || bytes.Compare(sig.Bytes(), buf) != 0 {
return false
}
return true
}
var decStdData = struct {
msg string
clientKey string
serverKey string
publicKey string
cipher string
}{
"1234567812345678",
"A70B23314F7B21CD1C7A40F3FC7CA2DB2AC0481FD7E20F392805B6B96A0411F5",
"D2DF651440C395E7B017CA5AA4B4DC5D9F482DAAEE86C973B57BA33D981E35A5",
"CE33FE6C69EFE72ADC8135150F6F934D5CAB106B012CE8A63B4C97CFBB85E39086B1EA8943E79C46CBC5080D003255FF9A5E72D057563DA3417C5CB543B38D36",
"1BE948BCF838D742398251CBDA2CD695F65CF001E8558BA8C9D6FAD716BA98572464D07B3C72FDAE759871E5E2582BD74913A7CEB68C3D8F96A60B97857B539525A814397F94CAC93A45D4F53E7B5AE49B2515E0921A2D6856A817D40B5303173481981EC6E3C17F90CBD296B54AD092",
}
func decTest() bool {
dc := sm2.NewPrivateKey()
dc.SetString(decStdData.clientKey, 16)
ds := sm2.NewPrivateKey()
ds.SetString(decStdData.serverKey, 16)
buf, _ := hex.DecodeString(decStdData.publicKey)
pk := sm2.NewPublicKey()
pk.SetBytes(buf)
cipher := sm2.NewCipher()
buf, _ = hex.DecodeString(decStdData.cipher)
cipher.SetBytes(buf)
clientDecCtx := NewClientDecContext()
buf, _ = clientDecCtx.Decrypt_one(cipher)
buf, _ = ServerDecrypt(buf, ds)
plain, _ := clientDecCtx.Decrypt_two(buf, dc)
if string(plain) != decStdData.msg {
return false
}
return true
}
var ExchangeStdData = struct {
sponsorClientKey string
sponsorServerKey string
sponsorPubKey string
responsorClientKey string
responsorServerKey string
responsorPubKey string
k1 string
k2 string
key string
}{
"F51259452B6AA06628413B6B3AD1BDB73999385D649BEA82D294BA24187B9EDE",
"1027D73961109348B3178E4F037ADEA699076768ED18BF4727DA37C79A1AAF95",
"3F371664CF68ABB6DCF99D5286E4DA85848A2D2B1C7A5EF40EAD8890028101229EA360323FD4E591D6920D423CB4AC9AC4977F95830149B34A11F0FE8AB4924C",
"4BAC27CF1C30006AF721879A4C8C68D8EF75B11FC485C1431BB2EA0D61131B36",
"25A87003DDC27BCF631EF747035B75466157D4AF0C9ACDA1FE523963FFA39757",
"A5768900DD340CE2689A19A99791226F6DD696D627BF86620B5D682AE0FB0729B0C2728839E2CE5724E84F805D12E5F3B42C8BFAF312B8210063AEDA0B17D3EA",
"37441408A82D69DBCFC11D9B4D1E477D879B22D1CC159F1BD957CCD1CE727F47",
"165216E57689D8B400C434BA96D097CBB04ABAD4067B71D4EEAEBC645B93FBBC",
"5C4E57CD8179D829A315AB6CC7110BF5",
}
func exchangeStdTest() bool {
sponsorClientKey := sm2.NewPrivateKey()
sponsorClientKey.SetString(ExchangeStdData.sponsorClientKey, 16)
sponsorServerKey := sm2.NewPrivateKey()
sponsorServerKey.SetString(ExchangeStdData.sponsorServerKey, 16)
sponsorPublicKey := sm2.NewPublicKey()
buf, _ := hex.DecodeString(ExchangeStdData.sponsorPubKey)
sponsorPublicKey.SetBytes(buf)
responsorClientKey := sm2.NewPrivateKey()
responsorClientKey.SetString(ExchangeStdData.responsorClientKey, 16)
responsorServerKey := sm2.NewPrivateKey()
responsorServerKey.SetString(ExchangeStdData.responsorServerKey, 16)
responsorPublicKey := sm2.NewPublicKey()
buf, _ = hex.DecodeString(ExchangeStdData.responsorPubKey)
responsorPublicKey.SetBytes(buf)
sponsor := []byte("alice")
responsor := []byte("bob")
keyLength := 16
cs := NewClientSponsor(sponsor, sponsorClientKey, sponsorPublicKey)
cr := NewClientResponsor(responsor, responsorClientKey, responsorPublicKey)
k1, _ := hex.DecodeString(ExchangeStdData.k1)
k2, _ := hex.DecodeString(ExchangeStdData.k2)
tempKeyOfSponsor, err := cs.GenerateAgreementData(k1)
if err != nil {
return false
}
toServer, err := cr.GenerateAgreementDataAndKey_1of2(sponsorPublicKey, tempKeyOfSponsor)
if err != nil {
return false
}
toClient, err := ServerKeyExchange(toServer, responsorServerKey)
if err != nil {
return false
}
keyOfResponsor, tempKeyOfResponsor, err := cr.GenerateAgreementDataAndKey_2of2(keyLength, sponsor, sponsorPublicKey, toClient, k2)
if err != nil {
return false
}
toServer, err = cs.GenerateKey_1of2(responsorPublicKey, tempKeyOfResponsor)
if err != nil {
return false
}
toClient, err = ServerKeyExchange(toServer, sponsorServerKey)
if err != nil {
return false
}
keyOfSponsor, err := cs.GenerateKey_2of2(keyLength, responsor, toClient, responsorPublicKey, sponsorClientKey)
if err != nil {
return false
}
key, _ := hex.DecodeString(ExchangeStdData.key)
if bytes.Compare(keyOfResponsor, keyOfSponsor) != 0 || bytes.Compare(keyOfResponsor, key) != 0 {
return false
}
return true
}
func TestSignStd(t *testing.T) {
if !SignStdTest() {
t.Fatal("测试失败")
}
if !exchangeStdTest() {
t.Fatal("测试失败")
}
if !decTest() {
t.Fatal("测试失败")
}
}
+6
View File
@@ -0,0 +1,6 @@
# 协同生成签名密钥
- Client
-
# 协同生成加密密钥
+42
View File
@@ -0,0 +1,42 @@
package sm2a
import (
"math/big"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/sm/sm2"
)
type ClientDecContext struct {
cipher *sm2.Cipher
}
func NewClientDecContext() *ClientDecContext {
return &ClientDecContext{
cipher: sm2.NewCipher(),
}
}
// Decrypt_one 客户端协同解密第一步,输出cipher.C1并发送给服务端
func (c *ClientDecContext) Decrypt_one(cipher *sm2.Cipher) ([]byte, error) {
out := make([]byte, 0, 2*sm2.ByteSize())
cipher.X.Mod(cipher.X, sm2.OrderN())
cipher.Y.Mod(cipher.Y, sm2.OrderN())
out = append(out, gmath.BigIntToNByte(cipher.X, sm2.ByteSize())...)
out = append(out, gmath.BigIntToNByte(cipher.Y, sm2.ByteSize())...)
c.cipher.Set(cipher)
return out, nil
}
// Decrypt_two 客户端协同解密第二步,收到服务端计算结果 [d_s]·C1,计算解密明文。
func (c *ClientDecContext) Decrypt_two(in []byte, dc *sm2.PrivateKey) ([]byte, error) {
x := new(big.Int)
y := new(big.Int)
x.SetBytes(in[:sm2.ByteSize()])
y.SetBytes(in[sm2.ByteSize() : 2*sm2.ByteSize()])
xx, yy := sm2.Curve256.ScalarMult(c.cipher.X, c.cipher.Y, dc.D.Bytes())
x, y = sm2.Curve256.Add(x, y, xx, yy)
// (x,y) = d*C1, 后续解密同标准SM2解密一致
return sm2.Decrypt_aux(x, y, c.cipher)
}
+26
View File
@@ -0,0 +1,26 @@
package sm2a
import (
"math/big"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/sm/sm2"
)
// ServerDecrypt 服务端协同计算,返回[d_s]·C1给客户端
func ServerDecrypt(in []byte, ds *sm2.PrivateKey) ([]byte, error) {
x := new(big.Int)
y := new(big.Int)
x.SetBytes(in[:sm2.ByteSize()])
y.SetBytes(in[sm2.ByteSize() : 2*sm2.ByteSize()])
x, y = sm2.Curve256.ScalarMult(x, y, ds.D.Bytes())
out := make([]byte, 0, 2*sm2.ByteSize())
out = append(out, gmath.BigIntToNByte(x, sm2.ByteSize())...)
out = append(out, gmath.BigIntToNByte(y, sm2.ByteSize())...)
// 中间变量内存置0
gmath.ClearBigInt(x)
gmath.ClearBigInt(y)
return out, nil
}
+33
View File
@@ -0,0 +1,33 @@
package sm2a
import (
"testing"
"xdx.jelly/xgcl/grand"
"xdx.jelly/xgcl/sm/sm2"
)
func TestDec(t *testing.T) {
clientKeyCtx := NewClientEncKeyGenContext()
buf, _ := clientKeyCtx.ClientKeyGen_one(grand.Reader)
ds, pk, buf, _ := ServerEncKeyGen(buf, grand.Reader)
clientKeyCtx.ClientKeyGen_two(buf)
dc := clientKeyCtx.ClientKey
printLog("客户端私钥分量", clientKeyCtx.ClientKey.Bytes())
printLog("客户端公", clientKeyCtx.Pubkey.Bytes())
printLog("服务端公", pk.Bytes())
printLog("服务端私钥分", ds.Bytes())
msg := []byte("1234567812345678")
k := grand.GetRandom(sm2.ByteSize())
cipher, _ := sm2.Encrypt(pk, msg, k)
printLog("密文", cipher.Bytes())
clientDecCtx := NewClientDecContext()
buf, _ = clientDecCtx.Decrypt_one(cipher)
printLog("C1=", buf)
buf, _ = ServerDecrypt(buf, ds)
printLog("[ds]C1=", buf)
plain, _ := clientDecCtx.Decrypt_two(buf, dc)
printLog("协同解密原文", plain)
}
+48
View File
@@ -0,0 +1,48 @@
package sm2a
import (
"fmt"
"io"
"math/big"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/sm/sm2"
)
type ClientEncKeyGenContext struct {
ClientKey *sm2.PrivateKey
Pubkey *sm2.PublicKey
}
func NewClientEncKeyGenContext() *ClientEncKeyGenContext {
return &ClientEncKeyGenContext{
ClientKey: sm2.NewPrivateKey(),
Pubkey: sm2.NewPublicKey(),
}
}
// ClientKeyGen_one 客户端加密密钥生成第一步
func (c *ClientEncKeyGenContext) ClientKeyGen_one(rand io.Reader) ([]byte, error) {
buf := make([]byte, sm2.ByteSize())
if n, err := rand.Read(buf); n != len(buf) || err != nil {
return nil, fmt.Errorf("Generate random number error")
}
c.ClientKey.SetBytes(buf)
c.ClientKey.D.Mod(c.ClientKey.D, sm2.OrderN())
c.Pubkey = sm2.GenPublicKey(c.ClientKey)
out := make([]byte, 0, 2*sm2.ByteSize())
out = append(out, gmath.BigIntToNByte(c.Pubkey.X, sm2.ByteSize())...)
out = append(out, gmath.BigIntToNByte(c.Pubkey.Y, sm2.ByteSize())...)
return out, nil
}
// ClientKeyGen_one 客户端加密密钥生成第二步
func (c *ClientEncKeyGenContext) ClientKeyGen_two(in []byte) error {
x := new(big.Int)
y := new(big.Int)
x.SetBytes(in[:sm2.ByteSize()])
y.SetBytes(in[sm2.ByteSize() : 2*sm2.ByteSize()])
c.Pubkey.X, c.Pubkey.Y = sm2.Curve().Add(c.Pubkey.X, c.Pubkey.Y, x, y)
return nil
}
+32
View File
@@ -0,0 +1,32 @@
package sm2a
import (
"fmt"
"io"
"math/big"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/sm/sm2"
)
// ServerEncKeyGen 服务端加密密钥生成
func ServerEncKeyGen(in []byte, rand io.Reader) (*sm2.PrivateKey, *sm2.PublicKey, []byte, error) {
buf := make([]byte, sm2.ByteSize())
if n, err := rand.Read(buf); n != len(buf) || err != nil {
return nil, nil, nil, fmt.Errorf("Generate random number error")
}
ds, _ := sm2.GenPrivateKey(buf)
pk := sm2.GenPublicKey(ds)
out := make([]byte, 0, 2*sm2.ByteSize())
out = append(out, gmath.BigIntToNByte(pk.X, sm2.ByteSize())...)
out = append(out, gmath.BigIntToNByte(pk.Y, sm2.ByteSize())...)
x := new(big.Int)
y := new(big.Int)
x.SetBytes(in[:sm2.ByteSize()])
y.SetBytes(in[sm2.ByteSize() : 2*sm2.ByteSize()])
pk.X, pk.Y = sm2.Curve().Add(pk.X, pk.Y, x, y)
return ds, pk, out, nil
}
+292
View File
@@ -0,0 +1,292 @@
package sm2a
import (
"fmt"
"math/rand"
"sync"
"testing"
"time"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/grand"
"xdx.jelly/xgcl/sm/sm2"
)
func TestGenSignKeyRaw(t *testing.T) {
serverKeyCtx := NewServerSignKeyGenContext()
clientKeyCtx := NewClientSignKeyGenContext(grand.Reader)
buf, err := serverKeyCtx.ServerGenKey_one(grand.Reader)
if err != nil {
fmt.Println(err)
return
}
serverctx, err := serverKeyCtx.MarshalBinary()
if err != nil {
t.Log(err)
t.Fail()
return
}
buf, err = clientKeyCtx.ClientKeyGen_one(buf)
if err != nil {
fmt.Println(err)
return
}
_ = serverKeyCtx.UnmarshalBinary(serverctx)
buf, err = serverKeyCtx.ServerGenKey_two(buf, grand.Reader)
if err != nil {
fmt.Println(err)
return
}
serverctx, _ = serverKeyCtx.MarshalBinary()
buf, err = clientKeyCtx.ClientKeyGen_two(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Client public Key = ", clientKeyCtx.PubKey)
_ = serverKeyCtx.UnmarshalBinary(serverctx)
err = serverKeyCtx.ServerGenKey_three(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Server public Key = ", serverKeyCtx.PubKey)
dc, _ := clientKeyCtx.GetClientKey()
ds, _ := serverKeyCtx.GetServerKey()
pkc, _ := clientKeyCtx.GetPublicKey()
pks, _ := serverKeyCtx.GetPublicKey()
if !pkc.Equals(pks) {
t.Fatal("Public keys of server and client are not equal")
}
// check (1+d)^{-1} = dc+ds
d := sm2.NewPrivateKey()
d.D.Add(dc.D, ds.D)
d.D.ModInverse(d.D, sm2.OrderN())
d.D.Sub(d.D, gmath.BigInt1)
px, py := sm2.Curve256.ScalarBaseMult(d.Bytes())
fmt.Println("[d]*G=(", px.Text(16)+", "+py.Text(16)+")")
if px.Cmp(pkc.X) != 0 || py.Cmp(pkc.Y) != 0 {
t.Fatal("Private keys of server and client are not march")
}
d.D.Mul(clientKeyCtx.ClientSubKey.D, serverKeyCtx.ServerSubKey.D)
d.D.Sub(d.D, gmath.BigInt1)
d.D.Mod(d.D, sm2.OrderN())
px, py = sm2.Curve256.ScalarBaseMult(d.Bytes())
fmt.Println("[d]*G=(", px.Text(16)+", "+py.Text(16)+")")
if px.Cmp(pkc.X) != 0 || py.Cmp(pkc.Y) != 0 {
t.Fatal("Private keys of server and client are not march")
}
}
func TestGenSignKeyTps(t *testing.T) {
rand1 := rand.New(rand.NewSource(time.Now().UnixNano()))
wg := sync.WaitGroup{}
totalSuccess := 0
totalFailed := 0
var (
fail float32
total float32
)
beg := time.Now().UnixNano()
for j := 0; j < 10; j++ {
wg.Add(1)
wtt := time.Duration(rand1.Int() % 100)
time.Sleep(wtt * time.Millisecond)
go func(wg *sync.WaitGroup, t *testing.T) {
defer wg.Done()
for mi := 0; mi < 20; mi++ {
serverKeySeg := NewServerSignKeyGenContext()
clientKeySeg := NewClientSignKeyGenContext(grand.Reader)
buf, err := serverKeySeg.ServerGenKey_one(grand.Reader)
if err != nil {
fmt.Println(err)
return
}
serverctx, _ := serverKeySeg.MarshalBinary()
fmt.Printf("%d\n", len(serverctx))
//rand.Reader will got error? why
buf, err = clientKeySeg.ClientKeyGen_one(buf)
if err != nil {
fmt.Println(err)
return
}
_ = serverKeySeg.UnmarshalBinary(serverctx)
buf, err = serverKeySeg.ServerGenKey_two(buf, grand.Reader)
if err != nil {
fmt.Println(err)
return
}
serverctx, _ = serverKeySeg.MarshalBinary()
fmt.Printf("%X\n", serverctx)
buf, err = clientKeySeg.ClientKeyGen_two(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Client public Key = ", clientKeySeg.PubKey)
_ = serverKeySeg.UnmarshalBinary(serverctx)
err = serverKeySeg.ServerGenKey_three(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Server public Key = ", serverKeySeg.PubKey)
dc, _ := clientKeySeg.GetClientKey()
ds, _ := serverKeySeg.GetServerKey()
pkc, _ := clientKeySeg.GetPublicKey()
pks, _ := serverKeySeg.GetPublicKey()
if !pkc.Equals(pks) {
t.Fatal("Public keys of server and client are not equal")
}
// check (1+d)^{-1} = dc+ds
d := sm2.NewPrivateKey()
d.D.Add(dc.D, ds.D)
d.D.ModInverse(d.D, sm2.OrderN())
d.D.Sub(d.D, gmath.BigInt1)
px, py := sm2.Curve256.ScalarBaseMult(d.Bytes())
fmt.Println("p=(", px.Text(16)+", "+py.Text(16)+")")
if px.Cmp(pkc.X) != 0 || py.Cmp(pkc.Y) != 0 {
t.Fatal("Private keys of server and client are not march")
}
totalSuccess += 1
}
}(&wg, t)
}
wg.Wait()
end := time.Now().UnixNano()
fail = float32(totalFailed)
total = float32(totalSuccess + totalFailed)
rate := fail / total
elapseDenom := float32(end - beg)
elapse := elapseDenom / 1000000000
one := elapse / total
fmt.Printf("错误率: %f %% \n", rate*100)
fmt.Printf("耗时时间(单位:秒): %f seconds \n", elapse)
fmt.Printf("单次请求平均耗时(单位:秒) %f seconds \n", one)
fmt.Println("TPS: ", total/elapse)
fmt.Println("")
fmt.Println("")
fmt.Println("")
}
func TestEncKeyGen(t *testing.T) {
clientKeyCtx := NewClientEncKeyGenContext()
buf, _ := clientKeyCtx.ClientKeyGen_one(grand.Reader)
printLog("客户端第一步输出: ", buf)
ds, pk, buf, _ := ServerEncKeyGen(buf, grand.Reader)
printLog("服务端第一步输出: ", buf)
clientKeyCtx.ClientKeyGen_two(buf)
dc := clientKeyCtx.ClientKey
printLog("SM2加密密钥生成, 服务端生成公钥", pk.Bytes())
printLog("SM2加密密钥生成, 客户端生成公钥", clientKeyCtx.Pubkey.Bytes())
printLog("SM2加密密钥生成, 服务端私钥分量", ds.Bytes())
printLog("SM2加密密钥生成, 客户端私钥分量", dc.Bytes())
// 验证 [ds+dc]·G = pk
d := sm2.NewPrivateKey()
d.D.Add(dc.D, ds.D)
d.D.Mod(d.D, sm2.OrderN())
px, py := sm2.Curve256.ScalarBaseMult(d.Bytes())
if px.Cmp(pk.X) != 0 || py.Cmp(pk.Y) != 0 {
t.Fatal("Private keys of server and client are not march")
}
}
func TestGenSignKey(t *testing.T) {
serverKeyCtx := NewServerSignKeyGenContext()
clientKeyCtx := NewClientSignKeyGenContext(grand.Reader)
buf, err := serverKeyCtx.ServerGenKey_one(grand.Reader)
if err != nil {
fmt.Println(err)
return
}
printLog("服务端第一步输出: ", buf)
buf, err = clientKeyCtx.ClientKeyGen_one(buf)
if err != nil {
fmt.Println(err)
return
}
printLog("客户端第一步输出: ", buf)
buf, err = serverKeyCtx.ServerGenKey_two(buf, grand.Reader)
if err != nil {
fmt.Println(err)
return
}
printLog("服务端第二步输出: ", buf)
buf, err = clientKeyCtx.ClientKeyGen_two(buf)
if err != nil {
fmt.Println(err)
return
}
printLog("客户端第二步输出: ", buf)
err = serverKeyCtx.ServerGenKey_three(buf)
if err != nil {
fmt.Println(err)
return
}
dc, _ := clientKeyCtx.GetClientKey()
ds, _ := serverKeyCtx.GetServerKey()
pkc, _ := clientKeyCtx.GetPublicKey()
pks, _ := serverKeyCtx.GetPublicKey()
if !pkc.Equals(pks) {
t.Fatal("错误:客户端和服务端生成的公钥不相等")
}
fmt.Printf("客户端生成密钥分量 = %X\n", dc.Bytes())
fmt.Printf("服务端生成密钥分量 = %X\n", ds.Bytes())
fmt.Printf("客户端生成公钥 = %X\n", clientKeyCtx.PubKey.Bytes())
fmt.Printf("服务端生成公钥 = %X\n", serverKeyCtx.PubKey.Bytes())
// 验证密钥配对 (1+d)^{-1} = dc+ds
d := sm2.NewPrivateKey()
d.D.Add(dc.D, ds.D)
d.D.ModInverse(d.D, sm2.OrderN())
d.D.Sub(d.D, gmath.BigInt1)
px, py := sm2.Curve256.ScalarBaseMult(d.Bytes())
if px.Cmp(pkc.X) != 0 || py.Cmp(pkc.Y) != 0 {
t.Fatal("错误:客户端和服务端密钥不配对")
}
}
+171
View File
@@ -0,0 +1,171 @@
package sm2a
import (
"crypto/rand"
"fmt"
"io"
"math/big"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/grand/drng"
"xdx.jelly/xgcl/sm/sm2"
)
type ClientSignKeyGenContext struct {
X [SecureParam]*big.Int
Y [SecureParam]*big.Int
K [SecureParam]byte
ClientKey *sm2.PrivateKey // 客户端私钥,与sm2私钥是同样的形式
PubKey *sm2.PublicKey // 公钥
ClientSubKey *sm2.PrivateKey
Rand io.Reader
isClientKeyReady bool
}
func NewClientSignKeyGenContext(r io.Reader) *ClientSignKeyGenContext {
c := new(ClientSignKeyGenContext)
for i := 0; i < SecureParam; i++ {
c.X[i] = new(big.Int)
c.Y[i] = new(big.Int)
}
c.ClientKey = sm2.NewPrivateKey()
c.ClientSubKey = sm2.NewPrivateKey()
c.PubKey = sm2.NewPublicKey()
c.Rand = r
return c
}
func (c *ClientSignKeyGenContext) GetClientKey() (*sm2.PrivateKey, error) {
if c.isClientKeyReady {
return c.ClientKey, nil
}
return nil, fmt.Errorf("Key agreement not finished")
}
func (c *ClientSignKeyGenContext) GetClientSubKey() (*sm2.PrivateKey, error) {
if c.isClientKeyReady {
return c.ClientSubKey, nil
}
return nil, fmt.Errorf("Key agreement not finished")
}
func (c *ClientSignKeyGenContext) GetPublicKey() (*sm2.PublicKey, error) {
if c.isClientKeyReady {
return c.PubKey, nil
}
return nil, fmt.Errorf("Key agreement not finished")
}
// ClientKeyGen_one 客户端第一步,根据服务端第一步计算数据计算客户端的中间数据
// in = X_1||Y_1||X_2||Y_2||....||X_128||Y_128
// out = (h_i0, h_i1, F_ix, F_iy), ... , for i = 1...128
func (c *ClientSignKeyGenContext) ClientKeyGen_one(in []byte) (out []byte, err error) {
c.isClientKeyReady = false
buf := make([]byte, ((SecureParam + 7) >> 3))
if n, err := c.Rand.Read(buf); n != len(buf) || err != nil {
return nil, fmt.Errorf("Generate random number error")
}
for i := 0; i < SecureParam; i++ {
c.K[i] = (buf[i>>3] >> uint(i&7)) & 1
}
var fx, fy *big.Int
xx := new(big.Int)
xy := new(big.Int)
r := new(big.Int)
a := new(big.Int)
buf = make([]byte, sm2.ByteSize())
out = make([]byte, sm2.ByteSize()*4*SecureParam)
outPos := 0
// 使用基于SM3的随机比特生成器生成客户端签名密钥分量
c.ClientKey.D, err = rand.Int(drng.SM3Rng, sm2.OrderN())
if err != nil {
return nil, err
}
var n int
var i int
d := c.ClientKey.Get()
d.SetInt64(0)
// 计算给协同签名服务端的的中间数据
for i = 0; i < SecureParam; i++ {
if n, err = c.Rand.Read(buf); n != len(buf) || err != nil {
fmt.Printf("%d,%d, %x\n", i, n, len(buf))
return nil, fmt.Errorf("Generate random number error1")
}
r.SetBytes(buf)
if n, err = c.Rand.Read(buf); n != len(buf) || err != nil {
fmt.Printf("%d,%d, %x, %v\n", i, n, buf, err)
return nil, fmt.Errorf("Generate random number error2")
}
a.SetBytes(buf)
d.Add(d, a)
if n, err = c.Rand.Read(buf); n != len(buf) || err != nil {
fmt.Printf("%d,%d, %x, %v\n", i, n, buf, err)
return nil, fmt.Errorf("Generate random number error3")
}
fx, fy = sm2.Curve256.ScalarBaseMult(buf)
xbuf := in[2*i*sm2.ByteSize():]
xx.SetBytes(xbuf[:sm2.ByteSize()])
xy.SetBytes(xbuf[sm2.ByteSize() : 2*sm2.ByteSize()])
c.X[i], c.Y[i] = sm2.Curve256.ScalarMult(xx, xy, buf)
if c.K[i] == 1 {
fx, fy = sm2.Curve().Add(fx, fy, xx, xy)
outPos += marshal(out[outPos:], r, a, fx, fy, sm2.ByteSize())
} else {
outPos += marshal(out[outPos:], a, r, fx, fy, sm2.ByteSize())
}
}
return out, nil
}
// ClientKeyGen_two 客户端第二步,根据服务端第二步计算数据计算公钥和中间数据并发送给服务端
// in = (C_i0, C_i1), ...., for i = 1..128, 服务端计算的中间数据
// out = [u^{-1}]·G, 给服务端的中间数据
func (c *ClientSignKeyGenContext) ClientKeyGen_two(in []byte) (out []byte, err error) {
out = make([]byte, 2*sm2.ByteSize())
u := new(big.Int)
tmp1 := new(big.Int)
tmp2 := new(big.Int)
u.SetInt64(0)
for i := 0; i < SecureParam; i++ {
pos := (i*2 + int(c.K[i])) * sm2.ByteSize()
tmp1.SetBytes(in[pos : pos+sm2.ByteSize()])
pointToInt(tmp2, c.X[i], c.Y[i])
tmp1.Sub(tmp1, tmp2)
u.Add(u, tmp1)
u.Mod(u, sm2.Curve().Params().N)
}
u.ModInverse(u, sm2.Curve().Params().N)
tmp1, tmp2 = sm2.Curve256.ScalarBaseMult(u.Bytes())
copy(out, gmath.BigIntToNByte(tmp1, sm2.ByteSize()))
copy(out[sm2.ByteSize():], gmath.BigIntToNByte(tmp2, sm2.ByteSize()))
tmp1.SetBytes(in[2*SecureParam*sm2.ByteSize() : (2*SecureParam+1)*sm2.ByteSize()])
tmp2.SetBytes(in[(2*SecureParam+1)*sm2.ByteSize() : (2*SecureParam+2)*sm2.ByteSize()])
x, y := sm2.Curve256.ScalarMult(tmp1, tmp2, u.Bytes())
y.Sub(sm2.Curve().Params().P, y)
// 客户端计算公钥
x, y = sm2.Curve().Add(x, y, sm2.Curve().Params().Gx, sm2.Curve().Params().Gy)
y.Sub(sm2.Curve().Params().P, y)
c.PubKey.X.Set(x)
c.PubKey.Y.Set(y)
c.ClientSubKey.SetBigInt(u)
c.isClientKeyReady = true
return out, nil
}
func marshal(out []byte, a, b, c, d *big.Int, n int) int {
outPos := 0
outPos += copy(out[outPos:], gmath.BigIntToNByte(a, n))
outPos += copy(out[outPos:], gmath.BigIntToNByte(b, n))
outPos += copy(out[outPos:], gmath.BigIntToNByte(c, n))
outPos += copy(out[outPos:], gmath.BigIntToNByte(d, n))
return outPos
}
+266
View File
@@ -0,0 +1,266 @@
package sm2a
import (
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"math/big"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/grand/drng"
"xdx.jelly/xgcl/sm/sm2"
)
type ServerSignKeyGenContext struct {
B [SecureParam]*big.Int
X [SecureParam]*big.Int
ServerKey *sm2.PrivateKey
PubKey *sm2.PublicKey
ServerSubKey *sm2.PrivateKey // ServerSubKey * ClientSubKey = 1+d
isServerKeyReady bool
}
func NewServerSignKeyGenContext() *ServerSignKeyGenContext {
s := new(ServerSignKeyGenContext)
for i := 0; i < SecureParam; i++ {
s.B[i] = new(big.Int)
s.X[i] = new(big.Int)
}
s.ServerKey = sm2.NewPrivateKey()
s.PubKey = sm2.NewPublicKey()
s.ServerSubKey = sm2.NewPrivateKey()
return s
}
func (s *ServerSignKeyGenContext) GetServerKey() (*sm2.PrivateKey, error) {
if s.isServerKeyReady {
return s.ServerKey, nil
}
return nil, fmt.Errorf("Key agreement not finished")
}
func (s *ServerSignKeyGenContext) GetServerSubKey() (*sm2.PrivateKey, error) {
if s.isServerKeyReady {
return s.ServerSubKey, nil
}
return nil, fmt.Errorf("Key agreement not finished")
}
func (s *ServerSignKeyGenContext) GetPublicKey() (*sm2.PublicKey, error) {
if s.isServerKeyReady {
return s.PubKey, nil
}
return nil, fmt.Errorf("Key agreement not finished")
}
// MarshalBinary 保存ServerSignKeyGenContext
func (s *ServerSignKeyGenContext) MarshalBinary() ([]byte, error) {
out := make([]byte, 0, 8432)
size := sm2.ByteSize()
N := sm2.OrderN()
// binary.BigEndian.PutUint32(out[:4], uint32(SecureParam))
for i := 0; i < SecureParam; i++ {
s.B[i].Mod(s.B[i], N)
out = append(out, gmath.BigIntToNByte(s.B[i], size)...)
s.X[i].Mod(s.X[i], N)
out = append(out, gmath.BigIntToNByte(s.X[i], size)...)
}
// s.ServerSubKey.Mod(s.ServerSubKey, N)
// out = append(out, gmath.BigIntToNByte(s.ServerSubKey, size)...)
var buf4 [4]byte
buf, err := s.ServerSubKey.MarshalBinary()
if err != nil {
return nil, err
}
binary.BigEndian.PutUint32(buf4[:], uint32(len(buf)))
out = append(out, buf4[:]...)
out = append(out, buf...)
buf, err = s.ServerKey.MarshalBinary()
if err != nil {
return nil, err
}
binary.BigEndian.PutUint32(buf4[:], uint32(len(buf)))
out = append(out, buf4[:]...)
out = append(out, buf...)
buf, err = s.PubKey.MarshalBinary()
if err != nil {
return nil, err
}
binary.BigEndian.PutUint32(buf4[:], uint32(len(buf)))
out = append(out, buf4[:]...)
out = append(out, buf...)
if s.isServerKeyReady {
out = append(out, 1)
} else {
out = append(out, 0)
}
return out, nil
}
// UnmarshalBinary 恢复ServerSignKeyGenContext
func (s *ServerSignKeyGenContext) UnmarshalBinary(b []byte) error {
size := sm2.ByteSize()
for i := 0; i < SecureParam; i++ {
s.B[i].SetBytes(b[:size])
b = b[size:]
s.X[i].SetBytes(b[:size])
b = b[size:]
}
// s.ServerSubKey.SetBytes(b[:size])
// b = b[size:]
len := binary.BigEndian.Uint32(b)
b = b[4:]
if err := s.ServerSubKey.UnmarshalBinary(b[:len]); err != nil {
return err
}
b = b[len:]
len = binary.BigEndian.Uint32(b)
b = b[4:]
if err := s.ServerKey.UnmarshalBinary(b[:len]); err != nil {
return err
}
b = b[len:]
len = binary.BigEndian.Uint32(b)
b = b[4:]
if err := s.PubKey.UnmarshalBinary(b[:len]); err != nil {
return err
}
b = b[len:]
if b[0] != 0 {
s.isServerKeyReady = true
} else {
s.isServerKeyReady = false
}
return nil
}
// ServerGenKey_one 服务端第一步,生成协同密钥中间数据给客户端
// 计算(X_i,Y_i) = [x_i]·G
// 输出 X_1||Y_1||X_2||Y_2||....||X_128||Y_128
func (s *ServerSignKeyGenContext) ServerGenKey_one(rnd io.Reader) ([]byte, error) {
var err error
s.isServerKeyReady = false
buf := make([]byte, sm2.ByteSize())
out := make([]byte, 2*SecureParam*sm2.ByteSize())
defer func() {
for i := range buf {
buf[i] = 0 // 清除临时使用的内存数据
}
}()
// 使用基于SM3的随机比特生成器生成服务端签名密钥分量
s.ServerKey.D, err = rand.Int(drng.SM3Rng, sm2.OrderN())
if err != nil {
return nil, err
}
d := s.ServerKey.Get()
d.SetInt64(0)
// 服务端计算中间数据
for i := 0; i < SecureParam; i++ {
if n, err := rnd.Read(buf); n != len(buf) || err != nil {
return nil, fmt.Errorf("Generate random number error")
}
s.B[i].SetBytes(buf)
d.Add(d, s.B[i])
if n, err := rnd.Read(buf); n != len(buf) || err != nil {
return nil, fmt.Errorf("Generate random number error")
}
s.X[i].SetBytes(buf)
x, y := sm2.Curve256.ScalarBaseMult(buf)
copy(out[(i*2+1)*sm2.ByteSize()-len(x.Bytes()):], x.Bytes())
copy(out[(i*2+2)*sm2.ByteSize()-len(y.Bytes()):], y.Bytes())
}
d.Mod(d, sm2.OrderN())
return out, nil
}
// ServerGenKey_two 服务端第二步,收到客户端数据in,计算中间数据并返回给客户端
// 其中in = (h_i0, h_i1, F_ix, F_iy), ... , for i = 1...128
// 输出 (C_i0, C_i1), ...., for i = 1..128
func (s *ServerSignKeyGenContext) ServerGenKey_two(in []byte, rand io.Reader) ([]byte, error) {
tmp := new(big.Int)
h0 := new(big.Int)
h1 := new(big.Int)
fx := new(big.Int)
fy := new(big.Int)
out := make([]byte, 2*sm2.ByteSize()*SecureParam+2*sm2.ByteSize())
v := new(big.Int)
buf := make([]byte, sm2.ByteSize())
if n, err := rand.Read(buf); n != len(buf) || err != nil {
return nil, fmt.Errorf("Generate random number error")
}
v.SetBytes(buf)
v.Mod(v, sm2.Curve().Params().P)
pos := 0
for i := 0; i < SecureParam; i++ {
h0.SetBytes(in[pos : pos+sm2.ByteSize()])
pos += sm2.ByteSize()
h1.SetBytes(in[pos : pos+sm2.ByteSize()])
pos += sm2.ByteSize()
fx.SetBytes(in[pos : pos+sm2.ByteSize()])
pos += sm2.ByteSize()
fy.SetBytes(in[pos : pos+sm2.ByteSize()])
pos += sm2.ByteSize()
x, y := sm2.Curve256.ScalarMult(fx, fy, s.X[i].Bytes())
h0.Add(h0, s.B[i])
h0.Mul(h0, v)
pointToInt(tmp, x, y)
h0.Add(h0, tmp)
h0.Mod(h0, sm2.Curve().Params().N)
x, y = sm2.Curve256.ScalarBaseMult(s.X[i].Bytes())
y.Sub(sm2.Curve().Params().P, y)
fx, fy = sm2.Curve().Add(fx, fy, x, y)
x, y = sm2.Curve256.ScalarMult(fx, fy, s.X[i].Bytes())
h1.Add(h1, s.B[i])
h1.Mul(h1, v)
pointToInt(tmp, x, y)
h1.Add(h1, tmp)
h1.Mod(h1, sm2.Curve().Params().N)
copy(out[i*2*sm2.ByteSize():], gmath.BigIntToNByte(h0, sm2.ByteSize()))
copy(out[(i*2+1)*sm2.ByteSize():], gmath.BigIntToNByte(h1, sm2.ByteSize()))
}
fx, fy = sm2.Curve256.ScalarBaseMult(v.Bytes())
copy(out[SecureParam*2*sm2.ByteSize():], gmath.BigIntToNByte(fx, sm2.ByteSize()))
copy(out[(SecureParam*2+1)*sm2.ByteSize():], gmath.BigIntToNByte(fy, sm2.ByteSize()))
s.ServerSubKey.SetBigInt(v)
return out, nil
}
// ServerGenKey_three 服务端第三步,服务端计算公钥
// in = [u^{-1}]·G
func (s *ServerSignKeyGenContext) ServerGenKey_three(in []byte) (err error) {
x := new(big.Int)
y := new(big.Int)
x.SetBytes(in[:sm2.ByteSize()])
y.SetBytes(in[sm2.ByteSize() : 2*sm2.ByteSize()])
x, y = sm2.Curve256.ScalarMult(x, y, s.ServerSubKey.Bytes())
y.Sub(sm2.Curve().Params().P, y)
x, y = sm2.Curve().Add(x, y, sm2.Curve().Params().Gx, sm2.Curve().Params().Gy)
y.Sub(sm2.Curve().Params().P, y)
s.PubKey.X.Set(x)
s.PubKey.Y.Set(y)
s.isServerKeyReady = true
return nil
}
+836
View File
@@ -0,0 +1,836 @@
=== RUN TestGenSignKey
服务端第一步输出:
4f877abb734886b3166878c8d24b2f9cfea07a2c5cfbeba2d8fab36963aa58f6a1b15c17503ab01b
c096d85444ba4925ad9250e47ddbf88a39174e1c261aa1c1b71bb4a6c2cefe2a81bbe38bed1cbcb2
26708b75005c00e5d38c200ec624fcd5a2a5aa25498b69574cd2df644d073721290c0c86a0278cd3
98df0689abb5173344c556494df4ee3a88f45a62cf6dc5f47257791f247462244f78f551c24ea27d
583b54e38b97a2a565b4d32f1fb9ed825178c2de932bbddda096844aef061a524da7e3e2ac93ff0d
036a5fdb0eff17c58090441d8e5df43838512b869f9edb9e9fedea80158b9c87ca3703779fc55f17
355ff04d71e7e7adda1b099a6037e0ed131bb8ca6cdcf67713f0f7c0843e71f90ae8e2fffb4acabd
152b7bd325989af00ec9d9fc2633b002040fb6e163588943ce06247d52108e8aabeadb3b8352442b
198da21acd3b00f909157cc9a45f8934637133502a1055d781db1490e6aaa00b3db79a64f3312198
9970b557f6b39cc46586425324991d79a901b5a5018dc8bce9dbe869560ab2125321b8601b031171
2903cd92263380585fd3ce98f4d8a668117ad650e757965360b703457ccbc7aca70339bd2dcf9a9a
a410431d663696e1124b3b3e36541fb5d6ab1c38b9b951f32abea0c0e5807c12da0da28f6f4de803
801dd2c09241ee8c98d35512c88da496fea2fb8b84ea802aaf3aff06cd357beca8f2f45ea6f971c4
4e78280f84a94ff44e6d82c0aadd680459c9fc2a3bc59538c31deb52eb5ee4b95ecf3ab0169bc65d
8a335a3b275fc316bca24882a676dd0a4928411413ef301704f801dda52c4ed6a09b54951b560138
adfb65763e2dfa224c6f65a85c1125da006fd13896012373618804654f2def68b0df61137fb48979
41ee4fcec7c5ba4a831a5dc09a3f525b6f088b8213eda2017c45207b02e15e8b531295f3cfc131b7
3a3eebc4d63cf084462ab20940601d08c4b753c1f496df4f258ba0310487b97a6fea926b6eaaaa0e
c0499631141eed2ac3f7e04d9850ec1776ba5b15d7511c4964c32159d5f2d0533878c3e45d4b8015
3223aff38ab186e6cf0c7cbc5c3a5bc77d89f14c04f93b44cda522b848c84e92d3bd2df40ee4fa02
cfb25a153908480521dfd817874d7aef896615f992ef33fb48f40006d360eeb2e5a0fea0b18d4416
d3ed78a16fbad470b7a40c9ae46bee160891bb84a938a71ebad8554b20b62dda184fa9b77ab1477a
eca23d4dbec008af65ad26af2eae5dc1f1e59322330f16844f2f03460c697fd1f071c312788f7acc
ea5145a8f8a452be2df076cb8fc274b8d5c98921827b64c720782b6f6c7b81713ce5ca4dcb95ac11
ed287bf26b5a37af8257b815aa4b99fb6a7b470a86c19e5dc63e93557c02293ef668fbc4aceb5611
7fbcf72018bfa714574e5e1a98ae0345283a09581815a5e5fd7978ec4888f8aa0326a79262f2d7ba
cc306147fef385e221728f45b29ea72a718719330723e3bfdc7f2d661be7d6bf6982d10e18248874
723946f0eb0524f780f944fb651ac80cc2d0b92440c4bba67f6542b73c4eaa3e8c1b7e501f6553cb
465bb334f42c5d9fc987a245552c775b6846946607957813fe44b8d19cfb6878915f093db49df5ed
5974ee5b3ee696b7a95866a39ebb8d3b2f05b49854d39fbbe8629104e1be8d1966600ba786875b70
344c8242669457fb9b285b34bce53f5e2ef0cee44f0f754806a22ad57fc6e6a167b6e05f67fb8903
6dacb4ef8d0cbdc2431eb1f305a67eb7c409feaf86543b66ba84a9052809e6aee38b03107b50f7cd
b1ba845c55a2ec2213fcc51089fd1fbed660dc9af835aa2fcbfd138b67c87b2bc5b616405fa293ed
4d76b478c61b3b5265fe371c650310ecce5b07e91e756f97b715ced5bc1fa90f3656ee4fc65edc63
ae8e3ec86fb078cce006300f76d61af2ff3d824d5a0f1c5394f5e3a77ad0f66332944bbe79266c9a
025ef8f23dc55ff8c480168d3372870e4a25ea8936ccd11a7369d947f2ce7dfd1a636edda413253a
751bf56d7d513c1aaa6fe2718667b391df1ee18da953ea8f46abdf6cf86c5ba22fa35f4be32139a6
0ec8c22ddd6f788847437b9a983e438aab2d202aa0d597d178fa30c8551a69a75aa9d864c5a3ac31
2128b6a85b68e645aa6c94b1f33b43b76fd756d6e8a62661c7d350198b17e8f65525400df8dcd578
601616afc874e6add8804ee08cb0689ef0b76f89fbd805a6ac359affd2b299104a20b4866466dc60
ee3b5ffaef346cecf0e29c356bba8458f3283aaa32d9c178984473544c49d2a04a903b66d62f3e58
a96b9090eac4511e2216ef47fd3f877e7ee4cd1e34ba70d42a488bbe252621d3f6b2c2959b8b1174
09b7a32bf5d795d444c8b1f2a39700d62d7dd7058012daa4e50d14cac71b3e1322b70a78140b189a
91e040ee523af826d2405bf626f777179c53b67f22b979870f4290e66b89b3ab0ce284aaa3e85be7
d7f6c49faaa4ce62ad29444ed97a46f15be3cf9798aecd34ac2375b55ad8b42d6cc127a188f10634
f04cf82939f5288be42ded13970c42162ff00634d9fb949a5ebfe879e316b0745e2fdbf7ad33eb72
b90187b3bf6adbdcb5df03fde4ebd5eb150502ac1b42eef64563459acd1c3a8e650a1557964814ca
b952d0fdfb2a5c433f2e64ed8137449dad19f8233014e93838e95588337afd1f62d5e5c3f142d5d8
a8ecfe2e36822ba43d2bc7bf2bcfd4660f365d06f472e8debcf43d6a9dacf3f9263d0069abd36128
e7c75480975ed30b632661a7f71269b405cd5708f8f49854a7212a2d19e4f5e056337432feaefcd3
4414d4c4c4dd93dddf391568b5b6c3072bb500037d2a6f666e1a828c81096148e7b0059a2744d741
cc7ccebf555a44d7234e1723629cae3e92b18a3081e6b3372e54f5b953ced263555ef055ecdf4b2d
5bed8a9d3fbecf9562cc03466dbc9ac49afbed33ab2713c7880279b89f0219dc06649ce9537e775d
a996abe483151067201c6a25e55ef54b3d74c5e231a313b514062979a4a4f653501a3a2468eec047
0ca8fa7e00221abe0e79cd9785333a95cd5385279ce653fc670fe57c65fe88a0a5cd59c6ca9de278
c7b253d89c81f65bbe2198bfa200c5c2769454d6fb98f65482d58ac5884b98a6c5ef24290d0c51d9
7869d9bf9403af5d939f60490e3c8d06e7d461fbb0e776db8a46bb37d7b9c5c370f3ff4e568dd00e
c35cc2a8a69f43c3e45c7f9ad0521db4528b6c4038d2c76655541963f8a621b9f7a9e5ef3b576ced
acd7b8ad4209f0aa0ba4375d5e09c4a545ba081f1d58beae13f23512a3a433868f59093e6ea12833
2a0d973733f5b551c9d22d370e1b7a1cf348a8ea33ffff910d0508accddb50f683ae72bef2b93a85
fbbc59e4c17238e4f9374c207cddd0f2713acec1922e8cc585d3c03500351621ea657995a12e0390
60458b096729baf4717055258ec53aeadf4ba973a97c183fa5483307b566a5352f30213473949aca
b4f0d7550897411f08ea55e3ca1fa49563e978ae912832606d1703a1c3149232f2e584d7f579c77d
2a34c07b8b7fa1ca7c87401ec2afeb7c7bf0487df908b3e09095e4010d25b9b7f5653ef8cdb37f23
ec5e50bc1b7bde9ec7dd9b996418f1d99b0df169e5e7809fbeca14b0259ac692bb187b0e842abe15
c785883afe8de35a6ebb6cc2d01da436b5648df0e78ba8a53bd3502d9e7399dc4257fc5bac6674ae
fee86806869713ba1e801da4497ce5764aba6f679b03b8455c7c8094053dc1d86609532a1676437c
c040d243e6a6868c3c1f7bf53039180cad2f34206437bbd0221c47bc87a7fadaf814e5dc9952fdac
1384e4c8445326a127df0e68a9b9a926e7551ff2d708abaef63a8082911a31f79f762f7db898cd4e
e38fb60a824304d8a38b5693badfbb2a747067044e9c26460968a264a78fef3ac8568d18e65ecdd8
c46fc87b1d5d0112fd88b7922a72d4a36a2e1c633bd876d2edbf18b02be5d4bc97691da216ca852b
90ee12be17adff34a4cc4a6762d1dbcc9f2cb63c60daf3fa237de8cd4ff292eaf2a84a6918175a8c
8db8de3fd127506c8e28051c468e2919cc8ecce3cb494c2da3b97c11b19be498297786e09cd91c53
4f21989db68a3cea805d786d2c7c36b461e85b782b7851be2c8104550a4e3068f07c517bddc6ce9b
5db031a272d0b55c6f9f987935a89b753216916b0b3d6afb860183c98e1f4293e82bdf655615761c
65b5f4b1334dc230354e78e020918202c90b0a180851b633e1b07d693fd0142fd669f411402fa210
a73ec6bc3dbcb5c519c3a6e9dfd2ab705b3881406542bb3ab5d896fe88dac29746f5bf2cc701c3b1
44bcb6bde1d12353ac9edfc2aa8a6350c623641ecf5c120c08b3f3f23891ebfa64671b8c0f213eec
c9745e81c22b14c242e879a516972ee7d0faaf9b558f993c62a7e0ccbea8d8a51dbae4b1be8b9d13
2555fa59fbf7f20db55992ed6903762670f6e8f1b8d21c434fceebaa4fb00e76242b4f4defe2cdc1
b136298ac70ac77c40ad82bc2801b07328f331b2be2accaa58aab80999757235d8808bcb388fcdc6
6401e56c5ad275cae3dc995d0bf4dc56744f917f3a9ba727cf0db71dc65935c3aefaa2cd47c09253
e5479e74d0c0ff05b53b5c9bfe41fa86fa5360dd913c41262478f0b8f7e21e93d1006c6353d64630
34b7e95566300a4789bf0cf4fd336fe49d2f31d83c5538d245aa3ed01e50b4e86f0f9cae79e4eb6f
2064915b50a0051daf99c56280a0a1469f3d404330bb5d85e77ce93bbcb0abfdd061658db0cb342e
51fccf3dab9e9811c8895c75c60f877768987463d06937c8b3e8cd64c6bc05b3793dbd491c9c5563
8a2a860b0a6bea2f0137a6333433c642599d1097f32edab118674dc39e404795dfaf316efb0b3ee5
42427ff60fd54ae6c271f348adb56421d268fc31727405f1465a37b8da37efefa880bf1b0a6518e3
d427cb97630c711b33b8c09a9c44c3172ae1294c505a8450a62ca29c54dabd318844d08fec6236e8
5b04ac6923e6658b45bec3ee769da07e297e0a5dd80fa9854f06b6b9e41548705c8a4d6dbd983cf5
528030a9e0091607820239f5b62368fcaee946eb34499e9036ffcae047c35cddc0bf66c350a19bed
805057d5083f347a802dd4ec53a2161251f655c0f280ae7433a10d0cbfdf1ef6e429998e36b54e49
1d4a700a56ef7f4e99e4c03b1bde2da4d41cb32e9c92308a2a3b44e8cbcd87d362dfcc0ba94d26a1
104e7648bc16a8a28805ee63a906c7388961d3f1d187915b0b672c4b976499e2d453b2ff2a11d256
c0fb35607d9700878f2dbaa2fe67f7c7462facf77f0c48cdb28c1abea97ff11536c509b65f6ac074
aafb0ab9dd2383e028843e21c779aa412ab8ac9e70e4a4f6b090d7ba8a02ca4925a762369a513811
0fd3d5ee8fdaab81cacd09bae57f174824a31ef0a1a47e6fc788304439cbe686629dda1f691a79b4
b00118edec18d16d534ce51ef385db435fb17e492ff8175aeb920ebbdde6ffc87ebd9a95c91531bb
3477a5994269d04b0eaeb6990af4c5cebbab6d6555dd4cf97316422a31cdc052ac6f5d79e563efbb
9e66e48e93ed0aba1e8d26eac2e8d25ad4bcbaa272e4fb4b39bb3c26189422f4abcb955426b6794d
d31d3664d181df2536a2310dc0fcf288ca680c7651f0696f402cb3cc64b8209803ea4cc19e23625e
4881ad3264d0988e048839e2d583c6d301495efb555a4bfbc26b413084ea3cd586083011b00aa32b
5c8166fa0090ce37acc69afd8fa3b4d2eeac11ac4e24982cb93ef826c0dabf57e48de2ff9c95fa7c
3f8b60531c4d92be5b196e7c12f935624faa4231844719f853d0d573a57e51d486d336c066bd922e
90812e1bdc3382c8730e0bdeee9b2a21d140f0930afbdb368aface2b1f483428621b42608296f1ed
f2d9bd519bf73123fad54196c21dfa0a49d8b4cca893e16121dcffe571bde9e0115ff3ef749aa950
689ff826120c655d38d099fa0b8a0d17d1cdf2803287aa7fde502121394235290215f5a0cbbd4233
f33a24530da9fc14d6f0eb11e6ded843da51d4d4e0ccfecefd9e90608e023b1f973fd8a8f826b36f
ee9e381e64f260a1c762058dae4e6060c82757102ec90b53534fd674cc18e0327039b76601197f3b
18c478c24aa2fde0da89e2025ddb7efbc0762727f9fe590c01d113a6bd194b0d8c62af072af890f5
45ee4a69cb6bb4f56d149c51f0bb75ac4c5bf0fbc2e71d81a6d95e6f06bf33b3cbb49bfc060fb0d5
47af43c5b682fe20f843c7c111777e7a130ba04358300ac357269889df5025074044411fd8159e73
e43acc18dfd2ed3d6f3fe6d2d4c442a9fd9b40228344a705ae04caaa000552fa5853327a25c10f52
1d1f4f0a946485b5a64840a67955201b2c07e9ed42de6bddf94ad6f40939651828b22ca46c888e81
492f86488956db41619ce0a6e78505a76abdd944176a1d868634db187594b275031a57ddb8b55c74
ea65c41da653fa503183d198e0bc8bb7aff51dbbbfa2cdc7e2d1c3c764636416a4e8f977f0e29dd5
153f5c87b05f42de7a6cb2bc07b8edac172edb3c95722343773ef90cc26add62f548e6710207ff2e
edb909cd5e325ce38f389828faf6f17f273f4a84289b3ab0540c9106486341754f3f6992e99c8477
f30155a778f54fe48886ea75413da7dc7fcf0523cdfd9bb416ad91a0c91996e444c9eb880576996a
de10a4a932e4d770d60539c583a10f6039c3ee33e8b5218d5eaba720e8770042126553505dfd2138
878bc9d79c02236225e8e481baf5cb25fc62e411c3247aa0bd9ea793552626a3f29eb11825bba075
4993e1301bbc2c147c440942f38543b89b6f06e56bd13204c79ee4f04508e8255d241725320bd152
9c40d8af64144e320412e0edde4efeae4cc80cb493265d2589e577e177569b15f9c7359b1851a519
bd441e4ef7eab6547d13211b3d85069490f4f71438275dd2f5b01564c39e1dc9e1d969ef27cde7f6
921039c88a551fb5f1dacbe9c83e750fd3ccab2c298593bbc163bc663c919638bf6e66d9f3f91cd0
6c640c33f41e733589699f675e40f9f2a7c000f39b215e80c6a481af029c20adcce4ef7974a80fb1
b75ad764a8041cce640e0bb6674bd9eabb055a8a6275077043a7b687d97f4bb94eaf14df4ed6e370
742d674aaa1e3ea956cf47a33c5a707cecf3b041a9bc028c5fd5bdd69d9fe74e13d734771e98b48c
4442829012ba640a18f7963e3373d17dc2a4a5ddd3dfa6e213ab1e7b6590e856fda7c44d3169a822
d0c4170d1bb8498e306613f7932fd4cb0e93ec1d0ef049fafff269534994aaf0646a3cead0afefd9
99f3a0c949384f4f2a975464f9619c2ac0b6b9af9f3dc37e10ba79248dcdcb4022532beaaab26b94
45119148b57d8a86d91eb4d9f6ea8dd7bf05f8fbb5329d6a7cd2492603d083b5f57ff3ac4075e622
d9231726b2f0e6a024b68246e5422a1bf5ddb1ba4165e0bb223026e5df3219b2092094bfbaf19700
afc9c950e4ed7a16e56f9c81bb2b95d84ecb27d800a2ca438f03a813a01b1123f87715491a90bdd9
675c3344c52f3fcec6a5c95e54aa3b560ef6316a2d3fab2440ab8fe52c67b8c097ee22de4f8675ae
130915d594457d8e7ccc38c8241f9299ea10a5294a5f64233635cc5bac162d6cadd15510d5dc837d
4061499948ef0871908beb29bc6eb0511c26efb881594c7a7bcafe07c3ca92c68233264f46322854
d032512b087216263ac044a1b3ec1784408a62996f5069998f33af341b0439975890ec9c1063fc3d
5873cfc1515906531600440ad9dadc60dbd74e3592d2ad6a22da9b86b7af3556ae74c7a42e77c03f
ae5c3efcc3beea7a7ea181c8e3ab94c51e34d4710714e741dd2ad6fe92b6ecb47bf7693682a72ce1
623aa8030524b758316f4b7228adc82fe678ef7f169e39dd68cae8f0b576a34a11e4a821b99be50c
e45ef56d1875a8e1c40c3ff352f65989d71356e822baab805dae6490e3c5965a0afb6889fa1c97b2
18a093677fe1732556ac01bbe726bf956de13d87646edfa73d3a7794f5ba4db826085f20236fe27b
7a15ff9f9440cd4977b63a08ccc2c3b48c306bd3a8c974ef330da51963659fd4a74f4ba4f7f2aeb7
0368dde75eeb246cb06edbd6c2da49c0c922031005eaa9cdff6a6ffeafd712756471856c21d6a993
d7236ee6154ba5d976ff7de753484bfee3a2761e29ceac50beff4ead3cff62f5fa89ff2d649e62aa
8e140ba15bc1efd202cf6a374511197f6cca23d345da475c6b58e898aab5b3357acdd20b0583b067
4b90e4906e211c7f9bd4af977b942504b146e25d7dabb73caf437b691cadfcaea97c9c805f9ea42b
b6d3ab422476146fb585e892055f68c541dbc1bbb22b2a213448c9da795e58fc334da9e8118abd95
8cb54ec1ad71711c508853f6ea3d56ca590953e39cae066ce333ee65474fe9154d567ee13cba84ab
3736403c07ac6c1dc2ba5784bf03a8b3e9b520c6dd3e663c33500a760b3d3546ff93b37c11a64f8b
9d71be0e1b8132a7585ecbcc73f174978db7037db29244667701c094f6eb551e24679e17bfe3c8e9
9b15d78ee53f9b3fc836a10f56a87a7eb6a59c6d9d6743cb51d95950820152225b3ae6d3d406ed92
2deb8ebe59a3160235d98eec1dc6aa2b25898ef8695f9f54c2b97c5bbff780fc60c174cc3fc8aeed
ef41b7e31ff6f229248ab1edfff854e3f209a4b9f0a7ca038f64328ffc9004bae640af0137d69e02
be8a382435a30982f6fb5da93c55bb06bd428f4ab8297ac84c56645a8b2e443ff5c7ed745f57bd0d
dd46f86c0955bf6717ff3f845be79b22c94fc49abaf4740c40763f0d824b402748743e1e3d2949d6
ca0da3d655c663cde23cefe2897ad49f1f91d07aa2687ce6e63cc232fd59fb04988a536c9a00bc31
abe24a5c9b096e60c4fd07df4d2bf2d4ae1a93e330f6e1df37592236bfca637f58e32f72122899d2
b30495cd2f79eb9ed77879663955e6c74deae3064c7e0b7719d3f72ff76c3b96650d0c3ab947a150
219537279e0d717a3e44b6909ceb207b6d052e0f1bd8b912b7d2e6a1173afdc645bd7b0c428ce423
ee88e4ba52404a7e4f51482ad59f3ce02b88fe141aaf8f0fe5932cba781286a532f9b782d6487bf0
c6e6cea746ac81e62ac6f54a48a4d07989e36f9eb9b3fdf6fd8d7c81d560c5accfef62584a135234
7300ba22077060fdd1f10a4b4d5cbb3b921d41369ec033d7ad8a9f646ae32a4bb5d1ccf7c4a3b716
bff7768de862a7e2108d63379e7d5411cfa4c88808670c8ca8bb1da6dba606f10294ff8efce7f2a4
df6c1638c0de355c9c913f8deefe8c02bfe87723840b78efcdbaad08f841bbe6816d3ce131991eac
be8d6b09fc7d7c09007c849345fbd9d1e3e921e88cc7dfb8ecf78a08bbe815bf21975d2d223b728d
b364a60fe4edc1e8641e87e5c2d859955cf78ebc67c35e30f9d95bc9089663a3de8956afd0fc3704
4e274aafaabefe82445ea5a53d63cc84ba960f4893a34c9017afa4dd6b5bcaa410bb09870805548d
74c3cb8fc9a2abaf34eb2c807d54adb4a8c68774f5e60e153b30655d0e9d7807ebcced21fcc35ce6
11894450b577e9715161702014f25700222c6a173b4838e862d4ce825d31cdb0d5a8670f173342e1
018397c51a67458ae3f35eef811b0cc5d007a6e3c6095da9d240eab78f4341e7ade3256df51d61e7
0f245010233f4c5e8ff6d07b8ca9b38ec7238aae2743e13135fa43679ddc723811a20fba3e5984b6
1d7bbdc765a9000573f99e0b3ba4abf64103c60b10ef569265ee9032633a2f5cd896183139458b8b
1a9ff02d93cc54c1eb0b16af91ead859f8943495f2ed84941df9fcaef1bbc98658e26dc0a2a0883b
878fc2126c4117c3af98aca450e00fccc617f35a006f40ff31332ffe32a338a65f32dab2adb1b9dc
d0f235f1df08296f94a9088e3f309591dca98f0a02f57de3ff2fd54967235c5753d9390b8b148c5c
873b2549353507684a5774465258c988bb1554315f6b87c31563526aad4f8ebe0a5f50f49d7a1d54
0bf1ddd999d3af6047f3ea4f22468f87ebc4e83dee25ca05461072c900af0512d93ba03ed92552c9
2ecdee41aff373924478371730bbc9466564ed4687f9ea1cbd201ef8744221c3dd034bd299c8c140
959b1bcfe227f307a81ff321bd9d3b1f99e96a548c16f44c4ffa92d2281a0e8515486708e45f9bc7
ed2e469123d28f104f0dc491f988ae274b4487ded5e81450f4e54434d2b776be378b05ba81574f1a
d79c09386b0f8529759aa3e4ce9953f87f5f29d241ab9bbadc9f8bab4c26b1220b662283c9e16551
5e1af0bd16e66f24c98afc4966665da55c8b6b112619be9de787038772e3237c0bfeaba99ec3f8f4
bccd770e7f5abfca1705f69e7d08e0b79f4d1038f02068ebb88753224835f292f4623f7813349854
1d08e2334af1dac027721e7d404bef3ab5888c1ab2d3458ddd988559119765314fda52c2d4f8ac77
5a580df640648706beb3da490ecd999a81d77f7e5afa6fd62a1e413d722c9f7bd7c689ea0697fd6c
ea2309fbd56254c1a2febb3d3107e52874f44df35924ed90d14917e2d4927587da029f6e0aa0e633
a292b3cdc01e14920d600392b69a901d1df816d9a75fe9cf9411f444e82579247729bbd7da42c6a9
0dacf8714d978e2a0ed6123d2a8a490d5b8b32e321f093f0446a3f61e307c92dfa64f14ea98a1084
362d5ca5e33ef2546802574ec574683d8055cce07676370f96efe0c5d398447619b64e25f6c544f3
796bcea7a9e84608a0a7b247a0ebdcd5055e8a9878d5aa5803994aa2f8ee6d463f69b8b35cdf3662
876fe5fee299706dc214ae7f4175ff48b5194754b3dcd7f855eea4b8c1f7ebf082ebe83c0c2cd7aa
2bbbbf287234e875d53ca79595486ee08bcd019e7b134fa70b94f00a9ea2d69b3b995a43b6e5520e
25966a34e8fef81f0034e709e54b5fdd8b8866c36bcd1b5cf36ecea9ab0bf949ba2666f2146c1822
db440a8810fad1e54ac65cc2098e62bbe94dd62eb96f132336fd7f58020a881b662d5767b71dedfe
cb60e2578d16535cace0e0736386976c960cd64bbb296cd6813fa31b17242259aec1b25e21826f5d
971972d9565619f22f03e3a807a18fe72dceb839a02d8015257179d1638401d5b98c86e48cbc8c41
b8e5bb4a36ad1b3395553b635deaaeb817ea6b17deac87471880eb38e6ecd1d73089161230445749
155afbac0695d6c078e1ede3b3a33afa202fab49de737c9fde03d78f60c0f3ec847be9593ec887ca
48169a85dc4f72d172c13baa92e7fcaa2cb8ecacd4ff6e1f49f4abfce390eb8ad6a683da566b188b
b8cc990fbd862e005e82a22357a01cde7b5bc4f81c4df63f79cf587fffd4eaa702b4a48d3a9c81fb
8a5bb823a4de9192c5eb02e64ce6bfce97b0f9ae0cf380ebcef94ea37a2b90f92f4f12dfe9e051c8
e3b59c0d231edcaecc40faab2855edfc4687950abbf84cb7e2f9a974f3deb77d175d830796f9e375
d61282105ff49c08b8377997811d65d924057bb7ad162da65b96debfe3d46882
客户端第一步输出:
cc91c20f7102611b1ea475c55651d2a85ce47f6ca88b92c67ab68b500014c51d98c333bf28d486ea
36ca125b0479d0c59a35f4e506c685e34cdc3058e52455912510a0ce366e5ccc642f12368e85c20d
1cd9f06618f1003bee552d302aa6bcfe1e86906ba5386a010887a76c732bbaa470a7bcb6c86ca972
0d81e8d5ba6cf34d8c4330f49b796bc143ed790b75f074ac976dd42178f245a42fb1680cc132fb7b
99f3f2b7081d3f518849567091d2be709a066c356788206780f8524d67c32e70487276269285fc73
63ffc4851839e71f76c5c667746d6c99994422fa71a7dd30b3d6aefc03c3aad4a02a796fa1478a59
8f1a80290a345f6cd4e915a126f1be724544083fbb9c225d1dfe144427e60f992a2a468cea916df9
070ed52f2abef75fb3e4a396f67443e5d1f3382ed0f93f8ce779c1780f2770622f7e241f55aefdbd
0ee45585f81741faeaba605e22d5b9b7105b0a4686f1da4b692483679083d02aa0e8b983cdbe15a2
da116832440217d8178fc2ac66f73ef86792ef4782683b006bfde16da85233a3315c5640a544d0a6
1bd410a08d133339e7f5df7d7382c471fd7de82d693bd1d2bd247fe87a8a88d5d6a344e599c5e944
55397ca910b57f369deb72b71ef55ccf4a3da2d4bfe36f091931689a4b50d289d77a29b6c9eaf605
523cf79f6773de00bcaaa312b97e21182f9ec2e85a79e76f915372951e3c54809d5f731ad2ba6350
04418d220902f01199aa0514bdf38906d582c42f480f6161f0d69ba07c9df2aac51e336d954f6c80
4a7b6cb4a1ec35000ae3517f1dd2f522b6bec2fa0542bc02eab08fbb3d043ceefffbe56faf9f7fcb
03a99c8d89d777989a802b8e7b6aae16327125a22cdca59d2b0789dad00b6928eea41d3641210207
d96cd6a0d5a64d069eb8cb6f5921764cb436e46bf5727b9244b984afbe21fc79c0a6a334197ce2a4
d724df01b3f818504986378f18d415741e2acf93f9d6b509ef498b81b3fcdee4a71a09fa67226338
3f2e7c739e5a209665300f70f6f486a94cab369ac8ea611c6eb0db92cf8442c80b7721ec25f8d0cb
b6bb8c8dd2beca2ebeb9c6ff5743ece7ca2db2da0a6633c6078d5667929b24d5badeccb8edecd792
9c6b7463e02a9c75dcfe27c8d4b7bd681e12717e85324783a2ea806eb723f1cdb9449df0eacd05ea
13f03aab6e13c05c258068e44f53e38634cae34e724eea865e79f6b58f23f9bc623a502fa57b2b92
126471027c21a528adeb209910ac4f0eeecbc318f811261e41fbb9535facde60b2454c786d1a07f4
ebef224fae88a5ac2340f1690fe684adf2809514a7ae54cc49259aba271039a5220a53a52b4fdc5d
495f462c164dd0983b66942c96b8b222124ba7740bf8fc9adc8833bec2deb14be8d217205dc011a2
047894c73e396a74c6ccb270a38c23b9e84fe08d98cd9cd72620a8f62e3caf0ee7a588070ee77145
5f4320a7b47ae26c012f9b9578bae0cec34f8812a4f9e96dd665a36871457492369ba737f467940f
932aa7f74f60359e0bea84f00678b47cba95b3e3f17661383effe300dcf072db27021deec7b7e2b6
5b7537bf9e457120d81371eae0639f871a28acc3f92ec9e04dc7b5d8f2459fe7cce4359cce970ac6
a89625e90048deb5fe419cb4719242e5d9a22a392e4f7979f37e2a6ff071a697ecb5b3030378711e
b74264ac667fce63a50780eb7004b926f1c6c15e19a9987db9846e4acf40d8ddfebcd64fd88c151a
856ff3dd4234ca5a2e028150b41640178f7fc6a3e85ab35d5f9e6b7e5ea0294408583a57ee41d8b9
72d3c6f0a233487b6829bff6905b320ae8580bb794db9d0ab2faac12c97264f8281b35d72c877e68
af665b2a68e905423688640057b27afd4bf3b61f4cb2313fa0e29045cb0570f1acaed37d3c1bc87a
0ff871b5e5c7e4ca2f4d7c943383da187f774844ce3557c0c37683b99f0cd6ceded1883fa5860508
98713039509b0192db692621ec02274ad570be5db429dcee097b05ab006476be3257cd5e93b85992
733f04b2737933f147e08d75f0256d6cdbcb4de2e2e11fc3f5581a5e2dcaceb93418fa1968dd4b64
91de897f27bb38fb6d46ae53cf26fa438d41b79840275c702c4eef3457a49b1aed64e49c38fb1459
779a8d16b476a03c715e712f8c084f2b3d07358e48964e7521a7e723f95b1f7f21271b351542f227
33d38b42abd0992751437fac0b423efc7729512ea91f61407805d68d3e32c98b1fb3c2bfec1c45b4
64cbc1346d20c3f7f92f40b658ec4d7e6b9b2006d48246c9323ef8dc5a208eec763c55c62bcb8ee4
33b648c12b7284b615e4a2a0dc37307c47203b26424038ca35a771011d30e7b3b1201644dd3442d6
2e12aabb3e6759f36c8ec965dcbd25703b4771c0a4cd3f7aebd8c4a6869d6e472cffeb4f1bb323be
f4dc561d33dc6b1fb03ef793e4a51df9dd35f221ec6a568ecd92ec763ef8e8c233c26984651ad1ac
64c6930648aea34616551704cdfe6ae9378d9d5eaffca98a2de580dbef98a79503afe45d84c4ec8d
e3f532384c2d90e239fe42d5c5b8981dbd69c98d1bc8e0833ce999fcfceeeb32d89a211f631d1676
a212993f6b6e90552b394e7edc1a170e76d8d1eafc4ecfac33bda567882ddb71bb2f42ad4310f8ba
80a32eceb74903c04be21f306736ad181a10bc9de4d33624f45289960a187adeeb58b3125096a5e7
7bfa45230163a7eb2efe12acf67bb3f024c377f803194e6c58e6dfa88ddde0ee2c1c6fbc9307b2e3
4de8af89f24a1fd955ef10ea79d1e8ddd6d5be342ce49f23d69fbfcf9441e123aa0bfb759d471fda
c2d8da9eb7ebff7c6d3605f313fa8d96c7ddd78fd5e78eb238fa54940801b847c46f26707db39082
4d5f59935198160d9032a0a66e9a0d6f603f79c83acf6710e4f9079b40a0fe3ab29f54ebf1240a5e
f3b1ac59fa170f4975d8b39f52c9ced88cb4027e4c33c01a19712948b7abd16a0ce861d11a3384aa
cf6690829d5be83be5c95350edec96c7a594ed571116c94bc000dcc6f40f89fcc9bd342adeb4798d
d30b888d74685e4a758da6443c6e9dcde2f68d5332f35eb57b924ccf15b67c6b8700059ebc565bde
2fed38d0fcc67b19293f75a770d1d0bffa6ce8a6e9ed087e9c2502ec9303175f7e61219c7ba97a07
5be841778515b7acb91519367bd7b28b64e9d2ab31b2af13562f10bd47cdba2ffcf4914a7b1bc280
d29240c91bb07db1f6c4deaf2556dc71eff51fa907f93426a2f21bdb103935a83e743bd9054cab6a
2970fafdfceab89af4c4e7cf8fec5ba061f2c2222b451727f9fe13526af76f0f24e1d819f614deec
6db486b3d2ac89ea6924514dd6f78dfd0831318d4d9651e97c1f3901ae6c31fff37f83f244e46108
4b5b8e6686fa342ffd1a462cb9e2254225b414751398b13a79cf6f9ff06d2541383e75808fffdbdc
3faa48e769c49a8336c5478ef40a49d317c9cc63ed6daaf7d11e8a93e326f41a059a97046db8b2c3
a9ae99554c321ac3b1673baf48173b83460c5a80fe1d2a02207e7108e8d699b3521138093f3448a1
8e8d5278ded14b9fd246af6a145b792e7098846338c1bd530bd88b78bbc300965acea27e9fbf8cef
866e701d282b55860a630a8a7925911a428f523cf79cb56300b9c7a0a271b041a634eb2bf1967bd5
cbcc1b35099449dc87a9536fa0ee9461a515f15cf6ed856d14a158469f8d1dfebbdc093a980e4500
fb2c048c65052d4904b4cc2d4c03f9e45543b74a05f877307568774931233e2f41f393ab38ccee25
7a398b842dad5a2a3255951eecc7769cd83f5f74551cdc691c486b7c44786c2285279271d7596fde
0c9b4b9827601921227fc0effbb88f6dff50a0df0bc4360949268fee8cd54bf22dbb2acc4b5c0b36
6f0794e833523ee3ff1242b26d44d87be31e4a6d9b845cfd0be48497a77c19ea9b796338b5cab135
8a9221332e31d50e7d7a7f473551d088084eba186c43db6bd69fb5253961434942788877cd8038dc
01b6465d34a8bb3ac557c35e763041a5d80ff39e5690a5c92d02ee6c6e5f356c3ff484d94e7a9d07
0b7c31627368cd135906d574c8d8b0a6a3bb1fd167c8118ac8003e1da29a43ca2a8e9cb9a887c79c
2cd04f639f29d0818c5371a9623b9d84a520759b21f156171f36d84f2b24e6679ca5ca0016cfdb3e
f7ddd9faafba666daf6ab087ee71f274bdc6780246447be4ef3b131b1fff41c01a6bc0e36bf7d76a
0b25489535186d165af260361456054035bd1c6aa7b40acc9b55e8ad228cf07682ae28cbed1f0a02
fda631bc62feffabf4e0a7f8d1899f46d23acb7ec5d5d880f82b96ac7d6b31bef037acda9fb1c88b
2ecf02abeeaaacb99dc6f19113e3fbee11d2d9eefcc542cdce2945baadcf9c3508d7264c8b6b3bca
c2a0959f64f802f9abd74f1460a0aa514265dadb2792c70c36d6fbca31db329ac67d389cef83a6f0
ca4c0d5aaf00590f2ef40390179bdd3820245a2f6d7b1825189f525b4cec4ffe12bd7948706cc972
aceef19425abeb7a4e2c1c0fc09b450d01f0a5876f1e8f5ffd6af88add4c09d0452521cee34858a8
6123aba7537e7a307d01716450ac74bb50bde63e02c8b1c9cb35927a98ca6f40ea55b9d196463dce
67f024ae34b4111862bdd93e8530d52dd8a61dc9e5832319cb7c5aceb6200b360e3b9f2c4008d2b9
1d2e44cee99c16914c260c6c066bbc393f436aad496b08f51c8f27e0ffccd5d030f99faf30091b3f
cb461963c73d839f0c570c696dfa913bbdcc45a51aeedff5d813e86f629e83b7b66bdc8b20610de7
6989c2bc08617f4e471c0896f92d5dffad877f1b794c8cc0f25b0236d93ed4e60e41b33cbc906ad9
ca4945d0329644ec31a70357ded1b3f9f46bce117c10bf5756b2ce4b3346c72474b4991734ba4064
0bdd0f7e254d4e301e60b0ca3b0e21edf100fddde669ef14b305899b5e71cad835c5c69c91a0d5e8
64dde3faa51b72b1983ab0bce0bb954d14f2a8460f525d6a1fe5ff27076cc63f4fb3f965e39e64ef
a65a2f1f694a9632b3d07ed3187fa8463bc84a6ba7231b745ac2d21dca628d6f62218247ef51571b
f250225bdcf90cb61f6f90911a5e4e38090a08eac2041025663421657c54f2a1544718cd105654da
09a56cfc8db81638fb5d47957191ade1aa1a97b0f582a060ca83fa050d62e2ea61db66cf8f129674
02a77168a72d19bc3d90ad9d9c514d0ff9013085d36333ce92c8249fc7a93e81d0792457bcd62430
afd0fe2a0a127609f784b0cdf806ef53f080e473f2838c03f704eb9b5108dc746d772f18abf01122
ea5de99022d9ca0d46dce7d7e8f094224e607fb4bb5ed0021d51633e3637633b0d5df0bc5bfa2252
c99528d9e1c8b0459053ac0929b6951c8609e7b1afc21865b72bfaf909f16186e167fc2978ce5c80
b60962f1942140a7fa1ed273c96051692fb81594ef8a7a29e5a009067989df09f7da8f61e6bd478b
6eb3e3d52d998e311c19647a87a0a954b43db71a8d0eaeb8583afa424a2edd905a7fddb86e7f4971
3911107f0f000d6bba3084c0c55920f2cd3c57723428f9a472a3d2f67314fffa39297a41784e0fd8
ce7c4d9fd2fb59716d14dcaeeeca7e70d35dd9a7e0e6630f335bffe0d5fd84363a18a56286ea11a2
fc6fe1e3a0590f19e405bbb7bf647992213fd9f4feae982738572885abb3d8f718b073f32e09021d
8ba882aec99aa43903685953724cd212f21930d14020dc9608d23abf6929541d456b810736d970de
de0764d88a212e6b6e1220cf4226b1abbc4744721b6722419dc4e6034082e0a0e000d2450ae7757c
08620b92ce223d46d4a16dbc00ceccb2cf978b1cb47bfc6695352976e7e0ca16e0d0a597c28de9aa
c7b28795ae6fc454c87c4af98a7242a18a38330ef703e0aaee5768592ecb6a3847440e2d542961fb
ccf80b2a57ea6c4617e0b932512da549ec725b7066230fe9300fa0734de3b7ac71eb63bb677ba97e
6afbf274cf069b2e5e0f4da52907320aa355e43b924ad28879cdfaaaa3cd4512c102d1c7810799a4
3a40f8c5915db6ce34b99e5f32f65d8b24908b06d5086d46da3172827bdf5f416ca2689a31b81e1f
b6adbcae1fd95fc6595d4993da3474f07295ce72b5c10e01e0f4ceebf75089b782adcf76641224f8
cbed1a7773a9483dd9ed46db7a9de276486ac7666f27f5b1b1c440541ff00bd3fe276f6a0d2ff24d
141d0ab41f8f1c1e6aa230d6a1bf75f623496e9dc4f16d9eb35f557a12b32f498316039d2ef1abae
1c7a5eb99dcc627c6f8276c8adcb9c7bc8c572c0463b1e658f23fb68562a554932d135e8db44e29a
461f708c4a4ffdf8373cc20d15a294287a64af04bacf33677187bf2c20aec964e13d438dd4cdf21e
1ba094b13d88b3ac91344e71acaf92fb22ee5c8fa375bc92a744ee8582a58c15bd4dccb5e31538a6
5dce73834ef603d1d1d99b8c6b8b5e93b727cd0c06f2edb008f4a22938e19d0830afef08876960a6
bc0975045b6902965fd380f3c3c41390ee240ce9f5957ebfe274c4454ab0297aa72881e5a3c9d9b3
bb2f8aaf1dd8b28bc288c68e8f33ffa2666403e711c6bd85e5769b098b8834feaba4d130153048d0
3efa03c8cb0930b374cb136f3c25a131893955b9ed70e8d74f9f7af991f043ea6d8d392f6e25bddf
4d9b22111807abead1b822c116afe4f8cfa9ccd1d7862fa985de25379285440029b162684c3cac93
7b4ab061f2551e5e2b94bcbcbb3527e842626f9a1b993454e90ccd8c0064752d960077597b7cb04b
161c3072cab0c5096af1392ca2633bfcabd533890d875c09800618f63a88e944b23237fa0cd64c45
07d471ea64c20366d53a2a3af6a8d9939a1d7c412bb5ab17fcb3d639d518bc36ec3775b6867312ae
1ea2b907904f1fbcbbbbc0fcde2e0454d2234e09eec210ea49d37a50afb48dd89883265cec8a96b0
5165ca3ef7eecf62c25b87ca5936570ffdfd698d4417afbeb58a54f220afb5e4dccf0323acdb97e5
f1bccc1cfb2a859c306a6773b63d9c86e82fe333f99a56437d03ccd608a653355d72c1f362299e2b
3dcee5d968df660c6240b555f75c0d7d1515f38b035969d660890c673955145446f9446b7a47025a
c21dec4e7f2ebc58e0133c4987415f52d5528329893adf9c887efea50befb6c0ec113d8397aa50bd
ead8fe92f78d9d6486a0ffaa428af248a61994671cf5e98f7729148a53b59b713adabfed19cffdd6
c055c3ee6ddb553ab518af9ab2f99f7d953f8108016e1ef3afe323b173825950dba45b4ed02a4647
fb94b695ceaeee81d8564816d67935f2479f70b09416972fd576a3445be6f065bca4ba770f760392
65e27b5f4120cd654decc4441757d7a82562e7bf9af9f1afd2c3b766b35ecfefd1b689af5bda9e74
b011b21f26e52dd5dedfe8ae470da98ef645571d8cddf91bf8f6f7c632ffb9c404a3049675536955
2481cc96625180ddd620af0c01deb5238a550fd6f1dc60efa582fca0c5f4fad48a14605029e605d3
81231559989d9503d85b23beb8af40eba3b7104b983810cbb677860f61c5961f5c114276ef0cbf00
d58209c83c4edcf531089c1f6524ad5100eec9b2c3d568981b0e18355ed54851888d1aeaa69d94aa
4a2534fe328c5de33890f7bade634e0fea5ce23a2c2febd60f59fbf69348a27867ce649db783f4bb
f927f18e4c33bff4e0fa27b8fb7ba420ed66d4df8f68918fd62bad59c60ed4ed9a5b7483bd276c4f
825ec5fd16b5a98b7518481d82d5bfb99e0ff2b4aa61badf7a3327cb389d69fd25db1966f4b97b15
54f64824b111a79ab12b0e804208e48919e72dc3422e97c60fd493395d42096eafe3467dd0f9a07b
b26b107a557da157997336e011aa48569ee1b548cf57cedb66bce7415102ad29f3b500aca1f41c4c
c3dc83a8324089b18ec0c7403cb0e9126a52fc949d2da604bfc3e6593fe31c1fcfedfae4081fde77
d69040fd006abf19778f8cde937850193638b6edf77744028b1eef77af656048e9496aaf6f656b6d
26e980ff26946a0e5642f124e2418afbf9173e09bdb0326797287a688f85f1cbc5ca41a409eb19a8
f054c102cfc0e686491bcbb9ac25e67798f27680a9d57e5f5b4d0e975a163e9ffec907c29fcb3082
dfe1b09fffe70f33ead36519511e81068867b762851726fa5ffd1205155fec526c7bd6fb855eaf91
4948eda1eb111d3de7cf1b1d17f3cdc82880ecae17252273a446697439014a3ec32728b879712c94
4ad35d92b88e21dfdf1a16117c9fcade7da78522927e2efcffef43371602d1844cb5bccddc801d24
1f4920ff52cc9637ecf43eb5ab2461f9fe6331f2f9b6f5387c964e5c88580fc2ea4fb8bd796a5590
0e7d88f7832787cc5b93a6da4750b87dd2f2792349452710e1a4d86881378a9b8b9c7158f917008c
b9f325af6a0ce56a9c973ca30015f5b7c724151a6a17cef84e61563d3df19a6974e8804f1df62bc2
f1b66c64e9352a06655a810750557300a2d830c7e88b2523bf5bbe4ecce77b7e80f28c1f49a5eb3d
f3f66b83ef1c0c9f8b3fb87a005b1922d14ef03c750f4b561bfba8c006dc27496b7278d32c76637f
63ff6cfac8e58f78a0bb12df187830c28f13807fab4105601fe8fc0e939c65daf9d796bac8bd0734
022f9f6b41130923dca70ad2b3e959378cc7a9d14240accd1f94ff06e97c7e2897a1240915b3bd42
ef79f43d2edbcd85e145e6828be99f2c833d11862713f2cd7d6ba466550270bd1a6cf7350117ac15
863a39ae46db48b6757e1563eba104acb366d9fb030f108d8cde7ca41f67706ce4233431907170ee
2e59e514dc8e6c1da796af7d16df8368ca3887b22dc9d340863efb505c25161b2671225c924f3e47
9f9fb7a34e9200b6ed8a2d3ecf69a7ee5d6771db37b6d17eba59fbb9110288b746930dc416c7af83
04692ed8acf4e4c1978783837f3ba58a4d9034f8069b9c6b91f34b595cdd9c30646f0e76d81f26e2
6ae0bca1f2452cc67f65c8577d89bb7cab83c776a866a78e5e932a47a81ebc4c4d0573afce677080
107629bf4e763894b3878b8c526a79922fc366bd0e88f8cc28443ea6e3cd4699ddf3cf9fe311e730
953db52aa95df0d40f01240bb94def553cd825ceb7448803fe7651521449d7ff569c7c5a83a1151f
8be4b0b274a3c7621fa00131d4c46f5cd6103ea802e324f9fa88bea9bc6f02936870e7520d21b81d
82b405608d998511172b28b2efe3250be8b285074b316b2714cc19b166dd5e3c5e95e9b0a0cdf6bc
a0165a61434e6826b1f1ecdfd5c0188093ef90868f0b3acc45443e91401781ffdf40ff965e3ed2f6
f57323ac3ec442cc7f06df7a112312c680088069838ce5863116e5c0863e7af8986df381529470ad
8e15383dfafa1568e794fab21512da5bf501f74f745afc646208cc776804398b8fd4bb4f6577e9e6
5358161ab4fad2bbb214a9e4d2adcc9dad571c77f86ab50afa65795b032e89b58cf7878b916e92b2
a5d7c8deab4cce1dd9627550f7ccc05398017a0a7a3decdf3fa36c12c53fce4f7d43ffa5a9d8a5b0
01464d2de2c125072253033384a6d5f108642101c352621c8ae649b4d916e67e892170941975d04d
5d7a4b74d5db87edfe5faa1273981c54ddd19e69298a3f0c4527304542a12bd9c1e4e4c4a1304ebb
5e59305bb789715bd270cfedf26a96ff7825bdceefc234a542f53173a79bc2a2fdde3669c0b81018
d0f80a7101bbdb3c254fc72441f722ac79dcd0be3f7c30c51b72c71ae66cb59bb785d7a52014c089
51c23b53db5a7c164ab5e6d5924a285e59b85420c2cab7e473853a4cb330811b1861ca753782fe59
a990fd92137fd9ac8aad6dde23429d8d640712a4b76f0d1317ea11d462e37a9c44a72f663ea0a6f0
6b0bc43d47c11a4f870f214e2e13a2bc0d734de204b8137fbd92713106887d588f61e3bc4630c2cd
29a242c3d92416c041b3a796d47b6cba9fdeea978f83c4fd0ffe1230b5ad8495b70e1ab85d2bae02
e5b973f6e654a7f8e0464819cd5f80259949a88fb959bb0bb92c2968c82e7b03c77a611967adb646
46256acb41b3fc675624ca1263784c1664690126c863d6816bc1c4b6bfc6c765a10afa2024c48254
80e0b9d6d3f9393547b349edd2ed6cb47757a2b245f034e9fafe49de13dbd5c25d6cc3e1d015df59
04d7d9cbe5e4e3720e9f6e4ba93c521c236d261fa5ee05ad39d767cbb24a23694aca5ad5c6e9043b
00c829c9e58b25dbcc5bac4ad562de505af3a4c63be71cb6387c25f64d3478911e0996ae5ddd2863
94128d4f796ce074eee0743e8f7887388833b604318326f6ac4f6c66b9107cfdd328761d2d191d8c
a0ed0e0b8efedea7db82d67214fe9cd4e2b134f73e41d89488983c10f012af444201ce5278a78f54
3b5cc0f95bbe1e10e08bb8380c18a406db2a3ac3418eaafc6acc049a27340f7be79b1112007f6349
307f849251238b04089a46fe3520a1b3cbe927d439131c4cd36ab21fff73bc84b26a966a3656ebc5
73dae6e5d99d2164aad2c4d296ddc2459059c93332e6c8eeb05db32326f577659c9a98bcd39c747d
6bb58c37a43b7aadf2c68e082ea7c9c6398ed32797f622a5ca28e6b5f6fda993d6e6fd04ca6ccbc4
c6d00afdf21cf50282510648c75c5bf7e227cd4ef301e1e3c6aa04e1c8de73f6aa698bb34a3a8b0e
136412915ca5c0cce906df264923d0b3d9a3ca40300b918a987d6936ab1eaa931b0f57a3814ad9ce
e5123d52da8740e5303baffee3db88e5a1f43883f8cc99a7196ada841877a721dcb255a72d1c3d74
6d14a8948c9081a3d2bef0d452d01fb5e4b39d3d8ac27f003a613cbc2e6f631d07156738072cf43c
004436c52bb89fbafa7306269c8ce9c2e3eab129eb0f77ab3a7a08557e00cf930f116b2285d26e35
f304362a96315f7b64920a2c4f8935c98b34899205f8e6eb7e2a2b2a5aa0a7e26a229de4e201eb3c
05cd2eb5907eb49345e183be7203b553d44d45d0d05da34b7dd4b132cda4a730b6e278ea9aea0562
5333d0fbb85a60271c412b0ff98ac2e9c1d0b76b69bc6e8135cb42deb9055479f21cc96d8bdac4b3
78e3b230505f758b339d41d9908292d4530b3d951067b6b5b3b1e5227b548f057b489d59693011a8
5e3c1bee9986d7040044ef60dd3eb295cb53e8d63384562c3dc00a9e83b921ed461a01113c3bca06
f93a139d6e639c72f03116df59eeb00c29e56931c4e997e75d0c020273e227dff422fe73acf83587
0be165a2eff5c730095ac55b693cee052c46fcc8bc16d8d1ab6f38f8e5c0a66cd3bb3953a7aee17d
feebc188a40152e10a947469eda79f05dec8473a7c5994604d6ac85c45db6a385a8f7dba25ba8dd9
4db0aec85d4d015b61e031f53d1d2029e7257a0f2b0b80f4c72ffb7ca063fc5bf6cbeb5949065e56
58e575818fd47d59bad7147af67bb3a7f3152ca677f3ed0f52d62d66f6fc1392e16f41859bf9eef6
8b1cb7758b8d04b72016da09f5116d5433fc136b5454feae963b2a53add469c31ed90fe756b73f49
42dd3cd11322b26b2652f23e7ed710a1a16415e9f86f7fc88f9f8325d65a9aa5e7e68f0864e86e5c
c22864cd5693f23bf0c352768afd7448d8fd922771b10aaf369ccca70a294f62b5e1c21a94b58cc5
020e4ddcba69c9bec2bd7c9cbb9a379101368d6403b3f2807cd0df3fd276fe87a38484f400cc98f6
19661253b8c852fa195165b2cb1c4e669b16ef533c486e5b08f14b64710736de8a7fd70e8c86cdfe
cfe764f5fc4fe8c3540ac147b9d577efcb2b30f68e94c414889df600ff79391f4c77af1b07a71ea3
7e159d023eeedbd310bd87f7e561f0c7e6d215d612a4c96ca25cfa715d6dd9443ae065ac7fba1de7
4cd7ac8aee818d067e016a908e2a01bf2dfefc9977368d0fd079c4428a918abe2671970dcaa6da7c
3fc275d78d7d4e89984d13851daa5563b87f0e08be445c052c42bfdfb5b540025f196ec1ab6991e0
43cfc90d64727df38e79dbf866d3ab48b5d91546428e1fbfd602750d8d099d0a4103639f00b1ffe6
d2cc77fb6d78622d03812cc23713dad2ba9a538cc010a45b9cb9ec931e8220e9b134803f85d39ee7
a789ad225d054a88f58823b4172cfd4b4fd0d22da034a6d15d1920ae5533fd3aa744a410557a114d
b019799263349882a411f296e7f2fca95bef306c889aec4ff2de45db8ba6df8320c4c63086eeae06
56e5f78cf5b43d924e7546aa7b6af4e9c9be4b5253995b81872cb774233a0d4e26de06f7161f8b8e
f887fd00d25b7e238697acd9945a55ac0db856d730eba0685dbc6d4e077549a497b8938a27103105
62f5260fa745d257c5f9f4f680a6fcc2826ff1cbfdb2ad1c22e1e7a08eeb2c3b37646bf8c9294608
1f54eb2c1c5cb4f6c616c90a85a5bb023a4c28d39c562f696676ff39fee95a86a2cb4b7f79d77dec
879a209e4086057c8959fc8bfab2649115a719729f70b2db6f59de851f42d6924d34f25a3680d06f
1d01f8b0a961b3f8545ec64bb1e5c85646053729dfba4e779597881b5d0ac7c551fbaa542609cc1f
e150621314e2a93f171c35360eab65ca3e18e231a86abcec7f87a3e45e3ce84541f02d5c5bda469f
6686207c7a6a2e1b62de81554a734234fa72da540f542f673b671ff226814e215ea1dd43d05692f7
1a548ed10787fd13ff31adce0888877901f1ac641f681b720c68d0b06347ade8d786b70ac14cbe60
f66727c94f7989d65ec73a73598d9c3b43f34adbaf6d385e167557ff3ab8fcca40b6936f251c91e7
995d0aad23c9ca18f3a57c51efe5b6ebdc3be81eba34ed43f679c41940ab6dd375f02ff01ed3e11c
998d07a96b5e3a4ac58b33c8cdbb98870c5aea2f46e2adeea526c76adfb6d35d1c4d5adc45fbed9a
6ad698d407f03f33e09ca0f8be3b5211f8f5813aa70a99c9419d00e5ba504b6573fa6ceb0956200e
3d6fb39c1c0ab14d034f06d22cc3789580d52056f8368a3ac621c53413d034492110e7b752ecb1ba
8268c762fb0f99bdb6a4b78b16409d04128e357783017faaaccef6b95390e63914dce3efd395034a
9c35c90aaea04ac083cd43e5dc67d471672d37ef0f24d4322a6378967d0b92cc64761dc47d3a7dde
bad1678ef751cfa1a76a55e77aec0793ee9adc89f01d444dcc18483719b30f0f59885ee8ee34e802
454e7731639f12db761ee91b68af7d8e057106fc5d7ac980328e601544f2ac17ffc46635e6d30177
92ad36d65a564331248f1b87d879c513dc90fc4291ed32fd3d1632ff972681e4a62a5a532f01f8bc
f6db9ad5c197fc652491cb2ceb8294ddc1ae414a241f225cabdc38af74339cbdebf27328b960cc39
7438ec66a7c05ef719cdf765052389070e2fc7da9ba24d947312fd26eb4c8041e355374b6ed07a7a
9e7fef3678763d53cabca0cc5b289186baf3393a0092ade9a009693bf64efb93cc26d4959764bea6
a8f471c9cc46a25dcca0cb8f1fa1fcc24c8b22a9770e96f9f935f0ef1ea8d5f93f7b3ca590d25c7d
26aa6ea3aaa9f66fbaf3e474875362ab6c0c3d09c97b96698e823611ec3559159824901ae24e7671
294328c2814754c3850e4facdbc85e4bf3931635b720514e9b0c22e885f3ddb663fc5c39091711a6
0f3e171d880835980456b9c5d1709fcf38109dfee1bdaa9b41b23299248d65d95b1d057f5edd971d
264ebf2f7ac4acf3b263be1431156cd4f3633a5434070fd204fbc202c21ccdc3f9ed0b378554abb2
8867c44c8f574a87e3c941379486fc5d035bd1ca371747202aa810e40c87d4c8ad88ce43d1f7ad66
3246960828b243cf7b2a467a0c61234d03649bea514422a1f33e4e3b191de1a523cc0df918b6daf9
8a5153446f0b51f10e0152b0e85496b328a2d766d619f7956d9a5ea730d92f5f2e1e8177fb2148e4
6f4417fe5098647f33912da2fd12f140757ff4b40304d41e2ed261d706f18cc01cd2d57f39c5e676
5e2259dead1d13393c397b1743bf19439251f1fd9a84150bffcda27a055d8354b844c5d24c73477b
3078bc7700abe025eefd2193626e35009cc18f52bb3c7386c3d670c88a80c8b08cfd1cb5ddbfa025
95bcd40e4f37b2cfe1053801888e33908af8971b7d71aa99093eb72729f453483fbcb08ca52af668
f29449a51c095ab2a852f5abd28aa23144d0d248076b23f602d0867e11a09515c13fe8d9ba5b3d15
eb477a9975644c499e7abf499ac73d084427ecc2a991ce6b1a3f2e4603702d226d689555460be598
731843b34124c38684f1111a2844faf1afa12282b0799370bd2548fd62f0d4fa369f8ea3286950e0
021a2119ed296ea5053f3112d9dd34b44bda58da76fb11aa3e39c54fa3815535e03d124f0ce00821
927c0efdf1004fe6d09260f8e7866c4820b983eb934da86ae07d00e847311e97f619aabd641e1f3c
5f479feccf2d88d585e2685ec634f03b0755656525ad3ce625c9d509d93377daac37361e09fd0245
56fc5be458c0b92e3d395dcc1581313399cb20bcef02c66efa7dcf627346e13c09f64d25f5af5001
2a1c2c86268e020f39fd2ef1a71f974713555b007a22be74cda2c646f41e540913ac2165c9c60382
9b2a412ed69eee269f41366815c29e5be4af285bf0d61ed9591a46ca42a9e58a153309c67d0ad3ce
ee847a0970068c68d77fcc17e20b6dab619a2f887e4b1431540918cc1ccaa95a1ee0458739a54bf6
67a13a93f4411ec2d0df610960864498b20ea450a0e12034a91a2e43175ae545ea0d9599691e8f4f
49c65fddb478fa36fc9c5b598ad1b1337235060f474ce2bcc224ae2b47aa687e3a5b1484a88956c3
3ffadfc0af98d8b68bba76ff9815aba5f2f6730969f213d954ca5a6db984570f6db750784cbf87fd
10b7365cb44f685a494eb698a8244cf0d0937f95f404a72490416400925b706079c73a6841f99af1
a7494ccfcee80a4946858d490ccf8f059aad4c86c0465696c1942acbb320f3bc52e9d70e608e1bc9
179733a452903a1ec41e91c679fa18f8e359f9fb12cda43e811e27b12890d72a042687173e19770b
cf7a1578cf0a50a1bd37c0c3564956c8a63ae3485a87c69ebadb11f145b5e95ba1cdaa13874e4aec
6b6fc3a2f3a44bff4e7cd5074e453460b694f0dcf186cf70ce52d41145c13b57b056f7e8f1fefff4
469f9dcfb408e969d781e5c069a5009d70763909b6f1c1a98a3ca9a5f4ca3749c853bd796446ba58
08683c4d3e7a94fa9a77577bb630dbd14901145a4cf856561a766b16304d3899341636b3313b5ac2
6c3ffe4b3b27f067a43ad171337cecbd3ae99c1068368929293cba308ecc308f02e8c67aa5f19318
b9e4cd4550da294cc292658e529822e389c8aa84c66c31c3c20f85627d606a76dabf5ad2478c2435
8c785c513e85773729ee39880f96e67c9071129ea8f2c5207cac0c6b481ce143aae17d6386ad2834
2dd84fc4c0eb2c07e5c6426246abd5c81de2148e8b484a40891d82bde4cb8dfc45372836a7f28624
9346f1339ad5d0d6ed96e52513c681072d705b474971c9388a3ec3cd9785c5350409fc15bc2c0025
8c6d2f0cdadfc7aea544f0106b01716eec8aba2c9ca79c081ab5d196cb218ee0f64062b4d3a31632
0bb006d07cd5f47a1f8e9cf34317e05a1d8c37bf0ddabf55674387b569b2b13c4db9b21ccc8270aa
f9329441604f6f6dc20e047793bbe1d94e2781f5b96dcdc833b6746c091cf6c2da4f754b00554591
b90a326379999728a5b67143e4a8f13e5bf07cac5196216ead37249216bfe57e5e7eb7de6fa0f85e
7cbdd21f0111a6edacdb31cc852d1b9eee5cb43a9c0956dd69161544fafcc1f66890f5fcbf1507e3
3a9b3291fc6694c4af4ac9c9212d2727849637085c49bcac92b4ad1d2f2525d25425311df81ddee4
3d2b640e8817fcd122517c94f2d67e0e24c376b8beb37a85bd1fdaa2273c8c7c088052526a21d586
fb8ed93aa04c749300ff55c0e378d94c57dd98a37c06063e14e7631549bec6733888148f395182d3
067913f057a86257b9ad1440803a73994e641b8eca54722da9ebabf168357f0a054bd1f35cf978fc
d4f954774c6eeb7eeca12f4bc67b6b81e86e8788086453ce3a561a2a50704850fe660050624eef79
251d924702a6ce5137b87201e3f16e24de5100b4e61f1a04b645dfe6d13ebfb60d4c0f201591533f
57e2092191d4d35565253938cab9322331674a8ac775eefb8a401a0354cf6c2172e7e58233a52222
74f03bf2d3c7fdc0791700faa2f9349001e27755852e5c49c558d7021555f9deaa96efde1581552c
b1f7bd2011079d411afc1fea35ee191568cf403bd07b32d544291d8b502fb2414e0c9e941c2db52a
31b2bb97a6bc887baabb84929b7df2480c7b85ee8f23efba37e18941a00e842543fe6f1ee1f2ce67
bbee1abbb635e8bc1c78cf0ba5a4c2321ca22a49b07c83f5d7bdafdc1c845a02d488605066155017
5c520ebcc39b9390b434ae9439bfc59feb92fdfe0443f2373827e6df44dd999d7b5dd7f4fb6bead8
1cca1857c515d646f92036869e8adde0864d36946a1d9c44fc1fe2c7600ce23e09b7d9837011a6af
9cce7db66dba5da77da85fe8190983cfebff3ed21a6692ecd8bd408e4368fc1c00af0fe99f84d02d
bc389584ffc0ca7da7afd98891fc816f420f7d7d0502e4eb40f17a8f3be016b9db1ec56debb41f60
71c8b39ab328b593e0369b636b915107a12dbd7fe5eb0c1de47b679a68648e6f3ce234d0add1d5cc
86bfd80cebe2e4936e6c16ffada9116cd51c285e2212f97f327491fa13025463d8e8b4f1af4b5f96
1dac886405541660618b5c5024bbeb3ea39f4d61d29c948681eda08eec42967ac700b89e425ae042
17e4a1d5df211fc7e8422298c3d183ea3367837fd2e63998aac3295c784beab1e2feafceece79b06
a7f9feb1cf8e5290c5c3126c0dceb0c3ea4873dd156d139fdd6d68f2d09c6ced8665e870adb7967a
5ddd3db669e5c304065012582dc4fc24052c4f7e3df50027d578d14b3f013420154b3f9e681f9d3d
2c1f586c5ea29620db69d1ffe7dea9fa8424a78fe364c56f425ea0e8a203d13d8acdbfebbf4c344c
1b7672acd61760350b4b95da4592990510d2545965598cda2abbefee4728d6c694375c8251cee962
65922e8f035f69c786e991c1ec234c2d1c3718f70ab0247ad10943eebae866517c287a2591a7f9f8
5c2defd18b268e040fce80cd669f2bfc6a1877de08c7323e1cf7d1be1f13b08c3ef1fffc25e1c56c
c93d7e24265596c34fd50904f2bc2a1ec9dd0c7de28138c062e62a0eab55447f63cb75624be61919
a726dbe86753d7b612b3118d250cf167bd2464119f2ff41bb58292b9e786c0765bb715535561c6ce
403b23caa0372b43c91b861b7482bae271276b31401c929fb4f28728cec60c7d549056521ad47689
1825434df82c9f286a5bbe3f1e84cb5840c62243c9de9b3400a34b654892d6036f449644938ae604
f115e3fdf1fb9c04573b7db1f7f0128daafcf0e04d75b4da499aab4ac1cfc5ec18728ccaf207cab3
144b9a6bae0462bc1656da08efefb8b955affdbcb71c23ca216e31ac02d11aed3f4394ba96cc4631
8af2cb71b61150087c23504ca08d1e2620373fa9353a18dd42adb54efee3cdd4de915ee81e268b9c
d29ada0c858ac2f8c8f429d76ba1d585b15acea9e17a840f0d104f6f9f6f5409fd01a25c61cdfe9e
a393d942f35cbe8b97c10c8139a2c16cb078998602dc3f48828287d36c1239f7fb711598eca14cbe
a8e08c4b92147d22392f979f99b119c6fff99a0e09f4c1bdcc403d7812c14330141d7146c239eb10
a16cac9d8df328a75589c9fcafec74bc65bf9cc2812d715973eb47f2851288bb61a66e040561c885
60c156711f2daffcbc6ee60a914961c51fb4a8085fada068d6879084aabdda7d8ef0707922eeb105
b9772a37dbc2d6bf55fad755bcf8896d34a6f9177ef6d88a8aa0f3362e6210e49d3bf3af12a0bed4
a43c7aacc416d5005d9f71d9b8a710d8b3e8bb51ef2a971d211bea87ae1f3128c5284f2d52ee2dce
d514146ae080890461254b8fed3a2c08473bfef7ec5e4e7c623ea8de812c3116448578252b5c5861
acb8f99182cfb014914db4a58742d7f0cde06c08edb7dd595c69e3a054a9c6cc4d90905457f74865
16137182629ab45ef1f747e86027e0a72ffcbd8207e4c89143d0d94624109494f3c660bad9f07d64
534cab099456e79ea28111b4fbc853658158ff14e0faf4e761ac6214bd793d5d3974f2521791b57f
dcd63a4aff02f59df8321d6ecd880e7d2b4e1a604a85b42e931bddd3c695531fb2322812ce87e86a
2339f2523bb79dfecc24f00fa71d9ad557186cc02b1e30d551d6bf3508104b2bff827f0b189a3c56
c2b3c1bd266ddff4808bfe1b622acf2f6e66fa8c215d137129dc810b01d13d528b081f09ca129a75
3d237d01ec4058bbfee6f5ea5b072b60dbb1986e54301f636116b611ea0a02e2a67652b4357c9c33
b92772bdd19a74d2475045579a262c888882f1678fdc51a263bd1c85a7f12e7468eaebcd1ed25d5c
8ece48074f513948bca0fba17026e3f81f44141392d1bff3353cefcb9d5e4cff2ac989625c6fc4c1
eba49a199164d28d67fe574f6f90b8d2ca18abf968607ee44f92259377c4bb0cbc412d27e055d5d0
9feb9ea3dccc4c83470c128c3b863f67dfb361d8cc8a90b676b12c0e13c75bc9d604b5e8befda5b4
5473acb81d84d16a8acb344abe75ec61423d8fef8d579240a97368a6b2768b3f3d5f6639e7bda857
ed8c846aa88b7ecdd0ba97d2a65cfbe7b813c85a8a0dc96aa7946e0a844f87e406bb53328282628b
f69f8cb3c3e825930251397bef91b402d72151f808a2c67bf005633fd34775b0618190601e364144
0e2fde0f14289933ee7244b9d2d1ff57435aa9a1303772ff69aea719d1aff427939669e36dde188a
764d31641b8f302db0dc5255a956196699a7ee2fe2230b8dfca54c375b8291b5af39b96239c7a192
ca3bb62e53b604b869a17c1c8cc01bfe67d5a57b16e05ed5ed32ca1e172459821248de1a09f38fae
35d6003e89952348fdc4cccb373aa83b4481402b84129511357148226123b3b04236c00759ca0a24
97e56dc0ff4ccd96f526649c41ecefcca237c2da2177a4da29b520242f402cc3fcc0d287f5a49da5
e118e6b902afb3b171ded70bdc0d66e70286fc14810eec055295dc6383e208865f6a6100baa0632f
7eaa67781ded4b40e0ee71c769a5145b486979adb6d7c0c890e056b42319be8d6107aaadf1e1a44c
dc64c6aaeb9d5d01cd6de14a3b5f7d51f95a0265d097630a3354d8f6bdb64fc80e5cad3ed3d753cf
8f93f3c90ab3d6f4658c3a4e6134b22ae8fab10307d8696ea8593068b658ad3943108daf1c6e22a0
929c495ff361ffd635dc236be4078853aaa3ea4e78a9c2e08c6233113d87938b5a74a728396b7efb
21e1037fbc8e99a6b3bfde70cdb8c1cbaced4e88a47706f6ab485aeda9e9bfb348a783951f003161
ab42f89a85a655562ca0b3e9e69d5c647ac5e8c985ba2cc2913568eb72cf92e60f6e8539e9567fcf
9ffd6b8a3fbab22449106af756f138c759de0ec82602625035cd6cccf37e6b0f35bb9b00d66d129a
d9e6f50aa68ddc2925e784fad09a9d0a627083e6694b84afcb64b6ad9e3b5b16d6e24f86c0ed14d2
3ce815d690b44f24eb76580550a70438e548486767e2850774c60cd8a78f7937e764f8418da288d5
5fab3eccd1ff9d501443898f1d42bc71d09b692e568404a1cc5323b19b8e3cde0b41112529bf0fe4
aa3c957dead1b67cacddecda591be05fa527866545998cf23cdb3a007186398d03638af715a63ae8
c040e045fd8b8b0531b4a443ccd7bcf6474fa253e46f1631afea2448afa3843d1e20f8f35484e386
fe541b8d9d72cfaa66c7edc827a81370210e49279eb57b30e25561e70a4ea73ef5f0449a3cf80627
a6a4188b07cbf7ec218eefae345993ba9e331411f7aefc68a2fa92c2c72eb4f5afb1b4ef355c55a2
44dd8b751bcaf0d38516622582132e74f51454a3867ba42cfbd0917d2fd7ef3a27ca90f91f4d61d7
b530b23d1f0a2a0e4d2f9560e5ec95f7c4bd32274d212cfbcee7a7bd864ff993bcb3f8b008cf5570
3b632d2bdfa801e055b0f08641432fb022c2d00c7efda370fd335a76f8b888af6eea89dfc992fa98
f1b628d94b2c2acbe26bec5fd9b8dda7c5bdeee6f9530d3804fdfa9341998657bbc80a9a4377a501
e28d3329f064fef41cd9d5bb26173dd5f3d16db3df35b651e5d79f6d0964a662c707334fa6a532ad
c874ff15ac0e0434804518168110e779aa581e7a4a99eab28b04efb0bc7d1159bbf21262e18c938f
0daa0d2dcc059bece1ffdd104f237e60aa95cbdb3640e5762d4c993b21a6abd19cb93cd4bf98fe11
5bbe525e96aa9aeb6cb308e101903f17f411da610c6d7f600248a559490cc9967c4e4969572b559e
b287fb6cfcbb7d19ec66369ce5c497b5bb3f7ece1b8cf935d1bb771e0eacd3173ebf772216937233
55d5ecfb9075a07f67ef3cccd41877d9ce876b5f7906ed732ed3bb6cfd1e356d7f018529621c02a5
e9b94afc1e40e3272fb411653e35ee27ee1cacab46cd87a95fb43f0e96b7a2995817bd1f6c63934b
974f7215e2ea6f2ee824a1132e729e367552260a1f991108b1a47dc54689e3de92c0d4d8941431e3
5915120a83e417951077ef51235f6104d5f8d4ff40c51f727757d76f1e1c4e98360703d0e8653989
accdd6892ac26a47f742af5c39a51dedcc100a1ced79ef15ecaa49c9b8daa014143705ce8f0798c8
91cdfaf54d2a79203a5d4b2edfa87dbe117a0c749d260a0b98f5d5ca6b41b2d54439d76d4d9f447f
6d78c09fe229ea94ffcbe5c1a75cabc0b8a171fec524055815eb36c17bf9d1dd4863b3651ebb0275
44b843682e2e59d7ae3e88d4f8034852d8a6210d69b3e5696c649fa1c15a1babf63997755fdcd37b
44061fa559374498b5ea876c56fca0f8e452820f2c725677f24cfcba757dc395cd272d5543262d59
7e17bfc1e2e1390a1222efb132b0ee44acb81ac9c346c3777787c89eacdd9ba239d2e18a1ddc604f
fdbb2daefe8b17fdfe55af182a2c0e2cfa440c08e05afe74ff977169cd396ea5ca9c9ccfe1c8984c
68ba4e57b68caf80913959ebfbaa3f1178b2e98c9d3158e54ca5fb68c04471202f62696423b4c65e
1230f2886f1d7912dbe5dda2fb928e86b403b16b3bbfd0d0e63ccf02d290a8562d4184973c66c965
01341098a27fbc9fa1baae8d9dccc35e6f924a71ed2e9c8750b0555eee883a84b6410bc866526010
b981402f1c28c4239cf3e1133567751ac58c3a017f0cf647af47b6a3b73662c4252c1c6239241cd4
12219ae1a2aa7c4bedb690366dec23a08c959f1406cbfd0087d716c7e3c6b7b200149b9a7c57c031
848435a14f153a5baee7789b730f5b6f83f804bf3f1e654b2bce47d0cdbd734b38e6ce19eea56215
db83e8efb4f64cbd0b4f19d937bf414ee31b5f3287120dba2a7404abdbb949483f97b28896d7966c
fadc436a9b132d10adb2a187a3e608eb9d2ffcedb6b1b7b3426d3735b8f60762604e44dfa9d137d6
ec75f3e5eff33c7d9738669ad39f9b8ec2080209c3d3a545475a0108cb6c7a07f2ee0e1ae05ceba2
d82576bf25618aad9aa43654e6333f564c6dbca41e7c95d35bd289441b3f409556bfef45ce8230f5
107a5f9fa73e257c8b436494c7dc5387fea2c22849aa8427863800934e9ab4cf426411d86fa3d2c1
a6c3027bc7d343a84f23494e2c3a2839ab3958f506f6bf538b13d6c34be603f3fea401d45bf74ad9
98898cc3da1854aed25850e8d2b6b380df71f9aebaeeef1bce6f00b4fcdd41deb7a994d7b89700c9
3cd4e42771608c6676379f598a9c0292feade77cb2a6969d7c59013bd396a3fc6a33b1431d4ef635
1e652c6636b7d29ed02656ac46001d9666fa77c93a426897c0dc5e153c90718c32968318e2eb490d
f5e70b0f33b2a666a15e51681d879aaf9dc222a8e85672452d74c2f173406e9f34dc7b30162d58b9
c30439069cfcf2d7d22b2d24867924d07caafabe5859dd72bbdd7ed7128bf823784245128725d2eb
70dfef9f33921e2ef7a0619940ec9628160e08698a1380bde9f997971a0bbf83c2035823e3099d6d
979ddd6398c08db58ff74406dee2d69102950ecfc7ba9fd2c0bbbdd704279b950b3020dec3f8a2ba
72317414c5848c449b43a74019114271f4cbaac86112439a8bd13a64e6bd51b91038095ee20bcfc8
e5f36ddf7550a340fc75405c46f97c4194fe462b256212e247abf69951147447c00db225fefebd75
c3bf9534af3058b9b2d3eed40763a81e1318b8b77fa0abf342438ff213f89bc6cf77b773b736c8a7
f1049375745e68dc06d108acbc817966a8a4e2d4b2f468d5a36eeb6d3588fdf82a8141347ae5a857
abe3be4783ceff5b58d2a005f704de97cb0dc0942ce426066cabca864bc678f36fd98c3a661faea7
b1c53e0ef1201fe2121e3d3a59b6f9b98fee4dcc5b8e7adaa28bdc476a76c01135d15804e5bab84b
c736ecf30ad2f2bc831e960007936d0a6deb48538a611bc0f759e6a3d8c5934a9355d4f17744b534
e43b674f7fa1bb0c42aac569ee11a15fe52a315ff180e74f4a4749fffc9d4f07400b3be4cecbdfc3
67006c4494302b8c2d9e2f64dbb85d16ea2883f3ce7785138e581bebd2d3a6360582a0b8d2a6bfd7
2211f88249735c59bd176856b08c5d8593100cbe78d524abdffeaceba8f7ed817119281796229ff1
82f0f44cd02aaa2e4f98c13e85ea9de8403323ddae17084e77fbbe496cf9b36911ad6a028c94f597
39c5d02fdce649ff1b2c586982b2d2e09ff673e3e8a230d49b40d008706607930252bd3cf6be63cb
24f3283d42befe1bf114f351bd2d937f3681780c6b01619975c39c7419f63dc5b9cb3c359ec22f72
37c62ff14b617fae2401c3407eb2b7f552074ed7e9ab96fe266f5b2ec1a4c607d9ed85316361f9b4
c9892d2a72cb7b90d34e7e4ff727ac85ba30fea3af9917e794facd44a92678e1418028571d1ae52c
603856f1bfa07dbe4442afa98521fcee19be0a42bd90b26176253417f51c53d5d03020488d461592
cf34ec571c55bfc5bf4d2429fdf36ba40d51fc7edfae1506
服务端第二步输出:
6e9c5645343068fdf5dc641993bf1234dd16ba2adb3e3dd4ff61ac50215b996558e91fd65398fb19
abd1947bedfe529b3750fe6b04707facdff7320d9874bd48baf4d5beffeab810af6b622b1d30443c
85eff2e2175c853810d282d9715aacdccaa8dfa883e024e59c8a489c090cd0746da1245063b137b8
b8a737456b162864e74c6e7eeb66ac88f2f1596eb52309a3da1776f2365e1340641fcbaaf2b9273b
6b0e10ecb966d3d732841e7b3ea8bdfe5a4375d5a62eaa383ba0d13b6a9a82a1ea9219fd669248a7
8486e87a072fab910489bfaac15776cd4c2ef90dc7e819c2b1e33617eeccadff1a47b49dcebc03b1
c8a84df6fbdf6005de4a47f0d7e88b66c4513605fad7d6ce38dc46399dd9bee7b8fdf5ba7dbe1452
95beafebfac5fd9b454f8fc1e73a21cfd3dc1b7422af9f0459bcd55690f01449c389e30f62cecbd5
f4a79541359df5fbd373fb242fa61d63298624ff1b7e870c217299a0bac1e8752d0d0bfd5e64bcaf
3f3525171265c59b989ad5b974aec972d89fc5cc22655e56ef4d4c27c1dab42aa654e2b8b98b42d1
f227a0f80063c901901ee25485f8359c13d9cbcbafc9381cf22f81ad50a0ac2941639599055fc06c
6c8254d47ac9682822dcf0674d38cf07b272f86b7bb73dd1c05a0d54b71056719939be1ac62de5f8
26d3b2dd79a2597e2fee51cca011b3bd2cbb3dad7cd00563f46daafd0d180e5c8e7bf066d7b5decd
665715c1f023c0c676ec9e4571d0900a82bf0b2943b75fa7c88c7bc73383455088a87c261df8703f
58cb53a2eba4e701441c5026aae0c2e4689c2eeb9a9bacadd3b2c29ed03f8d0bc059b7e2f51ad6a6
35ebe830be794d237c5c9e70ce4c2b3df8f9e71fb0b3b2a3e69d88c021fb20f3969aba84c603deb0
53c3b1a81c0b8b77c9e4b51dd032d72b42cf0def4aff2ef03105294820b926b05f4f811cd97f8432
247f1334d4576752d5a4c28f966d03dc3e10db6a79ea5d57235fb680c46809b6bcb37c72722e6a49
524d62a994564df27c8a9f91369141b244cc484326ca89cf3acc9ad067aeb7ed420826ddd4447c19
d0cf1a0f7712145467a836b827b0353fbf73b379d2268b5ca03f41d4a490b0233dcd6ffed76aa902
688261e2745b1d18af0fb30ffdde79f815f3015260c758902b224036665003feb5961e987daeaeca
2b0d417a012fa1e48acf5ae25239ff90a9dab35d967b9ba139e387317e6fe4e378d549f0f687e9cc
60f7ffa3014669d0046de1a0452bdf283fa945ef7bb9b0d0ccc58646e9dc4979ba6b1399ba3a4efe
a94b4c003ec1da2316eafd878dd7ac5c06ba7c98a865f967351014a935c9e856b79d40989c249acf
f9dd19cbbeec8c199c086500c21ac1f862a790fa23ada6ea8034a1c6c6b0a7e39960ab26906c61f8
21260e66684f2259739c6e423ac50a8212e6f13c2d4fab724e19247675b1ca1238c9cd1006ec94e7
67412e5c408b576e79757c4be7229c8b8b8482d7890f6a9fd1ab65eaa1521d3bd98438b112270ec4
fd134f39fd77839eb4a8abf244091b0207da4cc687e0b252e05250cc0f2ed7f33fe890bdc2831b37
2a9c7f6359cf5db99eea4855929d03a51bb05f9294c199ebdeb8c9c59be3d0d9dad320ac39406b59
60a7bde842b32f1628dde80741468fc909aa74417be3e98205e14fe9951e36614184b10f71ae63aa
b150eaf8f8a0512ff991a5482a6d870f62c6f09da746f09f132d0d363633b6e573ce143a15003cda
e4f80ff81048d8dc8e109ccb3c6225d8a7cda39762514dd35c6f5a45e003cdbe3e6eeadef7f028f8
171220a94aedb0c12f072bf28ea0cb02152f49836a9bd35b8fb508aace2fd6d922bb105e77580a36
56b473de029cd35491e4e589996f859c632f4e938de80b2fa384649e27016fe44ee49dd23ad044c0
466372f6d529383480cef033c8b48759bc4ef82a078451a298b3221c0e2bda2e879238e6aff56d0f
84b310cf6d9fc6b3604453f05fcf971fab91505d69bb79ef2969d228d00a18f9921c7876095f009a
1508f39412dfa7ed5e88109d638a9afd301c16e5001a5ecab3524ade7334a9a3297459fbb39ba6c4
5208daf3335ee2a4d8c6785d9af09d5823657b751373383333caa7ebd685f3773327840fbe3f763c
90bda9420d9409c840da8cd8fcdaf521c4001dbe5f70cb238c690fecfd6c87ee9e150a8c82fbc823
7783bf967395ad7ec9e7c9ba67802352755d6c9361be9c402668cbd24e2fb174a7b9a9a964186ecf
9afb4ce85b9ef95460f07431779636fabb4d7cb91b32f417aedc6cc73d88d8385d148ec187847ddd
331ff68d6b349484e5123aacb725de7be0b017bcf8d9fb9f84d0d66a1debbb1671fbc2c9f6410990
09107a7307b221ae141711afde7d4292418a00be66579a2e497ac46a2e5452471176031eb0b0d760
c4c72aea86400f6f81a1b088cc700dbabe35adeb660b36041b3f52e4da5e3e11e7671603432de666
e53c674d8eed6d87274f9c6f35ce400f5ff06b847f8f995a321db2253d53edf2a048716845b6c55f
e5f58bc2c949240f4d3c72ee9931a0893fc10f3c239d45be01dfccc9dbaef46436f2130d90a49660
f7675f879aeca55bd95fa46e5d92b18816f1ceec0aaec092074c66c19a3e4ba9d0f0e2ffc2489cb9
1bd105096be576eda24939781c523a138ece5bd74a7e37223e207d32ac2064c72b55c9b8172b515c
b137db904290fab30b5f8435a85f35d3c8ff9bc15b52ca89c8390a8faf6978aae25ecaee1925bda1
906d8b2ba710e8c0a7c4e637a43c8606aaf353817234541b6bead74926949234d537d0ed60dbf783
5271eb6651371d8b1def350d965e638bdbd0cf0d15b30070f48e19c48c2049cb6ac0b17843b8241e
9aa2d8bfc422ea2dcf31f00c172ab4d6abe6fed51ba5a8609f8c1e960076cdea302da268bc6db47e
9bbd41344bc3aaf68b7d4fb8f7b25c130b61976dd923c63f03c583513b38f564f307b1f1d1d0155f
3560adbc2c033b0791138336786b852be472e1404d06cc09b2759095edb7254e6ce3e255e04d278e
ee7af5602af52dc49094dfc31474d64edb237f623b608efc9aede080600431480227d8edfd34806f
5440084aa472f98051e2a81f1d1202b30d818a063af4faf8605a6d2a22af4ee585b21773bb69c7b0
1ad6f0194264c1016fe6a0f3251b4a4a45a94027a14529a7cb81e191cf5801c57ad0892f33fc45bb
8d061947bb52b73be761da4c0202883ac2ecd8c0a2d8e016a339bb8807cf2ac6d512806bd2901b93
d2379ef20ca4c29f0000d30baa59e2aec98eee32b347d62f8f2e3475ffa49fa16d017d90514f3689
ce7a55b739fa4c9605fd7097880cbe6b0e2bf6c26b10671fae3bcb507c27057628b6b5ae3e867614
e709822794671ced3755d906c218d8d51772ed207bbae5071ee20dfdb2ba4741ccb14ae78ead4e3d
ebfb1c228456d1f8921ec5f855832678bc2f6307c5ba83a58c0752df5d3357d3661539c0f9f9e717
19a3afc2bc8352685b1413988c6526fc90e8b6656b86a4b3113070cd374f6d4e90a5630a25af3b47
e6d9341c07b0cc2ad1842e6b0f209dea122350aa1587336f38b545cf3fcf6234c58c8a15d6a98e3d
260a5a64b4ebf2fdfdd486cc482d815e17c732f7efc411943d7a17c8e750c2dc3200be3dd6fbdb1e
d46588fba991c1007f801528374c75f031f8702bda197155f1ad96ff2954e574988525bc1a7d67ee
cd0c7c675a885814ce9cb65527e1861f70eab885bd35bc23ba5d6397e64917ea1529c6071fc34db9
4c0023ec9d3a83b2f3f728bb8b8a04877325f267139df9f43377cb2cd103eead5805db8cc827550c
fa66b97db1d73db63848498c77a6f242b9fb2bc1dd579613add5e98cc387bb11b2476c33c3bc1f9f
93e41c958f8bfb38bce598f6d04f0cbc45e04455e69afff5b1783298c7bb860d7acbb48ece908874
7822d95cbbf1cbfab2e4b2801c210845afe37fc8a03819f34ed17131d5842733399886d53ec16045
1cc3b935b5904467faa7482abdb345d24d59e32af8b815d8876f012f06f4524547b38f2ac062a522
afd3a094306faf0b0b77f5f619e8e3935d814c40af5a58556e796dd843b5c40c63b90e1d95306dd6
6c25f5244f746fcc056c2d4c02289a8de483b34838baa7dfb970e6a8a2a4ca5c52f858ac5da1b73e
6f9b40493f9b736654e7fde3683f25dc85518ab959c742a31fd92a8e8815099d28f0663e1d1065f6
1a334b154cec940b4b5122206cf5d8e8167d1a6264de831e321fc8eea0cd7d5bd08c896f878675ee
4cefe46cb56d73f60913b2c68c45adc576f9abdfa15b8928ca630aea2bf9879f569325044f63222c
5e7eaeafbb6537127117d5039899f71f74bfb911cc2351b257a3afca3fb6021e17ea2941ee006f04
70cb87e12a06edb525858616e4fb573b64f77efad75dda811b1a5fcb3305829eac8d356d1c2327a9
b2ff9c0c06f729e6596e253fda946f67530d055d5b11a8fd1ad9f0ae87913ba2acd56e28b6a4377a
de325ff0b000e62615aa1938a80c1255ac504d67c405cfdf76efb035a0133f9bccc5caca3237f821
66b9f829f07dc89b4a6646db82b7332c8f50c4413e83f989746200e28e242236d5c1e778d2f976f5
2dc7ead5b8673bff683d72c0011c9447934ec4a622e90c3a63cb819f0e95bbe4e48f605659213291
f0e927ba25b322ce25f6f2d2da5d8681823d6a3a450634c033df9e390f07e12801eaa5a1e89b0f86
9982dd98c94b457ec73ac544204edcc8dbfc04e942127661ce96eeadbd88a64c97e07ae7fe63f4da
3c77f63be97fd6445ba0b80bfe52b6e4094a6928ecc7588351dc2eccf05d05e436982e3d4e4d1e9b
844c6517faa2fd2270e17c9ba3d71b58626df29bf876f07421175deb68a6518b9b77b596bba3f8d1
dadd9b0c92023d2267d0c1bd5bcfc4015c27324f3435979cecf5d71654be03ef6ca6f1a85174ce84
2ff75982d03447c92f2f57c28321a3e6096367f035bda191ba5512dd34133177da95efe60b12e4b8
79b63e7c4010f9583e8cbc23e64dd572f706876ef4275729a0d6b6022807316285d3d16bdd721ad1
21ce72c6e59e0ce4b1dff05f5a1d8d520736a8b3a8f524b0270c4ff03bfd30599fe401731ab9369d
ec7cedaa499b92698368655e1e749f665f9b2ce9982ca1635205973956b4870cb25c92061e219032
443afe4958844e5aabfdcae94f16bb04b23db6563a150524dde2fd95c0b330006f2167d3f0566bdb
a348e1c522c69b0577cf7a323649c159231b3f45e20a32df61cc28ddb07ca3c2d14f8cdcfd3f9784
04b0f2492bfb6d5f9a0acbdd8150c0befd8ba01bfd0469bb1cfbe3976cd34a2e6534d61dbfc8164b
8f9f37a2e93f8adc39138419492294983ca0d7b65b0e7f40a6bd199eb035c803b47a81e7ccf501f4
5a59d0ba71b99fb94cfce67fab27141da66e2cf9c2d5360816ec8101426d1ead68c686ed9ee1d8a0
af83cb3462046c5872ebd4f93056036525e6bd9ee8424201d9a62d6816d1244cae8ddade586fb5b2
915cb878b49e77ab58bcc59c930eb3403d2407b7e36f5158d650ccecabbfee38e7d0a7e7ba3768a4
3168156523d89da05607e284ac3371498f4dade330160f323d6d7849f0b132ea2edbf195a41c9504
1183605575bb21a5151c6bfe1294486be3f22835a916a84b10e42cdc05fc7b01920b20e1d124695a
a6ee412b129203db704cd9b24ea81af6e9b53698203e385af60ea0badc1f111bd68348e72c2f5b25
db423c16c0468cc86e9112ab37988d8d31e8807b368d6f9e565223e70b8ce01f9067e9bfd0d5406a
25e43ae9e9eecf12d9bf43b4519975652d0e83fc1af032c8549b4de24842dc54a10b65acc662074d
31b223f00c1eff1e2e78421703ad58fed4e4abe070b8acd2aa67afe77bfb8b0cc4f81ee2acc7784d
2037b736029e75566a4bcbc28c41b9345cfbfa4cb97c1b5b2a61fd7d42d9799185cf401781b1e4d5
df0a270501903d7777325f03fd01b67f3e107525e68a9147c9016fcb6256bf6c3d5cccb9a4b12837
0850f8eeb05b9b100673b64abc79f5c62b2eba1c41719e8faac2b579561c20f03ffb2c565978e751
4025b214f0f5a8bcc11ac8741c8e669202791f0e86fec8ee3a77b745213e6bf4b7bfee8a8887cd53
8e1207c24816262c79283c86acfc6c9e04abb4dcfc2131f538824dac2825010de27ed863fa7c519b
e33127f4afe43bc5205071c5ac7b297165d779f1bfdd2d04ae73e299421b41f4d292a2d28921a13a
09f9810cf7fb11b3d9e4fdde3f6afd0a5f5fd4f37abfbd8a06acbb12caf9229a51bd3973b1bb0962
364372a1631b7ded09c9c62b5f070b87dc23354ee15e95a365efcceba23d5e6f5f4aaacc7c6d8969
bd94d24cf6917f913b7aa66a7f3f7dd867b958bbf959b66791395590192e0c23dcb651e0bb2648ce
678f51ec07c2001a1edaaa168e0ed5010bdbcca1986b715c6bbe1bbb14d2f1cac4c605046486b119
a0b61c20075458b2f70e82e864ef7f2ca45d9b61f36c1d9a0f8ff6e8dfc05d1571133a16eaef239a
629e424a24830ec6026a4e7e9f8823ca02d2e76340a0dff6bc5c32644a4884b5278257b0fde4a6de
588730e16aaa5a9cc30e6f123e393d4e1b3db0f5c33669ad77a5591d431d71ccf29b972aa1b13f57
83c7704feba2a5de453c9b8c3fa9936fff3928b88138c50bd77f2e3c073e0899adea273f17110615
233bbab5d05f8238b0b018c0f56ddaf0bc766b594e8412cc2f72594f48dfd3a06536bff953f70eff
fc08627508e9cc8cea199c167b7b9e201ae96a19391c2fb88d6aadac9daa33937a9743fb92ca623c
b59fb4f4275d30d3ccfcb1f4513e397bff9f5985ac8efc7213df69d4d47a1ce298f395bae570cabc
b1abd8060a389649f1e214203768b19552241ffa89432b5fb9c569332c89948200923eb199a5307a
0060c40d21059358cf63ab0d507607313460b51b27f45ba4de9713c4ba532ef4ffea3507306251c2
3188b75077d1235e02fc8d99243dcd547d358a3dd6cfee079142467aaba5c2e1e906a2b5997d1c9d
df5f1d01c1c88c7b963a6647517465a064927d0cde532e73eed6c1575023d10318c513aefd09ae93
33c57bd4f864323ee205520964510ab8c6ae27a0f074b322fc205c8c3ffe931249e64038a367d04f
3b30daf98128c516dd9a5abfbd90974e85faa82977bcb67e27d2068371dad5a0736c0b2b7b3a2568
a35ea11a40685f4a696433c59ce873567c8d4fdacede81ca85af48a6ef1c8fffca213c5efb65b48b
d76cebd9ace1beed127ad8584c2876f9463cfdeafd7c57d4485677894f72e0d53118904b06f9d20b
cb696604df486f0d931c1ea255edcff43ba8e47e9bd04eeeab56fcd91bf7b84673d328f1b3785f96
e731880d0cea0d3763a92b9899ea5040bb45581781e2382593fde3bf1716268768c6753301c5b45a
8039a4e7a20e27ead122d045c18e21075612b48a94463570766ebd30d1311ae11f94fd89a75598ec
957ca070931eb91f50debf4544c9de4c35a4c0734c8cbfbaf6819982a7c34020b371a97be068f93d
3e599d88f0bb6f69af713e41b4fa46b5868bad5673dd56c7d25a48b68159d4a880b0b4d7249ed957
374f5c5e11b7ecbfaf611fdbd49071db788a22eb4bfce1ea62c8493024cd3b6e1e53e5da20f2faca
90ac7f050280f3e670d0e0c08eef949cd115a5a6e047fe59a172acd56ed6675675cf6f2377007a1f
54f0aab2fcf45e431161d0e53061e1983fe02d79a918f610799dc29e0a0be4c4ce4d72c728414956
9978886f08ab78afa387fc316e3610a626dbbca49a3170970b7000e00d78771de79ec5d288b0cf4b
57a27f9cbf5c8ae603d9e8e92c6b924a3ef8924f7446e19214391a93db9ab7dc3f64be382c781347
4857442b29a20ac7689816ff53f6404765ff306d5d7e1881d20326706afbd2c37839638f83c2bcca
e58ef7f09ed9d3049a6ce6a98fcdd2e97c8d579929714fa3386febad50765d4affd3f691e3601725
d4f4a44a482729c79889125cb03025b6526c2d2990b82016043a94320b0edf72e338de8428993664
81a1ce20aefb17ce416f8edf8dde465f6cfb45eeceb63797a63c9cab45f4f06dd2129c8e845711c8
90b0926efb90c8bbdc4896e001a365f6ca8fb7e52e863d26a8f8dd54dbb10a45d6d76201dc8588d8
ff03ff5e0d112466f400cd2d1a16dbb7df9851b3017608e4ff71d7e672835856b0566e44183e49cc
821eaad0736648b19cc2818f6cc36aafba3bb4483e534ca46b8370ca4e5e02cffe854ad989f74f88
6f00ae042481256624932fe1fafa0ff71f8ac94697025daf50e0fd76a5b6a7661badcb047ee83cb9
bddedf6b6f38d01ddb9d84e3ef6e398e2ed2426e726dc1865772364d12de18bb8e2bffae6a07bf35
e4ad10757bd9a17497bc043f132122509f56358443f28130283006472002e98cd44fdb0a024cc18b
bc6eaa03f1259a9ba47d7cd2c03e5f2f1e474a0b428cb488d526a3e5744ceab2ee19d77ac40b48f5
d3d3b3f96114e518b3256f60c5f1b97a440c8b06443610c0b94e6cb906c66e7f4f91336bd5eb2241
bea0155363c389f03f94cd264f2ba6cd7cac7063fa260e9d4f628c0750d666eb03627782f62031c9
a01cf0226424e989ade64adda37c913991e9a9367fcc2aa865fc4c8da8ee205dddfd934d00d9777a
7058cc85feaa4185703c68ed79b16d0ddee88bf268dd5894d0b3470bd64f19940ee3fbe80a4157d0
b4e1b2bda9cb5183423810a842ceba114b0ab7cdcc58cc7c8313a68dc12abe76bc68afa97bfa18d2
ebc5a54e2c697ef410bddbe5e390453b3d4584d6bdeb85ed25337be6753ee105b5495fd36329dfe7
cf454a8f44a818e6984e5661c195be9923f75887bf0728b4e239def29e3740a58e7b03418d749fdf
c5d399b8ce88c3e3fb08e77d34230f26bf8e09a0a59d7102fa74c62cd40b2304788c9149fa1d7b9b
5b6ad7580c38dbfd8adabc1731c3a0e3c54793a885293805086f8c03298f7a2a02916a1fd7327858
ab4b905c44e5c165a946dee139205e837e468128e3d688bc1b159fb84e3d46c1ba705b5e094d05e3
54d70c6ce2b6fa34c4acfc24a16446004a5a4439ab7d8a12689a2d9855315f56952af0cc58934c5b
9dcefa1e64ed727528f3a88ebac83e53cf8b2f2e371040acae6f6b13b234b6f12e5c21554c503d95
6baf1bd8bde6f6c124311fb141f10cb546ace750a459dacc5aae46de78d2cd3c70d96dcc8b4c1111
38cb8bd1183472eb892f0fcae34e8b48713cc92ba534a31dadc3c5292013b3b11a29db6a6f38de4e
91afe09578fa0aa9eb82fc039ddd3d32ffe924d42e5ae8909f898339be449d58f69f637410b69c39
44072ba42389062d1dd967719a6b66a27c10f298ba658298c3c0e584c29b4cc9f1df6ae2d382c952
5cde35c972369cd4161393f4ac7a03a00cc0279b4c99708eb8f946b09f2a54c36a73caf4829ccd91
2d8f15a9fbfa889bcbe4a936de4abca875961e2cd88c3bd3a628910dcf7407495a2e994223dfb5b5
d1773ebb9ff52e6fa68bef221c48652a7156950cf07539920125e8951390c79e47c3cc07ebb8cee9
c224be3685b3f582e257a98493aefeff5a32e0068e55c6c1f3cb04809b02995306653da33ffa25f7
080e3aa4669e0669b1234e8886c0a057194ce7214e9bd025f22e7103e6088a16f80599f41f1614c5
c8caf6393503b1ed8e6726c4877d3628867e3aee4bf20f5ffd6e7ad8b797e489a23d5b668af170f6
09e0a36093100fee7315e31d7df8ea86853e9c2dc229df0014ab7f148143cf237550c00e3e7d5ddd
a10aba0dc0e5d7e52ec40543c5eb452aeda97eb860f7feca4318ce98212be80480bd57db88bde156
13085bb18aef051a825cf23f3d5eda5f7b1a35e96448fcfab5c8268111f40af08024050179f4f879
bc1b5e8907a4525e6e82665b68a9c50d929e0035496e2efc53ebb9bd69da03d8f78fd970d176fafd
0e2394cb3c3432eff483fcf13b03eaf6c6cc2615aaa47d59c5fef2f94c30e08d382cd7bca070daf9
be8142b71125e6082b179f6c4b8f1290f63b4ab20ca0e89017742fc2232e8399b895e6f59964bc03
911b402d5e6b8b375d28edeecf606fae7d125f3182dd011e97dbd9700b21ac80514cf741a3fa69ff
2d56b348e08affbb8b5275187995fcd0e7dd95fb4d4357b71546ca0f69c150246230ed62946df402
59fe80e1ce0580daee437280f7a8ad1d8376f9341fab61c2802a08ab6fab7e40c4766a34a0996b8a
fdd069c507110d51c5baa924e3b303dc27520b53f420ea5b23bee56b077b4c306390e74f8c83eae6
d8fa43ac60c060592277e00185853f1ab0b309ac4c7fb41d392835bfc74abf7b54cb4955f22ef5f2
bf7a9a7f304dae6a4d051228f6b4ad2ff3e543b7c38f5d599d9c66f4d14e01f45cfc0b0993319951
be89708bfa152cdcbdb2f6673664b33fb19cca412f040f1c4740000d9e9b746d53876d16c696bfa9
d48da7d3a8c047f0f7820d40d01eba42213213953c66b092760e99647b27935a2f04fce019f4825b
cd42553909de7fc038212d49f6c4d8e737142ae39629b9f6f0a7f41ee7debed4bc9cd91e727a400a
108988284246ecc2033761abb398056d38fdf82cb937508bc8702ec407f156e3ea62eeb4c3ac57ae
0d32235427e14054e91b84c681453b8e6e66e1716ed5464d57d6e62bb4b14980570db8ed9826f1b5
c3038383883d58183fcba6fdda48038293078e1321403c18c9e10046a4778153867db67f70ff7fdc
d004b95044e2ced01f87824342696afdd75830b4ae0da598df2423da9bee00d97632aacc7b0aad46
3b27aa699f0ac327a3e585b397257723db108cbd089bf6477a3e17ab2da850963e7dd1c11ac3564a
7ba2dc60865f6de4ab8162e4c0edafe52ba53d46fcc62ee4dd28d34afc1b3eb0bd4dbc5a1b1b3d16
fa8898a03d3776087b3a8adb3468df075eee5e823b4ef199c5f16515a3a07931281073be34cb85f7
8cdd64e03fef568276b1ab5f19eb88c73d110a613b95e5cefbefbeff089d6c1766111c27be08723a
c0dda5d92a5a4dffc56a5f26490a69460897751a574272a6e84f7cb067bb0a825eae58897ba417a9
024f8d98f09ac9b51fe0d7a04a3398887793717a84dc909c28042c38433fd7a4bc97cdebc038a4bd
70c20b744f536ae81b9a9a2d1334a6cf8d6210dde9984ef40ba026f3d80c02f1fd83e09b31c7c20b
4ec23917e9eec93bd9afd0b04fe253efec8047ea92bc4f8774f2f6cd537407fdfa0a0bbe6321a9fe
b54f4b72e44e38bc81789aa5dcfddbddb7dd5d0f8181865b68f149b406694c751df27ce162e1059c
8de6e79271b40b9a58266661da1895e1fba771917eac11a3e97f9252178f2d1a9ad72812c90dc94d
5847d3fc1ac567dee1b540762270290855015dcc56ad4a878fd95c5e0393b14525b5ffed1d460ded
c27db90e97eac8ec2163f313046d4593e249614070c3f81d7591bdc087e8bccafeea9014be9ca21a
5b979b1007208e755c023a067e2f986eda0cf3e8b3a03d820aa03dcff499753e76ab861e6ef145e3
0c7be65832326eaa84090cee3605e3dcb8699c0c36cbd954fcf99bcfe7e9e06822422063ebb27292
ea19866e3a406111d81d2cb57684a8be
客户端第二步输出:
124b65fee967319519369643a5c4a0182feeda020950ae2c50dc8c510a3540a2197d9d480c5d92f1
f4d938ca974ce2d548536266b534b31caab977cdd3f5c8ec
客户端生成密钥分量 = AFD4E03601F43852FF53EF2BB06F41E0DD61854B905652985E3F39CB3780F222
服务端生成密钥分量 = 8A182D81F249FD3D4AD9C7A0BB2E4331D5EA166FF0F27CA686DD0026C77DC8F0
客户端生成公钥 = E8C5C32AB79AFCA6D2CBCAA906A2D03687802BDB7891BE3BAAE887DB45EF342BA90EAF4BF2E8D7594F5BED93304813BB5A1CA498DA6B978B2DB76F8B5CD4C027
服务端生成公钥 = E8C5C32AB79AFCA6D2CBCAA906A2D03687802BDB7891BE3BAAE887DB45EF342BA90EAF4BF2E8D7594F5BED93304813BB5A1CA498DA6B978B2DB76F8B5CD4C027
--- PASS: TestGenSignKey (0.06s)
PASS
ok xdx.jelly/xgcl/x/tpc/sm2/sm2a 0.431s
+9
View File
@@ -0,0 +1,9 @@
package sm2a
import "xdx.jelly/xgcl/sm/sm2"
// in: point (x,y)
// out: [ds](x,y)
func ServerKeyExchange(in []byte, ds *sm2.PrivateKey) ([]byte, error) {
return ServerDecrypt(in, ds)
}
+183
View File
@@ -0,0 +1,183 @@
package sm2a
import (
"fmt"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/grand"
"xdx.jelly/xgcl/sm/sm2"
)
type ClientSponsor struct {
mixPoint *sm2.PublicKey
KeyBits uint32
z []byte
r *sm2.PrivateKey // sponsor's temp private key
clientKey *sm2.PrivateKey
publicKey *sm2.PublicKey
tempKeyOfSponsor *sm2.PublicKey
tempKeyOfResponsor *sm2.PublicKey
}
func NewClientSponsor(id []byte, clientKey *sm2.PrivateKey, publicKey *sm2.PublicKey) *ClientSponsor {
return &ClientSponsor{
z: sm2.PreComputeWithIdAndPubkey(id, publicKey),
clientKey: sm2.NewPrivateKey().Set(clientKey),
publicKey: sm2.NewPublicKey().Set(publicKey),
tempKeyOfSponsor: sm2.NewPublicKey(),
tempKeyOfResponsor: sm2.NewPublicKey(),
}
}
// GenerateAgreementData 发起方客户端计算密钥交换数据
func (c *ClientSponsor) GenerateAgreementData(rnd []byte) (tempKeyOfSponsor *sm2.PublicKey, err error) {
if len(rnd) < sm2.ByteSize() {
rnd = make([]byte, sm2.ByteSize())
if _, err := grand.GenerateRandom(rnd); err != nil {
return nil, err
}
}
c.r, _ = sm2.GenPrivateKey(rnd[:sm2.ByteSize()])
tempKeyOfSponsor = sm2.GenPublicKey(c.r)
c.tempKeyOfSponsor.Set(tempKeyOfSponsor)
return tempKeyOfSponsor, nil
}
// GenerateKey_1of2 发起方客户端计算密钥第一步
func (c *ClientSponsor) GenerateKey_1of2(pubkeyOfResponsor, tempKeyOfResponsor *sm2.PublicKey) ([]byte, error) {
if !pubkeyOfResponsor.IsValid() || !tempKeyOfResponsor.IsValid() {
return nil, fmt.Errorf("Input Public Key are not valid")
}
c.mixPoint = mixPoint(pubkeyOfResponsor, tempKeyOfResponsor) // P+[x_R]·R
return marshalPoint(c.mixPoint), nil
}
// GenerateKey_2of2 发起方客户端计算密钥第二步
func (c *ClientSponsor) GenerateKey_2of2(keyLength int, idOfResponsor []byte, fromServer []byte, pubkeyOfResponsor *sm2.PublicKey, clientKey *sm2.PrivateKey) (key []byte, err error) {
if len(fromServer) < 2*sm2.ByteSize() {
return nil, fmt.Errorf("Input Public Key are not valid")
}
tempKeyOfServer := unmarshalPoint(fromServer)
if !tempKeyOfServer.IsValid() || !pubkeyOfResponsor.IsValid() {
return nil, fmt.Errorf("Input Public Key are not valid")
}
// U = [d_c + d_s + \bar{x_1} * r_A]*MixPointB
// = [d_s]*MixPointB + [d_c + \bar{x_1} * r_A]*MixPointB
w := sm2.XBar(c.tempKeyOfSponsor.X)
w.Mul(w, c.r.D)
w.Add(w, clientKey.D)
w.Mod(w, sm2.OrderN())
x, y := sm2.Curve256.ScalarMult(c.mixPoint.X, c.mixPoint.Y, w.Bytes())
x, y = sm2.Curve().Add(x, y, tempKeyOfServer.X, tempKeyOfServer.Y) // U = (x,y)
// printLog("U= %x\n", append(append([]byte{}, x.Bytes()...), y.Bytes()...))
if gmath.IsBigInt0(x) && gmath.IsBigInt0(y) {
return nil, fmt.Errorf("U is zero")
}
key = make([]byte, keyLength)
sm2.Kdf(key,
gmath.BigIntToNByte(x, sm2.ByteSize()),
gmath.BigIntToNByte(y, sm2.ByteSize()),
c.z,
sm2.PreComputeWithIdAndPubkey(idOfResponsor, pubkeyOfResponsor))
return key, nil
}
type ClientResponsor struct {
z []byte
clientKey *sm2.PrivateKey
publicKey *sm2.PublicKey
mixPoint *sm2.PublicKey
}
// NewResponsor return a responsor's instance
func NewClientResponsor(id []byte, clientKey *sm2.PrivateKey, publicKey *sm2.PublicKey) *ClientResponsor {
return &ClientResponsor{
z: sm2.PreComputeWithIdAndPubkey(id, publicKey),
clientKey: sm2.NewPrivateKey().Set(clientKey),
publicKey: sm2.NewPublicKey().Set(publicKey),
}
}
// GenerateAgreementDataAndKey_1of2 响应方客户端生成数据和密钥第一步
func (cr *ClientResponsor) GenerateAgreementDataAndKey_1of2(pubkeyOfSponsor, tempKeyOfSponsor *sm2.PublicKey) (toServer []byte, err error) {
if !pubkeyOfSponsor.IsValid() || !tempKeyOfSponsor.IsValid() {
return nil, fmt.Errorf("Input Public Key are not valid")
}
cr.mixPoint = mixPoint(pubkeyOfSponsor, tempKeyOfSponsor)
return marshalPoint(cr.mixPoint), nil
}
// GenerateAgreementDataAndKey_1of2 响应方客户端生成数据和密钥第二步
func (cr *ClientResponsor) GenerateAgreementDataAndKey_2of2(keyLength int, idOfSponsor []byte, pubkeyOfSponsor *sm2.PublicKey, fromServer []byte, rnd []byte) (key []byte, tempKeyOfResponsor *sm2.PublicKey, err error) {
if len(fromServer) < 2*sm2.ByteSize() {
return nil, nil, fmt.Errorf("Input Public Key are not valid")
}
tempKeyOfServer := unmarshalPoint(fromServer)
if !pubkeyOfSponsor.IsValid() || !tempKeyOfServer.IsValid() {
return nil, nil, fmt.Errorf("Input Public Key are not valid")
}
if len(rnd) < sm2.ByteSize() {
rnd = make([]byte, sm2.ByteSize())
if _, err := grand.GenerateRandom(rnd); err != nil {
return nil, nil, err
}
}
rb, _ := sm2.GenPrivateKey(rnd[:sm2.ByteSize()])
tempKeyOfResponsor = sm2.GenPublicKey(rb)
w := sm2.XBar(tempKeyOfResponsor.X)
w.Mul(w, rb.D)
w.Add(w, cr.clientKey.D)
w.Mod(w, sm2.OrderN())
x, y := sm2.Curve256.ScalarMult(cr.mixPoint.X, cr.mixPoint.Y, w.Bytes())
x, y = sm2.Curve().Add(x, y, tempKeyOfServer.X, tempKeyOfServer.Y) // V = (x,y)
// printLog("V=", append(append([]byte{}, x.Bytes()...), y.Bytes()...))
if gmath.IsBigInt0(x) && gmath.IsBigInt0(y) {
return nil, nil, fmt.Errorf("U is zero")
}
key = make([]byte, keyLength)
sm2.Kdf(key,
gmath.BigIntToNByte(x, sm2.ByteSize()),
gmath.BigIntToNByte(y, sm2.ByteSize()),
sm2.PreComputeWithIdAndPubkey(idOfSponsor, pubkeyOfSponsor),
cr.z,
)
return key, tempKeyOfResponsor, nil
}
// mixPoint return p+[x_r]·r
func mixPoint(p, r *sm2.PublicKey) *sm2.PublicKey {
xBar := sm2.XBar(r.X)
x, y := sm2.Curve256.ScalarMult(r.X, r.Y, xBar.Bytes())
x, y = sm2.Curve().Add(x, y, p.X, p.Y)
return &sm2.PublicKey{X: x, Y: y}
}
func marshalPoint(p *sm2.PublicKey) []byte {
out := make([]byte, 0, 2*sm2.ByteSize())
out = append(out, gmath.BigIntToNByte(p.X, sm2.ByteSize())...)
out = append(out, gmath.BigIntToNByte(p.Y, sm2.ByteSize())...)
return out
}
func unmarshalPoint(b []byte) *sm2.PublicKey {
ret := sm2.NewPublicKey()
ret.X.SetBytes(b[:sm2.ByteSize()])
ret.Y.SetBytes(b[sm2.ByteSize() : 2*sm2.ByteSize()])
return ret
}
+174
View File
@@ -0,0 +1,174 @@
package sm2a
import (
"bytes"
"fmt"
"testing"
"time"
"xdx.jelly/xgcl/grand"
)
func TestExchangeBoth(t *testing.T) {
// 发起方密钥生成
vv := false
sponsorClientKeyCtx := NewClientEncKeyGenContext()
out, _ := sponsorClientKeyCtx.ClientKeyGen_one(grand.Reader)
sponsorServerKey, sponsorPublicKey, out, _ := ServerEncKeyGen(out, grand.Reader)
sponsorClientKeyCtx.ClientKeyGen_two(out)
sponsorClientKey := sponsorClientKeyCtx.ClientKey
if vv {
printLog("发起方客户端私钥分量=", sponsorClientKeyCtx.ClientKey.Bytes())
printLog("发起方服务端私钥分量=", sponsorServerKey.Bytes())
printLog("发起方公钥=", sponsorClientKeyCtx.Pubkey.Bytes())
}
// 响应方密钥生成
responsorClientKeyCtx := NewClientEncKeyGenContext()
out, _ = responsorClientKeyCtx.ClientKeyGen_one(grand.Reader)
responsorServerKey, responsorPublicKey, out, _ := ServerEncKeyGen(out, grand.Reader)
responsorClientKeyCtx.ClientKeyGen_two(out)
responsorClientKey := responsorClientKeyCtx.ClientKey
if vv {
printLog("响应方客户端私钥分量=", responsorClientKeyCtx.ClientKey.Bytes())
printLog("响应方服务端私钥分量=", responsorServerKey.Bytes())
printLog("响应方公钥=", responsorClientKeyCtx.Pubkey.Bytes())
}
sponsor := []byte("alice")
responsor := []byte("bob")
keyLength := 16
cs := NewClientSponsor(sponsor, sponsorClientKey, sponsorPublicKey)
cr := NewClientResponsor(responsor, responsorClientKey, responsorPublicKey)
tempKeyOfSponsor, err := cs.GenerateAgreementData(grand.GetRandom(32))
if err != nil {
t.Fatal("GenerateAgreementData failed")
}
if vv {
printLog("Ra=", tempKeyOfSponsor.Bytes())
}
// 协同计算GenerateAgreementDataAndKey
toServer, err := cr.GenerateAgreementDataAndKey_1of2(sponsorPublicKey, tempKeyOfSponsor)
if err != nil {
t.Fatal("cr.GenerateAgreementDataAndKey_1of2 failed")
}
if vv {
printLog("Qa=", toServer)
}
toClient, err := ServerKeyExchange(toServer, responsorServerKey)
if err != nil {
t.Fatal("cr.GenerateAgreementDataAndKey_1of2 failed")
}
if vv {
printLog("[h*db_s]Qa=", toClient)
}
keyOfResponsor, tempKeyOfResponsor, err := cr.GenerateAgreementDataAndKey_2of2(keyLength, sponsor, sponsorPublicKey, toClient, grand.GetRandom(32))
if err != nil {
t.Fatal("cr.GenerateAgreementDataAndKey_2of2 failed")
}
if vv {
printLog("Rb=", tempKeyOfResponsor.Bytes())
}
toServer, err = cs.GenerateKey_1of2(responsorPublicKey, tempKeyOfResponsor)
if err != nil {
t.Fatal("cs.GenerateKey_1of2 failed")
}
if vv {
printLog("Qb=", toServer)
}
toClient, err = ServerKeyExchange(toServer, sponsorServerKey)
if err != nil {
t.Fatal("cs.GenerateKey_1of2 failed")
}
if vv {
printLog("[h*db_s]Qb=", toClient)
}
keyOfSponsor, err := cs.GenerateKey_2of2(keyLength, responsor, toClient, responsorPublicKey, sponsorClientKey)
if err != nil {
t.Fatal("cs.GenerateKey_2of2 failed")
}
printLog("ka=", keyOfSponsor)
if vv {
fmt.Printf("响应方密钥: %X\n", keyOfResponsor)
fmt.Printf("发起方密钥: %X\n", keyOfSponsor)
}
if bytes.Compare(keyOfResponsor, keyOfSponsor) != 0 {
t.Fatal("Key agreement failed")
}
}
func TestExchangeBenchmark(t *testing.T) {
// 发起方密钥生成
sponsorClientKeyCtx := NewClientEncKeyGenContext()
out, _ := sponsorClientKeyCtx.ClientKeyGen_one(grand.Reader)
sponsorServerKey, sponsorPublicKey, out, _ := ServerEncKeyGen(out, grand.Reader)
sponsorClientKeyCtx.ClientKeyGen_two(out)
sponsorClientKey := sponsorClientKeyCtx.ClientKey
// 响应方密钥生成
responsorClientKeyCtx := NewClientEncKeyGenContext()
out, _ = responsorClientKeyCtx.ClientKeyGen_one(grand.Reader)
responsorServerKey, responsorPublicKey, out, _ := ServerEncKeyGen(out, grand.Reader)
responsorClientKeyCtx.ClientKeyGen_two(out)
responsorClientKey := responsorClientKeyCtx.ClientKey
sponsor := []byte("alice")
responsor := []byte("bob")
keyLength := 16
cnt := 1000
errors := 0
start := time.Now()
for i := 0; i < cnt; i++ {
cs := NewClientSponsor(sponsor, sponsorClientKey, sponsorPublicKey)
cr := NewClientResponsor(responsor, responsorClientKey, responsorPublicKey)
tempKeyOfSponsor, err := cs.GenerateAgreementData(grand.GetRandom(32))
if err != nil {
t.Fatal("GenerateAgreementData failed")
}
// 协同计算GenerateAgreementDataAndKey
toServer, err := cr.GenerateAgreementDataAndKey_1of2(sponsorPublicKey, tempKeyOfSponsor)
if err != nil {
t.Fatal("cr.GenerateAgreementDataAndKey_1of2 failed")
}
toClient, err := ServerKeyExchange(toServer, responsorServerKey)
if err != nil {
t.Fatal("cr.GenerateAgreementDataAndKey_1of2 failed")
}
keyOfResponsor, tempKeyOfResponsor, err := cr.GenerateAgreementDataAndKey_2of2(keyLength, sponsor, sponsorPublicKey, toClient, grand.GetRandom(32))
if err != nil {
t.Fatal("cr.GenerateAgreementDataAndKey_2of2 failed")
}
toServer, err = cs.GenerateKey_1of2(responsorPublicKey, tempKeyOfResponsor)
if err != nil {
t.Fatal("cs.GenerateKey_1of2 failed")
}
toClient, err = ServerKeyExchange(toServer, sponsorServerKey)
if err != nil {
t.Fatal("cs.GenerateKey_1of2 failed")
}
keyOfSponsor, err := cs.GenerateKey_2of2(keyLength, responsor, toClient, responsorPublicKey, sponsorClientKey)
if err != nil {
t.Fatal("cs.GenerateKey_2of2 failed")
}
if bytes.Compare(keyOfResponsor, keyOfSponsor) != 0 {
errors += 1
}
}
end := time.Now()
elapsed := end.Sub(start)
fmt.Printf("总运行次数:%d\n", cnt)
fmt.Printf("总失败次数:%d\n", errors)
fmt.Printf("错误率:%f\n", float64(errors)/float64(cnt))
fmt.Printf("耗时时间(单位:秒):%f\n", elapsed.Seconds()*20)
fmt.Printf("单次请求平均耗时(单位:秒):%f\n", elapsed.Seconds()/float64(cnt)*20)
fmt.Printf("TPS%f\n", float64(cnt)/elapsed.Seconds()/20)
}
+64
View File
@@ -0,0 +1,64 @@
package sm2a
import (
"io"
"math/big"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/sm/sm2"
"xdx.jelly/xgcl/sm/sm3"
)
type ClientSignContext struct {
pk *sm2.PublicKey
k1 *big.Int
rand io.Reader
}
func NewClientSignContext(pk *sm2.PublicKey, r io.Reader) *ClientSignContext {
c := &ClientSignContext{
pk: sm2.NewPublicKey(),
k1: new(big.Int),
rand: r,
}
c.pk.Set(pk)
return c
}
// Initial 客户端签名第一步,初始化,输入预处理后的待签名数据e,输出协同计算中间数据给服务端
// 输出 = e || [k_1](P+G)
func (c *ClientSignContext) Initial(e []byte) ([]byte, error) {
buf := make([]byte, sm2.ByteSize())
if n, err := c.rand.Read(buf); n != len(buf) || err != nil {
return nil, err
}
c.k1.SetBytes(buf)
c.k1.Mod(c.k1, sm2.OrderN())
x, y := sm2.Curve().Add(c.pk.X, c.pk.Y, sm2.BaseX(), sm2.BaseY())
x, y = sm2.Curve256.ScalarMult(x, y, c.k1.Bytes())
out := make([]byte, sm3.Size+2*sm2.ByteSize())
pos := copy(out, e)
pos += copy(out[pos:], gmath.BigIntToNByte(x, sm2.ByteSize()))
copy(out[pos:], gmath.BigIntToNByte(y, sm2.ByteSize()))
return out, nil
}
// Final 客户端根据服务端协同计算结果,计算签名值
// in = r || s1
func (c *ClientSignContext) Final(clientKey *sm2.PrivateKey, in []byte) (*sm2.Signature, error) {
sig := sm2.NewSignature()
sig.R.SetBytes(in[:sm2.ByteSize()])
s1 := new(big.Int)
s1.SetBytes(in[sm2.ByteSize():])
sig.S.Mul(sig.R, clientKey.D)
sig.S.Add(sig.S, c.k1)
sig.S.Add(sig.S, s1)
sig.S.Sub(sig.S, sig.R)
sig.S.Mod(sig.S, sm2.OrderN())
sig.R.Mod(sig.R, sm2.OrderN())
return sig, nil
}
+47
View File
@@ -0,0 +1,47 @@
package sm2a
import (
"io"
"math/big"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/sm/sm2"
"xdx.jelly/xgcl/sm/sm3"
)
// ServerSign 服务端根据客户端的协同签名中间数据计算
// in = e || [k_1](P+G)
// out = r || k_2 + r*d_s
func ServerSign(serverKey *sm2.PrivateKey, pk *sm2.PublicKey, in []byte, rand io.Reader) ([]byte, error) {
k2 := make([]byte, sm2.ByteSize())
if n, err := rand.Read(k2); n != len(k2) || err != nil {
return nil, err
}
var x, y *big.Int
tx := new(big.Int)
ty := new(big.Int)
r := new(big.Int)
r.SetBytes(in[:sm3.Size])
tx.SetBytes(in[sm3.Size : sm3.Size+sm2.ByteSize()])
ty.SetBytes(in[sm3.Size+sm2.ByteSize() : sm3.Size+2*sm2.ByteSize()])
x, y = sm2.Curve().Add(pk.X, pk.Y, sm2.BaseX(), sm2.BaseY())
x, y = sm2.Curve256.ScalarMult(x, y, k2)
x, y = sm2.Curve().Add(x, y, tx, ty)
r.Add(r, x)
r.Mod(r, sm2.OrderN())
x.Mul(r, serverKey.D)
y.SetBytes(k2)
x.Add(x, y)
x.Mod(x, sm2.OrderN())
out := make([]byte, 2*sm2.ByteSize())
copy(out, gmath.BigIntToNByte(r, sm2.ByteSize()))
copy(out[sm2.ByteSize():], gmath.BigIntToNByte(x, sm2.ByteSize()))
// 清除中间变量
gmath.ClearBigInt(x)
gmath.ClearBigInt(y)
gmath.ClearBigInt(tx)
gmath.ClearBigInt(ty)
gmath.ClearBigInt(r)
return out, nil
}
+205
View File
@@ -0,0 +1,205 @@
package sm2a
import (
"fmt"
"math/rand"
"sync"
"testing"
"time"
"xdx.jelly/xgcl/gmath"
"xdx.jelly/xgcl/grand"
"xdx.jelly/xgcl/sm/sm2"
)
func TestSign(t *testing.T) {
// 生成客户端和服务端签名密钥分量
serverKeySeg := NewServerSignKeyGenContext()
clientKeySeg := NewClientSignKeyGenContext(grand.Reader)
buf, err := serverKeySeg.ServerGenKey_one(grand.Reader)
if err != nil {
fmt.Println(err)
return
}
buf, err = clientKeySeg.ClientKeyGen_one(buf)
if err != nil {
fmt.Println(err)
return
}
buf, err = serverKeySeg.ServerGenKey_two(buf, grand.Reader)
if err != nil {
fmt.Println(err)
return
}
buf, err = clientKeySeg.ClientKeyGen_two(buf)
if err != nil {
fmt.Println(err)
return
}
err = serverKeySeg.ServerGenKey_three(buf)
if err != nil {
fmt.Println(err)
return
}
dc := clientKeySeg.ClientKey
ds := serverKeySeg.ServerKey
dc.D.Mod(dc.D, sm2.Curve().Params().N)
// 协同计算签名
e := make([]byte, sm2.ByteSize())
grand.GenerateRandom(e)
printLog("签名数据e ", e)
pk := clientKeySeg.PubKey
printLog("终端签名分量", dc.Bytes())
printLog("服务端签名分量", ds.Bytes())
printLog("公钥", pk.Bytes())
clientSign := NewClientSignContext(pk, grand.Reader)
buf, _ = clientSign.Initial(e)
printLog("T=", buf)
buf, _ = ServerSign(ds, pk, buf, grand.Reader)
printLog("r=", buf[:32])
printLog("s1=", buf[32:])
sig, _ := clientSign.Final(dc, buf)
printLog("r=", sig.R.Bytes())
printLog("s=", sig.S.Bytes())
printLog("签名结果", sig.Bytes())
if !sm2.Verify(e, pk, sig) {
t.Fatal("验签失败")
}
}
func TestBm(t *testing.T) {
rand1 := rand.New(rand.NewSource(time.Now().UnixNano()))
var wt time.Duration = 5
serverKeySeg := NewServerSignKeyGenContext()
clientKeySeg := NewClientSignKeyGenContext(grand.Reader)
buf, err := serverKeySeg.ServerGenKey_one(grand.Reader)
if err != nil {
fmt.Println(err)
return
}
serverctx, _ := serverKeySeg.MarshalBinary()
fmt.Printf("%d\n", len(serverctx))
//rand.Reader will got error? why
buf, err = clientKeySeg.ClientKeyGen_one(buf)
if err != nil {
fmt.Println(err)
return
}
// fmt.Printf("%x\n", buf)
// fmt.Println("length of buf is ", len(buf))
serverKeySeg.UnmarshalBinary(serverctx)
buf, err = serverKeySeg.ServerGenKey_two(buf, grand.Reader)
if err != nil {
fmt.Println(err)
return
}
serverctx, _ = serverKeySeg.MarshalBinary()
fmt.Printf("%x\n", serverctx)
buf, err = clientKeySeg.ClientKeyGen_two(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Client public Key = ", clientKeySeg.PubKey)
serverKeySeg.UnmarshalBinary(serverctx)
err = serverKeySeg.ServerGenKey_three(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Server public Key = ", serverKeySeg.PubKey)
dc := clientKeySeg.ClientKey
ds := serverKeySeg.ServerKey
d := sm2.NewPrivateKey()
d.D.Add(dc.D, ds.D)
d.D.ModInverse(d.D, sm2.OrderN())
d.D.Sub(d.D, gmath.BigInt1)
px, py := sm2.Curve256.ScalarBaseMult(d.Bytes())
fmt.Println("p=(", px.Text(16)+", "+py.Text(16)+")")
e := make([]byte, sm2.ByteSize())
pk := clientKeySeg.PubKey
//
wg := sync.WaitGroup{}
totalSuccess := 0
totalFailed := 0
var (
fail float32
total float32
)
beg := time.Now().UnixNano()
for j := 0; j < 1; j++ {
wg.Add(1)
wt = time.Duration(rand1.Int() % 100)
time.Sleep(wt * time.Millisecond)
go func(wg *sync.WaitGroup) {
defer wg.Done()
for mi := 0; mi < 2000; mi++ {
clientSign := NewClientSignContext(pk, grand.Reader)
buf2, err := clientSign.Initial(e)
if err != nil {
totalFailed++
continue
}
buf2, err = ServerSign(ds, pk, buf2, grand.Reader)
if err != nil {
totalFailed++
continue
}
//sig, err := clientSign.Final(dc, buf2)
_, err = clientSign.Final(dc, buf2)
if err != nil {
totalFailed++
continue
}
totalSuccess++
//fmt.Println(sig)
//fmt.Println(sm2.Verify(e, pk, sig))
}
}(&wg)
}
wg.Wait()
end := time.Now().UnixNano()
fail = float32(totalFailed)
total = float32(totalSuccess + totalFailed)
rate := fail / total
elapseDenom := float32(end - beg)
elapse := elapseDenom / 1000000000
one := elapse / total
fmt.Printf("错误率: %f %% \n", rate*100)
fmt.Printf("耗时时间(单位:秒): %f seconds \n", elapse)
fmt.Printf("单次请求平均耗时(单位:秒) %f seconds \n", one)
fmt.Println("TPS: ", total/elapse)
fmt.Println("")
fmt.Println("")
fmt.Println("")
}
+5
View File
@@ -0,0 +1,5 @@
package sm2a
const (
SecureParam = 128
)
+27
View File
@@ -0,0 +1,27 @@
package sm2a
import (
"fmt"
"math/big"
"xdx.jelly/xgcl/sm/sm2"
)
// pointToInt hash a point (x,y) to a big Int
func pointToInt(r, x, y *big.Int) {
r.Add(x, y)
r.Mod(r, sm2.OrderN())
}
func printLog(info string, data []byte) {
fmt.Println(info)
i := 0
for _, c := range data {
fmt.Printf("%02X", c)
i++
if i%40 == 0 {
fmt.Println()
}
}
fmt.Println()
}