From 43db7d9fdaf9877325aae02cdad9b7bf3adc01e9 Mon Sep 17 00:00:00 2001 From: polwex Date: Wed, 16 Jul 2025 16:54:11 +0700 Subject: XOR encryption working!!! fuck yeah!!! --- lib/passkey.ts | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) (limited to 'lib/passkey.ts') diff --git a/lib/passkey.ts b/lib/passkey.ts index 6deb656..b03ecf5 100644 --- a/lib/passkey.ts +++ b/lib/passkey.ts @@ -1,9 +1,16 @@ "use client"; +import "core-js/actual/typed-array"; import * as age from "age-encryption"; import { Platform } from "react-native"; import * as SecureStore from "expo-secure-store"; import type { AsyncRes } from "./types"; +import { randomBytes } from "ethers"; +// encreeptoor +import { sha256 } from "@noble/hashes/sha256"; +import { extract } from "@noble/hashes/hkdf"; +import { base64 } from "@scure/base"; +import { bytes2hex } from "./utils/bit"; const PASSKEY_CREDENTIAL_ID_KEY = "urbit_wallet_passkey_id"; const RELYING_PARTY_ID = "wallet.urbit.org"; // Change this to your domain @@ -212,3 +219,141 @@ export async function authenticateWithPasskey(): AsyncRes { return { error: `${error}` }; } } + +export async function getPasskeyFromUser(): AsyncRes { + try { + const nonce = randomBytes(16); + // Generate a random challenge + const challenge = new Uint8Array(32); + crypto.getRandomValues(challenge); + + const getCredentialOptions: CredentialRequestOptions = { + publicKey: { + allowCredentials: [], + challenge, + rpId: + window.location.hostname === "localhost" + ? "localhost" + : RELYING_PARTY_ID, + userVerification: "required", + extensions: { prf: { eval: prfInputs(nonce) } }, + }, + }; + + const credential = (await navigator.credentials.get( + getCredentialOptions, + )) as PublicKeyCredential; + console.log({ credential }); + + if (credential) { + console.log("User provided passkey:", credential); + const results = credential.getClientExtensionResults().prf; + console.log({ results }); + return { ok: credential }; + } + + return { error: "No passkey provided" }; + } catch (error) { + console.error("Error getting passkey from user:", error); + return { error: `${error}` }; + } +} + +const label = "age-encryption.org/fido2prf"; +function prfInputs(nonce: Uint8Array): AuthenticationExtensionsPRFValues { + const prefix = new TextEncoder().encode(label); + + const first = new Uint8Array(prefix.length + nonce.length + 1); + first.set(prefix, 0); + first[prefix.length] = 0x01; + first.set(nonce, prefix.length + 1); + + const second = new Uint8Array(prefix.length + nonce.length + 1); + second.set(prefix, 0); + second[prefix.length] = 0x02; + second.set(nonce, prefix.length + 1); + + return { first, second }; +} + +function deriveKey(results: AuthenticationExtensionsPRFValues): Uint8Array { + if (results.second === undefined) { + throw Error("Missing second PRF result"); + } + const prf = new Uint8Array( + results.first.byteLength + results.second.byteLength, + ); + prf.set(new Uint8Array(results.first as ArrayBuffer), 0); + prf.set( + new Uint8Array(results.second as ArrayBuffer), + results.first.byteLength, + ); + return extract(sha256, prf, label); +} + +async function getCredentialWithPRF( + nonce: Uint8Array, +): Promise { + const credential = (await navigator.credentials.get({ + publicKey: { + allowCredentials: [], + challenge: randomBytes(16), + rpId: + window.location.hostname === "localhost" + ? "localhost" + : RELYING_PARTY_ID, + userVerification: "required", + extensions: { prf: { eval: prfInputs(nonce) } }, + }, + })) as PublicKeyCredential; + + const results = credential.getClientExtensionResults().prf?.results; + if (results === undefined) { + throw Error("PRF extension not available (need macOS 15+, Chrome 132+)"); + } + return results; +} + +export async function pkEncryptXOR(dataBytes: Uint8Array): Promise { + const nonce = randomBytes(16); + const results = await getCredentialWithPRF(nonce); + const key = deriveKey(results); + + const encrypted = new Uint8Array(dataBytes.length); + + console.log({ key: key.byteLength, data: encrypted.byteLength }); + // XOR with derived key (repeat key if data is longer) + const hash = Uint8Array.from(key, (v, i) => v ^ encrypted[i]); + for (let i = 0; i < dataBytes.length; i++) { + encrypted[i] = dataBytes[i] ^ key[i % key.length]; + } + + console.log({ hash, encrypted }); + // Return nonce + encrypted data as base64 + const result = new Uint8Array(nonce.length + encrypted.length); + result.set(nonce); + result.set(encrypted, nonce.length); + + return base64.encode(result); +} + +export async function pkDecryptXOR(encryptedData: string): Promise { + const data = base64.decode(encryptedData); + const nonce = data.slice(0, 16); + const encrypted = data.slice(16); + + const results = await getCredentialWithPRF(nonce); + const key = deriveKey(results); + + const decrypted = new Uint8Array(encrypted.length); + + // XOR with derived key + for (let i = 0; i < encrypted.length; i++) { + decrypted[i] = encrypted[i] ^ key[i % key.length]; + } + const hash = Uint8Array.from(key, (v, i) => v ^ encrypted[i]); + + const one = bytes2hex(decrypted); + const two = bytes2hex(hash); + return one; +} -- cgit v1.2.3