summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenId/RelyingParty/AssociateRequest.cs
blob: cf3d7f843d9484e35e2b557f1256d8cad473f2a0 (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
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Org.Mentalis.Security.Cryptography;

namespace DotNetOpenId.RelyingParty {
	[DebuggerDisplay("Mode: {Args[\"openid.mode\"]}, {Args[\"openid.assoc_type\"]}, OpenId: {Protocol.Version}")]
	class AssociateRequest : DirectRequest {
		/// <summary>
		/// Instantiates an <see cref="AssociateRequest"/> object.
		/// </summary>
		/// <param name="relyingParty">The RP instance that is creating this request.</param>
		/// <param name="provider">The discovered OpenID Provider endpoint information.</param>
		/// <param name="args">The arguments assembled for sending to the Provider.</param>
		/// <param name="dh">Optional.  Supplied only if Diffie-Hellman is used for encrypting the association secret key.</param>
		AssociateRequest(OpenIdRelyingParty relyingParty, ServiceEndpoint provider, IDictionary<string, string> args, DiffieHellman dh)
			: base(relyingParty, provider, args) {
			DH = dh;
		}
		public DiffieHellman DH { get; private set; }

		public static AssociateRequest Create(OpenIdRelyingParty relyingParty, ServiceEndpoint provider) {
			if (relyingParty == null) throw new ArgumentNullException("relyingParty");
			if (provider == null) throw new ArgumentNullException("provider");

			string assoc_type, session_type;
			bool requireDiffieHellman = !string.Equals(provider.ProviderEndpoint.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
			if (HmacShaAssociation.TryFindBestAssociation(provider.Protocol,
				relyingParty.Settings.MinimumHashBitLength, relyingParty.Settings.MaximumHashBitLength,
				requireDiffieHellman, out assoc_type, out session_type)) {
				return Create(relyingParty, provider, assoc_type, session_type, true);
			} else {
				// There are no associations that meet all requirements.
				Logger.Warn("Security requirements and protocol combination knock out all possible association types.  Dumb mode forced.");
				return null;
			}
		}

		public static AssociateRequest Create(OpenIdRelyingParty relyingParty, ServiceEndpoint provider, string assoc_type, string session_type, bool allowNoSession) {
			if (relyingParty == null) throw new ArgumentNullException("relyingParty");
			if (provider == null) throw new ArgumentNullException("provider");
			if (assoc_type == null) throw new ArgumentNullException("assoc_type");
			if (session_type == null) throw new ArgumentNullException("session_type");
			Debug.Assert(Array.IndexOf(provider.Protocol.Args.SignatureAlgorithm.All, assoc_type) >= 0);
			Debug.Assert(Array.IndexOf(provider.Protocol.Args.SessionType.All, session_type) >= 0);

			if (!HmacShaAssociation.IsDHSessionCompatible(provider.Protocol, assoc_type, session_type)) {
				throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
					Strings.IncompatibleAssociationAndSessionTypes, assoc_type, session_type));
			}

			var args = new Dictionary<string, string>();
			Protocol protocol = provider.Protocol;

			args.Add(protocol.openid.mode, protocol.Args.Mode.associate);
			args.Add(protocol.openid.assoc_type, assoc_type);

			DiffieHellman dh = null;

			if (provider.ProviderEndpoint.Scheme == Uri.UriSchemeHttps && allowNoSession) {
				Logger.InfoFormat("Requesting association with {0} (assoc_type = '{1}', session_type = '{2}').",
						provider.ProviderEndpoint, assoc_type, protocol.Args.SessionType.NoEncryption);
				args.Add(protocol.openid.session_type, protocol.Args.SessionType.NoEncryption);
			} else {
				Logger.InfoFormat("Requesting association with {0} (assoc_type = '{1}', session_type = '{2}').",
						provider.ProviderEndpoint, assoc_type, session_type);

				// Initiate Diffie-Hellman Exchange
				dh = DiffieHellmanUtil.CreateDiffieHellman();

				byte[] dhPublic = dh.CreateKeyExchange();
				string cpub = DiffieHellmanUtil.UnsignedToBase64(dhPublic);

				args.Add(protocol.openid.session_type, session_type);
				args.Add(protocol.openid.dh_consumer_public, cpub);

				DHParameters dhps = dh.ExportParameters(true);

				if (dhps.P != DiffieHellmanUtil.DEFAULT_MOD || dhps.G != DiffieHellmanUtil.DEFAULT_GEN) {
					args.Add(protocol.openid.dh_modulus, DiffieHellmanUtil.UnsignedToBase64(dhps.P));
					args.Add(protocol.openid.dh_gen, DiffieHellmanUtil.UnsignedToBase64(dhps.G));
				}
			}

			return new AssociateRequest(relyingParty, provider, args, dh);
		}
		AssociateResponse response;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)] // code execution in getter
		public AssociateResponse Response {
			get {
				if (response == null) {
					try {
						response = new AssociateResponse(RelyingParty, Provider, GetResponse(), DH);
					} catch (OpenIdException ex) {
						if (ex.Query != null) {
							response = new AssociateResponse(RelyingParty, Provider, ex.Query, DH);
						}
						// Silently fail at associate attempt, since we can recover
						// using dumb mode.
					}
				}
				return response;
			}
		}
	}
}