/// <summary> /// Performs any transformation on an incoming message that may be necessary and/or /// validates an incoming message based on the rules of this channel binding element. /// </summary> /// <param name="message">The incoming message to process.</param> /// <returns> /// The protections (if any) that this binding element applied to the message. /// Null if this binding element did not even apply to this binding element. /// </returns> /// <exception cref="ProtocolException"> /// Thrown when the binding element rules indicate that this message is invalid and should /// NOT be processed. /// </exception> /// <remarks> /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> public MessageProtections?ProcessIncomingMessage(IProtocolMessage message) { IndirectSignedResponse response = message as IndirectSignedResponse; if (this.UseRequestNonce(response)) { if (!response.ReturnToParametersSignatureValidated) { Logger.OpenId.Error("Incoming message is expected to have a nonce, but the return_to parameter is not signed."); } string nonceValue = response.GetReturnToArgument(NonceParameter); ErrorUtilities.VerifyProtocol( nonceValue != null && response.ReturnToParametersSignatureValidated, this.securitySettings.RejectUnsolicitedAssertions ? OpenIdStrings.UnsolicitedAssertionsNotAllowed : OpenIdStrings.UnsolicitedAssertionsNotAllowedFrom1xOPs); CustomNonce nonce = CustomNonce.Deserialize(nonceValue); DateTime expirationDate = nonce.CreationDateUtc + MaximumMessageAge; if (expirationDate < DateTime.UtcNow) { throw new ExpiredMessageException(expirationDate, message); } IReplayProtectedProtocolMessage replayResponse = response; if (!this.nonceStore.StoreNonce(replayResponse.NonceContext, nonce.RandomPartAsString, nonce.CreationDateUtc)) { Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", replayResponse.Nonce, replayResponse.UtcCreationDate); throw new ReplayedMessageException(message); } return(MessageProtections.ReplayProtection); } return(null); }
/// <summary> /// Performs any transformation on an incoming message that may be necessary and/or /// validates an incoming message based on the rules of this channel binding element. /// </summary> /// <param name="message">The incoming message to process.</param> /// <returns> /// True if the <paramref name="message"/> applied to this binding element /// and the operation was successful. False if the operation did not apply to this message. /// </returns> /// <exception cref="ProtocolException"> /// Thrown when the binding element rules indicate that this message is invalid and should /// NOT be processed. /// </exception> /// <remarks> /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> public bool PrepareMessageForReceiving(IProtocolMessage message) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null && response.Version.Major < 2) { string nonceValue = response.GetReturnToArgument(NonceParameter); ErrorUtilities.VerifyProtocol(nonceValue != null, OpenIdStrings.UnsolicitedAssertionsNotAllowedFrom1xOPs); CustomNonce nonce = CustomNonce.Deserialize(nonceValue); DateTime expirationDate = nonce.CreationDateUtc + MaximumMessageAge; if (expirationDate < DateTime.UtcNow) { throw new ExpiredMessageException(expirationDate, message); } if (!this.nonceStore.StoreNonce(nonce.RandomPartAsString, nonce.CreationDateUtc)) { throw new ReplayedMessageException(message); } return(true); } return(false); }
/// <summary> /// Gets a specific association referenced in a given message's association handle. /// </summary> /// <param name="signedMessage">The signed message whose association handle should be used to lookup the association to return.</param> /// <returns>The referenced association; or <c>null</c> if such an association cannot be found.</returns> /// <remarks> /// If the association handle set in the message does not match any valid association, /// the association handle property is cleared, and the /// <see cref="ITamperResistantOpenIdMessage.InvalidateHandle"/> property is set to the /// handle that could not be found. /// </remarks> private Association GetSpecificAssociation(ITamperResistantOpenIdMessage signedMessage) { Association association = null; if (!string.IsNullOrEmpty(signedMessage.AssociationHandle)) { IndirectSignedResponse indirectSignedMessage = signedMessage as IndirectSignedResponse; if (this.IsOnProvider) { // Since we have an association handle, we're either signing with a smart association, // or verifying a dumb one. bool signing = string.IsNullOrEmpty(signedMessage.Signature); AssociationRelyingPartyType type = signing ? AssociationRelyingPartyType.Smart : AssociationRelyingPartyType.Dumb; association = this.opAssociations.GetAssociation(type, signedMessage.AssociationHandle); if (association == null) { // There was no valid association with the requested handle. // Let's tell the RP to forget about that association. signedMessage.InvalidateHandle = signedMessage.AssociationHandle; signedMessage.AssociationHandle = null; } } else if (this.rpAssociations != null) // if on a smart RP { Uri providerEndpoint = indirectSignedMessage.ProviderEndpoint; association = this.rpAssociations.GetAssociation(providerEndpoint, signedMessage.AssociationHandle); } } return(association); }
public void SignedResponsesIncludeExtraDataInSignature() { Protocol protocol = Protocol.Default; SigningBindingElement sbe = new SigningBindingElement(new AssociationMemoryStore <AssociationRelyingPartyType>(), new ProviderSecuritySettings()); sbe.Channel = new TestChannel(this.MessageDescriptions); IndirectSignedResponse response = new IndirectSignedResponse(protocol.Version, RPUri); response.ReturnTo = RPUri; response.ProviderEndpoint = OPUri; response.ExtraData["someunsigned"] = "value"; response.ExtraData["openid.somesigned"] = "value"; Assert.IsNotNull(sbe.ProcessOutgoingMessage(response)); ITamperResistantOpenIdMessage signedResponse = (ITamperResistantOpenIdMessage)response; // Make sure that the extra parameters are signed. // Since the signing algorithm only allows for signing parameters that start with // 'openid.', other parameters should not be signed. Assert.IsNotNull(signedResponse.SignedParameterOrder); string[] signedParameters = signedResponse.SignedParameterOrder.Split(','); Assert.IsTrue(signedParameters.Contains("somesigned")); Assert.IsFalse(signedParameters.Contains("someunsigned")); }
public void SignaturesMatchKnownGood() { Protocol protocol = Protocol.V20; var settings = new ProviderSecuritySettings(); var cryptoStore = new MemoryCryptoKeyStore(); byte[] associationSecret = Convert.FromBase64String("rsSwv1zPWfjPRQU80hciu8FPDC+GONAMJQ/AvSo1a2M="); string handle = "mock"; cryptoStore.StoreKey(ProviderAssociationKeyStorage.SharedAssociationBucket, handle, new CryptoKey(associationSecret, DateTime.UtcNow.AddDays(1))); var store = new ProviderAssociationKeyStorage(cryptoStore); SigningBindingElement signer = new ProviderSigningBindingElement(store, settings); signer.Channel = new TestChannel(this.MessageDescriptions); IndirectSignedResponse message = new IndirectSignedResponse(protocol.Version, new Uri("http://rp")); ITamperResistantOpenIdMessage signedMessage = message; message.ProviderEndpoint = new Uri("http://provider"); signedMessage.UtcCreationDate = DateTime.Parse("1/1/2009"); signedMessage.AssociationHandle = handle; Assert.IsNotNull(signer.ProcessOutgoingMessage(message)); Assert.AreEqual("o9+uN7qTaUS9v0otbHTuNAtbkpBm14+es9QnNo6IHD4=", signedMessage.Signature); }
public void MissingSignedParameter() { var cryptoStore = new MemoryCryptoKeyStore(); byte[] associationSecret = Convert.FromBase64String("rsSwv1zPWfjPRQU80hciu8FPDC+GONAMJQ/AvSo1a2M="); string handle = "{634477555066085461}{TTYcIg==}{32}"; cryptoStore.StoreKey(ProviderAssociationKeyStorage.PrivateAssociationBucket, handle, new CryptoKey(associationSecret, DateTime.UtcNow.AddDays(1))); var signer = new ProviderSigningBindingElement(new ProviderAssociationKeyStorage(cryptoStore), new ProviderSecuritySettings()); var testChannel = new TestChannel(new OpenIdProviderMessageFactory()); signer.Channel = testChannel; var buggyRPMessage = new Dictionary <string, string>() { { "openid.assoc_handle", "{634477555066085461}{TTYcIg==}{32}" }, { "openid.claimed_id", "https://openid.stackexchange.com/user/f5e91123-e5b4-43c5-871f-5f276c75d31a" }, { "openid.identity", "https://openid.stackexchange.com/user/f5e91123-e5b4-43c5-871f-5f276c75d31a" }, { "openid.mode", "check_authentication" }, { "openid.op_endpoint", "https://openid.stackexchange.com/openid/provider" }, { "openid.response_nonce", "2011-08-01T00:32:10Zvdyt3efw" }, { "openid.return_to", "http://openid-consumer.appspot.com/finish?session_id=1543025&janrain_nonce=2011-08-01T00%3A32%3A09ZIPGz7D" }, { "openid.sig", "b0Rll6Kt1KKBWWBEg/qBvW3sQYtmhOUmpI0/UREBVZ0=" }, { "openid.signed", "claimed_id,identity,assoc_handle,op_endpoint,return_to,response_nonce,ns.sreg,sreg.email,sreg.fullname" }, { "openid.sreg.email", "*****@*****.**" }, { "openid.sreg.fullname", "Kevin K Montrose" }, }; var message = (CheckAuthenticationRequest)testChannel.Receive(buggyRPMessage, new MessageReceivingEndpoint(OPUri, HttpDeliveryMethods.PostRequest)); var originalResponse = new IndirectSignedResponse(message, signer.Channel); signer.ProcessIncomingMessage(originalResponse); }
/// <summary> /// Performs any transformation on an incoming message that may be necessary and/or /// validates an incoming message based on the rules of this channel binding element. /// </summary> /// <param name="message">The incoming message to process.</param> /// <returns> /// True if the <paramref name="message"/> applied to this binding element /// and the operation was successful. False if the operation did not apply to this message. /// </returns> /// <exception cref="ProtocolException"> /// Thrown when the binding element rules indicate that this message is invalid and should /// NOT be processed. /// </exception> /// <remarks> /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> public bool PrepareMessageForReceiving(IProtocolMessage message) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null && response.Version.Major < 2) { // Although GetReturnToArgument may return null if the parameters are not signed, // the ReturnToSignatureBindingElement should have thrown an exception already // if this is a 1.0 OP signed response without a valid signature since 1.0 OPs // are not supposed to be able to send unsolicited assertions. // Any safe solicited assertion would include our signature, allowing us to find // these values. if (response.ProviderEndpoint == null) { string op_endpoint = response.GetReturnToArgument(ProviderEndpointParameterName); response.ProviderEndpoint = new Uri(op_endpoint); } PositiveAssertionResponse authResponse = response as PositiveAssertionResponse; if (authResponse != null) { if (authResponse.ClaimedIdentifier == null) { authResponse.ClaimedIdentifier = response.GetReturnToArgument(ClaimedIdentifierParameterName); } } return(true); } return(false); }
/// <summary> /// Performs any transformation on an incoming message that may be necessary and/or /// validates an incoming message based on the rules of this channel binding element. /// </summary> /// <param name="message">The incoming message to process.</param> /// <returns> /// The protections (if any) that this binding element applied to the message. /// Null if this binding element did not even apply to this binding element. /// </returns> /// <exception cref="ProtocolException"> /// Thrown when the binding element rules indicate that this message is invalid and should /// NOT be processed. /// </exception> /// <remarks> /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> public MessageProtections?ProcessIncomingMessage(IProtocolMessage message) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null) { // We can't use response.GetReturnToArgument(string) because that relies // on us already having validated this signature. NameValueCollection returnToParameters = HttpUtility.ParseQueryString(response.ReturnTo.Query); // Only check the return_to signature if one is present. if (returnToParameters[ReturnToSignatureHandleParameterName] != null) { // Set the safety flag showing whether the return_to url had a valid signature. byte[] expectedBytes = this.GetReturnToSignature(response.ReturnTo); string actual = returnToParameters[ReturnToSignatureParameterName]; actual = OpenIdUtilities.FixDoublyUriDecodedBase64String(actual); byte[] actualBytes = Convert.FromBase64String(actual); response.ReturnToParametersSignatureValidated = MessagingUtilities.AreEquivalentConstantTime(actualBytes, expectedBytes); if (!response.ReturnToParametersSignatureValidated) { Logger.Bindings.WarnFormat("The return_to signature failed verification."); } return(MessageProtections.None); } } return(null); }
/// <summary> /// Performs any transformation on an incoming message that may be necessary and/or /// validates an incoming message based on the rules of this channel binding element. /// </summary> /// <param name="message">The incoming message to process.</param> /// <returns> /// True if the <paramref name="message"/> applied to this binding element /// and the operation was successful. False if the operation did not apply to this message. /// </returns> /// <exception cref="ProtocolException"> /// Thrown when the binding element rules indicate that this message is invalid and should /// NOT be processed. /// </exception> /// <remarks> /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> public bool PrepareMessageForReceiving(IProtocolMessage message) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null) { // We can't use response.GetReturnToArgument(string) because that relies // on us already having validated this signature. NameValueCollection returnToParameters = HttpUtility.ParseQueryString(response.ReturnTo.Query); // Only check the return_to signature if one is present. if (returnToParameters[ReturnToSignatureHandleParameterName] != null) { // Set the safety flag showing whether the return_to url had a valid signature. string expected = this.GetReturnToSignature(response.ReturnTo); string actual = returnToParameters[ReturnToSignatureParameterName]; actual = OpenIdUtilities.FixDoublyUriDecodedBase64String(actual); response.ReturnToParametersSignatureValidated = actual == expected; if (!response.ReturnToParametersSignatureValidated) { Logger.WarnFormat("The return_to signature failed verification."); } return(true); } } return(false); }
internal AnonymousRequest(OpenIdProvider provider, SignedResponseRequest request) : base(provider, request) { Requires.NotNull(provider, "provider"); Requires.That(!(request is CheckIdRequest), "request", "request cannot be CheckIdRequest"); this.positiveResponse = new IndirectSignedResponse(request); }
internal AnonymousRequest(OpenIdProvider provider, SignedResponseRequest request) : base(provider, request) { Contract.Requires <ArgumentNullException>(provider != null); Contract.Requires <ArgumentException>(!(request is CheckIdRequest), "Instantiate " + typeof(AuthenticationRequest).Name + " to handle this kind of message."); this.positiveResponse = new IndirectSignedResponse(request); }
public override void SetUp() { base.SetUp(); IndirectSignedResponse responseMessage = new IndirectSignedResponse(Protocol.Default.Version, RPUri); this.extensions = responseMessage.Extensions; this.response = new DotNetOpenAuth.OpenId.RelyingParty.PositiveAnonymousResponse(responseMessage); }
/// <summary> /// Creates a response message with one extensions. /// </summary> /// <param name="protocol">The protocol to construct the message with.</param> /// <returns>The message ready to send from OP to RP.</returns> private IndirectSignedResponse CreateResponseWithExtensions(Protocol protocol) { Requires.NotNull(protocol, "protocol"); IndirectSignedResponse response = new IndirectSignedResponse(protocol.Version, RPUri); response.ProviderEndpoint = OPUri; response.Extensions.Add(new MockOpenIdExtension("pv", "ev")); return(response); }
/// <summary> /// Creates a response message with one extensions. /// </summary> /// <param name="protocol">The protocol to construct the message with.</param> /// <returns>The message ready to send from OP to RP.</returns> private IndirectSignedResponse CreateResponseWithExtensions(Protocol protocol) { Contract.Requires <ArgumentNullException>(protocol != null); IndirectSignedResponse response = new IndirectSignedResponse(protocol.Version, RPUri); response.ProviderEndpoint = OPUri; response.Extensions.Add(new MockOpenIdExtension("pv", "ev")); return(response); }
public void ProviderTest() { var responseMessage = new IndirectSignedResponse(Protocol.V20.Version, this.returnTo); responseMessage.ProviderEndpoint = OPUri; var response = new PositiveAnonymousResponse(responseMessage); Assert.IsNotNull(response.Provider); Assert.AreEqual(OPUri, response.Provider.Uri); Assert.AreEqual(responseMessage.Version, response.Provider.Version); }
public override void SetUp() { base.SetUp(); this.protocol = Protocol.V20; this.request = new CheckIdRequest(this.protocol.Version, OPUri, AuthenticationRequestMode.Setup); this.request.ReturnTo = RPUri; this.response = new IndirectSignedResponse(this.request); this.unsolicited = new IndirectSignedResponse(this.protocol.Version, RPUri); }
/// <summary> /// Analyzes an incoming request message payload to discover what kind of /// message is embedded in it and returns the type, or null if no match is found. /// </summary> /// <param name="recipient">The intended or actual recipient of the request message.</param> /// <param name="fields">The name/value pairs that make up the message payload.</param> /// <returns> /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can /// deserialize to. Null if the request isn't recognized as a valid protocol message. /// </returns> public IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary <string, string> fields) { RequestBase message = null; // Discern the OpenID version of the message. Protocol protocol = Protocol.V11; string ns; if (fields.TryGetValue(Protocol.V20.openid.ns, out ns)) { ErrorUtilities.VerifyProtocol(string.Equals(ns, Protocol.OpenId2Namespace, StringComparison.Ordinal), MessagingStrings.UnexpectedMessagePartValue, Protocol.V20.openid.ns, ns); protocol = Protocol.V20; } string mode; if (fields.TryGetValue(protocol.openid.mode, out mode)) { if (string.Equals(mode, protocol.Args.Mode.cancel) || (string.Equals(mode, protocol.Args.Mode.setup_needed) && (protocol.Version.Major >= 2 || fields.ContainsKey(protocol.openid.user_setup_url)))) { message = new NegativeAssertionResponse(protocol.Version, recipient.Location, mode); } else if (string.Equals(mode, protocol.Args.Mode.id_res)) { if (fields.ContainsKey(protocol.openid.identity)) { message = new PositiveAssertionResponse(protocol.Version, recipient.Location); } else { ErrorUtilities.VerifyProtocol(!fields.ContainsKey(protocol.openid.claimed_id), OpenIdStrings.IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent); message = new IndirectSignedResponse(protocol.Version, recipient.Location); } } else if (string.Equals(mode, protocol.Args.Mode.error)) { message = new IndirectErrorResponse(protocol.Version, recipient.Location); } else { ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessagePartValue, protocol.openid.mode, mode); } } if (message != null) { message.SetAsIncoming(); } return(message); }
/// <summary> /// Gets the dictionary of message parts that should be deserialized into extensions. /// </summary> /// <param name="message">The message.</param> /// <param name="ignoreUnsigned">If set to <c>true</c> only signed extensions will be available.</param> /// <returns> /// A dictionary of message parts, including only signed parts when appropriate. /// </returns> private IDictionary <string, string> GetExtensionsDictionary(IProtocolMessage message, bool ignoreUnsigned) { RequiresEx.ValidState(this.Channel != null); IndirectSignedResponse signedResponse = message as IndirectSignedResponse; if (signedResponse != null && ignoreUnsigned) { return(signedResponse.GetSignedMessageParts(this.Channel)); } else { return(this.Channel.MessageDescriptions.GetAccessor(message)); } }
/// <summary> /// Gets a specific association referenced in a given message's association handle. /// </summary> /// <param name="signedMessage">The signed message whose association handle should be used to lookup the association to return.</param> /// <returns> /// The referenced association; or <c>null</c> if such an association cannot be found. /// </returns> protected override Association GetSpecificAssociation(ITamperResistantOpenIdMessage signedMessage) { Association association = null; if (!string.IsNullOrEmpty(signedMessage.AssociationHandle)) { IndirectSignedResponse indirectSignedMessage = signedMessage as IndirectSignedResponse; if (this.rpAssociations != null) // if on a smart RP { Uri providerEndpoint = indirectSignedMessage.ProviderEndpoint; association = this.rpAssociations.GetAssociation(providerEndpoint, signedMessage.AssociationHandle); } } return(association); }
public void CtorAndProperties() { var responseMessage = new IndirectSignedResponse(Protocol.V20.Version, this.returnTo); var ext = new ClaimsResponse(); responseMessage.Extensions.Add(ext); var response = new PositiveAnonymousResponse(responseMessage); Assert.AreEqual(AuthenticationStatus.ExtensionsOnly, response.Status); Assert.AreSame(responseMessage, response.Response); Assert.IsNull(response.ClaimedIdentifier); Assert.IsNull(response.FriendlyIdentifierForDisplay); Assert.IsNull(response.Exception); Assert.IsNull(response.Provider); Assert.AreSame(ext, response.GetUntrustedExtension <ClaimsResponse>()); }
/// <summary> /// Gets the dictionary of message parts that should be deserialized into extensions. /// </summary> /// <param name="message">The message.</param> /// <returns>A dictionary of message parts, including only signed parts when appropriate.</returns> private IDictionary <string, string> GetExtensionsDictionary(IProtocolMessage message) { // An IndirectSignedResponse message (the only one we care to filter parts for) // can be received both by RPs and OPs (during check_auth). // Whichever party is reading the extensions, apply their security policy regarding // signing. (Although OPs have no reason to deserialize extensions during check_auth) // so that scenario might be optimized away eventually. bool extensionsShouldBeSigned = this.rpSecuritySettings != null ? !this.rpSecuritySettings.AllowUnsignedIncomingExtensions : this.opSecuritySettings.SignOutgoingExtensions; IndirectSignedResponse signedResponse = message as IndirectSignedResponse; if (signedResponse != null && extensionsShouldBeSigned) { return(signedResponse.GetSignedMessageParts()); } else { return(new MessageDictionary(message)); } }
public void ExtensionResponsesAreSigned() { Protocol protocol = Protocol.Default; var op = this.CreateProvider(); IndirectSignedResponse response = this.CreateResponseWithExtensions(protocol); op.Channel.PrepareResponse(response); ITamperResistantOpenIdMessage signedResponse = (ITamperResistantOpenIdMessage)response; string extensionAliasKey = signedResponse.ExtraData.Single(kv => kv.Value == MockOpenIdExtension.MockTypeUri).Key; Assert.IsTrue(extensionAliasKey.StartsWith("openid.ns.")); string extensionAlias = extensionAliasKey.Substring("openid.ns.".Length); // Make sure that the extension members and the alias=namespace declaration are all signed. Assert.IsNotNull(signedResponse.SignedParameterOrder); string[] signedParameters = signedResponse.SignedParameterOrder.Split(','); Assert.IsTrue(signedParameters.Contains(extensionAlias + ".Part")); Assert.IsTrue(signedParameters.Contains(extensionAlias + ".data")); Assert.IsTrue(signedParameters.Contains("ns." + extensionAlias)); }
/// <summary> /// Initializes a new instance of the <see cref="PositiveAnonymousResponse"/> class. /// </summary> /// <param name="response">The response message.</param> protected internal PositiveAnonymousResponse(IndirectSignedResponse response) { Requires.NotNull(response, "response"); this.response = response; if (response.ProviderEndpoint != null && response.Version != null) { this.provider = new ProviderEndpointDescription(response.ProviderEndpoint, response.Version); } // Derived types of this are responsible to log an appropriate message for themselves. if (Logger.OpenId.IsInfoEnabled && this.GetType() == typeof(PositiveAnonymousResponse)) { Logger.OpenId.Info("Received anonymous (identity-less) positive assertion."); } if (response.ProviderEndpoint != null) { Reporting.RecordEventOccurrence(this, response.ProviderEndpoint.AbsoluteUri); } }
public void GetReturnToArgumentAndNames() { UriBuilder returnToBuilder = new UriBuilder(this.response.ReturnTo); returnToBuilder.AppendQueryArgs(new Dictionary <string, string> { { "a", "b" } }); this.request.ReturnTo = returnToBuilder.Uri; // First pretend that the return_to args were signed. this.response = new IndirectSignedResponse(this.request); this.response.ReturnToParametersSignatureValidated = true; Assert.AreEqual(1, this.response.GetReturnToParameterNames().Count()); Assert.IsTrue(this.response.GetReturnToParameterNames().Contains("a")); Assert.AreEqual("b", this.response.GetReturnToArgument("a")); // Now simulate them NOT being signed. They should still be visible at this level. this.response = new IndirectSignedResponse(this.request); this.response.ReturnToParametersSignatureValidated = false; Assert.AreEqual(1, this.response.GetReturnToParameterNames().Count()); Assert.IsTrue(this.response.GetReturnToParameterNames().Contains("a")); Assert.AreEqual("b", this.response.GetReturnToArgument("a")); }
public void SignaturesMatchKnownGood() { Protocol protocol = Protocol.V20; var settings = new ProviderSecuritySettings(); var store = new AssociationMemoryStore <AssociationRelyingPartyType>(); byte[] associationSecret = Convert.FromBase64String("rsSwv1zPWfjPRQU80hciu8FPDC+GONAMJQ/AvSo1a2M="); Association association = HmacShaAssociation.Create("mock", associationSecret, TimeSpan.FromDays(1)); store.StoreAssociation(AssociationRelyingPartyType.Smart, association); SigningBindingElement signer = new SigningBindingElement(store, settings); signer.Channel = new TestChannel(this.MessageDescriptions); IndirectSignedResponse message = new IndirectSignedResponse(protocol.Version, new Uri("http://rp")); ITamperResistantOpenIdMessage signedMessage = message; message.ProviderEndpoint = new Uri("http://provider"); signedMessage.UtcCreationDate = DateTime.Parse("1/1/2009"); signedMessage.AssociationHandle = association.Handle; Assert.IsNotNull(signer.ProcessOutgoingMessage(message)); Assert.AreEqual("o9+uN7qTaUS9v0otbHTuNAtbkpBm14+es9QnNo6IHD4=", signedMessage.Signature); }
public Task <MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null && response.Version.Major < 2) { // GetReturnToArgument may return parameters that are not signed, // but we must allow for that since in OpenID 1.x, a stateless RP has // no way to preserve the provider endpoint and claimed identifier otherwise. // We'll verify the positive assertion later in the // RelyingParty.PositiveAuthenticationResponse constructor anyway. // If this is a 1.0 OP signed response without these parameters then we didn't initiate // the request ,and since 1.0 OPs are not supposed to be able to send unsolicited // assertions it's an invalid case that we throw an exception for. if (response.ProviderEndpoint == null) { string op_endpoint = response.GetReturnToArgument(ProviderEndpointParameterName); ErrorUtilities.VerifyProtocol(op_endpoint != null, MessagingStrings.RequiredParametersMissing, message.GetType().Name, ProviderEndpointParameterName); response.ProviderEndpoint = new Uri(op_endpoint); } PositiveAssertionResponse authResponse = response as PositiveAssertionResponse; if (authResponse != null) { if (authResponse.ClaimedIdentifier == null) { string claimedId = response.GetReturnToArgument(ClaimedIdentifierParameterName); ErrorUtilities.VerifyProtocol(claimedId != null, MessagingStrings.RequiredParametersMissing, message.GetType().Name, ClaimedIdentifierParameterName); authResponse.ClaimedIdentifier = claimedId; } } return(NoneTask); } return(NullTask); }
/// <summary> /// Verifies the integrity and applicability of an incoming message. /// </summary> /// <param name="message">The message just received.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// A task that completes with the asynchronous operation. /// </returns> /// <exception cref="ProtocolException">Thrown when the message is somehow invalid, except for check_authentication messages. /// This can be due to tampering, replay attack or expiration, among other things.</exception> protected override async Task ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var checkAuthRequest = message as CheckAuthenticationRequest; if (checkAuthRequest != null) { IndirectSignedResponse originalResponse = new IndirectSignedResponse(checkAuthRequest, this); try { await base.ProcessIncomingMessageAsync(originalResponse, cancellationToken); checkAuthRequest.IsValid = true; } catch (ProtocolException) { checkAuthRequest.IsValid = false; } } else { await base.ProcessIncomingMessageAsync(message, cancellationToken); } // Convert an OpenID indirect error message, which we never expect // between two good OpenID implementations, into an exception. // We don't process DirectErrorResponse because associate negotiations // commonly get a derivative of that message type and handle it. var errorMessage = message as IndirectErrorResponse; if (errorMessage != null) { string exceptionMessage = string.Format( CultureInfo.CurrentCulture, OpenIdStrings.IndirectErrorFormattedMessage, errorMessage.ErrorMessage, errorMessage.Contact, errorMessage.Reference); throw new ProtocolException(exceptionMessage, message); } }
/// <summary> /// Gets a specific association referenced in a given message's association handle. /// </summary> /// <param name="signedMessage">The signed message whose association handle should be used to lookup the association to return.</param> /// <returns> /// The referenced association; or <c>null</c> if such an association cannot be found. /// </returns> protected override Association GetSpecificAssociation(ITamperResistantOpenIdMessage signedMessage) { Association association = null; if (!string.IsNullOrEmpty(signedMessage.AssociationHandle)) { IndirectSignedResponse indirectSignedMessage = signedMessage as IndirectSignedResponse; // Since we have an association handle, we're either signing with a smart association, // or verifying a dumb one. bool signing = string.IsNullOrEmpty(signedMessage.Signature); bool isPrivateAssociation = !signing; association = this.opAssociations.Deserialize(signedMessage, isPrivateAssociation, signedMessage.AssociationHandle); if (association == null) { // There was no valid association with the requested handle. // Let's tell the RP to forget about that association. signedMessage.InvalidateHandle = signedMessage.AssociationHandle; signedMessage.AssociationHandle = null; } } return(association); }
/// <summary> /// Determines whether the relying party sending an authentication request is /// vulnerable to replay attacks. /// </summary> /// <param name="request">The request message from the Relying Party. Useful, but may be null for conservative estimate results.</param> /// <param name="response">The response message to be signed.</param> /// <returns> /// <c>true</c> if the relying party is vulnerable; otherwise, <c>false</c>. /// </returns> private static bool IsRelyingPartyVulnerableToReplays(SignedResponseRequest request, IndirectSignedResponse response) { Requires.NotNull(response, "response"); // OpenID 2.0 includes replay protection as part of the protocol. if (response.Version.Major >= 2) { return(false); } // This library's RP may be on the remote end, and may be using 1.x merely because // discovery on the Claimed Identifier suggested this was a 1.x OP. // Since this library's RP has a built-in request_nonce parameter for replay // protection, we'll allow for that. var returnToArgs = HttpUtility.ParseQueryString(response.ReturnTo.Query); if (!string.IsNullOrEmpty(returnToArgs[Protocol.ReturnToNonceParameter])) { return(false); } // If the OP endpoint _AND_ RP return_to URL uses HTTPS then no one // can steal and replay the positive assertion. // We can only ascertain this if the request message was handed to us // so we know what our own OP endpoint is. If we don't have a request // message, then we'll default to assuming it's insecure. if (request != null) { if (request.Recipient.IsTransportSecure() && response.Recipient.IsTransportSecure()) { return(false); } } // Nothing left to protect against replays. RP is vulnerable. return(true); }
/// <summary> /// Analyzes an incoming request message payload to discover what kind of /// message is embedded in it and returns the type, or null if no match is found. /// </summary> /// <param name="recipient">The intended or actual recipient of the request message.</param> /// <param name="fields">The name/value pairs that make up the message payload.</param> /// <returns> /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can /// deserialize to. Null if the request isn't recognized as a valid protocol message. /// </returns> public IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary <string, string> fields) { ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient"); ErrorUtilities.VerifyArgumentNotNull(fields, "fields"); RequestBase message = null; // Discern the OpenID version of the message. Protocol protocol = Protocol.V11; string ns; if (fields.TryGetValue(Protocol.V20.openid.ns, out ns)) { ErrorUtilities.VerifyProtocol(string.Equals(ns, Protocol.OpenId2Namespace, StringComparison.Ordinal), MessagingStrings.UnexpectedMessagePartValue, Protocol.V20.openid.ns, ns); protocol = Protocol.V20; } string mode; if (fields.TryGetValue(protocol.openid.mode, out mode)) { if (string.Equals(mode, protocol.Args.Mode.associate)) { if (fields.ContainsKey(protocol.openid.dh_consumer_public)) { message = new AssociateDiffieHellmanRequest(protocol.Version, recipient.Location); } else { message = new AssociateUnencryptedRequest(protocol.Version, recipient.Location); } } else if (string.Equals(mode, protocol.Args.Mode.checkid_setup) || string.Equals(mode, protocol.Args.Mode.checkid_immediate)) { AuthenticationRequestMode authMode = string.Equals(mode, protocol.Args.Mode.checkid_immediate) ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup; if (fields.ContainsKey(protocol.openid.identity)) { message = new CheckIdRequest(protocol.Version, recipient.Location, authMode); } else { message = new SignedResponseRequest(protocol.Version, recipient.Location, authMode); } } else if (string.Equals(mode, protocol.Args.Mode.cancel) || (string.Equals(mode, protocol.Args.Mode.setup_needed) && (protocol.Version.Major >= 2 || fields.ContainsKey(protocol.openid.user_setup_url)))) { message = new NegativeAssertionResponse(protocol.Version, recipient.Location, mode); } else if (string.Equals(mode, protocol.Args.Mode.id_res)) { if (fields.ContainsKey(protocol.openid.identity)) { message = new PositiveAssertionResponse(protocol.Version, recipient.Location); } else { message = new IndirectSignedResponse(protocol.Version, recipient.Location); } } else if (string.Equals(mode, protocol.Args.Mode.check_authentication)) { message = new CheckAuthenticationRequest(protocol.Version, recipient.Location); } else if (string.Equals(mode, protocol.Args.Mode.error)) { message = new IndirectErrorResponse(protocol.Version, recipient.Location); } else { ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessagePartValue, protocol.openid.mode, mode); } } if (message != null) { message.SetAsIncoming(); } return(message); }