{"id":13501819,"url":"https://github.com/ofek/privy","last_synced_at":"2025-04-06T16:12:34.998Z","repository":{"id":60722564,"uuid":"81870414","full_name":"ofek/privy","owner":"ofek","description":"An easy, fast lib to correctly password-protect your data","archived":false,"fork":false,"pushed_at":"2018-11-06T19:13:42.000Z","size":38,"stargazers_count":255,"open_issues_count":1,"forks_count":14,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-03-30T13:08:27.842Z","etag":null,"topics":["aes","encryption","hmac","keys","passwords","python","secrets"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ofek.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-13T20:51:52.000Z","updated_at":"2025-02-19T08:10:42.000Z","dependencies_parsed_at":"2022-10-03T21:02:08.686Z","dependency_job_id":null,"html_url":"https://github.com/ofek/privy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofek%2Fprivy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofek%2Fprivy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofek%2Fprivy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofek%2Fprivy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ofek","download_url":"https://codeload.github.com/ofek/privy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247509235,"owners_count":20950232,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aes","encryption","hmac","keys","passwords","python","secrets"],"created_at":"2024-07-31T22:01:51.788Z","updated_at":"2025-04-06T16:12:34.979Z","avatar_url":"https://github.com/ofek.png","language":"Python","funding_links":[],"categories":["Python","Frameworks and Libs"],"sub_categories":["Python"],"readme":"Privy\n=====\n\n.. image:: https://img.shields.io/pypi/v/privy.svg?style=flat-square\n    :target: https://pypi.org/project/privy\n\n.. image:: https://img.shields.io/travis/ofek/privy/master.svg?style=flat-square\n    :target: https://travis-ci.org/ofek/privy\n\n.. image:: https://img.shields.io/codecov/c/github/ofek/privy/master.svg?style=flat-square\n    :target: https://codecov.io/gh/ofek/privy\n\n.. image:: https://img.shields.io/pypi/pyversions/privy.svg?style=flat-square\n    :target: https://pypi.org/project/privy\n\n.. image:: https://img.shields.io/pypi/l/privy.svg?style=flat-square\n    :target: https://choosealicense.com/licenses\n\n-----\n\nPrivy is a small and fast utility for password-protecting secret data such as\nAPI keys, cryptocurrency wallets, or seeds for digital signatures.\n\nTable of Contents\n~~~~~~~~~~~~~~~~~\n\n.. contents::\n    :backlinks: top\n    :local:\n\nUsage\n-----\n\nSay for example you are using GnuPG. You are about to sign a message but it first\nrequires your password. Does your password become the input to unlock your stored\nprivate key? No, it is first hashed by a secure `key derivation function`_. That\nhash then becomes the input to a symmetric cipher such as AES which then decrypts\nyour stored private key. That is what Privy does.\n\nFear not! With Privy, this become trivially easy:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import privy\n    \u003e\u003e\u003e\n    \u003e\u003e\u003e # After creating secret, immediately encrypt it using Privy.\n    \u003e\u003e\u003e data = b'secret'\n    \u003e\u003e\u003e\n    \u003e\u003e\u003e hidden = privy.hide(data, ask_for_password())\n    \u003e\u003e\u003e hidden\n    '1$2$fL7xRh8WKe...'\n\nNow you can safely store or transmit the hidden secret. Whenever your user needs\nto use their secret again, ask for their password to take a peek.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e privy.peek(hidden, password)\n    b'secret'\n\nInstallation\n------------\n\nPrivy is available on Linux/macOS and Windows and supports Python 2.7, 3.3+, PyPy, and PyPy3.3-5.5+.\n\n.. code-block:: bash\n\n    $ pip install privy\n\nEncryption scheme\n-----------------\n\nSecrets are encrypted using the `Fernet`_ protocol. Specifically, it uses AES for\nencryption and has built-in authentication using HMAC. The private key used for\nencryption is derived from the password using a `key derivation function`_. The\nkey derivation function used is `Argon2`_, the winner of the `Password Hashing\nCompetition`_. Both Argon2i and Argon2d variants are supported.\n\nEncrypted format\n----------------\n\n``ascii(Argon2 algorithm || security level || base64(salt) || base64(Fernet token))``\n\nAPI\n---\n\nThere are 2 functions: ``hide`` and ``peek``.\n\nhide\n^^^^\n\n``hide(secret, password, security=2, salt=None, server=True)``\n\nEncrypts ``secret`` using ``password``. Returns the hidden secret as unicode.\n\n* Parameters\n\n  - **secret** (``bytes``) - The secret to encrypt.\n  - **password** (``bytes`` or ``unicode``) - The password used to access the secret.\n  - **security** (``int``) - A number 0-20 inclusive. Higher values are more secure at\n    the cost of slower computation and greater use of memory. See `security levels`_.\n  - **salt** (``bytes``) - The salt used for the password hash. Defaults to ``os.urandom(32)``.\n  - **server** (``bool``) - If ``True``, it is assumed side-channel attack protection is\n    needed and therefore the Argon2i algorithm will be used. Otherwise, the password will\n    be hashed using the Argon2d algorithm.\n\npeek\n^^^^\n\n``peek(hidden, password, expires=None)``\n\nDecrypts ``hidden`` using ``password``. Returns the secret as ``bytes``.\n\n* Parameters\n\n  - **hidden** (``bytes`` or ``unicode``) - The hidden secret to decrypt.\n  - **password** (``bytes`` or ``unicode``) - The password used to access the secret.\n  - **expires** (``int``) - The maximum number of seconds since encryption that\n    is allowed. The default is no expiration.\n\nA ``ValueError`` will be raised if the password is wrong, the password was attempted on a\ndifferent hidden secret, or the number of seconds since encryption is \u003e ``expires`` argument.\n\nSecurity levels\n---------------\n\nAll expected times were taken from tests on an Intel Core i7-2670QM @ 2.2 GHz when decrypting\na 256 KiB secret.\n\nThis is the command, where ``SL`` is the desired security level:\n\n.. code-block:: bash\n\n    $ python -m timeit -s \"import privy, os; pw = 'password'; s = os.urandom(1024 * 256); h = privy.hide(s, pw, SL)\" \"privy.peek(h, pw)\"\n\n+--------+-----------------+---------------+-----------------+\n| Levels | Argon2 settings | Expected time | Notes           |\n+========+=================+===============+=================+\n| 0      | m=8 KiB, t=1    | 7 msec        | Lowest possible |\n+--------+-----------------+---------------+-----------------+\n| 1      | m=4 MiB, t=10   | 54 msec       |                 |\n+--------+-----------------+---------------+-----------------+\n| 2      | m=8 MiB, t=10   | 99 msec       | Default         |\n+--------+-----------------+---------------+-----------------+\n| 3      | m=32 MiB, t=10  | 367 msec      |                 |\n+--------+-----------------+---------------+-----------------+\n| 4      | m=48 MiB, t=10  | 540 msec      |                 |\n+--------+-----------------+---------------+-----------------+\n| 5      | m=96 MiB, t=10  | 1.1 sec       | Good choice     |\n+--------+-----------------+---------------+-----------------+\n| 6      | m=256 MiB, t=10 | 3 sec         |                 |\n+--------+-----------------+---------------+-----------------+\n| 7      | m=512 MiB, t=10 | 6 sec         |                 |\n+--------+-----------------+---------------+-----------------+\n| 8      | m=768 MiB, t=10 | 9 sec         |                 |\n+--------+-----------------+---------------+-----------------+\n| 9      | m=1 GiB, t=10   | 12.2 sec      |                 |\n+--------+-----------------+---------------+-----------------+\n| 10     | m=2 GiB, t=20   | 48 sec        | For use on      |\n+--------+-----------------+---------------+ users' machines |\n| 11     | m=3 GiB, t=30   | 107           |                 |\n+--------+-----------------+---------------+                 |\n| 12     | m=4 GiB, t=40   | ?             |                 |\n+--------+-----------------+---------------+                 |\n| 13     | m=5 GiB, t=50   | ?             |                 |\n+--------+-----------------+---------------+                 |\n| 14     | m=6 GiB, t=60   | ?             |                 |\n+--------+-----------------+---------------+                 |\n| 15     | m=7 GiB, t=70   | ?             |                 |\n+--------+-----------------+---------------+                 |\n| 16     | m=8 GiB, t=80   | ?             |                 |\n+--------+-----------------+---------------+                 |\n| 17     | m=9 GiB, t=90   | ?             |                 |\n+--------+-----------------+---------------+                 |\n| 18     | m=10 GiB, t=100 | ?             |                 |\n+--------+-----------------+---------------+                 |\n| 19     | m=11 GiB, t=110 | ?             |                 |\n+--------+-----------------+---------------+                 |\n| 20     | m=12 GiB, t=120 | ?             |                 |\n+--------+-----------------+---------------+-----------------+\n\nLicense\n-------\n\nPrivy is distributed under the terms of either\n\n- `MIT License \u003chttps://choosealicense.com/licenses/mit\u003e`_\n- `Apache License, Version 2.0 \u003chttps://choosealicense.com/licenses/apache-2.0\u003e`_\n\nat your option.\n\nChangelog\n---------\n\nImportant changes are emphasized.\n\n6.0.0\n^^^^^\n\n* **Breaking:** Support for Python 3.3 has been dropped.\n\n5.0.0\n^^^^^\n\n* **Breaking:** Privy is now dual-licensed under the terms of MIT and Apache v2.0.\n* Only documented methods ``hide`` and ``peek`` are now exposed in the root namespace.\n* Travis now runs tests with the latest versions of PyPy and PyPy3.\n* Improvements to documentation.\n\n4.0.0\n^^^^^\n\n* **Breaking:** For saner conformity, security level 7 now utilizes 512 MiB of RAM instead of 448.\n* Major improvements to documentation.\n\n3.0.0\n^^^^^\n\n* Added security levels 11-20. These are quite resource intensive and are therefore\n  only acceptable for individual use.\n\n2.0.1\n^^^^^\n\n* **Breaking:** Due to requests, the encrypted format now uses url-safe base64 instead of hex.\n\n1.0.0\n^^^^^\n\n* Initial release\n\n.. _Fernet: https://github.com/fernet/spec/blob/master/Spec.md\n.. _key derivation function: https://en.wikipedia.org/wiki/Key_derivation_function\n.. _Argon2: https://github.com/p-h-c/phc-winner-argon2\n.. _Password Hashing Competition: https://en.wikipedia.org/wiki/Password_Hashing_Competition\n.. _security levels: https://github.com/ofek/privy#security-levels\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fofek%2Fprivy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fofek%2Fprivy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fofek%2Fprivy/lists"}