fiftyone.pipeline.did 4.5.38 → 4.5.40

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/didClient.js CHANGED
@@ -21,6 +21,7 @@
21
21
  * ********************************************************************* */
22
22
 
23
23
  const FodId = require('./fodId');
24
+ const layout = require('./internal/layout');
24
25
  const IdType = require('./idType');
25
26
  const packageVersion = require('./package.json').version;
26
27
 
@@ -777,7 +778,7 @@ function ensureEncodedLength (value) {
777
778
  * @returns {Date} the moment the envelope says it was created
778
779
  */
779
780
  function dateOf (fodId) {
780
- return new Date(OWID_EPOCH_MS + fodId.dateMinutes * MINUTE_MS);
781
+ return new Date(OWID_EPOCH_MS + fodId.date * MINUTE_MS);
781
782
  }
782
783
 
783
784
  /**
@@ -794,9 +795,9 @@ function dateOf (fodId) {
794
795
  */
795
796
  function payloadLengthValid (fodId) {
796
797
  const matchKeyLength = fodId.type === IdType.RANDOM
797
- ? FodId.GUID_LENGTH
798
- : FodId.MATCH_KEY_LENGTH;
799
- return fodId.payload.length >= FodId.HEADER_LENGTH + matchKeyLength;
798
+ ? layout.GUID_LENGTH
799
+ : layout.MATCH_KEY_LENGTH;
800
+ return fodId.payload.length >= layout.HEADER_LENGTH + matchKeyLength;
800
801
  }
801
802
 
802
803
  /**
@@ -25,15 +25,20 @@
25
25
  *
26
26
  * The 51Degrees Cloud service issues real 51Dids. To keep this example
27
27
  * self-contained and offline, it builds a sample 51Did in process - generate
28
- * an ECDSA P-256 key pair, sign a canonical 37-byte payload - then parses it
29
- * back and prints the three payload fields. It also shows the headline use
28
+ * an ECDSA P-256 key pair, sign a canonical payload - then parses it back
29
+ * and prints the payload fields. It also shows the headline use
30
30
  * case: a 51Did is re-issued fresh on every call (the envelope, hence the
31
31
  * base64, changes), but the match key is stable. Compare match keys, never
32
32
  * envelopes.
33
33
  */
34
34
 
35
35
  const { webcrypto } = require('crypto');
36
- const { FodId, IdType } = require('../index');
36
+ const { FodId, IdType, Usage } = require('../index');
37
+ // The payload byte layout is internal to the package, so a consumer
38
+ // never reaches for it. This example builds a sample 51Did to work on,
39
+ // which the cloud would otherwise issue, so it reads the layout from
40
+ // inside the package.
41
+ const layout = require('../internal/layout');
37
42
 
38
43
  const subtle = webcrypto.subtle;
39
44
  const VERSION = 2;
@@ -45,14 +50,27 @@ function uint32LE (v) {
45
50
  }
46
51
 
47
52
  function samplePayload () {
48
- const p = new Uint8Array(FodId.PAYLOAD_LENGTH); // Probabilistic (flags 0x00)
49
- p[FodId.LICENSE_ID_OFFSET] = 0x78;
50
- p[FodId.LICENSE_ID_OFFSET + 1] = 0x56;
51
- p[FodId.LICENSE_ID_OFFSET + 2] = 0x34;
52
- p[FodId.LICENSE_ID_OFFSET + 3] = 0x12;
53
- for (let i = 0; i < FodId.MATCH_KEY_LENGTH; i++) {
54
- p[FodId.MATCH_KEY_OFFSET + i] = 0x20 + i;
53
+ // One byte longer than the least length, because the terms byte follows
54
+ // the match key. A 51Did whose payload stops at the match key carries
55
+ // no terms byte and reads as Terms.NOT_STATED.
56
+ const p = new Uint8Array(layout.PAYLOAD_LENGTH + layout.TERMS_LENGTH);
57
+ // Bits 6 and 7 are zero, so the type is Probabilistic. Bits 4 and 5 are
58
+ // zero, so the payload version is 0, which is the layout this package
59
+ // reads. Bits 0 to 2 are the usage and they are cumulative, so 0b011
60
+ // grants standard marketing and, with it, non-marketing use.
61
+ p[layout.FLAGS_OFFSET] = 0b0000_0011;
62
+ p[layout.LICENSE_ID_OFFSET] = 0x78;
63
+ p[layout.LICENSE_ID_OFFSET + 1] = 0x56;
64
+ p[layout.LICENSE_ID_OFFSET + 2] = 0x34;
65
+ p[layout.LICENSE_ID_OFFSET + 3] = 0x12;
66
+ for (let i = 0; i < layout.MATCH_KEY_LENGTH; i++) {
67
+ p[layout.MATCH_KEY_OFFSET + i] = 0x20 + i;
55
68
  }
69
+ // The terms document this sample was created under, being index 1, the
70
+ // Model Terms for Marketing version 2. The byte is an index into a table
71
+ // in the specification and is not a version number, and an issuer writes
72
+ // it for every marketing identifier.
73
+ p[layout.PAYLOAD_LENGTH] = 1;
56
74
  return p;
57
75
  }
58
76
 
@@ -94,9 +112,11 @@ async function run () {
94
112
  console.log('51Did parsed from base64:');
95
113
  console.log(' Domain :', fodId.domain);
96
114
  console.log(' Type :', IdType.name(fodId.type));
97
- console.log(' Flags : 0x' + fodId.flags.toString(16));
115
+ console.log(' Usage :', Usage.name(fodId.usage));
116
+ console.log(' From cons.:', fodId.usageFromConsent);
98
117
  console.log(' LicenseId :', fodId.licenseId);
99
118
  console.log(' Match key :', Buffer.from(fodId.matchKey).toString('hex'));
119
+ console.log(' Terms :', fodId.terms);
100
120
  console.log(' Verifies :', await fodId.verify(publicPem));
101
121
 
102
122
  // Re-issue the same payload at a later time. The envelope differs and the
@@ -129,7 +149,7 @@ async function run () {
129
149
  ', status ' + result.status);
130
150
  }
131
151
  const shortPayload = await issue(
132
- keyPair.privateKey, payload.slice(0, FodId.PAYLOAD_LENGTH - 1), DATE);
152
+ keyPair.privateKey, payload.slice(0, layout.PAYLOAD_LENGTH - 1), DATE);
133
153
  const short = FodId.tryParse(shortPayload);
134
154
  console.log(' a payload one byte short -> ok ' + short.ok +
135
155
  ', status ' + short.status);
package/fodId.js CHANGED
@@ -21,7 +21,10 @@
21
21
  * ********************************************************************* */
22
22
 
23
23
  const owid = require('owid');
24
+ const layout = require('./internal/layout');
24
25
  const IdType = require('./idType');
26
+ const Usage = require('./usage');
27
+ const Terms = require('./internal/terms');
25
28
  const FodIdParseError = require('./fodIdParseError');
26
29
 
27
30
  /**
@@ -44,7 +47,15 @@ const ParseStatus = Object.freeze(Object.assign({}, owid.ParseStatus, {
44
47
  * the match key that type carries after the header (16 GUID bytes for
45
48
  * Random, 32 hash bytes for Probabilistic and HashedEmail).
46
49
  */
47
- INVALID_TYPE_PAYLOAD_LENGTH: 'InvalidTypePayloadLength'
50
+ INVALID_TYPE_PAYLOAD_LENGTH: 'InvalidTypePayloadLength',
51
+ /**
52
+ * Bits 4 and 5 of the flags byte name a payload layout version this
53
+ * package does not know, so no field is read. A later version exists
54
+ * precisely because a field moved, so reading the payload under the
55
+ * layout this package knows would answer with values that are wrong
56
+ * rather than absent.
57
+ */
58
+ UNSUPPORTED_PAYLOAD_VERSION: 'UnsupportedPayloadVersion'
48
59
  }));
