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.

235 lines 9.8 kB
"use strict"; /** * Pure JavaScript RSA-SHA2 Signature Wrapper 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.wrapRSAKeyWithSHA2 = wrapRSAKeyWithSHA2; exports.createRSASHA2SignatureCallback = createRSASHA2SignatureCallback; exports.shouldUseRSASHA2Wrapper = shouldUseRSASHA2Wrapper; exports.getRecommendedRSASHA2Algorithm = getRecommendedRSASHA2Algorithm; // @ts-ignore const sshpk = __importStar(require("sshpk")); /** * Wraps an ssh2-streams RSA key to use RSA-SHA2 signatures via sshpk * @param originalKey - The original ssh2-streams key object * @param privateKeyPEM - The raw private key in PEM format * @param algorithm - RSA signature algorithm ('sha256' or 'sha512') * @returns Wrapped key object with RSA-SHA2 signatures */ function wrapRSAKeyWithSHA2(originalKey, privateKeyPEM, algorithm = 'sha256') { // Validate that this is an RSA key if (!originalKey || originalKey.type !== 'ssh-rsa') { throw new Error('RSA-SHA2 wrapper can only be applied to RSA keys'); } // Parse key with sshpk let sshpkKey; try { sshpkKey = sshpk.parsePrivateKey(privateKeyPEM, 'auto'); } catch (error) { throw new Error(`Failed to parse private key with sshpk: ${error instanceof Error ? error.message : String(error)}`); } const wrappedKey = { type: originalKey.type, getPublicSSH() { // Use original key's public SSH format return originalKey.getPublicSSH(); }, sign(data) { try { // Use sshpk for RSA-SHA2 signature const signer = sshpkKey.createSign(algorithm); signer.update(data); const signature = signer.sign(); // Extract raw RSA signature bytes for ssh2-streams compatibility 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'); } } catch (error) { // Fallback to original ssh2-streams signing if sshpk fails console.warn(`sshpk RSA-SHA2 signing failed, falling back to original: ${error instanceof Error ? error.message : String(error)}`); return originalKey.sign(data); } }, verify(data, signature) { try { // Use sshpk for verification const verifier = sshpkKey.createVerify(algorithm); verifier.update(data); // Try to parse signature let sshpkSig; try { sshpkSig = sshpk.parseSignature(signature, 'ssh-rsa', 'ssh'); } catch (parseError) { sshpkSig = signature; } return verifier.verify(sshpkSig); } catch (error) { // Fallback to original verification console.warn(`sshpk RSA-SHA2 verification failed, falling back to original: ${error instanceof Error ? error.message : String(error)}`); return originalKey.verify(data, signature); } } }; return wrappedKey; } /** * Creates an RSA-SHA2 signature callback for ssh2-streams authentication using sshpk only * @param originalKey - The original ssh2-streams key object * @param privateKeyPEM - The raw private key in PEM format * @param passphrase - Optional passphrase for encrypted keys * @param algorithm - RSA signature algorithm ('sha256' or 'sha512') * @returns Signature callback function for ssh2-streams authPK */ function createRSASHA2SignatureCallback(originalKey, privateKeyPEM, passphrase, algorithm = 'sha256') { // Validate that this is an RSA key if (!originalKey || originalKey.type !== 'ssh-rsa') { // Return original callback for non-RSA keys return (buf, cb) => { try { const signature = originalKey.sign(buf); cb(signature); } catch (error) { throw new Error(`Original key signing failed: ${error instanceof Error ? error.message : String(error)}`); } }; } // Try to parse key with sshpk let sshpkKey = null; try { const sshpkOptions = {}; if (passphrase) { sshpkOptions.passphrase = passphrase; } sshpkKey = sshpk.parsePrivateKey(privateKeyPEM, 'auto', sshpkOptions); } catch (sshpkError) { console.warn(`Failed to parse key with sshpk for RSA-SHA2, using original signing: ${sshpkError instanceof Error ? sshpkError.message : String(sshpkError)}`); } // If we couldn't parse with sshpk, fall back to original signing if (!sshpkKey) { return (buf, cb) => { try { const signature = originalKey.sign(buf); cb(signature); } catch (signError) { throw new Error(`Fallback signing failed: ${signError instanceof Error ? signError.message : String(signError)}`); } }; } // Return sshpk RSA-SHA2 signature callback return (buf, cb) => { try { // Generate RSA-SHA2 signature using sshpk const signer = sshpkKey.createSign(algorithm); signer.update(buf); const signature = signer.sign(); // Convert to buffer format - extract raw RSA signature bytes let signatureBuffer; if (signature && typeof signature.toBuffer === 'function') { // Try to get raw signature (not SSH wire format) try { signatureBuffer = signature.toBuffer('asn1'); } catch (e) { // Fallback to raw buffer signatureBuffer = signature.toBuffer(); } } else if (signature && signature.signature && Buffer.isBuffer(signature.signature)) { signatureBuffer = signature.signature; } else if (Buffer.isBuffer(signature)) { signatureBuffer = signature; } else { throw new Error('Unable to extract raw signature bytes from sshpk signature object'); } cb(signatureBuffer); } catch (error) { console.warn(`sshpk RSA-SHA2 signing failed, falling back to original: ${error instanceof Error ? error.message : String(error)}`); try { // Fallback to original ssh2-streams signing const fallbackSignature = originalKey.sign(buf); cb(fallbackSignature); } catch (fallbackError) { throw new Error(`Both sshpk RSA-SHA2 and fallback signing failed: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`); } } }; } /** * Determines if a key should use the RSA-SHA2 wrapper * @param keyType - The SSH key type (e.g., 'ssh-rsa', 'ssh-ed25519') * @returns True if the key should be wrapped for RSA-SHA2 */ function shouldUseRSASHA2Wrapper(keyType) { return keyType === 'ssh-rsa'; } /** * Gets the appropriate RSA-SHA2 algorithm based on key size * @param keySize - RSA key size in bits * @returns Recommended algorithm ('sha256' or 'sha512') */ function getRecommendedRSASHA2Algorithm(keySize) { // Use SHA-512 for larger keys (4096+), SHA-256 for smaller keys return keySize >= 4096 ? 'sha512' : 'sha256'; } //# sourceMappingURL=rsa-sha2-wrapper.js.map