Skip to content

Commit

Permalink
feat(connector-fabric): support for FabricSigningCredentialType.WsX509
Browse files Browse the repository at this point in the history
A secure identity provider based on web-sockets and the we WS.X-509 identity credential
Allow a fabric app to securely connect to a users external keystore client. Requires:
- brioux/ws-wallet as a prototype crypto wallet to setup the WS-X.509 identity
- brioux/ws-identity server generates new session tickets for the target Fabric app
  and handles connection authentication and communication with ws-wallet client

* brioux/ws-identity-client package for calls from ws-wallet or fabric connector to ws-identity

Behaviour:
- Fabric app requests session ticket from ws-identity referencing target user's public address (hex)
- Ticket is sent to user's ws-wallet for signing. Provide Session ID and its signature as API tokens
- ws-wallet passes API tokens to ws-identity server for authentication and to open web-socket
- User provides API tokens to authorize Fabric app to send signature requests to ws-wallet

Note two API tokens are required:
 1. Unique Session ID requested by Fabric APP
 2. Signature verifying ownership of the public address referenced by the Session ID

The signature is not stored by the ws-identity server after authentication
Session ID points to the active web-socket required by the Fabric APP to
generate or use an enrolled WS-X.509 identity credential.

Using image ghcr.io/brioux/ws-identity:0.0.1 for testing
Dont forget to bump ws-wallet dev dependency when updating

Signed-off-by: brioux <Bertrand.rioux@gmail.com>
  • Loading branch information
brioux authored and petermetz committed Oct 26, 2021
1 parent df94397 commit 50e666f
Show file tree
Hide file tree
Showing 17 changed files with 939 additions and 30 deletions.
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"authzn",
"Besu",
"Bools",
"brioux",
"cafile",
"caio",
"cccs",
Expand Down
50 changes: 43 additions & 7 deletions packages/cactus-plugin-ledger-connector-fabric/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ The above diagram shows the sequence diagraom of transact() method of the Plugin

![run-transaction-endpoint-enroll](docs/architecture/images/run-transaction-endpoint-enroll.png)

The above diagram shows the sequence diagraom of enroll() method of the PluginLedgerConnectorFabric class. The caller to this function, which in reference to the above sequence diagram is API server, sends Signer object along with EnrollmentRequest as an argument to the enroll() method. Based on the singerType (FabricSigningCredentialType.X509, FabricSigningCredentialType.VaultX509 .. more in TODO), corresponding identity is enrolled and stored inside keychain.
The above diagram shows the sequence diagraom of enroll() method of the PluginLedgerConnectorFabric class. The caller to this function, which in reference to the above sequence diagram is API server, sends Signer object along with EnrollmentRequest as an argument to the enroll() method. Based on the singerType (FabricSigningCredentialType.X509, FabricSigningCredentialType.VaultX509, FabricSigningCredentialType.WsX509), corresponding identity is enrolled and stored inside keychain.

## Usage

