Privnote Data Decoder

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

class PrivnoteLink:
    _id: str = None
    _password: bytes = None
    _session: Session = field(default_factory=lambda: Session(client_identifier="firefox_115", random_tls_extension_order=True))
    def from_link(cls, link):
        instance = cls() = link
        return instance

    def link(self):
        if self._id is not None and self._password is not None:
            return f"{self._id}#{self.decode_password()}"
            return None

    def link(self, value):
        if value.startswith(""):
            value = "https://" + value
        value = value.split("")[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')
            return None

    def read(self):
        headers = {"X-Requested-With": "XMLHttpRequest"}
        encrypted_data = self._session.delete(, headers=headers).json()
        if not encrypted_data.get('data'):
            print(f'Note may have already been destroyed.')
        _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.MODE_CBC, iv)
        return unpad(cipher.decrypt(_data), AES.block_size).decode()

    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):

    def __repr__(self):
        return f"PrivnoteLink(_id={self._id}, _password={self.decode_password()})"

if __name__ == "__main__":
    privnote = PrivnoteLink.from_link("")
    decrypted_note =