Compare commits
No commits in common. "develop" and "master" have entirely different histories.
|
@ -1,13 +0,0 @@
|
|||
name: Build/Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- develop-*
|
||||
|
||||
jobs:
|
||||
main:
|
||||
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
|
||||
secrets: inherit
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"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*"
|
||||
}
|
||||
}
|
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -1,31 +0,0 @@
|
|||
## [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)
|
|
@ -0,0 +1,2 @@
|
|||
#!/usr/bin/env node
|
||||
export {};
|
|
@ -0,0 +1,19 @@
|
|||
#!/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;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
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 {};
|
|
@ -0,0 +1,129 @@
|
|||
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
48
package.json
48
package.json
|
@ -1,38 +1,30 @@
|
|||
{
|
||||
"name": "@lumeweb/kernel-sandbox",
|
||||
"version": "0.0.2-develop.6",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "gitea@git.lumeweb.com:LumeWeb/kernel-sandbox.git"
|
||||
},
|
||||
"name": "@lumeweb/kernel-tester",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"prepare": "presetter bootstrap",
|
||||
"build": "run build build:bin build:lib",
|
||||
"semantic-release": "semantic-release"
|
||||
"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/"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"kernel-sandbox": "bin/sandbox.js"
|
||||
"dependencies": {
|
||||
"libkernel": "^0.1.41",
|
||||
"libskynet": "^0.0.48",
|
||||
"libskynetnode": "^0.1.3",
|
||||
"static-server": "^2.2.1",
|
||||
"puppeteer": "^15.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lumeweb/presetter-kernel-module-preset": "^0.1.0-develop.43",
|
||||
"@rollup/plugin-wasm": "^6.1.3",
|
||||
"presetter": "*"
|
||||
"@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"
|
||||
},
|
||||
"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"
|
||||
"bin": {
|
||||
"kernel-sandbox": "bin/sandbox.js"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export {};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
|||
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,14 +1,138 @@
|
|||
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 kernel from "@lumeweb/libkernel/kernel";
|
||||
|
||||
import { overwriteRegistryEntry } from "libskynetnode";
|
||||
import * as kernel from "libkernel";
|
||||
import { webcrypto } from "crypto";
|
||||
// @ts-ignore
|
||||
import StaticServer from "static-server";
|
||||
import { Page } from "puppeteer";
|
||||
import { errTuple } from "libskynet";
|
||||
|
||||
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 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) {
|
||||
const server = new StaticServer({
|
||||
rootPath: path.resolve(__dirname, "..", "public"),
|
||||
|
@ -27,14 +151,25 @@ export async function loadTester(page: Page, port = 8080) {
|
|||
await page.evaluate(() => {
|
||||
return kernel.init();
|
||||
});
|
||||
await page.evaluate(() => {
|
||||
// @ts-ignore
|
||||
return window.main.loginRandom();
|
||||
});
|
||||
}
|
||||
declare function loginRandom(): Promise<any>;
|
||||
declare global {
|
||||
interface Window {
|
||||
loginRandom: typeof loginRandom;
|
||||
|
||||
class Tester {
|
||||
private page: Page;
|
||||
|
||||
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
|
||||
// @ts-ignore
|
||||
import { loadTester } from "../lib/index.js";
|
||||
import { loadTester, login } from "../dist/index.js";
|
||||
|
||||
import puppeteer, { Browser, Page, ProtocolError } from "puppeteer";
|
||||
|
||||
|
@ -10,6 +10,7 @@ let browser: Browser;
|
|||
browser = await puppeteer.launch({ headless: false, devtools: true });
|
||||
|
||||
const page = (await browser.pages()).pop() as Page;
|
||||
await login(page);
|
||||
await loadTester(page);
|
||||
})();
|
||||
|
||||
|
|
150
src/tester.ts
150
src/tester.ts
|
@ -1,147 +1,7 @@
|
|||
import * as kernel from "@lumeweb/libkernel/kernel";
|
||||
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;
|
||||
}
|
||||
}
|
||||
import * as kernel from "libkernel";
|
||||
import * as skynet from "libskynet";
|
||||
|
||||
// @ts-ignore
|
||||
window.kernel = kernel;
|
||||
window.login = login;
|
||||
|
||||
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!",
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// @ts-ignore
|
||||
window.skynet = skynet;
|
||||
|
|
53
src/vars.ts
53
src/vars.ts
|
@ -1,53 +0,0 @@
|
|||
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();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"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