1
0
forked from Kieran/snort

bug: fix build for older browsers

This commit is contained in:
Kieran 2023-05-17 12:59:30 +01:00
parent 6e16e7b5fb
commit e0251ab389
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
18 changed files with 104 additions and 73 deletions

View File

@ -3,14 +3,14 @@
"version": "0.1.8", "version": "0.1.8",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@cashu/cashu-ts": "^0.6.1",
"@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1", "@fortawesome/free-solid-svg-icons": "^6.2.1",
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@cashu/cashu-ts": "^0.6.1",
"@jukben/emoji-search": "^2.0.1", "@jukben/emoji-search": "^2.0.1",
"@lightninglabs/lnc-web": "^0.2.3-alpha", "@lightninglabs/lnc-web": "^0.2.3-alpha",
"@noble/curves": "^1.0.0",
"@noble/hashes": "^1.2.0", "@noble/hashes": "^1.2.0",
"@noble/secp256k1": "^1.7.0",
"@protobufjs/base64": "^1.1.2", "@protobufjs/base64": "^1.1.2",
"@reduxjs/toolkit": "^1.9.1", "@reduxjs/toolkit": "^1.9.1",
"@scure/bip32": "^1.1.5", "@scure/bip32": "^1.1.5",
@ -69,7 +69,11 @@
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
">0.5%" "chrome >= 67",
"edge >= 79",
"firefox >= 68",
"opera >= 54",
"safari >= 14"
], ],
"development": [ "development": [
"last 1 chrome version", "last 1 chrome version",

View File

@ -1,4 +1,4 @@
import * as secp from "@noble/secp256k1"; import * as utils from "@noble/curves/abstract/utils";
import * as base64 from "@protobufjs/base64"; import * as base64 from "@protobufjs/base64";
import { hmacSha256, unwrap } from "Util"; import { hmacSha256, unwrap } from "Util";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
@ -19,8 +19,8 @@ export default function useImgProxy() {
function signUrl(u: string) { function signUrl(u: string) {
const result = hmacSha256( const result = hmacSha256(
secp.utils.hexToBytes(unwrap(settings).key), utils.hexToBytes(unwrap(settings).key),
secp.utils.hexToBytes(unwrap(settings).salt), utils.hexToBytes(unwrap(settings).salt),
te.encode(u) te.encode(u)
); );
return urlSafe(base64.encode(result, 0, result.byteLength)); return urlSafe(base64.encode(result, 0, result.byteLength));

View File

@ -1,5 +1,4 @@
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import * as secp from "@noble/secp256k1";
import { EmailRegex, MnemonicRegex } from "Const"; import { EmailRegex, MnemonicRegex } from "Const";
import { LoginStore } from "Login"; import { LoginStore } from "Login";
@ -21,7 +20,7 @@ export default function useLoginHandler() {
throw new Error(insecureMsg); throw new Error(insecureMsg);
} }
const hexKey = bech32ToHex(key); const hexKey = bech32ToHex(key);
if (secp.utils.isValidPrivateKey(hexKey)) { if (hexKey.length === 64) {
LoginStore.loginWithPrivateKey(hexKey); LoginStore.loginWithPrivateKey(hexKey);
} else { } else {
throw new Error("INVALID PRIVATE KEY"); throw new Error("INVALID PRIVATE KEY");
@ -39,7 +38,7 @@ export default function useLoginHandler() {
const ent = generateBip39Entropy(key); const ent = generateBip39Entropy(key);
const keyHex = entropyToPrivateKey(ent); const keyHex = entropyToPrivateKey(ent);
LoginStore.loginWithPrivateKey(keyHex); LoginStore.loginWithPrivateKey(keyHex);
} else if (secp.utils.isValidPrivateKey(key)) { } else if (key.length === 64) {
if (!hasSubtleCrypto) { if (!hasSubtleCrypto) {
throw new Error(insecureMsg); throw new Error(insecureMsg);
} }

View File

@ -1,5 +1,6 @@
import { HexKey, RelaySettings } from "@snort/nostr"; import { HexKey, RelaySettings } from "@snort/nostr";
import * as secp from "@noble/secp256k1"; import * as secp from "@noble/curves/secp256k1";
import * as utils from "@noble/curves/abstract/utils";
import { DefaultRelays, SnortPubKey } from "Const"; import { DefaultRelays, SnortPubKey } from "Const";
import { LoginStore, UserPreferences, LoginSession } from "Login"; import { LoginStore, UserPreferences, LoginSession } from "Login";
@ -57,7 +58,7 @@ export function clearEntropy(state: LoginSession) {
*/ */
export async function generateNewLogin() { export async function generateNewLogin() {
const ent = generateBip39Entropy(); const ent = generateBip39Entropy();
const entropy = secp.utils.bytesToHex(ent); const entropy = utils.bytesToHex(ent);
const privateKey = entropyToPrivateKey(ent); const privateKey = entropyToPrivateKey(ent);
let newRelays: Record<string, RelaySettings> = {}; let newRelays: Record<string, RelaySettings> = {};
@ -76,7 +77,7 @@ export async function generateNewLogin() {
console.warn(e); console.warn(e);
} }
const publicKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(privateKey)); const publicKey = utils.bytesToHex(secp.schnorr.getPublicKey(privateKey));
const publisher = new EventPublisher(publicKey, privateKey); const publisher = new EventPublisher(publicKey, privateKey);
const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays); const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays);
publisher.broadcast(ev); publisher.broadcast(ev);
@ -85,8 +86,8 @@ export async function generateNewLogin() {
} }
export function generateRandomKey() { export function generateRandomKey() {
const privateKey = secp.utils.bytesToHex(secp.utils.randomPrivateKey()); const privateKey = utils.bytesToHex(secp.schnorr.utils.randomPrivateKey());
const publicKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(privateKey)); const publicKey = utils.bytesToHex(secp.schnorr.getPublicKey(privateKey));
return { return {
privateKey, privateKey,
publicKey, publicKey,

View File

@ -1,4 +1,6 @@
import * as secp from "@noble/secp256k1"; import * as secp from "@noble/curves/secp256k1";
import * as utils from "@noble/curves/abstract/utils";
import { HexKey, RelaySettings } from "@snort/nostr"; import { HexKey, RelaySettings } from "@snort/nostr";
import { DefaultRelays } from "Const"; import { DefaultRelays } from "Const";
@ -113,7 +115,7 @@ export class MultiAccountStore extends ExternalStore<LoginSession> {
} }
loginWithPrivateKey(key: HexKey, entropy?: string, relays?: Record<string, RelaySettings>) { loginWithPrivateKey(key: HexKey, entropy?: string, relays?: Record<string, RelaySettings>) {
const pubKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(key)); const pubKey = utils.bytesToHex(secp.schnorr.getPublicKey(key));
if (this.#accounts.has(pubKey)) { if (this.#accounts.has(pubKey)) {
throw new Error("Already logged in with this pubkey"); throw new Error("Already logged in with this pubkey");
} }
@ -167,7 +169,7 @@ export class MultiAccountStore extends ExternalStore<LoginSession> {
const privKey = window.localStorage.getItem(LegacyKeys.PrivateKeyItem); const privKey = window.localStorage.getItem(LegacyKeys.PrivateKeyItem);
if (privKey) { if (privKey) {
const pubKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(privKey)); const pubKey = utils.bytesToHex(secp.schnorr.getPublicKey(privKey));
this.#accounts.set(pubKey, { this.#accounts.set(pubKey, {
...LoggedOut, ...LoggedOut,
privateKey: privKey, privateKey: privKey,

View File

@ -1,4 +1,5 @@
import * as secp from "@noble/secp256k1"; import * as secp from "@noble/curves/secp256k1";
import * as utils from "@noble/curves/abstract/utils";
import { EventKind, HexKey, RawEvent, Tag } from "@snort/nostr"; import { EventKind, HexKey, RawEvent, Tag } from "@snort/nostr";
import base64 from "@protobufjs/base64"; import base64 from "@protobufjs/base64";
import { sha256, unixNow } from "Util"; import { sha256, unixNow } from "Util";
@ -29,7 +30,7 @@ export abstract class EventExt {
e.id = this.createId(e); e.id = this.createId(e);
const sig = await secp.schnorr.sign(e.id, key); const sig = await secp.schnorr.sign(e.id, key);
e.sig = secp.utils.bytesToHex(sig); e.sig = utils.bytesToHex(sig);
if (!(await secp.schnorr.verify(e.sig, e.id, e.pubkey))) { if (!(await secp.schnorr.verify(e.sig, e.id, e.pubkey))) {
throw new Error("Signing failed"); throw new Error("Signing failed");
} }
@ -158,7 +159,7 @@ export abstract class EventExt {
} }
static async #getDmSharedKey(pubkey: HexKey, privkey: HexKey) { static async #getDmSharedKey(pubkey: HexKey, privkey: HexKey) {
const sharedPoint = secp.getSharedSecret(privkey, "02" + pubkey); const sharedPoint = secp.secp256k1.getSharedSecret(privkey, "02" + pubkey);
const sharedX = sharedPoint.slice(1, 33); const sharedX = sharedPoint.slice(1, 33);
return await window.crypto.subtle.importKey("raw", sharedX, { name: "AES-CBC" }, false, ["encrypt", "decrypt"]); return await window.crypto.subtle.importKey("raw", sharedX, { name: "AES-CBC" }, false, ["encrypt", "decrypt"]);
} }

View File

@ -1,4 +1,5 @@
import * as secp from "@noble/secp256k1"; import * as secp from "@noble/curves/secp256k1";
import * as utils from "@noble/curves/abstract/utils";
import { import {
EventKind, EventKind,
FullRelaySettings, FullRelaySettings,
@ -60,7 +61,7 @@ export class EventPublisher {
constructor(pubKey: string, privKey?: string) { constructor(pubKey: string, privKey?: string) {
if (privKey) { if (privKey) {
this.#privateKey = privKey; this.#privateKey = privKey;
this.#pubKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(privKey)); this.#pubKey = utils.bytesToHex(secp.schnorr.getPublicKey(privKey));
} else { } else {
this.#pubKey = pubKey; this.#pubKey = pubKey;
} }

View File

@ -1,4 +1,4 @@
import * as secp from "@noble/secp256k1"; import * as utils from "@noble/curves/abstract/utils";
import { EventKind } from "@snort/nostr"; import { EventKind } from "@snort/nostr";
import { FileExtensionRegex, VoidCatHost } from "Const"; import { FileExtensionRegex, VoidCatHost } from "Const";
import { EventPublisher } from "System/EventPublisher"; import { EventPublisher } from "System/EventPublisher";
@ -25,7 +25,7 @@ export default async function VoidCat(
"Content-Type": "application/octet-stream", "Content-Type": "application/octet-stream",
"V-Content-Type": file.type, "V-Content-Type": file.type,
"V-Filename": filename, "V-Filename": filename,
"V-Full-Digest": secp.utils.bytesToHex(new Uint8Array(digest)), "V-Full-Digest": utils.bytesToHex(new Uint8Array(digest)),
"V-Description": "Upload from https://snort.social", "V-Description": "Upload from https://snort.social",
"V-Strip-Metadata": "true", "V-Strip-Metadata": "true",
}, },

View File

@ -1,4 +1,5 @@
import * as secp from "@noble/secp256k1"; import * as secp from "@noble/curves/secp256k1";
import * as utils from "@noble/curves/abstract/utils";
import { sha256 as hash } from "@noble/hashes/sha256"; import { sha256 as hash } from "@noble/hashes/sha256";
import { hmac } from "@noble/hashes/hmac"; import { hmac } from "@noble/hashes/hmac";
import { bytesToHex } from "@noble/hashes/utils"; import { bytesToHex } from "@noble/hashes/utils";
@ -19,11 +20,11 @@ import {
import { MetadataCache } from "Cache"; import { MetadataCache } from "Cache";
export const sha256 = (str: string | Uint8Array): u256 => { export const sha256 = (str: string | Uint8Array): u256 => {
return secp.utils.bytesToHex(hash(str)); return utils.bytesToHex(hash(str));
}; };
export function getPublicKey(privKey: HexKey) { export function getPublicKey(privKey: HexKey) {
return secp.utils.bytesToHex(secp.schnorr.getPublicKey(privKey)); return utils.bytesToHex(secp.schnorr.getPublicKey(privKey));
} }
export async function openFile(): Promise<File | undefined> { export async function openFile(): Promise<File | undefined> {
@ -63,7 +64,7 @@ export function bech32ToHex(str: string) {
try { try {
const nKey = bech32.decode(str, 1_000); const nKey = bech32.decode(str, 1_000);
const buff = bech32.fromWords(nKey.words); const buff = bech32.fromWords(nKey.words);
return secp.utils.bytesToHex(Uint8Array.from(buff)); return utils.bytesToHex(Uint8Array.from(buff));
} catch { } catch {
return str; return str;
} }
@ -116,7 +117,7 @@ export function hexToBech32(hrp: string, hex?: string) {
try { try {
if (hrp === NostrPrefix.Note || hrp === NostrPrefix.PrivateKey || hrp === NostrPrefix.PublicKey) { if (hrp === NostrPrefix.Note || hrp === NostrPrefix.PrivateKey || hrp === NostrPrefix.PublicKey) {
const buf = secp.utils.hexToBytes(hex); const buf = utils.hexToBytes(hex);
return bech32.encode(hrp, bech32.toWords(buf)); return bech32.encode(hrp, bech32.toWords(buf));
} else { } else {
return encodeTLV(hrp as NostrPrefix, hex); return encodeTLV(hrp as NostrPrefix, hex);
@ -488,7 +489,7 @@ export function findTag(e: RawEvent, tag: string) {
} }
export function hmacSha256(key: Uint8Array, ...messages: Uint8Array[]) { export function hmacSha256(key: Uint8Array, ...messages: Uint8Array[]) {
return hmac(hash, key, secp.utils.concatBytes(...messages)); return hmac(hash, key, utils.concatBytes(...messages));
} }
export function getRelayName(url: string) { export function getRelayName(url: string) {

View File

@ -1,4 +1,4 @@
import * as secp from "@noble/secp256k1"; import * as utils from "@noble/curves/abstract/utils";
import * as bip39 from "@scure/bip39"; import * as bip39 from "@scure/bip39";
import { wordlist } from "@scure/bip39/wordlists/english"; import { wordlist } from "@scure/bip39/wordlists/english";
import { HDKey } from "@scure/bip32"; import { HDKey } from "@scure/bip32";
@ -18,7 +18,7 @@ export function generateBip39Entropy(mnemonic?: string): Uint8Array {
* Convert hex-encoded entropy into mnemonic phrase * Convert hex-encoded entropy into mnemonic phrase
*/ */
export function hexToMnemonic(hex: string): string { export function hexToMnemonic(hex: string): string {
const bytes = secp.utils.hexToBytes(hex); const bytes = utils.hexToBytes(hex);
return bip39.entropyToMnemonic(bytes, wordlist); return bip39.entropyToMnemonic(bytes, wordlist);
} }
@ -33,5 +33,5 @@ export function entropyToPrivateKey(entropy: Uint8Array): string {
throw new Error("INVALID KEY DERIVATION"); throw new Error("INVALID KEY DERIVATION");
} }
return secp.utils.bytesToHex(newKey.privateKey); return utils.bytesToHex(newKey.privateKey);
} }

View File

@ -29,8 +29,8 @@
"semi": false "semi": false
}, },
"dependencies": { "dependencies": {
"@noble/curves": "^1.0.0",
"@noble/hashes": "^1.2.0", "@noble/hashes": "^1.2.0",
"@noble/secp256k1": "^1.7.1",
"@types/chai": "^4.3.4", "@types/chai": "^4.3.4",
"@types/uuid": "^9.0.1", "@types/uuid": "^9.0.1",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
@ -47,8 +47,22 @@
"directories": { "directories": {
"test": "test" "test": "test"
}, },
"browserslist": {
"production": [
"chrome >= 67",
"edge >= 79",
"firefox >= 68",
"opera >= 54",
"safari >= 14"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"description": "" "description": ""
} }

View File

@ -38,7 +38,7 @@ export class Conn {
onError, onError,
}: { }: {
url: URL url: URL
onMessage: (msg: IncomingMessage) => Promise<void> onMessage: (msg: IncomingMessage) => void
onOpen: () => Promise<void> onOpen: () => Promise<void>
onClose: () => void onClose: () => void
onError: (err: unknown) => void onError: (err: unknown) => void
@ -55,7 +55,7 @@ export class Conn {
throw new NostrError(`invalid message data: ${value}`) throw new NostrError(`invalid message data: ${value}`)
} }
const msg = parseIncomingMessage(value) const msg = parseIncomingMessage(value)
await onMessage(msg) onMessage(msg)
} catch (err) { } catch (err) {
onError(err) onError(err)
} }

View File

@ -1,7 +1,7 @@
import { NostrError } from "../common" import { NostrError } from "../common"
import { RawEvent, parseEvent } from "../event" import { RawEvent, parseEvent } from "../event"
import { Conn } from "./conn" import { Conn } from "./conn"
import * as secp from "@noble/secp256k1" import * as utils from "@noble/curves/abstract/utils";
import { EventEmitter } from "./emitter" import { EventEmitter } from "./emitter"
import { fetchRelayInfo, ReadyState, Relay } from "./relay" import { fetchRelayInfo, ReadyState, Relay } from "./relay"
import { Filters } from "../filters" import { Filters } from "../filters"
@ -71,21 +71,21 @@ export class Nostr extends EventEmitter {
opts?.fetchInfo === false opts?.fetchInfo === false
? Promise.resolve({}) ? Promise.resolve({})
: fetchRelayInfo(relayUrl).catch((e) => { : fetchRelayInfo(relayUrl).catch((e) => {
this.#error(e) this.#error(e)
return {} return {}
}) })
// If there is no existing connection, open a new one. // If there is no existing connection, open a new one.
const conn = new Conn({ const conn = new Conn({
url: relayUrl, url: relayUrl,
// Handle messages on this connection. // Handle messages on this connection.
onMessage: async (msg) => { onMessage: (msg) => {
if (msg.kind === "event") { if (msg.kind === "event") {
this.emit( this.emit(
"event", "event",
{ {
event: await parseEvent(msg.event), event: parseEvent(msg.event),
subscriptionId: msg.subscriptionId, subscriptionId: msg.subscriptionId,
}, },
this this
@ -128,8 +128,7 @@ export class Nostr extends EventEmitter {
if (conn.relay.readyState !== ReadyState.CONNECTING) { if (conn.relay.readyState !== ReadyState.CONNECTING) {
this.#error( this.#error(
new NostrError( new NostrError(
`bug: expected connection to ${relayUrl.toString()} to have readyState CONNECTING, got ${ `bug: expected connection to ${relayUrl.toString()} to have readyState CONNECTING, got ${conn.relay.readyState
conn.relay.readyState
}` }`
) )
) )
@ -294,7 +293,7 @@ export class Nostr extends EventEmitter {
relay.info === undefined relay.info === undefined
? undefined ? undefined
: // Deep copy of the info. : // Deep copy of the info.
JSON.parse(JSON.stringify(relay.info)) JSON.parse(JSON.stringify(relay.info))
return { ...relay, info } return { ...relay, info }
} }
}) })
@ -330,5 +329,5 @@ interface ConnState {
export type SubscriptionId = string export type SubscriptionId = string
function randomSubscriptionId(): SubscriptionId { function randomSubscriptionId(): SubscriptionId {
return secp.utils.bytesToHex(secp.utils.randomBytes(32)) return utils.bytesToHex(globalThis.crypto.getRandomValues(new Uint8Array(32)))
} }

View File

@ -1,4 +1,6 @@
import * as secp from "@noble/secp256k1" import * as secp from "@noble/curves/secp256k1"
import * as utils from "@noble/curves/abstract/utils";
import {sha256 as sha} from "@noble/hashes/sha256";
import base64 from "base64-js" import base64 from "base64-js"
import { bech32 } from "bech32" import { bech32 } from "bech32"
@ -43,7 +45,7 @@ export function getPublicKey(priv: HexOrBechPrivateKey): PublicKey {
* Convert the data to lowercase hex. * Convert the data to lowercase hex.
*/ */
function toHex(data: Uint8Array): Hex { function toHex(data: Uint8Array): Hex {
return secp.utils.bytesToHex(data).toLowerCase() return utils.bytesToHex(data).toLowerCase()
} }
/** /**
@ -76,15 +78,15 @@ function parseKey(key: string, bechPrefix: string): Hex {
/** /**
* Get the SHA256 hash of the data, in hex format. * Get the SHA256 hash of the data, in hex format.
*/ */
export async function sha256(data: Uint8Array): Promise<Hex> { export function sha256(data: Uint8Array): Hex {
return toHex(await secp.utils.sha256(data)) return toHex(sha(data))
} }
/** /**
* Sign the data using elliptic curve cryptography. * Sign the data using elliptic curve cryptography.
*/ */
export async function schnorrSign(data: Hex, priv: PrivateKey): Promise<Hex> { export function schnorrSign(data: Hex, priv: PrivateKey): Hex {
return toHex(await secp.schnorr.sign(data, priv)) return toHex(secp.schnorr.sign(data, priv))
} }
/** /**
@ -94,7 +96,7 @@ export function schnorrVerify(
sig: Hex, sig: Hex,
data: Hex, data: Hex,
key: PublicKey key: PublicKey
): Promise<boolean> { ): boolean {
return secp.schnorr.verify(sig.toString(), data.toString(), key.toString()) return secp.schnorr.verify(sig.toString(), data.toString(), key.toString())
} }
@ -103,7 +105,7 @@ export async function aesEncryptBase64(
recipient: PublicKey, recipient: PublicKey,
plaintext: string plaintext: string
): Promise<AesEncryptedBase64> { ): Promise<AesEncryptedBase64> {
const sharedPoint = secp.getSharedSecret(sender, "02" + recipient) const sharedPoint = secp.secp256k1.getSharedSecret(sender, "02" + recipient)
const sharedKey = sharedPoint.slice(1, 33) const sharedKey = sharedPoint.slice(1, 33)
if (typeof window === "object") { if (typeof window === "object") {
const key = await window.crypto.subtle.importKey( const key = await window.crypto.subtle.importKey(
@ -149,7 +151,7 @@ export async function aesDecryptBase64(
recipient: PrivateKey, recipient: PrivateKey,
{ data, iv }: AesEncryptedBase64 { data, iv }: AesEncryptedBase64
): Promise<string> { ): Promise<string> {
const sharedPoint = secp.getSharedSecret(recipient, "02" + sender) const sharedPoint = secp.secp256k1.getSharedSecret(recipient, "02" + sender)
const sharedKey = sharedPoint.slice(1, 33) const sharedKey = sharedPoint.slice(1, 33)
if (typeof window === "object") { if (typeof window === "object") {
const decodedData = base64.toByteArray(data) const decodedData = base64.toByteArray(data)

View File

@ -128,12 +128,12 @@ export async function signEvent<T extends RawEvent>(
if (priv !== undefined) { if (priv !== undefined) {
priv = parsePrivateKey(priv) priv = parsePrivateKey(priv)
event.pubkey = getPublicKey(priv) event.pubkey = getPublicKey(priv)
const id = await serializeEventId( const id = serializeEventId(
// This conversion is safe because the pubkey field is set above. // This conversion is safe because the pubkey field is set above.
event as unknown as UnsignedWithPubkey<T> event as unknown as UnsignedWithPubkey<T>
) )
event.id = id event.id = id
event.sig = await schnorrSign(id, priv) event.sig = schnorrSign(id, priv)
return event as T return event as T
} else { } else {
if (typeof window === "undefined" || window.nostr === undefined) { if (typeof window === "undefined" || window.nostr === undefined) {
@ -158,15 +158,15 @@ export async function signEvent<T extends RawEvent>(
/** /**
* Parse an event from its raw format. * Parse an event from its raw format.
*/ */
export async function parseEvent(event: RawEvent): Promise<Event> { export function parseEvent(event: RawEvent): Event {
if (event.id !== (await serializeEventId(event))) { if (event.id !== (serializeEventId(event))) {
throw new NostrError( throw new NostrError(
`invalid id ${event.id} for event ${JSON.stringify( `invalid id ${event.id} for event ${JSON.stringify(
event event
)}, expected ${await serializeEventId(event)}` )}, expected ${serializeEventId(event)}`
) )
} }
if (!(await schnorrVerify(event.sig, event.id, event.pubkey))) { if (!(schnorrVerify(event.sig, event.id, event.pubkey))) {
throw new NostrError(`invalid signature for event ${JSON.stringify(event)}`) throw new NostrError(`invalid signature for event ${JSON.stringify(event)}`)
} }
@ -221,9 +221,9 @@ export async function parseEvent(event: RawEvent): Promise<Event> {
} }
} }
async function serializeEventId( function serializeEventId(
event: UnsignedWithPubkey<RawEvent> event: UnsignedWithPubkey<RawEvent>
): Promise<EventId> { ): EventId {
const serialized = JSON.stringify([ const serialized = JSON.stringify([
0, 0,
event.pubkey, event.pubkey,
@ -232,7 +232,7 @@ async function serializeEventId(
event.tags, event.tags,
event.content, event.content,
]) ])
return await sha256(Uint8Array.from(charCodes(serialized))) return sha256(Uint8Array.from(charCodes(serialized)))
} }
function* charCodes(data: string): Iterable<number> { function* charCodes(data: string): Iterable<number> {

View File

@ -1,4 +1,4 @@
import * as secp from "@noble/secp256k1"; import * as utils from "@noble/curves/abstract/utils";
import { bech32 } from "bech32"; import { bech32 } from "bech32";
import { HexKey } from "."; import { HexKey } from ".";
@ -29,7 +29,7 @@ export interface TLVEntry {
export function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string) { export function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string) {
const enc = new TextEncoder(); const enc = new TextEncoder();
const buf = prefix === NostrPrefix.Address ? enc.encode(id) : secp.utils.hexToBytes(id); const buf = prefix === NostrPrefix.Address ? enc.encode(id) : utils.hexToBytes(id);
const tl0 = [0, buf.length, ...buf]; const tl0 = [0, buf.length, ...buf];
const tl1 = const tl1 =
@ -40,7 +40,7 @@ export function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], ki
}) })
.flat() ?? []; .flat() ?? [];
const tl2 = author ? [2, 32, ...secp.utils.hexToBytes(author)] : []; const tl2 = author ? [2, 32, ...utils.hexToBytes(author)] : [];
const tl3 = kind ? [3, 4, ...new Uint8Array(new Uint32Array([kind]).buffer).reverse()] : [] const tl3 = kind ? [3, 4, ...new Uint8Array(new Uint32Array([kind]).buffer).reverse()] : []
return bech32.encode(prefix, bech32.toWords([...tl0, ...tl1, ...tl2, ...tl3]), 1_000); return bech32.encode(prefix, bech32.toWords([...tl0, ...tl1, ...tl2, ...tl3]), 1_000);
@ -72,11 +72,11 @@ function decodeTLVEntry(type: TLVEntryType, prefix: string, data: Uint8Array) {
if (prefix === NostrPrefix.Address) { if (prefix === NostrPrefix.Address) {
return new TextDecoder("ASCII").decode(data); return new TextDecoder("ASCII").decode(data);
} else { } else {
return secp.utils.bytesToHex(data); return utils.bytesToHex(data);
} }
} }
case TLVEntryType.Author: { case TLVEntryType.Author: {
return secp.utils.bytesToHex(data); return utils.bytesToHex(data);
} }
case TLVEntryType.Kind: { case TLVEntryType.Kind: {
return new Uint32Array(new Uint8Array(data.reverse()).buffer)[0]; return new Uint32Array(new Uint8Array(data.reverse()).buffer)[0];

View File

@ -1,4 +1,4 @@
import * as secp from "@noble/secp256k1"; import * as utils from "@noble/curves/abstract/utils";
import { bech32 } from "bech32"; import { bech32 } from "bech32";
export function unwrap<T>(v: T | undefined | null): T { export function unwrap<T>(v: T | undefined | null): T {
@ -17,7 +17,7 @@ export function hexToBech32(hrp: string, hex?: string) {
} }
try { try {
const buf = secp.utils.hexToBytes(hex); const buf = utils.hexToBytes(hex);
return bech32.encode(hrp, bech32.toWords(buf)); return bech32.encode(hrp, bech32.toWords(buf));
} catch (e) { } catch (e) {
console.warn("Invalid hex", hex, e); console.warn("Invalid hex", hex, e);

View File

@ -1741,6 +1741,13 @@
dependencies: dependencies:
eslint-scope "5.1.1" eslint-scope "5.1.1"
"@noble/curves@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932"
integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==
dependencies:
"@noble/hashes" "1.3.0"
"@noble/curves@~0.8.3": "@noble/curves@~0.8.3":
version "0.8.3" version "0.8.3"
resolved "https://registry.npmjs.org/@noble/curves/-/curves-0.8.3.tgz" resolved "https://registry.npmjs.org/@noble/curves/-/curves-0.8.3.tgz"
@ -1753,7 +1760,7 @@
resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz"
integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==
"@noble/secp256k1@^1.7.0", "@noble/secp256k1@^1.7.1": "@noble/secp256k1@^1.7.0":
version "1.7.1" version "1.7.1"
resolved "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz" resolved "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz"
integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==