//-----------------------------------------------------------------------
//
// 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;
}
}
}