同态加密使用说明(Go)

最近更新时间:2024-01-15 14:42:01

我的收藏

操作场景

用户可以利用 TBaaS 提供的同态加密的能力,便捷的在智能合约中使用同态加密。使用同态加密可以分为以下三个步骤:
1. 使用 TBaaS 提供的 paitool 生成同态公私钥,同时使用生成的公钥对用户数据线下加密。
2. 使用 TBaaS 提供的 Go 语言智能合约包 paillier,编写同态相关的业务层操作,例如同态加、同态减、部分同态乘等。
3. 当业务逻辑完成后,用户可以从智能合约中获取到对应的同态加密数据,线下使用对应的同态私钥进行数据解密,从而可以获取链上业务操作完成后的真实数据。

操作步骤

Paitool 同态公私钥生成以及数据加密

1. 依次执行以下命令,使用 paitool 生成两组同态加密的公私钥对。
./paitool genkey -length=2048 -pkout=pk1.pai -skout=sk1.pai
./paitool genkey -length=2048 -pkout=pk2.pai -skout=sk2.pai
其中,pk1.pai, pk2.pai 是同态加密公钥文件,sk1.pai, sk2.pai 是同态加密私钥文件。
2. 依次执行以下命令,使用 paitool 对数字100和200分别用不同的公钥进行同态加密。
./paitool encrypt -pkin=pk1.pai -plaintext=100 -cipherout=cipher1.pai
./paitool encrypt -pkin=pk2.pai -plaintext=200 -cipherout=cipher2.pai
其中,cipher1.pai 是数字100同态加密后的密文数据,cipher2.pai 是数字200加密后的密文数据。

智能合约同态加密示例

Hyperledger Fabric 提供了很多官方的智能合约样例,具体请参考 fabric 官方示例。本示例以 Hyperledger Fabric 官方提供的 example02 样例为例进行同态修改。该示例的 Init 函数用于初始化两个 key/value 键值对,其中 value 是用同态加密后的数据,Invoke 函数用于根据不同业务逻辑进行细分调用,最终调用以下业务逻辑接口:
invoke:用于 key 之间的 value 转移。
query:用于查询 key 所对应的值。
您可以访问 智能合约代码 获得完整代码,以下将对代码中的重要函数进行分析。

Init 函数示例

Init 函数主要用于在智能合约实例化和升级的时候默认调用。在实现 Init 函数的过程中,可以使用 Go 语言版本的合约 API 来对参数和账本进行操作。在这个示例中,通过调用 API GetFunctionAndParameters 获取到用户输入参数。在获取用户输入参数后,通过调用 API PutState 将数据写到账本中。
// Init函数用于初始化两个键值对,用户输入的参数为KEY1_NAME, VALUE1, KEY2_NAME, VALUE2,其中VALUE1和VALUE2都是同态加密后的数据
// 在例子中,VALUE1是cipher1.pai的内容, VALUE2是ciphe2.pai的内容
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Init")
// 调用API GetFunctionAndParameters 获取用户输入参数
_, args := stub.GetFunctionAndParameters()
var A, B string // Entities
var Aval, Bval string // Asset holdings
var err error

if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}

// Initialize the chaincode
A = args[0]
Aval = args[1]

B = args[2]
Bval = args[3]

// 调用API PutState把数据写入账本
// Write the state to the ledger
err = stub.PutState(A, []byte(Aval))
if err != nil {
return shim.Error(err.Error())
}

err = stub.PutState(B, []byte(Bval))
if err != nil {
return shim.Error(err.Error())
}

return shim.Success(nil)
}

Invoke 函数示例

Invoke 函数可以对用户的不同的智能合约业务逻辑进行拆分。本示例通过调用 API GetFunctionAndParameters 获取到用户的具体业务类型和参数,再分别调用不同的函数,如 invoke 和 query 函数。
// Invoke把用户调用的function细分到几个子function, 包含invoke和query
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Invoke")
// 调用API GetFunctionAndParameters获取用户输入的业务类型和参数
function, args := stub.GetFunctionAndParameters()
if function == "invoke" {
// Make payment of X units from A to B
return t.invoke(stub, args)
} else if function == "query" {
// the old "Query" is now implemtned in invoke
return t.query(stub, args)
}
return shim.Error("Invalid invoke function name")
}

业务逻辑 invoke 函数示例

业务逻辑 invoke 函数主要用于实现业务逻辑中的资产转移。本示例中通过调用 API GetState 获取到 KEY 对应的同态加密资产总值,通过调用用户业务逻辑实现资产转移,通过调用 API PutState 将用户最终资产写入账本。 在此过程中,调用了同态加密的接口 GetPublicKeyFromHex 用于获取同态公钥,GetCiphertextFromHex 用于获取同态加密数据,Sub 用于同态密文和明文相减,Add 用于同态密文和明文相加以及 GetCiphertextHex 用于获取同态加密后的16进制密文数据。
// invoke实现两个键之间的value转移,输入为KEY1_NAME, KEY1_PUBKEYINHEX, KEY2_NAME,KEY2_PUBKEYINHEX,VALUE
// 在例子中,KEY1_PUBKEYINHEX是pk1.pai内容的base64, KEY2_PUBKEYINHEX是pk2.pai的内容base64
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A, B string // Entities
var Apkpem, Bpkpem []byte //public key in hex

