snmp_agent_usm.c
Go to the documentation of this file.
1 /**
2  * @file snmp_agent_usm.c
3  * @brief User-based Security Model (USM) for SNMPv3
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP 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  * @section Description
28  *
29  * This module implements the User-based Security Model (USM) for Simple
30  * Network Management Protocol (SNMP) version 3. Refer to the following
31  * RFCs for complete details:
32  * - RFC 3414: User-based Security Model (USM) for SNMPv3
33  * - RFC 3826: AES Cipher Algorithm in the SNMP User-based Security Model
34  * - RFC 7860: HMAC-SHA-2 Authentication Protocols in the User-based Security Model
35  *
36  * @author Oryx Embedded SARL (www.oryx-embedded.com)
37  * @version 2.4.0
38  **/
39 
40 //Switch to the appropriate trace level
41 #define TRACE_LEVEL SNMP_TRACE_LEVEL
42 
43 //Dependencies
44 #include "core/net.h"
45 #include "snmp/snmp_agent.h"
46 #include "snmp/snmp_agent_usm.h"
47 #include "core/crypto.h"
48 #include "encoding/asn1.h"
49 #include "mac/hmac.h"
50 #include "debug.h"
51 
52 //Check TCP/IP stack configuration
53 #if (SNMP_AGENT_SUPPORT == ENABLED && SNMP_V3_SUPPORT == ENABLED)
54 
55 //usmStatsUnsupportedSecLevels.0 object (1.3.6.1.6.3.15.1.1.1.0)
56 const uint8_t usmStatsUnsupportedSecLevelsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 1, 0};
57 //usmStatsNotInTimeWindows.0 object (1.3.6.1.6.3.15.1.1.2.0)
58 const uint8_t usmStatsNotInTimeWindowsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 2, 0};
59 //usmStatsUnknownUserNames.0 object (1.3.6.1.6.3.15.1.1.3.0)
60 const uint8_t usmStatsUnknownUserNamesObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 3, 0};
61 //usmStatsUnknownEngineIDs.0 object (1.3.6.1.6.3.15.1.1.4.0)
62 const uint8_t usmStatsUnknownEngineIdsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 4, 0};
63 //usmStatsWrongDigests.0 object (1.3.6.1.6.3.15.1.1.5.0)
64 const uint8_t usmStatsWrongDigestsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 5, 0};
65 //usmStatsDecryptionErrors.0 object (1.3.6.1.6.3.15.1.1.6.0)
66 const uint8_t usmStatsDecryptionErrorsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 6, 0};
67 
68 
69 /**
70  * @brief Create a new user entry
71  * @param[in] context Pointer to the SNMP agent context
72  * @return Pointer to the newly created entry
73  **/
74 
76 {
77  uint_t i;
78  SnmpUserEntry *entry;
79 
80  //Initialize pointer
81  entry = NULL;
82 
83  //Sanity check
84  if(context != NULL)
85  {
86  //Loop through the list of users
87  for(i = 0; i < SNMP_AGENT_MAX_USERS; i++)
88  {
89  //Check current status
90  if(context->userTable[i].status == MIB_ROW_STATUS_UNUSED)
91  {
92  //An unused entry has been found
93  entry = &context->userTable[i];
94  //We are done
95  break;
96  }
97  }
98 
99  //Check whether the user table runs out of space
100  if(entry == NULL)
101  {
102  //Loop through the list of users
103  for(i = 0; i < SNMP_AGENT_MAX_USERS; i++)
104  {
105  //Check current status
106  if(context->userTable[i].status == MIB_ROW_STATUS_NOT_READY)
107  {
108  //Reuse the current entry
109  entry = &context->userTable[i];
110  //We are done
111  break;
112  }
113  }
114  }
115  }
116 
117  //Return a pointer to the newly created entry
118  return entry;
119 }
120 
121 
122 /**
123  * @brief Search the user table for a given user name
124  * @param[in] context Pointer to the SNMP agent context
125  * @param[in] name Pointer to the user name
126  * @param[in] length Length of the user name
127  * @return Pointer to the matching entry
128  **/
129 
131  const char_t *name, size_t length)
132 {
133  uint_t i;
134  SnmpUserEntry *entry;
135 
136  //Initialize pointer
137  entry = NULL;
138 
139  //Sanity check
140  if(context != NULL && name != NULL)
141  {
142  //Loop through the list of users
143  for(i = 0; i < SNMP_AGENT_MAX_USERS; i++)
144  {
145  //Check current status
146  if(context->userTable[i].status != MIB_ROW_STATUS_UNUSED)
147  {
148  //Check the length of the user name
149  if(osStrlen(context->userTable[i].name) == length)
150  {
151  //Compare user names
152  if(!osStrncmp(context->userTable[i].name, name, length))
153  {
154  //A matching entry has been found
155  entry = &context->userTable[i];
156  //We are done
157  break;
158  }
159  }
160  }
161  }
162  }
163 
164  //Return a pointer to the matching entry
165  return entry;
166 }
167 
168 
169 /**
170  * @brief Password to key algorithm
171  * @param[in] authProtocol Authentication protocol (MD5, SHA-1, SHA-224,
172  * SHA-256, SHA384 or SHA512)
173  * @param[in] password NULL-terminated string that contains the password
174  * @param[out] key Pointer to the resulting key (Ku)
175  * @return Error code
176  **/
177 
178 error_t snmpGenerateKey(SnmpAuthProtocol authProtocol, const char_t *password,
179  SnmpKey *key)
180 {
181  size_t i;
182  size_t n;
183  size_t passwordLen;
184  const HashAlgo *hashAlgo;
185  HashContext hashContext;
186 
187  //Check parameters
188  if(password == NULL || key == NULL)
190 
191  //Clear SNMP key
192  osMemset(key, 0, sizeof(SnmpKey));
193 
194  //Get the hash algorithm to be used to generate the key
195  hashAlgo = snmpGetHashAlgo(authProtocol);
196 
197  //Invalid authentication protocol?
198  if(hashAlgo == NULL)
200 
201  //Retrieve the length of the password
202  passwordLen = osStrlen(password);
203 
204  //SNMP implementations must ensure that passwords are at least 8 characters
205  //in length (see RFC 3414 11.2)
206  if(passwordLen < 8)
207  return ERROR_INVALID_LENGTH;
208 
209  //Initialize hash context
210  hashAlgo->init(&hashContext);
211 
212  //Loop until we have done 1 megabyte
213  for(i = 0; i < 1048576; i += n)
214  {
215  n = MIN(passwordLen, 1048576 - i);
216  hashAlgo->update(&hashContext, password, n);
217  }
218 
219  //Finalize hash computation
220  hashAlgo->final(&hashContext, key->b);
221 
222  //Successful processing
223  return NO_ERROR;
224 }
225 
226 
227 /**
228  * @brief Key localization algorithm
229  * @param[in] authProtocol Authentication protocol (MD5, SHA-1, SHA-224,
230  * SHA-256, SHA384 or SHA512)
231  * @param[in] engineId Pointer to the engine ID
232  * @param[in] engineIdLen Length of the engine ID
233  * @param[in] key Pointer to the key to be localized (Ku)
234  * @param[out] localizedKey Pointer to the resulting key (Kul)
235  * @return Error code
236  **/
237 
238 error_t snmpLocalizeKey(SnmpAuthProtocol authProtocol, const uint8_t *engineId,
239  size_t engineIdLen, SnmpKey *key, SnmpKey *localizedKey)
240 {
241  const HashAlgo *hashAlgo;
242  HashContext hashContext;
243 
244  //Check parameters
245  if(engineId == NULL && engineIdLen > 0)
247  if(key == NULL || localizedKey == NULL)
249 
250  //Get the hash algorithm to be used to generate the key
251  hashAlgo = snmpGetHashAlgo(authProtocol);
252 
253  //Invalid authentication protocol?
254  if(hashAlgo == NULL)
256 
257  //Localize the key with the engine ID
258  hashAlgo->init(&hashContext);
259  hashAlgo->update(&hashContext, key, hashAlgo->digestSize);
260  hashAlgo->update(&hashContext, engineId, engineIdLen);
261  hashAlgo->update(&hashContext, key, hashAlgo->digestSize);
262  hashAlgo->final(&hashContext, localizedKey->b);
263 
264  //Successful processing
265  return NO_ERROR;
266 }
267 
268 
269 /**
270  * @brief Change secret key
271  * @param[in] hashAlgo Hash algorithm to be used
272  * @param[in] random Pointer to the random component
273  * @param[in] delta Pointer to the delta component
274  * @param[in,out] key Pointer to the secret key K
275  **/
276 
277 void snmpChangeKey(const HashAlgo *hashAlgo, const uint8_t *random,
278  const uint8_t *delta, SnmpKey *key)
279 {
280  uint_t i;
281  HashContext hashContext;
282  uint8_t digest[SNMP_MAX_KEY_SIZE];
283 
284  //The random component is appended to the existing value of the K, and the
285  //result is input to the hash algorithm H to produce a digest value
286  hashAlgo->init(&hashContext);
287  hashAlgo->update(&hashContext, key, hashAlgo->digestSize);
288  hashAlgo->update(&hashContext, random, hashAlgo->digestSize);
289  hashAlgo->final(&hashContext, digest);
290 
291  //This digest value is XOR-ed with the unused portion of the delta component
292  //to produce the new value of K
293  for(i = 0; i < hashAlgo->digestSize; i++)
294  {
295  key->b[i] = digest[i] ^ delta[i];
296  }
297 }
298 
299 
300 /**
301  * @brief Clone security parameters
302  * @param[in,out] user Security profile of the user
303  * @param[in] cloneFromUser Security profile of the clone-from user
304  **/
305 
307  const SnmpUserEntry *cloneFromUser)
308 {
309  //Clone security parameters
310  user->mode = cloneFromUser->mode;
311  user->authProtocol = cloneFromUser->authProtocol;
312  user->rawAuthKey = cloneFromUser->rawAuthKey;
313  user->localizedAuthKey = cloneFromUser->localizedAuthKey;
314  user->privProtocol = cloneFromUser->privProtocol;
315  user->rawPrivKey = cloneFromUser->rawPrivKey;
316  user->localizedPrivKey = cloneFromUser->localizedPrivKey;
317 }
318 
319 
320 /**
321  * @brief Check security parameters
322  * @param[in] user Security profile of the user
323  * @param[in,out] message Pointer to the incoming SNMP message
324  * @param[in] engineId Pointer to the authoritative engine ID
325  * @param[in] engineIdLen Length of the authoritative engine ID
326  * @return Error code
327  **/
328 
330  SnmpMessage *message, const uint8_t *engineId, size_t engineIdLen)
331 {
332  //Check the length of the authoritative engine ID
333  if(message->msgAuthEngineIdLen != engineIdLen)
335 
336  //If the value of the msgAuthoritativeEngineID field is unknown, then an
337  //error indication (unknownEngineID) is returned to the calling module
338  if(osMemcmp(message->msgAuthEngineId, engineId, engineIdLen))
340 
341  //If no information is available for the user, then an error indication
342  //(unknownSecurityName) is returned to the calling module
343  if(user == NULL || user->status != MIB_ROW_STATUS_ACTIVE)
345 
346  //Check whether the securityLevel specifies that the message should
347  //be authenticated
349  {
350  //Make sure the authFlag is set
351  if((message->msgFlags & SNMP_MSG_FLAG_AUTH) == 0)
353  }
354 
355  //Check whether the securityLevel specifies that the message should
356  //be encrypted
358  {
359  //Make sure the privFlag is set
360  if((message->msgFlags & SNMP_MSG_FLAG_PRIV) == 0)
362  }
363 
364  //Security parameters are valid
365  return NO_ERROR;
366 }
367 
368 
369 /**
370  * @brief Refresh SNMP engine time
371  * @param[in] context Pointer to the SNMP agent context
372  **/
373 
375 {
377  int32_t newEngineTime;
378 
379  //Number of seconds elapsed since the last call
380  delta = (osGetSystemTime() - context->systemTime) / 1000;
381  //Increment SNMP engine time
382  newEngineTime = context->engineTime + delta;
383 
384  //Check whether the SNMP engine time has rolled over
385  if(newEngineTime < context->engineTime)
386  {
387  //If snmpEngineTime ever reaches its maximum value (2147483647), then
388  //snmpEngineBoots is incremented as if the SNMP engine has re-booted
389  //and snmpEngineTime is reset to zero and starts incrementing again
390  context->engineBoots++;
391  context->engineTime = 0;
392  }
393  else
394  {
395  //Update SNMP engine time
396  context->engineTime = newEngineTime;
397  }
398 
399  //Save timestamp
400  context->systemTime += delta * 1000;
401 }
402 
403 
404 /**
405  * @brief Replay protection
406  * @param[in] context Pointer to the SNMP agent context
407  * @param[in,out] message Pointer to the incoming SNMP message
408  * @return Error code
409  **/
410 
412 {
413  error_t error;
414 
415 #if (SNMP_AGENT_INFORM_SUPPORT == ENABLED)
416  //Check whether the discovery process is complete
417  if(context->informContextEngineLen > 0)
418  {
419  //Compare engine IDs
420  if(message->msgAuthEngineIdLen == context->informContextEngineLen)
421  {
422  if(!osMemcmp(message->msgAuthEngineId, context->informContextEngine,
423  context->informContextEngineLen))
424  {
425  //We are done
426  return NO_ERROR;
427  }
428  }
429  }
430 #endif
431 
432  //Initialize status code
433  error = NO_ERROR;
434 
435  //If any of the following conditions is true, then the message is
436  //considered to be outside of the time window
437  if(context->engineBoots == INT32_MAX)
438  {
439  //The local value of snmpEngineBoots is 2147483647
440  error = ERROR_NOT_IN_TIME_WINDOW;
441  }
442  else if(context->engineBoots != message->msgAuthEngineBoots)
443  {
444  //The value of the msgAuthoritativeEngineBoots field differs from
445  //the local value of snmpEngineBoots
446  error = ERROR_NOT_IN_TIME_WINDOW;
447  }
448  else if((context->engineTime - message->msgAuthEngineTime) > SNMP_TIME_WINDOW ||
449  (message->msgAuthEngineTime - context->engineTime) > SNMP_TIME_WINDOW)
450  {
451  //The value of the msgAuthoritativeEngineTime field differs from the
452  //local notion of snmpEngineTime by more than +/- 150 seconds
453  error = ERROR_NOT_IN_TIME_WINDOW;
454  }
455 
456  //If the message is considered to be outside of the time window then an
457  //error indication (notInTimeWindow) is returned to the calling module
458  return error;
459 }
460 
461 
462 /**
463  * @brief Authenticate outgoing SNMP message
464  * @param[in] user Security profile of the user
465  * @param[in,out] message Pointer to the outgoing SNMP message
466  * @return Error code
467  **/
468 
470 {
471  const HashAlgo *hashAlgo;
472  size_t macLen;
473  HmacContext hmacContext;
474 
475  //Get the hash algorithm to be used for HMAC computation
476  hashAlgo = snmpGetHashAlgo(user->authProtocol);
477 
478  //Invalid authentication protocol?
479  if(hashAlgo == NULL)
480  return ERROR_FAILURE;
481 
482  //Retrieve the length of the truncated MAC
483  macLen = snmpGetMacLength(user->authProtocol);
484 
485  //Check the length of the msgAuthenticationParameters field
486  if(message->msgAuthParametersLen != macLen)
487  return ERROR_FAILURE;
488 
489  //The MAC is calculated over the whole message
490  hmacInit(&hmacContext, hashAlgo, user->localizedAuthKey.b, hashAlgo->digestSize);
491  hmacUpdate(&hmacContext, message->pos, message->length);
492  hmacFinal(&hmacContext, NULL);
493 
494  //Replace the msgAuthenticationParameters field with the calculated MAC
495  osMemcpy(message->msgAuthParameters, hmacContext.digest, macLen);
496 
497  //Successful message authentication
498  return NO_ERROR;
499 }
500 
501 
502 /**
503  * @brief Authenticate incoming SNMP message
504  * @param[in] user Security profile of the user
505  * @param[in] message Pointer to the incoming SNMP message
506  * @return Error code
507  **/
508 
510 {
511  const HashAlgo *hashAlgo;
512  size_t macLen;
513  uint8_t mac[SNMP_MAX_TRUNCATED_MAC_SIZE];
514  HmacContext hmacContext;
515 
516  //Get the hash algorithm to be used for HMAC computation
517  hashAlgo = snmpGetHashAlgo(user->authProtocol);
518 
519  //Invalid authentication protocol?
520  if(hashAlgo == NULL)
522 
523  //Retrieve the length of the truncated MAC
524  macLen = snmpGetMacLength(user->authProtocol);
525 
526  //Check the length of the msgAuthenticationParameters field
527  if(message->msgAuthParametersLen != macLen)
529 
530  //The MAC received in the msgAuthenticationParameters field is saved
531  osMemcpy(mac, message->msgAuthParameters, macLen);
532 
533  //The digest in the msgAuthenticationParameters field is replaced by
534  //a null octet string
535  osMemset(message->msgAuthParameters, 0, macLen);
536 
537  //The MAC is calculated over the whole message
538  hmacInit(&hmacContext, hashAlgo, user->localizedAuthKey.b, hashAlgo->digestSize);
539  hmacUpdate(&hmacContext, message->buffer, message->bufferLen);
540  hmacFinal(&hmacContext, NULL);
541 
542  //Restore the value of the msgAuthenticationParameters field
543  osMemcpy(message->msgAuthParameters, mac, macLen);
544 
545  //The newly calculated MAC is compared with the MAC value that was
546  //saved in the first step
547  if(osMemcmp(hmacContext.digest, mac, macLen))
549 
550  //Successful message authentication
551  return NO_ERROR;
552 }
553 
554 
555 /**
556  * @brief Data encryption
557  * @param[in] user Security profile of the user
558  * @param[in,out] message Pointer to the outgoing SNMP message
559  * @param[in,out] salt Pointer to the salt integer
560  * @return Error code
561  **/
562 
564  uint64_t *salt)
565 {
566  error_t error;
567  uint_t i;
568  size_t n;
569  Asn1Tag tag;
570 
571  //Debug message
572  TRACE_DEBUG("Scoped PDU (%" PRIuSIZE " bytes):\r\n", message->length);
573  //Display the contents of the scopedPDU
574  TRACE_DEBUG_ARRAY(" ", message->pos, message->length);
575  //Display ASN.1 structure
576  asn1DumpObject(message->pos, message->length, 0);
577 
578 #if (SNMP_DES_SUPPORT == ENABLED)
579  //DES-CBC privacy protocol?
581  {
582  DesContext desContext;
583  uint8_t iv[DES_BLOCK_SIZE];
584 
585  //The data to be encrypted is treated as sequence of octets. Its length
586  //should be an integral multiple of 8
587  if((message->length % 8) != 0)
588  {
589  //If it is not, the data is padded at the end as necessary
590  n = 8 - (message->length % 8);
591  //The actual pad value is irrelevant
592  osMemset(message->pos + message->length, n, n);
593  //Update the length of the data
594  message->length += n;
595  }
596 
597  //The 32-bit snmpEngineBoots is converted to the first 4 octets of our salt
598  STORE32BE(message->msgAuthEngineBoots, message->msgPrivParameters);
599  //The 32-bit integer is then converted to the last 4 octet of our salt
600  STORE32BE(*salt, message->msgPrivParameters + 4);
601 
602  //The resulting salt is then put into the msgPrivacyParameters field
603  message->msgPrivParametersLen = 8;
604 
605  //Initialize DES context
606  error = desInit(&desContext, user->localizedPrivKey.b, 8);
607  //Initialization failed?
608  if(error)
609  return error;
610 
611  //The last 8 octets of the 16-octet secret (private privacy key) are
612  //used as pre-IV
614 
615  //The msgPrivacyParameters field is XOR-ed with the pre-IV to obtain the IV
616  for(i = 0; i < DES_BLOCK_SIZE; i++)
617  {
618  iv[i] ^= message->msgPrivParameters[i];
619  }
620 
621  //Perform CBC encryption
622  error = cbcEncrypt(DES_CIPHER_ALGO, &desContext, iv, message->pos,
623  message->pos, message->length);
624  //Any error to report?
625  if(error)
626  return error;
627  }
628  else
629 #endif
630 #if (SNMP_AES_SUPPORT == ENABLED)
631  //AES-128-CFB privacy protocol?
633  {
634  AesContext aesContext;
635  uint8_t iv[AES_BLOCK_SIZE];
636 
637  //The 32-bit snmpEngineBoots is converted to the first 4 octets of the IV
638  STORE32BE(message->msgAuthEngineBoots, iv);
639  //The 32-bit snmpEngineTime is converted to the subsequent 4 octets
640  STORE32BE(message->msgAuthEngineTime, iv + 4);
641  //The 64-bit integer is then converted to the last 8 octets
642  STORE64BE(*salt, iv + 8);
643 
644  //The 64-bit integer must be placed in the msgPrivacyParameters field to
645  //enable the receiving entity to compute the correct IV and to decrypt
646  //the message
647  STORE64BE(*salt, message->msgPrivParameters);
648  message->msgPrivParametersLen = 8;
649 
650  //Initialize AES context
651  error = aesInit(&aesContext, user->localizedPrivKey.b, 16);
652  //Initialization failed?
653  if(error)
654  return error;
655 
656  //Perform CFB-128 encryption
657  error = cfbEncrypt(AES_CIPHER_ALGO, &aesContext, 128, iv, message->pos,
658  message->pos, message->length);
659  //Any error to report?
660  if(error)
661  return error;
662  }
663  else
664 #endif
665  //Invalid privacy protocol?
666  {
667  //Report an error
668  return ERROR_FAILURE;
669  }
670 
671  //The encryptedPDU is encapsulated within an octet string
672  tag.constructed = FALSE;
675  tag.length = message->length;
676  tag.value = NULL;
677 
678  //Write the corresponding ASN.1 tag
679  error = asn1WriteTag(&tag, TRUE, message->pos, &n);
680  //Any error to report?
681  if(error)
682  return error;
683 
684  //Move backward
685  message->pos -= n;
686  //Total length of the encryptedPDU
687  message->length += n;
688 
689  //The salt integer is then modified. It is incremented by one and wrap
690  //when it reaches its maximum value
691  *salt += 1;
692 
693  //Successful encryption
694  return NO_ERROR;
695 }
696 
697 
698 /**
699  * @brief Data decryption
700  * @param[in] user Security profile of the user
701  * @param[in,out] message Pointer to the incoming SNMP message
702  * @return Error code
703  **/
704 
706 {
707  error_t error;
708  uint_t i;
709  Asn1Tag tag;
710 
711  //The encryptedPDU is encapsulated within an octet string
712  error = asn1ReadTag(message->pos, message->length, &tag);
713  //Failed to decode ASN.1 tag?
714  if(error)
715  return error;
716 
717  //Enforce encoding, class and type
719  //The tag does not match the criteria?
720  if(error)
721  return error;
722 
723  //Point to the encryptedPDU
724  message->pos = (uint8_t *) tag.value;
725  //Length of the encryptedPDU
726  message->length = tag.length;
727 
728 #if (SNMP_DES_SUPPORT == ENABLED)
729  //DES-CBC privacy protocol?
731  {
732  DesContext desContext;
733  uint8_t iv[DES_BLOCK_SIZE];
734 
735  //Before decryption, the encrypted data length is verified. The length
736  //of the encrypted data must be a multiple of 8 octets
737  if((message->length % 8) != 0)
739 
740  //Check the length of the msgPrivacyParameters field
741  if(message->msgPrivParametersLen != 8)
743 
744  //Initialize DES context
745  error = desInit(&desContext, user->localizedPrivKey.b, 8);
746  //Initialization failed?
747  if(error)
748  return error;
749 
750  //The last 8 octets of the 16-octet secret (private privacy key) are
751  //used as pre-IV
753 
754  //The msgPrivacyParameters field is XOR-ed with the pre-IV to obtain the IV
755  for(i = 0; i < DES_BLOCK_SIZE; i++)
756  {
757  iv[i] ^= message->msgPrivParameters[i];
758  }
759 
760  //Perform CBC decryption
761  error = cbcDecrypt(DES_CIPHER_ALGO, &desContext, iv, message->pos,
762  message->pos, message->length);
763  //Any error to report?
764  if(error)
765  return error;
766  }
767  else
768 #endif
769 #if (SNMP_AES_SUPPORT == ENABLED)
770  //AES-128-CFB privacy protocol?
772  {
773  AesContext aesContext;
774  uint8_t iv[AES_BLOCK_SIZE];
775 
776  //Check the length of the msgPrivacyParameters field
777  if(message->msgPrivParametersLen != 8)
779 
780  //The 32-bit snmpEngineBoots is converted to the first 4 octets of the IV
781  STORE32BE(message->msgAuthEngineBoots, iv);
782  //The 32-bit snmpEngineTime is converted to the subsequent 4 octets
783  STORE32BE(message->msgAuthEngineTime, iv + 4);
784  //The 64-bit integer is then converted to the last 8 octets
785  osMemcpy(iv + 8, message->msgPrivParameters, 8);
786 
787  //Initialize AES context
788  error = aesInit(&aesContext, user->localizedPrivKey.b, 16);
789  //Initialization failed?
790  if(error)
791  return error;
792 
793  //Perform CFB-128 encryption
794  error = cfbDecrypt(AES_CIPHER_ALGO, &aesContext, 128, iv, message->pos,
795  message->pos, message->length);
796  //Any error to report?
797  if(error)
798  return error;
799  }
800  else
801 #endif
802  //Invalid privacy protocol?
803  {
804  //Report an error
806  }
807 
808  //Debug message
809  TRACE_DEBUG("Scoped PDU (%" PRIuSIZE " bytes):\r\n", message->length);
810  //Display the contents of the scopedPDU
811  TRACE_DEBUG_ARRAY(" ", message->pos, message->length);
812  //Display ASN.1 structure
813  asn1DumpObject(message->pos, message->length, 0);
814 
815  //Successful decryption
816  return NO_ERROR;
817 }
818 
819 
820 /**
821  * @brief Get the hash algorithm to be used for a given authentication protocol
822  * @param[in] authProtocol Authentication protocol (MD5, SHA-1, SHA-224,
823  * SHA-256, SHA384 or SHA512)
824  * @return Pointer to the corresponding hash algorithm
825  **/
826 
828 {
829  const HashAlgo *hashAlgo;
830 
831 #if (SNMP_MD5_SUPPORT == ENABLED)
832  //HMAC-MD5-96 authentication protocol?
833  if(authProtocol == SNMP_AUTH_PROTOCOL_MD5)
834  {
835  //Use MD5 hash algorithm
836  hashAlgo = MD5_HASH_ALGO;
837  }
838  else
839 #endif
840 #if (SNMP_SHA1_SUPPORT == ENABLED)
841  //HMAC-SHA-1-96 authentication protocol?
842  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
843  {
844  //Use SHA-1 hash algorithm
845  hashAlgo = SHA1_HASH_ALGO;
846  }
847  else
848 #endif
849 #if (SNMP_SHA224_SUPPORT == ENABLED)
850  //HMAC-SHA-224-128 authentication protocol?
851  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
852  {
853  //Use SHA-224 hash algorithm
854  hashAlgo = SHA224_HASH_ALGO;
855  }
856  else
857 #endif
858 #if (SNMP_SHA256_SUPPORT == ENABLED)
859  //HMAC-SHA-256-192 authentication protocol?
860  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
861  {
862  //Use SHA-256 hash algorithm
863  hashAlgo = SHA256_HASH_ALGO;
864  }
865  else
866 #endif
867 #if (SNMP_SHA384_SUPPORT == ENABLED)
868  //HMAC-SHA-384-256 authentication protocol?
869  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
870  {
871  //Use SHA-384 hash algorithm
872  hashAlgo = SHA384_HASH_ALGO;
873  }
874  else
875 #endif
876 #if (SNMP_SHA512_SUPPORT == ENABLED)
877  //HMAC-SHA-512-384 authentication protocol?
878  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
879  {
880  //Use SHA-512 hash algorithm
881  hashAlgo = SHA512_HASH_ALGO;
882  }
883  else
884 #endif
885  //Invalid authentication protocol?
886  {
887  //The authentication protocol is not supported
888  hashAlgo = NULL;
889  }
890 
891  //Return the hash algorithm to be used
892  return hashAlgo;
893 }
894 
895 
896 /**
897  * @brief Get the length of the truncated MAC for a given authentication protocol
898  * @param[in] authProtocol Authentication protocol (MD5, SHA-1, SHA-224,
899  * SHA-256, SHA384 or SHA512)
900  * @return Length of the truncated MAC, in bytes
901  **/
902 
903 size_t snmpGetMacLength(SnmpAuthProtocol authProtocol)
904 {
905  size_t macLen;
906 
907 #if (SNMP_MD5_SUPPORT == ENABLED)
908  //HMAC-MD5-96 authentication protocol?
909  if(authProtocol == SNMP_AUTH_PROTOCOL_MD5)
910  {
911  //The length of the truncated MAC is 96 bits
912  macLen = 12;
913  }
914  else
915 #endif
916 #if (SNMP_SHA1_SUPPORT == ENABLED)
917  //HMAC-SHA-1-96 authentication protocol?
918  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
919  {
920  //The length of the truncated MAC is 96 bits
921  macLen = 12;
922  }
923  else
924 #endif
925 #if (SNMP_SHA224_SUPPORT == ENABLED)
926  //HMAC-SHA-224-128 authentication protocol?
927  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
928  {
929  //The length of the truncated MAC is 128 bits
930  macLen = 16;
931  }
932  else
933 #endif
934 #if (SNMP_SHA256_SUPPORT == ENABLED)
935  //HMAC-SHA-256-192 authentication protocol?
936  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
937  {
938  //The length of the truncated MAC is 192 bits
939  macLen = 24;
940  }
941  else
942 #endif
943 #if (SNMP_SHA384_SUPPORT == ENABLED)
944  //HMAC-SHA-384-256 authentication protocol?
945  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
946  {
947  //The length of the truncated MAC is 256 bits
948  macLen = 32;
949  }
950  else
951 #endif
952 #if (SNMP_SHA512_SUPPORT == ENABLED)
953  //HMAC-SHA-512-384 authentication protocol?
954  if(authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
955  {
956  //The length of the truncated MAC is 384 bits
957  macLen = 48;
958  }
959  else
960 #endif
961  //Invalid authentication protocol?
962  {
963  //The authentication protocol is not supported
964  macLen = 0;
965  }
966 
967  //Return the length of the truncated MAC
968  return macLen;
969 }
970 
971 #endif
__weak_func error_t aesInit(AesContext *context, const uint8_t *key, size_t keyLen)
Key expansion.
Definition: aes.c:242
#define AES_CIPHER_ALGO
Definition: aes.h:45
#define AES_BLOCK_SIZE
Definition: aes.h:43
error_t asn1ReadTag(const uint8_t *data, size_t length, Asn1Tag *tag)
Read an ASN.1 tag from the input stream.
Definition: asn1.c:52
error_t asn1WriteTag(Asn1Tag *tag, bool_t reverse, uint8_t *data, size_t *written)
Write an ASN.1 tag.
Definition: asn1.c:334
error_t asn1CheckTag(const Asn1Tag *tag, bool_t constructed, uint_t objClass, uint_t objType)
Enforce the type of a specified tag.
Definition: asn1.c:653
error_t asn1DumpObject(const uint8_t *data, size_t length, uint_t level)
Display an ASN.1 data object.
Definition: asn1.c:706
ASN.1 (Abstract Syntax Notation One)
@ ASN1_TYPE_OCTET_STRING
Definition: asn1.h:72
#define ASN1_CLASS_UNIVERSAL
Definition: asn1.h:52
__weak_func error_t cbcDecrypt(const CipherAlgo *cipher, void *context, uint8_t *iv, const uint8_t *c, uint8_t *p, size_t length)
CBC decryption.
Definition: cbc.c:108
__weak_func error_t cbcEncrypt(const CipherAlgo *cipher, void *context, uint8_t *iv, const uint8_t *p, uint8_t *c, size_t length)
CBC encryption.
Definition: cbc.c:61
__weak_func error_t cfbEncrypt(const CipherAlgo *cipher, void *context, uint_t s, uint8_t *iv, const uint8_t *p, uint8_t *c, size_t length)
CFB encryption.
Definition: cfb.c:64
__weak_func error_t cfbDecrypt(const CipherAlgo *cipher, void *context, uint_t s, uint8_t *iv, const uint8_t *c, uint8_t *p, size_t length)
CFB decryption.
Definition: cfb.c:124
uint8_t message[]
Definition: chap.h:154
uint8_t delta
Definition: coap_common.h:196
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
char char_t
Definition: compiler_port.h:48
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
#define STORE64BE(a, p)
Definition: cpu_endian.h:322
General definitions for cryptographic algorithms.
Debugging facilities.
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:108
#define TRACE_DEBUG(...)
Definition: debug.h:107
__weak_func error_t desInit(DesContext *context, const uint8_t *key, size_t keyLen)
Initialize a DES context using the supplied key.
Definition: des.c:296
#define DES_BLOCK_SIZE
Definition: des.h:43
#define DES_CIPHER_ALGO
Definition: des.h:45
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_UNKNOWN_ENGINE_ID
Definition: error.h:260
@ ERROR_DECRYPTION_FAILED
Definition: error.h:241
@ ERROR_NOT_IN_TIME_WINDOW
Definition: error.h:265
@ ERROR_UNSUPPORTED_SECURITY_LEVEL
Definition: error.h:264
@ ERROR_AUTHENTICATION_FAILED
Definition: error.h:69
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_UNKNOWN_USER_NAME
Definition: error.h:261
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
__weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen)
Initialize HMAC calculation.
Definition: hmac.c:140
__weak_func void hmacFinal(HmacContext *context, uint8_t *digest)
Finish the HMAC calculation.
Definition: hmac.c:218
__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
HMAC (Keyed-Hashing for Message Authentication)
uint8_t iv[]
Definition: ike.h:1502
#define MD5_HASH_ALGO
Definition: md5.h:49
@ MIB_ROW_STATUS_UNUSED
Definition: mib_common.h:102
@ MIB_ROW_STATUS_ACTIVE
Definition: mib_common.h:103
@ MIB_ROW_STATUS_NOT_READY
Definition: mib_common.h:105
TCP/IP stack core.
#define osMemset(p, value, length)
Definition: os_port.h:135
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define MIN(a, b)
Definition: os_port.h:63
#define osMemcmp(p1, p2, length)
Definition: os_port.h:153
#define osStrlen(s)
Definition: os_port.h:165
#define ENABLED
Definition: os_port.h:37
#define osStrncmp(s1, s2, length)
Definition: os_port.h:177
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t systime_t
System time.
char_t name[]
#define SHA1_HASH_ALGO
Definition: sha1.h:49
#define SHA224_HASH_ALGO
Definition: sha224.h:45
#define SHA256_HASH_ALGO
Definition: sha256.h:49
#define SHA384_HASH_ALGO
Definition: sha384.h:45
#define SHA512_HASH_ALGO
Definition: sha512.h:49
SNMP agent (Simple Network Management Protocol)
#define SnmpAgentContext
Definition: snmp_agent.h:36
#define SNMP_AGENT_MAX_USERS
Definition: snmp_agent.h:83
void snmpChangeKey(const HashAlgo *hashAlgo, const uint8_t *random, const uint8_t *delta, SnmpKey *key)
Change secret key.
error_t snmpLocalizeKey(SnmpAuthProtocol authProtocol, const uint8_t *engineId, size_t engineIdLen, SnmpKey *key, SnmpKey *localizedKey)
Key localization algorithm.
const uint8_t usmStatsUnknownUserNamesObject[10]
error_t snmpEncryptData(const SnmpUserEntry *user, SnmpMessage *message, uint64_t *salt)
Data encryption.
error_t snmpGenerateKey(SnmpAuthProtocol authProtocol, const char_t *password, SnmpKey *key)
Password to key algorithm.
error_t snmpCheckEngineTime(SnmpAgentContext *context, SnmpMessage *message)
Replay protection.
size_t snmpGetMacLength(SnmpAuthProtocol authProtocol)
Get the length of the truncated MAC for a given authentication protocol.
const uint8_t usmStatsUnsupportedSecLevelsObject[10]
const uint8_t usmStatsNotInTimeWindowsObject[10]
error_t snmpDecryptData(const SnmpUserEntry *user, SnmpMessage *message)
Data decryption.
void snmpRefreshEngineTime(SnmpAgentContext *context)
Refresh SNMP engine time.
void snmpCloneSecurityParameters(SnmpUserEntry *user, const SnmpUserEntry *cloneFromUser)
Clone security parameters.
const uint8_t usmStatsUnknownEngineIdsObject[10]
error_t snmpAuthIncomingMessage(const SnmpUserEntry *user, SnmpMessage *message)
Authenticate incoming SNMP message.
SnmpUserEntry * snmpCreateUserEntry(SnmpAgentContext *context)
Create a new user entry.
error_t snmpCheckSecurityParameters(const SnmpUserEntry *user, SnmpMessage *message, const uint8_t *engineId, size_t engineIdLen)
Check security parameters.
const uint8_t usmStatsDecryptionErrorsObject[10]
const HashAlgo * snmpGetHashAlgo(SnmpAuthProtocol authProtocol)
Get the hash algorithm to be used for a given authentication protocol.
error_t snmpAuthOutgoingMessage(const SnmpUserEntry *user, SnmpMessage *message)
Authenticate outgoing SNMP message.
SnmpUserEntry * snmpFindUserEntry(SnmpAgentContext *context, const char_t *name, size_t length)
Search the user table for a given user name.
const uint8_t usmStatsWrongDigestsObject[10]
User-based Security Model (USM) for SNMPv3.
SnmpAuthProtocol
@ SNMP_AUTH_PROTOCOL_SHA512
HMAC-SHA-512-384.
@ SNMP_AUTH_PROTOCOL_NONE
No authentication.
@ SNMP_AUTH_PROTOCOL_SHA224
HMAC-SHA-224-128.
@ SNMP_AUTH_PROTOCOL_MD5
HMAC-MD5-96.
@ SNMP_AUTH_PROTOCOL_SHA384
HMAC-SHA-384-256.
@ SNMP_AUTH_PROTOCOL_SHA1
HMAC-SHA-1-96.
@ SNMP_AUTH_PROTOCOL_SHA256
HMAC-SHA-256-192.
#define SNMP_MAX_KEY_SIZE
#define SNMP_DES_SUPPORT
#define SNMP_MAX_TRUNCATED_MAC_SIZE
#define SNMP_TIME_WINDOW
@ SNMP_PRIV_PROTOCOL_DES
DES-CBC.
@ SNMP_PRIV_PROTOCOL_NONE
No privacy.
@ SNMP_PRIV_PROTOCOL_AES
AES-128-CFB.
@ SNMP_MSG_FLAG_PRIV
@ SNMP_MSG_FLAG_AUTH
AES algorithm context.
Definition: aes.h:58
ASN.1 tag.
Definition: asn1.h:102
const uint8_t * value
Definition: asn1.h:107
uint_t objClass
Definition: asn1.h:104
uint_t objType
Definition: asn1.h:105
bool_t constructed
Definition: asn1.h:103
size_t length
Definition: asn1.h:106
DES algorithm context.
Definition: des.h:58
Common interface for hash algorithms.
Definition: crypto.h:1014
HashAlgoFinal final
Definition: crypto.h:1026
HashAlgoUpdate update
Definition: crypto.h:1025
size_t digestSize
Definition: crypto.h:1020
HashAlgoInit init
Definition: crypto.h:1024
HMAC algorithm context.
Definition: hmac.h:59
uint8_t digest[MAX_HASH_DIGEST_SIZE]
Definition: hmac.h:63
SNMP secret key.
uint8_t b[SNMP_MAX_KEY_SIZE]
SNMP message.
User table entry.
MibRowStatus status
Status of the user.
SnmpKey localizedAuthKey
Localized authentication key.
SnmpPrivProtocol privProtocol
Privacy protocol.
SnmpAuthProtocol authProtocol
Authentication protocol.
SnmpKey localizedPrivKey
Localized privacy key.
SnmpAccess mode
Access mode.
SnmpKey rawPrivKey
Raw privacy key.
SnmpKey rawAuthKey
Raw authentication key.
uint8_t length
Definition: tcp.h:368
uint8_t random[32]
Definition: tls.h:1754
Generic hash algorithm context.