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.4
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) == 0)
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
error_t snmpEncryptData(const SnmpUserEntry *user, SnmpMessage *message, uint64_t *salt)
Data encryption.
@ ERROR_UNKNOWN_ENGINE_ID
Definition: error.h:261
@ SNMP_AUTH_PROTOCOL_MD5
HMAC-MD5-96.
HashAlgoInit init
Definition: crypto.h:1056
__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
#define SNMP_MAX_KEY_SIZE
Generic hash algorithm context.
@ ERROR_UNKNOWN_USER_NAME
Definition: error.h:262
#define SHA256_HASH_ALGO
Definition: sha256.h:49
HMAC algorithm context.
Definition: hmac.h:59
error_t snmpDecryptData(const SnmpUserEntry *user, SnmpMessage *message)
Data decryption.
#define SHA1_HASH_ALGO
Definition: sha1.h:49
#define SHA512_HASH_ALGO
Definition: sha512.h:49
#define SNMP_MAX_TRUNCATED_MAC_SIZE
SnmpKey localizedPrivKey
Localized privacy key.
uint8_t delta
Definition: coap_common.h:196
@ ERROR_DECRYPTION_FAILED
Definition: error.h:242
@ SNMP_AUTH_PROTOCOL_SHA224
HMAC-SHA-224-128.
SnmpAccess mode
Access mode.
#define SNMP_AGENT_MAX_USERS
Definition: snmp_agent.h:83
#define SNMP_TIME_WINDOW
uint8_t message[]
Definition: chap.h:154
__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
#define TRUE
Definition: os_port.h:50
size_t digestSize
Definition: crypto.h:1052
@ SNMP_AUTH_PROTOCOL_SHA384
HMAC-SHA-384-256.
error_t snmpCheckEngineTime(SnmpAgentContext *context, SnmpMessage *message)
Replay protection.
HashAlgoUpdate update
Definition: crypto.h:1057
error_t asn1DumpObject(const uint8_t *data, size_t length, uint_t level)
Display an ASN.1 data object.
Definition: asn1.c:706
#define osMemcmp(p1, p2, length)
Definition: os_port.h:153
char_t name[]
#define osStrlen(s)
Definition: os_port.h:165
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
User table entry.
@ MIB_ROW_STATUS_NOT_READY
Definition: mib_common.h:105
SNMP agent (Simple Network Management Protocol)
@ SNMP_MSG_FLAG_PRIV
AES algorithm context.
Definition: aes.h:58
void snmpCloneSecurityParameters(SnmpUserEntry *user, const SnmpUserEntry *cloneFromUser)
Clone security parameters.
#define AES_BLOCK_SIZE
Definition: aes.h:43
SnmpUserEntry * snmpCreateUserEntry(SnmpAgentContext *context)
Create a new user entry.
size_t length
Definition: asn1.h:106
SnmpKey localizedAuthKey
Localized authentication key.
const uint8_t usmStatsNotInTimeWindowsObject[10]
#define FALSE
Definition: os_port.h:46
DES algorithm context.
Definition: des.h:58
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
@ SNMP_PRIV_PROTOCOL_NONE
No privacy.
error_t
Error codes.
Definition: error.h:43
__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
#define ASN1_CLASS_UNIVERSAL
Definition: asn1.h:52
SnmpUserEntry * snmpFindUserEntry(SnmpAgentContext *context, const char_t *name, size_t length)
Search the user table for a given user name.
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
ASN.1 tag.
Definition: asn1.h:102
@ ERROR_NOT_IN_TIME_WINDOW
Definition: error.h:266
__weak_func error_t aesInit(AesContext *context, const uint8_t *key, size_t keyLen)
Key expansion.
Definition: aes.c:242
#define MD5_HASH_ALGO
Definition: md5.h:49
error_t snmpAuthIncomingMessage(const SnmpUserEntry *user, SnmpMessage *message)
Authenticate incoming SNMP message.
error_t snmpCheckSecurityParameters(const SnmpUserEntry *user, SnmpMessage *message, const uint8_t *engineId, size_t engineIdLen)
Check security parameters.
@ SNMP_AUTH_PROTOCOL_SHA256
HMAC-SHA-256-192.
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ MIB_ROW_STATUS_UNUSED
Definition: mib_common.h:102
const HashAlgo * snmpGetHashAlgo(SnmpAuthProtocol authProtocol)
Get the hash algorithm to be used for a given authentication protocol.
General definitions for cryptographic algorithms.
@ SNMP_AUTH_PROTOCOL_SHA1
HMAC-SHA-1-96.
#define SNMP_DES_SUPPORT
@ SNMP_AUTH_PROTOCOL_SHA512
HMAC-SHA-512-384.
__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
uint8_t b[SNMP_MAX_KEY_SIZE]
error_t asn1WriteTag(Asn1Tag *tag, bool_t reverse, uint8_t *data, size_t *written)
Write an ASN.1 tag.
Definition: asn1.c:334
const uint8_t usmStatsWrongDigestsObject[10]
uint8_t iv[]
Definition: ike.h:1502
const uint8_t usmStatsUnknownEngineIdsObject[10]
#define DES_CIPHER_ALGO
Definition: des.h:45
@ SNMP_MSG_FLAG_AUTH
uint_t objClass
Definition: asn1.h:104
uint8_t length
Definition: tcp.h:368
#define MIN(a, b)
Definition: os_port.h:63
MibRowStatus status
Status of the user.
#define ENABLED
Definition: os_port.h:37
void snmpChangeKey(const HashAlgo *hashAlgo, const uint8_t *random, const uint8_t *delta, SnmpKey *key)
Change secret key.
uint8_t random[32]
Definition: tls.h:1771
HashAlgoFinal final
Definition: crypto.h:1058
uint32_t systime_t
System time.
__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 SHA384_HASH_ALGO
Definition: sha384.h:45
@ ASN1_TYPE_OCTET_STRING
Definition: asn1.h:72
size_t snmpGetMacLength(SnmpAuthProtocol authProtocol)
Get the length of the truncated MAC for a given authentication protocol.
SNMP secret key.
#define TRACE_DEBUG(...)
Definition: debug.h:107
char char_t
Definition: compiler_port.h:48
#define STORE64BE(a, p)
Definition: cpu_endian.h:322
@ MIB_ROW_STATUS_ACTIVE
Definition: mib_common.h:103
uint8_t digest[MAX_HASH_DIGEST_SIZE]
Definition: hmac.h:63
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:108
SnmpAuthProtocol authProtocol
Authentication protocol.
uint8_t n
const uint8_t usmStatsDecryptionErrorsObject[10]
@ ERROR_AUTHENTICATION_FAILED
Definition: error.h:69
__weak_func void hmacFinal(HmacContext *context, uint8_t *digest)
Finish the HMAC calculation.
Definition: hmac.c:218
#define DES_BLOCK_SIZE
Definition: des.h:43
__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
SnmpKey rawAuthKey
Raw authentication key.
error_t snmpLocalizeKey(SnmpAuthProtocol authProtocol, const uint8_t *engineId, size_t engineIdLen, SnmpKey *key, SnmpKey *localizedKey)
Key localization algorithm.
SnmpAuthProtocol
bool_t constructed
Definition: asn1.h:103
SNMP message.
#define AES_CIPHER_ALGO
Definition: aes.h:45
SnmpPrivProtocol privProtocol
Privacy protocol.
#define SnmpAgentContext
Definition: snmp_agent.h:36
error_t snmpGenerateKey(SnmpAuthProtocol authProtocol, const char_t *password, SnmpKey *key)
Password to key algorithm.
#define osStrncmp(s1, s2, length)
Definition: os_port.h:177
#define SHA224_HASH_ALGO
Definition: sha224.h:45
Common interface for hash algorithms.
Definition: crypto.h:1046
const uint8_t usmStatsUnsupportedSecLevelsObject[10]
error_t snmpAuthOutgoingMessage(const SnmpUserEntry *user, SnmpMessage *message)
Authenticate outgoing SNMP message.
@ SNMP_PRIV_PROTOCOL_AES
AES-128-CFB.
void snmpRefreshEngineTime(SnmpAgentContext *context)
Refresh SNMP engine time.
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:135
TCP/IP stack core.
@ SNMP_AUTH_PROTOCOL_NONE
No authentication.
__weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen)
Initialize HMAC calculation.
Definition: hmac.c:140
@ SNMP_PRIV_PROTOCOL_DES
DES-CBC.
SnmpKey rawPrivKey
Raw privacy key.
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
const uint8_t * value
Definition: asn1.h:107
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
@ NO_ERROR
Success.
Definition: error.h:44
HMAC (Keyed-Hashing for Message Authentication)
Debugging facilities.
uint_t objType
Definition: asn1.h:105
ASN.1 (Abstract Syntax Notation One)
User-based Security Model (USM) for SNMPv3.
const uint8_t usmStatsUnknownUserNamesObject[10]
@ ERROR_UNSUPPORTED_SECURITY_LEVEL
Definition: error.h:265
systime_t osGetSystemTime(void)
Retrieve system time.