refactor: port over logic from extension background page to implement x25519/ed25519 based login with a random key
This commit is contained in:
parent
c027a02650
commit
101748af06
|
@ -9,6 +9,8 @@
|
|||
"version": "0.0.2-develop.4",
|
||||
"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"
|
||||
},
|
||||
|
@ -1782,6 +1784,14 @@
|
|||
"vite-plugin-optimizer": "^1.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/ciphers": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.1.4.tgz",
|
||||
"integrity": "sha512-d3ZR8vGSpy3v/nllS+bD/OMN5UZqusWiQqkyj7AwzTnhXFH72pF5oB4Ach6DQ50g5kXxC28LdaYBEpsyv9KOUQ==",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
"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"
|
||||
},
|
||||
|
|
25
src/index.ts
25
src/index.ts
|
@ -4,26 +4,11 @@ import * as kernel from "@lumeweb/libkernel/kernel";
|
|||
// @ts-ignore
|
||||
import StaticServer from "static-server";
|
||||
import { Page } from "puppeteer";
|
||||
import { bufToHex, ed25519 } from "@lumeweb/libkernel";
|
||||
|
||||
import * as url from "url";
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
||||
|
||||
export function generateSeedPhrase() {
|
||||
return ed25519.utils.randomPrivateKey();
|
||||
}
|
||||
|
||||
export async function login(page: Page, seed = generateSeedPhrase()) {
|
||||
await page.goto("https://kernel.lumeweb.com");
|
||||
|
||||
let seedHex = bufToHex(seed);
|
||||
|
||||
await page.evaluate((seed: string) => {
|
||||
window.localStorage.setItem("key", seed);
|
||||
}, seedHex);
|
||||
}
|
||||
|
||||
export async function loadTester(page: Page, port = 8080) {
|
||||
const server = new StaticServer({
|
||||
rootPath: path.resolve(__dirname, "..", "public"),
|
||||
|
@ -42,4 +27,14 @@ 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env node
|
||||
// @ts-ignore
|
||||
import { loadTester, login } from "../lib/index.js";
|
||||
import { loadTester } from "../lib/index.js";
|
||||
|
||||
import puppeteer, { Browser, Page, ProtocolError } from "puppeteer";
|
||||
|
||||
|
@ -10,7 +10,6 @@ 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);
|
||||
})();
|
||||
|
||||
|
|
135
src/tester.ts
135
src/tester.ts
|
@ -1,6 +1,35 @@
|
|||
import * as kernel from "@lumeweb/libkernel/kernel";
|
||||
// @ts-ignore
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
window.kernel = kernel;
|
||||
window.login = login;
|
||||
|
||||
window.addEventListener("message", (event) => {
|
||||
const data = event.data?.data;
|
||||
|
@ -11,4 +40,108 @@ window.addEventListener("message", (event) => {
|
|||
}
|
||||
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();
|
||||
}
|
Loading…
Reference in New Issue