@konemono/nostr-login 1.7.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.prettierrc.json +13 -0
  2. package/README.md +167 -0
  3. package/dist/const/index.d.ts +1 -0
  4. package/dist/iife-module.d.ts +1 -0
  5. package/dist/index.d.ts +27 -0
  6. package/dist/index.esm.js +18 -0
  7. package/dist/index.esm.js.map +1 -0
  8. package/dist/modules/AuthNostrService.d.ts +84 -0
  9. package/dist/modules/BannerManager.d.ts +20 -0
  10. package/dist/modules/ModalManager.d.ts +25 -0
  11. package/dist/modules/Nip46.d.ts +56 -0
  12. package/dist/modules/Nostr.d.ts +34 -0
  13. package/dist/modules/NostrExtensionService.d.ts +17 -0
  14. package/dist/modules/NostrParams.d.ts +8 -0
  15. package/dist/modules/Popup.d.ts +7 -0
  16. package/dist/modules/ProcessManager.d.ts +10 -0
  17. package/dist/modules/Signer.d.ts +9 -0
  18. package/dist/modules/index.d.ts +8 -0
  19. package/dist/types.d.ts +72 -0
  20. package/dist/unpkg.js +17 -0
  21. package/dist/utils/index.d.ts +27 -0
  22. package/dist/utils/nip44.d.ts +9 -0
  23. package/index.html +30 -0
  24. package/package.json +28 -0
  25. package/rollup.config.js +55 -0
  26. package/src/const/index.ts +1 -0
  27. package/src/iife-module.ts +81 -0
  28. package/src/index.ts +347 -0
  29. package/src/modules/AuthNostrService.ts +756 -0
  30. package/src/modules/BannerManager.ts +146 -0
  31. package/src/modules/ModalManager.ts +635 -0
  32. package/src/modules/Nip46.ts +441 -0
  33. package/src/modules/Nostr.ts +107 -0
  34. package/src/modules/NostrExtensionService.ts +99 -0
  35. package/src/modules/NostrParams.ts +18 -0
  36. package/src/modules/Popup.ts +27 -0
  37. package/src/modules/ProcessManager.ts +67 -0
  38. package/src/modules/Signer.ts +25 -0
  39. package/src/modules/index.ts +8 -0
  40. package/src/types.ts +124 -0
  41. package/src/utils/index.ts +326 -0
  42. package/src/utils/nip44.ts +185 -0
  43. package/tsconfig.json +15 -0
