☰

# src/checksums/bcrypt

Theme:

- Manual
- Standard library
- Index
- Compiler docs
- Fusion docs
- devel, stable

Search:

bcrypt is a Blowfish-based password hashing algorithm that is designed to be adaptively expensive to provide resistance against brute force based attacks and additionally includes a salt for lookup table resistance.

Although bcrypt has been around for a long time, dating back to 1999, for many projects it is still a reasonable choice due to its adjustable cost factor that can provide security against all but the most well funded attackers.

This module's design is based loosely on Python's `bcrypt` module and supports generating the newer version `2b` hashes as well as verifying the older `2a` and the PHP equivalent of `2b` called `2y`.

Example:

    import src/checksums/bcrypt

    # Generate a salt with a specific cost factor and use it to hash a password.
    let hashed = bcrypt("correct horse battery stape", generateSalt(8))

Example:

    import src/checksums/bcrypt

    # Verify a password against a known good hash from i.e. a database.
    let knownGood = "$2b$06$LzUyyYdKBoEy9V4NTvxDH.O11KQP30/Zyp5pQAQ.0Cy89WnkD5Jjy"

    assert verify("correct horse battery staple", knownGood)

# Types

    CostFactor = range[4 .. 31]

  Adjustable cost factor. The value is a logarithm of 2, which means that a cost of 5 is twice as expensive as a cost of 4, and a cost of 16 is 2048 times more expensive than a cost of 5.

    Hash = distinct HashBytes

  A 192 bit hash value produced by the `bcrypt` function.

    Salt = object
      costFactor*: CostFactor

  A random 128 bit salt used to provide security against rainbow table attacks that also includes the bcrypt version and cost factor.

    SaltedHash = tuple[salt: Salt, hash: Hash]

# Procs

    proc `$`(s: Hash): string {....raises: [], tags: [], forbids: [].}

  Renders the given `Hash` into the canonical bcrypt-type Base64 representation.

    proc `$`(s: Salt): string {....raises: [], tags: [], forbids: [].}

  Renders the given `Salt` into the canonical bcrypt-type Base64 representation along with its version and cost factor information.

    proc `$`(s: SaltedHash): string {....raises: [], tags: [], forbids: [].}

  Renders the given `SaltedHash` into the canonical bcrypt-type Base64 representation resulting in the actual hash string to be stored.

    proc bcrypt(password: openArray[char]; salt: Salt): SaltedHash {....raises: [],
        tags: [], forbids: [].}

  Produces a `SaltedHash` from the given password string and salt.

  Be careful when accepting a salt from a source outside of your control as a malicious user could pass in salts with a very high cost factor, resulting in denial of service attack.

  Example:

      let hashed = bcrypt("correct horse battery stape", generateSalt(8))

    proc generateSalt(cost: CostFactor): Salt {....raises: ResourceExhaustedError,
        tags: [], forbids: [].}

  Generates a new, random salt with the provided `CostFactor`. Only salts with subversion `2b` are generated since it's the newest and default version of the reference bcrypt implementation.

    proc parseSalt(salt: string): Salt {....raises: ValueError, tags: [], forbids: [].}

  Parses a `Salt` from the given string (which may be a full bcrypt hash or only the preamble).

  It accepts the `2a`, `2b` and `2y` subversions.

  Example:

      # Parse full hash
      let salt1 = parseSalt "$2b$06$LzUyyYdKBoEy9V4NTvxDH."

      # Parse salt part
      let salt2 = parseSalt "$2b$06$LzUyyYdKBoEy9V4NTvxDH.PvwrAArbP0DUvDUFf8ChnJl6/79lh3C"

      assert $salt1 == "$2b$06$LzUyyYdKBoEy9V4NTvxDH."
      assert $salt2 == "$2b$06$LzUyyYdKBoEy9V4NTvxDH."

    proc verify(password: openArray[char]; knownGood: string): bool {.
        ...raises: [ValueError], tags: [], forbids: [].}

  Verifies a given plaintext password against a hash from a known good source such as a database or other data storage.

  Be careful when accepting a hash from a source outside of your control as a malicious user could pass salts with a very high cost factor, resulting in denial of service attack.

  Example:

      let knownGood = "$2b$06$LzUyyYdKBoEy9V4NTvxDH.O11KQP30/Zyp5pQAQ.0Cy89WnkD5Jjy"

      assert verify("correct horse battery staple", knownGood)