diff --git a/Src/Fido2.Models/Converters/Base64UrlConverter.cs b/Src/Fido2.Models/Converters/Base64UrlConverter.cs index 62c72480..473313a0 100644 --- a/Src/Fido2.Models/Converters/Base64UrlConverter.cs +++ b/Src/Fido2.Models/Converters/Base64UrlConverter.cs @@ -1,4 +1,6 @@ -using System.Buffers; +#nullable enable + +using System.Buffers; using System.Buffers.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -12,13 +14,44 @@ public sealed class Base64UrlConverter : JsonConverter { public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (!reader.HasValueSequence) + byte[]? rentedBuffer = null; + + scoped ReadOnlySpan source; + + if (!reader.HasValueSequence && !reader.ValueIsEscaped) { - return Base64Url.DecodeFromUtf8(reader.ValueSpan); + source = reader.ValueSpan; } else { - return Base64Url.DecodeFromChars(reader.GetString()); + int valueLength = reader.HasValueSequence ? checked((int)reader.ValueSequence.Length) : reader.ValueSpan.Length; + + Span buffer = valueLength <= 32 ? stackalloc byte[32] : (rentedBuffer = ArrayPool.Shared.Rent(valueLength)); + int bytesRead = reader.CopyString(buffer); + source = buffer[..bytesRead]; + } + + try + { + return Base64Url.DecodeFromUtf8(source); + } + catch + { + if (Base64.IsValid(source)) + { + throw new JsonException("Expected data to be in Base64Url format, but received Base64 encoding instead"); + } + else + { + throw new JsonException("Invalid Base64Url data"); + } + } + finally + { + if (rentedBuffer != null) + { + ArrayPool.Shared.Return(rentedBuffer); + } } }