@@ -0,0 +1,185 @@
1
+ import { chacha20 } from "@noble/ciphers/chacha"
2
+ import { concatBytes, randomBytes, utf8ToBytes } from "@noble/hashes/utils"
3
+ import { equalBytes } from "@noble/ciphers/utils";
4
+ import { secp256k1 } from "@noble/curves/secp256k1"
5
+ import {
6
+ expand as hkdf_expand,
7
+ extract as hkdf_extract,
8
+ } from "@noble/hashes/hkdf"
9
+ import { sha256 } from "@noble/hashes/sha256"
10
+ import { hmac } from "@noble/hashes/hmac";
11
+ import { base64 } from "@scure/base";
12
+ import { getPublicKey } from 'nostr-tools'
13
+
14
+ // from https://github.com/nbd-wtf/nostr-tools
15
+
16
+ const decoder = new TextDecoder()
17
+
18
+ const u = {
19
+ minPlaintextSize: 0x0001, // 1b msg => padded to 32b
20
+ maxPlaintextSize: 0xffff, // 65535 (64kb-1) => padded to 64kb
21
+
22
+ utf8Encode: utf8ToBytes,
23
+ utf8Decode(bytes: Uint8Array): string {
24
+ return decoder.decode(bytes);
25
+ },
26
+
27
+ getConversationKey(privkeyA: string, pubkeyB: string): Uint8Array {
28
+ const sharedX = secp256k1
29
+ .getSharedSecret(privkeyA, "02" + pubkeyB)
30
+ .subarray(1, 33);
31
+ return hkdf_extract(sha256, sharedX, "nip44-v2");
32
+ },
33
+
34
+ getMessageKeys(conversationKey: Uint8Array, nonce: Uint8Array) {
35
+ const keys = hkdf_expand(sha256, conversationKey, nonce, 76);
36
+ return {
37
+ chacha_key: keys.subarray(0, 32),
38
+ chacha_nonce: keys.subarray(32, 44),
39
+ hmac_key: keys.subarray(44, 76),
40
+ };
41
+ },
42
+
43
+ calcPaddedLen(len: number): number {
44
+ if (!Number.isSafeInteger(len) || len < 1)
45
+ throw new Error("expected positive integer");
46
+ if (len <= 32) return 32;
47
+ const nextPower = 1 << (Math.floor(Math.log2(len - 1)) + 1);
48
+ const chunk = nextPower <= 256 ? 32 : nextPower / 8;
49
+ return chunk * (Math.floor((len - 1) / chunk) + 1);
50
+ },
51
+
52
+ writeU16BE(num: number): Uint8Array {
53
+ if (
54
+ !Number.isSafeInteger(num) ||
55
+ num < u.minPlaintextSize ||
56
+ num > u.maxPlaintextSize
57
+ )
58
+ throw new Error(
59
+ "invalid plaintext size: must be between 1 and 65535 bytes"
60
+ );
61
+ const arr = new Uint8Array(2);
62
+ new DataView(arr.buffer).setUint16(0, num, false);
63
+ return arr;
64
+ },
65
+
66
+ pad(plaintext: string): Uint8Array {
67
+ const unpadded = u.utf8Encode(plaintext);
68
+ const unpaddedLen = unpadded.length;
69
+ const prefix = u.writeU16BE(unpaddedLen);
70
+ const suffix = new Uint8Array(u.calcPaddedLen(unpaddedLen) - unpaddedLen);
71
+ return concatBytes(prefix, unpadded, suffix);
72
+ },
73
+
74
+ unpad(padded: Uint8Array): string {
75
+ const unpaddedLen = new DataView(padded.buffer).getUint16(0);
76
+ const unpadded = padded.subarray(2, 2 + unpaddedLen);
77
+ if (
78
+ unpaddedLen < u.minPlaintextSize ||
79
+ unpaddedLen > u.maxPlaintextSize ||
80
+ unpadded.length !== unpaddedLen ||
81
+ padded.length !== 2 + u.calcPaddedLen(unpaddedLen)
82
+ )
83
+ throw new Error("invalid padding");
84
+ return u.utf8Decode(unpadded);
85
+ },
86
+
87
+ hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {
88
+ if (aad.length !== 32)
89
+ throw new Error("AAD associated data must be 32 bytes");
90
+ const combined = concatBytes(aad, message);
91
+ return hmac(sha256, key, combined);
92
+ },
93
+
94
+ // metadata: always 65b (version: 1b, nonce: 32b, max: 32b)
95
+ // plaintext: 1b to 0xffff
96
+ // padded plaintext: 32b to 0xffff
97
+ // ciphertext: 32b+2 to 0xffff+2
98
+ // raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)
99
+ // compressed payload (base64): 132b to 87472b
100
+ decodePayload(payload: string): {
101
+ nonce: Uint8Array;
102
+ ciphertext: Uint8Array;
103
+ mac: Uint8Array;
104
+ } {
105
+ if (typeof payload !== "string")
106
+ throw new Error("payload must be a valid string");
107
+ const plen = payload.length;
108
+ if (plen < 132 || plen > 87472)
109
+ throw new Error("invalid payload length: " + plen);
110
+ if (payload[0] === "#") throw new Error("unknown encryption version");
111
+ let data: Uint8Array;
112
+ try {
113
+ data = base64.decode(payload);
114
+ } catch (error) {
115
+ throw new Error("invalid base64: " + (error as Error).message);
116
+ }
117
+ const dlen = data.length;
118
+ if (dlen < 99 || dlen > 65603)
119
+ throw new Error("invalid data length: " + dlen);
120
+ const vers = data[0];
121
+ if (vers !== 2) throw new Error("unknown encryption version " + vers);
122
+ return {
123
+ nonce: data.subarray(1, 33),
124
+ ciphertext: data.subarray(33, -32),
125
+ mac: data.subarray(-32),
126
+ };
127
+ },
128
+ };
129
+
130
+ export function encryptNip44(
131
+ plaintext: string,
132
+ conversationKey: Uint8Array,
133
+ nonce: Uint8Array = randomBytes(32)
134
+ ): string {
135
+ const { chacha_key, chacha_nonce, hmac_key } = u.getMessageKeys(
136
+ conversationKey,
137
+ nonce
138
+ );
139
+ const padded = u.pad(plaintext);
140
+ const ciphertext = chacha20(chacha_key, chacha_nonce, padded);
141
+ const mac = u.hmacAad(hmac_key, ciphertext, nonce);
142
+ return base64.encode(
143
+ concatBytes(new Uint8Array([2]), nonce, ciphertext, mac)
144
+ );
145
+ }
146
+
147
+ export function decryptNip44(payload: string, conversationKey: Uint8Array): string {
148
+ const { nonce, ciphertext, mac } = u.decodePayload(payload);
149
+ const { chacha_key, chacha_nonce, hmac_key } = u.getMessageKeys(
150
+ conversationKey,
151
+ nonce
152
+ );
153
+ const calculatedMac = u.hmacAad(hmac_key, ciphertext, nonce);
154
+ if (!equalBytes(calculatedMac, mac)) throw new Error("invalid MAC");
155
+ const padded = chacha20(chacha_key, chacha_nonce, ciphertext);
156
+ return u.unpad(padded);
157
+ }
158
+
159
+ export class Nip44 {
160
+ private cache = new Map<string, Uint8Array>()
161
+
162
+ public createKey(privkey: string, pubkey: string) {
163
+ return u.getConversationKey(privkey, pubkey)
164
+ }
165
+
166
+ private getKey(privkey: string, pubkey: string, extractable?: boolean) {
167
+ const id = getPublicKey(privkey) + pubkey
168
+ let cryptoKey = this.cache.get(id)
169
+ if (cryptoKey) return cryptoKey
170
+
171
+ const key = this.createKey(privkey, pubkey)
172
+ this.cache.set(id, key)
173
+ return key
174
+ }
175
+
176
+ public encrypt(privkey: string, pubkey: string, text: string): string {
177
+ const key = this.getKey(privkey, pubkey)
178
+ return encryptNip44(text, key)
179
+ }
180
+
181
+ public decrypt(privkey: string, pubkey: string, data: string): string {
182
+ const key = this.getKey(privkey, pubkey)
183
+ return decryptNip44(data, key)
184
+ }
185
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "esnext",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "skipLibCheck": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "strict": true,
10
+ "declaration": true,
11
+ "outDir": "./dist",
12
+ },
13
+ "include": ["src/**/*.ts"],
14
+ "exclude": ["node_modules"],
15
+ }