diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2011-05-08 06:44:54 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2011-05-08 06:44:54 -0700 |
commit | 9abbc3d8243c2a4a92155b8cb61ebf06fcbeaec3 (patch) | |
tree | 173dbc658bdda10b6cf59a5df6ef73dc0a8e827e /src | |
parent | eb34013591399e60071055e9a56e6ba7ed10a0e0 (diff) | |
download | DotNetOpenAuth-9abbc3d8243c2a4a92155b8cb61ebf06fcbeaec3.zip DotNetOpenAuth-9abbc3d8243c2a4a92155b8cb61ebf06fcbeaec3.tar.gz DotNetOpenAuth-9abbc3d8243c2a4a92155b8cb61ebf06fcbeaec3.tar.bz2 |
Added a binary DataBag serializer mechanism that cuts the association handle size down from 300+ characters to 141 characters.
Diffstat (limited to 'src')
6 files changed, 153 insertions, 21 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index ce09c0e..70b079c 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -318,6 +318,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="InfoCard\Token\TokenUtility.cs" /> <Compile Include="InfoCard\Token\TokenDecryptor.cs" /> <Compile Include="InfoCard\WellKnownIssuers.cs" /> + <Compile Include="Messaging\BinaryDataBagFormatter.cs" /> <Compile Include="Messaging\CachedDirectWebResponse.cs" /> <Compile Include="Messaging\ChannelContract.cs" /> <Compile Include="Messaging\DataBagFormatterBase.cs" /> diff --git a/src/DotNetOpenAuth/Messaging/BinaryDataBagFormatter.cs b/src/DotNetOpenAuth/Messaging/BinaryDataBagFormatter.cs new file mode 100644 index 0000000..0dfd8aa --- /dev/null +++ b/src/DotNetOpenAuth/Messaging/BinaryDataBagFormatter.cs @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------- +// <copyright file="BinaryDataBagFormatter.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +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; + 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> + /// 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="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) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> class. + /// </summary> + /// <param name="symmetricSecret">The symmetric secret to use for signing and encrypting.</param> + /// <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> + 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."); + } + + protected override byte[] SerializeCore(T message) { + var stream = new MemoryStream(); + message.Serialize(stream); + return stream.ToArray(); + } + + protected override void DeserializeCore(T message, byte[] data) { + var stream = new MemoryStream(data); + message.Deserialize(stream); + + // Perform basic validation on message that the MessageSerializer would have normally performed. + var messageDescription = MessageDescriptions.Get(message); + var dictionary = messageDescription.GetDictionary(message); + messageDescription.EnsureMessagePartsPassBasicValidation(dictionary); + IMessage m = message; + m.EnsureValidMessage(); + } + } +} diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index d2e0add..2119d78 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -1343,6 +1343,31 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Writes a buffer, prefixed with its own length. + /// </summary> + /// <param name="writer">The binary writer.</param> + /// <param name="buffer">The buffer.</param> + internal static void WriteBuffer(this BinaryWriter writer, byte[] buffer) { + Contract.Requires<ArgumentNullException>(writer != null, "writer"); + Contract.Requires<ArgumentNullException>(buffer != null, "buffer"); + writer.Write(buffer.Length); + writer.Write(buffer, 0, buffer.Length); + } + + /// <summary> + /// Reads a buffer that is prefixed with its own length. + /// </summary> + /// <param name="reader">The binary reader positioned at the buffer length.</param> + /// <returns>The read buffer.</returns> + internal static byte[] ReadBuffer(this BinaryReader reader) { + Contract.Requires<ArgumentNullException>(reader != null, "reader"); + int length = reader.ReadInt32(); + byte[] buffer = new byte[length]; + ErrorUtilities.VerifyProtocol(reader.Read(buffer, 0, length) == length, "Unexpected buffer length."); + return buffer; + } + + /// <summary> /// Constructs a Javascript expression that will create an object /// on the user agent when assigned to a variable. /// </summary> diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index bdd2827..ea33f49 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -100,6 +100,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { Map<bool>(value => value.ToString().ToLowerInvariant(), null, safeBool); Map<CultureInfo>(c => c.Name, null, str => new CultureInfo(str)); Map<CultureInfo[]>(cs => string.Join(",", cs.Select(c => c.Name).ToArray()), null, str => str.Split(',').Select(s => new CultureInfo(s)).ToArray()); + Map<Type>(t => t.FullName, null, str => Type.GetType(str)); } /// <summary> @@ -244,17 +245,21 @@ namespace DotNetOpenAuth.Messaging.Reflection { value)); } } else { - if (this.property != null) { - this.property.SetValue(message, this.ToValue(value), null); - } else { - this.field.SetValue(message, this.ToValue(value)); - } + this.SetValueAsObject(message, this.ToValue(value)); } } catch (Exception ex) { throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartReadFailure, message.GetType(), this.Name, value); } } + 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. @@ -300,6 +305,19 @@ 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> @@ -396,19 +414,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> - private object GetValueAsObject(IMessage message) { - if (this.property != null) { - return this.property.GetValue(message, null); - } else { - return this.field.GetValue(message); - } - } - - /// <summary> /// Validates that the message part and its attribute have agreeable settings. /// </summary> /// <exception cref="ArgumentException"> diff --git a/src/DotNetOpenAuth/Messaging/TimestampEncoder.cs b/src/DotNetOpenAuth/Messaging/TimestampEncoder.cs index 93df88b..b83a426 100644 --- a/src/DotNetOpenAuth/Messaging/TimestampEncoder.cs +++ b/src/DotNetOpenAuth/Messaging/TimestampEncoder.cs @@ -16,7 +16,7 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// The reference date and time for calculating time stamps. /// </summary> - private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + internal static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); /// <summary> /// Initializes a new instance of the <see cref="TimestampEncoder"/> class. diff --git a/src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs b/src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs index 1b5be94..18f7c0f 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs @@ -7,11 +7,12 @@ namespace DotNetOpenAuth.OpenId.Provider { using System; using System.Collections.Generic; + using System.IO; using System.Linq; using System.Text; using DotNetOpenAuth.Messaging; - internal class AssociationDataBag : DataBag { + internal class AssociationDataBag : DataBag, IStreamSerializingMessage { /// <summary> /// Initializes a new instance of the <see cref="AssociationDataBag"/> class. /// </summary> @@ -33,7 +34,22 @@ namespace DotNetOpenAuth.OpenId.Provider { internal AssociationRelyingPartyType AssociationType { get; set; } internal static IDataBagFormatter<AssociationDataBag> CreateFormatter(byte[] symmetricSecret) { - return new UriStyleMessageFormatter<AssociationDataBag>(symmetricSecret, signed: true, encrypted: true); + return new BinaryDataBagFormatter<AssociationDataBag>(symmetricSecret, signed: true, encrypted: true); + } + + public void Serialize(Stream stream) { + var writer = new BinaryWriter(stream); + writer.Write(this.IsPrivateAssociation); + writer.WriteBuffer(this.Secret); + writer.Write((int)(this.ExpiresUtc - TimestampEncoder.Epoch).TotalSeconds); + writer.Flush(); + } + + 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()); } } } |