diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-04-26 18:12:56 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-04-26 18:12:56 -0700 |
commit | ed8e510edf71df2cda6dd18e263540626b3537ae (patch) | |
tree | 4948589adb3b3c97b2d66c795477db51d3f82774 /samples/DotNetOpenAuth.ApplicationBlock | |
parent | 5569ca589ff4d7b6571fb9753d42410ca80655c1 (diff) | |
download | DotNetOpenAuth-ed8e510edf71df2cda6dd18e263540626b3537ae.zip DotNetOpenAuth-ed8e510edf71df2cda6dd18e263540626b3537ae.tar.gz DotNetOpenAuth-ed8e510edf71df2cda6dd18e263540626b3537ae.tar.bz2 |
Moved PPID OP Provider code out of the library and into the ApplicationBlock.
Diffstat (limited to 'samples/DotNetOpenAuth.ApplicationBlock')
4 files changed, 173 insertions, 0 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj index 976a325..83d3527 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj +++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj @@ -60,6 +60,8 @@ <Compile Include="CustomExtensions\AcmeResponse.cs" /> <Compile Include="GoogleConsumer.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Provider\AnonymousIdentifierProviderBase.cs" /> + <Compile Include="Provider\AuthenticationRequestExtensions.cs" /> <Compile Include="TwitterConsumer.cs" /> <Compile Include="Util.cs" /> </ItemGroup> diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs new file mode 100644 index 0000000..5354093 --- /dev/null +++ b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------- +// <copyright file="AnonymousIdentifierProviderBase.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.ApplicationBlock.Provider { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Security.Cryptography; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId; + + public abstract class AnonymousIdentifierProviderBase { + private int newSaltLength = 20; + + /// <summary> + /// Initializes a new instance of the <see cref="StandardAnonymousIdentifierProvider"/> class. + /// </summary> + public AnonymousIdentifierProviderBase(Uri baseIdentifier) { + if (baseIdentifier == null) { + throw new ArgumentNullException("baseIdentifier"); + } + + this.Hasher = HashAlgorithm.Create("SHA256"); + this.Encoder = Encoding.UTF8; + this.BaseIdentifier = baseIdentifier; + } + + public Uri BaseIdentifier { get; private set; } + + protected HashAlgorithm Hasher { get; private set; } + + protected Encoding Encoder { get; private set; } + + protected int NewSaltLength { + get { + return newSaltLength; + } + + set { + if (value <= 0) { + throw new ArgumentOutOfRangeException("value"); + } + + newSaltLength = value; + } + } + + #region IAnonymousIdentifierProvider Members + + public Uri GetAnonymousIdentifier(Identifier localIdentifier, Realm relyingPartyRealm) { + byte[] salt = GetHashSaltForLocalIdentifier(localIdentifier); + string valueToHash = localIdentifier + "#" + (relyingPartyRealm ?? string.Empty); + byte[] valueAsBytes = this.Encoder.GetBytes(valueToHash); + byte[] bytesToHash = new byte[valueAsBytes.Length + salt.Length]; + valueAsBytes.CopyTo(bytesToHash, 0); + salt.CopyTo(bytesToHash, valueAsBytes.Length); + byte[] hash = this.Hasher.ComputeHash(bytesToHash); + string base64Hash = Convert.ToBase64String(hash); + Uri anonymousIdentifier = AppendIdentifiers(this.BaseIdentifier, base64Hash); + return anonymousIdentifier; + } + + #endregion + + protected virtual byte[] GetNewSalt() { + // We COULD use a crypto random function, but for a salt it seems overkill. + return Util.GetNonCryptoRandomData(this.NewSaltLength); + } + + protected Uri AppendIdentifiers(Uri baseIdentifier, string uriHash) { + if (baseIdentifier == null) { + throw new ArgumentNullException("baseIdentifier"); + } + if (String.IsNullOrEmpty(uriHash)) { + throw new ArgumentNullException("uriHash"); + } + + if (string.IsNullOrEmpty(baseIdentifier.Query)) { + // The uriHash will appear on the path itself. + string pathEncoded = Uri.EscapeUriString(uriHash.Replace('/', '_')); + return new Uri(baseIdentifier, pathEncoded); + } else { + // The uriHash will appear on the query string. + string dataEncoded = Uri.EscapeDataString(uriHash); + return new Uri(baseIdentifier + dataEncoded); + } + } + + /// <summary> + /// Gets the salt to use for generating an anonymous identifier for a given OP local identifier. + /// </summary> + /// <param name="localIdentifier">The OP local identifier.</param> + /// <returns>The salt to use in the hash.</returns> + /// <remarks> + /// It is important that this method always return the same value for a given + /// <paramref name="localIdentifier"/>. + /// New salts can be generated for local identifiers without previously assigned salt + /// values by calling <see cref="GetNewSalt"/> or by a custom method. + /// </remarks> + protected abstract byte[] GetHashSaltForLocalIdentifier(Identifier localIdentifier); + +#if CONTRACTS_FULL + /// <summary> + /// Verifies conditions that should be true for any valid state of this object. + /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] + [ContractInvariantMethod] + protected void ObjectInvariant() { + Contract.Invariant(this.Hasher != null); + Contract.Invariant(this.Encoder != null); + Contract.Invariant(this.BaseIdentifier != null); + Contract.Invariant(this.NewHashLength > 0); + } +#endif + } +} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs new file mode 100644 index 0000000..496b14b --- /dev/null +++ b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using DotNetOpenAuth.OpenId.Provider; +using DotNetOpenAuth.OpenId; + +namespace DotNetOpenAuth.ApplicationBlock.Provider { + public static class AuthenticationRequestExtensions { + /// <summary> + /// Removes all personally identifiable information from the positive assertion. + /// </summary> + /// <remarks> + /// The openid.claimed_id and openid.identity values are hashed. + /// </remarks> + public static void ScrubPersonallyIdentifiableInformation(this IAuthenticationRequest request, Identifier localIdentifier, AnonymousIdentifierProviderBase anonymousIdentifierProvider, bool pairwiseUnique) { + if (request == null) { + throw new ArgumentNullException("request"); + } + if (!request.IsDirectedIdentity) { + throw new InvalidOperationException("This operation is supported only under identifier select (directed identity) scenarios."); + } + if (anonymousIdentifierProvider == null) { + throw new ArgumentNullException("anonymousIdentifierProvider"); + } + if (localIdentifier == null) { + throw new ArgumentNullException("localIdentifier"); + } + + // When generating the anonymous identifiers, the openid.identity and openid.claimed_id + // will always end up with matching values. + var anonymousIdentifier = anonymousIdentifierProvider.GetAnonymousIdentifier(localIdentifier, pairwiseUnique ? request.Realm : null); + request.ClaimedIdentifier = anonymousIdentifier; + request.LocalIdentifier = anonymousIdentifier; + } + } +} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs index ea7da97..8a188ac 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs @@ -5,6 +5,8 @@ using DotNetOpenAuth.Messaging; internal static class Util { + internal static readonly Random NonCryptoRandomDataGenerator = new Random(); + /// <summary> /// Enumerates through the individual set bits in a flag enum. /// </summary> @@ -28,6 +30,17 @@ } /// <summary> + /// Gets a buffer of random data (not cryptographically strong). + /// </summary> + /// <param name="length">The length of the sequence to generate.</param> + /// <returns>The generated values, which may contain zeros.</returns> + internal static byte[] GetNonCryptoRandomData(int length) { + byte[] buffer = new byte[length]; + NonCryptoRandomDataGenerator.NextBytes(buffer); + return buffer; + } + + /// <summary> /// Copies the contents of one stream to another. /// </summary> /// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param> |