public void IsValid() { Protocol protocol = Protocol.Default; var request = new CheckAuthenticationRequest(protocol.Version, OPUri); var response = new CheckAuthenticationResponse(protocol.Version, request); var dictionary = this.MessageDescriptions.GetAccessor(response); Assert.AreEqual("false", dictionary["is_valid"]); response.IsValid = true; Assert.AreEqual("true", dictionary["is_valid"]); }
/// <summary> /// Analyzes an incoming request message payload to discover what kind of /// message is embedded in it and returns the type, or null if no match is found. /// </summary> /// <param name="request">The message that was sent as a request that resulted in the response.</param> /// <param name="fields">The name/value pairs that make up the message payload.</param> /// <returns> /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can /// deserialize to. Null if the request isn't recognized as a valid protocol message. /// </returns> public IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) { DirectResponseBase message = null; // Discern the OpenID version of the message. Protocol protocol = Protocol.V11; string ns; if (fields.TryGetValue(Protocol.V20.openidnp.ns, out ns)) { ErrorUtilities.VerifyProtocol(string.Equals(ns, Protocol.OpenId2Namespace, StringComparison.Ordinal), MessagingStrings.UnexpectedMessagePartValue, Protocol.V20.openidnp.ns, ns); protocol = Protocol.V20; } // Handle error messages generally. if (fields.ContainsKey(protocol.openidnp.error)) { message = new DirectErrorResponse(protocol.Version, request); } var associateRequest = request as AssociateRequest; if (associateRequest != null) { if (protocol.Version.Major >= 2 && fields.ContainsKey(protocol.openidnp.error_code)) { // This is a special recognized error case that we create a special message for. message = new AssociateUnsuccessfulResponse(protocol.Version, associateRequest); } else if (message == null) { if (OpenIdUtilities.IsDiffieHellmanPresent) { var associateDiffieHellmanRequest = request as AssociateDiffieHellmanRequest; if (associateDiffieHellmanRequest != null) { message = new AssociateDiffieHellmanRelyingPartyResponse(protocol.Version, associateDiffieHellmanRequest); } } var associateUnencryptedRequest = request as AssociateUnencryptedRequest; if (associateUnencryptedRequest != null) { message = new AssociateUnencryptedResponseRelyingParty(protocol.Version, associateUnencryptedRequest); } } } var checkAuthenticationRequest = request as CheckAuthenticationRequest; if (checkAuthenticationRequest != null && message == null) { message = new CheckAuthenticationResponse(protocol.Version, checkAuthenticationRequest); } if (message != null) { message.SetAsIncoming(); } return message; }
private void ParameterizedAuthenticationTest(Protocol protocol, bool statelessRP, bool sharedAssociation, bool positive, bool immediate, bool tamper) { Contract.Requires<ArgumentException>(!statelessRP || !sharedAssociation, "The RP cannot be stateless while sharing an association with the OP."); Contract.Requires<ArgumentException>(positive || !tamper, "Cannot tamper with a negative response."); var securitySettings = new ProviderSecuritySettings(); var cryptoKeyStore = new MemoryCryptoKeyStore(); var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore); Association association = sharedAssociation ? HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, associationStore, securitySettings) : null; var coordinator = new OpenIdCoordinator( rp => { var request = new CheckIdRequest(protocol.Version, OPUri, immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup); if (association != null) { StoreAssociation(rp, OPUri, association); request.AssociationHandle = association.Handle; } request.ClaimedIdentifier = "http://claimedid"; request.LocalIdentifier = "http://localid"; request.ReturnTo = RPUri; request.Realm = RPUri; rp.Channel.Respond(request); if (positive) { if (tamper) { try { rp.Channel.ReadFromRequest<PositiveAssertionResponse>(); Assert.Fail("Expected exception {0} not thrown.", typeof(InvalidSignatureException).Name); } catch (InvalidSignatureException) { TestLogger.InfoFormat("Caught expected {0} exception after tampering with signed data.", typeof(InvalidSignatureException).Name); } } else { var response = rp.Channel.ReadFromRequest<PositiveAssertionResponse>(); Assert.IsNotNull(response); Assert.AreEqual(request.ClaimedIdentifier, response.ClaimedIdentifier); Assert.AreEqual(request.LocalIdentifier, response.LocalIdentifier); Assert.AreEqual(request.ReturnTo, response.ReturnTo); // Attempt to replay the message and verify that it fails. // Because in various scenarios and protocol versions different components // notice the replay, we can get one of two exceptions thrown. // When the OP notices the replay we get a generic InvalidSignatureException. // When the RP notices the replay we get a specific ReplayMessageException. try { CoordinatingChannel channel = (CoordinatingChannel)rp.Channel; channel.Replay(response); Assert.Fail("Expected ProtocolException was not thrown."); } catch (ProtocolException ex) { Assert.IsTrue(ex is ReplayedMessageException || ex is InvalidSignatureException, "A {0} exception was thrown instead of the expected {1} or {2}.", ex.GetType(), typeof(ReplayedMessageException).Name, typeof(InvalidSignatureException).Name); } } } else { var response = rp.Channel.ReadFromRequest<NegativeAssertionResponse>(); Assert.IsNotNull(response); if (immediate) { // Only 1.1 was required to include user_setup_url if (protocol.Version.Major < 2) { Assert.IsNotNull(response.UserSetupUrl); } } else { Assert.IsNull(response.UserSetupUrl); } } }, op => { if (association != null) { var key = cryptoKeyStore.GetCurrentKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1)); op.CryptoKeyStore.StoreKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value); } var request = op.Channel.ReadFromRequest<CheckIdRequest>(); Assert.IsNotNull(request); IProtocolMessage response; if (positive) { response = new PositiveAssertionResponse(request); } else { response = new NegativeAssertionResponse(request, op.Channel); } op.Channel.Respond(response); if (positive && (statelessRP || !sharedAssociation)) { var checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>(); var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); checkauthResponse.IsValid = checkauthRequest.IsValid; op.Channel.Respond(checkauthResponse); if (!tamper) { // Respond to the replay attack. checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>(); checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); checkauthResponse.IsValid = checkauthRequest.IsValid; op.Channel.Respond(checkauthResponse); } } }); if (tamper) { coordinator.IncomingMessageFilter = message => { var assertion = message as PositiveAssertionResponse; if (assertion != null) { // Alter the Local Identifier between the Provider and the Relying Party. // If the signature binding element does its job, this should cause the RP // to throw. assertion.LocalIdentifier = "http://victim"; } }; } if (statelessRP) { coordinator.RelyingParty = new OpenIdRelyingParty(null); } coordinator.Run(); }
private async Task ParameterizedAuthenticationTestAsync(Protocol protocol, bool statelessRP, bool sharedAssociation, bool positive, bool immediate, bool tamper) { Requires.That(!statelessRP || !sharedAssociation, null, "The RP cannot be stateless while sharing an association with the OP."); Requires.That(positive || !tamper, null, "Cannot tamper with a negative response."); var securitySettings = new ProviderSecuritySettings(); var cryptoKeyStore = new MemoryCryptoKeyStore(); var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore); Association association = sharedAssociation ? HmacShaAssociationProvider.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, associationStore, securitySettings) : null; int opStep = 0; HandleProvider( async (op, req) => { if (association != null) { var key = cryptoKeyStore.GetCurrentKey( ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1)); op.CryptoKeyStore.StoreKey( ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value); } switch (++opStep) { case 1: var request = await op.Channel.ReadFromRequestAsync<CheckIdRequest>(req, CancellationToken.None); Assert.IsNotNull(request); IProtocolMessage response; if (positive) { response = new PositiveAssertionResponse(request); } else { response = await NegativeAssertionResponse.CreateAsync(request, CancellationToken.None, op.Channel); } return await op.Channel.PrepareResponseAsync(response); case 2: if (positive && (statelessRP || !sharedAssociation)) { var checkauthRequest = await op.Channel.ReadFromRequestAsync<CheckAuthenticationRequest>(req, CancellationToken.None); var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); checkauthResponse.IsValid = checkauthRequest.IsValid; return await op.Channel.PrepareResponseAsync(checkauthResponse); } throw Assumes.NotReachable(); case 3: if (positive && (statelessRP || !sharedAssociation)) { if (!tamper) { // Respond to the replay attack. var checkauthRequest = await op.Channel.ReadFromRequestAsync<CheckAuthenticationRequest>(req, CancellationToken.None); var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); checkauthResponse.IsValid = checkauthRequest.IsValid; return await op.Channel.PrepareResponseAsync(checkauthResponse); } } throw Assumes.NotReachable(); default: throw Assumes.NotReachable(); } }); { var rp = this.CreateRelyingParty(statelessRP); if (tamper) { rp.Channel.IncomingMessageFilter = message => { var assertion = message as PositiveAssertionResponse; if (assertion != null) { // Alter the Local Identifier between the Provider and the Relying Party. // If the signature binding element does its job, this should cause the RP // to throw. assertion.LocalIdentifier = "http://victim"; } }; } var request = new CheckIdRequest( protocol.Version, OPUri, immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup); if (association != null) { StoreAssociation(rp, OPUri, association); request.AssociationHandle = association.Handle; } request.ClaimedIdentifier = "http://claimedid"; request.LocalIdentifier = "http://localid"; request.ReturnTo = RPUri; request.Realm = RPUri; var redirectRequest = await rp.Channel.PrepareResponseAsync(request); Uri redirectResponse; this.HostFactories.AllowAutoRedirects = false; using (var httpClient = rp.Channel.HostFactories.CreateHttpClient()) { using (var response = await httpClient.GetAsync(redirectRequest.Headers.Location)) { Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect)); redirectResponse = response.Headers.Location; } } var assertionMessage = new HttpRequestMessage(HttpMethod.Get, redirectResponse); if (positive) { if (tamper) { try { await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(assertionMessage, CancellationToken.None); Assert.Fail("Expected exception {0} not thrown.", typeof(InvalidSignatureException).Name); } catch (InvalidSignatureException) { TestLogger.InfoFormat( "Caught expected {0} exception after tampering with signed data.", typeof(InvalidSignatureException).Name); } } else { var response = await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(assertionMessage, CancellationToken.None); Assert.IsNotNull(response); Assert.AreEqual(request.ClaimedIdentifier, response.ClaimedIdentifier); Assert.AreEqual(request.LocalIdentifier, response.LocalIdentifier); Assert.AreEqual(request.ReturnTo, response.ReturnTo); // Attempt to replay the message and verify that it fails. // Because in various scenarios and protocol versions different components // notice the replay, we can get one of two exceptions thrown. // When the OP notices the replay we get a generic InvalidSignatureException. // When the RP notices the replay we get a specific ReplayMessageException. try { await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(assertionMessage, CancellationToken.None); Assert.Fail("Expected ProtocolException was not thrown."); } catch (ProtocolException ex) { Assert.IsTrue( ex is ReplayedMessageException || ex is InvalidSignatureException, "A {0} exception was thrown instead of the expected {1} or {2}.", ex.GetType(), typeof(ReplayedMessageException).Name, typeof(InvalidSignatureException).Name); } } } else { var response = await rp.Channel.ReadFromRequestAsync<NegativeAssertionResponse>(assertionMessage, CancellationToken.None); Assert.IsNotNull(response); if (immediate) { // Only 1.1 was required to include user_setup_url if (protocol.Version.Major < 2) { Assert.IsNotNull(response.UserSetupUrl); } } else { Assert.IsNull(response.UserSetupUrl); } } } }