Skip to content

Instantly share code, notes, and snippets.

@tannercollin
Last active November 1, 2021 15:58
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tannercollin/d7474c6afba0dc2026dd996b9dedf197 to your computer and use it in GitHub Desktop.
Save tannercollin/d7474c6afba0dc2026dd996b9dedf197 to your computer and use it in GitHub Desktop.
Standard Notes protocol v004 reference decryption demo
# Standard Notes protocol v004 reference decryption demo
# by Tanner Collin, MIT license
#
# Install dependencies in your Python 3 environment:
# pip install argon2-cffi requests pycryptodome
#
# do not use your real account
# use a fresh account so there's no 003 items
# or use this test account
email = 'standardnotes-fs004@domain.com'
password = 'testaccount'
import argon2
import hashlib
import json
import requests
from binascii import hexlify, unhexlify
from base64 import b64decode
from Crypto.Cipher import ChaCha20_Poly1305
p = dict(email=email, api='20200115')
r = requests.get('https://sync.standardnotes.org/auth/params', params=p)
auth_params = r.json()
print('Auth params:', auth_params)
print()
string_to_hash = email + ':' + auth_params['pw_nonce']
print('Hashing:', string_to_hash)
salt = hashlib.sha256(string_to_hash.encode()).hexdigest()[:32]
print('Salt:', salt)
argon2_hash = argon2.low_level.hash_secret_raw(
secret=password.encode(),
salt=unhexlify(salt),
time_cost=5,
memory_cost=65536,
parallelism=1,
hash_len=64,
type=argon2.Type.ID,
)
derived_key = hexlify(argon2_hash).decode()
print('Derived key:', derived_key, 'Length:', len(derived_key))
master_key = derived_key[:64]
server_password = derived_key[64:]
print()
print('Master key:', master_key)
print('Server password:', server_password)
d = dict(
email=email,
api='20200115',
ephemeral=False,
password=server_password,
)
r = requests.post('https://sync.standardnotes.org/auth/sign_in', data=d)
sign_in = r.json()
session = sign_in['session']
key_params = sign_in['key_params']
user = sign_in['user']
print()
print('Session:', session)
print('Key params:', key_params)
print('User:', user)
h = {'Authorization': 'Bearer ' + session['access_token']}
d = dict(items=[], compute_integrity=True, limit=150, api='20200115')
r = requests.post('https://sync.standardnotes.org/items/sync', headers=h, data=d)
sync = r.json()
print()
print('retrieved_items:', sync['retrieved_items'])
print('sync_token', sync['sync_token'])
default_items_key = None
notes = {}
for item in sync['retrieved_items']:
uuid = item['uuid']
content = item['content']
content_type = item['content_type']
enc_item_key = item['enc_item_key']
print()
print('Processing item', uuid)
print(' content_type', content_type)
print(' Decrypting enc_item_key')
version, nonce, ciphertext, encoded_authenticated_data = enc_item_key.split(':')
authenticated_data = json.loads(b64decode(encoded_authenticated_data).decode())
print(' version:', version)
print(' nonce:', nonce)
print(' ciphertext:', ciphertext)
print(' auth data:', authenticated_data)
if content_type == 'SN|ItemsKey':
key = master_key
else:
key = default_items_key
cipher = ChaCha20_Poly1305.new(key=unhexlify(key), nonce=unhexlify(nonce))
item_key = cipher.decrypt(b64decode(ciphertext))[:-16].decode()
print(' item_key', item_key)
print(' Decrypting content')
version, nonce, ciphertext, encoded_authenticated_data = content.split(':')
authenticated_data = json.loads(b64decode(encoded_authenticated_data).decode())
print(' version:', version)
print(' nonce:', nonce)
print(' ciphertext:', ciphertext)
print(' auth data:', authenticated_data)
cipher = ChaCha20_Poly1305.new(key=unhexlify(item_key), nonce=unhexlify(nonce))
plaintext = cipher.decrypt(b64decode(ciphertext))[:-16].decode()
print(' plaintext', plaintext)
plainjson = json.loads(plaintext)
if plainjson.get('isDefault', False):
default_items_key = plainjson['itemsKey']
if content_type == 'Note':
notes[plainjson['title']] = plainjson['text']
print()
print()
print('Here are your notes:')
for title, text in notes.items():
print()
print(title)
print(text)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment