From b172a245b1255d610d4ec520c2b895308e5bfe33 Mon Sep 17 00:00:00 2001 From: Sebastian Gerske Date: Mon, 22 Oct 2018 12:46:27 +0200 Subject: [PATCH] added encrypt and decrypt document --- src/secretstore/DocumentKeys.ts | 5 -- src/secretstore/HexHelpler.ts | 10 +++ src/secretstore/ParityClient.ts | 31 ++++----- src/secretstore/SecretStore.ts | 83 ++++++++++++++++--------- src/secretstore/SecretStoreClient.ts | 64 +++++++++---------- src/secretstore/keys/GeneratedKey.ts | 6 ++ src/secretstore/keys/KeyBase.ts | 3 + src/secretstore/keys/RetrievedKey.ts | 6 ++ test/secretstore/ParityClient.test_.ts | 24 ++++--- test/secretstore/SecretStore.test_.ts | 29 ++++++--- test/secretstore/keys/GeneratedKey.json | 5 ++ test/secretstore/keys/RetrievedKey.json | 8 +++ test/secretstore/keys/ServerKey.json | 1 + test/tsconfig.json | 1 + 14 files changed, 177 insertions(+), 99 deletions(-) delete mode 100644 src/secretstore/DocumentKeys.ts create mode 100644 src/secretstore/HexHelpler.ts create mode 100644 src/secretstore/keys/GeneratedKey.ts create mode 100644 src/secretstore/keys/KeyBase.ts create mode 100644 src/secretstore/keys/RetrievedKey.ts create mode 100644 test/secretstore/keys/GeneratedKey.json create mode 100644 test/secretstore/keys/RetrievedKey.json create mode 100644 test/secretstore/keys/ServerKey.json diff --git a/src/secretstore/DocumentKeys.ts b/src/secretstore/DocumentKeys.ts deleted file mode 100644 index 1adb0ea..0000000 --- a/src/secretstore/DocumentKeys.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default class DocumentKeys { - commonPoint: string - encryptedKey: string - encryptedPoint: string -} diff --git a/src/secretstore/HexHelpler.ts b/src/secretstore/HexHelpler.ts new file mode 100644 index 0000000..a9ec07d --- /dev/null +++ b/src/secretstore/HexHelpler.ts @@ -0,0 +1,10 @@ +export default class HexHelpler { + public static removeLeading0xPrefix(key) { + return key.startsWith("0x") ? key.replace("0x", "") : key + } + + public static add0xPrefix(key) { + return key.startsWith("0x") ? key : "0x" + key + } + +} diff --git a/src/secretstore/ParityClient.ts b/src/secretstore/ParityClient.ts index cdaf8e7..e3e4c84 100644 --- a/src/secretstore/ParityClient.ts +++ b/src/secretstore/ParityClient.ts @@ -2,11 +2,8 @@ import * as jayson from "jayson" import {Client} from "jayson" import {URL} from "url" import Logger from "../utils/Logger" -import DocumentKeys from "./DocumentKeys" - -function add0xPrefix(key) { - return key.startsWith("0x") ? key : "0x" + key -} +import HexHelpler from "./HexHelpler" +import GeneratedKey from "./keys/GeneratedKey" export default class ParityClient { @@ -23,7 +20,7 @@ export default class ParityClient { public async signKeyId(keyId): Promise { return this.sendJsonRpcRequest(this.rpcClient, "secretstore_signRawHash", - [this.address, this.password, add0xPrefix(keyId)]) + [this.address, this.password, HexHelpler.add0xPrefix(keyId)]) .then((result: string) => { return result }) @@ -34,32 +31,36 @@ export default class ParityClient { "secretstore_generateDocumentKey", [this.address, this.password, serverKey]) .then((result: any) => { - return { + const generatedKey = { commonPoint: result.common_point, encryptedKey: result.encrypted_key, encryptedPoint: result.encrypted_point, - } as DocumentKeys + } as GeneratedKey + return generatedKey }) } - public encryptDocument(encryptedKey, document: any): Promise { + public encryptDocument(encryptedKey: string, document: any): Promise { // `document` must be encoded in hex when sent to encryption - return this.sendJsonRpcRequest(this.rpcClient, "secretstore_encrypt", - [this.address, this.password, encryptedKey, - add0xPrefix(new Buffer(JSON.stringify(document)).toString("hex"))]) + const documentString = JSON.stringify(document) + const documentHexed = HexHelpler.add0xPrefix(new Buffer(documentString).toString("hex")) + return this.sendJsonRpcRequest(this.rpcClient, + "secretstore_encrypt", + [this.address, this.password, encryptedKey, documentHexed]) .then((result: string) => { return result }) } public decryptDocument(decryptedSecret: string, commonPoint: string, - decryptShadows: string, encryptedDocument: string): Promise { + decryptShadows: string[], encryptedDocument: string): Promise { return this.sendJsonRpcRequest(this.rpcClient, "secretstore_shadowDecrypt", [this.address, this.password, decryptedSecret, commonPoint, decryptShadows, encryptedDocument]) .then((result: string) => { - return result + const documentString = new Buffer(HexHelpler.removeLeading0xPrefix(result), "hex").toString("utf8") + return JSON.parse(documentString) }) } @@ -77,4 +78,4 @@ export default class ParityClient { }) }) } -} \ No newline at end of file +} diff --git a/src/secretstore/SecretStore.ts b/src/secretstore/SecretStore.ts index 59b6150..9b5becb 100644 --- a/src/secretstore/SecretStore.ts +++ b/src/secretstore/SecretStore.ts @@ -1,4 +1,5 @@ -import Logger from "../utils/Logger" +import GeneratedKey from "./keys/GeneratedKey" +import RetrievedKey from "./keys/RetrievedKey" import ParityClient from "./ParityClient" import SecretStoreClient from "./SecretStoreClient" @@ -13,53 +14,77 @@ export default class SecretStore { url: config.parityUrl, address: config.address, password: config.password, }) - this.secretStoreClient = new SecretStoreClient(config.secretStoreUrl) + this.secretStoreClient = new SecretStoreClient({url: config.secretStoreUrl}) } - public async generateServerKey(serverKeyId: string): Promise { + public async encryptDocument(serverKeyId: string, document: any): Promise { + const serverKey = await this.generateServerKey(serverKeyId) + // generate document key + const documentKey: GeneratedKey = await this.generateDocumentKey(serverKey) + + // store the document key in secret store + await this.storeDocumentKey(serverKeyId, documentKey) + + // encrypt document + const encryptedDocument = + await this.partiyClient.encryptDocument(documentKey.encryptedKey, document) + return encryptedDocument + } + + public async decryptDocument(serverkeyId: string, encryptedDocument: string): Promise { + + // get document key from secret store + const documentKey: RetrievedKey = await this.retrieveDocumentKey(serverkeyId) + + const decryptDocument: any = await this.partiyClient.decryptDocument( + documentKey.decryptedSecret, documentKey.commonPoint, + documentKey.decryptShadows, encryptedDocument) + + return decryptDocument + } + + private async generateServerKey(serverKeyId: string): Promise { + + // sign server key id const serverKeyIdSig = await this.partiyClient.signKeyId(serverKeyId) - Logger.log("serverKeyId:", serverKeyId, "serverKeyIdSig:", serverKeyIdSig) + // Logger.log("serverKeyId:", serverKeyId, "serverKeyIdSig:", serverKeyIdSig) - const key = await this.secretStoreClient.generateServerKey(serverKeyId, serverKeyIdSig) + // generate server key + const serverKey = await this.secretStoreClient.generateServerKey(serverKeyId, serverKeyIdSig) - Logger.log("key:", key) - - return key + return serverKey } - public async storeDocumentKey(serverKeyId: string, documentKeyId): Promise { - - const serverKeyIdSig = await this.partiyClient.signKeyId(serverKeyId) - Logger.log("serverKeyId:", serverKeyId, "serverKeyIdSig:", serverKeyIdSig) - const serverKey = await this.secretStoreClient.generateServerKey( - serverKeyId, serverKeyIdSig) - Logger.log("key:", serverKey) - - const documentKey = this.partiyClient.generateDocumentKeyFromKey(serverKey) - - return documentKey + private async generateDocumentKey(serverKey: string): Promise { + // generate document key from server key + const documentKeys: GeneratedKey = await this.partiyClient.generateDocumentKeyFromKey(serverKey) + return documentKeys } - public async retrieveDocumentKey(serverKeyId: string): Promise { + private async storeDocumentKey(serverKeyId: string, documentKeys: GeneratedKey): Promise { + // sign server key id const serverKeyIdSig = await this.partiyClient.signKeyId(serverKeyId) - Logger.log("serverKeyId:", serverKeyId, "serverKeyIdSig:", serverKeyIdSig) + // store document key in secret store + await this.secretStoreClient.storeDocumentKey(serverKeyId, serverKeyIdSig, + documentKeys.commonPoint, documentKeys.encryptedPoint) - const key = await this.secretStoreClient.retrieveDocumentKey(serverKeyId, serverKeyIdSig) - - Logger.log("key:", key) - - return key + return true } - public encryptDocument(document: string) { - } + private async retrieveDocumentKey(serverKeyId: string): Promise { - public decryptDocument(encryptedDocument: string) { + // sign server key id + const serverKeyIdSig = await this.partiyClient.signKeyId(serverKeyId) + // Logger.log("serverKeyId:", serverKeyId, "serverKeyIdSig:", serverKeyIdSig) + + // retrieve document key from secret store + const documentKeys: RetrievedKey = await this.secretStoreClient.retrieveDocumentKey(serverKeyId, serverKeyIdSig) + return documentKeys } } diff --git a/src/secretstore/SecretStoreClient.ts b/src/secretstore/SecretStoreClient.ts index a2b4ce9..9d5f3dd 100644 --- a/src/secretstore/SecretStoreClient.ts +++ b/src/secretstore/SecretStoreClient.ts @@ -1,25 +1,27 @@ import fetch from "node-fetch" -import Logger from "../utils/Logger" - -function removeLeading0xPrefix(key) { - return key.startsWith("0x") ? key.replace("0x", "") : key -} +import HexHelpler from "./HexHelpler" +import RetrievedKey from "./keys/RetrievedKey" export default class SecretStoreClient { - constructor(private url: string, private threshold?: number) { - this.url = url - this.threshold = threshold || 1 + private threshold: number + private url: string + + constructor(config: { url: string, threshold?: number }) { + this.url = config.url + this.threshold = config.threshold || 1 } public async generateServerKey(serverKeyId: string, serverKeyIdSig: string): Promise { const url = [ this.url, "shadow", serverKeyId, - removeLeading0xPrefix(serverKeyIdSig), + HexHelpler.removeLeading0xPrefix(serverKeyIdSig), this.threshold, ].join("/") + // Logger.log("url", url) + const result = await fetch(url, { method: "POST", }) @@ -40,27 +42,20 @@ export default class SecretStoreClient { return result } - /* - curl -X POST http://localhost:8082/shadow/ - 0000000000000000000000000000000000000000000000000000000000000000/ - de12681e0b8f7a428f12a6694a5f7e1324deef3d627744d95d51b862afc13799251831b3611ae436c452b54cdf5c4e78b361a396ae183e8b4c34519e895e623c00/ - 368244efaf441c2dabf7a723355a97b3b86f27bdb2827ae6f34ddece5132efd37af4ba808957b7113b4296bc4ae9ec7be38f9de6bae00504e775883a50d4658a/ - b7ad0603946987f1a154ae7f074e45da224eaa83704aac16a2d43a675d219654cf087b5d7aacce0790a65abbc1a495b26e71a5c6e9a4a71b543bf0048935bc13 - */ - public async storeDocumentKey(serverKeyId: string, serverKeyIdSig: string, - commonPoint: string, encryptedPoint: string) { - const url = [this.url, "shadow", serverKeyId, removeLeading0xPrefix(serverKeyIdSig), - removeLeading0xPrefix(commonPoint), removeLeading0xPrefix(encryptedPoint)] + commonPoint: string, encryptedPoint: string): Promise { + const url = [this.url, "shadow", serverKeyId, HexHelpler.removeLeading0xPrefix(serverKeyIdSig), + HexHelpler.removeLeading0xPrefix(commonPoint), HexHelpler.removeLeading0xPrefix(encryptedPoint)] .join("/") - Logger.log("url", url) + // Logger.log("url", url) + const result = await fetch(url, { method: "POST", }) .then((response) => { - if (response.ok) { - return response + if (response.ok && response.status === 200) { + return true } throw Error(`Unable to store document Keys ${response.statusText}`) }) @@ -75,25 +70,29 @@ export default class SecretStoreClient { return result } - public async retrieveDocumentKey(documentKeyId, documentKeyIdSig) { + public async retrieveDocumentKey(serverKeyId, serverKeyIdSig): Promise { - const url = [ - this.url, documentKeyId, - removeLeading0xPrefix(documentKeyIdSig), - ].join("/") + const url = [this.url, "shadow", serverKeyId, HexHelpler.removeLeading0xPrefix(serverKeyIdSig)] + .join("/") - Logger.log(url) + // Logger.log("url", url) - const result = await fetch(url, { + const result: RetrievedKey = await fetch(url, { method: "GET", }) .then((response) => { - if (response.ok) { return response.json() } throw Error(`Unable to retrieve decryption Keys ${response.statusText}`) }) + .then((data) => { + return { + commonPoint: data.common_point, + decryptedSecret: data.decrypted_secret, + decryptShadows: data.decrypt_shadows, + } as RetrievedKey + }) .catch((e) => { throw Error(`Unable to retrieve decryption keys: ${e.message}`) }) @@ -102,7 +101,6 @@ export default class SecretStoreClient { throw Error(`Unable to retrieve decryption Keys`) } - // results should have (decrypted_secret, common_point, decrypt_shadows) - return JSON.parse(result) + return result } } diff --git a/src/secretstore/keys/GeneratedKey.ts b/src/secretstore/keys/GeneratedKey.ts new file mode 100644 index 0000000..0a426af --- /dev/null +++ b/src/secretstore/keys/GeneratedKey.ts @@ -0,0 +1,6 @@ +import KeyBase from "./KeyBase" + +export default class GeneratedKey extends KeyBase { + public encryptedKey: string + public encryptedPoint: string +} diff --git a/src/secretstore/keys/KeyBase.ts b/src/secretstore/keys/KeyBase.ts new file mode 100644 index 0000000..2d6b8e5 --- /dev/null +++ b/src/secretstore/keys/KeyBase.ts @@ -0,0 +1,3 @@ +export default class KeyBase { + public commonPoint: string +} diff --git a/src/secretstore/keys/RetrievedKey.ts b/src/secretstore/keys/RetrievedKey.ts new file mode 100644 index 0000000..95e2f86 --- /dev/null +++ b/src/secretstore/keys/RetrievedKey.ts @@ -0,0 +1,6 @@ +import KeyBase from "./KeyBase" + +export default class RetrievedKey extends KeyBase { + public decryptedSecret: string + public decryptShadows: string[] +} diff --git a/test/secretstore/ParityClient.test_.ts b/test/secretstore/ParityClient.test_.ts index 1f97125..a47661e 100644 --- a/test/secretstore/ParityClient.test_.ts +++ b/test/secretstore/ParityClient.test_.ts @@ -2,8 +2,11 @@ import BigNumber from "bignumber.js" import {assert} from "chai" import ConfigProvider from "../../src/ConfigProvider" import Config from "../../src/models/Config" -import DocumentKeys from "../../src/secretstore/DocumentKeys" +import GeneratedKey from "../../src/secretstore/keys/GeneratedKey" import ParityClient from "../../src/secretstore/ParityClient" +import * as GeneratedKeyMaterial from "./keys/GeneratedKey.json" +import * as RetrievedKeyMaterial from "./keys/RetrievedKey.json" +import * as ServerKey from "./keys/ServerKey.json" const parityUrl = "http://localhost:8545" @@ -13,13 +16,11 @@ ConfigProvider.configure({ const address = "0xa50f397644973dba99624404b2894825840aa03b" const password = "unittest" -const serverKey = - "0x36131d552e561d8231cd91c8020d869e14c11b16e79fb80ecf8302ea0a0539c969dbc0b547398daf293c259431d7c483ee5974b0ef179297edbe6d39af4374d5" - const testDocument = { so: "secure", soWow: true, } + const parityClient: ParityClient = new ParityClient({ url: parityUrl, address, password, @@ -47,7 +48,7 @@ describe("ParityClient", () => { describe("#generateDocumentKeyFromKey()", () => { it("should generate a document key from a server key", async () => { - const documentKey = await parityClient.generateDocumentKeyFromKey(serverKey) + const documentKey = await parityClient.generateDocumentKeyFromKey(ServerKey) assert(documentKey) }) }) @@ -55,7 +56,7 @@ describe("ParityClient", () => { describe("#encryptDocument()", () => { it("should encrypt an document", async () => { - const documentKey: DocumentKeys = await parityClient.generateDocumentKeyFromKey(serverKey) + const documentKey: GeneratedKey = await parityClient.generateDocumentKeyFromKey(ServerKey) const encryptedDocument = await parityClient.encryptDocument(documentKey.encryptedKey, testDocument) assert(encryptedDocument) }) @@ -64,9 +65,16 @@ describe("ParityClient", () => { describe("#decryptDocument()", () => { it("should decrypt an document", async () => { - const documentKey: DocumentKeys = await parityClient.generateDocumentKeyFromKey(serverKey) - const encryptedDocument = await parityClient.encryptDocument(documentKey.encryptedKey, testDocument) + const encryptedDocument: string = + await parityClient.encryptDocument(GeneratedKeyMaterial.encryptedKey, testDocument) assert(encryptedDocument) + + const decryptedDocument: any = await parityClient.decryptDocument( + RetrievedKeyMaterial.decryptedSecret, RetrievedKeyMaterial.commonPoint, + RetrievedKeyMaterial.decryptShadows, encryptedDocument) + assert(decryptedDocument) + + assert(testDocument.soWow === decryptedDocument.soWow) }) }) }) diff --git a/test/secretstore/SecretStore.test_.ts b/test/secretstore/SecretStore.test_.ts index 7ca8c87..9fdfc62 100644 --- a/test/secretstore/SecretStore.test_.ts +++ b/test/secretstore/SecretStore.test_.ts @@ -11,6 +11,13 @@ ConfigProvider.configure({ nodeUri: parityUrl, } as Config) +const testDocument = { + so: "ocean", + soWow: true, + many: "text is nice to have", + i: "blow up this document with blind text", +} + const address = "0xa50f397644973dba99624404b2894825840aa03b" const password = "unittest" @@ -29,24 +36,28 @@ function generateRandomId(): string { describe("SecretStore", () => { - describe("#generateServerKey()", () => { - it("should generate Server key", async () => { + describe("#encryptDocument()", () => { + it("should encrypt an document", async () => { const serverKeyId = generateRandomId() - const serverKey = await secretStore.generateServerKey(serverKeyId) - assert(serverKey) + const encryptedDocument = await secretStore.encryptDocument(serverKeyId, testDocument) + assert(encryptedDocument) }) }) - describe("#storeDocumentKey()", () => { - it("should store Document key", async () => { + describe("#decryptDocument()", () => { + it("should decrypt an document", async () => { const serverKeyId = generateRandomId() - const documentKeyId = generateRandomId() - const documentKey = await secretStore.storeDocumentKey(serverKeyId, documentKeyId) - assert(documentKey) + const encryptedDocument: string = await secretStore.encryptDocument(serverKeyId, testDocument) + assert(encryptedDocument) + + const decryptedDocument: any = await secretStore.decryptDocument(serverKeyId, encryptedDocument) + assert(decryptedDocument) + + assert(testDocument.soWow === decryptedDocument.soWow) }) }) }) diff --git a/test/secretstore/keys/GeneratedKey.json b/test/secretstore/keys/GeneratedKey.json new file mode 100644 index 0000000..a0469fe --- /dev/null +++ b/test/secretstore/keys/GeneratedKey.json @@ -0,0 +1,5 @@ +{ + "commonPoint": "0xba52313ef585265b52434b1c45a7aa19cf59e371b8d4f5d5f3ef45863e51341b9648dc83ce11b7158347b2cfb2613f92689d6d3704e84dee4eafd38abd0c7ca6", + "encryptedKey": "0x04e77b1d4b3919354550900d871c8ee698ca592e45ee715dad607c408fc18982bae479b3c752dec27eebbe00b102b512e6454e747138f4d882a2fa3dbe84bb3851518766c01e216fc5b01f7a4eb2e18ea2f159063801b0e43f5f632089a80ab982520707ee9e3f4980931a8814e44febdd960b366336ee35b26bfe523950d28bb6bdc0a65240c43b999cae3cd8258a06cb8dd52b2a2516bf15ad4d00d8c50f4e5f8bc48cf7173db5cce77a182093404402", + "encryptedPoint": "0x1697d31ba17eb819177004618f2a5e00fceb355b12ceb4a476ade59adfc9f6579f9b951ef0695eb852f1ff1deff1f12205760b8d1c5a46ba44cce7d54571200a" +} \ No newline at end of file diff --git a/test/secretstore/keys/RetrievedKey.json b/test/secretstore/keys/RetrievedKey.json new file mode 100644 index 0000000..b84eda5 --- /dev/null +++ b/test/secretstore/keys/RetrievedKey.json @@ -0,0 +1,8 @@ +{ + "commonPoint": "0xba52313ef585265b52434b1c45a7aa19cf59e371b8d4f5d5f3ef45863e51341b69b7237c31ee48ea7cb84d304d9ec06d976292c8fb17b211b1502c7442f37f89", + "decryptedSecret": "0x7b6bee06bd00dc825d2c99f324b47e4aee9219d5453076a735cfc3d55c730df285c6e0117ceb90805aee7cfb86e0574d04c17aa09cdff9957b4a91a2af79a3d3", + "decryptShadows": [ + "0x0429f62a48a25306bb64d8a5b74b0d6ee01f062fe1f7238f6b5078e3e7726751b46dd8834c5346bca3d6059ddc923747ecfea545579210db09f8a6708ab05f452ace3956408afddcb3b8796637198109fa046af23c6bcd6da5424cf8d7165d805c6265ea4e358731e4a71d32760d0423d12cf117d9b401bd2d7c321a4b9d47a79d383adce06c45352e23f2bec3262d637b", + "0x045fdbbb9782c046f3023e1d699ee9ae80413e44162ecf4f5ac6ace369f7e0604035abb3b7aed9d087293561ef75eb9296a6582cbefb1af7f449a97bcb5282f9b6cbb1059d595b2e2ec6cef40fceacf228462bebe985b8738e22c6ec196b25e408b7cbb608a74a167a2b5d4e12182e2942ded30d18a6b99b243395d4a4cabe25168560946ce3dd5d77b48b5148c78491e1" + ] +} \ No newline at end of file diff --git a/test/secretstore/keys/ServerKey.json b/test/secretstore/keys/ServerKey.json new file mode 100644 index 0000000..17e92d3 --- /dev/null +++ b/test/secretstore/keys/ServerKey.json @@ -0,0 +1 @@ +"0x92c18be2b3f6aea31fc1e58f833936f16f635c0657bf03c3b05a0e3f5809ffec64736f0774b9be536e65a125b05fd21c0ae32a03c1338dcbe6ddb1ce2cd5b95a" \ No newline at end of file diff --git a/test/tsconfig.json b/test/tsconfig.json index 4ca49cc..ef87166 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "resolveJsonModule": true, "lib": [ "es6", "es7"