blob: 1df7267961849ac5edc9c58ffede4fde4711fc4b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
//-----------------------------------------------------------------------
// <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="AnonymousIdentifierProviderBase"/> class.
/// </summary>
/// <param name="baseIdentifier">The base URI on which to append the anonymous part.</param>
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 this.newSaltLength;
}
set {
if (value <= 0) {
throw new ArgumentOutOfRangeException("value");
}
this.newSaltLength = value;
}
}
#region IAnonymousIdentifierProvider Members
public Uri GetAnonymousIdentifier(Identifier localIdentifier, Realm relyingPartyRealm) {
byte[] salt = this.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 = this.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
}
}
|