Skip to content

Commit 6176cd5

Browse files
committed
Peer cli assist for client request/response processing
Signed-off-by: michael steiner <[email protected]>
1 parent c5a0db0 commit 6176cd5

File tree

4 files changed

+226
-13
lines changed

4 files changed

+226
-13
lines changed

fabric/bin/peer.sh

+95-2
Original file line numberDiff line numberDiff line change
@@ -500,13 +500,106 @@ handle_lifecycle_chaincode_initEnclave() {
500500
# Chaincode command wrappers
501501
#--------------------------------------------
502502

503+
handle_chaincode_call() {
504+
CMD=$1; shift
505+
506+
OTHER_ARGS=()
507+
while [[ $# > 0 ]]; do
508+
case "$1" in
509+
-C|--channelID)
510+
CHAN_ID=$2;
511+
shift; shift
512+
;;
513+
--peerAddresses)
514+
PEER_ADDRESS=$2
515+
EXPLICIT_PEER=1
516+
shift; shift
517+
;;
518+
-n|--name)
519+
CC_NAME=$2;
520+
shift; shift
521+
;;
522+
-c|--ctor)
523+
CC_MSG=$2;
524+
shift; shift
525+
;;
526+
-I|--isInit)
527+
IS_INIT=$2;
528+
shift; shift
529+
;;
530+
--transient)
531+
TRANSIENT=$2;
532+
shift; shift
533+
;;
534+
*)
535+
# these args we pass to both enclave query and validation invoke
536+
# e.g., --clientauth, --(tls|ca|cert|key)*, --(waitFor|conn*)*, --order*
537+
OTHER_ARGS+=( "$1" )
538+
shift
539+
;;
540+
esac
541+
done
542+
543+
# - iff it is not a fpc pkg
544+
if [ ! -f "${FABRIC_STATE_DIR}/is-fpc-c-chaincode.${CC_NAME}"* ]; then
545+
[ -z ${DEBUG+x} ] || say "non-FPC chaincode"
546+
return
547+
else
548+
[ -z ${DEBUG+x} ] || say "FPC chaincode"
549+
fi
550+
551+
# - check for unsupported options $IS_INIT, $TRANSIENT
552+
[ -z "${IS_INIT}" ] || die "--isInit/-I is not supported for for FPC chaincodes"
553+
[ -z "${TRANSIENT}" ] || die "--transient is not supported for for FPC chaincodes"
554+
555+
556+
557+
# [ -z ${DEBUG+x} ] || ...
558+
559+
560+
# - query ercc.queryChaincodeEncryptionKey for chaincode encryption key
561+
# cc_ek=....
562+
# [ -z ${DEBUG+x} ] || ...
563+
# - if no endpoint specified, query ercc.queryChaincodeEndPoints for endpoint
564+
# [ -z ${DEBUG+x} ] || ...
565+
#
566+
# - start cli assist with input to fd 3 and output from fd 4, a fifo
567+
# result_pipe=$(mktemp -u -t peer_cli_assistXXXX)
568+
# mkfifo $result_pipe || die "can't create fifo '${result_pipe}'"
569+
# exec 3> >(${PEER_ASSIST_CMD} handleRequestAndResponse "${cc_ek}" "${result_pipe}")
570+
# assist_pid=$!
571+
# exec 4<${result_pipe}
572+
# - send clear-text request to assist
573+
# [ -z ${DEBUG+x} ] || ...
574+
# echo >&3 "${request_json}"
575+
# - receive encrypted request from assist
576+
# read <&4 encrypted_request
577+
# [ -z ${DEBUG+x} ] || ...
578+
# - query enclave
579+
# encrypted_response=... endpoint __invoke ${encrypted_request}
580+
# (must include `--peerAddresses PEER_ADDRESS`, but only once?)
581+
# - send encrypted response to assist
582+
# [ -z ${DEBUG+x} ] || ...
583+
# echo >&3 "${encrypted_response}"
584+
# - receive decrypted response from assist
585+
# read <&4 decrypted_response
586+
# [ -z ${DEBUG+x} ] || ...
587+
# - get assist return code
588+
# wait ${assist_pid}
589+
# rc=$?
590+
# [ ${rc} == 0 ] || die "assist could not properly transform requests/responses (rc=${rc})"
591+
# [ -z ${DEBUG+x} ] || ...
592+
# - invoke validation of enclave endorsement
593+
# validation_response=invoke __endorse ${encrypted_response}
594+
}
595+
503596
handle_chaincode_invoke() {
504-
# TODO: eventually add client-side encryption/decryption
597+
handle_chaincode_call "invoke" "$@"
505598
return
506599
}
507600

