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); }