Privacy protocol helper class.
This class is used to define privacy protocol encryption type in other classes using integer constants representing each protocol supported, and allows for easy instantiation of privacy protocol when used for encryption or decryption of data in a encryption method independent way. Example of how to use this class: int myPrivacyProtocol = PrivacyProtocol.AES128; IPrivacyProtocol privacyImplementation = PrivacyProtocol.GetInstance(myPrivacyProtocol); byte[] result = privacyImplementation.Encrypt(....);
Esempio n. 1
0
        /// <summary>Generate privacy key from authentication password and engine id</summary>
        /// <returns>Privacy key on success or null on failure</returns>
        public byte[] GeneratePrivacyKey()
        {
            if (userSecurityModel.Authentication == AuthenticationDigests.None)
            {
                return(null);
            }

            if (userSecurityModel.Privacy == EPrivacyProtocols.None)
            {
                return(null);
            }

            if (userSecurityModel.PrivacySecret == null || userSecurityModel.PrivacySecret.Length <= 0)
            {
                return(null);
            }

            IAuthenticationDigest authProto = Authentication.GetInstance(userSecurityModel.Authentication);

            if (authProto != null)
            {
                IPrivacyProtocol privProto = PrivacyProtocol.GetInstance(userSecurityModel.Privacy);
                if (privProto != null)
                {
                    return(privProto.PasswordToKey(userSecurityModel.PrivacySecret, userSecurityModel.EngineId, authProto));
                }
            }

            return(null);
        }
Esempio n. 2
0
        /// <summary>Encode SNMP version 3 packet</summary>
        /// <remarks>
        /// Before encoding the packet into a byte array you need to ensure all required information is
        /// set. Examples of required information is request type, Vbs (Oid + values pairs), USM settings including
        /// SecretName, authentication method and secret (if needed), privacy method and secret (if needed), etc.
        /// </remarks>
        /// <returns>Byte array BER encoded SNMP packet.</returns>
        public override byte[] Encode()
        {
            byte[] pkey = null;
            byte[] akey = null;

            if (messageFlags.Authentication && userSecurityModel.EngineId.Length > 0)
            {
                IAuthenticationDigest auth = Authentication.GetInstance(userSecurityModel.Authentication);

                if (auth == null)
                {
                    throw new SnmpException(SnmpException.EErrorCode.UnsupportedNoAuthPriv, "Invalid authentication protocol.");
                }

                akey = auth.PasswordToKey(userSecurityModel.AuthenticationSecret, userSecurityModel.EngineId);

                if (messageFlags.Privacy && userSecurityModel.EngineId.Length > 0)
                {
                    IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(userSecurityModel.Privacy);
                    if (privacyProtocol == null)
                    {
                        throw new SnmpException(SnmpException.EErrorCode.UnsupportedPrivacyProtocol, "Specified privacy protocol is not supported.");
                    }
                    pkey = privacyProtocol.PasswordToKey(userSecurityModel.PrivacySecret, userSecurityModel.EngineId, auth);
                }
            }

            return(Encode(akey, pkey));
        }
