summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenId/Provider/ProviderSession.cs
blob: bbfd27800d2824b675a5652d947ead24d95d3319 (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
using System;
using System.Collections.Specialized;
using Org.Mentalis.Security.Cryptography;
using System.Text;
using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics;
using System.Security.Cryptography;

namespace DotNetOpenId.Provider {
	[DebuggerDisplay("{SessionType}")]
	internal abstract class ProviderSession {
		protected ProviderSession(OpenIdProvider provider) {
			if (provider == null) throw new ArgumentNullException("provider");
			Provider = provider;
		}

		protected OpenIdProvider Provider { get; private set; }
		protected Protocol Protocol { get { return Provider.Protocol; } }
		public abstract string SessionType { get; }
		public abstract Dictionary<string, string> Answer(byte[] secret);
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1820:TestForEmptyStringsUsingStringLength")]
		public static ProviderSession CreateSession(OpenIdProvider provider) {
			if (provider == null) throw new ArgumentNullException("provider");
			Protocol protocol = provider.Protocol;
			string session_type = protocol.Version.Major >= 2 ?
				Util.GetRequiredArg(provider.Query, protocol.openid.session_type) :
				(Util.GetOptionalArg(provider.Query, protocol.openid.session_type) ?? "");

			if (protocol.Args.SessionType.NoEncryption.Equals(session_type, StringComparison.Ordinal)) {
				return new PlainTextProviderSession(provider);
			} else if (Array.IndexOf(protocol.Args.SessionType.AllDiffieHellman, session_type) >= 0) {
				return new DiffieHellmanProviderSession(provider);
			} else {
				throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
					Strings.InvalidOpenIdQueryParameterValue,
					protocol.openid.session_type, session_type), provider.Query) {
						ExtraArgsToReturn = AssociateRequest.CreateAssociationTypeHints(provider),
					};
			}
		}
	}

	/// <summary>
	/// An object that knows how to handle association requests with no session type.
	/// </summary>
	internal class PlainTextProviderSession : ProviderSession {
		public PlainTextProviderSession(OpenIdProvider provider) : base(provider) {
			// Extra requirements for OpenId 2.0 compliance.  Although the 1.0 spec
			// doesn't require use of an encrypted session, it's stupid not to encrypt
			// the shared secret key, so we'll enforce the rule for 1.0 and 2.0 RPs.
			if (!provider.RequestUrl.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) {
				throw new OpenIdException(Strings.EncryptionRequired, provider.Query);
			}
		}
		public override string SessionType {
			get { return Protocol.Args.SessionType.NoEncryption; }
		}

		public override Dictionary<string, string> Answer(byte[] secret) {
			var nvc = new Dictionary<string, string>();
			nvc.Add(Protocol.openidnp.mac_key, Convert.ToBase64String(secret));
			return nvc;
		}
	}

	/// <summary>
	/// An object that knows how to handle association requests with the Diffie-Hellman session type.
	/// </summary>
	internal class DiffieHellmanProviderSession : ProviderSession, IDisposable {
		byte[] consumerPublicKey;
		DiffieHellman dh;
		string sessionType;

		public DiffieHellmanProviderSession(OpenIdProvider provider)
			: base(provider) {
			sessionType = Util.GetRequiredArg(provider.Query, Protocol.openid.session_type);
			Debug.Assert(Array.IndexOf(Protocol.Args.SessionType.AllDiffieHellman, sessionType) >= 0, "We should not have been invoked if this wasn't a recognized DH session request.");

			byte[] dh_modulus = Util.GetOptionalBase64Arg(Provider.Query, Protocol.openid.dh_modulus) ?? DiffieHellmanUtil.DEFAULT_MOD;
			byte[] dh_gen = Util.GetOptionalBase64Arg(Provider.Query, Protocol.openid.dh_gen) ?? DiffieHellmanUtil.DEFAULT_GEN;
			dh = new DiffieHellmanManaged(dh_modulus, dh_gen, 1024);

			consumerPublicKey = Util.GetRequiredBase64Arg(Provider.Query, Protocol.openid.dh_consumer_public);
		}

		public override string SessionType {
			get { return sessionType; }
		}

		public override Dictionary<string, string> Answer(byte[] secret) {
			byte[] mac_key = DiffieHellmanUtil.SHAHashXorSecret(DiffieHellmanUtil.Lookup(Protocol, SessionType),
				dh, consumerPublicKey, secret);
			var nvc = new Dictionary<string, string>();

			nvc.Add(Protocol.openidnp.dh_server_public, DiffieHellmanUtil.UnsignedToBase64(dh.CreateKeyExchange()));
			nvc.Add(Protocol.openidnp.enc_mac_key, Convert.ToBase64String(mac_key));

			return nvc;
		}

		#region IDisposable Members

		~DiffieHellmanProviderSession() {
			Dispose(false);
		}
		void Dispose(bool disposing) {
			if (disposing) {
				if (dh != null) {
					((IDisposable)dh).Dispose();
					dh = null;
				}
			}
		}
		public void Dispose() {
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		#endregion
	}
}