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