Expand Down Expand Up @@ -78,30 +78,40 @@ const vaultConfig:IVaultConfig = {
endpoint : "http://localhost:8200",
transitEngineMountPath: "/transit",
}
// web-socket server config for supporting vault identity provider
const webSocketConfig:IVaultConfig = {
server: socketServer as FabricSocketServer
}
// provide list of identity signing to be supported
const supportedIdentity:FabricSigningCredentialType[] = [FabricSigningCredentialType.VaultX509,FabricSigningCredentialType.X509]
const supportedIdentity:FabricSigningCredentialType[] = [FabricSigningCredentialType.VaultX509,FabricSigningCredentialType.WsX509,FabricSigningCredentialType.X509]
const pluginOptions:IPluginLedgerConnectorFabricOptions = {
// other options
vaultConfig : vaultConfig,
webSocketConfig : webSocketConfig,
supportedIdentity:supportedIdentity
// .. other options
}
const connector: PluginLedgerConnectorFabric = new PluginLedgerConnectorFabric(pluginOptions);
```

To enroll an identity
```typescript
await connector.enroll(
{
keychainId: "keychain-identifier-for storing-certData",
keychainRef: "cert-data-identifier",
type: FabricSigningCredentialType.VaultX509, // FabricSigningCredentialType.X509

// require in case of vault
type: FabricSigningCredentialType.VaultX509, // FabricSigningCredentialType.X509
vaultTransitKey: {
token: "vault-token",
keyName: "vault-key-label",
},
// required in case of web-socket server
type: FabricSigningCredentialType.WsX509,
webSocketKey: {
signature: signature,
sessionId: sessionId,
},
},
{
enrollmentID: "client2",
Expand All @@ -111,19 +121,26 @@ await connector.enroll(
},
);
```

To Register an identity using register's key
```typescript
const secret = await connector.register(
{
keychainId: "keychain-id-that-store-certData-of-registrar",
keychainRef: "certData-label",
type: FabricSigningCredentialType.VaultX509, // FabricSigningCredentialType.X509

// require in case of vault
type: FabricSigningCredentialType.VaultX509, // FabricSigningCredentialType.X509
vaultTransitKey: {
token: testToken,
keyName: registrarKey,
},
// required in case of web-socket server
type: FabricSigningCredentialType.WsX509,
webSocketKey: {
signature: signature,
sessionId: sessionId,
},
},
{
enrollmentID: "client-enrollmentID",
Expand All @@ -140,13 +157,19 @@ const resp = await connector.transact{
signingCredential: {
keychainId: keychainId,
keychainRef: "client-certData-id",
type: FabricSigningCredentialType.VaultX509, // FabricSigningCredentialType.X509

// require in case of vault
type: FabricSigningCredentialType.VaultX509, // FabricSigningCredentialType.X509
vaultTransitKey: {
token: testToken,
keyName: registrarKey,
},
// required in case of web-socket server
type: FabricSigningCredentialType.WsX509,
webSocketKey: {
signature: signature,
sessionId: sessionId,
},
},
// .. other options
}
Expand All @@ -165,6 +188,9 @@ await connector.rotateKey(
token: testToken,
keyName: registrarKey,
},
// key rotation currently not available using web-socket server
// web-socket connection not used to manages external keys
// user should re-enroll with new pub/priv key pair
},
{
enrollmentID: string;
Expand Down Expand Up @@ -195,7 +221,16 @@ Currently Cactus Fabric Connector supports following Identity Providers

- X509 : Simple and unsecured provider wherein `private` key is stored along with certificate in some `datastore`. Whenever connector require signature on fabric message, private key is brought from the `datastore` and message signed at the connector.
- Vault-X.509 : Secure provider wherein `private` key is stored with vault's transit transit engine and certificate in `certDatastore`. Rather then bringing the key to the connector, message digest are sent to the vault server which returns the `signature`.
- WS-X.509 (Future Work) : Secure provider wherein `private` key is stored with `client` and certificate in `certDatastore`. To get the fabric messages signed, message digest is sent to the client via `webSocket` connection opened by the client in the beginning.
- WS-X.509 : Secure provider wherein `private` key is stored with `client` and certificate in `certDatastore`. To get the fabric messages signed, message digest is sent to the client via `webSocket` connection opened by the client in the beginning (as described above)

### setting up a WS-X.509 provider
The following packages are used to access private keys (via web-socket) stored on a clients external device (e.g., browser, mobile app, or an IoT device...).
-[ws-identity](https://github.com/brioux/ws-identity): web-socket server that issues new ws-session tickets, authenticates incoming connections, and sends signature requests
-[ws-identity-client](https://github.com/brioux/ws-identity-client): backend connector to send requests from fabric application to ws-identity
-[ws-wallet](https://github.com/brioux/ws-wallet): external clients crypto key tool: create new key pair, request session ticket and open web-socket connection with ws-identity

### Building the ws-identity docker image


## Running the tests

Expand All @@ -218,6 +253,7 @@ Build with a specific version of the npm package:
DOCKER_BUILDKIT=1 docker build --build-arg NPM_PKG_VERSION=0.4.1 -f ./packages/cactus-plugin-ledger-connector-fabric/Dockerfile . -t cplcb
```


#### Running the container

Launch container with plugin configuration as an **environment variable**:
Expand Down
6 changes: 4 additions & 2 deletions packages/cactus-plugin-ledger-connector-fabric/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@
"prom-client": "13.2.0",
"temp": "0.9.4",
"typescript-optional": "2.0.1",
"uuid": "8.3.2"
"uuid": "8.3.2",
"ws-identity-client": "1.0.2"
},
"devDependencies": {
"@hyperledger/cactus-plugin-keychain-memory": "1.0.0-rc.1",
Expand All @@ -104,6 +105,7 @@
"@types/node-vault": "0.9.13",
"@types/temp": "0.9.1",
"@types/uuid": "8.3.1",
"fs-extra": "10.0.0"
"fs-extra": "10.0.0",
"ws-wallet": "1.1.5"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,37 @@
},
"description": "vault key details for signing fabric message with private key stored with transit engine."
},
"WebSocketKey" : {
"type": "object",
"nullable": false,
"required": [
"sessionId",
"signature"
],
"properties": {
"sessionId": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"nullable": false,
"description": "session Id to access client"
},
"signature": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"nullable": false,
"description": "signature of the session ID"
}
},
"description": "web-socket key details for signing fabric message with private key stored with external client"
},
"FabricSigningCredentialType" : {
"type": "string",
"enum": [
"X.509",
"Vault-X.509"
"Vault-X.509",
"WS-X.509"
],
"nullable": false,
"description": "different type of identity provider for singing fabric messages supported by this package"
Expand Down Expand Up @@ -110,6 +136,10 @@
"vaultTransitKey" : {
"$ref" : "#/components/schemas/VaultTransitKey",
"properties" : "vault key details , if Vault-X.509 identity provider to be used for singing fabric messages"
},
"webSocketKey" : {
"$ref" : "#/components/schemas/WebSocketKey",
"properties" : "web-socket key details , if WS-X.509 identity provider to be used for singing fabric messages"
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ export async function createGateway(
keyName: ctx.gatewayOptions.wallet.keychain.vaultTransitKey.keyName,
});
break;
case FabricSigningCredentialType.WsX509:
if (
!ctx.gatewayOptions.wallet.keychain ||
!ctx.gatewayOptions.wallet.keychain.webSocketKey
) {
throw new Error(
`require ctx.gatewayOptions.wallet.keychain.webSocketKey`,
);
}
key = ctx.secureIdentity.getWebSocketKey({
sessionId: ctx.gatewayOptions.wallet.keychain.webSocketKey.sessionId,
signature: ctx.gatewayOptions.wallet.keychain.webSocketKey.signature,
});
break;
case FabricSigningCredentialType.X509:
key = ctx.secureIdentity.getDefaultKey({
private: certData.credentials.privateKey as string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,12 @@ export interface FabricSigningCredential {
* @memberof FabricSigningCredential
*/
vaultTransitKey?: VaultTransitKey;
/**
*
* @type {WebSocketKey}
* @memberof FabricSigningCredential
*/
webSocketKey?: WebSocketKey;
}
/**
* different type of identity provider for singing fabric messages supported by this package
Expand All @@ -560,7 +566,8 @@ export interface FabricSigningCredential {

export enum FabricSigningCredentialType {
X509 = 'X.509',
VaultX509 = 'Vault-X.509'
VaultX509 = 'Vault-X.509',
WsX509 = 'WS-X.509'
}

/**
Expand Down Expand Up @@ -979,6 +986,25 @@ export interface VaultTransitKey {
*/
token: string;
}
/**
* web-socket key details for signing fabric message with private key stored with external client
* @export
* @interface WebSocketKey
*/
export interface WebSocketKey {
/**
* session Id to access client
* @type {string}
* @memberof WebSocketKey
*/
sessionId: string;
/**
* signature of the session ID
* @type {string}
* @memberof WebSocketKey
*/
signature: string;
}

/**
* DefaultApi - axios parameter creator
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
import { IdentityProvider, IdentityData, Identity } from "fabric-network";
import { FabricSigningCredentialType } from "../generated/openapi/typescript-axios/api";
import { Checks } from "@hyperledger/cactus-common";
import { ICryptoSuite, User, Utils, ICryptoKey } from "fabric-common";
import {
Checks,
LogLevelDesc,
Logger,
LoggerProvider,
} from "@hyperledger/cactus-common";
import { Key } from "./internal/key";
import { InternalCryptoSuite } from "./internal/crypto-suite";
import { VaultTransitClient } from "./vault-client";
import { WebSocketClient } from "./web-socket-client";

export interface IVaultConfig {
endpoint: string;
transitEngineMountPath: string;
}

export interface IWebSocketConfig {
endpoint: string;
pathPrefix: string;
strictSSL?: boolean;
}

export interface ISecureIdentityProvidersOptions {
activatedProviders: FabricSigningCredentialType[];
logLevel: LogLevelDesc;

// vault server config
vaultConfig?: IVaultConfig;
webSocketConfig?: IWebSocketConfig;
}

export interface IIdentity extends Identity {
Expand All @@ -37,6 +44,10 @@ export interface VaultKey {
token: string;
}

export interface WebSocketKey {
sessionId: string;
signature: string;
}
export interface DefaultKey {
// pem encoded private key
private: string;
Expand Down Expand Up @@ -70,6 +81,14 @@ export class SecureIdentityProviders implements IdentityProvider {
);
this.log.debug(`${fnTag} Vault-X.509 identity provider activated`);
}
if (opts.activatedProviders.includes(FabricSigningCredentialType.WsX509)) {
if (!opts.webSocketConfig) {
throw new Error(`${fnTag} require options.webSocketConfig`);
}
this.log.debug(
`${fnTag} WS-X.509 identity provider for host ${opts.webSocketConfig?.endpoint}${opts.webSocketConfig?.pathPrefix}`, //`,
);
}
this.defaultSuite = Utils.newCryptoSuite();
}

Expand Down Expand Up @@ -115,11 +134,21 @@ export class SecureIdentityProviders implements IdentityProvider {
}),
);
}
getWebSocketKey(key: WebSocketKey): Key {
const client = new WebSocketClient({
endpoint: this.opts.webSocketConfig?.endpoint as string,
pathPrefix: this.opts.webSocketConfig?.pathPrefix as string,
signature: key.signature,
sessionId: key.sessionId,
strictSSL: this.opts.webSocketConfig?.strictSSL,
logLevel: this.opts.logLevel,
});
return new Key(`sessionId: ${key.sessionId}`, client);
}

getDefaultKey(key: DefaultKey): ICryptoKey {
return this.defaultSuite.createKeyFromRaw(key.private);
}

// not required things
readonly type = "";
getCryptoSuite(): ICryptoSuite {
Expand Down
Loading

0 comments on commit 50e666f

Please sign in to comment.