예제 #1
0
        /// <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)
                {
                    var associateDiffieHellmanRequest = request as AssociateDiffieHellmanRequest;
                    var associateUnencryptedRequest   = request as AssociateUnencryptedRequest;

                    if (associateDiffieHellmanRequest != null)
                    {
                        message = new AssociateDiffieHellmanResponse(protocol.Version, associateDiffieHellmanRequest);
                    }

                    if (associateUnencryptedRequest != null)
                    {
                        message = new AssociateUnencryptedResponse(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);
        }
예제 #2
0
        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"]);
        }
예제 #3
0
        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);
                    }
                }
            }
        }
예제 #4
0
        private void ParameterizedAuthenticationTest(Protocol protocol, bool statelessRP, bool sharedAssociation, bool positive, bool immediate, bool tamper)
        {
            Requires.True(!statelessRP || !sharedAssociation, null, "The RP cannot be stateless while sharing an association with the OP.");
            Requires.True(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;
            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();
        }