@electerm/ssh2 1.16.1 → 1.17.0

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.
@@ -7,43 +7,40 @@ DEFS_Debug := \
7
7
  '-DUSING_UV_SHARED=1' \
8
8
  '-DUSING_V8_SHARED=1' \
9
9
  '-DV8_DEPRECATION_WARNINGS=1' \
10
- '-DV8_DEPRECATION_WARNINGS' \
11
- '-DV8_IMMINENT_DEPRECATION_WARNINGS' \
12
10
  '-D_GLIBCXX_USE_CXX11_ABI=1' \
11
+ '-D_FILE_OFFSET_BITS=64' \
13
12
  '-D_DARWIN_USE_64_BIT_INODE=1' \
14
13
  '-D_LARGEFILE_SOURCE' \
15
- '-D_FILE_OFFSET_BITS=64' \
16
14
  '-DOPENSSL_NO_PINSHARED' \
17
15
  '-DOPENSSL_THREADS' \
18
16
  '-DOPENSSL_API_COMPAT=0x10100000L' \
19
17
  '-DREAL_OPENSSL_MAJOR=3' \
20
18
  '-DBUILDING_NODE_EXTENSION' \
21
19
  '-DDEBUG' \
22
- '-D_DEBUG' \
23
- '-DV8_ENABLE_CHECKS'
20
+ '-D_DEBUG'
24
21
 
25
22
  # Flags passed to all source files.
26
23
  CFLAGS_Debug := \
27
24
  -O0 \
28
25
  -gdwarf-2 \
29
- -mmacosx-version-min=10.15 \
30
- -arch x86_64 \
26
+ -fno-strict-aliasing \
27
+ -mmacosx-version-min=11.0 \
28
+ -arch \
29
+ x86_64 \
31
30
  -Wall \
32
31
  -Wendif-labels \
33
32
  -W \
34
33
  -Wno-unused-parameter
35
34
 
36
35
  # Flags passed to only C files.
37
- CFLAGS_C_Debug := \
38
- -fno-strict-aliasing
36
+ CFLAGS_C_Debug :=
39
37
 
40
38
  # Flags passed to only C++ files.
41
39
  CFLAGS_CC_Debug := \
42
40
  -std=gnu++17 \
43
41
  -stdlib=libc++ \
44
42
  -fno-rtti \
45
- -fno-exceptions \
46
- -fno-strict-aliasing
43
+ -fno-exceptions
47
44
 
48
45
  # Flags passed to only ObjC files.
49
46
  CFLAGS_OBJC_Debug :=
@@ -52,13 +49,13 @@ CFLAGS_OBJC_Debug :=
52
49
  CFLAGS_OBJCC_Debug :=
53
50
 
54
51
  INCS_Debug := \
55
- -I/Users/home/Library/Caches/node-gyp/18.17.1/include/node \
56
- -I/Users/home/Library/Caches/node-gyp/18.17.1/src \
57
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/config \
58
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/openssl/include \
59
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/uv/include \
60
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/zlib \
61
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/v8/include \
52
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/include/node \
53
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/src \
54
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/openssl/config \
55
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/openssl/openssl/include \
56
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/uv/include \
57
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/zlib \
58
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/v8/include \
62
59
  -I$(srcdir)/../../../node_modules/nan
63
60
 
64
61
  DEFS_Release := \
@@ -66,12 +63,10 @@ DEFS_Release := \
66
63
  '-DUSING_UV_SHARED=1' \
67
64
  '-DUSING_V8_SHARED=1' \
68
65
  '-DV8_DEPRECATION_WARNINGS=1' \
69
- '-DV8_DEPRECATION_WARNINGS' \
70
- '-DV8_IMMINENT_DEPRECATION_WARNINGS' \
71
66
  '-D_GLIBCXX_USE_CXX11_ABI=1' \
67
+ '-D_FILE_OFFSET_BITS=64' \
72
68
  '-D_DARWIN_USE_64_BIT_INODE=1' \
73
69
  '-D_LARGEFILE_SOURCE' \
74
- '-D_FILE_OFFSET_BITS=64' \
75
70
  '-DOPENSSL_NO_PINSHARED' \