49
60
 
50
61
  /**
@@ -82,42 +93,17 @@ const ParseStatus = Object.freeze(Object.assign({}, owid.ParseStatus, {
82
93
  * A FodId composes the OWID the OWID library read (holds it and delegates
83
94
  * the envelope fields to it). That OWID is frozen and hands out its byte
84
95
  * arrays as copies, so nothing a caller holds can change the identifier.
96
+ *
97
+ * Every field has a named accessor here, so nothing needs the payload
98
+ * bytes or their offsets. The byte layout itself is specified once for all
99
+ * languages, and that specification is the authority rather than this
100
+ * comment:
101
+ * https://github.com/51Degrees/specifications/blob/main/did-specification/identifier-layout.md
102
+ *
103
+ * What each 51Did package offers on top of that layout is specified at:
104
+ * https://github.com/51Degrees/specifications/blob/main/did-specification/package-surface.md
85
105
  */
86
106
  class FodId {
87
- static FLAGS_OFFSET = 0;
88
- static LICENSE_ID_OFFSET = 1;
89
- static LICENSE_ID_LENGTH = 4;
90
- /** Byte offset of the match key field within the payload. */
91
- static MATCH_KEY_OFFSET = 5;
92
- /**
93
- * Byte length of the match key field for Probabilistic and HashedEmail
94
- * identifiers, being a SHA-256.
95
- */
96
- static MATCH_KEY_LENGTH = 32;
97
- /**
98
- * Deprecated alias for {@link FodId.MATCH_KEY_OFFSET}. The stable,
99
- * comparable part of a 51Did is now called the match key, mirroring the
100
- * Model Terms for Marketing vocabulary. This alias will be removed in a
101
- * future release.
102
- * @deprecated Renamed to MATCH_KEY_OFFSET. This alias will be removed in
103
- * a future release.
104
- */
105
- static HASH_OFFSET = FodId.MATCH_KEY_OFFSET;
106
- /**
107
- * Deprecated alias for {@link FodId.MATCH_KEY_LENGTH}. The stable,
108
- * comparable part of a 51Did is now called the match key, mirroring the
109
- * Model Terms for Marketing vocabulary. This alias will be removed in a
110
- * future release.
111
- * @deprecated Renamed to MATCH_KEY_LENGTH. This alias will be removed in
112
- * a future release.
113
- */
114
- static HASH_LENGTH = FodId.MATCH_KEY_LENGTH;
115
- static HEADER_LENGTH = FodId.MATCH_KEY_OFFSET;
116
- /** Byte length of the GUID match key carried by Random identifiers. */
117
- static GUID_LENGTH = 16;
118
- static RANDOM_PAYLOAD_LENGTH = 21;
119
- static PAYLOAD_LENGTH = FodId.MATCH_KEY_OFFSET + FodId.MATCH_KEY_LENGTH;
120
-
121
107
  /**
122
108
  * Why a read succeeded or failed, being the OWID library's statuses plus
123
109
  * `PAYLOAD_TOO_SHORT` and `INVALID_TYPE_PAYLOAD_LENGTH`. Frozen.
@@ -167,6 +153,8 @@ class FodId {
167
153
  this._licenseId = read.value._licenseId;
168
154
  /** @type {Uint8Array} this identifier's own copy of the match key bytes */
169
155
  this._matchKey = read.value._matchKey;
156
+ /** @type {number} the terms index, zero where the payload carries none */
157
+ this._termsIndex = read.value._termsIndex;
170
158
  }
