Skip to content

Commit cd3b7bb

Browse files
committed
fix(security): propagate unexpected Argon2 verification failures
1 parent 83282fc commit cd3b7bb

5 files changed

Lines changed: 46 additions & 17 deletions

File tree

‎src/CodeBeam.UltimateAuth.Server/Extensions/UAuthExceptionHandlingExtensions.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public static IApplicationBuilder UseUAuthExceptionHandling(this IApplicationBui
2525
});
2626
}
2727

28+
// TODO: Standardize runtime error codes as stable machine-readable identifiers.
29+
// Keep human-readable details separate from codes and ProblemDetails.Type.
2830
private static Task WriteProblemDetails(HttpContext context, UAuthRuntimeException ex)
2931
{
3032
var status = ex.StatusCode;

‎src/client/CodeBeam.UltimateAuth.Client/Infrastructure/UAuthResultMapper.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ private static void EnsureTransport(UAuthTransportResult raw)
110110
};
111111
}
112112

113+
// TODO: Preserve flattened ProblemDetails extensions during deserialization.
114+
// Ensure custom extensions and traceId survive the server-to-SDK round-trip.
113115
private static UAuthProblem? TryDeserializeProblem(UAuthTransportResult raw)
114116
{
115117
if (raw.Body is null || raw.Body.Value.ValueKind != JsonValueKind.Object)

‎src/security/CodeBeam.UltimateAuth.Security.Argon2/Argon2PasswordHasher.cs‎

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,36 @@ public bool Verify(PasswordHash hash, string secret)
4949
!int.TryParse(parts[2], out var parallelism))
5050
return false;
5151

52-
try
53-
{
54-
var salt = Convert.FromBase64String(parts[3]);
55-
var expectedHash = Convert.FromBase64String(parts[4]);
56-
57-
var argon2 = new Argon2id(Encoding.UTF8.GetBytes(secret))
58-
{
59-
Salt = salt,
60-
Iterations = iterations,
61-
MemorySize = memory,
62-
DegreeOfParallelism = parallelism
63-
};
52+
if (iterations <= 0 || memory <= 0 || parallelism <= 0)
53+
return false;
6454

65-
var actualHash = argon2.GetBytes(expectedHash.Length);
55+
byte[] salt;
56+
byte[] expectedHash;
6657

67-
return CryptographicOperations.FixedTimeEquals(actualHash, expectedHash);
58+
try
59+
{
60+
salt = Convert.FromBase64String(parts[3]);
61+
expectedHash = Convert.FromBase64String(parts[4]);
6862
}
69-
catch
63+
catch (FormatException)
7064
{
7165
return false;
7266
}
67+
68+
if (salt.Length == 0 || expectedHash.Length == 0)
69+
return false;
70+
71+
var argon2 = new Argon2id(Encoding.UTF8.GetBytes(secret))
72+
{
73+
Salt = salt,
74+
Iterations = iterations,
75+
MemorySize = memory,
76+
DegreeOfParallelism = parallelism
77+
};
78+
79+
var actualHash = argon2.GetBytes(expectedHash.Length);
80+
81+
return CryptographicOperations.FixedTimeEquals(actualHash, expectedHash);
7382
}
7483

7584
public bool NeedsRehash(PasswordHash hash)

‎tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthFlowClientTests.cs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,7 @@ public async Task BeginPkce_Should_Throw_When_AuthorizationCode_Missing()
515515

516516
Func<Task> act = async () => await client.BeginPkceAsync();
517517

518-
await act.Should().ThrowAsync<InvalidOperationException>()
519-
.WithMessage("*Invalid PKCE authorize response*");
518+
await act.Should().ThrowAsync<UAuthProtocolException>().WithMessage("*authorization code*");
520519
}
521520

522521
[Fact]

‎tests/CodeBeam.UltimateAuth.Tests.Unit/Security/Argon2PasswordHasherTest.cs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,23 @@ public void NeedsRehash_Should_Return_True_When_Parallelism_Changed()
291291
.BeTrue();
292292
}
293293

294+
[Theory]
295+
[InlineData("0.65536.1.c2FsdA==.aGFzaA==")]
296+
[InlineData("3.0.1.c2FsdA==.aGFzaA==")]
297+
[InlineData("3.65536.0.c2FsdA==.aGFzaA==")]
298+
[InlineData("3.65536.1..aGFzaA==")]
299+
[InlineData("3.65536.1.c2FsdA==.")]
300+
public void Verify_ShouldReturnFalse_ForMalformedHash(string encoded)
301+
{
302+
var hasher = CreateHasher();
303+
304+
var hash = PasswordHash.Create(
305+
PasswordAlgorithms.Argon2,
306+
encoded);
307+
308+
hasher.Verify(hash, "password123").Should().BeFalse();
309+
}
310+
294311
private static Argon2PasswordHasher CreateHasher()
295312
{
296313
return CreateHasher(new Argon2Options());

0 commit comments

Comments
 (0)