148 lines
4.2 KiB
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
|
|
}
|