argon2_bind

This module provides an interface to the Argon2 reference C implementation. It depends on libargon2, and can be used dynamically with the shared library or statically compiled with the static library. The aim is to provide a high-level and fully-featured implementation of the C reference backed by comprehensive documentation, tests, and fuzzing.

Basic usage

The basic flow of using this module which mimicks the argon2 cli is:

  1. Set the wanted Argon2 hash parameters
  2. Set a password and salt
  3. Call getOutput with the previous parameters
  4. Verify the encoded portion of the returned object via isVerified
  5. Utilize the verified output

Example

import argon2_bind

const params = setupArgon2Params(
  timeCost = 3,
  memoryCostK = 1 shl 8,
  parallelism = 2,
  algoType = Argon2id,
)

const salt = "somesalt"
var pass = "password"

var res: Argon2Output
try:
  res = pass.getOutput(salt, params)
except Argon2Error:
  echo getCurrentExceptionMsg()
  quit 1

if res.encoded.isVerified(pass):
  echo res.hash

# Output:
# @[163, 22, 29, 233, 157, 14, 124, 7, 98, 54, 75, 44, 75, 62, 162, 185,
# 80, 0, 89, 115, 248, 135, 157, 84, 40, 127, 216, 189, 86, 146, 31, 54]

Types

Argon2Error = object of CatchableError
  
Catchable error arising from argon2_bind module.   Source Edit
Argon2Type = enum
  Argon2d, Argon2i, Argon2id

Hashing algorithm variants available.

Argon2d is faster and uses data-depending memory access, which makes it highly resistant against GPU cracking attacks and suitable for applications with no threats from side-channel timing attacks (eg. cryptocurrencies). Argon2i instead uses data-independent memory access, which is preferred for password hashing and password-based key derivation, but it is slower as it makes more passes over the memory to protect from tradeoff attacks. Argon2id is a hybrid of Argon2i and Argon2d, using a combination of data-depending and data-independent memory accesses, which gives some of Argon2i's resistance to side-channel cache timing attacks and much of Argon2d's resistance to GPU cracking attacks.

  Source Edit
Argon2Version = enum
  Argon2Version10 = 0x00000010, Argon2Version13 = 0x00000013

Hashing algorithm version.

See also:

  Source Edit
Argon2Params {...}{.bycopy.} = object
  timeCost*, memoryCostK*, parallelism*, hashLen*: uint32
  algoType*: Argon2Type
  version*: Argon2Version

Hashing parameters required to be passed.

Constraints

timeCost must be at least 1.

memoryCostK is in KiB, and the minimum must adhere to the formula memoryCostK/parallelism >= 8.

parallelism refers to the lanes and threads to be utilized. As mentioned via the memoryCostK constraint, each lane must have 8KiB of memory available to it.

hashLen minimum is 4.

See also:

  Source Edit
Argon2Output = object
  hash*: seq[byte]
  encoded*: string

hash will contain the finalized hash value as a sequence of bytes.

encoded key will contain the ad-hoc encoded output as a string. It is basically a stringified structure of all parameters, excluding the password, but including the hash value as the last variable in base64.

See also:

  Source Edit

Consts

Argon2CurrentVersion = Argon2Version13

Denotes the current version, which is the one recommended to be used.

Output will of course differ between each version, keep note of the

version used. It is also noted in the encoded output.

See also:

  Source Edit
