summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2011-05-08 06:44:54 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2011-05-08 06:44:54 -0700
commit9abbc3d8243c2a4a92155b8cb61ebf06fcbeaec3 (patch)
tree173dbc658bdda10b6cf59a5df6ef73dc0a8e827e /src
parenteb34013591399e60071055e9a56e6ba7ed10a0e0 (diff)
downloadDotNetOpenAuth-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')
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj1
-rw-r--r--src/DotNetOpenAuth/Messaging/BinaryDataBagFormatter.cs85
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs25
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs41
-rw-r--r--src/DotNetOpenAuth/Messaging/TimestampEncoder.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AssociationDataBag.cs20
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&lt;T&gt;"/> 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&lt;T&gt;"/> 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());
}
}
}