gdc-common-utils-ts 2.3.27 → 2.3.29

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.
@@ -21,7 +21,6 @@ export function setRelatedPersonIdentifier(claims, value) {
21
21
  return {
22
22
  ...claims,
23
23
  [RelatedPersonClaim.IdentifierValue]: normalizedValue,
24
- [RelatedPersonClaim.Identifier]: normalizedValue,
25
24
  };
26
25
  }
27
26
  /**
@@ -34,6 +34,6 @@ export declare const EXAMPLE_APPOINTMENT_CLAIMS: {
34
34
  readonly "Appointment.description": "Medication follow-up visit.";
35
35
  readonly "Appointment.start": "2026-07-01T09:00:00Z";
36
36
  readonly "Appointment.end": "2026-07-01T09:30:00Z";
37
- readonly "Appointment.participant.actor": `${string},${string}`;
38
- readonly "Appointment.participant.status": AppointmentParticipantStatus.Accepted;
37
+ readonly "Appointment.actor": `${string},${string}`;
38
+ readonly "Appointment.part-status": AppointmentParticipantStatus.Accepted;
39
39
  };
@@ -28,7 +28,7 @@ export declare const EXAMPLE_INDIVIDUAL_MEMBER_IDENTITY: Readonly<{
28
28
  export declare const EXAMPLE_RELATED_PERSON_DISABLE_INPUT: {
29
29
  readonly memberClaims: {
30
30
  readonly '@context': "org.hl7.fhir.api";
31
- readonly "RelatedPerson.identifier.value": "rel-001";
31
+ readonly "RelatedPerson.identifier": "rel-001";
32
32
  readonly "RelatedPerson.patient": string;
33
33
  readonly "RelatedPerson.telecom": "mailto:parent.guardian@example.org";
34
34
  readonly "RelatedPerson.relationship": "v3-RoleCode|RESPRSN";
@@ -64,7 +64,7 @@ export declare const EXAMPLE_RELATED_PERSON_DISABLE_BUNDLE_ENTRY: {
64
64
  readonly meta: {
65
65
  readonly claims: {
66
66
  readonly '@context': "org.hl7.fhir.api";
67
- readonly "RelatedPerson.identifier.value": "rel-001";
67
+ readonly "RelatedPerson.identifier": "rel-001";
68
68
  readonly "RelatedPerson.patient": string;
69
69
  readonly "RelatedPerson.telecom": "mailto:parent.guardian@example.org";
70
70
  readonly "RelatedPerson.relationship": "v3-RoleCode|RESPRSN";
@@ -98,7 +98,7 @@ export declare const EXAMPLE_RELATED_PERSON_DISABLE_BUNDLE_PAYLOAD: {
98
98
  readonly meta: {
99
99
  readonly claims: {
100
100
  readonly '@context': "org.hl7.fhir.api";
101
- readonly "RelatedPerson.identifier.value": "rel-001";
101
+ readonly "RelatedPerson.identifier": "rel-001";
102
102
  readonly "RelatedPerson.patient": string;
103
103
  readonly "RelatedPerson.telecom": "mailto:parent.guardian@example.org";
104
104
  readonly "RelatedPerson.relationship": "v3-RoleCode|RESPRSN";
@@ -139,7 +139,7 @@ export declare const EXAMPLE_RELATED_PERSON_PURGE_BUNDLE_ENTRY: {
139
139
  readonly meta: {
140
140
  readonly claims: {
141
141
  readonly '@context': "org.hl7.fhir.api";
142
- readonly "RelatedPerson.identifier.value": "rel-001";
142
+ readonly "RelatedPerson.identifier": "rel-001";
143
143
  readonly "RelatedPerson.patient": string;
144
144
  readonly "RelatedPerson.telecom": "mailto:parent.guardian@example.org";
145
145
  readonly "RelatedPerson.relationship": "v3-RoleCode|RESPRSN";
@@ -171,7 +171,7 @@ export declare const EXAMPLE_RELATED_PERSON_PURGE_BUNDLE_PAYLOAD: {
171
171
  readonly meta: {
172
172
  readonly claims: {
173
173
  readonly '@context': "org.hl7.fhir.api";
174
- readonly "RelatedPerson.identifier.value": "rel-001";
174
+ readonly "RelatedPerson.identifier": "rel-001";
175
175
  readonly "RelatedPerson.patient": string;
176
176
  readonly "RelatedPerson.telecom": "mailto:parent.guardian@example.org";
177
177
  readonly "RelatedPerson.relationship": "v3-RoleCode|RESPRSN";
@@ -292,7 +292,7 @@ export declare const EXAMPLE_RELATED_PERSON_LIST_RECORD_ACTIVE: {
292
292
  readonly meta: {
293
293
  readonly claims: {
294
294
  readonly '@context': "org.hl7.fhir.api";
295
- readonly "RelatedPerson.identifier.value": "rel-001";
295
+ readonly "RelatedPerson.identifier": "rel-001";
296
296
  readonly "RelatedPerson.patient": string;
297
297
  readonly "RelatedPerson.telecom": "mailto:parent.guardian@example.org";
298
298
  readonly "RelatedPerson.relationship": "v3-RoleCode|RESPRSN";
@@ -313,7 +313,7 @@ export declare const EXAMPLE_RELATED_PERSON_LIST_RECORD_INACTIVE: {
313
313
  readonly meta: {
314
314
  readonly claims: {
315
315
  readonly '@context': "org.hl7.fhir.api";
316
- readonly "RelatedPerson.identifier.value": "urn:uuid:related-person-002";
316
+ readonly "RelatedPerson.identifier": "urn:uuid:related-person-002";
317
317
  readonly "RelatedPerson.patient": string;
318
318
  readonly "RelatedPerson.telecom": "mailto:caregiver.two@example.org";
319
319
  readonly "RelatedPerson.relationship": "http://terminology.hl7.org/CodeSystem/v3-RoleCode|NMTH";
@@ -336,7 +336,7 @@ export declare const EXAMPLE_RELATED_PERSON_LIST_RESPONSE_BODY: {
336
336
  readonly meta: {
337
337
  readonly claims: {
338
338
  readonly '@context': "org.hl7.fhir.api";
339
- readonly "RelatedPerson.identifier.value": "rel-001";
339
+ readonly "RelatedPerson.identifier": "rel-001";
340
340
  readonly "RelatedPerson.patient": string;
341
341
  readonly "RelatedPerson.telecom": "mailto:parent.guardian@example.org";
342
342
  readonly "RelatedPerson.relationship": "v3-RoleCode|RESPRSN";
@@ -356,7 +356,7 @@ export declare const EXAMPLE_RELATED_PERSON_LIST_RESPONSE_BODY: {
356
356
  readonly meta: {
357
357
  readonly claims: {
358
358
  readonly '@context': "org.hl7.fhir.api";
359
- readonly "RelatedPerson.identifier.value": "urn:uuid:related-person-002";
359
+ readonly "RelatedPerson.identifier": "urn:uuid:related-person-002";
360
360
  readonly "RelatedPerson.patient": string;
361
361
  readonly "RelatedPerson.telecom": "mailto:caregiver.two@example.org";
362
362
  readonly "RelatedPerson.relationship": "http://terminology.hl7.org/CodeSystem/v3-RoleCode|NMTH";
@@ -5,7 +5,7 @@ export declare enum ClaimConsent {
5
5
  'category' = "Consent.category",
6
6
  'categoryText' = "Consent.category-text",
7
7
  'categoryDisplay' = "Consent.category-display",
8
- 'resourceType' = "Consent.resourceType",
8
+ 'resourceType' = "Consent.resource-type",
9
9
  'subject' = "Consent.subject",
10
10
  'patient' = "Consent.patient",
11
11
  'actorIdentifier' = "Consent.actor-identifier",
@@ -7,7 +7,7 @@ export var ClaimConsent;
7
7
  ClaimConsent["category"] = "Consent.category";
8
8
  ClaimConsent["categoryText"] = "Consent.category-text";
9
9
  ClaimConsent["categoryDisplay"] = "Consent.category-display";
10
- ClaimConsent["resourceType"] = "Consent.resourceType";
10
+ ClaimConsent["resourceType"] = "Consent.resource-type";
11
11
  ClaimConsent["subject"] = "Consent.subject";
12
12
  ClaimConsent["patient"] = "Consent.patient";
13
13
  ClaimConsent["actorIdentifier"] = "Consent.actor-identifier";
@@ -45,7 +45,7 @@ export declare const AllowedIndexableClaims: {
45
45
  readonly General: readonly ["MedicationStatement.identifier", "MedicationStatement.subject", "MedicationStatement.patient", "MedicationStatement.status", "MedicationStatement.category", "MedicationStatement.effective", "MedicationStatement.code", "MedicationStatement.medication", "MedicationStatement.part-of", "MedicationStatement.source", "MedicationStatement.medication-identifier", "MedicationStatement.medication-serial-number", "MedicationStatement.medication-expiration-date"];
46
46
  };
47
47
  readonly DocumentReference: {
48
- readonly General: readonly ["DocumentReference.identifier", "DocumentReference.subject", "DocumentReference.author", "DocumentReference.attester", "DocumentReference.basedOn", "DocumentReference.category", "DocumentReference.contenthash", "DocumentReference.contenttype", "DocumentReference.context", "DocumentReference.creation", "DocumentReference.date", "DocumentReference.event-code", "DocumentReference.event-reference", "DocumentReference.format-uri", "DocumentReference.language", "DocumentReference.location", "DocumentReference.modality", "DocumentReference.relatesto", "DocumentReference.relation", "DocumentReference.type"];
48
+ readonly General: readonly ["DocumentReference.identifier", "DocumentReference.subject", "DocumentReference.author", "DocumentReference.attester", "DocumentReference.based-on", "DocumentReference.category", "DocumentReference.contenthash", "DocumentReference.contenttype", "DocumentReference.context", "DocumentReference.creation", "DocumentReference.date", "DocumentReference.event-code", "DocumentReference.event-reference", "DocumentReference.format-uri", "DocumentReference.language", "DocumentReference.location", "DocumentReference.modality", "DocumentReference.relatesto", "DocumentReference.relation", "DocumentReference.type"];
49
49
  };
50
50
  };
51
51
  export type AllowedIndexableClaimsKey = keyof typeof AllowedIndexableClaims;
@@ -181,9 +181,9 @@ export declare const AppointmentClaim: {
181
181
  readonly NoteText: "Appointment.note-text";
182
182
  readonly PatientInstruction: "Appointment.patient-instruction";
183
183
  readonly BasedOn: "Appointment.based-on";
184
- readonly ParticipantActor: "Appointment.participant.actor";
185
- readonly ParticipantStatus: "Appointment.participant.status";
186
- readonly ParticipantType: "Appointment.participant.type";
184
+ readonly ParticipantActor: "Appointment.actor";
185
+ readonly ParticipantStatus: "Appointment.part-status";
186
+ readonly ParticipantType: "Appointment.participant-type";
187
187
  };
188
188
  export type AppointmentClaimKey = typeof AppointmentClaim[keyof typeof AppointmentClaim];
189
189
  /**
@@ -170,9 +170,9 @@ export const AppointmentClaim = {
170
170
  NoteText: 'Appointment.note-text',
171
171
  PatientInstruction: 'Appointment.patient-instruction',
172
172
  BasedOn: 'Appointment.based-on',
173
- ParticipantActor: 'Appointment.participant.actor',
174
- ParticipantStatus: 'Appointment.participant.status',
175
- ParticipantType: 'Appointment.participant.type',
173
+ ParticipantActor: 'Appointment.actor',
174
+ ParticipantStatus: 'Appointment.part-status',
175
+ ParticipantType: 'Appointment.participant-type',
176
176
  };
177
177
  // src/models/fhir/Appointment.values.ts
178
178
  /**
@@ -5,7 +5,7 @@ export declare const ClinicalImpressionClaim: {
5
5
  readonly Description: "ClinicalImpression.description";
6
6
  readonly Subject: "ClinicalImpression.subject";
7
7
  readonly Encounter: "ClinicalImpression.encounter";
8
- readonly EffectiveDateTime: "ClinicalImpression.effectiveDateTime";
8
+ readonly EffectiveDateTime: "ClinicalImpression.effective-datetime";
9
9
  readonly Date: "ClinicalImpression.date";
10
10
  readonly Assessor: "ClinicalImpression.assessor";
11
11
  readonly Problem: "ClinicalImpression.problem";
@@ -6,7 +6,7 @@ export const ClinicalImpressionClaim = {
6
6
  Description: 'ClinicalImpression.description',
7
7
  Subject: 'ClinicalImpression.subject',
8
8
  Encounter: 'ClinicalImpression.encounter',
9
- EffectiveDateTime: 'ClinicalImpression.effectiveDateTime',
9
+ EffectiveDateTime: 'ClinicalImpression.effective-datetime',
10
10
  Date: 'ClinicalImpression.date',
11
11
  Assessor: 'ClinicalImpression.assessor',
12
12
  Problem: 'ClinicalImpression.problem',
@@ -2,7 +2,7 @@ import type { ClaimSpec } from './types';
2
2
  export declare const DocumentReferenceClaim: {
3
3
  readonly Attester: "DocumentReference.attester";
4
4
  readonly Author: "DocumentReference.author";
5
- readonly BasedOn: "DocumentReference.basedOn";
5
+ readonly BasedOn: "DocumentReference.based-on";
6
6
  readonly Category: "DocumentReference.category";
7
7
  readonly ContentData: "DocumentReference.contentdata";
8
8
  readonly ContentHash: "DocumentReference.contenthash";
@@ -4,7 +4,7 @@
4
4
  export const DocumentReferenceClaim = {
5
5
  Attester: 'DocumentReference.attester',
6
6
  Author: 'DocumentReference.author',
7
- BasedOn: 'DocumentReference.basedOn',
7
+ BasedOn: 'DocumentReference.based-on',
8
8
  Category: 'DocumentReference.category',
9
9
  ContentData: 'DocumentReference.contentdata',
10
10
  ContentHash: 'DocumentReference.contenthash',
@@ -1,8 +1,9 @@
1
1
  import type { ClaimSpec } from './types';
2
2
  export declare const RelatedPersonClaim: {
3
- /** @deprecated Prefer `RelatedPerson.identifier.value` for canonical exactness. */
3
+ /** Canonical FHIR API identifier search parameter. */
4
4
  readonly Identifier: "RelatedPerson.identifier";
