From 4d97069a94c10ca460a57822a36b81b19d51ccf2 Mon Sep 17 00:00:00 2001 From: "Mr. Angry" Date: Tue, 13 Sep 2016 02:37:22 -0400 Subject: [PATCH] Adding TLS changes to SDK Merging in TLS related changes from previous work which were mistakenly left out of this branch. These changes were originally made and tested on the Fabric05 branch. Fixes FAB-374 Change-Id: If5b482d00297d840924dab78f35824a4245e1f02 Signed-off-by: Anna D Derbakova Signed-off-by: Allen Bailey --- sdk/node/src/hfc.ts | 115 +++++++++++++++++++++++++++++++-------- sdk/node/src/sdk_util.ts | 3 +- 2 files changed, 94 insertions(+), 24 deletions(-) diff --git a/sdk/node/src/hfc.ts b/sdk/node/src/hfc.ts index e2e33665959..001597cecab 100644 --- a/sdk/node/src/hfc.ts +++ b/sdk/node/src/hfc.ts @@ -43,8 +43,16 @@ * the server side transaction processing path. */ -// Instruct boringssl to use ECC for tls. -process.env['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA'; +process.env.GRPC_SSL_CIPHER_SUITES = process.env.GRPC_SSL_CIPHER_SUITES + ? process.env.GRPC_SSL_CIPHER_SUITES + : 'ECDHE-RSA-AES128-GCM-SHA256:' + + 'ECDHE-RSA-AES128-SHA256:' + + 'ECDHE-RSA-AES256-SHA384:' + + 'ECDHE-RSA-AES256-GCM-SHA384:' + + 'ECDHE-ECDSA-AES128-GCM-SHA256:' + + 'ECDHE-ECDSA-AES128-SHA256:' + + 'ECDHE-ECDSA-AES256-SHA384:' + + 'ECDHE-ECDSA-AES256-GCM-SHA384' ; var debugModule = require('debug'); var fs = require('fs'); @@ -209,6 +217,12 @@ export interface Enrollment { chainKey:string; } +// GRPCOptions +export interface GRPCOptions { + pem: string; + hostnameOverride: string; +} + // A request to get a batch of TCerts export class GetTCertBatchRequest { constructor( public name: string, @@ -346,6 +360,8 @@ export interface DeployRequest extends TransactionRequest { chaincodePath:string; // The name identifier for the chaincode to deploy in development mode. chaincodeName:string; + // The directory on the server side, where the certificate.pem will be copied + certificatePath:string; } /** @@ -461,10 +477,11 @@ export class Chain { /** * Add a peer given an endpoint specification. - * @param endpoint The endpoint of the form: { url: "grpcs://host:port", tls: { .... } } + * @param url The URL of the peer. + * @param opts Optional GRPC options. * @returns {Peer} Returns a new peer. */ - addPeer(url:string, pem?:string):Peer { + addPeer(url:string, opts?:GRPCOptions):Peer { //check to see if the peer is already part of the chain this.peers.forEach(function(peer){ @@ -477,7 +494,7 @@ export class Chain { } }) - let peer = new Peer(url, this, pem); + let peer = new Peer(url, this, opts); this.peers.push(peer); return peer; }; @@ -508,9 +525,10 @@ export class Chain { /** * Set the member services URL * @param {string} url Member services URL of the form: "grpc://host:port" or "grpcs://host:port" + * @param {GRPCOptions} opts optional GRPC options */ - setMemberServicesUrl(url:string, pem?:string):void { - this.setMemberServices(newMemberServices(url,pem)); + setMemberServicesUrl(url:string, opts?:GRPCOptions):void { + this.setMemberServices(newMemberServices(url,opts)); } /** @@ -552,6 +570,20 @@ export class Chain { this.preFetchMode = preFetchMode; } + /** + * Enable or disable ECDSA mode for GRPC. + */ + setECDSAModeForGRPC(enabled:boolean):void { + // TODO: Handle multiple chains in different modes appropriately; this will not currently work + // since it is based env variables. + if (enabled) { + // Instruct boringssl to use ECC for tls. + process.env.GRPC_SSL_CIPHER_SUITES = 'HIGH+ECDSA'; + } else { + delete process.env.GRPC_SSL_CIPHER_SUITES; + } + } + /** * Determine if dev mode is enabled. */ @@ -1677,6 +1709,16 @@ export class TransactionContext extends events.EventEmitter { // Substitute the hashStrHash for the image name dockerFileContents = util.format(dockerFileContents, hash); + // Add the certificate path on the server, if it is being passed in + debug("type of request.certificatePath: " + typeof(request.certificatePath)); + debug("request.certificatePath: " + request.certificatePath); + if (request.certificatePath !== "" && request.certificatePath !== undefined) { + debug("Adding COPY certificate.pem command"); + + dockerFileContents = dockerFileContents + "\n" + "COPY certificate.pem %s"; + dockerFileContents = util.format(dockerFileContents, request.certificatePath); + } + // Create a Docker file with dockerFileContents let dockerFilePath = projDir + "/Dockerfile"; fs.writeFile(dockerFilePath, dockerFileContents, function(err) { @@ -2077,16 +2119,19 @@ export class Peer { * and returns the new Peer. * @param {string} url The URL with format of "grpcs://host:port". * @param {Chain} chain The chain of which this peer is a member. - * @param {string} pem The certificate file, in PEM format, - * to use with the gRPC protocol (that is, with TransportCredentials). - * Required when using the grpcs protocol. + * @param {GRPCOptions} optional GRPC options to use with the gRPC, + * protocol (that is, with TransportCredentials) including a root + * certificate file, in PEM format, and hostnameOverride. A certificate + * is required when using the grpcs (TLS) protocol. * @returns {Peer} The new peer. */ - constructor(url:string, chain:Chain, pem:string) { + constructor(url:string, chain:Chain, opts:GRPCOptions) { this.url = url; this.chain = chain; + let pem = getPemFromOpts(opts); + opts = getOptsFromOpts(opts); this.ep = new Endpoint(url,pem); - this.peerClient = new _fabricProto.Peer(this.ep.addr, this.ep.creds); + this.peerClient = new _fabricProto.Peer(this.ep.addr, this.ep.creds, opts); } /** @@ -2272,16 +2317,14 @@ class MemberServicesImpl implements MemberServices { * @param config The config information required by this member services implementation. * @returns {MemberServices} A MemberServices object. */ - constructor(url:string,pem:string) { + constructor(url:string,opts:GRPCOptions) { + var pem = getPemFromOpts(opts); + opts = getOptsFromOpts(opts); let ep = new Endpoint(url,pem); - var options = { - 'grpc.ssl_target_name_override' : 'tlsca', - 'grpc.default_authority': 'tlsca' - }; - this.ecaaClient = new _caProto.ECAA(ep.addr, ep.creds, options); - this.ecapClient = new _caProto.ECAP(ep.addr, ep.creds, options); - this.tcapClient = new _caProto.TCAP(ep.addr, ep.creds, options); - this.tlscapClient = new _caProto.TLSCAP(ep.addr, ep.creds, options); + this.ecaaClient = new _caProto.ECAA(ep.addr, ep.creds, opts); + this.ecapClient = new _caProto.ECAP(ep.addr, ep.creds, opts); + this.tcapClient = new _caProto.TCAP(ep.addr, ep.creds, opts); + this.tlscapClient = new _caProto.TLSCAP(ep.addr, ep.creds, opts); this.cryptoPrimitives = new crypto.Crypto(DEFAULT_HASH_ALGORITHM, DEFAULT_SECURITY_LEVEL); } @@ -2317,6 +2360,9 @@ class MemberServicesImpl implements MemberServices { this.cryptoPrimitives.setHashAlgorithm(hashAlgorithm); } + /** + * Get the crypto object. + */ getCrypto():crypto.Crypto { return this.cryptoPrimitives; } @@ -2599,8 +2645,8 @@ class MemberServicesImpl implements MemberServices { } // end MemberServicesImpl -function newMemberServices(url,pem) { - return new MemberServicesImpl(url,pem); +function newMemberServices(url:string,opts:GRPCOptions) { + return new MemberServicesImpl(url,opts); } /** @@ -2709,6 +2755,29 @@ function rolesToMask(roles?:string[]):number { return mask; } +// Get the PEM from the options +function getPemFromOpts(opts:any):string { + if (isObject(opts)) return opts.pem; + return opts; +} + +// Normalize opts +function getOptsFromOpts(opts:any):GRPCOptions { + if (isObject(opts)) { + delete opts.pem; + if (opts.hostnameOverride) { + opts['grpc.ssl_target_name_override'] = opts.hostnameOverride; + opts['grpc.default_authority'] = opts.hostnameOverride; + delete opts.hostnameOverride; + } + return opts; + } + if (isString(opts)) { + // backwards compatible to handle pem as opts + return { pem: opts }; + } +} + function endsWith(str:string, suffix:string) { return str.length >= suffix.length && str.substr(str.length - suffix.length) === suffix; }; diff --git a/sdk/node/src/sdk_util.ts b/sdk/node/src/sdk_util.ts index cc3bdb6d251..0253e538b45 100644 --- a/sdk/node/src/sdk_util.ts +++ b/sdk/node/src/sdk_util.ts @@ -149,7 +149,8 @@ export function GenerateTarGz(src, dest, cb) { ".yaml", ".json", ".c", - ".h" + ".h", + ".pem" ]; // Create the pack stream specifying the ignore/filtering function