Netherlands - Generic Functions for data exchange Implementation Guide
0.10.0 - ci-build
Netherlands - Generic Functions for data exchange Implementation Guide - Local Development build (v0.10.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions
| Page standards status: Draft |
Generic Function Pseudonymisation defines how a national Pseudonymization Register Service (PRS) is used to convert an identifier (e.g. a Burgerservicenummer, BSN) into a recipient-specific, single-use pseudonym. The goal is to allow generic functions such as GF Localization to exchange patient-bound information without ever revealing the underlying BSN to the receiving party (e.g. the Nationale Verwijs Index, NVI).
The PRS combines two cryptographic building blocks:
Identifier;The PRS returns the evaluation result encrypted as a JWE for the intended recipient. The client never sees the cleartext pseudonym; the recipient never sees the BSN.
The basic process for obtaining and using a pseudonym is:
Identifier for the patient (e.g. {landCode: NL, type: BSN, value: 123456789}).Identifier using HKDF, using a fixed info string that binds the result to the intended recipient.blind_factor (kept locally) and a blinded_input (sent to the PRS).blinded_input and returns the result as a JWE (evaluated_output) encrypted with the public key of the intended recipient.evaluated_output together with the blind_factor (a.k.a. oprf_key) to the recipient as part of a downstream transaction (for example, a GF Localization registration or query).blind_factor to obtain the final, stable pseudonym for that recipient.For more background on how the result is consumed, see GF Localization.
This guide makes the following national choices for pseudonymisation:
info string). The same identifier therefore yields different pseudonyms for different recipients, preventing cross-service correlation.The PRS is the central national service that evaluates blinded inputs and returns a recipient-encrypted JWE. The PRS:
blinded_input values;recipient_organization and recipient_scope;A Pseudonymization Client is typically embedded in or alongside an EHR, PACS, or another generic-function client (e.g. a Localization Client). The client:
Identifier from authoritative source data (e.g. the patient's BSN held in the EHR);info string for the intended recipient and scope;blind_factor confidential;blinded_input (and the recipient identifiers) to the PRS;blind_factor to the recipient as part of a single downstream transaction;blind_factor beyond the lifetime of the transaction.The Recipient (for example, the NVI) is the party for which the pseudonym is intended. The Recipient:
blind_factor provided by the client to obtain the stable, recipient-specific pseudonym;The PRS exposes a single logical transaction: evaluate a blinded input.
The client POSTs the blinded_input together with identifiers for the intended recipient to the PRS:
POST /evaluate HTTP/1.1
Host: <prs-base-url>
Authorization: Bearer <oauth-token-with-scope-prs:read>
Content-Type: application/json
{
"blinded_input": "<base64url-encoded blinded_input>",
"recipient_organization": "ura:90000901",
"recipient_scope": "nationale-verwijsindex"
}
A successful response carries the OPRF evaluation as a JWE encrypted to the recipient's public key:
HTTP/1.1 200 OK
Content-Type: application/json
{
"evaluated_output": "<JWE compact serialization>"
}
The pair (evaluated_output, blind_factor) together forms the patient identifier that is passed to the recipient. See GF Localization — Registration of Localization Records for how this identifier is represented in a List resource.
The Identifier is a small JSON object that uniquely identifies the natural person being pseudonymised. For example, a Dutch citizen identified by its BSN:
{
"landCode": "NL",
"type": "BSN",
"value": "999940003"
}
The Identifier is never sent to the PRS; it is the input to the local HKDF step.
The pseudonym handed to OPRF is derived as:
"{recipient_organization}|{recipient_scope}|v1"The 32-byte HKDF output is then blinded with the OPRF, yielding two values:
blind_factor — a secret kept by the client and later used by the recipient to de-blind the PRS response;blinded_input — the value sent to the PRS for evaluation.Both values are exchanged base64url-encoded.
The PRS response is a JWE compact serialization encrypted to the recipient's public key. The JWE is opaque to the client and is intended for one-time use in a single downstream transaction.
The following snippet (adapted from the reference implementation OPRF.py) shows the client-side HKDF and OPRF steps:
import base64
import json
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
import pyoprf
def create_blinded_input(personal_identifier, recipient_organization, recipient_scope):
info = f"{recipient_organization}|{recipient_scope}|v1".encode("utf-8")
pid = json.dumps(personal_identifier).encode("utf-8")
pseudonym = HKDF(
algorithm=hashes.SHA256(), length=32, salt=None, info=info
).derive(pid)
blind_factor, blinded_input = pyoprf.blind(pseudonym)
return (
base64.urlsafe_b64encode(blind_factor).decode(),
base64.urlsafe_b64encode(blinded_input).decode(),
)
A full reference flow including the call to the PRS is available in the gfmodules-nationale-verwijsindex-registratie-service repository.
A care provider's Localization Client needs to register the existence of patient data at the NVI (see the Radiologist registration use case). Before submitting the List resource it:
Identifier from the patient's BSN;recipient_organization = "ura:<NVI URA>" and recipient_scope = "nationale-verwijsindex";(blind_factor, blinded_input);POST /evaluate on the PRS to obtain the JWE;(evaluated_output, blind_factor) as the NVI patient identifier and places it in List.subject.identifier;A consulting practitioner's client wants to discover which organisations hold data for a patient. The pseudonym is computed in exactly the same way as for registration, but the resulting NVI patient identifier is used as the value of the subject:identifier search parameter on GET [base]/List (see Localization). Because pseudonyms are deterministic for a given recipient and scope, the value will match the pseudonyms used by the registering parties.