UNPKG

net-snmp

Version:

JavaScript implementation of the Simple Network Management Protocol (SNMP)

1,765 lines (1,489 loc) 197 kB
// Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com> const ber = require ("asn1-ber").Ber; const smartbuffer = require ("smart-buffer"); const dgram = require ("dgram"); const net = require ("net"); const events = require ("events"); const util = require ("util"); const crypto = require ("crypto"); const mibparser = require ("./lib/mib"); const Buffer = require('buffer').Buffer; var DEBUG = false; const MIN_SIGNED_INT32 = -2147483648; const MAX_SIGNED_INT32 = 2147483647; const MIN_UNSIGNED_INT32 = 0; const MAX_UNSIGNED_INT32 = 4294967295; const MAX_UNSIGNED_INT64 = 18446744073709551615; const DES_IMPLEMENTATION = 'library'; // Use test harness trace or debug functions falling back to console.debug in normal mode. // eslint-disable-next-line no-undef var debugfn = typeof(global.debug) === 'function'? global.trace ?? global.debug : console.debug; function debug () { if ( DEBUG ) { debugfn.apply (this, arguments); } } /***************************************************************************** ** Constants **/ function _expandConstantObject (object) { var keys = []; for (var key in object) keys.push (key); for (var i = 0; i < keys.length; i++) object[object[keys[i]]] = parseInt (keys[i]); } var ErrorStatus = { 0: "NoError", 1: "TooBig", 2: "NoSuchName", 3: "BadValue", 4: "ReadOnly", 5: "GeneralError", 6: "NoAccess", 7: "WrongType", 8: "WrongLength", 9: "WrongEncoding", 10: "WrongValue", 11: "NoCreation", 12: "InconsistentValue", 13: "ResourceUnavailable", 14: "CommitFailed", 15: "UndoFailed", 16: "AuthorizationError", 17: "NotWritable", 18: "InconsistentName" }; _expandConstantObject (ErrorStatus); var ObjectType = { 1: "Boolean", 2: "Integer", 3: "BitString", 4: "OctetString", 5: "Null", 6: "OID", 64: "IpAddress", 65: "Counter", 66: "Gauge", 67: "TimeTicks", 68: "Opaque", 70: "Counter64", 128: "NoSuchObject", 129: "NoSuchInstance", 130: "EndOfMibView" }; _expandConstantObject (ObjectType); // ASN.1 ObjectType.INTEGER = ObjectType.Integer; ObjectType["OCTET STRING"] = ObjectType.OctetString; ObjectType["OBJECT IDENTIFIER"] = ObjectType.OID; // SNMPv2-SMI ObjectType.Integer32 = ObjectType.Integer; ObjectType.Counter32 = ObjectType.Counter; ObjectType.Gauge32 = ObjectType.Gauge; ObjectType.Unsigned32 = ObjectType.Gauge32; var PduType = { 160: "GetRequest", 161: "GetNextRequest", 162: "GetResponse", 163: "SetRequest", 164: "Trap", 165: "GetBulkRequest", 166: "InformRequest", 167: "TrapV2", 168: "Report" }; _expandConstantObject (PduType); var TrapType = { 0: "ColdStart", 1: "WarmStart", 2: "LinkDown", 3: "LinkUp", 4: "AuthenticationFailure", 5: "EgpNeighborLoss", 6: "EnterpriseSpecific" }; _expandConstantObject (TrapType); var SecurityLevel = { 1: "noAuthNoPriv", 2: "authNoPriv", 3: "authPriv" }; _expandConstantObject (SecurityLevel); var AuthProtocols = { "1": "none", "2": "md5", "3": "sha", "4": "sha224", "5": "sha256", "6": "sha384", "7": "sha512" }; _expandConstantObject (AuthProtocols); var PrivProtocols = { "1": "none", "2": "des", "4": "aes", "6": "aes256b", "8": "aes256r" }; _expandConstantObject (PrivProtocols); var UsmStatsBase = "1.3.6.1.6.3.15.1.1"; var UsmStats = { "1": "Unsupported Security Level", "2": "Not In Time Window", "3": "Unknown User Name", "4": "Unknown Engine ID", "5": "Wrong Digest (incorrect password, community or key)", "6": "Decryption Error" }; _expandConstantObject (UsmStats); var MibProviderType = { "1": "Scalar", "2": "Table" }; _expandConstantObject (MibProviderType); var Version1 = 0; var Version2c = 1; var Version3 = 3; var Version = { "1": Version1, "2c": Version2c, "3": Version3 }; var AgentXPduType = { 1: "Open", 2: "Close", 3: "Register", 4: "Unregister", 5: "Get", 6: "GetNext", 7: "GetBulk", 8: "TestSet", 9: "CommitSet", 10: "UndoSet", 11: "CleanupSet", 12: "Notify", 13: "Ping", 14: "IndexAllocate", 15: "IndexDeallocate", 16: "AddAgentCaps", 17: "RemoveAgentCaps", 18: "Response" }; _expandConstantObject (AgentXPduType); var AccessControlModelType = { 0: "None", 1: "Simple" }; _expandConstantObject (AccessControlModelType); var AccessLevel = { 0: "None", 1: "ReadOnly", 2: "ReadWrite" }; _expandConstantObject (AccessLevel); // SMIv2 MAX-ACCESS values var MaxAccess = { 0: "not-accessible", 1: "accessible-for-notify", 2: "read-only", 3: "read-write", 4: "read-create" }; _expandConstantObject (MaxAccess); // SMIv1 ACCESS value mapping to SMIv2 MAX-ACCESS var AccessToMaxAccess = { "not-accessible": "not-accessible", "read-only": "read-only", "read-write": "read-write", "write-only": "read-write" }; var RowStatus = { // status values 1: "active", 2: "notInService", 3: "notReady", // actions 4: "createAndGo", 5: "createAndWait", 6: "destroy" }; _expandConstantObject (RowStatus); var ResponseInvalidCode = { 1: "EIp4AddressSize", 2: "EUnknownObjectType", 3: "EUnknownPduType", 4: "ECouldNotDecrypt", 5: "EAuthFailure", 6: "EReqResOidNoMatch", // 7: "ENonRepeaterCountMismatch", // no longer used 8: "EOutOfOrder", 9: "EVersionNoMatch", 10: "ECommunityNoMatch", 11: "EUnexpectedReport", 12: "EResponseNotHandled", 13: "EUnexpectedResponse" }; _expandConstantObject (ResponseInvalidCode); var OidFormat = { "oid": "oid", "path": "path", "module": "module" }; /***************************************************************************** ** Exception class definitions **/ function ResponseInvalidError (message, code, info) { this.name = "ResponseInvalidError"; this.message = message; this.code = code; this.info = info; Error.captureStackTrace(this, ResponseInvalidError); } util.inherits (ResponseInvalidError, Error); function RequestInvalidError (message) { this.name = "RequestInvalidError"; this.message = message; Error.captureStackTrace(this, RequestInvalidError); } util.inherits (RequestInvalidError, Error); function RequestFailedError (message, status) { this.name = "RequestFailedError"; this.message = message; this.status = status; Error.captureStackTrace(this, RequestFailedError); } util.inherits (RequestFailedError, Error); function RequestTimedOutError (message) { this.name = "RequestTimedOutError"; this.message = message; Error.captureStackTrace(this, RequestTimedOutError); } util.inherits (RequestTimedOutError, Error); function ProcessingError (message, error, rinfo, buffer) { this.name = "ProcessingError"; this.message = message; this.error = error; this.rinfo = rinfo; this.buffer = buffer; Error.captureStackTrace(this, ProcessingError); } util.inherits (ProcessingError, Error); /***************************************************************************** ** OID and varbind helper functions **/ function isVarbindError (varbind) { return !!(varbind.type == ObjectType.NoSuchObject || varbind.type == ObjectType.NoSuchInstance || varbind.type == ObjectType.EndOfMibView); } function varbindError (varbind) { return (ObjectType[varbind.type] || "NotAnError") + ": " + varbind.oid; } function oidFollowsOid (oidString, nextString) { var oid = {str: oidString, len: oidString.length, idx: 0}; var next = {str: nextString, len: nextString.length, idx: 0}; var dotCharCode = ".".charCodeAt (0); function getNumber (item) { var n = 0; if (item.idx >= item.len) return null; while (item.idx < item.len) { var charCode = item.str.charCodeAt (item.idx++); if (charCode == dotCharCode) return n; n = (n ? (n * 10) : n) + (charCode - 48); } return n; } while (1) { var oidNumber = getNumber (oid); var nextNumber = getNumber (next); if (oidNumber !== null) { if (nextNumber !== null) { if (nextNumber > oidNumber) { return true; } else if (nextNumber < oidNumber) { return false; } } else { return true; } } else { return true; } } } function oidInSubtree (oidString, nextString) { var oid = oidString.split ("."); var next = nextString.split ("."); if (oid.length > next.length) return false; for (var i = 0; i < oid.length; i++) { if (next[i] != oid[i]) return false; } return true; } function readInt32 (buffer) { var parsedInt = buffer.readInt (); if ( ! Number.isInteger(parsedInt) ) { throw new TypeError('Value read as integer ' + parsedInt + ' is not an integer'); } if ( parsedInt < MIN_SIGNED_INT32 || parsedInt > MAX_SIGNED_INT32 ) { throw new RangeError('Read integer ' + parsedInt + ' is outside the signed 32-bit range'); } return parsedInt; } function readUint32 (buffer) { var parsedInt = buffer.readInt (); if ( ! Number.isInteger(parsedInt) ) { throw new TypeError('Value read as integer ' + parsedInt + ' is not an integer'); } parsedInt = (parsedInt>>>0); if ( parsedInt < MIN_UNSIGNED_INT32 || parsedInt > MAX_UNSIGNED_INT32 ) { throw new RangeError('Read integer ' + parsedInt + ' is outside the unsigned 32-bit range'); } return parsedInt; } function readUint64 (buffer) { var value = buffer.readString (ObjectType.Counter64, true); return value; } function readIpAddress (buffer) { var bytes = buffer.readString (ObjectType.IpAddress, true); if (bytes.length != 4) throw new ResponseInvalidError ("Length '" + bytes.length + "' of IP address '" + bytes.toString ("hex") + "' is not 4", ResponseInvalidCode.EIp4AddressSize); var value = bytes[0] + "." + bytes[1] + "." + bytes[2] + "." + bytes[3]; return value; } function readVarbindValue (buffer, type) { var value; if (type == ObjectType.Boolean) { value = buffer.readBoolean (); } else if (type == ObjectType.Integer) { value = readInt32 (buffer); } else if (type == ObjectType.BitString) { value = buffer.readBitString(); } else if (type == ObjectType.OctetString) { value = buffer.readString (null, true); } else if (type == ObjectType.Null) { buffer.readByte (); buffer.readByte (); value = null; } else if (type == ObjectType.OID) { value = buffer.readOID (); } else if (type == ObjectType.IpAddress) { value = readIpAddress (buffer); } else if (type == ObjectType.Counter) { value = readUint32 (buffer); } else if (type == ObjectType.Gauge) { value = readUint32 (buffer); } else if (type == ObjectType.TimeTicks) { value = readUint32 (buffer); } else if (type == ObjectType.Opaque) { value = buffer.readString (ObjectType.Opaque, true); } else if (type == ObjectType.Counter64) { value = readUint64 (buffer); } else if (type == ObjectType.NoSuchObject) { buffer.readByte (); buffer.readByte (); value = null; } else if (type == ObjectType.NoSuchInstance) { buffer.readByte (); buffer.readByte (); value = null; } else if (type == ObjectType.EndOfMibView) { buffer.readByte (); buffer.readByte (); value = null; } else { throw new ResponseInvalidError ("Unknown type '" + type + "' in response", ResponseInvalidCode.EUnknownObjectType); } return value; } function readVarbinds (buffer, varbinds) { buffer.readSequence (); while (1) { buffer.readSequence (); if ( buffer.peek () != ObjectType.OID ) break; var oid = buffer.readOID (); var type = buffer.peek (); if (type == null) break; var value = readVarbindValue (buffer, type); varbinds.push ({ oid: oid, type: type, value: value }); } } function writeInt32 (buffer, type, value) { if ( ! Number.isInteger(value) ) { throw new TypeError('Value to write as integer ' + value + ' is not an integer'); } if ( value < MIN_SIGNED_INT32 || value > MAX_SIGNED_INT32 ) { throw new RangeError('Integer to write ' + value + ' is outside the signed 32-bit range'); } buffer.writeInt(value, type); } function writeUint32 (buffer, type, value) { if ( ! Number.isInteger(value) ) { throw new TypeError('Value to write as integer ' + value + ' is not an integer'); } if ( value < MIN_UNSIGNED_INT32 || value > MAX_UNSIGNED_INT32 ) { throw new RangeError('Integer to write ' + value + ' is outside the unsigned 32-bit range'); } buffer.writeInt(value, type); } function writeUint64 (buffer, value) { buffer.writeBuffer (value, ObjectType.Counter64); } function writeVarbinds (buffer, varbinds) { buffer.startSequence (); for (var i = 0; i < varbinds.length; i++) { buffer.startSequence (); buffer.writeOID (varbinds[i].oid); if (varbinds[i].type && varbinds[i].hasOwnProperty("value")) { var type = varbinds[i].type; var value = varbinds[i].value; switch ( type ) { case ObjectType.Boolean: buffer.writeBoolean (value ? true : false); break; case ObjectType.Integer: // also Integer32 writeInt32 (buffer, ObjectType.Integer, value); break; case ObjectType.OctetString: if (typeof value == "string") buffer.writeString (value); else buffer.writeBuffer (value, ObjectType.OctetString); break; case ObjectType.Null: buffer.writeNull (); break; case ObjectType.OID: buffer.writeOID (value); break; case ObjectType.IpAddress: var bytes = value.split ("."); if (bytes.length != 4) throw new RequestInvalidError ("Invalid IP address '" + value + "'"); buffer.writeBuffer (Buffer.from (bytes), 64); break; case ObjectType.Counter: // also Counter32 writeUint32 (buffer, ObjectType.Counter, value); break; case ObjectType.Gauge: // also Gauge32 & Unsigned32 writeUint32 (buffer, ObjectType.Gauge, value); break; case ObjectType.TimeTicks: writeUint32 (buffer, ObjectType.TimeTicks, value); break; case ObjectType.Opaque: buffer.writeBuffer (value, ObjectType.Opaque); break; case ObjectType.Counter64: writeUint64 (buffer, value); break; case ObjectType.NoSuchObject: case ObjectType.NoSuchInstance: case ObjectType.EndOfMibView: buffer.writeByte (type); buffer.writeByte (0); break; default: throw new RequestInvalidError ("Unknown type '" + type + "' in request"); } } else { buffer.writeNull (); } buffer.endSequence (); } buffer.endSequence (); } const ObjectTypeUtil = {}; ObjectTypeUtil.castSetValue = function (type, value, constraints) { switch (type) { case ObjectType.Boolean: { return !! value; } case ObjectType.Integer: case ObjectType.Integer32: { if ( typeof value != "number" && typeof value != "string" ) { throw new Error("Invalid Integer", value); } const parsedValue = typeof value == "number" ? value : parseInt(value, 10); if ( isNaN(parsedValue) ) { throw new Error("Invalid Integer", value); } if ( constraints && ! ObjectTypeUtil.doesIntegerMeetConstraints (parsedValue, constraints) ) { throw new Error("Integer does not meet constraints", value); } return parsedValue; } case ObjectType.OctetString: { if ( ! ( value instanceof Buffer || typeof value == "string" ) ) { throw new Error("Invalid OctetString", value); } if ( constraints && ! ObjectTypeUtil.doesStringMeetConstraints (value, constraints) ) { throw new Error("OctetString does not meet constraints", value); } if ( value instanceof Buffer ) { return value.toString(); } else { return value; } } case ObjectType.OID: { if ( typeof value != "string" || ! value.match(/^([0-9]+)(\.[0-9]+)+$/) ) { throw new Error("Invalid OID", value); } return value; } case ObjectType.Counter: case ObjectType.Counter32: case ObjectType.Gauge: case ObjectType.Gauge32: case ObjectType.Unsigned32: { // Counters should be initialized to 0 (RFC2578, end of section 7.9) // We'll do so. // return 0; // That ^^^ was fine when castSetValue was used only for DEFVAL // But now it's used in other set value scenarios // So we need to cast the given value to a whole number const parsedValue = parseInt(value, 10); if ( isNaN(parsedValue) ) { throw new Error(`Invalid Integer for ${type}`, value); } if ( parsedValue < 0 ) { throw new Error(`Integer is negative for ${type}`, value); } if ( parsedValue > MAX_UNSIGNED_INT32 ) { throw new Error(`Integer is greater than max unsigned int32 for ${type}`, value); } return parsedValue; } case ObjectType.Counter64: { if ( value instanceof Buffer ) { if ( value.length !== 8 ) { throw new Error(`Counter64 buffer is not 8 bytes`, value); } return value; } const parsedValue = parseInt(value, 10); if ( isNaN(parsedValue) ) { throw new Error(`Invalid Integer for Counter64`, value); } if ( parsedValue < 0 ) { throw new Error(`Integer is negative for Counter64`, value); } if ( parsedValue > MAX_UNSIGNED_INT64 ) { throw new Error(`Integer is greater than max unsigned int64 for Counter64`, value); } return parsedValue; } case ObjectType.IpAddress: { const octets = value.split ("."); if ( typeof value != "string" || octets.length != 4 ) { throw new Error("Invalid IpAddress", value); } for ( const octet of octets ) { if ( isNaN (octet) ) { throw new Error("Invalid IpAddress", value); } if ( parseInt (octet) < 0 || parseInt (octet) > 255) { throw new Error("Invalid IpAddress", value); } } return value; } default: { // Assume the caller knows what he's doing return value; } } }; ObjectTypeUtil.isValid = function (type, value, constraints) { switch (type) { case ObjectType.Boolean: { return typeof value == "boolean"; } case ObjectType.Integer: case ObjectType.Integer32: { // Allow strings that can be parsed as integers const parsedValue = Number(value); if ( isNaN(parsedValue) || ! Number.isInteger(parsedValue) || parsedValue < MIN_SIGNED_INT32 || parsedValue > MAX_SIGNED_INT32 ) { return false; } if ( constraints && ! ObjectTypeUtil.doesIntegerMeetConstraints (parsedValue, constraints) ) { return false; } return true; } case ObjectType.OctetString: { // Allow string or buffer if ( typeof value != "string" && ! (value instanceof Buffer) ) { return false; } if ( constraints && ! ObjectTypeUtil.doesStringMeetConstraints (value, constraints) ) { return false; } return true; } case ObjectType.OID: { return typeof value == "string" && value.match (/^([0-9]+)(\.[0-9]+)+$/); } case ObjectType.Counter: case ObjectType.Counter32: case ObjectType.Gauge: case ObjectType.Gauge32: case ObjectType.Unsigned32: { // Allow strings that can be parsed as integers const parsed = Number(value); return ! isNaN (parsed) && Number.isInteger (parsed) && parsed >= 0 && parsed <= MAX_UNSIGNED_INT32; } case ObjectType.Counter64: { if ( value instanceof Buffer ) { // Allow buffers of 8 bytes - should do further check to see if it translates to a valid integer return value.length == 8; } else { // Allow strings that can be parsed as integers const parsed = Number(value); return ! isNaN (parsed) && Number.isInteger (parsed) && parsed >= 0; } } case ObjectType.IpAddress: { const octets = value.split("."); if ( octets.length !== 4 ) { return false; } for ( const octet of octets ) { if ( isNaN (octet) ) { return false; } if ( parseInt (octet) < 0 || parseInt (octet) > 255) { return false; } } return true; } // return true for all other types until we are certain all object types are covered with specific rules default: { return true; } } }; ObjectTypeUtil.doesIntegerMeetConstraints = function (value, constraints) { if ( ! constraints ) { return true; } if ( constraints.enumeration ) { if ( constraints.enumeration[value] ) { return true; } else { return false; } } else if ( constraints.ranges ) { for ( const range of constraints.ranges ) { const min = "min" in range ? range.min : Number.MIN_SAFE_INTEGER; const max = "max" in range ? range.max : Number.MAX_SAFE_INTEGER; if ( value >= min && value <= max ) { return true; } } return false; } return true; }; ObjectTypeUtil.doesStringMeetConstraints = function (value, constraints) { if ( ! constraints ) { return true; } if ( constraints.sizes ) { // if size is constrained, value must have a length property if ( value.length === undefined ) { return false; } const len = value.length; for ( const range of constraints.sizes ) { const min = "min" in range ? range.min : Number.MIN_SAFE_INTEGER; const max = "max" in range ? range.max : Number.MAX_SAFE_INTEGER; if ( len >= min && len <= max ) { return true; } } return false; } return true; }; ObjectTypeUtil.getEnumerationNumberFromName = function (enumeration, name) { for ( const [enumNumber, enumName] of Object.entries (enumeration) ) { if ( enumName === name ) { return Number (enumNumber); } } return null; }; /***************************************************************************** ** PDU class definitions **/ var SimplePdu = function () { }; SimplePdu.prototype.toBuffer = function (buffer) { buffer.startSequence (this.type); writeInt32 (buffer, ObjectType.Integer, this.id); writeInt32 (buffer, ObjectType.Integer, (this.type == PduType.GetBulkRequest) ? (this.options.nonRepeaters || 0) : 0); writeInt32 (buffer, ObjectType.Integer, (this.type == PduType.GetBulkRequest) ? (this.options.maxRepetitions || 0) : 0); writeVarbinds (buffer, this.varbinds); buffer.endSequence (); }; SimplePdu.prototype.initializeFromVariables = function (id, varbinds, options) { this.id = id; this.varbinds = varbinds; this.options = options || {}; this.contextName = (options && options.context) ? options.context : ""; }; SimplePdu.prototype.initializeFromBuffer = function (reader) { this.type = reader.peek (); reader.readSequence (); this.id = readInt32 (reader); this.nonRepeaters = readInt32 (reader); this.maxRepetitions = readInt32 (reader); this.varbinds = []; readVarbinds (reader, this.varbinds); }; SimplePdu.prototype.getResponsePduForRequest = function () { var responsePdu = GetResponsePdu.createFromVariables(this.id, [], {}); if ( this.contextEngineID ) { responsePdu.contextEngineID = this.contextEngineID; responsePdu.contextName = this.contextName; } return responsePdu; }; SimplePdu.createFromVariables = function (pduClass, id, varbinds, options) { var pdu = new pduClass (id, varbinds, options); pdu.id = id; pdu.varbinds = varbinds; pdu.options = options || {}; pdu.contextName = (options && options.context) ? options.context : ""; return pdu; }; var GetBulkRequestPdu = function () { this.type = PduType.GetBulkRequest; GetBulkRequestPdu.super_.apply (this, arguments); }; util.inherits (GetBulkRequestPdu, SimplePdu); GetBulkRequestPdu.createFromBuffer = function (reader) { var pdu = new GetBulkRequestPdu (); pdu.initializeFromBuffer (reader); return pdu; }; var GetNextRequestPdu = function () { this.type = PduType.GetNextRequest; GetNextRequestPdu.super_.apply (this, arguments); }; util.inherits (GetNextRequestPdu, SimplePdu); GetNextRequestPdu.createFromBuffer = function (reader) { var pdu = new GetNextRequestPdu (); pdu.initializeFromBuffer (reader); return pdu; }; var GetRequestPdu = function () { this.type = PduType.GetRequest; GetRequestPdu.super_.apply (this, arguments); }; util.inherits (GetRequestPdu, SimplePdu); GetRequestPdu.createFromBuffer = function (reader) { var pdu = new GetRequestPdu(); pdu.initializeFromBuffer (reader); return pdu; }; GetRequestPdu.createFromVariables = function (id, varbinds, options) { var pdu = new GetRequestPdu(); pdu.initializeFromVariables (id, varbinds, options); return pdu; }; var InformRequestPdu = function () { this.type = PduType.InformRequest; InformRequestPdu.super_.apply (this, arguments); }; util.inherits (InformRequestPdu, SimplePdu); InformRequestPdu.createFromBuffer = function (reader) { var pdu = new InformRequestPdu(); pdu.initializeFromBuffer (reader); return pdu; }; var SetRequestPdu = function () { this.type = PduType.SetRequest; SetRequestPdu.super_.apply (this, arguments); }; util.inherits (SetRequestPdu, SimplePdu); SetRequestPdu.createFromBuffer = function (reader) { var pdu = new SetRequestPdu (); pdu.initializeFromBuffer (reader); return pdu; }; var TrapPdu = function () { this.type = PduType.Trap; }; TrapPdu.prototype.toBuffer = function (buffer) { buffer.startSequence (this.type); buffer.writeOID (this.enterprise); buffer.writeBuffer (Buffer.from (this.agentAddr.split (".")), ObjectType.IpAddress); writeInt32 (buffer, ObjectType.Integer, this.generic); writeInt32 (buffer, ObjectType.Integer, this.specific); writeUint32 (buffer, ObjectType.TimeTicks, this.upTime || Math.floor (process.uptime () * 100)); writeVarbinds (buffer, this.varbinds); buffer.endSequence (); }; TrapPdu.createFromBuffer = function (reader) { var pdu = new TrapPdu(); reader.readSequence (); pdu.enterprise = reader.readOID (); pdu.agentAddr = readIpAddress (reader); pdu.generic = readInt32 (reader); pdu.specific = readInt32 (reader); pdu.upTime = readUint32 (reader); pdu.varbinds = []; readVarbinds (reader, pdu.varbinds); return pdu; }; TrapPdu.createFromVariables = function (typeOrOid, varbinds, options) { var pdu = new TrapPdu (); pdu.agentAddr = options.agentAddr || "127.0.0.1"; pdu.upTime = options.upTime; if (typeof typeOrOid == "string") { pdu.generic = TrapType.EnterpriseSpecific; pdu.specific = parseInt (typeOrOid.match (/\.(\d+)$/)[1]); pdu.enterprise = typeOrOid.replace (/\.(\d+)$/, ""); } else { pdu.generic = typeOrOid; pdu.specific = 0; pdu.enterprise = "1.3.6.1.4.1"; } pdu.varbinds = varbinds; return pdu; }; var TrapV2Pdu = function () { this.type = PduType.TrapV2; TrapV2Pdu.super_.apply (this, arguments); }; util.inherits (TrapV2Pdu, SimplePdu); TrapV2Pdu.createFromBuffer = function (reader) { var pdu = new TrapV2Pdu(); pdu.initializeFromBuffer (reader); return pdu; }; TrapV2Pdu.createFromVariables = function (id, varbinds, options) { var pdu = new TrapV2Pdu(); pdu.initializeFromVariables (id, varbinds, options); return pdu; }; var SimpleResponsePdu = function() { }; SimpleResponsePdu.prototype.toBuffer = function (writer) { writer.startSequence (this.type); writeInt32 (writer, ObjectType.Integer, this.id); writeInt32 (writer, ObjectType.Integer, this.errorStatus || 0); writeInt32 (writer, ObjectType.Integer, this.errorIndex || 0); writeVarbinds (writer, this.varbinds); writer.endSequence (); }; SimpleResponsePdu.prototype.initializeFromBuffer = function (reader) { reader.readSequence (this.type); this.id = readInt32 (reader); this.errorStatus = readInt32 (reader); this.errorIndex = readInt32 (reader); this.varbinds = []; readVarbinds (reader, this.varbinds); }; SimpleResponsePdu.prototype.initializeFromVariables = function (id, varbinds, options) { this.id = id; this.varbinds = varbinds; this.options = options || {}; }; var GetResponsePdu = function () { this.type = PduType.GetResponse; GetResponsePdu.super_.apply (this, arguments); }; util.inherits (GetResponsePdu, SimpleResponsePdu); GetResponsePdu.createFromBuffer = function (reader) { var pdu = new GetResponsePdu (); pdu.initializeFromBuffer (reader); return pdu; }; GetResponsePdu.createFromVariables = function (id, varbinds, options) { var pdu = new GetResponsePdu(); pdu.initializeFromVariables (id, varbinds, options); return pdu; }; var ReportPdu = function () { this.type = PduType.Report; ReportPdu.super_.apply (this, arguments); }; util.inherits (ReportPdu, SimpleResponsePdu); ReportPdu.createFromBuffer = function (reader) { var pdu = new ReportPdu (); pdu.initializeFromBuffer (reader); return pdu; }; ReportPdu.createFromVariables = function (id, varbinds, options) { var pdu = new ReportPdu(); pdu.initializeFromVariables (id, varbinds, options); return pdu; }; var readPdu = function (reader, scoped) { var pdu; var contextEngineID; var contextName; if ( scoped ) { reader = new ber.Reader (reader.readString (ber.Sequence | ber.Constructor, true)); contextEngineID = reader.readString (ber.OctetString, true); contextName = reader.readString (); } var type = reader.peek (); if (type == PduType.GetResponse) { pdu = GetResponsePdu.createFromBuffer (reader); } else if (type == PduType.Report ) { pdu = ReportPdu.createFromBuffer (reader); } else if (type == PduType.Trap ) { pdu = TrapPdu.createFromBuffer (reader); } else if (type == PduType.TrapV2 ) { pdu = TrapV2Pdu.createFromBuffer (reader); } else if (type == PduType.InformRequest ) { pdu = InformRequestPdu.createFromBuffer (reader); } else if (type == PduType.GetRequest ) { pdu = GetRequestPdu.createFromBuffer (reader); } else if (type == PduType.SetRequest ) { pdu = SetRequestPdu.createFromBuffer (reader); } else if (type == PduType.GetNextRequest ) { pdu = GetNextRequestPdu.createFromBuffer (reader); } else if (type == PduType.GetBulkRequest ) { pdu = GetBulkRequestPdu.createFromBuffer (reader); } else { throw new ResponseInvalidError ("Unknown PDU type '" + type + "' in response", ResponseInvalidCode.EUnknownPduType); } if ( scoped ) { pdu.contextEngineID = contextEngineID; pdu.contextName = contextName; } pdu.scoped = scoped; return pdu; }; var createDiscoveryPdu = function (context) { return GetRequestPdu.createFromVariables(_generateId(), [], {context: context}); }; var Authentication = {}; Authentication.HMAC_BUFFER_SIZE = 1024*1024; Authentication.algorithms = {}; Authentication.algorithms[AuthProtocols.md5] = { KEY_LENGTH: 16, AUTHENTICATION_CODE_LENGTH: 12, CRYPTO_ALGORITHM: 'md5' }; Authentication.algorithms[AuthProtocols.sha] = { KEY_LENGTH: 20, AUTHENTICATION_CODE_LENGTH: 12, CRYPTO_ALGORITHM: 'sha1' }; Authentication.algorithms[AuthProtocols.sha224] = { KEY_LENGTH: 28, AUTHENTICATION_CODE_LENGTH: 16, CRYPTO_ALGORITHM: 'sha224' }; Authentication.algorithms[AuthProtocols.sha256] = { KEY_LENGTH: 32, AUTHENTICATION_CODE_LENGTH: 24, CRYPTO_ALGORITHM: 'sha256' }; Authentication.algorithms[AuthProtocols.sha384] = { KEY_LENGTH: 48, AUTHENTICATION_CODE_LENGTH: 32, CRYPTO_ALGORITHM: 'sha384' }; Authentication.algorithms[AuthProtocols.sha512] = { KEY_LENGTH: 64, AUTHENTICATION_CODE_LENGTH: 48, CRYPTO_ALGORITHM: 'sha512' }; Authentication.authToKeyCache = {}; Authentication.computeCacheKey = function (authProtocol, authPasswordString, engineID) { var engineIDString = engineID.toString('base64'); return authProtocol + authPasswordString + engineIDString; }; // Adapted from RFC3414 Appendix A.2.1. Password to Key Sample Code for MD5 Authentication.passwordToKey = function (authProtocol, authPasswordString, engineID) { var hashAlgorithm; var firstDigest; var finalDigest; var buf; var cryptoAlgorithm = Authentication.algorithms[authProtocol].CRYPTO_ALGORITHM; var cacheKey = Authentication.computeCacheKey(authProtocol, authPasswordString, engineID); if (Authentication.authToKeyCache[cacheKey] !== undefined) { return Authentication.authToKeyCache[cacheKey]; } buf = Buffer.alloc (Authentication.HMAC_BUFFER_SIZE, authPasswordString); hashAlgorithm = crypto.createHash(cryptoAlgorithm); hashAlgorithm.update(buf); firstDigest = hashAlgorithm.digest(); // debug ("First digest: " + firstDigest.toString('hex')); hashAlgorithm = crypto.createHash(cryptoAlgorithm); hashAlgorithm.update(firstDigest); hashAlgorithm.update(engineID); hashAlgorithm.update(firstDigest); finalDigest = hashAlgorithm.digest(); // debug ("Localized key: " + finalDigest.toString('hex')); Authentication.authToKeyCache[cacheKey] = finalDigest; return finalDigest; }; Authentication.getParametersLength = function (authProtocol) { return Authentication.algorithms[authProtocol].AUTHENTICATION_CODE_LENGTH; }; Authentication.writeParameters = function (messageBuffer, authProtocol, authPassword, engineID, digestInMessage) { var digestToAdd; digestToAdd = Authentication.calculateDigest (messageBuffer, authProtocol, authPassword, engineID); digestToAdd.copy (digestInMessage); // debug ("Added Auth Parameters: " + digestToAdd.toString('hex')); }; Authentication.isAuthentic = function (messageBuffer, authProtocol, authPassword, engineID, digestInMessage) { var savedDigest; var calculatedDigest; if (digestInMessage.length !== Authentication.algorithms[authProtocol].AUTHENTICATION_CODE_LENGTH) return false; // save original authenticationParameters field in message savedDigest = Buffer.from (digestInMessage); // clear the authenticationParameters field in message digestInMessage.fill (0); calculatedDigest = Authentication.calculateDigest (messageBuffer, authProtocol, authPassword, engineID); // replace previously cleared authenticationParameters field in message savedDigest.copy (digestInMessage); // debug ("Digest in message: " + digestInMessage.toString('hex')); // debug ("Calculated digest: " + calculatedDigest.toString('hex')); return calculatedDigest.equals (digestInMessage); }; Authentication.calculateDigest = function (messageBuffer, authProtocol, authPassword, engineID) { var authKey = Authentication.passwordToKey (authProtocol, authPassword, engineID); var cryptoAlgorithm = Authentication.algorithms[authProtocol].CRYPTO_ALGORITHM; var hmacAlgorithm = crypto.createHmac (cryptoAlgorithm, authKey); hmacAlgorithm.update (messageBuffer); var digest = hmacAlgorithm.digest (); return digest.subarray (0, Authentication.algorithms[authProtocol].AUTHENTICATION_CODE_LENGTH); }; var Encryption = {}; Encryption.encryptPdu = function (privProtocol, scopedPdu, privPassword, authProtocol, engine) { var encryptFunction = Encryption.algorithms[privProtocol].encryptPdu; return encryptFunction (scopedPdu, privProtocol, privPassword, authProtocol, engine); }; Encryption.decryptPdu = function (privProtocol, encryptedPdu, privParameters, privPassword, authProtocol, engine) { var decryptFunction = Encryption.algorithms[privProtocol].decryptPdu; return decryptFunction (encryptedPdu, privProtocol, privParameters, privPassword, authProtocol, engine); }; Encryption.debugEncrypt = function (encryptionKey, iv, plainPdu, encryptedPdu) { debug ("Key: " + encryptionKey.toString ('hex')); debug ("IV: " + iv.toString ('hex')); debug ("Plain: " + plainPdu.toString ('hex')); debug ("Encrypted: " + encryptedPdu.toString ('hex')); }; Encryption.debugDecrypt = function (decryptionKey, iv, encryptedPdu, plainPdu) { debug ("Key: " + decryptionKey.toString ('hex')); debug ("IV: " + iv.toString ('hex')); debug ("Encrypted: " + encryptedPdu.toString ('hex')); debug ("Plain: " + plainPdu.toString ('hex')); }; Encryption.generateLocalizedKey = function (algorithm, authProtocol, privPassword, engineID) { var privLocalizedKey; var encryptionKey; privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engineID); encryptionKey = Buffer.alloc (algorithm.KEY_LENGTH); privLocalizedKey.copy (encryptionKey, 0, 0, algorithm.KEY_LENGTH); return encryptionKey; }; Encryption.generateLocalizedKeyBlumenthal = function (algorithm, authProtocol, privPassword, engineID) { let authKeyLength; let privLocalizedKey; let encryptionKey; let rounds; let hashInput; let nextHash; let hashAlgorithm; authKeyLength = Authentication.algorithms[authProtocol].KEY_LENGTH; rounds = Math.ceil (algorithm.KEY_LENGTH / authKeyLength ); encryptionKey = Buffer.alloc (algorithm.KEY_LENGTH); privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engineID); nextHash = privLocalizedKey; for ( let round = 0 ; round < rounds ; round++ ) { nextHash.copy (encryptionKey, round * authKeyLength, 0, authKeyLength); if ( round < rounds - 1 ) { hashAlgorithm = crypto.createHash (Authentication.algorithms[authProtocol].CRYPTO_ALGORITHM); hashInput = Buffer.alloc ( (round + 1) * authKeyLength); encryptionKey.copy (hashInput, round * authKeyLength, 0, (round + 1) * authKeyLength); hashAlgorithm.update (hashInput); nextHash = hashAlgorithm.digest (); } } return encryptionKey; }; Encryption.generateLocalizedKeyReeder = function (algorithm, authProtocol, privPassword, engineID) { let authKeyLength; let privLocalizedKey; let encryptionKey; let rounds; let nextPasswordInput; authKeyLength = Authentication.algorithms[authProtocol].KEY_LENGTH; rounds = Math.ceil (algorithm.KEY_LENGTH / authKeyLength ); encryptionKey = Buffer.alloc (algorithm.KEY_LENGTH); nextPasswordInput = privPassword; for ( let round = 0 ; round < rounds ; round++ ) { privLocalizedKey = Authentication.passwordToKey (authProtocol, nextPasswordInput, engineID); privLocalizedKey.copy (encryptionKey, round * authKeyLength, 0, authKeyLength); nextPasswordInput = privLocalizedKey; } return encryptionKey; }; Encryption.encryptPduDes = function (scopedPdu, privProtocol, privPassword, authProtocol, engine) { var des = Encryption.algorithms[PrivProtocols.des]; var privLocalizedKey; var encryptionKey; var preIv; var salt; var iv; var i; var paddedScopedPduLength; var paddedScopedPdu; var encryptedPdu; encryptionKey = Encryption.generateLocalizedKey (des, authProtocol, privPassword, engine.engineID); privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID); encryptionKey = Buffer.alloc (des.KEY_LENGTH); privLocalizedKey.copy (encryptionKey, 0, 0, des.KEY_LENGTH); preIv = Buffer.alloc (des.BLOCK_LENGTH); privLocalizedKey.copy (preIv, 0, des.KEY_LENGTH, des.KEY_LENGTH + des.BLOCK_LENGTH); salt = Buffer.alloc (des.BLOCK_LENGTH); // set local SNMP engine boots part of salt to 1, as we have no persistent engine state salt.fill ('00000001', 0, 4, 'hex'); // set local integer part of salt to random salt.fill (crypto.randomBytes (4), 4, 8); iv = Buffer.alloc (des.BLOCK_LENGTH); for (i = 0; i < iv.length; i++) { iv[i] = preIv[i] ^ salt[i]; } if (scopedPdu.length % des.BLOCK_LENGTH == 0) { paddedScopedPdu = scopedPdu; } else { paddedScopedPduLength = des.BLOCK_LENGTH * (Math.floor (scopedPdu.length / des.BLOCK_LENGTH) + 1); paddedScopedPdu = Buffer.alloc (paddedScopedPduLength); scopedPdu.copy (paddedScopedPdu, 0, 0, scopedPdu.length); } if (DES_IMPLEMENTATION === 'native') { // TODO: Implement native encryption } else { const cipher = crypto.createCipheriv (des.CRYPTO_ALGORITHM, encryptionKey, iv); encryptedPdu = cipher.update (paddedScopedPdu); encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]); } // Encryption.debugEncrypt (encryptionKey, iv, paddedScopedPdu, encryptedPdu); return { encryptedPdu: encryptedPdu, msgPrivacyParameters: salt }; }; Encryption.decryptPduDes = function (encryptedPdu, privProtocol, privParameters, privPassword, authProtocol, engine) { var des = Encryption.algorithms[PrivProtocols.des]; var privLocalizedKey; var decryptionKey; var preIv; var salt; var iv; var i; var decryptedPdu; privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID); decryptionKey = Buffer.alloc (des.KEY_LENGTH); privLocalizedKey.copy (decryptionKey, 0, 0, des.KEY_LENGTH); preIv = Buffer.alloc (des.BLOCK_LENGTH); privLocalizedKey.copy (preIv, 0, des.KEY_LENGTH, des.KEY_LENGTH + des.BLOCK_LENGTH); salt = privParameters; iv = Buffer.alloc (des.BLOCK_LENGTH); for (i = 0; i < iv.length; i++) { iv[i] = preIv[i] ^ salt[i]; } if (DES_IMPLEMENTATION === 'native') { // TODO: Implement native decryption } else { const decipher = crypto.createDecipheriv (des.CRYPTO_ALGORITHM, decryptionKey, iv); decipher.setAutoPadding (false); decryptedPdu = decipher.update (encryptedPdu); decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]); } // Encryption.debugDecrypt (decryptionKey, iv, encryptedPdu, decryptedPdu); return decryptedPdu; }; Encryption.generateIvAes = function (aes, engineBoots, engineTime, salt) { var iv; var engineBootsBuffer; var engineTimeBuffer; // iv = engineBoots(4) | engineTime(4) | salt(8) iv = Buffer.alloc (aes.BLOCK_LENGTH); engineBootsBuffer = Buffer.alloc (4); engineBootsBuffer.writeUInt32BE (engineBoots); engineTimeBuffer = Buffer.alloc (4); engineTimeBuffer.writeUInt32BE (engineTime); engineBootsBuffer.copy (iv, 0, 0, 4); engineTimeBuffer.copy (iv, 4, 0, 4); salt.copy (iv, 8, 0, 8); return iv; }; Encryption.encryptPduAes = function (scopedPdu, privProtocol, privPassword, authProtocol, engine) { var aes = Encryption.algorithms[privProtocol]; var localizationAlgorithm = aes.localizationAlgorithm; var encryptionKey; var salt; var iv; var cipher; var encryptedPdu; encryptionKey = localizationAlgorithm (aes, authProtocol, privPassword, engine.engineID); salt = Buffer.alloc (8).fill (crypto.randomBytes (8), 0, 8); iv = Encryption.generateIvAes (aes, engine.engineBoots, engine.engineTime, salt); cipher = crypto.createCipheriv (aes.CRYPTO_ALGORITHM, encryptionKey, iv); encryptedPdu = cipher.update (scopedPdu); encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]); // Encryption.debugEncrypt (encryptionKey, iv, scopedPdu, encryptedPdu); return { encryptedPdu: encryptedPdu, msgPrivacyParameters: salt }; }; Encryption.decryptPduAes = function (encryptedPdu, privProtocol, privParameters, privPassword, authProtocol, engine) { var aes = Encryption.algorithms[privProtocol]; var localizationAlgorithm = aes.localizationAlgorithm; var decryptionKey; var iv; var decipher; var decryptedPdu; decryptionKey = localizationAlgorithm (aes, authProtocol, privPassword, engine.engineID); iv = Encryption.generateIvAes (aes, engine.engineBoots, engine.engineTime, privParameters); decipher = crypto.createDecipheriv (aes.CRYPTO_ALGORITHM, decryptionKey, iv); decryptedPdu = decipher.update (encryptedPdu); decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]); // Encryption.debugDecrypt (decryptionKey, iv, encryptedPdu, decryptedPdu); return decryptedPdu; }; Encryption.algorithms = {}; Encryption.algorithms[PrivProtocols.des] = { CRYPTO_ALGORITHM: 'des-cbc', KEY_LENGTH: 8, BLOCK_LENGTH: 8, encryptPdu: Encryption.encryptPduDes, decryptPdu: Encryption.decryptPduDes, localizationAlgorithm: Encryption.generateLocalizedKey }; Encryption.algorithms[PrivProtocols.aes] = { CRYPTO_ALGORITHM: 'aes-128-cfb', KEY_LENGTH: 16, BLOCK_LENGTH: 16, encryptPdu: Encryption.encryptPduAes, decryptPdu: Encryption.decryptPduAes, localizationAlgorithm: Encryption.generateLocalizedKey }; Encryption.algorithms[PrivProtocols.aes256b] = { CRYPTO_ALGORITHM: 'aes-256-cfb', KEY_LENGTH: 32, BLOCK_LENGTH: 16, encryptPdu: Encryption.encryptPduAes, decryptPdu: Encryption.decryptPduAes, localizationAlgorithm: Encryption.generateLocalizedKeyBlumenthal }; Encryption.algorithms[PrivProtocols.aes256r] = { CRYPTO_ALGORITHM: 'aes-256-cfb', KEY_LENGTH: 32, BLOCK_LENGTH: 16, encryptPdu: Encryption.encryptPduAes, decryptPdu: Encryption.decryptPduAes, localizationAlgorithm: Encryption.generateLocalizedKeyReeder }; /***************************************************************************** ** Message class definition **/ var Message = function () { }; Message.prototype.getReqId = function () { return this.version == Version3 ? this.msgGlobalData.msgID : this.pdu.id; }; Message.prototype.toBuffer = function () { if ( this.version == Version3 ) { return this.toBufferV3(); } else { return this.toBufferCommunity(); } }; Message.prototype.toBufferCommunity = function () { if (this.buffer) return this.buffer; var writer = new ber.Writer (); writer.startSequence (); writeInt32 (writer, ObjectType.Integer, this.version); writer.writeString (this.community); this.pdu.toBuffer (writer); writer.endSequence (); this.buffer = writer.buffer; return this.buffer; }; Message.prototype.toBufferV3 = function () { var encryptionResult; if (this.buffer) return this.buffer; // ScopedPDU var scopedPduWriter = new ber.Writer (); scopedPduWriter.startSequence (); var contextEngineID = this.pdu.contextEngineID ? this.pdu.contextEngineID : this.msgSecurityParameters.msgAuthoritativeEngineID; if ( contextEngineID.length == 0 ) { scopedPduWriter.writeString (""); } else { scopedPduWriter.writeBuffer (contextEngineID, ber.OctetString); } scopedPduWriter.writeString (this.pdu.contextName); this.pdu.toBuffer (scopedPduWriter); scopedPduWriter.endSequence (); if ( this.hasPrivacy() ) { var authoritativeEngine = { engineID: this.msgSecurityParameters.msgAuthoritativeEngineID, engineBoots: this.msgSecurityParameters.msgAuthoritativeEngineBoots, engineTime: this.msgSecurityParameters.msgAuthoritativeEngineTime, }; encryptionResult = Encryption.encryptPdu (this.user.privProtocol, scopedPduWriter.buffer, this.user.privKey, this.user.authProtocol, authoritativeEngine); } var writer = new ber.Writer (); writer.startSequence (); writeInt32 (writer, ObjectType.Integer, this.version); // HeaderData writer.startSequence (); writeInt32 (writer, ObjectType.Integer, this.msgGlobalData.msgID); writeInt32 (writer, ObjectType.Integer, this.msgGlobalData.msgMaxSize); writer.writeByte (ber.OctetString); writer.writeByte (1); writer.writeByte (this.msgGlobalData.msgFlags); writeInt32 (writer, ObjectType.Integer, this.msgGlobalData.msgSecurityModel); writer.endSequence (); // msgSecurityParameters writer.startSequence (ber.OctetString); writer.startSequence (); //writer.writeString (this.msgSecurityParameters.msgAuthoritativeEngineID); // writing a zero-length buffer fails - should fix asn1-ber for this condition if ( this.msgSecurityParameters.msgAuthoritativeEngineID.length == 0 ) { writer.writeString (""); } else { writer.writeBuffer (this.msgSecurityParameters.msgAuthoritativeEngineID, ber.OctetString); } writeInt32 (writer, ObjectType.Integer, this.msgSecurityParameters.msgAuthoritativeEngineBoots); writeInt32 (writer, ObjectType.Integer, this.msgSecurityParameters.msgAuthoritativeEngineTime); writer.writeString (this.msgSecurityParameters.msgUserName); var msgAuthenticationParameters = ''; if ( this.hasAuthentication() ) { var authParametersLength = Authentication.getParametersLength (this.user.authProtocol); msgAuthenticationParameters = Buffer.alloc (authParametersLength); writer.writeBuffer (msgAuthenticationParameters, ber.OctetString); } else { writer.writeString (""); } var msgAuthenticationParametersOffset = writer._offset - msgAuthenticationParameters.length; if ( this.hasPrivacy() ) { writer.writeBuffer (encryptionResult.msgPrivacyParameters, ber.OctetString); } else { writer.writeString (""); } msgAuthenticationParametersOffset -= writer._offset; writer.endSequence (); writer.endSequence (); msgAuthenticationParametersOffset += writer._offset; if ( this.hasPrivacy() ) { writer.writeBuffer (encryptionResult.encryptedPdu, ber.OctetString); } else { writer.writeBuffer (scopedPduWriter.buffer); } msgAuthenticationParametersOffset -= writer._offset; writer.endSequence (); msgAuthenticationParametersOffset += writer._offset; this.buffer = writer.buffer; if ( this.hasAuthentication() ) { msgAuthenticationParameters = this.buffer.subarray (msgAuthenticationParametersOffset, msgAuthenticationParametersOffset + msgAuthenticationParameters.length); Authentication.writeParameters (this.buffer, this.user.authProtocol, this.user.authKey, this.msgSecurityParameters.msgAuthoritativeEngineID, msgAuthenticationParameters); } return this.buffer; }; Message.prototype.processIncomingSecurity = function (user, responseCb) { if ( this.hasPrivacy() ) { if ( ! this.decryptPdu(user, responseCb) ) { return false; } } if ( this.hasAuthentication() && ! this.isAuthenticationDisabled() ) { return this.checkAuthentication(user, responseCb); } else { return true; } }; Message.prototype.decryptPdu = function (user, responseCb) { var decryptedPdu; var decryptedPduReader; try { var authoratitiveEngine = { engineID: this.msgSecurityParameters.msgAuthoritativeEngineID, engineBoots: this.msgSecurityParameters.msgAuthoritativeEngineBoots, engineTime: this.msgSecurityParameters.msgAuthoritativeEngineTime }; decryptedPdu = Encryption.decryptPdu(user.privProtocol, this.encryptedPdu, this.msgSecurityParameters.msgPrivacyParameters, user.privKey, user.authProtocol, authoratitiveEngine); decryptedPduReader = new ber.Reader (decryptedPdu); this.pdu = readPdu(decryptedPduReader, true); return true; } catch (error) { responseCb (new ResponseInvalidError ("Failed to decrypt PDU: " + error, ResponseInvalidCode.ECouldNotDecrypt)); return false; } }; Message.prototype.checkAuthentication = function (user, responseCb) { if ( Authentication.isAuthentic(this.buffer, user.authProtocol, user.authKey, this.msgSecurityParameters.msgAuthoritativeEngineID, this.msgSecurityParameters.msgAuthenticationParameters) ) { return true; } else { responseCb (new ResponseInvalidError ("Authentication digest " + this.msgSecurityParameters.msgAuthenticationParameters.toString ('hex') + " received in message does not match digest " + Authentication.calculateDigest (this.buffer, user.authProtocol, user.authKey, this.msgSecurityParameters.msgAuthoritativeEngineID).toString ('hex') + " calculated for message", ResponseInvalidCode.EAuthFailure, { user })); return false; } }; Message.prototype.setMsgFlags = function (bitPosition, flag) { if ( this.msgGlobalData && this.msgGlobalData !== undefined && this.msgGlobalData !== null ) { if ( flag ) { this.msgGlobalData.msgFlags = this.msgGlobalData.msgFlags | ( 2 ** bitPosition ); } else { this.msgGlobalData.msgFlags = this.msgGlobalData.msgFlags & ( 255 - 2 ** bitPosition ); } } }; Messa