//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId.ChannelElements { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; /// /// The signing binding element for OpenID Relying Parties. /// internal class RelyingPartySigningBindingElement : SigningBindingElement { /// /// The association store used by Relying Parties to look up the secrets needed for signing. /// private readonly IRelyingPartyAssociationStore rpAssociations; /// /// Initializes a new instance of the class. /// /// The association store used to look up the secrets needed for signing. May be null for dumb Relying Parties. internal RelyingPartySigningBindingElement(IRelyingPartyAssociationStore associationStore) { this.rpAssociations = associationStore; } /// /// Gets a specific association referenced in a given message's association handle. /// /// The signed message whose association handle should be used to lookup the association to return. /// /// The referenced association; or null if such an association cannot be found. /// protected override Association GetSpecificAssociation(ITamperResistantOpenIdMessage signedMessage) { Association association = null; if (!string.IsNullOrEmpty(signedMessage.AssociationHandle)) { IndirectSignedResponse indirectSignedMessage = signedMessage as IndirectSignedResponse; if (this.rpAssociations != null) { // if on a smart RP Uri providerEndpoint = indirectSignedMessage.ProviderEndpoint; association = this.rpAssociations.GetAssociation(providerEndpoint, signedMessage.AssociationHandle); } } return association; } /// /// Gets the association to use to sign or verify a message. /// /// The message to sign or verify. /// /// The association to use to sign or verify the message. /// protected override Association GetAssociation(ITamperResistantOpenIdMessage signedMessage) { // We're on a Relying Party verifying a signature. IDirectedProtocolMessage directedMessage = (IDirectedProtocolMessage)signedMessage; if (this.rpAssociations != null) { return this.rpAssociations.GetAssociation(directedMessage.Recipient, signedMessage.AssociationHandle); } else { return null; } } /// /// Verifies the signature by unrecognized handle. /// /// The message. /// The signed message. /// The protections applied. /// /// The applied protections. /// protected override MessageProtections VerifySignatureByUnrecognizedHandle(IProtocolMessage message, ITamperResistantOpenIdMessage signedMessage, MessageProtections protectionsApplied) { // We did not recognize the association the provider used to sign the message. // Ask the provider to check the signature then. var indirectSignedResponse = (IndirectSignedResponse)signedMessage; var checkSignatureRequest = new CheckAuthenticationRequest(indirectSignedResponse, this.Channel); var checkSignatureResponse = this.Channel.Request(checkSignatureRequest); if (!checkSignatureResponse.IsValid) { Logger.Bindings.Error("Provider reports signature verification failed."); throw new InvalidSignatureException(message); } // If the OP confirms that a handle should be invalidated as well, do that. if (!string.IsNullOrEmpty(checkSignatureResponse.InvalidateHandle)) { if (this.rpAssociations != null) { this.rpAssociations.RemoveAssociation(indirectSignedResponse.ProviderEndpoint, checkSignatureResponse.InvalidateHandle); } } // When we're in dumb mode we can't provide our own replay protection, // but for OpenID 2.0 Providers we can rely on them providing it as part // of signature verification. if (message.Version.Major >= 2) { protectionsApplied |= MessageProtections.ReplayProtection; } return protectionsApplied; } } }