AnnouncementsIntroducing Arkoren

    Menu

    Hashing Drivers

    Configure Argon2id or bcrypt and understand their stored formats, input limits, and shared metrics.

    What's on this page
    1. Compare Argon2id and bcrypt, then construct a shared hasher without lifecycle hooks.
    2. Configure new-hash policies and recognize each driver's format and input limits.
    3. Observe aggregate facade outcomes without exporting credentials or stored hashes.
    On this page

    Choose Argon2id for new password storage. Choose bcrypt when an existing user database or another system requires bcrypt compatibility. Both run in your process, generate a fresh random salt for every hash, and store work settings inside the encoded record. Neither verifies the other’s format; changing algorithms requires a verification-time migration.

    Compatibility Matrix

    This comparison covers Arkoren’s two hashing drivers, not every feature of their underlying libraries. Verification reads stored work settings; your new-hash configuration does not cap verification cost.

    Driver Random Salt Work Settings Memory Settings Parallelism Settings Rehash Detection Other Driver’s Format
    Argon2id Yes Yes No
    bcrypt Yes No No No

    Both support concurrent callers after construction and require no lifecycle hooks. Neither has credentials, a network connection, a native-client accessor, or a companion adapter. Facade metrics are available with either driver.

    Argon2id

    The Argon2 driver creates Argon2id records by default. It also supports Argon2i when another system requires that variant. From your application’s module directory, install the driver:

    go get arkoren.dev/driver/argon2

    Create Hasher

    Import the driver and facade, then construct one shared hasher:

    import (
    	"arkoren.dev/core/service/hash"
    	"arkoren.dev/driver/argon2/argon2hash"
    )
    
    hasher := hash.New(argon2hash.New())

    Construction performs no hashing or lifecycle I/O and returns no error. The published settings are immutable. Use this hasher to store and check passwords.

    Set Policy

    Defaults come from the native library’s argon2.DefaultConfig: Argon2id version 19, 64 MiB of memory, three iterations, four parallel lanes, a 16-byte salt, and a 32-byte digest. Measure these defaults under your deployment’s resource limits before changing them, following Choosing Cost.

    If measurements justify 128 MiB per operation while retaining the other defaults, construct an alternative policy:

    hasher := hash.New(argon2hash.New(func(policy *argon2hash.Builder) {
    	policy.MemoryCost(128 * 1024)
    }))

    MemoryCost takes kibibytes, not bytes. TimeCost, Parallelism, SaltLength, and HashLength configure the other dimensions. Zero settings restore native defaults after all callbacks. Nonzero settings are not validated at construction; use application-controlled settings, not arbitrary request values.

    For an Argon2i interoperability requirement, import native constants from github.com/matthewhartstonge/argon2 and construct this alternative:

    hasher := hash.New(argon2hash.New(func(policy *argon2hash.Builder) {
    	policy.Mode(argon2.ModeArgon2i)
    	policy.TimeCost(3)
    }))

    Keep at least three iterations for Argon2i. Retain Argon2id for new passwords otherwise. Only version 19 (argon2.Version13) is supported. Setting version 16 or another nonzero unsupported version does not fail construction, but Hash returns an error before computing a digest. Other native hashing failures are returned to the caller.

    Stored Records

    Check accepts supported version-19 Argon2id and Argon2i records regardless of the configured new-hash variant. It uses the record’s memory, iteration, and parallelism values as stored. Accept only application-owned records and bound concurrent work.

    A mismatch returns false, nil. Malformed records and unsupported versions return an error wrapping hash.ErrInvalidHash. Version 16 is rejected, not silently treated as version 19.

    NeedsRehash compares memory, iterations, parallelism, mode, version, salt length, and digest length. Malformed or unsupported records also return true; that does not authorize login or replacement without a successful check. Neither hashing nor checking modifies the caller’s input bytes.

    For all settings, see the argon2hash API reference.

    bcrypt

    The bcrypt driver uses Go’s extended crypto library. Choose it for bcrypt compatibility, and test imported records with known fixture passwords before depending on historical implementation behavior. Install it from your application’s module directory:

    go get arkoren.dev/driver/bcrypt

    Create Hasher

    Construct the driver and facade once:

    import (
    	"arkoren.dev/core/service/hash"
    	"arkoren.dev/driver/bcrypt/bcrypthash"
    )
    
    hasher := hash.New(bcrypthash.New())

    Construction returns no error and performs no hashing or lifecycle I/O. Use the shared password operations, enforcing bcrypt’s input limit before both hashing and checking.

    Set Policy

    Arkoren’s default cost is bcrypthash.DefaultCost, or 12. A cost increase of one approximately doubles the computation. If measurements justify cost 13, construct an alternative hasher:

    hasher := hash.New(bcrypthash.New(func(policy *bcrypthash.Builder) {
    	policy.Cost(13)
    }))

    Zero selects 12; other values are clamped to the supported range of 4 through 31. Clamping is not a resource-safety policy: the upper endpoint is extraordinarily expensive. Keep the cost application-controlled. Low settings such as 4 are useful for isolated behavior tests, not a production recommendation.

    Stored Records

    Hash rejects passwords longer than 72 bytes with the native bcrypt.ErrPasswordTooLong error. Characters can occupy multiple bytes. Your application must also reject overlength attempts before Check, because native verification can accept a longer value sharing a valid 72-byte prefix. Never truncate an attempt or migrate its unverified suffix into Argon2id.

    Before computing a password check, the driver validates the complete stored record: exact length, separators, two decimal cost digits, and canonical unpadded bcrypt base64 for the 22-character salt and 31-character digest. Truncation, trailing data, invalid characters, line breaks, and nonzero unused encoding bits produce an error wrapping hash.ErrInvalidHash. A canonical record with a wrong password returns false, nil instead.

    Complete legacy $2$ records are supported. Minor labels, including $2a$, $2b$, $2x$, $2y$, and unrecognized minor labels, follow the native verifier; the driver adds no minor-label allowlist. This does not establish compatibility with every historical bcrypt implementation.

    NeedsRehash validates the same complete record without password computation, then compares stored cost with configured cost. A supported minor label alone does not request renewal. Checking uses the stored cost even after you change the new-hash policy. Input bytes remain unchanged.

    For all settings, see the bcrypthash API reference.

    Observe Operations

    To measure outcomes through either facade, pass your existing telemetry service as metrics when constructing the facade around an existing driver:

    hasher := hash.New(driver, func(policy *hash.Builder) {
    	policy.Telemetry(metrics)
    })

    Omitting telemetry or setting nil follows telemetry.Default() per call. Passing telemetry.New(nil) explicitly disables reporting; import telemetry from arkoren.dev/core/service/telemetry. See Telemetry for service configuration.

    Facade counts and durations cover encoding and driver outcomes, not traces or events. A password mismatch is not an error; a rehash-needed observation is not a persisted password update. Metric labels contain no password, stored hash, or native error text. Direct driver calls bypass these metrics.

    For facade operations and metric names, see the hash API reference.

    Open a bulk documentation archive?

    This file contains every guide in its scope, not API declarations. Loading it all into an agent can exhaust its context window and increase processing time and cost. For coding tasks, start with the index and read only the relevant pages.

    Use the full archive for offline indexing, bulk processing, or deliberate large-context use.

    Browse the index instead
    Open archive