public async Task WhenVerificationFails_ReturnsFailureResult() { A.CallTo(() => _signatureParser.Parse(_httpRequest, _options)) .Returns(_signature); var client = new Client(_signature.KeyId, "Unit test app", new HMACSignatureAlgorithm("s3cr3t", HashAlgorithmName.SHA256), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); A.CallTo(() => _clientStore.Get(_signature.KeyId)) .Returns(client); var verificationResultCreator = A.Fake <IVerificationResultCreator>(); A.CallTo(() => _verificationResultCreatorFactory.Create(client, _signature)) .Returns(verificationResultCreator); var failure = SignatureVerificationFailure.SignatureExpired("Invalid signature."); A.CallTo(() => _signatureVerifier.VerifySignature(A <HttpRequestForSigning> ._, A <Signature> ._, A <Client> ._)) .Returns(failure); A.CallTo(() => verificationResultCreator.CreateForFailure(failure)) .Returns(new RequestSignatureVerificationResultFailure(client, _signature, failure)); var actual = await _sut.VerifySignature(_httpRequest, _options); actual.Should().BeAssignableTo <RequestSignatureVerificationResultFailure>(); actual.As <RequestSignatureVerificationResultFailure>().IsSuccess.Should().BeFalse(); actual.As <RequestSignatureVerificationResultFailure>().Failure.Should().Be(failure); }
public async Task WhenVerificationFails_InvokesConfiguredCallback() { _httpRequest.Headers["Authorization"] = "tests-scheme abc123"; var cause = SignatureVerificationFailure.InvalidSignatureString("Invalid signature"); var failureResult = new RequestSignatureVerificationResultFailure( new Client( "app1", "Unit test app", new CustomSignatureAlgorithm("test"), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), RequestTargetEscaping.RFC3986), new HttpRequestForVerification(), cause); A.CallTo(() => _requestSignatureVerifier.VerifySignature(_httpRequest, _options)) .Returns(failureResult); RequestSignatureVerificationResult resultFromCallback = null; _options.OnIdentityVerificationFailed = (request, failure) => { resultFromCallback = failure; return(Task.CompletedTask); }; await _sut.DoAuthenticate(); resultFromCallback.Should().Be(failureResult); }
public override SignatureVerificationFailure VerifySync(HttpRequestForSigning signedRequest, Signature signature, Client client) { var expires = signature.Created.HasValue && signature.Expires.HasValue ? signature.Expires.Value - signature.Created.Value : new TimeSpan?(); var signingString = _signingStringComposer.Compose( signedRequest, signature.Headers, signature.Created, expires, signature.Nonce); _logger?.LogDebug("Composed the following signing string for request verification: {0}", signingString); byte[] receivedSignature; try { receivedSignature = _base64Converter.FromBase64(signature.String); } catch (FormatException ex) { return(SignatureVerificationFailure.InvalidSignatureString(ex.Message, ex)); } var isValidSignature = client.SignatureAlgorithm.VerifySignature(signingString, receivedSignature); _logger?.LogDebug("The verification of the signature {0}.", isValidSignature ? "succeeded" : "failed"); if (!isValidSignature) { return(SignatureVerificationFailure.InvalidSignatureString("The signature string does not match the expected value.")); } return(null); }
public async Task <RequestSignatureVerificationResult> VerifySignature(IOwinRequest request, SignedHttpRequestAuthenticationOptions options) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } var signatureParsingResult = _signatureParser.Parse(request, options); if (signatureParsingResult is SignatureParsingFailure parsingFailure) { var failure = SignatureVerificationFailure.InvalidSignature(parsingFailure.Description, parsingFailure.Failure); _logger?.LogWarning("Request signature verification failed ({0}): {1}", failure.Code, failure.Message); return(new RequestSignatureVerificationResultFailure(client: null, requestForVerification: null, failure)); } var parsedSignature = ((SignatureParsingSuccess)signatureParsingResult).Signature; var eventTask = options.OnSignatureParsed; if (eventTask != null) { await eventTask.Invoke(request, parsedSignature).ConfigureAwait(continueOnCapturedContext: false); } var requestForVerification = request.ToHttpRequestForVerification(parsedSignature); return(await _verificationOrchestrator.VerifySignature(requestForVerification).ConfigureAwait(continueOnCapturedContext: false)); }
public override SignatureVerificationFailure VerifySync(HttpRequestForSigning signedRequest, Signature signature, Client client) { if ( signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Created) && AlgorithmNamesThatDoNotAllowCreatedHeader.Contains(client.SignatureAlgorithm.Name, StringComparer.OrdinalIgnoreCase)) { return(SignatureVerificationFailure.InvalidCreatedHeader( $"It is not allowed to take the {HeaderName.PredefinedHeaderNames.Created} into account, when the signature algorithm is {client.SignatureAlgorithm.Name}.")); } if (!signature.Created.HasValue) { return(SignatureVerificationFailure.InvalidCreatedHeader($"The signature does not contain a value for the {nameof(signature.Created)} property, but it is required.")); } var createdHeaderValues = signedRequest.Headers.GetValues(HeaderName.PredefinedHeaderNames.Created); if (createdHeaderValues != StringValues.Empty && signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Created)) { if (!long.TryParse(createdHeaderValues.First(), out var parsedCreatedValue)) { return(SignatureVerificationFailure.InvalidCreatedHeader( $"The request does not contain a valid value for the {HeaderName.PredefinedHeaderNames.Created} header.")); } var createdHeaderValue = DateTimeOffset.FromUnixTimeSeconds(parsedCreatedValue); if (createdHeaderValue != signature.Created.Value) { return(SignatureVerificationFailure.InvalidCreatedHeader( $"The signature creation time does not match the value of the {HeaderName.PredefinedHeaderNames.Created} request header.")); } } return(null); }
public async Task WhenSignatureVerificationFails_InvokesConfiguredCallback() { _request.Headers["Authorization"] = "TestScheme abc123"; var failureResult = new RequestSignatureVerificationResultFailure( new Client("c1", "test", SignatureAlgorithm.CreateForVerification("s3cr3t"), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)), new Signature(), SignatureVerificationFailure.HeaderMissing("A header is missing.", null)); A.CallTo(() => _options.RequestSignatureVerifier.VerifySignature( A <IOwinRequest> .That.Matches(ConvertedRequest), A <SignedHttpRequestAuthenticationOptions> ._)) .Returns(failureResult); RequestSignatureVerificationResult resultFromCallback = null; _options.OnIdentityVerificationFailed = (request, failure) => { resultFromCallback = failure; return(Task.CompletedTask); }; await _method(); resultFromCallback.Should().Be(failureResult); }
public override SignatureVerificationFailure VerifySync(HttpRequestForVerification signedRequest, Signature signature, Client client) { var compositionRequest = _stringCompositionRequestFactory.CreateForVerification(signedRequest, client, signature); var signingString = _signingStringComposer.Compose(compositionRequest); _logger?.LogDebug("Composed the following signing string for request verification: {0}", signingString); byte[] receivedSignature; try { receivedSignature = _base64Converter.FromBase64(signature.String); } catch (FormatException ex) { return(SignatureVerificationFailure.InvalidSignatureString(ex.Message, ex)); } var isValidSignature = client.SignatureAlgorithm.VerifySignature(signingString, receivedSignature); _logger?.LogDebug("The verification of the signature {0}.", isValidSignature ? "succeeded" : "failed"); if (!isValidSignature) { return(SignatureVerificationFailure.InvalidSignatureString("The signature string does not match the expected value.")); } return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForSigning signedRequest, Signature signature, Client client) { if (!signature.Headers.Contains(HeaderName.PredefinedHeaderNames.RequestTarget)) { return(SignatureVerificationFailure.HeaderMissing($"The {HeaderName.PredefinedHeaderNames.RequestTarget} header is required to be included in the signature.")); } if (client.SignatureAlgorithm.ShouldIncludeDateHeader() && !signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Date)) { return(SignatureVerificationFailure.HeaderMissing($"The inclusion of the {HeaderName.PredefinedHeaderNames.Date} header is required when using security algorithm '{client.SignatureAlgorithm.Name}'.")); } if (client.SignatureAlgorithm.ShouldIncludeCreatedHeader() && !signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Created)) { return(SignatureVerificationFailure.HeaderMissing($"The inclusion of the {HeaderName.PredefinedHeaderNames.Created} header is required when using security algorithm '{client.SignatureAlgorithm.Name}'.")); } if (client.SignatureAlgorithm.ShouldIncludeExpiresHeader() && !signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Expires)) { return(SignatureVerificationFailure.HeaderMissing($"The inclusion of the {HeaderName.PredefinedHeaderNames.Expires} header is required when using security algorithm '{client.SignatureAlgorithm.Name}'.")); } foreach (var headerName in signature.Headers) { if (headerName != HeaderName.PredefinedHeaderNames.RequestTarget) { if (!signedRequest.Headers.Contains(headerName)) { return(SignatureVerificationFailure.HeaderMissing($"The request header {headerName} is missing, but it is required to validate the signature.")); } } } return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForSigning signedRequest, Signature signature, Client client) { if ( signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Created) && AlgorithmNamesThatDoNotAllowCreatedValue.Any(alg => signature.Algorithm.StartsWith(alg, StringComparison.OrdinalIgnoreCase))) { return(SignatureVerificationFailure.InvalidCreatedHeader( $"It is not allowed to take the {HeaderName.PredefinedHeaderNames.Created} into account, when the signature algorithm is {signature.Algorithm}.")); } return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForSigning signedRequest, Signature signature, Client client) { if (!signature.Expires.HasValue) { return(SignatureVerificationFailure.HeaderMissing($"The signature does not contain a value for the {nameof(signature.Expires)} property, but it is required.")); } if (signature.Expires.Value < _systemClock.UtcNow) { return(SignatureVerificationFailure.SignatureExpired("The signature is expired.")); } return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForVerification signedRequest, Signature signature, Client client) { if (signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Expires) && !signature.Expires.HasValue) { return(SignatureVerificationFailure.HeaderMissing($"The signature does not contain a value for the {nameof(signature.Expires)} property, but it is required.")); } if (signature.Expires.HasValue && signature.Expires.Value < _systemClock.UtcNow.Add(-client.ClockSkew)) { return(SignatureVerificationFailure.SignatureExpired("The signature is expired.")); } return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForSigning signedRequest, Signature signature, Client client) { if (signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Created) && !signature.Created.HasValue) { return(SignatureVerificationFailure.InvalidCreatedHeader($"The signature does not contain a value for the {nameof(signature.Created)} property, but it is required.")); } if (signature.Created.HasValue && signature.Created.Value > _systemClock.UtcNow.Add(client.ClockSkew)) { return(SignatureVerificationFailure.InvalidCreatedHeader("The signature is not valid yet. Its creation time is in the future.")); } return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForVerification signedRequest, Signature signature, Client client) { // Algorithm parameter is not required if (string.IsNullOrEmpty(signature.Algorithm)) { _logger?.LogDebug("Algorithm verification is not required, because there is no algorithm specified in the signature."); return(null); } // hs2019 is always allowed if (signature.Algorithm == Signature.DefaultSignatureAlgorithm) { return(null); } var algorithmParts = new List <string>(); if (!string.IsNullOrEmpty(signature.Algorithm)) { var separatorIndex = signature.Algorithm.IndexOf('-'); if (separatorIndex < 0 || separatorIndex >= signature.Algorithm.Length - 1) { algorithmParts.Add(signature.Algorithm); } else { algorithmParts.Add(signature.Algorithm.Substring(0, separatorIndex)); algorithmParts.Add(signature.Algorithm.Substring(separatorIndex + 1)); } } if (algorithmParts.Count < 2) { return(SignatureVerificationFailure.InvalidSignatureAlgorithm($"The specified signature algorithm ({signature.Algorithm}) is not supported.")); } if (!SupportedSignatureAlgorithmNames.Contains(algorithmParts[0])) { return(SignatureVerificationFailure.InvalidSignatureAlgorithm($"The specified signature algorithm ({signature.Algorithm}) is not supported.")); } if (!SupportedHashAlgorithmNames.Contains(algorithmParts[1].ToUpperInvariant())) { return(SignatureVerificationFailure.InvalidSignatureAlgorithm($"The specified hash algorithm ({algorithmParts[1]}) is not supported.")); } return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForSigning signedRequest, Signature signature, Client client) { // Algorithm parameter is not required if (string.IsNullOrEmpty(signature.Algorithm)) { _logger?.LogDebug("Algorithm match verification is not required, because there is no algorithm specified in the signature."); return(null); } // hs2019 is always ok if (signature.Algorithm == Signature.DefaultSignatureAlgorithm) { return(null); } var algorithmParts = new List <string>(); if (!string.IsNullOrEmpty(signature.Algorithm)) { var separatorIndex = signature.Algorithm.IndexOf('-'); if (separatorIndex < 0 || separatorIndex >= signature.Algorithm.Length - 1) { algorithmParts.Add(signature.Algorithm); } else { algorithmParts.Add(signature.Algorithm.Substring(0, separatorIndex)); algorithmParts.Add(signature.Algorithm.Substring(separatorIndex + 1)); } } if (algorithmParts.Count < 2) { return(SignatureVerificationFailure.InvalidSignatureAlgorithm($"The specified signature algorithm ({signature.Algorithm}) is not supported.")); } if (!client.SignatureAlgorithm.Name.Equals(algorithmParts[0], StringComparison.OrdinalIgnoreCase)) { return(SignatureVerificationFailure.InvalidSignatureAlgorithm($"The specified signature algorithm ({algorithmParts[0]}) does not match the registered signature algorithm for the client with id {client.Id}.")); } if (!client.SignatureAlgorithm.HashAlgorithm.Name.Equals(algorithmParts[1], StringComparison.OrdinalIgnoreCase)) { return(SignatureVerificationFailure.InvalidSignatureAlgorithm($"The specified hash algorithm ({algorithmParts[1]}) does not match the registered hash algorithm for the client with id {client.Id}.")); } return(null); }
public async Task <RequestSignatureVerificationResult> VerifySignature(HttpRequest request, SignedRequestAuthenticationOptions options) { if (request == null) { throw new ArgumentNullException(nameof(request)); } Client client = null; Signature signature = null; try { signature = _signatureParser.Parse(request); client = await _clientStore.Get(signature.KeyId); var requestForSigning = await request.ToRequestForSigning(signature); var verificationFailure = await _signatureVerifier.VerifySignature(requestForSigning, signature, client); var verificationResultCreator = _verificationResultCreatorFactory.Create(client, signature); var result = verificationFailure == null ? verificationResultCreator.CreateForSuccess() : verificationResultCreator.CreateForFailure(verificationFailure); if (result is RequestSignatureVerificationResultSuccess success) { _logger?.LogDebug($"Request signature verification succeeded for principal {success.Principal?.Identity?.Name ?? "[null]"}."); } else if (result is RequestSignatureVerificationResultFailure failure) { _logger?.LogWarning("Request signature verification failed ({0}): {1}", failure.Failure.Code, failure.Failure.Message); } return(result); } catch (InvalidClientException ex) { var failure = SignatureVerificationFailure.InvalidClient(ex.Message, ex); _logger?.LogWarning("Request signature verification failed ({0}): {1}", failure.Code, failure.Message); return(new RequestSignatureVerificationResultFailure(client, signature, failure)); } catch (InvalidSignatureException ex) { var failure = SignatureVerificationFailure.InvalidSignature(ex.Message, ex); _logger?.LogWarning("Request signature verification failed ({0}): {1}", failure.Code, failure.Message); return(new RequestSignatureVerificationResultFailure(client, signature, failure)); } }
public async Task WhenVerificationFails_ReturnsFailureResult() { _httpRequest.Headers["Authorization"] = "tests-scheme abc123"; var cause = SignatureVerificationFailure.InvalidSignatureString("Invalid signature"); A.CallTo(() => _requestSignatureVerifier.VerifySignature(_httpRequest, _options)) .Returns(new RequestSignatureVerificationResultFailure( new Client("app1", "Unit test app", new CustomSignatureAlgorithm("test"), TimeSpan.FromMinutes(1)), new Signature(), cause)); var actual = await _sut.DoAuthenticate(); actual.Succeeded.Should().BeFalse(); actual.Failure.Should().BeAssignableTo <SignatureVerificationException>(); actual.Failure.Message.Should().Be(cause.ToString()); }
public async Task WhenSignatureVerificationFails_ReturnsNull() { _request.Headers["Authorization"] = "TestScheme abc123"; var failureResult = new RequestSignatureVerificationResultFailure( new Client("c1", "test", SignatureAlgorithm.CreateForVerification("s3cr3t"), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)), new Signature(), SignatureVerificationFailure.HeaderMissing("A header is missing.", null)); A.CallTo(() => _options.RequestSignatureVerifier.VerifySignature( A <IOwinRequest> .That.Matches(ConvertedRequest), A <SignedHttpRequestAuthenticationOptions> ._)) .Returns(failureResult); var actual = await _method(); actual.Should().BeNull(); }
public override async Task <SignatureVerificationFailure> Verify(HttpRequestForSigning signedRequest, Signature signature, Client client) { if (string.IsNullOrEmpty(signature.Nonce)) { return(null); } var previousNonce = await _nonceStore.Get(client.Id, signature.Nonce).ConfigureAwait(false); if (previousNonce != null && previousNonce.Expiration >= _systemClock.UtcNow) { return(SignatureVerificationFailure.ReplayedRequest($"The nonce '{previousNonce.Value}' for client {client.Id} ({client.Name}) is not unique and has been used before. It expires at {previousNonce.Expiration:R}.")); } var nonce = new Nonce( clientId: client.Id, value: signature.Nonce, expiration: _systemClock.UtcNow.Add(client.NonceLifetime)); await _nonceStore.Register(nonce).ConfigureAwait(false); return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForVerification signedRequest, Signature signature, Client client) { if (signature.Headers.Contains(HeaderName.PredefinedHeaderNames.RequestTarget) && !signature.Headers.Contains(HeaderName.PredefinedHeaderNames.RequestTarget)) { return(SignatureVerificationFailure.HeaderMissing($"The {HeaderName.PredefinedHeaderNames.RequestTarget} header is required to be included in the signature.")); } foreach (var headerName in signature.Headers) { var isPresent = PseudoHeaders.Contains(headerName); if (!isPresent) { isPresent = signedRequest.Headers.Contains(headerName); } if (!isPresent) { return(SignatureVerificationFailure.HeaderMissing($"The request header {headerName} is missing, but it is required to validate the signature.")); } } return(null); }
public override SignatureVerificationFailure VerifySync(HttpRequestForSigning signedRequest, Signature signature, Client client) { if (signature.Headers.Contains(HeaderName.PredefinedHeaderNames.Digest) && !signedRequest.Headers.Contains(HeaderName.PredefinedHeaderNames.Digest)) { return(SignatureVerificationFailure.HeaderMissing($"The {HeaderName.PredefinedHeaderNames.Digest} header is indicated as part of the signature, but it is not included in the request.")); } if (!signedRequest.Headers.Contains(HeaderName.PredefinedHeaderNames.Digest)) { _logger?.LogDebug("{0} header verification is not required, because it is not present in the request to verify.", HeaderName.PredefinedHeaderNames.Digest); return(null); } if (signedRequest.Body == null) { return(SignatureVerificationFailure.InvalidDigestHeader($"The {HeaderName.PredefinedHeaderNames.Digest} header verification failed. The request has no body.")); } var digestHeaderValue = signedRequest.Headers.GetValues(HeaderName.PredefinedHeaderNames.Digest).FirstOrDefault(); var digestParams = new List <string>(); if (!string.IsNullOrEmpty(digestHeaderValue)) { var separatorIndex = digestHeaderValue.IndexOf('='); if (separatorIndex < 0 || separatorIndex >= digestHeaderValue.Length - 1) { digestParams.Add(digestHeaderValue); } else { digestParams.Add(digestHeaderValue.Substring(0, separatorIndex)); digestParams.Add(digestHeaderValue.Substring(separatorIndex + 1)); } } if (digestParams.Count < 2) { return(SignatureVerificationFailure.InvalidDigestHeader($"The {HeaderName.PredefinedHeaderNames.Digest} request header is invalid.")); } if (!Constants.DigestHashAlgorithms.TryGetValue(digestParams[0], out var digestAlgorithmName)) { return(SignatureVerificationFailure.InvalidDigestHeader($"The {HeaderName.PredefinedHeaderNames.Digest} algorithm name ({digestParams[0] ?? "[null]"}) is invalid.")); } using (var hashAlgorithm = HashAlgorithmFactory.Create(new HashAlgorithmName(digestAlgorithmName))) { if (hashAlgorithm == null) { return(SignatureVerificationFailure.InvalidDigestHeader($"The {HeaderName.PredefinedHeaderNames.Digest} algorithm name ({digestParams[0] ?? "[null]"}) is currently not supported.")); } var payloadBytes = hashAlgorithm.ComputeHash(signedRequest.Body); var calculatedDigest = _base64Converter.ToBase64(payloadBytes); var receivedDigest = digestParams[1]; if (calculatedDigest != receivedDigest) { return(SignatureVerificationFailure.InvalidDigestHeader("The digest header verification failed.")); } } return(null); }
public async Task <RequestSignatureVerificationResult> VerifySignature(HttpRequest request, SignedRequestAuthenticationOptions options) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } Client client = null; Signature signature = null; try { signature = _signatureParser.Parse(request, options); var eventTask = options.OnSignatureParsed; if (eventTask != null) { await eventTask.Invoke(request, signature).ConfigureAwait(false); } try { signature.Validate(); } catch (ValidationException ex) { throw new InvalidSignatureException( "The signature is invalid. See inner exception.", ex); } client = await _clientStore.Get(signature.KeyId).ConfigureAwait(false); if (client == null) { var failure = SignatureVerificationFailure.InvalidClient($"No {nameof(Client)}s with id '{signature.KeyId}' are registered in the server store."); _logger?.LogWarning("Request signature verification failed ({0}): {1}", failure.Code, failure.Message); return(new RequestSignatureVerificationResultFailure(client, signature, failure)); } var requestForSigning = await request.ToRequestForSigning(signature).ConfigureAwait(false); var verificationFailure = await _signatureVerifier.VerifySignature(requestForSigning, signature, client).ConfigureAwait(false); var verificationResultCreator = _verificationResultCreatorFactory.Create(client, signature); var result = verificationFailure == null ? verificationResultCreator.CreateForSuccess() : verificationResultCreator.CreateForFailure(verificationFailure); if (result is RequestSignatureVerificationResultSuccess success) { _logger?.LogDebug($"Request signature verification succeeded for principal {success.Principal?.Identity?.Name ?? "[null]"}."); } else if (result is RequestSignatureVerificationResultFailure failure) { _logger?.LogWarning("Request signature verification failed ({0}): {1}", failure.Failure.Code, failure.Failure.Message); } return(result); } catch (InvalidSignatureException ex) { var failure = SignatureVerificationFailure.InvalidSignature(ex.Message, ex); _logger?.LogWarning("Request signature verification failed ({0}): {1}", failure.Code, failure.Message); return(new RequestSignatureVerificationResultFailure(client, signature, failure)); } }