File: Signing\Timestamp\Accuracy.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Packaging\NuGet.Packaging.csproj (NuGet.Packaging)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Security.Cryptography;
using NuGet.Packaging.Signing.DerEncoding;

namespace NuGet.Packaging.Signing
{
    /*
        From RFC 3161 (https://tools.ietf.org/html/rfc3161):

           Accuracy ::= SEQUENCE {
                 seconds        INTEGER              OPTIONAL,
                 millis     [0] INTEGER  (1..999)    OPTIONAL,
                 micros     [1] INTEGER  (1..999)    OPTIONAL  }
    */
    /// <remarks>This is public only to facilitate testing.</remarks>
    public sealed class Accuracy
    {
        public int? Seconds { get; }
        public int? Milliseconds { get; }
        public int? Microseconds { get; }

        private Accuracy(
            int? seconds,
            int? milliseconds,
            int? microseconds)
        {
            Seconds = seconds;
            Milliseconds = milliseconds;
            Microseconds = microseconds;
        }

        public static Accuracy Read(byte[] bytes)
        {
            var reader = DerSequenceReader.CreateForPayload(bytes);

            return Read(reader);
        }

        internal static Accuracy Read(DerSequenceReader reader)
        {
            var accuracyReader = reader.ReadSequence();
            int? seconds = null;
            int? milliseconds = null;
            int? microseconds = null;

            if (accuracyReader.HasTag(DerSequenceReader.DerTag.Integer))
            {
                seconds = accuracyReader.ReadInteger();

                if (seconds < 0)
                {
                    // The ASN.1 definition does not disallow negative numbers
                    // but it doesn't make sense to allow negative numbers.
                    throw new CryptographicException(Strings.InvalidAsn1);
                }
            }

            if (accuracyReader.HasTag(DerSequenceReader.ContextSpecificTagFlag))
            {
                milliseconds = accuracyReader.ReadInteger();

                if (milliseconds < 1 || milliseconds > 999)
                {
                    throw new CryptographicException(Strings.InvalidAsn1);
                }
            }

            if (accuracyReader.HasTag(DerSequenceReader.ContextSpecificTagFlag | 1))
            {
                microseconds = accuracyReader.ReadInteger();

                if (microseconds < 1 || microseconds > 999)
                {
                    throw new CryptographicException(Strings.InvalidAsn1);
                }
            }

            if (accuracyReader.HasData)
            {
                throw new CryptographicException(Strings.InvalidAsn1);
            }

            return new Accuracy(seconds, milliseconds, microseconds);
        }

        public long? GetTotalMicroseconds()
        {
            return Seconds.GetValueOrDefault() * 1_000_000L +
                   Milliseconds.GetValueOrDefault() * 1_000L +
                   Microseconds.GetValueOrDefault();
        }
    }
}