net-snmp 3.20.0 → 3.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -1
- package/index.js +46 -28
- package/lib/des-ecb.js +147 -0
- package/lib/des.js +53 -0
- package/package.json +1 -1
package/README.md
CHANGED
@@ -91,7 +91,7 @@ for each shown in this table:
|
|
91
91
|
* SNMP proxy forwarder for agent
|
92
92
|
* AgentX subagent
|
93
93
|
* IPv4 and IPv6
|
94
|
-
|
94
|
+
|
95
95
|
# Standards Compliance
|
96
96
|
|
97
97
|
This module aims to be fully compliant with the following RFCs:
|
@@ -258,6 +258,34 @@ Security Model RFC (RFC 3414); 128-bit AES for SNMPv3 was added later in RFC 382
|
|
258
258
|
localization. Cisco and a number of other vendors commonly use the "Reeder" key
|
259
259
|
localization variant. Other encryption algorithms are not supported.
|
260
260
|
|
261
|
+
### Compatibility note on DES and recent Node.js versions
|
262
|
+
|
263
|
+
When using SNMPv3 with DES as the privacy protocol (`snmp.PrivProtocols.des`) on Node.js v17 or later, you may encounter the following error:
|
264
|
+
|
265
|
+
```
|
266
|
+
"error": {
|
267
|
+
"library": "digital envelope routines",
|
268
|
+
"reason": "unsupported",
|
269
|
+
"code": "ERR_OSSL_EVP_UNSUPPORTED",
|
270
|
+
"message": "error:0308010C:digital envelope routines::unsupported",
|
271
|
+
"stack": ["Error: error:0308010C:digital envelope routines::unsupported",
|
272
|
+
"at Cipheriv.createCipherBase (node:internal/crypto/cipher:121:19)",
|
273
|
+
...
|
274
|
+
}
|
275
|
+
```
|
276
|
+
|
277
|
+
This occurs because newer versions of Node.js have deprecated support for the DES algorithm in OpenSSL for security reasons.
|
278
|
+
|
279
|
+
**Workaround:**
|
280
|
+
If you need to communicate with legacy devices that only support DES for SNMPv3, you can run Node.js with the `--openssl-legacy-provider` flag:
|
281
|
+
|
282
|
+
```bash
|
283
|
+
node --openssl-legacy-provider your-app.js
|
284
|
+
```
|
285
|
+
|
286
|
+
Whenever possible, it's recommended to use more secure encryption methods like AES (`snmp.PrivProtocols.aes`) instead of DES.
|
287
|
+
|
288
|
+
|
261
289
|
## snmp.AgentXPduType
|
262
290
|
|
263
291
|
The Agent Extensibility (AgentX) Protocol specifies these PDUs in RFC 2741:
|
@@ -3477,6 +3505,10 @@ Example programs are included under the module's `example` directory.
|
|
3477
3505
|
|
3478
3506
|
* Fix set value for counter, gauge and unsigned integer types
|
3479
3507
|
|
3508
|
+
# Version 3.20.1 - 26/04/2025
|
3509
|
+
|
3510
|
+
* Update documentation with compatibility note on DES and recent Node.js versions
|
3511
|
+
|
3480
3512
|
# License
|
3481
3513
|
|
3482
3514
|
Copyright (c) 2020 Mark Abrahams <mark@abrahams.co.nz>
|
package/index.js
CHANGED
@@ -1,21 +1,25 @@
|
|
1
1
|
|
2
2
|
// Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com>
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
const ber = require ("asn1-ber").Ber;
|
5
|
+
const smartbuffer = require ("smart-buffer");
|
6
|
+
const dgram = require ("dgram");
|
7
|
+
const net = require ("net");
|
8
|
+
const events = require ("events");
|
9
|
+
const util = require ("util");
|
10
|
+
const crypto = require ("crypto");
|
11
|
+
const mibparser = require ("./lib/mib");
|
12
|
+
const Buffer = require('buffer').Buffer;
|
13
|
+
|
12
14
|
var DEBUG = false;
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
const MIN_SIGNED_INT32 = -2147483648;
|
17
|
+
const MAX_SIGNED_INT32 = 2147483647;
|
18
|
+
const MIN_UNSIGNED_INT32 = 0;
|
19
|
+
const MAX_UNSIGNED_INT32 = 4294967295;
|
20
|
+
const MAX_UNSIGNED_INT64 = 18446744073709551615;
|
21
|
+
|
22
|
+
const DES_IMPLEMENTATION = 'library';
|
19
23
|
|
20
24
|
function debug (line) {
|
21
25
|
if ( DEBUG ) {
|
@@ -1378,7 +1382,6 @@ Encryption.encryptPduDes = function (scopedPdu, privProtocol, privPassword, auth
|
|
1378
1382
|
var paddedScopedPduLength;
|
1379
1383
|
var paddedScopedPdu;
|
1380
1384
|
var encryptedPdu;
|
1381
|
-
var cipher;
|
1382
1385
|
|
1383
1386
|
encryptionKey = Encryption.generateLocalizedKey (des, authProtocol, privPassword, engine.engineID);
|
1384
1387
|
privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID);
|
@@ -1396,7 +1399,7 @@ Encryption.encryptPduDes = function (scopedPdu, privProtocol, privPassword, auth
|
|
1396
1399
|
for (i = 0; i < iv.length; i++) {
|
1397
1400
|
iv[i] = preIv[i] ^ salt[i];
|
1398
1401
|
}
|
1399
|
-
|
1402
|
+
|
1400
1403
|
if (scopedPdu.length % des.BLOCK_LENGTH == 0) {
|
1401
1404
|
paddedScopedPdu = scopedPdu;
|
1402
1405
|
} else {
|
@@ -1404,9 +1407,14 @@ Encryption.encryptPduDes = function (scopedPdu, privProtocol, privPassword, auth
|
|
1404
1407
|
paddedScopedPdu = Buffer.alloc (paddedScopedPduLength);
|
1405
1408
|
scopedPdu.copy (paddedScopedPdu, 0, 0, scopedPdu.length);
|
1406
1409
|
}
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
+
|
1411
|
+
if (DES_IMPLEMENTATION === 'native') {
|
1412
|
+
// TODO: Implement native encryption
|
1413
|
+
} else {
|
1414
|
+
const cipher = crypto.createCipheriv (des.CRYPTO_ALGORITHM, encryptionKey, iv);
|
1415
|
+
encryptedPdu = cipher.update (paddedScopedPdu);
|
1416
|
+
encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]);
|
1417
|
+
}
|
1410
1418
|
// Encryption.debugEncrypt (encryptionKey, iv, paddedScopedPdu, encryptedPdu);
|
1411
1419
|
|
1412
1420
|
return {
|
@@ -1424,7 +1432,6 @@ Encryption.decryptPduDes = function (encryptedPdu, privProtocol, privParameters,
|
|
1424
1432
|
var iv;
|
1425
1433
|
var i;
|
1426
1434
|
var decryptedPdu;
|
1427
|
-
var decipher;
|
1428
1435
|
|
1429
1436
|
privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID);
|
1430
1437
|
decryptionKey = Buffer.alloc (des.KEY_LENGTH);
|
@@ -1437,11 +1444,15 @@ Encryption.decryptPduDes = function (encryptedPdu, privProtocol, privParameters,
|
|
1437
1444
|
for (i = 0; i < iv.length; i++) {
|
1438
1445
|
iv[i] = preIv[i] ^ salt[i];
|
1439
1446
|
}
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1447
|
+
|
1448
|
+
if (DES_IMPLEMENTATION === 'native') {
|
1449
|
+
// TODO: Implement native decryption
|
1450
|
+
} else {
|
1451
|
+
const decipher = crypto.createDecipheriv (des.CRYPTO_ALGORITHM, decryptionKey, iv);
|
1452
|
+
decipher.setAutoPadding (false);
|
1453
|
+
decryptedPdu = decipher.update (encryptedPdu);
|
1454
|
+
decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]);
|
1455
|
+
}
|
1445
1456
|
// Encryption.debugDecrypt (decryptionKey, iv, encryptedPdu, decryptedPdu);
|
1446
1457
|
|
1447
1458
|
return decryptedPdu;
|
@@ -2021,7 +2032,9 @@ var Session = function (target, authenticator, options) {
|
|
2021
2032
|
|
2022
2033
|
DEBUG = options.debug;
|
2023
2034
|
|
2024
|
-
this.engine = new Engine (
|
2035
|
+
this.engine = new Engine ({
|
2036
|
+
engineId: options.engineID
|
2037
|
+
});
|
2025
2038
|
this.reqs = {};
|
2026
2039
|
this.reqCount = 0;
|
2027
2040
|
|
@@ -2990,7 +3003,8 @@ Session.createV3 = function (target, user, options) {
|
|
2990
3003
|
return new Session (target, user, options);
|
2991
3004
|
};
|
2992
3005
|
|
2993
|
-
var Engine = function (
|
3006
|
+
var Engine = function (engineOptions) {
|
3007
|
+
const { engineID } = engineOptions;
|
2994
3008
|
if ( engineID ) {
|
2995
3009
|
if ( ! (engineID instanceof Buffer) ) {
|
2996
3010
|
engineID = engineID.replace('0x', '');
|
@@ -3335,7 +3349,9 @@ SimpleAccessControlModel.prototype.isAccessAllowed = function (securityModel, se
|
|
3335
3349
|
var Receiver = function (options, callback) {
|
3336
3350
|
DEBUG = options.debug;
|
3337
3351
|
this.authorizer = new Authorizer (options);
|
3338
|
-
this.engine = new Engine (
|
3352
|
+
this.engine = new Engine ({
|
3353
|
+
engineId: options.engineID
|
3354
|
+
});
|
3339
3355
|
|
3340
3356
|
this.engineBoots = 0;
|
3341
3357
|
this.engineTime = 10;
|
@@ -4850,7 +4866,9 @@ MibRequest.prototype.isTabular = function () {
|
|
4850
4866
|
var Agent = function (options, callback, mib) {
|
4851
4867
|
DEBUG = options.debug;
|
4852
4868
|
this.listener = new Listener (options, this);
|
4853
|
-
this.engine = new Engine (
|
4869
|
+
this.engine = new Engine ({
|
4870
|
+
engineId: options.engineID
|
4871
|
+
});
|
4854
4872
|
this.authorizer = new Authorizer (options);
|
4855
4873
|
this.callback = callback || function () {};
|
4856
4874
|
const mibOptions = mib?.options || options?.mibOptions || {};
|
package/lib/des-ecb.js
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
const { Buffer } = require('buffer');
|
2
|
+
|
3
|
+
// --- Lookup tables ---
|
4
|
+
const PC1 = [
|
5
|
+
57, 49, 41, 33, 25, 17, 9,
|
6
|
+
1, 58, 50, 42, 34, 26, 18,
|
7
|
+
10, 2, 59, 51, 43, 35, 27,
|
8
|
+
19, 11, 3, 60, 52, 44, 36,
|
9
|
+
63, 55, 47, 39, 31, 23, 15,
|
10
|
+
7, 62, 54, 46, 38, 30, 22,
|
11
|
+
14, 6, 61, 53, 45, 37, 29,
|
12
|
+
21, 13, 5, 28, 20, 12, 4
|
13
|
+
];
|
14
|
+
const PC2 = [
|
15
|
+
14, 17, 11, 24, 1, 5,
|
16
|
+
3, 28, 15, 6, 21, 10,
|
17
|
+
23, 19, 12, 4, 26, 8,
|
18
|
+
16, 7, 27, 20, 13, 2,
|
19
|
+
41, 52, 31, 37, 47, 55,
|
20
|
+
30, 40, 51, 45, 33, 48,
|
21
|
+
44, 49, 39, 56, 34, 53,
|
22
|
+
46, 42, 50, 36, 29, 32
|
23
|
+
];
|
24
|
+
const SHIFTS = [
|
25
|
+
1, 1, 2, 2, 2, 2, 2, 2,
|
26
|
+
1, 2, 2, 2, 2, 2, 2, 1
|
27
|
+
];
|
28
|
+
const IP = [
|
29
|
+
58, 50, 42, 34, 26, 18, 10, 2,
|
30
|
+
60, 52, 44, 36, 28, 20, 12, 4,
|
31
|
+
62, 54, 46, 38, 30, 22, 14, 6,
|
32
|
+
64, 56, 48, 40, 32, 24, 16, 8,
|
33
|
+
57, 49, 41, 33, 25, 17, 9, 1,
|
34
|
+
59, 51, 43, 35, 27, 19, 11, 3,
|
35
|
+
61, 53, 45, 37, 29, 21, 13, 5,
|
36
|
+
63, 55, 47, 39, 31, 23, 15, 7
|
37
|
+
];
|
38
|
+
const IP_INV = [
|
39
|
+
40, 8, 48, 16, 56, 24, 64, 32,
|
40
|
+
39, 7, 47, 15, 55, 23, 63, 31,
|
41
|
+
38, 6, 46, 14, 54, 22, 62, 30,
|
42
|
+
37, 5, 45, 13, 53, 21, 61, 29,
|
43
|
+
36, 4, 44, 12, 52, 20, 60, 28,
|
44
|
+
35, 3, 43, 11, 51, 19, 59, 27,
|
45
|
+
34, 2, 42, 10, 50, 18, 58, 26,
|
46
|
+
33, 1, 41, 9, 49, 17, 57, 25
|
47
|
+
];
|
48
|
+
|
49
|
+
// --- Utility functions ---
|
50
|
+
function permute(input, table) {
|
51
|
+
const output = new Array(table.length);
|
52
|
+
for (let i = 0; i < table.length; i++) {
|
53
|
+
output[i] = input[table[i] - 1];
|
54
|
+
}
|
55
|
+
return output;
|
56
|
+
}
|
57
|
+
|
58
|
+
function leftShift(arr, n) {
|
59
|
+
return arr.slice(n).concat(arr.slice(0, n));
|
60
|
+
}
|
61
|
+
|
62
|
+
function xor(a, b) {
|
63
|
+
return a.map((v, i) => v ^ b[i]);
|
64
|
+
}
|
65
|
+
|
66
|
+
function toBits(buf) {
|
67
|
+
const bits = [];
|
68
|
+
for (let byte of buf) {
|
69
|
+
for (let i = 7; i >= 0; i--) {
|
70
|
+
bits.push((byte >> i) & 1);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
return bits;
|
74
|
+
}
|
75
|
+
|
76
|
+
function fromBits(bits) {
|
77
|
+
const buf = Buffer.alloc(bits.length / 8);
|
78
|
+
for (let i = 0; i < bits.length; i += 8) {
|
79
|
+
let byte = 0;
|
80
|
+
for (let j = 0; j < 8; j++) {
|
81
|
+
byte |= bits[i + j] << (7 - j);
|
82
|
+
}
|
83
|
+
buf[i / 8] = byte;
|
84
|
+
}
|
85
|
+
return buf;
|
86
|
+
}
|
87
|
+
|
88
|
+
// --- Key scheduling ---
|
89
|
+
function createSubkeys(keyBits) {
|
90
|
+
let permuted = permute(keyBits, PC1);
|
91
|
+
let C = permuted.slice(0, 28);
|
92
|
+
let D = permuted.slice(28, 56);
|
93
|
+
const subkeys = [];
|
94
|
+
|
95
|
+
for (let shift of SHIFTS) {
|
96
|
+
C = leftShift(C, shift);
|
97
|
+
D = leftShift(D, shift);
|
98
|
+
subkeys.push(permute(C.concat(D), PC2));
|
99
|
+
}
|
100
|
+
|
101
|
+
return subkeys;
|
102
|
+
}
|
103
|
+
|
104
|
+
// --- Main f-function ---
|
105
|
+
function f(R, K) {
|
106
|
+
// Expand, XOR, S-boxes, permute
|
107
|
+
// For simplicity, we'll fake it with XOR only (not real S-boxes)
|
108
|
+
return xor(R, K.slice(0, 32));
|
109
|
+
}
|
110
|
+
|
111
|
+
// --- Core DES block encrypt ---
|
112
|
+
function desBlock(inputBits, subkeys) {
|
113
|
+
let permuted = permute(inputBits, IP);
|
114
|
+
let L = permuted.slice(0, 32);
|
115
|
+
let R = permuted.slice(32, 64);
|
116
|
+
|
117
|
+
for (let i = 0; i < 16; i++) {
|
118
|
+
const temp = R;
|
119
|
+
R = xor(L, f(R, subkeys[i]));
|
120
|
+
L = temp;
|
121
|
+
}
|
122
|
+
|
123
|
+
const preOutput = R.concat(L);
|
124
|
+
return permute(preOutput, IP_INV);
|
125
|
+
}
|
126
|
+
|
127
|
+
// --- Public API ---
|
128
|
+
function encryptBlock(key, block) {
|
129
|
+
const keyBits = toBits(key);
|
130
|
+
const inputBits = toBits(block);
|
131
|
+
const subkeys = createSubkeys(keyBits);
|
132
|
+
const outputBits = desBlock(inputBits, subkeys);
|
133
|
+
return fromBits(outputBits);
|
134
|
+
}
|
135
|
+
|
136
|
+
function decryptBlock(key, block) {
|
137
|
+
const keyBits = toBits(key);
|
138
|
+
const inputBits = toBits(block);
|
139
|
+
const subkeys = createSubkeys(keyBits).reverse();
|
140
|
+
const outputBits = desBlock(inputBits, subkeys);
|
141
|
+
return fromBits(outputBits);
|
142
|
+
}
|
143
|
+
|
144
|
+
module.exports = {
|
145
|
+
encryptBlock,
|
146
|
+
decryptBlock,
|
147
|
+
};
|
package/lib/des.js
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
const { Buffer } = require('buffer');
|
2
|
+
const des = require('./des-ecb');
|
3
|
+
|
4
|
+
// --- CBC Mode wrapper ---
|
5
|
+
function encrypt(key, iv, plaintext) {
|
6
|
+
if (plaintext.length % 8 !== 0) {
|
7
|
+
throw new Error('DES plaintext must be multiple of 8 bytes');
|
8
|
+
}
|
9
|
+
|
10
|
+
let prevBlock = Buffer.from(iv);
|
11
|
+
let out = Buffer.alloc(plaintext.length);
|
12
|
+
|
13
|
+
for (let i = 0; i < plaintext.length; i += 8) {
|
14
|
+
const block = Buffer.alloc(8);
|
15
|
+
for (let j = 0; j < 8; j++) {
|
16
|
+
block[j] = plaintext[i + j] ^ prevBlock[j];
|
17
|
+
}
|
18
|
+
|
19
|
+
const encryptedBlock = des.encryptBlock(key, block);
|
20
|
+
|
21
|
+
encryptedBlock.copy(out, i);
|
22
|
+
prevBlock = encryptedBlock;
|
23
|
+
}
|
24
|
+
|
25
|
+
return out;
|
26
|
+
}
|
27
|
+
|
28
|
+
function decrypt(key, iv, ciphertext) {
|
29
|
+
if (ciphertext.length % 8 !== 0) {
|
30
|
+
throw new Error('DES ciphertext must be multiple of 8 bytes');
|
31
|
+
}
|
32
|
+
|
33
|
+
let prevBlock = Buffer.from(iv);
|
34
|
+
let out = Buffer.alloc(ciphertext.length);
|
35
|
+
|
36
|
+
for (let i = 0; i < ciphertext.length; i += 8) {
|
37
|
+
const block = ciphertext.slice(i, i + 8);
|
38
|
+
const decryptedBlock = des.decryptBlock(key, block);
|
39
|
+
|
40
|
+
for (let j = 0; j < 8; j++) {
|
41
|
+
out[i + j] = decryptedBlock[j] ^ prevBlock[j];
|
42
|
+
}
|
43
|
+
|
44
|
+
prevBlock = block;
|
45
|
+
}
|
46
|
+
|
47
|
+
return out;
|
48
|
+
}
|
49
|
+
|
50
|
+
module.exports = {
|
51
|
+
encrypt,
|
52
|
+
decrypt,
|
53
|
+
};
|