171
159
 
172
160
  /**
@@ -303,16 +291,31 @@ class FodId {
303
291
  return new FodId(owidInstance);
304
292
  }
305
293
 
306
- /** @returns {number} the 1-byte usage flags bit-mask (0-255). */
307
- get flags () {
308
- return this._flags;
309
- }
310
-
311
294
  /** @returns {number} the IdType carried in bits 6-7 of the flags. */
312
295
  get type () {
313
296
  return IdType.fromFlags(this._flags);
314
297
  }
315
298
 
299
+ /**
300
+ * The Usage carried in bits 0-2 of the flags, as the highest usage
301
+ * granted. See Usage for why it is read that way.
302
+ * @returns {number} a Usage value
303
+ */
304
+ get usage () {
305
+ return Usage.fromFlags(this._flags);
306
+ }
307
+
308
+ /**
309
+ * Whether the usage was derived from an IAB consent string the caller
310
+ * sent, rather than stated by the caller directly. Bit 3 of the flags.
311
+ * Both are legitimate ways to arrive at a usage, and this says nothing
312
+ * about which usage it is.
313
+ * @returns {boolean}
314
+ */
315
+ get usageFromConsent () {
316
+ return (this._flags & 0b1000) !== 0;
317
+ }
318
+
316
319
  /**
317
320
  * The 4-byte little-endian field at offset 1 of the payload, as an
318
321
  * unsigned integer (0-4294967295).
@@ -342,16 +345,26 @@ class FodId {
342
345
  }
343
346
 
344
347
  /**
345
- * Deprecated alias for {@link FodId#matchKey}. The stable, comparable
346
- * part of a 51Did is now called the match key, mirroring the Model Terms
347
- * for Marketing vocabulary. This alias will be removed in a future
348
- * release.
349
- * @deprecated Renamed to matchKey. This alias will be removed in a future
350
- * release.
351
- * @returns {Uint8Array} the same bytes as {@link FodId#matchKey}
348
+ * The address of the terms document this 51Did was created under, read
349
+ * from the byte after the match key. The byte is an index into a table
350
+ * in the specification and this package turns the index into the
351
+ * address, so a caller never handles the byte. Nothing here fetches the
352
+ * address, because what to do with the document is the caller's
353
+ * decision.
354
+ *
355
+ * Null covers both an index of zero, which says the terms are not
356
+ * stated in the identifier, and an index added to the table after this
357
+ * package was released, which it cannot name. A caller cannot tell
358
+ * those two apart, which is deliberate, because both lead to the same
359
+ * place, being that the identifier does not say which terms it was
360
+ * created under and the answer has to come from somewhere else. No
361
+ * address is ever built from an index, since that would name a document
362
+ * nobody wrote.
363
+ * @returns {string|null} the address, or null where the identifier names
364
+ * no document this package knows, which is never an empty string
352
365
  */
