ah.c
Go to the documentation of this file.
1 /**
2  * @file ah.c
3  * @brief AH (IP Authentication Header)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2022-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneIPSEC Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.6
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL AH_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ipsec/ipsec.h"
36 #include "ipsec/ipsec_inbound.h"
37 #include "ipsec/ipsec_outbound.h"
39 #include "ipsec/ipsec_misc.h"
40 #include "ah/ah.h"
41 #include "core/tcp_fsm.h"
42 #include "core/raw_socket.h"
43 #include "ipv4/icmp.h"
44 #include "debug.h"
45 
46 //Check IPsec library configuration
47 #if (AH_SUPPORT == ENABLED)
48 
49 
50 /**
51  * @brief Protect an outbound IPv4 packet using AH
52  * @param[in] context Pointer to the IPsec context
53  * @param[in] sa Pointer to the security association
54  * @param[in] interface Underlying network interface
55  * @param[in] pseudoHeader IPv4 pseudo header
56  * @param[in] fragId Fragment identification field
57  * @param[in] buffer Multi-part buffer containing the payload
58  * @param[in] offset Offset to the first byte of the payload
59  * @param[in] ancillary Additional options passed to the stack along with
60  * the packet
61  * @return Error code
62  **/
63 
65  NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader,
66  uint16_t fragId, NetBuffer *buffer, size_t offset,
67  NetTxAncillary *ancillary)
68 {
69  error_t error;
70  size_t length;
71  AhHeader *ahHeader;
72  Ipv4Header ipv4Header;
73  Ipv4PseudoHeader pseudoHeader2;
74 
75  //Retrieve the length of the data
76  length = netBufferGetLength(buffer) - offset;
77 
78  //Sanity check
79  if(offset < (sizeof(AhHeader) + sa->icvLen))
80  return ERROR_FAILURE;
81 
82  //Make room for the AH header
83  offset -= sizeof(AhHeader) + sa->icvLen;
84  length += sizeof(AhHeader) + sa->icvLen;
85 
86  //The AH header is inserted after the IP header and before a next layer
87  //protocol
88  ahHeader = netBufferAt(buffer, offset, 0);
89 
90  //The sender increments the sequence number counter for this SA and inserts
91  //the low-order 32 bits of the value into the Sequence Number field (refer
92  //to RFC 4302, section 3.3.2)
93  sa->seqNum++;
94 
95  //Format AH header
96  ahHeader->nextHeader = pseudoHeader->protocol;
97  ahHeader->payloadLen = (sizeof(AhHeader) + sa->icvLen) / 4 - 2;
98  ahHeader->reserved = 0;
99  ahHeader->spi = htonl(sa->spi);
100  ahHeader->seqNum = htonl(sa->seqNum);
101 
102  //The Integrity Check Value field is also set to zero in preparation for
103  //this computation (refer to RFC 4302, section 3.3.3.1)
104  osMemset(ahHeader->icv, 0, sa->icvLen);
105 
106  //Format outer IPv4 header
107  osMemset(&ipv4Header, 0, sizeof(Ipv4Header));
108  ipv4Header.version = IPV4_VERSION;
109  ipv4Header.headerLength = 5;
110  ipv4Header.typeOfService = 0;
111  ipv4Header.totalLength = htons(length + sizeof(Ipv4Header));
112  ipv4Header.identification = htons(fragId);
113  ipv4Header.fragmentOffset = 0;
114  ipv4Header.timeToLive = 0;
115  ipv4Header.protocol = IPV4_PROTOCOL_AH;
116  ipv4Header.headerChecksum = 0;
117  ipv4Header.srcAddr = pseudoHeader->srcAddr;
118  ipv4Header.destAddr = pseudoHeader->destAddr;
119 
120  //Compute ICV value
121  error = ahGenerateIcv(context, sa, &ipv4Header, ahHeader, buffer,
122  offset + sizeof(AhHeader) + sa->icvLen);
123  //Any error to report?
124  if(error)
125  return error;
126 
127  //Fix the Next Layer Protocol value
128  pseudoHeader2 = *pseudoHeader;
129  pseudoHeader2.protocol = IPV4_PROTOCOL_AH;
130 
131  //Debug message
132  TRACE_INFO("AH Header:\r\n");
133  //Dump AH header contents for debugging purpose
134  ahDumpHeader(ahHeader);
135 
136  //Send AH packet
137  error = ipsecSendIpv4Packet(interface, &pseudoHeader2, fragId, buffer,
138  offset, ancillary);
139 
140  //Return status code
141  return error;
142 }
143 
144 
145 /**
146  * @brief Process AH protected packet
147  * @param[in] interface Underlying network interface
148  * @param[in] ipv4Header Pointer to the IPv4 header
149  * @param[in] buffer Multi-part buffer containing the AH protected packet
150  * @param[in] offset Offset to the first byte of the AH header
151  * @param[in] ancillary Additional options passed to the stack along with
152  * the packet
153  * @return Error code
154  **/
155 
157  const Ipv4Header *ipv4Header, const NetBuffer *buffer, size_t offset,
158  NetRxAncillary *ancillary)
159 {
160  error_t error;
161  size_t n;
162  size_t length;
163  uint64_t seq;
164  IpsecContext *context;
165  IpsecSadEntry *sa;
166  AhHeader *ahHeader;
167  IpsecSelector selector;
168  IpPseudoHeader pseudoHeader;
169 
170  //Point to the IPsec context
171  context = interface->netContext->ipsecContext;
172  //Sanity check
173  if(context == NULL)
174  return ERROR_FAILURE;
175 
176  //Retrieve the length of the payload
177  length = netBufferGetLength(buffer) - offset;
178 
179  //Malformed packet?
180  if(length < sizeof(AhHeader))
181  return ERROR_INVALID_HEADER;
182 
183  //Point to the AH header
184  ahHeader = netBufferAt(buffer, offset, sizeof(AhHeader));
185  //Malformed AH packet?
186  if(ahHeader == NULL)
187  return ERROR_FAILURE;
188 
189  //If a packet offered to AH for processing appears to be an IP fragment,
190  //the receiver must discard the packet (refer to RFC 4302, section 3.4.1)
191  if((ntohs(ipv4Header->fragmentOffset) & IPV4_OFFSET_MASK) != 0 ||
192  (ntohs(ipv4Header->fragmentOffset) & IPV4_FLAG_MF) != 0)
193  {
194  return ERROR_INVALID_HEADER;
195  }
196 
197  //Upon receipt of a packet containing an IP Authentication Header, the
198  //receiver determines the appropriate (unidirectional) SA via lookup in
199  //the SAD (refer to RFC 4302, section 3.4.2)
201  ntohl(ahHeader->spi));
202 
203  //If no valid Security Association exists for this packet the receiver
204  //must discard the packet. This is an auditable event
205  if(sa == NULL)
206  {
207  //Debug message
208  TRACE_WARNING("AH: No matching SA found!\r\n");
209  //Report an error
210  return ERROR_POLICY_FAILURE;
211  }
212 
213  //The Payload Length field specifies the length of AH header in 32-bit
214  //words (4-byte units), minus 2
215  n = (ahHeader->payloadLen + 2) * sizeof(uint32_t);
216 
217  //Check the length of the AH header
218  if(n > length || n != (sizeof(AhHeader) + sa->icvLen))
219  return ERROR_INVALID_HEADER;
220 
221  //Debug message
222  TRACE_INFO("Parsing AH header...\r\n");
223  //Dump AH header contents for debugging purpose
224  ahDumpHeader(ahHeader);
225 
226  //Because only the low-order 32 bits are transmitted with the packet, the
227  //receiver must deduce and track the sequence number subspace into which
228  //each packet falls
229  seq = ipsecGetSeqNum(sa, ntohl(ahHeader->seqNum));
230 
231  //For each received packet, the receiver must verify that the packet
232  //contains a Sequence Number that does not duplicate the Sequence Number of
233  //any other packets received during the life of this SA. This should be the
234  //first AH check applied to a packet after it has been matched to an SA, to
235  //speed rejection of duplicate packets (refer to RFC 4302, section 3.4.3)
236  error = ipsecCheckReplayWindow(sa, seq);
237 
238  //Duplicate packets are rejected
239  if(error)
240  {
241  //Debug message
242  TRACE_WARNING("AH: Invalid sequence number!\r\n");
243  //Report an error
245  }
246 
247  //If the received packet falls within the window and is not a duplicate, or
248  //if the packet is to the right of the window, then the receiver proceeds to
249  //ICV verification
250  error = ahVerifyIcv(context, sa, ipv4Header, ahHeader, buffer,
251  offset + sizeof(AhHeader) + sa->icvLen);
252 
253  //If the ICV validation fails, the receiver must discard the received IP
254  //datagram as invalid. This is is an auditable event (refer to RFC 4302,
255  //section 3.4.3)
256  if(error)
257  {
258  //Debug message
259  TRACE_WARNING("AH: ICV validation failed!\r\n");
260  //Report an error
262  }
263 
264  //The receive window is updated only if the ICV verification succeeds
265  ipsecUpdateReplayWindow(sa, seq);
266 
267  //Point to the payload
268  offset += n;
269  length -= n;
270 
271  //Match the packet against the inbound selectors identified by the SAD entry
272  //to verify that the received packet is appropriate for the SA via which it
273  //was received (refer to RFC 4301, section 5.2)
274  error = ipsecGetInboundIpv4PacketSelector(ipv4Header, ahHeader->nextHeader,
275  buffer, offset, &selector);
276  //Any error to report?
277  if(error)
278  return error;
279 
280  //If an IPsec system receives an inbound packet on an SA and the packet's
281  //header fields are not consistent with the selectors for the SA, it must
282  //discard the packet. This is an auditable event
283  if(!ipsecIsSubsetSelector(&selector, &sa->selector))
284  return ERROR_POLICY_FAILURE;
285 
286  //Form the IPv4 pseudo header
287  pseudoHeader.length = sizeof(Ipv4PseudoHeader);
288  pseudoHeader.ipv4Data.srcAddr = ipv4Header->srcAddr;
289  pseudoHeader.ipv4Data.destAddr = ipv4Header->destAddr;
290  pseudoHeader.ipv4Data.reserved = 0;
291  pseudoHeader.ipv4Data.protocol = ahHeader->nextHeader;
292  pseudoHeader.ipv4Data.length = htons(length);
293 
294  //If the computed and received ICVs match, then the datagram is valid, and
295  //it is accepted (refer to RFC 4302, section 3.4.4)
296  error = ipv4DispatchDatagram(interface, &pseudoHeader, buffer, offset,
297  ancillary);
298 
299  //Return status code
300  return error;
301 }
302 
303 
304 /**
305  * @brief ICV generation
306  * @param[in] context Pointer to the IPsec context
307  * @param[in] sa Pointer to the SA
308  * @param[in] ipv4Header Pointer to the IPv4 header
309  * @param[in,out] ahHeader Pointer to the AH header
310  * @param[in] buffer Multi-part buffer containing the payload
311  * @param[in] offset Offset to the first byte of the payload
312  * @return Error code
313  **/
314 
316  const Ipv4Header *ipv4Header, AhHeader *ahHeader, const NetBuffer *buffer,
317  size_t offset)
318 {
319  error_t error;
320  uint_t i;
321  size_t n;
322  uint8_t *p;
323 
324 #if (AH_CMAC_SUPPORT == ENABLED)
325  //CMAC integrity algorithm?
326  if(sa->authMacAlgo == MAC_ALGO_CMAC)
327  {
328  CmacContext *cmacContext;
329 
330  //Point to the CMAC context
331  cmacContext = &context->macContext.cmacContext;
332 
333  //The SAD entry specifies the algorithm employed for ICV computation
334  error = cmacInit(cmacContext, sa->authCipherAlgo, sa->authKey,
335  sa->authKeyLen);
336 
337  //Check status code
338  if(!error)
339  {
340  //Compute CMAC over the IP or extension header fields before the AH
341  //header that are either immutable in transit or that are predictable
342  //in value upon arrival at the endpoint for the AH SA
343  cmacUpdate(cmacContext, ipv4Header, sizeof(Ipv4Header));
344 
345  //Compute CMAC over the Next Header, Payload Length, Reserved, SPI,
346  //Sequence Number (low-order 32 bits) fields, and the ICV (which is
347  //set to zero for this computation)
348  cmacUpdate(cmacContext, ahHeader, sizeof(AhHeader) + sa->icvLen);
349 
350  //Everything after AH is assumed to be immutable in transit
351  for(i = 0; i < buffer->chunkCount; i++)
352  {
353  //Is there any data to process from the current chunk?
354  if(offset < buffer->chunk[i].length)
355  {
356  //Point to the first byte to be processed
357  p = (uint8_t *) buffer->chunk[i].address + offset;
358  //Compute the number of bytes to process at a time
359  n = buffer->chunk[i].length - offset;
360 
361  //Update CMAC calculation
362  cmacUpdate(cmacContext, p, n);
363 
364  //Process the next block from the start
365  offset = 0;
366  }
367  else
368  {
369  //Skip the current chunk
370  offset -= buffer->chunk[i].length;
371  }
372  }
373 
374  //Extended sequence numbers?
375  if(sa->esn)
376  {
377  //If the ESN option is elected for an SA, then the high-order 32
378  //bits of the ESN must be included in the ICV computation
379  uint32_t seqh = htonl(sa->seqNum >> 32);
380 
381  //For purposes of ICV computation, these bits are appended
382  //(implicitly) immediately after the end of the payload
383  cmacUpdate(cmacContext, (uint8_t *) &seqh, 4);
384  }
385 
386  //Finalize CMAC calculation
387  cmacFinal(cmacContext, ahHeader->icv, sa->icvLen);
388  }
389  }
390  else
391 #endif
392 #if (AH_HMAC_SUPPORT == ENABLED)
393  //HMAC integrity algorithm?
394  if(sa->authMacAlgo == MAC_ALGO_HMAC)
395  {
396  HmacContext *hmacContext;
397 
398  //Point to the HMAC context
399  hmacContext = &context->macContext.hmacContext;
400 
401  //The SAD entry specifies the algorithm employed for ICV computation
402  error = hmacInit(hmacContext, sa->authHashAlgo, sa->authKey,
403  sa->authKeyLen);
404 
405  //Check status code
406  if(!error)
407  {
408  //Compute HMAC over the IP or extension header fields before the AH
409  //header that are either immutable in transit or that are predictable
410  //in value upon arrival at the endpoint for the AH SA
411  hmacUpdate(hmacContext, ipv4Header, sizeof(Ipv4Header));
412 
413  //Compute HMAC over the Next Header, Payload Length, Reserved, SPI,
414  //Sequence Number (low-order 32 bits) fields, and the ICV (which is
415  //set to zero for this computation)
416  hmacUpdate(hmacContext, ahHeader, sizeof(AhHeader) + sa->icvLen);
417 
418  //Everything after AH is assumed to be immutable in transit
419  for(i = 0; i < buffer->chunkCount; i++)
420  {
421  //Is there any data to process from the current chunk?
422  if(offset < buffer->chunk[i].length)
423  {
424  //Point to the first byte to be processed
425  p = (uint8_t *) buffer->chunk[i].address + offset;
426  //Compute the number of bytes to process at a time
427  n = buffer->chunk[i].length - offset;
428 
429  //Update HMAC calculation
430  hmacUpdate(hmacContext, p, n);
431 
432  //Process the next block from the start
433  offset = 0;
434  }
435  else
436  {
437  //Skip the current chunk
438  offset -= buffer->chunk[i].length;
439  }
440  }
441 
442  //Extended sequence numbers?
443  if(sa->esn)
444  {
445  //If the ESN option is elected for an SA, then the high-order 32
446  //bits of the ESN must be included in the ICV computation
447  uint32_t seqh = htonl(sa->seqNum >> 32);
448 
449  //For purposes of ICV computation, these bits are appended
450  //(implicitly) immediately after the end of the payload
451  hmacUpdate(hmacContext, (uint8_t *) &seqh, 4);
452  }
453 
454  //Finalize HMAC calculation
455  hmacFinal(hmacContext, NULL);
456  //The output of the HMAC can be truncated
457  osMemcpy(ahHeader->icv, hmacContext->digest, sa->icvLen);
458  }
459  }
460  else
461 #endif
462  //Unknown integrity algorithm?
463  {
464  //Report an error
465  error = ERROR_FAILURE;
466  }
467 
468  //Return status code
469  return error;
470 }
471 
472 
473 /**
474  * @brief ICV verification
475  * @param[in] context Pointer to the IPsec context
476  * @param[in] sa Pointer to the SA
477  * @param[in] ipv4Header Pointer to the IPv4 header
478  * @param[in] ahHeader Pointer to the AH header
479  * @param[in] buffer Multi-part buffer containing the payload
480  * @param[in] offset Offset to the first byte of the payload
481  * @return Error code
482  **/
483 
485  const Ipv4Header *ipv4Header, const AhHeader *ahHeader,
486  const NetBuffer *buffer, size_t offset)
487 {
488  error_t error;
489  uint8_t mask;
490  uint_t i;
491  size_t n;
492  uint8_t *p;
493  Ipv4Header *ipv4Header2;
494  AhHeader *ahHeader2;
495  uint8_t temp[IPV4_MAX_HEADER_LENGTH];
496  uint8_t checksum[AH_MAX_DIGEST_SIZE];
497 
498  //Calculate the length of the IPv4 header
499  n = ipv4Header->headerLength * 4;
500  //Copy the IPv4 header
501  osMemcpy(temp, ipv4Header, n);
502  //Point to the IPv4 header
503  ipv4Header2 = (Ipv4Header *) temp;
504 
505  //If a field may be modified during transit, the value of the field is set
506  //to zero for purposes of the ICV computation
507  ipv4Header2->typeOfService = 0;
508  ipv4Header2->fragmentOffset = 0;
509  ipv4Header2->timeToLive = 0;
510  ipv4Header2->headerChecksum = 0;
511 
512  //Mutable options are zeroed before performing the ICV calculation
513  ahProcessMutableIpv4Options(ipv4Header2);
514 
515 #if (AH_CMAC_SUPPORT == ENABLED)
516  //CMAC integrity algorithm?
517  if(sa->authMacAlgo == MAC_ALGO_CMAC)
518  {
519  CmacContext *cmacContext;
520 
521  //Point to the CMAC context
522  cmacContext = &context->macContext.cmacContext;
523 
524  //The SAD entry specifies the algorithm employed for ICV computation,
525  //and indicates the key required to validate the ICV
526  error = cmacInit(cmacContext, sa->authCipherAlgo, sa->authKey,
527  sa->authKeyLen);
528 
529  //Check status code
530  if(!error)
531  {
532  //Compute CMAC over the IP or extension header fields before the AH
533  //header that are either immutable in transit or that are predictable
534  //in value upon arrival at the endpoint for the AH SA
535  cmacUpdate(cmacContext, temp, n);
536 
537  //The Payload Length field specifies the length of AH header in 32-bit
538  //words (4-byte units), minus 2
539  n = (ahHeader->payloadLen + 2) * sizeof(uint32_t);
540 
541  //Copy the AH header
542  osMemcpy(temp, ahHeader, n);
543  //Point to the AH header
544  ahHeader2 = (AhHeader *) temp;
545 
546  //The Integrity Check Value field is also set to zero in preparation
547  //for this computation (refer to RFC 4302, section 3.3.3.1)
548  osMemset(ahHeader2->icv, 0, sa->icvLen);
549 
550  //Compute CMAC over the Next Header, Payload Length, Reserved, SPI,
551  //Sequence Number (low-order 32 bits) fields, and the ICV (which is
552  //set to zero for this computation)
553  cmacUpdate(cmacContext, temp, n);
554 
555  //Everything after AH is assumed to be immutable in transit
556  for(i = 0; i < buffer->chunkCount; i++)
557  {
558  //Is there any data to process from the current chunk?
559  if(offset < buffer->chunk[i].length)
560  {
561  //Point to the first byte to be processed
562  p = (uint8_t *) buffer->chunk[i].address + offset;
563  //Compute the number of bytes to process at a time
564  n = buffer->chunk[i].length - offset;
565 
566  //Update CMAC calculation
567  cmacUpdate(cmacContext, p, n);
568 
569  //Process the next block from the start
570  offset = 0;
571  }
572  else
573  {
574  //Skip the current chunk
575  offset -= buffer->chunk[i].length;
576  }
577  }
578 
579  //Extended sequence numbers?
580  if(sa->esn)
581  {
582  //If the ESN option is elected for an SA, then the high-order 32
583  //bits of the ESN must be included in the ICV computation
584  uint32_t seqh = ipsecGetSeqNum(sa, ntohl(ahHeader->seqNum)) >> 32;
585 
586  //Convert the 32-bit value to network byte order
587  seqh = htonl(seqh);
588 
589  //For purposes of ICV computation, these bits are appended
590  //(implicitly) immediately after the end of the payload
591  cmacUpdate(cmacContext, (uint8_t *) &seqh, 4);
592  }
593 
594  //Finalize CMAC computation
595  cmacFinal(cmacContext, checksum, sa->icvLen);
596  }
597  }
598  else
599 #endif
600 #if (AH_HMAC_SUPPORT == ENABLED)
601  //HMAC integrity algorithm?
602  if(sa->authMacAlgo == MAC_ALGO_HMAC)
603  {
604  HmacContext *hmacContext;
605 
606  //Point to the HMAC context
607  hmacContext = &context->macContext.hmacContext;
608 
609  //The SAD entry specifies the algorithm employed for ICV computation,
610  //and indicates the key required to validate the ICV
611  error = hmacInit(hmacContext, sa->authHashAlgo, sa->authKey,
612  sa->authKeyLen);
613 
614  //Check status code
615  if(!error)
616  {
617  //Compute HMAC over the IP or extension header fields before the AH
618  //header that are either immutable in transit or that are predictable
619  //in value upon arrival at the endpoint for the AH SA
620  hmacUpdate(hmacContext, temp, n);
621 
622  //The Payload Length field specifies the length of AH header in 32-bit
623  //words (4-byte units), minus 2
624  n = (ahHeader->payloadLen + 2) * 4;
625 
626  //Copy the AH header
627  osMemcpy(temp, ahHeader, n);
628  //Point to the AH header
629  ahHeader2 = (AhHeader *) temp;
630 
631  //The Integrity Check Value field is also set to zero in preparation
632  //for this computation (refer to RFC 4302, section 3.3.3.1)
633  osMemset(ahHeader2->icv, 0, sa->icvLen);
634 
635  //Compute HMAC over the Next Header, Payload Length, Reserved, SPI,
636  //Sequence Number (low-order 32 bits) fields, and the ICV (which is
637  //set to zero for this computation)
638  hmacUpdate(hmacContext, temp, n);
639 
640  //Everything after AH is assumed to be immutable in transit
641  for(i = 0; i < buffer->chunkCount; i++)
642  {
643  //Is there any data to process from the current chunk?
644  if(offset < buffer->chunk[i].length)
645  {
646  //Point to the first byte to be processed
647  p = (uint8_t *) buffer->chunk[i].address + offset;
648  //Compute the number of bytes to process at a time
649  n = buffer->chunk[i].length - offset;
650 
651  //Update HMAC calculation
652  hmacUpdate(hmacContext, p, n);
653 
654  //Process the next block from the start
655  offset = 0;
656  }
657  else
658  {
659  //Skip the current chunk
660  offset -= buffer->chunk[i].length;
661  }
662  }
663 
664  //Extended sequence numbers?
665  if(sa->esn)
666  {
667  //If the ESN option is elected for an SA, then the high-order 32
668  //bits of the ESN must be included in the ICV computation
669  uint32_t seqh = ipsecGetSeqNum(sa, ntohl(ahHeader->seqNum)) >> 32;
670 
671  //Convert the 32-bit value to network byte order
672  seqh = htonl(seqh);
673 
674  //For purposes of ICV computation, these bits are appended
675  //(implicitly) immediately after the end of the payload
676  hmacUpdate(hmacContext, (uint8_t *) &seqh, 4);
677  }
678 
679  //Finalize HMAC computation
680  hmacFinal(hmacContext, checksum);
681  }
682  }
683  else
684 #endif
685  //Unknown integrity algorithm?
686  {
687  //Report an error
688  error = ERROR_INVALID_MAC;
689  }
690 
691  //Check status code
692  if(!error)
693  {
694  //Debug message
695  TRACE_DEBUG_ARRAY("Calculated ICV = ", checksum, sa->icvLen);
696 
697  //The calculated checksum is bitwise compared to the received ICV
698  for(mask = 0, i = 0; i < sa->icvLen; i++)
699  {
700  mask |= checksum[i] ^ ahHeader->icv[i];
701  }
702 
703  //If the ICV validation fails, the receiver must discard the received IP
704  //datagram as invalid. This is is an auditable event (refer to RFC 4302,
705  //section 3.4.3)
706  error = (mask == 0) ? NO_ERROR : ERROR_INVALID_MAC;
707  }
708 
709  //Return status code
710  return error;
711 }
712 
713 
714 /**
715  * @brief Zeroize mutable IPv4 options
716  * @param[in] header Pointer to the IPv4 header
717  **/
718 
720 {
721  size_t i;
722  size_t n;
723  size_t length;
724  Ipv4Option *option;
725 
726  //Compute the length of the options field
727  length = (header->headerLength * 4) - sizeof(TcpHeader);
728 
729  //Point to the very first option
730  i = 0;
731 
732  //Loop through the list of options
733  while(i < length)
734  {
735  //Point to the current option
736  option = (Ipv4Option *) (header->options + i);
737 
738  //Check option type
739  if(option->type == IPV4_OPTION_EEOL)
740  {
741  //This option code indicates the end of the option list
742  break;
743  }
744  else if(option->type == IPV4_OPTION_NOP)
745  {
746  //This option consists of a single octet
747  i++;
748  }
749  else
750  {
751  //Malformed option?
752  if((i + 1) >= length)
753  {
754  break;
755  }
756 
757  //The option code is followed by a one-byte length field
758  n = option->length;
759 
760  //Check the length of the option
761  if(n < sizeof(Ipv4Option) || (i + n) > length)
762  {
763  break;
764  }
765 
766  //Mutable option?
767  if(option->type != IPV4_OPTION_SEC &&
768  option->type != IPV4_OPTION_ESEC &&
769  option->type != IPV4_OPTION_CIPSO &&
770  option->type != IPV4_OPTION_RTRALT &&
771  option->type != IPV4_OPTION_SDB)
772  {
773  //The entire option is zeroed before performing the ICV calculation
774  osMemset(option, 0, n);
775  }
776 
777  //Jump to the next option
778  i += n;
779  }
780  }
781 }
782 
783 
784 /**
785  * @brief Dump AH header for debugging purpose
786  * @param[in] ahHeader Pointer to the AH header
787  **/
788 
789 void ahDumpHeader(const AhHeader *ahHeader)
790 {
791  size_t n;
792 
793  //The Payload Length field specifies the length of AH header in 32-bit
794  //words (4-byte units), minus 2
795  n = (ahHeader->payloadLen + 2) * sizeof(uint32_t);
796 
797  //Check the length of the AH header
798  if(n >= sizeof(AhHeader))
799  {
800  //Retrieve the length of the ICV tag
801  n -= sizeof(AhHeader);
802 
803  //Dump AH header contents
804  TRACE_DEBUG(" Next Header = %" PRIu8 "\r\n", ahHeader->nextHeader);
805  TRACE_DEBUG(" Payload Length = %" PRIu8 "\r\n", ahHeader->payloadLen);
806  TRACE_DEBUG(" SPI = 0x%08" PRIX32 "\r\n", ntohl(ahHeader->spi));
807  TRACE_DEBUG(" Sequence Number = 0x%08" PRIX32 "\r\n", ntohl(ahHeader->seqNum));
808  TRACE_DEBUG_ARRAY(" ICV = ", ahHeader->icv, n);
809  }
810 }
811 
812 #endif
@ MAC_ALGO_CMAC
#define htons(value)
Definition: cpu_endian.h:413
HMAC algorithm context.
Definition: hmac.h:59
error_t ahGenerateIcv(IpsecContext *context, IpsecSadEntry *sa, const Ipv4Header *ipv4Header, AhHeader *ahHeader, const NetBuffer *buffer, size_t offset)
ICV generation.
Definition: ah.c:315
#define AH_MAX_DIGEST_SIZE
Definition: ah.h:130
#define Ipv4Header
Definition: ipv4.h:36
@ IPV4_OFFSET_MASK
Definition: ipv4.h:265
@ IPV4_OPTION_SDB
Selective Directed Broadcast.
Definition: ipv4.h:313
@ ERROR_WRONG_SEQUENCE_NUMBER
Definition: error.h:183
@ IPSEC_PROTOCOL_AH
Definition: ipsec.h:199
IPsec selector.
Definition: ipsec.h:309
IPsec processing of outbound IP traffic.
uint8_t p
Definition: ndp.h:300
uint16_t checksum
Definition: tcp.h:362
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
uint_t chunkCount
Definition: net_mem.h:90
@ ERROR_INVALID_HEADER
Definition: error.h:87
error_t ahProcessInboundIpv4Packet(NetInterface *interface, const Ipv4Header *ipv4Header, const NetBuffer *buffer, size_t offset, NetRxAncillary *ancillary)
Process AH protected packet.
Definition: ah.c:156
@ IPV4_OPTION_ESEC
Extended Security.
Definition: ipv4.h:304
@ IPV4_OPTION_NOP
No Operation.
Definition: ipv4.h:293
@ ERROR_INVALID_MAC
Definition: error.h:113
error_t ipsecCheckReplayWindow(const IpsecSadEntry *sa, uint64_t seqNum)
Perform replay detection.
uint64_t ipsecGetSeqNum(IpsecSadEntry *sa, uint32_t seql)
Determine the higher-order bits of the sequence number.
uint16_t length
Definition: net_mem.h:79
@ IPV4_PROTOCOL_AH
Definition: ipv4.h:282
#define htonl(value)
Definition: cpu_endian.h:414
#define osMemcpy(dest, src, length)
Definition: os_port.h:147
ICMP (Internet Control Message Protocol)
error_t
Error codes.
Definition: error.h:43
error_t ahVerifyIcv(IpsecContext *context, IpsecSadEntry *sa, const Ipv4Header *ipv4Header, const AhHeader *ahHeader, const NetBuffer *buffer, size_t offset)
ICV verification.
Definition: ah.c:484
error_t ipsecSendIpv4Packet(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send an IPv4 packet.
AhHeader
Definition: ah.h:178
TCP finite state machine.
void * address
Definition: net_mem.h:78
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ IPV4_FLAG_MF
Definition: ipv4.h:264
Helper routines for IPsec.
bool_t ipsecIsSubsetSelector(const IpsecSelector *selector1, const IpsecSelector *selector2)
Test if a selector is a subset of another selector.
Definition: ipsec_misc.c:373
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:40
#define NetTxAncillary
Definition: net_misc.h:36
uint8_t mask
Definition: web_socket.h:319
#define Ipv4PseudoHeader
Definition: ipv4.h:40
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
IPsec processing of inbound IP traffic.
IpsecSadEntry * ipsecFindInboundSadEntry(IpsecContext *context, IpsecProtocol protocol, uint32_t spi)
Search the SAD database for a matching inbound entry.
Definition: ipsec_misc.c:145
Anti-replay mechanism.
AH (IP Authentication Header)
CMAC algorithm context.
Definition: cmac.h:54
#define IpsecSadEntry
Definition: ipsec.h:36
TCP/IP raw sockets.
#define ntohs(value)
Definition: cpu_endian.h:421
__weak_func void hmacUpdate(HmacContext *context, const void *data, size_t length)
Update the HMAC context with a portion of the message being hashed.
Definition: hmac.c:201
#define TRACE_WARNING(...)
Definition: debug.h:93
#define TRACE_DEBUG(...)
Definition: debug.h:119
uint8_t digest[MAX_HASH_DIGEST_SIZE]
Definition: hmac.h:63
@ IPV4_OPTION_EEOL
End of Options List.
Definition: ipv4.h:292
ChunkDesc chunk[]
Definition: net_mem.h:92
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:120
IPsec (IP security)
uint8_t n
#define IpPseudoHeader
Definition: ip.h:36
@ ERROR_AUTHENTICATION_FAILED
Definition: error.h:69
__weak_func void hmacFinal(HmacContext *context, uint8_t *digest)
Finish the HMAC calculation.
Definition: hmac.c:218
Ipv4Option
Definition: ipv4.h:384
#define IPV4_VERSION
Definition: ipv4.h:108
#define IpsecContext
Definition: ipsec.h:40
@ MAC_ALGO_HMAC
#define IPV4_MAX_HEADER_LENGTH
Definition: ipv4.h:116
error_t cmacInit(CmacContext *context, const CipherAlgo *cipher, const void *key, size_t keyLen)
Initialize CMAC calculation.
Definition: cmac.c:107
void cmacUpdate(CmacContext *context, const void *data, size_t dataLen)
Update the CMAC context with a portion of the message being hashed.
Definition: cmac.c:191
@ IPV4_OPTION_RTRALT
Router Alert.
Definition: ipv4.h:312
void * netBufferAt(const NetBuffer *buffer, size_t offset, size_t length)
Returns a pointer to a data segment.
Definition: net_mem.c:418
@ IPV4_OPTION_SEC
Security.
Definition: ipv4.h:302
error_t ahProtectOutboundIpv4Packet(IpsecContext *context, IpsecSadEntry *sa, NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Protect an outbound IPv4 packet using AH.
Definition: ah.c:64
error_t ipsecGetInboundIpv4PacketSelector(const Ipv4Header *ipv4Header, uint8_t nextHeader, const NetBuffer *buffer, size_t offset, IpsecSelector *selector)
Extract packet's selector from inbound IPv4 packet.
@ IPV4_OPTION_CIPSO
Commercial Security.
Definition: ipv4.h:305
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:141
TcpHeader
Definition: tcp.h:365
__weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen)
Initialize HMAC calculation.
Definition: hmac.c:140
error_t cmacFinal(CmacContext *context, uint8_t *mac, size_t macLen)
Finish the CMAC calculation.
Definition: cmac.c:237
void ipsecUpdateReplayWindow(IpsecSadEntry *sa, uint64_t seqNum)
Update sliding window.
@ ERROR_POLICY_FAILURE
Definition: error.h:300
#define ntohl(value)
Definition: cpu_endian.h:422
void ahDumpHeader(const AhHeader *ahHeader)
Dump AH header for debugging purpose.
Definition: ah.c:789
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
void ahProcessMutableIpv4Options(Ipv4Header *header)
Zeroize mutable IPv4 options.
Definition: ah.c:719
error_t ipv4DispatchDatagram(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary)
Dispatch incoming IPv4 datagram.
Definition: ipv4.c:972