Added replay detection tests.
[dotnetoauth.git] / src / DotNetOpenAuth.Test / OpenId / AuthenticationTests.cs
blobfb62b66b12a748f480c69691278360f7a44001d7
1 //-----------------------------------------------------------------------
2 // <copyright file="AuthenticationTests.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth.Test.OpenId {
8 using System;
9 using System.Collections.Generic;
10 using System.Linq;
11 using System.Text;
12 using DotNetOpenAuth.Messaging;
13 using DotNetOpenAuth.Messaging.Bindings;
14 using DotNetOpenAuth.OpenId;
15 using DotNetOpenAuth.OpenId.ChannelElements;
16 using DotNetOpenAuth.OpenId.Messages;
17 using DotNetOpenAuth.Test.Mocks;
18 using Microsoft.VisualStudio.TestTools.UnitTesting;
20 [TestClass]
21 public class AuthenticationTests : OpenIdTestBase {
22 [TestInitialize]
23 public override void SetUp() {
24 base.SetUp();
27 [TestMethod]
28 public void SharedAssociationPositive() {
29 this.ParameterizedPositiveAuthenticationTest(true, true, false);
32 /// <summary>
33 /// Verifies that a shared association protects against tampering.
34 /// </summary>
35 [TestMethod]
36 public void SharedAssociationTampered() {
37 this.ParameterizedPositiveAuthenticationTest(true, true, true);
40 [TestMethod]
41 public void SharedAssociationNegative() {
42 this.ParameterizedPositiveAuthenticationTest(true, false, false);
45 [TestMethod]
46 public void PrivateAssociationPositive() {
47 this.ParameterizedPositiveAuthenticationTest(false, true, false);
50 /// <summary>
51 /// Verifies that a private association protects against tampering.
52 /// </summary>
53 [TestMethod]
54 public void PrivateAssociationTampered() {
55 this.ParameterizedPositiveAuthenticationTest(false, true, true);
58 [TestMethod]
59 public void NoAssociationNegative() {
60 this.ParameterizedPositiveAuthenticationTest(false, false, false);
63 private void ParameterizedPositiveAuthenticationTest(bool sharedAssociation, bool positive, bool tamper) {
64 foreach (Protocol protocol in Protocol.AllPracticalVersions) {
65 this.ParameterizedPositiveAuthenticationTest(protocol, sharedAssociation, positive, tamper);
69 private void ParameterizedPositiveAuthenticationTest(Protocol protocol, bool sharedAssociation, bool positive, bool tamper) {
70 ErrorUtilities.VerifyArgument(positive || !tamper, "Cannot tamper with a negative response.");
71 Uri userSetupUrl = protocol.Version.Major < 2 ? new Uri("http://usersetupurl") : null;
72 Association association = sharedAssociation ? HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart) : null;
73 var coordinator = new OpenIdCoordinator(
74 rp => {
75 var request = new CheckIdRequest(protocol.Version, ProviderUri, true);
77 if (association != null) {
78 rp.AssociationStore.StoreAssociation(ProviderUri, association);
79 request.AssociationHandle = association.Handle;
82 request.ClaimedIdentifier = "http://claimedid";
83 request.LocalIdentifier = "http://localid";
84 request.ReturnTo = RPUri;
85 rp.Channel.Send(request);
86 if (positive) {
87 if (tamper) {
88 try {
89 rp.Channel.ReadFromRequest<PositiveAssertionResponse>();
90 Assert.Fail("Expected exception {0} not thrown.", typeof(InvalidSignatureException).Name);
91 } catch (InvalidSignatureException) {
92 TestLogger.InfoFormat("Caught expected {0} exception after tampering with signed data.", typeof(InvalidSignatureException).Name);
94 } else {
95 var response = rp.Channel.ReadFromRequest<PositiveAssertionResponse>();
96 Assert.IsNotNull(response);
97 Assert.AreEqual(request.ClaimedIdentifier, response.ClaimedIdentifier);
98 Assert.AreEqual(request.LocalIdentifier, response.LocalIdentifier);
99 Assert.AreEqual(request.ReturnTo, response.ReturnTo);
101 // Attempt to replay the message and verify that it fails.
102 // Because in various scenarios and protocol versions different components
103 // notice the replay, we can get one of two exceptions thrown.
104 // When the OP notices the replay we get a generic InvalidSignatureException.
105 // When the RP notices the replay we get a specific ReplayMessageException.
106 Type expectedExceptionType = sharedAssociation || protocol.Version.Major < 2 ? typeof(ReplayedMessageException) : typeof(InvalidSignatureException);
107 try {
108 CoordinatingChannel channel = (CoordinatingChannel)rp.Channel;
109 channel.Replay(response);
110 Assert.Fail("Expected exception {0} was not thrown.", expectedExceptionType.Name);
111 } catch (ProtocolException ex) {
112 Assert.IsInstanceOfType(ex, expectedExceptionType);
115 } else {
116 var response = rp.Channel.ReadFromRequest<NegativeAssertionResponse>();
117 Assert.IsNotNull(response);
118 Assert.AreEqual(userSetupUrl, response.UserSetupUrl);
121 op => {
122 if (association != null) {
123 op.AssociationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association);
126 var request = op.Channel.ReadFromRequest<CheckIdRequest>();
127 Assert.IsNotNull(request);
128 IProtocolMessage response;
129 if (positive) {
130 response = new PositiveAssertionResponse(request);
131 } else {
132 response = new NegativeAssertionResponse(request) { UserSetupUrl = userSetupUrl };
134 op.Channel.Send(response);
136 if (positive && !sharedAssociation) {
137 var checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>();
138 var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest);
139 checkauthResponse.IsValid = checkauthRequest.IsValid;
140 op.Channel.Send(checkauthResponse);
142 if (!tamper) {
143 // Respond to the replay attack.
144 checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>();
145 checkauthResponse = new CheckAuthenticationResponse(checkauthRequest);
146 checkauthResponse.IsValid = checkauthRequest.IsValid;
147 op.Channel.Send(checkauthResponse);
151 if (tamper) {
152 coordinator.IncomingMessageFilter = message => {
153 var assertion = message as PositiveAssertionResponse;
154 if (assertion != null) {
155 // Alter the Local Identifier between the Provider and the Relying Party.
156 // If the signature binding element does its job, this should cause the RP
157 // to throw.
158 assertion.LocalIdentifier = "http://victim";
162 coordinator.Run();