I was feeling bored, so I decided to recreate the pyPrivnote
library, which was made around 6 years ago and hasn’t received any updates since. Anyway, this is just a small portion of the code, and I haven’t yet decided if I want to post the full code because, you know, I’m feeling a bit lazy.
This code takes a Privnote URL, splits the URL by the #
symbol since the string after the #
serves as the key for encoding and decoding. Then, we take this key and perform some basic AES decryption to retrieve the raw paste data.
from base64 import b64decode
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from dataclasses import dataclass, field
from tls_client import Session
@dataclass
class PrivnoteLink:
_id: str = None
_password: bytes = None
_session: Session = field(default_factory=lambda: Session(client_identifier="firefox_115", random_tls_extension_order=True))
@classmethod
def from_link(cls, link):
instance = cls()
instance.link = link
return instance
@property
def link(self):
if self._id is not None and self._password is not None:
return f"https://privnote.com/{self._id}#{self.decode_password()}"
else:
return None
@link.setter
def link(self, value):
if value.startswith("privnote.com/"):
value = "https://" + value
value = value.split("https://privnote.com/")[1]
value = value.split("#", maxsplit=1)
self._id = value[0]
if len(value) == 2 and value[1]:
self._password = bytes(value[1], encoding="utf-8")
def decode_password(self):
if self._password is not None:
return self._password.decode('utf-8')
else:
return None
def read(self):
headers = {"X-Requested-With": "XMLHttpRequest"}
encrypted_data = self._session.delete(self.link, headers=headers).json()
if not encrypted_data.get('data'):
print(f'Note may have already been destroyed.')
return
_data = b64decode(encrypted_data.get('data'))
salt = _data[8:16]
_data = _data[16:]
pbe = self._openSSLkey(self._password, salt)
key = pbe["key"]
iv = pbe["iv"]
cipher = AES.new(key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(_data), AES.block_size).decode()
@staticmethod
def _openSSLkey(password_bytes, salt_arr):
passalt = password_bytes + salt_arr
result = cur_hash = md5(passalt).digest()
for _ in range(2):
cur_hash = md5(cur_hash + passalt).digest()
result += cur_hash
return {
"key": result[0:4 * 8],
"iv": result[4 * 8:4 * 8 + 16]
}
def __str__(self):
return self.link
def __repr__(self):
return f"PrivnoteLink(_id={self._id}, _password={self.decode_password()})"
if __name__ == "__main__":
privnote = PrivnoteLink.from_link("https://privnote.com/NWIpX2Sz#tzK9VUYir")
decrypted_note = privnote.read()
print(decrypted_note)