Skip to content

Commit

Permalink
support for nip44
Browse files Browse the repository at this point in the history
  • Loading branch information
diegogurpegui committed Jun 30, 2024
1 parent 96570a8 commit 7ff59e5
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 3 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ This allows you to sign [Nostr](https://github.com/fiatjaf/nostr) events on web-

It provides a `window.nostr` object which has the following methods:

```
```javascript
async window.nostr.getPublicKey(): string // returns your public key as hex
async window.nostr.signEvent(event): Event // returns the full event object signed
async window.nostr.getRelays(): { [url: string]: RelayPolicy } // returns a map of relays

async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext+iv as specified in nip04
async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext+iv as specified in nip04

async window.nostr.nip44.encrypt(pubkey, plaintext): string // takes pubkey, plaintext, returns ciphertext as specified in nip-44
async window.nostr.nip44.decrypt(pubkey, ciphertext): string // takes pubkey, ciphertext, returns plaintext as specified in nip-44
```

## Install
Expand Down
42 changes: 42 additions & 0 deletions src/LRUCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export class LRUCache<K, V> {
private maxSize: number;
private map: Map<K, V>;
private keys: any[];

constructor(maxSize) {
this.maxSize = maxSize;
this.map = 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 !== undefined) {
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());
}
}
}
33 changes: 32 additions & 1 deletion src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import {
validateEvent,
finalizeEvent,
getEventHash,
getPublicKey
getPublicKey,
nip44
} from 'nostr-tools';
import { nip04, EventTemplate } from 'nostr-tools';

import * as Storage from './storage';
import { AuthorizationCondition, PromptResponse } from './types';
import { PERMISSIONS_REQUIRED, convertHexToUint8Array } from './common';
import { LRUCache } from './LRUCache';

let openPrompt: { resolve: Function; reject: Function } | null = null;

Expand Down Expand Up @@ -105,6 +107,16 @@ async function handleContentScriptMessage({ type, params, host }) {
let { peer, ciphertext } = params;
return nip04.decrypt(sk, peer, ciphertext);
}
case 'nip44.encrypt': {
const { peer, plaintext } = params;
const key = getSharedSecret(sk, peer);
return nip44.v2.decrypt(plaintext, key);
}
case 'nip44.decrypt': {
const { peer, ciphertext } = params;
const key = getSharedSecret(sk, peer);
return nip44.v2.decrypt(ciphertext, key);
}
}
} catch (error) {
return { error: { message: error.message, stack: error.stack } };
Expand Down Expand Up @@ -186,3 +198,22 @@ function promptPermission(host, level, params) {
async function readPermissionLevel(host: string): Promise<number> {
return (await Storage.readActivePermissions())[host]?.level || 0;
}

// prepare a cache of the last 100 shared keys used
const secretsCache = new LRUCache<string, Uint8Array>(100);
let previousSk: Uint8Array | null = null;
function getSharedSecret(sk: Uint8Array, peer: string) {
// Detect a private key change and erase the cache if they changed their key
if (previousSk !== sk) {
secretsCache.clear();
}

let key = secretsCache.get(peer);

if (!key) {
key = nip44.v2.utils.getConversationKey(sk, peer);
secretsCache.set(peer, key);
}

return key;
}
4 changes: 3 additions & 1 deletion src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const PERMISSION_NAMES = {
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)',
'nip44.decrypt': 'decrypt messages from peers (nip44)'
};

export function getAllowedCapabilities(permission): string[] {
Expand Down
10 changes: 10 additions & 0 deletions src/nostr-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ window.nostr = {
}
},

nip44: {
async encrypt(peer, plaintext) {
return window.nostr._call('nip44.encrypt', { peer, plaintext });
},

async decrypt(peer, ciphertext) {
return window.nostr._call('nip44.decrypt', { peer, ciphertext });
}
},

_call(type, params) {
let id = Math.random().toString().slice(-4);
console.log(
Expand Down

0 comments on commit 7ff59e5

Please sign in to comment.