Skip to content

Commit

Permalink
Merge pull request #22 from nqminds/vc_rest_api
Browse files Browse the repository at this point in the history
VC Schemas and VC Rest API
  • Loading branch information
AshleySetter authored Jan 15, 2024
2 parents dccf90c + 29832c2 commit a4fdbbc
Show file tree
Hide file tree
Showing 16 changed files with 543 additions and 114 deletions.
284 changes: 171 additions & 113 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions packages/nist_vc_rest_server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# NIST VC (Verifiable Credential) REST Server

Provides a REST API server with route to sign data matching schemas in @nqminds/nist-brski-schemas and return a VC and a route to verify a VC and return the data contained within it. No authentication has been added to the Rest API as it is intended to be used only locally and not exposed to external traffic.

After the @nqminds/nist-brski-schemas schemas have been instantiated on a volt running on the registrar a Rest API Instance can be started on the registrar which may be used to verify VCs received by the registrar.

A user / process that wishes to communicate information to the registrar must sign into the volt running on the registrar and export their config for the volt. They can then run an instance on the Rest API on their local machine and sign the data they wish to communicate as a VC which can then be communicated to the registrar. At present, only the config of the user who is the owner of the schemas may be used with the Rest API to sign and verify VCs.
45 changes: 45 additions & 0 deletions packages/nist_vc_rest_server/bin/vc-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env node

import http from 'node:http';
import { promisify } from 'node:util';
import express from "express";
import server from "../src/server.js";

const PORT = process.env.PORT ?? 3000;

async function main() {
const app = express();

/** Setup CORS so that other websites can access this server */
app.use((req, res, next) => {
// set the CORS policy
res.header('Access-Control-Allow-Origin', '*');
// set the CORS headers
res.header('Access-Control-Allow-Headers', 'origin, X-Requested-With,Content-Type,Accept, Authorization');
// set the CORS method headers
if (req.method === 'OPTIONS') {
res.header('Access-Control-Allow-Methods', 'GET, PATCH, DELETE, POST');
return res.status(200).json({});
}
next();
});

console.warn(
"CORS: Allowing API connections from any origin.\n" +
"This is a potential security risk."
);

const router = await server();
app.use(router);

const httpServer = http.createServer(app);

await promisify(httpServer.listen).bind(httpServer)(PORT);

console.log(`The server is running on port ${PORT}`)
}

main().catch((error) => {
console.error("Starting the server failed with: ", error);
process.exitCode = 1;
});
3 changes: 3 additions & 0 deletions packages/nist_vc_rest_server/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"voltConfigPath": "../volt-config.json"
}
28 changes: 28 additions & 0 deletions packages/nist_vc_rest_server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "@nqminds/nist_vc_rest_server",
"version": "1.0.0",
"description": "",
"main": "src/server.js",
"bin": "./bin/vc-server.js",
"scripts": {
"dev": "nodemon ./bin/vc-server.js",
"build": "rm -rf build/ && prettier --write src/ && tsc"
},
"nodemonConfig": {
"ext": "js",
"ignore": [],
"delay": "2"
},
"author": "",
"type": "module",
"license": "ISC",
"dependencies": {
"@grpc/grpc-js": "^1.9.13",
"@nqminds/verifiable-schemas-toolchain": "^1.0.3",
"@tdxvolt/volt-client-grpc": "^0.15.1",
"express": "^4.18.2",
"express-openapi-validator": "^5.1.2",
"nodemon": "^3.0.2",
"uuid": "^9.0.1"
}
}
128 changes: 128 additions & 0 deletions packages/nist_vc_rest_server/src/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import {createRequire} from "node:module";
import express from 'express';
import grpc from "@grpc/grpc-js";
import * as OpenApiValidator from 'express-openapi-validator';
import {v4 as uuidv4} from "uuid";

import { VoltClient } from "@tdxvolt/volt-client-grpc";
import {sign, verify} from "@nqminds/verifiable-schemas-toolchain/src/verifiable-credentials.js"

/**
* Automatically calls `next(error)` when an `async function` errors.
*
* @example
* router.get("/my-url/path", asyncHandler(async (req, res) => {
* // automatically calls next(error) on error
* await blah();
* res.send("done");
* }));
*
* @template [P=import("express-serve-static-core").ParamsDictionary]
* @template [ResBody=any]
* @template [ReqBody=any]
* @template [ReqQuery=import("express-serve-static-core").Query]
* @template {Record<string, any>} [LocalsObj=Record<string, any>]
*
* @param {(
* (req: import("express-serve-static-core").Request<P, ResBody, ReqBody, ReqQuery, LocalsObj> & {
* user?: import("@nqminds/taibom-client").User},
* res: import("express-serve-static-core").Response<ResBody, LocalsObj>
* ) => Promise<void>
* )} asyncHandler - The async handler like `asyc (req, res) => {...}`.
* There may be a `req.user` object, if the API route is an authenticated API
* route.
* @returns {import('express').RequestHandler<P, ResBody, ReqBody, ReqQuery, LocalsObj>}
* A callback version of the handler.
*/
function asyncHandler(asyncHandler) {
return function (req, res, next) {
Promise.resolve(asyncHandler(req, res)).catch((error) => next(error));
}
}

