< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Services.PasswordHashing
Assembly: ArturRios.Heimdall.Command
File(s): /home/ci/agent/workspace/heimdall-api-docs_main/app/src/Application/ArturRios.Heimdall.Command/Services/PasswordHashing.cs
Line coverage
100%
Covered lines: 10
Uncovered lines: 0
Coverable lines: 10
Total lines: 67
Line coverage: 100%
Branch coverage
100%
Covered branches: 8
Total branches: 8
Branch coverage: 100%
Method coverage
100%
Covered methods: 4
Fully covered methods: 4
Total methods: 4
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Insecure()100%11100%
get_Configuration()100%11100%
FromEnvironment(...)100%44100%
EnsureAllowed(...)100%44100%

File(s)

/home/ci/agent/workspace/heimdall-api-docs_main/app/src/Application/ArturRios.Heimdall.Command/Services/PasswordHashing.cs

#LineLine coverage
 1using ArturRios.Util.Hashing;
 2
 3namespace ArturRios.Heimdall.Command.Services;
 4
 5/// <summary>
 6///     The Argon2id cost every password and code derivation in the process uses: the library's
 7///     600 MB / 16-lane default (NFR-18), unless <see cref="InsecureTestVariable" /> asks for the
 8///     cheapest cost Argon2id accepts.
 9/// </summary>
 10/// <remarks>
 11///     <para>
 12///         The switch exists for the test suite. A derivation at the production cost is 600 MB, and
 13///         the suite derives thousands of times: the CI agent's test process grew past its memory
 14///         limit and was killed, and Command.Tests spent most of its time in Argon2id. Nothing a test
 15///         asserts depends on the cost, only on derivation and verification agreeing, so the suite
 16///         sets the variable in <c>tests/default.runsettings</c> and derives through
 17///         <see cref="Configuration" /> everywhere, fixtures included.
 18///     </para>
 19///     <para>
 20///         A switch rather than three numbers, and named for what it is, so that it cannot be half
 21///         tuned: every stored hash can only be verified with the cost it was made with, so a changed
 22///         cost locks every person out. Start-up refuses it in the environments Heimdall is
 23///         deployed in, Production and homologation's Staging (<see cref="EnsureAllowed" />).
 24///     </para>
 25///     <para>
 26///         Read once, when the type is first used: the decoy hash and every gate call must agree for
 27///         the life of the process.
 28///     </para>
 29/// </remarks>
 30public static class PasswordHashing
 31{
 32    /// <summary>The environment variable that, set to <c>true</c>, selects <see cref="Insecure" />.</summary>
 33    public const string InsecureTestVariable = "HEIMDALL_INSECURE_TEST_PASSWORD_HASHING";
 34
 35    /// <summary>The cheapest cost Argon2id accepts: one lane, one pass, 8 KB. For tests only.</summary>
 2236    public static HashConfiguration Insecure { get; } = new(1, 1, 8);
 37
 38    /// <summary>The cost in effect for this process.</summary>
 172939    public static HashConfiguration Configuration { get; } = FromEnvironment(Environment.GetEnvironmentVariable);
 40
 41    /// <summary>
 42    ///     <see cref="Insecure" /> when <paramref name="read" /> gives <c>true</c> (ignoring case and
 43    ///     surrounding space) for <see cref="InsecureTestVariable" />; the production cost for anything
 44    ///     else, unset included.
 45    /// </summary>
 46    public static HashConfiguration FromEnvironment(Func<string, string?> read) =>
 1047        string.Equals(read(InsecureTestVariable)?.Trim(), "true", StringComparison.OrdinalIgnoreCase)
 1048            ? Insecure
 1049            : new HashConfiguration();
 50
 51    /// <summary>
 52    ///     Refuses <paramref name="configuration" /> when it is <see cref="Insecure" /> in an environment
 53    ///     Heimdall is deployed in.
 54    /// </summary>
 55    /// <exception cref="InvalidOperationException">
 56    ///     <paramref name="isDeployed" /> and <paramref name="configuration" /> is <see cref="Insecure" />.
 57    /// </exception>
 58    public static void EnsureAllowed(bool isDeployed, HashConfiguration configuration)
 59    {
 97660        if (isDeployed && ReferenceEquals(configuration, Insecure))
 61        {
 162            throw new InvalidOperationException(
 163                $"{InsecureTestVariable} is set in a deployed environment. Refusing to start: it makes " +
 164                "every password hash trivial to brute-force, and no hash stored with it verifies once it is removed.");
 65        }
 97566    }
 67}