forked from cgorenflo/fabric
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
147 lines (123 loc) · 4.22 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
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
}