Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers snmp_usm.c Source File

snmp_usm.c

Go to the documentation of this file.
00001 /**
00002  * @file snmp_usm.c
00003  * @brief User-based Security Model (USM) for SNMPv3
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @section Description
00026  *
00027  * This module implements the User-based Security Model (USM) for Simple
00028  * Network Management Protocol (SNMP) version 3. Refer to the following
00029  * RFCs for complete details:
00030  * - RFC 3414: User-based Security Model (USM) for SNMPv3
00031  * - RFC 3826: AES Cipher Algorithm in the SNMP User-based Security Model
00032  * - RFC 7860: HMAC-SHA-2 Authentication Protocols in the User-based Security Model
00033  *
00034  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00035  * @version 1.7.6
00036  **/
00037 
00038 //Switch to the appropriate trace level
00039 #define TRACE_LEVEL SNMP_TRACE_LEVEL
00040 
00041 //Dependencies
00042 #include "core/net.h"
00043 #include "snmp/snmp_common.h"
00044 #include "snmp/snmp_usm.h"
00045 #include "crypto.h"
00046 #include "asn1.h"
00047 #include "hmac.h"
00048 #include "debug.h"
00049 
00050 //Check TCP/IP stack configuration
00051 #if (SNMP_V3_SUPPORT == ENABLED)
00052 
00053 //usmStatsUnsupportedSecLevels.0 object (1.3.6.1.6.3.15.1.1.1.0)
00054 const uint8_t usmStatsUnsupportedSecLevelsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 1, 0};
00055 //usmStatsNotInTimeWindows.0 object (1.3.6.1.6.3.15.1.1.2.0)
00056 const uint8_t usmStatsNotInTimeWindowsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 2, 0};
00057 //usmStatsUnknownUserNames.0 object (1.3.6.1.6.3.15.1.1.3.0)
00058 const uint8_t usmStatsUnknownUserNamesObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 3, 0};
00059 //usmStatsUnknownEngineIDs.0 object (1.3.6.1.6.3.15.1.1.4.0)
00060 const uint8_t usmStatsUnknownEngineIdsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 4, 0};
00061 //usmStatsWrongDigests.0 object (1.3.6.1.6.3.15.1.1.5.0)
00062 const uint8_t usmStatsWrongDigestsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 5, 0};
00063 //usmStatsDecryptionErrors.0 object (1.3.6.1.6.3.15.1.1.6.0)
00064 const uint8_t usmStatsDecryptionErrorsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 6, 0};
00065 
00066 
00067 /**
00068  * @brief Password to key algorithm
00069  * @param[in] authProtocol Authentication protocol (MD5 or SHA-1)
00070  * @param[in] password NULL-terminated string that contains the password
00071  * @param[in] engineId Pointer to the engine ID
00072  * @param[in] engineIdLen Length of the engine ID
00073  * @param[out] key Pointer to the resulting key
00074  * @return Error code
00075  **/
00076 
00077 error_t snmpGenerateKey(SnmpAuthProtocol authProtocol, const char_t *password,
00078    const uint8_t *engineId, size_t engineIdLen, SnmpKey *key)
00079 {
00080    size_t i;
00081    size_t n;
00082    size_t passwordLen;
00083    const HashAlgo *hash;
00084    uint8_t context[MAX_HASH_CONTEXT_SIZE];
00085 
00086    //Clear SNMP key
00087    memset(key, 0, sizeof(SnmpKey));
00088 
00089 #if (SNMP_MD5_SUPPORT == ENABLED)
00090    //HMAC-MD5-96 authentication protocol?
00091    if(authProtocol == SNMP_AUTH_PROTOCOL_MD5)
00092    {
00093       //Use MD5 to generate the key
00094       hash = MD5_HASH_ALGO;
00095    }
00096    else
00097 #endif
00098 #if (SNMP_SHA1_SUPPORT == ENABLED)
00099    //HMAC-SHA-1-96 authentication protocol?
00100    if(authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
00101    {
00102       //Use SHA-1 to generate the key
00103       hash = SHA1_HASH_ALGO;
00104    }
00105    else
00106 #endif
00107 #if (SNMP_SHA224_SUPPORT == ENABLED)
00108    //HMAC-SHA-224-128 authentication protocol?
00109    if(authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
00110    {
00111       //Use SHA-224 to generate the key
00112       hash = SHA224_HASH_ALGO;
00113    }
00114    else
00115 #endif
00116 #if (SNMP_SHA256_SUPPORT == ENABLED)
00117    //HMAC-SHA-256-192 authentication protocol?
00118    if(authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
00119    {
00120       //Use SHA-256 to generate the key
00121       hash = SHA256_HASH_ALGO;
00122    }
00123    else
00124 #endif
00125 #if (SNMP_SHA384_SUPPORT == ENABLED)
00126    //HMAC-SHA-384-256 authentication protocol?
00127    if(authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
00128    {
00129       //Use SHA-384 to generate the key
00130       hash = SHA384_HASH_ALGO;
00131    }
00132    else
00133 #endif
00134 #if (SNMP_SHA512_SUPPORT == ENABLED)
00135    //HMAC-SHA-512-384 authentication protocol?
00136    if(authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
00137    {
00138       //Use SHA-512 to generate the key
00139       hash = SHA512_HASH_ALGO;
00140    }
00141    else
00142 #endif
00143    {
00144       //Invalid authentication protocol
00145       return ERROR_INVALID_PARAMETER;
00146    }
00147 
00148    //Retrieve the length of the password
00149    passwordLen = strlen(password);
00150 
00151    //SNMP implementations must ensure that passwords are at
00152    //least 8 characters in length (see RFC 3414 11.2)
00153    if(passwordLen < 8)
00154       return ERROR_INVALID_LENGTH;
00155 
00156    //Initialize hash context
00157    hash->init(context);
00158 
00159    //Loop until we have done 1 megabyte
00160    for(i = 0; i < 1048576; i += n)
00161    {
00162       n = MIN(passwordLen, 1048576 - i);
00163       hash->update(context, password, n);
00164    }
00165 
00166    //Finalize hash computation
00167    hash->final(context, key->b);
00168 
00169    //Key localization
00170    hash->init(context);
00171    hash->update(context, key, hash->digestSize);
00172    hash->update(context, engineId, engineIdLen);
00173    hash->update(context, key, hash->digestSize);
00174    hash->final(context, key->b);
00175 
00176    //Successful processing
00177    return NO_ERROR;
00178 }
00179 
00180 
00181 /**
00182  * @brief Check security parameters
00183  * @param[in] user Security profile of the user
00184  * @param[in,out] message Pointer to the incoming SNMP message
00185  * @param[in] engineId Pointer to the authoritative engine ID
00186  * @param[in] engineIdLen Length of the authoritative engine ID
00187  * @return Error code
00188  **/
00189 
00190 error_t snmpCheckSecurityParameters(const SnmpUserInfo *user,
00191    SnmpMessage *message, const uint8_t *engineId, size_t engineIdLen)
00192 {
00193    //Check the length of the authoritative engine ID
00194    if(message->msgAuthEngineIdLen != engineIdLen)
00195       return ERROR_UNKNOWN_ENGINE_ID;
00196 
00197    //If the value of the msgAuthoritativeEngineID field is unknown, then an
00198    //error indication (unknownEngineID) is returned to the calling module
00199    if(memcmp(message->msgAuthEngineId, engineId, engineIdLen))
00200       return ERROR_UNKNOWN_ENGINE_ID;
00201 
00202    //If no information is available for the user, then an error indication
00203    //(unknownSecurityName) is returned to the calling module
00204    if(user == NULL)
00205       return ERROR_UNKNOWN_USER_NAME;
00206 
00207    //Check whether the securityLevel specifies that the message should
00208    //be authenticated
00209    if(user->authProtocol != SNMP_AUTH_PROTOCOL_NONE)
00210    {
00211       //Make sure the authFlag is set
00212       if(!(message->msgFlags & SNMP_MSG_FLAG_AUTH))
00213          return ERROR_UNSUPPORTED_SECURITY_LEVEL;
00214    }
00215 
00216    //Check whether the securityLevel specifies that the message should
00217    //be encrypted
00218    if(user->privProtocol != SNMP_PRIV_PROTOCOL_NONE)
00219    {
00220       //Make sure the privFlag is set
00221       if(!(message->msgFlags & SNMP_MSG_FLAG_PRIV))
00222          return ERROR_UNSUPPORTED_SECURITY_LEVEL;
00223    }
00224 
00225    //Security parameters are valid
00226    return NO_ERROR;
00227 }
00228 
00229 
00230 /**
00231  * @brief Authenticate outgoing SNMP message
00232  * @param[in] user Security profile of the user
00233  * @param[in,out] message Pointer to the outgoing SNMP message
00234  * @return Error code
00235  **/
00236 
00237 error_t snmpAuthOutgoingMessage(const SnmpUserInfo *user, SnmpMessage *message)
00238 {
00239    const HashAlgo *hash;
00240    size_t hmacDigestSize;
00241    HmacContext hmacContext;
00242 
00243 #if (SNMP_MD5_SUPPORT == ENABLED)
00244    //HMAC-MD5-96 authentication protocol?
00245    if(user->authProtocol == SNMP_AUTH_PROTOCOL_MD5)
00246    {
00247       //Use MD5 hash algorithm
00248       hash = MD5_HASH_ALGO;
00249       //Length of the message digest
00250       hmacDigestSize = 12;
00251    }
00252    else
00253 #endif
00254 #if (SNMP_SHA1_SUPPORT == ENABLED)
00255    //HMAC-SHA-1-96 authentication protocol?
00256    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
00257    {
00258       //Use SHA-1 hash algorithm
00259       hash = SHA1_HASH_ALGO;
00260       //Length of the message digest
00261       hmacDigestSize = 12;
00262    }
00263    else
00264 #endif
00265 #if (SNMP_SHA224_SUPPORT == ENABLED)
00266    //HMAC-SHA-224-128 authentication protocol?
00267    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
00268    {
00269       //Use SHA-224 hash algorithm
00270       hash = SHA224_HASH_ALGO;
00271       //Length of the message digest
00272       hmacDigestSize = 16;
00273    }
00274    else
00275 #endif
00276 #if (SNMP_SHA256_SUPPORT == ENABLED)
00277    //HMAC-SHA-256-192 authentication protocol?
00278    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
00279    {
00280       //Use SHA-256 hash algorithm
00281       hash = SHA256_HASH_ALGO;
00282       //Length of the message digest
00283       hmacDigestSize = 24;
00284    }
00285    else
00286 #endif
00287 #if (SNMP_SHA384_SUPPORT == ENABLED)
00288    //HMAC-SHA-384-256 authentication protocol?
00289    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
00290    {
00291       //Use SHA-384 hash algorithm
00292       hash = SHA384_HASH_ALGO;
00293       //Length of the message digest
00294       hmacDigestSize = 32;
00295    }
00296    else
00297 #endif
00298 #if (SNMP_SHA512_SUPPORT == ENABLED)
00299    //HMAC-SHA-512-384 authentication protocol?
00300    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
00301    {
00302       //Use SHA-512 hash algorithm
00303       hash = SHA512_HASH_ALGO;
00304       //Length of the message digest
00305       hmacDigestSize = 48;
00306    }
00307    else
00308 #endif
00309    //Invalid authentication protocol?
00310    {
00311       //Report en error
00312       return ERROR_FAILURE;
00313    }
00314 
00315    //Check the length of the msgAuthenticationParameters field
00316    if(message->msgAuthParametersLen != hmacDigestSize)
00317       return ERROR_FAILURE;
00318 
00319    //The MAC is calculated over the whole message
00320    hmacInit(&hmacContext, hash, user->authKey.b, hash->digestSize);
00321    hmacUpdate(&hmacContext, message->pos, message->length);
00322    hmacFinal(&hmacContext, NULL);
00323 
00324    //Replace the msgAuthenticationParameters field with the calculated MAC
00325    memcpy(message->msgAuthParameters, hmacContext.digest, hmacDigestSize);
00326 
00327    //Successful message authentication
00328    return NO_ERROR;
00329 }
00330 
00331 
00332 /**
00333  * @brief Authenticate incoming SNMP message
00334  * @param[in] user Security profile of the user
00335  * @param[in] message Pointer to the incoming SNMP message
00336  * @return Error code
00337  **/
00338 
00339 error_t snmpAuthIncomingMessage(const SnmpUserInfo *user, SnmpMessage *message)
00340 {
00341    const HashAlgo *hash;
00342    size_t hmacDigestSize;
00343    uint8_t hmacDigest[SNMP_MAX_HMAC_DIGEST_SIZE];
00344    HmacContext hmacContext;
00345 
00346 #if (SNMP_MD5_SUPPORT == ENABLED)
00347    //HMAC-MD5-96 authentication protocol?
00348    if(user->authProtocol == SNMP_AUTH_PROTOCOL_MD5)
00349    {
00350       //Use MD5 hash algorithm
00351       hash = MD5_HASH_ALGO;
00352       //Length of the message digest
00353       hmacDigestSize = 12;
00354    }
00355    else
00356 #endif
00357 #if (SNMP_SHA1_SUPPORT == ENABLED)
00358    //HMAC-SHA-1-96 authentication protocol?
00359    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
00360    {
00361       //Use SHA-1 hash algorithm
00362       hash = SHA1_HASH_ALGO;
00363       //Length of the message digest
00364       hmacDigestSize = 12;
00365    }
00366    else
00367 #endif
00368 #if (SNMP_SHA224_SUPPORT == ENABLED)
00369    //HMAC-SHA-224-128 authentication protocol?
00370    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
00371    {
00372       //Use SHA-224 hash algorithm
00373       hash = SHA224_HASH_ALGO;
00374       //Length of the message digest
00375       hmacDigestSize = 16;
00376    }
00377    else
00378 #endif
00379 #if (SNMP_SHA256_SUPPORT == ENABLED)
00380    //HMAC-SHA-256-192 authentication protocol?
00381    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
00382    {
00383       //Use SHA-256 hash algorithm
00384       hash = SHA256_HASH_ALGO;
00385       //Length of the message digest
00386       hmacDigestSize = 24;
00387    }
00388    else
00389 #endif
00390 #if (SNMP_SHA384_SUPPORT == ENABLED)
00391    //HMAC-SHA-384-256 authentication protocol?
00392    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
00393    {
00394       //Use SHA-384 hash algorithm
00395       hash = SHA384_HASH_ALGO;
00396       //Length of the message digest
00397       hmacDigestSize = 32;
00398    }
00399    else
00400 #endif
00401 #if (SNMP_SHA512_SUPPORT == ENABLED)
00402    //HMAC-SHA-512-384 authentication protocol?
00403    if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
00404    {
00405       //Use SHA-512 hash algorithm
00406       hash = SHA512_HASH_ALGO;
00407       //Length of the message digest
00408       hmacDigestSize = 48;
00409    }
00410    else
00411 #endif
00412    //Invalid authentication protocol?
00413    {
00414       //Report an error
00415       return ERROR_AUTHENTICATION_FAILED;
00416    }
00417 
00418    //Check the length of the msgAuthenticationParameters field
00419    if(message->msgAuthParametersLen != hmacDigestSize)
00420       return ERROR_AUTHENTICATION_FAILED;
00421 
00422    //The MAC received in the msgAuthenticationParameters field is saved
00423    memcpy(hmacDigest, message->msgAuthParameters, hmacDigestSize);
00424 
00425    //The digest in the msgAuthenticationParameters field is replaced by
00426    //a null octet string
00427    memset(message->msgAuthParameters, 0, hmacDigestSize);
00428 
00429    //The MAC is calculated over the whole message
00430    hmacInit(&hmacContext, hash, user->authKey.b, hash->digestSize);
00431    hmacUpdate(&hmacContext, message->buffer, message->bufferLen);
00432    hmacFinal(&hmacContext, NULL);
00433 
00434    //Restore the value of the msgAuthenticationParameters field
00435    memcpy(message->msgAuthParameters, hmacDigest, hmacDigestSize);
00436 
00437    //The newly calculated MAC is compared with the MAC value that was
00438    //saved in the first step
00439    if(memcmp(hmacContext.digest, hmacDigest, hmacDigestSize))
00440       return ERROR_AUTHENTICATION_FAILED;
00441 
00442    //Successful message authentication
00443    return NO_ERROR;
00444 }
00445 
00446 
00447 /**
00448  * @brief Data encryption
00449  * @param[in] user Security profile of the user
00450  * @param[in,out] message Pointer to the outgoing SNMP message
00451  * @param[in,out] salt Pointer to the salt integer
00452  * @return Error code
00453  **/
00454 
00455 error_t snmpEncryptData(const SnmpUserInfo *user, SnmpMessage *message, uint64_t *salt)
00456 {
00457    error_t error;
00458    uint_t i;
00459    size_t n;
00460    Asn1Tag tag;
00461 
00462    //Debug message
00463    TRACE_DEBUG("Scoped PDU (%" PRIuSIZE " bytes):\r\n", message->length);
00464    //Display the contents of the scopedPDU
00465    TRACE_DEBUG_ARRAY("  ", message->pos, message->length);
00466    //Display ASN.1 structure
00467    asn1DumpObject(message->pos, message->length, 0);
00468 
00469 #if (SNMP_DES_SUPPORT == ENABLED)
00470    //DES-CBC privacy protocol?
00471    if(user->privProtocol == SNMP_PRIV_PROTOCOL_DES)
00472    {
00473       DesContext desContext;
00474       uint8_t iv[DES_BLOCK_SIZE];
00475 
00476       //The data to be encrypted is treated as sequence of octets. Its length
00477       //should be an integral multiple of 8
00478       if(message->length % 8)
00479       {
00480          //If it is not, the data is padded at the end as necessary
00481          n = 8 - (message->length % 8);
00482          //The actual pad value is irrelevant
00483          memset(message->pos + message->length, n, n);
00484          //Update the length of the data
00485          message->length += n;
00486       }
00487 
00488       //The 32-bit snmpEngineBoots is converted to the first 4 octets of our salt
00489       STORE32BE(message->msgAuthEngineBoots, message->msgPrivParameters);
00490       //The 32-bit integer is then converted to the last 4 octet of our salt
00491       STORE32BE(*salt, message->msgPrivParameters + 4);
00492 
00493       //The resulting salt is then put into the msgPrivacyParameters field
00494       message->msgPrivParametersLen = 8;
00495 
00496       //Initialize DES context
00497       error = desInit(&desContext, user->privKey.b, 8);
00498       //Initialization failed?
00499       if(error)
00500          return error;
00501 
00502       //The last 8 octets of the 16-octet secret (private privacy key) are
00503       //used as pre-IV
00504       memcpy(iv, user->privKey.b + DES_BLOCK_SIZE, DES_BLOCK_SIZE);
00505 
00506       //The msgPrivacyParameters field is XOR-ed with the pre-IV to obtain the IV
00507       for(i = 0; i < DES_BLOCK_SIZE; i++)
00508          iv[i] ^= message->msgPrivParameters[i];
00509 
00510       //Perform CBC encryption
00511       error = cbcEncrypt(DES_CIPHER_ALGO, &desContext, iv,
00512          message->pos, message->pos, message->length);
00513       //Any error to report?
00514       if(error)
00515          return error;
00516    }
00517    else
00518 #endif
00519 #if (SNMP_AES_SUPPORT == ENABLED)
00520    //AES-128-CFB privacy protocol?
00521    if(user->privProtocol == SNMP_PRIV_PROTOCOL_AES)
00522    {
00523       AesContext aesContext;
00524       uint8_t iv[AES_BLOCK_SIZE];
00525 
00526       //The 32-bit snmpEngineBoots is converted to the first 4 octets of the IV
00527       STORE32BE(message->msgAuthEngineBoots, iv);
00528       //The 32-bit snmpEngineTime is converted to the subsequent 4 octets
00529       STORE32BE(message->msgAuthEngineTime, iv + 4);
00530       //The 64-bit integer is then converted to the last 8 octets
00531       STORE64BE(*salt, iv + 8);
00532 
00533       //The 64-bit integer must be placed in the msgPrivacyParameters field to
00534       //enable the receiving entity to compute the correct IV and to decrypt
00535       //the message
00536       STORE64BE(*salt, message->msgPrivParameters);
00537       message->msgPrivParametersLen = 8;
00538 
00539       //Initialize AES context
00540       error = aesInit(&aesContext, user->privKey.b, 16);
00541       //Initialization failed?
00542       if(error)
00543          return error;
00544 
00545       //Perform CFB-128 encryption
00546       error = cfbEncrypt(AES_CIPHER_ALGO, &aesContext, 128, iv,
00547          message->pos, message->pos, message->length);
00548       //Any error to report?
00549       if(error)
00550          return error;
00551    }
00552    else
00553 #endif
00554    //Invalid privacy protocol?
00555    {
00556       //Report an error
00557       return ERROR_FAILURE;
00558    }
00559 
00560    //The encryptedPDU is encapsulated within an octet string
00561    tag.constructed = FALSE;
00562    tag.objClass = ASN1_CLASS_UNIVERSAL;
00563    tag.objType = ASN1_TYPE_OCTET_STRING;
00564    tag.length = message->length;
00565    tag.value = NULL;
00566 
00567    //Write the corresponding ASN.1 tag
00568    error = asn1WriteTag(&tag, TRUE, message->pos, &n);
00569    //Any error to report?
00570    if(error)
00571       return error;
00572 
00573    //Move backward
00574    message->pos -= n;
00575    //Total length of the encryptedPDU
00576    message->length += n;
00577 
00578    //The salt integer is then modified. It is incremented by one and wrap
00579    //when it reaches its maximum value
00580    *salt += 1;
00581 
00582    //Successful encryption
00583    return NO_ERROR;
00584 }
00585 
00586 
00587 /**
00588  * @brief Data decryption
00589  * @param[in] user Security profile of the user
00590  * @param[in,out] message Pointer to the incoming SNMP message
00591  * @return Error code
00592  **/
00593 
00594 error_t snmpDecryptData(const SnmpUserInfo *user, SnmpMessage *message)
00595 {
00596    error_t error;
00597    uint_t i;
00598    Asn1Tag tag;
00599 
00600    //The encryptedPDU is encapsulated within an octet string
00601    error = asn1ReadTag(message->pos, message->length, &tag);
00602    //Failed to decode ASN.1 tag?
00603    if(error)
00604       return error;
00605 
00606    //Enforce encoding, class and type
00607    error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
00608    //The tag does not match the criteria?
00609    if(error)
00610       return error;
00611 
00612    //Point to the encryptedPDU
00613    message->pos = (uint8_t *) tag.value;
00614    //Length of the encryptedPDU
00615    message->length = tag.length;
00616 
00617 #if (SNMP_DES_SUPPORT == ENABLED)
00618    //DES-CBC privacy protocol?
00619    if(user->privProtocol == SNMP_PRIV_PROTOCOL_DES)
00620    {
00621       DesContext desContext;
00622       uint8_t iv[DES_BLOCK_SIZE];
00623 
00624       //Before decryption, the encrypted data length is verified. The length
00625       //of the encrypted data must be a multiple of 8 octets
00626       if(message->length % 8)
00627          return ERROR_DECRYPTION_FAILED;
00628 
00629       //Check the length of the msgPrivacyParameters field
00630       if(message->msgPrivParametersLen != 8)
00631          return ERROR_DECRYPTION_FAILED;
00632 
00633       //Initialize DES context
00634       error = desInit(&desContext, user->privKey.b, 8);
00635       //Initialization failed?
00636       if(error)
00637          return error;
00638 
00639       //The last 8 octets of the 16-octet secret (private privacy key) are
00640       //used as pre-IV
00641       memcpy(iv, user->privKey.b + DES_BLOCK_SIZE, DES_BLOCK_SIZE);
00642 
00643       //The msgPrivacyParameters field is XOR-ed with the pre-IV to obtain the IV
00644       for(i = 0; i < DES_BLOCK_SIZE; i++)
00645          iv[i] ^= message->msgPrivParameters[i];
00646 
00647       //Perform CBC decryption
00648       error = cbcDecrypt(DES_CIPHER_ALGO, &desContext, iv,
00649          message->pos, message->pos, message->length);
00650       //Any error to report?
00651       if(error)
00652          return error;
00653    }
00654    else
00655 #endif
00656 #if (SNMP_AES_SUPPORT == ENABLED)
00657    //AES-128-CFB privacy protocol?
00658    if(user->privProtocol == SNMP_PRIV_PROTOCOL_AES)
00659    {
00660       AesContext aesContext;
00661       uint8_t iv[AES_BLOCK_SIZE];
00662 
00663       //Check the length of the msgPrivacyParameters field
00664       if(message->msgPrivParametersLen != 8)
00665          return ERROR_DECRYPTION_FAILED;
00666 
00667       //The 32-bit snmpEngineBoots is converted to the first 4 octets of the IV
00668       STORE32BE(message->msgAuthEngineBoots, iv);
00669       //The 32-bit snmpEngineTime is converted to the subsequent 4 octets
00670       STORE32BE(message->msgAuthEngineTime, iv + 4);
00671       //The 64-bit integer is then converted to the last 8 octets
00672       memcpy(iv + 8, message->msgPrivParameters, 8);
00673 
00674       //Initialize AES context
00675       error = aesInit(&aesContext, user->privKey.b, 16);
00676       //Initialization failed?
00677       if(error)
00678          return error;
00679 
00680       //Perform CFB-128 encryption
00681       error = cfbDecrypt(AES_CIPHER_ALGO, &aesContext, 128, iv,
00682          message->pos, message->pos, message->length);
00683       //Any error to report?
00684       if(error)
00685          return error;
00686    }
00687    else
00688 #endif
00689    //Invalid privacy protocol?
00690    {
00691       //Report an error
00692       return ERROR_DECRYPTION_FAILED;
00693    }
00694 
00695    //Debug message
00696    TRACE_DEBUG("Scoped PDU (%" PRIuSIZE " bytes):\r\n", message->length);
00697    //Display the contents of the scopedPDU
00698    TRACE_DEBUG_ARRAY("  ", message->pos, message->length);
00699    //Display ASN.1 structure
00700    asn1DumpObject(message->pos, message->length, 0);
00701 
00702    //Successful decryption
00703    return NO_ERROR;
00704 }
00705 
00706 #endif
00707