mirror of
https://github.com/arx-research/libhalo.git
synced 2026-01-09 13:18:04 -05:00
204 lines
6.5 KiB
TypeScript
204 lines
6.5 KiB
TypeScript
/**
|
|
* LibHaLo - Programmatically interact with HaLo tags from the web browser, mobile application or the desktop.
|
|
* Copyright by Arx Research, Inc., a Delaware corporation
|
|
* License: MIT
|
|
*/
|
|
|
|
import {HaloLogicError, HaloTagError} from "../api/common.js";
|
|
import elliptic from 'elliptic';
|
|
import {HaloCommandObject, HaloResponseObject} from "../types.js";
|
|
|
|
const ec = new elliptic.ec('secp256k1');
|
|
|
|
function assert(condition: unknown) {
|
|
if (!condition) {
|
|
throw new Error("Assertion failed.");
|
|
}
|
|
}
|
|
|
|
class SkipTest extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = "SkipTest";
|
|
}
|
|
}
|
|
|
|
const tests = [
|
|
["testLegacySign1", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise<HaloResponseObject>) {
|
|
if (driver !== 'webnfc') {
|
|
const resPkeys = await exec({
|
|
"name": "get_pkeys"
|
|
});
|
|
|
|
const pk1 = ec.keyFromPublic(resPkeys.publicKeys[1], 'hex');
|
|
const digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1";
|
|
|
|
const res = await exec({
|
|
"name": "sign",
|
|
"keyNo": 1,
|
|
"digest": digest,
|
|
"legacySignCommand": true
|
|
});
|
|
|
|
assert(pk1.verify(digest, res.signature.der));
|
|
} else {
|
|
const digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1";
|
|
|
|
const res = await exec({
|
|
"name": "sign",
|
|
"keyNo": 1,
|
|
"digest": digest,
|
|
"legacySignCommand": true
|
|
});
|
|
|
|
const pk1 = ec.keyFromPublic(res.publicKey, 'hex');
|
|
assert(pk1.verify(digest, res.signature.der));
|
|
}
|
|
}],
|
|
["testSign1", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise<HaloResponseObject>) {
|
|
const digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1";
|
|
|
|
const res = await exec({
|
|
"name": "sign",
|
|
"keyNo": 1,
|
|
"digest": digest
|
|
});
|
|
|
|
const pk1 = ec.keyFromPublic(res.publicKey, 'hex');
|
|
assert(pk1.verify(digest, res.signature.der));
|
|
}],
|
|
["testKeyGen3", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise<HaloResponseObject>) {
|
|
let resGenKey;
|
|
|
|
try {
|
|
resGenKey = await exec({
|
|
"name": "gen_key",
|
|
"entropy": "c18ae10f225d48908ac485ba8a43c0f7bd3a9c25e214f49e4eb617561511d8f8"
|
|
});
|
|
} catch (e) {
|
|
if (e instanceof HaloTagError) {
|
|
if (e.name === "ERROR_CODE_KEY_ALREADY_EXISTS") {
|
|
throw new SkipTest("Unable to test key generation - key already exists.");
|
|
} else {
|
|
throw e;
|
|
}
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
assert(resGenKey.needsConfirm);
|
|
|
|
const resGenKeyConfirm = await exec({
|
|
"name": "gen_key_confirm",
|
|
"publicKey": resGenKey.publicKey
|
|
});
|
|
|
|
assert(resGenKeyConfirm.status === "ok");
|
|
|
|
const resGenKeyFinalize = await exec({
|
|
"name": "gen_key_finalize"
|
|
});
|
|
|
|
assert(resGenKeyConfirm.status === "ok");
|
|
}],
|
|
["testSign3", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise<HaloResponseObject>) {
|
|
const digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1";
|
|
|
|
const res = await exec({
|
|
"name": "sign",
|
|
"keyNo": 3,
|
|
"digest": digest
|
|
});
|
|
|
|
const pk3 = ec.keyFromPublic(res.publicKey, 'hex');
|
|
assert(pk3.verify(digest, res.signature.der));
|
|
}],
|
|
["testSign1Typed", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise<HaloResponseObject>) {
|
|
const domain = {
|
|
name: 'Ether Mail',
|
|
version: '1',
|
|
chainId: 1,
|
|
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
|
|
};
|
|
|
|
const types = {
|
|
Person: [
|
|
{ name: 'name', type: 'string' },
|
|
{ name: 'wallet', type: 'address' }
|
|
],
|
|
Mail: [
|
|
{ name: 'from', type: 'Person' },
|
|
{ name: 'to', type: 'Person' },
|
|
{ name: 'contents', type: 'string' }
|
|
]
|
|
};
|
|
|
|
const value = {
|
|
from: {
|
|
name: 'Cow',
|
|
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826'
|
|
},
|
|
to: {
|
|
name: 'Bob',
|
|
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB'
|
|
},
|
|
contents: 'Hello, Bob!'
|
|
};
|
|
|
|
const res = await exec({
|
|
"name": "sign",
|
|
"keyNo": 1,
|
|
"typedData": {domain, types, value}
|
|
});
|
|
|
|
const pk1 = ec.keyFromPublic(res.publicKey, 'hex');
|
|
assert(res.input.digest === "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2");
|
|
assert(res.input.primaryType === "Mail");
|
|
assert(res.input.domainHash === "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f");
|
|
assert(pk1.verify(res.input.digest, res.signature.der));
|
|
}]
|
|
];
|
|
|
|
async function __runTestSuite(__unsafe: {__this_is_unsafe: true}, driver: string, exec: (cmd: HaloCommandObject) => Promise<HaloResponseObject>) {
|
|
if (!__unsafe.__this_is_unsafe) {
|
|
throw new HaloLogicError("This method is used for internal testing, shouldn't be invoked directly.");
|
|
}
|
|
|
|
const passed = [];
|
|
const skipped = [];
|
|
const failed = [];
|
|
|
|
for (const testObj of tests) {
|
|
console.log(testObj[0]);
|
|
|
|
try {
|
|
const testFunc = testObj[1] as (driver: string, exec: (cmd: HaloCommandObject) => Promise<HaloResponseObject>) => Promise<void>;
|
|
await testFunc(driver, exec);
|
|
console.log(testObj[0], 'PASSED');
|
|
passed.push(testObj[0]);
|
|
} catch (e) {
|
|
if (e instanceof SkipTest) {
|
|
console.error(testObj[0], 'SKIPPED (' + e.message + ')');
|
|
skipped.push(testObj[0]);
|
|
} else {
|
|
console.error(testObj[0], 'FAILED');
|
|
console.error(e);
|
|
failed.push(testObj[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
"stats": {
|
|
"passed": passed.length,
|
|
"skipped": skipped.length,
|
|
"failed": failed.length
|
|
},
|
|
"skipped": skipped,
|
|
"failedTests": failed
|
|
};
|
|
}
|
|
|
|
export {__runTestSuite};
|