5
- readonly IdentifierValue: "RelatedPerson.identifier.value";
5
+ /** @deprecated Use `Identifier`; retained as a source-compatible property alias. */
6
+ readonly IdentifierValue: "RelatedPerson.identifier";
6
7
  readonly Active: "RelatedPerson.active";
7
8
  readonly Patient: "RelatedPerson.patient";
8
9
  readonly Relationship: "RelatedPerson.relationship";
@@ -1,9 +1,10 @@
1
1
  // Copyright 2026 Conéctate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
2
2
  // File: src/models/interoperable-claims/related-person-claims.ts
3
3
  export const RelatedPersonClaim = {
4
- /** @deprecated Prefer `RelatedPerson.identifier.value` for canonical exactness. */
4
+ /** Canonical FHIR API identifier search parameter. */
5
5
  Identifier: 'RelatedPerson.identifier',
6
- IdentifierValue: 'RelatedPerson.identifier.value',
6
+ /** @deprecated Use `Identifier`; retained as a source-compatible property alias. */
7
+ IdentifierValue: 'RelatedPerson.identifier',
7
8
  Active: 'RelatedPerson.active',
8
9
  Patient: 'RelatedPerson.patient',
9
10
  Relationship: 'RelatedPerson.relationship',
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Normalizes the extensible `<service-category>:<membership-profile>` contract
3
+ * used by dataspace onboarding.
4
+ *
5
+ * This intentionally does not embed a product-specific sector allowlist.
6
+ * Governance deployments may supply `allowedScopes`; otherwise syntactically
7
+ * valid future categories remain interoperable.
8
+ */
9
+ export declare function normalizeDataspaceMembershipScope(rawScope: string, options?: {
10
+ defaultProfile?: string;
11
+ allowedScopes?: readonly string[];
12
+ }): string;
13
+ /** Parses and normalizes a comma-separated deployment allowlist. */
14
+ export declare function parseDataspaceMembershipScopeCsv(rawScopes: string | undefined): string[];
@@ -0,0 +1,38 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ const SCOPE_SEGMENT_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
3
+ /**
4
+ * Normalizes the extensible `<service-category>:<membership-profile>` contract
5
+ * used by dataspace onboarding.
6
+ *
7
+ * This intentionally does not embed a product-specific sector allowlist.
8
+ * Governance deployments may supply `allowedScopes`; otherwise syntactically
9
+ * valid future categories remain interoperable.
10
+ */
11
+ export function normalizeDataspaceMembershipScope(rawScope, options = {}) {
12
+ const defaultProfile = (options.defaultProfile || 'provider').trim().toLowerCase();
13
+ let normalized = String(rawScope || '').trim().toLowerCase();
14
+ if (!normalized)
15
+ throw new Error('Dataspace membership scope cannot be empty.');
16
+ if (!normalized.includes(':'))
17
+ normalized = `${normalized}:${defaultProfile}`;
18
+ const parts = normalized.split(':');
19
+ if (parts.length !== 2 || parts.some((part) => !SCOPE_SEGMENT_PATTERN.test(part))) {
20
+ throw new Error(`Invalid dataspace membership scope "${rawScope}". Expected <service-category>:<membership-profile>.`);
21
+ }
22
+ if (options.allowedScopes?.length) {
23
+ const allowed = new Set(options.allowedScopes.map((scope) => scope.trim().toLowerCase()).filter(Boolean));
24
+ if (!allowed.has(normalized)) {
25
+ throw new Error(`Dataspace membership scope "${normalized}" is not allowed by deployment policy.`);
26
+ }
27
+ }
28
+ return normalized;
29
+ }
30
+ /** Parses and normalizes a comma-separated deployment allowlist. */
31
+ export function parseDataspaceMembershipScopeCsv(rawScopes) {
32
+ if (!rawScopes?.trim())
33
+ return [];
34
+ return Array.from(new Set(rawScopes
35
+ .split(',')
36
+ .map((scope) => normalizeDataspaceMembershipScope(scope))
37
+ .filter(Boolean)));
38
+ }
@@ -0,0 +1,51 @@
1
+ export type DeterministicSeedEcAlgorithm = 'ES384' | 'ES256K';
2
+ export type DeterministicSeedEcCurve = 'P-384' | 'secp256k1';
3
+ export type ScryptDerivationProfile = {
4
+ profile: string;
5
+ log2N: number;
6
+ N: number;
7
+ r: number;
8
+ p: number;
9
+ dkLen: number;
10
+ };
11
+ export type DeterministicEcPemKeyMaterial = {
12
+ privateKeyPem: string;
13
+ publicJwk: {
14
+ kty: 'EC';
15
+ crv: DeterministicSeedEcCurve;
16
+ x: string;
17
+ y: string;
18
+ };
19
+ kidRfc7638: string;
20
+ };
21
+ /** Parses the portable `<log2N>:<r>:<p>:<dkLen>` scrypt profile. */
22
+ export declare function parseScryptDerivationProfile(rawProfile: string | undefined, fallbackProfile?: string): ScryptDerivationProfile;
23
+ /** Decodes a seed salt as hexadecimal when unambiguous, otherwise as UTF-8. */
24
+ export declare function parseDeterministicSeedSalt(rawSalt: string | undefined, fallbackSalt: string): {
25
+ salt: Uint8Array;
26
+ raw: string;
27
+ encoding: 'hex' | 'utf8';
28
+ };
29
+ /**
30
+ * Derives the compatibility EC PEM/JWK pair used by ICA seed profiles.
31
+ *
32
+ * The byte-for-byte algorithm is deliberately stable: changing its SHA-512
33
+ * expansion would silently change every key regenerated from an existing
34
+ * private seed.
35
+ */
36
+ export declare function deriveDeterministicEcPemKeyPair(seed: string, curve: DeterministicSeedEcCurve): DeterministicEcPemKeyMaterial;
37
+ /**
38
+ * Applies scrypt and explicit domain separation before deterministic EC key
39
+ * expansion. Existing `profile + salt + separationTag` values are a durable
40
+ * key identity contract and must remain recorded with encrypted seed custody.
41
+ */
42
+ export declare function deriveScryptSeparatedEcPemKeyPair(input: {
43
+ passphrase: string;
44
+ salt: Uint8Array;
45
+ profile: ScryptDerivationProfile;
46
+ alg: DeterministicSeedEcAlgorithm;
47
+ separationTag: string;
48
+ }): DeterministicEcPemKeyMaterial & {
49
+ deterministicSeed: string;
50
+ separatedSeedHex: string;
51
+ };
@@ -0,0 +1,88 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { createECDH, createHash, createPrivateKey, scryptSync } from 'node:crypto';
3
+ import { computeRfc7638JwkThumbprint } from './jwk-thumbprint.js';
4
+ /** Parses the portable `<log2N>:<r>:<p>:<dkLen>` scrypt profile. */
5
+ export function parseScryptDerivationProfile(rawProfile, fallbackProfile = '17:8:1:48') {
6
+ const profile = (rawProfile || fallbackProfile).trim();
7
+ const values = profile.split(':').map((value) => Number.parseInt(value, 10));
8
+ if (values.length !== 4 || values.some((value) => !Number.isFinite(value) || value <= 0)) {
9
+ throw new Error('Invalid scrypt profile. Expected <log2N>:<r>:<p>:<dkLen>, e.g. 17:8:1:48.');
10
+ }
11
+ const [log2N, r, p, dkLen] = values;
12
+ if (log2N < 10 || log2N > 24) {
13
+ throw new Error('scrypt log2N must be between 10 and 24.');
14
+ }
15
+ return { profile, log2N, N: 2 ** log2N, r, p, dkLen };
16
+ }
17
+ /** Decodes a seed salt as hexadecimal when unambiguous, otherwise as UTF-8. */
18
+ export function parseDeterministicSeedSalt(rawSalt, fallbackSalt) {
19
+ const value = (rawSalt || '').trim() || fallbackSalt;
20
+ const isHex = /^[0-9a-fA-F]+$/.test(value) && value.length % 2 === 0;
21
+ return {
22
+ salt: Buffer.from(value, isHex ? 'hex' : 'utf8'),
23
+ raw: isHex ? value.toLowerCase() : value,
24
+ encoding: isHex ? 'hex' : 'utf8',
25
+ };
26
+ }
27
+ /**
28
+ * Derives the compatibility EC PEM/JWK pair used by ICA seed profiles.
29
+ *
30
+ * The byte-for-byte algorithm is deliberately stable: changing its SHA-512
31
+ * expansion would silently change every key regenerated from an existing
32
+ * private seed.
33
+ */
34
+ export function deriveDeterministicEcPemKeyPair(seed, curve) {
35
+ const nodeCurve = curve === 'P-384' ? 'secp384r1' : 'secp256k1';
36
+ const keyLength = curve === 'P-384' ? 48 : 32;
37
+ for (let counter = 0; counter < 256; counter += 1) {
38
+ const material = createHash('sha512').update(`${seed}:${curve}:${counter}`).digest();
39
+ const candidate = material.subarray(0, keyLength);
40
+ try {
41
+ const ecdh = createECDH(nodeCurve);
42
+ ecdh.setPrivateKey(candidate);
43
+ const privateBytes = ecdh.getPrivateKey();
44
+ const publicBytes = ecdh.getPublicKey(undefined, 'uncompressed');
45
+ const x = publicBytes.subarray(1, 1 + keyLength).toString('base64url');
46
+ const y = publicBytes.subarray(1 + keyLength, 1 + (2 * keyLength)).toString('base64url');
47
+ const publicJwk = { kty: 'EC', crv: curve, x, y };
48
+ const privateKey = createPrivateKey({
49
+ key: { ...publicJwk, d: privateBytes.toString('base64url') },
50
+ format: 'jwk',
51
+ });
52
+ return {
53
+ privateKeyPem: privateKey.export({ type: 'pkcs8', format: 'pem' }).toString(),
54
+ publicJwk,
55
+ kidRfc7638: computeRfc7638JwkThumbprint(publicJwk),
56
+ };
57
+ }
58
+ catch {
59
+ // Retry the next deterministic candidate until the curve accepts it.
60
+ }
61
+ }
62
+ throw new Error(`Unable to derive deterministic ${curve} key from seed.`);
63
+ }
64
+ /**
65
+ * Applies scrypt and explicit domain separation before deterministic EC key
66
+ * expansion. Existing `profile + salt + separationTag` values are a durable
67
+ * key identity contract and must remain recorded with encrypted seed custody.
68
+ */
69
+ export function deriveScryptSeparatedEcPemKeyPair(input) {
70
+ const derivedSeed = scryptSync(input.passphrase, input.salt, input.profile.dkLen, {
71
+ N: input.profile.N,
72
+ r: input.profile.r,
73
+ p: input.profile.p,
74
+ maxmem: 128 * input.profile.N * input.profile.r * 2,
75
+ });
76
+ const separatedSeedHex = createHash('sha256')
77
+ .update(derivedSeed)
78
+ .update(Buffer.from('|'))
79
+ .update(Buffer.from(input.separationTag, 'utf8'))
80
+ .digest('hex');
81
+ const deterministicSeed = `scrypt:${input.profile.profile}:${separatedSeedHex}`;
82
+ const curve = input.alg === 'ES384' ? 'P-384' : 'secp256k1';
83
+ return {
84
+ ...deriveDeterministicEcPemKeyPair(deterministicSeed, curve),
85
+ deterministicSeed,
86
+ separatedSeedHex,
87
+ };
88
+ }
@@ -21,6 +21,8 @@ export * from './dataspace-discovery';
21
21
  export * from './dataspace-discovery-defaults';
22
22
  export * from './dataspace-protocol';
23
23
  export * from './deterministic-jwk';
24
+ export * from './deterministic-seed-key';
25
+ export * from './dataspace-membership-scope';
24
26
  export * from './employee';
25
27
  export * from './evidence-blockchain-references';
26
28
  export * from './didcomm';
@@ -21,6 +21,8 @@ export * from './dataspace-discovery.js';
21
21
  export * from './dataspace-discovery-defaults.js';
22
22
  export * from './dataspace-protocol.js';
23
23
  export * from './deterministic-jwk.js';
24
+ export * from './deterministic-seed-key.js';
25
+ export * from './dataspace-membership-scope.js';
24
26
  export * from './employee.js';
25
27
  export * from './evidence-blockchain-references.js';
26
28
  export * from './didcomm.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "2.3.27",
3
+ "version": "2.3.29",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },