feat: add support for nip44
This commit is contained in:
@@ -1127,10 +1127,10 @@
|
||||
sum += a.length;
|
||||
}
|
||||
const res = new Uint8Array(sum);
|
||||
for (let i2 = 0, pad2 = 0; i2 < arrays.length; i2++) {
|
||||
for (let i2 = 0, pad3 = 0; i2 < arrays.length; i2++) {
|
||||
const a = arrays[i2];
|
||||
res.set(a, pad2);
|
||||
pad2 += a.length;
|
||||
res.set(a, pad3);
|
||||
pad3 += a.length;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -2163,16 +2163,16 @@
|
||||
this.blockLen = this.iHash.blockLen;
|
||||
this.outputLen = this.iHash.outputLen;
|
||||
const blockLen = this.blockLen;
|
||||
const pad2 = new Uint8Array(blockLen);
|
||||
pad2.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
||||
for (let i2 = 0; i2 < pad2.length; i2++)
|
||||
pad2[i2] ^= 54;
|
||||
this.iHash.update(pad2);
|
||||
const pad3 = new Uint8Array(blockLen);
|
||||
pad3.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
||||
for (let i2 = 0; i2 < pad3.length; i2++)
|
||||
pad3[i2] ^= 54;
|
||||
this.iHash.update(pad3);
|
||||
this.oHash = hash.create();
|
||||
for (let i2 = 0; i2 < pad2.length; i2++)
|
||||
pad2[i2] ^= 54 ^ 92;
|
||||
this.oHash.update(pad2);
|
||||
clean(pad2);
|
||||
for (let i2 = 0; i2 < pad3.length; i2++)
|
||||
pad3[i2] ^= 54 ^ 92;
|
||||
this.oHash.update(pad3);
|
||||
clean(pad3);
|
||||
}
|
||||
update(buf) {
|
||||
aexists(this);
|
||||
@@ -2785,7 +2785,7 @@
|
||||
const l = abytes(item, void 0, "key").length;
|
||||
return l === publicKey || l === publicKeyUncompressed;
|
||||
}
|
||||
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
||||
function getSharedSecret2(secretKeyA, publicKeyB, isCompressed = true) {
|
||||
if (isProbPub(secretKeyA) === true)
|
||||
throw new Error("first arg must be private key");
|
||||
if (isProbPub(publicKeyB) === false)
|
||||
@@ -2800,7 +2800,7 @@
|
||||
randomSecretKey
|
||||
};
|
||||
const keygen = createKeygen(randomSecretKey, getPublicKey2);
|
||||
return Object.freeze({ getPublicKey: getPublicKey2, getSharedSecret, keygen, Point, utils, lengths });
|
||||
return Object.freeze({ getPublicKey: getPublicKey2, getSharedSecret: getSharedSecret2, keygen, Point, utils, lengths });
|
||||
}
|
||||
function ecdsa(Point, hash, ecdsaOpts = {}) {
|
||||
ahash(hash);
|
||||
@@ -2816,7 +2816,7 @@
|
||||
const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
|
||||
const { Fp, Fn } = Point;
|
||||
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
||||
const { keygen, getPublicKey: getPublicKey2, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
|
||||
const { keygen, getPublicKey: getPublicKey2, getSharedSecret: getSharedSecret2, utils, lengths } = ecdh(Point, ecdsaOpts);
|
||||
const defaultSigOpts = {
|
||||
prehash: true,
|
||||
lowS: typeof ecdsaOpts.lowS === "boolean" ? ecdsaOpts.lowS : true,
|
||||
@@ -3022,7 +3022,7 @@
|
||||
return Object.freeze({
|
||||
keygen,
|
||||
getPublicKey: getPublicKey2,
|
||||
getSharedSecret,
|
||||
getSharedSecret: getSharedSecret2,
|
||||
utils,
|
||||
lengths,
|
||||
Point,
|
||||
@@ -4339,7 +4339,7 @@
|
||||
h[9] = d9;
|
||||
}
|
||||
finalize() {
|
||||
const { h, pad: pad2 } = this;
|
||||
const { h, pad: pad3 } = this;
|
||||
const g = new Uint16Array(10);
|
||||
let c = h[1] >>> 13;
|
||||
h[1] &= 8191;
|
||||
@@ -4378,10 +4378,10 @@
|
||||
h[5] = (h[6] >>> 2 | h[7] << 11) & 65535;
|
||||
h[6] = (h[7] >>> 5 | h[8] << 8) & 65535;
|
||||
h[7] = (h[8] >>> 8 | h[9] << 5) & 65535;
|
||||
let f = h[0] + pad2[0];
|
||||
let f = h[0] + pad3[0];
|
||||
h[0] = f & 65535;
|
||||
for (let i2 = 1; i2 < 8; i2++) {
|
||||
f = (h[i2] + pad2[i2] | 0) + (f >>> 16) | 0;
|
||||
f = (h[i2] + pad3[i2] | 0) + (f >>> 16) | 0;
|
||||
h[i2] = f & 65535;
|
||||
}
|
||||
clean2(g);
|
||||
@@ -7122,6 +7122,110 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
// node_modules/.pnpm/nostr-tools@2.23.3/node_modules/nostr-tools/lib/esm/nip44.js
|
||||
var utf8Decoder2 = new TextDecoder("utf-8");
|
||||
var utf8Encoder2 = new TextEncoder();
|
||||
var minPlaintextSize2 = 1;
|
||||
var maxPlaintextSize2 = 65535;
|
||||
function getConversationKey2(privkeyA, pubkeyB) {
|
||||
const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes("02" + pubkeyB)).subarray(1, 33);
|
||||
return extract(sha256, sharedX, utf8Encoder2.encode("nip44-v2"));
|
||||
}
|
||||
function getMessageKeys2(conversationKey, nonce) {
|
||||
const keys = expand(sha256, conversationKey, nonce, 76);
|
||||
return {
|
||||
chacha_key: keys.subarray(0, 32),
|
||||
chacha_nonce: keys.subarray(32, 44),
|
||||
hmac_key: keys.subarray(44, 76)
|
||||
};
|
||||
}
|
||||
function calcPaddedLen2(len) {
|
||||
if (!Number.isSafeInteger(len) || len < 1)
|
||||
throw new Error("expected positive integer");
|
||||
if (len <= 32)
|
||||
return 32;
|
||||
const nextPower = 1 << Math.floor(Math.log2(len - 1)) + 1;
|
||||
const chunk = nextPower <= 256 ? 32 : nextPower / 8;
|
||||
return chunk * (Math.floor((len - 1) / chunk) + 1);
|
||||
}
|
||||
function writeU16BE2(num2) {
|
||||
if (!Number.isSafeInteger(num2) || num2 < minPlaintextSize2 || num2 > maxPlaintextSize2)
|
||||
throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");
|
||||
const arr = new Uint8Array(2);
|
||||
new DataView(arr.buffer).setUint16(0, num2, false);
|
||||
return arr;
|
||||
}
|
||||
function pad2(plaintext) {
|
||||
const unpadded = utf8Encoder2.encode(plaintext);
|
||||
const unpaddedLen = unpadded.length;
|
||||
const prefix = writeU16BE2(unpaddedLen);
|
||||
const suffix = new Uint8Array(calcPaddedLen2(unpaddedLen) - unpaddedLen);
|
||||
return concatBytes(prefix, unpadded, suffix);
|
||||
}
|
||||
function unpad2(padded) {
|
||||
const unpaddedLen = new DataView(padded.buffer).getUint16(0);
|
||||
const unpadded = padded.subarray(2, 2 + unpaddedLen);
|
||||
if (unpaddedLen < minPlaintextSize2 || unpaddedLen > maxPlaintextSize2 || unpadded.length !== unpaddedLen || padded.length !== 2 + calcPaddedLen2(unpaddedLen))
|
||||
throw new Error("invalid padding");
|
||||
return utf8Decoder2.decode(unpadded);
|
||||
}
|
||||
function hmacAad2(key, message, aad) {
|
||||
if (aad.length !== 32)
|
||||
throw new Error("AAD associated data must be 32 bytes");
|
||||
const combined = concatBytes(aad, message);
|
||||
return hmac(sha256, key, combined);
|
||||
}
|
||||
function decodePayload2(payload) {
|
||||
if (typeof payload !== "string")
|
||||
throw new Error("payload must be a valid string");
|
||||
const plen = payload.length;
|
||||
if (plen < 132 || plen > 87472)
|
||||
throw new Error("invalid payload length: " + plen);
|
||||
if (payload[0] === "#")
|
||||
throw new Error("unknown encryption version");
|
||||
let data;
|
||||
try {
|
||||
data = base64.decode(payload);
|
||||
} catch (error) {
|
||||
throw new Error("invalid base64: " + error.message);
|
||||
}
|
||||
const dlen = data.length;
|
||||
if (dlen < 99 || dlen > 65603)
|
||||
throw new Error("invalid data length: " + dlen);
|
||||
const vers = data[0];
|
||||
if (vers !== 2)
|
||||
throw new Error("unknown encryption version " + vers);
|
||||
return {
|
||||
nonce: data.subarray(1, 33),
|
||||
ciphertext: data.subarray(33, -32),
|
||||
mac: data.subarray(-32)
|
||||
};
|
||||
}
|
||||
function encrypt3(plaintext, conversationKey, nonce = randomBytes(32)) {
|
||||
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys2(conversationKey, nonce);
|
||||
const padded = pad2(plaintext);
|
||||
const ciphertext = chacha20(chacha_key, chacha_nonce, padded);
|
||||
const mac = hmacAad2(hmac_key, ciphertext, nonce);
|
||||
return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));
|
||||
}
|
||||
function decrypt3(payload, conversationKey) {
|
||||
const { nonce, ciphertext, mac } = decodePayload2(payload);
|
||||
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys2(conversationKey, nonce);
|
||||
const calculatedMac = hmacAad2(hmac_key, ciphertext, nonce);
|
||||
if (!equalBytes(calculatedMac, mac))
|
||||
throw new Error("invalid MAC");
|
||||
const padded = chacha20(chacha_key, chacha_nonce, ciphertext);
|
||||
return unpad2(padded);
|
||||
}
|
||||
var v22 = {
|
||||
utils: {
|
||||
getConversationKey: getConversationKey2,
|
||||
calcPaddedLen: calcPaddedLen2
|
||||
},
|
||||
encrypt: encrypt3,
|
||||
decrypt: decrypt3
|
||||
};
|
||||
|
||||
// node_modules/.pnpm/async-mutex@0.3.2/node_modules/async-mutex/index.mjs
|
||||
var E_TIMEOUT = new Error("timeout while waiting for mutex to become available");
|
||||
var E_ALREADY_LOCKED = new Error("mutex already locked");
|
||||
@@ -7281,17 +7385,51 @@
|
||||
}
|
||||
};
|
||||
|
||||
// extension/utils.js
|
||||
var LRUCache = class {
|
||||
constructor(maxSize) {
|
||||
this.maxSize = maxSize;
|
||||
this.map = /* @__PURE__ */ new Map();
|
||||
this.keys = [];
|
||||
}
|
||||
clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
has(k) {
|
||||
return this.map.has(k);
|
||||
}
|
||||
get(k) {
|
||||
const v = this.map.get(k);
|
||||
if (v !== void 0) {
|
||||
this.keys.push(k);
|
||||
if (this.keys.length > this.maxSize * 2) {
|
||||
this.keys.splice(-this.maxSize);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
set(k, v) {
|
||||
this.map.set(k, v);
|
||||
this.keys.push(k);
|
||||
if (this.map.size > this.maxSize) {
|
||||
this.map.delete(this.keys.shift());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// extension/common.js
|
||||
var import_webextension_polyfill = __toESM(require_browser_polyfill());
|
||||
var NO_PERMISSIONS_REQUIRED = {
|
||||
replaceURL: true
|
||||
replaceURL: true,
|
||||
peekPublicKey: true
|
||||
};
|
||||
var PERMISSION_NAMES = Object.fromEntries([
|
||||
["getPublicKey", "read your public key"],
|
||||
["getRelays", "read your list of preferred relays"],
|
||||
["signEvent", "sign events using your private key"],
|
||||
["nip04.encrypt", "encrypt messages to peers"],
|
||||
["nip04.decrypt", "decrypt messages from peers"]
|
||||
["nip04.decrypt", "decrypt messages from peers"],
|
||||
["nip44.encrypt", "encrypt messages to peers"],
|
||||
["nip44.decrypt", "decrypt messages from peers"]
|
||||
]);
|
||||
function matchConditions(conditions, event) {
|
||||
if (conditions?.kinds) {
|
||||
@@ -7349,8 +7487,8 @@
|
||||
import_webextension_polyfill.default.storage.local.set({ policies });
|
||||
}
|
||||
async function showNotification(host, answer, type, params) {
|
||||
const ok = await import_webextension_polyfill.default.storage.local.get("notifications");
|
||||
if (ok) {
|
||||
const { notifications } = await import_webextension_polyfill.default.storage.local.get("notifications");
|
||||
if (notifications) {
|
||||
const action = answer ? "allowed" : "denied";
|
||||
import_webextension_polyfill.default.notifications.create(void 0, {
|
||||
type: "basic",
|
||||
@@ -7368,14 +7506,48 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
async function getPosition(width2, height2) {
|
||||
let left = 0;
|
||||
let top = 0;
|
||||
try {
|
||||
const lastFocused = await import_webextension_polyfill.default.windows.getLastFocused();
|
||||
if (lastFocused && lastFocused.top !== void 0 && lastFocused.left !== void 0 && lastFocused.width !== void 0 && lastFocused.height !== void 0) {
|
||||
top = Math.round(lastFocused.top + (lastFocused.height - height2) / 2);
|
||||
left = Math.round(lastFocused.left + (lastFocused.width - width2) / 2);
|
||||
} else {
|
||||
console.error("Last focused window properties are undefined.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error getting window position:", error);
|
||||
}
|
||||
return {
|
||||
top,
|
||||
left
|
||||
};
|
||||
}
|
||||
|
||||
// extension/background.js
|
||||
var { hexToBytes: hexToBytes2 } = utils_exports;
|
||||
var { encrypt: encrypt3, decrypt: decrypt3 } = nip04_exports;
|
||||
var { encrypt: encrypt4, decrypt: decrypt4 } = nip04_exports;
|
||||
var openPrompt = null;
|
||||
var promptMutex = new Mutex();
|
||||
var releasePromptMutex = () => {
|
||||
};
|
||||
var secretsCache = new LRUCache(100);
|
||||
var previousSk = null;
|
||||
function getSharedSecret(sk, peer) {
|
||||
if (previousSk !== sk) {
|
||||
secretsCache.clear();
|
||||
}
|
||||
let key = secretsCache.get(peer);
|
||||
if (!key) {
|
||||
key = v22.utils.getConversationKey(sk, peer);
|
||||
secretsCache.set(peer, key);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
var width = 440;
|
||||
var height = 420;
|
||||
import_webextension_polyfill2.default.runtime.onInstalled.addListener((_, __, reason) => {
|
||||
if (reason === "install")
|
||||
import_webextension_polyfill2.default.runtime.openOptionsPage();
|
||||
@@ -7402,6 +7574,12 @@
|
||||
async function handleContentScriptMessage({ type, params, host }) {
|
||||
if (NO_PERMISSIONS_REQUIRED[type]) {
|
||||
switch (type) {
|
||||
case "peekPublicKey": {
|
||||
const allowed = await getPermissionStatus(host, "getPublicKey");
|
||||
if (allowed === true)
|
||||
return performOperation("getPublicKey", params);
|
||||
return "";
|
||||
}
|
||||
case "replaceURL": {
|
||||
const { protocol_handler: ph } = await import_webextension_polyfill2.default.storage.local.get([
|
||||
"protocol_handler"
|
||||
@@ -7459,12 +7637,15 @@
|
||||
const url = `${import_webextension_polyfill2.default.runtime.getURL(
|
||||
"prompt.html"
|
||||
)}?${qs.toString()}`;
|
||||
const { top, left } = getPosition(width, height);
|
||||
if (import_webextension_polyfill2.default.windows) {
|
||||
import_webextension_polyfill2.default.windows.create({
|
||||
url,
|
||||
type: "popup",
|
||||
width: 600,
|
||||
height: 600
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left
|
||||
});
|
||||
} else {
|
||||
import_webextension_polyfill2.default.tabs.create({
|
||||
@@ -7474,11 +7655,11 @@
|
||||
}
|
||||
});
|
||||
if (!accept)
|
||||
return { error: "denied" };
|
||||
return { error: { message: "denied" } };
|
||||
} catch (err) {
|
||||
releasePromptMutex();
|
||||
return {
|
||||
error: `error: ${err}`
|
||||
error: { message: err.message, stack: err.stack }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -7510,11 +7691,21 @@
|
||||
}
|
||||
case "nip04.encrypt": {
|
||||
const { peer, plaintext } = params;
|
||||
return encrypt3(sk, peer, plaintext);
|
||||
return encrypt4(sk, peer, plaintext);
|
||||
}
|
||||
case "nip04.decrypt": {
|
||||
const { peer, ciphertext } = params;
|
||||
return decrypt3(sk, peer, ciphertext);
|
||||
return decrypt4(sk, peer, ciphertext);
|
||||
}
|
||||
case "nip44.encrypt": {
|
||||
const { peer, plaintext } = params;
|
||||
const key = getSharedSecret(sk, peer);
|
||||
return v22.encrypt(plaintext, key);
|
||||
}
|
||||
case "nip44.decrypt": {
|
||||
const { peer, ciphertext } = params;
|
||||
const key = getSharedSecret(sk, peer);
|
||||
return v22.decrypt(ciphertext, key);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user