Compare commits
32 Commits
Author | SHA1 | Date |
---|---|---|
semantic-release-bot | e1d057fb24 | |
Derrick Hammer | 4a9c1bc53e | |
Derrick Hammer | 101748af06 | |
semantic-release-bot | 538bae67e2 | |
Derrick Hammer | c027a02650 | |
Derrick Hammer | bb54af0d37 | |
semantic-release-bot | 19953f63c2 | |
Derrick Hammer | 22176703f9 | |
Derrick Hammer | ba6d16a07d | |
Derrick Hammer | b17db64a70 | |
Derrick Hammer | c249d12fa0 | |
semantic-release-bot | 52fecb2a6e | |
Derrick Hammer | 44b26011c0 | |
Derrick Hammer | 8e93f8f53b | |
semantic-release-bot | d11b5aec8f | |
Derrick Hammer | 86727b745a | |
Derrick Hammer | 6bcbba46f9 | |
Derrick Hammer | e671a49d9a | |
semantic-release-bot | 529ea38724 | |
Derrick Hammer | 253ebf2623 | |
Derrick Hammer | 3b7b960995 | |
Derrick Hammer | 0617890621 | |
Derrick Hammer | 1cc5456434 | |
Derrick Hammer | 893b5f530e | |
Derrick Hammer | b6b273ff0f | |
Derrick Hammer | 4403ef8151 | |
Derrick Hammer | d477417ff6 | |
Derrick Hammer | daaa338854 | |
Derrick Hammer | 46d22fe544 | |
Derrick Hammer | 3ec85772ae | |
Derrick Hammer | 4c8750bcb2 | |
Derrick Hammer | 2e5a28a3a9 |
|
@ -0,0 +1,13 @@
|
||||||
|
name: Build/Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
- develop-*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
|
||||||
|
secrets: inherit
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"preset": [
|
||||||
|
"@lumeweb/presetter-kernel-module-preset"
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"browser": true,
|
||||||
|
"vite": {
|
||||||
|
"build": {
|
||||||
|
"copyPublicDir": false,
|
||||||
|
"lib": {
|
||||||
|
"entry": "{source}/tester.js",
|
||||||
|
"fileName": "tester",
|
||||||
|
"formats": {
|
||||||
|
"0": "umd"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"release": {
|
||||||
|
"plugins": {
|
||||||
|
"3": [
|
||||||
|
"@semantic-release/npm",
|
||||||
|
{
|
||||||
|
"npmPublish": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build:vite": "vite build",
|
||||||
|
"build:bin": "shx mv lib/tester.umd.cjs public/tester.js && shx mkdir -p bin && shx mv build/sandbox* bin/",
|
||||||
|
"build:lib": "shx mv build/* lib/ && shx rm lib/tester*"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
## [0.0.2-develop.6](https://git.lumeweb.com/LumeWeb/kernel-sandbox/compare/v0.0.2-develop.5...v0.0.2-develop.6) (2023-07-21)
|
||||||
|
|
||||||
|
## [0.0.2-develop.5](https://git.lumeweb.com/LumeWeb/kernel-sandbox/compare/v0.0.2-develop.4...v0.0.2-develop.5) (2023-07-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* switch to https ([bb54af0](https://git.lumeweb.com/LumeWeb/kernel-sandbox/commit/bb54af0d37e5bba25190123ec88bd2871cd047c8))
|
||||||
|
|
||||||
|
## [0.0.2-develop.4](https://git.lumeweb.com/LumeWeb/kernel-sandbox/compare/v0.0.2-develop.3...v0.0.2-develop.4) (2023-07-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* update import path ([b17db64](https://git.lumeweb.com/LumeWeb/kernel-sandbox/commit/b17db64a70bfe2248faec21b4b67b10772e1b917))
|
||||||
|
|
||||||
|
## [0.0.2-develop.3](https://git.lumeweb.com/LumeWeb/kernel-sandbox/compare/v0.0.2-develop.2...v0.0.2-develop.3) (2023-07-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* set the key to "key" ([8e93f8f](https://git.lumeweb.com/LumeWeb/kernel-sandbox/commit/8e93f8f53b643bc069f8fa9db4b74f5b242b9158))
|
||||||
|
|
||||||
|
## [0.0.2-develop.2](https://git.lumeweb.com/LumeWeb/kernel-sandbox/compare/v0.0.2-develop.1...v0.0.2-develop.2) (2023-07-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* trigger release ([86727b7](https://git.lumeweb.com/LumeWeb/kernel-sandbox/commit/86727b745a22dbe055afebc7dab144a49721cb1b))
|
||||||
|
|
||||||
|
## [0.0.2-develop.1](https://git.lumeweb.com/LumeWeb/kernel-sandbox/compare/v0.0.1...v0.0.2-develop.1) (2023-07-21)
|
|
@ -1,2 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
export {};
|
|
|
@ -1,19 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
// @ts-ignore
|
|
||||||
import { loadTester, login } from "../dist/index.js";
|
|
||||||
import puppeteer, { ProtocolError } from "puppeteer";
|
|
||||||
let browser;
|
|
||||||
(async () => {
|
|
||||||
browser = await puppeteer.launch({ headless: false, devtools: true });
|
|
||||||
const page = (await browser.pages()).pop();
|
|
||||||
await login(page);
|
|
||||||
await loadTester(page);
|
|
||||||
})();
|
|
||||||
process.on("SIGTERM", async () => {
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
process.on("uncaughtException", (e) => {
|
|
||||||
if (!(e instanceof ProtocolError)) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { Page } from "puppeteer";
|
|
||||||
import { errTuple } from "libskynet";
|
|
||||||
export declare const KERNEL_TEST_SUITE = "AQCPJ9WRzMpKQHIsPo8no3XJpUydcDCjw7VJy8lG1MCZ3g";
|
|
||||||
export declare const KERNEL_HELPER_MODULE = "AQCoaLP6JexdZshDDZRQaIwN3B7DqFjlY7byMikR7u1IEA";
|
|
||||||
export declare const TEST_KERNEL_SKLINK = "AQDJDoXMJiiEMBxXodQvUV89qtQHsnXWyV1ViQ9M1pMjUg";
|
|
||||||
export declare function generateSeedPhrase(): string;
|
|
||||||
export declare function login(page: Page, seed?: string): Promise<void>;
|
|
||||||
export declare function loadTester(page: Page, port?: number): Promise<void>;
|
|
||||||
declare class Tester {
|
|
||||||
private page;
|
|
||||||
constructor(page: Page);
|
|
||||||
callModule(id: string, method: string, data?: {}): Promise<errTuple>;
|
|
||||||
}
|
|
||||||
export declare const tester: (page: Page) => Tester;
|
|
||||||
export {};
|
|
|
@ -1,129 +0,0 @@
|
||||||
import { b64ToBuf, bufToHex, deriveChildSeed, dictionary, seedPhraseToSeed, taggedRegistryEntryKeys, } from "libskynet";
|
|
||||||
import { SEED_BYTES, seedToChecksumWords } from "libskynet/dist/seed.js";
|
|
||||||
import { DICTIONARY_UNIQUE_PREFIX } from "libskynet/dist/dictionary.js";
|
|
||||||
import * as path from "path";
|
|
||||||
import { overwriteRegistryEntry } from "libskynetnode";
|
|
||||||
import * as kernel from "libkernel";
|
|
||||||
import { webcrypto } from "crypto";
|
|
||||||
// @ts-ignore
|
|
||||||
import StaticServer from "static-server";
|
|
||||||
import * as url from "url";
|
|
||||||
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
|
||||||
export const KERNEL_TEST_SUITE = "AQCPJ9WRzMpKQHIsPo8no3XJpUydcDCjw7VJy8lG1MCZ3g";
|
|
||||||
export const KERNEL_HELPER_MODULE = "AQCoaLP6JexdZshDDZRQaIwN3B7DqFjlY7byMikR7u1IEA";
|
|
||||||
export const TEST_KERNEL_SKLINK = "AQDJDoXMJiiEMBxXodQvUV89qtQHsnXWyV1ViQ9M1pMjUg";
|
|
||||||
const SEED_ENTROPY_WORDS = 13;
|
|
||||||
const crypto = webcrypto;
|
|
||||||
export function generateSeedPhrase() {
|
|
||||||
// Get the random numbers for the seed phrase. Typically, you need to
|
|
||||||
// have code that avoids bias by checking the random results and
|
|
||||||
// re-rolling the random numbers if the result is outside of the range
|
|
||||||
// of numbers that would produce no bias. Because the search space
|
|
||||||
// (1024) evenly divides the random number space (2^16), we can skip
|
|
||||||
// this step and just use a modulus instead. The result will have no
|
|
||||||
// bias, but only because the search space is a power of 2.
|
|
||||||
let randNums = new Uint16Array(SEED_ENTROPY_WORDS);
|
|
||||||
crypto.getRandomValues(randNums);
|
|
||||||
// Generate the seed phrase from the randNums.
|
|
||||||
let seedWords = [];
|
|
||||||
for (let i = 0; i < SEED_ENTROPY_WORDS; i++) {
|
|
||||||
let wordIndex = randNums[i] % dictionary.length;
|
|
||||||
seedWords.push(dictionary[wordIndex]);
|
|
||||||
}
|
|
||||||
// Convert the seedWords to a seed.
|
|
||||||
let [seed] = seedWordsToSeed(seedWords);
|
|
||||||
// Compute the checksum.
|
|
||||||
let [checksumOne, checksumTwo, err2] = seedToChecksumWords(seed);
|
|
||||||
// Assemble the final seed phrase and set the text field.
|
|
||||||
return [...seedWords, checksumOne, checksumTwo].join(" ");
|
|
||||||
}
|
|
||||||
function seedWordsToSeed(seedWords) {
|
|
||||||
// Input checking.
|
|
||||||
if (seedWords.length !== SEED_ENTROPY_WORDS) {
|
|
||||||
return [
|
|
||||||
new Uint8Array(0),
|
|
||||||
`Seed words should have length ${SEED_ENTROPY_WORDS} but has length ${seedWords.length}`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
// We are getting 16 bytes of entropy.
|
|
||||||
let bytes = new Uint8Array(SEED_BYTES);
|
|
||||||
let curByte = 0;
|
|
||||||
let curBit = 0;
|
|
||||||
for (let i = 0; i < SEED_ENTROPY_WORDS; i++) {
|
|
||||||
// Determine which number corresponds to the next word.
|
|
||||||
let word = -1;
|
|
||||||
for (let j = 0; j < dictionary.length; j++) {
|
|
||||||
if (seedWords[i].slice(0, DICTIONARY_UNIQUE_PREFIX) ===
|
|
||||||
dictionary[j].slice(0, DICTIONARY_UNIQUE_PREFIX)) {
|
|
||||||
word = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (word === -1) {
|
|
||||||
return [
|
|
||||||
new Uint8Array(0),
|
|
||||||
`word '${seedWords[i]}' at index ${i} not found in dictionary`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
let wordBits = 10;
|
|
||||||
if (i === SEED_ENTROPY_WORDS - 1) {
|
|
||||||
wordBits = 8;
|
|
||||||
}
|
|
||||||
// Iterate over the bits of the 10- or 8-bit word.
|
|
||||||
for (let j = 0; j < wordBits; j++) {
|
|
||||||
let bitSet = (word & (1 << (wordBits - j - 1))) > 0;
|
|
||||||
if (bitSet) {
|
|
||||||
bytes[curByte] |= 1 << (8 - curBit - 1);
|
|
||||||
}
|
|
||||||
curBit += 1;
|
|
||||||
if (curBit >= 8) {
|
|
||||||
// Current byte has 8 bits, go to the next byte.
|
|
||||||
curByte += 1;
|
|
||||||
curBit = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [bytes, null];
|
|
||||||
}
|
|
||||||
export async function login(page, seed = generateSeedPhrase()) {
|
|
||||||
await page.goto("http://skt.us");
|
|
||||||
let userSeed;
|
|
||||||
[userSeed] = seedPhraseToSeed(seed);
|
|
||||||
let seedHex = bufToHex(userSeed);
|
|
||||||
await page.evaluate((seed) => {
|
|
||||||
window.localStorage.setItem("v1-seed", seed);
|
|
||||||
}, seedHex);
|
|
||||||
let kernelEntrySeed = deriveChildSeed(userSeed, "userPreferredKernel2");
|
|
||||||
// Get the registry keys.
|
|
||||||
let [keypair, dataKey] = taggedRegistryEntryKeys(kernelEntrySeed, "user kernel");
|
|
||||||
await overwriteRegistryEntry(keypair, dataKey, b64ToBuf(TEST_KERNEL_SKLINK)[0]);
|
|
||||||
}
|
|
||||||
export async function loadTester(page, port = 8080) {
|
|
||||||
const server = new StaticServer({
|
|
||||||
rootPath: path.resolve(__dirname, "..", "public"),
|
|
||||||
port,
|
|
||||||
host: "localhost",
|
|
||||||
});
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
server.start(resolve);
|
|
||||||
});
|
|
||||||
const stop = () => server.stop();
|
|
||||||
process.on("SIGTERM", stop);
|
|
||||||
page.browser().on("disconnected", stop);
|
|
||||||
await page.goto(`http://localhost:${port}/`);
|
|
||||||
await page.evaluate(() => {
|
|
||||||
return kernel.init();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
class Tester {
|
|
||||||
page;
|
|
||||||
constructor(page) {
|
|
||||||
this.page = page;
|
|
||||||
}
|
|
||||||
async callModule(id, method, data = {}) {
|
|
||||||
return this.page.evaluate(async (id, method, data) => {
|
|
||||||
return kernel.callModule(id, method, data);
|
|
||||||
}, id, method, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export const tester = (page) => new Tester(page);
|
|
File diff suppressed because it is too large
Load Diff
50
package.json
50
package.json
|
@ -1,30 +1,38 @@
|
||||||
{
|
{
|
||||||
"name": "@lumeweb/kernel-tester",
|
"name": "@lumeweb/kernel-sandbox",
|
||||||
"version": "0.1.0",
|
"version": "0.0.2-develop.6",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "gitea@git.lumeweb.com:LumeWeb/kernel-sandbox.git"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && rollup -c && mv dist/tester* public && mkdir -p bin && mv build/sandbox* bin/ && mv build/index* dist && rm -f dist/tester.js && rm -rf build/"
|
"prepare": "presetter bootstrap",
|
||||||
|
"build": "run build build:bin build:lib",
|
||||||
|
"semantic-release": "semantic-release"
|
||||||
},
|
},
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
|
||||||
"libkernel": "^0.1.41",
|
|
||||||
"libskynet": "^0.0.48",
|
|
||||||
"libskynetnode": "^0.1.3",
|
|
||||||
"static-server": "^2.2.1",
|
|
||||||
"puppeteer": "^15.4.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@rollup/plugin-commonjs": "^22.0.1",
|
|
||||||
"@rollup/plugin-json": "^4.1.0",
|
|
||||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
|
||||||
"@types/node": "^18.0.0",
|
|
||||||
"@types/ws": "^8.5.3",
|
|
||||||
"prettier": "^2.7.1",
|
|
||||||
"rollup": "^2.75.7",
|
|
||||||
"rollup-plugin-polyfill-node": "^0.9.0",
|
|
||||||
"typescript": "^4.5"
|
|
||||||
},
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"kernel-sandbox": "bin/sandbox.js"
|
"kernel-sandbox": "bin/sandbox.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@lumeweb/presetter-kernel-module-preset": "^0.1.0-develop.43",
|
||||||
|
"@rollup/plugin-wasm": "^6.1.3",
|
||||||
|
"presetter": "*"
|
||||||
|
},
|
||||||
|
"readme": "ERROR: No README data found!",
|
||||||
|
"dependencies": {
|
||||||
|
"@lumeweb/libkernel": "^0.1.0-develop.23",
|
||||||
|
"@noble/ciphers": "^0.1.4",
|
||||||
|
"p-defer": "^4.0.0",
|
||||||
|
"puppeteer": "^20.7.4",
|
||||||
|
"static-server": "^2.2.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"public",
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export {};
|
|
5223
public/tester.js
5223
public/tester.js
File diff suppressed because it is too large
Load Diff
|
@ -1,15 +0,0 @@
|
||||||
import resolve from "@rollup/plugin-node-resolve";
|
|
||||||
import commonjs from "@rollup/plugin-commonjs";
|
|
||||||
import json from "@rollup/plugin-json";
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
input: "build/tester.js",
|
|
||||||
output: {
|
|
||||||
file: "dist/tester.js",
|
|
||||||
format: "iife",
|
|
||||||
},
|
|
||||||
plugins: [resolve(), commonjs(), json()],
|
|
||||||
inlineDynamicImports: true,
|
|
||||||
},
|
|
||||||
];
|
|
157
src/index.ts
157
src/index.ts
|
@ -1,138 +1,14 @@
|
||||||
import {
|
|
||||||
b64ToBuf,
|
|
||||||
bufToHex,
|
|
||||||
deriveChildSeed,
|
|
||||||
dictionary,
|
|
||||||
seedPhraseToSeed,
|
|
||||||
taggedRegistryEntryKeys,
|
|
||||||
} from "libskynet";
|
|
||||||
import { SEED_BYTES, seedToChecksumWords } from "libskynet/dist/seed.js";
|
|
||||||
import { DICTIONARY_UNIQUE_PREFIX } from "libskynet/dist/dictionary.js";
|
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { overwriteRegistryEntry } from "libskynetnode";
|
import * as kernel from "@lumeweb/libkernel/kernel";
|
||||||
import * as kernel from "libkernel";
|
|
||||||
import { webcrypto } from "crypto";
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import StaticServer from "static-server";
|
import StaticServer from "static-server";
|
||||||
import { Page } from "puppeteer";
|
import { Page } from "puppeteer";
|
||||||
import { errTuple } from "libskynet";
|
|
||||||
|
|
||||||
import * as url from "url";
|
import * as url from "url";
|
||||||
|
|
||||||
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
||||||
|
|
||||||
export const KERNEL_TEST_SUITE =
|
|
||||||
"AQCPJ9WRzMpKQHIsPo8no3XJpUydcDCjw7VJy8lG1MCZ3g";
|
|
||||||
export const KERNEL_HELPER_MODULE =
|
|
||||||
"AQCoaLP6JexdZshDDZRQaIwN3B7DqFjlY7byMikR7u1IEA";
|
|
||||||
export const TEST_KERNEL_SKLINK =
|
|
||||||
"AQDJDoXMJiiEMBxXodQvUV89qtQHsnXWyV1ViQ9M1pMjUg";
|
|
||||||
const SEED_ENTROPY_WORDS = 13;
|
|
||||||
const crypto = webcrypto as unknown as Crypto;
|
|
||||||
|
|
||||||
export function generateSeedPhrase() {
|
|
||||||
// Get the random numbers for the seed phrase. Typically, you need to
|
|
||||||
// have code that avoids bias by checking the random results and
|
|
||||||
// re-rolling the random numbers if the result is outside of the range
|
|
||||||
// of numbers that would produce no bias. Because the search space
|
|
||||||
// (1024) evenly divides the random number space (2^16), we can skip
|
|
||||||
// this step and just use a modulus instead. The result will have no
|
|
||||||
// bias, but only because the search space is a power of 2.
|
|
||||||
let randNums = new Uint16Array(SEED_ENTROPY_WORDS);
|
|
||||||
crypto.getRandomValues(randNums);
|
|
||||||
// Generate the seed phrase from the randNums.
|
|
||||||
let seedWords = [];
|
|
||||||
for (let i = 0; i < SEED_ENTROPY_WORDS; i++) {
|
|
||||||
let wordIndex = randNums[i] % dictionary.length;
|
|
||||||
seedWords.push(dictionary[wordIndex]);
|
|
||||||
}
|
|
||||||
// Convert the seedWords to a seed.
|
|
||||||
let [seed] = seedWordsToSeed(seedWords);
|
|
||||||
// Compute the checksum.
|
|
||||||
let [checksumOne, checksumTwo, err2] = seedToChecksumWords(
|
|
||||||
seed as Uint8Array
|
|
||||||
);
|
|
||||||
// Assemble the final seed phrase and set the text field.
|
|
||||||
return [...seedWords, checksumOne, checksumTwo].join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
function seedWordsToSeed(seedWords: string[]) {
|
|
||||||
// Input checking.
|
|
||||||
if (seedWords.length !== SEED_ENTROPY_WORDS) {
|
|
||||||
return [
|
|
||||||
new Uint8Array(0),
|
|
||||||
`Seed words should have length ${SEED_ENTROPY_WORDS} but has length ${seedWords.length}`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
// We are getting 16 bytes of entropy.
|
|
||||||
let bytes = new Uint8Array(SEED_BYTES);
|
|
||||||
let curByte = 0;
|
|
||||||
let curBit = 0;
|
|
||||||
for (let i = 0; i < SEED_ENTROPY_WORDS; i++) {
|
|
||||||
// Determine which number corresponds to the next word.
|
|
||||||
let word = -1;
|
|
||||||
for (let j = 0; j < dictionary.length; j++) {
|
|
||||||
if (
|
|
||||||
seedWords[i].slice(0, DICTIONARY_UNIQUE_PREFIX) ===
|
|
||||||
dictionary[j].slice(0, DICTIONARY_UNIQUE_PREFIX)
|
|
||||||
) {
|
|
||||||
word = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (word === -1) {
|
|
||||||
return [
|
|
||||||
new Uint8Array(0),
|
|
||||||
`word '${seedWords[i]}' at index ${i} not found in dictionary`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
let wordBits = 10;
|
|
||||||
if (i === SEED_ENTROPY_WORDS - 1) {
|
|
||||||
wordBits = 8;
|
|
||||||
}
|
|
||||||
// Iterate over the bits of the 10- or 8-bit word.
|
|
||||||
for (let j = 0; j < wordBits; j++) {
|
|
||||||
let bitSet = (word & (1 << (wordBits - j - 1))) > 0;
|
|
||||||
if (bitSet) {
|
|
||||||
bytes[curByte] |= 1 << (8 - curBit - 1);
|
|
||||||
}
|
|
||||||
curBit += 1;
|
|
||||||
if (curBit >= 8) {
|
|
||||||
// Current byte has 8 bits, go to the next byte.
|
|
||||||
curByte += 1;
|
|
||||||
curBit = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [bytes, null];
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function login(page: Page, seed = generateSeedPhrase()) {
|
|
||||||
await page.goto("http://skt.us");
|
|
||||||
|
|
||||||
let userSeed: Uint8Array;
|
|
||||||
|
|
||||||
[userSeed] = seedPhraseToSeed(seed);
|
|
||||||
let seedHex = bufToHex(userSeed);
|
|
||||||
|
|
||||||
await page.evaluate((seed: string) => {
|
|
||||||
window.localStorage.setItem("v1-seed", seed);
|
|
||||||
}, seedHex);
|
|
||||||
|
|
||||||
let kernelEntrySeed = deriveChildSeed(userSeed, "userPreferredKernel2");
|
|
||||||
|
|
||||||
// Get the registry keys.
|
|
||||||
let [keypair, dataKey] = taggedRegistryEntryKeys(
|
|
||||||
kernelEntrySeed,
|
|
||||||
"user kernel"
|
|
||||||
);
|
|
||||||
|
|
||||||
await overwriteRegistryEntry(
|
|
||||||
keypair,
|
|
||||||
dataKey,
|
|
||||||
b64ToBuf(TEST_KERNEL_SKLINK)[0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadTester(page: Page, port = 8080) {
|
export async function loadTester(page: Page, port = 8080) {
|
||||||
const server = new StaticServer({
|
const server = new StaticServer({
|
||||||
rootPath: path.resolve(__dirname, "..", "public"),
|
rootPath: path.resolve(__dirname, "..", "public"),
|
||||||
|
@ -151,25 +27,14 @@ export async function loadTester(page: Page, port = 8080) {
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
return kernel.init();
|
return kernel.init();
|
||||||
});
|
});
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
return window.main.loginRandom();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
declare function loginRandom(): Promise<any>;
|
||||||
class Tester {
|
declare global {
|
||||||
private page: Page;
|
interface Window {
|
||||||
|
loginRandom: typeof loginRandom;
|
||||||
constructor(page: Page) {
|
|
||||||
this.page = page;
|
|
||||||
}
|
|
||||||
|
|
||||||
async callModule(id: string, method: string, data = {}): Promise<errTuple> {
|
|
||||||
return this.page.evaluate(
|
|
||||||
async (id, method, data) => {
|
|
||||||
return kernel.callModule(id, method, data);
|
|
||||||
},
|
|
||||||
id,
|
|
||||||
method,
|
|
||||||
data
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tester = (page: Page) => new Tester(page);
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { loadTester, login } from "../dist/index.js";
|
import { loadTester } from "../lib/index.js";
|
||||||
|
|
||||||
import puppeteer, { Browser, Page, ProtocolError } from "puppeteer";
|
import puppeteer, { Browser, Page, ProtocolError } from "puppeteer";
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ let browser: Browser;
|
||||||
browser = await puppeteer.launch({ headless: false, devtools: true });
|
browser = await puppeteer.launch({ headless: false, devtools: true });
|
||||||
|
|
||||||
const page = (await browser.pages()).pop() as Page;
|
const page = (await browser.pages()).pop() as Page;
|
||||||
await login(page);
|
|
||||||
await loadTester(page);
|
await loadTester(page);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
150
src/tester.ts
150
src/tester.ts
|
@ -1,7 +1,147 @@
|
||||||
import * as kernel from "libkernel";
|
import * as kernel from "@lumeweb/libkernel/kernel";
|
||||||
import * as skynet from "libskynet";
|
import { x25519 } from "@noble/curves/ed25519";
|
||||||
|
import { bytesToHex, hexToBytes } from "@noble/curves/abstract/utils";
|
||||||
|
import defer from "p-defer";
|
||||||
|
import { randomBytes } from "@noble/hashes/utils";
|
||||||
|
import { secretbox } from "@noble/ciphers/salsa";
|
||||||
|
import {
|
||||||
|
addQuery,
|
||||||
|
deleteQuery,
|
||||||
|
getAuthStatus,
|
||||||
|
getAuthStatusDefer,
|
||||||
|
getAuthStatusKnown,
|
||||||
|
getLoggedInDefer,
|
||||||
|
getQueries,
|
||||||
|
getQueriesNonce,
|
||||||
|
getQuery,
|
||||||
|
increaseQueriesNonce,
|
||||||
|
resetLoggedInDefer,
|
||||||
|
setAuthStatus,
|
||||||
|
setAuthStatusKnown,
|
||||||
|
} from "./vars.js";
|
||||||
|
import { ed25519 } from "@lumeweb/libkernel";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
kernel: typeof kernel;
|
||||||
|
login: typeof login;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
window.kernel = kernel;
|
window.kernel = kernel;
|
||||||
// @ts-ignore
|
window.login = login;
|
||||||
window.skynet = skynet;
|
|
||||||
|
window.addEventListener("message", (event) => {
|
||||||
|
const data = event.data?.data;
|
||||||
|
if (event.data.method === "log") {
|
||||||
|
if (data?.isErr === false) {
|
||||||
|
console.log(data.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.error(data.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data.method === "kernelAuthStatus") {
|
||||||
|
setAuthStatus(data);
|
||||||
|
if (!getAuthStatusKnown()) {
|
||||||
|
getAuthStatusDefer().resolve();
|
||||||
|
setAuthStatusKnown(true);
|
||||||
|
console.log("bootloader is now initialized");
|
||||||
|
if (!getAuthStatus().loginComplete) {
|
||||||
|
console.log("user is not logged in: waiting until login is confirmed");
|
||||||
|
} else {
|
||||||
|
getLoggedInDefer().resolve();
|
||||||
|
}
|
||||||
|
if (getAuthStatus().logoutComplete) {
|
||||||
|
resetLoggedInDefer();
|
||||||
|
setAuthStatusKnown(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(event.data.nonce in getQueries())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let receiveResult = getQuery(event.data.nonce);
|
||||||
|
if (event.data.method === "response") {
|
||||||
|
deleteQuery(event.data.nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
receiveResult(event.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getKernelIframe() {
|
||||||
|
const iframes = Array.from(document.getElementsByTagName("iframe"));
|
||||||
|
|
||||||
|
if (!iframes.length) {
|
||||||
|
console.error("could not find kernel iframe");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iframes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loginRandom() {
|
||||||
|
return login(ed25519.utils.randomPrivateKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function login(key: Uint8Array) {
|
||||||
|
let privKey = x25519.utils.randomPrivateKey();
|
||||||
|
|
||||||
|
const iframe = getKernelIframe();
|
||||||
|
|
||||||
|
if (!iframe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pubKey: string | Uint8Array = await queryKernel({
|
||||||
|
method: "exchangeCommunicationKeys",
|
||||||
|
data: bytesToHex(x25519.getPublicKey(privKey)),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pubKey) {
|
||||||
|
alert(`Failed to login: could not get communication key`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey = hexToBytes(pubKey as string);
|
||||||
|
|
||||||
|
const secret = x25519.getSharedSecret(privKey, pubKey);
|
||||||
|
const nonce = randomBytes(24);
|
||||||
|
const box = secretbox(secret, nonce);
|
||||||
|
const ciphertext = box.seal(key);
|
||||||
|
|
||||||
|
await queryKernel({
|
||||||
|
method: "setLoginKey",
|
||||||
|
data: {
|
||||||
|
data: bytesToHex(ciphertext),
|
||||||
|
nonce: bytesToHex(nonce),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryKernel(query: any): Promise<any> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let receiveResponse = function (data: any) {
|
||||||
|
resolve(data.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
getAuthStatusDefer().promise.then(() => {
|
||||||
|
let nonce = getQueriesNonce();
|
||||||
|
increaseQueriesNonce();
|
||||||
|
query.nonce = nonce;
|
||||||
|
addQuery(nonce, receiveResponse);
|
||||||
|
if (getKernelIframe()?.contentWindow !== null) {
|
||||||
|
getKernelIframe()?.contentWindow?.postMessage(
|
||||||
|
query,
|
||||||
|
"https://kernel.lumeweb.com",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"kernelFrame.contentWindow was null, cannot send message!",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import defer from "p-defer";
|
||||||
|
import { KernelAuthStatus } from "@lumeweb/libweb";
|
||||||
|
|
||||||
|
let authStatus: KernelAuthStatus;
|
||||||
|
let authStatusKnown = false;
|
||||||
|
let authStatusDefer = defer();
|
||||||
|
let queriesNonce = 1;
|
||||||
|
let queries: any = {};
|
||||||
|
let loggedInDefer = defer();
|
||||||
|
|
||||||
|
export function getAuthStatusKnown() {
|
||||||
|
return authStatusKnown;
|
||||||
|
}
|
||||||
|
export function setAuthStatusKnown(status: boolean) {
|
||||||
|
authStatusKnown = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAuthStatus(): KernelAuthStatus {
|
||||||
|
return authStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setAuthStatus(status: KernelAuthStatus) {
|
||||||
|
authStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAuthStatusDefer() {
|
||||||
|
return authStatusDefer;
|
||||||
|
}
|
||||||
|
export function getQueriesNonce(): number {
|
||||||
|
return queriesNonce;
|
||||||
|
}
|
||||||
|
export function getQueries() {
|
||||||
|
return queries;
|
||||||
|
}
|
||||||
|
export function deleteQuery(nonce: any) {
|
||||||
|
delete queries[nonce];
|
||||||
|
}
|
||||||
|
export function getQuery(nonce: any) {
|
||||||
|
return queries[nonce];
|
||||||
|
}
|
||||||
|
export function increaseQueriesNonce() {
|
||||||
|
queriesNonce++;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addQuery(nonce: any, func: Function) {
|
||||||
|
queries[nonce] = func;
|
||||||
|
}
|
||||||
|
export function getLoggedInDefer() {
|
||||||
|
return loggedInDefer;
|
||||||
|
}
|
||||||
|
export function resetLoggedInDefer() {
|
||||||
|
loggedInDefer = defer();
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "esnext",
|
|
||||||
"module": "esnext",
|
|
||||||
"declaration": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"outDir": "./build",
|
|
||||||
"strict": true,
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src",
|
|
||||||
],
|
|
||||||
"exclude": ["node_modules", "**/__tests__/*"]
|
|
||||||
}
|
|
Loading…
Reference in New Issue