internal Rfc3161TimestampTokenInfo(IntPtr pTsContext)
        {
            var context = (Rfc3161TimestampWin32.CRYPT_TIMESTAMP_CONTEXT)Marshal.PtrToStructure(pTsContext, typeof(Rfc3161TimestampWin32.CRYPT_TIMESTAMP_CONTEXT));

            byte[] encoded = new byte[context.cbEncoded];
            Marshal.Copy(context.pbEncoded, encoded, 0, context.cbEncoded);

            var cms = new SignedCms();

            cms.Decode(encoded);

            if (!string.Equals(cms.ContentInfo.ContentType.Value, Oids.TSTInfoContentType, StringComparison.Ordinal))
            {
                throw new CryptographicException(Strings.InvalidAsn1);
            }

            RawData = cms.ContentInfo.Content;

            _decoded = TstInfo.Read(RawData);
        }
        public Rfc3161TimestampTokenInfo(
            string policyId,
            Oid hashAlgorithmId,
            byte[] messageHash,
            byte[] serialNumber,
            DateTimeOffset timestamp,
            long?accuracyInMicroseconds = null,
            bool isOrdering             = false,
            byte[] nonce   = null,
            byte[] tsaName = null,
            X509ExtensionCollection extensions = null)
            : base(TimestampTokenInfoId, new byte[] { })
        {
            if (policyId == null)
            {
                throw new ArgumentNullException(nameof(policyId));
            }
            if (!Rfc3161TimestampUtils.IsLegalOid(policyId))
            {
                throw new ArgumentException("Policy identifier does not represent a legal value", nameof(hashAlgorithmId));
            }
            if (hashAlgorithmId == null)
            {
                throw new ArgumentNullException(nameof(hashAlgorithmId));
            }
            if (!Rfc3161TimestampUtils.IsLegalOid(hashAlgorithmId.Value))
            {
                throw new ArgumentException("Hash algorithm does not represent a legal value", nameof(hashAlgorithmId));
            }
            if (messageHash == null)
            {
                throw new ArgumentNullException(nameof(messageHash));
            }
            if (messageHash.Length == 0)
            {
                throw new ArgumentException("Non-empty array is required", nameof(messageHash));
            }
            if (serialNumber == null)
            {
                throw new ArgumentNullException(nameof(serialNumber));
            }
            if (serialNumber.Length == 0)
            {
                throw new ArgumentException("Non-empty array is required", nameof(serialNumber));
            }

            var accuracy = accuracyInMicroseconds.GetValueOrDefault();

            if (accuracy < 0 || accuracy > 4294967295000000)
            {
                throw new ArgumentOutOfRangeException(nameof(accuracyInMicroseconds));
            }

            var tstInfo = new TstInfo
            {
                Version                = 1,
                PolicyId               = policyId,
                HashAlgorithmId        = new Oid(hashAlgorithmId.Value, hashAlgorithmId.FriendlyName),
                HashedMessage          = (byte[])messageHash.Clone(),
                SerialNumber           = (byte[])serialNumber.Clone(),
                Timestamp              = timestamp.ToUniversalTime(),
                AccuracyInMicroseconds = accuracyInMicroseconds,
                IsOrdering             = isOrdering,
                Nonce      = (byte[])nonce?.Clone(),
                TsaName    = (byte[])tsaName?.Clone(),
                Extensions = ShallowCopy(extensions, preserveNull: true),
            };

            RawData  = Encode(tstInfo);
            _decoded = tstInfo;
        }
        private static unsafe byte[] Encode(TstInfo tstInfo)
        {
            var algOid = tstInfo.HashAlgorithmId.Value;

            var policyOidBytes           = new byte[tstInfo.PolicyId.Length + 1];
            var algOidBytes              = new byte[algOid.Length + 1];
            var serialNumberBigEndian    = tstInfo.SerialNumber;
            var serialNumberLittleEndian = new byte[serialNumberBigEndian.Length];

            for (var i = 0; i < serialNumberLittleEndian.Length; i++)
            {
                serialNumberLittleEndian[i] = serialNumberBigEndian[serialNumberBigEndian.Length - i - 1];
            }

            var filetime = tstInfo.Timestamp.ToFileTime();

            fixed(byte *pbPolicyOid = policyOidBytes)
            fixed(char *pszPolicyOid    = tstInfo.PolicyId)
            fixed(byte *pbHashedMessage = tstInfo.HashedMessage)
            fixed(byte *pbSerialNumber  = serialNumberLittleEndian)
            fixed(char *pszAlgOid       = algOid)
            fixed(byte *pbAlgOid        = algOidBytes)
            fixed(byte *pbNonce         = tstInfo.Nonce)
            fixed(byte *pbTsaName       = tstInfo.TsaName)
            {
                Encoding.ASCII.GetBytes(pszPolicyOid, tstInfo.PolicyId.Length, pbPolicyOid, policyOidBytes.Length);
                Encoding.ASCII.GetBytes(pszAlgOid, algOid.Length, pbAlgOid, algOidBytes.Length);

                var info = new Rfc3161TimestampWin32.CRYPT_TIMESTAMP_INFO
                {
                    dwVersion      = tstInfo.Version,
                    pszTSAPolicyId = (IntPtr)pbPolicyOid,
                    fOrdering      = tstInfo.IsOrdering,
                };

                var accuracy = default(Rfc3161TimestampWin32.CRYPT_TIMESTAMP_ACCURACY);

                info.HashAlgorithm.pszOid = (IntPtr)pbAlgOid;
                info.HashedMessage.cbData = (uint)tstInfo.HashedMessage.Length;
                info.HashedMessage.pbData = (IntPtr)pbHashedMessage;
                info.SerialNumber.cbData  = (uint)serialNumberLittleEndian.Length;
                info.SerialNumber.pbData  = (IntPtr)pbSerialNumber;

                info.ftTime.dwLowDateTime  = (int)(filetime & 0xFFFFFFFF);
                info.ftTime.dwHighDateTime = (int)(filetime >> 32);

                if (tstInfo.AccuracyInMicroseconds.HasValue)
                {
                    var val = tstInfo.AccuracyInMicroseconds.Value;
                    val = Math.DivRem(val, 1000, out var rem);
                    accuracy.dwMicros = (int)rem;
                    val = Math.DivRem(val, 1000, out rem);
                    accuracy.dwMillis = (int)rem;

                    if (val > int.MaxValue)
                    {
                        Debug.Fail($"accuracy value {tstInfo.AccuracyInMicroseconds.Value} had seconds component {val}, which should have been stopped");
                        throw new CryptographicException();
                    }

                    accuracy.dwSeconds = (int)val;
                    info.pvAccuracy    = (IntPtr)(&accuracy);
                }

                if (tstInfo.Nonce != null)
                {
                    info.Nonce.cbData = (uint)tstInfo.Nonce.Length;
                    info.Nonce.pbData = (IntPtr)pbNonce;
                }

                if (tstInfo.TsaName != null)
                {
                    info.Tsa.cbData = (uint)tstInfo.TsaName.Length;
                    info.Tsa.pbData = (IntPtr)pbTsaName;
                }

                if (tstInfo.Extensions != null)
                {
                    throw new NotImplementedException();
                }

                var  encodedDataPtr = IntPtr.Zero;
                uint cbEncoded      = 0;

                try
                {
                    if (!Rfc3161TimestampWin32.CryptEncodeObjectEx(
                            Rfc3161TimestampWin32.CryptEncodingTypes.X509_ASN_ENCODING,
                            Rfc3161TimestampWin32.TIMESTAMP_INFO,
                            (IntPtr)(&info),
                            Rfc3161TimestampWin32.CryptEncodeObjectFlags.CRYPT_ENCODE_ALLOC_FLAG,
                            IntPtr.Zero,
                            (IntPtr)(&encodedDataPtr),
                            ref cbEncoded))
                    {
                        throw new CryptographicException(Marshal.GetLastWin32Error());
                    }

                    var encoded = new byte[cbEncoded];
                    Marshal.Copy(encodedDataPtr, encoded, 0, (int)cbEncoded);
                    return(encoded);
                }
                finally
                {
                    if (encodedDataPtr != IntPtr.Zero)
                    {
                        Rfc3161TimestampWin32.LocalFree(encodedDataPtr);
                    }
                }
            }
        }
        private static TstInfo ReadTstInfo(IntPtr pTstInfo)
        {
            var info = (Rfc3161TimestampWin32.CRYPT_TIMESTAMP_INFO)Marshal.PtrToStructure(pTstInfo, typeof(Rfc3161TimestampWin32.CRYPT_TIMESTAMP_INFO));

            var tstInfo = new TstInfo
            {
                Version       = info.dwVersion,
                PolicyId      = Marshal.PtrToStringAnsi(info.pszTSAPolicyId),
                HashedMessage = CopyFromNative(ref info.HashedMessage),
                SerialNumber  = CopyFromNative(ref info.SerialNumber),
                IsOrdering    = info.fOrdering,
                Nonce         = CopyFromNative(ref info.Nonce),
                TsaName       = CopyFromNative(ref info.Tsa),
            };

            // Convert to BigEndian.
            Array.Reverse(tstInfo.SerialNumber);

            var hashAlgOidValue = Marshal.PtrToStringAnsi(info.HashAlgorithm.pszOid);
            Oid hashAlgOid;

            try
            {
                hashAlgOid = Oid.FromOidValue(hashAlgOidValue, OidGroup.HashAlgorithm);
            }
            catch (CryptographicException)
            {
                hashAlgOid = new Oid(hashAlgOidValue, hashAlgOidValue);
            }

            tstInfo.HashAlgorithmId = hashAlgOid;

            long filetime = info.ftTime.dwHighDateTime;

            filetime <<= 32;
            filetime  |= (info.ftTime.dwLowDateTime & 0xFFFFFFFFL);

            tstInfo.Timestamp = DateTimeOffset.FromFileTime(filetime).ToUniversalTime();

            if (info.pvAccuracy != IntPtr.Zero)
            {
                var accuracy = (Rfc3161TimestampWin32.CRYPT_TIMESTAMP_ACCURACY)Marshal.PtrToStructure(info.pvAccuracy, typeof(Rfc3161TimestampWin32.CRYPT_TIMESTAMP_ACCURACY));

                var accuracyMicroSeconds =
                    accuracy.dwSeconds * 1_000_000L +
                    accuracy.dwMillis * 1000L +
                    accuracy.dwSeconds;

                tstInfo.AccuracyInMicroseconds = accuracyMicroSeconds;
            }

            if (info.cExtension > 0)
            {
                throw new NotImplementedException();
            }

            if (tstInfo.HashedMessage == null || tstInfo.SerialNumber == null)
            {
                throw new CryptographicException();
            }

            return(tstInfo);
        }
 public override void CopyFrom(AsnEncodedData asnEncodedData)
 {
     _decoded = null;
     base.CopyFrom(asnEncodedData);
 }