docker-compose-files/hyperledger_fabric/latest/examples/chaincode/go/enccc_example/utils.go

148 lines
4.2 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"encoding/json"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/chaincode/shim/ext/entities"
"github.com/pkg/errors"
)
// the functions below show some best practices on how
// to use entities to perform cryptographic operations
// over the ledger state
// getStateAndDecrypt retrieves the value associated to key,
// decrypts it with the supplied entity and returns the result
// of the decryption
func getStateAndDecrypt(stub shim.ChaincodeStubInterface, ent entities.Encrypter, key string) ([]byte, error) {
// at first we retrieve the ciphertext from the ledger
ciphertext, err := stub.GetState(key)
if err != nil {
return nil, err
}
// GetState will return a nil slice if the key does not exist.
// Note that the chaincode logic may want to distinguish between
// nil slice (key doesn't exist in state db) and empty slice
// (key found in state db but value is empty). We do not
// distinguish the case here
if len(ciphertext) == 0 {
return nil, errors.New("no ciphertext to decrypt")
}
return ent.Decrypt(ciphertext)
}
// encryptAndPutState encrypts the supplied value using the
// supplied entity and puts it to the ledger associated to
// the supplied KVS key
func encryptAndPutState(stub shim.ChaincodeStubInterface, ent entities.Encrypter, key string, value []byte) error {
// at first we use the supplied entity to encrypt the value
ciphertext, err := ent.Encrypt(value)
if err != nil {
return err
}
return stub.PutState(key, ciphertext)
}
// getStateDecryptAndVerify retrieves the value associated to key,
// decrypts it with the supplied entity, verifies the signature
// over it and returns the result of the decryption in case of
// success
func getStateDecryptAndVerify(stub shim.ChaincodeStubInterface, ent entities.EncrypterSignerEntity, key string) ([]byte, error) {
// here we retrieve and decrypt the state associated to key
val, err := getStateAndDecrypt(stub, ent, key)
if err != nil {
return nil, err
}
// we unmarshal a SignedMessage from the decrypted state
msg := &entities.SignedMessage{}
err = msg.FromBytes(val)
if err != nil {
return nil, err
}
// we verify the signature
ok, err := msg.Verify(ent)
if err != nil {
return nil, err
} else if !ok {
return nil, errors.New("invalid signature")
}
return msg.Payload, nil
}
// signEncryptAndPutState signs the supplied value, encrypts
// the supplied value together with its signature using the
// supplied entity and puts it to the ledger associated to
// the supplied KVS key
func signEncryptAndPutState(stub shim.ChaincodeStubInterface, ent entities.EncrypterSignerEntity, key string, value []byte) error {
// here we create a SignedMessage, set its payload
// to value and the ID of the entity and
// sign it with the entity
msg := &entities.SignedMessage{Payload: value, ID: []byte(ent.ID())}
err := msg.Sign(ent)
if err != nil {
return err
}
// here we serialize the SignedMessage
b, err := msg.ToBytes()
if err != nil {
return err
}
// here we encrypt the serialized version associated to args[0]
return encryptAndPutState(stub, ent, key, b)
}
type keyValuePair struct {
Key string `json:"key"`
Value string `json:"value"`
}
// getStateByRangeAndDecrypt retrieves a range of KVS pairs from the
// ledger and decrypts each value with the supplied entity; it returns
// a json-marshalled slice of keyValuePair
func getStateByRangeAndDecrypt(stub shim.ChaincodeStubInterface, ent entities.Encrypter, startKey, endKey string) ([]byte, error) {
// we call get state by range to go through the entire range
iterator, err := stub.GetStateByRange(startKey, endKey)
if err != nil {
return nil, err
}
defer iterator.Close()
// we decrypt each entry - the assumption is that they have all been encrypted with the same key
keyvalueset := []keyValuePair{}
for iterator.HasNext() {
el, err := iterator.Next()
if err != nil {
return nil, err
}
v, err := ent.Decrypt(el.Value)
if err != nil {
return nil, err
}
keyvalueset = append(keyvalueset, keyValuePair{el.Key, string(v)})
}
bytes, err := json.Marshal(keyvalueset)
if err != nil {
return nil, err
}
return bytes, nil
}