Package: argonautica
argonautica-pyOverviewargonauticais a Python package for hashing passwords that uses the cryptographically-secureargon2hashing algorithm.Argon2 won thePassword Hashing Competitionin 2015, a several year project to identify a successor tobcrypt,scrypt, and other common hashing algorithms.argonauticawas built with a simple use-case in mind: hashing passwords for storage in a
website's database. That said, it's also "feature-complete", meaning anything you can do with
the cannonicalC implementationof argon2
you can do with argonautica*.* Indeed, argonautica has a feature that even the cannonical C implementation
lacks, i.e. hashing passwords with secret keys (the C implementation implements this, but
does not expose it publicly)AlternativesThere are several Python packages that implement argon2, including the excellentpasslib, which usesargon2_cffi, but...AFAIK,argonauticais the only Python implementation of argon2 that supports hashing with secret keys. Not even thecannonical C implementationof argon2 exposes this feature publicly (it's in the code, but unfortunately not accessable via the public API).argonauticais the only Python implementation of argon2 to useSIMDinstructions to peform it's hashing algorithm, which means it can be quite fast. The downside is that you have to compile it for your specific machine (this is why thepip install argonauticaprocess takes time). That said, on the developer's early 2014 Macbook Air, which hasSIMDinstruction throughAVX2, argonautica runs ~30% faster than passlib on default settings.argonauticasupports the latest argon2 variant: argon2id, which, unless you have a reason not to, you should be using. A number of Python implementations do not yet support this variant.Finally,argonauticais the only Python implementation of argon2 written inRust(as opposed to C or C++).Rustis a "systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety."RequirementsPythonversion 3.4 or higher (orPyPyversion 3.5 or higher)Rustversion 1.26 or higherLLVMversion 3.9 or higherInstallationRust:Follow the instructionshere, which will just tell you to run the following command in your terminal and follow the on-screen instructions:curl https://sh.rustup.rs -sSf \| shLLVM:macOS:brew install llvm, which requiresHomebrewDebian-based linux:apt-get install llvm-dev libclang-dev clangArch linux:pacman -S clangOther linux: Use your distribution's package managerWindows: Download a pre-built binaryhereargonautica:pip install --upgrade piporpip install setuptools-rust. Note: setuptool-rust is not required if you have pip version 10.0 or abovepip install argonautica. Unfortunately, this step may take several minutes, as argonautica needs to compile it's Rust code for your specific CPU (due to its use of SIMD instructions). The upside, however, is that once compiled, argonautica should run blazingly fastUsageHashingfromargonauticaimportHasherhasher=Hasher(secret_key='somesecret')hash=hasher.hash(password='P@ssw0rd')print(hash)# 👆 prints a random hash as the defeault `Hasher` uses a random salt by defaultVerifyingfromargonauticaimportVerifierverifier=Verifier(secret_key='somesecret')is_valid=verifier.verify(hash='$argon2id$v=19$m=4096,t=192,p=2$ULwasg5z5byOAork0UEhoTBVxIvAafKuceNz9NdCVXU$YxhaPnqRDys',password='P@ssw0rd',)assert(is_valid)ConfigurationfromargonauticaimportHasher,Verifierfromargonautica.configimportBackend,Variant,Versionhasher=Hasher(secret_key=None)# 👆 A secret key (passed as a keyword argument) is required to instantiate a# Hasher, a Verifier, or an Argon2, but you are allowed to pass `None`# in order to forgo using a secret key (this is not recommended)hasher.additional_data=None# Default is None# 👆 Although rarely used, argon2 allows you to hash a password# with not only salt and a secret key, but also with "additional data",# which acts as a kind of secondary secret key. Like a secret key, it affects# the outcome of the hash and is not stored in the string-encoded output, meaning# later, to verify against a hash created with additional data, you will need to# supply the same additional data manually to the Verifier (just like you have to# do with a secret key). Again, this is rarely used.hasher.backend=Backend.C# Default is Backend.C# 👆 argonautica was designed to support multiple backends (meaning multiple# implementations of the underlying argon2 algorithm). Currently only the# C backend is supported, which uses the cannonical argon2 library written# in C to actually do the work. In the future a Rust backend will also be# supported, but, for the moment, you must use Backend.C, which is the# default. Using Backend.Rust will result in an error (again, for the# moment).hasher.hash_len=32# Default is 32# 👆 The hash length in bytes is configurable. The default is 32.# This is probably a good number to use. 16 is also probably fine.# You probably shouldn't go below 16hasher.iterations=192# Default is 192# 👆 Argon2 has a notion of "iterations" or "time cost". All else equal# and generally speaking, the greater the number of iterations, the# longer it takes to perform the hash and the more secure the resulting# hash. More iterations basically means more CPU load. This and "memory# size" (see below) are the two primary parameters to adjust in order# to increase or decrease the security of your hash. The default is# 192 iterations, which was chosen because, along with the default# memory size of 4096, this leads to a hashing time of approximately# 300 milliseconds on the early-2014 Macbook Air that is the developer's# machine. If you're going to use argonautica in production, you should# probably tweak this parameter (and the memory size parameter) in order# to increase the time it takes to hash to the maximum you can# reasonably allow for your use-case (e.g. to probably about 300-500# milliseconds for the use-case of hashing user passwords for a website)hasher.lanes=2# Default is multiprocessing.cpu_count()# 👆 Argon2 can break up its work into one or more "lanes" during some parts of# the hashing algorithm. If you configure it with multiple lanes and you also# use multiple threads (see below) the hashing algorithm will performed its# work in parallel in some parts, potentially speeding up the time it takes to# produce a hash without diminishing the security of the result. By default,# the number of lanes is set to the number of logical cores on your machinehasher.memory_size=4096# Default is 4096# 👆 Argon2 has a notion of "memory size" or "memory cost" (in kibibytes). All else# equal and generally speaking, the greater the memory size, the longer it takes to# perform the hash and the more secure the resulting hash. More memory size basically# means more memory used. This and "iterations" (see above) are, again, generally# speaking, the two parameters to adjust in order to increase or decrease the# security of your hash. The default is 4096 kibibytes, which was chosen because,# again, along with the default iterations of 192, this leads to a hashing time of# approximately 300 milliseconds on the early-2014 Macbook Air that is the# developer's machine. If you're going to use argonautica in production, you should# probably tweak this parameter (and the iterations parameter) in order to increase# the time it takes to hash to the maximum you can reasonably allow for your use-case# (e.g. to probably about 300-500 milliseconds for the use-case of hashing user# passwords for a website)hasher.threads=2# Default is multiprocessing.cpu_count()# 👆 If you have configured a Hasher to use more than one lane (see above), you# can get the hashing algorithm to run in parallel during some parts of the# computation by setting the number of threads to be greater than one as well,# potentially speeding up the time it takes to produce a hash without diminishing# the security of the result. By default, the number of threads is set to the number# of logical cores on your machine. If you set the number of threads to a number# greater than the number of lanes, `Hasher` will automatically reduce the number# of threads to the number of laneshasher.variant=Variant.Argon2id# Default is Variant.Argon2id# 👆 Argon2 has three variants: Argon2d, Argon2i, and Argon2id. Here is how these# variants are explained in the RFC: "Argon2 has one primary variant: Argon2id,# and two supplementary variants: Argon2d and Argon2i. Argon2d uses data-dependent# memory access, which makes it suitable for ... applications with no threats from# side-channel timing attacks. Argon2i uses data-independent memory access, which# is preferred for password hashing and password-based key derivation. Argon2id# works as Argon2i for the first half of the first iteration over the memory, and# as Argon2d for the rest, thus providing both side-channel attack protection and# brute-force cost savings due to time-memory tradeoffs." If you do not know which# variant to use, use the default, which is Argon2idhasher.version=Version._0x13# Default is Version._0x13# 👆 Argon2 has two versions: 0x10 and 0x13. The latest version is 0x13 (as of 5/18).# Unless you have a very specific reason not to, you should use the latest# version (0x13), which is also the defaulthash=hasher.hash(password='P@ssw0rd',salt='somesalt',# You can set your own salt, or use the default: RandomSalt(32))assert(hash=='$argon2id$v=19$m=4096,t=192,p=2$c29tZXNhbHQ$8nD3gRm+NeOcIiIrlnzDAdnK4iD+K0mVqFXowGs13M4')verifier=Verifier(secret_key=None)verifier.additional_data=None# As with Hasher, you can configure a Verifier's additional dataverifier.backend=Backend.C# As with Hasher, you can configure a Verifier's backendverifier.threads=2# As with Hasher, you can configure a Verifier's threadsis_valid=verifier.verify(hash=hash,password='P@ssw0rd')assert(is_valid)Miscellaneousmypyargonauticausesmypytype annotations everywhere in the code, which, in the author's humble opinion, is a very useful form of documentation; so if you're ever confused about what types to use for arguments, just pop open the code and take a look at the function signatures.Argon2Argon2is a convenience class that holds both aHasherand aVerifier. If you'd like to use just one class that knows how both to hash and to verify, instantiate anArgon2. It works essentially the same way asHasherandVerifierdo.fromargonauticaimportArgon2argon2=Argon2(secret_key='somesecret')hash=argon2.hash(password='P@ssw0rd')print(hash)is_valid=argon2.verify(hash=hash,password='P@ssw0rd')assert(is_valid)RandomSaltRandomSaltis a special kind of salt that will create new random salt bytes before each hash. A RandomSalt knows its length (in number of bytes). The defaultHasheruses aRandomSaltwith length of 32 bytes, but you can use your ownRandomSaltof custom length. When you instantiate aRandomSalt, the constructor takes a length, e.g.my_random_salt = RandomSalt(16)fromargonauticaimportHasherfromargonautica.dataimportRandomSalthasher=Hasher(salt=RandomSalt(16),# 👆 Here we're using a RandomSalt of length of 16 bytes# instead of the default, which is a RandomSalt of length 32 bytessecret_key="somesecret")hash=hasher.hash(password='P@ssw0rd')print(hash)HashRawHashing withargonauticaproduces a string-encoded hash, but sometimes you might want the "raw material" behind this hash, i.e. the raw hash bytes, the raw salt bytes, or raw parameters, which are the three component parts of a string-encoded hash. To obtain these raw parts...fromargonautica.utilsimportdecode,HashRawhash='$argon2id$v=19$m=4096,t=128,p=2$c29tZXNhbHQ$WwD2/wGGTuw7u4BW8sLM0Q'# Create a `HashRaw` using the `decode` functionhash_raw=decode(hash)# Pull out the raw parametersiterations=hash_raw.iterations# 128lanes=hash_raw.lanes# 2memory_size=hash_raw.memory_size# 4096variant=hash_raw.variant# Variant.Argon2idversion=hash_raw.version# Version._0x13# Pull out the raw bytesraw_hash_bytes=hash_raw.raw_hash_bytes# b'[\x00\xf6\xff\x01\x86N\xec;\xbb\x80V\xf2\xc2\xcc\xd1'raw_salt_bytes=hash_raw.raw_salt_bytes# b'somesalt'# Turn a `HashRaw` back into a string-encoded hash using the `encode` methodhash2=hash_raw.encode()assert(hash==hash2)Licenseargonauticais licensed under either of:The Apache License, Version 2.0, orThe MIT licenseat your option.