/**
* @typedef {object} ServerConfig - Config for NIST VC REST server.
* @property {string} [databaseId] - **Warning**⚠️: This field will be
* overwritten with the ID of the TDX Volt database to use.
* @property {string} serverParentId - ID or Alias of TDX VOLT's parent directory
*/

/**
* Creates an instance of the server API routes.
*
* @param {ServerConfig} [config] The configuration object. If not set,
* will default to the `../config.json` file.
* @param {string} [voltConfigPath] - The path to the TDX Volt Configuration file.
* By default, this is the `../volt-config.json` file.
* @returns {Promise<express.Router>} The express router.
*/
export default async function server(config, voltConfigPath = fileURLToPath(
new URL("../volt-config.json", import.meta.url),
)) {
const router = express.Router(); // eslint-disable-line new-cap

if (!config) {
config = /** @type {ServerConfig} */ (JSON.parse(await readFile(
new URL("../config.json", import.meta.url),
{encoding: "utf8"}),
));
}

if (config.voltConfigPath) {
voltConfigPath = fileURLToPath(new URL(config.voltConfigPath, import.meta.url))
}

const client = new VoltClient(grpc);
await client.initialise(voltConfigPath);
await client.connect();

// Body parser middleware
router.use(express.urlencoded({ extended: false }));
router.use(express.json());

// checks that all requests/responses match OpenAPI spec
const require = createRequire(import.meta.url);

router.use((err, req, res, next) => {
if (err instanceof OpenApiValidator.error.Unauthorized) {
// print a login box when trying to access an API route directly
// workaround for https://github.com/cdimascio/express-openapi-validator/issues/471
res.header("WWW-Authenticate", 'Basic realm="User Visible Realm", charset="UTF-8"');
}
next(err);
});

router.get("/hello", asyncHandler(async (req, res) => {
res.send("hello");
}));

router.post("/sign/:schemaName", asyncHandler(async (req, res) => {
const schemaName = req.params.schemaName;
const claimBody = req.body;
const schemaId = `https://github.com/nqminds/nist-brski/blob/main/packages/schemas/src/${schemaName}.yaml`
const vc = await sign(schemaId,
claimBody,
{client});
res.send(vc)
}));

router.post("/verify/:schemaName", asyncHandler(async (req, res) => {
const schemaName = req.params.schemaName;
const vc = req.body;
const manufacturerTrustSchemaId = `https://github.com/nqminds/nist-brski/blob/main/packages/schemas/src/${schemaName}.yaml`
const verified = await verify(vc, {client});
res.send(verified)
}));

/** Error handling */
router.use((req, res, next) => {
const error = new Error('not found');
return res.status(404).json({
message: error.message
});
});

return router;
}
1 change: 1 addition & 0 deletions packages/schemas/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output
31 changes: 30 additions & 1 deletion packages/schemas/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
# Schemas directory

