272 lines
8.6 KiB
Go
272 lines
8.6 KiB
Go
package outsource
|
||
|
||
import (
|
||
"bytes"
|
||
"math/big"
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/assert"
|
||
"xdx.jelly/xgcl/gmath"
|
||
"xdx.jelly/xgcl/grand"
|
||
"xdx.jelly/xgcl/he/paillier"
|
||
"xdx.jelly/xgcl/sm/sm2"
|
||
"xdx.jelly/xgcl/sm/sm3"
|
||
"xdx.jelly/xgcl/tpc/sm2/sm2m"
|
||
)
|
||
|
||
// osGenerateKey 客户端,外包,服务端协同密钥生成
|
||
func osGenerateKey(t *testing.T) (*paillier.PrivateKey, *paillier.PublicKey, *paillier.Cipher, *paillier.Cipher, *sm2.PrivateKey, *sm2.PublicKey) {
|
||
rnd := grand.Reader
|
||
salt := []byte("12345678")
|
||
paiPrivKey, err := paillier.GenerateKeyFromPassword(2048, []byte("password123"), salt, 1024)
|
||
assert.Nil(t, err)
|
||
paiPubKey := paiPrivKey.Public()
|
||
|
||
// 一、密钥生成
|
||
|
||
// 1)客户端第一步, a1, a2 发 外包服务器
|
||
clientKeyGen := &ClientKeyGenerator{}
|
||
a1, a2, err := clientKeyGen.Step1(paiPubKey, rnd)
|
||
assert.Nil(t, err)
|
||
|
||
// 2) 外包服务器第一步
|
||
// 保存encryptedClientKey1,encryptedClientKey2,
|
||
// 把 P 发客户端
|
||
// encryptedClientKey1 -签名用
|
||
// encryptedClientKey2 -解密加密密钥保护结构用
|
||
osKenGen := &OSKeyGenerator{}
|
||
encryptedClientKey1, encryptedClientKey2, P, err := osKenGen.Step1(a1, a2, paiPubKey, rnd)
|
||
assert.Nil(t, err)
|
||
|
||
// 3) 客户端第二步
|
||
// clientTempKey发协同签名服务端
|
||
clientTempKey, err := clientKeyGen.Step2(P)
|
||
|
||
// 4)协同签名服务器
|
||
// serverTempKey 发客户端,保存serverKey-服务端密钥分量
|
||
b := make([]byte, 32)
|
||
rnd.Read(b)
|
||
serverKey, serverTempKey, publicKey, _ := sm2m.ServerGenSignKey(clientTempKey, b)
|
||
|
||
// 5)客户端第三步
|
||
// 把(serverTempKey,S)发送给外包服务器
|
||
S, err := clientKeyGen.Step3(serverTempKey)
|
||
assert.Nil(t, err)
|
||
|
||
// 6)外包服务器第二步
|
||
// 把T, osPublicKey发给客户端。保存osPublicKey为用户签名公钥
|
||
T, osPublicKey, err := osKenGen.Step2(serverTempKey, S)
|
||
assert.Nil(t, err)
|
||
|
||
// 7)客户端第四步
|
||
// clientPublicKey, 客户端生成的公钥,如果没有返错,则应与ocPublicKey一致。
|
||
clientPublicKey, err := clientKeyGen.Step4(T)
|
||
assert.Nil(t, err)
|
||
assert.True(t, clientPublicKey.Equals(osPublicKey))
|
||
|
||
return paiPrivKey, paiPubKey, encryptedClientKey1, encryptedClientKey2, serverKey, publicKey
|
||
}
|
||
|
||
// 外包签名流程
|
||
func TestOSSign(t *testing.T) {
|
||
//O、生成密钥
|
||
var err error
|
||
rnd := grand.Reader
|
||
paiPrivKey, paiPubKey, encryptedClientKey1, _, serverKey, publicKey := osGenerateKey(t)
|
||
|
||
e := make([]byte, 32)
|
||
rnd.Read(e)
|
||
|
||
clientCtx := NewClientSignContext(paiPrivKey, rnd)
|
||
// 1) client -> os
|
||
|
||
// 2)os -> client
|
||
osServerCtx := NewOSSignContext()
|
||
pp, err := osServerCtx.Step1(rnd) // P'
|
||
assert.Nil(t, err)
|
||
|
||
// 3) client -> hfserver
|
||
p, err := clientCtx.Step1(e, pp)
|
||
assert.Nil(t, err)
|
||
|
||
// 组合数据data = (e,p)=e||px||py并发送给协同服务端
|
||
data := make([]byte, sm3.Size+2*sm2.ByteSize())
|
||
pos := copy(data, e)
|
||
pos += copy(data[pos:], gmath.BigIntToNByte(p.X, sm2.ByteSize()))
|
||
copy(data[pos:], gmath.BigIntToNByte(p.Y, sm2.ByteSize()))
|
||
|
||
// 4) hfserver -> client
|
||
data, err = sm2m.ServerSign(serverKey, data, rnd)
|
||
assert.Nil(t, err)
|
||
|
||
// 5) clinet -> os
|
||
// 解析数据
|
||
r := new(big.Int).SetBytes(data[:sm2.ByteSize()])
|
||
s1 := new(big.Int).SetBytes(data[sm2.ByteSize() : 2*sm2.ByteSize()])
|
||
s2 := new(big.Int).SetBytes(data[2*sm2.ByteSize():])
|
||
|
||
s1p, s2p, err := clientCtx.Step2(r, s1, s2)
|
||
assert.Nil(t, err)
|
||
|
||
// 6) 外包服务器解析data并计算c,把c发送给客户端
|
||
w, err := osServerCtx.Step2(s1p, s2p, encryptedClientKey1, paiPubKey)
|
||
assert.Nil(t, err)
|
||
|
||
// 7) 客户端计算签名值
|
||
sig, err := clientCtx.Step3(w)
|
||
assert.Nil(t, err)
|
||
|
||
// 8) 客户端验证签名
|
||
assert.True(t, sm2.Verify(e, publicKey, sig))
|
||
}
|
||
|
||
// 外包计算完整流程示例
|
||
// deprecated: 签名的客户端第3和第5步有遗失. 签名见上面的流程TestOSSign. 相当于客户端选择了随机数k1” = 1.
|
||
func TestOutsourceWithPaillier(t *testing.T) {
|
||
//O、客户端生成paillier密钥
|
||
var err error
|
||
paiPrivKey, paiPubKey, encryptedClientKey1, encryptedClientKey2, serverKey, publicKey := osGenerateKey(t)
|
||
|
||
// 外包服务收到客户端发来的同态公钥
|
||
paiPubKeyClientData, _ := paiPubKey.Marshal()
|
||
paiPubKey = new(paillier.PublicKey)
|
||
err = paiPubKey.Unmarshal(paiPubKeyClientData)
|
||
|
||
// 外包服务将公钥数据paiPubKeyServerData存储数据库中, 包括预计算数据.
|
||
paiPubKeyServerData, _ := paiPubKey.MarshalExt()
|
||
|
||
// 外包服务从数据库中恢复用户的同态公钥, 用于外包同态计算.
|
||
_ = paiPubKey.UnmarshalExt(paiPubKeyServerData)
|
||
|
||
rnd := grand.Reader
|
||
|
||
_, _ = encryptedClientKey1, rnd
|
||
/*
|
||
// 二、签名
|
||
|
||
// 1)客户端发起请求
|
||
e := make([]byte, 32)
|
||
rnd.Read(e)
|
||
|
||
// 2)外包方计算
|
||
outsourcintCtx := new(OSSignContext)
|
||
PPrime, err := outsourcintCtx.Step1(rnd) // P'
|
||
assert.Nil(t, err)
|
||
|
||
// outsourcintCtx把PPrime发给客户端,保存outsourcintCtx.Marshal()
|
||
|
||
// 3) 客户端组合数据data = (e,p)=e||px||py并发送给协同服务端
|
||
data := make([]byte, sm3.Size+2*sm2.ByteSize())
|
||
pos := copy(data, e)
|
||
pos += copy(data[pos:], gmath.BigIntToNByte(PPrime.X, sm2.ByteSize()))
|
||
copy(data[pos:], gmath.BigIntToNByte(PPrime.Y, sm2.ByteSize()))
|
||
|
||
// 4) 协同服务端计算,发回data
|
||
data, err = sm2m.ServerSign(serverKey, data, rnd)
|
||
assert.Nil(t, err)
|
||
|
||
// 5) 客户端解析data = r || s1 || s2, 把s1, s2发给外包服务器。
|
||
r := new(big.Int)
|
||
r.SetBytes(data[:sm2.ByteSize()])
|
||
s1 := new(big.Int).SetBytes(data[sm2.ByteSize() : 2*sm2.ByteSize()])
|
||
s2 := new(big.Int).SetBytes(data[2*sm2.ByteSize():])
|
||
|
||
// 6) 外包服务器解析data并计算c,把c发送给客户端
|
||
c, err := outsourcintCtx.Step2(s1, s2, encryptedClientKey1, paiPubKey)
|
||
assert.Nil(t, err)
|
||
|
||
// 7) 客户端计算签名值
|
||
s, _ := paillier.Decrypt(c, paiPrivKey)
|
||
s.Sub(s, r)
|
||
s.Mod(s, sm2.OrderN())
|
||
sig := &sm2.Signature{
|
||
R: r,
|
||
S: s,
|
||
}
|
||
|
||
// 8) 客户端验证签名
|
||
assert.True(t, sm2.Verify(e, publicKey, sig))
|
||
*/
|
||
|
||
// 三、使用签名密钥解密
|
||
plaintext := grand.GetRandom(16)
|
||
cipher, err := sm2.Encrypt(publicKey, plaintext, grand.GetRandom(32))
|
||
assert.Nil(t, err)
|
||
|
||
// 1) 客户端把(x,y)=C1 发送给外包服务器
|
||
// 2)外包服务计算, 把paiCipher, xF, yF发送给客户端
|
||
paiCipher, xF, yF, err := OSDecrypt(encryptedClientKey2, paiPubKey, cipher.X, cipher.Y, grand.Reader)
|
||
assert.Nil(t, err)
|
||
|
||
// 3) 客户端计算
|
||
clientDecCtx := &ClientDecContext{}
|
||
xH, yH, err := clientDecCtx.Step1(paiCipher, xF, yF, paiPrivKey)
|
||
assert.Nil(t, err)
|
||
|
||
// 4) 协同签名服务器计算
|
||
inData := make([]byte, 2*sm2.ByteSize())
|
||
xH.FillBytes(inData[:sm2.ByteSize()])
|
||
yH.FillBytes(inData[sm2.ByteSize():])
|
||
outData, err := sm2m.ServerDec(serverKey, inData)
|
||
|
||
// 5) 客户端计算
|
||
x := new(big.Int).SetBytes(outData[:sm2.ByteSize()])
|
||
y := new(big.Int).SetBytes(outData[sm2.ByteSize():])
|
||
decPlain, err := clientDecCtx.Step2(cipher, x, y)
|
||
assert.Nil(t, err)
|
||
|
||
assert.True(t, bytes.Equal(plaintext, decPlain))
|
||
|
||
// 四、解密密钥分拆
|
||
// 假设客户端解密得到的解密密钥de
|
||
de, err := sm2.GenerateKey(sm2.Curve(), grand.Reader)
|
||
assert.Nil(t, err)
|
||
|
||
// 客户端分拆解密密钥, deServer给协同签名服务端
|
||
deClient, deServer, err := sm2m.SplitDecryptKey(de, grand.Reader)
|
||
assert.Nil(t, err)
|
||
|
||
// 加密deClient给外包服务端保存。
|
||
encryptedEncKey, err := EncryptClientDecKey(deClient, paiPubKey, grand.Reader)
|
||
assert.Nil(t, err)
|
||
|
||
// 五、使用解密密钥解密(同使用签名密钥解密)
|
||
plaintext = grand.GetRandom(16)
|
||
cipher, err = sm2.Encrypt(&de.PublicKey, plaintext, grand.GetRandom(32))
|
||
assert.Nil(t, err)
|
||
|
||
// (一)对比非OS的协同解密
|
||
clientCTX := sm2m.NewClientDecContext()
|
||
out, _ := clientCTX.Initial(cipher)
|
||
out, _ = sm2m.ServerDec(deServer, out)
|
||
decPlain, _ = clientCTX.Final(deClient, out)
|
||
assert.True(t, bytes.Equal(plaintext, decPlain))
|
||
|
||
// (二)OS解密
|
||
|
||
// 1) 客户端把(x,y)=C1 发送给外包服务器
|
||
// 2)外包服务计算, 把paiCipher, xF, yF发送给客户端
|
||
paiCipher, xF, yF, err = OSDecrypt(encryptedEncKey, paiPubKey, cipher.X, cipher.Y, grand.Reader)
|
||
assert.Nil(t, err)
|
||
|
||
// 3) 客户端计算
|
||
clientDecCtx = &ClientDecContext{}
|
||
xH, yH, err = clientDecCtx.Step1(paiCipher, xF, yF, paiPrivKey)
|
||
assert.Nil(t, err)
|
||
|
||
// 4) 协同签名服务器计算
|
||
inData = make([]byte, 2*sm2.ByteSize())
|
||
xH.FillBytes(inData[:sm2.ByteSize()])
|
||
yH.FillBytes(inData[sm2.ByteSize():])
|
||
outData, err = sm2m.ServerDec(deServer, inData)
|
||
|
||
// 5) 客户端计算
|
||
x = new(big.Int).SetBytes(outData[:sm2.ByteSize()])
|
||
y = new(big.Int).SetBytes(outData[sm2.ByteSize():])
|
||
decPlain, err = clientDecCtx.Step2(cipher, x, y)
|
||
assert.Nil(t, err)
|
||
|
||
assert.True(t, bytes.Equal(plaintext, decPlain))
|
||
|
||
}
|