76
71
  '-DOPENSSL_THREADS' \
77
72
  '-DOPENSSL_API_COMPAT=0x10100000L' \
@@ -82,24 +77,24 @@ DEFS_Release := \
82
77
  CFLAGS_Release := \
83
78
  -O3 \
84
79
  -gdwarf-2 \
85
- -mmacosx-version-min=10.15 \
86
- -arch x86_64 \
80
+ -fno-strict-aliasing \
81
+ -mmacosx-version-min=11.0 \
82
+ -arch \
83
+ x86_64 \
87
84
  -Wall \
88
85
  -Wendif-labels \
89
86
  -W \
90
87
  -Wno-unused-parameter
91
88
 
92
89
  # Flags passed to only C files.
93
- CFLAGS_C_Release := \
94
- -fno-strict-aliasing
90
+ CFLAGS_C_Release :=
95
91
 
96
92
  # Flags passed to only C++ files.
97
93
  CFLAGS_CC_Release := \
98
94
  -std=gnu++17 \
99
95
  -stdlib=libc++ \
100
96
  -fno-rtti \
101
- -fno-exceptions \
102
- -fno-strict-aliasing
97
+ -fno-exceptions
103
98
 
104
99
  # Flags passed to only ObjC files.
105
100
  CFLAGS_OBJC_Release :=
@@ -108,13 +103,13 @@ CFLAGS_OBJC_Release :=
108
103
  CFLAGS_OBJCC_Release :=
109
104
 
110
105
  INCS_Release := \
111
- -I/Users/home/Library/Caches/node-gyp/18.17.1/include/node \
112
- -I/Users/home/Library/Caches/node-gyp/18.17.1/src \
113
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/config \
114
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/openssl/include \
115
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/uv/include \
116
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/zlib \
117
- -I/Users/home/Library/Caches/node-gyp/18.17.1/deps/v8/include \
106
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/include/node \
107
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/src \
108
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/openssl/config \
109
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/openssl/openssl/include \
110
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/uv/include \
111
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/zlib \
112
+ -I/Users/zxd/Library/Caches/node-gyp/22.19.0/deps/v8/include \
118
113
  -I$(srcdir)/../../../node_modules/nan
119
114
 
120
115
  OBJS := \
