From c4f96115afda9f88a89524ef6a9e3e5099cde8a5 Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:02:27 +0200 Subject: [PATCH 1/9] pseudo for long texts --- .../extensions/BeExtKeyPseudonymization.fsh | 18 ++++ .../fsh/extensions/BeExtPseudonymization.fsh | 9 +- input/fsh/instances/patient1 copy.fsh | 98 +++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 input/fsh/extensions/BeExtKeyPseudonymization.fsh create mode 100644 input/fsh/instances/patient1 copy.fsh diff --git a/input/fsh/extensions/BeExtKeyPseudonymization.fsh b/input/fsh/extensions/BeExtKeyPseudonymization.fsh new file mode 100644 index 0000000..ec1d2d3 --- /dev/null +++ b/input/fsh/extensions/BeExtKeyPseudonymization.fsh @@ -0,0 +1,18 @@ +Extension: BeExtKeyPseudonymization +Id: be-ext-key-pseudonymization +Title: "Key Pseudonymization Extension" +Description: "This holds a pseudonymized key that can be used for all encrypted fields in the resource (long text pseudonymisation)" +* ^context.type = #element +* ^context.expression = "Resource" +* . ^short = "Pseudonymization key (See Blinded Pseudonymization Cookbook, Annex: Processing of input data exceeding 32 +bytes)" +* id 1..1 MS +* id ^short = "kid (also available in JWE)" +* extension contains key 1..1 MS and jwe 0..1 MS +* extension[key].value[x] only string +* extension[key].valueString 1..1 MS +* extension[key].valueString ^short = "pseudonymized key" +* extension[key].value[x].extension contains BeExtPseudonymization named pseudonymization 1..1 +* extension[jwe].value[x] only string +* extension[jwe].valueString 0..1 MS +* extension[jwe].valueString ^short = "information about the key in a compact JWE string" \ No newline at end of file diff --git a/input/fsh/extensions/BeExtPseudonymization.fsh b/input/fsh/extensions/BeExtPseudonymization.fsh index fb098f5..155e04c 100644 --- a/input/fsh/extensions/BeExtPseudonymization.fsh +++ b/input/fsh/extensions/BeExtPseudonymization.fsh @@ -5,6 +5,11 @@ Description: "This is a marker interface. If the field is pseudonymized, the str * ^context.type = #element * ^context.expression = "Element" * . ^short = "Pseudonymization data" -* extension contains marker 1..1 MS +* extension contains marker 1..1 MS and + kid 0..1 MS * extension[marker].value[x] only boolean -* extension[marker].valueBoolean = true \ No newline at end of file +* extension[marker].valueBoolean = true +* extension[kid].value[x] only string +* extension[kid].valueString 0..1 MS +* extension[kid].valueString ^short = "kid, only in case of Blinded Pseudonymization Cookbook, Annex: Processing of input data exceeding 32 +bytes" \ No newline at end of file diff --git a/input/fsh/instances/patient1 copy.fsh b/input/fsh/instances/patient1 copy.fsh new file mode 100644 index 0000000..3e77dd3 --- /dev/null +++ b/input/fsh/instances/patient1 copy.fsh @@ -0,0 +1,98 @@ +Instance: patient2 +InstanceOf: Patient +Usage: #example +* meta.versionId = "1" +* meta.lastUpdated = "2019-07-01T13:30:55.864+00:00" +* extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" +* extension[=].id = "fcc557e7-40fa-4fde-b802-12a461cd176f" +* extension[=].extension[+].url = "jwe" +* extension[=].extension[=].valueString = "eyJraWQiOiJmY2M1NTdlNy00MGZhLTRmZGUtYjgwMi0xMmE0NjFjZDE3NmYiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0=" +* extension[=].extension[+].url = "key" +* extension[=].extension[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true +* extension[+].url = "http://hl7.org/fhir/StructureDefinition/patient-birthPlace" +* extension[=].valueAddress.extension.url = "http://hl7.org/fhir/StructureDefinition/language" +* extension[=].valueAddress.extension.valueCode = #nl +* extension[=].valueAddress.city = "Namen" +* extension[=].valueAddress.country = "BE" +* extension[+].url = "http://hl7.org/fhir/StructureDefinition/patient-nationality" +* extension[=].extension.url = "code" +* extension[=].extension.valueCodeableConcept = $cd-fed-country#BE "Belgium" +* extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-intended-profile" +* extension[=].valueCanonical = "https://www.ehealth.fgov.be/standards/fhir/core/StructureDefinition/be-patient|2.1.0" +* identifier[0].use = #official +* identifier[=].type = $v2-0203#SB "Social Beneficiary Identifier" +* identifier[=].system = "https://www.ehealth.fgov.be/standards/fhir/core/NamingSystem/ssin" +* identifier[=].value = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* identifier[=].value.extension[BeExtPseudonymization].extension[marker].valueBoolean = true +* identifier[=].value.extension[BeExtPseudonymization].extension[kid].valueString = "fcc557e7-40fa-4fde-b802-12a461cd176f" +* identifier[+].use = #usual +* identifier[=].type = $v2-0203#MR "Medical record number" +* identifier[=].system = "https://www.goodhealthhospital.be/standards/fhir/NamingSystem/patientrecord" +* identifier[=].value = "45XXP0PA-4" +* active = true +* name.use = #official +* name.family = "La Paradisio" +* name.given[0] = "Josephine" +* name.given[+] = "Nessa" +* telecom[0].system = #email +* telecom[=].value = "nessa.laparadisio@belgium.be" +* telecom[+].system = #phone +* telecom[=].value = "+322476792979" +* telecom[=].use = #mobile +* telecom[+].system = #phone +* telecom[=].value = "+3226718655" +* telecom[=].use = #home +* telecom[+].system = #phone +* telecom[=].value = "+322476799" +* telecom[=].use = #work +* gender = #female +* birthDate = "1979-12-11" +* birthDate.extension.url = "http://hl7.org/fhir/StructureDefinition/patient-birthTime" +* birthDate.extension.valueDateTime = "1979-12-11T13:28:17-05:00" +* address[0].extension.url = "http://hl7.org/fhir/StructureDefinition/language" +* address[=].extension.valueCode = #nl +* address[=].use = #home +* address[=].type = #both +* address[=].text = "Sloordelle 42, 1160 Oudergem" +* address[=].line = "Sloordelle 42" +* address[=].line.extension[0].url = "http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-streetName" +* address[=].line.extension[=].valueString = "Sloordelle" +* address[=].line.extension[+].url = "http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-houseNumber" +* address[=].line.extension[=].valueString = "42" +* address[=].city = "Oudergem" +* address[=].postalCode = "1160" +* address[=].country = "BE" +* address[+].extension.url = "http://hl7.org/fhir/StructureDefinition/language" +* address[=].extension.valueCode = #fr +* address[=].use = #home +* address[=].type = #both +* address[=].text = "42, Allee des Colzas, 1160 Auderghem" +* address[=].line = "42, Allee des Colzas" +* address[=].line.extension[0].url = "http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-streetName" +* address[=].line.extension[=].valueString = "Allee des Colzas" +* address[=].line.extension[+].url = "http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-houseNumber" +* address[=].line.extension[=].valueString = "42" +* address[=].city = "Auderghem" +* address[=].postalCode = "1160" +* address[=].country = "BE" +* address[+].use = #work +* address[=].type = #both +* address[=].text = "377, Avenue Prince d'Orange, 1420 Braine-lʼAlleud" +* address[=].line = "377, Avenue Prince d'Orange" +* address[=].line.extension[0].url = "http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-streetName" +* address[=].line.extension[=].valueString = "Avenue Prince d'Orange" +* address[=].line.extension[+].url = "http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-houseNumber" +* address[=].line.extension[=].valueString = "377" +* address[=].city = "Braine-lʼAlleud" +* address[=].postalCode = "1420" +* address[=].country = "BE" +* maritalStatus.coding[0] = $v3-MaritalStatus#D "Divorced" +* maritalStatus.coding[+] = $cd-civilstate#41 "Divorced since 1/10/1994" +* contact.relationship.coding[0] = $v2-0131#N "Next-of-Kin" +* contact.relationship.coding[+] = $cd-contact-person#mother +* contact.name.family = "Vogels" +* contact.name.given = "Leia" +* contact.telecom.system = #phone +* contact.telecom.value = "+31201234567" +* contact.telecom.use = #mobile From 3124a2edecb27543d9ce6b037e1b963640813419 Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:46:31 +0200 Subject: [PATCH 2/9] moved extension to meta for non DomainResource resources --- .../fsh/extensions/BeExtKeyPseudonymization.fsh | 2 +- input/fsh/instances/bundle1.fsh | 7 +++++++ input/fsh/instances/contained1.fsh | 5 +++++ input/fsh/instances/parameters1.fsh | 16 ++++++++++++++++ .../{patient1 copy.fsh => patient2.fsh} | 16 +++++++--------- 5 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 input/fsh/instances/bundle1.fsh create mode 100644 input/fsh/instances/contained1.fsh create mode 100644 input/fsh/instances/parameters1.fsh rename input/fsh/instances/{patient1 copy.fsh => patient2.fsh} (85%) diff --git a/input/fsh/extensions/BeExtKeyPseudonymization.fsh b/input/fsh/extensions/BeExtKeyPseudonymization.fsh index ec1d2d3..5b7d00a 100644 --- a/input/fsh/extensions/BeExtKeyPseudonymization.fsh +++ b/input/fsh/extensions/BeExtKeyPseudonymization.fsh @@ -3,7 +3,7 @@ Id: be-ext-key-pseudonymization Title: "Key Pseudonymization Extension" Description: "This holds a pseudonymized key that can be used for all encrypted fields in the resource (long text pseudonymisation)" * ^context.type = #element -* ^context.expression = "Resource" +* ^context.expression = "Meta" * . ^short = "Pseudonymization key (See Blinded Pseudonymization Cookbook, Annex: Processing of input data exceeding 32 bytes)" * id 1..1 MS diff --git a/input/fsh/instances/bundle1.fsh b/input/fsh/instances/bundle1.fsh new file mode 100644 index 0000000..07874af --- /dev/null +++ b/input/fsh/instances/bundle1.fsh @@ -0,0 +1,7 @@ +Instance: bundle1 +InstanceOf: Bundle +* type = #collection +* entry[+].resource = patient1 +* entry[=].fullUrl = "urn:uuid:be78855a-2ac2-4907-a209-c018ab7bbaa5" +* entry[+].resource = patient2 +* entry[=].fullUrl = "urn:uuid:2c9e9f79-1a05-4a34-ab1b-07328c53a323" \ No newline at end of file diff --git a/input/fsh/instances/contained1.fsh b/input/fsh/instances/contained1.fsh new file mode 100644 index 0000000..44709d2 --- /dev/null +++ b/input/fsh/instances/contained1.fsh @@ -0,0 +1,5 @@ +Instance: contained1 +InstanceOf: Patient +* contained[+] = patient2 +* link.other = Reference( #patient2 ) +* link.type = #replaced-by \ No newline at end of file diff --git a/input/fsh/instances/parameters1.fsh b/input/fsh/instances/parameters1.fsh new file mode 100644 index 0000000..aa21ab9 --- /dev/null +++ b/input/fsh/instances/parameters1.fsh @@ -0,0 +1,16 @@ +Instance: parameters1 +InstanceOf: Parameters +Usage: #example +* meta.extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" +* meta.extension[=].id = "fcc557e7-40fa-4fde-b802-12a461cd176f" +* meta.extension[=].extension[+].url = "jwe" +* meta.extension[=].extension[=].valueString = "eyJraWQiOiJmY2M1NTdlNy00MGZhLTRmZGUtYjgwMi0xMmE0NjFjZDE3NmYiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0=" +* meta.extension[=].extension[+].url = "key" +* meta.extension[=].extension[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true +* parameter[0].name = "name" +* parameter[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* parameter[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true +* parameter[=].valueString.extension[BeExtPseudonymization].extension[kid].valueString = "fcc557e7-40fa-4fde-b802-12a461cd176f" +* parameter[+].name = "version" +* parameter[=].valueString = "1.0.0" \ No newline at end of file diff --git a/input/fsh/instances/patient1 copy.fsh b/input/fsh/instances/patient2.fsh similarity index 85% rename from input/fsh/instances/patient1 copy.fsh rename to input/fsh/instances/patient2.fsh index 3e77dd3..9f73596 100644 --- a/input/fsh/instances/patient1 copy.fsh +++ b/input/fsh/instances/patient2.fsh @@ -1,15 +1,13 @@ Instance: patient2 InstanceOf: Patient Usage: #example -* meta.versionId = "1" -* meta.lastUpdated = "2019-07-01T13:30:55.864+00:00" -* extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" -* extension[=].id = "fcc557e7-40fa-4fde-b802-12a461cd176f" -* extension[=].extension[+].url = "jwe" -* extension[=].extension[=].valueString = "eyJraWQiOiJmY2M1NTdlNy00MGZhLTRmZGUtYjgwMi0xMmE0NjFjZDE3NmYiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0=" -* extension[=].extension[+].url = "key" -* extension[=].extension[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" -* extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true +* meta.extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" +* meta.extension[=].id = "fcc557e7-40fa-4fde-b802-12a461cd176f" +* meta.extension[=].extension[+].url = "jwe" +* meta.extension[=].extension[=].valueString = "eyJraWQiOiJmY2M1NTdlNy00MGZhLTRmZGUtYjgwMi0xMmE0NjFjZDE3NmYiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0=" +* meta.extension[=].extension[+].url = "key" +* meta.extension[=].extension[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true * extension[+].url = "http://hl7.org/fhir/StructureDefinition/patient-birthPlace" * extension[=].valueAddress.extension.url = "http://hl7.org/fhir/StructureDefinition/language" * extension[=].valueAddress.extension.valueCode = #nl From c19c5f45bf8cd544dd03fc54509a6afe73225957 Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:40:37 +0200 Subject: [PATCH 3/9] add long texts with remarks from hannes --- .gitignore | 2 ++ .../codesystem/BeCSPseudonymizationType.fsh | 8 +++++ .../extensions/BeExtKeyPseudonymization.fsh | 6 ++-- .../fsh/extensions/BeExtPseudonymization.fsh | 17 +++++---- input/fsh/instances/parameters1.fsh | 7 ++-- input/fsh/instances/patient2.fsh | 7 ++-- .../invariants/BeInvKeyPseudonymization.fsh | 4 +++ .../fsh/valueset/BeVSPseudonymizationType.fsh | 6 ++++ input/pagecontent/guidance.md | 35 +++++++++++++++---- 9 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 input/fsh/codesystem/BeCSPseudonymizationType.fsh create mode 100644 input/fsh/invariants/BeInvKeyPseudonymization.fsh create mode 100644 input/fsh/valueset/BeVSPseudonymizationType.fsh diff --git a/.gitignore b/.gitignore index 4672598..feb6aa2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ Thumbs.db # backup files # ################ *.bak +target +src diff --git a/input/fsh/codesystem/BeCSPseudonymizationType.fsh b/input/fsh/codesystem/BeCSPseudonymizationType.fsh new file mode 100644 index 0000000..ae40819 --- /dev/null +++ b/input/fsh/codesystem/BeCSPseudonymizationType.fsh @@ -0,0 +1,8 @@ +CodeSystem: BeCSPseudonymizationType +Id: be-cs-pseudonymization-type +Title: "Types of pseudonymization" +Description: "The technique used for pseudonymization" +* ^experimental = false +* ^caseSensitive = true +* #direct "Direct pseudonym, for text shorter than 32 bytes" +* #encrypted "Encrypted pseudonym, for text longer than 32 bytes" \ No newline at end of file diff --git a/input/fsh/extensions/BeExtKeyPseudonymization.fsh b/input/fsh/extensions/BeExtKeyPseudonymization.fsh index 5b7d00a..fae285e 100644 --- a/input/fsh/extensions/BeExtKeyPseudonymization.fsh +++ b/input/fsh/extensions/BeExtKeyPseudonymization.fsh @@ -8,11 +8,9 @@ Description: "This holds a pseudonymized key that can be used for all encrypted bytes)" * id 1..1 MS * id ^short = "kid (also available in JWE)" -* extension contains key 1..1 MS and jwe 0..1 MS +* extension contains key 1..1 MS * extension[key].value[x] only string * extension[key].valueString 1..1 MS * extension[key].valueString ^short = "pseudonymized key" * extension[key].value[x].extension contains BeExtPseudonymization named pseudonymization 1..1 -* extension[jwe].value[x] only string -* extension[jwe].valueString 0..1 MS -* extension[jwe].valueString ^short = "information about the key in a compact JWE string" \ No newline at end of file +* extension[key] obeys BeInvKeyPseudonymization diff --git a/input/fsh/extensions/BeExtPseudonymization.fsh b/input/fsh/extensions/BeExtPseudonymization.fsh index 155e04c..b8c51e2 100644 --- a/input/fsh/extensions/BeExtPseudonymization.fsh +++ b/input/fsh/extensions/BeExtPseudonymization.fsh @@ -1,15 +1,20 @@ Extension: BeExtPseudonymization Id: be-ext-pseudonymization Title: "Pseudonymization Extension" -Description: "This is a marker interface. If the field is pseudonymized, the string field SHALL have this extension. The original text field SHALL contain the x, y and transitInfo in JWE encoded form. transitInfo is optional depending on the situation." +Description: "This is a marker interface. If the field is pseudonymized, the string field SHALL have this extension. " * ^context.type = #element * ^context.expression = "Element" * . ^short = "Pseudonymization data" * extension contains marker 1..1 MS and - kid 0..1 MS + format 0..1 MS and + version 0..1 MS * extension[marker].value[x] only boolean * extension[marker].valueBoolean = true -* extension[kid].value[x] only string -* extension[kid].valueString 0..1 MS -* extension[kid].valueString ^short = "kid, only in case of Blinded Pseudonymization Cookbook, Annex: Processing of input data exceeding 32 -bytes" \ No newline at end of file +* extension[format].value[x] only code +* extension[format].valueCode 0..1 MS +* extension[format].valueCode ^short = "provide encrypted only in case of Blinded Pseudonymization Cookbook, Annex: Processing of input data exceeding 32 +bytes" +* extension[format].valueCode from BeVSPseudonymizationType +* extension[version].value[x] only positiveInt +* extension[version].valuePositiveInt 0..1 MS +* extension[version].valuePositiveInt ^short = "version of the pseudonym encoding" \ No newline at end of file diff --git a/input/fsh/instances/parameters1.fsh b/input/fsh/instances/parameters1.fsh index aa21ab9..9ecf861 100644 --- a/input/fsh/instances/parameters1.fsh +++ b/input/fsh/instances/parameters1.fsh @@ -3,14 +3,13 @@ InstanceOf: Parameters Usage: #example * meta.extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" * meta.extension[=].id = "fcc557e7-40fa-4fde-b802-12a461cd176f" -* meta.extension[=].extension[+].url = "jwe" -* meta.extension[=].extension[=].valueString = "eyJraWQiOiJmY2M1NTdlNy00MGZhLTRmZGUtYjgwMi0xMmE0NjFjZDE3NmYiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0=" * meta.extension[=].extension[+].url = "key" -* meta.extension[=].extension[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* meta.extension[=].extension[=].valueString = "urn:be:fgov:pseudo:v2:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true * parameter[0].name = "name" * parameter[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * parameter[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true -* parameter[=].valueString.extension[BeExtPseudonymization].extension[kid].valueString = "fcc557e7-40fa-4fde-b802-12a461cd176f" +* parameter[=].valueString.extension[BeExtPseudonymization].extension[format].valueCode = #encrypted +* parameter[=].valueString.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 1 * parameter[+].name = "version" * parameter[=].valueString = "1.0.0" \ No newline at end of file diff --git a/input/fsh/instances/patient2.fsh b/input/fsh/instances/patient2.fsh index 9f73596..8c2a2fe 100644 --- a/input/fsh/instances/patient2.fsh +++ b/input/fsh/instances/patient2.fsh @@ -3,10 +3,8 @@ InstanceOf: Patient Usage: #example * meta.extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" * meta.extension[=].id = "fcc557e7-40fa-4fde-b802-12a461cd176f" -* meta.extension[=].extension[+].url = "jwe" -* meta.extension[=].extension[=].valueString = "eyJraWQiOiJmY2M1NTdlNy00MGZhLTRmZGUtYjgwMi0xMmE0NjFjZDE3NmYiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0=" * meta.extension[=].extension[+].url = "key" -* meta.extension[=].extension[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* meta.extension[=].extension[=].valueString = "urn:be:fgov:pseudo:v2:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true * extension[+].url = "http://hl7.org/fhir/StructureDefinition/patient-birthPlace" * extension[=].valueAddress.extension.url = "http://hl7.org/fhir/StructureDefinition/language" @@ -23,7 +21,8 @@ Usage: #example * identifier[=].system = "https://www.ehealth.fgov.be/standards/fhir/core/NamingSystem/ssin" * identifier[=].value = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * identifier[=].value.extension[BeExtPseudonymization].extension[marker].valueBoolean = true -* identifier[=].value.extension[BeExtPseudonymization].extension[kid].valueString = "fcc557e7-40fa-4fde-b802-12a461cd176f" +* identifier[=].value.extension[BeExtPseudonymization].extension[format].valueCode = #encrypted +* identifier[=].value.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 1 * identifier[+].use = #usual * identifier[=].type = $v2-0203#MR "Medical record number" * identifier[=].system = "https://www.goodhealthhospital.be/standards/fhir/NamingSystem/patientrecord" diff --git a/input/fsh/invariants/BeInvKeyPseudonymization.fsh b/input/fsh/invariants/BeInvKeyPseudonymization.fsh new file mode 100644 index 0000000..764472c --- /dev/null +++ b/input/fsh/invariants/BeInvKeyPseudonymization.fsh @@ -0,0 +1,4 @@ +Invariant: BeInvKeyPseudonymization +Description: "the pseudonymized key should always start with 'urn:be:fgov:pseudo:v2:'" +Severity: #error +Expression: "Extension.value.toString().startsWith('urn:be:fgov:pseudo:v2:')" \ No newline at end of file diff --git a/input/fsh/valueset/BeVSPseudonymizationType.fsh b/input/fsh/valueset/BeVSPseudonymizationType.fsh new file mode 100644 index 0000000..7a4fb96 --- /dev/null +++ b/input/fsh/valueset/BeVSPseudonymizationType.fsh @@ -0,0 +1,6 @@ +ValueSet: BeVSPseudonymizationType +Id: be-vs-pseudonymization-type +Title: "Types of pseudonymization" +Description: "The technique used for pseudonymization" +* ^experimental = false +* include codes from system BeCSPseudonymizationType \ No newline at end of file diff --git a/input/pagecontent/guidance.md b/input/pagecontent/guidance.md index d4e69e3..f860910 100644 --- a/input/pagecontent/guidance.md +++ b/input/pagecontent/guidance.md @@ -1,4 +1,4 @@ -### The use of pseudonymisation in FHIR for short texts +### The use of pseudonymisation in FHIR Pseudonymisation is the activity of replacing meaningful data with a synonym that hides the original data, but when needed this synonym can be replaced by the original data. The aim is to hide data from readers that do not need it, due to legal (GDPR) or other reasons, but still allow the links between different data elements for those who need it. Additional encryption techniques may be used to restrict the access to the information to those who need it. @@ -16,15 +16,23 @@ This solution only applies for short texts, i.e. text that fall within the lengt * Pseudonymisation should interfere as little as possible with the standard FHIR APIs for searching information, without endangering the essence of pseudonymisation. * Pseudonymisation should be as coherent as possible, so that the developer can (re)use the same techniques whenever he encounters pseudonymisation. -#### The solution: +#### The solution for short texts, less than 32 bytes: * Within the FHIR document, a pseudonymised value will be marked by an extension. This extension is applicable to any text field (string). -* The original value of the string will be replaced by the pseudonym. This pseudonym is a JWE encoded string, containing the transitinfo, x and y value. -* At this moment, the extension does not contain any other fields, but these might be added in the solution for long texts. -* Searching on a pseudonymised field will be done using the normal search parameter. The fact that this search parameter contains a pseudonym will be indicated by a urn-style prefix. The pseudonym will be represented by the same JWE encoded string as described in item 2. +* The original value of the string will be replaced by the pseudonym. This pseudonym can take following forms: + - {base64 json string, containing x, y, and transitInfo} + - urn:be:fgov:pseudo:v1:{base64 json string, containing x, y, and transitInfo} + +* The extension will have following fields: + - marker: true (mandatory), indicates that this field is a pseudonym. + - format: direct|encrypted (optional) default is direct + + direct indicates that the field is an immediate result of the pseudonymization service + + encrypted see below for texts larger than 32 bytes. + - version: no version defaults to version 1 +* Searching on a pseudonymised field will be done using the normal search parameter. The fact that this search parameter contains a pseudonym will be indicated by a urn-style prefix. The pseudonym will be represented by the same way as described in item 2. "urn:be:fgov:pseudo-encrypted:" fields cannot be used in a search, if the search parameters are not available as a resource. * Depending on the need of the implementing server, and the length of the query string, the implementing server will be able to use both GET and POST to execute the search, according to the FHIR specifications. The use of POST might be necessary in case of the combination of several pseudonymised search parameters in one query string. -Example of a json containing a pseudonym, before the application of JWE: +Example of a json containing a pseudonym, before the application of the base64 encoding: ``` { @@ -47,6 +55,21 @@ urn:be:fgov:ehealth:pseudo:v1:eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiMj ``` +#### The solution for short texts, larger than 32 bytes: + +* Within the FHIR document, a pseudonymised value will be marked by an extension. This extension is applicable to any text field (string). +* The original value of the string will be replaced by the pseudonym. This pseudonym can take following forms: + - urn:be:fgov:pseudo-encrypted:v1:{KID}:{JWE} +* The extension will have following fields: + - marker: true (mandatory), indicates that this field is a pseudonym. + - format: direct|encrypted (optional), default is direct + + direct see above for texts less than 32 bytes + + encrypted indicates that the field is encrypted with a key you can find in the .meta section of the resource, in the extension with url "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization". + - version: no version defaults to version 1 +* In each resource of the document, you will add an extension with url "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" + - This extension contains one extension containing a string value, with url "key". This is the encryption key that can be used to blockcipher the long text fields. The key is 32 bytes or less, so direct pseudonymization applies. + - This .valueString field is pseudonymized in the direct way, using a pseudonymize extension for short texts. + ### Ensuring computable integrity clarifying the use of meta.profile and semantic integrity by using the BeExtIntendedProfile extension #### Prerequisites: From 88e8e5443ab5fef7f42224699bf46672e0a0bac0 Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:53:18 +0200 Subject: [PATCH 4/9] add v2 information --- input/pagecontent/guidance.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/input/pagecontent/guidance.md b/input/pagecontent/guidance.md index f860910..49a2746 100644 --- a/input/pagecontent/guidance.md +++ b/input/pagecontent/guidance.md @@ -22,6 +22,7 @@ This solution only applies for short texts, i.e. text that fall within the lengt * The original value of the string will be replaced by the pseudonym. This pseudonym can take following forms: - {base64 json string, containing x, y, and transitInfo} - urn:be:fgov:pseudo:v1:{base64 json string, containing x, y, and transitInfo} + - urn:be:fgov:pseudo:v2:{SEC1}:{transitInfo} - this type of encoding prevents the double Base64 encoding in v1. See the annexes of the Blinded Pseudonymization Cookbook for more info. * The extension will have following fields: - marker: true (mandatory), indicates that this field is a pseudonym. @@ -49,6 +50,14 @@ If you want to use the pseudonym for searching, you take the resulting value, an ``` urn:be:fgov:ehealth:pseudo:v1: ``` +or + +``` +urn:be:fgov:ehealth:pseudo:v2: +``` +depending on the type of pseudonymization you are using. Beware that you can only use the long text solution (urn:be:fgov:pseudo-encrypted:) in searches if the body of the query request contains a FHIR resource that can handle the pseudonymized key. + + The resulting value will look like this: ``` urn:be:fgov:ehealth:pseudo:v1:eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiMjAyMi0xMi... From 4cbde1cf5d9186ccc581c03dfe42b367f686d47c Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Wed, 4 Sep 2024 11:12:02 +0200 Subject: [PATCH 5/9] added clarification --- input/pagecontent/guidance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/input/pagecontent/guidance.md b/input/pagecontent/guidance.md index 49a2746..f142220 100644 --- a/input/pagecontent/guidance.md +++ b/input/pagecontent/guidance.md @@ -29,7 +29,7 @@ This solution only applies for short texts, i.e. text that fall within the lengt - format: direct|encrypted (optional) default is direct + direct indicates that the field is an immediate result of the pseudonymization service + encrypted see below for texts larger than 32 bytes. - - version: no version defaults to version 1 + - version: no version defaults to 1. If the version is different from 1, it is mandatory. * Searching on a pseudonymised field will be done using the normal search parameter. The fact that this search parameter contains a pseudonym will be indicated by a urn-style prefix. The pseudonym will be represented by the same way as described in item 2. "urn:be:fgov:pseudo-encrypted:" fields cannot be used in a search, if the search parameters are not available as a resource. * Depending on the need of the implementing server, and the length of the query string, the implementing server will be able to use both GET and POST to execute the search, according to the FHIR specifications. The use of POST might be necessary in case of the combination of several pseudonymised search parameters in one query string. @@ -74,7 +74,7 @@ urn:be:fgov:ehealth:pseudo:v1:eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiMj - format: direct|encrypted (optional), default is direct + direct see above for texts less than 32 bytes + encrypted indicates that the field is encrypted with a key you can find in the .meta section of the resource, in the extension with url "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization". - - version: no version defaults to version 1 + - version: no version defaults to 1. If the version is different from 1, it is mandatory. * In each resource of the document, you will add an extension with url "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" - This extension contains one extension containing a string value, with url "key". This is the encryption key that can be used to blockcipher the long text fields. The key is 32 bytes or less, so direct pseudonymization applies. - This .valueString field is pseudonymized in the direct way, using a pseudonymize extension for short texts. From 49b32952e40fe185f38bc873f37494758b85b5a9 Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:39:21 +0200 Subject: [PATCH 6/9] typo --- input/pagecontent/guidance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input/pagecontent/guidance.md b/input/pagecontent/guidance.md index f142220..2cc4f83 100644 --- a/input/pagecontent/guidance.md +++ b/input/pagecontent/guidance.md @@ -64,7 +64,7 @@ urn:be:fgov:ehealth:pseudo:v1:eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiMj ``` -#### The solution for short texts, larger than 32 bytes: +#### The solution for long texts, larger than 32 bytes: * Within the FHIR document, a pseudonymised value will be marked by an extension. This extension is applicable to any text field (string). * The original value of the string will be replaced by the pseudonym. This pseudonym can take following forms: From a9b87f0978dcdf230c019a99fe43ff5bbdcf610a Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:07:05 +0200 Subject: [PATCH 7/9] added Benny's proposal + corrected/added examples --- .../BeCSPseudonymizationVersion.fsh | 9 + input/fsh/instances/capabilitystatement.fsh | 216 ++++++++++++++++++ input/fsh/instances/parameters1.fsh | 2 +- input/fsh/instances/patient2.fsh | 2 +- input/pagecontent/guidance.md | 26 ++- 5 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 input/fsh/codesystem/BeCSPseudonymizationVersion.fsh create mode 100644 input/fsh/instances/capabilitystatement.fsh diff --git a/input/fsh/codesystem/BeCSPseudonymizationVersion.fsh b/input/fsh/codesystem/BeCSPseudonymizationVersion.fsh new file mode 100644 index 0000000..43395be --- /dev/null +++ b/input/fsh/codesystem/BeCSPseudonymizationVersion.fsh @@ -0,0 +1,9 @@ +CodeSystem: BeCSPseudonymizationVersion +Id: be-cs-pseudonymization-version +Title: "BeCSPseudonymizationVersion" +Description: "List of pseudonymization versions that can be used a.o. in the Capabilities Statement" +* ^experimental = false +* ^caseSensitive = true +* #urn:be:fgov:ehealth:pseudo:v1 +* #urn:be:fgov:ehealth:pseudo:v2 +* #urn:be:fgov:pseudo-encrypted:v1 diff --git a/input/fsh/instances/capabilitystatement.fsh b/input/fsh/instances/capabilitystatement.fsh new file mode 100644 index 0000000..e723519 --- /dev/null +++ b/input/fsh/instances/capabilitystatement.fsh @@ -0,0 +1,216 @@ +Instance: capabilitystatement +InstanceOf: CapabilityStatement +Usage: #definition +* name = "RestServer" +* status = #active +* date = "2024-10-21T09:29:32.761+00:00" +* publisher = "Not provided" +* kind = #instance +* software.name = "Fictitious FHIR Server" +* implementation.description = "HAPI FHIR" +* implementation.url = "http://localhost:8080/fhir" +* fhirVersion = #4.0.1 +* format[0] = #application/fhir+xml +* format[+] = #xml +* format[+] = #application/fhir+json +* format[+] = #json +* format[+] = #application/x-turtle +* format[+] = #ttl +* rest.mode = #server +* rest.security.service[+] = BeCSPseudonymizationVersion#urn:be:fgov:ehealth:pseudo:v1 +* rest.security.service[+] = BeCSPseudonymizationVersion#urn:be:fgov:ehealth:pseudo:v2 +* rest.security.service[+] = BeCSPseudonymizationVersion#urn:be:fgov:pseudo-encrypted:v1 +* rest.resource[0].type = #CodeSystem +* rest.resource[=].profile = "http://hl7.org/fhir/StructureDefinition/CodeSystem" +* rest.resource[=].interaction[0].code = #read +* rest.resource[=].interaction[+].code = #search-type +* rest.resource[=].interaction[+].code = #delete +* rest.resource[=].searchInclude = "*" +* rest.resource[=].searchParam[0].name = "code" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "A code defined in the code system" +* rest.resource[=].searchParam[+].name = "context" +* rest.resource[=].searchParam[=].type = #token +* rest.resource[=].searchParam[=].documentation = "A use context assigned to the code system" +* rest.resource[=].searchParam[+].name = "context-quantity" +* rest.resource[=].searchParam[=].type = #quantity +* rest.resource[=].searchParam[=].documentation = "A quantity- or range-valued use context assigned to the code system" +* rest.resource[=].searchParam[+].name = "context-type" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "A type of use context assigned to the code system" +* rest.resource[=].searchParam[+].name = "date" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The code system publication date" +* rest.resource[=].searchParam[+].name = "description" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The description of the code system" +* rest.resource[=].searchParam[+].name = "id" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[+].name = "identifier" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "External identifier for the code system" +* rest.resource[=].searchParam[+].name = "jurisdiction" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Intended jurisdiction for the code system" +* rest.resource[=].searchParam[+].name = "name" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Computationally friendly name of the code system" +* rest.resource[=].searchParam[+].name = "publisher" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Name of the publisher of the code system" +* rest.resource[=].searchParam[+].name = "reference" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[+].name = "status" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The current status of the code system" +* rest.resource[=].searchParam[+].name = "title" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The human-friendly name of the code system" +* rest.resource[=].searchParam[+].name = "url" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The uri that identifies the code system" +* rest.resource[=].searchParam[+].name = "version" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The business version of the code system" +* rest.resource[=].operation[0].name = "validate-code" +* rest.resource[=].operation[=].definition = "http://localhost:8080/fhir/OperationDefinition/CodeSystemValueSet-it-validate-code" +* rest.resource[=].operation[+].name = "upload-external-code-system" +* rest.resource[=].operation[=].definition = "http://localhost:8080/fhir/OperationDefinition/CodeSystem-t-upload-external-code-system" +* rest.resource[=].operation[+].name = "subsumes" +* rest.resource[=].operation[=].definition = "http://localhost:8080/fhir/OperationDefinition/CodeSystem-it-subsumes" +* rest.resource[=].operation[+].name = "lookup" +* rest.resource[=].operation[=].definition = "http://localhost:8080/fhir/OperationDefinition/CodeSystem-it-lookup" +* rest.resource[+].type = #ConceptMap +* rest.resource[=].profile = "http://hl7.org/fhir/StructureDefinition/ConceptMap" +* rest.resource[=].interaction[0].code = #read +* rest.resource[=].interaction[+].code = #search-type +* rest.resource[=].searchInclude = "*" +* rest.resource[=].searchParam.name = "url" +* rest.resource[=].searchParam.type = #string +* rest.resource[=].searchParam.documentation = "The uri that identifies the concept map" +* rest.resource[=].operation.name = "translate" +* rest.resource[=].operation.definition = "http://localhost:8080/fhir/OperationDefinition/ConceptMap-t-translate" +* rest.resource[+].type = #Medication +* rest.resource[=].profile = "http://hl7.org/fhir/StructureDefinition/Medication" +* rest.resource[=].interaction.code = #read +* rest.resource[=].searchInclude = "*" +* rest.resource[+].type = #OperationDefinition +* rest.resource[=].profile = "http://hl7.org/fhir/StructureDefinition/OperationDefinition" +* rest.resource[=].interaction.code = #read +* rest.resource[=].searchInclude = "*" +* rest.resource[+].type = #StructureDefinition +* rest.resource[=].profile = "http://hl7.org/fhir/StructureDefinition/StructureDefinition" +* rest.resource[=].interaction[0].code = #update +* rest.resource[=].interaction[+].code = #read +* rest.resource[=].interaction[+].code = #search-type +* rest.resource[=].interaction[+].code = #delete +* rest.resource[=].interaction[+].code = #create +* rest.resource[=].searchInclude = "*" +* rest.resource[=].searchParam[0].name = "code" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[+].name = "context" +* rest.resource[=].searchParam[=].type = #token +* rest.resource[=].searchParam[=].documentation = "A use context assigned to the structure definition" +* rest.resource[=].searchParam[+].name = "context-quantity" +* rest.resource[=].searchParam[=].type = #quantity +* rest.resource[=].searchParam[=].documentation = "A quantity- or range-valued use context assigned to the structure definition" +* rest.resource[=].searchParam[+].name = "context-type" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "A type of use context assigned to the structure definition" +* rest.resource[=].searchParam[+].name = "date" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The structure definition publication date" +* rest.resource[=].searchParam[+].name = "description" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The description of the structure definition" +* rest.resource[=].searchParam[+].name = "expansion" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[+].name = "identifier" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "External identifier for the structure definition" +* rest.resource[=].searchParam[+].name = "jurisdiction" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Intended jurisdiction for the structure definition" +* rest.resource[=].searchParam[+].name = "name" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Computationally friendly name of the structure definition" +* rest.resource[=].searchParam[+].name = "publisher" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Name of the publisher of the structure definition" +* rest.resource[=].searchParam[+].name = "reference" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[+].name = "status" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The current status of the structure definition" +* rest.resource[=].searchParam[+].name = "title" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The human-friendly name of the structure definition" +* rest.resource[=].searchParam[+].name = "url" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The uri that identifies the structure definition" +* rest.resource[=].searchParam[+].name = "version" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The business version of the structure definition" +* rest.resource[+].type = #ValueSet +* rest.resource[=].profile = "http://hl7.org/fhir/StructureDefinition/ValueSet" +* rest.resource[=].interaction[0].code = #update +* rest.resource[=].interaction[+].code = #read +* rest.resource[=].interaction[+].code = #search-type +* rest.resource[=].interaction[+].code = #delete +* rest.resource[=].interaction[+].code = #create +* rest.resource[=].searchInclude = "*" +* rest.resource[=].searchParam[0].name = "_id" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The ID of the resource" +* rest.resource[=].searchParam[+].name = "code" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "This special parameter searches for codes in the value set. See additional notes on the ValueSet resource" +* rest.resource[=].searchParam[+].name = "context" +* rest.resource[=].searchParam[=].type = #token +* rest.resource[=].searchParam[=].documentation = "A use context assigned to the value set" +* rest.resource[=].searchParam[+].name = "context-quantity" +* rest.resource[=].searchParam[=].type = #quantity +* rest.resource[=].searchParam[=].documentation = "A quantity- or range-valued use context assigned to the value set" +* rest.resource[=].searchParam[+].name = "context-type" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "A type of use context assigned to the value set" +* rest.resource[=].searchParam[+].name = "date" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The value set publication date" +* rest.resource[=].searchParam[+].name = "description" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The description of the value set" +* rest.resource[=].searchParam[+].name = "expansion" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Identifies the value set expansion (business identifier)" +* rest.resource[=].searchParam[+].name = "identifier" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "External identifier for the value set" +* rest.resource[=].searchParam[+].name = "jurisdiction" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Intended jurisdiction for the value set" +* rest.resource[=].searchParam[+].name = "name" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Computationally friendly name of the value set" +* rest.resource[=].searchParam[+].name = "publisher" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "Name of the publisher of the value set" +* rest.resource[=].searchParam[+].name = "reference" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "A code system included or excluded in the value set or an imported value set" +* rest.resource[=].searchParam[+].name = "status" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The current status of the value set" +* rest.resource[=].searchParam[+].name = "title" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The human-friendly name of the value set" +* rest.resource[=].searchParam[+].name = "url" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The uri that identifies the value set" +* rest.resource[=].searchParam[+].name = "version" +* rest.resource[=].searchParam[=].type = #string +* rest.resource[=].searchParam[=].documentation = "The business version of the value set" +* rest.resource[=].operation[0].name = "validate-code" +* rest.resource[=].operation[=].definition = "http://localhost:8080/fhir/OperationDefinition/CodeSystemValueSet-it-validate-code" +* rest.resource[=].operation[+].name = "expand" +* rest.resource[=].operation[=].definition = "http://localhost:8080/fhir/OperationDefinition/ValueSet-it-expand" \ No newline at end of file diff --git a/input/fsh/instances/parameters1.fsh b/input/fsh/instances/parameters1.fsh index 9ecf861..2e1b68f 100644 --- a/input/fsh/instances/parameters1.fsh +++ b/input/fsh/instances/parameters1.fsh @@ -7,7 +7,7 @@ Usage: #example * meta.extension[=].extension[=].valueString = "urn:be:fgov:pseudo:v2:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true * parameter[0].name = "name" -* parameter[=].valueString = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* parameter[=].valueString = "urn:be:fgov:pseudo-encrypted:v1:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iD:geip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * parameter[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true * parameter[=].valueString.extension[BeExtPseudonymization].extension[format].valueCode = #encrypted * parameter[=].valueString.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 1 diff --git a/input/fsh/instances/patient2.fsh b/input/fsh/instances/patient2.fsh index 8c2a2fe..cfa2b07 100644 --- a/input/fsh/instances/patient2.fsh +++ b/input/fsh/instances/patient2.fsh @@ -19,7 +19,7 @@ Usage: #example * identifier[0].use = #official * identifier[=].type = $v2-0203#SB "Social Beneficiary Identifier" * identifier[=].system = "https://www.ehealth.fgov.be/standards/fhir/core/NamingSystem/ssin" -* identifier[=].value = "OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* identifier[=].value = "urn:be:fgov:pseudo-encrypted:v1:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7i:SHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * identifier[=].value.extension[BeExtPseudonymization].extension[marker].valueBoolean = true * identifier[=].value.extension[BeExtPseudonymization].extension[format].valueCode = #encrypted * identifier[=].value.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 1 diff --git a/input/pagecontent/guidance.md b/input/pagecontent/guidance.md index 2cc4f83..c47747a 100644 --- a/input/pagecontent/guidance.md +++ b/input/pagecontent/guidance.md @@ -71,7 +71,7 @@ urn:be:fgov:ehealth:pseudo:v1:eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiMj - urn:be:fgov:pseudo-encrypted:v1:{KID}:{JWE} * The extension will have following fields: - marker: true (mandatory), indicates that this field is a pseudonym. - - format: direct|encrypted (optional), default is direct + - format: direct or encrypted (optional), default is direct + direct see above for texts less than 32 bytes + encrypted indicates that the field is encrypted with a key you can find in the .meta section of the resource, in the extension with url "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization". - version: no version defaults to 1. If the version is different from 1, it is mandatory. @@ -79,6 +79,30 @@ urn:be:fgov:ehealth:pseudo:v1:eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiMj - This extension contains one extension containing a string value, with url "key". This is the encryption key that can be used to blockcipher the long text fields. The key is 32 bytes or less, so direct pseudonymization applies. - This .valueString field is pseudonymized in the direct way, using a pseudonymize extension for short texts. +#### Content negotiation for pseudonymisation in FHIR + +As clients and servers may have different capabilities with regard to the support of pseudonymisation representations, both clients and servers can express their capabilities and conduct negotiations regarding the pseudonymisation representations to be used. + +The client will be able to express his preferences by using the Accept-Be-Pseudo HTTP header. This header SHALL contain a comma separated list of the prefix values as described above. + +``` +Accept-Be-Pseudo: urn:be:fgov:ehealth:pseudo:v1, urn:be:fgov:ehealth:pseudo:v2, urn:be:fgov:pseudo-encrypted:v1 +``` +This header has been designed according to the guidelines in [RFC 6648](https://www.rfc-editor.org/rfc/rfc6648) + +If the header is not present in the request, the server will default to the lowest version supported as indicated in the capabilities statement (see further). + +The server will indicate the version of pseudonymisation used using the Content-Be-Pseudo header. + +``` +Content-Be-Pseudo: urn:be:fgov:ehealth:pseudo:v2, urn:be:fgov:pseudo-encrypted:v1 +``` +This header is only an suggestion, and the indications in the FHIR resource itself always take precedence over the value in the header. If the header is missing, and there is no conclusive evidence (taking into account the described defaults), the value defaults to the lowest supported version in the capabilities statement. + +The server has the opportunity to indicate its preferred use of pseudonymisation in the capabilities statement, as one or more ``rest.security.service`` codeable concepts from CodeSystem [be-cs-pseudonymization-version](./CodeSystem-be-cs-pseudonymization-version.html). The client is supposed, as in [good fhir practice](https://www.hl7.org/fhir/R4/http.html#capabilities), to request the capabilities statement of the server, to check the type of pseudonymisation that is expected. If no explicit pseudonymisation representation is present, the client can try to use his own preference, but must be prepared to accept a refusal in the form of a 422 Error Code. In general, sending pseudonymised content to a server that is not capable of handling it, will provoke undefined behaviour with regard to the pseudonymisation definition. + +During the pseudonymisation content negotiation, the client and server should choose the highest version that is supported by both client and server. Versions will be ordered by using the ordering rules of positive integers. + ### Ensuring computable integrity clarifying the use of meta.profile and semantic integrity by using the BeExtIntendedProfile extension #### Prerequisites: From 495c6f1d37c80986ae0a246e8213c1e2c8536dd7 Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:06:10 +0200 Subject: [PATCH 8/9] example cleanup --- input/fsh/instances/capabilitystatement.fsh | 2 +- input/fsh/instances/parameters1.fsh | 6 ++++-- input/fsh/instances/patient2.fsh | 13 +++++++----- sushi-config.yaml | 22 +++++++++++++++++++++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/input/fsh/instances/capabilitystatement.fsh b/input/fsh/instances/capabilitystatement.fsh index e723519..9c07423 100644 --- a/input/fsh/instances/capabilitystatement.fsh +++ b/input/fsh/instances/capabilitystatement.fsh @@ -1,6 +1,6 @@ Instance: capabilitystatement InstanceOf: CapabilityStatement -Usage: #definition +Usage: #example * name = "RestServer" * status = #active * date = "2024-10-21T09:29:32.761+00:00" diff --git a/input/fsh/instances/parameters1.fsh b/input/fsh/instances/parameters1.fsh index 2e1b68f..6237961 100644 --- a/input/fsh/instances/parameters1.fsh +++ b/input/fsh/instances/parameters1.fsh @@ -4,10 +4,12 @@ Usage: #example * meta.extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" * meta.extension[=].id = "fcc557e7-40fa-4fde-b802-12a461cd176f" * meta.extension[=].extension[+].url = "key" -* meta.extension[=].extension[=].valueString = "urn:be:fgov:pseudo:v2:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* meta.extension[=].extension[=].valueString = "urn:be:fgov:pseudo:v2:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iD:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true +* meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[format].valueCode = #direct +* meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 2 * parameter[0].name = "name" -* parameter[=].valueString = "urn:be:fgov:pseudo-encrypted:v1:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iD:geip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* parameter[=].valueString = "urn:be:fgov:pseudo-encrypted:v1:fcc557e7-40fa-4fde-b802-12a461cd176f:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iD:geip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * parameter[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true * parameter[=].valueString.extension[BeExtPseudonymization].extension[format].valueCode = #encrypted * parameter[=].valueString.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 1 diff --git a/input/fsh/instances/patient2.fsh b/input/fsh/instances/patient2.fsh index cfa2b07..dae3d57 100644 --- a/input/fsh/instances/patient2.fsh +++ b/input/fsh/instances/patient2.fsh @@ -4,8 +4,10 @@ Usage: #example * meta.extension[+].url = "https://www.ehealth.fgov.be/standards/fhir/infsec/StructureDefinition/be-ext-key-pseudonymization" * meta.extension[=].id = "fcc557e7-40fa-4fde-b802-12a461cd176f" * meta.extension[=].extension[+].url = "key" -* meta.extension[=].extension[=].valueString = "urn:be:fgov:pseudo:v2:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* meta.extension[=].extension[=].valueString = "urn:be:fgov:pseudo:v2:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iD:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[marker].valueBoolean = true +* meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[format].valueCode = #direct +* meta.extension[=].extension[=].valueString.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 2 * extension[+].url = "http://hl7.org/fhir/StructureDefinition/patient-birthPlace" * extension[=].valueAddress.extension.url = "http://hl7.org/fhir/StructureDefinition/language" * extension[=].valueAddress.extension.valueCode = #nl @@ -19,10 +21,8 @@ Usage: #example * identifier[0].use = #official * identifier[=].type = $v2-0203#SB "Social Beneficiary Identifier" * identifier[=].system = "https://www.ehealth.fgov.be/standards/fhir/core/NamingSystem/ssin" -* identifier[=].value = "urn:be:fgov:pseudo-encrypted:v1:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7i:SHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* identifier[=].value = "urn:be:fgov:pseudo:v1:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" * identifier[=].value.extension[BeExtPseudonymization].extension[marker].valueBoolean = true -* identifier[=].value.extension[BeExtPseudonymization].extension[format].valueCode = #encrypted -* identifier[=].value.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 1 * identifier[+].use = #usual * identifier[=].type = $v2-0203#MR "Medical record number" * identifier[=].system = "https://www.goodhealthhospital.be/standards/fhir/NamingSystem/patientrecord" @@ -75,7 +75,10 @@ Usage: #example * address[=].country = "BE" * address[+].use = #work * address[=].type = #both -* address[=].text = "377, Avenue Prince d'Orange, 1420 Braine-lʼAlleud" +* address[=].text = "urn:be:fgov:pseudo-encrypted:v1:fcc557e7-40fa-4fde-b802-12a461cd176f:OZADJVppdeQzwgvAUjQNaLvuf94ulY6iDgeip7iSHAW7TNrDBa0XMGeS6G3s/HWLSQ4eirpcox28GghzbtaiUzg=.UPOBi75XsreuYfQwyVvIaHgpzrrdS6joS8JaPlkMPxeU8FmFHRtteJp/FAq91pEllcbH4V4PRSC+QEm0C9thkO4=" +* address[=].text.extension[BeExtPseudonymization].extension[marker].valueBoolean = true +* address[=].text.extension[BeExtPseudonymization].extension[format].valueCode = #encrypted +* address[=].text.extension[BeExtPseudonymization].extension[version].valuePositiveInt = 1 * address[=].line = "377, Avenue Prince d'Orange" * address[=].line.extension[0].url = "http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-streetName" * address[=].line.extension[=].valueString = "Avenue Prince d'Orange" diff --git a/sushi-config.yaml b/sushi-config.yaml index 3120e1f..9d727af 100644 --- a/sushi-config.yaml +++ b/sushi-config.yaml @@ -163,7 +163,10 @@ menu: Guidance: guidance.html Artifacts: Extensions: artifacts.html#structures-extension-definitions + ValueSets: artifacts.html#terminology-value-sets + CodeSystems: artifacts.html#terminology-code-systems Examples: artifacts.html#example-example-instances + Downloads: downloads.html Changes: changes.html History: https://www.ehealth.fgov.be/standards/fhir/infsec/history.html @@ -196,5 +199,24 @@ parameters: propagate-status: true display-warnings: true +resources: + CapabilityStatement/capabilitystatement: + name: "Capability Statement with Pseudonymisation" + description: This capability statement contains the entries to indicate that you support pseudo v1 and v2 and pseudo-encryption v1. + Bundle/bundle1: + name: "Bundle with pseudonymisation examples" + description: Each resource in the bundle has its own pseudo-encryption key + Parameters/parameters1: + name: "Parameters object with long text encryption and key" + description: Long text encryption for resources which are not domain resources + Patient/patient1: + name: "Patient with v1 Pseudonymisation" + description: Example of v1 Pseudonymisation without prefix + Patient/patient2: + name: "Patient with v2 Pseudonymisation and long text pseudonymisation" + description: Example of a domain resource containing both direct and encrypted pseudonymisation + Patient/contained1: + name: "Contained resource with long text pseudonymisation" + description: Example of a contained resource, which always has its own pseudonymisation key if long text pseudonymisation is applied. From f4b407796e4f9c4144c3bb81dfdbface2c1d36c8 Mon Sep 17 00:00:00 2001 From: "Bart Decuypere (eHealth)" <90335317+bdc-ehealth@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:06:59 +0200 Subject: [PATCH 9/9] removed invariant, superfluous after Benny's negotiation solution --- input/fsh/extensions/BeExtKeyPseudonymization.fsh | 1 - input/fsh/invariants/BeInvKeyPseudonymization.fsh | 4 ---- 2 files changed, 5 deletions(-) delete mode 100644 input/fsh/invariants/BeInvKeyPseudonymization.fsh diff --git a/input/fsh/extensions/BeExtKeyPseudonymization.fsh b/input/fsh/extensions/BeExtKeyPseudonymization.fsh index fae285e..44a371b 100644 --- a/input/fsh/extensions/BeExtKeyPseudonymization.fsh +++ b/input/fsh/extensions/BeExtKeyPseudonymization.fsh @@ -13,4 +13,3 @@ bytes)" * extension[key].valueString 1..1 MS * extension[key].valueString ^short = "pseudonymized key" * extension[key].value[x].extension contains BeExtPseudonymization named pseudonymization 1..1 -* extension[key] obeys BeInvKeyPseudonymization diff --git a/input/fsh/invariants/BeInvKeyPseudonymization.fsh b/input/fsh/invariants/BeInvKeyPseudonymization.fsh deleted file mode 100644 index 764472c..0000000 --- a/input/fsh/invariants/BeInvKeyPseudonymization.fsh +++ /dev/null @@ -1,4 +0,0 @@ -Invariant: BeInvKeyPseudonymization -Description: "the pseudonymized key should always start with 'urn:be:fgov:pseudo:v2:'" -Severity: #error -Expression: "Extension.value.toString().startsWith('urn:be:fgov:pseudo:v2:')" \ No newline at end of file