Skip to content

Commit

Permalink
feat(common): KeyConverter class to and from PEM/hex/buffe
Browse files Browse the repository at this point in the history
Signed-off-by: suyukuoacn <su-yu.kuo@accenture.com>
  • Loading branch information
suyukuoacn authored and petermetz committed Sep 17, 2020
1 parent ad01dee commit fc80106
Show file tree
Hide file tree
Showing 10 changed files with 611 additions and 13 deletions.
9 changes: 4 additions & 5 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ module.exports = (config) => {
// 'Electron',
// "ElectronWithGui",
// 'Chrome',
// 'ChromeHeadless',
"ChromeHeadlessDebug",
"ChromeHeadless",
// "ChromeHeadlessDebug",
],

files: [
{
pattern: "packages/cactus-common/src/test/typescript/unit/**/*.ts",
},
// FIXME: For whatever reason only the first test gets executed not all of them
"./packages/cactus-common/src/test/typescript/unit/**/*",
],

plugins: [
Expand Down
46 changes: 44 additions & 2 deletions packages/cactus-common/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/cactus-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"dependencies": {
"joi": "14.3.1",
"json-stable-stringify": "1.0.1",
"key-encoder": "2.0.3",
"loglevel": "1.6.7",
"loglevel-plugin-prefix": "0.8.4",
"secp256k1": "4.0.2",
Expand Down
128 changes: 128 additions & 0 deletions packages/cactus-common/src/main/typescript/key-converter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import KeyEncoder from "key-encoder";

export enum KeyFormat {
Raw = "raw",
Hex = "hex",
PEM = "pem",
}

export class KeyConverter {
private keyEncoder: KeyEncoder;

public readonly supportedKeyFormats: string[];

constructor() {
this.supportedKeyFormats = Object.values(KeyFormat);
this.keyEncoder = new KeyEncoder("secp256k1");
}

/**
* Convert public key from one format to another format
* @param key
* @param fromFormat
* @param toFormat
*/
public publicKeyAs(
key: string | Uint8Array,
fromFormat: KeyFormat,
toFormat: KeyFormat
): string | Uint8Array {
this.validateKeyFormatValue(fromFormat);
this.validateKeyFormatValue(toFormat);

let keyValue: string = "";
let convertToRaw = false;

if (key instanceof Uint8Array) {
keyValue = Buffer.from(key).toString("hex");
} else {
keyValue = key;
}

if (fromFormat === KeyFormat.Hex) {
fromFormat = KeyFormat.Raw;
}

if (toFormat === KeyFormat.Raw) {
convertToRaw = true;
} else if (toFormat === KeyFormat.Hex) {
toFormat = KeyFormat.Raw;
}

let resultKey: string | Uint8Array = this.keyEncoder.encodePublic(
keyValue,
fromFormat,
toFormat
);

if (convertToRaw) {
resultKey = Uint8Array.from(Buffer.from(resultKey, "hex"));
}

return resultKey;
}

/**
* Convert private key from one format to another format
* @param key
* @param fromFormat
* @param toFormat
*/
public privateKeyAs(
key: string | Buffer,
fromFormat: KeyFormat,
toFormat: KeyFormat
): string | Buffer {
this.validateKeyFormatValue(fromFormat);
this.validateKeyFormatValue(toFormat);

let keyValue = key;
let convertToRaw = false;

if (fromFormat === KeyFormat.Raw) {
if (key instanceof Buffer) {
keyValue = key.toString("hex");
}
} else if (fromFormat === KeyFormat.Hex) {
fromFormat = KeyFormat.Raw;
}

if (toFormat === KeyFormat.Raw) {
convertToRaw = true;
} else if (toFormat === KeyFormat.Hex) {
toFormat = KeyFormat.Raw;
}

let resultKey: string | Buffer = this.keyEncoder.encodePrivate(
keyValue,
fromFormat,
toFormat
);

if (convertToRaw) {
resultKey = Buffer.from(resultKey, "hex");
}

return resultKey;
}

/**
* This method will validate if the input key format match to one of the enum value.
* @param keyFormat
*/
private validateKeyFormatValue(keyFormat: KeyFormat): void {
const fnTag = "KeyConverter#publicKeyAs()";

if (!this.supportedKeyFormats.some((val) => val === keyFormat)) {
const csv =
this.supportedKeyFormats.join(", ") +
` => (` +
Object.keys(KeyFormat)
.map((f) => `KeyFormat.${f}`)
.join(", ") +
`)`;
const msg = `${fnTag} Invalid KeyFormat ${keyFormat} Supported: (${csv})`;
throw new Error(msg);
}
}
}
1 change: 1 addition & 0 deletions packages/cactus-common/src/main/typescript/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export {

export { ISignerKeyPair } from "./signer-key-pair";
export { Secp256k1Keys } from "./secp256k1-keys";
export { KeyFormat, KeyConverter } from "./key-converter";
26 changes: 26 additions & 0 deletions packages/cactus-common/src/main/typescript/signer-key-pairs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import crypto from "crypto";
import secp256k1 from "secp256k1";

export interface ISignerKeyPairs {
privateKey: any;
publicKey: any;
}

export class Secp256k1Keys {
/**
* Generate random private and public secp256k1 key in Buffer format
* @return Generated key pair
*/
static generateKeyPairsBuffer(): ISignerKeyPairs {
let privKey: any;
// generate secp256K1 private key
do {
privKey = crypto.randomBytes(32);
} while (!secp256k1.privateKeyVerify(privKey));

// generate secp256K1 public key
const pubKey = secp256k1.publicKeyCreate(privKey);

return { privateKey: privKey, publicKey: pubKey };
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// tslint:disable-next-line: no-var-requires
import test from "tape";
import test, { Test } from "tape";

import { Logger, LoggerProvider } from "../../../main/typescript/public-api";

test("Library can be loaded", (assert: any) => {
assert.plan(2);
test("Library can be loaded", (assert: Test) => {
assert.ok(Logger);
assert.ok(LoggerProvider);
assert.end();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ test("Simple JSON Test", async (assert: Test) => {
const sign2 = jsObjectSigner.sign(payload2);

assert.equals(sign1.toString, sign2.toString);
assert.end();
});

test("Simple Nested JSON Test", async (assert: Test) => {
Expand All @@ -70,6 +71,7 @@ test("Simple Nested JSON Test", async (assert: Test) => {
const sign2 = jsObjectSigner.sign(outer2);

assert.equals(sign1.toString, sign2.toString);
assert.end();
});

test("Simple Date JSON Test", async (assert: Test) => {
Expand Down Expand Up @@ -106,6 +108,7 @@ test("Simple Date JSON Test", async (assert: Test) => {
const sign2 = jsObjectSigner.sign(outer2);

assert.equals(sign1.toString, sign2.toString);
assert.end();
});

test("Circular JSON Test", async (assert: Test) => {
Expand All @@ -121,6 +124,7 @@ test("Circular JSON Test", async (assert: Test) => {
obj.b = obj;

assert.throws(() => jsObjectSigner.sign(obj));
assert.end();
});

test("Very Signature Test", async (assert: Test) => {
Expand All @@ -136,6 +140,7 @@ test("Very Signature Test", async (assert: Test) => {
const verify = jsObjectSigner.verify(payload1, sign1, keyPairs.publicKey);

assert.equals(true, verify);
assert.end();
});

test("Test optional sign function", async (assert: Test) => {
Expand All @@ -156,6 +161,7 @@ test("Test optional sign function", async (assert: Test) => {
const sign2 = jsObjectSigner.sign(outer2);

assert.equals(sign1.toString, sign2.toString);
assert.end();
});

test("Test optional verify sign function", async (assert: Test) => {
Expand All @@ -175,6 +181,7 @@ test("Test optional verify sign function", async (assert: Test) => {
const verify = jsObjectSigner.verify(outer1, sign1, keyPairs.publicKey);

assert.equals(true, verify);
assert.end();
});

test("Test optional hash function", async (assert: Test) => {
Expand All @@ -195,6 +202,7 @@ test("Test optional hash function", async (assert: Test) => {
const sign2 = jsObjectSigner.sign(outer2);

assert.equals(sign1.toString, sign2.toString);
assert.end();
});

test("Test missing required constructor field", async (assert: Test) => {
Expand All @@ -207,4 +215,5 @@ test("Test missing required constructor field", async (assert: Test) => {
} catch (e) {
assert.equal(e.message, "JsObjectSigner#ctor options.privateKey falsy.");
}
assert.end();
});
Loading

0 comments on commit fc80106

Please sign in to comment.