Twinkle Notes: Cross-platform encrypted notes app
github.com
github.com
The trade-offs for a user who's committed to not paying for the premium features are mainly about convenience and security (having to maintain a fork, and missing out on automatic security updates), which ressembles those they would have to make by pirating a closed-source app.
Right now the license is AGPL, is this temporary (since the website calls the app "Source available" vs "Open-Source")?
PS: The submission title should begin with "Show HN:".
https://github.com/twinkle-labs/twinkle-notes/blob/45206f9d6...
For AES in CBC mode, IVs have two requirements:
1. They must never repeat.
2. They must be unpredictable.
Generating them from a SHA256 hash of some low-entropy data is not a good practice.
https://paragonie.com/blog/2016/05/how-generate-secure-rando...
Furthermore, not authenticating your ciphertext means padding oracle attacks can be launched against the app.
https://github.com/twinkle-labs/twinkle-notes/blob/8ad7d9d0b...
> They must be unpredictable
I am wondering if that is necessary, because the hacker can't perform those attacks without the user's actively using the app at the same time. From what I learned, the attacking process requires the presence of key somewhere. If the attacker can get on user's device while one is using it, then it's almost a hopeless situation. Please educate me if I am wrong.
> I am wondering if that is necessary,
Yes, it is necessary. The IND security of Cipher Block Chaining (CBC) depends entirely on the IV being from a cryptographically secure random generator.
CBC mode requires unique and random IVs. CTR mode requires unique IVs (but can be predictable).
That's why we call the CTR input a nonce (number to be used once) and the CBC input an IV (initialization vector). Since they have different security requirements, we refer to them differently. Unfortunately, some cryptography libraries just name the parameter IV.
Because AES-CTR and ChaCha both refer to it as a nonce, and CBC calls it an initialization vector, the IV/nonce distinction does matter. But if you misuse the terms folks will know what you meant to say. Just don't mix it up when it comes time to implement.
The father I go down this rabbit hole the more I learn security should be like a religion of security (only one without dogma).
Thanks.
To OP, I would suggest using secretstream from libsodium, which abstracts all these problems away for you.
I covered that:
> Furthermore, not authenticating your ciphertext means padding oracle attacks can be launched against the app.
The list above was just the requirements for the initialization vector, not the list of problems with the code.
See the section about "CBC with Counters".
The consequence of the security proof by Rogaway, et al. for CBC mode is that IVs must be unique AND unpredictable for CBC mode to be secure*.
SHA256 is a deterministic pseudorandom function if you know all of the inputs. By studying the source code, we can see what gets fed into the SHA256 inputs. The cost to brute force all possible inputs is definitely much lower than 2^128, therefore it weakens the IND security of the AES-CBC scheme.
CBC security only provides indistinguishability. It isn't secure against adaptive chosen-ciphertext attacks.
That section is talking about using an incrementing counter as the IV--it doesn't say anything about a PRNG being a bad choice (in this case, the PRNG being a SHA256).
>"By studying the source code, we can see what gets fed into the SHA256 inputs. The cost to brute force all possible inputs is definitely much lower than 2^128, therefore it weakens the IND security of the AES-CBC scheme."
Are you talking about brute forcing the IV? The IV is not a secret to anyone--it's usually appended to the cipher text.
Let's try this again.
You have two types of inputs that APIs refer to as "IVs".
Nonces: Must never repeat.
Initialization vectors: Must never repeat AND must be unpredictable.
Counters are acceptable for nonces. When an academic cryptographer says in a security proof that a counter doesn't suffice for the security of a scheme, what they're really saying is that you're in the latter category (must be unpredictable too) rather than the former (where predictable is okay as long as it never repeats).
If it can be predictable, it can be a counter. If it can't be a counter, it must be unpredictable.
The correct way to get an unpredictable IV is to use a CSPRNG.
SHA256(some predictable low-entropy inputs) fails to meet the bar for unpredictability.
> Are you talking about brute forcing the IV? The IV is not a secret to anyone--it's usually appended to the cipher text.
IVs aren't secret, but given the security proof I linked, IVs cannot be predictable. They must be unpredictable.
JS: https://www.npmjs.com/package/sodium-plus (I wrote this one.)
Lisp: https://github.com/orthecreedence/cl-sodium
Java (Android): https://github.com/terl/lazysodium-android
Other bindings: https://libsodium.gitbook.io/doc/bindings_for_other_language...
Can you elaborate on this? Quickly looking over your libsodium based sodium-plus it doesn't seem like it has been audited. (I know libsodium itself had an audit)
Where as for example tweetnacl-js [0] has been audited.
That's not meant to take a dump on your project, but I just don't understand why it is better, or preferable.
Libsodium offers Argon2 password hashing and key stretching, XChaCha20-Poly1305 AEAD constructions, BLAKE2b generic hashing, SipHash-2-4 for collision-resistant hash tables, etc.
Most people who say "use NaCl" really mean "use NaCl/libsodium".
Regarding my project: sodium-plus will wrap sodium-native (a thin Node wrapper to libsodium proper, which has been audited) if it is installed. To date, libsodium.js has not. Therefore, if you care about public code audits, you can use audited libsodium with sodium-plus just by installing sodium-native alongside it.
* If I choose to use this notes app on multiple devices, and am careful enough to be the only person using it and complete any note taking on one device at a time (closing the app properly), I should be able to store and sync the sqlite database on any cloud storage service and wouldn't need the paid sync option, correct? In other words, the sync option makes multiple clients opening and making updates in the same time period a non-issue?
* The costing seems a bit weird when one digs in, because it defaults to counting in one GB increments even though the unit is stated as GBm and compared to things like KWh in the explanation. Even if you store only a few bytes of notes (or a few KBs), the documentation says that you'd still pay a minimum of 1GBm. So that works out to a $7.176 a year if you don't cross 1GB in storage used.
* Since this is open source with AGPL, anyone could fork it to support a custom sync mechanism that allows to store the database on cloud storage/sync services, but would have to make the source available if they distribute the app to others.
It would be quite slow.
> a minimum of 1GBm
Our servers are quite dumb that if you are hosting your space with us, we need to reserve some storage space for you. That's a fixed cost for us even if you are not effectively using that storage.
Your servers not being able to account for smaller storage sizes is a technical limitation that you have built. You can take a look at tarsnap for inspiration on how to account for very small amounts of data. I’m not saying you should account for bytes, but even taking it at a fraction of what you’re currently doing would be better and fairer to paying customers. You would also discourage hoarders who thrive on utilizing a lot more and get subsidized by those who barely store much.
Those kinds of syncing work will files, and treat each individual file as a blob. Apps don't work this way. Apps need databases and fine-grained change tracking. You absolutely don't want to make a change in two places on two separate devices and then have a sync conflict. If you throw the sqlite db file on dropbox, that's exactly what will happen.
You could try to build your app around files, but now you are rebuilding a database (and not a good one).
There are a few services that offer syncing more appropriate for apps, like Apple's CloudKit (I think that's the one?). These could work, but in Apple's case you can only run your your app on macOS/iOS.
There are a few generic syncing layers like PouchDB, and there are tradeoffs with all of those. Some of these tradeoffs are deal breakers. (there aren't a ton of good options yet)
But there are good reasons why you an app can't just eschew syncing onto the user, and assume they'll find a good syncing service. It needs to be tightly integrated with how the database works.
Yes you essentially need to rebuild a database, but this is for example what 1Password did.
But the majority of apps need more complex needs and you definitely should not go and build a new database. The user experience will end up being flaky (syncing problems) and/or slow (the database is not good). Not to mention that now in addition to setting up the app, the user now needs to go sign up for one of those services if they haven't already.
With integrated syncing, the user just fires up the app and syncing works and they never have to care about how it works.
There's a reason a whole field of research exists into things like CRDTs. Distributed apps are not trivial, and only ones with simple data needs could get by without a more rigorous approach.
Thx!
I get: /opt/app.twinkle.notes/twinkle (base) dist:/opt/app.twinkle.notes var:/home/q/.config/twinkle dist=/opt/app.twinkle.notes var=/home/q/.config/twinkle * Twinkle Lisp 2020 *
DevTools listening on ws://127.0.0.1:16780/devtools/browser/6d2dd982-a67d-4915-8517-83d4394054a3 error: file '/opt/app.twinkle.notes/lisp/lib/sqlite3.l': position 13622: line 386: invalid number #1: /opt/app.twinkle.notes/lisp/init.l:42: (load "lib/sqlite3.l") [2020-02-05 23:23:09,394950 init#0] finished due to run time error
This is the deb file on your website.
Personally, I've used Joplin [0] some months ago, and Im using QOwnNotes [1] now. Both are open source, cross platform, and can synchronize with Nextcloud. QOwnNotes may feel a bit faster since its written in Qt/C++ (Joplin is an Electron app).
However, I still find hard to write tables in Markdown manually.
Notes are stored in local db yet notebooks in the db can be mapped on file folders (e.g. folder on DropBox containing html files - each note is a single HTML file).
The note db file can be protected/encrypted by OS means.
Windows/Mac/Linux.
Currently using this Chrome extension: https://chrome.google.com/webstore/detail/marcador/nneoamjib...
Started as just adding bookmarks to your Dropbox, but now it lets you add plain text notes.
And it only has access to its own app directory--not your whole Dropbox!
Personally I'm having a good time with Tiddlywiki with a markdown plugin and nice context-of-backlinks setup (inspired by roamresearch.com )
But, there are so many choices. Does it really have to be html-based?