UNPKG

pure-js-sftp

Version:

A pure JavaScript SFTP client with revolutionary RSA-SHA2 compatibility fixes. Zero native dependencies, built on ssh2-streams with 100% SSH key support.

243 lines 9.46 kB
"use strict"; /** * Pure JavaScript SSH Key Parser using sshpk only * Eliminates all Node.js crypto dependencies for maximum VSCode/webpack compatibility */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.parseKey = parseKey; // @ts-ignore const sshpk = __importStar(require("sshpk")); function parseKey(keyData, passphrase) { let keyString; if (Buffer.isBuffer(keyData)) { keyString = keyData.toString('utf8'); } else { keyString = keyData; } // Normalize line endings and trim keyString = keyString.replace(/\r\n/g, '\n').trim(); try { // Use sshpk exclusively for all key parsing const options = {}; if (passphrase !== undefined && passphrase !== null && passphrase !== '') { options.passphrase = passphrase; } const sshpkKey = sshpk.parsePrivateKey(keyString, 'auto', options); if (sshpkKey) { return createKeyFromSSHPK(sshpkKey); } } catch (sshpkError) { // If sshpk fails, return null console.warn(`Key parsing failed: ${sshpkError instanceof Error ? sshpkError.message : String(sshpkError)}`); } return null; } // Create our ParsedKey interface from sshpk key objects function createKeyFromSSHPK(sshpkKey) { // Map sshpk types to our SSH types const typeMapping = { 'rsa': 'ssh-rsa', 'ed25519': 'ssh-ed25519', 'ecdsa': getECDSAType(sshpkKey) }; const sshType = typeMapping[sshpkKey.type] || sshpkKey.type; return { type: sshType, comment: sshpkKey.comment || '', sign(data, algorithm) { try { // Determine signing algorithm let signingAlgo; if (sshType === 'ssh-rsa') { if (algorithm === 'ssh-rsa') { signingAlgo = 'sha1'; } else if (algorithm === 'rsa-sha2-256') { signingAlgo = 'sha256'; } else if (algorithm === 'rsa-sha2-512') { signingAlgo = 'sha512'; } else { // Default to sha256 for RSA-SHA2 compatibility signingAlgo = 'sha256'; } } else { signingAlgo = getHashAlgorithm(sshType, algorithm); } const signer = sshpkKey.createSign(signingAlgo); signer.update(data); const signature = signer.sign(); // Extract raw signature bytes properly from sshpk signature object if (sshType === 'ssh-rsa') { // For RSA keys, ssh2-streams expects raw signature bytes if (signature && typeof signature.toBuffer === 'function') { // Try to get raw signature (not SSH wire format) try { return signature.toBuffer('asn1'); } catch (e) { // Fallback to raw buffer return signature.toBuffer(); } } else if (signature && signature.signature && Buffer.isBuffer(signature.signature)) { return signature.signature; } else if (Buffer.isBuffer(signature)) { return signature; } else { throw new Error('Unable to extract raw signature bytes from sshpk signature object'); } } else { // For Ed25519/ECDSA, ssh2-streams expects different format if (signature && typeof signature.toBuffer === 'function') { return signature.toBuffer('ssh'); } else if (signature && signature.signature && Buffer.isBuffer(signature.signature)) { return signature.signature; } else if (Buffer.isBuffer(signature)) { return signature; } else { throw new Error('Unable to extract signature bytes from sshpk signature object'); } } } catch (error) { throw new Error(`sshpk signing failed: ${error instanceof Error ? error.message : String(error)}`); } }, verify(data, signature, algorithm) { try { // Use sshpk's verification capability const hashAlgo = getHashAlgorithm(sshType, algorithm); const verifier = sshpkKey.createVerify(hashAlgo); verifier.update(data); // Try to parse the signature buffer as SSH format let sshpkSig; try { sshpkSig = sshpk.parseSignature(signature, sshType, 'ssh'); } catch (parseError) { // If parsing fails, try with raw signature sshpkSig = signature; } return verifier.verify(sshpkSig); } catch (error) { return false; } }, isPrivateKey() { return sshpkKey.isPrivate(); }, getPrivatePEM() { try { return sshpkKey.toBuffer('pem').toString(); } catch (error) { throw new Error(`sshpk PEM export failed: ${error instanceof Error ? error.message : String(error)}`); } }, getPublicPEM() { try { const publicKey = sshpkKey.toPublic(); return publicKey.toBuffer('pem').toString(); } catch (error) { throw new Error(`sshpk public PEM export failed: ${error instanceof Error ? error.message : String(error)}`); } }, getPublicSSH() { try { const publicKey = sshpkKey.toPublic(); return publicKey.toBuffer('ssh'); } catch (error) { throw new Error(`sshpk SSH export failed: ${error instanceof Error ? error.message : String(error)}`); } }, equals(other) { try { return this.type === other.type && this.getPublicSSH().equals(other.getPublicSSH()); } catch (error) { return false; } } }; } function getECDSAType(sshpkKey) { if (sshpkKey.curve) { switch (sshpkKey.curve) { case 'nistp256': return 'ecdsa-sha2-nistp256'; case 'nistp384': return 'ecdsa-sha2-nistp384'; case 'nistp521': return 'ecdsa-sha2-nistp521'; default: return 'ecdsa-sha2-' + sshpkKey.curve; } } return 'ecdsa-sha2-nistp256'; // default } function getHashAlgorithm(sshType, algorithm) { if (algorithm) { if (algorithm.includes('sha1') || algorithm === 'ssh-rsa') { return 'sha1'; } else if (algorithm.includes('sha256') || algorithm === 'rsa-sha2-256') { return 'sha256'; } else if (algorithm.includes('sha512') || algorithm === 'rsa-sha2-512') { return 'sha512'; } } // Default hash algorithms based on key type switch (sshType) { case 'ssh-rsa': return 'sha256'; // Default to SHA256 for RSA (RSA-SHA2) case 'ssh-ed25519': return 'sha512'; // Ed25519 uses SHA512 case 'ecdsa-sha2-nistp256': return 'sha256'; case 'ecdsa-sha2-nistp384': return 'sha384'; case 'ecdsa-sha2-nistp521': return 'sha512'; default: return 'sha256'; } } //# sourceMappingURL=enhanced-key-parser.js.map