There isn’t much point to this post, but I was fooling around with something and want to record what I did. Just so I can refer to it later. š
FSNotes is a not taking app for macOS and iOS. It’s nice, and I used it for a while, but I don’t really use it that much coz I prefer Bear. One thing that caught my attention with FSNotes was that it supports storing secure notes.
That is neat! I’ve always wanted something that just lets me store encrypted text notes. Back in my Windows days (years ago!) I used to use LockNote (which seems to be still around). You create a note and the file itself is encrypted – as opposed to having an encrypted VHD file or similar and storing plain text notes in that.
Yes Bear encrypts notes, but it puts them in the cloud instead of having it locally. Ditto StandardNotes, which is another app I have used in the past (and which I didn’t like much either coz it just didn’t look good; I know… I go too much by looks, but it does matter to me).
There’s probably other apps like FSNotes that store encrypted notes locally, but this was the first one I discovered and so I wanted to explore more.
When you create an encrypted note in FSNotes it stores it as an Encrypted Text Bundle. The file extension is .etp
, which stands for Encrypted Text Pack. From what I understand, that’s a zipped Text Bundle file encrypted with RNCryptor.
What is RNCryptor? That seems to be a cross platform AES Encryptor/Decryptor data format, and it has implementations in many programming languages (Swift seems to be the primary one, but there’s a Python one too which is what I am interested in). What this means is that if I create an encrypted note in FSNotes, I should be able to use some Python (or other languages) to decrypt it. This is all very new to me; I am not familiar with Python, but it is something I want to sort of get familiar with, so I decided I should explore how one may decrypt an FSNotes encrypted note using Python. That’s the point of this blog post!
To begin with, I created a new note in FSNotes and encrypted it.
This requires I setup a master password in FSNotes, which I did.
The note is now locked. If I were to go to the note in Finder, copy it somewhere and rename .etp
to .zip
and try to open it, it won’t work.
If I try and view it, it’s gibberish.
(At this point I am going to rename the file to “HelloWorld.etp” (removing the space and exclamation mark) just to keep things simple. That’s why the file name is different in the code below).
What if I read this file in Python?
1 2 3 |
# opening the file with "rb" - read-only, binary file fileContents = open('HelloWorld.etp', 'rb').read() print(fileContents) |
Looks like this:
So, how can I decrypt this?
According to the RNCryptor-Python page all you do is something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import rncryptor data = '...' password = '...' # rncryptor.RNCryptor's methods cryptor = rncryptor.RNCryptor() encrypted_data = cryptor.encrypt(data, password) decrypted_data = cryptor.decrypt(encrypted_data, password) assert data == decrypted_data # rncryptor's functions encrypted_data = rncryptor.encrypt(data, password) decrypted_data = rncryptor.decrypt(encrypted_data, password) assert data == decrypted_data |
That didn’t make much sense to me, and when I fooled around with it I kept getting errors: “UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa2 in position 2: invalid start byte
”
Googling on that got me to an issue where someone gave the following code:
1 2 3 4 5 6 7 8 9 10 11 |
import rncryptor class BinaryRNCryptor(rncryptor.RNCryptor): def post_decrypt_data(self, data): return data[:-rncryptor.bord(data[-1])] b = BinaryRNCryptor() s = open('./source/two.mid', 'rb').read() e = b.encrypt(s, p) d = b.decrypt(e, p) assert d == s |
Looks like it’s a bug in the module and it doesn’t work well with binary data. Apparently it’s not an easy fix, so above code is the workaround. This issue has some details on what the code is doing; not sure why they haven’t fixed it in the main code.
Anyhoo…
With the above knowledge, here’s what I can do to decrypt the encrypted “HelloWorld.etp” note using Python.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import rncryptor # read the file - 'rb' means read-only, binary file fileContents = open('HelloWorld.etp', 'rb').read() # workaround from https://github.com/RNCryptor/RNCryptor-python/issues/8 class BinaryRNCryptor(rncryptor.RNCryptor): def post_decrypt_data(self, data): return data[:-rncryptor.bord(data[-1])] b = BinaryRNCryptor() # decrypt with the password password = '@QtA1PzsR@PNkBoL9JBchw4#p5rOWnPheH5GBLY5LDv3rJ6@$rfsh%CtJeNTBDWYbhKo0f9bI@0Aq^GkJ#QZ4KDPU$T*3' decryptedContents = b.decrypt(fileContents, password) # Create a new file - 'xb' means create, binary file newFile = open("HelloWorld.zip", "xb") newFile.write(decryptedContents) newFile.close() |
The above code reads the encrypted text pack file and creates a decrypted zip file (because, remember, an encrypted text pack is basically a zipped text bundled encrypted with RNCryptor; we removed the encryption, so what we are left with is a zip file).
Expand the zip file. And you get the decrypted markdown.
1 2 3 4 |
$ cat HelloWorld/text.md # Hello World! Hi there, I am an encrypted note. |
Like I said at the beginning, no real utility to this exercise, just something I wanted to pursue this fine Saturday night. š