private bool ProcessResponse(
            ReadOnlyMemory <byte> source,
            [NotNullWhen(true)] out Rfc3161TimestampToken?token,
            out Rfc3161RequestResponseStatus status,
            out int bytesConsumed,
            bool shouldThrow)
        {
            status = Rfc3161RequestResponseStatus.Unknown;
            token  = null;

            Rfc3161TimeStampResp resp;

            try
            {
                AsnValueReader reader         = new AsnValueReader(source.Span, AsnEncodingRules.DER);
                int            localBytesRead = reader.PeekEncodedValue().Length;

                Rfc3161TimeStampResp.Decode(ref reader, source, out resp);
                bytesConsumed = localBytesRead;
            }
            catch (CryptographicException) when(!shouldThrow)
            {
                bytesConsumed = 0;
                status        = Rfc3161RequestResponseStatus.DoesNotParse;
                return(false);
            }

            // bytesRead will be set past this point

            PkiStatus pkiStatus = (PkiStatus)resp.Status.Status;

            if (pkiStatus != PkiStatus.Granted &&
                pkiStatus != PkiStatus.GrantedWithMods)
            {
                if (shouldThrow)
                {
                    throw new CryptographicException(
                              string.Format(
                                  Strings.Cryptography_TimestampReq_Failure,
                                  pkiStatus,
                                  resp.Status.FailInfo.GetValueOrDefault()));
                }

                status = Rfc3161RequestResponseStatus.RequestFailed;
                return(false);
            }

            if (!Rfc3161TimestampToken.TryDecode(resp.TimeStampToken.GetValueOrDefault(), out token, out _))
            {
                if (shouldThrow)
                {
                    throw new CryptographicException(Strings.Cryptography_TimestampReq_BadResponse);
                }

                bytesConsumed = 0;
                status        = Rfc3161RequestResponseStatus.DoesNotParse;
                return(false);
            }

            status = ValidateResponse(token, shouldThrow);
            return(status == Rfc3161RequestResponseStatus.Accepted);
        }
        private Rfc3161RequestResponseStatus ValidateResponse(
            Rfc3161TimestampToken token,
            bool shouldThrow)
        {
            Debug.Assert(token != null);

            // This method validates the acceptance criteria sprinkled throughout the
            // field descriptions in https://tools.ietf.org/html/rfc3161#section-2.4.1 and
            // https://tools.ietf.org/html/rfc3161#section-2.4.2

            if (!token.VerifyHash(GetMessageHash().Span, HashAlgorithmId.Value))
            {
                if (shouldThrow)
                {
                    throw new CryptographicException(Strings.Cryptography_BadHashValue);
                }

                return(Rfc3161RequestResponseStatus.HashMismatch);
            }

            Rfc3161TimestampTokenInfo tokenInfo = token.TokenInfo;

            // We only understand V1 messaging and validation
            if (tokenInfo.Version != 1)
            {
                if (shouldThrow)
                {
                    throw new CryptographicException(Strings.Cryptography_TimestampReq_BadResponse);
                }

                return(Rfc3161RequestResponseStatus.VersionTooNew);
            }

            // reqPolicy is what the policy SHOULD be, so we can't reject it here.

            ReadOnlyMemory <byte>?requestNonce  = GetNonce();
            ReadOnlyMemory <byte>?responseNonce = tokenInfo.GetNonce();

            // The RFC says that if a nonce was in the request it MUST be present in
            // the response and it MUST be equal.
            //
            // It does not say that if no nonce was requested that the response MUST NOT include one, so
            // don't check anything if no nonce was requested.
            if (requestNonce != null)
            {
                if (responseNonce == null ||
                    !requestNonce.Value.Span.SequenceEqual(responseNonce.Value.Span))
                {
                    if (shouldThrow)
                    {
                        throw new CryptographicException(Strings.Cryptography_TimestampReq_BadNonce);
                    }

                    return(Rfc3161RequestResponseStatus.NonceMismatch);
                }
            }

            SignedCms tokenCms = token.AsSignedCms();

            if (RequestSignerCertificate)
            {
                // If the certificate was requested it
                // A) MUST be present in token.AsSignedCms().Certificates
                // B) the ESSCertID(2) identifier MUST be correct.
                //
                // Other certificates are permitted, and will not be validated.

                if (tokenCms.SignerInfos[0].Certificate == null)
                {
                    if (shouldThrow)
                    {
                        throw new CryptographicException(Strings.Cryptography_TimestampReq_NoCertFound);
                    }

                    return(Rfc3161RequestResponseStatus.RequestedCertificatesMissing);
                }
            }
            else
            {
                // If no certificate was requested then the CMS Certificates collection
                // MUST be empty.

                if (tokenCms.Certificates.Count != 0)
                {
                    if (shouldThrow)
                    {
                        throw new CryptographicException(Strings.Cryptography_TimestampReq_UnexpectedCertFound);
                    }

                    return(Rfc3161RequestResponseStatus.UnexpectedCertificates);
                }
            }

            return(Rfc3161RequestResponseStatus.Accepted);
        }