From f057db0933525425af12378ca4dc0fc151bb2f33 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Thu, 15 Jan 2009 16:29:54 -0800 Subject: [PATCH] Individual association types can now have configured lifetimes. We've almost eliminated the static Configuration class. --- .../OpenId/AuthenticationTests.cs | 8 ++- .../OpenId/Extensions/ExtensionTestBase.cs | 4 +- .../OpenId/OpenIdCoordinator.cs | 4 +- src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs | 4 +- .../OpenId/RelyingParty/OpenIdRelyingPartyTests.cs | 2 +- .../Configuration/AssociationTypeCollection.cs | 59 ++++++++++++++++++++++ .../Configuration/AssociationTypeElement.cs | 55 ++++++++++++++++++++ .../Configuration/HostNameElement.cs | 2 +- .../ProviderSecuritySettingsElement.cs | 18 +++++++ src/DotNetOpenAuth/DotNetOpenAuth.csproj | 2 + src/DotNetOpenAuth/OpenId/Association.cs | 9 +--- .../OpenId/ChannelElements/OpenIdChannel.cs | 11 ++-- .../ChannelElements/SigningBindingElement.cs | 47 +++++++++++------ src/DotNetOpenAuth/OpenId/Configuration.cs | 12 ----- src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs | 29 ++++++++--- .../Messages/AssociateDiffieHellmanResponse.cs | 9 ++-- .../OpenId/Messages/AssociateRequest.cs | 7 ++- .../OpenId/Messages/AssociateSuccessfulResponse.cs | 19 ++++--- .../Messages/AssociateUnencryptedResponse.cs | 10 ++-- .../OpenId/Provider/OpenIdProvider.cs | 4 +- .../OpenId/Provider/ProviderSecuritySettings.cs | 17 +++++++ .../OpenId/RelyingParty/OpenIdRelyingParty.cs | 5 +- 22 files changed, 260 insertions(+), 77 deletions(-) create mode 100644 src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs create mode 100644 src/DotNetOpenAuth/Configuration/AssociationTypeElement.cs diff --git a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs index 5ac1deb..1bfeaa9 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs @@ -6,14 +6,11 @@ namespace DotNetOpenAuth.Test.OpenId { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId; - using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -70,7 +67,8 @@ namespace DotNetOpenAuth.Test.OpenId { private void ParameterizedPositiveAuthenticationTest(Protocol protocol, bool sharedAssociation, bool positive, bool tamper) { ErrorUtilities.VerifyArgument(positive || !tamper, "Cannot tamper with a negative response."); Uri userSetupUrl = protocol.Version.Major < 2 ? new Uri("http://usersetupurl") : null; - Association association = sharedAssociation ? HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart) : null; + ProviderSecuritySettings securitySettings = new ProviderSecuritySettings(); + Association association = sharedAssociation ? HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, securitySettings) : null; var coordinator = new OpenIdCoordinator( rp => { var request = new CheckIdRequest(protocol.Version, ProviderUri, AuthenticationRequestMode.Immediate); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestBase.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestBase.cs index 09a9a8a..a052e55 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestBase.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestBase.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Extensions; using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Messaging; @@ -20,7 +21,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Protocol protocol, IEnumerable requests, IEnumerable responses) { - Association association = HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart); + ProviderSecuritySettings securitySettings = new ProviderSecuritySettings(); + Association association = HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, securitySettings); var coordinator = new OpenIdCoordinator( rp => { RegisterExtension(rp.Channel, Mocks.MockOpenIdExtension.Factory); diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs index e007343..af9c5db 100644 --- a/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs +++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs @@ -55,11 +55,11 @@ namespace DotNetOpenAuth.Test.OpenId { private void EnsurePartiesAreInitialized() { if (this.RelyingParty == null) { - this.RelyingParty = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore(TimeSpan.FromHours(3))); + this.RelyingParty = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore()); } if (this.Provider == null) { - this.Provider = new OpenIdProvider(new StandardProviderApplicationStore(TimeSpan.FromHours(3))); + this.Provider = new OpenIdProvider(new StandardProviderApplicationStore()); } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs index 13079a7..9d15215 100644 --- a/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs +++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs @@ -52,7 +52,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// /// The new instance. protected OpenIdRelyingParty CreateRelyingParty() { - var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore(TimeSpan.FromMinutes(5))); + var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore()); rp.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; return rp; } @@ -62,7 +62,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// /// The new instance. protected OpenIdProvider CreateProvider() { - var op = new OpenIdProvider(new StandardProviderApplicationStore(TimeSpan.FromMinutes(5))); + var op = new OpenIdProvider(new StandardProviderApplicationStore()); op.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; return op; } diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs index 39958f4..a20ac30 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { [TestMethod, ExpectedException(typeof(ArgumentNullException))] public void SecuritySettingsSetNull() { - var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore(TimeSpan.FromMinutes(5))); + var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore()); rp.SecuritySettings = null; } diff --git a/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs b/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs new file mode 100644 index 0000000..454168c --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Andrew Arnott. All rights reserved. +// +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Linq; + using System.Text; + + /// + /// Describes a collection of association type sub-elements in a .config file. + /// + internal class AssociationTypeCollection : ConfigurationElementCollection, IEnumerable { + /// + /// Initializes a new instance of the class. + /// + public AssociationTypeCollection() { + } + + #region IEnumerable Members + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public new IEnumerator GetEnumerator() { + return this.Cast().GetEnumerator(); + } + + #endregion + + /// + /// When overridden in a derived class, creates a new . + /// + /// + /// A new . + /// + protected override ConfigurationElement CreateNewElement() { + return new AssociationTypeElement(); + } + + /// + /// Gets the element key for a specified configuration element when overridden in a derived class. + /// + /// The to return the key for. + /// + /// An that acts as the key for the specified . + /// + protected override object GetElementKey(ConfigurationElement element) { + return ((AssociationTypeElement)element).AssociationType; + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/AssociationTypeElement.cs b/src/DotNetOpenAuth/Configuration/AssociationTypeElement.cs new file mode 100644 index 0000000..dce8d74 --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/AssociationTypeElement.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Andrew Arnott. All rights reserved. +// +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Linq; + using System.Text; + + /// + /// Describes an association type and its maximum lifetime as an element + /// in a .config file. + /// + internal class AssociationTypeElement : ConfigurationElement { + /// + /// The name of the attribute that stores the association type. + /// + private const string AssociationTypeConfigName = "type"; + + /// + /// The name of the attribute that stores the association's maximum lifetime. + /// + private const string MaximumLifetimeConfigName = "lifetime"; + + /// + /// Initializes a new instance of the class. + /// + internal AssociationTypeElement() { + } + + /// + /// Gets or sets the protocol name of the association. + /// + [ConfigurationProperty(AssociationTypeConfigName, IsRequired = true, IsKey = true)] + ////[StringValidator(MinLength = 1)] + public string AssociationType { + get { return (string)this[AssociationTypeConfigName]; } + set { this[AssociationTypeConfigName] = value; } + } + + /// + /// Gets or sets the maximum time a shared association should live. + /// + /// The default value is 14 days. + [ConfigurationProperty(MaximumLifetimeConfigName, IsRequired = true)] + public TimeSpan MaximumLifetime { + get { return (TimeSpan)this[MaximumLifetimeConfigName]; } + set { this[MaximumLifetimeConfigName] = value; } + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/HostNameElement.cs b/src/DotNetOpenAuth/Configuration/HostNameElement.cs index 1b8cf04..304fe92 100644 --- a/src/DotNetOpenAuth/Configuration/HostNameElement.cs +++ b/src/DotNetOpenAuth/Configuration/HostNameElement.cs @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.Configuration { /// /// Gets or sets the name of the host on the white or black list. /// - [ConfigurationProperty(NameConfigName, IsRequired = true)] + [ConfigurationProperty(NameConfigName, IsRequired = true, IsKey = true)] ////[StringValidator(MinLength = 1)] public string Name { get { return (string)this[NameConfigName]; } diff --git a/src/DotNetOpenAuth/Configuration/ProviderSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/ProviderSecuritySettingsElement.cs index eb6c486..8181a23 100644 --- a/src/DotNetOpenAuth/Configuration/ProviderSecuritySettingsElement.cs +++ b/src/DotNetOpenAuth/Configuration/ProviderSecuritySettingsElement.cs @@ -29,6 +29,11 @@ namespace DotNetOpenAuth.Configuration { private const string MaximumHashBitLengthConfigName = "maximumHashBitLength"; /// + /// The name of the associations collection sub-element. + /// + private const string AssociationsConfigName = "associations"; + + /// /// Initializes a new instance of the class. /// public ProviderSecuritySettingsElement() { @@ -63,6 +68,16 @@ namespace DotNetOpenAuth.Configuration { } /// + /// Gets or sets the configured lifetimes of the various association types. + /// + [ConfigurationProperty(AssociationsConfigName, IsDefaultCollection = false)] + [ConfigurationCollection(typeof(AssociationTypeCollection))] + public AssociationTypeCollection AssociationLifetimes { + get { return (AssociationTypeCollection)this[AssociationsConfigName] ?? new AssociationTypeCollection(); } + set { this[AssociationsConfigName] = value; } + } + + /// /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file. /// /// The newly created security settings object. @@ -71,6 +86,9 @@ namespace DotNetOpenAuth.Configuration { settings.MinimumHashBitLength = this.MinimumHashBitLength; settings.MaximumHashBitLength = this.MaximumHashBitLength; settings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks; + foreach (AssociationTypeElement element in this.AssociationLifetimes) { + settings.AssociationLifetimes.Add(element.AssociationType, element.MaximumLifetime); + } return settings; } } diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 5f26824..fcb9226 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -64,6 +64,8 @@ + + diff --git a/src/DotNetOpenAuth/OpenId/Association.cs b/src/DotNetOpenAuth/OpenId/Association.cs index d18735f..29183da 100644 --- a/src/DotNetOpenAuth/OpenId/Association.cs +++ b/src/DotNetOpenAuth/OpenId/Association.cs @@ -93,17 +93,10 @@ namespace DotNetOpenAuth.OpenId { protected internal byte[] SecretKey { get; private set; } /// - /// Gets the duration any association and secret key the Provider generates will be good for. - /// - protected static TimeSpan SmartAssociationLifetime { - get { return Configuration.SmartAssociationLifetime; } - } - - /// /// Gets the duration a secret key used for signing dumb client requests will be good for. /// protected static TimeSpan DumbSecretLifetime { - get { return Configuration.DumbSecretLifetime; } + get { return Configuration.MaximumUserAgentAuthenticationTime; } } /// diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs index daf9778..1b53da3 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs @@ -16,6 +16,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId.Extensions; using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.Provider; /// /// A channel that knows how to send and receive OpenID messages. @@ -54,8 +55,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// /// The association store to use. /// The nonce store to use. - internal OpenIdChannel(IAssociationStore associationStore, INonceStore nonceStore) - : this(associationStore, nonceStore, new OpenIdMessageFactory()) { + /// The security settings. + internal OpenIdChannel(IAssociationStore associationStore, INonceStore nonceStore, ProviderSecuritySettings securitySettings) + : this(associationStore, nonceStore, new OpenIdMessageFactory(), securitySettings) { } /// @@ -77,8 +79,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// The association store to use. /// The nonce store to use. /// An object that knows how to distinguish the various OpenID message types for deserialization purposes. - private OpenIdChannel(IAssociationStore associationStore, INonceStore nonceStore, IMessageFactory messageTypeProvider) : - this(messageTypeProvider, InitializeBindingElements(new SigningBindingElement(associationStore), nonceStore, null, false)) { + /// The security settings. + private OpenIdChannel(IAssociationStore associationStore, INonceStore nonceStore, IMessageFactory messageTypeProvider, ProviderSecuritySettings securitySettings) : + this(messageTypeProvider, InitializeBindingElements(new SigningBindingElement(associationStore, securitySettings), nonceStore, null, false)) { } /// diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs index 5fda0b7..b3acc93 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs @@ -14,6 +14,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.Provider; + using DotNetOpenAuth.OpenId.RelyingParty; /// /// Signs and verifies authentication assertions. @@ -30,21 +32,30 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { private readonly IAssociationStore opAssociations; /// + /// The security settings at the Provider. + /// Only defined when this element is instantiated to service a Provider. + /// + private readonly ProviderSecuritySettings opSecuritySettings; + + /// /// Initializes a new instance of the SigningBindingElement class for use by a Relying Party. /// - /// The association store used to look up the secrets needed for signing. - internal SigningBindingElement(IAssociationStore associations) { - this.rpAssociations = associations; + /// The association store used to look up the secrets needed for signing. May be null for dumb Relying Parties. + internal SigningBindingElement(IAssociationStore associationStore) { + this.rpAssociations = associationStore; } /// /// Initializes a new instance of the SigningBindingElement class for use by a Provider. /// - /// The association store used to look up the secrets needed for signing. - internal SigningBindingElement(IAssociationStore associations) { - ErrorUtilities.VerifyArgumentNotNull(associations, "associations"); - - this.opAssociations = associations; + /// The association store used to look up the secrets needed for signing. + /// The security settings. + internal SigningBindingElement(IAssociationStore associationStore, ProviderSecuritySettings securitySettings) { + ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore"); + ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); + + this.opAssociations = associationStore; + this.opSecuritySettings = securitySettings; } #region IChannelBindingElement Properties @@ -137,7 +148,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { // If the OP confirms that a handle should be invalidated as well, do that. if (!string.IsNullOrEmpty(checkSignatureResponse.InvalidateHandle)) { - this.rpAssociations.RemoveAssociation(indirectSignedResponse.ProviderEndpoint, checkSignatureResponse.InvalidateHandle); + if (this.rpAssociations != null) { + this.rpAssociations.RemoveAssociation(indirectSignedResponse.ProviderEndpoint, checkSignatureResponse.InvalidateHandle); + } } } @@ -160,13 +173,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { // get included in the list of checked parameters. Protocol protocol = Protocol.Lookup(signedMessage.Version); var partsRequiringProtection = from part in MessageDescription.Get(signedMessage.GetType(), signedMessage.Version).Mapping.Values - where part.RequiredProtection != ProtectionLevel.None - select part.Name; + where part.RequiredProtection != ProtectionLevel.None + select part.Name; ErrorUtilities.VerifyInternal(partsRequiringProtection.All(name => name.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal)), "Signing only works when the parameters start with the 'openid.' prefix."); string[] signedParts = signedMessage.SignedParameterOrder.Split(','); var unsignedParts = from partName in partsRequiringProtection - where !signedParts.Contains(partName.Substring(protocol.openid.Prefix.Length)) - select partName; + where !signedParts.Contains(partName.Substring(protocol.openid.Prefix.Length)) + select partName; ErrorUtilities.VerifyProtocol(!unsignedParts.Any(), OpenIdStrings.SignatureDoesNotIncludeMandatoryParts, string.Join(", ", unsignedParts.ToArray())); } @@ -228,7 +241,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } else { // We're on a Relying Party verifying a signature. IDirectedProtocolMessage directedMessage = (IDirectedProtocolMessage)signedMessage; - return this.rpAssociations.GetAssociation(directedMessage.Recipient, signedMessage.AssociationHandle); + if (this.rpAssociations != null) { + return this.rpAssociations.GetAssociation(directedMessage.Recipient, signedMessage.AssociationHandle); + } else { + return null; + } } } @@ -279,7 +296,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { Protocol protocol = Protocol.Default; Association association = this.opAssociations.GetAssociation(AssociationRelyingPartyType.Dumb); if (association == null) { - association = HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.HMAC_SHA256, AssociationRelyingPartyType.Dumb); + association = HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.HMAC_SHA256, AssociationRelyingPartyType.Dumb, this.opSecuritySettings); this.opAssociations.StoreAssociation(AssociationRelyingPartyType.Dumb, association); } diff --git a/src/DotNetOpenAuth/OpenId/Configuration.cs b/src/DotNetOpenAuth/OpenId/Configuration.cs index d0bc688..b3cbb35 100644 --- a/src/DotNetOpenAuth/OpenId/Configuration.cs +++ b/src/DotNetOpenAuth/OpenId/Configuration.cs @@ -19,8 +19,6 @@ namespace DotNetOpenAuth.OpenId { /// static Configuration() { MaximumUserAgentAuthenticationTime = TimeSpan.FromMinutes(5); - SmartAssociationLifetime = TimeSpan.FromDays(14); - DumbSecretLifetime = TimeSpan.FromMinutes(5); } /// @@ -32,15 +30,5 @@ namespace DotNetOpenAuth.OpenId { /// it an instance member, or put it inside the IConsumerApplicationStore interface. /// internal static TimeSpan MaximumUserAgentAuthenticationTime { get; private set; } - - /// - /// Gets the duration any association and secret key the Provider generates will be good for. - /// - internal static TimeSpan SmartAssociationLifetime { get; private set; } - - /// - /// Gets the duration a secret key used for signing dumb client requests will be good for. - /// - internal static TimeSpan DumbSecretLifetime { get; private set; } } } diff --git a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs index b569ba9..98d6b5d 100644 --- a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs +++ b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs @@ -12,12 +12,19 @@ namespace DotNetOpenAuth.OpenId { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.Provider; /// /// An association that uses the HMAC-SHA family of algorithms for message signing. /// internal class HmacShaAssociation : Association { /// + /// The default lifetime of a shared association when no lifetime is given + /// for a specific association type. + /// + private static readonly TimeSpan DefaultMaximumLifetime = TimeSpan.FromDays(14); + + /// /// A list of HMAC-SHA algorithms in order of decreasing bit lengths. /// private static HmacSha[] hmacShaAssociationTypes = { @@ -118,15 +125,18 @@ namespace DotNetOpenAuth.OpenId { /// /// The protocol. /// Type of the association. - /// - /// A value indicating whether the new association will be used privately by the Provider for "dumb mode" authentication - /// or shared with the Relying Party for "smart mode" authentication. - /// + /// A value indicating whether the new association will be used privately by the Provider for "dumb mode" authentication + /// or shared with the Relying Party for "smart mode" authentication. + /// The security settings of the Provider. /// The newly created association. /// /// The new association is NOT automatically put into an association store. This must be done by the caller. /// - internal static HmacShaAssociation Create(Protocol protocol, string associationType, AssociationRelyingPartyType associationUse) { + internal static HmacShaAssociation Create(Protocol protocol, string associationType, AssociationRelyingPartyType associationUse, ProviderSecuritySettings securitySettings) { + ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol"); + ErrorUtilities.VerifyNonZeroLength(associationType, "associationType"); + ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); + // Generate the handle. It must be unique, so we use a time element and a random data element to generate it. string uniq = MessagingUtilities.GetCryptoRandomDataAsBase64(4); string handle = "{" + associationType + "}{" + DateTime.UtcNow.Ticks + "}{" + uniq + "}"; @@ -135,7 +145,14 @@ namespace DotNetOpenAuth.OpenId { int secretLength = GetSecretLength(protocol, associationType); byte[] secret = MessagingUtilities.GetCryptoRandomData(secretLength); - TimeSpan lifetime = associationUse == AssociationRelyingPartyType.Smart ? SmartAssociationLifetime : DumbSecretLifetime; + TimeSpan lifetime; + if (associationUse == AssociationRelyingPartyType.Smart) { + if (!securitySettings.AssociationLifetimes.TryGetValue(associationType, out lifetime)) { + lifetime = DefaultMaximumLifetime; + } + } else { + lifetime = DumbSecretLifetime; + } return Create(protocol, associationType, handle, secret, lifetime); } diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs index d486854..b6b9671 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.Messages { using System.Security.Cryptography; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; + using DotNetOpenAuth.OpenId.Provider; using Org.Mentalis.Security.Cryptography; /// @@ -64,20 +65,22 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Creates the association at the provider side after the association request has been received. /// /// The association request. + /// The security settings of the Provider. /// The newly created association. /// - /// The response message is updated to include the details of the created association by this method, + /// The response message is updated to include the details of the created association by this method, /// but the resulting association is not added to the association store and must be done by the caller. /// - protected override Association CreateAssociationAtProvider(AssociateRequest request) { + protected override Association CreateAssociationAtProvider(AssociateRequest request, ProviderSecuritySettings securitySettings) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); var diffieHellmanRequest = request as AssociateDiffieHellmanRequest; ErrorUtilities.VerifyArgument(diffieHellmanRequest != null, "request"); + ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); this.SessionType = this.SessionType ?? request.SessionType; // Go ahead and create the association first, complete with its secret that we're about to share. - Association association = HmacShaAssociation.Create(this.Protocol, this.AssociationType, AssociationRelyingPartyType.Smart); + Association association = HmacShaAssociation.Create(this.Protocol, this.AssociationType, AssociationRelyingPartyType.Smart, securitySettings); this.AssociationHandle = association.Handle; this.ExpiresIn = association.SecondsTillExpiration; diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs index f2806c4..3ab22b4 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId.Messages { using System.Linq; using System.Text; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; /// @@ -109,6 +110,7 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Creates a Provider's response to an incoming association request. /// /// The association store where a new association (if created) will be stored. Must not be null. + /// The security settings on the Provider. /// /// The appropriate association response that is ready to be sent back to the Relying Party. /// @@ -118,15 +120,16 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Successful association response messages will derive from . /// Failed association response messages will derive from . /// - internal IProtocolMessage CreateResponse(IAssociationStore associationStore) { + internal IProtocolMessage CreateResponse(IAssociationStore associationStore, ProviderSecuritySettings securitySettings) { ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore"); + ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); var 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); + Association association = successResponse.CreateAssociation(this, securitySettings); associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association); } diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs index 424d041..45208d4 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId.Messages { using System.Linq; using System.Text; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.Provider; /// /// The base class that all successful association response messages derive from. @@ -95,16 +96,18 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Called to create the Association based on a request previously given by the Relying Party. /// /// The prior request for an association. + /// The security settings for the Provider. Should be null for Relying Parties. /// The created association. /// - /// The response message is updated to include the details of the created association by this method, + /// The response message is updated to include the details of the created association by this method, /// but the resulting association is not added to the association store and must be done by the caller. - /// This method is called by both the Provider and the Relying Party, but actually performs + /// This method is called by both the Provider and the Relying Party, but actually performs /// quite different operations in either scenario. /// - internal Association CreateAssociation(AssociateRequest request) { + internal Association CreateAssociation(AssociateRequest request, ProviderSecuritySettings securitySettings) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); ErrorUtilities.VerifyInternal(!this.associationCreated, "The association has already been created."); + Association association; // If this message is outgoing, then we need to initialize some common @@ -112,7 +115,8 @@ namespace DotNetOpenAuth.OpenId.Messages { if (this.Incoming) { association = this.CreateAssociationAtRelyingParty(request); } else { - association = this.CreateAssociationAtProvider(request); + ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); + association = this.CreateAssociationAtProvider(request, securitySettings); this.ExpiresIn = association.SecondsTillExpiration; this.AssociationHandle = association.Handle; } @@ -126,15 +130,16 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Called to create the Association based on a request previously given by the Relying Party. /// /// The prior request for an association. + /// The security settings of the Provider. /// The created association. /// - /// The caller will update this message's and + /// The caller will update this message's and /// properties based on the returned by this method, but any other /// association type specific properties must be set by this method. - /// The response message is updated to include the details of the created association by this method, + /// The response message is updated to include the details of the created association by this method, /// but the resulting association is not added to the association store and must be done by the caller. /// - protected abstract Association CreateAssociationAtProvider(AssociateRequest request); + protected abstract Association CreateAssociationAtProvider(AssociateRequest request, ProviderSecuritySettings securitySettings); /// /// Called to create the Association based on a request previously given by the Relying Party. diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateUnencryptedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateUnencryptedResponse.cs index 0c91091..ce14589 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateUnencryptedResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateUnencryptedResponse.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Messages { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; + using DotNetOpenAuth.OpenId.Provider; /// /// The successful unencrypted association response message. @@ -35,18 +36,19 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Called to create the Association based on a request previously given by the Relying Party. /// /// The prior request for an association. + /// The security settings of the Provider. /// The created association. /// - /// The caller will update this message's - /// and + /// The caller will update this message's + /// and /// /// properties based on the returned by this method, but any other /// association type specific properties must be set by this method. /// The response message is updated to include the details of the created association by this method, /// but the resulting association is not added to the association store and must be done by the caller. /// - protected override Association CreateAssociationAtProvider(AssociateRequest request) { - Association association = HmacShaAssociation.Create(Protocol, this.AssociationType, AssociationRelyingPartyType.Smart); + protected override Association CreateAssociationAtProvider(AssociateRequest request, ProviderSecuritySettings securitySettings) { + Association association = HmacShaAssociation.Create(Protocol, this.AssociationType, AssociationRelyingPartyType.Smart, securitySettings); this.MacKey = association.SecretKey; return association; } diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs index a17b16d..694d986 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs @@ -53,9 +53,9 @@ namespace DotNetOpenAuth.OpenId.Provider { ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore"); ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore"); - this.Channel = new OpenIdChannel(associationStore, nonceStore); this.AssociationStore = associationStore; this.SecuritySettings = ProviderSection.Configuration.SecuritySettings.CreateSecuritySettings(); + this.Channel = new OpenIdChannel(this.AssociationStore, nonceStore, this.SecuritySettings); } /// @@ -165,7 +165,7 @@ namespace DotNetOpenAuth.OpenId.Provider { var associateMessage = incomingMessage as AssociateRequest; if (associateMessage != null) { - return new AutoResponsiveRequest(this, incomingMessage, associateMessage.CreateResponse(this.AssociationStore)); + return new AutoResponsiveRequest(this, incomingMessage, associateMessage.CreateResponse(this.AssociationStore, this.SecuritySettings)); } throw ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessageReceivedOfMany); diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs index b025142..cb35c3c 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs @@ -5,11 +5,20 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId.Provider { + using System; + using System.Collections.Generic; + using DotNetOpenAuth.Messaging; + /// /// Security settings that are applicable to providers. /// public sealed class ProviderSecuritySettings : SecuritySettings { /// + /// The subset of association types and their customized lifetimes. + /// + private IDictionary associationLifetimes = new Dictionary(); + + /// /// Initializes a new instance of the class. /// internal ProviderSecuritySettings() @@ -17,6 +26,14 @@ namespace DotNetOpenAuth.OpenId.Provider { } /// + /// Gets a subset of the available association types and their + /// customized maximum lifetimes. + /// + public IDictionary AssociationLifetimes { + get { return this.associationLifetimes; } + } + + /// /// Gets or sets a value indicating whether OpenID 1.x relying parties that may not be /// protecting their users from replay attacks are protected from /// replay attacks by this provider. diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs index c0687b5..351090f 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -75,7 +75,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // If we're a dumb-mode RP, then 2.0 OPs are responsible for preventing replays. ErrorUtilities.VerifyArgument(associationStore == null || nonceStore != null, OpenIdStrings.AssociationStoreRequiresNonceStore); - this.Channel = new OpenIdChannel(associationStore, nonceStore, secretStore); this.AssociationStore = associationStore; this.SecuritySettings = RelyingPartySection.Configuration.SecuritySettings.CreateSecuritySettings(); @@ -85,6 +84,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { if (nonceStore == null) { this.SecuritySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20; } + + this.Channel = new OpenIdChannel(this.AssociationStore, nonceStore, secretStore); } /// @@ -590,7 +591,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { var associateSuccessfulResponse = associateResponse as AssociateSuccessfulResponse; var associateUnsuccessfulResponse = associateResponse as AssociateUnsuccessfulResponse; if (associateSuccessfulResponse != null) { - Association association = associateSuccessfulResponse.CreateAssociation(associateRequest); + Association association = associateSuccessfulResponse.CreateAssociation(associateRequest, null); this.AssociationStore.StoreAssociation(provider.Endpoint, association); return association; } else if (associateUnsuccessfulResponse != null) { -- 2.11.4.GIT