@thecryptodonkey/toll-booth 3.7.0 → 3.7.1
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.
- package/dist/macaroon.js +37 -7
- package/package.json +1 -1
package/dist/macaroon.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
1
2
|
import { newMacaroon, importMacaroon } from 'macaroon';
|
|
2
3
|
const LOCATION = 'toll-booth';
|
|
4
|
+
/**
|
|
5
|
+
* Aperture-compatible L402 identifier layout (version 0):
|
|
6
|
+
* [0..1] uint16 big-endian version (0)
|
|
7
|
+
* [2..33] 32-byte payment hash
|
|
8
|
+
* [34..65] 32-byte random token ID
|
|
9
|
+
*/
|
|
10
|
+
const L402_ID_VERSION = 0;
|
|
11
|
+
const L402_ID_SIZE = 66;
|
|
3
12
|
const KNOWN_CAVEATS = new Set(['payment_hash', 'credit_balance', 'currency', 'route', 'expires', 'ip']);
|
|
4
13
|
/** Caveat keys that encode monetary value and must not be set via the caveats parameter. */
|
|
5
14
|
const RESERVED_CAVEAT_KEYS = new Set(['payment_hash', 'credit_balance', 'currency']);
|
|
@@ -16,7 +25,7 @@ const MAX_CUSTOM_CAVEATS = 16;
|
|
|
16
25
|
export function mintMacaroon(rootKey, paymentHash, creditBalanceSats, caveats, currency) {
|
|
17
26
|
const keyBytes = hexToBytes(rootKey);
|
|
18
27
|
const m = newMacaroon({
|
|
19
|
-
identifier: paymentHash,
|
|
28
|
+
identifier: encodeL402Identifier(paymentHash),
|
|
20
29
|
location: LOCATION,
|
|
21
30
|
rootKey: keyBytes,
|
|
22
31
|
version: 2,
|
|
@@ -68,13 +77,15 @@ export function verifyMacaroon(rootKey, macaroonBase64, context) {
|
|
|
68
77
|
return 'duplicate caveat';
|
|
69
78
|
return null;
|
|
70
79
|
}, []);
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
const
|
|
80
|
+
// Decode the aperture-compatible binary identifier to extract the
|
|
81
|
+
// payment hash. The identifier is set at mint time and covered by
|
|
82
|
+
// the root signature, so it cannot be tampered with.
|
|
83
|
+
const paymentHash = decodeL402Identifier(m.identifier);
|
|
84
|
+
if (!paymentHash)
|
|
85
|
+
return { valid: false };
|
|
75
86
|
const caveats = parseCaveats(macaroonBase64);
|
|
76
87
|
// Cross-check: the payment_hash caveat must match the identifier
|
|
77
|
-
if (caveats.payment_hash && caveats.payment_hash !==
|
|
88
|
+
if (caveats.payment_hash && caveats.payment_hash !== paymentHash) {
|
|
78
89
|
return { valid: false };
|
|
79
90
|
}
|
|
80
91
|
if (context) {
|
|
@@ -108,7 +119,7 @@ export function verifyMacaroon(rootKey, macaroonBase64, context) {
|
|
|
108
119
|
const currency = caveats.currency ?? 'sat';
|
|
109
120
|
return {
|
|
110
121
|
valid: true,
|
|
111
|
-
paymentHash
|
|
122
|
+
paymentHash,
|
|
112
123
|
creditBalance,
|
|
113
124
|
currency,
|
|
114
125
|
customCaveats: Object.keys(customCaveats).length > 0 ? customCaveats : undefined,
|
|
@@ -153,6 +164,22 @@ export function parseCaveats(macaroonBase64) {
|
|
|
153
164
|
}
|
|
154
165
|
return result;
|
|
155
166
|
}
|
|
167
|
+
function encodeL402Identifier(paymentHash) {
|
|
168
|
+
const id = new Uint8Array(L402_ID_SIZE);
|
|
169
|
+
id[0] = (L402_ID_VERSION >> 8) & 0xff;
|
|
170
|
+
id[1] = L402_ID_VERSION & 0xff;
|
|
171
|
+
id.set(hexToBytes(paymentHash), 2);
|
|
172
|
+
id.set(randomBytes(32), 34);
|
|
173
|
+
return id;
|
|
174
|
+
}
|
|
175
|
+
function decodeL402Identifier(id) {
|
|
176
|
+
if (id.length < L402_ID_SIZE)
|
|
177
|
+
return undefined;
|
|
178
|
+
const version = (id[0] << 8) | id[1];
|
|
179
|
+
if (version !== L402_ID_VERSION)
|
|
180
|
+
return undefined;
|
|
181
|
+
return bytesToHex(id.slice(2, 34));
|
|
182
|
+
}
|
|
156
183
|
function hexToBytes(hex) {
|
|
157
184
|
const bytes = new Uint8Array(hex.length / 2);
|
|
158
185
|
for (let i = 0; i < hex.length; i += 2) {
|
|
@@ -160,6 +187,9 @@ function hexToBytes(hex) {
|
|
|
160
187
|
}
|
|
161
188
|
return bytes;
|
|
162
189
|
}
|
|
190
|
+
function bytesToHex(bytes) {
|
|
191
|
+
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
192
|
+
}
|
|
163
193
|
function uint8ToBase64(bytes) {
|
|
164
194
|
return Buffer.from(bytes).toString('base64');
|
|
165
195
|
}
|
package/package.json
CHANGED