var Aval, Bval string // Asset encrypted holdings
var X *big.Int // Transaction value
var err error

if len(args) != 5 {
return shim.Error("Incorrect number of arguments. Expecting 5")
}

A = args[0]
Apkpem, err = base64.StdEncoding.DecodeString(args[1])
if err != nil {
return shim.Error("Failed to get Apkpem in hex")
}

B = args[2]
Bpkpem, err = base64.StdEncoding.DecodeString(args[3])
if err != nil{
return shim.Error("Failed to get Bpkpem in hex")
}
X, isOK := new(big.Int).SetString(args[4], 10)
if !isOK {
return shim.Error("Fail to get X")
}

// Get the state from the ledger
// API GetState获取对应账户的资产,这里的资产是同态加密后的数据
Avalbytes, err := stub.GetState(A)
if err != nil {
return shim.Error("Failed to get state")
}
if Avalbytes == nil {
return shim.Error("Entity not found")
}
Aval = string(Avalbytes)

Bvalbytes, err := stub.GetState(B)
if err != nil {
return shim.Error("Failed to get state")
}
if Bvalbytes == nil {
return shim.Error("Entity not found")
}
Bval = string(Bvalbytes)

// 执行具体业务逻辑,这里是对应资产进行转移
// Perform the execution
// 调用同态接口GetPublicKeyFromHex获取公钥信息
Apk, err := paillier.GetPublicKeyFromHex(string(Apkpem))
if err != nil {
return shim.Error("Fail to get A public key in PublicKey")
}
// 调用同态接口GetCiphertextFromHex获取同态密文信息
Acipher, err := paillier.GetCiphertextFromHex(Aval)
if err != nil {
return shim.Error("Fail to get Ciphertext for A")
}


// 调用同态接口Sub执行密文和明文相减
Aciphernew, err := paillier.Sub(Apk, Acipher, X.Text(10))
if err != nil {
return shim.Error("Fail to compute Aciphernew")
}
// 调用同态接口GetCiphertextHex获取同态密文16进制string
Avalnew, err := paillier.GetCiphertextHex(Aciphernew)
if err != nil {
return shim.Error("Fail to get Avalnew")
}

Bpk, err := paillier.GetPublicKeyFromHex(string(Bpkpem))
if err != nil {
return shim.Error("Fail to get B public key in PublicKey")
}

Bcipher, err := paillier.GetCiphertextFromHex(Bval)
if err != nil {
return shim.Error("Fail to get Ciphertext for B")
}
// 调用同态接口Add执行密文和明文相加
Bciphernew, err := paillier.Add(Bpk, Bcipher, X.Text(10))
if err != nil {
return shim.Error("Fail to compute Bciphernew")
}

Bvalnew, err := paillier.GetCiphertextHex(Bciphernew)
if err != nil {
return shim.Error("Fail to get Bvalnew")
}

// API PutState将对应资产写入账本
// Write the state back to the ledger
err = stub.PutState(A, []byte(Avalnew))
if err != nil {
return shim.Error(err.Error())
}

err = stub.PutState(B, []byte(Bvalnew))
if err != nil {
return shim.Error(err.Error())
}

return shim.Success(nil)
}

业务逻辑 query 函数示例

业务逻辑 query 函数主要用于实现业务逻辑中的账户查询功能,本示例通过调用 API GetState 查询对应账户的资产。
// query主要是查询键对应的值,输入为KEY_NAME
// query callback representing the query of a chaincode
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A string // Entities
var err error

if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
}

A = args[0]

// Get the state from the ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
jsonResp := "{\\"Error\\":\\"Failed to get state for " + A + "\\"}"
return shim.Error(jsonResp)
}

if Avalbytes == nil {
jsonResp := "{\\"Error\\":\\"Nil amount for " + A + "\\"}"
return shim.Error(jsonResp)
}

return shim.Success(Avalbytes)
}

Paitool 同态数据解密

依次执行以下命令,通过智能合约从链上获取到最新的业务逻辑操作过的同态密文数据。拥有对应同态私钥的用户,可以解密出相应的明文。
./paitool decrypt -skin=sk1.pai -cipherin=cipher1new.pai
./paitool decrypt -skin=sk2.pai -cipherin=cipher2new.pai
其中,cipher1new.pai 对应的是智能合约中 KEY1_NAME 存储的值,cipher2new.pai 对应的是智能合约中 KEY2_NAME 存储的值。