Skip to content

Commit 81f0d39

Browse files
authored
Merge pull request #39 from merlos/feature/zero-knowledge
Feature: Enable Zero knowledge
2 parents ed7084e + 2d4c31f commit 81f0d39

18 files changed

Lines changed: 796 additions & 170 deletions

File tree

.ruby-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.3.6
1+
3.4.4

Gemfile.lock

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,12 @@ GEM
7878
base64 (0.2.0)
7979
bcrypt (3.1.20)
8080
bcrypt_pbkdf (1.1.1)
81-
bcrypt_pbkdf (1.1.1-arm64-darwin)
82-
bcrypt_pbkdf (1.1.1-x86_64-darwin)
8381
benchmark (0.4.0)
8482
bigdecimal (3.1.8)
8583
bindex (0.8.1)
8684
bootsnap (1.18.4)
8785
msgpack (~> 1.2)
88-
brakeman (6.2.2)
86+
brakeman (7.1.1)
8987
racc
9088
builder (3.3.0)
9189
capybara (3.40.0)
@@ -160,6 +158,7 @@ GEM
160158
marcel (1.0.4)
161159
matrix (0.4.2)
162160
mini_mime (1.1.5)
161+
mini_portile2 (2.8.9)
163162
minitest (5.25.4)
164163
msgpack (1.7.5)
165164
net-imap (0.5.1)
@@ -177,17 +176,24 @@ GEM
177176
net-protocol
178177
net-ssh (7.3.0)
179178
nio4r (2.7.4)
180-
nokogiri (1.17.1-aarch64-linux)
179+
nokogiri (1.18.10)
180+
mini_portile2 (~> 2.8.2)
181181
racc (~> 1.4)
182-
nokogiri (1.17.1-arm-linux)
182+
nokogiri (1.18.10-aarch64-linux-gnu)
183183
racc (~> 1.4)
184-
nokogiri (1.17.1-arm64-darwin)
184+
nokogiri (1.18.10-aarch64-linux-musl)
185185
racc (~> 1.4)
186-
nokogiri (1.17.1-x86-linux)
186+
nokogiri (1.18.10-arm-linux-gnu)
187187
racc (~> 1.4)
188-
nokogiri (1.17.1-x86_64-darwin)
188+
nokogiri (1.18.10-arm-linux-musl)
189189
racc (~> 1.4)
190-
nokogiri (1.17.1-x86_64-linux)
190+
nokogiri (1.18.10-arm64-darwin)
191+
racc (~> 1.4)
192+
nokogiri (1.18.10-x86_64-darwin)
193+
racc (~> 1.4)
194+
nokogiri (1.18.10-x86_64-linux-gnu)
195+
racc (~> 1.4)
196+
nokogiri (1.18.10-x86_64-linux-musl)
191197
racc (~> 1.4)
192198
ostruct (0.6.1)
193199
parallel (1.26.3)
@@ -310,16 +316,16 @@ GEM
310316
fugit (~> 1.11.0)
311317
railties (>= 7.1)
312318
thor (~> 1.3.1)
313-
sqlite3 (2.4.1-aarch64-linux-gnu)
314-
sqlite3 (2.4.1-aarch64-linux-musl)
315-
sqlite3 (2.4.1-arm-linux-gnu)
316-
sqlite3 (2.4.1-arm-linux-musl)
317-
sqlite3 (2.4.1-arm64-darwin)
318-
sqlite3 (2.4.1-x86-linux-gnu)
319-
sqlite3 (2.4.1-x86-linux-musl)
320-
sqlite3 (2.4.1-x86_64-darwin)
321-
sqlite3 (2.4.1-x86_64-linux-gnu)
322-
sqlite3 (2.4.1-x86_64-linux-musl)
319+
sqlite3 (2.7.4-aarch64-linux-gnu)
320+
sqlite3 (2.7.4-aarch64-linux-musl)
321+
sqlite3 (2.7.4-arm-linux-gnu)
322+
sqlite3 (2.7.4-arm-linux-musl)
323+
sqlite3 (2.7.4-arm64-darwin)
324+
sqlite3 (2.7.4-x86-linux-gnu)
325+
sqlite3 (2.7.4-x86-linux-musl)
326+
sqlite3 (2.7.4-x86_64-darwin)
327+
sqlite3 (2.7.4-x86_64-linux-gnu)
328+
sqlite3 (2.7.4-x86_64-linux-musl)
323329
sshkit (1.23.2)
324330
base64
325331
net-scp (>= 1.1.2)