508601
handle_chaincode_query() {
509-
# TODO: eventually add client-side encryption/decryption
602+
handle_chaincode_call "query" "$@"
510603
return
511604
}
512605

utils/fabric/Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ GO_CMDS= get-fabric-container-name peer-cli-assist
1010

1111
build: $(GO_CMDS)
1212

13-
$(GO_CMDS):
13+
$(GO_CMDS): FORCE
1414
$(GO) build $(GOTAGS) ./$@.src/$@.go
1515

16+
FORCE:
17+
1618
test: build
1719

1820
clean:

utils/fabric/peer-cli-assist.src/peer-cli-assist.go

+100-10
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,39 @@
77
package main
88

99
import (
10+
"bufio"
11+
"encoding/json"
1012
"fmt"
1113
"io/ioutil"
1214
"os"
15+
"strings"
1316

1417
"github.com/hyperledger-labs/fabric-private-chaincode/client_sdk/go/fpc"
18+
"github.com/hyperledger-labs/fabric-private-chaincode/client_sdk/go/fpc/crypto"
19+
"github.com/hyperledger/fabric/common/flogging"
1520
)
1621

22+
var logger = flogging.MustGetLogger("peer-cli-assist")
23+
1724
func printHelp() {
1825
fmt.Printf(
19-
`Usage: %s [attestation2Evidence | createEncryptRequest | processEncryptedResponse]
26+
`Usage: %s [attestation2Evidence | handleRequestAndResponse <cid> <pipe>]
2027
- attestation2Evidence: convert attestation to evidence in (base64-encoded) Credentials protobuf
21-
- createEncryptRequest: create a (base64-encoded) encrypted fpc chaincode request protobuf
22-
- processEncryptedResponse: decrypt and validate an (base64-encoded) encrypted fpc chaincode response protobuf
23-
Input and outpus are via stdin and stdout, respectively.`,
28+
(Input and outpus are via stdin and stdout, respectively.)
29+
- handleRequestAndResponse: handles the encryption of invocation requests as well as the decryption
30+
of the corresponding responses.
31+
Expects three parameters
32+
- <c_ek> the chaincode encryption key, as returned from ercc.QueryChaincodeEncryptionKey
33+
(i.e., a base64-encoded string)
34+
- <pipe> a path to an (existing) fifo file through which the results are communicated back
35+
As input, expects two lines
36+
- a (single line!) json string in peer cli format '{"Function": "...", "Args": [...]}' with the invocation params,
37+
after which it returns (as single line) the (base64-encoded) ChaincodeRequestMessage protobuf, and then
38+
- a (base64-encoded) ChaincodeResponseMessage protobuf, after which it will decrypt it and
39+
return a json-encoded fabric response protobuf object
40+
`,
2441
os.Args[0])
42+
// TODO: above we have to fix the response payload format (json?)
2543
}
2644

