first commit
This commit is contained in:
386
build/node_modules/ssh2-streams/lib/constants.js
generated
vendored
Normal file
386
build/node_modules/ssh2-streams/lib/constants.js
generated
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
var semver = require('semver');
|
||||
|
||||
var i;
|
||||
var keys;
|
||||
var len;
|
||||
|
||||
var MESSAGE = exports.MESSAGE = {
|
||||
// Transport layer protocol -- generic (1-19)
|
||||
DISCONNECT: 1,
|
||||
IGNORE: 2,
|
||||
UNIMPLEMENTED: 3,
|
||||
DEBUG: 4,
|
||||
SERVICE_REQUEST: 5,
|
||||
SERVICE_ACCEPT: 6,
|
||||
|
||||
// Transport layer protocol -- algorithm negotiation (20-29)
|
||||
KEXINIT: 20,
|
||||
NEWKEYS: 21,
|
||||
|
||||
// Transport layer protocol -- key exchange method-specific (30-49)
|
||||
|
||||
// User auth protocol -- generic (50-59)
|
||||
USERAUTH_REQUEST: 50,
|
||||
USERAUTH_FAILURE: 51,
|
||||
USERAUTH_SUCCESS: 52,
|
||||
USERAUTH_BANNER: 53,
|
||||
|
||||
// User auth protocol -- user auth method-specific (60-79)
|
||||
|
||||
// Connection protocol -- generic (80-89)
|
||||
GLOBAL_REQUEST: 80,
|
||||
REQUEST_SUCCESS: 81,
|
||||
REQUEST_FAILURE: 82,
|
||||
|
||||
// Connection protocol -- channel-related (90-127)
|
||||
CHANNEL_OPEN: 90,
|
||||
CHANNEL_OPEN_CONFIRMATION: 91,
|
||||
CHANNEL_OPEN_FAILURE: 92,
|
||||
CHANNEL_WINDOW_ADJUST: 93,
|
||||
CHANNEL_DATA: 94,
|
||||
CHANNEL_EXTENDED_DATA: 95,
|
||||
CHANNEL_EOF: 96,
|
||||
CHANNEL_CLOSE: 97,
|
||||
CHANNEL_REQUEST: 98,
|
||||
CHANNEL_SUCCESS: 99,
|
||||
CHANNEL_FAILURE: 100
|
||||
|
||||
// Reserved for client protocols (128-191)
|
||||
|
||||
// Local extensions (192-155)
|
||||
};
|
||||
for (i = 0, keys = Object.keys(MESSAGE), len = keys.length; i < len; ++i)
|
||||
MESSAGE[MESSAGE[keys[i]]] = keys[i];
|
||||
// context-specific message codes:
|
||||
MESSAGE.KEXDH_INIT = 30;
|
||||
MESSAGE.KEXDH_REPLY = 31;
|
||||
MESSAGE.KEXDH_GEX_REQUEST = 34;
|
||||
MESSAGE.KEXDH_GEX_GROUP = 31;
|
||||
MESSAGE.KEXDH_GEX_INIT = 32;
|
||||
MESSAGE.KEXDH_GEX_REPLY = 33;
|
||||
MESSAGE.KEXECDH_INIT = 30; // included here for completeness
|
||||
MESSAGE.KEXECDH_REPLY = 31; // included here for completeness
|
||||
MESSAGE.USERAUTH_PASSWD_CHANGEREQ = 60;
|
||||
MESSAGE.USERAUTH_PK_OK = 60;
|
||||
MESSAGE.USERAUTH_INFO_REQUEST = 60;
|
||||
MESSAGE.USERAUTH_INFO_RESPONSE = 61;
|
||||
|
||||
var DYNAMIC_KEXDH_MESSAGE = exports.DYNAMIC_KEXDH_MESSAGE = {};
|
||||
DYNAMIC_KEXDH_MESSAGE[MESSAGE.KEXDH_GEX_GROUP] = 'KEXDH_GEX_GROUP';
|
||||
DYNAMIC_KEXDH_MESSAGE[MESSAGE.KEXDH_GEX_REPLY] = 'KEXDH_GEX_REPLY';
|
||||
|
||||
var KEXDH_MESSAGE = exports.KEXDH_MESSAGE = {};
|
||||
KEXDH_MESSAGE[MESSAGE.KEXDH_INIT] = 'KEXDH_INIT';
|
||||
KEXDH_MESSAGE[MESSAGE.KEXDH_REPLY] = 'KEXDH_REPLY';
|
||||
|
||||
var DISCONNECT_REASON = exports.DISCONNECT_REASON = {
|
||||
HOST_NOT_ALLOWED_TO_CONNECT: 1,
|
||||
PROTOCOL_ERROR: 2,
|
||||
KEY_EXCHANGE_FAILED: 3,
|
||||
RESERVED: 4,
|
||||
MAC_ERROR: 5,
|
||||
COMPRESSION_ERROR: 6,
|
||||
SERVICE_NOT_AVAILABLE: 7,
|
||||
PROTOCOL_VERSION_NOT_SUPPORTED: 8,
|
||||
HOST_KEY_NOT_VERIFIABLE: 9,
|
||||
CONNECTION_LOST: 10,
|
||||
BY_APPLICATION: 11,
|
||||
TOO_MANY_CONNECTIONS: 12,
|
||||
AUTH_CANCELED_BY_USER: 13,
|
||||
NO_MORE_AUTH_METHODS_AVAILABLE: 14,
|
||||
ILLEGAL_USER_NAME: 15
|
||||
};
|
||||
for (i = 0, keys = Object.keys(DISCONNECT_REASON), len = keys.length;
|
||||
i < len;
|
||||
++i) {
|
||||
DISCONNECT_REASON[DISCONNECT_REASON[keys[i]]] = keys[i];
|
||||
}
|
||||
|
||||
var CHANNEL_OPEN_FAILURE = exports.CHANNEL_OPEN_FAILURE = {
|
||||
ADMINISTRATIVELY_PROHIBITED: 1,
|
||||
CONNECT_FAILED: 2,
|
||||
UNKNOWN_CHANNEL_TYPE: 3,
|
||||
RESOURCE_SHORTAGE: 4
|
||||
};
|
||||
for (i = 0, keys = Object.keys(CHANNEL_OPEN_FAILURE), len = keys.length;
|
||||
i < len;
|
||||
++i) {
|
||||
CHANNEL_OPEN_FAILURE[CHANNEL_OPEN_FAILURE[keys[i]]] = keys[i];
|
||||
}
|
||||
|
||||
var TERMINAL_MODE = exports.TERMINAL_MODE = {
|
||||
TTY_OP_END: 0, // Indicates end of options.
|
||||
VINTR: 1, // Interrupt character; 255 if none. Similarly for the
|
||||
// other characters. Not all of these characters are
|
||||
// supported on all systems.
|
||||
VQUIT: 2, // The quit character (sends SIGQUIT signal on POSIX
|
||||
// systems).
|
||||
VERASE: 3, // Erase the character to left of the cursor.
|
||||
VKILL: 4, // Kill the current input line.
|
||||
VEOF: 5, // End-of-file character (sends EOF from the terminal).
|
||||
VEOL: 6, // End-of-line character in addition to carriage return
|
||||
// and/or linefeed.
|
||||
VEOL2: 7, // Additional end-of-line character.
|
||||
VSTART: 8, // Continues paused output (normally control-Q).
|
||||
VSTOP: 9, // Pauses output (normally control-S).
|
||||
VSUSP: 10, // Suspends the current program.
|
||||
VDSUSP: 11, // Another suspend character.
|
||||
VREPRINT: 12, // Reprints the current input line.
|
||||
VWERASE: 13, // Erases a word left of cursor.
|
||||
VLNEXT: 14, // Enter the next character typed literally, even if it
|
||||
// is a special character
|
||||
VFLUSH: 15, // Character to flush output.
|
||||
VSWTCH: 16, // Switch to a different shell layer.
|
||||
VSTATUS: 17, // Prints system status line (load, command, pid, etc).
|
||||
VDISCARD: 18, // Toggles the flushing of terminal output.
|
||||
IGNPAR: 30, // The ignore parity flag. The parameter SHOULD be 0
|
||||
// if this flag is FALSE, and 1 if it is TRUE.
|
||||
PARMRK: 31, // Mark parity and framing errors.
|
||||
INPCK: 32, // Enable checking of parity errors.
|
||||
ISTRIP: 33, // Strip 8th bit off characters.
|
||||
INLCR: 34, // Map NL into CR on input.
|
||||
IGNCR: 35, // Ignore CR on input.
|
||||
ICRNL: 36, // Map CR to NL on input.
|
||||
IUCLC: 37, // Translate uppercase characters to lowercase.
|
||||
IXON: 38, // Enable output flow control.
|
||||
IXANY: 39, // Any char will restart after stop.
|
||||
IXOFF: 40, // Enable input flow control.
|
||||
IMAXBEL: 41, // Ring bell on input queue full.
|
||||
ISIG: 50, // Enable signals INTR, QUIT, [D]SUSP.
|
||||
ICANON: 51, // Canonicalize input lines.
|
||||
XCASE: 52, // Enable input and output of uppercase characters by
|
||||
// preceding their lowercase equivalents with "\".
|
||||
ECHO: 53, // Enable echoing.
|
||||
ECHOE: 54, // Visually erase chars.
|
||||
ECHOK: 55, // Kill character discards current line.
|
||||
ECHONL: 56, // Echo NL even if ECHO is off.
|
||||
NOFLSH: 57, // Don't flush after interrupt.
|
||||
TOSTOP: 58, // Stop background jobs from output.
|
||||
IEXTEN: 59, // Enable extensions.
|
||||
ECHOCTL: 60, // Echo control characters as ^(Char).
|
||||
ECHOKE: 61, // Visual erase for line kill.
|
||||
PENDIN: 62, // Retype pending input.
|
||||
OPOST: 70, // Enable output processing.
|
||||
OLCUC: 71, // Convert lowercase to uppercase.
|
||||
ONLCR: 72, // Map NL to CR-NL.
|
||||
OCRNL: 73, // Translate carriage return to newline (output).
|
||||
ONOCR: 74, // Translate newline to carriage return-newline
|
||||
// (output).
|
||||
ONLRET: 75, // Newline performs a carriage return (output).
|
||||
CS7: 90, // 7 bit mode.
|
||||
CS8: 91, // 8 bit mode.
|
||||
PARENB: 92, // Parity enable.
|
||||
PARODD: 93, // Odd parity, else even.
|
||||
TTY_OP_ISPEED: 128, // Specifies the input baud rate in bits per second.
|
||||
TTY_OP_OSPEED: 129 // Specifies the output baud rate in bits per second.
|
||||
};
|
||||
for (i = 0, keys = Object.keys(TERMINAL_MODE), len = keys.length; i < len; ++i)
|
||||
TERMINAL_MODE[TERMINAL_MODE[keys[i]]] = keys[i];
|
||||
|
||||
var CHANNEL_EXTENDED_DATATYPE = exports.CHANNEL_EXTENDED_DATATYPE = {
|
||||
STDERR: 1
|
||||
};
|
||||
for (i = 0, keys = Object.keys(CHANNEL_EXTENDED_DATATYPE), len = keys.length;
|
||||
i < len;
|
||||
++i) {
|
||||
CHANNEL_EXTENDED_DATATYPE[CHANNEL_EXTENDED_DATATYPE[keys[i]]] = keys[i];
|
||||
}
|
||||
|
||||
exports.SIGNALS = ['ABRT', 'ALRM', 'FPE', 'HUP', 'ILL', 'INT',
|
||||
'QUIT', 'SEGV', 'TERM', 'USR1', 'USR2', 'KILL',
|
||||
'PIPE'];
|
||||
|
||||
var DEFAULT_KEX = [
|
||||
'diffie-hellman-group14-sha1' // REQUIRED
|
||||
];
|
||||
var SUPPORTED_KEX = [
|
||||
'diffie-hellman-group1-sha1' // REQUIRED
|
||||
];
|
||||
if (semver.gte(process.version, '0.11.12')) {
|
||||
// https://tools.ietf.org/html/rfc4419#section-4
|
||||
DEFAULT_KEX = [
|
||||
'diffie-hellman-group-exchange-sha256'
|
||||
].concat(DEFAULT_KEX);
|
||||
SUPPORTED_KEX = [
|
||||
'diffie-hellman-group-exchange-sha1'
|
||||
].concat(SUPPORTED_KEX);
|
||||
}
|
||||
if (semver.gte(process.version, '0.11.14')) {
|
||||
// https://tools.ietf.org/html/rfc5656#section-10.1
|
||||
DEFAULT_KEX = [
|
||||
'ecdh-sha2-nistp256',
|
||||
'ecdh-sha2-nistp384',
|
||||
'ecdh-sha2-nistp521'
|
||||
].concat(DEFAULT_KEX);
|
||||
}
|
||||
var KEX_BUF = new Buffer(DEFAULT_KEX.join(','), 'ascii');
|
||||
SUPPORTED_KEX = DEFAULT_KEX.concat(SUPPORTED_KEX);
|
||||
|
||||
var DEFAULT_SERVER_HOST_KEY = [
|
||||
'ssh-rsa'
|
||||
];
|
||||
var SUPPORTED_SERVER_HOST_KEY = [
|
||||
'ssh-dss'
|
||||
];
|
||||
if (semver.gte(process.version, '5.2.0')) {
|
||||
// ECDSA keys are only supported in v5.2.0+ because of a crypto change that
|
||||
// made it possible to (efficiently) generate an ECDSA public key from a
|
||||
// private key (commit nodejs/node#da5ac55c83eb2c09cfb3baf7875529e8f1113529)
|
||||
DEFAULT_SERVER_HOST_KEY.push(
|
||||
'ecdsa-sha2-nistp256',
|
||||
'ecdsa-sha2-nistp384',
|
||||
'ecdsa-sha2-nistp521'
|
||||
);
|
||||
}
|
||||
var SERVER_HOST_KEY_BUF = new Buffer(DEFAULT_SERVER_HOST_KEY.join(','),
|
||||
'ascii');
|
||||
SUPPORTED_SERVER_HOST_KEY = DEFAULT_SERVER_HOST_KEY.concat(
|
||||
SUPPORTED_SERVER_HOST_KEY
|
||||
);
|
||||
|
||||
var DEFAULT_CIPHER = [];
|
||||
var SUPPORTED_CIPHER = [
|
||||
'aes256-cbc',
|
||||
'aes192-cbc',
|
||||
'aes128-cbc',
|
||||
'blowfish-cbc',
|
||||
'3des-cbc',
|
||||
|
||||
// http://tools.ietf.org/html/rfc4345#section-4:
|
||||
'arcfour256',
|
||||
'arcfour128',
|
||||
|
||||
'cast128-cbc',
|
||||
'arcfour'
|
||||
];
|
||||
if (semver.gte(process.version, '0.11.12')) {
|
||||
// node v0.11.12 introduced support for setting AAD, which is needed for
|
||||
// AES-GCM in SSH2
|
||||
DEFAULT_CIPHER = [
|
||||
// http://tools.ietf.org/html/rfc5647
|
||||
'aes128-gcm',
|
||||
'aes128-gcm@openssh.com',
|
||||
'aes256-gcm',
|
||||
'aes256-gcm@openssh.com'
|
||||
].concat(DEFAULT_CIPHER);
|
||||
}
|
||||
DEFAULT_CIPHER = [
|
||||
// http://tools.ietf.org/html/rfc4344#section-4
|
||||
'aes128-ctr',
|
||||
'aes192-ctr',
|
||||
'aes256-ctr'
|
||||
].concat(DEFAULT_CIPHER);
|
||||
var CIPHER_BUF = new Buffer(DEFAULT_CIPHER.join(','), 'ascii');
|
||||
SUPPORTED_CIPHER = DEFAULT_CIPHER.concat(SUPPORTED_CIPHER);
|
||||
|
||||
var DEFAULT_HMAC = [
|
||||
'hmac-sha2-256',
|
||||
'hmac-sha2-512',
|
||||
'hmac-sha1',
|
||||
];
|
||||
var SUPPORTED_HMAC = [
|
||||
'hmac-md5',
|
||||
'hmac-sha2-256-96', // first 96 bits of HMAC-SHA256
|
||||
'hmac-sha2-512-96', // first 96 bits of HMAC-SHA512
|
||||
'hmac-ripemd160',
|
||||
'hmac-sha1-96', // first 96 bits of HMAC-SHA1
|
||||
'hmac-md5-96' // first 96 bits of HMAC-MD5
|
||||
];
|
||||
var HMAC_BUF = new Buffer(DEFAULT_HMAC.join(','), 'ascii');
|
||||
SUPPORTED_HMAC = DEFAULT_HMAC.concat(SUPPORTED_HMAC);
|
||||
|
||||
var DEFAULT_COMPRESS = [
|
||||
'none',
|
||||
'zlib@openssh.com', // ZLIB (LZ77) compression, except
|
||||
// compression/decompression does not start until after
|
||||
// successful user authentication
|
||||
'zlib' // ZLIB (LZ77) compression
|
||||
];
|
||||
var SUPPORTED_COMPRESS = [];
|
||||
var COMPRESS_BUF = new Buffer(DEFAULT_COMPRESS.join(','), 'ascii');
|
||||
SUPPORTED_COMPRESS = DEFAULT_COMPRESS.concat(SUPPORTED_COMPRESS);
|
||||
|
||||
exports.ALGORITHMS = {
|
||||
KEX: DEFAULT_KEX,
|
||||
KEX_BUF: KEX_BUF,
|
||||
SUPPORTED_KEX: SUPPORTED_KEX,
|
||||
|
||||
SERVER_HOST_KEY: DEFAULT_SERVER_HOST_KEY,
|
||||
SERVER_HOST_KEY_BUF: SERVER_HOST_KEY_BUF,
|
||||
SUPPORTED_SERVER_HOST_KEY: SUPPORTED_SERVER_HOST_KEY,
|
||||
|
||||
CIPHER: DEFAULT_CIPHER,
|
||||
CIPHER_BUF: CIPHER_BUF,
|
||||
SUPPORTED_CIPHER: SUPPORTED_CIPHER,
|
||||
|
||||
HMAC: DEFAULT_HMAC,
|
||||
HMAC_BUF: HMAC_BUF,
|
||||
SUPPORTED_HMAC: SUPPORTED_HMAC,
|
||||
|
||||
COMPRESS: DEFAULT_COMPRESS,
|
||||
COMPRESS_BUF: COMPRESS_BUF,
|
||||
SUPPORTED_COMPRESS: SUPPORTED_COMPRESS
|
||||
};
|
||||
exports.SSH_TO_OPENSSL = {
|
||||
// ECDH key exchange
|
||||
'ecdh-sha2-nistp256': 'prime256v1', // OpenSSL's name for 'secp256r1'
|
||||
'ecdh-sha2-nistp384': 'secp384r1',
|
||||
'ecdh-sha2-nistp521': 'secp521r1',
|
||||
// Ciphers
|
||||
'aes128-gcm': 'aes-128-gcm',
|
||||
'aes256-gcm': 'aes-256-gcm',
|
||||
'aes128-gcm@openssh.com': 'aes-128-gcm',
|
||||
'aes256-gcm@openssh.com': 'aes-256-gcm',
|
||||
'3des-cbc': 'des-ede3-cbc',
|
||||
'blowfish-cbc': 'bf-cbc',
|
||||
'aes256-cbc': 'aes-256-cbc',
|
||||
'aes192-cbc': 'aes-192-cbc',
|
||||
'aes128-cbc': 'aes-128-cbc',
|
||||
'idea-cbc': 'idea-cbc',
|
||||
'cast128-cbc': 'cast-cbc',
|
||||
'rijndael-cbc@lysator.liu.se': 'aes-256-cbc',
|
||||
'arcfour128': 'rc4',
|
||||
'arcfour256': 'rc4',
|
||||
'arcfour512': 'rc4',
|
||||
'arcfour': 'rc4',
|
||||
'camellia128-cbc': 'camellia-128-cbc',
|
||||
'camellia192-cbc': 'camellia-192-cbc',
|
||||
'camellia256-cbc': 'camellia-256-cbc',
|
||||
'camellia128-cbc@openssh.com': 'camellia-128-cbc',
|
||||
'camellia192-cbc@openssh.com': 'camellia-192-cbc',
|
||||
'camellia256-cbc@openssh.com': 'camellia-256-cbc',
|
||||
'3des-ctr': 'des-ede3',
|
||||
'blowfish-ctr': 'bf-ecb',
|
||||
'aes256-ctr': 'aes-256-ctr',
|
||||
'aes192-ctr': 'aes-192-ctr',
|
||||
'aes128-ctr': 'aes-128-ctr',
|
||||
'cast128-ctr': 'cast5-ecb',
|
||||
'camellia128-ctr': 'camellia-128-ecb',
|
||||
'camellia192-ctr': 'camellia-192-ecb',
|
||||
'camellia256-ctr': 'camellia-256-ecb',
|
||||
'camellia128-ctr@openssh.com': 'camellia-128-ecb',
|
||||
'camellia192-ctr@openssh.com': 'camellia-192-ecb',
|
||||
'camellia256-ctr@openssh.com': 'camellia-256-ecb',
|
||||
// HMAC
|
||||
'hmac-sha1-96': 'sha1',
|
||||
'hmac-sha1': 'sha1',
|
||||
'hmac-sha2-256': 'sha256',
|
||||
'hmac-sha2-256-96': 'sha256',
|
||||
'hmac-sha2-512': 'sha512',
|
||||
'hmac-sha2-512-96': 'sha512',
|
||||
'hmac-md5-96': 'md5',
|
||||
'hmac-md5': 'md5',
|
||||
'hmac-ripemd160': 'ripemd160'
|
||||
};
|
||||
|
||||
var BUGS = exports.BUGS = {
|
||||
BAD_DHGEX: 1,
|
||||
OLD_EXIT: 2,
|
||||
DYN_RPORT_BUG: 4
|
||||
};
|
||||
|
||||
exports.BUGGY_IMPLS = [
|
||||
[ 'Cisco-1.25', BUGS.BAD_DHGEX ],
|
||||
[ /^[0-9.]+$/, BUGS.OLD_EXIT ], // old SSH.com implementations
|
||||
[ /^OpenSSH_5\.\d+/, BUGS.DYN_RPORT_BUG ]
|
||||
];
|
||||
1186
build/node_modules/ssh2-streams/lib/jsbn.js
generated
vendored
Normal file
1186
build/node_modules/ssh2-streams/lib/jsbn.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
239
build/node_modules/ssh2-streams/lib/keyParser.js
generated
vendored
Normal file
239
build/node_modules/ssh2-streams/lib/keyParser.js
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
// TODO:
|
||||
// * handle multi-line header values (OpenSSH)?
|
||||
// * more thorough validation?
|
||||
|
||||
var utils;
|
||||
var Ber = require('asn1').Ber;
|
||||
var semver = require('semver');
|
||||
|
||||
var RE_PPK = /^PuTTY-User-Key-File-2: ssh-(rsa|dss)\r?\nEncryption: (aes256-cbc|none)\r?\nComment: ([^\r\n]*)\r?\nPublic-Lines: \d+\r?\n([\s\S]+?)\r?\nPrivate-Lines: \d+\r?\n([\s\S]+?)\r?\nPrivate-MAC: ([^\r\n]+)/;
|
||||
var RE_HEADER_OPENSSH_PRIV = /^-----BEGIN (RSA|DSA|EC) PRIVATE KEY-----$/i;
|
||||
var RE_FOOTER_OPENSSH_PRIV = /^-----END (?:RSA|DSA|EC) PRIVATE KEY-----$/i;
|
||||
var RE_HEADER_OPENSSH_PUB = /^((?:(?:ssh-(rsa|dss))|ecdsa-sha2-nistp(256|384|521))(?:-cert-v0[01]@openssh.com)?) ([A-Z0-9a-z\/+=]+)(?:$|\s+([\S].*)?)$/i;
|
||||
var RE_HEADER_RFC4716_PUB = /^---- BEGIN SSH2 PUBLIC KEY ----$/i;
|
||||
var RE_FOOTER_RFC4716_PUB = /^---- END SSH2 PUBLIC KEY ----$/i;
|
||||
var RE_HEADER_OPENSSH = /^([^:]+):\s*([\S].*)?$/i;
|
||||
var RE_HEADER_RFC4716 = /^([^:]+): (.*)?$/i;
|
||||
|
||||
module.exports = function(data) {
|
||||
if (Buffer.isBuffer(data))
|
||||
data = data.toString('utf8');
|
||||
else if (typeof data !== 'string')
|
||||
return new Error('Key data must be a Buffer or string');
|
||||
|
||||
var ret = {
|
||||
fulltype: undefined,
|
||||
type: undefined,
|
||||
curve: undefined,
|
||||
extra: undefined,
|
||||
comment: undefined,
|
||||
encryption: undefined,
|
||||
private: undefined,
|
||||
privateOrig: undefined,
|
||||
public: undefined,
|
||||
publicOrig: undefined
|
||||
};
|
||||
var m;
|
||||
var i;
|
||||
var len;
|
||||
|
||||
data = data.trim().split(/\r\n|\n/);
|
||||
|
||||
while (!data[0].length)
|
||||
data.shift();
|
||||
while (!data.slice(-1)[0].length)
|
||||
data.pop();
|
||||
|
||||
var orig = data.join('\n');
|
||||
|
||||
if ((m = RE_HEADER_OPENSSH_PRIV.exec(data[0]))
|
||||
&& RE_FOOTER_OPENSSH_PRIV.test(data.slice(-1))) {
|
||||
// OpenSSH private key
|
||||
var keyType = m[1].toLowerCase();
|
||||
if (keyType === 'dsa')
|
||||
keyType = 'dss';
|
||||
|
||||
if (keyType === 'ec' && semver.lt(process.version, '5.2.0')) {
|
||||
return new Error(
|
||||
'EC private keys are not supported in this version of node'
|
||||
);
|
||||
}
|
||||
|
||||
if (!RE_HEADER_OPENSSH.test(data[1])) {
|
||||
// unencrypted, no headers
|
||||
var privData = new Buffer(data.slice(1, -1).join(''), 'base64');
|
||||
if (keyType !== 'ec') {
|
||||
ret.fulltype = 'ssh-' + keyType;
|
||||
} else {
|
||||
// ECDSA
|
||||
var asnReader = new Ber.Reader(privData);
|
||||
asnReader.readSequence();
|
||||
asnReader.readInt();
|
||||
asnReader.readString(Ber.OctetString, true);
|
||||
asnReader.readByte(); // Skip "complex" context type byte
|
||||
var offset = asnReader.readLength(); // Skip context length
|
||||
if (offset !== null) {
|
||||
asnReader._offset = offset;
|
||||
switch (asnReader.readOID()) {
|
||||
case '1.2.840.10045.3.1.7':
|
||||
// prime256v1/secp256r1
|
||||
ret.fulltype = 'ecdsa-sha2-nistp256';
|
||||
break;
|
||||
case '1.3.132.0.34':
|
||||
// secp384r1
|
||||
ret.fulltype = 'ecdsa-sha2-nistp384';
|
||||
break;
|
||||
case '1.3.132.0.35':
|
||||
// secp521r1
|
||||
ret.fulltype = 'ecdsa-sha2-nistp521';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret.fulltype === undefined)
|
||||
return new Error('Unsupported EC private key type');
|
||||
}
|
||||
ret.private = privData;
|
||||
} else {
|
||||
// possibly encrypted, headers
|
||||
for (i = 1, len = data.length; i < len; ++i) {
|
||||
m = RE_HEADER_OPENSSH.exec(data[i]);
|
||||
if (m) {
|
||||
m[1] = m[1].toLowerCase();
|
||||
if (m[1] === 'dek-info') {
|
||||
m[2] = m[2].split(',');
|
||||
ret.encryption = m[2][0].toLowerCase();
|
||||
if (m[2].length > 1)
|
||||
ret.extra = m[2].slice(1);
|
||||
}
|
||||
} else if (data[i].length)
|
||||
break;
|
||||
}
|
||||
ret.private = new Buffer(data.slice(i, -1).join(''), 'base64');
|
||||
}
|
||||
ret.type = keyType;
|
||||
ret.privateOrig = new Buffer(orig);
|
||||
} else if (m = RE_HEADER_OPENSSH_PUB.exec(data[0])) {
|
||||
// OpenSSH public key
|
||||
ret.fulltype = m[1];
|
||||
ret.type = (m[2] || 'ec').toLowerCase();
|
||||
ret.public = new Buffer(m[4], 'base64');
|
||||
ret.publicOrig = new Buffer(orig);
|
||||
ret.comment = m[5];
|
||||
if (m[3]) // ECDSA only
|
||||
ret.curve = 'nistp' + m[3];
|
||||
} else if (RE_HEADER_RFC4716_PUB.test(data[0])
|
||||
&& RE_FOOTER_RFC4716_PUB.test(data.slice(-1))) {
|
||||
if (data[1].indexOf(': ') === -1) {
|
||||
// no headers
|
||||
ret.public = new Buffer(data.slice(1, -1).join(''), 'base64');
|
||||
} else {
|
||||
// headers
|
||||
for (i = 1, len = data.length; i < len; ++i) {
|
||||
if (data[i].indexOf(': ') === -1) {
|
||||
if (data[i].length)
|
||||
break; // start of key data
|
||||
else
|
||||
continue; // empty line
|
||||
}
|
||||
while (data[i].substr(-1) === '\\') {
|
||||
if (i + 1 < len) {
|
||||
data[i] = data[i].slice(0, -1) + data[i + 1];
|
||||
data.splice(i + 1, 1);
|
||||
--len;
|
||||
} else
|
||||
return new Error('RFC4716 public key missing header continuation line');
|
||||
}
|
||||
m = RE_HEADER_RFC4716.exec(data[i]);
|
||||
if (m) {
|
||||
m[1] = m[1].toLowerCase();
|
||||
if (m[1] === 'comment') {
|
||||
ret.comment = m[2] || '';
|
||||
if (ret.comment[0] === '"' && ret.comment.substr(-1) === '"')
|
||||
ret.comment = ret.comment.slice(1, -1);
|
||||
}
|
||||
} else
|
||||
return new Error('RFC4716 public key invalid header line');
|
||||
}
|
||||
ret.public = new Buffer(data.slice(i, -1).join(''), 'base64');
|
||||
}
|
||||
len = ret.public.readUInt32BE(0, true);
|
||||
var fulltype = ret.public.toString('ascii', 4, 4 + len);
|
||||
ret.fulltype = fulltype;
|
||||
if (fulltype === 'ssh-dss')
|
||||
ret.type = 'dss';
|
||||
else if (fulltype === 'ssh-rsa')
|
||||
ret.type = 'rsa';
|
||||
else
|
||||
return new Error('Unsupported RFC4716 public key type: ' + fulltype);
|
||||
ret.public = ret.public.slice(11);
|
||||
ret.publicOrig = new Buffer(orig);
|
||||
} else if (m = RE_PPK.exec(orig)) {
|
||||
// m[1] = short type
|
||||
// m[2] = encryption type
|
||||
// m[3] = comment
|
||||
// m[4] = base64-encoded public key data:
|
||||
// for "ssh-rsa":
|
||||
// string "ssh-rsa"
|
||||
// mpint e (public exponent)
|
||||
// mpint n (modulus)
|
||||
// for "ssh-dss":
|
||||
// string "ssh-dss"
|
||||
// mpint p (modulus)
|
||||
// mpint q (prime)
|
||||
// mpint g (base number)
|
||||
// mpint y (public key parameter: g^x mod p)
|
||||
// m[5] = base64-encoded private key data:
|
||||
// for "ssh-rsa":
|
||||
// mpint d (private exponent)
|
||||
// mpint p (prime 1)
|
||||
// mpint q (prime 2)
|
||||
// mpint iqmp ([inverse of q] mod p)
|
||||
// for "ssh-dss":
|
||||
// mpint x (private key parameter)
|
||||
// m[6] = SHA1 HMAC over:
|
||||
// string name of algorithm ("ssh-dss", "ssh-rsa")
|
||||
// string encryption type
|
||||
// string comment
|
||||
// string public key data
|
||||
// string private-plaintext (including the final padding)
|
||||
|
||||
// avoid cyclic require by requiring on first use
|
||||
if (!utils)
|
||||
utils = require('./utils');
|
||||
|
||||
ret.ppk = true;
|
||||
ret.type = m[1];
|
||||
ret.fulltype = 'ssh-' + m[1];
|
||||
if (m[2] !== 'none')
|
||||
ret.encryption = m[2];
|
||||
ret.comment = m[3];
|
||||
|
||||
ret.public = new Buffer(m[4].replace(/\r?\n/g, ''), 'base64');
|
||||
var privateKey = new Buffer(m[5].replace(/\r?\n/g, ''), 'base64');
|
||||
|
||||
ret.privateMAC = m[6].replace(/\r?\n/g, '');
|
||||
|
||||
// automatically verify private key MAC if we don't need to wait for
|
||||
// decryption
|
||||
if (!ret.encryption) {
|
||||
var valid = utils.verifyPPKMAC(ret, undefined, privateKey);
|
||||
if (!valid)
|
||||
throw new Error('PPK MAC mismatch');
|
||||
}
|
||||
|
||||
// generate a PEM encoded version of the public key
|
||||
var pubkey = utils.genPublicKey(ret);
|
||||
ret.public = pubkey.public;
|
||||
ret.publicOrig = pubkey.publicOrig;
|
||||
|
||||
ret.private = privateKey;
|
||||
|
||||
// automatically convert private key data to OpenSSL format (including PEM)
|
||||
// if we don't need to wait for decryption
|
||||
if (!ret.encryption)
|
||||
utils.convertPPKPrivate(ret);
|
||||
} else
|
||||
return new Error('Unsupported key format');
|
||||
|
||||
return ret;
|
||||
};
|
||||
2967
build/node_modules/ssh2-streams/lib/sftp.js
generated
vendored
Normal file
2967
build/node_modules/ssh2-streams/lib/sftp.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5403
build/node_modules/ssh2-streams/lib/ssh.js
generated
vendored
Normal file
5403
build/node_modules/ssh2-streams/lib/ssh.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
817
build/node_modules/ssh2-streams/lib/utils.js
generated
vendored
Normal file
817
build/node_modules/ssh2-streams/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,817 @@
|
||||
var crypto = require('crypto');
|
||||
|
||||
var Ber = require('asn1').Ber;
|
||||
var BigInteger = require('./jsbn'); // only for converting PPK -> OpenSSL format
|
||||
|
||||
var SSH_TO_OPENSSL = require('./constants').SSH_TO_OPENSSL;
|
||||
|
||||
var RE_STREAM = /^arcfour/i;
|
||||
var RE_KEY_LEN = /(.{64})/g;
|
||||
// XXX the value of 2400 from dropbear is only for certain strings, not all
|
||||
// strings. for example the list strings used during handshakes
|
||||
var MAX_STRING_LEN = Infinity;//2400; // taken from dropbear
|
||||
var PPK_IV = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
module.exports = {
|
||||
iv_inc: iv_inc,
|
||||
isStreamCipher: isStreamCipher,
|
||||
readInt: readInt,
|
||||
readString: readString,
|
||||
parseKey: require('./keyParser'),
|
||||
genPublicKey: genPublicKey,
|
||||
convertPPKPrivate: convertPPKPrivate,
|
||||
verifyPPKMAC: verifyPPKMAC,
|
||||
decryptKey: decryptKey,
|
||||
DSASigBERToBare: DSASigBERToBare,
|
||||
DSASigBareToBER: DSASigBareToBER,
|
||||
ECDSASigASN1ToSSH: ECDSASigASN1ToSSH,
|
||||
ECDSASigSSHToASN1: ECDSASigSSHToASN1,
|
||||
RSAKeySSHToASN1: RSAKeySSHToASN1,
|
||||
DSAKeySSHToASN1: DSAKeySSHToASN1,
|
||||
ECDSAKeySSHToASN1: ECDSAKeySSHToASN1
|
||||
};
|
||||
|
||||
function iv_inc(iv) {
|
||||
var n = 12;
|
||||
var c = 0;
|
||||
do {
|
||||
--n;
|
||||
c = iv[n];
|
||||
if (c === 255)
|
||||
iv[n] = 0;
|
||||
else {
|
||||
iv[n] = ++c;
|
||||
return;
|
||||
}
|
||||
} while (n > 4);
|
||||
}
|
||||
|
||||
function isStreamCipher(name) {
|
||||
return RE_STREAM.test(name);
|
||||
}
|
||||
|
||||
function readInt(buffer, start, stream, cb) {
|
||||
var bufferLen = buffer.length;
|
||||
if (start < 0 || start >= bufferLen || (bufferLen - start) < 4) {
|
||||
stream && stream._cleanup(cb);
|
||||
return false;
|
||||
}
|
||||
|
||||
return buffer.readUInt32BE(start, true);
|
||||
}
|
||||
|
||||
function DSASigBERToBare(signature) {
|
||||
if (signature.length <= 40)
|
||||
return signature;
|
||||
// This is a quick and dirty way to get from BER encoded r and s that
|
||||
// OpenSSL gives us, to just the bare values back to back (40 bytes
|
||||
// total) like OpenSSH (and possibly others) are expecting
|
||||
var asnReader = new Ber.Reader(signature);
|
||||
asnReader.readSequence();
|
||||
var r = asnReader.readString(Ber.Integer, true);
|
||||
var s = asnReader.readString(Ber.Integer, true);
|
||||
var rOffset = 0;
|
||||
var sOffset = 0;
|
||||
if (r.length < 20) {
|
||||
var rNew = new Buffer(20);
|
||||
r.copy(rNew, 1);
|
||||
r = rNew;
|
||||
r[0] = 0;
|
||||
}
|
||||
if (s.length < 20) {
|
||||
var sNew = new Buffer(20);
|
||||
s.copy(sNew, 1);
|
||||
s = sNew;
|
||||
s[0] = 0;
|
||||
}
|
||||
if (r.length > 20 && r[0] === 0x00)
|
||||
rOffset = 1;
|
||||
if (s.length > 20 && s[0] === 0x00)
|
||||
sOffset = 1;
|
||||
var newSig = new Buffer((r.length - rOffset) + (s.length - sOffset));
|
||||
r.copy(newSig, 0, rOffset);
|
||||
s.copy(newSig, r.length - rOffset, sOffset);
|
||||
return newSig;
|
||||
}
|
||||
|
||||
function DSASigBareToBER(signature) {
|
||||
if (signature.length > 40)
|
||||
return signature;
|
||||
// Change bare signature r and s values to ASN.1 BER values for OpenSSL
|
||||
var asnWriter = new Ber.Writer();
|
||||
asnWriter.startSequence();
|
||||
var r = signature.slice(0, 20);
|
||||
var s = signature.slice(20);
|
||||
if (r[0] & 0x80) {
|
||||
var rNew = new Buffer(21);
|
||||
rNew[0] = 0x00;
|
||||
r.copy(rNew, 1);
|
||||
r = rNew;
|
||||
} else if (r[0] === 0x00 && !(r[1] & 0x80)) {
|
||||
r = r.slice(1);
|
||||
}
|
||||
if (s[0] & 0x80) {
|
||||
var sNew = new Buffer(21);
|
||||
sNew[0] = 0x00;
|
||||
s.copy(sNew, 1);
|
||||
s = sNew;
|
||||
} else if (s[0] === 0x00 && !(s[1] & 0x80)) {
|
||||
s = s.slice(1);
|
||||
}
|
||||
asnWriter.writeBuffer(r, Ber.Integer);
|
||||
asnWriter.writeBuffer(s, Ber.Integer);
|
||||
asnWriter.endSequence();
|
||||
return asnWriter.buffer;
|
||||
}
|
||||
|
||||
function ECDSASigASN1ToSSH(signature) {
|
||||
if (signature[0] === 0x00)
|
||||
return signature;
|
||||
// Convert SSH signature parameters to ASN.1 BER values for OpenSSL
|
||||
var asnReader = new Ber.Reader(signature);
|
||||
asnReader.readSequence();
|
||||
var r = asnReader.readString(Ber.Integer, true);
|
||||
var s = asnReader.readString(Ber.Integer, true);
|
||||
if (r === null || s === null)
|
||||
throw new Error('Invalid signature');
|
||||
var newSig = new Buffer(4 + r.length + 4 + s.length);
|
||||
newSig.writeUInt32BE(r.length, 0, true);
|
||||
r.copy(newSig, 4);
|
||||
newSig.writeUInt32BE(s.length, 4 + r.length, true);
|
||||
s.copy(newSig, 4 + 4 + r.length);
|
||||
return newSig;
|
||||
}
|
||||
|
||||
function ECDSASigSSHToASN1(signature, self, callback) {
|
||||
// Convert SSH signature parameters to ASN.1 BER values for OpenSSL
|
||||
var r = readString(signature, 0, self, callback);
|
||||
if (r === false)
|
||||
return false;
|
||||
var s = readString(signature, signature._pos, self, callback);
|
||||
if (s === false)
|
||||
return false;
|
||||
|
||||
var asnWriter = new Ber.Writer();
|
||||
asnWriter.startSequence();
|
||||
asnWriter.writeBuffer(r, Ber.Integer);
|
||||
asnWriter.writeBuffer(s, Ber.Integer);
|
||||
asnWriter.endSequence();
|
||||
return asnWriter.buffer;
|
||||
}
|
||||
|
||||
function RSAKeySSHToASN1(key, self, callback) {
|
||||
// Convert SSH key parameters to ASN.1 BER values for OpenSSL
|
||||
var e = readString(key, key._pos, self, callback);
|
||||
if (e === false)
|
||||
return false;
|
||||
var n = readString(key, key._pos, self, callback);
|
||||
if (n === false)
|
||||
return false;
|
||||
|
||||
var asnWriter = new Ber.Writer();
|
||||
asnWriter.startSequence();
|
||||
// algorithm
|
||||
asnWriter.startSequence();
|
||||
asnWriter.writeOID('1.2.840.113549.1.1.1'); // rsaEncryption
|
||||
// algorithm parameters (RSA has none)
|
||||
asnWriter.writeNull();
|
||||
asnWriter.endSequence();
|
||||
|
||||
// subjectPublicKey
|
||||
asnWriter.startSequence(Ber.BitString);
|
||||
asnWriter.writeByte(0x00);
|
||||
asnWriter.startSequence();
|
||||
asnWriter.writeBuffer(n, Ber.Integer);
|
||||
asnWriter.writeBuffer(e, Ber.Integer);
|
||||
asnWriter.endSequence();
|
||||
asnWriter.endSequence();
|
||||
asnWriter.endSequence();
|
||||
return asnWriter.buffer;
|
||||
}
|
||||
|
||||
function DSAKeySSHToASN1(key, self, callback) {
|
||||
// Convert SSH key parameters to ASN.1 BER values for OpenSSL
|
||||
var p = readString(key, key._pos, self, callback);
|
||||
if (p === false)
|
||||
return false;
|
||||
var q = readString(key, key._pos, self, callback);
|
||||
if (q === false)
|
||||
return false;
|
||||
var g = readString(key, key._pos, self, callback);
|
||||
if (g === false)
|
||||
return false;
|
||||
var y = readString(key, key._pos, self, callback);
|
||||
if (y === false)
|
||||
return false;
|
||||
|
||||
var asnWriter = new Ber.Writer();
|
||||
asnWriter.startSequence();
|
||||
// algorithm
|
||||
asnWriter.startSequence();
|
||||
asnWriter.writeOID('1.2.840.10040.4.1'); // id-dsa
|
||||
// algorithm parameters
|
||||
asnWriter.startSequence();
|
||||
asnWriter.writeBuffer(p, Ber.Integer);
|
||||
asnWriter.writeBuffer(q, Ber.Integer);
|
||||
asnWriter.writeBuffer(g, Ber.Integer);
|
||||
asnWriter.endSequence();
|
||||
asnWriter.endSequence();
|
||||
|
||||
// subjectPublicKey
|
||||
asnWriter.startSequence(Ber.BitString);
|
||||
asnWriter.writeByte(0x00);
|
||||
asnWriter.writeBuffer(y, Ber.Integer);
|
||||
asnWriter.endSequence();
|
||||
asnWriter.endSequence();
|
||||
return asnWriter.buffer;
|
||||
}
|
||||
|
||||
function ECDSAKeySSHToASN1(key, self, callback) {
|
||||
// Convert SSH key parameters to ASN.1 BER values for OpenSSL
|
||||
var curve = readString(key, key._pos, self, callback);
|
||||
if (curve === false)
|
||||
return false;
|
||||
var Q = readString(key, key._pos, self, callback);
|
||||
if (Q === false)
|
||||
return false;
|
||||
|
||||
var ecCurveOID;
|
||||
switch (curve.toString('ascii')) {
|
||||
case 'nistp256':
|
||||
// prime256v1/secp256r1
|
||||
ecCurveOID = '1.2.840.10045.3.1.7';
|
||||
break;
|
||||
case 'nistp384':
|
||||
// secp384r1
|
||||
ecCurveOID = '1.3.132.0.34';
|
||||
break;
|
||||
case 'nistp521':
|
||||
// secp521r1
|
||||
ecCurveOID = '1.3.132.0.35';
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
var asnWriter = new Ber.Writer();
|
||||
asnWriter.startSequence();
|
||||
// algorithm
|
||||
asnWriter.startSequence();
|
||||
asnWriter.writeOID('1.2.840.10045.2.1'); // id-ecPublicKey
|
||||
// algorithm parameters (namedCurve)
|
||||
asnWriter.writeOID(ecCurveOID);
|
||||
asnWriter.endSequence();
|
||||
|
||||
// subjectPublicKey
|
||||
asnWriter.startSequence(Ber.BitString);
|
||||
asnWriter.writeByte(0x00);
|
||||
// XXX: hack to write a raw buffer without a tag -- yuck
|
||||
asnWriter._ensure(Q.length);
|
||||
Q.copy(asnWriter._buf, asnWriter._offset, 0, Q.length);
|
||||
asnWriter._offset += Q.length;
|
||||
// end hack
|
||||
asnWriter.endSequence();
|
||||
asnWriter.endSequence();
|
||||
return asnWriter.buffer;
|
||||
}
|
||||
|
||||
function decryptKey(keyInfo, passphrase) {
|
||||
if (keyInfo._decrypted || !keyInfo.encryption)
|
||||
return;
|
||||
|
||||
var keylen = 0;
|
||||
var key;
|
||||
var iv;
|
||||
var dc;
|
||||
|
||||
keyInfo.encryption = (SSH_TO_OPENSSL[keyInfo.encryption]
|
||||
|| keyInfo.encryption);
|
||||
switch (keyInfo.encryption) {
|
||||
case 'aes-256-cbc':
|
||||
case 'aes-256-ctr':
|
||||
keylen = 32;
|
||||
break;
|
||||
case 'des-ede3-cbc':
|
||||
case 'des-ede3':
|
||||
case 'aes-192-cbc':
|
||||
case 'aes-192-ctr':
|
||||
keylen = 24;
|
||||
break;
|
||||
case 'aes-128-cbc':
|
||||
case 'aes-128-ctr':
|
||||
case 'cast-cbc':
|
||||
case 'bf-cbc':
|
||||
keylen = 16;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported cipher for encrypted key: '
|
||||
+ keyInfo.encryption);
|
||||
}
|
||||
|
||||
if (keyInfo.ppk) {
|
||||
iv = PPK_IV;
|
||||
|
||||
key = Buffer.concat([
|
||||
crypto.createHash('sha1')
|
||||
.update('\x00\x00\x00\x00' + passphrase, 'utf8')
|
||||
.digest(),
|
||||
crypto.createHash('sha1')
|
||||
.update('\x00\x00\x00\x01' + passphrase, 'utf8')
|
||||
.digest()
|
||||
]);
|
||||
key = key.slice(0, keylen);
|
||||
} else {
|
||||
iv = new Buffer(keyInfo.extra[0], 'hex');
|
||||
|
||||
key = crypto.createHash('md5')
|
||||
.update(passphrase, 'utf8')
|
||||
.update(iv.slice(0, 8))
|
||||
.digest();
|
||||
|
||||
while (keylen > key.length) {
|
||||
key = Buffer.concat([
|
||||
key,
|
||||
(crypto.createHash('md5')
|
||||
.update(key)
|
||||
.update(passphrase, 'utf8')
|
||||
.update(iv)
|
||||
.digest()).slice(0, 8)
|
||||
]);
|
||||
}
|
||||
if (key.length > keylen)
|
||||
key = key.slice(0, keylen);
|
||||
}
|
||||
|
||||
dc = crypto.createDecipheriv(keyInfo.encryption, key, iv);
|
||||
dc.setAutoPadding(false);
|
||||
keyInfo.private = Buffer.concat([ dc.update(keyInfo.private), dc.final() ]);
|
||||
|
||||
keyInfo._decrypted = true;
|
||||
|
||||
if (keyInfo.privateOrig) {
|
||||
// Update our original base64-encoded version of the private key
|
||||
var orig = keyInfo.privateOrig.toString('utf8');
|
||||
var newOrig = /^(.+(?:\r\n|\n))/.exec(orig)[1];
|
||||
var b64key = keyInfo.private.toString('base64');
|
||||
|
||||
newOrig += b64key.match(/.{1,70}/g).join('\n');
|
||||
newOrig += /((?:\r\n|\n).+)$/.exec(orig)[1];
|
||||
|
||||
keyInfo.privateOrig = newOrig;
|
||||
} else if (keyInfo.ppk) {
|
||||
var valid = verifyPPKMAC(keyInfo, passphrase, keyInfo.private);
|
||||
if (!valid)
|
||||
throw new Error('PPK MAC mismatch');
|
||||
// Automatically convert private key data to OpenSSL format
|
||||
// (including PEM)
|
||||
convertPPKPrivate(keyInfo);
|
||||
}
|
||||
|
||||
// Fill in full key type
|
||||
// TODO: make DRY, we do this also in keyParser
|
||||
if (keyInfo.type !== 'ec') {
|
||||
keyInfo.fulltype = 'ssh-' + keyInfo.type;
|
||||
} else {
|
||||
// ECDSA
|
||||
var asnReader = new Ber.Reader(keyInfo.private);
|
||||
asnReader.readSequence();
|
||||
asnReader.readInt();
|
||||
asnReader.readString(Ber.OctetString, true);
|
||||
asnReader.readByte(); // Skip "complex" context type byte
|
||||
var offset = asnReader.readLength(); // Skip context length
|
||||
if (offset !== null) {
|
||||
asnReader._offset = offset;
|
||||
switch (asnReader.readOID()) {
|
||||
case '1.2.840.10045.3.1.7':
|
||||
// prime256v1/secp256r1
|
||||
keyInfo.fulltype = 'ecdsa-sha2-nistp256';
|
||||
break;
|
||||
case '1.3.132.0.34':
|
||||
// secp384r1
|
||||
keyInfo.fulltype = 'ecdsa-sha2-nistp384';
|
||||
break;
|
||||
case '1.3.132.0.35':
|
||||
// secp521r1
|
||||
keyInfo.fulltype = 'ecdsa-sha2-nistp521';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keyInfo.fulltype === undefined)
|
||||
return new Error('Unsupported EC private key type');
|
||||
}
|
||||
}
|
||||
|
||||
function genPublicKey(keyInfo) {
|
||||
var publicKey;
|
||||
var i;
|
||||
|
||||
// RSA
|
||||
var n;
|
||||
var e;
|
||||
|
||||
// DSA
|
||||
var p;
|
||||
var q;
|
||||
var g;
|
||||
var y;
|
||||
|
||||
// ECDSA
|
||||
var d;
|
||||
var Q;
|
||||
var ecCurveOID;
|
||||
var ecCurveName;
|
||||
|
||||
if (keyInfo.private) {
|
||||
// parsing private key in ASN.1 format in order to generate a public key
|
||||
var privKey = keyInfo.private;
|
||||
var asnReader = new Ber.Reader(privKey);
|
||||
var errMsg;
|
||||
|
||||
if (asnReader.readSequence() === null) {
|
||||
errMsg = 'Malformed private key (expected sequence)';
|
||||
if (keyInfo._decrypted)
|
||||
errMsg += '. Bad passphrase?';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
// version (ignored)
|
||||
if (asnReader.readInt() === null) {
|
||||
errMsg = 'Malformed private key (expected version)';
|
||||
if (keyInfo._decrypted)
|
||||
errMsg += '. Bad passphrase?';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
if (keyInfo.type === 'rsa') {
|
||||
// modulus (n) -- integer
|
||||
n = asnReader.readString(Ber.Integer, true);
|
||||
if (n === null) {
|
||||
errMsg = 'Malformed private key (expected RSA n value)';
|
||||
if (keyInfo._decrypted)
|
||||
errMsg += '. Bad passphrase?';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
// public exponent (e) -- integer
|
||||
e = asnReader.readString(Ber.Integer, true);
|
||||
if (e === null) {
|
||||
errMsg = 'Malformed private key (expected RSA e value)';
|
||||
if (keyInfo._decrypted)
|
||||
errMsg += '. Bad passphrase?';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
publicKey = new Buffer(4 + 7 // ssh-rsa
|
||||
+ 4 + n.length
|
||||
+ 4 + e.length);
|
||||
|
||||
publicKey.writeUInt32BE(7, 0, true);
|
||||
publicKey.write('ssh-rsa', 4, 7, 'ascii');
|
||||
|
||||
i = 4 + 7;
|
||||
publicKey.writeUInt32BE(e.length, i, true);
|
||||
e.copy(publicKey, i += 4);
|
||||
|
||||
publicKey.writeUInt32BE(n.length, i += e.length, true);
|
||||
n.copy(publicKey, i += 4);
|
||||
} else if (keyInfo.type === 'dss') { // DSA
|
||||
// prime (p) -- integer
|
||||
p = asnReader.readString(Ber.Integer, true);
|
||||
if (p === null) {
|
||||
errMsg = 'Malformed private key (expected DSA p value)';
|
||||
if (keyInfo._decrypted)
|
||||
errMsg += '. Bad passphrase?';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
// group order (q) -- integer
|
||||
q = asnReader.readString(Ber.Integer, true);
|
||||
if (q === null) {
|
||||
errMsg = 'Malformed private key (expected DSA q value)';
|
||||
if (keyInfo._decrypted)
|
||||
errMsg += '. Bad passphrase?';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
// group generator (g) -- integer
|
||||
g = asnReader.readString(Ber.Integer, true);
|
||||
if (g === null) {
|
||||
errMsg = 'Malformed private key (expected DSA g value)';
|
||||
if (keyInfo._decrypted)
|
||||
errMsg += '. Bad passphrase?';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
// public key value (y) -- integer
|
||||
y = asnReader.readString(Ber.Integer, true);
|
||||
if (y === null) {
|
||||
errMsg = 'Malformed private key (expected DSA y value)';
|
||||
if (keyInfo._decrypted)
|
||||
errMsg += '. Bad passphrase?';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
publicKey = new Buffer(4 + 7 // ssh-dss
|
||||
+ 4 + p.length
|
||||
+ 4 + q.length
|
||||
+ 4 + g.length
|
||||
+ 4 + y.length);
|
||||
|
||||
publicKey.writeUInt32BE(7, 0, true);
|
||||
publicKey.write('ssh-dss', 4, 7, 'ascii');
|
||||
|
||||
i = 4 + 7;
|
||||
publicKey.writeUInt32BE(p.length, i, true);
|
||||
p.copy(publicKey, i += 4);
|
||||
|
||||
publicKey.writeUInt32BE(q.length, i += p.length, true);
|
||||
q.copy(publicKey, i += 4);
|
||||
|
||||
publicKey.writeUInt32BE(g.length, i += q.length, true);
|
||||
g.copy(publicKey, i += 4);
|
||||
|
||||
publicKey.writeUInt32BE(y.length, i += g.length, true);
|
||||
y.copy(publicKey, i += 4);
|
||||
} else { // ECDSA
|
||||
d = asnReader.readString(Ber.OctetString, true);
|
||||
if (d === null)
|
||||
throw new Error('Malformed private key (expected ECDSA private key)');
|
||||
asnReader.readByte(); // Skip "complex" context type byte
|
||||
var offset = asnReader.readLength(); // Skip context length
|
||||
if (offset === null)
|
||||
throw new Error('Malformed private key (expected ECDSA context value)');
|
||||
asnReader._offset = offset;
|
||||
ecCurveOID = asnReader.readOID();
|
||||
if (ecCurveOID === null)
|
||||
throw new Error('Malformed private key (expected ECDSA curve)');
|
||||
var tempECDH;
|
||||
switch (ecCurveOID) {
|
||||
case '1.2.840.10045.3.1.7':
|
||||
// prime256v1/secp256r1
|
||||
keyInfo.curve = ecCurveName = 'nistp256';
|
||||
tempECDH = crypto.createECDH('prime256v1');
|
||||
break;
|
||||
case '1.3.132.0.34':
|
||||
// secp384r1
|
||||
keyInfo.curve = ecCurveName = 'nistp384';
|
||||
tempECDH = crypto.createECDH('secp384r1');
|
||||
break;
|
||||
case '1.3.132.0.35':
|
||||
// secp521r1
|
||||
keyInfo.curve = ecCurveName = 'nistp521';
|
||||
tempECDH = crypto.createECDH('secp521r1');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Malformed private key (unsupported EC curve)');
|
||||
}
|
||||
tempECDH.setPrivateKey(d);
|
||||
Q = tempECDH.getPublicKey();
|
||||
|
||||
publicKey = new Buffer(4 + 19 // ecdsa-sha2-<curve name>
|
||||
+ 4 + 8 // <curve name>
|
||||
+ 4 + Q.length);
|
||||
|
||||
publicKey.writeUInt32BE(19, 0, true);
|
||||
publicKey.write('ecdsa-sha2-' + ecCurveName, 4, 19, 'ascii');
|
||||
|
||||
publicKey.writeUInt32BE(8, 23, true);
|
||||
publicKey.write(ecCurveName, 27, 8, 'ascii');
|
||||
|
||||
publicKey.writeUInt32BE(Q.length, 35, true);
|
||||
Q.copy(publicKey, 39);
|
||||
}
|
||||
} else if (keyInfo.public) {
|
||||
publicKey = keyInfo.public;
|
||||
if (keyInfo.type === 'ec') {
|
||||
// TODO: support adding ecdsa-* prefix
|
||||
ecCurveName = keyInfo.curve;
|
||||
} else if (publicKey[0] !== 0
|
||||
// check for missing ssh-{dsa,rsa} prefix
|
||||
|| publicKey[1] !== 0
|
||||
|| publicKey[2] !== 0
|
||||
|| publicKey[3] !== 7
|
||||
|| publicKey[4] !== 115
|
||||
|| publicKey[5] !== 115
|
||||
|| publicKey[6] !== 104
|
||||
|| publicKey[7] !== 45
|
||||
|| ((publicKey[8] !== 114
|
||||
|| publicKey[9] !== 115
|
||||
|| publicKey[10] !== 97)
|
||||
&&
|
||||
((publicKey[8] !== 100
|
||||
|| publicKey[9] !== 115
|
||||
|| publicKey[10] !== 115)))) {
|
||||
var newPK = new Buffer(4 + 7 + publicKey.length);
|
||||
publicKey.copy(newPK, 11);
|
||||
newPK.writeUInt32BE(7, 0, true);
|
||||
if (keyInfo.type === 'rsa')
|
||||
newPK.write('ssh-rsa', 4, 7, 'ascii');
|
||||
else
|
||||
newPK.write('ssh-dss', 4, 7, 'ascii');
|
||||
publicKey = newPK;
|
||||
}
|
||||
} else
|
||||
throw new Error('Missing data generated by parseKey()');
|
||||
|
||||
// generate a public key format for use with OpenSSL
|
||||
|
||||
i = 4 + 7;
|
||||
|
||||
var fulltype;
|
||||
var asn1KeyBuf;
|
||||
if (keyInfo.type === 'rsa') {
|
||||
fulltype = 'ssh-rsa';
|
||||
asn1KeyBuf = RSAKeySSHToASN1(publicKey.slice(4 + 7));
|
||||
} else if (keyInfo.type === 'dss') {
|
||||
fulltype = 'ssh-dss';
|
||||
asn1KeyBuf = DSAKeySSHToASN1(publicKey.slice(4 + 7));
|
||||
} else { // ECDSA
|
||||
fulltype = 'ecdsa-sha2-' + ecCurveName;
|
||||
asn1KeyBuf = ECDSAKeySSHToASN1(publicKey.slice(4 + 19));
|
||||
}
|
||||
|
||||
if (!asn1KeyBuf)
|
||||
throw new Error('Invalid SSH-formatted public key');
|
||||
|
||||
var b64key = asn1KeyBuf.toString('base64').replace(RE_KEY_LEN, '$1\n');
|
||||
var fullkey = '-----BEGIN PUBLIC KEY-----\n'
|
||||
+ b64key
|
||||
+ (b64key[b64key.length - 1] === '\n' ? '' : '\n')
|
||||
+ '-----END PUBLIC KEY-----';
|
||||
|
||||
return {
|
||||
type: keyInfo.type,
|
||||
fulltype: fulltype,
|
||||
curve: ecCurveName,
|
||||
public: publicKey,
|
||||
publicOrig: new Buffer(fullkey)
|
||||
};
|
||||
}
|
||||
|
||||
function verifyPPKMAC(keyInfo, passphrase, privateKey) {
|
||||
if (keyInfo._macresult !== undefined)
|
||||
return keyInfo._macresult;
|
||||
else if (!keyInfo.ppk)
|
||||
throw new Error("Key isn't a PPK");
|
||||
else if (!keyInfo.privateMAC)
|
||||
throw new Error('Missing MAC');
|
||||
else if (!privateKey)
|
||||
throw new Error('Missing raw private key data');
|
||||
else if (keyInfo.encryption && typeof passphrase !== 'string')
|
||||
throw new Error('Missing passphrase for encrypted PPK');
|
||||
else if (keyInfo.encryption && !keyInfo._decrypted)
|
||||
throw new Error('PPK must be decrypted before verifying MAC');
|
||||
|
||||
var mac = keyInfo.privateMAC;
|
||||
var typelen = keyInfo.fulltype.length;
|
||||
// encryption algorithm is converted at this point for use with OpenSSL,
|
||||
// so we need to use the original value so that the MAC is calculated
|
||||
// correctly
|
||||
var enc = (keyInfo.encryption ? 'aes256-cbc' : 'none');
|
||||
var enclen = enc.length;
|
||||
var commlen = Buffer.byteLength(keyInfo.comment);
|
||||
var pub = keyInfo.public;
|
||||
var publen = pub.length;
|
||||
var privlen = privateKey.length;
|
||||
var macdata = new Buffer(4 + typelen
|
||||
+ 4 + enclen
|
||||
+ 4 + commlen
|
||||
+ 4 + publen
|
||||
+ 4 + privlen);
|
||||
var p = 0;
|
||||
|
||||
macdata.writeUInt32BE(typelen, p, true);
|
||||
macdata.write(keyInfo.fulltype, p += 4, typelen, 'ascii');
|
||||
macdata.writeUInt32BE(enclen, p += typelen, true);
|
||||
macdata.write(enc, p += 4, enclen, 'ascii');
|
||||
macdata.writeUInt32BE(commlen, p += enclen, true);
|
||||
macdata.write(keyInfo.comment, p += 4, commlen, 'utf8');
|
||||
macdata.writeUInt32BE(publen, p += commlen, true);
|
||||
pub.copy(macdata, p += 4);
|
||||
macdata.writeUInt32BE(privlen, p += publen, true);
|
||||
privateKey.copy(macdata, p += 4);
|
||||
|
||||
if (typeof passphrase !== 'string')
|
||||
passphrase = '';
|
||||
|
||||
var mackey = crypto.createHash('sha1')
|
||||
.update('putty-private-key-file-mac-key', 'ascii')
|
||||
.update(passphrase, 'utf8')
|
||||
.digest();
|
||||
|
||||
var calcMAC = crypto.createHmac('sha1', mackey)
|
||||
.update(macdata)
|
||||
.digest('hex');
|
||||
|
||||
return (keyInfo._macresult = (calcMAC === mac));
|
||||
}
|
||||
|
||||
function convertPPKPrivate(keyInfo) {
|
||||
if (!keyInfo.ppk || !keyInfo.public || !keyInfo.private)
|
||||
throw new Error("Key isn't a PPK");
|
||||
else if (keyInfo._converted)
|
||||
return false;
|
||||
|
||||
var pub = keyInfo.public;
|
||||
var priv = keyInfo.private;
|
||||
var asnWriter = new Ber.Writer();
|
||||
var p;
|
||||
var q;
|
||||
|
||||
if (keyInfo.type === 'rsa') {
|
||||
var e = readString(pub, 4 + 7);
|
||||
var n = readString(pub, pub._pos);
|
||||
var d = readString(priv, 0);
|
||||
p = readString(priv, priv._pos);
|
||||
q = readString(priv, priv._pos);
|
||||
var iqmp = readString(priv, priv._pos);
|
||||
var p1 = new BigInteger(p, 256);
|
||||
var q1 = new BigInteger(q, 256);
|
||||
var dmp1 = new BigInteger(d, 256);
|
||||
var dmq1 = new BigInteger(d, 256);
|
||||
|
||||
dmp1 = new Buffer(dmp1.mod(p1.subtract(BigInteger.ONE)).toByteArray());
|
||||
dmq1 = new Buffer(dmq1.mod(q1.subtract(BigInteger.ONE)).toByteArray());
|
||||
|
||||
asnWriter.startSequence();
|
||||
asnWriter.writeInt(0x00, Ber.Integer);
|
||||
asnWriter.writeBuffer(n, Ber.Integer);
|
||||
asnWriter.writeBuffer(e, Ber.Integer);
|
||||
asnWriter.writeBuffer(d, Ber.Integer);
|
||||
asnWriter.writeBuffer(p, Ber.Integer);
|
||||
asnWriter.writeBuffer(q, Ber.Integer);
|
||||
asnWriter.writeBuffer(dmp1, Ber.Integer);
|
||||
asnWriter.writeBuffer(dmq1, Ber.Integer);
|
||||
asnWriter.writeBuffer(iqmp, Ber.Integer);
|
||||
asnWriter.endSequence();
|
||||
} else {
|
||||
p = readString(pub, 4 + 7);
|
||||
q = readString(pub, pub._pos);
|
||||
var g = readString(pub, pub._pos);
|
||||
var y = readString(pub, pub._pos);
|
||||
var x = readString(priv, 0);
|
||||
|
||||
asnWriter.startSequence();
|
||||
asnWriter.writeInt(0x00, Ber.Integer);
|
||||
asnWriter.writeBuffer(p, Ber.Integer);
|
||||
asnWriter.writeBuffer(q, Ber.Integer);
|
||||
asnWriter.writeBuffer(g, Ber.Integer);
|
||||
asnWriter.writeBuffer(y, Ber.Integer);
|
||||
asnWriter.writeBuffer(x, Ber.Integer);
|
||||
asnWriter.endSequence();
|
||||
}
|
||||
|
||||
var b64key = asnWriter.buffer.toString('base64').replace(RE_KEY_LEN, '$1\n');
|
||||
var fullkey = '-----BEGIN '
|
||||
+ (keyInfo.type === 'rsa' ? 'RSA' : 'DSA')
|
||||
+ ' PRIVATE KEY-----\n'
|
||||
+ b64key
|
||||
+ (b64key[b64key.length - 1] === '\n' ? '' : '\n')
|
||||
+ '-----END '
|
||||
+ (keyInfo.type === 'rsa' ? 'RSA' : 'DSA')
|
||||
+ ' PRIVATE KEY-----';
|
||||
|
||||
keyInfo.private = asnWriter.buffer;
|
||||
keyInfo.privateOrig = new Buffer(fullkey);
|
||||
keyInfo._converted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
function readString(buffer, start, encoding, stream, cb, maxLen) {
|
||||
if (encoding && !Buffer.isBuffer(encoding) && typeof encoding !== 'string') {
|
||||
if (typeof cb === 'number')
|
||||
maxLen = cb;
|
||||
cb = stream;
|
||||
stream = encoding;
|
||||
encoding = undefined;
|
||||
}
|
||||
|
||||
start || (start = 0);
|
||||
var bufferLen = buffer.length;
|
||||
var left = (bufferLen - start);
|
||||
var len;
|
||||
var end;
|
||||
if (start < 0 || start >= bufferLen || left < 4) {
|
||||
stream && stream._cleanup(cb);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = buffer.readUInt32BE(start, true);
|
||||
if (len > (maxLen || MAX_STRING_LEN) || left < (4 + len)) {
|
||||
stream && stream._cleanup(cb);
|
||||
return false;
|
||||
}
|
||||
|
||||
start += 4;
|
||||
end = start + len;
|
||||
buffer._pos = end;
|
||||
|
||||
if (encoding) {
|
||||
if (Buffer.isBuffer(encoding)) {
|
||||
buffer.copy(encoding, 0, start, end);
|
||||
return encoding;
|
||||
} else
|
||||
return buffer.toString(encoding, start, end);
|
||||
} else
|
||||
return buffer.slice(start, end);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user