Klarbefund: self-assessment
Data protection, security and regulatory status of the Klarbefund iPhone app,
assessed by the project itself against the code in clients/ios/ as of
2026-10-15. This is a self-assessment, not a certification, an audit or a
legal opinion. Every "met" below points to the code or test that shows it;
anything not shown is marked open. Accuracy evidence is in
evidence.md; running it on a Mac in run-on-mac.md.
The code references below were last checked against the sources on 2026-10-15,
the same day the evidence was last produced (see the run record in
evidence.md).
Status values: met (implemented and shown in code or tests), partly (implemented in part, or shown only by design), open (not done), n/a (does not apply to this app).
1. Scope
| In scope | Out of scope |
|---|---|
The iOS app (clients/ios/Sources/MeinBefund, Sources/Shared), release build |
The demo hub and its Azure deployment, assessed separately (ADR-045, BSI C5 notes in the CI workflows) |
| Its optional calls to the hub (EHDS connect, ADR-049/054) | Apple's platform (iOS, Secure Enclave, HealthKit), relied on as provided |
| Its optional call to the cloud explanation service (ADR-034) | The model providers behind that service |
2. Data protection (GDPR)
The data processed is health data, a special category under Art. 9 GDPR.
| Principle | Status | How the app meets it | Where |
|---|---|---|---|
| Data protection by default (Art. 25) | met | No account. Scanning, OCR, coding, storage and export run on the device; nothing leaves it unless the person sends it. | Scanning.swift, ReportStore.swift, OnDeviceAnalysis.swift |
| Data minimisation (Art. 5(1)(c)) | met | The store holds no name, birth date or insurance number. A cloud question carries only the values the person ticks, at most 40, and no identifying field; the service enforces this again server-side. | Sources/Shared/CloudAnalysis.swift, CloudAnalysisContractTests.swift, ADR-034 |
| Purpose limitation and consent (Art. 6, 9(2)(a)) | met | Study participation, ePA objection and registry/research use are separate consents, each revocable. A cloud call names its provider in the request itself, so a blanket setting cannot stand in for it. | Sources/Shared/Consents.swift, ConsentsTests.swift, CloudConsentSheet.swift |
| Rights of access, portability, erasure (Art. 15, 17, 20) | met | Full export (JSON, FHIR R4, OMOP CDM, PDF) and full deletion from the app; an EHDS hub account the app created can be deleted by it. | DataExport.swift, ReportExport.swift, OmopExportTests.swift, ADR-054 |
| Security of processing (Art. 32) | met | See the security section. | |
| Transparency (Art. 13) | partly | Permission texts in English and German, privacy manifest declares health data, not linked, not tracked. A full privacy notice for an operator is not part of the repository. | project.yml (usage strings), PrivacyInfo.xcprivacy, LegalViews.swift |
| Transfer to third countries (Art. 44 ff.) | partly | Default is on device: nothing is transferred. The person can switch at any time to the hosted service, whose default is Azure OpenAI in the EU data zone (DataZoneStandard, no transfer), or to their own Azure OpenAI resource or their own Anthropic key. Only Anthropic, hosted or own key, processes in the United States; the app says so on the settings screen. For the hosted Anthropic option the operator needs a processor agreement and a transfer basis. |
ProviderSettings.swift, services/claude-federation/src/providers.ts, ADR-034 |
| Records of processing, DPIA (Art. 30, 35) | open | No record of processing and no DPIA. A DPIA is likely required for any operator (health data, new technology). The project can supply a template; the operator owns it. | |
| Controller role | open | Using the app on one's own device is personal use. Whoever runs the cloud explanation service or a hub the app connects to is a controller or processor and must be named. |
3. Security: BSI TR-03161 part 1 (mobile applications)
Assessed at the level of the test objective groups of TR-03161 part 1, version 3.0. A certification maps each individual requirement against the official PDF and is carried out by a BSI-recognised test laboratory; this table is the input to that gap analysis, not a replacement for it.
| Group | Status | Evidence | Gap |
|---|---|---|---|
| O.Purp: purpose, minimisation | met | Purpose stated in the app and README.md ("Not a medical device"); minimisation and per-call consent as above; no tracking (NSPrivacyTracking false). |
Formal intended-purpose statement (see regulation). |
| O.Arch: architecture | met | On-device by default; the phone holds no API credential (ADR-034); hub accounts only from an App Attest-verified app (ADR-054); provenance model keeps OCR values preliminary (Provenance.swift). |
Threat model written: docs/security/threat-model-klarbefund.md. |
| O.Source: source code | partly | CodeQL on every iOS change (codeql-swift.yml, Scripts/codeql-swift.sh; 0 findings on 2026-10-15); debug-only launch arguments compiled out of release builds (#if DEBUG); Swift 6 strict concurrency; SecurityPostureTests pin the build settings this table cites. |
No obfuscation (open source by design; argue as not applicable). |
| O.TrdP: third-party software | met | No third-party package at all: Package.swift and project.yml declare no external dependency; only Apple frameworks. |
SBOM: docs/sbom/klarbefund-ios.cdx.json, regenerated by scripts/security/generate-sbom.sh --check. |
| O.Cryp: cryptography | met | Only Apple CryptoKit: each record sealed with AES-GCM under a 256-bit key; key in the Keychain as WhenUnlockedThisDeviceOnly; no own cryptography (ReportStore.swift, export-compliance note in project.yml). |
|
| O.Rand: random numbers | met | Keys from SymmetricKey(size: .bits256), Apple's system RNG. |
|
| O.Auth: authentication | met | No local account. Hub: OIDC with PKCE in ASWebAuthenticationSession (OIDCSession.swift), device grant with Keycloak consent (ADR-049), App Attest (ADR-054); tokens in the Keychain, ThisDeviceOnly (EHDSConnection.swift). |
App lock since 2026-10-15: Face ID or Touch ID, passcode as fallback, on by default (AppLock.swift, AppLockPolicy, ADR-060). |
| O.Pass: passwords | n/a | The app handles no passwords; sign-in happens in the system web session. | |
| O.Data: data at rest | met | NSFileProtectionComplete as the default data protection (entitlement); records AES-GCM sealed; store excluded from backup; exports and diagnostics written with complete protection; app switcher covered (PrivacyCover.swift); log lines carry counts and codes only. A cable test failed to copy stored data. |
Independent verification of the cable test. |
| O.Paid: paid resources | n/a | No in-app purchases. | |
| O.Ntwk: network | partly | App Transport Security on, exception only for local networking (project.yml); all hub and service calls over HTTPS. |
No certificate pinning, declined with reasons in ADR-060 (ATS, no exception domain, managed rotation). |
| O.Resi: resilience | open | App Attest proves app integrity to the hub, not to the app itself. | No jailbreak or debugger detection, declined with reasons in ADR-060 (bypassable; the sealed store and App Attest are the controls). |
| O.Plat: platform | met | Camera and HealthKit asked for at the moment of use, HealthKit read-only with no copy kept; no notifications; app switcher covered. |
Backend parts (TR-03161 part 3) apply to the cloud explanation service and the hub, not to the app, and are out of scope here. TR-03161 certification is mandatory for a DiGA; Klarbefund is not one (see below).
4. Regulation
| Framework | Assessment | Status |
|---|---|---|
| Medical device (MDR 2017/745 / IVDR 2017/746) | Intended purpose as stated: store, display and export the values a laboratory printed, and explain what a measurement is. No diagnosis, risk assessment, prognosis or treatment advice; no normal/abnormal verdict. Under MDCG 2019-11 Rev.1 storage, communication and display of data is generally not a medical device function. Because the values come from in-vitro samples, the IVDR, not MDR rule 11, would apply if it qualified (ADR-034). Borderline: the trends screen colours values against published bands (ADR-039). The project's position is that a comparison with a quoted, sourced number is not a finding. | partly: written qualification and an external opinion open |
| DiGA (§ 139e SGB V) | Not applicable: a DiGA must be a CE-marked medical device, and Klarbefund is deliberately not one. Consequently no DiGA-specific certificates (ISO 27001, ISO 13485, TR-03161) are claimed. | n/a |
| EU AI Act (2024/1689) | The optional explanation feature uses a language model, on device by default or a named cloud provider. As long as the app is not a medical device it is not a high-risk system under Art. 6(1). Transparency: the answer must be recognisable as AI-generated (Art. 50). A hosted answer ends with "Answered by CloudConsentSheet.swift); the on-device answer says "Answered by on-device, model Apple on-device model" through the same footer (OnDeviceAnalysis.swift); SecurityPostureTests pins both. |
met: every answer names provider and model |
| EHDS (2025/327) | Primary use: the app reads the person's own record from the hub with consent (ADR-049). If the app is ever offered as claiming interoperability with EHR systems, it becomes a wellness application under Art. 47 and needs the label described there. | n/a today |
| Apple distribution | TestFlight build approved by Apple's beta review for external testers (ADR-050); privacy manifest and App Privacy label declare health data, not linked, not tracked. | met |
5. What would close the open items
Done on 2026-10-15, by the project itself (see
docs/security/security-test-report-2026-10-15.md):
the threat model, the SBOM, a configuration test suite, and a self-conducted
security test with CodeQL, Semgrep, grype, trivy, gitleaks and anonymous
black-box checks of the hub. Still open:
- Written intended-purpose statement and MDCG 2019-11 qualification, with an external opinion on the trends colouring.
- Hosted explanation service: name the operator as controller or processor, sign the processor agreement with Azure (EU data zone, the default), and for the opt-in Anthropic option document the transfer basis or switch it off.
- DPIA template and data-flow diagram for an operator.
- An independent penetration test (a release build on a device, the hub as each role in office hours), and the requirement-level TR-03161 part 1 gap list against the official PDF. The self-conducted test says what such a test would add.
- Done 2026-10-15 (ADR-060): the app lock is implemented, on by default; certificate pinning and jailbreak detection are declined with written reasons. Remaining for a laboratory: judge those reasons.