@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.
Files changed (46) hide show
  1. package/dist/common/encrypt/encrypt-constants.d.mts +0 -4
  2. package/dist/common/encrypt/encrypt-constants.d.mts.map +1 -1
  3. package/dist/common/encrypt/encrypt-constants.mjs +0 -4
  4. package/dist/common/encrypt/encrypt-constants.mjs.map +1 -1
  5. package/dist/common/encrypt/encrypt-helper.d.mts +2 -0
  6. package/dist/common/encrypt/encrypt-helper.d.mts.map +1 -0
  7. package/dist/common/encrypt/encrypt-helper.mjs +2 -0
  8. package/dist/common/encrypt/encrypt-helper.mjs.map +1 -0
  9. package/dist/common/encrypt/encrypt-types.d.mts +0 -34
  10. package/dist/common/encrypt/encrypt-types.d.mts.map +1 -1
  11. package/dist/common/encrypt/encrypt-types.mjs +0 -4
  12. package/dist/common/encrypt/encrypt-types.mjs.map +1 -1
  13. package/dist/common/other/ibgib-helper.d.mts +15 -1
  14. package/dist/common/other/ibgib-helper.d.mts.map +1 -1
  15. package/dist/common/other/ibgib-helper.mjs +17 -4
  16. package/dist/common/other/ibgib-helper.mjs.map +1 -1
  17. package/dist/common/secret/secret-constants.d.mts +30 -0
  18. package/dist/common/secret/secret-constants.d.mts.map +1 -0
  19. package/dist/common/secret/secret-constants.mjs +42 -0
  20. package/dist/common/secret/secret-constants.mjs.map +1 -0
  21. package/dist/common/secret/secret-helper.d.mts +85 -0
  22. package/dist/common/secret/secret-helper.d.mts.map +1 -0
  23. package/dist/common/secret/secret-helper.mjs +358 -0
  24. package/dist/common/secret/secret-helper.mjs.map +1 -0
  25. package/dist/common/secret/secret-types.d.mts +192 -0
  26. package/dist/common/secret/secret-types.d.mts.map +1 -0
  27. package/dist/common/secret/secret-types.mjs +8 -0
  28. package/dist/common/secret/secret-types.mjs.map +1 -0
  29. package/dist/common/secret/secret.respec.d.mts +7 -0
  30. package/dist/common/secret/secret.respec.d.mts.map +1 -0
  31. package/dist/common/secret/secret.respec.mjs +160 -0
  32. package/dist/common/secret/secret.respec.mjs.map +1 -0
  33. package/dist/witness/space/metaspace/metaspace-base.d.mts +2 -1
  34. package/dist/witness/space/metaspace/metaspace-base.d.mts.map +1 -1
  35. package/dist/witness/space/metaspace/metaspace-base.mjs +24 -18
  36. package/dist/witness/space/metaspace/metaspace-base.mjs.map +1 -1
  37. package/dist/witness/space/metaspace/metaspace-types.d.mts +2 -1
  38. package/dist/witness/space/metaspace/metaspace-types.d.mts.map +1 -1
  39. package/package.json +3 -3
  40. package/src/common/other/ibgib-helper.mts +28 -3
  41. package/src/common/secret/secret-constants.mts +13 -0
  42. package/src/common/secret/secret-helper.mts +211 -54
  43. package/src/common/secret/secret-types.mts +138 -8
  44. package/src/common/secret/secret.respec.mts +144 -5
  45. package/src/witness/space/metaspace/metaspace-base.mts +3 -2
  46. 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 { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
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, classname,
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 (uuid) {
62
- if (!uuid.match(UUID_REGEXP)) {
63
- errors.push(`uuid must match regexp: ${UUID_REGEXP} (E: 1b035a31d191435217010ac272681c82)`);
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(`uuid required. (E: 55fee43e758fcf542866bdabe7321633)`);
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
- SecretIbGib,
108
+ ibGib,
86
109
  }: {
87
- SecretIbGib: SecretIbGib_V1,
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: SecretIbGib }) ?? [];
115
+ const intrinsicErrors: string[] = await validateIbGibIntrinsically({ ibGib: ibGib }) ?? [];
93
116
 
94
- if (!SecretIbGib.data) { throw new Error(`SecretIbGib.data required (E: df00780071919f55c762ee0756cf46bc)`); }
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
- const dataErrors = validateCommonSecretData({ data: SecretIbGib.data });
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
- // ad hoc validation here.
154
+ const { name, type } = data;
137
155
 
138
- const { name, uuid } = data;
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] [classname] [SecretName] [SecretId]'
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
- SecretClassname: string,
157
- SecretName: string,
158
- SecretId: string,
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 pieces = ib.split(' ');
181
+ const [atom, type, name] = ib.split(' ');
165
182
 
166
- return {
167
- SecretClassname: pieces[2],
168
- SecretName: pieces[3],
169
- SecretId: pieces[4],
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
- fnPromptSecret: (space: IbGibSpaceAny) => Promise<SecretIbGib_V1 | undefined>,
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
- fnPromptSecret: (space: IbGibSpaceAny) => Promise<SecretIbGib_V1 | undefined>,
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
- * We won't save the entire hash, just the hash.slice(16),
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
- hash16816_SHA256: string;
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.