From 511a4a5e2996dad1fd9b2f3075b8090c70692606 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 18 Jan 2009 21:47:48 -0800 Subject: [PATCH] Added associate renegotiate capability to RP. Enabled one of the basic renegotiate tests. --- .../OpenId/AssociationHandshakeTests.cs | 88 +++++++++--- src/DotNetOpenAuth.vsmdi | 156 +++++++++++---------- .../OpenId/ChannelElements/OpenIdMessageFactory.cs | 23 +-- src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs | 23 +-- .../OpenId/Messages/AssociateRequest.cs | 95 ++++++++++--- .../Messages/AssociateUnsuccessfulResponse.cs | 1 + .../OpenId/OpenIdStrings.Designer.cs | 18 +++ src/DotNetOpenAuth/OpenId/OpenIdStrings.resx | 6 + .../OpenId/RelyingParty/OpenIdRelyingParty.cs | 47 ++++++- 9 files changed, 324 insertions(+), 133 deletions(-) diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs index a7d9f08..479d688 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs @@ -29,57 +29,111 @@ namespace DotNetOpenAuth.Test.OpenId { this.ParameterizedAssociationTest(new Uri("http://host")); } - [TestMethod, Ignore] + [TestMethod] public void AssociateDiffieHellmanOverHttps() { // TODO: test the RP and OP agreeing to use Diffie-Hellman over HTTPS. - throw new NotImplementedException(); + Assert.Inconclusive(); } /// /// Verifies that the RP and OP can renegotiate an association type if the RP's /// initial request for an association is for a type the OP doesn't support. /// - [TestMethod, Ignore] + [TestMethod] public void AssociateRenegotiateBitLength() { - // TODO: test where the RP asks for an association type that the OP doesn't support - throw new NotImplementedException(); + Protocol protocol = Protocol.V20; + + // The strategy is to make a simple request of the RP to establish an association, + // and to more carefully observe the Provider-side of things to make sure that both + // the OP and RP are behaving as expected. + OpenIdCoordinator coordinator = new OpenIdCoordinator( + rp => { + var opDescription = new ProviderEndpointDescription(ProviderUri, protocol.Version); + Association association = rp.GetOrCreateAssociation(opDescription); + Assert.AreEqual(protocol.Args.SignatureAlgorithm.HMAC_SHA1, association.GetAssociationType(protocol)); + }, + op => { + op.SecuritySettings.MaximumHashBitLength = 160; // Force OP to reject HMAC-SHA256 + + // Receive initial request for an HMAC-SHA256 association. + AutoResponsiveRequest req = (AutoResponsiveRequest) op.GetRequest(); + AutoResponsiveRequest_Accessor reqAccessor = AutoResponsiveRequest_Accessor.AttachShadow(req); + AssociateRequest associateRequest = (AssociateRequest)reqAccessor.RequestMessage; + Assert.AreEqual(protocol.Args.SignatureAlgorithm.HMAC_SHA256, associateRequest.AssociationType); + + // Ensure that the response is a suggestion that the RP try again with HMAC-SHA1 + AssociateUnsuccessfulResponse renegotiateResponse = (AssociateUnsuccessfulResponse)reqAccessor.ResponseMessage; + Assert.AreEqual(protocol.Args.SignatureAlgorithm.HMAC_SHA1, renegotiateResponse.AssociationType); + req.Response.Send(); + + // Receive second attempt request for an HMAC-SHA1 association. + req = (AutoResponsiveRequest)op.GetRequest(); + reqAccessor = AutoResponsiveRequest_Accessor.AttachShadow(req); + associateRequest = (AssociateRequest)reqAccessor.RequestMessage; + Assert.AreEqual(protocol.Args.SignatureAlgorithm.HMAC_SHA1, associateRequest.AssociationType); + + // Ensure that the response is a success response. + AssociateSuccessfulResponse successResponse = (AssociateSuccessfulResponse)reqAccessor.ResponseMessage; + Assert.AreEqual(protocol.Args.SignatureAlgorithm.HMAC_SHA1, successResponse.AssociationType); + req.Response.Send(); + }); + coordinator.Run(); + } + + /// + /// Verifies that the OP rejects an associate request + /// when the HMAC and DH bit lengths do not match. + /// + [TestMethod] + public void OPReceivesAssociateWithMismatchingAssociationAndSessionBitLengths() { + // TODO: implement this. + Assert.Inconclusive(); + } + + /// + /// Verifies that the RP rejects an associate renegotiate request + /// when the HMAC and DH bit lengths do not match. + /// + [TestMethod] + public void RPReceivesAssociateRenegotiateWithMismatchingAssociationAndSessionBitLengths() { + Assert.Inconclusive("Not yet implemented."); } /// /// Verifies that the RP cannot get caught in an infinite loop if a bad OP /// keeps sending it association retry messages. /// - [TestMethod, Ignore] + [TestMethod] public void AssociateRenegotiateBitLengthRPStopsAfterOneRetry() { // TODO: code here - throw new NotImplementedException(); + Assert.Inconclusive(); } /// /// Verifies security settings limit RP's initial associate request /// - [TestMethod, Ignore] + [TestMethod] public void AssociateRequestDeterminedBySecuritySettings() { // TODO: Code here - throw new NotImplementedException(); + Assert.Inconclusive(); } /// /// Verifies security settings limit RP's acceptance of OP's counter-suggestion /// - [TestMethod, Ignore] + [TestMethod] public void AssociateRenegotiateLimitedByRPSecuritySettings() { // TODO: Code here - throw new NotImplementedException(); + Assert.Inconclusive(); } /// /// Verifies security settings limit OP's set of acceptable association types. /// - [TestMethod, Ignore] + [TestMethod] public void AssociateLimitedByOPSecuritySettings() { // TODO: Code here - throw new NotImplementedException(); + Assert.Inconclusive(); } /// @@ -87,20 +141,20 @@ namespace DotNetOpenAuth.Test.OpenId { /// associate error response from the OP when no suggested association /// type is included. /// - [TestMethod, Ignore] + [TestMethod] public void AssociateContinueAfterOpenIdError() { // TODO: Code here - throw new NotImplementedException(); + Assert.Inconclusive(); } /// /// Verifies that the RP can recover from an invalid or non-existent /// response from the OP, for example in the HTTP timeout case. /// - [TestMethod, Ignore] + [TestMethod] public void AssociateContinueAfterHttpError() { // TODO: Code here - throw new NotImplementedException(); + Assert.Inconclusive(); } /// diff --git a/src/DotNetOpenAuth.vsmdi b/src/DotNetOpenAuth.vsmdi index f331ba9..6b4d2ff 100644 --- a/src/DotNetOpenAuth.vsmdi +++ b/src/DotNetOpenAuth.vsmdi @@ -23,23 +23,22 @@ - + - - + - + - + @@ -51,47 +50,46 @@ + - + - - + + - + - - - + - + - + @@ -101,46 +99,44 @@ - + + + + + - + - - + - - - + - - - - - + + @@ -149,6 +145,7 @@ + @@ -159,29 +156,28 @@ - + - + - - - + + - + - + + - - + @@ -190,7 +186,7 @@ - + @@ -200,25 +196,24 @@ - + - + - + - + - @@ -234,24 +229,25 @@ + - + - + - + - + @@ -263,28 +259,29 @@ - - + - + + - - + + - + - + + @@ -294,8 +291,7 @@ - - + @@ -304,30 +300,33 @@ - + + - + + - - + + - + + @@ -335,32 +334,32 @@ - + - - - + + + - + - + @@ -370,9 +369,9 @@ - + - + @@ -380,16 +379,17 @@ - + - + - + - + + @@ -397,25 +397,27 @@ - - + + - + + - - + + - + + diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs index aa117bc..85c450b 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs @@ -104,18 +104,25 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { protocol = Protocol.V20; } - var associateDiffieHellmanRequest = request as AssociateDiffieHellmanRequest; - var associateUnencryptedRequest = request as AssociateUnencryptedRequest; - var checkAuthenticationRequest = request as CheckAuthenticationRequest; + var associateRequest = request as AssociateRequest; + if (associateRequest != null) { + if (fields.ContainsKey(protocol.openidnp.error_code)) { + message = new AssociateUnsuccessfulResponse(associateRequest); + } else { + var associateDiffieHellmanRequest = request as AssociateDiffieHellmanRequest; + var associateUnencryptedRequest = request as AssociateUnencryptedRequest; - if (associateDiffieHellmanRequest != null) { - message = new AssociateDiffieHellmanResponse(associateDiffieHellmanRequest); - } + if (associateDiffieHellmanRequest != null) { + message = new AssociateDiffieHellmanResponse(associateDiffieHellmanRequest); + } - if (associateUnencryptedRequest != null) { - message = new AssociateUnencryptedResponse(associateUnencryptedRequest); + if (associateUnencryptedRequest != null) { + message = new AssociateUnencryptedResponse(associateUnencryptedRequest); + } + } } + var checkAuthenticationRequest = request as CheckAuthenticationRequest; if (checkAuthenticationRequest != null) { message = new CheckAuthenticationResponse(checkAuthenticationRequest); } diff --git a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs index 98d6b5d..5091efd 100644 --- a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs +++ b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.OpenId { using System; + using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Security.Cryptography; @@ -158,31 +159,33 @@ namespace DotNetOpenAuth.OpenId { } /// - /// Looks for the longest hash length for a given protocol for which we have an association, + /// Looks for the first association type in a preferred-order list that is + /// likely to be supported given a specific OpenID version and the security settings, /// and perhaps a matching Diffie-Hellman session type. /// /// The OpenID version that dictates which associations are available. + /// A value indicating whether to consider higher strength security to be better. Use true for initial association requests from the Relying Party; use false from Providers when the Relying Party asks for an unrecognized association in order to pick a suggested alternative that is likely to be supported on both sides. /// The set of requirements the selected association type must comply to. - /// True for HTTP associations, False for HTTPS associations. + /// Use true for HTTP associations, false for HTTPS associations. /// The resulting association type's well known protocol name. (i.e. HMAC-SHA256) /// The resulting session type's well known protocol name, if a matching one is available. (i.e. DH-SHA256) - /// True if a qualifying association could be found; false otherwise. - internal static bool TryFindBestAssociation(Protocol protocol, SecuritySettings securityRequirements, bool requireMatchingDHSessionType, out string associationType, out string sessionType) { + /// + /// True if a qualifying association could be found; false otherwise. + /// + internal static bool TryFindBestAssociation(Protocol protocol, bool highSecurityIsBetter, SecuritySettings securityRequirements, bool requireMatchingDHSessionType, out string associationType, out string sessionType) { ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol"); ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements"); associationType = null; sessionType = null; - // We assume this enumeration is in decreasing bit length order. - foreach (HmacSha sha in hmacShaAssociationTypes) { + IEnumerable preferredOrder = highSecurityIsBetter ? hmacShaAssociationTypes : hmacShaAssociationTypes.Reverse(); + foreach (HmacSha sha in preferredOrder) { int hashSizeInBits = sha.SecretLength * 8; - if (hashSizeInBits > securityRequirements.MaximumHashBitLength) { + if (hashSizeInBits > securityRequirements.MaximumHashBitLength || + hashSizeInBits < securityRequirements.MinimumHashBitLength) { continue; } - if (hashSizeInBits < securityRequirements.MinimumHashBitLength) { - break; - } sessionType = DiffieHellmanUtilities.GetNameForSize(protocol, hashSizeInBits); if (requireMatchingDHSessionType && sessionType == null) { continue; diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs index 3ab22b4..3320cc6 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs @@ -79,31 +79,50 @@ namespace DotNetOpenAuth.OpenId.Messages { ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements"); ErrorUtilities.VerifyArgumentNotNull(provider, "provider"); - AssociateRequest associateRequest; - // Apply our knowledge of the endpoint's transport, OpenID version, and - // security requirements to decide the best association + // security requirements to decide the best association. bool unencryptedAllowed = provider.Endpoint.IsTransportSecure(); bool useDiffieHellman = !unencryptedAllowed; string associationType, sessionType; - if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.ProtocolVersion), securityRequirements, useDiffieHellman, out associationType, out sessionType)) { + if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.ProtocolVersion), true, securityRequirements, useDiffieHellman, out associationType, out sessionType)) { // There are no associations that meet all requirements. Logger.Warn("Security requirements and protocol combination knock out all possible association types. Dumb mode forced."); return null; } + return Create(securityRequirements, provider, associationType, sessionType); + } + + /// + /// Creates an association request message that is appropriate for a given Provider. + /// + /// The set of requirements the selected association type must comply to. + /// The provider to create an association with. + /// Type of the association. + /// Type of the session. + /// + /// The message to send to the Provider to request an association. + /// Null if no association could be created that meet the security requirements + /// and the provider OpenID version. + /// + internal static AssociateRequest Create(SecuritySettings securityRequirements, ProviderEndpointDescription provider, string associationType, string sessionType) { + ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements"); + ErrorUtilities.VerifyArgumentNotNull(provider, "provider"); + ErrorUtilities.VerifyNonZeroLength(associationType, "associationType"); + ErrorUtilities.VerifyArgumentNotNull(sessionType, "sessionType"); + + bool unencryptedAllowed = provider.Endpoint.IsTransportSecure(); if (unencryptedAllowed) { - associateRequest = new AssociateUnencryptedRequest(provider.ProtocolVersion, provider.Endpoint); + var associateRequest = new AssociateUnencryptedRequest(provider.ProtocolVersion, provider.Endpoint); associateRequest.AssociationType = associationType; + return associateRequest; } else { - var diffieHellmanAssociateRequest = new AssociateDiffieHellmanRequest(provider.ProtocolVersion, provider.Endpoint); - diffieHellmanAssociateRequest.AssociationType = associationType; - diffieHellmanAssociateRequest.SessionType = sessionType; - diffieHellmanAssociateRequest.InitializeRequest(); - associateRequest = diffieHellmanAssociateRequest; + var associateRequest = new AssociateDiffieHellmanRequest(provider.ProtocolVersion, provider.Endpoint); + associateRequest.AssociationType = associationType; + associateRequest.SessionType = sessionType; + associateRequest.InitializeRequest(); + return associateRequest; } - - return associateRequest; } /// @@ -124,13 +143,18 @@ namespace DotNetOpenAuth.OpenId.Messages { ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore"); ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); - var response = this.CreateResponseCore(); + IProtocolMessage response; + if (securitySettings.IsAssociationInPermittedRange(Protocol, this.AssociationType)) { + response = this.CreateResponseCore(); - // Create and store the association if this is a successful response. - var successResponse = response as AssociateSuccessfulResponse; - if (successResponse != null) { - Association association = successResponse.CreateAssociation(this, securitySettings); - associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association); + // Create and store the association if this is a successful response. + var successResponse = response as AssociateSuccessfulResponse; + if (successResponse != null) { + Association association = successResponse.CreateAssociation(this, securitySettings); + associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association); + } + } else { + response = this.CreateUnsuccessfulResponse(securitySettings); } return response; @@ -150,5 +174,40 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Failed association response messages will derive from . /// protected abstract IProtocolMessage CreateResponseCore(); + + /// + /// Creates a response that notifies the Relying Party that the requested + /// association type is not supported by this Provider, and offers + /// an alternative association type, if possible. + /// + /// The security settings that apply to this Provider. + /// The response to send to the Relying Party. + private AssociateUnsuccessfulResponse CreateUnsuccessfulResponse(ProviderSecuritySettings securitySettings) { + ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); + + var unsuccessfulResponse = new AssociateUnsuccessfulResponse(this); + + // The strategy here is to suggest that the RP try again with the lowest + // permissible security settings, giving the RP the best chance of being + // able to match with a compatible request. + bool unencryptedAllowed = this.Recipient.IsTransportSecure(); + bool useDiffieHellman = !unencryptedAllowed; + string associationType, sessionType; + if (HmacShaAssociation.TryFindBestAssociation(Protocol, false, securitySettings, useDiffieHellman, out associationType, out sessionType)) { + ErrorUtilities.VerifyInternal(this.AssociationType != associationType, "The RP asked for an association that should have been allowed, but the OP is trying to suggest the same one as an alternative!"); + unsuccessfulResponse.AssociationType = associationType; + unsuccessfulResponse.SessionType = sessionType; + Logger.InfoFormat( + "Association requested of type '{0}' and session '{1}', which the Provider does not support. Sending back suggested alternative of '{0}' with session '{1}'.", + this.AssociationType, + this.SessionType, + unsuccessfulResponse.AssociationType, + unsuccessfulResponse.SessionType); + } else { + Logger.InfoFormat("Association requested of type '{0}' and session '{1}', which the Provider does not support. No alternative association type qualified for suggesting back to the Relying Party.", this.AssociationType, this.SessionType); + } + + return unsuccessfulResponse; + } } } diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs index e24dbf5..de62933 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs @@ -33,6 +33,7 @@ namespace DotNetOpenAuth.OpenId.Messages { /// The originating request. internal AssociateUnsuccessfulResponse(AssociateRequest originatingRequest) : base(originatingRequest) { + this.ErrorMessage = string.Format(OpenIdStrings.AssociationOrSessionTypeUnrecognizedOrNotSupported, originatingRequest.AssociationType, originatingRequest.SessionType); } /// diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs index 7a7bb3c..c60843f 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs @@ -61,6 +61,15 @@ namespace DotNetOpenAuth.OpenId { } /// + /// Looks up a localized string similar to The requested association type '{0}' with session type '{1}' is unrecognized or not supported by this Provider due to security requirements.. + /// + internal static string AssociationOrSessionTypeUnrecognizedOrNotSupported { + get { + return ResourceManager.GetString("AssociationOrSessionTypeUnrecognizedOrNotSupported", resourceCulture); + } + } + + /// /// Looks up a localized string similar to The length of the shared secret ({0}) does not match the length required by the association type ('{1}').. /// internal static string AssociationSecretAndTypeLengthMismatch { @@ -214,6 +223,15 @@ namespace DotNetOpenAuth.OpenId { } /// + /// Looks up a localized string similar to The Provider requested association type '{0}' and session type '{1}', which are not compatible with each other.. + /// + internal static string IncompatibleAssociationAndSessionTypes { + get { + return ResourceManager.GetString("IncompatibleAssociationAndSessionTypes", resourceCulture); + } + } + + /// /// Looks up a localized string similar to {0} (Contact: {1}, Reference: {2}). /// internal static string IndirectErrorFormattedMessage { diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx index 9a76dab..ae1a971 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx @@ -259,4 +259,10 @@ Discovered endpoint info: Providing a DateTime whose Kind is Unspecified is not allowed. + + The requested association type '{0}' with session type '{1}' is unrecognized or not supported by this Provider due to security requirements. + + + The Provider requested association type '{0}' and session type '{1}', which are not compatible with each other. + \ No newline at end of file diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs index 0c29301..18f75a8 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -581,7 +581,25 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } var associateRequest = AssociateRequest.Create(this.SecuritySettings, provider); - if (associateRequest == null) { + + const int RenegotiateRetries = 1; + return this.CreateNewAssociation(provider, associateRequest, RenegotiateRetries); + } + + /// + /// Creates a new association with a given Provider. + /// + /// The provider to create an association with. + /// The associate request. May be null, which will always result in a null return value.. + /// The number of times to try the associate request again if the Provider suggests it. + /// + /// The newly created association, or null if no association can be created with + /// the given Provider given the current security settings. + /// + private Association CreateNewAssociation(ProviderEndpointDescription provider, AssociateRequest associateRequest, int retriesRemaining) { + ErrorUtilities.VerifyArgumentNotNull(provider, "provider"); + + if (associateRequest == null || retriesRemaining < 0) { // this can happen if security requirements and protocol conflict // to where there are no association types to choose from. return null; @@ -595,8 +613,31 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.AssociationStore.StoreAssociation(provider.Endpoint, association); return association; } else if (associateUnsuccessfulResponse != null) { - // TODO: code here - throw new NotImplementedException(); + if (string.IsNullOrEmpty(associateUnsuccessfulResponse.AssociationType)) { + Logger.Debug("Provider rejected an association request and gave no suggestion as to an alternative association type. Giving up."); + return null; + } + + if (!this.SecuritySettings.IsAssociationInPermittedRange(Protocol.Lookup(provider.ProtocolVersion), associateUnsuccessfulResponse.AssociationType)) { + Logger.DebugFormat("Provider rejected an association request and suggested '{0}' as an association to try, which this Relying Party does not support. Giving up."); + return null; + } + + if (retriesRemaining <= 0) { + Logger.Debug("Unable to agree on an association type with the Provider in the allowed number of retries. Giving up."); + return null; + } + + // Make sure the Provider isn't suggesting an incompatible pair of association/session types. + Protocol protocol = Protocol.Lookup(provider.ProtocolVersion); + ErrorUtilities.VerifyProtocol( + HmacShaAssociation.IsDHSessionCompatible(protocol, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType), + OpenIdStrings.IncompatibleAssociationAndSessionTypes, + associateUnsuccessfulResponse.AssociationType, + associateUnsuccessfulResponse.SessionType); + + associateRequest = AssociateRequest.Create(this.SecuritySettings, provider, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType); + return this.CreateNewAssociation(provider, associateRequest, retriesRemaining - 1); } else { throw new ProtocolException(MessagingStrings.UnexpectedMessageReceivedOfMany); } -- 2.11.4.GIT