Security concept¶
How Krywerk protects data, whom it trusts in doing so, which methods it uses and why – and where
its limits are. Audience: IT administrators, data protection officers, auditors.
Please report vulnerabilities as described in SECURITY.md.
As of version 0.9 (2026-10). An external security audit is still pending.
1. Security goals¶
| Goal | What it means for Krywerk |
|---|---|
| Confidentiality | File contents and file names are readable only by authorized people – not by the cloud provider, not by the operator of the key server, not by third parties with access to the storage location. |
| Integrity | Modified or swapped encrypted blocks are detected on read (modern format). |
| Availability | Without the password or access, data stays unreadable; this is why team folders have a recovery key and emergency access. |
| Compatibility | Open, documented formats (Cryptomator format 8, Hub protocol); no dependency on Krywerk. |
2. Threat model¶
Protected against:
- Cloud providers, NAS operators, lost USB sticks: they only see ciphertext and encrypted names.
- Operators of the key server and attackers who copy the server database: vault keys are stored there only encrypted for individual people; private keys only encrypted with the person's unlock code or the device key.
- Tampering with encrypted files (modern format): AES-GCM detects any change per block.
- Former members of a team folder: “Renew key” creates a new vault key; new files are no longer readable for them.
Not protected against (deliberate limits):
- Malware or attackers on a computer on which a drive is unlocked – they see what the signed-in person sees.
- Metadata at the storage location: the number, approximate size, timestamps and folder structure (depth) of the files remain visible.
- Anyone who once had a vault key can keep decrypting data they already downloaded. “Renew key” only protects future changes.
- A malicious key server can deny access (availability) and see metadata (who is a member of which group/folder). See section 5 on the exchange of keys.
- The legacy Boxcryptor format has no integrity protection for contents (see 4.3).
3. Trust boundaries¶
flowchart LR
subgraph T1["Trusted: the person's computer (unlocked)"]
APP["Krywerk app / krywerk-mount"]
KEYS["Device key<br/>DPAPI · Keychain · Secret Service"]
end
subgraph T2["Partly trusted: organization"]
KS["Krywerk Server<br/>encrypted keys, metadata"]
IDP["Identity provider<br/>confirms identity"]
end
subgraph T3["Not trusted"]
CLOUD["Storage location (cloud, NAS)<br/>ciphertext only"]
NET["Network"]
end
APP -- "TLS (HTTPS enforced)" --> KS
APP -- "ciphertext only" --> CLOUD
APP -. "browser, PKCE" .-> IDP
KS -- "JWKS" --> IDP
4. Methods and rationale¶
Krywerk implements no cryptographic primitives of its own. All primitives come from
reviewed, widely used Rust libraries (RustCrypto: aes, aes-gcm, aes-siv, aes-kw,
scrypt, pbkdf2, hmac, sha2, p384, rsa; ring/rustls for TLS; jsonwebtoken).
Random numbers come exclusively from the operating system (OsRng). Key material is overwritten
after use (zeroize). Comparisons of MACs, tokens and checksums run in constant time.
Custom logic exists only where a format requires it; it is tested against reference vectors: Concat KDF for JWE (RFC 7518 § 4.6.2) and Shamir secret sharing over GF(2⁸) for emergency access (multiplication without data-dependent branches, test vectors from FIPS 197).
4.1 Personal vaults and team folders (Cryptomator format 8)¶
| Purpose | Method | Why |
|---|---|---|
| File content | AES-256-GCM, 32 KiB blocks, 96-bit random nonce per block, block number and file nonce as associated data | Authenticated encryption: detects modification, swapping and truncation of blocks; standard of the format |
| File and folder names | AES-SIV (RFC 5297), folder ID as associated data | Deterministic (needed to find a file by its name), misuse-resistant; moving a ciphertext into a different folder makes it invalid |
| Master key from password | scrypt (N = 32768, r = 8, p = 1) → AES Key Wrap (RFC 3394) | Memory-hard against GPU/ASIC attacks; parameters as in the Cryptomator apps (compatibility) |
| Vault configuration | JWT, HS256/HS512 signed with the master key | Detects tampering with the format information |
4.2 Team folders (Hub protocol)¶
| Purpose | Method | Why |
|---|---|---|
| Vault key per person | JWE ECDH-ES with P-384, content A256GCM |
The server only stores ciphertext for the person's public key; P-384 is prescribed by the Hub protocol (CNSA-compliant) |
| The person's private keys | JWE PBES2-HS512+A256KW with the unlock code, ≥ 1,000,000 iterations |
The server never knows the unlock code; many iterations slow down guessing |
| Device | separate P-384 key per device, private part in the operating system's key store | The device unlocks without the unlock code; losing a device affects only that device |
| Signatures | ES384 | Prescribed by the protocol |
| Sign-in | OpenID Connect, authorization code with PKCE (S256), loopback redirect to 127.0.0.1 with a random path | State of the art for desktop apps (RFC 8252); no client secret in the app |
| Transport | HTTPS with rustls; HTTP only for localhost |
Plain-text HTTP to remote servers is ruled out |
| Recovery key | 44 words (master key + CRC-32) | Prescribed by the Hub format; printed and stored offline |
| Emergency access | Shamir secret sharing (k of n), shares encrypted per council member with JWE | No single person can open the vault alone |
4.3 Boxcryptor format (transition only)¶
Krywerk reads and writes the legacy format only so that existing data stays usable and can be migrated. The format is fixed and has known weaknesses:
| Purpose | Method | Assessment |
|---|---|---|
| File content | AES-256-CBC per 4 KiB block, block IV from HMAC | No integrity protection of contents – modifications are not detected |
| File key | RSA-4096 OAEP (SHA-1) | Secure as a method; see the note on RUSTSEC-2023-0071 |
| Account password | PBKDF2-HMAC-SHA512 | Not memory-hard |
| File names | AES-CFB-128, custom encoding | Deterministic, without integrity protection |
This is why Krywerk recommends migrating to the modern format and labels Boxcryptor drives as “Boxcryptor (legacy)”.
RUSTSEC-2023-0071 (Marvin attack on rsa): An attacker who can time many RSA decryptions
of a service could reconstruct the private key. Krywerk decrypts RSA only locally when opening
your own Boxcryptor data; there is no service that decrypts for third parties and therefore no
timing oracle. The advisory is excluded in deny.toml with a justification and will be removed
once a fixed rsa version is available.
5. Key server: permissions and limits¶
- Authentication: Every API request needs an access token from the identity provider. The server
checks the signature (asymmetric algorithms only, no
none/HS*), the issuer (iss), expiry (exp, 30 s tolerance), and that the token was issued for an allowed client (audorazp); ID tokens are rejected. - Authorization: Every route checks ownership or role (person, group, vault role); other people's objects return 403/404.
- Web administration: separate sign-in (code flow with PKCE,
state,nonce), session cookie__Host-…,HttpOnly,Secure,SameSite=Lax; CSRF token and origin check on every form; strict Content Security Policy without scripts. Only a hash of the session ID is stored. - Overload: rate limiting per client IP for the API (default 6000/min, generous because of NAT
in schools) and for the web administration sign-in (10/min), followed by HTTP 429. Behind a
reverse proxy, only the last
X-Forwarded-Forentry, appended by your own proxy, counts. - Web administration sessions: Locking a person takes effect immediately (every request checks
the account). Roles come from the sign-in token; if a role is revoked at the identity provider,
this applies from the next sign-in, at the latest after
web.session_hours(default 8 h). - Headers: CSP with
frame-ancestors 'none',X-Content-Type-Options,Referrer-Policyon all responses,Cache-Control: no-storefor the API, HSTS with HTTPS. - Database: parameterized SQL statements only; versioned migrations.
- Audit log: who changed what and when (members, access grants, emergency); IP addresses only
with
audit.record_ip; retained for 365 days by default. - What the server sees: names, email addresses, groups, devices, access grants and encrypted keys – never files, file names, passwords, unlock codes or decrypted keys.
Exchange of public keys (web of trust, spec §16a): When granting access, the app encrypts the vault key to the public key that the server supplies for the receiving person. So that a manipulated server cannot slip in a key of its own:
- People compare fingerprints (SHA-256 over the RFC 7638 thumbprints of both public keys)
outside the server (by phone, in person) and thereby confirm the other person's keys: the app
signs them with its own ECDSA key (JWS ES384) and stores the signature on the server. At least
max(wotIdVerifyLen, 8)characters must be typed in – a server setting of 0 or 2 is not adopted, because so few characters could be matched with a few hundred generated keys. - The server stores signatures only if they are valid with the caller's stored key and refer to
exactly the target's current keys, and it returns the shortest chain of trust (at most
wot.max_depth + 1links). Every chain is verified in the app against your own key stored locally; the server is not trusted for this check. - “Grant access” in the app and
krywerk-hub freigebenonly grant access to verified people; unverified people only after an explicit selection with a warning or with--auch-unbestaetigt. The verification is computed for exactly the keys that are being encrypted to.
Remaining limits:
- Your own key – trust on first use: The app learns your own key when it decrypts the private
keys with the unlock code (unlocking a device, first verification or first display of the
fingerprint) or creates them itself (setting up the account), and stores it locally
(
hub-wot-keys.json, public keys only). It does not learn it from the device JWE, which the server could construct itself for the public device key. If the server later reports different keys of your own, nobody counts as verified any more until the new unlock code is entered. - Emergency access: The verification status of council members and requesters is only displayed (as in Cryptomator Hub), not enforced. The request key of an emergency request is not signed.
- Verifying is voluntary. Anyone who selects unverified people despite the warning trusts the server.
- Krywerk Server does not yet show which signatures have become invalid due to key changes; the app reports such people as “not verified”.
6. Desktop app¶
- Passwords are only remembered on request and are then stored in the operating system's key store (Windows DPAPI, macOS Keychain, Linux Secret Service).
- The log (
log.txt) contains no passwords, keys or file contents. - The user interface (Tauri) may only call the app's own commands; Content Security Policy
default-src 'self', no loading of content from the internet. - No telemetry, no crash reports, no automatic update check.
7. Mapping to the OWASP Top 10 (2021)¶
| Category | Measures | Open |
|---|---|---|
| A01 Broken access control | check per route, roles from the token, IDs as UUIDs | Web of trust (A-14) |
| A02 Cryptographic failures | reviewed libraries only, HTTPS enforced, see section 4 | – |
| A03 Injection | parameterized SQL, automatic HTML escaping, CSV formulas neutralized | – |
| A04 Insecure design | threat model (section 2), clean-room specification | Web of trust (A-14) |
| A05 Security misconfiguration | required settings checked at startup, unknown options rejected, no default passwords | – |
| A06 Vulnerable and outdated components | cargo deny and cargo audit in CI, SBOM per release |
– |
| A07 Identification and authentication failures | OIDC with PKCE, check of aud/azp, short web sessions |
– |
| A08 Software and data integrity failures | AES-GCM/SIV in the vault, signed vault configuration, Cargo.lock |
Signed installation packages (HUMAN) |
| A09 Security logging and monitoring failures | audit log of administrative actions, no secrets in logs | – |
| A10 SSRF | the server only fetches from the configured identity provider | – |
8. Verification¶
- Unit and integration tests for all crypto building blocks with test vectors (RFC, FIPS, vectors of the reference Hub, cross-checks with Cryptomator's Java libraries).
- End-to-end tests against Krywerk Server and the official Cryptomator Hub.
- In every CI run:
cargo deny(security advisories, licenses, sources),gitleaksover the entire Git history, Clippy without warnings.