Encryption Architecture

This page documents the complete security architecture of cloud-share, including every cryptographic operation performed on items in transit.


End-to-end encryption model

Cloud Share encrypts content at the application layer on the sender before it leaves the process memory. The Dev Tunnel and HTTPS add a second layer of transport encryption. The receiver decrypts at the application layer after downloading.

sequenceDiagram
    participant SP as Sender Process
    participant DT as Dev Tunnel / HTTPS
    participant RP as Receiver Process

    SP->>SP: SHA-512(plaintext) → stored
    SP->>SP: AES-256-GCM encrypt<br/>(key=SHA-256(secret), nonce=random 12B)
    SP->>SP: Base64(nonce ‖ cipher ‖ tag)
    SP->>DT: Send over HTTPS/TLS
    DT->>RP: Encrypted payload
    RP->>RP: Base64 decode
    RP->>RP: AES-256-GCM decrypt
    RP->>RP: SHA-512 verify
    RP->>RP: ✅ Plaintext content delivered

Key derivation

Secret generation

On each sender start, a 12-character alphanumeric secret is generated using a cryptographically secure random number generator. It is:

  • Never written to disk
  • Never logged
  • Invalidated when the sender process stops

Key derivation function

flowchart LR
    A["secret\n12-char alphanumeric"] --> B["SHA-256\nUTF-8 bytes of secret"]
    B --> C["32-byte key\n256 bits → AES-256 key"]
ParameterValue
Input12-char alphanumeric secret (e.g. aB3kP9mQ2rTs)
FunctionSHA-256
Output32 bytes (256 bits) — used directly as AES-256 key

This is a one-way transformation: given the 32-byte key, it is computationally infeasible to recover the secret.


AES-256-GCM encryption

Each queue item is individually encrypted with a unique nonce:

Encryption

flowchart LR
    A["SecureRandom\n12 bytes"] --> B[nonce]
    C["SHA-256(secret)"] --> D[key 256-bit]
    B --> E["AES-256-GCM.Encrypt\nkey, nonce, plaintext"]
    D --> E
    F[plaintext] --> E
    E --> G["(ciphertext, auth_tag)"]
    G --> H["Base64(nonce ‖ ciphertext ‖ auth_tag)"]
    H --> I[payload stored in queue]

Decryption

flowchart TD
    A["Base64Decode(payload)"] --> B[bytes]
    B --> C["nonce = bytes[0..11]"]
    B --> D["auth_tag = bytes[last 16B]"]
    B --> E["cipher = bytes[middle]"]
    C --> F["AES-256-GCM.Decrypt\nkey, nonce, cipher, auth_tag"]
    D --> F
    E --> F
    F --> G{Auth tag valid?}
    G -- Yes --> H[plaintext]
    G -- No --> I[❌ Reject — tampered]

If the auth_tag does not match (ciphertext was tampered), decryption throws and the item is rejected.

ParameterValue
AlgorithmAES (Advanced Encryption Standard)
Key size256 bits
ModeGCM (Galois/Counter Mode)
Nonce12 bytes, randomly generated per item
Auth tag16 bytes
EncodingBase64

ℹ️ Info: GCM mode is an authenticated encryption scheme. The authentication tag protects both the ciphertext and any associated data. Any modification to the ciphertext — even a single flipped bit — causes tag validation to fail.


SHA-512 integrity hash

Why both GCM auth tag and SHA-512?

  • The GCM auth tag verifies the ciphertext was not tampered with in transit
  • The SHA-512 hash is computed on the plaintext before encryption, providing an independent integrity check at the content level

This gives defense-in-depth: even if decryption succeeds, an additional application-level hash comparison confirms the content is exactly what the sender intended to share.

Hash computation (sender)

  hash = SHA-512( plaintext_bytes )
  

The hex-encoded hash string is stored alongside the encrypted item in the queue and returned to the receiver in the item metadata JSON.

Hash verification (receiver)

flowchart TD
    A["AES-256-GCM.Decrypt(...)"] --> B[decrypted_bytes]
    B --> C["SHA-512(decrypted_bytes)\n= computed_hash"]
    D[item_metadata.hash\n= expected_hash] --> E
    C --> E{"FixedTimeEquals\ncomputed_hash == expected_hash?"}
    E -- Yes --> F[✅ Accept item]
    E -- No --> G["❌ DELETE /item/{id}\nDiscard content, show error"]

Constant-time comparison

The SHA-512 hash comparison uses constant-time equality (CryptographicOperations.FixedTimeEquals in .NET). This prevents timing side-channel attacks where an attacker could infer the correct hash byte-by-byte by measuring how long the comparison takes.

In standard string comparison, the function returns early when it finds the first differing byte — the time taken leaks information about how many bytes match. Constant-time comparison always takes the same amount of time regardless of where the difference occurs.


What happens on hash mismatch

A hash mismatch is treated as a serious integrity failure:

  1. Receiver UI shows a red error modal: “Hash mismatch — content may have been tampered with”
  2. Receiver sends DELETE /item/{id}?secret=X to the sender
  3. Sender permanently removes the item from the queue
  4. Decrypted content is discarded in memory — never written to disk
  5. The item cannot be received again from the same sender session

Additional security controls

Filename sanitization

File and zip item filenames are sanitized on the sender to prevent path traversal attacks. Characters like ../, ..\\, and null bytes are stripped or rejected before the filename is stored.

File size limits

Item typeLimit
Text10 MB
File100 MB
Zip bundle100 MB

Requests exceeding these limits are rejected with 400 Bad Request before any content is processed.

Request authentication

Every endpoint except GET / requires the ?secret=<token> query parameter. Requests with a missing or incorrect secret receive 401 Unauthorized. The secret is compared in a way that avoids early-exit to prevent timing attacks on the secret itself.

HTTPS transport

All traffic traverses the Dev Tunnel over HTTPS (TLS 1.2/1.3). The tunnel URL uses a valid certificate from *.devtunnels.ms. This provides:

  • Encryption of the HTTP layer (including the secret in query parameters)
  • Server authentication (the tunnel URL is genuine DevTunnels infrastructure)
  • Protection against man-in-the-middle attacks at the transport layer

Security summary

ThreatMitigation
Eavesdropping in transitAES-256-GCM + HTTPS/TLS
Content tampering in transitGCM authentication tag + SHA-512 hash
Unauthorized access to queueSecret token on every API call
Timing attacks on secretConstant-time comparison
Timing attacks on hashFixedTimeEquals constant-time comparison
Path traversal via filenameFilename sanitization
Oversized payload DoS10 MB / 100 MB limits
Persistent access after sessionSecret invalidated on sender stop
Zip content interceptionOptional AES-256 zip-level encryption