Skip to content

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 (aud or azp); 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-For entry, 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-Policy on all responses, Cache-Control: no-store for 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 + 1 links). 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 freigeben only 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), gitleaks over the entire Git history, Clippy without warnings.