@agnostack/verifyd 2.4.1-alpha.1 → 2.5.0-alpha.2
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/README.md +75 -64
- package/dist/esm/lib/index.js +3 -0
- package/dist/esm/lib/types.js +1 -0
- package/dist/esm/lib/utils/index.js +1 -0
- package/dist/esm/lib/utils/rawbody.js +35 -0
- package/dist/esm/lib/verification.js +94 -0
- package/dist/esm/react/hooks/index.js +1 -0
- package/dist/esm/react/hooks/useVerification.js +47 -0
- package/dist/esm/react/index.js +2 -0
- package/dist/esm/react/types.js +0 -0
- package/dist/esm/shared/WebCrypto.js +350 -0
- package/dist/esm/shared/authorization.js +26 -0
- package/dist/esm/shared/display.js +429 -0
- package/dist/esm/shared/errors.js +37 -0
- package/dist/esm/shared/index.js +6 -0
- package/dist/esm/shared/request.js +60 -0
- package/dist/esm/shared/types.js +0 -0
- package/dist/esm/shared/verification.js +94 -0
- package/package.json +21 -7
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { isString, objectEmpty, ensureString, ensureArray, } from './display';
|
|
11
|
+
import { CryptoError } from './errors';
|
|
12
|
+
export class WebCrypto {
|
|
13
|
+
constructor({ crypto: _crypto, util: _util } = {}) {
|
|
14
|
+
this._crypto = _crypto !== null && _crypto !== void 0 ? _crypto : {};
|
|
15
|
+
this._util = _util !== null && _util !== void 0 ? _util : {};
|
|
16
|
+
}
|
|
17
|
+
get subtle() {
|
|
18
|
+
var _a;
|
|
19
|
+
return (_a = this._crypto) === null || _a === void 0 ? void 0 : _a.subtle;
|
|
20
|
+
}
|
|
21
|
+
getWebCrypto() {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
var _a, _b, _c, _d;
|
|
24
|
+
if (!((_a = this._crypto) === null || _a === void 0 ? void 0 : _a.subtle)) {
|
|
25
|
+
// 1. Try globalThis.crypto (available in all browsers and Node 18+)
|
|
26
|
+
if ((typeof globalThis !== 'undefined') && ((_b = globalThis.crypto) === null || _b === void 0 ? void 0 : _b.subtle)) {
|
|
27
|
+
this._crypto = globalThis.crypto;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// 2. If still no subtle, fall back to polyfills (Node.js < 18, unusual environments)
|
|
31
|
+
if (!((_c = this._crypto) === null || _c === void 0 ? void 0 : _c.subtle)) {
|
|
32
|
+
try {
|
|
33
|
+
this._crypto = (yield import('isomorphic-webcrypto')).default;
|
|
34
|
+
}
|
|
35
|
+
catch (_ignore) {
|
|
36
|
+
console.info('Failed to import isomorphic-webcrypto, retrying w/ node crypto');
|
|
37
|
+
try {
|
|
38
|
+
this._crypto = (yield import('crypto')).default;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
// eslint-disable-next-line max-len
|
|
42
|
+
console.error(`Failed to import node crypto, ensure 'isomorphic-webcrypto' (or node 'crypto') is installed and/or pass in implementation via 'new WebCrypto({ crypto })'`);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (!((_d = this._crypto) === null || _d === void 0 ? void 0 : _d.subtle)) {
|
|
48
|
+
throw new CryptoError('Invalid crypto, missing subtle');
|
|
49
|
+
}
|
|
50
|
+
return this._crypto;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
getTextDecoder() {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
var _a;
|
|
56
|
+
if ((_a = this._util) === null || _a === void 0 ? void 0 : _a.TextDecoder) {
|
|
57
|
+
return this._util.TextDecoder;
|
|
58
|
+
}
|
|
59
|
+
if ((typeof globalThis !== 'undefined') && (typeof globalThis.TextDecoder === 'function')) {
|
|
60
|
+
return globalThis.TextDecoder;
|
|
61
|
+
}
|
|
62
|
+
if ((typeof window !== 'undefined') && (typeof window.TextDecoder === 'function')) {
|
|
63
|
+
return window.TextDecoder;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const TextDecoder = (yield import('util')).TextDecoder;
|
|
67
|
+
this._util.TextDecoder = TextDecoder;
|
|
68
|
+
return TextDecoder;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error(`Failed to import 'utils.TextDecoder', ensure 'util' is available and/or pass in implementation via 'new WebCrypto({ util })'`);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
getTextEncoder() {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
var _a;
|
|
79
|
+
if ((_a = this._util) === null || _a === void 0 ? void 0 : _a.TextEncoder) {
|
|
80
|
+
return this._util.TextEncoder;
|
|
81
|
+
}
|
|
82
|
+
if ((typeof globalThis !== 'undefined') && (typeof globalThis.TextEncoder === 'function')) {
|
|
83
|
+
return globalThis.TextEncoder;
|
|
84
|
+
}
|
|
85
|
+
if ((typeof window !== 'undefined') && (typeof window.TextEncoder === 'function')) {
|
|
86
|
+
return window.TextEncoder;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const TextEncoder = (yield import('util')).TextEncoder;
|
|
90
|
+
this._util.TextEncoder = TextEncoder;
|
|
91
|
+
return TextEncoder;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error(`Failed to import 'utils.TextEncoder', ensure 'util' is available and/or pass in implementation via 'new WebCrypto({ util })'`);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
timingSafeEqual(value1, value2) {
|
|
100
|
+
if ((value1 == undefined) ||
|
|
101
|
+
(value2 == undefined) ||
|
|
102
|
+
(value1.length !== value2.length)) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
let result = 0;
|
|
106
|
+
// eslint-disable-next-line no-plusplus
|
|
107
|
+
for (let i = 0; i < value1.length; i++) {
|
|
108
|
+
// eslint-disable-next-line no-bitwise
|
|
109
|
+
result |= value1[i] ^ value2[i];
|
|
110
|
+
}
|
|
111
|
+
return (result === 0);
|
|
112
|
+
}
|
|
113
|
+
stringToHex(stringValue) {
|
|
114
|
+
return (Array.from(ensureString(stringValue), (char) => (char.charCodeAt(0).toString(16).padStart(2, '0'))).join(''));
|
|
115
|
+
}
|
|
116
|
+
hexToString(hexValue) {
|
|
117
|
+
if (!isString(hexValue)) {
|
|
118
|
+
throw new CryptoError('hexValue must be a string');
|
|
119
|
+
}
|
|
120
|
+
if (!/^(?:[0-9a-f]{2})+$/.test(hexValue)) {
|
|
121
|
+
throw new CryptoError('hexValue must be a valid hex string');
|
|
122
|
+
}
|
|
123
|
+
return ensureArray(hexValue.match(/.{1,2}/g))
|
|
124
|
+
.map((byte) => String.fromCharCode(parseInt(byte, 16)))
|
|
125
|
+
.join('');
|
|
126
|
+
}
|
|
127
|
+
arrayBufferToString(arrayBuffer) {
|
|
128
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
129
|
+
const uint8Array = new Uint8Array(arrayBuffer);
|
|
130
|
+
const Decoder = yield this.getTextDecoder();
|
|
131
|
+
return new Decoder().decode(uint8Array);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
arrayToArrayBuffer(array) {
|
|
135
|
+
return ((ArrayBuffer.from != undefined)
|
|
136
|
+
? ArrayBuffer.from(array)
|
|
137
|
+
: new Uint8Array(array).buffer);
|
|
138
|
+
}
|
|
139
|
+
ensureArrayBuffer(arrayOrArrayBuffer) {
|
|
140
|
+
return ((arrayOrArrayBuffer instanceof ArrayBuffer)
|
|
141
|
+
? arrayOrArrayBuffer
|
|
142
|
+
: this.arrayToArrayBuffer(arrayOrArrayBuffer));
|
|
143
|
+
}
|
|
144
|
+
getKeyOperations(keyType) {
|
|
145
|
+
switch (keyType) {
|
|
146
|
+
case 'paired':
|
|
147
|
+
case 'private':
|
|
148
|
+
case 'privateKey': {
|
|
149
|
+
return ['deriveKey'];
|
|
150
|
+
}
|
|
151
|
+
case 'secret':
|
|
152
|
+
case 'secretKey':
|
|
153
|
+
case 'sharedSecret': {
|
|
154
|
+
return ['encrypt', 'decrypt'];
|
|
155
|
+
}
|
|
156
|
+
default: {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
getKeyAlgorythm(keyType) {
|
|
162
|
+
switch (keyType) {
|
|
163
|
+
case 'derivedKey':
|
|
164
|
+
case 'derived':
|
|
165
|
+
case 'secret':
|
|
166
|
+
case 'secretKey':
|
|
167
|
+
case 'sharedSecret': {
|
|
168
|
+
return {
|
|
169
|
+
name: 'AES-GCM',
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
default: {
|
|
173
|
+
return {
|
|
174
|
+
name: 'ECDH',
|
|
175
|
+
namedCurve: 'P-256',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
generateKeyPair() {
|
|
181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
+
const crypto = yield this.getWebCrypto();
|
|
183
|
+
const keyPair = yield crypto.subtle.generateKey(this.getKeyAlgorythm('paired'), true, this.getKeyOperations('paired'));
|
|
184
|
+
return keyPair;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
generateSharedSecret() {
|
|
188
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
189
|
+
const crypto = yield this.getWebCrypto();
|
|
190
|
+
const keyPair = yield crypto.subtle.generateKey(this.getKeyAlgorythm('sharedSecret'), true, this.getKeyOperations('sharedSecret'));
|
|
191
|
+
return keyPair;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
generateHMAC(message, derivedKey) {
|
|
195
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
196
|
+
if (!message || !derivedKey) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
const crypto = yield this.getWebCrypto();
|
|
200
|
+
const Encoder = yield this.getTextEncoder();
|
|
201
|
+
const signature = yield crypto.subtle.sign('HMAC', derivedKey, new Encoder().encode(message));
|
|
202
|
+
return this.stringToHex(this.arrayBufferToString(signature));
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
verifyHMAC(message, derivedKey, verifiableHMAC) {
|
|
206
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
207
|
+
const calculatedHMAC = yield this.generateHMAC(message, derivedKey);
|
|
208
|
+
return this.timingSafeEqual(calculatedHMAC, verifiableHMAC);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// NOTE: intentionally separated out as async to be able to .catch
|
|
212
|
+
getStorableKey(key) {
|
|
213
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
214
|
+
const crypto = yield this.getWebCrypto();
|
|
215
|
+
const exportedJWK = yield crypto.subtle.exportKey('jwk', key);
|
|
216
|
+
return this.stringToHex(JSON.stringify(exportedJWK));
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// NOTE: intentionally separated out as async to be able to .catch
|
|
220
|
+
parseStorableHex(storableHex) {
|
|
221
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
222
|
+
return JSON.parse(this.hexToString(storableHex) || '{}');
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
restoreStorableKey(keyType, storableHex) {
|
|
226
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
227
|
+
// eslint-disable-next-line eqeqeq
|
|
228
|
+
if (storableHex == undefined) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
const crypto = yield this.getWebCrypto();
|
|
232
|
+
const exportedJWK = yield this.parseStorableHex(storableHex).catch((ignore) => {
|
|
233
|
+
console.error('Failed to parse storable hex value', ignore);
|
|
234
|
+
return undefined;
|
|
235
|
+
});
|
|
236
|
+
if (objectEmpty(exportedJWK)) {
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
return crypto.subtle.importKey('jwk', exportedJWK, this.getKeyAlgorythm(keyType), true, this.getKeyOperations(keyType));
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
getStorableKeyPair(keyPair) {
|
|
243
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
244
|
+
const storableKeys = {};
|
|
245
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
246
|
+
for (const [keyType, key] of Object.entries(keyPair)) {
|
|
247
|
+
// eslint-disable-next-line no-await-in-loop
|
|
248
|
+
storableKeys[keyType] = yield this.getStorableKey(key);
|
|
249
|
+
}
|
|
250
|
+
return storableKeys;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
restoreStorableKeyPair(keyPair) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
const restoredKeys = {};
|
|
256
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
257
|
+
for (const [keyType, key] of Object.entries(keyPair)) {
|
|
258
|
+
// eslint-disable-next-line no-await-in-loop
|
|
259
|
+
restoredKeys[keyType] = yield this.restoreStorableKey(keyType, key);
|
|
260
|
+
}
|
|
261
|
+
return restoredKeys;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
deriveSharedKey(_a) {
|
|
265
|
+
return __awaiter(this, arguments, void 0, function* ({ publicKey, privateKey }) {
|
|
266
|
+
if (!publicKey || !privateKey) {
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
const crypto = yield this.getWebCrypto();
|
|
270
|
+
const derivedKey = yield crypto.subtle.deriveKey({
|
|
271
|
+
name: 'ECDH',
|
|
272
|
+
public: publicKey,
|
|
273
|
+
}, privateKey, {
|
|
274
|
+
name: 'AES-GCM',
|
|
275
|
+
length: 256,
|
|
276
|
+
}, true, ['encrypt', 'decrypt']);
|
|
277
|
+
return derivedKey;
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
deriveHMACKey(_a) {
|
|
281
|
+
return __awaiter(this, arguments, void 0, function* ({ publicKey, privateKey }) {
|
|
282
|
+
if (!publicKey || !privateKey) {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
const crypto = yield this.getWebCrypto();
|
|
286
|
+
const derivedKey = yield crypto.subtle.deriveKey({
|
|
287
|
+
name: 'ECDH',
|
|
288
|
+
public: publicKey,
|
|
289
|
+
}, privateKey, {
|
|
290
|
+
name: 'HMAC',
|
|
291
|
+
hash: { name: 'SHA-256' },
|
|
292
|
+
length: 256, // Adjusted key length, e.g., 128 bits
|
|
293
|
+
}, true, ['sign', 'verify']);
|
|
294
|
+
return derivedKey;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
getVerificationKeys(_a) {
|
|
298
|
+
return __awaiter(this, arguments, void 0, function* ({ publicKey, privateKey }) {
|
|
299
|
+
if (!publicKey || !privateKey) {
|
|
300
|
+
return {};
|
|
301
|
+
}
|
|
302
|
+
const sharedKeyPair = yield this.restoreStorableKeyPair({ publicKey, privateKey });
|
|
303
|
+
const derivedHMACKey = yield this.deriveHMACKey(sharedKeyPair);
|
|
304
|
+
const derivedSecretKey = yield this.deriveSharedKey(sharedKeyPair);
|
|
305
|
+
return {
|
|
306
|
+
derivedSecretKey,
|
|
307
|
+
derivedHMACKey,
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
encryptMessage(decryptedMessage, derivedKey) {
|
|
312
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
313
|
+
if (!decryptedMessage || !derivedKey) {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
const crypto = yield this.getWebCrypto();
|
|
317
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
318
|
+
const Encoder = yield this.getTextEncoder();
|
|
319
|
+
const encodedMessage = new Encoder().encode(decryptedMessage);
|
|
320
|
+
const ciphertext = yield crypto.subtle.encrypt({
|
|
321
|
+
name: 'AES-GCM',
|
|
322
|
+
iv,
|
|
323
|
+
}, derivedKey, encodedMessage);
|
|
324
|
+
const encryptedMessage = new Uint8Array([
|
|
325
|
+
...iv,
|
|
326
|
+
...new Uint8Array(ciphertext)
|
|
327
|
+
]);
|
|
328
|
+
return Array.from(encryptedMessage);
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
decryptMessage(encryptedMessage, derivedKey) {
|
|
332
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
333
|
+
if (!encryptedMessage || !derivedKey) {
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
const crypto = yield this.getWebCrypto();
|
|
337
|
+
// NOTE: this presumed an array or arrayBuffer coming in as encryptedMessage (will fail w/ IV error if its a string)
|
|
338
|
+
const encryptedArrayBuffer = this.ensureArrayBuffer(encryptedMessage);
|
|
339
|
+
const iv = encryptedArrayBuffer.slice(0, 12);
|
|
340
|
+
const ciphertext = encryptedArrayBuffer.slice(12);
|
|
341
|
+
const decryptedArrayBuffer = yield crypto.subtle.decrypt({
|
|
342
|
+
name: 'AES-GCM',
|
|
343
|
+
iv,
|
|
344
|
+
}, derivedKey, ciphertext);
|
|
345
|
+
const Decoder = yield this.getTextDecoder();
|
|
346
|
+
const decryptedMessage = new Decoder().decode(decryptedArrayBuffer);
|
|
347
|
+
return decryptedMessage;
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { stringEmpty } from './display';
|
|
11
|
+
import { WebCrypto } from './WebCrypto';
|
|
12
|
+
export const getAuthorizationHelpers = (sharedSecret_1, ...args_1) => __awaiter(void 0, [sharedSecret_1, ...args_1], void 0, function* (sharedSecret, { crypto: _crypto, util: _util } = {}) {
|
|
13
|
+
if (stringEmpty(sharedSecret)) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
|
|
17
|
+
const sharedKey = yield webCrypto.restoreStorableKey('sharedSecret', sharedSecret);
|
|
18
|
+
return {
|
|
19
|
+
encrypt: (decryptedMessage) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
+
return (webCrypto.encryptMessage(decryptedMessage, sharedKey));
|
|
21
|
+
}),
|
|
22
|
+
decrypt: (encryptedMessage) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
+
return (webCrypto.decryptMessage(encryptedMessage, sharedKey));
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
});
|