1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
|
//-----------------------------------------------------------------------
// <copyright file="Channel.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace DotNetOAuth.Messaging {
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using DotNetOAuth.Messaging.Reflection;
/// <summary>
/// Manages sending direct messages to a remote party and receiving responses.
/// </summary>
internal abstract class Channel {
/// <summary>
/// The maximum allowable size for a 301 Redirect response before we send
/// a 200 OK response with a scripted form POST with the parameters instead
/// in order to ensure successfully sending a large payload to another server
/// that might have a maximum allowable size restriction on its GET request.
/// </summary>
private static int indirectMessageGetToPostThreshold = 2 * 1024; // 2KB, recommended by OpenID group
/// <summary>
/// The template for indirect messages that require form POST to forward through the user agent.
/// </summary>
/// <remarks>
/// We are intentionally using " instead of the html single quote ' below because
/// the HtmlEncode'd values that we inject will only escape the double quote, so
/// only the double-quote used around these values is safe.
/// </remarks>
private static string indirectMessageFormPostFormat = @"
<html>
<body onload=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; document.getElementById('openid_message').submit()"">
<form id=""openid_message"" action=""{0}"" method=""post"" accept-charset=""UTF-8"" enctype=""application/x-www-form-urlencoded"" onSubmit=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; return true;"">
{1}
<input id=""submit_button"" type=""submit"" value=""Continue"" />
</form>
</body>
</html>
";
/// <summary>
/// A tool that can figure out what kind of message is being received
/// so it can be deserialized.
/// </summary>
private IMessageTypeProvider messageTypeProvider;
/// <summary>
/// A list of binding elements in the order they must be applied to outgoing messages.
/// </summary>
/// <remarks>
/// Incoming messages should have the binding elements applied in reverse order.
/// </remarks>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private List<IChannelBindingElement> bindingElements = new List<IChannelBindingElement>();
/// <summary>
/// Initializes a new instance of the <see cref="Channel"/> class.
/// </summary>
/// <param name="messageTypeProvider">
/// A class prepared to analyze incoming messages and indicate what concrete
/// message types can deserialize from it.
/// </param>
/// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param>
protected Channel(IMessageTypeProvider messageTypeProvider, params IChannelBindingElement[] bindingElements) {
if (messageTypeProvider == null) {
throw new ArgumentNullException("messageTypeProvider");
}
this.messageTypeProvider = messageTypeProvider;
this.bindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
}
/// <summary>
/// Gets the binding elements used by this channel, in the order they are applied to outgoing messages.
/// </summary>
/// <remarks>
/// Incoming messages are processed by this binding elements in the reverse order.
/// </remarks>
protected internal ReadOnlyCollection<IChannelBindingElement> BindingElements {
get {
return this.bindingElements.AsReadOnly();
}
}
/// <summary>
/// Gets a tool that can figure out what kind of message is being received
/// so it can be deserialized.
/// </summary>
protected IMessageTypeProvider MessageTypeProvider {
get { return this.messageTypeProvider; }
}
/// <summary>
/// Queues an indirect message (either a request or response)
/// or direct message response for transmission to a remote party.
/// </summary>
/// <param name="message">The one-way message to send</param>
/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
internal Response Send(IProtocolMessage message) {
if (message == null) {
throw new ArgumentNullException("message");
}
this.PrepareMessageForSending(message);
Logger.DebugFormat("Sending message: {0}", message);
switch (message.Transport) {
case MessageTransport.Direct:
// This is a response to a direct message.
return this.SendDirectMessageResponse(message);
case MessageTransport.Indirect:
var directedMessage = message as IDirectedProtocolMessage;
if (directedMessage == null) {
throw new ArgumentException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.IndirectMessagesMustImplementIDirectedProtocolMessage,
typeof(IDirectedProtocolMessage).FullName),
"message");
}
if (directedMessage.Recipient == null) {
throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
}
return this.SendIndirectMessage(directedMessage);
default:
throw new ArgumentException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.UnrecognizedEnumValue,
"Transport",
message.Transport),
"message");
}
}
/// <summary>
/// Gets the protocol message embedded in the given HTTP request, if present.
/// </summary>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
/// <remarks>
/// Requires an HttpContext.Current context.
/// </remarks>
/// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
internal IProtocolMessage ReadFromRequest() {
return this.ReadFromRequest(this.GetRequestFromContext());
}
/// <summary>
/// Gets the protocol message embedded in the given HTTP request, if present.
/// </summary>
/// <typeparam name="TREQUEST">The expected type of the message to be received.</typeparam>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
/// <remarks>
/// Requires an HttpContext.Current context.
/// </remarks>
/// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
internal TREQUEST ReadFromRequest<TREQUEST>()
where TREQUEST : class, IProtocolMessage {
return this.ReadFromRequest<TREQUEST>(this.GetRequestFromContext());
}
/// <summary>
/// Gets the protocol message that may be embedded in the given HTTP request.
/// </summary>
/// <typeparam name="TREQUEST">The expected type of the message to be received.</typeparam>
/// <param name="httpRequest">The request to search for an embedded message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
/// <exception cref="ProtocolException">
/// Thrown if no message is recognized in the request
/// or an unexpected type of message is received.
/// </exception>
protected internal TREQUEST ReadFromRequest<TREQUEST>(HttpRequestInfo httpRequest)
where TREQUEST : class, IProtocolMessage {
IProtocolMessage request = this.ReadFromRequest(httpRequest);
if (request == null) {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.ExpectedMessageNotReceived,
typeof(TREQUEST)));
}
var expectedRequest = request as TREQUEST;
if (expectedRequest == null) {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.UnexpectedMessageReceived,
typeof(TREQUEST),
request.GetType()));
}
return expectedRequest;
}
/// <summary>
/// Gets the protocol message that may be embedded in the given HTTP request.
/// </summary>
/// <param name="httpRequest">The request to search for an embedded message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
protected internal IProtocolMessage ReadFromRequest(HttpRequestInfo httpRequest) {
IProtocolMessage requestMessage = this.ReadFromRequestInternal(httpRequest);
if (requestMessage != null) {
this.VerifyMessageAfterReceiving(requestMessage);
}
return requestMessage;
}
/// <summary>
/// Sends a direct message to a remote party and waits for the response.
/// </summary>
/// <typeparam name="TRESPONSE">The expected type of the message to be received.</typeparam>
/// <param name="request">The message to send.</param>
/// <returns>The remote party's response.</returns>
/// <exception cref="ProtocolException">
/// Thrown if no message is recognized in the response
/// or an unexpected type of message is received.
/// </exception>
protected internal TRESPONSE Request<TRESPONSE>(IDirectedProtocolMessage request)
where TRESPONSE : class, IProtocolMessage {
IProtocolMessage response = this.Request(request);
if (response == null) {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.ExpectedMessageNotReceived,
typeof(TRESPONSE)));
}
var expectedResponse = response as TRESPONSE;
if (expectedResponse == null) {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.UnexpectedMessageReceived,
typeof(TRESPONSE),
response.GetType()));
}
return expectedResponse;
}
/// <summary>
/// Sends a direct message to a remote party and waits for the response.
/// </summary>
/// <param name="request">The message to send.</param>
/// <returns>The remote party's response.</returns>
protected internal IProtocolMessage Request(IDirectedProtocolMessage request) {
if (request == null) {
throw new ArgumentNullException("request");
}
this.PrepareMessageForSending(request);
Logger.DebugFormat("Sending request: {0}", request);
IProtocolMessage response = this.RequestInternal(request);
if (response != null) {
Logger.DebugFormat("Received response: {0}", response);
this.VerifyMessageAfterReceiving(response);
Logger.DebugFormat("Response verified.");
}
return response;
}
/// <summary>
/// Gets the protocol message that may be in the given HTTP response stream.
/// </summary>
/// <param name="responseStream">The response that is anticipated to contain an OAuth message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
protected internal IProtocolMessage ReadFromResponse(Stream responseStream) {
IProtocolMessage message = this.ReadFromResponseInternal(responseStream);
this.VerifyMessageAfterReceiving(message);
return message;
}
/// <summary>
/// Gets the current HTTP request being processed.
/// </summary>
/// <returns>The HttpRequestInfo for the current request.</returns>
/// <remarks>
/// Requires an HttpContext.Current context.
/// </remarks>
/// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
protected virtual HttpRequestInfo GetRequestFromContext() {
if (HttpContext.Current == null) {
throw new InvalidOperationException(MessagingStrings.HttpContextRequired);
}
return new HttpRequestInfo(HttpContext.Current.Request);
}
/// <summary>
/// Gets the protocol message that may be embedded in the given HTTP request.
/// </summary>
/// <param name="request">The request to search for an embedded message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
protected virtual IProtocolMessage ReadFromRequestInternal(HttpRequestInfo request) {
if (request == null) {
throw new ArgumentNullException("request");
}
// Search Form data first, and if nothing is there search the QueryString
var fields = request.Form.ToDictionary();
if (fields.Count == 0) {
fields = request.QueryString.ToDictionary();
}
return this.Receive(fields, request.GetRecipient());
}
/// <summary>
/// Deserializes a dictionary of values into a message.
/// </summary>
/// <param name="fields">The dictionary of values that were read from an HTTP request or response.</param>
/// <param name="recipient">Information about where the message was been directed. Null for direct response messages.</param>
/// <returns>The deserialized message, or null if no message could be recognized in the provided data.</returns>
protected virtual IProtocolMessage Receive(Dictionary<string, string> fields, MessageReceivingEndpoint recipient) {
if (fields == null) {
throw new ArgumentNullException("fields");
}
Type messageType = this.MessageTypeProvider.GetRequestMessageType(fields);
// If there was no data, or we couldn't recognize it as a message, abort.
if (messageType == null) {
return null;
}
// We have a message! Assemble it.
var serializer = MessageSerializer.Get(messageType);
IProtocolMessage message = serializer.Deserialize(fields, recipient);
return message;
}
/// <summary>
/// Queues an indirect message for transmittal via the user agent.
/// </summary>
/// <param name="message">The message to send.</param>
/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
protected virtual Response SendIndirectMessage(IDirectedProtocolMessage message) {
if (message == null) {
throw new ArgumentNullException("message");
}
var serializer = MessageSerializer.Get(message.GetType());
var fields = serializer.Serialize(message);
Response response;
if (CalculateSizeOfPayload(fields) > indirectMessageGetToPostThreshold) {
response = this.CreateFormPostResponse(message, fields);
} else {
response = this.Create301RedirectResponse(message, fields);
}
return response;
}
/// <summary>
/// Encodes an HTTP response that will instruct the user agent to forward a message to
/// some remote third party using a 301 Redirect GET method.
/// </summary>
/// <param name="message">The message to forward.</param>
/// <param name="fields">The pre-serialized fields from the message.</param>
/// <returns>The encoded HTTP response.</returns>
protected virtual Response Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
if (message == null) {
throw new ArgumentNullException("message");
}
if (message.Recipient == null) {
throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
}
if (fields == null) {
throw new ArgumentNullException("fields");
}
WebHeaderCollection headers = new WebHeaderCollection();
UriBuilder builder = new UriBuilder(message.Recipient);
MessagingUtilities.AppendQueryArgs(builder, fields);
headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri);
Logger.DebugFormat("Redirecting to {0}", builder.Uri.AbsoluteUri);
Response response = new Response {
Status = HttpStatusCode.Redirect,
Headers = headers,
Body = null,
OriginalMessage = message
};
return response;
}
/// <summary>
/// Encodes an HTTP response that will instruct the user agent to forward a message to
/// some remote third party using a form POST method.
/// </summary>
/// <param name="message">The message to forward.</param>
/// <param name="fields">The pre-serialized fields from the message.</param>
/// <returns>The encoded HTTP response.</returns>
protected virtual Response CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
if (message == null) {
throw new ArgumentNullException("message");
}
if (message.Recipient == null) {
throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
}
if (fields == null) {
throw new ArgumentNullException("fields");
}
WebHeaderCollection headers = new WebHeaderCollection();
StringWriter bodyWriter = new StringWriter(CultureInfo.InvariantCulture);
StringBuilder hiddenFields = new StringBuilder();
foreach (var field in fields) {
hiddenFields.AppendFormat(
"\t<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />\r\n",
HttpUtility.HtmlEncode(field.Key),
HttpUtility.HtmlEncode(field.Value));
}
bodyWriter.WriteLine(
indirectMessageFormPostFormat,
HttpUtility.HtmlEncode(message.Recipient.AbsoluteUri),
hiddenFields);
bodyWriter.Flush();
Response response = new Response {
Status = HttpStatusCode.OK,
Headers = headers,
Body = bodyWriter.ToString(),
OriginalMessage = message
};
return response;
}
/// <summary>
/// Gets the protocol message that may be in the given HTTP response stream.
/// </summary>
/// <param name="responseStream">The response that is anticipated to contain an OAuth message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
protected abstract IProtocolMessage ReadFromResponseInternal(Stream responseStream);
/// <summary>
/// Sends a direct message to a remote party and waits for the response.
/// </summary>
/// <param name="request">The message to send.</param>
/// <returns>The remote party's response.</returns>
protected abstract IProtocolMessage RequestInternal(IDirectedProtocolMessage request);
/// <summary>
/// Queues a message for sending in the response stream where the fields
/// are sent in the response stream in querystring style.
/// </summary>
/// <param name="response">The message to send as a response.</param>
/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
/// <remarks>
/// This method implements spec V1.0 section 5.3.
/// </remarks>
protected abstract Response SendDirectMessageResponse(IProtocolMessage response);
/// <summary>
/// Prepares a message for transmit by applying signatures, nonces, etc.
/// </summary>
/// <param name="message">The message to prepare for sending.</param>
/// <remarks>
/// This method should NOT be called by derived types
/// except when sending ONE WAY request messages.
/// </remarks>
protected void PrepareMessageForSending(IProtocolMessage message) {
if (message == null) {
throw new ArgumentNullException("message");
}
MessageProtection appliedProtection = MessageProtection.None;
foreach (IChannelBindingElement bindingElement in this.bindingElements) {
if (bindingElement.PrepareMessageForSending(message)) {
appliedProtection |= bindingElement.Protection;
}
}
// Ensure that the message's protection requirements have been satisfied.
if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
throw new UnprotectedMessageException(message, appliedProtection);
}
EnsureValidMessageParts(message);
message.EnsureValidMessage();
}
/// <summary>
/// Calculates a fairly accurate estimation on the size of a message that contains
/// a given set of fields.
/// </summary>
/// <param name="fields">The fields that would be included in a message.</param>
/// <returns>The size (in bytes) of the message payload.</returns>
private static int CalculateSizeOfPayload(IDictionary<string, string> fields) {
Debug.Assert(fields != null, "fields == null");
int size = 0;
foreach (var field in fields) {
size += field.Key.Length;
size += field.Value.Length;
size += 2; // & and =
}
return size;
}
/// <summary>
/// Ensures a consistent and secure set of binding elements and
/// sorts them as necessary for a valid sequence of operations.
/// </summary>
/// <param name="elements">The binding elements provided to the channel.</param>
/// <returns>The properly ordered list of elements.</returns>
/// <exception cref="ProtocolException">Thrown when the binding elements are incomplete or inconsistent with each other.</exception>
private static IEnumerable<IChannelBindingElement> ValidateAndPrepareBindingElements(IEnumerable<IChannelBindingElement> elements) {
if (elements == null) {
return new IChannelBindingElement[0];
}
if (elements.Contains(null)) {
throw new ArgumentException(MessagingStrings.SequenceContainsNullElement, "elements");
}
// Filter the elements between the mere transforming ones and the protection ones.
var transformationElements = new List<IChannelBindingElement>(
elements.Where(element => element.Protection == MessageProtection.None));
var protectionElements = new List<IChannelBindingElement>(
elements.Where(element => element.Protection != MessageProtection.None));
bool wasLastProtectionPresent = true;
foreach (MessageProtection protectionKind in Enum.GetValues(typeof(MessageProtection))) {
if (protectionKind == MessageProtection.None) {
continue;
}
int countProtectionsOfThisKind = protectionElements.Count(element => (element.Protection & protectionKind) == protectionKind);
// Each protection binding element is backed by the presence of its dependent protection(s).
if (countProtectionsOfThisKind > 0 && !wasLastProtectionPresent) {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.RequiredProtectionMissing,
protectionKind));
}
// At most one binding element for each protection type.
if (countProtectionsOfThisKind > 1) {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.TooManyBindingsOfferingSameProtection,
protectionKind,
countProtectionsOfThisKind));
}
wasLastProtectionPresent = countProtectionsOfThisKind > 0;
}
// Put the binding elements in order so they are correctly applied to outgoing messages.
// Start with the transforming (non-protecting) binding elements first and preserve their original order.
var orderedList = new List<IChannelBindingElement>(transformationElements);
// Now sort the protection binding elements among themselves and add them to the list.
orderedList.AddRange(protectionElements.OrderBy(element => element.Protection, BindingElementOutgoingMessageApplicationOrder));
return orderedList;
}
/// <summary>
/// Puts binding elements in their correct outgoing message processing order.
/// </summary>
/// <param name="protection1">The first protection type to compare.</param>
/// <param name="protection2">The second protection type to compare.</param>
/// <returns>
/// -1 if <paramref name="element1"/> should be applied to an outgoing message before <paramref name="element2"/>.
/// 1 if <paramref name="element2"/> should be applied to an outgoing message before <paramref name="element1"/>.
/// 0 if it doesn't matter.
/// </returns>
private static int BindingElementOutgoingMessageApplicationOrder(MessageProtection protection1, MessageProtection protection2) {
Debug.Assert(protection1 != MessageProtection.None || protection2 != MessageProtection.None, "This comparison function should only be used to compare protection binding elements. Otherwise we change the order of user-defined message transformations.");
// Now put the protection ones in the right order.
return -((int)protection1).CompareTo((int)protection2); // descending flag ordinal order
}
/// <summary>
/// Verifies that all required message parts are initialized to values
/// prior to sending the message to a remote party.
/// </summary>
/// <param name="message">The message to verify.</param>
/// <exception cref="ProtocolException">
/// Thrown when any required message part does not have a value.
/// </exception>
private static void EnsureValidMessageParts(IProtocolMessage message) {
Debug.Assert(message != null, "message == null");
MessageDictionary dictionary = new MessageDictionary(message);
MessageDescription description = MessageDescription.Get(message.GetType());
description.EnsureRequiredMessagePartsArePresent(dictionary.Keys);
}
/// <summary>
/// Verifies the integrity and applicability of an incoming message.
/// </summary>
/// <param name="message">The message just received.</param>
/// <exception cref="ProtocolException">
/// Thrown when the message is somehow invalid.
/// This can be due to tampering, replay attack or expiration, among other things.
/// </exception>
private void VerifyMessageAfterReceiving(IProtocolMessage message) {
Debug.Assert(message != null, "message == null");
MessageProtection appliedProtection = MessageProtection.None;
foreach (IChannelBindingElement bindingElement in this.bindingElements.Reverse<IChannelBindingElement>()) {
if (bindingElement.PrepareMessageForReceiving(message)) {
appliedProtection |= bindingElement.Protection;
}
}
// Ensure that the message's protection requirements have been satisfied.
if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
throw new UnprotectedMessageException(message, appliedProtection);
}
// We do NOT verify that all required message parts are present here... the
// message deserializer did for us. It would be too late to do it here since
// they might look initialized by the time we have an IProtocolMessage instance.
message.EnsureValidMessage();
}
}
}
|