Esempio n. 3
0
        /// <summary>Build cached authentication and privacy encryption keys if they are appropriate for the selected security mode.</summary>
        /// <remarks>
        /// This method should be called after discovery process has been completed and all security related values
        /// have been set. For noAuthNoPriv, none of the keys are generated. authNoPriv will result in authentication
        /// key cached. authPriv will generate authentication and privacy keys.
        ///
        /// For successful key caching you need to set both relevant protocols and secret values.
        /// </remarks>
        public void BuildCachedSecurityKeys()
        {
            authenticationKey = privacyKey = null;

            if (engineId == null || engineId.Length <= 0)
            {
                return;
            }

            if (authenticationSecret == null || authenticationSecret.Length <= 0)
            {
                return;
            }

            if (authenticationProtocol != AuthenticationDigests.None)
            {
                IAuthenticationDigest authProto = Security.Authentication.GetInstance(authenticationProtocol);
                if (authProto != null)
                {
                    authenticationKey = authProto.PasswordToKey(authenticationSecret, engineId);
                    if (privacyProtocol != EPrivacyProtocols.None && privacySecret != null && privacySecret.Length > 0)
                    {
                        IPrivacyProtocol privProto = PrivacyProtocol.GetInstance(privacyProtocol);
                        if (privProto != null)
                        {
                            privacyKey = privProto.PasswordToKey(privacySecret, engineId, authProto);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Build cached authentication and privacy encryption keys if they are appropriate for the selected security mode.
        /// </summary>
        /// <remarks>
        /// This method should be called after discovery process has been completed and all security related values
        /// have been set. For noAuthNoPriv, none of the keys are generated. authNoPriv will result in authentication
        /// key cached. authPriv will generate authentication and privacy keys.
        ///
        /// For successful key caching you need to set both relevant protocols and secret values.
        /// </remarks>
        public void BuildCachedSecurityKeys()
        {
            _authenticationKey = _privacyKey = null;

            if (_engineId == null || _engineId.Length <= 0)
            {
                return;
            }
            if (_authenticationSecret == null || _authenticationSecret.Length <= 0)
            {
                return;
            }
            if (_authenticationProtocol != AuthenticationDigests.None)
            {
                IAuthenticationDigest authProto = SnmpSharpNet.Authentication.GetInstance(_authenticationProtocol);
                if (authProto != null)
                {
                    _authenticationKey = authProto.PasswordToKey(_authenticationSecret, _engineId);
                    if (_privacyProtocol != PrivacyProtocols.None && _privacySecret != null && _privacySecret.Length > 0)
                    {
                        IPrivacyProtocol privProto = PrivacyProtocol.GetInstance(_privacyProtocol);
                        if (privProto != null)
                        {
                            _privacyKey = privProto.PasswordToKey(_privacySecret, _engineId, authProto);
                        }
                    }
                }
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Decode SNMP version 3 packet. This method will perform authentication check and decode privacy protected <see cref="ScopedPdu"/>. This method will
        /// not check for the timeliness of the packet, correct engine boot value or engine id because it does not have a reference to the engine time prior to this call.
        /// </summary>
        /// <param name="berBuffer">BER encoded SNMP version 3 packet buffer</param>
        /// <param name="length">Buffer length</param>
        public override int Decode(byte[] berBuffer, int length)
        {
            byte[] pkey = null;
            byte[] akey = null;
            if (MsgFlags.Authentication && USM.EngineId.Length > 0)
            {
                IAuthenticationDigest auth = Authentication.GetInstance(USM.Authentication);
                if (auth == null)
                {
                    throw new SnmpException(SnmpException.UnsupportedNoAuthPriv, "Invalid authentication protocol.");
                }

                akey = auth.PasswordToKey(USM.AuthenticationSecret, USM.EngineId);
                if (MsgFlags.Privacy && USM.EngineId.Length > 0)
                {
                    IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(USM.Privacy);
                    if (privacyProtocol == null)
                    {
                        throw new SnmpException(SnmpException.UnsupportedPrivacyProtocol, "Specified privacy protocol is not supported.");
                    }

                    pkey = privacyProtocol.PasswordToKey(USM.PrivacySecret, USM.EngineId, auth);
                }
            }
            return(Decode(berBuffer, length, akey, pkey));
        }
        /// <summary>BER encode security model field.</summary>
        /// <remarks>
        /// USM security model is a SEQUENCE encoded inside a OCTETSTRING. To encode it, first encode the sequence
        /// of class values then "wrap" it inside a OCTETSTRING field
        /// </remarks>
        /// <param name="buffer">Buffer to store encoded USM security model header</param>
        public override void encode(MutableByte buffer)
        {
            MutableByte tmp = new MutableByte();

            // First encode all the values that will form the sequence
            _engineId.encode(tmp);
            // Encode engine boots
            _engineBoots.encode(tmp);
            // encode engine time
            _engineTime.encode(tmp);
            _securityName.encode(tmp);
            if (_authentication != AuthenticationDigests.None)
            {
                if (_authenticationParameters.Length <= 0)
                {
                    // If authentication is used, set authentication parameters field to 12 bytes set to 0x00
                    _authenticationParameters.Set(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
                }
            }
            else
            {
                _authenticationParameters.Reset();
            }
            _authenticationParameters.encode(tmp);
            if (_privacy != PrivacyProtocols.None)
            {
                if (_privacyParameters.Length <= 0)
                {
                    IPrivacyProtocol privProto = PrivacyProtocol.GetInstance(_privacy);
                    if (privProto != null)
                    {
                        byte[] parameter = new byte[privProto.PrivacyParametersLength];
                        for (int i = 0; i < privProto.PrivacyParametersLength; i++)
                        {
                            parameter[i] = 0x00; // This is not necessary since all array members are, by default, initialized to 0
                        }
                        _privacyParameters.Set(parameter);
                    }
                    else
                    {
                        throw new SnmpException(SnmpException.UnsupportedPrivacyProtocol, "Unrecognized privacy protocol specified.");
                    }
                }
            }
            else
            {
                _privacyParameters.Reset();
            }
            _privacyParameters.encode(tmp);
            MutableByte tmp1 = new MutableByte();

            BuildHeader(tmp1, SnmpConstants.SMI_SEQUENCE, tmp.Length);
            tmp1.Append(tmp);

            BuildHeader(buffer, OCTETSTRING, tmp1.Length);

            buffer.Append(tmp1);
        }
Esempio n. 7
0
        /// <summary>Encode SNMP version 3 packet</summary>
        /// <param name="authKey">Authentication key (not password)</param>
        /// <param name="privKey">Privacy key (not password)</param>
        /// <remarks>
        /// Before encoding the packet into a byte array you need to ensure all required information is
        /// set. Examples of required information is request type, Vbs (Oid + values pairs), USM settings including
        /// SecretName, authentication method and secret (if needed), privacy method and secret (if needed), etc.
        /// </remarks>
        /// <returns>Byte array BER encoded SNMP packet.</returns>
        public byte[] Encode(byte[] authKey, byte[] privKey)
        {
            MutableByte buffer = new MutableByte();

            // encode the global message data sequence header information
            MutableByte globalMessageData = new MutableByte();

            // if message id is 0 then generate a new, random message id
            if (messageId.Value == 0)
            {
                Random rand = new Random();
                messageId.Value = rand.Next(1, int.MaxValue);
            }

            // encode message id
            messageId.Encode(globalMessageData);

            // encode max message size
            maxMessageSize.Encode(globalMessageData);

            // message flags
            messageFlags.Encode(globalMessageData);

            // security model code
            securityModel.Value = userSecurityModel.Type;
            securityModel.Encode(globalMessageData);

            // add global message data to the main buffer
            // encode sequence header and add data
            AsnType.BuildHeader(buffer, SnmpConstants.SmiSequence, globalMessageData.Length);
            buffer.Append(globalMessageData);

            MutableByte packetHeader = new MutableByte(buffer);

            // before going down this road, check if this is a discovery packet
            OctetString savedUserName  = new OctetString();
            bool        privacy        = messageFlags.Privacy;
            bool        authentication = messageFlags.Authentication;
            bool        reportable     = messageFlags.Reportable;

            if (userSecurityModel.EngineId.Length <= 0)
            {
                // save USM settings prior to encoding a Discovery packet
                savedUserName.Set(userSecurityModel.SecurityName);
                userSecurityModel.SecurityName.Reset(); // delete security name for discovery packets
                messageFlags.Authentication = false;
                messageFlags.Privacy        = false;
                messageFlags.Reportable     = true;
            }

            userSecurityModel.Encode(buffer);

            if (userSecurityModel.EngineId.Length <= 0)
            {
                // restore saved USM values
                userSecurityModel.SecurityName.Set(savedUserName);
                messageFlags.Authentication = authentication;
                messageFlags.Privacy        = privacy;
                messageFlags.Reportable     = reportable;
            }

            // Check if privacy encryption is required
            MutableByte encodedPdu = new MutableByte();

            if (messageFlags.Privacy && userSecurityModel.EngineId.Length > 0)
            {
                IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(userSecurityModel.Privacy);
                if (privacyProtocol == null)
                {
                    throw new SnmpException(SnmpException.EErrorCode.UnsupportedPrivacyProtocol, "Specified privacy protocol is not supported.");
                }

                // Get BER encoded ScopedPdu
                MutableByte unencryptedPdu = new MutableByte();
                scopedPdu.Encode(unencryptedPdu);

                // we have to expand the key
                IAuthenticationDigest auth = Authentication.GetInstance(userSecurityModel.Authentication);
                if (auth == null)
                {
                    throw new SnmpException(SnmpException.EErrorCode.UnsupportedNoAuthPriv, "Invalid authentication protocol. noAuthPriv mode not supported.");
                }

                byte[] encryptedBuffer = privacyProtocol.Encrypt(unencryptedPdu, 0, unencryptedPdu.Length, privKey, userSecurityModel.EngineBoots, userSecurityModel.EngineTime, out byte[] privacyParameters, auth);

                userSecurityModel.PrivacyParameters.Set(privacyParameters);

                OctetString encryptedOctetString = new OctetString(encryptedBuffer);
                encryptedOctetString.Encode(encodedPdu);

                // now redo packet encoding
                buffer.Reset();
                buffer.Set(packetHeader);
                userSecurityModel.Encode(buffer);

                int preEncodedLength = encodedPdu.Length;
                buffer.Append(encodedPdu);

                if (maxMessageSize.Value != 0)
                {
                    // verify compliance with maximum message size
                    if ((encodedPdu.Length - preEncodedLength) > maxMessageSize)
                    {
                        throw new SnmpException(SnmpException.EErrorCode.MaximumMessageSizeExceeded, "ScopedPdu exceeds maximum message size.");
                    }
                }
            }
            else
            {
                scopedPdu.Encode(encodedPdu);
                buffer.Append(encodedPdu);
            }

            Encode(buffer);

            if (messageFlags.Authentication && userSecurityModel.EngineId.Length > 0)
            {
                userSecurityModel.Authenticate(authKey, ref buffer);

                // Now re-encode the packet with the authentication information
                userSecurityModel.Encode(packetHeader);
                packetHeader.Append(encodedPdu);

                Encode(packetHeader);
                buffer = packetHeader;
            }

            return(buffer);
        }
Esempio n. 8
0
        /// <summary>
        /// Decode SNMP version 3 packet. This method will perform authentication check and decode privacy protected <see cref="ScopedPdu"/>. This method will
        /// not check for the timeliness of the packet, correct engine boot value or engine id because it does not have a reference to the engine time prior to this call.
        /// </summary>
        /// <param name="berBuffer">BER encoded SNMP version 3 packet buffer</param>
        /// <param name="length">Buffer length</param>
        /// <param name="authKey">Authentication key (not password)</param>
        /// <param name="privKey">Privacy key (not password)</param>
        /// <returns>The length of the decoded segment</returns>
        public int Decode(byte[] berBuffer, int length, byte[] authKey, byte[] privKey)
        {
            MutableByte buffer = new MutableByte(berBuffer, length);

            int offset = 0;

            // let base class parse first sequence and SNMP version number
            offset = base.Decode(buffer, length);

            // check for correct SNMP protocol version
            if (protocolVersion != (int)ESnmpVersion.Ver3)
            {
                throw new SnmpInvalidVersionException("Expecting SNMP version 3.");
            }

            // now grab the global message data sequence header information
            byte asnType = AsnType.ParseHeader(buffer, ref offset, out int len);

            if (asnType != SnmpConstants.SmiSequence)
            {
                throw new SnmpDecodingException("Invalid sequence type in global message data sequence.");
            }

            // check that packet size can accommodate the length specified in the header
            if (len > (buffer.Length - offset))
            {
                throw new OverflowException("Packet is too small to contain the data described in the header.");
            }

            // retrieve message id
            offset = messageId.Decode(buffer, offset);

            // max message size
            offset = maxMessageSize.Decode(buffer, offset);

            // message flags
            offset = messageFlags.Decode(buffer, offset);

            // verify that a valid authentication/privacy configuration is present in the packet
            if (messageFlags.Authentication == false && messageFlags.Privacy == true)
            {
                throw new SnmpException(SnmpException.EErrorCode.UnsupportedNoAuthPriv, "SNMP version 3 noAuthPriv security combination is not supported.");
            }

            // security model code
            offset = securityModel.Decode(buffer, offset);

            // we only support USM. code = 0x03
            if (securityModel.Value != userSecurityModel.Type)
            {
                throw new SnmpException(SnmpException.EErrorCode.UnsupportedSecurityModel, "Class only support SNMP Version 3 User Security Model.");
            }

            // parse user security model
            offset = userSecurityModel.Decode(buffer, offset);

            // Authenticate message if authentication flag is set and packet is not a discovery packet
            if (messageFlags.Authentication && userSecurityModel.EngineId.Length > 0)
            {
                // Authenticate packet
                if (userSecurityModel.AuthenticationParameters.Length != 12)
                {
                    throw new SnmpAuthenticationException("Invalid authentication parameter field length.");
                }
                if (!userSecurityModel.IsAuthentic(authKey, buffer))
                {
                    throw new SnmpAuthenticationException("Authentication of the incoming packet failed.");
                }
            }

            // Decode ScopedPdu if it is privacy protected and packet is not a discovery packet
            if (messageFlags.Privacy && userSecurityModel.EngineId.Length > 0)
            {
                IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(userSecurityModel.Privacy);

                if (privacyProtocol == null)
                {
                    throw new SnmpException(SnmpException.EErrorCode.UnsupportedPrivacyProtocol, "Privacy protocol requested is not supported.");
                }

                if (userSecurityModel.PrivacyParameters.Length != privacyProtocol.PrivacyParametersLength)
                {
                    throw new SnmpException(SnmpException.EErrorCode.InvalidPrivacyParameterLength, "Invalid privacy parameters field length.");
                }

                // Initialize a temporary OctetString class to hold encrypted ScopedPdu
                OctetString encryptedScopedPdu = new OctetString();
                offset = encryptedScopedPdu.Decode(buffer, offset);

                // decode encrypted packet
                byte[] decryptedScopedPdu = privacyProtocol.Decrypt(encryptedScopedPdu, 0, encryptedScopedPdu.Length, privKey, userSecurityModel.EngineBoots, userSecurityModel.EngineTime, userSecurityModel.PrivacyParameters);
                int    tempOffset         = 0;
                offset = scopedPdu.Decode(decryptedScopedPdu, tempOffset);
            }
            else
            {
                offset = scopedPdu.Decode(buffer, offset);
            }

            return(offset);
        }
        /// <summary>
        /// Encode SNMP version 3 packet
        /// </summary>
        /// <remarks>
        /// Before encoding the packet into a byte array you need to ensure all required information is
        /// set. Examples of required information is request type, Vbs (Oid + values pairs), USM settings including
        /// SecretName, authentication method and secret (if needed), privacy method and secret (if needed), etc.
        /// </remarks>
        /// <returns>Byte array BER encoded SNMP packet.</returns>
        public override byte[] encode()
        {
            MutableByte buffer = new MutableByte();
            // encode the global message data sequence header information

            MutableByte globalMessageData = new MutableByte();

            // if message id is 0 then generate a new, random message id
            if (_messageId.Value == 0)
            {
                Random rand = new Random();
                _messageId.Value = rand.Next(1, Int32.MaxValue);
            }

            // encode message id
            _messageId.encode(globalMessageData);

            // encode max message size
            _maxMessageSize.encode(globalMessageData);

            // message flags
            _msgFlags.encode(globalMessageData);

            // security model code
            _securityModel.Value = _userSecurityModel.Type;
            _securityModel.encode(globalMessageData);

            // add global message data to the main buffer
            // encode sequence header and add data
            AsnType.BuildHeader(buffer, SnmpConstants.SMI_SEQUENCE, globalMessageData.Length);
            buffer.Append(globalMessageData);

            MutableByte packetHeader = new MutableByte(buffer);

            // before going down this road, check if this is a discovery packet
            OctetString savedUserName  = new OctetString();
            bool        privacy        = _msgFlags.Privacy;
            bool        authentication = _msgFlags.Authentication;
            bool        reportable     = _msgFlags.Reportable;

            if (_userSecurityModel.EngineId.Length <= 0)
            {
                // save USM settings prior to encoding a Discovery packet
                savedUserName.Set(_userSecurityModel.SecurityName);
                _userSecurityModel.SecurityName.Reset();                 // delete security name for discovery packets
                _msgFlags.Authentication = false;
                _msgFlags.Privacy        = false;
                _msgFlags.Reportable     = true;
            }

            _userSecurityModel.encode(buffer);

            if (_userSecurityModel.EngineId.Length <= 0)
            {
                // restore saved USM values
                _userSecurityModel.SecurityName.Set(savedUserName);
                _msgFlags.Authentication = authentication;
                _msgFlags.Privacy        = privacy;
                _msgFlags.Reportable     = reportable;
            }

            // Check if privacy encryption is required
            MutableByte encodedPdu = new MutableByte();

            if (_msgFlags.Privacy && _userSecurityModel.EngineId.Length > 0)
            {
                IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(_userSecurityModel.Privacy);
                if (privacyProtocol == null)
                {
                    throw new SnmpException(SnmpException.UnsupportedPrivacyProtocol, "Specified privacy protocol is not supported.");
                }

                // Get BER encoded ScopedPdu
                MutableByte unencryptedPdu = new MutableByte();
                _scopedPdu.encode(unencryptedPdu);

                byte[] privacyParameters = null;
                // we have to expand the key
                IAuthenticationDigest auth = Authentication.GetInstance(_userSecurityModel.Authentication);
                if (auth == null)
                {
                    throw new SnmpException(SnmpException.UnsupportedNoAuthPriv, "Invalid authentication protocol. noAuthPriv mode not supported.");
                }

                byte[] pkey = privacyProtocol.PasswordToKey(_userSecurityModel.PrivacySecret, _userSecurityModel.EngineId, auth);

                byte[] encryptedBuffer = privacyProtocol.Encrypt(unencryptedPdu, 0, unencryptedPdu.Length, pkey, _userSecurityModel.EngineBoots, _userSecurityModel.EngineTime, out privacyParameters, auth);

                _userSecurityModel.PrivacyParameters.Set(privacyParameters);
                OctetString encryptedOctetString = new OctetString(encryptedBuffer);
                encryptedOctetString.encode(encodedPdu);
                // now redo packet encoding
                buffer.Reset();
                buffer.Set(packetHeader);
                _userSecurityModel.encode(buffer);
                int preEncodedLength = encodedPdu.Length;
                buffer.Append(encodedPdu);
                if (_maxMessageSize.Value != 0)
                {
                    // verify compliance with maximum message size
                    if ((encodedPdu.Length - preEncodedLength) > _maxMessageSize)
                    {
                        throw new SnmpException(SnmpException.MaximumMessageSizeExceeded, "ScopedPdu exceeds maximum message size.");
                    }
                }
            }
            else
            {
                _scopedPdu.encode(encodedPdu);
                buffer.Append(encodedPdu);
            }

            int preVersionLength = buffer.Length;

            base.encode(buffer);

            int versionHeaderLength = buffer.Length - preVersionLength;

            if (_msgFlags.Authentication && _userSecurityModel.EngineId.Length > 0)
            {
                _userSecurityModel.Authenticate(ref buffer);
                // Now re-encode the packet with the authentication information
                _userSecurityModel.encode(packetHeader);
                packetHeader.Append(encodedPdu);
                base.encode(packetHeader);
                buffer = packetHeader;
            }
            return(buffer);
        }
        /// <summary>
        /// Decode SNMP version 3 packet. This method will perform authentication check and decode privacy protected <see cref="ScopedPdu"/>. This method will
        /// not check for the timeliness of the packet, correct engine boot value or engine id because it does not have a reference to the engine time prior to this call.
        /// </summary>
        /// <param name="berBuffer">BER encoded SNMP version 3 packet buffer</param>
        /// <param name="length">Buffer length</param>
        public override int decode(byte[] berBuffer, int length)
        {
            MutableByte buffer = new MutableByte(berBuffer, length);

            int offset = 0;

            // let base class parse first sequence and SNMP version number
            offset = base.decode(buffer, length);

            // check for correct SNMP protocol version
            if (_protocolVersion != (int)SnmpVersion.Ver3)
            {
                throw new SnmpInvalidVersionException("Expecting SNMP version 3.");
            }

            // now grab the global message data sequence header information
            int  len     = 0;
            byte asnType = AsnType.ParseHeader(buffer, ref offset, out len);

            // check that packet size can accommodate the length specified in the header
            if (len > (buffer.Length - offset))
            {
                throw new OverflowException("Packet is too small to contain the data described in the header.");
            }

            // retrieve message id
            offset = _messageId.decode(buffer, offset);

            // max message size
            offset = _maxMessageSize.decode(buffer, offset);

            // message flags
            offset = _msgFlags.decode(buffer, offset);

            // verify that a valid authentication/privacy configuration is present in the packet
            if (_msgFlags.Authentication == false && _msgFlags.Privacy == true)
            {
                throw new SnmpException(SnmpException.UnsupportedNoAuthPriv, "SNMP version 3 noAuthPriv security combination is not supported.");
            }

            // security model code
            offset = _securityModel.decode(buffer, offset);

            // we only support USM. code = 0x03
            if (_securityModel.Value != _userSecurityModel.Type)
            {
                throw new SnmpException(SnmpException.UnsupportedSecurityModel, "Class only support SNMP Version 3 User Security Model.");
            }

            // parse user security model
            offset = _userSecurityModel.decode(buffer, offset);

            // Authenticate message if authentication flag is set and packet is not a discovery packet
            if (_msgFlags.Authentication && _userSecurityModel.EngineId.Length > 0)
            {
                // Authenticate packet
                if (_userSecurityModel.AuthenticationParameters.Length != 12)
                {
                    throw new SnmpAuthenticationException("Invalid authentication parameter field length.");
                }
                if (!_userSecurityModel.IsAuthentic(buffer))
                {
                    throw new SnmpAuthenticationException("Authentication of the incoming packet failed.");
                }
            }

            // Decode ScopedPdu if it is privacy protected and packet is not a discovery packet
            if (_msgFlags.Privacy && _userSecurityModel.EngineId.Length > 0)
            {
                IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(_userSecurityModel.Privacy);
                if (privacyProtocol == null)
                {
                    throw new SnmpException(SnmpException.UnsupportedPrivacyProtocol, "Privacy protocol requested is not supported.");
                }
                if (_userSecurityModel.PrivacyParameters.Length != privacyProtocol.PrivacyParametersLength)
                {
                    throw new SnmpException(SnmpException.InvalidPrivacyParameterLength, "Invalid privacy parameters field length.");
                }

                // build privacy key
                IAuthenticationDigest auth = Authentication.GetInstance(_userSecurityModel.Authentication);
                if (auth == null)
                {
                    throw new SnmpException(SnmpException.UnsupportedNoAuthPriv, "Invalid authentication protocol.");
                }

                byte[] pkey = auth.PasswordToKey(_userSecurityModel.PrivacySecret, _userSecurityModel.EngineId);

                // extend key if it is too short
                if (pkey.Length < privacyProtocol.MinimumKeyLength)
                {
                    pkey = privacyProtocol.ExtendShortKey(pkey, new OctetString(_userSecurityModel.PrivacySecret), _userSecurityModel.EngineId, auth);
                }

                // Initialize a temporary OctetString class to hold encrypted ScopedPdu
                OctetString encryptedScopedPdu = new OctetString();
                offset = encryptedScopedPdu.decode(buffer, offset);

                // decode encrypted packet
                byte[] decryptedScopedPdu = privacyProtocol.Decrypt(encryptedScopedPdu, 0, encryptedScopedPdu.Length, pkey, _userSecurityModel.EngineBoots, _userSecurityModel.EngineTime, _userSecurityModel.PrivacyParameters);
                int    tempOffset         = 0;
                offset = _scopedPdu.decode(decryptedScopedPdu, tempOffset);
            }
            else
            {
                offset = _scopedPdu.decode(buffer, offset);
            }
            return(offset);
        }