ah_algorithms.c
Go to the documentation of this file.
1 /**
2  * @file ah_algorithms.c
3  * @brief AH algorithm negotiation
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2022-2024 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.4.0
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_misc.h"
37 #include "ah/ah.h"
38 #include "ah/ah_algorithms.h"
39 #include "ike/ike_algorithms.h"
40 #include "hash/hash_algorithms.h"
41 #include "debug.h"
42 
43 //Check IPsec library configuration
44 #if (AH_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief List of supported integrity algorithms
49  **/
50 
51 static const uint16_t ahSupportedAuthAlgos[] =
52 {
53 #if (AH_HMAC_SUPPORT == ENABLED && AH_SHA256_SUPPORT == ENABLED)
55 #endif
56 #if (AH_HMAC_SUPPORT == ENABLED && AH_SHA384_SUPPORT == ENABLED)
58 #endif
59 #if (AH_HMAC_SUPPORT == ENABLED && AH_SHA512_SUPPORT == ENABLED)
61 #endif
62 #if (AH_CMAC_SUPPORT == ENABLED && AH_AES_128_SUPPORT == ENABLED)
64 #endif
65 #if (AH_HMAC_SUPPORT == ENABLED && AH_SHA1_SUPPORT == ENABLED)
67 #endif
68 #if (AH_HMAC_SUPPORT == ENABLED && AH_MD5_SUPPORT == ENABLED)
70 #endif
71 };
72 
73 
74 /**
75  * @brief List of supported ESN transforms
76  **/
77 
78 static const uint16_t ahSupportedEsnTranforms[] =
79 {
80 #if (AH_ESN_SUPPORT == ENABLED)
82 #endif
84 };
85 
86 
87 /**
88  * @brief Select the relevant MAC algorithm
89  * @param[in] childSa Pointer to the Child SA
90  * @param[in] authAlgoId Authentication algorithm identifier
91  * @return Error code
92  **/
93 
94 error_t ahSelectAuthAlgo(IkeChildSaEntry *childSa, uint16_t authAlgoId)
95 {
96  error_t error;
97 
98  //Initialize status code
99  error = NO_ERROR;
100 
101 #if (AH_HMAC_SUPPORT == ENABLED && AH_MD5_SUPPORT == ENABLED)
102  //HMAC-MD5-96 authentication algorithm?
103  if(authAlgoId == IKE_TRANSFORM_ID_AUTH_HMAC_MD5_96)
104  {
105  childSa->authHashAlgo = MD5_HASH_ALGO;
106  childSa->authCipherAlgo = NULL;
107  childSa->authKeyLen = MD5_DIGEST_SIZE;
108  childSa->icvLen = 12;
109  }
110  else
111 #endif
112 #if (AH_HMAC_SUPPORT == ENABLED && AH_SHA1_SUPPORT == ENABLED)
113  //HMAC-SHA1-96 authentication algorithm?
114  if(authAlgoId == IKE_TRANSFORM_ID_AUTH_HMAC_SHA1_96)
115  {
116  childSa->authHashAlgo = SHA1_HASH_ALGO;
117  childSa->authCipherAlgo = NULL;
118  childSa->authKeyLen = SHA1_DIGEST_SIZE;
119  childSa->icvLen = 12;
120  }
121  else
122 #endif
123 #if (AH_HMAC_SUPPORT == ENABLED && AH_SHA256_SUPPORT == ENABLED)
124  //HMAC-SHA256-128 authentication algorithm?
126  {
127  childSa->authHashAlgo = SHA256_HASH_ALGO;
128  childSa->authCipherAlgo = NULL;
129  childSa->authKeyLen = SHA256_DIGEST_SIZE;
130  childSa->icvLen = 16;
131  }
132  else
133 #endif
134 #if (AH_HMAC_SUPPORT == ENABLED && AH_SHA384_SUPPORT == ENABLED)
135  //HMAC-SHA384-192 authentication algorithm?
137  {
138  childSa->authHashAlgo = SHA384_HASH_ALGO;
139  childSa->authCipherAlgo = NULL;
140  childSa->authKeyLen = SHA384_DIGEST_SIZE;
141  childSa->icvLen = 24;
142  }
143  else
144 #endif
145 #if (AH_HMAC_SUPPORT == ENABLED && AH_SHA512_SUPPORT == ENABLED)
146  //HMAC-SHA512-256 authentication algorithm?
148  {
149  childSa->authHashAlgo = SHA512_HASH_ALGO;
150  childSa->authCipherAlgo = NULL;
151  childSa->authKeyLen = SHA512_DIGEST_SIZE;
152  childSa->icvLen = 32;
153  }
154  else
155 #endif
156 #if (AH_CMAC_SUPPORT == ENABLED && AH_AES_128_SUPPORT == ENABLED)
157  //AES-CMAC-96 authentication algorithm?
158  if(authAlgoId == IKE_TRANSFORM_ID_AUTH_AES_CMAC_96)
159  {
160  childSa->authHashAlgo = NULL;
161  childSa->authCipherAlgo = AES_CIPHER_ALGO;
162  childSa->authKeyLen = 16;
163  childSa->icvLen = 12;
164  }
165  else
166 #endif
167  //Unknown authentication algorithm?
168  {
169  //Report an error
170  error = ERROR_UNSUPPORTED_ALGO;
171  }
172 
173  //Return status code
174  return error;
175 }
176 
177 
178 /**
179  * @brief Add the supported AH transforms to the proposal
180  * @param[in] context Pointer to the IKE context
181  * @param[in,out] proposal Pointer to the Proposal substructure
182  * @param[in,out] lastSubstruc Pointer to the Last Substruc field
183  * @return Error code
184  **/
185 
187  uint8_t **lastSubstruc)
188 {
189  error_t error;
190 
191  //Add supported integrity transforms
192  error = ahAddSupportedAuthTransforms(context, proposal, lastSubstruc);
193 
194  //Check status code
195  if(!error)
196  {
197  //An initiator who supports ESNs will usually include two ESN transforms,
198  //with values "0" and "1", in its proposals (refer to RFC 7296,
199  //section 3.3.2)
200  error = ahAddSupportedEsnTransforms(context, proposal, lastSubstruc);
201  }
202 
203  //Return status code
204  return error;
205 }
206 
207 
208 /**
209  * @brief Add the supported integrity transforms to the proposal
210  * @param[in] context Pointer to the IKE context
211  * @param[in,out] proposal Pointer to the Proposal substructure
212  * @param[in,out] lastSubstruc Pointer to the Last Substruc field
213  * @return Error code
214  **/
215 
217  IkeProposal *proposal, uint8_t **lastSubstruc)
218 {
219  error_t error;
220  uint_t i;
221 
222  //Initialize status code
223  error = NO_ERROR;
224 
225  //Loop through the list of supported integrity transforms
226  for(i = 0; i < arraysize(ahSupportedAuthAlgos) && !error; i++)
227  {
228  //Add a new transform to the proposal
230  ahSupportedAuthAlgos[i], 0, proposal, lastSubstruc);
231  }
232 
233  //Return status code
234  return error;
235 }
236 
237 
238 /**
239  * @brief Add the supported ESN transforms to the proposal
240  * @param[in] context Pointer to the IKE context
241  * @param[in,out] proposal Pointer to the Proposal substructure
242  * @param[in,out] lastSubstruc Pointer to the Last Substruc field
243  * @return Error code
244  **/
245 
247  IkeProposal *proposal, uint8_t **lastSubstruc)
248 {
249  error_t error;
250  uint_t i;
251 
252  //Initialize status code
253  error = NO_ERROR;
254 
255  //Loop through the list of supported ESN transforms
256  for(i = 0; i < arraysize(ahSupportedEsnTranforms) && !error; i++)
257  {
258  //Add a new transform to the proposal
260  ahSupportedEsnTranforms[i], 0, proposal, lastSubstruc);
261  }
262 
263  //Return status code
264  return error;
265 }
266 
267 
268 /**
269  * @brief Integrity transform negotiation
270  * @param[in] context Pointer to the IKE context
271  * @param[in] proposal Pointer to the Proposal substructure
272  * @param[in] proposalLen Length of the Proposal substructure, in bytes
273  * @return Selected integrity transform, if any
274  **/
275 
276 uint16_t ahSelectAuthTransform(IkeContext *context, const IkeProposal *proposal,
277  size_t proposalLen)
278 {
279  //Select the integrity transform to use
280  return ikeSelectTransform(IKE_TRANSFORM_TYPE_INTEG, ahSupportedAuthAlgos,
281  arraysize(ahSupportedAuthAlgos), proposal, proposalLen);
282 }
283 
284 
285 /**
286  * @brief ESN transform negotiation
287  * @param[in] context Pointer to the IKE context
288  * @param[in] proposal Pointer to the Proposal substructure
289  * @param[in] proposalLen Length of the Proposal substructure, in bytes
290  * @return Selected ESN transform, if any
291  **/
292 
293 uint16_t ahSelectEsnTransform(IkeContext *context, const IkeProposal *proposal,
294  size_t proposalLen)
295 {
296  //Select the ESN transform to use
297  return ikeSelectTransform(IKE_TRANSFORM_TYPE_ESN, ahSupportedEsnTranforms,
298  arraysize(ahSupportedEsnTranforms), proposal, proposalLen);
299 }
300 
301 
302 /**
303  * @brief Select a single proposal
304  * @param[in] childSa Pointer to the Child SA
305  * @param[in] payload Pointer to the Security Association payload
306  * @return Error code
307  **/
308 
310 {
311  error_t error;
312  size_t n;
313  size_t length;
314  const uint8_t *p;
315  const IkeProposal *proposal;
316 
317  //Clear the set of parameters
318  childSa->protocol = IPSEC_PROTOCOL_INVALID;
319  childSa->encAlgoId = IKE_TRANSFORM_ID_INVALID;
320  childSa->encKeyLen = 0;
321  childSa->authAlgoId = IKE_TRANSFORM_ID_INVALID;
322  childSa->esn = IKE_TRANSFORM_ID_INVALID;
323 
324  //Retrieve the length of the SA payload
325  length = ntohs(payload->header.payloadLength);
326 
327  //Malformed payload?
328  if(length < sizeof(IkeSaPayload))
329  return ERROR_INVALID_MESSAGE;
330 
331  //Point to the first byte of the Proposals field
332  p = payload->proposals;
333  //Determine the length of the Proposals field
334  length -= sizeof(IkeSaPayload);
335 
336  //Initialize status code
337  error = ERROR_INVALID_PROPOSAL;
338 
339  //The Security Association payload contains one or more Proposal
340  //substructures
341  while(1)
342  {
343  //Malformed payload?
344  if(length < sizeof(IkeProposal))
345  {
346  //Report an error
347  error = ERROR_INVALID_MESSAGE;
348  break;
349  }
350 
351  //Point to the Proposal substructure
352  proposal = (IkeProposal *) p;
353 
354  //The Proposal Length field indicates the length of this proposal,
355  //including all transforms and attributes that follow
356  n = ntohs(proposal->proposalLength);
357 
358  //Check the length of the proposal
359  if(n < sizeof(IkeProposal) || n > length)
360  {
361  //Report an error
362  error = ERROR_INVALID_MESSAGE;
363  break;
364  }
365 
366  //Check protocol identifier
367  if(proposal->protocolId == IKE_PROTOCOL_ID_AH)
368  {
369  //Valid SPI value?
370  if(proposal->spiSize == IPSEC_SPI_SIZE &&
371  osMemcmp(proposal->spi, IPSEC_INVALID_SPI, IPSEC_SPI_SIZE) != 0)
372  {
373  //Integrity transform negotiation
374  childSa->authAlgoId = ahSelectAuthTransform(childSa->context,
375  proposal, n);
376 
377  //ESN transform negotiation
378  childSa->esn = ahSelectEsnTransform(childSa->context, proposal, n);
379 
380  //Valid proposal?
381  if(childSa->authAlgoId != IKE_TRANSFORM_ID_INVALID &&
382  childSa->esn != IKE_TRANSFORM_ID_INVALID)
383  {
384  //Select AH security protocol
385  childSa->protocol = IPSEC_PROTOCOL_AH;
386 
387  //The initiator SPI is supplied in the SPI field of the SA
388  //payload
389  osMemcpy(childSa->remoteSpi, proposal->spi, proposal->spiSize);
390 
391  //Successful negotiation
392  error = NO_ERROR;
393  break;
394  }
395  }
396  }
397 
398  //Jump to the next proposal
399  p += n;
400  length -= n;
401  }
402 
403  //Return status code
404  return error;
405 }
406 
407 
408 /**
409  * @brief Check whether the selected proposal is acceptable
410  * @param[in] childSa Pointer to the Child SA
411  * @param[in] payload Pointer to the Security Association payload
412  * @return Error code
413  **/
414 
416 {
417  size_t n;
418  size_t length;
419  const uint8_t *p;
420  const IkeProposal *proposal;
421 
422  //Clear the set of parameters
423  childSa->encAlgoId = IKE_TRANSFORM_ID_INVALID;
424  childSa->encKeyLen = 0;
425  childSa->authAlgoId = IKE_TRANSFORM_ID_INVALID;
426  childSa->esn = IKE_TRANSFORM_ID_INVALID;
427 
428  //Retrieve the length of the SA payload
429  length = ntohs(payload->header.payloadLength);
430 
431  //Malformed payload?
432  if(length < sizeof(IkeSaPayload))
433  return ERROR_INVALID_MESSAGE;
434 
435  //Point to the first byte of the Proposals field
436  p = payload->proposals;
437  //Determine the length of the Proposals field
438  length -= sizeof(IkeSaPayload);
439 
440  //Malformed payload?
441  if(length < sizeof(IkeProposal))
442  return ERROR_INVALID_MESSAGE;
443 
444  //Point to the Proposal substructure
445  proposal = (IkeProposal *) p;
446 
447  //The Proposal Length field indicates the length of this proposal,
448  //including all transforms and attributes that follow
449  n = ntohs(proposal->proposalLength);
450 
451  //The responder must accept a single proposal (refer to RFC 7296,
452  //section 2.7)
453  if(n != length)
454  return ERROR_INVALID_MESSAGE;
455 
456  //Check protocol identifier
457  if(proposal->protocolId != IKE_PROTOCOL_ID_AH)
458  return ERROR_INVALID_MESSAGE;
459 
460  //During subsequent negotiations, the SPI Size field is equal to the size,
461  //in octets, of the SPI of the corresponding protocol (4 for ESP and AH)
462  if(proposal->spiSize != IPSEC_SPI_SIZE)
463  return ERROR_INVALID_MESSAGE;
464 
465  //The SPI value of zero is reserved and must not be sent on the wire (refer
466  //to RFC 4302, section 2.4)
467  if(osMemcmp(proposal->spi, IPSEC_INVALID_SPI, IPSEC_SPI_SIZE) == 0)
468  return ERROR_INVALID_MESSAGE;
469 
470  //The responder SPI is supplied in the SPI field of the SA payload
471  osMemcpy(childSa->remoteSpi, proposal->spi, proposal->spiSize);
472 
473  //The accepted cryptographic suite must contain exactly one transform of
474  //each type included in the proposal (refer to RFC 7296, section 2.7)
475  if(ikeGetNumTransforms(IKE_TRANSFORM_TYPE_INTEG, proposal, n) != 1 ||
477  {
478  return ERROR_INVALID_PROPOSAL;
479  }
480 
481  //Get the selected integrity transform
482  childSa->authAlgoId = ahSelectAuthTransform(childSa->context, proposal, n);
483  //Get the selected ESN transform
484  childSa->esn = ahSelectEsnTransform(childSa->context, proposal, n);
485 
486  //The initiator of an exchange must check that the accepted offer is
487  //consistent with one of its proposals, and if not must terminate the
488  //exchange (refer to RFC 7296, section 3.3.6)
489  if(childSa->authAlgoId != IKE_TRANSFORM_ID_INVALID &&
490  childSa->esn != IKE_TRANSFORM_ID_INVALID)
491  {
492  return NO_ERROR;
493  }
494  else
495  {
496  return ERROR_INVALID_PROPOSAL;
497  }
498 }
499 
500 #endif
#define AES_CIPHER_ALGO
Definition: aes.h:45
AH (IP Authentication Header)
error_t ahSelectAuthAlgo(IkeChildSaEntry *childSa, uint16_t authAlgoId)
Select the relevant MAC algorithm.
Definition: ah_algorithms.c:94
error_t ahCheckSaProposal(IkeChildSaEntry *childSa, const IkeSaPayload *payload)
Check whether the selected proposal is acceptable.
error_t ahSelectSaProposal(IkeChildSaEntry *childSa, const IkeSaPayload *payload)
Select a single proposal.
uint16_t ahSelectEsnTransform(IkeContext *context, const IkeProposal *proposal, size_t proposalLen)
ESN transform negotiation.
error_t ahAddSupportedTransforms(IkeContext *context, IkeProposal *proposal, uint8_t **lastSubstruc)
Add the supported AH transforms to the proposal.
uint16_t ahSelectAuthTransform(IkeContext *context, const IkeProposal *proposal, size_t proposalLen)
Integrity transform negotiation.
error_t ahAddSupportedEsnTransforms(IkeContext *context, IkeProposal *proposal, uint8_t **lastSubstruc)
Add the supported ESN transforms to the proposal.
error_t ahAddSupportedAuthTransforms(IkeContext *context, IkeProposal *proposal, uint8_t **lastSubstruc)
Add the supported integrity transforms to the proposal.
AH algorithm negotiation.
unsigned int uint_t
Definition: compiler_port.h:50
#define ntohs(value)
Definition: cpu_endian.h:421
Debugging facilities.
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
@ ERROR_INVALID_PROPOSAL
Definition: error.h:299
@ ERROR_UNSUPPORTED_ALGO
Definition: error.h:126
@ NO_ERROR
Success.
Definition: error.h:44
Collection of hash algorithms.
@ IKE_TRANSFORM_ID_ESN_NO
No Extended Sequence Numbers.
Definition: ike.h:913
@ IKE_TRANSFORM_ID_ESN_YES
Extended Sequence Numbers.
Definition: ike.h:914
IkeProposal
Definition: ike.h:1312
IkeSaPayload
Definition: ike.h:1295
#define IkeChildSaEntry
Definition: ike.h:686
@ IKE_TRANSFORM_ID_AUTH_AES_CMAC_96
Definition: ike.h:863
@ IKE_TRANSFORM_ID_AUTH_HMAC_SHA1_96
Definition: ike.h:857
@ IKE_TRANSFORM_ID_AUTH_HMAC_MD5_96
Definition: ike.h:856
@ IKE_TRANSFORM_ID_AUTH_HMAC_SHA2_512_256
Definition: ike.h:869
@ IKE_TRANSFORM_ID_AUTH_HMAC_SHA2_384_192
Definition: ike.h:868
@ IKE_TRANSFORM_ID_AUTH_HMAC_SHA2_256_128
Definition: ike.h:867
#define IkeContext
Definition: ike.h:678
@ IKE_PROTOCOL_ID_AH
AH protocol.
Definition: ike.h:769
@ IKE_TRANSFORM_TYPE_INTEG
Integrity Algorithm.
Definition: ike.h:782
@ IKE_TRANSFORM_TYPE_ESN
Extended Sequence Numbers.
Definition: ike.h:784
uint16_t ikeSelectTransform(IkeTransformType transformType, const uint16_t *algoList, uint_t algoListLen, const IkeProposal *proposal, size_t proposalLen)
Transform negotiation.
uint_t ikeGetNumTransforms(IkeTransformType transformType, const IkeProposal *proposal, size_t proposalLen)
Get the number of transforms that match a given transform type.
error_t ikeAddTransform(IkeTransformType transformType, uint16_t transformId, uint16_t keyLen, IkeProposal *proposal, uint8_t **lastSubstruc)
Add the supported transforms to the proposal.
IKEv2 algorithm negotiation.
#define IKE_TRANSFORM_ID_INVALID
IPsec (IP security)
#define IPSEC_SPI_SIZE
Definition: ipsec.h:138
@ IPSEC_PROTOCOL_INVALID
Definition: ipsec.h:191
@ IPSEC_PROTOCOL_AH
Definition: ipsec.h:192
const uint8_t IPSEC_INVALID_SPI[4]
Definition: ipsec_misc.c:40
Helper routines for IPsec.
uint8_t payload[]
Definition: ipv6.h:277
#define MD5_DIGEST_SIZE
Definition: md5.h:45
#define MD5_HASH_ALGO
Definition: md5.h:49
uint8_t p
Definition: ndp.h:300
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define osMemcmp(p1, p2, length)
Definition: os_port.h:153
#define arraysize(a)
Definition: os_port.h:71
#define SHA1_HASH_ALGO
Definition: sha1.h:49
#define SHA1_DIGEST_SIZE
Definition: sha1.h:45
#define SHA256_DIGEST_SIZE
Definition: sha256.h:45
#define SHA256_HASH_ALGO
Definition: sha256.h:49
#define SHA384_HASH_ALGO
Definition: sha384.h:45
#define SHA384_DIGEST_SIZE
Definition: sha384.h:41
#define SHA512_HASH_ALGO
Definition: sha512.h:49
#define SHA512_DIGEST_SIZE
Definition: sha512.h:45
uint8_t length
Definition: tcp.h:368