Skip to content

Latest commit

 

History

History
223 lines (144 loc) · 3.9 KB

File metadata and controls

223 lines (144 loc) · 3.9 KB

Zero-Trust File Drop — System Documentation

This document explains where keys are stored, how signing and verification work, and what the server and client are responsible for.


1. Key Ownership and Storage

Public Keys

Public keys are generated by the client during registration and sent to the server.

They are stored server-side at:

storage/users/<canonical_username>/public.pem

Server code reference:

await fs.promises.writeFile(path.join(dir, 'public.pem'), publicKeyPem);

The server exposes public keys via:

GET /api/users/:username/public-key

The server never modifies or generates public keys.


Private Keys

Private keys are generated locally by the client and never leave the client machine.

They are stored locally at:

<project_root>/crypto/keys/_private.pem

Client code reference:

def key_paths(username):
    u = canon(username)
    return (
        CRYPTO_KEYS_DIR / f"{u}_private.pem",
        CRYPTO_KEYS_DIR / f"{u}_public.pem",
    )

The server has no access to private keys at any point.


2. File ID vs Signature

The fileId is NOT a cryptographic signature.

  • fileId is a UUID generated by the server
  • it is only used to locate files on disk

Server code reference:

const id = crypto.randomUUID();
res.json({ fileId: id });

The actual digital signature is stored separately.


3. Where the Signature Lives

The digital signature is stored inside meta.json as:

signatureB64

Client meta creation:

"signatureB64": base64.b64encode(sig).decode(),

The signature is never displayed unless the client explicitly shows it.


4. What Is Signed

The client signs the encrypted file bytes (ciphertext), not the plaintext.

This ensures integrity of the stored and transmitted data.

Client code reference:

sig = sign_data(cipher, sender_priv)

5. Signature Verification Flow

Verification occurs entirely on the client during download.

Steps:

  1. Download meta.json
  2. Download blob.bin (ciphertext)
  3. Fetch sender public key from server
  4. Verify signature against ciphertext
  5. Abort if invalid
  6. Decrypt only if valid

Verification code reference:

sig_ok = verify_signature(ciphertext, signature, sender_pub)
if not sig_ok:
    raise RuntimeError("Signature verification FAILED")

6. Which Keys Are Used

Signing:

  • sender private key

Verification:

  • sender public key (fetched from server)

Crypto implementation reference:

signature = s_private_key.sign(...)
s_public_key.verify(signature, data, ...)

Key Transport Protocol

7. What the Server Does and Does Not Do

The server DOES:

  • authenticate users with JWT
  • store public keys
  • store encrypted blobs and metadata
  • authorize access

The server DOES NOT:

  • decrypt files
  • decrypt AES keys
  • verify signatures
  • access plaintext

All cryptographic trust decisions are enforced client-side.


9. JWT Notes

JWT is used only for authentication with the server.

If the token expires:

  • client must log in again
  • a new token replaces the old one

JWT is unrelated to encryption or signing.


10. Supported File Types

All file types are supported.

The server treats files as opaque encrypted bytes and does not inspect contents.


11. Directory Layout

Server

storage/
└── users/
    ├── _index.json
    ├── alice/
    │   ├── public.pem
    │   └── files/
    │       └── <fileId>/
    │           ├── blob.bin
    │           └── meta.json

Client

crypto/keys/
├── alice_private.pem
├── alice_public.pem

~/.zt_file_client/
└── session.json

12. Relevant Endpoints

POST /api/auth/register
POST /api/auth/login
GET /api/users/:username/public-key
POST /api/files/upload
GET /api/files/:id/meta
GET /api/files/:id/blob
GET /api/files/inbox
GET /api/files/outbox