@ibgib/core-gib 0.0.49 → 0.0.50
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/common/encrypt/encrypt-constants.d.mts +0 -4
- package/dist/common/encrypt/encrypt-constants.d.mts.map +1 -1
- package/dist/common/encrypt/encrypt-constants.mjs +0 -4
- package/dist/common/encrypt/encrypt-constants.mjs.map +1 -1
- package/dist/common/encrypt/encrypt-helper.d.mts +2 -0
- package/dist/common/encrypt/encrypt-helper.d.mts.map +1 -0
- package/dist/common/encrypt/encrypt-helper.mjs +2 -0
- package/dist/common/encrypt/encrypt-helper.mjs.map +1 -0
- package/dist/common/encrypt/encrypt-types.d.mts +0 -34
- package/dist/common/encrypt/encrypt-types.d.mts.map +1 -1
- package/dist/common/encrypt/encrypt-types.mjs +0 -4
- package/dist/common/encrypt/encrypt-types.mjs.map +1 -1
- package/dist/common/other/ibgib-helper.d.mts +15 -1
- package/dist/common/other/ibgib-helper.d.mts.map +1 -1
- package/dist/common/other/ibgib-helper.mjs +17 -4
- package/dist/common/other/ibgib-helper.mjs.map +1 -1
- package/dist/common/secret/secret-constants.d.mts +30 -0
- package/dist/common/secret/secret-constants.d.mts.map +1 -0
- package/dist/common/secret/secret-constants.mjs +42 -0
- package/dist/common/secret/secret-constants.mjs.map +1 -0
- package/dist/common/secret/secret-helper.d.mts +85 -0
- package/dist/common/secret/secret-helper.d.mts.map +1 -0
- package/dist/common/secret/secret-helper.mjs +358 -0
- package/dist/common/secret/secret-helper.mjs.map +1 -0
- package/dist/common/secret/secret-types.d.mts +192 -0
- package/dist/common/secret/secret-types.d.mts.map +1 -0
- package/dist/common/secret/secret-types.mjs +8 -0
- package/dist/common/secret/secret-types.mjs.map +1 -0
- package/dist/common/secret/secret.respec.d.mts +7 -0
- package/dist/common/secret/secret.respec.d.mts.map +1 -0
- package/dist/common/secret/secret.respec.mjs +160 -0
- package/dist/common/secret/secret.respec.mjs.map +1 -0
- package/dist/witness/space/metaspace/metaspace-base.d.mts +2 -1
- package/dist/witness/space/metaspace/metaspace-base.d.mts.map +1 -1
- package/dist/witness/space/metaspace/metaspace-base.mjs +24 -18
- package/dist/witness/space/metaspace/metaspace-base.mjs.map +1 -1
- package/dist/witness/space/metaspace/metaspace-types.d.mts +2 -1
- package/dist/witness/space/metaspace/metaspace-types.d.mts.map +1 -1
- package/package.json +3 -3
- package/src/common/other/ibgib-helper.mts +28 -3
- package/src/common/secret/secret-constants.mts +13 -0
- package/src/common/secret/secret-helper.mts +211 -54
- package/src/common/secret/secret-types.mts +138 -8
- package/src/common/secret/secret.respec.mts +144 -5
- package/src/witness/space/metaspace/metaspace-base.mts +3 -2
- package/src/witness/space/metaspace/metaspace-types.mts +1 -1
|
@@ -15,20 +15,22 @@
|
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
17
|
extractErrorMsg, delay, getSaferSubstring,
|
|
18
|
-
getTimestampInTicks, getUUID, pretty,
|
|
18
|
+
getTimestampInTicks, getUUID, pretty, HashAlgorithm, getTimestamp,
|
|
19
19
|
} from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
20
20
|
import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
|
|
21
21
|
import { UUID_REGEXP, CLASSNAME_REGEXP, } from '@ibgib/helper-gib/dist/constants.mjs';
|
|
22
22
|
import { Ib, } from '@ibgib/ts-gib/dist/types.mjs';
|
|
23
23
|
import { validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
|
|
24
|
+
import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
|
|
24
25
|
|
|
25
26
|
import { GLOBAL_LOG_A_LOT } from "../../core-constants.mjs";
|
|
26
|
-
import { SecretData_V1, SecretIbGib_V1, SecretInfo, SecretInfo_Password, SecretType, } from './secret-types.mjs';
|
|
27
|
+
import { CheckIfPasswordProbablyCorrectInfo, SecretData_V1, SecretIbGib_V1, SecretInfo, SecretInfo_Password, SecretType, VALID_SECRET_TYPES, } from './secret-types.mjs';
|
|
27
28
|
import { SECRET_ATOM, SECRET_NAME_REGEXP, SECRET_REL8N_NAME, } from './secret-constants.mjs';
|
|
28
29
|
import { MetaspaceService } from "../../witness/space/metaspace/metaspace-types.mjs";
|
|
29
30
|
import { IbGibSpaceAny } from "../../witness/space/space-base-v1.mjs";
|
|
30
31
|
import { SpecialIbGibType } from "../other/other-types.mjs";
|
|
31
|
-
import {
|
|
32
|
+
import { INVALID_DATE_STRING } from '../other/other-constants.mjs';
|
|
33
|
+
import { hash16816 } from '../other/ibgib-helper.mjs';
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
36
|
* for verbose logging
|
|
@@ -46,7 +48,9 @@ export function validateCommonSecretData({
|
|
|
46
48
|
if (!data) { throw new Error(`data required (E: f7d9a12390e2d3821c07c115a289b5af)`); }
|
|
47
49
|
const errors: string[] = [];
|
|
48
50
|
const {
|
|
49
|
-
name, uuid
|
|
51
|
+
name, /*uuid,*/ classname,
|
|
52
|
+
expirationUTC,
|
|
53
|
+
type,
|
|
50
54
|
} =
|
|
51
55
|
data;
|
|
52
56
|
|
|
@@ -58,20 +62,39 @@ export function validateCommonSecretData({
|
|
|
58
62
|
errors.push(`name required. (E: fe088277e7677ad295b5e914ed644ab4)`);
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
if (
|
|
62
|
-
if (!
|
|
63
|
-
errors.push(`
|
|
65
|
+
if (type) {
|
|
66
|
+
if (!VALID_SECRET_TYPES.includes(type)) {
|
|
67
|
+
errors.push(`type (${type}) is invalid. must be one of ${VALID_SECRET_TYPES.join(', ')} (E: 413a1c899dcc4623a38be35bdf144217)`);
|
|
64
68
|
}
|
|
65
69
|
} else {
|
|
66
|
-
errors.push(`
|
|
70
|
+
errors.push(`type required. (E: 6126767cc5fd4c898d432b139652e5d1)`);
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
// if (uuid) {
|
|
74
|
+
// if (!uuid.match(UUID_REGEXP)) {
|
|
75
|
+
// errors.push(`uuid must match regexp: ${UUID_REGEXP} (E: 1b035a31d191435217010ac272681c82)`);
|
|
76
|
+
// }
|
|
77
|
+
// } else {
|
|
78
|
+
// errors.push(`uuid required. (E: 55fee43e758fcf542866bdabe7321633)`);
|
|
79
|
+
// }
|
|
80
|
+
|
|
69
81
|
if (classname) {
|
|
70
82
|
if (!classname.match(CLASSNAME_REGEXP)) {
|
|
71
83
|
errors.push(`classname must match regexp: ${CLASSNAME_REGEXP} (E: 301c325035ad3297c2744fd25ec618dd)`);
|
|
72
84
|
}
|
|
73
85
|
}
|
|
74
86
|
|
|
87
|
+
if (expirationUTC) {
|
|
88
|
+
let date = new Date(expirationUTC);
|
|
89
|
+
if (date.toString() === INVALID_DATE_STRING) {
|
|
90
|
+
errors.push(`invalid expirationUTC (${expirationUTC}) (E: 969a126f12b742adbf9d671d3fdd782f)`)
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
errors.push(`expirationUTC required (E: f1ff3bcec74946908f0f37f9282e917d)`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
75
98
|
return errors;
|
|
76
99
|
} catch (error) {
|
|
77
100
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -82,24 +105,27 @@ export function validateCommonSecretData({
|
|
|
82
105
|
}
|
|
83
106
|
|
|
84
107
|
export async function validateCommonSecretIbGib({
|
|
85
|
-
|
|
108
|
+
ibGib,
|
|
86
109
|
}: {
|
|
87
|
-
|
|
110
|
+
ibGib: SecretIbGib_V1,
|
|
88
111
|
}): Promise<string[] | undefined> {
|
|
89
112
|
const lc = `[${validateCommonSecretIbGib.name}]`;
|
|
90
113
|
try {
|
|
91
114
|
if (logalot) { console.log(`${lc} starting... (I: 596f5952f6a18ebb23ff270520818eaa)`); }
|
|
92
|
-
const intrinsicErrors: string[] = await validateIbGibIntrinsically({ ibGib:
|
|
115
|
+
const intrinsicErrors: string[] = await validateIbGibIntrinsically({ ibGib: ibGib }) ?? [];
|
|
93
116
|
|
|
94
|
-
if (!
|
|
117
|
+
if (!ibGib.data) { throw new Error(`SecretIbGib.data required (E: df00780071919f55c762ee0756cf46bc)`); }
|
|
95
118
|
const ibErrors: string[] = [];
|
|
96
|
-
let { SecretClassname, SecretName, SecretId } =
|
|
97
|
-
parseSecretIb({ ib: SecretIbGib.ib });
|
|
98
|
-
if (!SecretClassname) { ibErrors.push(`SecretClassname required (E: 273042f54b363c094b466f85baef3c10)`); }
|
|
99
|
-
if (!SecretName) { ibErrors.push(`SecretName required (E: 670718e47214d0cdfa844d8f7d756477)`); }
|
|
100
|
-
if (!SecretId) { ibErrors.push(`SecretId required (E: 56c5874056ff8d07e1875cc38eccf0c3)`); }
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
// ib
|
|
121
|
+
let { atom, type, name, } =
|
|
122
|
+
parseSecretIb({ ib: ibGib.ib });
|
|
123
|
+
if (atom !== SECRET_ATOM) { ibErrors.push(`invalid secret atom. must be ${SECRET_ATOM} (E: 56c5874056ff8d07e1875cc38eccf0c3)`); }
|
|
124
|
+
if (!type) { ibErrors.push(`secret type required (E: 273042f54b363c094b466f85baef3c10)`); }
|
|
125
|
+
if (!name) { ibErrors.push(`secret name required (E: 670718e47214d0cdfa844d8f7d756477)`); }
|
|
126
|
+
|
|
127
|
+
// data
|
|
128
|
+
const dataErrors = validateCommonSecretData({ data: ibGib.data });
|
|
103
129
|
|
|
104
130
|
let result = [...(intrinsicErrors ?? []), ...(ibErrors ?? []), ...(dataErrors ?? [])];
|
|
105
131
|
if (result.length > 0) {
|
|
@@ -117,26 +143,17 @@ export async function validateCommonSecretIbGib({
|
|
|
117
143
|
|
|
118
144
|
export function getSecretIb({
|
|
119
145
|
data,
|
|
120
|
-
classname,
|
|
121
146
|
}: {
|
|
122
147
|
data: SecretData_V1,
|
|
123
|
-
classname?: string,
|
|
124
148
|
}): Ib {
|
|
125
149
|
const lc = `[${getSecretIb.name}]`;
|
|
126
150
|
try {
|
|
127
151
|
const validationErrors = validateCommonSecretData({ data });
|
|
128
152
|
if (validationErrors.length > 0) { throw new Error(`invalid Secret data: ${validationErrors} (E: 07a71e9ba5d574274e2c6a289a3fbabe)`); }
|
|
129
|
-
if (classname) {
|
|
130
|
-
if (data.classname && data.classname !== classname) { throw new Error(`classname does not match data.classname (E: 4955d77f6b12b65958dd54f17a8ecbd8)`); }
|
|
131
|
-
} else {
|
|
132
|
-
classname = data.classname;
|
|
133
|
-
if (!classname) { throw new Error(`classname required (E: 0530c26eb1f2225e5ea4a48ba63eed6e)`); }
|
|
134
|
-
}
|
|
135
153
|
|
|
136
|
-
|
|
154
|
+
const { name, type } = data;
|
|
137
155
|
|
|
138
|
-
|
|
139
|
-
return `${SECRET_ATOM} ${classname} ${name} ${uuid}`;
|
|
156
|
+
return `${SECRET_ATOM} ${type} ${name}`;
|
|
140
157
|
} catch (error) {
|
|
141
158
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
142
159
|
throw error;
|
|
@@ -144,7 +161,7 @@ export function getSecretIb({
|
|
|
144
161
|
}
|
|
145
162
|
|
|
146
163
|
/**
|
|
147
|
-
* Current schema is '[SECRET_ATOM] [
|
|
164
|
+
* Current schema is '[SECRET_ATOM] [type] [name]'
|
|
148
165
|
*
|
|
149
166
|
* NOTE this is space-delimited
|
|
150
167
|
*/
|
|
@@ -153,21 +170,23 @@ export function parseSecretIb({
|
|
|
153
170
|
}: {
|
|
154
171
|
ib: Ib,
|
|
155
172
|
}): {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
atom: string,
|
|
174
|
+
type: string,
|
|
175
|
+
name: string,
|
|
159
176
|
} {
|
|
160
177
|
const lc = `[${parseSecretIb.name}]`;
|
|
161
178
|
try {
|
|
162
179
|
if (!ib) { throw new Error(`Secret ib required (E: 115e8097c30538526536b240ce4b8e87)`); }
|
|
163
180
|
|
|
164
|
-
const
|
|
181
|
+
const [atom, type, name] = ib.split(' ');
|
|
165
182
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
183
|
+
if (atom !== SECRET_ATOM) { throw new Error(`atom !== ${SECRET_ATOM} (E: 942f3ab95687772a5483ec926ad98124)`); }
|
|
184
|
+
|
|
185
|
+
if (type !== SecretType.password) { console.warn(`${lc} type !== SecretType.password. atow (02/2024) password is the only thing we got. (W: 66511ca217b045629a7816adc4e41fad)`) }
|
|
186
|
+
|
|
187
|
+
if (!name.match(SECRET_NAME_REGEXP)) { throw new Error(`!name.match(SECRET_NAME_REGEXP) (E: 6c95dbb65d3d6d9e0400412bf7ba6824)`); }
|
|
188
|
+
|
|
189
|
+
return { atom, type, name, };
|
|
171
190
|
} catch (error) {
|
|
172
191
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
173
192
|
throw error;
|
|
@@ -180,20 +199,28 @@ export async function createAndRegisterNewSecret({
|
|
|
180
199
|
secretInfo,
|
|
181
200
|
metaspace,
|
|
182
201
|
space,
|
|
183
|
-
fnPromptSecret,
|
|
184
202
|
}: {
|
|
185
203
|
secretType: SecretType,
|
|
186
204
|
secretInfo: SecretInfo,
|
|
187
205
|
metaspace: MetaspaceService,
|
|
188
206
|
space: IbGibSpaceAny,
|
|
189
|
-
|
|
190
|
-
}): Promise<void> {
|
|
207
|
+
}): Promise<SecretIbGib_V1> {
|
|
191
208
|
const lc = `[${createAndRegisterNewSecret.name}]`;
|
|
192
209
|
try {
|
|
193
210
|
if (logalot) { console.log(`${lc} starting... (I: 08a3b2c784ff4689a8abf3ba8f1d7af4)`); }
|
|
194
211
|
|
|
212
|
+
let resSecretIbGib: SecretIbGib_V1;
|
|
213
|
+
|
|
195
214
|
if (secretType !== SecretType.password) { throw new Error(`unknown secretType (${secretType}). only "password" secret type currently implemented. (E: ce3dd7aef4f48f9799f2d37217eed224)`); }
|
|
196
215
|
|
|
216
|
+
resSecretIbGib = await createAndRegisterNewSecret_password({
|
|
217
|
+
secretType: secretType as 'password',
|
|
218
|
+
secretInfo: secretInfo as SecretInfo_Password,
|
|
219
|
+
metaspace,
|
|
220
|
+
space,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return resSecretIbGib;
|
|
197
224
|
} catch (error) {
|
|
198
225
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
199
226
|
throw error;
|
|
@@ -207,32 +234,22 @@ async function createAndRegisterNewSecret_password({
|
|
|
207
234
|
secretInfo,
|
|
208
235
|
metaspace,
|
|
209
236
|
space,
|
|
210
|
-
fnPromptSecret,
|
|
211
237
|
}: {
|
|
212
238
|
secretType: "password",
|
|
213
239
|
secretInfo: SecretInfo_Password,
|
|
214
240
|
metaspace: MetaspaceService,
|
|
215
241
|
space: IbGibSpaceAny,
|
|
216
|
-
|
|
217
|
-
}): Promise<void> {
|
|
242
|
+
}): Promise<SecretIbGib_V1> {
|
|
218
243
|
const lc = `[${createAndRegisterNewSecret_password.name}]`;
|
|
219
244
|
try {
|
|
220
245
|
if (logalot) { console.log(`${lc} starting... (I: ab330733d4b6bb733f5b5655bcd70a24)`); }
|
|
221
246
|
|
|
222
247
|
if (secretType !== 'password') { throw new Error(`(UNEXPECTED) secretType !== 'password'? (E: b086c3494808a38ad967e55a82364824)`); }
|
|
223
248
|
|
|
224
|
-
const localIndex_secretIbGibs: IbGib_V1[] = await metaspace.getSpecialRel8dIbGibs({
|
|
225
|
-
type: SpecialIbGibType.secrets,
|
|
226
|
-
rel8nName: SECRET_REL8N_NAME,
|
|
227
|
-
space,
|
|
228
|
-
});
|
|
229
|
-
|
|
230
249
|
// create the secret ibgib
|
|
231
|
-
|
|
232
250
|
const data: SecretData_V1 = {
|
|
233
251
|
...secretInfo,
|
|
234
252
|
};
|
|
235
|
-
const dataValidationErrors = validateCommonSecretData({ data });
|
|
236
253
|
const resFirstGen = await Factory_V1.firstGen({
|
|
237
254
|
ib: getSecretIb({ data }),
|
|
238
255
|
parentIbGib: Factory_V1.primitive({ ib: SECRET_ATOM }),
|
|
@@ -241,10 +258,19 @@ async function createAndRegisterNewSecret_password({
|
|
|
241
258
|
nCounter: true,
|
|
242
259
|
tjp: { timestamp: true, },
|
|
243
260
|
});
|
|
244
|
-
|
|
245
261
|
const secretIbGib = resFirstGen.newIbGib as SecretIbGib_V1;
|
|
262
|
+
|
|
263
|
+
// validate right away before anything else
|
|
264
|
+
const validationErrors = await validateCommonSecretIbGib({ ibGib: secretIbGib }) ?? [];
|
|
265
|
+
if (validationErrors.length > 0) { throw new Error(`(UNEXPECTED) newly created secretIbGib has validationErrors? validationErrors: ${validationErrors} (E: 458708237973aea6c72121016d98af24)`); }
|
|
266
|
+
|
|
267
|
+
// save first in the space...
|
|
246
268
|
await metaspace.persistTransformResult({ resTransform: resFirstGen, space });
|
|
269
|
+
|
|
270
|
+
// register the new secret with the ibgib space in general...
|
|
247
271
|
await metaspace.registerNewIbGib({ ibGib: secretIbGib, space });
|
|
272
|
+
|
|
273
|
+
// register the new secret with the secrets index
|
|
248
274
|
await metaspace.rel8ToSpecialIbGib({
|
|
249
275
|
type: "secrets",
|
|
250
276
|
rel8nName: SECRET_REL8N_NAME,
|
|
@@ -256,6 +282,137 @@ async function createAndRegisterNewSecret_password({
|
|
|
256
282
|
// rel8nName: SECRET_REL8N_NAME,
|
|
257
283
|
// space,
|
|
258
284
|
// });
|
|
285
|
+
|
|
286
|
+
return secretIbGib;
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
289
|
+
throw error;
|
|
290
|
+
} finally {
|
|
291
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* executes the hash16816 fn on the incoming {@link password} using parameters
|
|
297
|
+
* {@link recursionCount}, {@link saltPrependedPerHash} and {@link algorithm}.
|
|
298
|
+
* Then selects a random contiguous substring of {@link substringLength} of the
|
|
299
|
+
* resultant hash.
|
|
300
|
+
*
|
|
301
|
+
* @returns {@link substringLength} of deterministic hash
|
|
302
|
+
*
|
|
303
|
+
* This is a novel idea only useful in a byzantine fault tolerant distributed
|
|
304
|
+
* computation context, so to understand how this is used, PLEASE...
|
|
305
|
+
* @see {@link CheckIfPasswordProbablyCorrectInfo}
|
|
306
|
+
*/
|
|
307
|
+
export async function getCheckIfPasswordProbablyCorrectInfo({
|
|
308
|
+
password,
|
|
309
|
+
substringLength,
|
|
310
|
+
recursionCount,
|
|
311
|
+
saltPrependedPerHash,
|
|
312
|
+
algorithm,
|
|
313
|
+
}: {
|
|
314
|
+
/**
|
|
315
|
+
* string that we're going to hash and return the substring of.
|
|
316
|
+
*/
|
|
317
|
+
password: string,
|
|
318
|
+
/**
|
|
319
|
+
* length of the substring of the hash
|
|
320
|
+
*/
|
|
321
|
+
substringLength: number,
|
|
322
|
+
/**
|
|
323
|
+
* salt when recursively hashing
|
|
324
|
+
*/
|
|
325
|
+
saltPrependedPerHash: string,
|
|
326
|
+
/**
|
|
327
|
+
* algorithm to use. defaults to 'SHA-256'
|
|
328
|
+
*/
|
|
329
|
+
algorithm: HashAlgorithm,
|
|
330
|
+
/**
|
|
331
|
+
* number of recursions to do. Defaults to 16816.
|
|
332
|
+
*/
|
|
333
|
+
recursionCount: number,
|
|
334
|
+
}): Promise<CheckIfPasswordProbablyCorrectInfo> {
|
|
335
|
+
const lc = `[${getCheckIfPasswordProbablyCorrectInfo.name}]`;
|
|
336
|
+
try {
|
|
337
|
+
if (logalot) { console.log(`${lc} starting... (I: 9dc41fcf83a6d0e6054d3a9e79803224)`); }
|
|
338
|
+
if (!substringLength) { throw new Error(`substringLength (${substringLength}) required (E: d1d4fbfaa1562abac7ec42cdd0264224)`); }
|
|
339
|
+
|
|
340
|
+
recursionCount ??= 16816;
|
|
341
|
+
algorithm ??= 'SHA-256';
|
|
342
|
+
|
|
343
|
+
const fullHash = await hash16816({
|
|
344
|
+
s: password,
|
|
345
|
+
algorithm: 'SHA-256',
|
|
346
|
+
recursionCount,
|
|
347
|
+
saltPrependedPerHash,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// we want to pick a random starting point for the substring. on the
|
|
351
|
+
// consuming end, we don't need to know this random index, we will just
|
|
352
|
+
// confirm that the fullHash generated when the user enters their
|
|
353
|
+
// password conatins this substring at all (not where it starts from).
|
|
354
|
+
|
|
355
|
+
// 64 = length of sha256 hash
|
|
356
|
+
// 64-1 = 0-indexed adjustment
|
|
357
|
+
const r = Math.random();
|
|
358
|
+
const substringStartIndex = Math.floor(r * (63 - substringLength));
|
|
359
|
+
|
|
360
|
+
const resSubstring = fullHash.substring(substringStartIndex, substringStartIndex + substringLength);
|
|
361
|
+
|
|
362
|
+
// defensive checks here (atow 02/2024), remove at some point once we
|
|
363
|
+
// see that this isn't throwing and it's tested well enough
|
|
364
|
+
if (resSubstring.length !== substringLength) { throw new Error(`(UNEXPECTED) resSubstring.length (${resSubstring.length}) !== substringLength (${substringLength})?\nfullHash: ${fullHash}\nr: ${r}\nsubstringStartIndex: ${substringStartIndex}\nresSubstring: ${resSubstring}(E: c45acd126b4c70041e92f6ad5329c424)`); }
|
|
365
|
+
const confirmResult = await passwordProbablyCorrect({
|
|
366
|
+
password, checkInfo: {
|
|
367
|
+
substring: resSubstring,
|
|
368
|
+
algorithm,
|
|
369
|
+
recursionCount,
|
|
370
|
+
saltPrependedPerHash,
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
if (!confirmResult) { throw new Error(`(UNEXPECTED) !confirmResult? uhh... (E: cc757a8ef07b6468793b1bddbafca924)`); }
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
substring: resSubstring,
|
|
377
|
+
saltPrependedPerHash: saltPrependedPerHash ?? '',
|
|
378
|
+
recursionCount,
|
|
379
|
+
algorithm,
|
|
380
|
+
};
|
|
381
|
+
} catch (error) {
|
|
382
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
383
|
+
throw error;
|
|
384
|
+
} finally {
|
|
385
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* confirmation side of verifying if a consumer's password is **probably**
|
|
391
|
+
* correct. Basically computes the silly hash of the password and checks to see
|
|
392
|
+
* if it contains the given substring.
|
|
393
|
+
*
|
|
394
|
+
* @see {@link CheckIfPasswordProbablyCorrectInfo}
|
|
395
|
+
* @see {@link getCheckIfPasswordProbablyCorrectInfo}
|
|
396
|
+
*/
|
|
397
|
+
export async function passwordProbablyCorrect({
|
|
398
|
+
password,
|
|
399
|
+
checkInfo,
|
|
400
|
+
}: {
|
|
401
|
+
password: string,
|
|
402
|
+
checkInfo: CheckIfPasswordProbablyCorrectInfo,
|
|
403
|
+
}): Promise<boolean> {
|
|
404
|
+
const lc = `[${passwordProbablyCorrect.name}]`;
|
|
405
|
+
try {
|
|
406
|
+
if (logalot) { console.log(`${lc} starting... (I: ce745a0d8b5de7a24ddc83fe432e4224)`); }
|
|
407
|
+
const { substring, recursionCount, saltPrependedPerHash, algorithm } = checkInfo;
|
|
408
|
+
const fullHash = await hash16816({
|
|
409
|
+
s: password,
|
|
410
|
+
recursionCount,
|
|
411
|
+
saltPrependedPerHash,
|
|
412
|
+
algorithm,
|
|
413
|
+
});
|
|
414
|
+
const resProbablyCorrect = fullHash.includes(substring);
|
|
415
|
+
return resProbablyCorrect;
|
|
259
416
|
} catch (error) {
|
|
260
417
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
261
418
|
throw error;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @module secret types (and enums)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { HashAlgorithm } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
5
6
|
import { IbGibData_V1, IbGibRel8ns_V1, IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
6
7
|
|
|
7
8
|
export type SecretType = "password";
|
|
@@ -19,16 +20,12 @@ export interface SecretInfo {
|
|
|
19
20
|
|
|
20
21
|
export interface SecretInfo_Password extends SecretInfo {
|
|
21
22
|
type: 'password';
|
|
23
|
+
|
|
22
24
|
/**
|
|
23
|
-
*
|
|
24
|
-
* because we are not checking to give authority, we are
|
|
25
|
-
* just checking to see if the user entered the same
|
|
26
|
-
* password for their own check.
|
|
27
|
-
*
|
|
28
|
-
* ## warnings
|
|
29
|
-
* NOTHING giving actual authorization should look at this.
|
|
25
|
+
* @see {@link CheckIfPasswordProbablyCorrectInfo}
|
|
30
26
|
*/
|
|
31
|
-
|
|
27
|
+
passwordProbablyCorrectInfo: CheckIfPasswordProbablyCorrectInfo;
|
|
28
|
+
|
|
32
29
|
/**
|
|
33
30
|
* Public hint to help you remember your secret (or help the bad person
|
|
34
31
|
* attack your secret).
|
|
@@ -36,6 +33,139 @@ export interface SecretInfo_Password extends SecretInfo {
|
|
|
36
33
|
hint?: string;
|
|
37
34
|
}
|
|
38
35
|
|
|
36
|
+
/**
|
|
37
|
+
* This contains information for doing a **convenience check** of whether or
|
|
38
|
+
* not the user/consumer has entered a password that is "probably" correct.
|
|
39
|
+
*
|
|
40
|
+
* ## READ THIS
|
|
41
|
+
*
|
|
42
|
+
* Distributed sovereignty has different requirements than a trusted server
|
|
43
|
+
* approach when it comes to handling secrets.
|
|
44
|
+
*
|
|
45
|
+
* ### trusted servers store entire hashes for authentication
|
|
46
|
+
*
|
|
47
|
+
* In a trusted server approach, we will check the password against some hashing
|
|
48
|
+
* mechanism with varying implementation details. But we will almost certainly
|
|
49
|
+
* check against the entire hash, because we will decide authentication based on
|
|
50
|
+
* this proof.
|
|
51
|
+
*
|
|
52
|
+
* ### distributed data works differently
|
|
53
|
+
*
|
|
54
|
+
* In the distributed model, however, the only utility of a password is in
|
|
55
|
+
* encryption and signatures. So when a user/consumer enters a password, it
|
|
56
|
+
* actually **does** something, i.e., creates derivative data. Either it creates
|
|
57
|
+
* an encrypted file or performs a signature (I hesitate to discuss asymmetric
|
|
58
|
+
* encryption because I still don't believe in it, and when keystones are
|
|
59
|
+
* implemented in ibgib, passwords will evolve the stone (it works _somewhat_
|
|
60
|
+
* similarly to a double-ratchet algorithm but is more hardened and expressive
|
|
61
|
+
* because it works on top of ibgib's already-established "chaining"
|
|
62
|
+
* infrastructure)).
|
|
63
|
+
*
|
|
64
|
+
* But the question is:
|
|
65
|
+
* **How does the user/consumer know that they entered the right password?**
|
|
66
|
+
*
|
|
67
|
+
* ### we can't store entire hashes
|
|
68
|
+
*
|
|
69
|
+
* In the distributed model, we assume a brute-forcer will have access to the
|
|
70
|
+
* encrypted data and any metadata we might store. This includes those would-be
|
|
71
|
+
* hashes and that would be bad - even if they are randomly salted.
|
|
72
|
+
*
|
|
73
|
+
* This is because this approach would preclude the ability for the user to
|
|
74
|
+
* encrypt some large data with mitigations against short-circuit decryption
|
|
75
|
+
* until the entire data block was decrypted (I refer you to encrypt-gib
|
|
76
|
+
* documentation here). IOW, the brute-forcer would only have to use the
|
|
77
|
+
* password and check against the hash, which can be an extremely quick
|
|
78
|
+
* operation - even if we set iterations insanely high on some KDF, this still
|
|
79
|
+
* would provide a relatively quick avenue for brute force attacks.
|
|
80
|
+
*
|
|
81
|
+
* ### we can store partial hashes
|
|
82
|
+
*
|
|
83
|
+
* We won't save the entire hash, just some substring of the hash. (We could
|
|
84
|
+
* conceivably store other metadata that checks against the hash, e.g., parity
|
|
85
|
+
* checks like the number of 1s, 2s, etc.).
|
|
86
|
+
*
|
|
87
|
+
* ### partial hash dynamics
|
|
88
|
+
*
|
|
89
|
+
* * The larger the partial hash, the less likely false collisions are produced.
|
|
90
|
+
* * when a user typos a password, we do NOT want a false collision.
|
|
91
|
+
* * when a brute-forcer makes a password attempt, we DO want a false
|
|
92
|
+
* collision.
|
|
93
|
+
* * a user's **unique** typos compose the set that we want to avoid false
|
|
94
|
+
* collisions.
|
|
95
|
+
* * the same typo only counts once.
|
|
96
|
+
*
|
|
97
|
+
* So we want to...
|
|
98
|
+
* * maximize the time wasted in brute forcing
|
|
99
|
+
* * or IOW, minimize the information gained by the brute forcer
|
|
100
|
+
* * this will statistically slow down brute force attacks that work against
|
|
101
|
+
* this information alone.
|
|
102
|
+
* * minimize the inconvenience by a password failure due to a false collision
|
|
103
|
+
* by the legitimate user/consumer.
|
|
104
|
+
* * there should be in place a well-hardened fail path for when the false
|
|
105
|
+
* password is given but the false collision allows it to pass the initial
|
|
106
|
+
* quick check phase.
|
|
107
|
+
*
|
|
108
|
+
* ### my implementation notes
|
|
109
|
+
*
|
|
110
|
+
* I am not doing this mathematically/rigorously, because that would take way
|
|
111
|
+
* too long, rather I am working from...
|
|
112
|
+
* * my personal experience with searching for hashes in the ibgib code bases.
|
|
113
|
+
* * Most ibgib codebase error messages are accompanied by a hash to uniquely
|
|
114
|
+
* identify the error.
|
|
115
|
+
* * I often search the codebase based on a substring of this when I am
|
|
116
|
+
* troubleshooting.
|
|
117
|
+
* * atow (02/2024) there are 1171 of these hashes in just core-gib.
|
|
118
|
+
* * To get a unique hash, the absolute MOST I have to actually enter is 4 or
|
|
119
|
+
* 5 letters
|
|
120
|
+
* * 5 is extremely rare.
|
|
121
|
+
* * unit testing observations
|
|
122
|
+
* * I've found that 4 letters gives false collisions on average every 900-ish
|
|
123
|
+
* hashes.
|
|
124
|
+
*
|
|
125
|
+
* As such, I am setting the length to 4. But note that in consuming code, we
|
|
126
|
+
* always just check for the substring, so changing this value in the future
|
|
127
|
+
* should not affect code a la an exception thrown, but rather, it will just
|
|
128
|
+
* change this dynamic of good faith typos vs. brute force cracking.
|
|
129
|
+
*
|
|
130
|
+
* ## warnings
|
|
131
|
+
*
|
|
132
|
+
* NOTHING GIVING ACTUAL AUTHORIZATION SHOULD CHECK AGAINST THIS INFO.
|
|
133
|
+
* NOTHING GIVING ACTUAL AUTHORIZATION SHOULD CHECK AGAINST THIS INFO.
|
|
134
|
+
* NOTHING GIVING ACTUAL AUTHORIZATION SHOULD CHECK AGAINST THIS INFO.
|
|
135
|
+
* NOTHING GIVING ACTUAL AUTHORIZATION SHOULD CHECK AGAINST THIS INFO.
|
|
136
|
+
*/
|
|
137
|
+
export interface CheckIfPasswordProbablyCorrectInfo {
|
|
138
|
+
/**
|
|
139
|
+
* NOTHING GIVING ACTUAL AUTHORIZATION SHOULD CHECK AGAINST THIS INFO.
|
|
140
|
+
* NOTHING GIVING ACTUAL AUTHORIZATION SHOULD CHECK AGAINST THIS INFO.
|
|
141
|
+
* NOTHING GIVING ACTUAL AUTHORIZATION SHOULD CHECK AGAINST THIS INFO.
|
|
142
|
+
* @see {CheckIfPasswordProbablyCorrectInfo}
|
|
143
|
+
*
|
|
144
|
+
* substring contained in the resultant recursive hash built on the password
|
|
145
|
+
* and other parameters of this info.
|
|
146
|
+
*
|
|
147
|
+
* other parameters:
|
|
148
|
+
* * {@link recursions}
|
|
149
|
+
* * {@link saltPrependedPerHash}
|
|
150
|
+
* * {@link algorithm}
|
|
151
|
+
*/
|
|
152
|
+
substring: string;
|
|
153
|
+
/**
|
|
154
|
+
* the number of times to recursively call the hash function, analogous to a
|
|
155
|
+
* naive key stretching algorithm.
|
|
156
|
+
*/
|
|
157
|
+
recursionCount: number;
|
|
158
|
+
/**
|
|
159
|
+
* salt that will be prepended to the password/intermediate hash each hash
|
|
160
|
+
* round.
|
|
161
|
+
*/
|
|
162
|
+
saltPrependedPerHash: string;
|
|
163
|
+
/**
|
|
164
|
+
* hash algorithm to use per hash round.
|
|
165
|
+
*/
|
|
166
|
+
algorithm: HashAlgorithm;
|
|
167
|
+
}
|
|
168
|
+
|
|
39
169
|
// export type SecretData_V1 extends = SecretInfo; // extend this with logical OR later
|
|
40
170
|
/**
|
|
41
171
|
* ibgib's intrinsic data.
|