File: Passkeys\Ctap2CborReader.cs
Web Access
Project: src\src\Identity\Core\src\Microsoft.AspNetCore.Identity.csproj (Microsoft.AspNetCore.Identity)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Formats.Cbor;
 
namespace Microsoft.AspNetCore.Identity;
 
/// <summary>
/// A variation of <see cref="CborReader"/> that is used to read COSE keys in a CTAP2 canonical CBOR encoding form.
/// </summary>
internal sealed class Ctap2CborReader : CborReader
{
    private int _remainingKeys;
    private int? _lastReadLabel;
 
    public static Ctap2CborReader Create(ReadOnlyMemory<byte> data)
    {
        var reader = new Ctap2CborReader(data);
        if (reader.ReadStartMap() is not { } keyCount)
        {
            throw new CborContentException("CTAP2 canonical CBOR encoding form requires there to be a definite number of keys.");
        }
        reader._remainingKeys = keyCount;
        return reader;
    }
 
    private Ctap2CborReader(ReadOnlyMemory<byte> data)
        : base(data, CborConformanceMode.Ctap2Canonical)
    {
    }
 
    public bool TryReadCoseKeyLabel(int expectedLabel)
    {
        // The 'expectedLabel' parameter can hold a label that
        // was read when handling a previous optional field.
        // We only need to read the next label if uninhabited.
        if (_lastReadLabel is null)
        {
            // Check that we have not reached the end of the COSE key object.
            if (_remainingKeys == 0)
            {
                return false;
            }
 
            _lastReadLabel = ReadInt32();
        }
 
        if (expectedLabel != _lastReadLabel.Value)
        {
            return false;
        }
 
        // Read was successful - vacate '_lastReadLabel' to advance reads.
        _lastReadLabel = null;
        _remainingKeys--;
        return true;
    }
 
    public void ReadCoseKeyLabel(int expectedLabel)
    {
        if (!TryReadCoseKeyLabel(expectedLabel))
        {
            throw new CborContentException("Unexpected COSE key label");
        }
    }
}