@@ -149,8 +144,9 @@ $(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
149
144
  LDFLAGS_Debug := \
150
145
  -undefined dynamic_lookup \
151
146
  -Wl,-search_paths_first \
152
- -mmacosx-version-min=10.15 \
153
- -arch x86_64 \
147
+ -mmacosx-version-min=11.0 \
148
+ -arch \
149
+ x86_64 \
154
150
  -L$(builddir) \
155
151
  -stdlib=libc++
156
152
 
@@ -161,8 +157,9 @@ LIBTOOLFLAGS_Debug := \
161
157
  LDFLAGS_Release := \
162
158
  -undefined dynamic_lookup \
163
159
  -Wl,-search_paths_first \
164
- -mmacosx-version-min=10.15 \
165
- -arch x86_64 \
160
+ -mmacosx-version-min=11.0 \
161
+ -arch \
162
+ x86_64 \
166
163
  -L$(builddir) \
167
164
  -stdlib=libc++
168
165
 
@@ -0,0 +1,243 @@
1
+ 'use strict';
2
+
3
+ const { readUInt32BE } = require('./utils.js');
4
+
5
+ /**
6
+ * Parse SSH certificate format from complete certificate buffer
7
+ * RFC: https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr.bin/ssh/PROTOCOL.certkeys?rev=1.17
8
+ *
9
+ * Certificate format (for ssh-rsa-cert-v01@openssh.com):
10
+ * string "ssh-rsa-cert-v01@openssh.com"
11
+ * string nonce
12
+ * mpint e (RSA exponent)
13
+ * mpint n (RSA modulus)
14
+ * uint64 serial
15
+ * uint32 type (1=user, 2=host)
16
+ * string key_id
17
+ * string valid_principals
18
+ * uint64 valid_after
19
+ * uint64 valid_before
20
+ * string critical_options
21
+ * string extensions
22
+ * string reserved
23
+ * string signature_key
24
+ * string signature
25
+ */
26
+ function parseSSHCertificate(certBuffer) {
27
+ if (!Buffer.isBuffer(certBuffer) || certBuffer.length < 8) {
28
+ return new Error('Invalid certificate buffer');
29
+ }
30
+
31
+ let pos = 0;
32
+
33
+ function readString() {
34
+ if (pos + 4 > certBuffer.length) {
35
+ throw new Error('Unexpected end of certificate');
36
+ }
37
+ const len = readUInt32BE(certBuffer, pos);
38
+ pos += 4;
39
+ if (pos + len > certBuffer.length) {
40
+ throw new Error('Unexpected end of certificate');
41
+ }
42
+ const result = certBuffer.slice(pos, pos + len);
43
+ pos += len;
44
+ return result;
45
+ }
46
+
47
+ function readUInt64() {
48
+ if (pos + 8 > certBuffer.length) {
49
+ throw new Error('Unexpected end of certificate');
50
+ }
51
+ const result = certBuffer.readBigUInt64BE(pos);
52
+ pos += 8;
53
+ return result;
54
+ }
55
+
56
+ function readUInt32() {
57
+ if (pos + 4 > certBuffer.length) {
58
+ throw new Error('Unexpected end of certificate');
59
+ }
60
+ const result = readUInt32BE(certBuffer, pos);
61
+ pos += 4;
62
+ return result;
63
+ }
64
+
65
+ try {
66
+ const cert = {};
67
+
68
+ // 1. Type string
69
+ const typeStr = readString().toString('utf8');
70
+ cert.type = typeStr;
71
+
72
+ // 2. Nonce (random bytes for uniqueness)
73
+ cert.nonce = readString();
74
+
75
+ // 3-4. Key-specific fields - skip them based on key type
76
+ // For RSA: e (exponent), n (modulus)
77
+ // For ECDSA: curve identifier, Q (point)
78
+ // For ED25519: pk (public key)
79
+ if (typeStr.startsWith('ssh-rsa')) {
80
+ // RSA: e, n
81
+ readString(); // e
82
+ readString(); // n
83
+ } else if (typeStr.startsWith('ecdsa-sha2-nistp')) {
84
+ // ECDSA: curve, Q
85
+ readString(); // curve identifier
86
+ readString(); // Q
87
+ } else if (typeStr.startsWith('ssh-ed25519')) {
88
+ // ED25519: pk
89
+ readString(); // pk
90
+ } else if (typeStr.startsWith('ssh-dss')) {
91
+ // DSA: p, q, g, y
92
+ readString(); // p
93
+ readString(); // q
94
+ readString(); // g
95
+ readString(); // y
96
+ } else {
97
+ return new Error(`Unsupported certificate type: ${typeStr}`);
98
+ }
99
+
100
+ // 5. Serial
101
+ cert.serial = readUInt64();
102
+
103
+ // 6. Type (1 = user cert, 2 = host cert)
104
+ const typeNum = readUInt32();
105
+ cert.certType = typeNum === 1 ? 'user' : typeNum === 2 ? 'host' : 'unknown';
106
+
107
+ // 7. Key ID (comment/identifier)
108
+ cert.keyId = readString().toString('utf8');
109
+
110
+ // 8. Valid principals (array of valid user/host names)
111
+ const principalsBuffer = readString();
112
+ cert.principals = [];
113
+ if (principalsBuffer.length > 0) {
114
+ let pPos = 0;
115
+ while (pPos < principalsBuffer.length) {
116
+ const len = readUInt32BE(principalsBuffer, pPos);
117
+ pPos += 4;
118
+ cert.principals.push(principalsBuffer.slice(pPos, pPos + len).toString('utf8'));
119
+ pPos += len;
120
+ }
121
+ }
122
+
123
+ // 9. Valid after
124
+ cert.validAfter = readUInt64();
125
+
126
+ // 10. Valid before
127
+ cert.validBefore = readUInt64();
128
+
129
+ // 11. Critical options
130
+ const criticalBuffer = readString();
131
+ cert.criticalOptions = {};
132
+ if (criticalBuffer.length > 0) {
133
+ let cPos = 0;
134
+ while (cPos < criticalBuffer.length) {
135
+ const nameLen = readUInt32BE(criticalBuffer, cPos);
136
+ cPos += 4;
137
+ const name = criticalBuffer.slice(cPos, cPos + nameLen).toString('utf8');
138
+ cPos += nameLen;
139
+ const dataLen = readUInt32BE(criticalBuffer, cPos);
140
+ cPos += 4;
141
+ const data = criticalBuffer.slice(cPos, cPos + dataLen);
142
+ cPos += dataLen;
143
+ cert.criticalOptions[name] = data;
144
+ }
145
+ }
146
+
147
+ // 12. Extensions
148
+ const extensionsBuffer = readString();
149
+ cert.extensions = {};
150
+ if (extensionsBuffer.length > 0) {
151
+ let ePos = 0;
152
+ while (ePos < extensionsBuffer.length) {
153
+ const nameLen = readUInt32BE(extensionsBuffer, ePos);
154
+ ePos += 4;
155
+ const name = extensionsBuffer.slice(ePos, ePos + nameLen).toString('utf8');
156
+ ePos += nameLen;
157
+ const dataLen = readUInt32BE(extensionsBuffer, ePos);
158
+ ePos += 4;
159
+ const data = extensionsBuffer.slice(ePos, ePos + dataLen);
160
+ ePos += dataLen;
161
+ cert.extensions[name] = data;
162
+ }
163
+ }
164
+
165
+ // 13. Reserved (currently unused)
166
+ readString();
167
+
168
+ // 14. Signature key
169
+ cert.signatureKeyBlob = readString();
170
+
171
+ // 15. Signature
172
+ cert.signature = readString();
173
+
174
+ return cert;
175
+ } catch (err) {
176
+ return err;
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Check if a buffer represents an SSH certificate
182
+ */
183
+ function isCertificate(data) {
184
+ if (!Buffer.isBuffer(data) || data.length < 16) {
185
+ return false;
186
+ }
187
+
188
+ try {
189
+ // Check if the type string indicates a certificate (ends with -cert-v01@openssh.com or similar)
190
+ if (data.length < 4) {
191
+ return false;
192
+ }
193
+
194
+ const len = readUInt32BE(data, 0);
195
+ if (len > 64 || data.length < 4 + len) {
196
+ return false;
197
+ }
198
+
199
+ const type = data.slice(4, 4 + len).toString('utf8');
200
+ return type.includes('-cert-v');
201
+ } catch {
202
+ return false;
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Check if a certificate is valid for the given username
208
+ */
209
+ function validateCertificate(cert, username) {
210
+ if (cert instanceof Error) {
211
+ return { valid: false, reason: cert.message };
212
+ }
213
+
214
+ const now = BigInt(Math.floor(Date.now() / 1000));
215
+
216
+ // Check if currently expired
217
+ if (cert.validBefore && now >= cert.validBefore) {
218
+ return { valid: false, reason: 'Certificate has expired' };
219
+ }
220
+
221
+ // Check if not yet valid
222
+ if (cert.validAfter && now < cert.validAfter) {
223
+ return { valid: false, reason: 'Certificate is not yet valid' };
224
+ }
225
+
226
+ // Check if username is in principals (for user certificates)
227
+ if (cert.certType === 'user' && cert.principals && cert.principals.length > 0) {
228
+ if (!cert.principals.includes(username)) {
229
+ return {
230
+ valid: false,
231
+ reason: `Username "${username}" not in certificate principals: ${cert.principals.join(', ')}`,
232
+ };
233
+ }
234
+ }
235
+
236
+ return { valid: true };
237
+ }
238
+
239
+ module.exports = {
240
+ parseSSHCertificate,
241
+ isCertificate,
242
+ validateCertificate,
243
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/ssh2",
3
- "version": "1.16.1",
3
+ "version": "1.17.0",
4
4
  "author": "Brian White <mscdex@mscdex.net>",
5
5
  "description": "SSH2 client and server modules written in pure JavaScript for node.js",
6
6
  "main": "./lib/index.js",