README.md

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,75 @@
11
# Lacrado: share self-destroying messages
22

3-
Lacrado is a simple web application that allows users to create messages that will be self destroyed after reaching a maximum number of views (1 by default) and/or after an expiration date (3 days).
3+
![Lacrado](./public/lacrado.gif)
44

5-
The typical use case scenario would be to share a credential via email. Whereas if you send directly the credential, it will be stored in the Inbox permanently, by sending a self destroying message link this can be avoided (and yes, still the receiver can do crazy things...). Messaging tools such as Signal WhatsApp or Telegram support a similar feature, so, this is just an alternative.
5+
Lacrado is a simple web application that allows users to create messages that will be self destroyed after reaching a maximum number of views (1 by default) and after an expiration date (3 days).
66

7-
Lacrado is a simple ruby on rails application.
7+
A typical use case scenario would be to share a credential via email. Whereas if you send directly the credential, it is very probable that the receiver may forget to delete them from the Inbox. By sending link to the self-destroying message, this risk can be avoided (... and, yes, the receiver still can do crazy things such as copying the credentials into a text file kept in the Desktop...).
8+
9+
Messaging tools such as Signal, WhatsApp or Telegram allow to send messages that are automatically deleted after being viewed. This is just an alternative.
10+
11+
## How does it work?
12+
13+
Lacrado is a Ruby on Rails application with client-side JavaScript that implements **zero-knowledge encryption**. This means the server never has access to the plain text content or encryption keys.
14+
15+
### Zero-Knowledge Encryption Flow
16+
17+
Let's say Bob wants to send an ephemeral self-destroying message to Alice:
18+
19+
#### 1. Creating a Message
20+
21+
1. **Bob writes the message** in his browser
22+
2. **Optional**: Bob sets a secondary password (password2) for additional security
23+
3. **Client-side encryption**:
24+
- Browser generates a random 16-character encryption key (password1)
25+
- Message is encrypted using AES-256-GCM with PBKDF2 key derivation (100,000 iterations)
26+
- Encryption uses password1 and password2 (if set)
27+
4. **Sent to server**: Only the encrypted content, view limit, expiration time, and a boolean flag indicating if password2 was set
28+
- ⚠️ The server **never** receives the plain text, password1, or password2 on this process.
29+
5. **Server stores**: Encrypted blob with metadata (views remaining, expiration time, password2_present flag)
30+
6. **Browser constructs URL**: `https://lacrado.example.com/{message_id}#{password1}`
31+
- The `#{password1}` hash fragment is not shared with server in this process.
32+
7. **Bob shares the link** with Alice
33+
34+
#### 2. Viewing a Message (non zero-knowledge)
35+
36+
1. **Alice opens the link**: `https://lacrado.example.com/{message_id}#{password1}`
37+
- ⚠️ Note: While the HTTP spec says hash fragments aren't sent to servers we've seen them appear.
38+
To ensure the server never has all the data to decrypt the message there are two options:
39+
1. Share the link without password1
40+
2. Add password2.
41+
42+
3. **Server responds with**: Encrypted content and password2_present flag (no decryption keys)
43+
44+
4. **Client-side decryption**:
45+
- Browser reads password1 from the URL hash (`window.location.hash`)
46+
- If password2_present is true, prompts Alice for password2
47+
- Decrypts the message locally using password1 and password2
48+
5. **Message displayed**: Alice sees the decrypted content
49+
50+
6. **View counter updated**: Browser notifies server to decrement views (no plain text sent)
51+
52+
7. **Auto-destruction**: If views reach zero, the message is permanently deleted from the database
53+
54+
### Short URLs (Without Password1)
55+
As we mentioned `#password1`shared with the server. Lacrado also supports short URLs without password1 in the hash: `https://lacrado.example.com/{message_id}`
56+
57+
When using short URLs:
58+
- Alice must manually enter the password1 (16-character key)
59+
- Bob shares password1 separately (e.g., via SMS, phone call)
60+
- Zero-knowledge guarantees apply
61+
- Useful for maximum security or when hash URLs are problematic
62+
63+
### Security Features
64+
65+
- **Zero-Knowledge**: Using short URLs and/or password2, the server does not have the keys to decrypt the message.
66+
- **Client-side encryption/decryption**: All cryptographic operations happen in the browser
67+
- **Web Crypto API**: Uses browser-native AES-256-GCM encryption
68+
- **Strong key derivation**: PBKDF2 with 100,000 iterations (16 character key)
69+
- **URL hash for password1**: Never sent to server in HTTP requests
70+
- **Optional password2**: Additional layer of security shared out-of-band
71+
- **Self-destruction**: Messages auto-delete after view limit or expiration
872

