├── .gitignore ├── LICENSE ├── README.md ├── creator ├── .gitignore ├── README.md ├── index.html ├── secret-template.html ├── serve.py └── values.js ├── examples ├── bounty.html ├── example-file.html ├── example-image.html └── example-message.html ├── images ├── cryptography.drawio └── cryptography.png └── notes.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Marco Primi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🔐 Portable Secret 2 | 3 | Better privacy, without special software. 4 | 5 | ## TL;DR; 6 | 7 | Portable Secret is a little hack that allows you to: 8 | 9 | - Send encrypted messages/images/files over insecure channels (email, messaging, ...) 10 | - Store sensitive information (passwords, documents) in insecure locations (web, Cloud drives, USB drives) 11 | 12 | It's portable because: **decrypting these secrets does not require special software!** 13 | All you need is a browser. 14 | 15 | I created Portable Secret **to securely exchange documents via email with my mother**, who can't be expected to learn [PGP](https://en.wikipedia.org/wiki/Pretty_Good_Privacy), [age](https://github.com/FiloSottile/age), or similar. 16 | 17 | I also use Portable Secret to store some my most sensitive secrets (private keys, 2FA recovery codes, etc.) 18 | 19 | Finally, I use it to store copies of important documents (like a picture of passport). These documents are accessible to me from anywhere, **even if all my trusted devices have been stolen or lost**. 20 | 21 | Sounds too good to be true? Keep reading. This is for you. 22 | 23 | --- 24 | 25 | Portable Secret is not a product and it is barely a project. 26 | **It is just a neat trick, a *hack***. 27 | 28 | The [source code](https://github.com/mprimi/portable-secret) and [creator tool](https://mprimi.github.io/portable-secret/creator/) are provided as a demonstration. 29 | 30 | **Update January 2023**: [Rocky W.](https://www.rocky.dev/) took this idea and ran with it, creating a beautiful full-fledged product: [PrivacyProtect.dev](https://www.privacyprotect.dev/) 31 | 32 | --- 33 | 34 | ## How it works 35 | 36 | A 'Portable Secret' is simply an HTML file that also contains: 37 | - An encrypted payload 38 | - Some Javascript that calls into the browser's [Web Cryptography APIs](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) 39 | 40 | Any (reasonably modern) web browser can open the file, even without an internet connection! 41 | If you know the password, you can recover the secret within. 42 | 43 | [Here's an example](https://mprimi.github.io/portable-secret/examples/example-message.html) (the password is `banana`) 44 | 45 | Notice that the file is self-contained and has no external dependencies! 46 | It can be carried on a USB drive and decrypted without an internet connection, on any device that has a web browser. 47 | 48 | To understand how it works, go ahead and [create yourself a secret](https://mprimi.github.io/portable-secret/creator/). Download and inspect the generated Portable Secret. 49 | 50 | The embedded code is straightforward: 51 | - Take the password and generate a key 52 | - Use the key to decrypt the payload 53 | - Display the decrypted secret 54 | 55 | --- 56 | 57 | ## How I use Portable Secret 58 | 59 | ### Private communication 60 | 61 | Do you want to communicate privately with people, but you can't expect them to learn how to use PGP? 62 | 63 | Send a Portable Secret. For example as email attachment. 64 | 65 | > Hey ___, attached to this email is the PDF and data you requested, but it's encrypted. Never double-click on attachments, it's dangerous! Give me a call when you get this, and I'll show you how to read it. 66 | 67 | Whenever they call me, I tell them: 68 | > Right-click on the attachment and 'Open with...' any browser. 69 | > The password is 'banana_split'. 70 | > Now you can save the decrypted PDF. 71 | 72 | ### Store top-level secrets 73 | 74 | Some secrets don't belong in your password manager. Things like backup private keys, 2FS recovery keys, wallet keys, safe combinations, treasure maps, etc. 75 | 76 | Using Portable Secret, I can keep copies of these critical keys all over the place (Cloud drives, USB drives, all my devices, etc). 77 | 78 | Even if some of these copies end up stolen (e.g. I lose a USB stick), I am not concerned anyone will be able to recover the secrets within. 79 | 80 | The passwords are long sequence of words that are trivial for me to remember (thanks to the hints provided), but impossible for anyone else to guess or crack. 81 | 82 | ### Emergency documents on the go 83 | 84 | Have you ever gotten stranded in a foreign country without any of your devices or documents? It's not fun. 85 | 86 | I keep a copy of my passport encrypted on the internet. It's just an HTML file, it's easy to host. If I find myself stranded again, I can use any computer/device to retrieve it. 87 | 88 | [Here's an example of ID document safely encrypted in plain sight](https://mprimi.github.io/portable-secret/examples/example-image.html). 89 | 90 | --- 91 | 92 | ## Cryptography 93 | 94 | The following is a schematic representation of the encryption scheme implemented by the PortableSecret creator, as well as the decryption happening in the PortableSecret itself. 95 | 96 |  97 | 98 | This scheme and its parameters follow best practices and guidelines recommended by [NIST](https://www.nist.gov/cryptography) and [OWASP](https://owasp.org). 99 | 100 | ### Encryption 101 | 102 | [`AES-GCM`](https://en.wikipedia.org/wiki/Galois/Counter_Mode) is used for Symmetric Authenticated Encryption. 103 | 104 | - Symmetric because the same key is used to encrypt and decrypt 105 | - *Authenticated* because it can verify the message **integrity** (if the ciphertext is tampered with, then it will fail to decrypt. As opposed to producing an invalid plaintext) 106 | 107 | ### Key derivation 108 | 109 | [`PBKDF2`](https://en.wikipedia.org/wiki/PBKDF2) is used to turn a text password into an AES key. 110 | The purpose of this module is slowing down dictionary-based brute-force attacks. 111 | 112 | Unfortunately, the Web Crypto APIs do not support stronger KDFs such as [`scrypt`](https://en.wikipedia.org/wiki/Scrypt), [`bcrypt`](https://en.wikipedia.org/wiki/Bcrypt), or [Argon2](https://en.wikipedia.org/wiki/Argon2). 113 | 114 | --- 115 | 116 | ## Bounty: Crack me if you can 117 | 118 | Do you think this cannot possibly be secure? Great, prove it. 119 | 120 | [This secret](https://mprimi.github.io/portable-secret/examples/bounty.html) contains the recovery key for a Bitcoin wallet. Crack it and take my money! 121 | 122 | --- 123 | 124 | ## Miscellaneous 125 | 126 | ### Choosing a good password 127 | 128 | Choosing a strong-enough password is key (pun intended). 129 | 130 | Eventually I'll fill in this paragraph. For now all you get is the obligatory XKCD: [correct-horse-battery-staple](https://xkcd.com/936/) 131 | 132 | ### On tools 133 | 134 | Portable Secret is a *tool*. As such, it can be used *wrong* (e.g. weak password), or used to do bad things (e.g., exfiltrate intellectual property). 135 | 136 | I cannot take responsibility for such misuse any more than a hammer manufacturer can take responsibility for me hammering my thumb, or using the hammer to attack someone. 137 | 138 | ### Prior art 139 | 140 | I came up with Portable Secret on my own, but I have since found a few projects that do something similar. 141 | 142 | [StatiCrypt](https://github.com/robinmoisson/staticrypt) 143 | 144 | [PolySafe](https://github.com/fmeum/polysafe) 145 | 146 | [`hscrypt`](https://smondet.gitlab.io/hscrypt/) 147 | 148 | [Hypervault](http://hypervault.github.io) 149 | 150 | [Encrypted HTML Vault](https://github.com/ccorcos/encrypted-html-vault) 151 | 152 | [UltraCrypt](https://9p4.github.io/hackna/) 153 | 154 | [html-vault](https://github.com/dividuum/html-vault) 155 | 156 | [Password Protect My File](https://github.com/louissobel/ppmf) 157 | 158 | [Emergency Contacts](https://github.com/jwillmer/emergency-contacts) 159 | 160 | [Digi-Cloak](https://github.com/kaushalmeena/digi-cloak) 161 | 162 | [Pretty Easy Privacy](https://prettyeasyprivacy.xyz/) 163 | 164 | If you are aware of other similar/related projects, please let me know and I'll link them here. 165 | 166 | ### Feedback 167 | 168 | I would love to hear what you think of this project, good, bad, or ugly. 169 | 170 | Please use [GH issue](https://github.com/mprimi/portable-secret/issues) to report a problems and make suggestions. For everything else, start a [GH Discussion](https://github.com/mprimi/portable-secret/discussions). 171 | 172 | You can also find my email on my homepage (linked from my GH profile). 173 | 174 | Or discuss on [HackerNews](https://news.ycombinator.com/item?id=34083366) 175 | 176 | ## References 177 | 178 | https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API 179 | 180 | https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html 181 | 182 | https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html 183 | 184 | https://en.wikipedia.org/wiki/Galois/Counter_Mode 185 | 186 | https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf 187 | 188 | https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs 189 | -------------------------------------------------------------------------------- /creator/.gitignore: -------------------------------------------------------------------------------- 1 | cert.crt 2 | private.key 3 | -------------------------------------------------------------------------------- /creator/README.md: -------------------------------------------------------------------------------- 1 | # Portable Secret Creator 2 | 3 | Creator could be embedded in a single HTML file with inlined Javascript, and be portable. 4 | 5 | However this has some issues: 6 | - Inlining all JS requires 2 levels of escaping, making it hard to modify/maintain those files 7 | - Some browsers disable `window.crypto` on local files and non-TLS servers 8 | 9 | So the easiest way to run the creator locally is: 10 | 11 | ``` 12 | openssl req -new -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out cert.crt -keyout private.key 13 | 14 | python3 serve.py 15 | ``` 16 | 17 | Then visit https://127.0.0.1:8443/index.html 18 | 19 | Notice that some browsers are not happy with the self-signed certificate. 20 | 21 | All of this is just for local development. 22 | You can use the creator at: https://mprimi.github.io/portable-secret/creator/ or host it on your own (HTTPS) server. 23 | -------------------------------------------------------------------------------- /creator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 35 | 322 | 323 | 324 |
326 | This tool runs entirely in your browser window. The secret never leaves your computer!
327 | But don't take my word for it. Check out the source code!
328 |
Secret type:
345 | 353 | 354 | 355 | 356 |372 |
⚠️ Don't modify these settings unless you know what you are doing
386 |Salt and IV are random input coming straight from your browser's Random Number Generator. Do not reuse across messages.
395 |Higher iteration count slow down key generation, making dictionary-based attack harder.
401 |
230 | This file contains a secret (message, file, or image) that can be recovered if you know the password.
231 | The secret can be decrypted without an internet connection, this file has no dependencies and no data leaves the browser window.
232 |
{{PASSWORD_HINT}}237 |
230 | You can check the status of the wallet here: here. 231 |
232 | 233 |4 words, joined by dots, capitalized as necessary 236 | 237 | 1. The swimming pool accident -- her first name 238 | 2. Name of the flowers on the balcony that I used to care for 239 | 3. Name of the guy that really made a difference (The Encounter, 2017) 240 | 4. The gift from Veronica (2 words without space) 241 |242 |
230 | This file contains a secret (message, file, or image) that can be recovered if you know the password.
231 | The secret can be decrypted without an internet connection, this file has no dependencies and no data leaves the browser window.
232 |
A yellow edible fruit with elongated shape. 6 letters, all lowercase 237 | 238 | (N.B. this is a _weak_ password. But for this example it will do) 239 |240 |
229 | This file contains a secret (message, file, or image) that can be recovered if you know the password.
230 | The secret can be decrypted without an internet connection, this file has no dependencies and no data leaves the browser window.
231 |
A yellow edible fruit with elongated shape. 6 letters, all lowercase 236 | 237 | (N.B. this is a _weak_ password. But for this example it will do) 238 |239 |
229 | This file contains a secret (message, file, or image) that can be recovered if you know the password.
230 | The secret can be decrypted without an internet connection, this file has no dependencies and no data leaves the browser window.
231 |
A yellow edible fruit with elongated shape. 6 letters, all lowercase 236 | 237 | (N.B. this is a _weak_ password. But for this example it will do)238 |