diff options
Diffstat (limited to 'src')
24 files changed, 278 insertions, 128 deletions
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj index 941857d..9bae939 100644 --- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj +++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj @@ -243,7 +243,7 @@ <Compile Include="OAuth\ProtocolTests.cs" /> <Compile Include="OAuth\ServiceProviderDescriptionTests.cs" /> <Compile Include="OAuth\ServiceProviderTests.cs" /> - <Compile Include="OpenId\AssociationsTests.cs" /> + <Compile Include="OpenId\RelyingParty\AssociationsTests.cs" /> <Compile Include="OpenId\AssociationTests.cs" /> <Compile Include="OpenId\AuthenticationTests.cs" /> <Compile Include="OpenId\ChannelElements\ExtensionsBindingElementTests.cs" /> diff --git a/src/DotNetOpenAuth.Test/Mocks/TestBadChannel.cs b/src/DotNetOpenAuth.Test/Mocks/TestBadChannel.cs index 439acbb..515e69e 100644 --- a/src/DotNetOpenAuth.Test/Mocks/TestBadChannel.cs +++ b/src/DotNetOpenAuth.Test/Mocks/TestBadChannel.cs @@ -17,8 +17,8 @@ namespace DotNetOpenAuth.Test.Mocks { : base(badConstructorParam ? null : new TestMessageFactory()) { } - internal new void Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) { - base.Create301RedirectResponse(message, fields); + internal new void Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields, bool payloadInFragment = false) { + base.Create301RedirectResponse(message, fields, payloadInFragment); } internal new void CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) { diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationsTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AssociationsTests.cs index b3d7e4d..531fb45 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AssociationsTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AssociationsTests.cs @@ -4,13 +4,14 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.OpenId { +namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.RelyingParty; using NUnit.Framework; [TestFixture] diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 70b079c..ed3f63f 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -341,6 +341,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Messaging\IncomingWebResponseContract.cs" /> <Compile Include="Messaging\IProtocolMessageWithExtensions.cs" /> <Compile Include="Messaging\InternalErrorException.cs" /> + <Compile Include="Messaging\IStreamSerializingDataBag.cs" /> <Compile Include="Messaging\KeyedCollectionDelegate.cs" /> <Compile Include="Messaging\MultipartPostPart.cs" /> <Compile Include="Messaging\NetworkDirectWebResponse.cs" /> @@ -498,7 +499,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OpenId\RelyingParty\AssociationMemoryStore.cs" /> <Compile Include="OpenId\Provider\ProviderAssociationStore.cs" /> <Compile Include="OpenId\RelyingParty\IAssociationStore.cs" /> - <Compile Include="OpenId\Associations.cs" /> + <Compile Include="OpenId\RelyingParty\Associations.cs" /> <Compile Include="OpenId\Behaviors\AXFetchAsSregTransform.cs" /> <Compile Include="OpenId\Behaviors\BehaviorStrings.Designer.cs"> <AutoGen>True</AutoGen> diff --git a/src/DotNetOpenAuth/Messaging/BinaryDataBagFormatter.cs b/src/DotNetOpenAuth/Messaging/BinaryDataBagFormatter.cs index 0dfd8aa..08c1219 100644 --- a/src/DotNetOpenAuth/Messaging/BinaryDataBagFormatter.cs +++ b/src/DotNetOpenAuth/Messaging/BinaryDataBagFormatter.cs @@ -14,37 +14,18 @@ namespace DotNetOpenAuth.Messaging { using System.Text; using DotNetOpenAuth.Messaging.Bindings; - [ContractClass(typeof(IStreamSerializingMessageContract))] - internal interface IStreamSerializingMessage { - void Serialize(Stream stream); - - void Deserialize(Stream stream); - } - - [ContractClassFor(typeof(IStreamSerializingMessage))] - internal abstract class IStreamSerializingMessageContract : IStreamSerializingMessage { - void IStreamSerializingMessage.Serialize(Stream stream) { - Contract.Requires(stream != null); - Contract.Requires(stream.CanWrite); - throw new NotImplementedException(); - } - - void IStreamSerializingMessage.Deserialize(Stream stream) { - Contract.Requires(stream != null); - Contract.Requires(stream.CanRead); - throw new NotImplementedException(); - } - } - - - internal class BinaryDataBagFormatter<T> : DataBagFormatterBase<T> where T : DataBag, IStreamSerializingMessage, new() { + /// <summary> + /// A compact binary <see cref="DataBag"/> serialization class. + /// </summary> + /// <typeparam name="T">The <see cref="DataBag"/>-derived type to serialize/deserialize.</typeparam> + internal class BinaryDataBagFormatter<T> : DataBagFormatterBase<T> where T : DataBag, IStreamSerializingDataBag, new() { /// <summary> /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> class. /// </summary> /// <param name="signingKey">The crypto service provider with the asymmetric key to use for signing or verifying the token.</param> /// <param name="encryptingKey">The crypto service provider with the asymmetric key to use for encrypting or decrypting the token.</param> /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> - /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <paramref name="decodeOnceOnly"/> is <c>true</c>.</param> /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected internal BinaryDataBagFormatter(RSACryptoServiceProvider signingKey = null, RSACryptoServiceProvider encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : base(signingKey, encryptingKey, compressed, maximumAge, decodeOnceOnly) { @@ -57,19 +38,29 @@ namespace DotNetOpenAuth.Messaging { /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param> /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param> /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> - /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <paramref name="decodeOnceOnly"/> is <c>true</c>.</param> /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected internal BinaryDataBagFormatter(byte[] symmetricSecret = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : base(symmetricSecret, signed, encrypted, compressed, maximumAge, decodeOnceOnly) { Contract.Requires<ArgumentException>(symmetricSecret != null || (!signed && !encrypted), "A secret is required when signing or encrypting is required."); } + /// <summary> + /// Serializes the <see cref="DataBag"/> instance to a buffer. + /// </summary> + /// <param name="message">The message.</param> + /// <returns>The buffer containing the serialized data.</returns> protected override byte[] SerializeCore(T message) { var stream = new MemoryStream(); message.Serialize(stream); return stream.ToArray(); } + /// <summary> + /// Deserializes the <see cref="DataBag"/> instance from a buffer. + /// </summary> + /// <param name="message">The message instance to initialize with data from the buffer.</param> + /// <param name="data">The data buffer.</param> protected override void DeserializeCore(T message, byte[] data) { var stream = new MemoryStream(data); message.Deserialize(stream); diff --git a/src/DotNetOpenAuth/Messaging/DataBagFormatterBase.cs b/src/DotNetOpenAuth/Messaging/DataBagFormatterBase.cs index 89d69d3..98f5e8c 100644 --- a/src/DotNetOpenAuth/Messaging/DataBagFormatterBase.cs +++ b/src/DotNetOpenAuth/Messaging/DataBagFormatterBase.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Messaging { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; + using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -15,7 +16,6 @@ namespace DotNetOpenAuth.Messaging { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.Messaging.Reflection; - using System.IO; /// <summary> /// A serializer for <see cref="DataBag"/>-derived types @@ -23,14 +23,14 @@ namespace DotNetOpenAuth.Messaging { /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> internal abstract class DataBagFormatterBase<T> : IDataBagFormatter<T> where T : DataBag, new() { /// <summary> - /// The length of the nonce to include in tokens that can be decoded once only. + /// The message description cache to use for data bag types. /// </summary> - private const int NonceLength = 6; + protected static readonly MessageDescriptionCollection MessageDescriptions = new MessageDescriptionCollection(); /// <summary> - /// The message description cache to use for data bag types. + /// The length of the nonce to include in tokens that can be decoded once only. /// </summary> - protected static readonly MessageDescriptionCollection MessageDescriptions = new MessageDescriptionCollection(); + private const int NonceLength = 6; /// <summary> /// The symmetric secret used for signing/encryption of verification codes and refresh tokens. @@ -85,29 +85,10 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> class. /// </summary> - /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param> - /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param> - /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> - /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> - /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> - private DataBagFormatterBase(bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) { - Contract.Requires<ArgumentException>(signed || decodeOnceOnly == null, "A signature must be applied if this data is meant to be decoded only once."); - Contract.Requires<ArgumentException>(maximumAge.HasValue || decodeOnceOnly == null, "A maximum age must be given if a message can only be decoded once."); - - this.signed = signed; - this.maximumAge = maximumAge; - this.decodeOnceOnly = decodeOnceOnly; - this.encrypted = encrypted; - this.compressed = compressed; - } - - /// <summary> - /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> class. - /// </summary> /// <param name="signingKey">The crypto service provider with the asymmetric key to use for signing or verifying the token.</param> /// <param name="encryptingKey">The crypto service provider with the asymmetric key to use for encrypting or decrypting the token.</param> /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> - /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <paramref name="decodeOnceOnly"/> is <c>true</c>.</param> /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected DataBagFormatterBase(RSACryptoServiceProvider signingKey = null, RSACryptoServiceProvider encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : this(signingKey != null, encryptingKey != null, compressed, maximumAge, decodeOnceOnly) { @@ -123,7 +104,7 @@ namespace DotNetOpenAuth.Messaging { /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param> /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param> /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> - /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <paramref name="decodeOnceOnly"/> is <c>true</c>.</param> /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected DataBagFormatterBase(byte[] symmetricSecret = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : this(signed, encrypted, compressed, maximumAge, decodeOnceOnly) { @@ -137,7 +118,26 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Serializes the specified message. + /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> class. + /// </summary> + /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param> + /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param> + /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <paramref name="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> + private DataBagFormatterBase(bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) { + Contract.Requires<ArgumentException>(signed || decodeOnceOnly == null, "A signature must be applied if this data is meant to be decoded only once."); + Contract.Requires<ArgumentException>(maximumAge.HasValue || decodeOnceOnly == null, "A maximum age must be given if a message can only be decoded once."); + + this.signed = signed; + this.maximumAge = maximumAge; + this.decodeOnceOnly = decodeOnceOnly; + this.encrypted = encrypted; + this.compressed = compressed; + } + + /// <summary> + /// Serializes the specified message, including compression, encryption, signing, and nonce handling where applicable. /// </summary> /// <param name="message">The message to serialize. Must not be null.</param> /// <returns>A non-null, non-empty value.</returns> @@ -175,12 +175,8 @@ namespace DotNetOpenAuth.Messaging { return Convert.ToBase64String(finalStream.ToArray()); } - protected abstract byte[] SerializeCore(T message); - - protected abstract void DeserializeCore(T message, byte[] data); - /// <summary> - /// Deserializes a <see cref="DataBag"/>. + /// Deserializes a <see cref="DataBag"/>, including decompression, decryption, signature and nonce validation where applicable. /// </summary> /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param> /// <param name="value">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> @@ -235,11 +231,26 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Serializes the <see cref="DataBag"/> instance to a buffer. + /// </summary> + /// <param name="message">The message.</param> + /// <returns>The buffer containing the serialized data.</returns> + protected abstract byte[] SerializeCore(T message); + + /// <summary> + /// Deserializes the <see cref="DataBag"/> instance from a buffer. + /// </summary> + /// <param name="message">The message instance to initialize with data from the buffer.</param> + /// <param name="data">The data buffer.</param> + protected abstract void DeserializeCore(T message, byte[] data); + + /// <summary> /// Determines whether the signature on this instance is valid. /// </summary> - /// <param name="message">The message whose signature is to be checked.</param> + /// <param name="signedData">The signed data.</param> + /// <param name="signature">The signature.</param> /// <returns> - /// <c>true</c> if the signature is valid; otherwise, <c>false</c>. + /// <c>true</c> if the signature is valid; otherwise, <c>false</c>. /// </returns> private bool IsSignatureValid(byte[] signedData, byte[] signature) { Contract.Requires<ArgumentNullException>(signedData != null, "message"); @@ -255,8 +266,10 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Calculates the signature for the data in this verification code. /// </summary> - /// <param name="message">The message whose signature is to be calculated.</param> - /// <returns>The calculated signature.</returns> + /// <param name="bytesToSign">The bytes to sign.</param> + /// <returns> + /// The calculated signature. + /// </returns> private byte[] CalculateSignature(byte[] bytesToSign) { Contract.Requires<ArgumentNullException>(bytesToSign != null, "bytesToSign"); Contract.Requires<InvalidOperationException>(this.asymmetricSigning != null || this.symmetricHasher != null); diff --git a/src/DotNetOpenAuth/Messaging/IStreamSerializingDataBag.cs b/src/DotNetOpenAuth/Messaging/IStreamSerializingDataBag.cs new file mode 100644 index 0000000..2003f9e --- /dev/null +++ b/src/DotNetOpenAuth/Messaging/IStreamSerializingDataBag.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// <copyright file="IStreamSerializingDataBag.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Diagnostics.Contracts; + using System.IO; + + /// <summary> + /// An interface implemented by <see cref="DataBag"/>-derived types that support binary serialization. + /// </summary> + [ContractClass(typeof(IStreamSerializingDataBaContract))] + internal interface IStreamSerializingDataBag { + /// <summary> + /// Serializes the instance to the specified stream. + /// </summary> + /// <param name="stream">The stream.</param> + void Serialize(Stream stream); + + /// <summary> + /// Initializes the fields on this instance from the specified stream. + /// </summary> + /// <param name="stream">The stream.</param> + void Deserialize(Stream stream); + } + + /// <summary> + /// Code Contract for the <see cref="IStreamSerializingDataBag"/> interface. + /// </summary> + [ContractClassFor(typeof(IStreamSerializingDataBag))] + internal abstract class IStreamSerializingDataBaContract : IStreamSerializingDataBag { + /// <summary> + /// Serializes the instance to the specified stream. + /// </summary> + /// <param name="stream">The stream.</param> + void IStreamSerializingDataBag.Serialize(Stream stream) { + Contract.Requires(stream != null); + Contract.Requires(stream.CanWrite); + throw new NotImplementedException(); + } + + /// <summary> + /// Initializes the fields on this instance from the specified stream. + /// </summary> + /// <param name="stream">The stream.</param> + void IStreamSerializingDataBag.Deserialize(Stream stream) { + Contract.Requires(stream != null); + Contract.Requires(stream.CanRead); + throw new NotImplementedException(); + } + } +} diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index ea33f49..aa7f5b7 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -252,14 +252,6 @@ namespace DotNetOpenAuth.Messaging.Reflection { } } - internal void SetValueAsObject(IMessage message, object value) { - if (this.property != null) { - this.property.SetValue(message, value, null); - } else { - this.field.SetValue(message, value); - } - } - /// <summary> /// Gets the normalized form of a value of a member of a given message. /// Used in serialization. @@ -305,19 +297,6 @@ namespace DotNetOpenAuth.Messaging.Reflection { } /// <summary> - /// Gets the value of the message part, without converting it to/from a string. - /// </summary> - /// <param name="message">The message instance to read from.</param> - /// <returns>The value of the member.</returns> - internal object GetValueAsObject(IMessage message) { - if (this.property != null) { - return this.property.GetValue(message, null); - } else { - return this.field.GetValue(message); - } - } - - /// <summary> /// Figures out the CLR default value for a given type. /// </summary> /// <param name="type">The type whose default value is being sought.</param> @@ -391,6 +370,32 @@ namespace DotNetOpenAuth.Messaging.Reflection { } /// <summary> + /// Gets the value of the message part, without converting it to/from a string. + /// </summary> + /// <param name="message">The message instance to read from.</param> + /// <returns>The value of the member.</returns> + private object GetValueAsObject(IMessage message) { + if (this.property != null) { + return this.property.GetValue(message, null); + } else { + return this.field.GetValue(message); + } + } + + /// <summary> + /// Sets the value of a message part directly with a given value. + /// </summary> + /// <param name="message">The message instance to read from.</param> + /// <param name="value">The value to set on the this part.</param> + private void SetValueAsObject(IMessage message, object value) { + if (this.property != null) { + this.property.SetValue(message, value, null); + } else { + this.field.SetValue(message, value); + } + } + + /// <summary> /// Converts a string representation of the member's value to the appropriate type. /// </summary> /// <param name="value">The string representation of the member's value.</param> diff --git a/src/DotNetOpenAuth/Messaging/UriStyleMessageFormatter.cs b/src/DotNetOpenAuth/Messaging/UriStyleMessageFormatter.cs index 24f696f..b435f1b 100644 --- a/src/DotNetOpenAuth/Messaging/UriStyleMessageFormatter.cs +++ b/src/DotNetOpenAuth/Messaging/UriStyleMessageFormatter.cs @@ -27,7 +27,7 @@ namespace DotNetOpenAuth.Messaging { /// <param name="signingKey">The crypto service provider with the asymmetric key to use for signing or verifying the token.</param> /// <param name="encryptingKey">The crypto service provider with the asymmetric key to use for encrypting or decrypting the token.</param> /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> - /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <paramref name="decodeOnceOnly"/> is <c>true</c>.</param> /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected internal UriStyleMessageFormatter(RSACryptoServiceProvider signingKey = null, RSACryptoServiceProvider encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : base(signingKey, encryptingKey, compressed, maximumAge, decodeOnceOnly) { @@ -40,19 +40,29 @@ namespace DotNetOpenAuth.Messaging { /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param> /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param> /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> - /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <paramref name="decodeOnceOnly"/> is <c>true</c>.</param> /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected internal UriStyleMessageFormatter(byte[] symmetricSecret = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : base(symmetricSecret, signed, encrypted, compressed, maximumAge, decodeOnceOnly) { Contract.Requires<ArgumentException>(symmetricSecret != null || (!signed && !encrypted), "A secret is required when signing or encrypting is required."); } + /// <summary> + /// Serializes the <see cref="DataBag"/> instance to a buffer. + /// </summary> + /// <param name="message">The message.</param> + /// <returns>The buffer containing the serialized data.</returns> protected override byte[] SerializeCore(T message) { var fields = MessageSerializer.Get(message.GetType()).Serialize(MessageDescriptions.GetAccessor(message)); string value = MessagingUtilities.CreateQueryString(fields); return Encoding.UTF8.GetBytes(value); } + /// <summary> + /// Deserializes the <see cref="DataBag"/> instance from a buffer. + /// </summary> + /// <param name="message">The message instance to initialize with data from the buffer.</param> + /// <param name="data">The data buffer.</param> protected override void DeserializeCore(T message, byte[] data) { string value = Encoding.UTF8.GetString(data); diff --git a/src/DotNetOpenAuth/OpenId/Association.cs b/src/DotNetOpenAuth/OpenId/Association.cs index 5c2a09e..0a59a7d 100644 --- a/src/DotNetOpenAuth/OpenId/Association.cs +++ b/src/DotNetOpenAuth/OpenId/Association.cs @@ -14,6 +14,7 @@ namespace DotNetOpenAuth.OpenId { using System.Text; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.RelyingParty; /// <summary> /// Stores a secret used in signing and verifying messages. @@ -159,7 +160,7 @@ namespace DotNetOpenAuth.OpenId { /// <returns> /// The newly dehydrated <see cref="Association"/>, which can be returned /// from a custom association store's - /// <see cref="IAssociationStore<TKey>.GetAssociation(TKey, SecuritySettings)"/> method. + /// <see cref="IAssociationStore.GetAssociation(Uri, SecuritySettings)"/> method. /// </returns> public static Association Deserialize(string handle, DateTime expiresUtc, byte[] privateData) { Contract.Requires<ArgumentNullException>(!String.IsNullOrEmpty(handle)); diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs index 049ea3a..5350e6c 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs @@ -58,6 +58,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Initializes a new instance of the <see cref="OpenIdChannel"/> class /// for use by a Provider. /// </summary> + /// <param name="associationStore">The OpenID Provider's association store or handle encoder.</param> /// <param name="nonceStore">The nonce store to use.</param> /// <param name="securitySettings">The security settings.</param> internal OpenIdChannel(ProviderAssociationStore associationStore, INonceStore nonceStore, ProviderSecuritySettings securitySettings) @@ -357,10 +358,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <summary> /// Initializes the binding elements. /// </summary> - /// <param name="associationStore">The association store.</param> + /// <param name="associationStore">The OpenID Provider's association store or handle encoder.</param> /// <param name="nonceStore">The nonce store to use.</param> /// <param name="securitySettings">The security settings to apply. Must be an instance of either <see cref="RelyingPartySecuritySettings"/> or <see cref="ProviderSecuritySettings"/>.</param> - /// <param name="nonVerifying">A value indicating whether the channel is set up with no functional security binding elements.</param> /// <returns> /// An array of binding elements which may be used to construct the channel. /// </returns> diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs index be64d82..444098e 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs @@ -292,9 +292,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { MessageDescription description = this.Channel.MessageDescriptions.Get(signedMessage); var signedParts = from part in description.Mapping.Values - where (part.RequiredProtection & System.Net.Security.ProtectionLevel.Sign) != 0 - && part.GetValue(signedMessage) != null - select part.Name; + where (part.RequiredProtection & System.Net.Security.ProtectionLevel.Sign) != 0 + && part.GetValue(signedMessage) != null + select part.Name; string prefix = Protocol.V20.openid.Prefix; ErrorUtilities.VerifyInternal(signedParts.All(name => name.StartsWith(prefix, StringComparison.Ordinal)), "All signed message parts must start with 'openid.'."); diff --git a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs index 0108566..4fa88e9 100644 --- a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs +++ b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs @@ -144,8 +144,11 @@ namespace DotNetOpenAuth.OpenId { /// <param name="associationType">Type of the association (i.e. HMAC-SHA1 or HMAC-SHA256)</param> /// <param name="associationUse">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.</param> + /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings of the Provider.</param> - /// <returns>The newly created association.</returns> + /// <returns> + /// The newly created association. + /// </returns> /// <remarks> /// The new association is NOT automatically put into an association store. This must be done by the caller. /// </remarks> diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs index 86e2d8c..d0d9c08 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs @@ -66,8 +66,11 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Creates the association at the provider side after the association request has been received. /// </summary> /// <param name="request">The association request.</param> + /// <param name="associationStore">The OpenID Provider's association store or handle encoder.</param> /// <param name="securitySettings">The security settings of the Provider.</param> - /// <returns>The newly created association.</returns> + /// <returns> + /// The newly created association. + /// </returns> /// <remarks> /// The response message is updated to include the details of the created association by this method, /// but the resulting association is <i>not</i> added to the association store and must be done by the caller. diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs index 937c7ff..ea7e3c6 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs @@ -129,14 +129,15 @@ namespace DotNetOpenAuth.OpenId.Messages { /// <summary> /// Creates a Provider's response to an incoming association request. /// </summary> + /// <param name="associationStore">The association store.</param> /// <param name="securitySettings">The security settings on the Provider.</param> /// <returns> /// The appropriate association response that is ready to be sent back to the Relying Party. /// </returns> /// <remarks> - /// <para>If an association is created, it will be automatically be added to the provided + /// <para>If an association is created, it will be automatically be added to the provided /// association store.</para> - /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>. + /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>. /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para> /// </remarks> internal IProtocolMessage CreateResponse(ProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs index e3d280f..db4937e 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs @@ -99,8 +99,11 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Called to create the Association based on a request previously given by the Relying Party. /// </summary> /// <param name="request">The prior request for an association.</param> + /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings for the Provider. Should be <c>null</c> for Relying Parties.</param> - /// <returns>The created association.</returns> + /// <returns> + /// The created association. + /// </returns> /// <remarks> /// The response message is updated to include the details of the created association by this method. /// This method is called by both the Provider and the Relying Party, but actually performs @@ -132,13 +135,16 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Called to create the Association based on a request previously given by the Relying Party. /// </summary> /// <param name="request">The prior request for an association.</param> + /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings of the Provider.</param> - /// <returns>The created association.</returns> + /// <returns> + /// The created association. + /// </returns> /// <remarks> - /// <para>The caller will update this message's <see cref="ExpiresIn"/> and <see cref="AssociationHandle"/> + /// <para>The caller will update this message's <see cref="ExpiresIn"/> and <see cref="AssociationHandle"/> /// properties based on the <see cref="Association"/> returned by this method, but any other /// association type specific properties must be set by this method.</para> - /// <para>The response message is updated to include the details of the created association by this method, + /// <para>The response message is updated to include the details of the created association by this method, /// but the resulting association is <i>not</i> added to the association store and must be done by the caller.</para> /// </remarks> protected abstract Association CreateAssociationAtProvider(AssociateRequest request, ProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings); diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateUnencryptedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateUnencryptedResponse.cs index 77079cc..3647404 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateUnencryptedResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateUnencryptedResponse.cs @@ -37,15 +37,18 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Called to create the Association based on a request previously given by the Relying Party. /// </summary> /// <param name="request">The prior request for an association.</param> + /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings of the Provider.</param> - /// <returns>The created association.</returns> + /// <returns> + /// The created association. + /// </returns> /// <remarks> - /// <para>The caller will update this message's - /// <see cref="AssociateSuccessfulResponse.ExpiresIn"/> and - /// <see cref="AssociateSuccessfulResponse.AssociationHandle"/> + /// <para>The caller will update this message's + /// <see cref="AssociateSuccessfulResponse.ExpiresIn"/> and + /// <see cref="AssociateSuccessfulResponse.AssociationHandle"/> /// properties based on the <see cref="Association"/> returned by this method, but any other /// association type specific properties must be set by this method.</para> - /// <para>The response message is updated to include the details of the created association by this method, + /// <para>The response message is updated to include the details of the created association by this method, /// but the resulting association is <i>not</i> added to the association store and must be done by the caller.</para> /// </remarks> protected override Association CreateAssociationAtProvider(AssociateRequest request, ProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs index e4e8c7c..5d10ca4 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs @@ -47,7 +47,7 @@ namespace DotNetOpenAuth.OpenId.Messages { // is not valid (any longer). OpenID 2.0 section 11.4.2.2. IndirectSignedResponse signedResponse = new IndirectSignedResponse(request, provider.Channel); string invalidateHandle = ((ITamperResistantOpenIdMessage)signedResponse).InvalidateHandle; - if (!string.IsNullOrEmpty(invalidateHandle) && provider.AssociationStore.IsValid(signedResponse, AssociationRelyingPartyType.Smart, invalidateHandle) == null) { + if (!string.IsNullOrEmpty(invalidateHandle) && !provider.AssociationStore.IsValid(signedResponse, AssociationRelyingPartyType.Smart, invalidateHandle)) { this.InvalidateHandle = invalidateHandle; } } diff --git a/src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs b/src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs index 18f7c0f..3caf05e 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs @@ -12,31 +12,49 @@ namespace DotNetOpenAuth.OpenId.Provider { using System.Text; using DotNetOpenAuth.Messaging; - internal class AssociationDataBag : DataBag, IStreamSerializingMessage { + /// <summary> + /// A signed and encrypted serialization of an association. + /// </summary> + internal class AssociationDataBag : DataBag, IStreamSerializingDataBag { /// <summary> /// Initializes a new instance of the <see cref="AssociationDataBag"/> class. /// </summary> public AssociationDataBag() { } + /// <summary> + /// Gets or sets the association secret. + /// </summary> [MessagePart(IsRequired = true)] internal byte[] Secret { get; set; } + /// <summary> + /// Gets or sets the UTC time that this association expires. + /// </summary> [MessagePart(IsRequired = true)] internal DateTime ExpiresUtc { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is for "dumb" mode RPs. + /// </summary> + /// <value> + /// <c>true</c> if this instance is private association; otherwise, <c>false</c>. + /// </value> [MessagePart(IsRequired = true)] internal bool IsPrivateAssociation { get { return this.AssociationType == AssociationRelyingPartyType.Dumb; } set { this.AssociationType = value ? AssociationRelyingPartyType.Dumb : AssociationRelyingPartyType.Smart; } } + /// <summary> + /// Gets or sets the type of the association (shared or private, a.k.a. smart or dumb). + /// </summary> internal AssociationRelyingPartyType AssociationType { get; set; } - internal static IDataBagFormatter<AssociationDataBag> CreateFormatter(byte[] symmetricSecret) { - return new BinaryDataBagFormatter<AssociationDataBag>(symmetricSecret, signed: true, encrypted: true); - } - + /// <summary> + /// Serializes the instance to the specified stream. + /// </summary> + /// <param name="stream">The stream.</param> public void Serialize(Stream stream) { var writer = new BinaryWriter(stream); writer.Write(this.IsPrivateAssociation); @@ -45,11 +63,24 @@ namespace DotNetOpenAuth.OpenId.Provider { writer.Flush(); } + /// <summary> + /// Initializes the fields on this instance from the specified stream. + /// </summary> + /// <param name="stream">The stream.</param> public void Deserialize(Stream stream) { var reader = new BinaryReader(stream); this.IsPrivateAssociation = reader.ReadBoolean(); this.Secret = reader.ReadBuffer(); this.ExpiresUtc = TimestampEncoder.Epoch + TimeSpan.FromSeconds(reader.ReadInt32()); } + + /// <summary> + /// Creates the formatter used for serialization of this type. + /// </summary> + /// <param name="symmetricSecret">The OpenID Provider's private symmetric secret to use to encrypt and sign the association data.</param> + /// <returns>A formatter for serialization.</returns> + internal static IDataBagFormatter<AssociationDataBag> CreateFormatter(byte[] symmetricSecret) { + return new BinaryDataBagFormatter<AssociationDataBag>(symmetricSecret, signed: true, encrypted: true); + } } } diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs index 304edbe..d31f5f7 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs @@ -73,7 +73,6 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <summary> /// Initializes a new instance of the <see cref="OpenIdProvider"/> class. /// </summary> - /// <param name="associationStore">The association store to use. Cannot be null.</param> /// <param name="nonceStore">The nonce store to use. Cannot be null.</param> private OpenIdProvider(INonceStore nonceStore) { Contract.Requires<ArgumentNullException>(nonceStore != null); @@ -178,6 +177,9 @@ namespace DotNetOpenAuth.OpenId.Provider { get { return this.Channel.WebRequestHandler; } } + /// <summary> + /// Gets the association store. + /// </summary> internal ProviderAssociationStore AssociationStore { get; private set; } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderAssociationStore.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderAssociationStore.cs index e503317..be4d01c 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/ProviderAssociationStore.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderAssociationStore.cs @@ -5,12 +5,11 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId.Provider { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using DotNetOpenAuth.Messaging; + /// <summary> + /// The OpenID Provider association serializer, used to encode/decode association handles. + /// </summary> internal class ProviderAssociationStore { /// <summary> /// Initializes a new instance of the <see cref="ProviderAssociationStore"/> class. @@ -24,23 +23,50 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </summary> internal byte[] Secret { get; set; } + /// <summary> + /// Encodes the specified association data bag. + /// </summary> + /// <param name="associationDataBag">The association data bag.</param> + /// <returns>The association handle that represents this association.</returns> internal string Encode(AssociationDataBag associationDataBag) { var formatter = AssociationDataBag.CreateFormatter(this.Secret); return formatter.Serialize(associationDataBag); } + /// <summary> + /// Retrieves an association given an association handle. + /// </summary> + /// <param name="containingMessage">The OpenID message that referenced this association handle.</param> + /// <param name="associationType">The expected type of the retrieved association.</param> + /// <param name="handle">The association handle.</param> + /// <returns>An association instance, or <c>null</c> if the association has expired or the signature is incorrect (which may be because the OP's symmetric key has changed).</returns> + /// <exception cref="ProtocolException">Thrown if the association is not of the expected type.</exception> internal Association Decode(IProtocolMessage containingMessage, AssociationRelyingPartyType associationType, string handle) { var formatter = AssociationDataBag.CreateFormatter(this.Secret); - var bag = formatter.Deserialize(containingMessage, handle); + AssociationDataBag bag; + try { + bag = formatter.Deserialize(containingMessage, handle); + } catch (ProtocolException) { + return null; + } + ErrorUtilities.VerifyProtocol(bag.AssociationType == associationType, "Unexpected association type."); Association assoc = Association.Deserialize(handle, bag.ExpiresUtc, bag.Secret); - return assoc; + return assoc.IsExpired ? null : assoc; } + /// <summary> + /// Determines whether the association with the specified handle is (still) valid. + /// </summary> + /// <param name="containingMessage">The OpenID message that referenced this association handle.</param> + /// <param name="associationType">The expected type of the retrieved association.</param> + /// <param name="handle">The association handle.</param> + /// <returns> + /// <c>true</c> if the specified containing message is valid; otherwise, <c>false</c>. + /// </returns> internal bool IsValid(IProtocolMessage containingMessage, AssociationRelyingPartyType associationType, string handle) { try { - this.Decode(containingMessage, associationType, handle); - return true; + return this.Decode(containingMessage, associationType, handle) != null; } catch (ProtocolException) { return false; } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationMemoryStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationMemoryStore.cs index 00540b4..1dc8126 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationMemoryStore.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationMemoryStore.cs @@ -12,7 +12,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Manages a set of associations in memory only (no database). /// </summary> - /// <typeparam name="Uri">The type of the key.</typeparam> /// <remarks> /// This class should be used for low-to-medium traffic relying party sites that can afford to lose associations /// if the app pool was ever restarted. High traffic relying parties and providers should write their own diff --git a/src/DotNetOpenAuth/OpenId/Associations.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/Associations.cs index cf49d1c..b171bec 100644 --- a/src/DotNetOpenAuth/OpenId/Associations.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/Associations.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.OpenId { +namespace DotNetOpenAuth.OpenId.RelyingParty { using System; using System.Collections.Generic; using System.Collections.ObjectModel; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAssociationStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAssociationStore.cs index 63c6526..695aa66 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAssociationStore.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAssociationStore.cs @@ -70,9 +70,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> - /// Code Contract for the <see cref="IAssociationStore<Uri>"/> class. + /// Code Contract for the <see cref="IAssociationStore"/> class. /// </summary> - /// <typeparam name="Uri">The type of the key.</typeparam> [ContractClassFor(typeof(IAssociationStore))] internal abstract class IAssociationStoreContract : IAssociationStore { #region IAssociationStore Members |