9-
TODO -- Explain the technicalities of how lacrado works.
10-
TODO -- Threat model
1173

1274
## Development
1375

app/assets/stylesheets/application.css

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,66 @@ fieldset {
324324
background-color: #ffff99; /* Light yellow color, similar to a marker */
325325
padding: 0 2px; /* Optional: Adds a slight padding for better marker effect */
326326
border-radius: 2px; /* Optional: Slightly round the edges */
327+
}
328+
329+
.message-destroyed {
330+
background-color: #ffcccc; /* Light red/pink color */
331+
color: #cc0000; /* Dark red text */
332+
padding: 0 4px;
333+
border-radius: 2px;
334+
font-weight: bold;
335+
}
336+
337+
/* Decrypted message paper style */
338+
.decrypted-message-paper {
339+
background-color: #f9f7f1; /* Slightly off-white paper color */
340+
font-family: 'Courier New', 'Courier', monospace;
341+
font-size: 1.2em;
342+
line-height: 1.5em;
343+
padding: 2em;
344+
margin: 2em 0;
345+
border: 1px solid #d0cdc1;
346+
border-radius: 4px;
347+
box-shadow:
348+
0 1px 3px rgba(0,0,0,0.12),
349+
0 1px 2px rgba(0,0,0,0.24),
350+
inset 0 0 50px rgba(0,0,0,0.02);
351+
color: #2c2c2c;
352+
white-space: pre-wrap; /* Preserve whitespace and line breaks */
353+
word-wrap: break-word; /* Break long words */
354+
max-width: 100%;
355+
overflow-wrap: break-word;
356+
}
357+
358+
/* Add a subtle texture effect */
359+
.decrypted-message-paper::before {
360+
content: '';
361+
position: absolute;
362+
top: 0;
363+
left: 0;
364+
right: 0;
365+
bottom: 0;
366+
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAJElEQVQYV2NkYGD4z8DAwMgABXABRiYGBgY0SRgfxocJwAQBABJuAwV4RQwuAAAAAElFTkSuQmCC');
367+
opacity: 0.03;
368+
pointer-events: none;
369+
}
370+
371+
/* Decrypt page navigation links */
372+
.decrypt-page-links {
373+
margin-top: 2em;
374+
text-align: center;
375+
}
376+
377+
.decrypt-page-links p {
378+
color: #666;
379+
font-size: 0.9em;
380+
}
381+
382+
.decrypt-page-links a {
383+
color: #007bff;
384+
text-decoration: none;
385+
}
386+
387+
.decrypt-page-links a:hover {
388+
text-decoration: underline;
327389
}

0 commit comments

Comments
 (0)