E2EE Backend part 1: Homomorphic Encryption

This is the first post in a series exploring a truly privacy-preserving, end-to-end encrypted backend and client.

The goal of this part: a demo that can calculate totals on encrypted user data without ever seeing the plaintext.

Why This Matters

Traditional E2EE protects data at rest and in transit, but the moment the server needs to do anything useful (e.g., compute total revenue, average score, or aggregate telemetry), it must decrypt. That defeats the purpose of zero-trust architecture.

Homomorphic encryption changes the game:
Encrypt(a) + Encrypt(b) = Encrypt(a + b)

The server adds (or multiplies) ciphertexts directly. The result decrypts to the correct plaintext on the client.

The Demo: Homomorphic Sum of 72 Encrypted Numbers

Here’s a complete, runnable Swift example using Apple’s excellent open-source HomomorphicEncryption framework (BFV scheme with UInt64).

// Key parts

let params = try EncryptionParameters<Bfv<UInt64>>(from: .n_8192_logq_40_60_60_logt_26)
let context = try Context(encryptionParameters: params)
let secretKey = try context.generateSecretKey()

// Encrypt each value separately (coefficient encoding, one value per ciphertext)
let (ciphertexts, plaintexts) = try encryptRandomNumbers(numberOfValues: 72, context: context, secretKey: secretKey)

let encryptedSum = try calculateEncryptedSum(ciphertexts: ciphertexts)

// Decrypt & verify
let decrypted = try encryptedSum.decrypt(using: secretKey)
let result = try decrypted.decode(format: .coefficient)[0]

print("Expected: \(plaintexts.reduce(0, +)) | Actual: \(result)")

Output

Expected sum: 41869
Result from homomorphic operation: 41869
Match: ✓ Success!

Repo: github.com/peterspath/HomomorphicSum

Why These Parameters?

Addition of ciphertexts is extremely cheap compared to multiplication (no relinearization needed).

How Addition Works Under the Hood (BFV)

In BFV, a ciphertext encrypts a plaintext polynomial in the ring R_t = Z_t[X]/(X^N + 1). When you add two ciphertexts:

ct₁ + ct₂ = (c0₁ + c0₂, c1₁ + c1₂) mod q

Decrypting this recovers exactly m₁ + m₂ mod t.

Because we used coefficient encoding with one value per ciphertext, each ciphertext holds a single scalar. This is the simplest way to demonstrate correctness before moving to packed/SIMD encoding.


Enjoyed this post?

Well, you could share the post with others, follow me with RSS Feeds, send me a comment via email, and/or leave a donation in the Tip Jar.


Tags

Category:

Year:


#100DaysToOffload 15 of 100