From 6179b767f3e3c0460d20382c93806aede44291af Mon Sep 17 00:00:00 2001 From: Believethehype Date: Thu, 15 Feb 2024 10:37:41 +0100 Subject: [PATCH] login with nsec/ncryptsec --- ui/noogle/src/components/Login.vue | 111 ++++++++++++++++++ .../android-signer/helpers/nip19.ts | 12 +- .../android-signer/helpers/nip49.ts | 62 ++++++++++ 3 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 ui/noogle/src/components/android-signer/helpers/nip49.ts diff --git a/ui/noogle/src/components/Login.vue b/ui/noogle/src/components/Login.vue index 0037bdd..e2dda6d 100644 --- a/ui/noogle/src/components/Login.vue +++ b/ui/noogle/src/components/Login.vue @@ -35,9 +35,27 @@

Use a Browser Nip07 Extension like getalby or nos2x to login or use Amber on Android

+ + + + + @@ -73,29 +91,39 @@ import Nip89 from "@/components/Nip89.vue"; import miniToastr from "mini-toastr"; import deadnip89s from "@/components/data/deadnip89s.json"; import amberSignerService from "./android-signer/AndroidSigner"; +import nip49, {decryptwrapper} from "./android-signer/helpers/nip49"; import { init as initNostrLogin } from "nostr-login" import { launch as launchNostrLoginDialog } from "nostr-login" import { logout as logoutNostrLogin } from "nostr-login" import {useDark, useToggle} from "@vueuse/core"; +import {ref} from "vue"; const isDark = useDark(); + //const toggleDark = useToggle(isDark); let nip89dvms = [] +const nsec = ref(""); let logger = false export default { + data() { return { current_user: "", avatar: "", signer: "", supports_android_signer: false, + ncryptsec: ref(""), + pw: ref("") + + }; }, + async mounted() { try { @@ -108,6 +136,9 @@ export default { if (localStorage.getItem('nostr-key-method') === 'nip07') { await this.sign_in_nip07() } + if (localStorage.getItem('nostr-key-method') === 'nsec') { + await this.sign_in_key(localStorage.getItem('nostr-key')) + } else if (localStorage.getItem('nostr-key-method') === 'nostr-login'){ console.log(localStorage.getItem('__nostrlogin_nip46')) await this.sign_in_nostr_login() @@ -242,6 +273,70 @@ export default { } catch (error) { console.log(error); } + }, + async sign_in_key(nsec = "") { + try { + await loadWasmAsync(); + if(logger){ + try { + initLogger(LogLevel.debug()); + } catch (error) { + console.log(error); + } + } + let key = "" + if (nsec !== ""){ + key = nsec + } + else{ + key = this.ncryptsec + if(this.ncryptsec.startsWith("ncryptsec")){ + + key = nip49.decryptwrapper(this.ncryptsec, this.pw) + } + } + + + + let keys = Keys.fromSkStr(key) + this.signer = ClientSigner.keys(keys) + let opts = new Options().waitForSend(false).connectionTimeout(Duration.fromSecs(5)); + let client = new ClientBuilder().signer(this.signer).opts(opts).build() + + for (const relay of store.state.relays){ + await client.addRelay(relay); + } + + const pubkey = keys.publicKey + await client.connect(); + + /* + const filter = new Filter().kind(6302).limit(20) + await client.reconcile(filter); + const filterl = new Filter().author(pubkey) + let test = await client.database.query([filterl]) + for (let ev of test){ + console.log(ev.asJson()) + }*/ + + + + + store.commit('set_client', client) + store.commit('set_pubkey', pubkey) + store.commit('set_hasEventListener', false) + console.log("LOGINANON") + localStorage.setItem('nostr-key-method', "nsec") + localStorage.setItem('nostr-key', keys.secretKey.toBech32()) + console.log("Client key connected") + await this.get_user_info(pubkey) + await this.reconcile_all_profiles(pubkey) + + + } catch (error) { + console.log(error); + miniToastr.showMessage(error) + } }, async sign_in_nip07() { @@ -595,4 +690,20 @@ async reconcile_all_profiles(publicKey) { width: 70px } + +.u-Input { + @apply bg-base-200 dark:bg-base-200 dark:text-white focus:ring-white border border-transparent px-3 py-1.5 text-sm leading-4 text-accent-content transition-colors duration-300 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900; + + width: 220px; + height: 35px; + + +} + +.modal{ + @apply dark:text-white +} + + + \ No newline at end of file diff --git a/ui/noogle/src/components/android-signer/helpers/nip19.ts b/ui/noogle/src/components/android-signer/helpers/nip19.ts index d3750e5..baa8872 100644 --- a/ui/noogle/src/components/android-signer/helpers/nip19.ts +++ b/ui/noogle/src/components/android-signer/helpers/nip19.ts @@ -1,5 +1,7 @@ import { getPublicKey, nip19 } from "nostr-tools"; +import { bech32 } from '@scure/base' +export const Bech32MaxSize = 5000 export function isHexKey(key?: string) { if (key?.toLowerCase()?.match(/^[0-9a-f]{64}$/)) return true; return false; @@ -20,4 +22,12 @@ export function getPubkeyFromDecodeResult(result?: nip19.DecodeResult) { case "nsec": return getPublicKey(result.data); } -} \ No newline at end of file +} + +function encodeBech32(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` { + let words = bech32.toWords(data) + return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}` +} +export function encodeBytes(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` { + return encodeBech32(prefix, bytes) +} diff --git a/ui/noogle/src/components/android-signer/helpers/nip49.ts b/ui/noogle/src/components/android-signer/helpers/nip49.ts new file mode 100644 index 0000000..b18e535 --- /dev/null +++ b/ui/noogle/src/components/android-signer/helpers/nip49.ts @@ -0,0 +1,62 @@ +import { scrypt } from '@noble/hashes/scrypt' +import { xchacha20poly1305 } from '@noble/ciphers/chacha' +import { concatBytes, randomBytes } from '@noble/hashes/utils' +import { Bech32MaxSize, encodeBytes } from './nip19' +import { bech32 } from '@scure/base' +import { hexToBytes, bytesToHex } from '@noble/hashes/utils' + + +export function encrypt(sec: Uint8Array, password: string, logn: number = 16, ksb: 0x00 | 0x01 | 0x02 = 0x02): string { + let salt = randomBytes(16) + let n = 2 ** logn + let key = scrypt(password, salt, { N: n, r: 8, p: 1, dkLen: 32 }) + let nonce = randomBytes(24) + let aad = Uint8Array.from([ksb]) + let xc2p1 = xchacha20poly1305(key, nonce, aad) + let ciphertext = xc2p1.encrypt(sec) + let b = concatBytes(Uint8Array.from([0x02]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext) + return encodeBytes('ncryptsec', b) +} + +export function decrypt(ncryptsec: string, password: string): Uint8Array { + let { prefix, words } = bech32.decode(ncryptsec, Bech32MaxSize) + if (prefix !== 'ncryptsec') { + throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`) + } + let b = new Uint8Array(bech32.fromWords(words)) + + let version = b[0] + if (version !== 0x02) { + throw new Error(`invalid version ${version}, expected 0x02`) + } + + let logn = b[1] + let n = 2 ** logn + + let salt = b.slice(2, 2 + 16) + let nonce = b.slice(2 + 16, 2 + 16 + 24) + let ksb = b[2 + 16 + 24] + let aad = Uint8Array.from([ksb]) + let ciphertext = b.slice(2 + 16 + 24 + 1) + + let key = scrypt(password, salt, { N: n, r: 8, p: 1, dkLen: 32 }) + let xc2p1 = xchacha20poly1305(key, nonce, aad) + let sec = xc2p1.decrypt(ciphertext) + + return sec +} + + +export function decryptwrapper(ncryptsec: string, password: string): String { + return bytesToHex(decrypt(ncryptsec, password)) +} + + +const nip49 = { + encrypt, + decrypt, + decryptwrapper + +}; + +export default nip49; \ No newline at end of file