ike_key_material.c
Go to the documentation of this file.
1 /**
2  * @file ike_key_material.c
3  * @brief Key material generation
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 IKE_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ike/ike.h"
36 #include "ike/ike_key_material.h"
37 #include "ike/ike_algorithms.h"
38 #include "ah/ah_algorithms.h"
39 #include "esp/esp_algorithms.h"
40 #include "kdf/ike_kdf.h"
41 #include "debug.h"
42 
43 //Check IKEv2 library configuration
44 #if (IKE_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief Generate keying material for the IKE SA
49  * @param[in] sa Pointer to the IKE SA
50  * @param[in] oldSa Pointer to the old IKE SA
51  * @return Error code
52  **/
53 
55 {
56  error_t error;
57  size_t keyMaterialLen;
58  IkeContext *context;
59  uint8_t skeyseed[IKE_MAX_DIGEST_SIZE];
60  DataFrag dataFrags[4];
61 
62  //Point to the IKE context
63  context = sa->context;
64 
65  //Select the relevant PRF algorithm
66  error = ikeSelectPrfAlgo(sa, sa->prfAlgoId);
67  //Any error to report?
68  if(error)
69  return error;
70 
71  //Select the relevant encryption algorithm
72  error = ikeSelectEncAlgo(sa, sa->encAlgoId, sa->encKeyLen);
73  //Any error to report?
74  if(error)
75  return error;
76 
77  //AEAD encryption algorithm?
78  if(ikeIsAeadEncAlgo(sa->encAlgoId))
79  {
80  //When an authenticated encryption algorithm is selected as the encryption
81  //algorithm for any IKE SA, an integrity algorithm must not be selected
82  //for that SA (refer to RFC 5282, section 8)
83  sa->authMacAlgo = MAC_ALGO_NONE;
84  sa->authHashAlgo = NULL;
85  sa->authCipherAlgo = NULL;
86  }
87  else
88  {
89  //Select the relevant MAC algorithm
90  error = ikeSelectAuthAlgo(sa, sa->authAlgoId);
91  //Any error to report?
92  if(error)
93  return error;
94  }
95 
96  //Length of necessary keying material
97  keyMaterialLen = 2 * sa->authKeyLen + 2 * (sa->encKeyLen + sa->saltLen) +
98  3 * sa->prfKeyLen;
99 
100  //Make sure that the buffer is large enough
101  if(keyMaterialLen > IKE_MAX_SA_KEY_MAT_LEN)
102  return ERROR_FAILURE;
103 
104  //Debug message
105  TRACE_DEBUG("Generating IKE SA keying material...\r\n");
106  TRACE_DEBUG(" Nonce (initiator):\r\n");
107  TRACE_DEBUG_ARRAY(" ", sa->initiatorNonce, sa->initiatorNonceLen);
108  TRACE_DEBUG(" Nonce (responder):\r\n");
109  TRACE_DEBUG_ARRAY(" ", sa->responderNonce, sa->responderNonceLen);
110  TRACE_DEBUG(" Shared secret:\r\n");
111  TRACE_DEBUG_ARRAY(" ", sa->sharedSecret, sa->sharedSecretLen);
112 
113  //IKE SA rekeying?
114  if(oldSa != NULL)
115  {
116  //Debug message
117  TRACE_DEBUG(" SK_d (old):\r\n");
118  TRACE_DEBUG_ARRAY(" ", oldSa->skd, oldSa->prfKeyLen);
119 
120  //Concatenate g^ir (new), Ni and Nr
121  dataFrags[0].buffer = sa->sharedSecret;
122  dataFrags[0].length = sa->sharedSecretLen;
123  dataFrags[1].buffer = sa->initiatorNonce;
124  dataFrags[1].length = sa->initiatorNonceLen;
125  dataFrags[2].buffer = sa->responderNonce;
126  dataFrags[2].length = sa->responderNonceLen;
127 
128  //Calculate SKEYSEED = prf(SK_d (old), g^ir (new) | Ni | Nr)
129  error = ikePrfEx(oldSa->prfMacAlgo, oldSa->prfHashAlgo, oldSa->prfCipherAlgo,
130  oldSa->skd, oldSa->prfKeyLen, dataFrags, 3, skeyseed);
131  //Any error to report?
132  if(error)
133  return error;
134  }
135  else
136  {
137  size_t bufferLen;
138  uint8_t buffer[2 * IKE_MAX_NONCE_SIZE];
139 
140  //For historical backward-compatibility reasons, there are two PRFs that
141  //are treated specially in this calculation
142  if(sa->prfAlgoId == IKE_TRANSFORM_ID_PRF_AES128_XCBC ||
143  sa->prfAlgoId == IKE_TRANSFORM_ID_PRF_AES128_CMAC)
144  {
145  //If the negotiated PRF is AES-XCBC-PRF-128 or AES-CMAC-PRF-128, only
146  //the first 64 bits of Ni and the first 64 bits of Nr are used in
147  //calculating SKEYSEED (refer to RFC 7296, section 2.14)
148  osMemcpy(buffer, sa->initiatorNonce, 8);
149  bufferLen = 8;
150  osMemcpy(buffer + bufferLen, sa->responderNonce, 8);
151  bufferLen += 8;
152  }
153  else
154  {
155  //Concatenate Ni and Nr
156  osMemcpy(buffer, sa->initiatorNonce, sa->initiatorNonceLen);
157  bufferLen = sa->initiatorNonceLen;
158  osMemcpy(buffer + bufferLen, sa->responderNonce, sa->responderNonceLen);
159  bufferLen += sa->responderNonceLen;
160  }
161 
162  //Each party generates a quantity called SKEYSEED = prf(Ni | Nr, g^ir)
163  error = ikePrf(sa->prfMacAlgo, sa->prfHashAlgo, sa->prfCipherAlgo,
164  buffer, bufferLen, sa->sharedSecret, sa->sharedSecretLen, skeyseed);
165  //Any error to report?
166  if(error)
167  return error;
168  }
169 
170  //Any shared secret derived from key establishment must be destroyed
171  //immediately after its use (refer to RFC 9206, section 10)
172  osMemset(sa->sharedSecret, 0, IKE_MAX_SHARED_SECRET_LEN);
173  sa->sharedSecretLen = 0;
174 
175  //Debug message
176  TRACE_DEBUG(" SKEYSEED:\r\n");
177  TRACE_DEBUG_ARRAY(" ", skeyseed, sa->prfKeyLen);
178 
179  //Concatenate Ni, Nr, SPIi and SPIr
180  dataFrags[0].buffer = sa->initiatorNonce;
181  dataFrags[0].length = sa->initiatorNonceLen;
182  dataFrags[1].buffer = sa->responderNonce;
183  dataFrags[1].length = sa->responderNonceLen;
184  dataFrags[2].buffer = sa->initiatorSpi;
185  dataFrags[2].length = IKE_SPI_SIZE;
186  dataFrags[3].buffer = sa->responderSpi;
187  dataFrags[3].length = IKE_SPI_SIZE;
188 
189  //SKEYSEED is used to calculate seven other secrets (refer to RFC 7296,
190  //section 2.14)
191  error = ikePrfPlusEx(sa->prfMacAlgo, sa->prfHashAlgo, sa->prfCipherAlgo,
192  skeyseed, sa->prfKeyLen, dataFrags, 4, sa->keyMaterial, keyMaterialLen);
193  //Any error to report?
194  if(error)
195  return error;
196 
197  //Debug message
198  TRACE_DEBUG(" Keying material:\r\n");
199  TRACE_DEBUG_ARRAY(" ", sa->keyMaterial, keyMaterialLen);
200 
201  //SK_d is used for deriving new keys for the Child SAs established with
202  //this IKE SA
203  sa->skd = sa->keyMaterial;
204 
205  //SK_ai and SK_ar used as a key to the integrity protection algorithm
206  //for authenticating the component messages of subsequent exchanges
207  sa->skai = sa->skd + sa->prfKeyLen;
208  sa->skar = sa->skai + sa->authKeyLen;
209 
210  //SK_ei and SK_er are used for encrypting and decrypting all subsequent
211  //exchanges
212  sa->skei = sa->skar + sa->authKeyLen;
213  sa->sker = sa->skei + sa->encKeyLen + sa->saltLen;
214 
215  //SK_pi and SK_pr are used when generating an AUTH payload
216  sa->skpi = sa->sker + sa->encKeyLen + sa->saltLen;
217  sa->skpr = sa->skpi + sa->prfKeyLen;
218 
219  //Debug message
220  TRACE_DEBUG(" SK_d:\r\n");
221  TRACE_DEBUG_ARRAY(" ", sa->skd, sa->prfKeyLen);
222  TRACE_DEBUG(" SK_ai:\r\n");
223  TRACE_DEBUG_ARRAY(" ", sa->skai, sa->authKeyLen);
224  TRACE_DEBUG(" SK_ar:\r\n");
225  TRACE_DEBUG_ARRAY(" ", sa->skar, sa->authKeyLen);
226  TRACE_DEBUG(" SK_ei:\r\n");
227  TRACE_DEBUG_ARRAY(" ", sa->skei, sa->encKeyLen + sa->saltLen);
228  TRACE_DEBUG(" SK_er:\r\n");
229  TRACE_DEBUG_ARRAY(" ", sa->sker, sa->encKeyLen + sa->saltLen);
230  TRACE_DEBUG(" SK_pi:\r\n");
231  TRACE_DEBUG_ARRAY(" ", sa->skpi, sa->prfKeyLen);
232  TRACE_DEBUG(" SK_pr:\r\n");
233  TRACE_DEBUG_ARRAY(" ", sa->skpr, sa->prfKeyLen);
234 
235  //Check encryption mode
236  if(sa->cipherMode != CIPHER_MODE_CBC)
237  {
238  //The IV must be chosen by the encryptor in a manner that ensures that
239  //the same IV value is used only once for a given key (refer to RFC 5282,
240  //section 3.1)
241  error = context->prngAlgo->generate(context->prngContext, sa->iv, 8);
242  //Any error to report?
243  if(error)
244  return error;
245 
246  //Debug message
247  TRACE_DEBUG(" IV:\r\n");
248  TRACE_DEBUG_ARRAY(" ", sa->iv, 8);
249  }
250 
251  //Successful processing
252  return NO_ERROR;
253 }
254 
255 
256 /**
257  * @brief Generate keying material for the Child SA
258  * @param[in] childSa Pointer to the Child SA
259  * @return Error code
260  **/
261 
263 {
264  error_t error;
265  uint_t n;
266  size_t keyMaterialLen;
267  IkeSaEntry *sa;
268  DataFrag dataFrags[3];
269 
270  //Point to the IKE SA
271  sa = childSa->sa;
272 
273 #if (AH_SUPPORT == ENABLED)
274  //AH protocol?
275  if(childSa->protocol == IPSEC_PROTOCOL_AH)
276  {
277  //AH does not provide confidentiality (encryption) service
278  childSa->cipherAlgo = NULL;
279  childSa->cipherMode = CIPHER_MODE_NULL;
280  childSa->encKeyLen = 0;
281  childSa->saltLen = 0;
282  childSa->ivLen = 0;
283 
284  //Select the relevant MAC algorithm
285  error = ahSelectAuthAlgo(childSa, childSa->authAlgoId);
286  //Any error to report?
287  if(error)
288  return error;
289  }
290  else
291 #endif
292 #if (ESP_SUPPORT == ENABLED)
293  //ESP protocol?
294  if(childSa->protocol == IPSEC_PROTOCOL_ESP)
295  {
296  //Select the relevant encryption algorithm
297  error = espSelectEncAlgo(childSa, childSa->encAlgoId, childSa->encKeyLen);
298  //Any error to report?
299  if(error)
300  return error;
301 
302  //AEAD encryption algorithm?
303  if(ikeIsAeadEncAlgo(childSa->encAlgoId))
304  {
305  //When an authenticated encryption algorithm is selected as the
306  //encryption algorithm for any IKE SA, an integrity algorithm must
307  //not be selected for that SA (refer to RFC 5282, section 8)
308  childSa->authMacAlgo = MAC_ALGO_NONE;
309  childSa->authHashAlgo = NULL;
310  childSa->authCipherAlgo = NULL;
311  }
312  else
313  {
314  //Select the relevant MAC algorithm
315  error = espSelectAuthAlgo(childSa, childSa->authAlgoId);
316  //Any error to report?
317  if(error)
318  return error;
319  }
320  }
321  else
322 #endif
323  //Invalid IPsec protocol?
324  {
325  //Report an error
326  return ERROR_INVALID_PROTOCOL;
327  }
328 
329  //Length of necessary keying material
330  keyMaterialLen = 2 * childSa->authKeyLen + 2 * (childSa->encKeyLen +
331  childSa->saltLen);
332 
333  //Make sure that the buffer is large enough
334  if(keyMaterialLen > IKE_MAX_CHILD_SA_KEY_MAT_LEN)
335  return ERROR_FAILURE;
336 
337  //Debug message
338  TRACE_DEBUG("Generating Child SA keying material...\r\n");
339  TRACE_DEBUG(" SK_d:\r\n");
340  TRACE_DEBUG_ARRAY(" ", sa->skd, sa->prfKeyLen);
341  TRACE_DEBUG(" Nonce (initiator):\r\n");
342  TRACE_DEBUG_ARRAY(" ", childSa->initiatorNonce, childSa->initiatorNonceLen);
343  TRACE_DEBUG(" Nonce (responder):\r\n");
344  TRACE_DEBUG_ARRAY(" ", childSa->responderNonce, childSa->responderNonceLen);
345 
346  //Concatenate g^ir (new), Ni and Nr
347  n = 0;
348 
349 #if (IKE_CHILD_SA_PFS_SUPPORT == ENABLED)
350  //Perfect forward secrecy?
351  if(childSa->pfs)
352  {
353  //g^ir (new) is the shared secret from the ephemeral Diffie-Hellman
354  //exchange of this CREATE_CHILD_SA exchange
355  dataFrags[n].buffer = childSa->sharedSecret;
356  dataFrags[n++].length = childSa->sharedSecretLen;
357  }
358 #endif
359 
360  //Ni and Nr are the nonces from the IKE_SA_INIT exchange if this request is
361  //the first Child SA created or the fresh Ni and Nr from the CREATE_CHILD_SA
362  //exchange if this is a subsequent creation (refer to RFC 7296, section 2.17)
363  dataFrags[n].buffer = childSa->initiatorNonce;
364  dataFrags[n++].length = childSa->initiatorNonceLen;
365  dataFrags[n].buffer = childSa->responderNonce;
366  dataFrags[n++].length = childSa->responderNonceLen;
367 
368  //Calculate KEYMAT = prf+(SK_d, g^ir (new) | Ni | Nr)
369  error = ikePrfPlusEx(sa->prfMacAlgo, sa->prfHashAlgo, sa->prfCipherAlgo,
370  sa->skd, sa->prfKeyLen, dataFrags, n, childSa->keyMaterial,
371  keyMaterialLen);
372  //Any error to report?
373  if(error)
374  return error;
375 
376 #if (IKE_CHILD_SA_PFS_SUPPORT == ENABLED)
377  //Any shared secret derived from key establishment must be destroyed
378  //immediately after its use (refer to RFC 9206, section 10)
379  osMemset(childSa->sharedSecret, 0, IKE_MAX_SHARED_SECRET_LEN);
380  childSa->sharedSecretLen = 0;
381 #endif
382 
383  //Debug message
384  TRACE_DEBUG(" Keying material:\r\n");
385  TRACE_DEBUG_ARRAY(" ", childSa->keyMaterial, keyMaterialLen);
386 
387  //All keys for SAs carrying data from the initiator to the responder are
388  //taken before SAs going from the responder to the initiator. the encryption
389  //key must be taken from the first bits and the integrity key must be taken
390  //from the remaining bits (refer to RFC 7296, section 2.17)
391  childSa->skei = childSa->keyMaterial;
392  childSa->skai = childSa->skei + childSa->encKeyLen + childSa->saltLen;
393  childSa->sker = childSa->skai + childSa->authKeyLen;
394  childSa->skar = childSa->sker + childSa->encKeyLen + childSa->saltLen;
395 
396  //Debug message
397  TRACE_DEBUG(" SK_ei:\r\n");
398  TRACE_DEBUG_ARRAY(" ", childSa->skei, childSa->encKeyLen + childSa->saltLen);
399  TRACE_DEBUG(" SK_ai:\r\n");
400  TRACE_DEBUG_ARRAY(" ", childSa->skai, childSa->authKeyLen);
401  TRACE_DEBUG(" SK_er:\r\n");
402  TRACE_DEBUG_ARRAY(" ", childSa->sker, childSa->encKeyLen + childSa->saltLen);
403  TRACE_DEBUG(" SK_ar:\r\n");
404  TRACE_DEBUG_ARRAY(" ", childSa->skar, childSa->authKeyLen);
405 
406 #if (ESP_SUPPORT == ENABLED)
407  //Check ESP encryption mode
408  if(childSa->protocol == IPSEC_PROTOCOL_ESP &&
409  childSa->cipherMode != CIPHER_MODE_CBC)
410  {
411  IkeContext *context;
412 
413  //Point to the IKE context
414  context = childSa->context;
415 
416  //The IV must be chosen by the encryptor in a manner that ensures that
417  //the same IV value is used only once for a given key
418  error = context->prngAlgo->generate(context->prngContext, childSa->iv, 8);
419  //Any error to report?
420  if(error)
421  return error;
422 
423  //Debug message
424  TRACE_DEBUG(" IV:\r\n");
425  TRACE_DEBUG_ARRAY(" ", childSa->iv, 8);
426  }
427 #endif
428 
429  //Successful processing
430  return NO_ERROR;
431 }
432 
433 #endif
AH algorithm negotiation.
@ CIPHER_MODE_CBC
Definition: cipher_modes.h:82
error_t ikeSelectEncAlgo(IkeSaEntry *sa, uint16_t encAlgoId, size_t encKeyLen)
Select the relevant encryption algorithm.
const void * buffer
Definition: crypto.h:1165
error_t ikePrf(MacAlgo macAlgo, const HashAlgo *hashAlgo, const CipherAlgo *cipherAlgo, const uint8_t *key, size_t keyLen, const uint8_t *data, size_t dataLen, uint8_t *output)
Pseudorandom function (prf function)
Definition: ike_kdf.c:55
@ IKE_TRANSFORM_ID_PRF_AES128_CMAC
Definition: ike.h:1018
@ IPSEC_PROTOCOL_AH
Definition: ipsec.h:199
#define IKE_MAX_SA_KEY_MAT_LEN
Definition: ike.h:740
ESP algorithm negotiation.
error_t ikeSelectPrfAlgo(IkeSaEntry *sa, uint16_t prfAlgoId)
Select the relevant PRF algorithm.
@ CIPHER_MODE_NULL
Definition: cipher_modes.h:79
#define IKE_MAX_DIGEST_SIZE
Definition: ike.h:764
error_t ikePrfPlusEx(MacAlgo macAlgo, const HashAlgo *hashAlgo, const CipherAlgo *cipherAlgo, const uint8_t *key, size_t keyLen, const DataFrag *dataFrags, uint_t dataNumFrags, uint8_t *output, size_t outputLen)
Function that outputs a pseudorandom stream (prf+ function)
Definition: ike_kdf.c:191
@ IPSEC_PROTOCOL_ESP
Definition: ipsec.h:200
#define IKE_MAX_SHARED_SECRET_LEN
Definition: ike.h:802
#define IkeContext
Definition: ike.h:832
error_t ikeSelectAuthAlgo(IkeSaEntry *sa, uint16_t authAlgoId)
Select the relevant MAC algorithm.
error_t ikePrfEx(MacAlgo macAlgo, const HashAlgo *hashAlgo, const CipherAlgo *cipherAlgo, const uint8_t *key, size_t keyLen, const DataFrag *dataFrags, uint_t dataNumFrags, uint8_t *output)
Pseudorandom function (prf function)
Definition: ike_kdf.c:92
#define osMemcpy(dest, src, length)
Definition: os_port.h:147
error_t
Error codes.
Definition: error.h:43
@ MAC_ALGO_NONE
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
Key material generation.
#define IKE_SPI_SIZE
Definition: ike.h:826
#define IKE_MAX_NONCE_SIZE
Definition: ike.h:236
Data fragment descriptor.
Definition: crypto.h:1164
error_t espSelectEncAlgo(IkeChildSaEntry *childSa, uint16_t encAlgoId, size_t encKeyLen)
Select the relevant encryption algorithm.
error_t ahSelectAuthAlgo(IkeChildSaEntry *childSa, uint16_t authAlgoId)
Select the relevant MAC algorithm.
Definition: ah_algorithms.c:94
size_t length
Definition: crypto.h:1166
@ IKE_TRANSFORM_ID_PRF_AES128_XCBC
Definition: ike.h:1014
@ ERROR_INVALID_PROTOCOL
Definition: error.h:101
bool_t ikeIsAeadEncAlgo(uint16_t encAlgoId)
Test if the transform ID identifies an AEAD encryption algorithm.
IKEv2 key derivation functions.
IKEv2 (Internet Key Exchange Protocol)
#define TRACE_DEBUG(...)
Definition: debug.h:119
#define IkeSaEntry
Definition: ike.h:836
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:120
error_t ikeGenerateChildSaKeyMaterial(IkeChildSaEntry *childSa)
Generate keying material for the Child SA.
uint8_t n
error_t ikeGenerateSaKeyMaterial(IkeSaEntry *sa, IkeSaEntry *oldSa)
Generate keying material for the IKE SA.
#define IKE_MAX_CHILD_SA_KEY_MAT_LEN
Definition: ike.h:747
error_t espSelectAuthAlgo(IkeChildSaEntry *childSa, uint16_t authAlgoId)
Select the relevant MAC algorithm.
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:141
#define IkeChildSaEntry
Definition: ike.h:840
IKEv2 algorithm negotiation.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.