@keetanetwork/anchor 0.0.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/.done +0 -0
- package/LICENSE +35 -0
- package/client/index.d.ts +10 -0
- package/client/index.d.ts.map +1 -0
- package/client/index.js +11 -0
- package/client/index.js.map +1 -0
- package/config.d.ts +10 -0
- package/config.d.ts.map +1 -0
- package/config.js +36 -0
- package/config.js.map +1 -0
- package/lib/certificates.d.ts +106 -0
- package/lib/certificates.d.ts.map +1 -0
- package/lib/certificates.js +463 -0
- package/lib/certificates.js.map +1 -0
- package/lib/encrypted-container.d.ts +106 -0
- package/lib/encrypted-container.d.ts.map +1 -0
- package/lib/encrypted-container.js +594 -0
- package/lib/encrypted-container.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +5 -0
- package/lib/index.js.map +1 -0
- package/lib/log/common.d.ts +35 -0
- package/lib/log/common.d.ts.map +1 -0
- package/lib/log/common.js +19 -0
- package/lib/log/common.js.map +1 -0
- package/lib/log/index.d.ts +59 -0
- package/lib/log/index.d.ts.map +1 -0
- package/lib/log/index.js +207 -0
- package/lib/log/index.js.map +1 -0
- package/lib/log/target_console.d.ts +13 -0
- package/lib/log/target_console.d.ts.map +1 -0
- package/lib/log/target_console.js +44 -0
- package/lib/log/target_console.js.map +1 -0
- package/lib/resolver.d.ts +308 -0
- package/lib/resolver.d.ts.map +1 -0
- package/lib/resolver.js +1429 -0
- package/lib/resolver.js.map +1 -0
- package/lib/utils/array.d.ts +10 -0
- package/lib/utils/array.d.ts.map +1 -0
- package/lib/utils/array.js +12 -0
- package/lib/utils/array.js.map +1 -0
- package/lib/utils/asn1.d.ts +13 -0
- package/lib/utils/asn1.d.ts.map +1 -0
- package/lib/utils/asn1.js +8 -0
- package/lib/utils/asn1.js.map +1 -0
- package/lib/utils/buffer.d.ts +4 -0
- package/lib/utils/buffer.d.ts.map +1 -0
- package/lib/utils/buffer.js +3 -0
- package/lib/utils/buffer.js.map +1 -0
- package/lib/utils/crypto.d.ts +4 -0
- package/lib/utils/crypto.d.ts.map +1 -0
- package/lib/utils/crypto.js +4 -0
- package/lib/utils/crypto.js.map +1 -0
- package/lib/utils/index.d.ts +5 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +5 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/json.d.ts +8 -0
- package/lib/utils/json.d.ts.map +1 -0
- package/lib/utils/json.js +164 -0
- package/lib/utils/json.js.map +1 -0
- package/lib/utils/never.d.ts +8 -0
- package/lib/utils/never.d.ts.map +1 -0
- package/lib/utils/never.js +14 -0
- package/lib/utils/never.js.map +1 -0
- package/npm-shrinkwrap.json +16517 -0
- package/package.json +42 -0
- package/services/kyc/client.d.ts +139 -0
- package/services/kyc/client.d.ts.map +1 -0
- package/services/kyc/client.js +390 -0
- package/services/kyc/client.js.map +1 -0
- package/services/kyc/common.d.ts +65 -0
- package/services/kyc/common.d.ts.map +1 -0
- package/services/kyc/common.js +2 -0
- package/services/kyc/common.js.map +1 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
import crypto from './utils/crypto.js';
|
|
2
|
+
import { lib as KeetaNetLib } from '@keetanetwork/keetanet-client';
|
|
3
|
+
import { Buffer } from './utils/buffer.js';
|
|
4
|
+
import { isArray } from './utils/array.js';
|
|
5
|
+
const zlibDeflate = KeetaNetLib.Utils.Buffer.ZlibDeflate; /* XXX:TODO: Change this to ZlibDeflateAsync when merged in */
|
|
6
|
+
const zlibInflate = KeetaNetLib.Utils.Buffer.ZlibInflate; /* XXX:TODO: Change this to ZlibInflateAsync when merged in */
|
|
7
|
+
const ASN1toJS = KeetaNetLib.Utils.ASN1.ASN1toJS;
|
|
8
|
+
const JStoASN1 = KeetaNetLib.Utils.ASN1.JStoASN1;
|
|
9
|
+
const Account = KeetaNetLib.Account;
|
|
10
|
+
const bufferToArrayBuffer = KeetaNetLib.Utils.Helper.bufferToArrayBuffer;
|
|
11
|
+
const oidDB = {
|
|
12
|
+
'aes-256-cbc': '2.16.840.1.101.3.4.1.42'
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Compiles the ASN.1 for the container
|
|
16
|
+
*
|
|
17
|
+
* @returns The ASN.1 DER data
|
|
18
|
+
*/
|
|
19
|
+
async function buildASN1(plaintext, encryptionOptions) {
|
|
20
|
+
const compressedPlaintext = Buffer.from(zlibDeflate(plaintext));
|
|
21
|
+
const sequence = [];
|
|
22
|
+
/*
|
|
23
|
+
* Version v2 (1)
|
|
24
|
+
*/
|
|
25
|
+
sequence[0] = 1;
|
|
26
|
+
/*
|
|
27
|
+
* Encrypted container box
|
|
28
|
+
*/
|
|
29
|
+
if (encryptionOptions) {
|
|
30
|
+
const { keys, cipherKey, cipherIV, cipherAlgo } = encryptionOptions;
|
|
31
|
+
if (keys === undefined || keys.length === 0 || cipherKey === undefined || cipherIV === undefined || cipherAlgo === undefined) {
|
|
32
|
+
throw (new Error('internal error: Unsupported method invocation'));
|
|
33
|
+
}
|
|
34
|
+
if (!(cipherAlgo in oidDB)) {
|
|
35
|
+
throw (new Error(`Unsupported algorithm: ${cipherAlgo}`));
|
|
36
|
+
}
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
38
|
+
const algorithmOID = oidDB[cipherAlgo];
|
|
39
|
+
const cipher = crypto.createCipheriv(cipherAlgo, cipherKey, cipherIV);
|
|
40
|
+
const encryptedData = Buffer.concat([
|
|
41
|
+
cipher.update(compressedPlaintext),
|
|
42
|
+
cipher.final()
|
|
43
|
+
]);
|
|
44
|
+
const cipherKeyArrayBuffer = bufferToArrayBuffer(cipherKey);
|
|
45
|
+
const encryptionKeysSequence = await Promise.all(keys.map(async function (key) {
|
|
46
|
+
const encryptedSymmetricKey = Buffer.from(await key.encrypt(cipherKeyArrayBuffer));
|
|
47
|
+
const retval = [
|
|
48
|
+
key.publicKeyAndType,
|
|
49
|
+
encryptedSymmetricKey
|
|
50
|
+
];
|
|
51
|
+
return (retval);
|
|
52
|
+
}));
|
|
53
|
+
sequence[1] = {
|
|
54
|
+
type: 'context',
|
|
55
|
+
value: 0,
|
|
56
|
+
kind: 'explicit',
|
|
57
|
+
contains: [
|
|
58
|
+
encryptionKeysSequence,
|
|
59
|
+
{ type: 'oid', oid: algorithmOID },
|
|
60
|
+
cipherIV,
|
|
61
|
+
encryptedData
|
|
62
|
+
]
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
/*
|
|
67
|
+
* Otherwise we simply pass in the compressed data
|
|
68
|
+
*/
|
|
69
|
+
sequence[1] = {
|
|
70
|
+
type: 'context',
|
|
71
|
+
value: 1,
|
|
72
|
+
kind: 'explicit',
|
|
73
|
+
contains: [compressedPlaintext]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const outputASN1 = JStoASN1(sequence);
|
|
77
|
+
const outputDER = Buffer.from(outputASN1.toBER(false));
|
|
78
|
+
return (outputDER);
|
|
79
|
+
}
|
|
80
|
+
function parseASN1Bare(input, acceptableEncryptionAlgorithms = ['aes-256-cbc', 'null']) {
|
|
81
|
+
const inputSequence = ASN1toJS(bufferToArrayBuffer(input));
|
|
82
|
+
if (!isArray(inputSequence, 2)) {
|
|
83
|
+
throw (new Error('Malformed data detected (incorrect base format)'));
|
|
84
|
+
}
|
|
85
|
+
const version = inputSequence[0];
|
|
86
|
+
if (typeof version !== 'bigint') {
|
|
87
|
+
throw (new Error('Malformed data detected (version expected at position 0)'));
|
|
88
|
+
}
|
|
89
|
+
if (version !== 1n) {
|
|
90
|
+
throw (new Error('Malformed data detected (unsupported version)'));
|
|
91
|
+
}
|
|
92
|
+
const valueBox = inputSequence[1];
|
|
93
|
+
if (typeof valueBox !== 'object' || valueBox === null) {
|
|
94
|
+
throw (new Error('Malformed data detected (data expected at position 1)'));
|
|
95
|
+
}
|
|
96
|
+
if (!('type' in valueBox) || typeof valueBox.type !== 'string') {
|
|
97
|
+
throw (new Error('Malformed data detected (expected type at position 1)'));
|
|
98
|
+
}
|
|
99
|
+
if (valueBox.type !== 'context') {
|
|
100
|
+
throw (new Error('Malformed data detected (expected context at position 1)'));
|
|
101
|
+
}
|
|
102
|
+
if (!('value' in valueBox) || typeof valueBox.value !== 'number') {
|
|
103
|
+
throw (new Error('Malformed data detected (expected context value at position 1)'));
|
|
104
|
+
}
|
|
105
|
+
if (valueBox.value !== 0 && valueBox.value !== 1) {
|
|
106
|
+
throw (new Error('Malformed data detected (expected context value of 0 or 1)'));
|
|
107
|
+
}
|
|
108
|
+
if (!('contains' in valueBox) || typeof valueBox.contains !== 'object' || valueBox.contains === null) {
|
|
109
|
+
throw (new Error('Malformed data detected (expected contents at position 1)'));
|
|
110
|
+
}
|
|
111
|
+
let isEncrypted;
|
|
112
|
+
if (valueBox.value === 0) {
|
|
113
|
+
isEncrypted = true;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
isEncrypted = false;
|
|
117
|
+
}
|
|
118
|
+
const value = valueBox.contains;
|
|
119
|
+
let containedCompressed;
|
|
120
|
+
let cipherInfo;
|
|
121
|
+
if (isEncrypted) {
|
|
122
|
+
if (!isArray(value, 4)) {
|
|
123
|
+
throw (new Error('Malformed data (incorrect number of elements within position 1 -- expected 4)'));
|
|
124
|
+
}
|
|
125
|
+
const keyInfoUnchecked = value[0];
|
|
126
|
+
if (!isArray(keyInfoUnchecked)) {
|
|
127
|
+
throw (new Error('Malformed data (expected sequence at position 2.0)'));
|
|
128
|
+
}
|
|
129
|
+
const keyInfo = keyInfoUnchecked.map(function (checkKeyInfo) {
|
|
130
|
+
if (!isArray(checkKeyInfo, 2)) {
|
|
131
|
+
throw (new Error('Malformed key information (expected sequence of 2 at position 1.0.x)'));
|
|
132
|
+
}
|
|
133
|
+
const publicKeyBuffer = checkKeyInfo[0];
|
|
134
|
+
if (!Buffer.isBuffer(publicKeyBuffer)) {
|
|
135
|
+
throw (new Error('Malformed key information (expected octet string for public key at position 1.0.x)'));
|
|
136
|
+
}
|
|
137
|
+
const publicKey = Account.fromPublicKeyAndType(publicKeyBuffer);
|
|
138
|
+
const encryptedSymmetricKey = checkKeyInfo[1];
|
|
139
|
+
if (!Buffer.isBuffer(encryptedSymmetricKey)) {
|
|
140
|
+
throw (new Error('Malformed key information (expected octet string for cipher key at position 1.0.x)'));
|
|
141
|
+
}
|
|
142
|
+
return ({
|
|
143
|
+
publicKey,
|
|
144
|
+
encryptedSymmetricKey
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
148
|
+
const encryptionAlgorithmOID = value[1];
|
|
149
|
+
/* XXX:TODO: Lookup the encryption algorithm from the OID */
|
|
150
|
+
const encryptionAlgorithm = 'aes-256-cbc';
|
|
151
|
+
const cipherIV = value[2];
|
|
152
|
+
if (!Buffer.isBuffer(cipherIV)) {
|
|
153
|
+
throw (new Error('Malformed data (cipher IV expected at position 1.2)'));
|
|
154
|
+
}
|
|
155
|
+
const encryptedCompressedValue = value[3];
|
|
156
|
+
if (!Buffer.isBuffer(encryptedCompressedValue)) {
|
|
157
|
+
throw (new Error('Malformed data (encrypted compressed buffer expected at position 1.3)'));
|
|
158
|
+
}
|
|
159
|
+
cipherInfo = {
|
|
160
|
+
keys: keyInfo,
|
|
161
|
+
cipherIV: cipherIV,
|
|
162
|
+
encryptedData: encryptedCompressedValue,
|
|
163
|
+
encryptionAlgorithm: encryptionAlgorithm
|
|
164
|
+
};
|
|
165
|
+
containedCompressed = encryptedCompressedValue;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
if (!isArray(value, 1)) {
|
|
169
|
+
throw (new Error('Malformed data (incorrect number of elements within position 1 -- expected 1)'));
|
|
170
|
+
}
|
|
171
|
+
const containedCompressedUnchecked = value[0];
|
|
172
|
+
if (!Buffer.isBuffer(containedCompressedUnchecked)) {
|
|
173
|
+
throw (new Error('Malformed data (compressed buffer expected at position 1.0)'));
|
|
174
|
+
}
|
|
175
|
+
if (!acceptableEncryptionAlgorithms.includes('null')) {
|
|
176
|
+
throw (new Error('Malformed data (plaintext found but the null encryption algorithm is not acceptable)'));
|
|
177
|
+
}
|
|
178
|
+
containedCompressed = containedCompressedUnchecked;
|
|
179
|
+
}
|
|
180
|
+
return ({
|
|
181
|
+
version: version,
|
|
182
|
+
isEncrypted: isEncrypted,
|
|
183
|
+
innerValue: containedCompressed,
|
|
184
|
+
...cipherInfo
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
async function parseASN1Decrypt(inputInfo, keys) {
|
|
188
|
+
let containedCompressed;
|
|
189
|
+
let cipherInfo;
|
|
190
|
+
if (inputInfo.isEncrypted) {
|
|
191
|
+
if (keys === undefined || keys.length === 0) {
|
|
192
|
+
throw (new Error('Encrypted Container found with encryption but no keys for decryption supplied'));
|
|
193
|
+
}
|
|
194
|
+
const algorithm = inputInfo.encryptionAlgorithm;
|
|
195
|
+
if (algorithm === undefined) {
|
|
196
|
+
throw (new Error('Encrypted Container found with encryption but no algorithm supplied'));
|
|
197
|
+
}
|
|
198
|
+
const keyInfo = inputInfo.keys;
|
|
199
|
+
if (keyInfo === undefined) {
|
|
200
|
+
throw (new Error('internal error: Encrypted container found with missing keys'));
|
|
201
|
+
}
|
|
202
|
+
let decryptionKeyInfo;
|
|
203
|
+
for (const checkKeyInfo of keyInfo) {
|
|
204
|
+
for (const key of keys) {
|
|
205
|
+
if (!key.hasPrivateKey) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (key.comparePublicKey(checkKeyInfo.publicKey)) {
|
|
209
|
+
decryptionKeyInfo = {
|
|
210
|
+
...checkKeyInfo,
|
|
211
|
+
privateKey: key
|
|
212
|
+
};
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (decryptionKeyInfo === undefined) {
|
|
218
|
+
throw (new Error('No keys found which can perform decryption on the supplied encryption box'));
|
|
219
|
+
}
|
|
220
|
+
const cipherKey = Buffer.from(await decryptionKeyInfo.privateKey.decrypt(bufferToArrayBuffer(decryptionKeyInfo.encryptedSymmetricKey)));
|
|
221
|
+
const cipherIV = inputInfo.cipherIV;
|
|
222
|
+
if (cipherIV === undefined) {
|
|
223
|
+
throw (new Error('internal error: No Cipher IV found'));
|
|
224
|
+
}
|
|
225
|
+
const encryptedCompressedValue = inputInfo.innerValue;
|
|
226
|
+
const decipher = crypto.createDecipheriv(algorithm, cipherKey, cipherIV);
|
|
227
|
+
containedCompressed = Buffer.concat([
|
|
228
|
+
decipher.update(encryptedCompressedValue),
|
|
229
|
+
decipher.final()
|
|
230
|
+
]);
|
|
231
|
+
cipherInfo = {
|
|
232
|
+
isEncrypted: true,
|
|
233
|
+
keys: keyInfo,
|
|
234
|
+
cipherIV: cipherIV,
|
|
235
|
+
cipherKey: cipherKey,
|
|
236
|
+
encryptedData: encryptedCompressedValue
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
containedCompressed = inputInfo.innerValue;
|
|
241
|
+
cipherInfo = {
|
|
242
|
+
isEncrypted: false
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const plaintext = Buffer.from(zlibInflate(containedCompressed));
|
|
246
|
+
return ({
|
|
247
|
+
version: inputInfo.version,
|
|
248
|
+
plaintext: plaintext,
|
|
249
|
+
...cipherInfo
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
async function parseASN1(input, keys) {
|
|
253
|
+
const inputInfo = parseASN1Bare(input);
|
|
254
|
+
const retval = await parseASN1Decrypt(inputInfo, keys);
|
|
255
|
+
return (retval);
|
|
256
|
+
}
|
|
257
|
+
export class EncryptedContainer {
|
|
258
|
+
static algorithm = 'aes-256-cbc';
|
|
259
|
+
/**
|
|
260
|
+
* Flag indicating whether we support exporting the plaintext
|
|
261
|
+
*/
|
|
262
|
+
#mayAccessPlaintext = true;
|
|
263
|
+
/**
|
|
264
|
+
* Encryption details
|
|
265
|
+
*/
|
|
266
|
+
_internalState;
|
|
267
|
+
/**
|
|
268
|
+
* The plaintext or encoded (and possibly encrypted) data
|
|
269
|
+
*/
|
|
270
|
+
#data;
|
|
271
|
+
constructor(principals) {
|
|
272
|
+
if (principals === null) {
|
|
273
|
+
this._internalState = {
|
|
274
|
+
principals: null
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
this._internalState = {
|
|
279
|
+
principals: principals,
|
|
280
|
+
cipherAlgo: EncryptedContainer.algorithm
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
;
|
|
284
|
+
this.#data = { plaintext: Buffer.alloc(0) };
|
|
285
|
+
}
|
|
286
|
+
get encrypted() {
|
|
287
|
+
return (this._internalState.principals !== null);
|
|
288
|
+
}
|
|
289
|
+
#isEncrypted() {
|
|
290
|
+
return (this.encrypted);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Create an instance of the EncryptedContainer from an encrypted blob,
|
|
294
|
+
* it will need to be decryptable with one of the specified principals
|
|
295
|
+
*
|
|
296
|
+
* After decryption happens, the list of principals with access to the
|
|
297
|
+
* resource will be reset to what is contained within the encrypted
|
|
298
|
+
* container
|
|
299
|
+
*/
|
|
300
|
+
static fromEncryptedBuffer(data, principals) {
|
|
301
|
+
const retval = new EncryptedContainer(principals);
|
|
302
|
+
retval.#setEncodedBuffer(data);
|
|
303
|
+
retval.#computeAndSetKeyInfo(true);
|
|
304
|
+
return (retval);
|
|
305
|
+
}
|
|
306
|
+
static fromEncodedBuffer(data, principals) {
|
|
307
|
+
const retval = new EncryptedContainer(principals);
|
|
308
|
+
retval.#setEncodedBuffer(data);
|
|
309
|
+
retval.#computeAndSetKeyInfo(false);
|
|
310
|
+
return (retval);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Create an instance of the EncryptedContainer from a plaintext.
|
|
314
|
+
*
|
|
315
|
+
* It will be decryptable by any one of the specified principals
|
|
316
|
+
*
|
|
317
|
+
* @param data The plaintext data to encrypt or encode
|
|
318
|
+
* @param principals The list of principals who can access the data if it is null then the data is not encrypted
|
|
319
|
+
* @param locked If true, the plaintext data will not be accessible from this instance; otherwise it will be -- default depends on principals
|
|
320
|
+
* @returns The EncryptedContainer instance with the plaintext data and principals set
|
|
321
|
+
*/
|
|
322
|
+
static fromPlaintext(data, principals, locked) {
|
|
323
|
+
const retval = new EncryptedContainer(principals);
|
|
324
|
+
if (locked === undefined) {
|
|
325
|
+
locked = true;
|
|
326
|
+
if (principals === null) {
|
|
327
|
+
locked = false;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (locked) {
|
|
331
|
+
retval.disablePlaintext();
|
|
332
|
+
}
|
|
333
|
+
retval.setPlaintext(data);
|
|
334
|
+
return (retval);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Set the plaintext buffer to the specified value
|
|
338
|
+
*/
|
|
339
|
+
setPlaintext(data) {
|
|
340
|
+
if (typeof data === 'string') {
|
|
341
|
+
data = Buffer.from(data, 'utf-8');
|
|
342
|
+
}
|
|
343
|
+
this.#data = { plaintext: data };
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Set the encoded blob to the specified value
|
|
347
|
+
*/
|
|
348
|
+
#setEncodedBuffer(data) {
|
|
349
|
+
this.#data = { encoded: data };
|
|
350
|
+
}
|
|
351
|
+
get _encoded() {
|
|
352
|
+
if ('encoded' in this.#data && this.#data.encoded !== undefined) {
|
|
353
|
+
return (this.#data.encoded);
|
|
354
|
+
}
|
|
355
|
+
return (undefined);
|
|
356
|
+
}
|
|
357
|
+
get _plaintext() {
|
|
358
|
+
if ('plaintext' in this.#data && this.#data.plaintext !== undefined) {
|
|
359
|
+
return (this.#data.plaintext);
|
|
360
|
+
}
|
|
361
|
+
return (undefined);
|
|
362
|
+
}
|
|
363
|
+
/*
|
|
364
|
+
* Return the decoded data from the encoded blob
|
|
365
|
+
* and populate the symmetric key parameters from the encoded blob if it is encrypted
|
|
366
|
+
*/
|
|
367
|
+
#computeAndSetKeyInfo(mustBeEncrypted) {
|
|
368
|
+
if (this._encoded === undefined) {
|
|
369
|
+
throw (new Error('No encoded data available'));
|
|
370
|
+
}
|
|
371
|
+
const plaintextWrapper = parseASN1Bare(this._encoded);
|
|
372
|
+
if (mustBeEncrypted && !plaintextWrapper.isEncrypted) {
|
|
373
|
+
throw (new Error('Unable to set key information from plaintext -- it is not encrypted but that was required'));
|
|
374
|
+
}
|
|
375
|
+
if (plaintextWrapper.isEncrypted) {
|
|
376
|
+
const principals = this._internalState.principals;
|
|
377
|
+
if (principals === null) {
|
|
378
|
+
throw (new Error('May not encrypt data with a null set of principals'));
|
|
379
|
+
}
|
|
380
|
+
/*
|
|
381
|
+
* Compute the new accounts by merging the input from the
|
|
382
|
+
* data and the existing list of principals
|
|
383
|
+
*/
|
|
384
|
+
/**
|
|
385
|
+
* The existing principals from the blob, with existing
|
|
386
|
+
* principals substituted in where appropriate
|
|
387
|
+
*/
|
|
388
|
+
const blobPrincipals = (plaintextWrapper.keys ?? []).map((keyInfo) => {
|
|
389
|
+
const currentPublicKey = keyInfo.publicKey;
|
|
390
|
+
if (!currentPublicKey.isAccount()) {
|
|
391
|
+
throw (new Error('internal error: Non-account found within the encryption key list'));
|
|
392
|
+
}
|
|
393
|
+
for (const checkExistingKey of principals) {
|
|
394
|
+
if (checkExistingKey.comparePublicKey(currentPublicKey)) {
|
|
395
|
+
return (checkExistingKey);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return (currentPublicKey);
|
|
399
|
+
});
|
|
400
|
+
this._internalState.principals = blobPrincipals;
|
|
401
|
+
// Confirm updated principals are populated correctly which sets container to encrypted
|
|
402
|
+
if (!this.encrypted) {
|
|
403
|
+
throw (new Error('internal error: Encrypted data found but not marked as encrypted'));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
this._internalState.principals = null;
|
|
408
|
+
if (this.encrypted) {
|
|
409
|
+
throw (new Error('internal error: Plaintext data found but marked as encrypted'));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return (plaintextWrapper);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Populate the plaintext (as well as symmetric key parameters) from
|
|
416
|
+
* the encoded blob
|
|
417
|
+
*/
|
|
418
|
+
async #computePlaintext() {
|
|
419
|
+
if (this._plaintext) {
|
|
420
|
+
return (this._plaintext);
|
|
421
|
+
}
|
|
422
|
+
if (this._encoded === undefined) {
|
|
423
|
+
throw (new Error('No plaintext or encoded data available'));
|
|
424
|
+
}
|
|
425
|
+
const info = this.#computeAndSetKeyInfo(this.encrypted);
|
|
426
|
+
let principals = this._internalState.principals;
|
|
427
|
+
if (info.isEncrypted) {
|
|
428
|
+
if (principals === null) {
|
|
429
|
+
throw (new Error('May not decrypt data with a null set of principals'));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
principals = [];
|
|
434
|
+
}
|
|
435
|
+
const plaintextWrapper = await parseASN1Decrypt(info, principals);
|
|
436
|
+
const plaintext = plaintextWrapper.plaintext;
|
|
437
|
+
this.#data = { ...this.#data, plaintext };
|
|
438
|
+
return (plaintext);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Compute the encoded version of the plaintext data
|
|
442
|
+
*/
|
|
443
|
+
async #computePlaintextEncoded() {
|
|
444
|
+
if (this._plaintext === undefined) {
|
|
445
|
+
throw (new Error('No plaintext data available'));
|
|
446
|
+
}
|
|
447
|
+
const structuredData = await buildASN1(this._plaintext);
|
|
448
|
+
return (structuredData);
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Populate the encrypted blob from the plaintext and symmetric key
|
|
452
|
+
* parameters. If the symmetric key parameters have not been
|
|
453
|
+
* initialized they will be initialized at this time.
|
|
454
|
+
*/
|
|
455
|
+
async #computeEncryptedEncoded() {
|
|
456
|
+
if (this._plaintext === undefined) {
|
|
457
|
+
throw (new Error('No encrypted nor plaintext data available'));
|
|
458
|
+
}
|
|
459
|
+
if (!this.#isEncrypted()) {
|
|
460
|
+
throw (new Error('internal error: Asked to encrypt a plaintext buffer'));
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* structured data is the ASN.1 encoded structure
|
|
464
|
+
*/
|
|
465
|
+
const structuredData = await buildASN1(this._plaintext, {
|
|
466
|
+
keys: this._internalState.principals,
|
|
467
|
+
cipherKey: crypto.randomBytes(32),
|
|
468
|
+
cipherIV: crypto.randomBytes(16),
|
|
469
|
+
cipherAlgo: this._internalState.cipherAlgo
|
|
470
|
+
});
|
|
471
|
+
return (structuredData);
|
|
472
|
+
}
|
|
473
|
+
async #computeEncoded() {
|
|
474
|
+
if (this._encoded !== undefined) {
|
|
475
|
+
return (this._encoded);
|
|
476
|
+
}
|
|
477
|
+
let computed;
|
|
478
|
+
if (!this.encrypted) {
|
|
479
|
+
computed = await this.#computePlaintextEncoded();
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
computed = await this.#computeEncryptedEncoded();
|
|
483
|
+
}
|
|
484
|
+
this.#data = { ...this.#data, encoded: computed };
|
|
485
|
+
return (computed);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Grant access to the secret for account(s) synchronously. This
|
|
489
|
+
* assumes the plaintext has already been computed and will fail
|
|
490
|
+
* if it is not
|
|
491
|
+
*/
|
|
492
|
+
grantAccessSync(accounts) {
|
|
493
|
+
if (this._plaintext === undefined) {
|
|
494
|
+
throw (new Error('Unable to grant access, plaintext not available'));
|
|
495
|
+
}
|
|
496
|
+
if (!this.#isEncrypted()) {
|
|
497
|
+
throw (new Error('May not manage access to a plaintext container'));
|
|
498
|
+
}
|
|
499
|
+
if (!Array.isArray(accounts)) {
|
|
500
|
+
accounts = [accounts];
|
|
501
|
+
}
|
|
502
|
+
// Encoded data is invalidated with the new permissions so set only the plaintext data
|
|
503
|
+
this.setPlaintext(this._plaintext);
|
|
504
|
+
this._internalState.principals.push(...accounts);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Grant access to the secret for account(s).
|
|
508
|
+
*/
|
|
509
|
+
async grantAccess(accounts) {
|
|
510
|
+
await this.#computePlaintext();
|
|
511
|
+
this.grantAccessSync(accounts);
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Revoke access to the secret for an account synchronously. This
|
|
515
|
+
* assumes the plaintext has already been computed and will fail
|
|
516
|
+
* if it is not
|
|
517
|
+
*/
|
|
518
|
+
revokeAccessSync(account) {
|
|
519
|
+
if (this._plaintext === undefined) {
|
|
520
|
+
throw (new Error('Unable to revoke access, plaintext not available'));
|
|
521
|
+
}
|
|
522
|
+
if (!this.#isEncrypted()) {
|
|
523
|
+
throw (new Error('May not manage access to a plaintext container'));
|
|
524
|
+
}
|
|
525
|
+
// Encoded data is invalidated with the new permissions so set only the plaintext data
|
|
526
|
+
this.setPlaintext(this._plaintext);
|
|
527
|
+
this._internalState.principals = this._internalState.principals.filter(function (checkAccount) {
|
|
528
|
+
return (!checkAccount.comparePublicKey(account));
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Revoke access to the secret for an account
|
|
533
|
+
*/
|
|
534
|
+
async revokeAccess(account) {
|
|
535
|
+
await this.#computePlaintext();
|
|
536
|
+
this.revokeAccessSync(account);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Disable access to the plaintext from this instance
|
|
540
|
+
*/
|
|
541
|
+
disablePlaintext() {
|
|
542
|
+
this.#mayAccessPlaintext = false;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Get the plaintext for this instance
|
|
546
|
+
*/
|
|
547
|
+
async getPlaintext() {
|
|
548
|
+
if (!this.#mayAccessPlaintext) {
|
|
549
|
+
throw (new Error('May not access plaintext'));
|
|
550
|
+
}
|
|
551
|
+
const plaintext = await this.#computePlaintext();
|
|
552
|
+
if (plaintext === undefined) {
|
|
553
|
+
throw (new Error('internal error: Plaintext could not be decoded'));
|
|
554
|
+
}
|
|
555
|
+
/*
|
|
556
|
+
* Make a copy of our internal buffer so that any changes made
|
|
557
|
+
* to either our internal buffer or by our caller do not
|
|
558
|
+
* interfere
|
|
559
|
+
*/
|
|
560
|
+
return (Buffer.from(plaintext));
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Get the serializable buffer which can be stored and reconstructed
|
|
564
|
+
*/
|
|
565
|
+
async getEncodedBuffer() {
|
|
566
|
+
const serialized = await this.#computeEncoded();
|
|
567
|
+
if (serialized === undefined) {
|
|
568
|
+
throw (new Error('internal error: Could not encode data'));
|
|
569
|
+
}
|
|
570
|
+
/*
|
|
571
|
+
* Make a copy of our internal buffer so that any changes made
|
|
572
|
+
* to either our internal buffer or by our caller do not
|
|
573
|
+
* interfere
|
|
574
|
+
*/
|
|
575
|
+
return (Buffer.from(serialized));
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Get the list of accounts which have access to read the plaintext of
|
|
579
|
+
* this container
|
|
580
|
+
*/
|
|
581
|
+
get principals() {
|
|
582
|
+
if (!this.#isEncrypted()) {
|
|
583
|
+
throw (new Error('May not manage access to a plaintext container'));
|
|
584
|
+
}
|
|
585
|
+
return (this._internalState.principals);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
/** @internal */
|
|
589
|
+
export const _Testing = {
|
|
590
|
+
buildASN1: buildASN1,
|
|
591
|
+
parseASN1: parseASN1
|
|
592
|
+
};
|
|
593
|
+
export default EncryptedContainer;
|
|
594
|
+
//# sourceMappingURL=encrypted-container.js.map
|