From 35554d4e65ce08a758b3078c241a8c31490ba0f5 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 16 Nov 2022 18:05:38 +1030 Subject: [PATCH] Support signing all responses using the storage node VDA private key. --- package.json | 2 +- sample.env | 2 ++ src/components/utils.js | 13 +++++++++++ src/controllers/auth.js | 8 +++---- src/controllers/user.js | 29 +++++++++++++------------ test/config.js | 2 +- test/server.js | 17 +++++++++++---- test/utils.js | 19 ++++++++++++++++ yarn.lock | 48 ----------------------------------------- 9 files changed, 68 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index aa6111b0..68630aad 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "cors": "^2.8.5", "did-resolver": "^3.1.0", "dotenv": "^8.2.0", - "ethers": "^4.0.42", "express": "^4.17.1", "express-basic-auth": "git+https://github.com/Mozzler/express-basic-auth.git", "jsonwebtoken": "^8.5.1", @@ -82,6 +81,7 @@ "babel-preset-es2015": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "claudia": "^5.14.1", + "ethers": "^5.7.2", "mocha": "^7.0.0", "nodemon": "^2.0.14", "pouchdb": "^7.2.2", diff --git a/sample.env b/sample.env index 1137169f..e799a137 100644 --- a/sample.env +++ b/sample.env @@ -18,6 +18,8 @@ DB_REFRESH_TOKENS="verida_refresh_tokens" DB_DB_INFO="verida_db_info" # How often garbage collection runs (1=100%, 0.5 = 50%) GC_PERCENT=0.1 +# Verida Private Key as hex string (used to sign responses). Including leading 0x. +VDA_PRIVATE_KEY= // alpha numeric only DB_PUBLIC_USER=784c2n780c9cn0789 diff --git a/src/components/utils.js b/src/components/utils.js index 28d88469..d148de66 100644 --- a/src/components/utils.js +++ b/src/components/utils.js @@ -32,6 +32,19 @@ class Utils { return dids ? dids.map(did => this.generateUsername(did, contextName)) : [] } + signResponse(response, privateKey) { + privateKey = new Uint8Array(Buffer.from(privateKey.substring(2),'hex')) + return EncryptionUtils.signData(response, privateKey) + } + + signedResponse(data, response) { + const signature = this.signResponse(data, process.env.VDA_PRIVATE_KEY) + return response.status(200).send({ + ...data, + signature + }); + } + } let utils = new Utils(); diff --git a/src/controllers/auth.js b/src/controllers/auth.js index 09c37cb8..341f01ae 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -10,10 +10,10 @@ class AuthController { const contextName = req.body.contextName; const authJwt = AuthManager.generateAuthJwt(did, contextName) - return res.status(200).send({ + return Utils.signedResponse({ status: "success", authJwt - }); + }, res); } /** @@ -176,10 +176,10 @@ class AuthController { const isValid = await AuthManager.verifyRefreshToken(refreshToken, contextName) if (isValid) { - return res.status(200).send({ + return Utils.signedResponse({ status: "success", expires: isValid.exp - }) + }, res) } else { return res.status(401).send({ status: "fail" diff --git a/src/controllers/user.js b/src/controllers/user.js index ecb00f11..90dd2a02 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -5,14 +5,14 @@ import Db from '../components/db.js' class UserController { async getPublic(req, res) { - return res.status(200).send({ + return Utils.signedResponse({ status: "success", user: { username: process.env.DB_PUBLIC_USER, password: process.env.DB_PUBLIC_PASS, dsn: Db.buildDsn(process.env.DB_PUBLIC_USER, process.env.DB_PUBLIC_PASS) } - }); + }, res); } // Grant a user access to a user's database @@ -45,9 +45,9 @@ class UserController { if (success) { await DbManager.saveUserDatabase(did, contextName, databaseName, databaseHash, options.permissions) - return res.status(200).send({ + return Utils.signedResponse({ status: "success" - }); + }, res); } } catch (err) { return res.status(400).send({ @@ -85,9 +85,9 @@ class UserController { if (success) { await DbManager.deleteUserDatabase(did, contextName, databaseName, databaseHash) - return res.status(200).send({ + return Utils.signedResponse({ status: "success" - }); + }, res); } } catch (err) { return res.status(500).send({ @@ -130,10 +130,10 @@ class UserController { } }; - return res.status(200).send({ + return Utils.signedResponse({ status: "success", results - }); + }, res); } // Update permissions on a user's database @@ -152,9 +152,9 @@ class UserController { if (success) { await DbManager.saveUserDatabase(did, contextName, databaseName, databaseHash, options.permissions) - return res.status(200).send({ + return Utils.signedResponse({ status: "success" - }); + }, res); } } catch (err) { return res.status(500).send({ @@ -178,11 +178,12 @@ class UserController { try { const result = await DbManager.getUserDatabases(did, contextName) + if (result) { - return res.status(200).send({ + return Utils.signedResponse({ status: "success", result - }); + }, res) } } catch (err) { return res.status(500).send({ @@ -215,10 +216,10 @@ class UserController { const result = await DbManager.getUserDatabase(did, contextName, databaseName) if (result) { - return res.status(200).send({ + return Utils.signedResponse({ status: "success", result - }); + }, res) } else { return res.status(404).send({ status: "fail", diff --git a/test/config.js b/test/config.js index de934218..f3744a5b 100644 --- a/test/config.js +++ b/test/config.js @@ -11,7 +11,7 @@ export default { }, }, ENVIRONMENT: EnvironmentType.TESTNET, - SERVER_URL: `https://sn-acacia1.tn.verida.tech`, + SERVER_URL: `http://localhost:5000`, VDA_PRIVATE_KEY: '0x19d3b996ec98a9a536efdffbae41e5eaaf117765a587483c69195c9460165c34', CONTEXT_NAME: 'Verida Storage Node Test: Test Application 1', DATABASE_SERVER: 'https://sn-acacia1.tn.verida.tech/', // http://localhost:5000/ for local testing when running local @verida/storage-node diff --git a/test/server.js b/test/server.js index ea763638..d994b089 100644 --- a/test/server.js +++ b/test/server.js @@ -1,5 +1,5 @@ import assert from 'assert'; -import dotenv from 'dotenv'; + import Axios from 'axios' import AuthManager from "../src/components/authManager"; @@ -7,6 +7,7 @@ import TestUtils from "./utils" import CONFIG from './config' +import dotenv from 'dotenv'; dotenv.config(); const { CONTEXT_NAME, SERVER_URL, TEST_DEVICE_ID } = CONFIG @@ -150,7 +151,6 @@ describe("Server tests", function() { // Valid response, which is unexpected resolve(false) }).catch((err) => { - console.log('b') if (err.response.data.status == 'fail') { resolve(true) } @@ -174,11 +174,12 @@ describe("Server tests", function() { it("Creates database", async () => { const response = await TestUtils.createDatabase(databaseName, accountInfo.did, CONTEXT_NAME, accessToken) assert.equal(response.data.status, "success", "Successful create response") + assert.ok(TestUtils.verifySignature(response), 'Have a valid signature in response') }) it("Gets active databases for a user", async () => { // create a second database - await Axios.post(`${SERVER_URL}/user/createDatabase`, { + const createResponse = await Axios.post(`${SERVER_URL}/user/createDatabase`, { databaseName: databaseName2, did: accountInfo.did, contextName: CONTEXT_NAME @@ -188,6 +189,8 @@ describe("Server tests", function() { } }); + assert.ok(TestUtils.verifySignature(createResponse), 'Have a valid signature in create response') + const response = await Axios.post(`${SERVER_URL}/user/databases`, { databaseName, did: accountInfo.did, @@ -200,6 +203,7 @@ describe("Server tests", function() { assert.equal(response.data.status, "success", "Successful databases response") assert.ok(response.data.result.length > 1, 'At least two database returned') + assert.ok(TestUtils.verifySignature(response), 'Have a valid signature in databases response') let found1 = false let found2 = false @@ -228,6 +232,8 @@ describe("Server tests", function() { } }); + assert.ok(TestUtils.verifySignature(response), 'Have a valid signature in response') + assert.equal(response.data.status, "success", "Successful database info response") const result = response.data.result @@ -249,6 +255,8 @@ describe("Server tests", function() { } }); + assert.ok(TestUtils.verifySignature(response), 'Have a valid signature in response') + assert.equal(response.data.status, "success", "Successful delete response") const response2 = await Axios.post(`${SERVER_URL}/user/deleteDatabase`, { @@ -284,7 +292,8 @@ describe("Server tests", function() { assert.equal(response.data.results.length, 3, "Deleted three databases") assert.ok(response.data.results.indexOf('DeleteAll_1') >= 0, 'Deleted correct databases (DeleteAll_1)') assert.ok(response.data.results.indexOf('DeleteAll_2') >= 0, 'Deleted correct databases (DeleteAll_2)') - assert.ok(response.data.results.indexOf('DeleteAll_3') >= 0, 'Deleted correct databases (DeleteAll_3)') + assert.ok(response.data.results.indexOf('DeleteAll_3') >= 0, 'Deleted correct databases (DeleteAll_3)') + assert.ok(TestUtils.verifySignature(response), 'Have a valid signature in response') }) diff --git a/test/utils.js b/test/utils.js index 4616cc55..039aa6ee 100644 --- a/test/utils.js +++ b/test/utils.js @@ -2,9 +2,18 @@ import Axios from 'axios' import PouchDb from 'pouchdb' import { AutoAccount } from "@verida/account-node" import { Network } from "@verida/client-ts" +import EncryptionUtils from '@verida/encryption-utils'; +import { ethers } from 'ethers' import CONFIG from './config.js' +import dotenv from 'dotenv'; +dotenv.config(); + +const VDA_PRIVATE_KEY = process.env.VDA_PRIVATE_KEY +const wallet = new ethers.Wallet(VDA_PRIVATE_KEY) +const VDA_PUBLIC_KEY = wallet.publicKey + class Utils { async ensureVeridaAccount(privateKey) { @@ -66,6 +75,16 @@ class Utils { return response } + verifySignature(response) { + if (!response.data.signature) { + return false + } + + const signature = response.data.signature + delete response.data['signature'] + return EncryptionUtils.verifySig(response.data, signature, VDA_PUBLIC_KEY) + } + } const utils = new Utils() diff --git a/yarn.lock b/yarn.lock index 48171b9b..4da09321 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3687,21 +3687,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -ethers@^4.0.42: - version "4.0.49" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894" - integrity sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg== - dependencies: - aes-js "3.0.0" - bn.js "^4.11.9" - elliptic "6.5.4" - hash.js "1.1.3" - js-sha3 "0.5.7" - scrypt-js "2.0.4" - setimmediate "1.0.4" - uuid "2.0.1" - xmlhttprequest "1.8.0" - ethers@^5.5.1: version "5.7.0" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.0.tgz#0055da174b9e076b242b8282638bc94e04b39835" @@ -4207,14 +4192,6 @@ hash-base@^3.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -hash.js@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" - integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.0" - hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" @@ -4540,11 +4517,6 @@ jmespath@0.16.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== -js-sha3@0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" - integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== - js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -5849,11 +5821,6 @@ schema-utils@^2.6.5: ajv "^6.12.4" ajv-keywords "^3.5.2" -scrypt-js@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" - integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== - scrypt-js@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" @@ -5913,11 +5880,6 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -setimmediate@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" - integrity sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog== - setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -6377,11 +6339,6 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" - integrity sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg== - uuid@8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" @@ -6524,11 +6481,6 @@ xmlbuilder@~9.0.1: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ== -xmlhttprequest@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" - integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA== - xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"