2745
func main() {
@@ -45,12 +63,13 @@ func main() {
4563
os.Exit(1)
4664
}
4765
fmt.Printf("%s\n", credentialsStringOut)
48-
case "createEncryptRequest":
49-
fmt.Fprintf(os.Stderr, "FATAL: command %s not yet implemented\n", os.Args[1])
50-
os.Exit(1)
51-
case "processEncryptedResponse":
52-
fmt.Fprintf(os.Stderr, "FATAL: command %s not yet implemented\n", os.Args[1])
53-
os.Exit(1)
66+
case "handleRequestAndResponse":
67+
if len(os.Args) != 4 {
68+
fmt.Fprintf(os.Stderr, "ERROR: command 'handleRequestAndResponse' needs exactly two arguments\n")
69+
printHelp()
70+
os.Exit(1)
71+
}
72+
handleEncryptedRequestAndResponse(os.Args[2], os.Args[3])
5473
default:
5574
fmt.Fprintf(os.Stderr, "ERROR: Illegal command '%s'\n", os.Args[1])
5675
printHelp()
@@ -59,3 +78,74 @@ func main() {
5978

6079
os.Exit(0)
6180
}
81+
82+
func handleEncryptedRequestAndResponse(chaincodeEncryptionKey string, resultPipeName string) {
83+
reader := bufio.NewReader(os.Stdin)
84+
resultPipeFile, err := os.OpenFile(resultPipeName, os.O_APPEND|os.O_WRONLY, 0644)
85+
if err != nil {
86+
fmt.Fprintf(os.Stderr, "ERROR: couldn't open pipe '%s': %v\n", resultPipeName, err)
87+
os.Exit(1)
88+
}
89+
90+
// read request
91+
requestJSON, err := reader.ReadString('\n')
92+
if err != nil {
93+
fmt.Fprintf(os.Stderr, "ERROR: couldn't read json request: %v\n", err)
94+
os.Exit(1)
95+
}
96+
type Request struct {
97+
Function string
98+
Args []string
99+
}
100+
clearRequest := &Request{}
101+
if err := json.Unmarshal([]byte(requestJSON), clearRequest); err != nil {
102+
fmt.Fprintf(os.Stderr, "ERROR: unexpected json '%s': %v\n", requestJSON, err)
103+
os.Exit(1)
104+
}
105+
106+
// setup crypto context
107+
ep := &crypto.EncryptionProviderImpl{
108+
GetCcEncryptionKey: func() ([]byte, error) {
109+
// TODO: might have to do some re-formatting, e.g., de-hex, here?
110+
return []byte(chaincodeEncryptionKey), nil
111+
}}
112+
113+
ctx, err := ep.NewEncryptionContext()
114+
if err != nil {
115+
fmt.Fprintf(os.Stderr, "ERROR: could not setup crypto context: %v\n", err)
116+
os.Exit(1)
117+
}
118+
logger.Debugf("Setup crypto context based on CC-ek '%v'", chaincodeEncryptionKey)
119+
120+
// encrypt request ...
121+
encryptedRequest, err := ctx.Conceal(clearRequest.Function, clearRequest.Args)
122+
if err != nil {
123+
fmt.Fprintf(os.Stderr, "ERROR: could not encrypt request: %v\n", err)
124+
os.Exit(1)
125+
}
126+
// ... and return it
127+
logger.Debugf("Transformed request '%v' to '%v' and write to pipe '%s'", clearRequest, encryptedRequest, resultPipeName)
128+
resultPipeFile.WriteString(fmt.Sprintf("%s\n", encryptedRequest))
129+
130+
// read encrypted response ...
131+
encryptedResponse, err := reader.ReadString('\n')
132+
if err != nil {
133+
fmt.Fprintf(os.Stderr, "ERROR: couldn't read encrypted response: %v\n", err)
134+
os.Exit(1)
135+
}
136+
encryptedResponse = strings.TrimSuffix(encryptedResponse, "\n")
137+
138+
// .. decrypt it ..
139+
// TODO: requires fix in Conceal & ecc/mock
140+
// - should be base64 encoded
141+
// - encrypted response should be a proper (serialized) response object, not only a string, and hence conceal should
142+
// return the deserialized response, not a byte array ..
143+
clearResponse, err := ctx.Reveal([]byte(encryptedResponse))
144+
if err != nil {
145+
fmt.Fprintf(os.Stderr, "ERROR: could not decrypt response: %v\n", err)
146+
os.Exit(1)
147+
}
148+
// TODO: create a (single-line) json encoding once we get above a proper response object ...
149+
logger.Debugf("Transformed response '%v' to '%v' and write to pipe '%s'", encryptedResponse, clearResponse, resultPipeName)
150+
resultPipeFile.WriteString(fmt.Sprintf("%s\n", clearResponse))
151+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/bash
2+
# Copyright 2020 Intel Corporation
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
#set -ex
7+
8+
result_pipe=$(mktemp -u -t testpipeXXXX)
9+
mkfifo $result_pipe
10+
11+
cc_ek="a-key"
12+
exec 3> >( ./peer-cli-assist handleRequestAndResponse "${cc_ek}" "${result_pipe}")
13+
assist_pid=$!
14+
exec 4<${result_pipe}
15+
16+
echo >&3 '{"Function":"init", "Args": ["MyAuctionHouse"]}'
17+
18+
read <&4 encrypted_request
19+
echo "encrypted_request='$encrypted_request'"
20+
21+
echo >&3 "I'm supposed to be an base64 encoded serialized ChaincodeResponseMessage"
22+
read <&4 decrypted_response
23+
echo "decrypted_response='$decrypted_response'"
24+
25+
wait ${assist_pid}
26+
echo "child exit code=$?"
27+
28+
rm $result_pipe

0 commit comments

Comments
 (0)