Compare commits

...
Author SHA1 Message Date
Cursor Agentand陈大猫 c2e5bdc245 fix(ssh): normalize SK certificate types before ECDSA verify
SKBaseKey.verify() converted SSH ECDSA signatures to ASN.1 only when the
key type was the plain sk-ecdsa name. Certificate keys kept the cert
name and rejected valid P-256 signatures.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
2026-10-25 01:50:24 +00:00
Cursor Agentand陈大猫 71da223d01 fix(ssh): reject RSA host-cert signature downgrades
rsa-sha2-256/512 host certificates now accept only their negotiated
signature and an ssh-rsa-cert blob. The any-RSA signature exception
stays on legacy ssh-rsa certificates. Host-certificate tests skip when
ssh-keygen is not installed.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
2026-10-25 01:40:05 +00:00
Cursor Agentand陈大猫 4cf870a6b4 fix(ssh): negotiate OpenSSH host certificates
ssh2 only offered plain host-key types, so a gateway that advertises
ssh-ed25519-cert-v01@openssh.com failed with "no matching host key format".
Offer the OpenSSH host-certificate algorithms, verify KEX signatures made
with the embedded key, and keep those algorithms in the legacy and
skip-ECDSA lists.

Fixes #3918

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
2026-10-25 01:33:11 +00:00
7 changed files with 745 additions and 87 deletions

No files matched your search