353
- get hash () {
354
- return this.matchKey;
366
+ get terms () {
367
+ return Terms.url(Terms.fromIndex(this._termsIndex));
355
368
  }
356
369
 
357
370
  /** @returns {number} the OWID version. */
@@ -365,24 +378,13 @@ class FodId {
365
378
  }
366
379
 
367
380
  /**
368
- * @returns {number} the OWID date as minutes since 2020-01-01 UTC, as an
369
- * unsigned 32-bit number, the same value as {@link FodId#dateMinutes}.
370
- */
371
- get date () {
372
- return this._owid.date;
373
- }
374
-
375
- /**
376
- * The envelope's own date as the unsigned 32-bit count of minutes since
377
- * 2020-01-01T00:00:00Z, exactly as the wire carries it. This is the value
378
- * the OWID `public-key?date=` parameter takes, and the integer to use when
379
- * comparing creation times. The OWID library now reads the field unsigned
380
- * too, so {@link FodId#date} agrees with this getter. The getter is kept
381
- * because callers were told to use it, and it still forces the unsigned
382
- * reading should the field ever arrive signed.
381
+ * The envelope's own date, being the count of minutes since
382
+ * 2020-01-01T00:00:00Z exactly as the wire carries it, read as an
383
+ * unsigned 32-bit number. This is the integer to compare when asking
384
+ * which of two identifiers was issued first.
383
385
  * @returns {number} minutes since 2020-01-01T00:00:00Z
384
386
  */
385
- get dateMinutes () {
387
+ get date () {
386
388
  return this._owid.date >>> 0;
387
389
  }
388
390
 
@@ -453,45 +455,62 @@ class FodId {
453
455
  * Reads the 51Did fields out of an envelope payload, answering with a
454
456
  * status rather than throwing. This is the one walk of the payload, shared
455
457
  * by every surface that reads a 51Did. The type is read from the header and
456
- * decides the least the payload must hold after the header. Anything beyond
457
- * the match key is a creator context section whose lengths belong to the
458
- * cloud, so a longer payload is accepted whatever its length.
458
+ * decides the least the payload must hold after the header. The terms byte
459
+ * follows the match key, and anything beyond the terms byte is a creator
460
+ * context section whose lengths belong to the cloud, so a longer payload is
461
+ * accepted whatever its length.
459
462
  * @param {Uint8Array} payload the payload bytes
460
463
  * @returns {{status: string, flags?: number, licenseId?: number,
461
- * matchKey?: Uint8Array, length: number, required: number, type?: number}}
462
- * `status` PARSED with the fields, or a 51Did status with the length the
463
- * type needed
464
+ * matchKey?: Uint8Array, termsIndex?: number, length: number,
465
+ * required: number, type?: number, payloadVersion?: number}} `status`
466
+ * PARSED with the fields, or a 51Did status with the length the type
467
+ * needed, and the version found where that is what the payload was refused
468
+ * for
464
469
  */
465
470
  function unpack (payload) {
466
471
  const length = payload.length;
467
- if (length < FodId.HEADER_LENGTH) {
472
+ if (length < layout.HEADER_LENGTH) {
468
473
  return {
469
474
  status: ParseStatus.PAYLOAD_TOO_SHORT,
470
475
  length,
471
- required: FodId.HEADER_LENGTH
476
+ required: layout.HEADER_LENGTH
477
+ };
478
+ }
479
+ const flags = payload[layout.FLAGS_OFFSET];
480
+ // The version is read before any field, because a later version exists
481
+ // precisely because a field moved. Reading a payload of a version this
482
+ // package does not know under the layout it does know would answer with
483
+ // values that are wrong rather than absent, which is worse than
484
+ // refusing, and a version that nothing checks protects nothing.
485
+ const payloadVersion = (flags >> 4) & 0b11;
486
+ if (payloadVersion !== layout.SUPPORTED_PAYLOAD_VERSION) {
487
+ return {
488
+ status: ParseStatus.UNSUPPORTED_PAYLOAD_VERSION,
489
+ length,
490
+ required: layout.HEADER_LENGTH,
491
+ payloadVersion
472
492
  };
473
493
  }
474
- const flags = payload[FodId.FLAGS_OFFSET];
475
494
  // Little-endian unsigned 32-bit. `>>> 0` forces unsigned so the high bit
476
495
  // does not produce a negative number.
477
496
  const licenseId = (
478
- payload[FodId.LICENSE_ID_OFFSET] |
479
- (payload[FodId.LICENSE_ID_OFFSET + 1] << 8) |
480
- (payload[FodId.LICENSE_ID_OFFSET + 2] << 16) |
481
- (payload[FodId.LICENSE_ID_OFFSET + 3] << 24)
497
+ payload[layout.LICENSE_ID_OFFSET] |
498
+ (payload[layout.LICENSE_ID_OFFSET + 1] << 8) |
499
+ (payload[layout.LICENSE_ID_OFFSET + 2] << 16) |
500
+ (payload[layout.LICENSE_ID_OFFSET + 3] << 24)
482
501
  ) >>> 0;
483
502
  const type = IdType.fromFlags(flags);
484
503
  let matchKeyLength;
485
504
  if (type === IdType.RANDOM) {
486
- matchKeyLength = FodId.GUID_LENGTH;
505
+ matchKeyLength = layout.GUID_LENGTH;
487
506
  } else if (type === IdType.RESERVED) {
488
507
  // Not yet assigned, so read best-effort, whatever follows the header
489
508
  // is the match key.
490
- matchKeyLength = length - FodId.HEADER_LENGTH;
509
+ matchKeyLength = length - layout.HEADER_LENGTH;
491
510
  } else {
492
- matchKeyLength = FodId.MATCH_KEY_LENGTH;
511
+ matchKeyLength = layout.MATCH_KEY_LENGTH;
493
512
  }
494
- const required = FodId.HEADER_LENGTH + matchKeyLength;
513
+ const required = layout.HEADER_LENGTH + matchKeyLength;
495
514
  if (length < required) {
496
515
  return {
497
516
  status: ParseStatus.INVALID_TYPE_PAYLOAD_LENGTH,
@@ -500,13 +519,28 @@ function unpack (payload) {
500
519
  type
501
520
  };
502
521
  }
522
+ // The terms byte sits after the match key, so where it sits follows the
523
+ // match key length the type selects. A payload with no byte to read is a
524
+ // terms index of zero, which says the terms are not stated, so absence
525
+ // and zero are the same answer and neither has to be told from the
526
+ // other.
527
+ //
528
+ // A Reserved type cannot carry a terms byte this reader can find, because
529
+ // the match key length for that type is not defined and every byte after
530
+ // the header is therefore the match key. Such an identifier reads as a
531
+ // terms index of zero, which is correct and is not a missing case here.
532
+ const termsOffset = layout.MATCH_KEY_OFFSET + matchKeyLength;
533
+ const termsIndex = termsOffset + layout.TERMS_LENGTH <= length
534
+ ? payload[termsOffset]
535
+ : Terms.NOT_STATED;
503
536
  return {
504
537
  status: ParseStatus.PARSED,
505
538
  flags,
506
539
  licenseId,
507
540
  // slice() copies, so the stored match key is this identifier's own.
508
541
  matchKey: payload.slice(
509
- FodId.MATCH_KEY_OFFSET, FodId.MATCH_KEY_OFFSET + matchKeyLength),
542
+ layout.MATCH_KEY_OFFSET, layout.MATCH_KEY_OFFSET + matchKeyLength),
543
+ termsIndex,
510
544
  length,
511
545
  required
512
546
  };
@@ -537,6 +571,7 @@ function readEnvelope (read) {
537
571
  fodId._flags = unpacked.flags;
538
572
  fodId._licenseId = unpacked.licenseId;
539
573
  fodId._matchKey = unpacked.matchKey;
574
+ fodId._termsIndex = unpacked.termsIndex;
540
575
  return { ok: true, value: fodId, status: ParseStatus.PARSED };
541
576
  }
542
577
 
@@ -581,7 +616,7 @@ function valueOrThrow (read) {
581
616
  }
582
617
 
583
618
  /**
584
- * The exception for a failed read. The two 51Did payload statuses keep the
619
+ * The exception for a failed read. The three 51Did payload statuses keep the
585
620
  * RangeError this package has always thrown for them, and every OWID status
586
621
  * is a FodIdParseError carrying the status. Each error carries `status` so
587
622
  * the reason can be acted on without reading the message.
@@ -599,6 +634,10 @@ function errorFor (read) {
599
634
  `51Did payload for the ${IdType.name(read.detail.type)} type must be ` +
600
635
  `at least ${read.detail.required} bytes, and ${read.detail.length} ` +
601
636
  'were given.');
637
+ } else if (read.status === ParseStatus.UNSUPPORTED_PAYLOAD_VERSION) {
638
+ error = new RangeError(
639
+ `51Did payload version ${read.detail.payloadVersion} is not one this ` +
640
+ 'package can read.');
602
641
  } else {
603
642
  return new FodIdParseError(read.status);
604
643
  }
@@ -26,7 +26,7 @@
26
26
  * constructor) when the OWID library refused the envelope. The status names
27
27
  * the reason in the same vocabulary the non-throwing surfaces report, so a
28
28
  * caller catching this can act on the reason without reading the message.
29
- * The two 51Did payload statuses are thrown as RangeError instead, as this
29
+ * The three 51Did payload statuses are thrown as RangeError instead, as this
30
30
  * package has always thrown them, and that RangeError carries `status` too.
31
31
  */
32
32
  class FodIdParseError extends Error {
package/index.js CHANGED
@@ -23,6 +23,7 @@
23
23
  const FodId = require('./fodId');
24
24
  const FodIdParseError = require('./fodIdParseError');
25
25
  const IdType = require('./idType');
26
+ const Usage = require('./usage');
26
27
  const {
27
28
  DidClient,
28
29
  RedeemResult,
@@ -39,6 +40,7 @@ module.exports = {
39
40
  FodId,
40
41
  FodIdParseError,
41
42
  IdType,
43
+ Usage,
42
44
  DidClient,
43
45
  RedeemResult,
44
46
  ContextResult,
@@ -0,0 +1,77 @@
1
+ /* *********************************************************************
2
+ * This Original Work is copyright of 51 Degrees Mobile Experts Limited.
3
+ * Copyright 2026 51 Degrees Mobile Experts Limited, Davidson House,
4
+ * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
5
+ *
6
+ * This Original Work is licensed under the European Union Public Licence
7
+ * (EUPL) v.1.2 and is subject to its terms as set out below.
8
+ *
9
+ * If a copy of the EUPL was not distributed with this file, You can obtain
10
+ * one at https://opensource.org/licenses/EUPL-1.2.
11
+ *
12
+ * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
13
+ * amended by the European Commission) shall be deemed incompatible for
14
+ * the purposes of the Work and the provisions of the compatibility
15
+ * clause in Article 5 of the EUPL shall not apply.
16
+ *
17
+ * If using the Work as, or as part of, a network application, by
18
+ * including the attribution notice(s) required under Article 5 of the EUPL
19
+ * in the end user terms of the application under an appropriate heading,
20
+ * such notice(s) shall fulfill the requirements of that article.
21
+ * ********************************************************************* */
22
+
23
+ /**
24
+ * The byte layout of a 51Did payload. Internal to this package, because the
25
+ * only reason to read the payload by hand is to read a field that already
26
+ * has a name on FodId, and reading the flags byte by hand is how the usage
27
+ * comes out backwards. The usage bits are cumulative, so a caller masking
28
+ * for the non-marketing bit alone reads every marketing identifier as
29
+ * non-marketing. Use the FodId accessors instead.
30
+ *
31
+ * The layout is described once for every language in the 51Did
32
+ * specification, which is the authority for these numbers:
33
+ * https://github.com/51Degrees/specifications/blob/main/did-specification/identifier-layout.md
34
+ *
35
+ * What each package offers on top of that layout is described in:
36
+ * https://github.com/51Degrees/specifications/blob/main/did-specification/package-surface.md
37
+ */
38
+ module.exports = Object.freeze({
39
+ /** Byte offset of the flags byte within the payload. */
40
+ FLAGS_OFFSET: 0,
41
+ /** Byte offset of the licence id field within the payload. */
42
+ LICENSE_ID_OFFSET: 1,
43
+ /** Byte length of the licence id field. */
44
+ LICENSE_ID_LENGTH: 4,
45
+ /** Byte offset of the match key field within the payload. */
46
+ MATCH_KEY_OFFSET: 5,
47
+ /**
48
+ * Byte length of the match key field for Probabilistic and HashedEmail
49
+ * identifiers, being a SHA-256.
50
+ */
51
+ MATCH_KEY_LENGTH: 32,
52
+ /**
53
+ * Byte length of the terms field, which follows the match key. Its
54
+ * offset is not a constant here, because the match key length depends on
55
+ * the identifier type, so the offset is worked out from the type. A
56
+ * payload that ends at the match key carries no terms byte and reads as
57
+ * a terms index of zero, so the least payload lengths below do not
58
+ * include it.
59
+ */
60
+ TERMS_LENGTH: 1,
61
+ /** Byte length of the flags and licence id fields together. */
62
+ HEADER_LENGTH: 5,
63
+ /** Byte length of the GUID match key carried by Random identifiers. */
64
+ GUID_LENGTH: 16,
65
+ /**
66
+ * The payload layout version this package reads, carried in bits 4 and
67
+ * 5 of the flags byte. Any other version is refused rather than read
68
+ * under this layout.
69
+ */
70
+ SUPPORTED_PAYLOAD_VERSION: 0,
71
+ /** Least payload length for a Random identifier. */
72
+ RANDOM_PAYLOAD_LENGTH: 21,
73
+ /**
74
+ * Least payload length for a Probabilistic or HashedEmail identifier.
75
+ */
76
+ PAYLOAD_LENGTH: 37
77
+ });