/// <summary> /// Creates the association at the provider side after the association request has been received. /// </summary> /// <param name="request">The association request.</param> /// <param name="securitySettings">The security settings of the Provider.</param> /// <returns>The newly created association.</returns> /// <remarks> /// The response message is updated to include the details of the created association by this method, /// but the resulting association is <i>not</i> added to the association store and must be done by the caller. /// </remarks> protected override Association CreateAssociationAtProvider(AssociateRequest request, ProviderSecuritySettings securitySettings) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); var diffieHellmanRequest = request as AssociateDiffieHellmanRequest; ErrorUtilities.VerifyArgument(diffieHellmanRequest != null, "request"); ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); this.SessionType = this.SessionType ?? request.SessionType; // Go ahead and create the association first, complete with its secret that we're about to share. Association association = HmacShaAssociation.Create(this.Protocol, this.AssociationType, AssociationRelyingPartyType.Smart, securitySettings); // We now need to securely communicate the secret to the relying party using Diffie-Hellman. // We do this by performing a DH algorithm on the secret and setting a couple of properties // that will be transmitted to the Relying Party. The RP will perform an inverse operation // using its part of a DH secret in order to decrypt the shared secret we just invented // above when we created the association. DiffieHellman dh = new DiffieHellmanManaged( diffieHellmanRequest.DiffieHellmanModulus ?? AssociateDiffieHellmanRequest.DefaultMod, diffieHellmanRequest.DiffieHellmanGen ?? AssociateDiffieHellmanRequest.DefaultGen, AssociateDiffieHellmanRequest.DefaultX); HashAlgorithm hasher = DiffieHellmanUtilities.Lookup(this.Protocol, this.SessionType); this.DiffieHellmanServerPublic = DiffieHellmanUtilities.EnsurePositive(dh.CreateKeyExchange()); this.EncodedMacKey = DiffieHellmanUtilities.SHAHashXorSecret(hasher, dh, diffieHellmanRequest.DiffieHellmanConsumerPublic, association.SecretKey); return association; }
/// <summary> /// Creates a new association of a given type at an OpenID Provider. /// </summary> /// <param name="protocol">The protocol.</param> /// <param name="associationType">Type of the association (i.e. HMAC-SHA1 or HMAC-SHA256)</param> /// <param name="associationUse">A value indicating whether the new association will be used privately by the Provider for "dumb mode" authentication /// or shared with the Relying Party for "smart mode" authentication.</param> /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings of the Provider.</param> /// <returns> /// The newly created association. /// </returns> /// <remarks> /// The new association is NOT automatically put into an association store. This must be done by the caller. /// </remarks> internal static HmacShaAssociation Create(Protocol protocol, string associationType, AssociationRelyingPartyType associationUse, IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Requires.NotNull(protocol, "protocol"); Requires.NotNullOrEmpty(associationType, "associationType"); Requires.NotNull(associationStore, "associationStore"); Requires.NotNull(securitySettings, "securitySettings"); Contract.Ensures(Contract.Result<HmacShaAssociation>() != null); int secretLength = HmacShaAssociation.GetSecretLength(protocol, associationType); // Generate the secret that will be used for signing byte[] secret = MessagingUtilities.GetCryptoRandomData(secretLength); TimeSpan lifetime; if (associationUse == AssociationRelyingPartyType.Smart) { if (!securitySettings.AssociationLifetimes.TryGetValue(associationType, out lifetime)) { lifetime = DefaultMaximumLifetime; } } else { lifetime = HmacShaAssociation.DumbSecretLifetime; } string handle = associationStore.Serialize(secret, DateTime.UtcNow + lifetime, associationUse == AssociationRelyingPartyType.Dumb); Contract.Assert(protocol != null); // All the way up to the method call, the condition holds, yet we get a Requires failure next Contract.Assert(secret != null); Contract.Assert(!string.IsNullOrEmpty(associationType)); var result = HmacShaAssociation.Create(protocol, associationType, handle, secret, lifetime); return result; }
/// <summary> /// Initializes a new instance of the SigningBindingElement class for use by a Provider. /// </summary> /// <param name="associationStore">The association store used to look up the secrets needed for signing.</param> /// <param name="securitySettings">The security settings.</param> internal SigningBindingElement(IAssociationStore<AssociationRelyingPartyType> associationStore, ProviderSecuritySettings securitySettings) { Contract.Requires<ArgumentNullException>(associationStore != null); Contract.Requires<ArgumentNullException>(securitySettings != null); this.opAssociations = associationStore; this.opSecuritySettings = securitySettings; }
/// <summary> /// Creates a response that notifies the Relying Party that the requested /// association type is not supported by this Provider, and offers /// an alternative association type, if possible. /// </summary> /// <param name="requestMessage">The request message.</param> /// <param name="securitySettings">The security settings that apply to this Provider.</param> /// <returns> /// The response to send to the Relying Party. /// </returns> private static AssociateUnsuccessfulResponse CreateUnsuccessfulResponse(IAssociateRequestProvider requestMessage, ProviderSecuritySettings securitySettings) { Requires.NotNull(requestMessage, "requestMessage"); Requires.NotNull(securitySettings, "securitySettings"); var unsuccessfulResponse = new AssociateUnsuccessfulResponse(requestMessage.Version, (AssociateRequest)requestMessage); // The strategy here is to suggest that the RP try again with the lowest // permissible security settings, giving the RP the best chance of being // able to match with a compatible request. bool unencryptedAllowed = requestMessage.Recipient.IsTransportSecure(); bool useDiffieHellman = !unencryptedAllowed; var request = (AssociateRequest)requestMessage; var protocol = requestMessage.GetProtocol(); string associationType, sessionType; if (HmacShaAssociation.TryFindBestAssociation(protocol, false, securitySettings, useDiffieHellman, out associationType, out sessionType)) { ErrorUtilities.VerifyInternal(request.AssociationType != associationType, "The RP asked for an association that should have been allowed, but the OP is trying to suggest the same one as an alternative!"); unsuccessfulResponse.AssociationType = associationType; unsuccessfulResponse.SessionType = sessionType; Logger.OpenId.InfoFormat( "Association requested of type '{0}' and session '{1}', which the Provider does not support. Sending back suggested alternative of '{0}' with session '{1}'.", request.AssociationType, request.SessionType, unsuccessfulResponse.AssociationType, unsuccessfulResponse.SessionType); } else { Logger.OpenId.InfoFormat("Association requested of type '{0}' and session '{1}', which the Provider does not support. No alternative association type qualified for suggesting back to the Relying Party.", request.AssociationType, request.SessionType); } return unsuccessfulResponse; }
/// <summary> /// Initializes a new instance of the <see cref="AutoResponsiveRequest"/> class /// for a response to an unrecognizable request. /// </summary> /// <param name="response">The response that is ready for transmittal.</param> /// <param name="securitySettings">The security settings.</param> internal AutoResponsiveRequest(IProtocolMessage response, ProviderSecuritySettings securitySettings) : base(IndirectResponseBase.GetVersion(response), securitySettings) { ErrorUtilities.VerifyArgumentNotNull(response, "response"); this.response = response; }
/// <summary> /// Initializes a new instance of the <see cref="AutoResponsiveRequest"/> class. /// </summary> /// <param name="request">The request message.</param> /// <param name="response">The response that is ready for transmittal.</param> /// <param name="securitySettings">The security settings.</param> internal AutoResponsiveRequest(IDirectedProtocolMessage request, IProtocolMessage response, ProviderSecuritySettings securitySettings) : base(request, securitySettings) { ErrorUtilities.VerifyArgumentNotNull(response, "response"); this.response = response; }
/// <summary> /// Initializes a new instance of the <see cref="ProviderSigningBindingElement"/> class. /// </summary> /// <param name="associationStore">The association store used to look up the secrets needed for signing.</param> /// <param name="securitySettings">The security settings.</param> internal ProviderSigningBindingElement(IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Requires.NotNull(associationStore, "associationStore"); Requires.NotNull(securitySettings, "securitySettings"); this.opAssociations = associationStore; this.opSecuritySettings = securitySettings; }
/// <summary> /// Adapts the default security settings to the requirements of this behavior. /// </summary> /// <param name="securitySettings">The original security settings.</param> void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) { if (securitySettings.MaximumHashBitLength < 256) { securitySettings.MaximumHashBitLength = 256; } SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA256, MaximumAssociationLifetime, securitySettings); SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1, MaximumAssociationLifetime, securitySettings); }
/// <summary> /// Initializes a new instance of the SigningBindingElement class for use by a Provider. /// </summary> /// <param name="associationStore">The association store used to look up the secrets needed for signing.</param> /// <param name="securitySettings">The security settings.</param> internal SigningBindingElement(IAssociationStore<AssociationRelyingPartyType> associationStore, ProviderSecuritySettings securitySettings) { ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore"); ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); this.opAssociations = associationStore; this.opSecuritySettings = securitySettings; }
public override void SetUp() { base.SetUp(); this.RelyingPartySecuritySettings = OpenIdElement.Configuration.RelyingParty.SecuritySettings.CreateSecuritySettings(); this.ProviderSecuritySettings = OpenIdElement.Configuration.Provider.SecuritySettings.CreateSecuritySettings(); this.AutoProviderScenario = Scenarios.AutoApproval; Identifier.EqualityOnStrings = true; this.HostFactories.InstallUntrustedWebReqestHandler = true; }
public override void SetUp() { base.SetUp(); this.RelyingPartySecuritySettings = DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.SecuritySettings.CreateSecuritySettings(); this.ProviderSecuritySettings = DotNetOpenAuthSection.Configuration.OpenId.Provider.SecuritySettings.CreateSecuritySettings(); this.MockResponder = MockHttpRequest.CreateUntrustedMockHttpHandler(); this.RequestHandler = this.MockResponder.MockWebRequestHandler; this.AutoProviderScenario = Scenarios.AutoApproval; Identifier.EqualityOnStrings = true; }
/// <summary> /// Called to create the Association based on a request previously given by the Relying Party. /// </summary> /// <param name="request">The prior request for an association.</param> /// <param name="response">The response.</param> /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings for the Provider. Should be <c>null</c> for Relying Parties.</param> /// <returns> /// The created association. /// </returns> /// <remarks> /// The response message is updated to include the details of the created association by this method. /// This method is called by both the Provider and the Relying Party, but actually performs /// quite different operations in either scenario. /// </remarks> internal static Association CreateAssociation(AssociateRequest request, IAssociateSuccessfulResponseProvider response, IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Requires.NotNull(request, "request"); Requires.NotNull(response, "response"); Requires.NotNull(securitySettings, "securitySettings"); // We need to initialize some common properties based on the created association. var association = response.CreateAssociationAtProvider(request, associationStore, securitySettings); response.ExpiresIn = association.SecondsTillExpiration; response.AssociationHandle = association.Handle; return association; }
/// <summary> /// Simulates an extension request and response. /// </summary> /// <param name="protocol">The protocol to use in the roundtripping.</param> /// <param name="requests">The extensions to add to the request message.</param> /// <param name="responses">The extensions to add to the response message.</param> /// <remarks> /// This method relies on the extension objects' Equals methods to verify /// accurate transport. The Equals methods should be verified by separate tests. /// </remarks> internal static void Roundtrip( Protocol protocol, IEnumerable<IOpenIdMessageExtension> requests, IEnumerable<IOpenIdMessageExtension> responses) { var securitySettings = new ProviderSecuritySettings(); var cryptoKeyStore = new MemoryCryptoKeyStore(); var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore); Association association = HmacShaAssociationProvider.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, associationStore, securitySettings); var coordinator = new OpenIdCoordinator( rp => { RegisterExtension(rp.Channel, Mocks.MockOpenIdExtension.Factory); var requestBase = new CheckIdRequest(protocol.Version, OpenIdTestBase.OPUri, AuthenticationRequestMode.Immediate); OpenIdTestBase.StoreAssociation(rp, OpenIdTestBase.OPUri, association); requestBase.AssociationHandle = association.Handle; requestBase.ClaimedIdentifier = "http://claimedid"; requestBase.LocalIdentifier = "http://localid"; requestBase.ReturnTo = OpenIdTestBase.RPUri; foreach (IOpenIdMessageExtension extension in requests) { requestBase.Extensions.Add(extension); } rp.Channel.Respond(requestBase); var response = rp.Channel.ReadFromRequest<PositiveAssertionResponse>(); var receivedResponses = response.Extensions.Cast<IOpenIdMessageExtension>(); CollectionAssert<IOpenIdMessageExtension>.AreEquivalentByEquality(responses.ToArray(), receivedResponses.ToArray()); }, op => { RegisterExtension(op.Channel, Mocks.MockOpenIdExtension.Factory); var key = cryptoKeyStore.GetCurrentKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1)); op.CryptoKeyStore.StoreKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value); var request = op.Channel.ReadFromRequest<CheckIdRequest>(); var response = new PositiveAssertionResponse(request); var receivedRequests = request.Extensions.Cast<IOpenIdMessageExtension>(); CollectionAssert<IOpenIdMessageExtension>.AreEquivalentByEquality(requests.ToArray(), receivedRequests.ToArray()); foreach (var extensionResponse in responses) { response.Extensions.Add(extensionResponse); } op.Channel.Respond(response); }); coordinator.Run(); }
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); }
/// <summary> /// Initializes the binding elements. /// </summary> /// <param name="cryptoKeyStore">The OpenID Provider's crypto key store.</param> /// <param name="nonceStore">The nonce store to use.</param> /// <param name="securitySettings">The security settings to apply. Must be an instance of either RelyingPartySecuritySettings or ProviderSecuritySettings.</param> /// <returns> /// An array of binding elements which may be used to construct the channel. /// </returns> private static IChannelBindingElement[] InitializeBindingElements(IProviderAssociationStore cryptoKeyStore, INonceStore nonceStore, ProviderSecuritySettings securitySettings) { Requires.NotNull(cryptoKeyStore, "cryptoKeyStore"); Requires.NotNull(securitySettings, "securitySettings"); Requires.NotNull(nonceStore, "nonceStore"); SigningBindingElement signingElement; signingElement = new ProviderSigningBindingElement(cryptoKeyStore, securitySettings); var extensionFactory = OpenIdExtensionFactoryAggregator.LoadFromConfiguration(); List<IChannelBindingElement> elements = new List<IChannelBindingElement>(8); elements.Add(new ExtensionsBindingElement(extensionFactory, securitySettings, true)); elements.Add(new StandardReplayProtectionBindingElement(nonceStore, true)); elements.Add(new StandardExpirationBindingElement()); elements.Add(signingElement); return elements.ToArray(); }
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); }
/// <summary> /// Creates a Provider's response to an incoming association request. /// </summary> /// <param name="requestMessage">The request message.</param> /// <param name="associationStore">The association store.</param> /// <param name="securitySettings">The security settings on the Provider.</param> /// <returns> /// The appropriate association response that is ready to be sent back to the Relying Party. /// </returns> /// <remarks> /// <para>If an association is created, it will be automatically be added to the provided /// association store.</para> /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>. /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para> /// </remarks> internal static IProtocolMessage CreateResponse(IAssociateRequestProvider requestMessage, IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Requires.NotNull(requestMessage, "requestMessage"); Requires.NotNull(associationStore, "associationStore"); Requires.NotNull(securitySettings, "securitySettings"); AssociateRequest request = (AssociateRequest)requestMessage; IProtocolMessage response; var protocol = requestMessage.GetProtocol(); if (securitySettings.IsAssociationInPermittedRange(protocol, request.AssociationType) && HmacShaAssociation.IsDHSessionCompatible(protocol, request.AssociationType, request.SessionType)) { response = requestMessage.CreateResponseCore(); // Create and store the association if this is a successful response. var successResponse = response as IAssociateSuccessfulResponseProvider; if (successResponse != null) { OpenIdProviderUtilities.CreateAssociation(request, successResponse, associationStore, securitySettings); } } else { response = CreateUnsuccessfulResponse(requestMessage, securitySettings); } return response; }
/// <summary> /// Called to create the Association based on a request previously given by the Relying Party. /// </summary> /// <param name="request">The prior request for an association.</param> /// <param name="response">The response.</param> /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings for the Provider. Should be <c>null</c> for Relying Parties.</param> /// <returns> /// The created association. /// </returns> /// <remarks> /// The response message is updated to include the details of the created association by this method. /// This method is called by both the Provider and the Relying Party, but actually performs /// quite different operations in either scenario. /// </remarks> internal static Association CreateAssociation(AssociateRequest request, IAssociateSuccessfulResponseProvider response, IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Requires.NotNull(request, "request"); Requires.NotNull(response, "response"); Requires.NotNull(securitySettings, "securitySettings"); // We need to initialize some common properties based on the created association. var association = response.CreateAssociationAtProvider(request, associationStore, securitySettings); response.ExpiresIn = association.SecondsTillExpiration; response.AssociationHandle = association.Handle; return(association); }
/// <summary> /// Creates a deep clone of this instance. /// </summary> /// <returns>A new instance that is a deep clone of this instance.</returns> internal ProviderSecuritySettings Clone() { var securitySettings = new ProviderSecuritySettings(); foreach (var pair in this.AssociationLifetimes) { securitySettings.AssociationLifetimes.Add(pair); } securitySettings.MaximumHashBitLength = this.MaximumHashBitLength; securitySettings.MinimumHashBitLength = this.MinimumHashBitLength; securitySettings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks; securitySettings.RequireSsl = this.RequireSsl; securitySettings.SignOutgoingExtensions = this.SignOutgoingExtensions; securitySettings.UnsolicitedAssertionVerification = this.UnsolicitedAssertionVerification; return securitySettings; }
/// <summary> /// Creates a new association of a given type at an OpenID Provider. /// </summary> /// <param name="protocol">The protocol.</param> /// <param name="associationType">Type of the association (i.e. HMAC-SHA1 or HMAC-SHA256)</param> /// <param name="associationUse">A value indicating whether the new association will be used privately by the Provider for "dumb mode" authentication /// or shared with the Relying Party for "smart mode" authentication.</param> /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings of the Provider.</param> /// <returns> /// The newly created association. /// </returns> /// <remarks> /// The new association is NOT automatically put into an association store. This must be done by the caller. /// </remarks> internal static HmacShaAssociation Create(Protocol protocol, string associationType, AssociationRelyingPartyType associationUse, IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Requires.NotNull(protocol, "protocol"); Requires.NotNullOrEmpty(associationType, "associationType"); Requires.NotNull(associationStore, "associationStore"); Requires.NotNull(securitySettings, "securitySettings"); int secretLength = HmacShaAssociation.GetSecretLength(protocol, associationType); // Generate the secret that will be used for signing byte[] secret = MessagingUtilities.GetCryptoRandomData(secretLength); TimeSpan lifetime; if (associationUse == AssociationRelyingPartyType.Smart) { if (!securitySettings.AssociationLifetimes.TryGetValue(associationType, out lifetime)) { lifetime = DefaultMaximumLifetime; } } else { lifetime = HmacShaAssociation.DumbSecretLifetime; } string handle = associationStore.Serialize(secret, DateTime.UtcNow + lifetime, associationUse == AssociationRelyingPartyType.Dumb); Assumes.True(protocol != null); // All the way up to the method call, the condition holds, yet we get a Requires failure next Assumes.True(secret != null); Assumes.True(!string.IsNullOrEmpty(associationType)); var result = HmacShaAssociation.Create(protocol, associationType, handle, secret, lifetime); return(result); }
/// <summary> /// Initializes a new instance of the <see cref="OpenIdProviderChannel"/> class. /// </summary> /// <param name="cryptoKeyStore">The OpenID Provider's association store or handle encoder.</param> /// <param name="nonceStore">The nonce store to use.</param> /// <param name="securitySettings">The security settings.</param> internal OpenIdProviderChannel(IProviderAssociationStore cryptoKeyStore, INonceStore nonceStore, ProviderSecuritySettings securitySettings) : this(cryptoKeyStore, nonceStore, new OpenIdProviderMessageFactory(), securitySettings) { Requires.NotNull(cryptoKeyStore, "cryptoKeyStore"); Requires.NotNull(securitySettings, "securitySettings"); }
/// <summary> /// Simulates an extension request and response. /// </summary> /// <param name="protocol">The protocol to use in the roundtripping.</param> /// <param name="requests">The extensions to add to the request message.</param> /// <param name="responses">The extensions to add to the response message.</param> /// <remarks> /// This method relies on the extension objects' Equals methods to verify /// accurate transport. The Equals methods should be verified by separate tests. /// </remarks> internal async Task RoundtripAsync( Protocol protocol, IEnumerable<IOpenIdMessageExtension> requests, IEnumerable<IOpenIdMessageExtension> responses) { var securitySettings = new ProviderSecuritySettings(); var cryptoKeyStore = new MemoryCryptoKeyStore(); var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore); Association association = HmacShaAssociationProvider.Create( protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, associationStore, securitySettings); this.HandleProvider( async (op, req) => { ExtensionTestUtilities.RegisterExtension(op.Channel, Mocks.MockOpenIdExtension.Factory); var key = cryptoKeyStore.GetCurrentKey( ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1)); op.CryptoKeyStore.StoreKey( ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value); var request = await op.Channel.ReadFromRequestAsync<CheckIdRequest>(req, CancellationToken.None); var response = new PositiveAssertionResponse(request); var receivedRequests = request.Extensions.Cast<IOpenIdMessageExtension>(); CollectionAssert<IOpenIdMessageExtension>.AreEquivalentByEquality(requests.ToArray(), receivedRequests.ToArray()); foreach (var extensionResponse in responses) { response.Extensions.Add(extensionResponse); } return await op.Channel.PrepareResponseAsync(response); }); { var rp = this.CreateRelyingParty(); ExtensionTestUtilities.RegisterExtension(rp.Channel, Mocks.MockOpenIdExtension.Factory); var requestBase = new CheckIdRequest(protocol.Version, OpenIdTestBase.OPUri, AuthenticationRequestMode.Immediate); OpenIdTestBase.StoreAssociation(rp, OpenIdTestBase.OPUri, association); requestBase.AssociationHandle = association.Handle; requestBase.ClaimedIdentifier = "http://claimedid"; requestBase.LocalIdentifier = "http://localid"; requestBase.ReturnTo = OpenIdTestBase.RPUri; foreach (IOpenIdMessageExtension extension in requests) { requestBase.Extensions.Add(extension); } var redirectingRequest = await rp.Channel.PrepareResponseAsync(requestBase); Uri redirectingResponseUri; this.HostFactories.AllowAutoRedirects = false; using (var httpClient = rp.Channel.HostFactories.CreateHttpClient()) { using (var redirectingResponse = await httpClient.GetAsync(redirectingRequest.Headers.Location)) { Assert.AreEqual(HttpStatusCode.Found, redirectingResponse.StatusCode); redirectingResponseUri = redirectingResponse.Headers.Location; } } var response = await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>( new HttpRequestMessage(HttpMethod.Get, redirectingResponseUri), CancellationToken.None); var receivedResponses = response.Extensions.Cast<IOpenIdMessageExtension>(); CollectionAssert<IOpenIdMessageExtension>.AreEquivalentByEquality(responses.ToArray(), receivedResponses.ToArray()); } }
/// <summary> /// Called to create the Association based on a request previously given by the Relying Party. /// </summary> /// <param name="request">The prior request for an association.</param> /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings of the Provider.</param> /// <returns> /// The created association. /// </returns> Association IAssociateSuccessfulResponseProvider.CreateAssociationAtProvider(AssociateRequest request, IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Requires.NotNull(request, "request"); Requires.NotNull(associationStore, "associationStore"); Requires.NotNull(securitySettings, "securitySettings"); throw new NotImplementedException(); }
/// <summary> /// Applies a well known set of security requirements to a default set of security settings. /// </summary> /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param> /// <remarks> /// Care should be taken to never decrease security when applying a profile. /// Profiles should only enhance security requirements to avoid being /// incompatible with each other. /// </remarks> void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) { // No special security to apply here. }
/// <summary> /// Creates a new association of a given type. /// </summary> /// <param name="protocol">The protocol.</param> /// <param name="associationType">Type of the association (i.e. HMAC-SHA1 or HMAC-SHA256)</param> /// <param name="associationUse">A value indicating whether the new association will be used privately by the Provider for "dumb mode" authentication /// or shared with the Relying Party for "smart mode" authentication.</param> /// <param name="securitySettings">The security settings of the Provider.</param> /// <returns>The newly created association.</returns> /// <remarks> /// The new association is NOT automatically put into an association store. This must be done by the caller. /// </remarks> internal static HmacShaAssociation Create(Protocol protocol, string associationType, AssociationRelyingPartyType associationUse, ProviderSecuritySettings securitySettings) { Contract.Requires<ArgumentNullException>(protocol != null); Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(associationType)); Contract.Requires<ArgumentNullException>(securitySettings != null); Contract.Ensures(Contract.Result<HmacShaAssociation>() != null); int secretLength = GetSecretLength(protocol, associationType); // Generate the handle. It must be unique, and preferably unpredictable, // so we use a time element and a random data element to generate it. string uniq = MessagingUtilities.GetCryptoRandomDataAsBase64(4); string handle = string.Format( CultureInfo.InvariantCulture, "{{{0}}}{{{1}}}{{{2}}}", DateTime.UtcNow.Ticks, uniq, secretLength); // Generate the secret that will be used for signing byte[] secret = MessagingUtilities.GetCryptoRandomData(secretLength); TimeSpan lifetime; if (associationUse == AssociationRelyingPartyType.Smart) { if (!securitySettings.AssociationLifetimes.TryGetValue(associationType, out lifetime)) { lifetime = DefaultMaximumLifetime; } } else { lifetime = DumbSecretLifetime; } Contract.Assert(protocol != null); // All the way up to the method call, the condition holds, yet we get a Requires failure next Contract.Assert(secret != null); Contract.Assert(!String.IsNullOrEmpty(associationType)); return Create(protocol, associationType, handle, secret, lifetime); }
/// <summary> /// Initializes a new instance of the <see cref="AutoResponsiveRequest"/> class. /// </summary> /// <param name="request">The request message.</param> /// <param name="response">The response that is ready for transmittal.</param> /// <param name="securitySettings">The security settings.</param> internal AutoResponsiveRequest(IDirectedProtocolMessage request, IProtocolMessage response, ProviderSecuritySettings securitySettings) : base(request, securitySettings) { Requires.NotNull(response, "response"); this.response = response; }
/// <summary> /// Called to create the Association based on a request previously given by the Relying Party. /// </summary> /// <param name="request">The prior request for an association.</param> /// <param name="associationStore">The Provider's association store.</param> /// <param name="securitySettings">The security settings of the Provider.</param> /// <returns> /// The created association. /// </returns> /// <remarks> /// <para>The caller will update this message's /// <see cref="AssociateSuccessfulResponse.ExpiresIn"/> and /// <see cref="AssociateSuccessfulResponse.AssociationHandle"/> /// properties based on the <see cref="Association"/> returned by this method, but any other /// association type specific properties must be set by this method.</para> /// <para>The response message is updated to include the details of the created association by this method, /// but the resulting association is <i>not</i> added to the association store and must be done by the caller.</para> /// </remarks> public Association CreateAssociationAtProvider(AssociateRequest request, IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Association association = HmacShaAssociationProvider.Create(Protocol, this.AssociationType, AssociationRelyingPartyType.Smart, associationStore, securitySettings); this.MacKey = association.SecretKey; return association; }
/// <summary> /// Initializes a new instance of the <see cref="AutoResponsiveRequest"/> class. /// </summary> /// <param name="request">The request message.</param> /// <param name="response">The response that is ready for transmittal.</param> /// <param name="securitySettings">The security settings.</param> internal AutoResponsiveRequest(IDirectedProtocolMessage request, IProtocolMessage response, ProviderSecuritySettings securitySettings) : base(request, securitySettings) { Contract.Requires <ArgumentNullException>(response != null); this.response = response; }
/// <summary> /// Initializes a new instance of the <see cref="OpenIdProviderChannel"/> class. /// </summary> /// <param name="cryptoKeyStore">The association store to use.</param> /// <param name="nonceStore">The nonce store to use.</param> /// <param name="messageTypeProvider">An object that knows how to distinguish the various OpenID message types for deserialization purposes.</param> /// <param name="securitySettings">The security settings.</param> private OpenIdProviderChannel(IProviderAssociationStore cryptoKeyStore, INonceStore nonceStore, IMessageFactory messageTypeProvider, ProviderSecuritySettings securitySettings) : base(messageTypeProvider, InitializeBindingElements(cryptoKeyStore, nonceStore, securitySettings)) { Requires.NotNull(cryptoKeyStore, "cryptoKeyStore"); Requires.NotNull(messageTypeProvider, "messageTypeProvider"); Requires.NotNull(securitySettings, "securitySettings"); }
/// <summary> /// Applies a well known set of security requirements to a default set of security settings. /// </summary> /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param> /// <remarks> /// Care should be taken to never decrease security when applying a profile. /// Profiles should only enhance security requirements to avoid being /// incompatible with each other. /// </remarks> void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) { // Nothing to do here. }
/// <summary> /// Creates a Provider's response to an incoming association request. /// </summary> /// <param name="associationStore">The association store.</param> /// <param name="securitySettings">The security settings on the Provider.</param> /// <returns> /// The appropriate association response that is ready to be sent back to the Relying Party. /// </returns> /// <remarks> /// <para>If an association is created, it will be automatically be added to the provided /// association store.</para> /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>. /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para> /// </remarks> internal IProtocolMessage CreateResponse(IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings) { Contract.Requires<ArgumentNullException>(associationStore != null); Contract.Requires<ArgumentNullException>(securitySettings != null); IProtocolMessage response; if (securitySettings.IsAssociationInPermittedRange(Protocol, this.AssociationType) && HmacShaAssociation.IsDHSessionCompatible(Protocol, this.AssociationType, this.SessionType)) { response = this.CreateResponseCore(); // Create and store the association if this is a successful response. var successResponse = response as AssociateSuccessfulResponse; if (successResponse != null) { successResponse.CreateAssociation(this, associationStore, securitySettings); } } else { response = this.CreateUnsuccessfulResponse(securitySettings); } return response; }
private void ParameterizedAuthenticationTest(Protocol protocol, bool statelessRP, bool sharedAssociation, bool positive, bool immediate, bool tamper) { Contract.Requires<ArgumentException>(!statelessRP || !sharedAssociation, "The RP cannot be stateless while sharing an association with the OP."); Contract.Requires<ArgumentException>(positive || !tamper, "Cannot tamper with a negative response."); var securitySettings = new ProviderSecuritySettings(); var cryptoKeyStore = new MemoryCryptoKeyStore(); var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore); Association association = sharedAssociation ? HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, associationStore, securitySettings) : null; var coordinator = new OpenIdCoordinator( rp => { var request = new CheckIdRequest(protocol.Version, OPUri, immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup); if (association != null) { StoreAssociation(rp, OPUri, association); request.AssociationHandle = association.Handle; } request.ClaimedIdentifier = "http://claimedid"; request.LocalIdentifier = "http://localid"; request.ReturnTo = RPUri; request.Realm = RPUri; rp.Channel.Respond(request); if (positive) { if (tamper) { try { rp.Channel.ReadFromRequest<PositiveAssertionResponse>(); Assert.Fail("Expected exception {0} not thrown.", typeof(InvalidSignatureException).Name); } catch (InvalidSignatureException) { TestLogger.InfoFormat("Caught expected {0} exception after tampering with signed data.", typeof(InvalidSignatureException).Name); } } else { var response = rp.Channel.ReadFromRequest<PositiveAssertionResponse>(); Assert.IsNotNull(response); Assert.AreEqual(request.ClaimedIdentifier, response.ClaimedIdentifier); Assert.AreEqual(request.LocalIdentifier, response.LocalIdentifier); Assert.AreEqual(request.ReturnTo, response.ReturnTo); // Attempt to replay the message and verify that it fails. // Because in various scenarios and protocol versions different components // notice the replay, we can get one of two exceptions thrown. // When the OP notices the replay we get a generic InvalidSignatureException. // When the RP notices the replay we get a specific ReplayMessageException. try { CoordinatingChannel channel = (CoordinatingChannel)rp.Channel; channel.Replay(response); Assert.Fail("Expected ProtocolException was not thrown."); } catch (ProtocolException ex) { Assert.IsTrue(ex is ReplayedMessageException || ex is InvalidSignatureException, "A {0} exception was thrown instead of the expected {1} or {2}.", ex.GetType(), typeof(ReplayedMessageException).Name, typeof(InvalidSignatureException).Name); } } } else { var response = rp.Channel.ReadFromRequest<NegativeAssertionResponse>(); Assert.IsNotNull(response); if (immediate) { // Only 1.1 was required to include user_setup_url if (protocol.Version.Major < 2) { Assert.IsNotNull(response.UserSetupUrl); } } else { Assert.IsNull(response.UserSetupUrl); } } }, op => { if (association != null) { var key = cryptoKeyStore.GetCurrentKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1)); op.CryptoKeyStore.StoreKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value); } var request = op.Channel.ReadFromRequest<CheckIdRequest>(); Assert.IsNotNull(request); IProtocolMessage response; if (positive) { response = new PositiveAssertionResponse(request); } else { response = new NegativeAssertionResponse(request, op.Channel); } op.Channel.Respond(response); if (positive && (statelessRP || !sharedAssociation)) { var checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>(); var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); checkauthResponse.IsValid = checkauthRequest.IsValid; op.Channel.Respond(checkauthResponse); if (!tamper) { // Respond to the replay attack. checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>(); checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); checkauthResponse.IsValid = checkauthRequest.IsValid; op.Channel.Respond(checkauthResponse); } } }); if (tamper) { coordinator.IncomingMessageFilter = message => { var assertion = message as PositiveAssertionResponse; if (assertion != null) { // Alter the Local Identifier between the Provider and the Relying Party. // If the signature binding element does its job, this should cause the RP // to throw. assertion.LocalIdentifier = "http://victim"; } }; } if (statelessRP) { coordinator.RelyingParty = new OpenIdRelyingParty(null); } coordinator.Run(); }
/// <summary> /// Applies a well known set of security requirements to a default set of security settings. /// </summary> /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param> /// <remarks> /// Care should be taken to never decrease security when applying a profile. /// Profiles should only enhance security requirements to avoid being /// incompatible with each other. /// </remarks> void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) { Contract.Requires <ArgumentNullException>(securitySettings != null); throw new System.NotImplementedException(); }
/// <summary> /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file. /// </summary> /// <returns>The newly created security settings object.</returns> public ProviderSecuritySettings CreateSecuritySettings() { ProviderSecuritySettings settings = new ProviderSecuritySettings(); settings.MinimumHashBitLength = this.MinimumHashBitLength; settings.MaximumHashBitLength = this.MaximumHashBitLength; settings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks; foreach (AssociationTypeElement element in this.AssociationLifetimes) { Contract.Assume(element != null); settings.AssociationLifetimes.Add(element.AssociationType, element.MaximumLifetime); } return settings; }
/// <summary> /// Applies a well known set of security requirements to a default set of security settings. /// </summary> /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param> /// <remarks> /// Care should be taken to never decrease security when applying a profile. /// Profiles should only enhance security requirements to avoid being /// incompatible with each other. /// </remarks> void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) { Requires.NotNull(securitySettings, "securitySettings"); throw new System.NotImplementedException(); }
/// <summary> /// Creates a Provider's response to an incoming association request. /// </summary> /// <param name="associationStore">The association store where a new association (if created) will be stored. Must not be null.</param> /// <param name="securitySettings">The security settings on the Provider.</param> /// <returns> /// The appropriate association response that is ready to be sent back to the Relying Party. /// </returns> /// <remarks> /// <para>If an association is created, it will be automatically be added to the provided /// association store.</para> /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>. /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para> /// </remarks> internal IProtocolMessage CreateResponse(IAssociationStore<AssociationRelyingPartyType> associationStore, ProviderSecuritySettings securitySettings) { ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore"); ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); IProtocolMessage response; if (securitySettings.IsAssociationInPermittedRange(Protocol, this.AssociationType) && HmacShaAssociation.IsDHSessionCompatible(Protocol, this.AssociationType, this.SessionType)) { response = this.CreateResponseCore(); // Create and store the association if this is a successful response. var successResponse = response as AssociateSuccessfulResponse; if (successResponse != null) { Association association = successResponse.CreateAssociation(this, securitySettings); associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association); } } else { response = this.CreateUnsuccessfulResponse(securitySettings); } return response; }