+10
View File
@@ -47,6 +47,16 @@ test("effectiveDefaultAlgorithms (modern) includes chacha20-poly1305", () => {
assert.ok(result.cipher.includes("chacha20-poly1305@openssh.com"));
});
test("effectiveDefaultAlgorithms offers OpenSSH host certificates before plain host keys", () => {
const cert = "ssh-ed25519-cert-v01@openssh.com";
const modern = effectiveDefaultAlgorithms(false);
assert.ok(modern.serverHostKey.includes(cert));
assert.ok(modern.serverHostKey.indexOf(cert) < modern.serverHostKey.indexOf("ssh-ed25519"));
const legacy = effectiveDefaultAlgorithms(true);
assert.ok(legacy.serverHostKey.includes(cert));
assert.ok(legacy.serverHostKey.includes("ssh-dss"));
});
test("effectiveDefaultAlgorithms (legacy) appends sha1 KEX, CBC, and ssh-dss", () => {
const modern = effectiveDefaultAlgorithms(false);
const legacy = effectiveDefaultAlgorithms(true);
+14
View File
@@ -77,6 +77,13 @@ export const SUPPORTED_HMAC_ALGORITHMS: readonly string[] = [
];
export const SUPPORTED_SERVER_HOST_KEY_ALGORITHMS: readonly string[] = [
"ssh-ed25519-cert-v01@openssh.com",
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
"rsa-sha2-512-cert-v01@openssh.com",
"rsa-sha2-256-cert-v01@openssh.com",
"ssh-rsa-cert-v01@openssh.com",
"ssh-ed25519",
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp384",
@@ -148,6 +155,13 @@ const MODERN_DEFAULT_ALGORITHMS: Readonly<Record<SSHAlgorithmCategory, readonly
"hmac-sha1",
],
serverHostKey: [
"ssh-ed25519-cert-v01@openssh.com",
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
"rsa-sha2-512-cert-v01@openssh.com",
"rsa-sha2-256-cert-v01@openssh.com",
"ssh-rsa-cert-v01@openssh.com",
"ssh-ed25519",
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp384",
@@ -0,0 +1,289 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { execFileSync } = require("node:child_process");
const {
DEFAULT_SERVER_HOST_KEY,
SUPPORTED_SERVER_HOST_KEY,
} = require("ssh2/lib/protocol/constants.js");
const {
parseDERKey,
parseKey,
} = require("ssh2/lib/protocol/keyParser.js");
const {
hostKeyBlobTypeAccepted,
hostKeySignatureTypeAccepted,
} = require("ssh2/lib/protocol/kex.js");
// Manual live check (optional; needs network and real Instabox credentials):
// ssh -vv -p 2222 USER@ssh.instabox.sh
// Instabox advertises only ssh-ed25519-cert-v01@openssh.com and signs the
// exchange hash as ssh-ed25519. After this patch, host-key negotiation should
// succeed; authentication still depends on the real certificate credentials.
const HOST_CERT = "ssh-ed25519-cert-v01@openssh.com";
const HOST_CERT_ALGORITHMS = [
HOST_CERT,
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
"rsa-sha2-512-cert-v01@openssh.com",
"rsa-sha2-256-cert-v01@openssh.com",
"ssh-rsa-cert-v01@openssh.com",
];
function sshKeygenSkipReason(error) {
if (error && error.code === "ENOENT")
return "ssh-keygen is not installed";
return false;
}
function detectSshKeygenSkip() {
try {
execFileSync("ssh-keygen", ["-h"], { stdio: "ignore" });
return false;
} catch (error) {
return sshKeygenSkipReason(error);
}
}
const sshKeygenSkip = detectSshKeygenSkip();
function mintEd25519HostCert(dir) {
const ca = path.join(dir, "ca");
const host = path.join(dir, "host");
execFileSync("ssh-keygen", ["-q", "-t", "ed25519", "-N", "", "-f", ca]);
execFileSync("ssh-keygen", ["-q", "-t", "ed25519", "-N", "", "-f", host]);
execFileSync("ssh-keygen", [
"-q",
"-s",
ca,
"-I",
"netcatty-hostcert",
"-h",
"-n",
"127.0.0.1",
"-V",
"+52w",
`${host}.pub`,
]);
const certLine = fs.readFileSync(`${host}-cert.pub`, "utf8").trim();
const [type, b64] = certLine.split(/\s+/);
const privateKey = parseKey(fs.readFileSync(host));
return {
type,
certBlob: Buffer.from(b64, "base64"),
privateKeyPem: fs.readFileSync(host),
privateKey,
};
}
test("ssh2 default and supported host key lists offer OpenSSH host certificates", () => {
for (const algo of HOST_CERT_ALGORITHMS) {
assert.equal(DEFAULT_SERVER_HOST_KEY.includes(algo), true, `${algo} missing from defaults`);
assert.equal(SUPPORTED_SERVER_HOST_KEY.includes(algo), true, `${algo} missing from supported`);
}
assert.ok(
DEFAULT_SERVER_HOST_KEY.indexOf(HOST_CERT) < DEFAULT_SERVER_HOST_KEY.indexOf("ssh-ed25519"),
"host certificates should be offered before the matching plain key",
);
assert.equal(DEFAULT_SERVER_HOST_KEY.includes("ssh-ed25519"), true);
assert.equal(DEFAULT_SERVER_HOST_KEY.includes("ssh-rsa"), true);
assert.equal(
SUPPORTED_SERVER_HOST_KEY.includes("sk-ssh-ed25519-cert-v01@openssh.com"),
true,
);
assert.equal(
SUPPORTED_SERVER_HOST_KEY.includes("sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"),
true,
);
});
test("missing ssh-keygen skips host-certificate minting instead of failing", () => {
const missing = Object.assign(new Error("spawn ssh-keygen ENOENT"), { code: "ENOENT" });
assert.equal(sshKeygenSkipReason(missing), "ssh-keygen is not installed");
const present = Object.assign(new Error("usage"), { status: 1 });
assert.equal(sshKeygenSkipReason(present), false);
assert.equal(sshKeygenSkipReason(null), false);
});
test("parseDERKey reads an OpenSSH ed25519 host certificate the way KEX does", { skip: sshKeygenSkip }, () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-hostcert-"));
try {
const minted = mintEd25519HostCert(dir);
assert.equal(minted.type, HOST_CERT);
assert.equal(minted.privateKey instanceof Error, false, minted.privateKey.message);
const typeLen = minted.certBlob.readUInt32BE(0);
const type = minted.certBlob.subarray(4, 4 + typeLen).toString();
const keyMaterial = minted.certBlob.subarray(4 + typeLen);
const parsed = parseDERKey(keyMaterial, type);
assert.equal(parsed instanceof Error, false, parsed instanceof Error ? parsed.message : "");
assert.equal(parsed.type, HOST_CERT);
const message = Buffer.from("kex-exchange-hash");
const signature = minted.privateKey.sign(message);
assert.equal(signature instanceof Error, false, signature instanceof Error ? signature.message : "");
assert.equal(parsed.verify(message, signature), true);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test("host key signature type accepts the underlying algorithm for host certificates", () => {
assert.equal(typeof hostKeySignatureTypeAccepted, "function");
assert.equal(hostKeySignatureTypeAccepted(HOST_CERT, "ssh-ed25519"), true);
assert.equal(hostKeySignatureTypeAccepted(HOST_CERT, HOST_CERT), true);
assert.equal(hostKeySignatureTypeAccepted(HOST_CERT, "ssh-rsa"), false);
assert.equal(
hostKeySignatureTypeAccepted("ecdsa-sha2-nistp256-cert-v01@openssh.com", "ecdsa-sha2-nistp256"),
true,
);
assert.equal(
hostKeySignatureTypeAccepted("rsa-sha2-256-cert-v01@openssh.com", "rsa-sha2-256"),
true,
);
assert.equal(
hostKeySignatureTypeAccepted("rsa-sha2-256-cert-v01@openssh.com", "rsa-sha2-512"),
false,
);
assert.equal(
hostKeySignatureTypeAccepted("rsa-sha2-256-cert-v01@openssh.com", "ssh-rsa"),
false,
);
assert.equal(
hostKeySignatureTypeAccepted("rsa-sha2-256-cert-v00@openssh.com", "ssh-rsa"),
false,
);
assert.equal(
hostKeySignatureTypeAccepted("rsa-sha2-512-cert-v01@openssh.com", "rsa-sha2-512"),
true,
);
assert.equal(
hostKeySignatureTypeAccepted("rsa-sha2-512-cert-v01@openssh.com", "rsa-sha2-256"),
false,
);
assert.equal(
hostKeySignatureTypeAccepted("rsa-sha2-512-cert-v01@openssh.com", "ssh-rsa"),
false,
);
assert.equal(
hostKeySignatureTypeAccepted("ssh-rsa-cert-v01@openssh.com", "rsa-sha2-256"),
true,
);
assert.equal(
hostKeySignatureTypeAccepted("ssh-rsa-cert-v01@openssh.com", "rsa-sha2-512"),
true,
);
assert.equal(
hostKeySignatureTypeAccepted("ssh-rsa-cert-v01@openssh.com", "ssh-rsa"),
true,
);
assert.equal(
hostKeySignatureTypeAccepted("ssh-rsa-cert-v00@openssh.com", "ssh-rsa"),
true,
);
assert.equal(
hostKeyBlobTypeAccepted("rsa-sha2-256-cert-v01@openssh.com", "ssh-rsa-cert-v01@openssh.com"),
true,
);
assert.equal(
hostKeyBlobTypeAccepted("rsa-sha2-512-cert-v01@openssh.com", "ssh-rsa-cert-v01@openssh.com"),
true,
);
assert.equal(
hostKeyBlobTypeAccepted("rsa-sha2-256-cert-v01@openssh.com", "ssh-rsa"),
false,
);
assert.equal(
hostKeyBlobTypeAccepted("rsa-sha2-512-cert-v01@openssh.com", "ssh-rsa"),
false,
);
assert.equal(hostKeyBlobTypeAccepted("rsa-sha2-256", "ssh-rsa"), true);
assert.equal(hostKeyBlobTypeAccepted("rsa-sha2-512", "ssh-rsa"), true);
assert.equal(hostKeySignatureTypeAccepted("ssh-ed25519", "ssh-ed25519"), true);
assert.equal(hostKeySignatureTypeAccepted("ssh-ed25519", HOST_CERT), false);
assert.equal(hostKeySignatureTypeAccepted("rsa-sha2-256", "ssh-rsa"), false);
assert.equal(hostKeySignatureTypeAccepted("rsa-sha2-512", "rsa-sha2-256"), false);
});
function loadSsh2WithCertHostKey(pem, certBlob) {
// ssh2.Server only accepts PEM text and then advertises privateKey.type.
// Reload it after wrapping parseKey so this one key is presented as an
// OpenSSH host certificate while signing still uses the embedded key.
const keyParser = require("ssh2/lib/protocol/keyParser.js");
const originalParseKey = keyParser.parseKey;
keyParser.parseKey = function parseCertHostKey(data, passphrase) {
const parsed = originalParseKey(data, passphrase);
if (!(parsed instanceof Error) && data === pem) {
parsed.type = HOST_CERT;
parsed.getPublicSSH = () => certBlob;
}
return parsed;
};
const reloadIds = ["ssh2/lib/server.js", "ssh2/lib/index.js", "ssh2"];
const resolved = reloadIds.map((id) => require.resolve(id));
const previous = resolved.map((filename) => require.cache[filename]);
for (const filename of resolved) delete require.cache[filename];
const ssh2 = require("ssh2");
for (let i = 0; i < resolved.length; i++) {
if (previous[i]) require.cache[resolved[i]] = previous[i];
else delete require.cache[resolved[i]];
}
keyParser.parseKey = originalParseKey;
return ssh2;
}
test("ssh2 client completes KEX against a cert-only ed25519 host key", { skip: sshKeygenSkip }, async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-hostcert-kex-"));
const minted = mintEd25519HostCert(dir);
assert.equal(minted.privateKey instanceof Error, false, minted.privateKey.message);
const { Client, Server } = loadSsh2WithCertHostKey(minted.privateKeyPem, minted.certBlob);
const server = new Server({ hostKeys: [minted.privateKeyPem] }, (client) => {
client.on("error", () => {});
client.on("authentication", (ctx) => ctx.reject());
});
await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});
try {
const authError = await new Promise((resolve, reject) => {
const client = new Client();
const timer = setTimeout(() => {
client.end();
reject(new Error("timed out waiting for KEX/auth"));
}, 10000);
client.on("ready", () => {
clearTimeout(timer);
client.end();
reject(new Error("authentication unexpectedly succeeded"));
});
client.on("error", (err) => {
clearTimeout(timer);
client.end();
resolve(err);
});
client.connect({
host: "127.0.0.1",
port: server.address().port,
username: "netcatty",
password: "not-the-password",
readyTimeout: 8000,
});
});
assert.match(authError.message, /authentication/i);
assert.doesNotMatch(
authError.message,
/Handshake failed|no matching host key|Wrong signature type|Unsupported OpenSSH|signature verification failed|Host key does not match/,
);
} finally {
await new Promise((resolve) => server.close(resolve));
fs.rmSync(dir, { recursive: true, force: true });
}
});
+88
View File
@@ -2,10 +2,12 @@
const test = require("node:test");
const assert = require("node:assert/strict");
const { createHash, generateKeyPairSync, sign } = require("node:crypto");
const {
parseKey,
getOpenSSHCertSignatureAlgorithm,
} = require("ssh2/lib/protocol/keyParser.js");
const { convertSignature } = require("ssh2/lib/protocol/utils.js");
function buildSkEd25519PublicLine() {
const type = Buffer.from("sk-ssh-ed25519@openssh.com");
@@ -146,3 +148,89 @@ test("ssh2 agent protocol preserves SK signature framing, flags and counter", as
}
}
});
function sshString(value) {
const body = Buffer.isBuffer(value) ? value : Buffer.from(value);
const out = Buffer.alloc(4 + body.length);
out.writeUInt32BE(body.length, 0);
body.copy(out, 4);
return out;
}
function opensshPublicLine(typeName, fields) {
const blob = Buffer.concat([
sshString(typeName),
...fields.map((field) => sshString(field)),
]);
return `${typeName} ${blob.toString("base64")} netcatty@sk`;
}
function skEcdsaSignature(rawSig, flags, counter) {
const out = Buffer.alloc(4 + rawSig.length + 5);
out.writeUInt32BE(rawSig.length, 0);
rawSig.copy(out, 4);
const pos = 4 + rawSig.length;
out[pos] = flags;
out.writeUInt32BE(counter, pos + 1);
return out;
}
test("ssh2 verifies an SK ECDSA certificate with the same P-256 signature as the plain key", () => {
const { publicKey, privateKey } = generateKeyPairSync("ec", {
namedCurve: "prime256v1",
});
const jwk = publicKey.export({ format: "jwk" });
const point = Buffer.concat([
Buffer.from([0x04]),
Buffer.from(jwk.x, "base64url"),
Buffer.from(jwk.y, "base64url"),
]);
const application = "ssh:";
const plain = parseKey(opensshPublicLine("sk-ecdsa-sha2-nistp256@openssh.com", [
"nistp256",
point,
application,
]));
const cert = parseKey(opensshPublicLine("sk-ecdsa-sha2-nistp256-cert-v01@openssh.com", [
Buffer.alloc(16, 3),
"nistp256",
point,
application,
]));
assert.equal(plain instanceof Error, false, plain instanceof Error ? plain.message : "");
assert.equal(cert instanceof Error, false, cert instanceof Error ? cert.message : "");
assert.equal(cert.type, "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com");
assert.equal(cert.getApplication(), application);
const message = Buffer.from("kex-exchange-hash");
const flags = 0x01;
const counter = 0x12345678;
const appHash = createHash("sha256").update(application).digest();
const dataHash = createHash("sha256").update(message).digest();
const signedData = Buffer.alloc(appHash.length + 1 + 4 + dataHash.length);
let offset = 0;
appHash.copy(signedData, offset);
offset += appHash.length;
signedData[offset++] = flags;
signedData.writeUInt32BE(counter, offset);
offset += 4;
dataHash.copy(signedData, offset);
const der = sign("sha256", signedData, privateKey);
const rawSig = convertSignature(der, "ecdsa-sha2-nistp256");
assert.ok(Buffer.isBuffer(rawSig));
const signature = skEcdsaSignature(rawSig, flags, counter);
const plainResult = plain.verify(message, signature);
assert.equal(
plainResult,
true,
plainResult instanceof Error ? plainResult.message : String(plainResult),
);
const certResult = cert.verify(message, signature);
assert.equal(
certResult,
true,
certResult instanceof Error ? certResult.message : String(certResult),
);
});
+16 -3
View File
@@ -114,6 +114,13 @@ function applyLegacyAlgorithms(algorithms) {
]),
);
algorithms.serverHostKey = [
"ssh-ed25519-cert-v01@openssh.com",
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
"rsa-sha2-512-cert-v01@openssh.com",
"rsa-sha2-256-cert-v01@openssh.com",
"ssh-rsa-cert-v01@openssh.com",
"ssh-ed25519", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521",
"rsa-sha2-512", "rsa-sha2-256",
"ssh-rsa", "ssh-dss",
@@ -150,10 +157,16 @@ function applyLegacyHmacAlgorithms(algorithms) {
}
// Mirror of ssh2's DEFAULT_SERVER_HOST_KEY (see ssh2/lib/protocol/constants.js)
// minus every ecdsa-sha2-*. Used when skipEcdsaHostKey is on but legacy mode
// is off, so the field would otherwise be undefined and ssh2 would fall back
// to its built-in defaults (which still include ecdsa-sha2-*).
// minus every ecdsa-sha2-* (plain keys and host certificates). Used when
// skipEcdsaHostKey is on but legacy mode is off, so the field would otherwise
// be undefined and ssh2 would fall back to its built-in defaults (which still
// include ecdsa-sha2-*). Non-ECDSA host certificates stay so a cert-only
// bastion can still be reached.
const MODERN_SERVER_HOST_KEY_WITHOUT_ECDSA = Object.freeze([
"ssh-ed25519-cert-v01@openssh.com",
"rsa-sha2-512-cert-v01@openssh.com",
"rsa-sha2-256-cert-v01@openssh.com",
"ssh-rsa-cert-v01@openssh.com",
"ssh-ed25519",
"rsa-sha2-512",
"rsa-sha2-256",
+20
View File
@@ -269,6 +269,19 @@ for (const [label, buildAlgorithms] of [
["SSH", sshBridge.buildAlgorithms],
["SFTP", sftpBridge.buildSftpAlgorithms],
]) {
test(`${label} legacy serverHostKey offers OpenSSH host certificates before plain keys`, () => {
withAlgorithmRuntime({}, () => {
const keys = buildAlgorithms(true).serverHostKey;
assert.ok(keys.includes("ssh-ed25519-cert-v01@openssh.com"));
assert.ok(keys.indexOf("ssh-ed25519-cert-v01@openssh.com") < keys.indexOf("ssh-ed25519"));
assert.ok(keys.includes("ecdsa-sha2-nistp256-cert-v01@openssh.com"));
assert.ok(keys.includes("rsa-sha2-512-cert-v01@openssh.com"));
assert.ok(keys.includes("rsa-sha2-256-cert-v01@openssh.com"));
assert.ok(keys.includes("ssh-rsa-cert-v01@openssh.com"));
assert.ok(keys.includes("ssh-dss"));
});
});
test(`${label} skipEcdsaHostKey removes every ecdsa-sha2-* from serverHostKey (legacy on)`, () => {
withAlgorithmRuntime({}, () => {
const algorithms = buildAlgorithms(true, { skipEcdsaHostKey: true });
@@ -280,6 +293,9 @@ for (const [label, buildAlgorithms] of [
assert.ok(algorithms.serverHostKey.includes("ssh-rsa"));
assert.ok(algorithms.serverHostKey.includes("ssh-dss"));
assert.ok(algorithms.serverHostKey.includes("ssh-ed25519"));
assert.ok(algorithms.serverHostKey.includes("ssh-ed25519-cert-v01@openssh.com"));
assert.ok(algorithms.serverHostKey.includes("rsa-sha2-512-cert-v01@openssh.com"));
assert.ok(algorithms.serverHostKey.includes("ssh-rsa-cert-v01@openssh.com"));
});
});
@@ -293,6 +309,10 @@ for (const [label, buildAlgorithms] of [
for (const algo of algorithms.serverHostKey) {
assert.ok(!algo.startsWith("ecdsa-sha2-"), `${algo} should be filtered out`);
}
assert.ok(algorithms.serverHostKey.includes("ssh-ed25519-cert-v01@openssh.com"));
assert.ok(algorithms.serverHostKey.includes("rsa-sha2-512-cert-v01@openssh.com"));
assert.ok(algorithms.serverHostKey.includes("rsa-sha2-256-cert-v01@openssh.com"));
assert.ok(algorithms.serverHostKey.includes("ssh-rsa-cert-v01@openssh.com"));
});
});
+308 -84
View File
@@ -1,7 +1,8 @@
diff --git a/node_modules/ssh2/lib/agent.js b/node_modules/ssh2/lib/agent.js
index bb495d1..32c48a0 100644
--- a/node_modules/ssh2/lib/agent.js
+++ b/node_modules/ssh2/lib/agent.js
@@ -6,7 +6,7 @@
@@ -6,7 +6,7 @@ const { resolve } = require('path');
const { readFile } = require('fs');
const { execFile, spawn } = require('child_process');
@@ -10,7 +11,7 @@ diff --git a/node_modules/ssh2/lib/agent.js b/node_modules/ssh2/lib/agent.js
const {
makeBufferParser,
@@ -655,8 +655,12 @@
@@ -655,8 +655,12 @@ const AgentProtocol = (() => {
// TODO: verify signature type based on key and options used
// during initial sign request
binaryParser.init(signature, 0);
@@ -25,7 +26,7 @@ diff --git a/node_modules/ssh2/lib/agent.js b/node_modules/ssh2/lib/agent.js
binaryParser.clear();
if (signature === undefined)
@@ -711,7 +715,10 @@
@@ -711,7 +715,10 @@ const AgentProtocol = (() => {
hash: undefined,
};
let ctx;
@@ -37,7 +38,7 @@ diff --git a/node_modules/ssh2/lib/agent.js b/node_modules/ssh2/lib/agent.js
if (flagsVal & SSH_AGENT_RSA_SHA2_256) {
ctx = 'rsa-sha2-256';
flags.hash = 'sha256';
@@ -721,7 +728,7 @@
@@ -721,7 +728,7 @@ const AgentProtocol = (() => {
}
}
if (ctx === undefined)
@@ -46,7 +47,7 @@ diff --git a/node_modules/ssh2/lib/agent.js b/node_modules/ssh2/lib/agent.js
const req = new AgentInboundRequest(msgType, ctx);
this[SYM_REQS].push(req);
@@ -732,6 +739,9 @@
@@ -732,6 +739,9 @@ const AgentProtocol = (() => {
default: {
const req = new AgentInboundRequest(msgType);
this[SYM_REQS].push(req);
@@ -56,7 +57,7 @@ diff --git a/node_modules/ssh2/lib/agent.js b/node_modules/ssh2/lib/agent.js
this.failureReply(req);
}
}
@@ -781,7 +791,11 @@
@@ -781,7 +791,11 @@ const AgentProtocol = (() => {
if (pubKey instanceof Error)
throw new Error('Invalid public key argument');
@@ -69,7 +70,7 @@ diff --git a/node_modules/ssh2/lib/agent.js b/node_modules/ssh2/lib/agent.js
switch (options.hash) {
case 'sha256':
flags = SSH_AGENT_RSA_SHA2_256;
@@ -992,19 +1006,24 @@
@@ -992,19 +1006,24 @@ const AgentProtocol = (() => {
let p = 0;
const sigFormat = req.getContext();
const sigFormatLen = Buffer.byteLength(sigFormat);
@@ -99,9 +100,10 @@ diff --git a/node_modules/ssh2/lib/agent.js b/node_modules/ssh2/lib/agent.js
return respond(this, req, buf);
}
diff --git a/node_modules/ssh2/lib/client.js b/node_modules/ssh2/lib/client.js
index 7291c2c..8943c9a 100644
--- a/node_modules/ssh2/lib/client.js
+++ b/node_modules/ssh2/lib/client.js
@@ -2170,6 +2170,29 @@
@@ -2170,6 +2170,29 @@ function getKeyAlgos(client, key, serverSigAlgs) {
return [];
}
return [['ssh-rsa', 'sha1']];
@@ -132,9 +134,10 @@ diff --git a/node_modules/ssh2/lib/client.js b/node_modules/ssh2/lib/client.js
}
diff --git a/node_modules/ssh2/lib/protocol/Protocol.js b/node_modules/ssh2/lib/protocol/Protocol.js
index 7302488..df0c9b3 100644
--- a/node_modules/ssh2/lib/protocol/Protocol.js
+++ b/node_modules/ssh2/lib/protocol/Protocol.js
@@ -58,6 +58,7 @@
@@ -58,6 +58,7 @@ const {
} = require('./kex.js');
const {
parseKey,
@@ -142,7 +145,7 @@ diff --git a/node_modules/ssh2/lib/protocol/Protocol.js b/node_modules/ssh2/lib/
} = require('./keyParser.js');
const MESSAGE_HANDLERS = require('./handlers.js');
const {
@@ -701,11 +702,19 @@
@@ -701,11 +702,19 @@ class Protocol {
if (signature === false)
throw new Error('Error while converting handshake signature');
@@ -163,7 +166,7 @@ diff --git a/node_modules/ssh2/lib/protocol/Protocol.js b/node_modules/ssh2/lib/
);
// TODO: simply copy from original "packet" to new `packet` to avoid
@@ -729,13 +738,17 @@
@@ -729,13 +738,17 @@ class Protocol {
writeUInt32BE(packet, pubKeyLen, p += algoLen);
packet.set(pubKey, p += 4);
@@ -186,7 +189,7 @@ diff --git a/node_modules/ssh2/lib/protocol/Protocol.js b/node_modules/ssh2/lib/
// Servers shouldn't send packet type 60 in response to signed publickey
// attempts, but if they do, interpret as type 60.
@@ -810,20 +823,32 @@
@@ -810,20 +823,32 @@ class Protocol {
if (!signature)
throw new Error('Error while converting handshake signature');
@@ -225,7 +228,7 @@ diff --git a/node_modules/ssh2/lib/protocol/Protocol.js b/node_modules/ssh2/lib/
this._authsQueue.push('hostbased');
@@ -1916,7 +1941,10 @@
@@ -1916,7 +1941,10 @@ class Protocol {
}
// SSH-protoversion-softwareversion (SP comments) CR LF
@@ -238,9 +241,10 @@ diff --git a/node_modules/ssh2/lib/protocol/Protocol.js b/node_modules/ssh2/lib/
// TODO: optimize this by starting n bytes from the end of this._buffer instead
// of the beginning
diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/protocol/SFTP.js
index 9f33c02..9751164 100644
--- a/node_modules/ssh2/lib/protocol/SFTP.js
+++ b/node_modules/ssh2/lib/protocol/SFTP.js
@@ -117,6 +117,20 @@
@@ -117,6 +117,20 @@ const OPENSSH_MAX_PKT_LEN = 256 * 1024;
const bufferParser = makeBufferParser();
@@ -261,7 +265,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
const fakeStderr = {
readable: false,
writable: false,
@@ -155,6 +169,8 @@
@@ -155,6 +169,8 @@ class SFTP extends EventEmitter {
this._writeReqid = -1;
this._requests = {};
this._maxInPktLen = OPENSSH_MAX_PKT_LEN;
@@ -270,7 +274,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._maxOutPktLen = 34000;
this._maxReadLen =
(this._isOpenSSH ? OPENSSH_MAX_PKT_LEN : 34000) - PKT_RW_OVERHEAD;
@@ -196,6 +212,53 @@
@@ -196,6 +212,53 @@ class SFTP extends EventEmitter {
this.emit('end');
return;
}
@@ -324,7 +328,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
/*
uint32 length
byte type
@@ -339,7 +402,7 @@
@@ -339,7 +402,7 @@ class SFTP extends EventEmitter {
uint32 pflags
ATTRS attrs
*/
@@ -333,7 +337,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + 4 + attrsLen);
@@ -349,7 +412,7 @@
@@ -349,7 +412,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -342,7 +346,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
writeUInt32BE(buf, flags, p += pathLen);
writeUInt32BE(buf, attrsFlags, p += 4);
if (attrsLen) {
@@ -734,7 +797,7 @@
@@ -734,7 +797,7 @@ class SFTP extends EventEmitter {
uint32 id
string filename
*/
@@ -351,7 +355,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + fnameLen);
@@ -744,7 +807,7 @@
@@ -744,7 +807,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, fnameLen, p);
@@ -360,7 +364,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { cb };
@@ -762,8 +825,8 @@
@@ -762,8 +825,8 @@ class SFTP extends EventEmitter {
string oldpath
string newpath
*/
@@ -371,7 +375,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + oldLen + 4 + newLen);
@@ -773,9 +836,9 @@
@@ -773,9 +836,9 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, oldLen, p);
@@ -383,7 +387,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { cb };
@@ -806,7 +869,7 @@
@@ -806,7 +869,7 @@ class SFTP extends EventEmitter {
string path
ATTRS attrs
*/
@@ -392,7 +396,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + attrsLen);
@@ -816,7 +879,7 @@
@@ -816,7 +879,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -401,7 +405,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
writeUInt32BE(buf, flags, p += pathLen);
if (attrsLen) {
p += 4;
@@ -844,7 +907,7 @@
@@ -844,7 +907,7 @@ class SFTP extends EventEmitter {
uint32 id
string path
*/
@@ -410,7 +414,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
@@ -854,7 +917,7 @@
@@ -854,7 +917,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -419,7 +423,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { cb };
@@ -987,7 +1050,7 @@
@@ -987,7 +1050,7 @@ class SFTP extends EventEmitter {
uint32 id
string path
*/
@@ -428,7 +432,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
@@ -997,7 +1060,7 @@
@@ -997,7 +1060,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -437,7 +441,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { cb };
@@ -1014,7 +1077,7 @@
@@ -1014,7 +1077,7 @@ class SFTP extends EventEmitter {
uint32 id
string path
*/
@@ -446,7 +450,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
@@ -1024,7 +1087,7 @@
@@ -1024,7 +1087,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -455,7 +459,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { cb };
@@ -1041,7 +1104,7 @@
@@ -1041,7 +1104,7 @@ class SFTP extends EventEmitter {
uint32 id
string path
*/
@@ -464,7 +468,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
@@ -1051,7 +1114,7 @@
@@ -1051,7 +1114,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -473,7 +477,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { cb };
@@ -1080,7 +1143,7 @@
@@ -1080,7 +1143,7 @@ class SFTP extends EventEmitter {
string path
ATTRS attrs
*/
@@ -482,7 +486,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + attrsLen);
@@ -1090,7 +1153,7 @@
@@ -1090,7 +1153,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -491,7 +495,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
writeUInt32BE(buf, flags, p += pathLen);
if (attrsLen) {
p += 4;
@@ -1205,7 +1268,7 @@
@@ -1205,7 +1268,7 @@ class SFTP extends EventEmitter {
uint32 id
string path
*/
@@ -500,7 +504,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
@@ -1215,7 +1278,7 @@
@@ -1215,7 +1278,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -509,7 +513,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = {
cb: (err, names) => {
@@ -1243,8 +1306,8 @@
@@ -1243,8 +1306,8 @@ class SFTP extends EventEmitter {
string linkpath
string targetpath
*/
@@ -520,7 +524,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + linkLen + 4 + targetLen);
@@ -1256,14 +1319,14 @@
@@ -1256,14 +1319,14 @@ class SFTP extends EventEmitter {
if (this._isOpenSSH) {
// OpenSSH has linkpath and targetpath positions switched
writeUInt32BE(buf, targetLen, p);
@@ -539,7 +543,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
}
this._requests[reqid] = { cb };
@@ -1281,7 +1344,7 @@
@@ -1281,7 +1344,7 @@ class SFTP extends EventEmitter {
uint32 id
string path
*/
@@ -548,7 +552,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
@@ -1291,7 +1354,7 @@
@@ -1291,7 +1354,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, pathLen, p);
@@ -557,7 +561,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = {
cb: (err, names) => {
@@ -1325,8 +1388,8 @@
@@ -1325,8 +1388,8 @@ class SFTP extends EventEmitter {
string oldpath
string newpath
*/
@@ -568,7 +572,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf =
Buffer.allocUnsafe(4 + 1 + 4 + 4 + 24 + 4 + oldLen + 4 + newLen);
@@ -1337,11 +1400,11 @@
@@ -1337,11 +1400,11 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, 24, p);
@@ -583,7 +587,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { cb };
@@ -1364,7 +1427,7 @@
@@ -1364,7 +1427,7 @@ class SFTP extends EventEmitter {
string "statvfs@openssh.com"
string path
*/
@@ -592,7 +596,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 19 + 4 + pathLen);
@@ -1374,9 +1437,9 @@
@@ -1374,9 +1437,9 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, 19, p);
@@ -604,7 +608,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { extended: 'statvfs@openssh.com', cb };
@@ -1411,7 +1474,7 @@
@@ -1411,7 +1474,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, 20, p);
@@ -613,7 +617,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
writeUInt32BE(buf, handleLen, p += 20);
buf.set(handle, p += 4);
@@ -1437,8 +1500,8 @@
@@ -1437,8 +1500,8 @@ class SFTP extends EventEmitter {
string oldpath
string newpath
*/
@@ -624,7 +628,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf =
Buffer.allocUnsafe(4 + 1 + 4 + 4 + 20 + 4 + oldLen + 4 + newLen);
@@ -1449,11 +1512,11 @@
@@ -1449,11 +1512,11 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, 20, p);
@@ -639,7 +643,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = { cb };
@@ -1488,7 +1551,7 @@
@@ -1488,7 +1551,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, 17, p);
@@ -648,7 +652,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
writeUInt32BE(buf, handleLen, p += 17);
buf.set(handle, p += 4);
@@ -1524,7 +1587,7 @@
@@ -1524,7 +1587,7 @@ class SFTP extends EventEmitter {
string path
ATTRS attrs
*/
@@ -657,7 +661,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf =
Buffer.allocUnsafe(4 + 1 + 4 + 4 + 20 + 4 + pathLen + 4 + attrsLen);
@@ -1535,10 +1598,10 @@
@@ -1535,10 +1598,10 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, 20, p);
@@ -670,7 +674,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
writeUInt32BE(buf, flags, p += pathLen);
if (attrsLen) {
@@ -1573,7 +1636,7 @@
@@ -1573,7 +1636,7 @@ class SFTP extends EventEmitter {
string "expand-path@openssh.com"
string path
*/
@@ -679,7 +683,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 23 + 4 + pathLen);
@@ -1583,10 +1646,10 @@
@@ -1583,10 +1646,10 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, 23, p);
@@ -692,7 +696,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
this._requests[reqid] = {
cb: (err, names) => {
@@ -1653,7 +1716,7 @@
@@ -1653,7 +1716,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, 9, p);
p += 4;
@@ -701,7 +705,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
p += 9;
writeUInt32BE(buf, srcHandle.length, p);
@@ -1708,7 +1771,7 @@
@@ -1708,7 +1771,7 @@ class SFTP extends EventEmitter {
string username
*/
let p = 0;
@@ -710,7 +714,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
const buf = Buffer.allocUnsafe(
4 + 1
+ 4
@@ -1728,12 +1791,12 @@
@@ -1728,12 +1791,12 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, 14, p);
p += 4;
@@ -725,7 +729,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
p += usernameLen;
this._requests[reqid] = {
@@ -1806,7 +1869,7 @@
@@ -1806,7 +1869,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, 30, p);
p += 4;
@@ -734,7 +738,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
p += 30;
writeUInt32BE(buf, 4 * uids.length, p);
@@ -1871,7 +1934,7 @@
@@ -1871,7 +1934,7 @@ class SFTP extends EventEmitter {
message || (message = '');
@@ -743,7 +747,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 4 + msgLen + 4);
@@ -1884,7 +1947,7 @@
@@ -1884,7 +1947,7 @@ class SFTP extends EventEmitter {
writeUInt32BE(buf, msgLen, p += 4);
p += 4;
if (msgLen) {
@@ -752,7 +756,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
p += msgLen;
}
@@ -1913,7 +1976,7 @@
@@ -1913,7 +1976,7 @@ class SFTP extends EventEmitter {
const dataLen = (
isBuffer
? data.length
@@ -761,7 +765,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
);
let p = 9;
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + dataLen);
@@ -1927,7 +1990,7 @@
@@ -1927,7 +1990,7 @@ class SFTP extends EventEmitter {
if (isBuffer)
buf.set(data, p += 4);
else if (isUTF8)
@@ -770,7 +774,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
else
buf.write(data, p += 4, dataLen, encoding);
}
@@ -1959,13 +2022,13 @@
@@ -1959,13 +2022,13 @@ class SFTP extends EventEmitter {
? ''
: name.filename
);
@@ -786,7 +790,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
if (typeof name.attrs === 'object' && name.attrs !== null) {
nameAttrs = attrsToBytes(name.attrs);
@@ -2011,11 +2074,11 @@
@@ -2011,11 +2074,11 @@ class SFTP extends EventEmitter {
? ''
: name.filename
);
@@ -800,7 +804,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
p += len;
}
}
@@ -2026,11 +2089,11 @@
@@ -2026,11 +2089,11 @@ class SFTP extends EventEmitter {
? ''
: name.longname
);
@@ -814,7 +818,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
p += len;
}
}
@@ -2749,7 +2812,7 @@
@@ -2749,7 +2812,7 @@ function requestLimits(sftp, cb) {
writeUInt32BE(buf, reqid, 5);
writeUInt32BE(buf, 18, p);
@@ -823,7 +827,7 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
sftp._requests[reqid] = { extended: 'limits@openssh.com', cb };
@@ -2953,18 +3016,28 @@
@@ -2953,18 +3016,28 @@ const CLIENT_HANDLERS = {
// spec not specifying an encoding because the specs for newer
// versions of the protocol all explicitly specify UTF-8 for
// filenames
@@ -856,21 +860,41 @@ diff --git a/node_modules/ssh2/lib/protocol/SFTP.js b/node_modules/ssh2/lib/prot
if (names !== undefined) {
sftp._debug && sftp._debug(
diff --git a/node_modules/ssh2/lib/protocol/constants.js b/node_modules/ssh2/lib/protocol/constants.js
index ad77592..a249e00 100644
--- a/node_modules/ssh2/lib/protocol/constants.js
+++ b/node_modules/ssh2/lib/protocol/constants.js
@@ -73,7 +73,11 @@
@@ -71,9 +71,30 @@ const DEFAULT_SERVER_HOST_KEY = [
];
if (eddsaSupported)
DEFAULT_SERVER_HOST_KEY.unshift('ssh-ed25519');
+// OpenSSH host certificates, offered before the matching plain key so a
+// cert-only server (for example ssh-ed25519-cert-v01@openssh.com) can
+// negotiate. Plain keys stay as fallbacks.
+const OPENSSH_HOST_CERT_ALGS = [
+ 'ecdsa-sha2-nistp256-cert-v01@openssh.com',
+ 'ecdsa-sha2-nistp384-cert-v01@openssh.com',
+ 'ecdsa-sha2-nistp521-cert-v01@openssh.com',
+ 'rsa-sha2-512-cert-v01@openssh.com',
+ 'rsa-sha2-256-cert-v01@openssh.com',
+ 'ssh-rsa-cert-v01@openssh.com',
+];
+if (eddsaSupported)
+ OPENSSH_HOST_CERT_ALGS.unshift('ssh-ed25519-cert-v01@openssh.com');
+DEFAULT_SERVER_HOST_KEY.unshift(...OPENSSH_HOST_CERT_ALGS);
const SUPPORTED_SERVER_HOST_KEY = DEFAULT_SERVER_HOST_KEY.concat([
'ssh-dss',
+ // FIDO/U2F security keys (client publickey auth / rare host keys).
+ 'sk-ecdsa-sha2-nistp256@openssh.com',
+ 'sk-ecdsa-sha2-nistp256-cert-v01@openssh.com',
]);
+if (eddsaSupported)
+if (eddsaSupported) {
+ SUPPORTED_SERVER_HOST_KEY.push('sk-ssh-ed25519@openssh.com');
+ SUPPORTED_SERVER_HOST_KEY.push('sk-ssh-ed25519-cert-v01@openssh.com');
+}
const canUseCipher = (() => {
@@ -160,6 +164,7 @@
@@ -160,6 +181,7 @@ const COMPAT = {
DYN_RPORT_BUG: 1 << 2,
BUG_DHGEX_LARGE: 1 << 3,
IMPLY_RSA_SHA2_SIGALGS: 1 << 4,
@@ -878,7 +902,7 @@ diff --git a/node_modules/ssh2/lib/protocol/constants.js b/node_modules/ssh2/lib
};
module.exports = {
@@ -330,6 +335,7 @@
@@ -330,6 +352,7 @@ module.exports = {
COMPAT_CHECKS: [
[ 'Cisco-1.25', COMPAT.BAD_DHGEX ],
[ /^Cisco-1[.]/, COMPAT.BUG_DHGEX_LARGE ],
@@ -887,9 +911,198 @@ diff --git a/node_modules/ssh2/lib/protocol/constants.js b/node_modules/ssh2/lib
[ /^OpenSSH_5[.][0-9]+/, COMPAT.DYN_RPORT_BUG ],
[ /^OpenSSH_7[.]4/, COMPAT.IMPLY_RSA_SHA2_SIGALGS ],
diff --git a/node_modules/ssh2/lib/protocol/kex.js b/node_modules/ssh2/lib/protocol/kex.js
index 811e631..422a198 100644
--- a/node_modules/ssh2/lib/protocol/kex.js
+++ b/node_modules/ssh2/lib/protocol/kex.js
@@ -1377,8 +1377,13 @@
@@ -30,7 +30,10 @@ const {
createDecipher,
MAC_INFO,
} = require('./crypto.js');
-const { parseDERKey } = require('./keyParser.js');
+const {
+ parseDERKey,
+ getOpenSSHCertSignatureAlgorithm,
+} = require('./keyParser.js');
const {
bufferFill,
bufferParser,
@@ -49,6 +52,68 @@ const {
let MESSAGE_HANDLERS;
+const LEGACY_RSA_HOST_CERT = /^ssh-rsa-cert-v0[01]@openssh\.com$/;
+const RSA_SHA2_256_HOST_CERT = /^rsa-sha2-256-cert-v0[01]@openssh\.com$/;
+const RSA_SHA2_512_HOST_CERT = /^rsa-sha2-512-cert-v0[01]@openssh\.com$/;
+const LEGACY_RSA_HOST_SIGNATURES = new Set([
+ 'rsa-sha2-256',
+ 'rsa-sha2-512',
+ 'ssh-rsa',
+]);
+
+// Client KEX accepts the negotiated host-key name or, for OpenSSH host
+// certificates, the underlying key algorithm. Real servers sign with the
+// embedded key (ssh-ed25519, ecdsa-sha2-*, rsa-sha2-*) rather than the
+// certificate algorithm name. OpenSSH allows any RSA signature only for the
+// legacy ssh-rsa certificate. rsa-sha2-256/512 certificates must use the
+// hash that was negotiated.
+function hostKeySignatureTypeAccepted(negotiatedServerHostKey, sigType) {
+ if (RSA_SHA2_256_HOST_CERT.test(negotiatedServerHostKey))
+ return sigType === 'rsa-sha2-256';
+ if (RSA_SHA2_512_HOST_CERT.test(negotiatedServerHostKey))
+ return sigType === 'rsa-sha2-512';
+ if (sigType === negotiatedServerHostKey)
+ return true;
+ if (sigType === getOpenSSHCertSignatureAlgorithm(negotiatedServerHostKey))
+ return true;
+ return LEGACY_RSA_HOST_CERT.test(negotiatedServerHostKey)
+ && LEGACY_RSA_HOST_SIGNATURES.has(sigType);
+}
+
+// Plain rsa-sha2-* keeps an ssh-rsa key blob. An RSA certificate negotiation
+// is KEY_RSA_CERT: the blob must be ssh-rsa-cert-v01@openssh.com, never a
+// plain ssh-rsa key.
+function hostKeyBlobTypeAccepted(negotiatedServerHostKey, hostPubKeyType) {
+ if (hostPubKeyType === negotiatedServerHostKey)
+ return true;
+ switch (negotiatedServerHostKey) {
+ case 'rsa-sha2-256':
+ case 'rsa-sha2-512':
+ return hostPubKeyType === 'ssh-rsa';
+ case 'rsa-sha2-256-cert-v01@openssh.com':
+ case 'rsa-sha2-512-cert-v01@openssh.com':
+ return hostPubKeyType === 'ssh-rsa-cert-v01@openssh.com';
+ default:
+ return false;
+ }
+}
+
+function hashAlgoForHostKey(negotiatedServerHostKey, sigType) {
+ // Negotiated RSA-SHA2 wins over the signature label so a mismatched
+ // signature cannot select a weaker hash.
+ if (negotiatedServerHostKey === 'rsa-sha2-256'
+ || RSA_SHA2_256_HOST_CERT.test(negotiatedServerHostKey))
+ return 'sha256';
+ if (negotiatedServerHostKey === 'rsa-sha2-512'
+ || RSA_SHA2_512_HOST_CERT.test(negotiatedServerHostKey))
+ return 'sha512';
+ if (sigType === 'rsa-sha2-256')
+ return 'sha256';
+ if (sigType === 'rsa-sha2-512')
+ return 'sha512';
+ return undefined;
+}
+
const GEX_MIN_BITS = 2048; // RFC 8270
const GEX_MAX_BITS = 8192; // RFC 8270
@@ -635,7 +700,7 @@ const createKeyExchange = (() => {
);
}
- if (sigType !== negotiated.serverHostKey) {
+ if (!hostKeySignatureTypeAccepted(negotiated.serverHostKey, sigType)) {
return doFatalError(
this._protocol,
`Wrong signature type: ${sigType}, `
@@ -659,7 +724,10 @@ const createKeyExchange = (() => {
);
}
- if (!(sigValue = sigSSHToASN1(sigValue, sigType))) {
+ if (!(sigValue = sigSSHToASN1(
+ sigValue,
+ getOpenSSHCertSignatureAlgorithm(sigType)
+ ))) {
return doFatalError(
this._protocol,
'Malformed signature',
@@ -685,12 +753,10 @@ const createKeyExchange = (() => {
}
}
- let hashAlgo;
- // Check if we need to override the default hash algorithm
- switch (this.negotiated.serverHostKey) {
- case 'rsa-sha2-256': hashAlgo = 'sha256'; break;
- case 'rsa-sha2-512': hashAlgo = 'sha512'; break;
- }
+ const hashAlgo = hashAlgoForHostKey(
+ this.negotiated.serverHostKey,
+ sigType
+ );
this._protocol._debug
&& this._protocol._debug('Verifying signature ...');
@@ -717,12 +783,7 @@ const createKeyExchange = (() => {
} else {
// Server
- let hashAlgo;
- // Check if we need to override the default hash algorithm
- switch (this.negotiated.serverHostKey) {
- case 'rsa-sha2-256': hashAlgo = 'sha256'; break;
- case 'rsa-sha2-512': hashAlgo = 'sha512'; break;
- }
+ const hashAlgo = hashAlgoForHostKey(this.negotiated.serverHostKey);
this._protocol._debug && this._protocol._debug(
'Generating signature ...'
@@ -739,7 +800,12 @@ const createKeyExchange = (() => {
);
}
- signature = convertSignature(signature, this._hostKey.type);
+ // OpenSSH signs a host certificate with the embedded key algorithm,
+ // not the certificate algorithm name.
+ const sigType = getOpenSSHCertSignatureAlgorithm(
+ this.negotiated.serverHostKey
+ );
+ signature = convertSignature(signature, sigType);
if (signature === false) {
return doFatalError(
this._protocol,
@@ -759,7 +825,6 @@ const createKeyExchange = (() => {
string <method-specific data>
string signature of H
*/
- const sigType = this.negotiated.serverHostKey;
const sigTypeLen = Buffer.byteLength(sigType);
const sigLen = 4 + sigTypeLen + 4 + signature.length;
let p = this._protocol._packetRW.write.allocStartKEX;
@@ -1167,22 +1232,16 @@ const createKeyExchange = (() => {
DISCONNECT_REASON.KEY_EXCHANGE_FAILED
);
}
- if (hostPubKeyType !== this.negotiated.serverHostKey) {
- // Check if we need to make an exception
- switch (this.negotiated.serverHostKey) {
- case 'rsa-sha2-256':
- case 'rsa-sha2-512':
- if (hostPubKeyType === 'ssh-rsa')
- break;
- // FALLTHROUGH
- default:
- return doFatalError(
- this._protocol,
- 'Host key does not match negotiated type',
- 'handshake',
- DISCONNECT_REASON.KEY_EXCHANGE_FAILED
- );
- }
+ if (!hostKeyBlobTypeAccepted(
+ this.negotiated.serverHostKey,
+ hostPubKeyType
+ )) {
+ return doFatalError(
+ this._protocol,
+ 'Host key does not match negotiated type',
+ 'handshake',
+ DISCONNECT_REASON.KEY_EXCHANGE_FAILED
+ );
}
this._hostKey = hostPubKey;
@@ -1377,8 +1436,13 @@ const createKeyExchange = (() => {
this._generator = null;
this._minBits = GEX_MIN_BITS;
this._prefBits = dhEstimate(this.negotiated);
@@ -904,10 +1117,20 @@ diff --git a/node_modules/ssh2/lib/protocol/kex.js b/node_modules/ssh2/lib/proto
this._maxBits = GEX_MAX_BITS;
}
start() {
@@ -1868,6 +1932,8 @@ function trySendNEWKEYS(kex) {
module.exports = {
KexInit,
+ hostKeyBlobTypeAccepted,
+ hostKeySignatureTypeAccepted,
kexinit,
onKEXPayload,
DEFAULT_KEXINIT_CLIENT: new KexInit({
diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib/protocol/keyParser.js
index a276c1a..c8ff9d1 100644
--- a/node_modules/ssh2/lib/protocol/keyParser.js
+++ b/node_modules/ssh2/lib/protocol/keyParser.js
@@ -35,6 +35,9 @@
@@ -35,6 +35,9 @@ const SYM_PRIV_PEM = Symbol('Private key PEM');
const SYM_PUB_PEM = Symbol('Public key PEM');
const SYM_PUB_SSH = Symbol('Public key SSH');
const SYM_DECRYPTED = Symbol('Decrypted Key');
@@ -917,7 +1140,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
// Create OpenSSL cipher name -> SSH cipher name conversion table
const CIPHER_INFO_OPENSSL = Object.create(null);
@@ -258,6 +261,55 @@
@@ -258,6 +261,55 @@ function genOpenSSHEdPub(pub) {
return publicKey;
}
@@ -973,7 +1196,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
function genOpenSSLEdPriv(priv) {
const asnWriter = new Ber.Writer();
asnWriter.startSequence();
@@ -451,6 +503,82 @@
@@ -451,6 +503,82 @@ const BaseKey = {
},
};
@@ -1035,7 +1258,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
+ signedData.set(dataHash, p);
+
+ let toVerify = rawSig;
+ if (this.type === 'sk-ecdsa-sha2-nistp256@openssh.com') {
+ if (getOpenSSHCertBaseType(this.type) === 'sk-ecdsa-sha2-nistp256@openssh.com') {
+ // Convert SSH ECDSA r,s blob to ASN.1 for OpenSSL
+ const { sigSSHToASN1 } = require('./utils.js');
+ toVerify = sigSSHToASN1(rawSig, 'ecdsa-sha2-nistp256');
@@ -1056,7 +1279,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
function OpenSSH_Private(type, comment, privPEM, pubPEM, pubSSH, algo,
decrypted) {
@@ -463,6 +591,26 @@
@@ -463,6 +591,26 @@ function OpenSSH_Private(type, comment, privPEM, pubPEM, pubSSH, algo,
this[SYM_DECRYPTED] = decrypted;
}
OpenSSH_Private.prototype = BaseKey;
@@ -1083,7 +1306,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
{
const regexp = /^-----BEGIN OPENSSH PRIVATE KEY-----(?:\r\n|\n)([\s\S]+)(?:\r\n|\n)-----END OPENSSH PRIVATE KEY-----$/;
OpenSSH_Private.parse = (str, passphrase) => {
@@ -771,6 +919,87 @@
@@ -771,6 +919,87 @@ OpenSSH_Private.prototype = BaseKey;
privPEM = genOpenSSLECDSAPriv(oid, ecpub, ecpriv);
break;
}
@@ -1171,7 +1394,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
default:
return new Error(`Unsupported OpenSSH private key type: ${type}`);
}
@@ -1186,31 +1415,97 @@
@@ -1186,31 +1415,97 @@ function OpenSSH_Public(type, comment, pubPEM, pubSSH, algo) {
this[SYM_DECRYPTED] = false;
}
OpenSSH_Public.prototype = BaseKey;
@@ -1281,7 +1504,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
};
}
@@ -1309,7 +1604,7 @@
@@ -1309,7 +1604,7 @@ RFC4716_Public.prototype = BaseKey;
}
@@ -1290,7 +1513,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
if (!isSupportedKeyType(baseType))
return new Error(`Unsupported OpenSSH public key type: ${baseType}`);
@@ -1317,6 +1612,10 @@
@@ -1317,6 +1612,10 @@ function parseDER(data, baseType, comment, fullType) {
let oid;
let pubPEM = null;
let pubSSH = null;
@@ -1301,7 +1524,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
switch (baseType) {
case 'ssh-rsa': {
@@ -1383,11 +1682,66 @@
@@ -1383,11 +1682,66 @@ function parseDER(data, baseType, comment, fullType) {
pubSSH = genOpenSSHECDSAPub(oid, ecpub);
break;
}
@@ -1369,7 +1592,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
}
function isSupportedKeyType(type) {
@@ -1397,8 +1751,10 @@
@@ -1397,8 +1751,10 @@ function isSupportedKeyType(type) {
case 'ecdsa-sha2-nistp256':
case 'ecdsa-sha2-nistp384':
case 'ecdsa-sha2-nistp521':
@@ -1380,7 +1603,7 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
if (eddsaSupported)
return true;
// FALLTHROUGH
@@ -1460,7 +1816,13 @@
@@ -1460,7 +1816,13 @@ function parseKey(data, passphrase) {
if (type !== undefined) {
data = binaryKeyParser.readRaw();
if (data !== undefined) {
@@ -1395,11 +1618,12 @@ diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib
// Ignore potentially useless errors in case the data was not actually
// in the binary format
if (ret instanceof Error)
@@ -1479,6 +1841,7 @@
@@ -1479,6 +1841,7 @@ function parseKey(data, passphrase) {
module.exports = {
isParsedKey,
isSupportedKeyType,
- parseDERKey: (data, type) => parseDER(data, type, '', type),
+ getOpenSSHCertSignatureAlgorithm,
parseDERKey: (data, type) => parseDER(data, type, '', type),
+ parseDERKey: (data, type) => parseDER(data, getOpenSSHCertBaseType(type), '', type),
parseKey,
};