diff options
Diffstat (limited to 'src/DotNetOpenAuth.OpenId/OpenId/Protocol.cs')
-rw-r--r-- | src/DotNetOpenAuth.OpenId/OpenId/Protocol.cs | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Protocol.cs b/src/DotNetOpenAuth.OpenId/OpenId/Protocol.cs new file mode 100644 index 0000000..a651f3c --- /dev/null +++ b/src/DotNetOpenAuth.OpenId/OpenId/Protocol.cs @@ -0,0 +1,472 @@ +// <auto-generated/> // disable StyleCop on this file +//----------------------------------------------------------------------- +// <copyright file="Protocol.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId { + using System; + using System.Collections.Generic; + using DotNetOpenAuth.Messaging; + using System.Globalization; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Diagnostics; + + /// <summary> + /// An enumeration of the OpenID protocol versions supported by this library. + /// </summary> + public enum ProtocolVersion { + /// <summary> + /// OpenID Authentication 1.0 + /// </summary> + V10, + /// <summary> + /// OpenID Authentication 1.1 + /// </summary> + V11, + /// <summary> + /// OpenID Authentication 2.0 + /// </summary> + V20, + } + + /// <summary> + /// Tracks the several versions of OpenID this library supports and the unique + /// constants to each version used in the protocol. + /// </summary> + [DebuggerDisplay("OpenID {Version}")] + internal sealed class Protocol { + /// <summary> + /// The value of the openid.ns parameter in the OpenID 2.0 specification. + /// </summary> + internal const string OpenId2Namespace = "http://specs.openid.net/auth/2.0"; + + /// <summary> + /// Scans a list for matches with some element of the OpenID protocol, + /// searching from newest to oldest protocol for the first and best match. + /// </summary> + /// <typeparam name="T">The type of element retrieved from the <see cref="Protocol"/> instance.</typeparam> + /// <param name="elementOf">Takes a <see cref="Protocol"/> instance and returns an element of it.</param> + /// <param name="list">The list to scan for matches.</param> + /// <returns>The protocol with the element that matches some item in the list.</returns> + internal static Protocol FindBestVersion<T>(Func<Protocol, T> elementOf, IEnumerable<T> list) { + foreach (var protocol in Protocol.AllVersions) { + foreach (var item in list) { + if (item != null && item.Equals(elementOf(protocol))) + return protocol; + } + } + return null; + } + + Protocol(QueryParameters queryBits) { + openidnp = queryBits; + openid = new QueryParameters(queryBits); + } + + // Well-known, supported versions of the OpenID spec. + public static readonly Protocol V10 = new Protocol(new QueryParameters()) { + Version = new Version(1, 0), + XmlNamespace = "http://openid.net/xmlns/1.0", + QueryDeclaredNamespaceVersion = null, + ClaimedIdentifierServiceTypeURI = "http://openid.net/signon/1.0", + OPIdentifierServiceTypeURI = null, // not supported + ClaimedIdentifierForOPIdentifier = null, // not supported + RPReturnToTypeURI = null, // not supported + HtmlDiscoveryProviderKey = "openid.server", + HtmlDiscoveryLocalIdKey = "openid.delegate", + }; + public static readonly Protocol V11 = new Protocol(new QueryParameters()) { + Version = new Version(1, 1), + XmlNamespace = "http://openid.net/xmlns/1.0", + QueryDeclaredNamespaceVersion = null, + ClaimedIdentifierServiceTypeURI = "http://openid.net/signon/1.1", + OPIdentifierServiceTypeURI = null, // not supported + ClaimedIdentifierForOPIdentifier = null, // not supported + RPReturnToTypeURI = null, // not supported + HtmlDiscoveryProviderKey = "openid.server", + HtmlDiscoveryLocalIdKey = "openid.delegate", + }; + public static readonly Protocol V20 = new Protocol(new QueryParameters() { + Realm = "realm", + op_endpoint = "op_endpoint", + response_nonce = "response_nonce", + error_code = "error_code", + user_setup_url = null, + }) { + Version = new Version(2, 0), + XmlNamespace = null, // no longer applicable + QueryDeclaredNamespaceVersion = Protocol.OpenId2Namespace, + ClaimedIdentifierServiceTypeURI = "http://specs.openid.net/auth/2.0/signon", + OPIdentifierServiceTypeURI = "http://specs.openid.net/auth/2.0/server", + ClaimedIdentifierForOPIdentifier = "http://specs.openid.net/auth/2.0/identifier_select", + RPReturnToTypeURI = "http://specs.openid.net/auth/2.0/return_to", + HtmlDiscoveryProviderKey = "openid2.provider", + HtmlDiscoveryLocalIdKey = "openid2.local_id", + Args = new QueryArguments() { + SessionType = new QueryArguments.SessionTypes() { + NoEncryption = "no-encryption", + DH_SHA256 = "DH-SHA256", + DH_SHA384 = "DH-SHA384", + DH_SHA512 = "DH-SHA512", + }, + SignatureAlgorithm = new QueryArguments.SignatureAlgorithms() { + HMAC_SHA256 = "HMAC-SHA256", + HMAC_SHA384 = "HMAC-SHA384", + HMAC_SHA512 = "HMAC-SHA512", + }, + Mode = new QueryArguments.Modes() { + setup_needed = "setup_needed", + }, + }, + }; + + /// <summary> + /// A list of all supported OpenID versions, in order starting from newest version. + /// </summary> + public readonly static List<Protocol> AllVersions = new List<Protocol>() { V20, V11, V10 }; + + /// <summary> + /// A list of all supported OpenID versions, in order starting from newest version. + /// V1.1 and V1.0 are considered the same and only V1.1 is in the list. + /// </summary> + public readonly static List<Protocol> AllPracticalVersions = new List<Protocol>() { V20, V11 }; + + /// <summary> + /// The default (or most recent) supported version of the OpenID protocol. + /// </summary> + public readonly static Protocol Default = AllVersions[0]; + public static Protocol Lookup(Version version) { + foreach (Protocol protocol in AllVersions) { + if (protocol.Version == version) return protocol; + } + throw new ArgumentOutOfRangeException("version"); + } + public static Protocol Lookup(ProtocolVersion version) { + switch (version) { + case ProtocolVersion.V10: return Protocol.V10; + case ProtocolVersion.V11: return Protocol.V11; + case ProtocolVersion.V20: return Protocol.V20; + default: throw new ArgumentOutOfRangeException("version"); + } + } + /// <summary> + /// Attempts to detect the right OpenID protocol version based on the contents + /// of an incoming OpenID <i>indirect</i> message or <i>direct request</i>. + /// </summary> + internal static Protocol Detect(IDictionary<string, string> query) { + Requires.NotNull(query, "query"); + return query.ContainsKey(V20.openid.ns) ? V20 : V11; + } + /// <summary> + /// Attempts to detect the right OpenID protocol version based on the contents + /// of an incoming OpenID <i>direct</i> response message. + /// </summary> + internal static Protocol DetectFromDirectResponse(IDictionary<string, string> query) { + Requires.NotNull(query, "query"); + return query.ContainsKey(V20.openidnp.ns) ? V20 : V11; + } + /// <summary> + /// Attemps to detect the highest OpenID protocol version supported given a set + /// of XRDS Service Type URIs included for some service. + /// </summary> + internal static Protocol Detect(IEnumerable<string> serviceTypeURIs) { + Requires.NotNull(serviceTypeURIs, "serviceTypeURIs"); + return FindBestVersion(p => p.OPIdentifierServiceTypeURI, serviceTypeURIs) ?? + FindBestVersion(p => p.ClaimedIdentifierServiceTypeURI, serviceTypeURIs) ?? + FindBestVersion(p => p.RPReturnToTypeURI, serviceTypeURIs); + } + + /// <summary> + /// The OpenID version that this <see cref="Protocol"/> instance describes. + /// </summary> + public Version Version; + /// <summary> + /// Returns the <see cref="ProtocolVersion"/> enum value for the <see cref="Protocol"/> instance. + /// </summary> + public ProtocolVersion ProtocolVersion { + get { + switch (Version.Major) { + case 1: return ProtocolVersion.V11; + case 2: return ProtocolVersion.V20; + default: throw new ArgumentException(null); // this should never happen + } + } + } + /// <summary> + /// The namespace of OpenId 1.x elements in XRDS documents. + /// </summary> + public string XmlNamespace; + /// <summary> + /// The value of the openid.ns parameter that appears on the query string + /// whenever data is passed between relying party and provider for OpenID 2.0 + /// and later. + /// </summary> + public string QueryDeclaredNamespaceVersion; + /// <summary> + /// The XRD/Service/Type value discovered in an XRDS document when + /// "discovering" on a Claimed Identifier (http://andrewarnott.yahoo.com) + /// </summary> + public string ClaimedIdentifierServiceTypeURI; + /// <summary> + /// The XRD/Service/Type value discovered in an XRDS document when + /// "discovering" on an OP Identifier rather than a Claimed Identifier. + /// (http://yahoo.com) + /// </summary> + public string OPIdentifierServiceTypeURI; + /// <summary> + /// The XRD/Service/Type value discovered in an XRDS document when + /// "discovering" on a Realm URL and looking for the endpoint URL + /// that can receive authentication assertions. + /// </summary> + public string RPReturnToTypeURI; + /// <summary> + /// Used as the Claimed Identifier and the OP Local Identifier when + /// the User Supplied Identifier is an OP Identifier. + /// </summary> + public string ClaimedIdentifierForOPIdentifier; + /// <summary> + /// The value of the 'rel' attribute in an HTML document's LINK tag + /// when the same LINK tag's HREF attribute value contains the URL to an + /// OP Endpoint URL. + /// </summary> + public string HtmlDiscoveryProviderKey; + /// <summary> + /// The value of the 'rel' attribute in an HTML document's LINK tag + /// when the same LINK tag's HREF attribute value contains the URL to use + /// as the OP Local Identifier. + /// </summary> + public string HtmlDiscoveryLocalIdKey; + /// <summary> + /// Parts of the protocol that define parameter names that appear in the + /// query string. Each parameter name is prefixed with 'openid.'. + /// </summary> + public readonly QueryParameters openid; + /// <summary> + /// Parts of the protocol that define parameter names that appear in the + /// query string. Each parameter name is NOT prefixed with 'openid.'. + /// </summary> + public readonly QueryParameters openidnp; + /// <summary> + /// The various 'constants' that appear as parameter arguments (values). + /// </summary> + public QueryArguments Args = new QueryArguments(); + + internal sealed class QueryParameters { + /// <summary> + /// The value "openid." + /// </summary> + public readonly string Prefix = "openid."; + [SuppressMessage("Microsoft.Performance", "CA1805:DoNotInitializeUnnecessarily")] + public QueryParameters() { } + [SuppressMessage("Microsoft.Performance", "CA1805:DoNotInitializeUnnecessarily")] + public QueryParameters(QueryParameters addPrefixTo) { + ns = addPrefix(addPrefixTo.ns); + return_to = addPrefix(addPrefixTo.return_to); + Realm = addPrefix(addPrefixTo.Realm); + mode = addPrefix(addPrefixTo.mode); + error = addPrefix(addPrefixTo.error); + error_code = addPrefix(addPrefixTo.error_code); + identity = addPrefix(addPrefixTo.identity); + op_endpoint = addPrefix(addPrefixTo.op_endpoint); + response_nonce = addPrefix(addPrefixTo.response_nonce); + claimed_id = addPrefix(addPrefixTo.claimed_id); + expires_in = addPrefix(addPrefixTo.expires_in); + assoc_type = addPrefix(addPrefixTo.assoc_type); + assoc_handle = addPrefix(addPrefixTo.assoc_handle); + session_type = addPrefix(addPrefixTo.session_type); + is_valid = addPrefix(addPrefixTo.is_valid); + sig = addPrefix(addPrefixTo.sig); + signed = addPrefix(addPrefixTo.signed); + user_setup_url = addPrefix(addPrefixTo.user_setup_url); + invalidate_handle = addPrefix(addPrefixTo.invalidate_handle); + dh_modulus = addPrefix(addPrefixTo.dh_modulus); + dh_gen = addPrefix(addPrefixTo.dh_gen); + dh_consumer_public = addPrefix(addPrefixTo.dh_consumer_public); + dh_server_public = addPrefix(addPrefixTo.dh_server_public); + enc_mac_key = addPrefix(addPrefixTo.enc_mac_key); + mac_key = addPrefix(addPrefixTo.mac_key); + } + string addPrefix(string original) { + return (original != null) ? Prefix + original : null; + } + // These fields default to 1.x specifications, and are overridden + // as necessary by later versions in the Protocol class initializers. + // Null values in any version suggests that that feature is absent from that version. + public string ns = "ns"; + public string return_to = "return_to"; + public string Realm = "trust_root"; + public string mode = "mode"; + public string error = "error"; + public string error_code = null; + public string identity = "identity"; + public string op_endpoint = null; + public string response_nonce = null; + public string claimed_id = "claimed_id"; + public string expires_in = "expires_in"; + public string assoc_type = "assoc_type"; + public string assoc_handle = "assoc_handle"; + public string session_type = "session_type"; + public string is_valid = "is_valid"; + public string sig = "sig"; + public string signed = "signed"; + public string user_setup_url = "user_setup_url"; + public string invalidate_handle = "invalidate_handle"; + public string dh_modulus = "dh_modulus"; + public string dh_gen = "dh_gen"; + public string dh_consumer_public = "dh_consumer_public"; + public string dh_server_public = "dh_server_public"; + public string enc_mac_key = "enc_mac_key"; + public string mac_key = "mac_key"; + +#if CONTRACTS_FULL + /// <summary> + /// Verifies conditions that should be true for any valid state of this object. + /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")] + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] + [ContractInvariantMethod] + private void ObjectInvariant() { + Contract.Invariant(!string.IsNullOrEmpty(this.Prefix)); + } +#endif + } + + internal sealed class QueryArguments { + public ErrorCodes ErrorCode = new ErrorCodes(); + public SessionTypes SessionType = new SessionTypes(); + public SignatureAlgorithms SignatureAlgorithm = new SignatureAlgorithms(); + public Modes Mode = new Modes(); + public IsValidValues IsValid = new IsValidValues(); + + internal sealed class ErrorCodes { + public string UnsupportedType = "unsupported-type"; + } + internal sealed class SessionTypes { + /// <summary> + /// A preference order list of all supported session types. + /// </summary> + public string[] All { get { return new[] { DH_SHA512, DH_SHA384, DH_SHA256, DH_SHA1, NoEncryption }; } } + public string[] AllDiffieHellman { get { return new[] { DH_SHA512, DH_SHA384, DH_SHA256, DH_SHA1 }; } } + public string DH_SHA1 = "DH-SHA1"; + public string DH_SHA256; + public string DH_SHA384; + public string DH_SHA512; + public string NoEncryption = string.Empty; + public string Best { + get { + foreach (string algorithmName in All) { + if (algorithmName != null) { + return algorithmName; + } + } + throw new ProtocolException(); // really bad... we have no signing algorithms at all + } + } + } + internal sealed class SignatureAlgorithms { + /// <summary> + /// A preference order list of signature algorithms we support. + /// </summary> + public string[] All { get { return new[] { HMAC_SHA512, HMAC_SHA384, HMAC_SHA256, HMAC_SHA1 }; } } + public string HMAC_SHA1 = "HMAC-SHA1"; + public string HMAC_SHA256; + public string HMAC_SHA384; + public string HMAC_SHA512; + public string Best { + get { + foreach (string algorithmName in All) { + if (algorithmName != null) { + return algorithmName; + } + } + throw new ProtocolException(); // really bad... we have no signing algorithms at all + } + } + } + internal sealed class Modes { + public string cancel = "cancel"; + public string error = "error"; + public string id_res = "id_res"; + public string checkid_immediate = "checkid_immediate"; + public string checkid_setup = "checkid_setup"; + public string check_authentication = "check_authentication"; + public string associate = "associate"; + public string setup_needed = "id_res"; // V2 overrides this + } + internal sealed class IsValidValues { + public string True = "true"; + public string False = "false"; + } + } + + /// <summary> + /// The maximum time a user can be allowed to take to complete authentication. + /// </summary> + /// <remarks> + /// This is used to calculate the length of time that nonces are stored. + /// This is internal until we can decide whether to leave this static, or make + /// it an instance member, or put it inside the IConsumerApplicationStore interface. + /// </remarks> + internal static TimeSpan MaximumUserAgentAuthenticationTime = TimeSpan.FromMinutes(5); + /// <summary> + /// The maximum permissible difference in clocks between relying party and + /// provider web servers, discounting time zone differences. + /// </summary> + /// <remarks> + /// This is used when storing/validating nonces from the provider. + /// If it is conceivable that a server's clock could be up to five minutes + /// off from true UTC time, then the maximum time skew should be set to + /// ten minutes to allow one server to be five minutes ahead and the remote + /// server to be five minutes behind and still be able to communicate. + /// </remarks> + internal static TimeSpan MaximumAllowableTimeSkew = TimeSpan.FromMinutes(10); + + /// <summary> + /// Checks whether a given Protocol version practically equals this one + /// for purposes of verifying a match for assertion verification. + /// </summary> + /// <param name="other">The other version to check against this one.</param> + /// <returns><c>true</c> if this and the given Protocol versions are essentially the same.</returns> + /// <remarks> + /// OpenID v1.0 never had a spec, and 1.0 and 1.1 are indistinguishable because of that. + /// Therefore for assertion verification, 1.0 and 1.1 are considered equivalent. + /// </remarks> + public bool EqualsPractically(Protocol other) { + if (other == null) { + return false; + } + + // Exact version match is definitely equality. + if (this.Version == other.Version) { + return true; + } + + // If both protocol versions are 1.x, it doesn't matter if one + // is 1.0 and the other is 1.1 for assertion verification purposes. + if (this.Version.Major == 1 && other.Version.Major == 1) { + return true; + } + + // Different version. + return false; + } + + public override bool Equals(object obj) { + Protocol other = obj as Protocol; + if (other == null) { + return false; + } + + return this.Version == other.Version; + } + public override int GetHashCode() { + return Version.GetHashCode(); + } + public override string ToString() { + return string.Format(CultureInfo.CurrentCulture, "OpenID Authentication {0}.{1}", Version.Major, Version.Minor); + } + } +} |