Schemas for VCs (Verifiable Credentials) to be used for the NIST BRSKI demonstrator, used to securely communicate information to the registrar. These schemas should be instantiated on a volt running on the registrar using [the VC toolchain](https://github.com/nqminds/vc-toolchain).

## To populate a volt with the schemas and set it up for the VC toolchain

### Step 0

### `npm i -g @nqminds/verifiable-schemas-toolchain`
This installs the VC toolchain utilities from https://github.com/nqminds/vc-toolchain such that they may be used globally on the command line.

### Install volt cmd line interface and fusebox from https://docs.tdxvolt.com/en/getting-started/quick-start

### Step 1 - `npm run prepare`
This runs the command `schemaTools parse-yaml-files ./src`. This validates the yaml schemas for the VCs in the src directory and generates the required documentatation & json files in the `./output` directory.

The directory structure should now look like so:
![Alt text](output_file_structure.png)
![Alt text](image.png)
### Step 2 - `schemaTools init --config volt.config.json`
Where the volt.config.json is a volt config file exported for a user on a volt, this initialises the volt connection & builds the schema directory.

![Alt text](volt_export_client_config.png)

### Step 3 - `volt up ./output/* schemas`
This uploads the output files to the schema directory, the structure in the volt should look like so:

![Alt text](volt_schema_structure.png)

## To create new schemas

To create a new schema run, navigate to src and run:
```bash
schemaTools newSchema <schema_name>
schemaTools new-schema <schema_name>
```

This will create a blank schema.yaml for you to edit:
Expand Down
Binary file added packages/schemas/output_file_structure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions packages/schemas/src/device_manufacturer_binding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
$id: https://github.com/nqminds/nist-brski/blob/main/packages/schemas/src/device_manufacturer_binding.yaml
$schema: https://json-schema.org/draft/2019-09/schema
title: Device Manufacturer Binding
description: Binds a manufacturer to a device instance
type: object
properties:
device:
type: string
description: "Id of device being bound"
manufacturer:
type: string
description: "Id of manufacturer device is being bound to"
issuanceDate:
type: string
format: date-time
description: "date-time at which claim was issued"
required:
- device
- manufacturer
- issuanceDate
examples:
- device: 64501d30-fbae-478e-bf72-8b49d47189f0
manufacturer: e16dfb44-5a6d-41f6-b7a7-c3885a0efc42
issuanceDate: "2022-02-05T10:30:00.1Z"
29 changes: 29 additions & 0 deletions packages/schemas/src/device_trust.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
$id: https://github.com/nqminds/nist-brski/blob/main/packages/schemas/src/device_trust.yaml
$schema: https://json-schema.org/draft/2019-09/schema
title: Device Trust
description: Statement of trust of a device to connect to the secure network by a user
type: object
properties:
user:
type: string
description: "Id of user stating trust/distrust of device"
device:
type: string
description: "Id of device user is stating trust/distrust of"
trust:
type: boolean
description: "Trust value"
issuanceDate:
type: string
format: date-time
description: "date-time at which claim was issued"
required:
- user
- device
- trust
- issuanceDate
examples:
- user: 3ec4950f-2306-4298-aa01-80190a74eff3
device: 64501d30-fbae-478e-bf72-8b49d47189f0
trust: True
issuanceDate: "2022-02-05T10:30:00.1Z"
24 changes: 24 additions & 0 deletions packages/schemas/src/device_type_binding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
$id: https://github.com/nqminds/nist-brski/blob/main/packages/schemas/src/device_type_binding.yaml
$schema: https://json-schema.org/draft/2019-09/schema
title: Device Type Binding
description: Binds a device type to a device instance
type: object
properties:
device:
type: string
description: "Id of device being bound"
deviceType:
type: string
description: "Id of device type device instance is being bound to"
issuanceDate:
type: string
format: date-time
description: "date-time at which claim was issued"
required:
- device
- deviceType
- issuanceDate
examples:
- device: 64501d30-fbae-478e-bf72-8b49d47189f0
deviceType: 76e66647-e791-437e-bf03-f3b030ab1220
issuanceDate: "2022-02-05T10:30:00.1Z"
24 changes: 24 additions & 0 deletions packages/schemas/src/device_type_vulnerable.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
$id: https://github.com/nqminds/nist-brski/blob/main/packages/schemas/src/device_type_vulnerable.yaml
$schema: https://json-schema.org/draft/2019-09/schema
title: Device Type Vulnerable
description: States whether a device type is considered vulnerable, whether it is above the allowed vulnerabilities threshold
type: object
properties:
deviceType:
type: string
description: "Id of device type being bound"
vulnerable:
type: boolean
description: "Is device type vulnerable - i.e. it is above allowed vulnerabilities threshold"
issuanceDate:
type: string
format: date-time
description: "date-time at which claim was issued"
required:
- deviceType
- vulnerable
- issuanceDate
examples:
- deviceType: 76e66647-e791-437e-bf03-f3b030ab1220
vulnerable: False
issuanceDate: "2022-02-05T10:30:00.1Z"
29 changes: 29 additions & 0 deletions packages/schemas/src/manufacturer_trust.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
$id: https://github.com/nqminds/nist-brski/blob/main/packages/schemas/src/manufacturer_trust.yaml
$schema: https://json-schema.org/draft/2019-09/schema
title: Manufacturer Trust
description: Statement of trust of a manufacturer by a user - that the manufacturer's MASA should be trusted to be contacted by the registrar
type: object
properties:
user:
type: string
description: "Id of user stating trust/distrust of manufacturer"
manufacturer:
type: string
description: "Id of manufacturer user is stating trust/distrust of"
trust:
type: boolean
description: "Trust value"
issuanceDate:
type: string
format: date-time
description: "date-time at which claim was issued"
required:
- user
- manufacturer
- trust
- issuanceDate
examples:
- user: 3ec4950f-2306-4298-aa01-80190a74eff3
manufacturer: e16dfb44-5a6d-41f6-b7a7-c3885a0efc42
trust: True
issuanceDate: "2022-02-05T10:30:00.1Z"
Binary file added packages/schemas/volt_export_client_config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/schemas/volt_schema_structure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a4fdbbc

Please sign in to comment.