Argon2DefaultParams = (timeCost: 3'u, memoryCostK: 4096'u, parallelism: 1'u,
                     hashLen: 32'u, algoType: Argon2i, version: Argon2Version13)

The default parameters to be utilized by the module. Utilized as a fallback in linked functions below.

These values match the current argon2 cli defaults.

See also:

  Source Edit

Funcs

func setupArgon2Params(timeCost: uint32 = Argon2DefaultParams.timeCost;
                      memoryCostK: uint32 = Argon2DefaultParams.memoryCostK;
                      parallelism: uint32 = Argon2DefaultParams.parallelism;
                      hashLen: uint32 = Argon2DefaultParams.hashLen;
                      algoType: Argon2Type = Argon2DefaultParams.algoType;
                      version: Argon2Version = Argon2DefaultParams.version): Argon2Params {...}{.
    inline, raises: [], tags: [].}

Returns the parameterized state needed by the hashing function, with each passed parameter being optional and having a fallback to the defaults if omitted.

See also:

Examples:

let firstParams = setupArgon2Params()
doAssert firstParams == Argon2DefaultParams
let secondParams = setupArgon2Params(hashLen = 4, memoryCostK = 1 shl 9)
doAssert secondParams ==
    Argon2Params(timeCost: 3, memoryCostK: 1 shl 9, parallelism: 1, hashLen: 4,
                 algoType: Argon2i, version: Argon2CurrentVersion)
  Source Edit
func getEncodedLen(argon2Params: Argon2Params; salt: seq[byte] | string): uint32 {...}{.
    inline.}

Requires parameterized argon2 object.

Requires a salt parameter as either a byte sequence or string.

Returns the expected encoded output length, depending on input parameters.

Mainly for internal use.

  Source Edit
func getOutput(pass, salt: seq[byte] | string;
              argon2Params: Argon2Params = Argon2DefaultParams): Argon2Output {...}{.
    inline, raises: [Argon2Error].}

Requires a password and salt, either as a sequence of bytes or string. The password must have at least 1 byte. The salt must be at least 8 bytes.

Optionally takes the typed parameters for argon2, does a fallback to Argon2DefaultParams if none are passed. Only at this point are the parameters sanity checked, in their pass to the C lib.

Returns an Argon2Output object.

Raises an Argon2Error on internal exception.

See also:

Examples:

let
  params = setupArgon2Params(hashLen = 4)
  salt = "somesalt"
  pass = "abc"
let res = pass.getOutput(salt, params)
doAssert res.hash == @[27.byte, 96, 149, 111]
doAssert res.encoded == "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$G2CVbw"
  Source Edit
func getRawHash(pass, salt: seq[byte] | string;
               argon2Params: Argon2Params = Argon2DefaultParams): seq[byte] {...}{.inline,
    raises: [Argon2Error].}

Refer to getOutput for input parameters which are shared here.

Returns a byte sequence of the computed hash value.

Raises an Argon2Error on internal exception.

See also:

Examples:

let
  params = setupArgon2Params(hashLen = 4)
  salt = "somesalt"
  pass = "abc"
let res = pass.getRawHash(salt, params)
doAssert res == @[27.byte, 96, 149, 111]
  Source Edit
func getEncodedHash(pass, salt: seq[byte] | string;
                   argon2Params: Argon2Params = Argon2DefaultParams): string {...}{.
    inline, raises: [Argon2Error].}

Refer to getOutput for guidance on the input parameters.

Returns a string of the argon2 encoded value. Note, this is not the hash value.

Raises an Argon2Error on internal exception.

See also:

Examples:

var expected = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ"
expected &= "$vi0N5Av/NnxgttHDWyVte+OheXu6wuyyFSWJsNEhCCI"
let
  salt = "somesalt"
  pass = "abc"
let res = pass.getEncodedHash(salt)
doAssert res == expected
let params = setupArgon2Params(hashLen = 4)
let res2 = pass.getEncodedHash(salt, params)
doAssert res2 == "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$G2CVbw"
  Source Edit
func isVerified(encoded: string; pass: seq[byte] | string): bool {...}{.inline,
    raises: [Argon2Error].}

Requires the encoded hash output as a string from either getEncodedHash or getOutput. Requires password of type byte sequence or string. Must be at least 1 byte.

Returns a bool, with true indicating success.

Raises an Argon2Error on internal exception.

Examples:

let pass = "pass"
let encodedHash = getEncodedHash(pass, "somesalt")
doAssert encodedHash.isVerified(pass) == true
doAssert encodedHash.isVerified("wrongpass") == false
try:
  discard encodedHash[0 .. ^2].isVerified(pass)
  doAssert false
except Argon2Error:
  doAssert true
except:
  doAssert false
  Source Edit