2014-08-21, James Robson, http://soundly.me
Evernote uses the
en-crypt field (from the ENML specification) to encrypt portions of its notes. The API does not provide a method to read and write this field, however.
The most you get is this description
Evernote derives your AES key from the passphrase you enter and does this using a well recognized method called PBKDF2 (Password Based Key Derivation Function 2). Your passphrase, along with a unique salt, runs through a HMAC/SHA-256 hashing function 50,000 times. The result is a 128 bit AES key. This key, along with an initialization vector, is used to encrypt your data in CBC (Cipher Block Chaining) mode.
found at this page here:
It would be nice though, if you could create an
en-crypt field from the API. Meaning your app could create encrypted fields and users could decrypt them in the Evernote app.
Well, it turns out that the description above more or less clues us in for what to look for, and poking around in the app and its notes gives us the rest.
First, I had to create an encrypted field, which you can only do in the desktop, it seems. Then I exported it as an
.enex file, which is really just XML:
.enex file in a capable text editor you will find the
en-crypt field, and it's pretty straightforward, really:
<en-crypt cipher="AES" length="128">RU5DMA4hFx2T/Uy8tI7R7aGAe8r/ncNiNxGykP7VbZQMMP6fcPzyQqGegjVvYQg4MtKBr57qb6vBLEpWHIKVY9XLQzeOXkqpy/kJtLRKTP21BsZrIeSX67XuM40z02M4XAZsIF7Ixxsg8m81SWYQ1oliEnac+FiWzI5dUwKpSdFVc7WB40/f2cK5Hc6C8hN3ILkgX6VXpnPjtRcjcm6aT7sCXAXgslU6Qwrv43X1bmqMlZT0pxpKRFpKrnkPV89TvGfvGUyQGfc50B3VzqzJvF2R4pEe3mGzKramIGKei1w1aFiT2eufwDgfJghkDWgIIW7QbZUiywoMwITrwpYJh1NCbObRGMqBLAB6kZFRVKbS5JWesKwgRmnHsdHQTpX4Sd/dYI3GC4FpdeK9C+E3YcBSVcCNBGt7N2+ce3o7q/K3Ynoq+8w0qHtNi1XGV2hD+LU6IK13FoRJwyT1hhkAMiFc3mgvT83hrynhVhByhkKGPixL3s1GFy5s51NmLSTur/b8pnicF+TgG4YXz8Jxvnalmf2SMd5voxuz9Ny5EK86P54MsuzjK0y6AO/te7pkxht6UP+SYItJI2s1r5fl07Sa/xlkVH28xcL3ODpr0DTWEh/Qq2pXRNLfpcISdBJIMaIPlcJGR8567WJK0A07yhsS9c1PnVUb+cLlI9PIG3Qxtkfp</en-crypt>
It's clear from the above that all the fields mentioned in the description (iv, salt, etc) are embedded in that base64 string. The question is, where and how?
I first converted it to hex in the Terminal:
% echo RU5DMA4hFx2T/Uy8tI7R7aGAe8r/ncNiNxGykP7VbZQMMP6fcPzyQqGegjVvYQg4MtKBr57qb6vBLEpWHIKVY9XLQzeOXkqpy/kJtLRKTP21BsZrIeSX67XuM40z02M4XAZsIF7Ixxsg8m81SWYQ1oliEnac+FiWzI5dUwKpSdFVc7WB40/f2cK5HpnPjtRcjcm6aT7sCXAXgslU6Qwrv43X1bmqMlZT0pxpKRFpKrnkPV89TvGfvGUyQGfc50B3VzqzJvF2R4pEe3mGzKramIGKei1w1aFiT2eufwDgfJghkDWgIIW7QbZUiywoMwITrwpYJh1NCbObRGMqBLAB6kZFRVKbS5JWesKwgRmnHsdHQTpX4Sd/dYI3GC4FpdeK9C+E3YcBSVcCNBGt7N2+ce3o7q/K3Ynoq+8w0qHtNi1XGV2hD+LU6IK13FoRJwyT1hhkAMiFc3mgvT83hrynhVhByhkKGPixL3s1GFy5s51NmLSTur/b8pnicF+TgG4YXz8Jxvnalmf2SMd5voxuz9Ny5EK86P54MsuzjK0y6AO/te7pkxht6UP+SYItJI2s1r5fl07Sa/xlkVH28xcL3ODpr0DTWEh/Qq2pXRNLfpcISdBJIMaIPlcJGR8567WJK0A07yhsS9c1PnVUb+cLlI9PIG3Qxtkfp | base64 -D | hexdump
Given this layout you can create an
en-crypt field which can be added to a note via the API, as long as you get the crypto functions right. Here's how to create the field in Python. This field, once created, can then be decrypted normally in the browser on the Evernote site:
import hashlib import binascii import os from pbkdf2 import pbkdf2 # locally, pbkdf2.py from aeshelper import AESCipher128CBC # locally, aeshelper.py import base64 import hmac payload = bytes() payload += b'ENC0' password = 'password' iters = 50000 # Three random values salt = os.urandom(16) salthmac = os.urandom(16) iv = os.urandom(16) payload += salt payload += salthmac payload += iv # we'll append the ciphertext last print 'salt: ' + binascii.hexlify(salt) print 'salthmac: ' + binascii.hexlify(salthmac) print 'iv: ' + binascii.hexlify(iv) # Derive key key = pbkdf2( 'password', salt, 50000, 16, hashlib.sha256 ) print 'Derived key: ' + binascii.hexlify(key) cleartext = '<h2>helowrld</h2>' ac = AESCipher128CBC(key) # Cipher hands us base64 by default ciphertext = base64.b64decode(ac.encrypt(cleartext, iv)) payload += ciphertext # Derive key for HMAC validation keyhmac = pbkdf2( 'password', salthmac, 50000, 16, hashlib.sha256 ) hd = hmac.new(keyhmac, None, hashlib.sha256) hd.update(payload) digest = hd.digest() payload += digest print 'keyhmac: ' + binascii.hexlify(keyhmac) print 'digest: ' + binascii.hexlify(digest) # Money: ncfield = '<en-crypt cipher="AES" length="128">' + base64.b64encode(payload) + '</en-crypt>' print ncfield # Now you can add the field to your note via the Evernote API/SDK, as you would normally...
Note: you'll need a couple of Python sources for the above to work: pbkdf2.py | aeshelper.py
If you add the above field to a note, you should then be able to select and decrypt the above message from your browser, on the Evernote site.
This does not do decryption of an existing
en-crypt field in a note, but I think it should be pretty obvious how you would be able to do that with the calls above.
Having the hex encoding of the payload (minus the nice annotations above), I had to figure out what parts of it were what. I sort of figured the salt(s) and IV would be prepended to the payload, but you still have to know which one comes first, etc.
Remember Evernote said in their description how many iterations, what mode of encryption, etc they used? Well so it was just a matter of hunting through that source for the obvious candidates, like
pbkdf2 and setting some breakpoints.
When the breakpoints hit, I had some valuable data to compare to my hex output.
And since we found the code where these values are used, we also get to reason about what is being checked. For instance, I don't recall Evernote saying in their description above that they also created a message digest of the salt(s), IV, and ciphertext and appended that to the end of the ciphertext in the payload. Like I said, trial and error, and finally the picture became clear.
That's pretty much it. This isn't a hack or compromise of Evernote's security, not in the least. From what I can tell they're doing the encryption right, and having other parties knowing how it works is actually a part of good crypto.
This is really just a way to access a portion of their service that, in my opinion, should be available in the API, because it provides opportunities to integrate with their workflow while securing cloud data. In fact, I would like to see a whole set of functionality around crypto in their API.