function SFTPStream(cfg, remoteIdentRaw) {
if (typeof cfg === 'string' && !remoteIdentRaw) {
remoteIdentRaw = cfg;
cfg = undefined;
}
if (typeof cfg !== 'object' || !cfg)
cfg = {};
TransformStream.call(this, {
highWaterMark: (typeof cfg.highWaterMark === 'number'
? cfg.highWaterMark
: 32 * 1024)
});
this.debug = (typeof cfg.debug === 'function' ? cfg.debug : DEBUG_NOOP);
this.server = (cfg.server ? true : false);
this._isOpenSSH = (remoteIdentRaw && RE_OPENSSH.test(remoteIdentRaw));
this._needContinue = false;
this._state = {
// common
status: 'packet_header',
writeReqid: -1,
pktLeft: undefined,
pktHdrBuf: new Buffer(9), // room for pktLen + pktType + req id
pktBuf: undefined,
pktType: undefined,
version: undefined,
extensions: {},
// client
maxDataLen: (this._isOpenSSH ? OPENSSH_MAX_DATA_LEN : 32768),
requests: {}
};
var self = this;
this.on('end', function() {
self.readable = false;
}).on('finish', onFinish)
.on('prefinish', onFinish);
function onFinish() {
self.writable = false;
self._cleanup(false);
}
if (!this.server)
this.push(CLIENT_VERSION_BUFFER);
}n/a
function Stats(initial) {
this.mode = (initial && initial.mode);
this.permissions = this.mode; // backwards compatiblity
this.uid = (initial && initial.uid);
this.gid = (initial && initial.gid);
this.size = (initial && initial.size);
this.atime = (initial && initial.atime);
this.mtime = (initial && initial.mtime);
}n/a
function SSH2Stream(cfg) {
if (typeof cfg !== 'object' || cfg === null)
cfg = {};
TransformStream.call(this, {
highWaterMark: (typeof cfg.highWaterMark === 'number'
? cfg.highWaterMark
: 32 * 1024)
});
this._needContinue = false;
this.bytesSent = this.bytesReceived = 0;
this.debug = (typeof cfg.debug === 'function' ? cfg.debug : DEBUG_NOOP);
this.server = (cfg.server === true);
this.maxPacketSize = (typeof cfg.maxPacketSize === 'number'
? cfg.maxPacketSize
: MAX_PACKET_SIZE);
// Bitmap that indicates any bugs the remote side has. This is determined
// by the reported software version.
this.remoteBugs = 0;
if (this.server) {
// TODO: Remove when we support group exchange for server implementation
this.remoteBugs = BUGS.BAD_DHGEX;
}
var self = this;
var hostKeys = cfg.hostKeys;
if (this.server && (typeof hostKeys !== 'object' || hostKeys === null))
throw new Error('hostKeys must be an object keyed on host key type');
this.config = {
// Server
hostKeys: hostKeys, // All keys supported by server
// Client/Server
ident: 'SSH-2.0-'
+ (cfg.ident
|| ('ssh2js' + MODULE_VER + (this.server ? 'srv' : ''))),
algorithms: {
kex: ALGORITHMS.KEX,
kexBuf: ALGORITHMS.KEX_BUF,
serverHostKey: ALGORITHMS.SERVER_HOST_KEY,
serverHostKeyBuf: ALGORITHMS.SERVER_HOST_KEY_BUF,
cipher: ALGORITHMS.CIPHER,
cipherBuf: ALGORITHMS.CIPHER_BUF,
hmac: ALGORITHMS.HMAC,
hmacBuf: ALGORITHMS.HMAC_BUF,
compress: ALGORITHMS.COMPRESS,
compressBuf: ALGORITHMS.COMPRESS_BUF
}
};
// RFC 4253 states the identification string must not contain NULL
this.config.ident.replace(RE_NULL, '');
if (this.config.ident.length + 2 /* Account for "\r\n" */ > 255)
throw new Error('ident too long');
if (typeof cfg.algorithms === 'object' && cfg.algorithms !== null) {
var algos = cfg.algorithms;
if (Array.isArray(algos.kex) && algos.kex.length > 0) {
this.config.algorithms.kex = algos.kex;
if (!Buffer.isBuffer(algos.kexBuf))
algos.kexBuf = new Buffer(algos.kex.join(','), 'ascii');
this.config.algorithms.kexBuf = algos.kexBuf;
}
if (Array.isArray(algos.serverHostKey) && algos.serverHostKey.length > 0) {
this.config.algorithms.serverHostKey = algos.serverHostKey;
if (!Buffer.isBuffer(algos.serverHostKeyBuf)) {
algos.serverHostKeyBuf = new Buffer(algos.serverHostKey.join(','),
'ascii');
}
this.config.algorithms.serverHostKeyBuf = algos.serverHostKeyBuf;
}
if (Array.isArray(algos.cipher) && algos.cipher.length > 0) {
this.config.algorithms.cipher = algos.cipher;
if (!Buffer.isBuffer(algos.cipherBuf))
algos.cipherBuf = new Buffer(algos.cipher.join(','), 'ascii');
this.config.algorithms.cipherBuf = algos.cipherBuf;
}
if (Array.isArray(algos.hmac) && algos.hmac.length > 0) {
this.config.algorithms.hmac = algos.hmac;
if (!Buffer.isBuffer(algos.hmacBuf))
algos.hmacBuf = new Buffer(algos.hmac.join(','), 'ascii');
this.config.algorithms.hmacBuf = algos.hmacBuf;
}
if (Array.isArray(algos.compress) && algos.compress.length > 0) {
this.config.algorithms.compress = algos.compress;
if (!Buffer.isBuffer(algos.compressBuf))
algos.compressBuf = new Buffer(algos.compress.join(','), 'ascii');
this.config.algorithms.compressBuf = algos.compressBuf;
}
}
this.reset(true);
// Common events
this.on('end', function() {
// Let GC collect any Buffers we were previously storing
self._state = undefined;
self.reset();
self._state.incoming.hmac.bufCompute = undefined;
self._state.outgoing.bufSeqno = undefined;
});
this.on('DISCONNECT', function(reason, code, desc, lang) {
onDISCONNECT(self, reason, code, desc, lang);
});
this.on('KEXINIT', function(init, firstFollows) {
onKEXINIT(self, ...n/a
function BigInteger(a, b, c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}n/a
function SFTPStream(cfg, remoteIdentRaw) {
if (typeof cfg === 'string' && !remoteIdentRaw) {
remoteIdentRaw = cfg;
cfg = undefined;
}
if (typeof cfg !== 'object' || !cfg)
cfg = {};
TransformStream.call(this, {
highWaterMark: (typeof cfg.highWaterMark === 'number'
? cfg.highWaterMark
: 32 * 1024)
});
this.debug = (typeof cfg.debug === 'function' ? cfg.debug : DEBUG_NOOP);
this.server = (cfg.server ? true : false);
this._isOpenSSH = (remoteIdentRaw && RE_OPENSSH.test(remoteIdentRaw));
this._needContinue = false;
this._state = {
// common
status: 'packet_header',
writeReqid: -1,
pktLeft: undefined,
pktHdrBuf: new Buffer(9), // room for pktLen + pktType + req id
pktBuf: undefined,
pktType: undefined,
version: undefined,
extensions: {},
// client
maxDataLen: (this._isOpenSSH ? OPENSSH_MAX_DATA_LEN : 32768),
requests: {}
};
var self = this;
this.on('end', function() {
self.readable = false;
}).on('finish', onFinish)
.on('prefinish', onFinish);
function onFinish() {
self.writable = false;
self._cleanup(false);
}
if (!this.server)
this.push(CLIENT_VERSION_BUFFER);
}n/a
function Stats(initial) {
this.mode = (initial && initial.mode);
this.permissions = this.mode; // backwards compatiblity
this.uid = (initial && initial.uid);
this.gid = (initial && initial.gid);
this.size = (initial && initial.size);
this.atime = (initial && initial.atime);
this.mtime = (initial && initial.mtime);
}n/a
function flagsToString(flags) {
for (var i = 0; i < stringFlagMapKeys.length; ++i) {
var key = stringFlagMapKeys[i];
if (stringFlagMap[key] === flags)
return key;
}
return null;
}n/a
function stringToFlags(str) {
var flags = stringFlagMap[str];
if (flags !== undefined)
return flags;
return null;
}n/a
function Transform(options) {
if (!(this instanceof Transform))
return new Transform(options);
Duplex.call(this, options);
this._transformState = new TransformState(this);
var stream = this;
// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
// we have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false;
if (options) {
if (typeof options.transform === 'function')
this._transform = options.transform;
if (typeof options.flush === 'function')
this._flush = options.flush;
}
// When the writable side finishes, then flush out anything remaining.
this.once('prefinish', function() {
if (typeof this._flush === 'function')
this._flush(function(er) {
done(stream, er);
});
else
done(stream);
});
}n/a
function Stats(initial) {
this.mode = (initial && initial.mode);
this.permissions = this.mode; // backwards compatiblity
this.uid = (initial && initial.uid);
this.gid = (initial && initial.gid);
this.size = (initial && initial.size);
this.atime = (initial && initial.atime);
this.mtime = (initial && initial.mtime);
}n/a
_checkModeProperty = function (property) {
return ((this.mode & constants.S_IFMT) === property);
}n/a
isBlockDevice = function () {
return this._checkModeProperty(constants.S_IFBLK);
}n/a
isCharacterDevice = function () {
return this._checkModeProperty(constants.S_IFCHR);
}n/a
isDirectory = function () {
return this._checkModeProperty(constants.S_IFDIR);
}n/a
isFIFO = function () {
return this._checkModeProperty(constants.S_IFIFO);
}n/a
isFile = function () {
return this._checkModeProperty(constants.S_IFREG);
}n/a
isSocket = function () {
return this._checkModeProperty(constants.S_IFSOCK);
}n/a
isSymbolicLink = function () {
return this._checkModeProperty(constants.S_IFLNK);
}n/a
__push = function (chunk, encoding) {
this._transformState.needTransform = false;
return Duplex.prototype.push.call(this, chunk, encoding);
}n/a
__read = function (n) {
var ts = this._transformState;
if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
ts.transforming = true;
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
} else {
// mark that we need a transform, so that any data that comes in
// will get processed, now that we've asked for it.
ts.needTransform = true;
}
}n/a
_cleanup = function (callback) {
var state = this._state;
state.pktBuf = undefined; // give GC something to do
var requests = state.requests;
var keys = Object.keys(requests);
var len = keys.length;
if (len) {
if (this.readable) {
var err = new Error('SFTP session ended early');
for (var i = 0, cb; i < len; ++i)
(cb = requests[keys[i]].cb) && cb(err);
}
state.requests = {};
}
if (this.readable)
this.push(null);
else if (!this._readableState.endEmitted && !this._readableState.flowing) {
// Ugh!
this.resume();
}
if (callback !== false) {
this.debug('DEBUG[SFTP]: Parser: Malformed packet');
callback && callback(new Error('Malformed packet'));
}
}n/a
_read = function (n) {
if (this._needContinue) {
this._needContinue = false;
this.emit('continue');
}
return this.__read(n);
}n/a
_transform = function (chunk, encoding, callback) {
var state = this._state;
var server = this.server;
var status = state.status;
var pktType = state.pktType;
var pktBuf = state.pktBuf;
var pktLeft = state.pktLeft;
var version = state.version;
var pktHdrBuf = state.pktHdrBuf;
var requests = state.requests;
var debug = this.debug;
var chunkLen = chunk.length;
var chunkPos = 0;
var buffer;
var chunkLeft;
var id;
while (true) {
if (status === 'discard') {
chunkLeft = (chunkLen - chunkPos);
if (pktLeft <= chunkLeft) {
chunkPos += pktLeft;
pktLeft = 0;
status = 'packet_header';
buffer = pktBuf = undefined;
} else {
pktLeft -= chunkLeft;
break;
}
} else if (pktBuf !== undefined) {
chunkLeft = (chunkLen - chunkPos);
if (pktLeft <= chunkLeft) {
chunk.copy(pktBuf,
pktBuf.length - pktLeft,
chunkPos,
chunkPos + pktLeft);
chunkPos += pktLeft;
pktLeft = 0;
buffer = pktBuf;
pktBuf = undefined;
continue;
} else {
chunk.copy(pktBuf, pktBuf.length - pktLeft, chunkPos);
pktLeft -= chunkLeft;
break;
}
} else if (status === 'packet_header') {
if (!buffer) {
pktLeft = 5;
pktBuf = pktHdrBuf;
} else {
// here we read the right-most 5 bytes from buffer (pktHdrBuf)
pktLeft = buffer.readUInt32BE(4, true) - 1; // account for type byte
pktType = buffer[8];
if (server) {
if (version === undefined && pktType !== REQUEST.INIT) {
debug('DEBUG[SFTP]: Parser: Unexpected packet before init');
status = 'bad_pkt';
} else if (version !== undefined && pktType === REQUEST.INIT) {
debug('DEBUG[SFTP]: Parser: Unexpected duplicate init');
status = 'bad_pkt';
} else if (pktLeft > MAX_PKT_LEN) {
debug('DEBUG[SFTP]: Parser: Packet length ('
+ pktLeft
+ ') exceeds max length ('
+ MAX_PKT_LEN
+ ')');
status = 'bad_pkt';
} else if (pktType === REQUEST.EXTENDED)
status = 'bad_pkt';
else if (REQUEST[pktType] === undefined) {
debug('DEBUG[SFTP]: Parser: Unsupported packet type: ' + pktType);
status = 'discard';
}
} else if (version === undefined && pktType !== RESPONSE.VERSION) {
debug('DEBUG[SFTP]: Parser: Unexpected packet before version');
status = 'bad_pkt';
} else if (version !== undefined && pktType === RESPONSE.VERSION) {
debug('DEBUG[SFTP]: Parser: Unexpected duplicate version');
status = 'bad_pkt';
} else if (RESPONSE[pktType] === undefined)
status = 'discard';
if (status === 'bad_pkt') {
// copy original packet info
pktHdrBuf.writeUInt32BE(pktLeft, 0, true);
pktHdrBuf[4] = pktType;
pktLeft = 4;
pktBuf = pktHdrBuf;
} else {
pktBuf = new Buffer(pktLeft);
status = 'payload';
}
}
} else if (status === 'payload') {
if (pktType === RESPONSE.VERSION || pktType === REQUEST.INIT) {
/*
uint32 version
<extension data>
*/
version = state.version = readInt(buffer, 0, this, callback);
if (version === false)
return;
if (version < 3) {
this._cleanup();
return callback(new Error('Incompatible SFTP version: ' + version));
} else if (server)
this.push(SERVER_VERSION_BUFFER);
var buflen = buffer.length;
var extname;
var extdata;
buffer._pos = 4;
while (buffer._pos < buflen) {
extname = readString(buffer, buffer._pos, 'ascii', this, callback);
if (extname === false)
return;
extdata = readString(buffer, buffer._pos, 'ascii', this, callback);
if (extdata === ...n/a
appendFile = function (path, data, options, callback_) {
if (this.server)
throw new Error('Client-only method called in server mode');
var callback;
if (typeof callback_ === 'function') {
callback = callback_;
} else if (typeof options === 'function') {
callback = options;
options = undefined;
}
if (typeof options === 'string')
options = { encoding: options, mode: 438, flag: 'a' };
else if (!options)
options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' };
else if (typeof options !== 'object')
throw new TypeError('Bad arguments');
if (!options.flag)
options = util._extend({ flag: 'a' }, options);
this.writeFile(path, data, options, callback);
}n/a
attrs = function (id, attrs) {
if (!this.server)
throw new Error('Server-only method called in client mode');
if (typeof attrs !== 'object')
throw new Error('attrs is not an object');
var info = attrsToBytes(attrs);
var buf = new Buffer(4 + 1 + 4 + 4 + info.nbytes);
var p = 13;
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = RESPONSE.ATTRS;
buf.writeUInt32BE(id, 5, true);
buf.writeUInt32BE(info.flags, 9, true);
if (info.flags && info.bytes) {
var bytes = info.bytes;
for (var j = 0, len = bytes.length; j < len; ++j)
for (var k = 0, len2 = bytes[j].length; k < len2; ++k)
buf[p++] = bytes[j][k];
}
this.debug('DEBUG[SFTP]: Outgoing: Writing ATTRS');
return this.push(buf);
}n/a
chmod = function (path, mode, cb) {
return this.setstat(path, {
mode: mode
}, cb);
}n/a
chown = function (path, uid, gid, cb) {
return this.setstat(path, {
uid: uid,
gid: gid
}, cb);
}n/a
close = function (handle, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!Buffer.isBuffer(handle))
throw new Error('handle is not a Buffer');
var state = this._state;
/*
uint32 id
string handle
*/
var handlelen = handle.length;
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + handlelen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.CLOSE;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(handlelen, p, true);
handle.copy(buf, p += 4);
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing CLOSE');
return this.push(buf);
}n/a
createReadStream = function (path, options) {
if (this.server)
throw new Error('Client-only method called in server mode');
return new ReadStream(this, path, options);
}n/a
createWriteStream = function (path, options) {
if (this.server)
throw new Error('Client-only method called in server mode');
return new WriteStream(this, path, options);
}n/a
data = function (id, data, encoding) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var isBuffer = Buffer.isBuffer(data);
if (!isBuffer && typeof data !== 'string')
throw new Error('data is not a Buffer or string');
if (!isBuffer)
encoding || (encoding = 'utf8');
var dataLen = (isBuffer ? data.length : Buffer.byteLength(data, encoding));
var buf = new Buffer(4 + 1 + 4 + 4 + dataLen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = RESPONSE.DATA;
buf.writeUInt32BE(id, 5, true);
buf.writeUInt32BE(dataLen, 9, true);
if (dataLen) {
if (isBuffer)
data.copy(buf, 13);
else
buf.write(data, 13, dataLen, encoding);
}
this.debug('DEBUG[SFTP]: Outgoing: Writing DATA');
return this.push(buf);
}n/a
exists = function (path, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
this.stat(path, function(err) {
cb && cb(err ? false : true);
});
}n/a
ext_openssh_fstatvfs = function (handle, cb) {
var state = this._state;
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!state.extensions['fstatvfs@openssh.com']
|| state.extensions['fstatvfs@openssh.com'].indexOf('2') === -1)
throw new Error('Server does not support this extended request');
else if (!Buffer.isBuffer(handle))
throw new Error('handle is not a Buffer');
/*
uint32 id
string "fstatvfs@openssh.com"
string handle
*/
var handlelen = handle.length;
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + 20 + 4 + handlelen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.EXTENDED;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(20, p, true);
buf.write('fstatvfs@openssh.com', p += 4, 20, 'ascii');
buf.writeUInt32BE(handlelen, p += 20, true);
buf.write(handle, p += 4, handlelen, 'utf8');
state.requests[reqid] = {
extended: 'fstatvfs@openssh.com',
cb: cb
};
this.debug('DEBUG[SFTP]: Outgoing: Writing fstatvfs@openssh.com');
return this.push(buf);
}n/a
ext_openssh_fsync = function (handle, cb) {
var state = this._state;
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!state.extensions['fsync@openssh.com']
|| state.extensions['fsync@openssh.com'].indexOf('1') === -1)
throw new Error('Server does not support this extended request');
else if (!Buffer.isBuffer(handle))
throw new Error('handle is not a Buffer');
/*
uint32 id
string "fsync@openssh.com"
string handle
*/
var handlelen = handle.length;
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + 17 + 4 + handlelen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.EXTENDED;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(17, p, true);
buf.write('fsync@openssh.com', p += 4, 17, 'ascii');
buf.writeUInt32BE(handlelen, p += 17, true);
buf.write(handle, p += 4, handlelen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing fsync@openssh.com');
return this.push(buf);
}n/a
ext_openssh_hardlink = function (oldPath, newPath, cb) {
var state = this._state;
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!state.extensions['hardlink@openssh.com']
|| state.extensions['hardlink@openssh.com'].indexOf('1') === -1)
throw new Error('Server does not support this extended request');
/*
uint32 id
string "hardlink@openssh.com"
string oldpath
string newpath
*/
var oldlen = Buffer.byteLength(oldPath);
var newlen = Buffer.byteLength(newPath);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + 20 + 4 + oldlen + 4 + newlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.EXTENDED;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(20, p, true);
buf.write('hardlink@openssh.com', p += 4, 20, 'ascii');
buf.writeUInt32BE(oldlen, p += 20, true);
buf.write(oldPath, p += 4, oldlen, 'utf8');
buf.writeUInt32BE(newlen, p += oldlen, true);
buf.write(newPath, p += 4, newlen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing hardlink@openssh.com');
return this.push(buf);
}n/a
ext_openssh_rename = function (oldPath, newPath, cb) {
var state = this._state;
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!state.extensions['posix-rename@openssh.com']
|| state.extensions['posix-rename@openssh.com'].indexOf('1') === -1)
throw new Error('Server does not support this extended request');
/*
uint32 id
string "posix-rename@openssh.com"
string oldpath
string newpath
*/
var oldlen = Buffer.byteLength(oldPath);
var newlen = Buffer.byteLength(newPath);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + 24 + 4 + oldlen + 4 + newlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.EXTENDED;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(24, p, true);
buf.write('posix-rename@openssh.com', p += 4, 24, 'ascii');
buf.writeUInt32BE(oldlen, p += 24, true);
buf.write(oldPath, p += 4, oldlen, 'utf8');
buf.writeUInt32BE(newlen, p += oldlen, true);
buf.write(newPath, p += 4, newlen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing posix-rename@openssh.com');
return this.push(buf);
}n/a
ext_openssh_statvfs = function (path, cb) {
var state = this._state;
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!state.extensions['statvfs@openssh.com']
|| state.extensions['statvfs@openssh.com'].indexOf('2') === -1)
throw new Error('Server does not support this extended request');
/*
uint32 id
string "statvfs@openssh.com"
string path
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + 19 + 4 + pathlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.EXTENDED;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(19, p, true);
buf.write('statvfs@openssh.com', p += 4, 19, 'ascii');
buf.writeUInt32BE(pathlen, p += 19, true);
buf.write(path, p += 4, pathlen, 'utf8');
state.requests[reqid] = {
extended: 'statvfs@openssh.com',
cb: cb
};
this.debug('DEBUG[SFTP]: Outgoing: Writing statvfs@openssh.com');
return this.push(buf);
}n/a
fastGet = function (remotePath, localPath, opts, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
fastXfer(this, fs, remotePath, localPath, opts, cb);
}n/a
fastPut = function (localPath, remotePath, opts, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
fastXfer(fs, this, localPath, remotePath, opts, cb);
}n/a
fchmod = function (handle, mode, cb) {
return this.fsetstat(handle, {
mode: mode
}, cb);
}n/a
fchown = function (handle, uid, gid, cb) {
return this.fsetstat(handle, {
uid: uid,
gid: gid
}, cb);
}n/a
fsetstat = function (handle, attrs, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!Buffer.isBuffer(handle))
throw new Error('handle is not a Buffer');
var flags = 0;
var attrBytes = 0;
var state = this._state;
if (typeof attrs === 'object') {
attrs = attrsToBytes(attrs);
flags = attrs.flags;
attrBytes = attrs.nbytes;
attrs = attrs.bytes;
} else if (typeof attrs === 'function')
cb = attrs;
/*
uint32 id
string handle
ATTRS attrs
*/
var handlelen = handle.length;
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + handlelen + 4 + attrBytes);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.FSETSTAT;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(handlelen, p, true);
handle.copy(buf, p += 4);
buf.writeUInt32BE(flags, p += handlelen);
if (flags) {
p += 4;
for (var i = 0, len = attrs.length; i < len; ++i)
for (var j = 0, len2 = attrs[i].length; j < len2; ++j)
buf[p++] = attrs[i][j];
}
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing FSETSTAT');
return this.push(buf);
}n/a
fstat = function (handle, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!Buffer.isBuffer(handle))
throw new Error('handle is not a Buffer');
var state = this._state;
/*
uint32 id
string handle
*/
var handlelen = handle.length;
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + handlelen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.FSTAT;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(handlelen, p, true);
handle.copy(buf, p += 4);
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing FSTAT');
return this.push(buf);
}n/a
futimes = function (handle, atime, mtime, cb) {
return this.fsetstat(handle, {
atime: toUnixTimestamp(atime),
mtime: toUnixTimestamp(mtime)
}, cb);
}n/a
handle = function (id, handle) {
if (!this.server)
throw new Error('Server-only method called in client mode');
if (!Buffer.isBuffer(handle))
throw new Error('handle is not a Buffer');
var handleLen = handle.length;
if (handleLen > 256)
throw new Error('handle too large (> 256 bytes)');
var buf = new Buffer(4 + 1 + 4 + 4 + handleLen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = RESPONSE.HANDLE;
buf.writeUInt32BE(id, 5, true);
buf.writeUInt32BE(handleLen, 9, true);
if (handleLen)
handle.copy(buf, 13);
this.debug('DEBUG[SFTP]: Outgoing: Writing HANDLE');
return this.push(buf);
}n/a
lstat = function (path, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string path
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.LSTAT;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing LSTAT');
return this.push(buf);
}n/a
mkdir = function (path, attrs, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var flags = 0;
var attrBytes = 0;
var state = this._state;
if (typeof attrs === 'function') {
cb = attrs;
attrs = undefined;
}
if (typeof attrs === 'object') {
attrs = attrsToBytes(attrs);
flags = attrs.flags;
attrBytes = attrs.nbytes;
attrs = attrs.bytes;
}
/*
uint32 id
string path
ATTRS attrs
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen + 4 + attrBytes);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.MKDIR;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
buf.writeUInt32BE(flags, p += pathlen);
if (flags) {
p += 4;
for (var i = 0, len = attrs.length; i < len; ++i)
for (var j = 0, len2 = attrs[i].length; j < len2; ++j)
buf[p++] = attrs[i][j];
}
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing MKDIR');
return this.push(buf);
}n/a
name = function (id, names) {
if (!this.server)
throw new Error('Server-only method called in client mode');
if (!Array.isArray(names) && typeof names === 'object')
names = [ names ];
else if (!Array.isArray(names))
throw new Error('names is not an object or array');
var count = names.length;
var namesLen = 0;
var nameAttrs;
var attrs = [];
var name;
var filename;
var longname;
var attr;
var len;
var len2;
var buf;
var p;
var i;
var j;
var k;
for (i = 0; i < count; ++i) {
name = names[i];
filename = (!name || !name.filename || typeof name.filename !== 'string'
? ''
: name.filename);
namesLen += 4 + Buffer.byteLength(filename);
longname = (!name || !name.longname || typeof name.longname !== 'string'
? ''
: name.longname);
namesLen += 4 + Buffer.byteLength(longname);
if (typeof name.attrs === 'object') {
nameAttrs = attrsToBytes(name.attrs);
namesLen += 4 + nameAttrs.nbytes;
attrs.push(nameAttrs);
} else {
namesLen += 4;
attrs.push(null);
}
}
buf = new Buffer(4 + 1 + 4 + 4 + namesLen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = RESPONSE.NAME;
buf.writeUInt32BE(id, 5, true);
buf.writeUInt32BE(count, 9, true);
p = 13;
for (i = 0; i < count; ++i) {
name = names[i];
filename = (!name || !name.filename || typeof name.filename !== 'string'
? ''
: name.filename);
len = Buffer.byteLength(filename);
buf.writeUInt32BE(len, p, true);
p += 4;
if (len) {
buf.write(filename, p, len, 'utf8');
p += len;
}
longname = (!name || !name.longname || typeof name.longname !== 'string'
? ''
: name.longname);
len = Buffer.byteLength(longname);
buf.writeUInt32BE(len, p, true);
p += 4;
if (len) {
buf.write(longname, p, len, 'utf8');
p += len;
}
attr = attrs[i];
if (attr) {
buf.writeUInt32BE(attr.flags, p, true);
p += 4;
if (attr.flags && attr.bytes) {
var bytes = attr.bytes;
for (j = 0, len = bytes.length; j < len; ++j)
for (k = 0, len2 = bytes[j].length; k < len2; ++k)
buf[p++] = bytes[j][k];
}
} else {
buf.writeUInt32BE(0, p, true);
p += 4;
}
}
this.debug('DEBUG[SFTP]: Outgoing: Writing NAME');
return this.push(buf);
}n/a
open = function (path, flags_, attrs, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
if (typeof attrs === 'function') {
cb = attrs;
attrs = undefined;
}
var flags = stringToFlags(flags_);
if (flags === null)
throw new Error('Unknown flags string: ' + flags_);
var attrFlags = 0;
var attrBytes = 0;
if (typeof attrs === 'string' || typeof attrs === 'number') {
attrs = { mode: attrs };
}
if (typeof attrs === 'object') {
attrs = attrsToBytes(attrs);
attrFlags = attrs.flags;
attrBytes = attrs.nbytes;
attrs = attrs.bytes;
}
/*
uint32 id
string filename
uint32 pflags
ATTRS attrs
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen + 4 + 4 + attrBytes);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.OPEN;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
buf.writeUInt32BE(flags, p += pathlen, true);
buf.writeUInt32BE(attrFlags, p += 4, true);
if (attrs && attrFlags) {
p += 4;
for (var i = 0, len = attrs.length; i < len; ++i)
for (var j = 0, len2 = attrs[i].length; j < len2; ++j)
buf[p++] = attrs[i][j];
}
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing OPEN');
return this.push(buf);
}n/a
opendir = function (path, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string path
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.OPENDIR;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing OPENDIR');
return this.push(buf);
}n/a
push = function (chunk, encoding) {
if (!this.readable)
return false;
if (chunk === null)
this.readable = false;
var ret = this.__push(chunk, encoding);
this._needContinue = (ret === false);
return ret;
}...
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');
...readData = function (handle, buf, off, len, position, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!Buffer.isBuffer(handle))
throw new Error('handle is not a Buffer');
else if (!Buffer.isBuffer(buf))
throw new Error('buffer is not a Buffer');
else if (off >= buf.length)
throw new Error('offset is out of bounds');
else if (off + len > buf.length)
throw new Error('length extends beyond buffer');
else if (position === null)
throw new Error('null position currently unsupported');
var state = this._state;
/*
uint32 id
string handle
uint64 offset
uint32 len
*/
var handlelen = handle.length;
var p = 9;
var pos = position;
var out = new Buffer(4 + 1 + 4 + 4 + handlelen + 8 + 4);
out.writeUInt32BE(out.length - 4, 0, true);
out[4] = REQUEST.READ;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
out.writeUInt32BE(reqid, 5, true);
out.writeUInt32BE(handlelen, p, true);
handle.copy(out, p += 4);
p += handlelen;
for (var i = 7; i >= 0; --i) {
out[p + i] = pos & 0xFF;
pos /= 256;
}
out.writeUInt32BE(len, p += 8, true);
state.requests[reqid] = {
cb: function(err, data, nb) {
if (err && err.code !== STATUS_CODE.EOF)
return cb(err);
cb(undefined, nb || 0, data, position);
},
buffer: buf.slice(off, off + len)
};
this.debug('DEBUG[SFTP]: Outgoing: Writing READ');
return this.push(out);
}n/a
readFile = function (path, options, callback_) {
if (this.server)
throw new Error('Client-only method called in server mode');
var callback;
if (typeof callback_ === 'function') {
callback = callback_;
} else if (typeof options === 'function') {
callback = options;
options = undefined;
}
var self = this;
if (typeof options === 'string')
options = { encoding: options, flag: 'r' };
else if (!options)
options = { encoding: null, flag: 'r' };
else if (typeof options !== 'object')
throw new TypeError('Bad arguments');
var encoding = options.encoding;
if (encoding && !Buffer.isEncoding(encoding))
throw new Error('Unknown encoding: ' + encoding);
// first, stat the file, so we know the size.
var size;
var buffer; // single buffer with file data
var buffers; // list for when size is unknown
var pos = 0;
var handle;
// SFTPv3 does not support using -1 for read position, so we have to track
// read position manually
var bytesRead = 0;
var flag = options.flag || 'r';
this.open(path, flag, 438 /*=0666*/, function(er, handle_) {
if (er)
return callback && callback(er);
handle = handle_;
self.fstat(handle, function tryStat(er, st) {
if (er) {
// Try stat() for sftp servers that may not support fstat() for
// whatever reason
self.stat(path, function(er_, st_) {
if (er_) {
return self.close(handle, function() {
callback && callback(er);
});
}
tryStat(null, st_);
});
return;
}
size = st.size;
if (size === 0) {
// the kernel lies about many files.
// Go ahead and try to read some bytes.
buffers = [];
return read();
}
buffer = new Buffer(size);
read();
});
});
function read() {
if (size === 0) {
buffer = new Buffer(8192);
self.readData(handle, buffer, 0, 8192, bytesRead, afterRead);
} else
self.readData(handle, buffer, pos, size - pos, bytesRead, afterRead);
}
function afterRead(er, nbytes) {
if (er) {
return self.close(handle, function() {
return callback && callback(er);
});
}
if (nbytes === 0)
return close();
bytesRead += nbytes;
pos += nbytes;
if (size !== 0) {
if (pos === size)
close();
else
read();
} else {
// unknown size, just read until we don't get bytes.
buffers.push(buffer.slice(0, nbytes));
read();
}
}
function close() {
self.close(handle, function(er) {
if (size === 0) {
// collected the data into the buffers list.
buffer = Buffer.concat(buffers, pos);
} else if (pos < size)
buffer = buffer.slice(0, pos);
if (encoding)
buffer = buffer.toString(encoding);
return callback && callback(er, buffer);
});
}
}n/a
readdir = function (where, opts, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
var doFilter;
if (typeof opts === 'function') {
cb = opts;
opts = {};
}
if (typeof opts !== 'object')
opts = {};
doFilter = (opts && opts.full ? false : true);
if (!Buffer.isBuffer(where) && typeof where !== 'string')
throw new Error('missing directory handle or path');
if (typeof where === 'string') {
var self = this;
var entries = [];
var e = 0;
return this.opendir(where, function reread(err, handle) {
if (err)
return cb(err);
self.readdir(handle, opts, function(err, list) {
var eof = (err && err.code === STATUS_CODE.EOF);
if (err && !eof) {
return self.close(handle, function() {
cb(err);
});
} else if (eof) {
return self.close(handle, function(err) {
if (err)
return cb(err);
cb(undefined, entries);
});
}
for (var i = 0, len = list.length; i < len; ++i, ++e)
entries[e] = list[i];
reread(undefined, handle);
});
});
}
/*
uint32 id
string handle
*/
var handlelen = where.length;
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + handlelen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.READDIR;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(handlelen, p, true);
where.copy(buf, p += 4);
state.requests[reqid] = {
cb: (doFilter
? function(err, list) {
if (err)
return cb(err);
for (var i = list.length - 1; i >= 0; --i) {
if (list[i].filename === '.' || list[i].filename === '..')
list.splice(i, 1);
}
cb(undefined, list);
}
: cb)
};
this.debug('DEBUG[SFTP]: Outgoing: Writing READDIR');
return this.push(buf);
}n/a
readlink = function (path, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string path
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.READLINK;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
state.requests[reqid] = {
cb: function(err, names) {
if (err)
return cb(err);
else if (!names || !names.length)
return cb(new Error('Response missing link info'));
cb(undefined, names[0].filename);
}
};
this.debug('DEBUG[SFTP]: Outgoing: Writing READLINK');
return this.push(buf);
}n/a
realpath = function (path, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string path
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.REALPATH;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
state.requests[reqid] = {
cb: function(err, names) {
if (err)
return cb(err);
else if (!names || !names.length)
return cb(new Error('Response missing path info'));
cb(undefined, names[0].filename);
}
};
this.debug('DEBUG[SFTP]: Outgoing: Writing REALPATH');
return this.push(buf);
}n/a
rename = function (oldPath, newPath, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string oldpath
string newpath
*/
var oldlen = Buffer.byteLength(oldPath);
var newlen = Buffer.byteLength(newPath);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + oldlen + 4 + newlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.RENAME;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(oldlen, p, true);
buf.write(oldPath, p += 4, oldlen, 'utf8');
buf.writeUInt32BE(newlen, p += oldlen, true);
buf.write(newPath, p += 4, newlen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing RENAME');
return this.push(buf);
}n/a
rmdir = function (path, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string path
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.RMDIR;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing RMDIR');
return this.push(buf);
}n/a
setstat = function (path, attrs, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var flags = 0;
var attrBytes = 0;
var state = this._state;
if (typeof attrs === 'object') {
attrs = attrsToBytes(attrs);
flags = attrs.flags;
attrBytes = attrs.nbytes;
attrs = attrs.bytes;
} else if (typeof attrs === 'function')
cb = attrs;
/*
uint32 id
string path
ATTRS attrs
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen + 4 + attrBytes);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.SETSTAT;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
buf.writeUInt32BE(flags, p += pathlen);
if (flags) {
p += 4;
for (var i = 0, len = attrs.length; i < len; ++i)
for (var j = 0, len2 = attrs[i].length; j < len2; ++j)
buf[p++] = attrs[i][j];
}
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing SETSTAT');
return this.push(buf);
}n/a
stat = function (path, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string path
*/
var pathlen = Buffer.byteLength(path);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + pathlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.STAT;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(path, p += 4, pathlen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing STAT');
return this.push(buf);
}n/a
status = function (id, code, message, lang) {
if (!this.server)
throw new Error('Server-only method called in client mode');
if (!STATUS_CODE[code] || typeof code !== 'number')
throw new Error('Bad status code: ' + code);
message || (message = '');
lang || (lang = '');
var msgLen = Buffer.byteLength(message);
var langLen = Buffer.byteLength(lang);
var buf = new Buffer(4 + 1 + 4 + 4 + 4 + msgLen + 4 + langLen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = RESPONSE.STATUS;
buf.writeUInt32BE(id, 5, true);
buf.writeUInt32BE(code, 9, true);
buf.writeUInt32BE(msgLen, 13, true);
if (msgLen)
buf.write(message, 17, msgLen, 'utf8');
buf.writeUInt32BE(langLen, 17 + msgLen, true);
if (langLen)
buf.write(lang, 17 + msgLen + 4, langLen, 'ascii');
this.debug('DEBUG[SFTP]: Outgoing: Writing STATUS');
return this.push(buf);
}n/a
symlink = function (targetPath, linkPath, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string linkpath
string targetpath
*/
var linklen = Buffer.byteLength(linkPath);
var targetlen = Buffer.byteLength(targetPath);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + linklen + 4 + targetlen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.SYMLINK;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
if (this._isOpenSSH) {
// OpenSSH has linkpath and targetpath positions switched
buf.writeUInt32BE(targetlen, p, true);
buf.write(targetPath, p += 4, targetlen, 'utf8');
buf.writeUInt32BE(linklen, p += targetlen, true);
buf.write(linkPath, p += 4, linklen, 'utf8');
} else {
buf.writeUInt32BE(linklen, p, true);
buf.write(linkPath, p += 4, linklen, 'utf8');
buf.writeUInt32BE(targetlen, p += linklen, true);
buf.write(targetPath, p += 4, targetlen, 'utf8');
}
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing SYMLINK');
return this.push(buf);
}n/a
unlink = function (filename, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
var state = this._state;
/*
uint32 id
string filename
*/
var fnamelen = Buffer.byteLength(filename);
var p = 9;
var buf = new Buffer(4 + 1 + 4 + 4 + fnamelen);
buf.writeUInt32BE(buf.length - 4, 0, true);
buf[4] = REQUEST.REMOVE;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
buf.writeUInt32BE(reqid, 5, true);
buf.writeUInt32BE(fnamelen, p, true);
buf.write(filename, p += 4, fnamelen, 'utf8');
state.requests[reqid] = { cb: cb };
this.debug('DEBUG[SFTP]: Outgoing: Writing REMOVE');
return this.push(buf);
}n/a
utimes = function (path, atime, mtime, cb) {
return this.setstat(path, {
atime: toUnixTimestamp(atime),
mtime: toUnixTimestamp(mtime)
}, cb);
}n/a
writeData = function (handle, buf, off, len, position, cb) {
if (this.server)
throw new Error('Client-only method called in server mode');
else if (!Buffer.isBuffer(handle))
throw new Error('handle is not a Buffer');
else if (!Buffer.isBuffer(buf))
throw new Error('buffer is not a Buffer');
else if (off > buf.length)
throw new Error('offset is out of bounds');
else if (off + len > buf.length)
throw new Error('length extends beyond buffer');
else if (position === null)
throw new Error('null position currently unsupported');
var self = this;
var state = this._state;
if (!len) {
cb && process.nextTick(function() { cb(undefined, 0); });
return;
}
var overflow = (len > state.maxDataLen
? len - state.maxDataLen
: 0);
var origPosition = position;
if (overflow)
len = state.maxDataLen;
/*
uint32 id
string handle
uint64 offset
string data
*/
var handlelen = handle.length;
var p = 9;
var out = new Buffer(4 + 1 + 4 + 4 + handlelen + 8 + 4 + len);
out.writeUInt32BE(out.length - 4, 0, true);
out[4] = REQUEST.WRITE;
var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID;
out.writeUInt32BE(reqid, 5, true);
out.writeUInt32BE(handlelen, p, true);
handle.copy(out, p += 4);
p += handlelen;
for (var i = 7; i >= 0; --i) {
out[p + i] = position & 0xFF;
position /= 256;
}
out.writeUInt32BE(len, p += 8, true);
buf.copy(out, p += 4, off, off + len);
state.requests[reqid] = {
cb: function(err) {
if (err)
cb && cb(err);
else if (overflow) {
self.writeData(handle,
buf,
off + len,
overflow,
origPosition + len,
cb);
} else
cb && cb(undefined, off + len);
}
};
this.debug('DEBUG[SFTP]: Outgoing: Writing WRITE');
return this.push(out);
}n/a
writeFile = function (path, data, options, callback_) {
if (this.server)
throw new Error('Client-only method called in server mode');
var callback;
if (typeof callback_ === 'function') {
callback = callback_;
} else if (typeof options === 'function') {
callback = options;
options = undefined;
}
var self = this;
if (typeof options === 'string')
options = { encoding: options, mode: 438, flag: 'w' };
else if (!options)
options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' };
else if (typeof options !== 'object')
throw new TypeError('Bad arguments');
if (options.encoding && !Buffer.isEncoding(options.encoding))
throw new Error('Unknown encoding: ' + options.encoding);
var flag = options.flag || 'w';
this.open(path, flag, options.mode, function(openErr, handle) {
if (openErr)
callback && callback(openErr);
else {
var buffer = (Buffer.isBuffer(data)
? data
: new Buffer('' + data, options.encoding || 'utf8'));
var position = (/a/.test(flag) ? null : 0);
// SFTPv3 does not support the notion of 'current position'
// (null position), so we just attempt to append to the end of the file
// instead
if (position === null) {
self.fstat(handle, function tryStat(er, st) {
if (er) {
// Try stat() for sftp servers that may not support fstat() for
// whatever reason
self.stat(path, function(er_, st_) {
if (er_) {
return self.close(handle, function() {
callback && callback(er);
});
}
tryStat(null, st_);
});
return;
}
writeAll(self, handle, buffer, 0, buffer.length, st.size, callback);
});
return;
}
writeAll(self, handle, buffer, 0, buffer.length, position, callback);
}
});
}n/a
function SSH2Stream(cfg) {
if (typeof cfg !== 'object' || cfg === null)
cfg = {};
TransformStream.call(this, {
highWaterMark: (typeof cfg.highWaterMark === 'number'
? cfg.highWaterMark
: 32 * 1024)
});
this._needContinue = false;
this.bytesSent = this.bytesReceived = 0;
this.debug = (typeof cfg.debug === 'function' ? cfg.debug : DEBUG_NOOP);
this.server = (cfg.server === true);
this.maxPacketSize = (typeof cfg.maxPacketSize === 'number'
? cfg.maxPacketSize
: MAX_PACKET_SIZE);
// Bitmap that indicates any bugs the remote side has. This is determined
// by the reported software version.
this.remoteBugs = 0;
if (this.server) {
// TODO: Remove when we support group exchange for server implementation
this.remoteBugs = BUGS.BAD_DHGEX;
}
var self = this;
var hostKeys = cfg.hostKeys;
if (this.server && (typeof hostKeys !== 'object' || hostKeys === null))
throw new Error('hostKeys must be an object keyed on host key type');
this.config = {
// Server
hostKeys: hostKeys, // All keys supported by server
// Client/Server
ident: 'SSH-2.0-'
+ (cfg.ident
|| ('ssh2js' + MODULE_VER + (this.server ? 'srv' : ''))),
algorithms: {
kex: ALGORITHMS.KEX,
kexBuf: ALGORITHMS.KEX_BUF,
serverHostKey: ALGORITHMS.SERVER_HOST_KEY,
serverHostKeyBuf: ALGORITHMS.SERVER_HOST_KEY_BUF,
cipher: ALGORITHMS.CIPHER,
cipherBuf: ALGORITHMS.CIPHER_BUF,
hmac: ALGORITHMS.HMAC,
hmacBuf: ALGORITHMS.HMAC_BUF,
compress: ALGORITHMS.COMPRESS,
compressBuf: ALGORITHMS.COMPRESS_BUF
}
};
// RFC 4253 states the identification string must not contain NULL
this.config.ident.replace(RE_NULL, '');
if (this.config.ident.length + 2 /* Account for "\r\n" */ > 255)
throw new Error('ident too long');
if (typeof cfg.algorithms === 'object' && cfg.algorithms !== null) {
var algos = cfg.algorithms;
if (Array.isArray(algos.kex) && algos.kex.length > 0) {
this.config.algorithms.kex = algos.kex;
if (!Buffer.isBuffer(algos.kexBuf))
algos.kexBuf = new Buffer(algos.kex.join(','), 'ascii');
this.config.algorithms.kexBuf = algos.kexBuf;
}
if (Array.isArray(algos.serverHostKey) && algos.serverHostKey.length > 0) {
this.config.algorithms.serverHostKey = algos.serverHostKey;
if (!Buffer.isBuffer(algos.serverHostKeyBuf)) {
algos.serverHostKeyBuf = new Buffer(algos.serverHostKey.join(','),
'ascii');
}
this.config.algorithms.serverHostKeyBuf = algos.serverHostKeyBuf;
}
if (Array.isArray(algos.cipher) && algos.cipher.length > 0) {
this.config.algorithms.cipher = algos.cipher;
if (!Buffer.isBuffer(algos.cipherBuf))
algos.cipherBuf = new Buffer(algos.cipher.join(','), 'ascii');
this.config.algorithms.cipherBuf = algos.cipherBuf;
}
if (Array.isArray(algos.hmac) && algos.hmac.length > 0) {
this.config.algorithms.hmac = algos.hmac;
if (!Buffer.isBuffer(algos.hmacBuf))
algos.hmacBuf = new Buffer(algos.hmac.join(','), 'ascii');
this.config.algorithms.hmacBuf = algos.hmacBuf;
}
if (Array.isArray(algos.compress) && algos.compress.length > 0) {
this.config.algorithms.compress = algos.compress;
if (!Buffer.isBuffer(algos.compressBuf))
algos.compressBuf = new Buffer(algos.compress.join(','), 'ascii');
this.config.algorithms.compressBuf = algos.compressBuf;
}
}
this.reset(true);
// Common events
this.on('end', function() {
// Let GC collect any Buffers we were previously storing
self._state = undefined;
self.reset();
self._state.incoming.hmac.bufCompute = undefined;
self._state.outgoing.bufSeqno = undefined;
});
this.on('DISCONNECT', function(reason, code, desc, lang) {
onDISCONNECT(self, reason, code, desc, lang);
});
this.on('KEXINIT', function(init, firstFollows) {
onKEXINIT(self, ...n/a
function send(self, payload, cb, bypass) {
var state = self._state;
if (!state)
return false;
var outstate = state.outgoing;
if (outstate.status === OUT_REKEYING && !bypass) {
if (typeof cb === 'function')
outstate.rekeyQueue.push([payload, cb]);
else
outstate.rekeyQueue.push(payload);
return false;
} else if (self._readableState.ended || self._writableState.ended)
return false;
var compress = outstate.compress.instance;
if (compress) {
compress.write(payload);
compress.flush(Z_PARTIAL_FLUSH, function() {
if (self._readableState.ended || self._writableState.ended)
return;
send_(self, compress.read(), cb);
});
return true;
} else
return send_(self, payload, cb);
}n/a
function Transform(options) {
if (!(this instanceof Transform))
return new Transform(options);
Duplex.call(this, options);
this._transformState = new TransformState(this);
var stream = this;
// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
// we have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false;
if (options) {
if (typeof options.transform === 'function')
this._transform = options.transform;
if (typeof options.flush === 'function')
this._flush = options.flush;
}
// When the writable side finishes, then flush out anything remaining.
this.once('prefinish', function() {
if (typeof this._flush === 'function')
this._flush(function(er) {
done(stream, er);
});
else
done(stream);
});
}n/a
__push = function (chunk, encoding) {
this._transformState.needTransform = false;
return Duplex.prototype.push.call(this, chunk, encoding);
}n/a
__read = function (n) {
var ts = this._transformState;
if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
ts.transforming = true;
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
} else {
// mark that we need a transform, so that any data that comes in
// will get processed, now that we've asked for it.
ts.needTransform = true;
}
}n/a
_cleanup = function (callback) {
this.reset();
this.debug('DEBUG: Parser: Malformed packet');
callback && callback(new Error('Malformed packet'));
}n/a
_read = function (n) {
if (this._needContinue) {
this._needContinue = false;
this.emit('continue');
}
return this.__read(n);
}n/a
_transform = function (chunk, encoding, callback, decomp) {
var skipDecrypt = false;
var doDecryptGCM = false;
var state = this._state;
var instate = state.incoming;
var outstate = state.outgoing;
var expect = instate.expect;
var decrypt = instate.decrypt;
var decompress = instate.decompress;
var chlen = chunk.length;
var chleft = 0;
var debug = this.debug;
var self = this;
var i = 0;
var p = i;
var buffer;
var buf;
var r;
this.bytesReceived += chlen;
while (true) {
if (expect.type !== undefined) {
if (i >= chlen)
break;
if (expect.type === EXP_TYPE_BYTES) {
chleft = (chlen - i);
var pktLeft = (expect.buf.length - expect.ptr);
if (pktLeft <= chleft) {
chunk.copy(expect.buf, expect.ptr, i, i + pktLeft);
i += pktLeft;
buffer = expect.buf;
expect.buf = undefined;
expect.ptr = 0;
expect.type = undefined;
} else {
chunk.copy(expect.buf, expect.ptr, i);
expect.ptr += chleft;
i += chleft;
}
continue;
} else if (expect.type === EXP_TYPE_HEADER) {
i += instate.search.push(chunk);
if (expect.type !== undefined)
continue;
} else if (expect.type === EXP_TYPE_LF) {
if (++expect.ptr + 4 /* Account for "SSH-" */ > 255) {
this.reset();
debug('DEBUG: Parser: Identification string exceeded 255 characters');
return callback(new Error('Max identification string size exceeded'));
}
if (chunk[i] === 0x0A) {
expect.type = undefined;
if (p < i) {
if (expect.buf === undefined)
expect.buf = chunk.toString('ascii', p, i);
else
expect.buf += chunk.toString('ascii', p, i);
}
buffer = expect.buf;
expect.buf = undefined;
++i;
} else {
if (++i === chlen && p < i) {
if (expect.buf === undefined)
expect.buf = chunk.toString('ascii', p, i);
else
expect.buf += chunk.toString('ascii', p, i);
}
continue;
}
}
}
if (instate.status === IN_INIT) {
if (this.server) {
// Retrieve what should be the start of the protocol version exchange
if (!buffer) {
debug('DEBUG: Parser: IN_INIT (waiting for identification begin)');
expectData(this, EXP_TYPE_BYTES, 4);
} else {
if (buffer[0] === 0x53 // S
&& buffer[1] === 0x53 // S
&& buffer[2] === 0x48 // H
&& buffer[3] === 0x2D) { // -
instate.status = IN_GREETING;
debug('DEBUG: Parser: IN_INIT (waiting for rest of identification)');
} else {
this.reset();
debug('DEBUG: Parser: Bad identification start');
return callback(new Error('Bad identification start'));
}
}
} else {
debug('DEBUG: Parser: IN_INIT');
// Retrieve any bytes that may come before the protocol version exchange
var ss = instate.search = new StreamSearch(IDENT_PREFIX_BUFFER);
ss.on('info', function onInfo(matched, data, start, end) {
if (data) {
if (instate.greeting === undefined)
instate.greeting = data.toString('binary', start, end);
else
instate.greeting += data.toString('binary', start, end);
}
if (matched) {
expect.type = undefined;
instate.search.removeListener('info', onInfo);
}
});
ss.maxMatches = 1;
expectData(this, EXP_TYPE_HEADER);
instate.status = IN_GREETING;
}
} else if (instate.status === IN_GREETING) {
debug('DEBUG: Parser: IN_GREETING');
instate.search = undefined;
// Retrieve the identification bytes after the "SSH-" header
p = i;
expectData(this, EXP_TYPE_LF);
instate.status = IN_HEADER;
} else if (instate.status === ...n/a
authFailure = function (authMethods, isPartial) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var authsQueue = this._state.authsQueue;
if (!authsQueue.length)
throw new Error('No auth in progress');
var methods;
if (typeof authMethods === 'boolean') {
isPartial = authMethods;
authMethods = undefined;
}
if (authMethods) {
methods = [];
for (var i = 0, len = authMethods.length; i < len; ++i) {
if (authMethods[i].toLowerCase() === 'none')
continue;
methods.push(authMethods[i]);
}
methods = methods.join(',');
} else
methods = '';
var methodsLen = methods.length;
var buf = new Buffer(1 + 4 + methodsLen + 1);
buf[0] = MESSAGE.USERAUTH_FAILURE;
buf.writeUInt32BE(methodsLen, 1, true);
buf.write(methods, 5, methodsLen, 'ascii');
buf[5 + methodsLen] = (isPartial === true ? 1 : 0);
this._state.authsQueue.shift();
this.debug('DEBUG: Outgoing: Writing USERAUTH_FAILURE');
return send(this, buf);
}n/a
authHostbased = function (username, pubKey, hostname, userlocal, cbSign) {
// TODO: Make DRY by sharing similar code with authPK()
if (this.server)
throw new Error('Client-only method called in server mode');
var self = this;
var outstate = this._state.outgoing;
var pubKeyFullType;
if (pubKey.public) {
pubKeyFullType = pubKey.fulltype;
pubKey = pubKey.public;
} else {
pubKeyFullType = pubKey.toString('ascii',
4,
4 + pubKey.readUInt32BE(0, true));
}
var userLen = Buffer.byteLength(username);
var algoLen = Buffer.byteLength(pubKeyFullType);
var pubKeyLen = pubKey.length;
var sesLen = outstate.sessionId.length;
var hostnameLen = Buffer.byteLength(hostname);
var userlocalLen = Buffer.byteLength(userlocal);
var p = 0;
var buf = new Buffer(4 + sesLen
+ 1
+ 4 + userLen
+ 4 + 14 // "ssh-connection"
+ 4 + 9 // "hostbased"
+ 4 + algoLen
+ 4 + pubKeyLen
+ 4 + hostnameLen
+ 4 + userlocalLen
);
buf.writeUInt32BE(sesLen, p, true);
outstate.sessionId.copy(buf, p += 4);
buf[p += sesLen] = MESSAGE.USERAUTH_REQUEST;
buf.writeUInt32BE(userLen, ++p, true);
buf.write(username, p += 4, userLen, 'utf8');
buf.writeUInt32BE(14, p += userLen, true);
buf.write('ssh-connection', p += 4, 14, 'ascii');
buf.writeUInt32BE(9, p += 14, true);
buf.write('hostbased', p += 4, 9, 'ascii');
buf.writeUInt32BE(algoLen, p += 9, true);
buf.write(pubKeyFullType, p += 4, algoLen, 'ascii');
buf.writeUInt32BE(pubKeyLen, p += algoLen, true);
pubKey.copy(buf, p += 4);
buf.writeUInt32BE(hostnameLen, p += pubKeyLen, true);
buf.write(hostname, p += 4, hostnameLen, 'ascii');
buf.writeUInt32BE(userlocalLen, p += hostnameLen, true);
buf.write(userlocal, p += 4, userlocalLen, 'utf8');
cbSign(buf, function(signature) {
if (pubKeyFullType === 'ssh-dss') {
signature = DSASigBERToBare(signature);
} else if (pubKeyFullType !== 'ssh-rsa') {
// ECDSA
signature = ECDSASigASN1ToSSH(signature);
}
var sigLen = signature.length;
var sigbuf = new Buffer((buf.length - sesLen) + sigLen);
buf.copy(sigbuf, 0, 4 + sesLen);
sigbuf.writeUInt32BE(sigLen, sigbuf.length - sigLen - 4, true);
signature.copy(sigbuf, sigbuf.length - sigLen);
self._state.authsQueue.push('hostbased');
self.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (hostbased)');
return send(self, sigbuf);
});
return true;
}n/a
authInfoReq = function (name, instructions, prompts) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var promptsLen = 0;
var nameLen = name ? Buffer.byteLength(name) : 0;
var instrLen = instructions ? Buffer.byteLength(instructions) : 0;
var p = 0;
var promptLen;
var prompt;
var len;
var i;
for (i = 0, len = prompts.length; i < len; ++i)
promptsLen += 4 + Buffer.byteLength(prompts[i].prompt) + 1;
var buf = new Buffer(1 + 4 + nameLen + 4 + instrLen + 4 + 4 + promptsLen);
buf[p++] = MESSAGE.USERAUTH_INFO_REQUEST;
buf.writeUInt32BE(nameLen, p, true);
p += 4;
if (name) {
buf.write(name, p, nameLen, 'utf8');
p += nameLen;
}
buf.writeUInt32BE(instrLen, p, true);
p += 4;
if (instructions) {
buf.write(instructions, p, instrLen, 'utf8');
p += instrLen;
}
buf.writeUInt32BE(0, p, true);
p += 4;
buf.writeUInt32BE(prompts.length, p, true);
p += 4;
for (i = 0, len = prompts.length; i < len; ++i) {
prompt = prompts[i];
promptLen = Buffer.byteLength(prompt.prompt);
buf.writeUInt32BE(promptLen, p, true);
p += 4;
if (promptLen) {
buf.write(prompt.prompt, p, promptLen, 'utf8');
p += promptLen;
}
buf[p++] = (prompt.echo ? 1 : 0);
}
this.debug('DEBUG: Outgoing: Writing USERAUTH_INFO_REQUEST');
return send(this, buf);
}n/a
authInfoRes = function (responses) {
if (this.server)
throw new Error('Client-only method called in server mode');
var responsesLen = 0;
var p = 0;
var resLen;
var len;
var i;
if (responses) {
for (i = 0, len = responses.length; i < len; ++i)
responsesLen += 4 + Buffer.byteLength(responses[i]);
}
var buf = new Buffer(1 + 4 + responsesLen);
buf[p++] = MESSAGE.USERAUTH_INFO_RESPONSE;
buf.writeUInt32BE(responses ? responses.length : 0, p, true);
if (responses) {
p += 4;
for (i = 0, len = responses.length; i < len; ++i) {
resLen = Buffer.byteLength(responses[i]);
buf.writeUInt32BE(resLen, p, true);
p += 4;
if (resLen) {
buf.write(responses[i], p, resLen, 'utf8');
p += resLen;
}
}
}
this.debug('DEBUG: Outgoing: Writing USERAUTH_INFO_RESPONSE');
return send(this, buf);
}n/a
authKeyboard = function (username) {
if (this.server)
throw new Error('Client-only method called in server mode');
var userLen = Buffer.byteLength(username);
var p = 0;
var buf = new Buffer(1
+ 4 + userLen
+ 4 + 14 // "ssh-connection"
+ 4 + 20 // "keyboard-interactive"
+ 4 // no language set
+ 4 // no submethods
);
buf[p] = MESSAGE.USERAUTH_REQUEST;
buf.writeUInt32BE(userLen, ++p, true);
buf.write(username, p += 4, userLen, 'utf8');
buf.writeUInt32BE(14, p += userLen, true);
buf.write('ssh-connection', p += 4, 14, 'ascii');
buf.writeUInt32BE(20, p += 14, true);
buf.write('keyboard-interactive', p += 4, 20, 'ascii');
buf.writeUInt32BE(0, p += 20, true);
buf.writeUInt32BE(0, p += 4, true);
this._state.authsQueue.push('keyboard-interactive');
this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (keyboard-interactive)');
return send(this, buf);
}n/a
authNone = function (username) {
if (this.server)
throw new Error('Client-only method called in server mode');
var userLen = Buffer.byteLength(username);
var p = 0;
var buf = new Buffer(1
+ 4 + userLen
+ 4 + 14 // "ssh-connection"
+ 4 + 4 // "none"
);
buf[p] = MESSAGE.USERAUTH_REQUEST;
buf.writeUInt32BE(userLen, ++p, true);
buf.write(username, p += 4, userLen, 'utf8');
buf.writeUInt32BE(14, p += userLen, true);
buf.write('ssh-connection', p += 4, 14, 'ascii');
buf.writeUInt32BE(4, p += 14, true);
buf.write('none', p += 4, 4, 'ascii');
this._state.authsQueue.push('none');
this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (none)');
return send(this, buf);
}n/a
authPK = function (username, pubKey, cbSign) {
if (this.server)
throw new Error('Client-only method called in server mode');
var self = this;
var outstate = this._state.outgoing;
var pubKeyFullType;
if (pubKey.public) {
pubKeyFullType = pubKey.fulltype;
pubKey = pubKey.public;
} else {
pubKeyFullType = pubKey.toString('ascii',
4,
4 + pubKey.readUInt32BE(0, true));
}
var userLen = Buffer.byteLength(username);
var algoLen = Buffer.byteLength(pubKeyFullType);
var pubKeyLen = pubKey.length;
var sesLen = outstate.sessionId.length;
var p = 0;
var buf = new Buffer((cbSign ? 4 + sesLen : 0)
+ 1
+ 4 + userLen
+ 4 + 14 // "ssh-connection"
+ 4 + 9 // "publickey"
+ 1
+ 4 + algoLen
+ 4 + pubKeyLen
);
if (cbSign) {
buf.writeUInt32BE(sesLen, p, true);
outstate.sessionId.copy(buf, p += 4);
buf[p += sesLen] = MESSAGE.USERAUTH_REQUEST;
} else
buf[p] = MESSAGE.USERAUTH_REQUEST;
buf.writeUInt32BE(userLen, ++p, true);
buf.write(username, p += 4, userLen, 'utf8');
buf.writeUInt32BE(14, p += userLen, true);
buf.write('ssh-connection', p += 4, 14, 'ascii');
buf.writeUInt32BE(9, p += 14, true);
buf.write('publickey', p += 4, 9, 'ascii');
buf[p += 9] = (cbSign ? 1 : 0);
buf.writeUInt32BE(algoLen, ++p, true);
buf.write(pubKeyFullType, p += 4, algoLen, 'ascii');
buf.writeUInt32BE(pubKeyLen, p += algoLen, true);
pubKey.copy(buf, p += 4);
if (!cbSign) {
this._state.authsQueue.push('publickey');
this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (publickey -- check)');
return send(this, buf);
}
cbSign(buf, function(signature) {
if (pubKeyFullType === 'ssh-dss') {
signature = DSASigBERToBare(signature);
} else if (pubKeyFullType !== 'ssh-rsa') {
// ECDSA
signature = ECDSASigASN1ToSSH(signature);
}
var sigLen = signature.length;
var sigbuf = new Buffer(1
+ 4 + userLen
+ 4 + 14 // "ssh-connection"
+ 4 + 9 // "publickey"
+ 1
+ 4 + algoLen
+ 4 + pubKeyLen
+ 4 // 4 + algoLen + 4 + sigLen
+ 4 + algoLen
+ 4 + sigLen);
p = 0;
sigbuf[p] = MESSAGE.USERAUTH_REQUEST;
sigbuf.writeUInt32BE(userLen, ++p, true);
sigbuf.write(username, p += 4, userLen, 'utf8');
sigbuf.writeUInt32BE(14, p += userLen, true);
sigbuf.write('ssh-connection', p += 4, 14, 'ascii');
sigbuf.writeUInt32BE(9, p += 14, true);
sigbuf.write('publickey', p += 4, 9, 'ascii');
sigbuf[p += 9] = 1;
sigbuf.writeUInt32BE(algoLen, ++p, true);
sigbuf.write(pubKeyFullType, p += 4, algoLen, 'ascii');
sigbuf.writeUInt32BE(pubKeyLen, p += algoLen, true);
pubKey.copy(sigbuf, p += 4);
sigbuf.writeUInt32BE(4 + algoLen + 4 + sigLen, p += pubKeyLen, true);
sigbuf.writeUInt32BE(algoLen, p += 4, true);
sigbuf.write(pubKeyFullType, p += 4, algoLen, 'ascii');
sigbuf.writeUInt32BE(sigLen, p += algoLen, true);
signature.copy(sigbuf, p += 4);
// Servers shouldn't send packet type 60 in response to signed publickey
// attempts, but if they do, interpret as type 60.
self._state.authsQueue.push('publickey');
self.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (publickey)');
return send(self, sigbuf);
});
return true;
}n/a
authPKOK = function (keyAlgo, key) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var authsQueue = this._state.authsQueue;
if (!authsQueue.length || authsQueue[0] !== 'publickey')
throw new Error('"publickey" auth not in progress');
var keyAlgoLen = keyAlgo.length;
var keyLen = key.length;
var buf = new Buffer(1 + 4 + keyAlgoLen + 4 + keyLen);
buf[0] = MESSAGE.USERAUTH_PK_OK;
buf.writeUInt32BE(keyAlgoLen, 1, true);
buf.write(keyAlgo, 5, keyAlgoLen, 'ascii');
buf.writeUInt32BE(keyLen, 5 + keyAlgoLen, true);
key.copy(buf, 5 + keyAlgoLen + 4);
this._state.authsQueue.shift();
this.debug('DEBUG: Outgoing: Writing USERAUTH_PK_OK');
return send(this, buf);
}n/a
authPasswdChg = function (prompt, lang) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var promptLen = Buffer.byteLength(prompt);
var langLen = lang ? lang.length : 0;
var p = 0;
var buf = new Buffer(1 + 4 + promptLen + 4 + langLen);
buf[p] = MESSAGE.USERAUTH_PASSWD_CHANGEREQ;
buf.writeUInt32BE(promptLen, ++p, true);
buf.write(prompt, p += 4, promptLen, 'utf8');
buf.writeUInt32BE(langLen, p += promptLen, true);
if (langLen)
buf.write(lang, p += 4, langLen, 'ascii');
this.debug('DEBUG: Outgoing: Writing USERAUTH_PASSWD_CHANGEREQ');
return send(this, buf);
}n/a
authPassword = function (username, password) {
if (this.server)
throw new Error('Client-only method called in server mode');
var userLen = Buffer.byteLength(username);
var passLen = Buffer.byteLength(password);
var p = 0;
var buf = new Buffer(1
+ 4 + userLen
+ 4 + 14 // "ssh-connection"
+ 4 + 8 // "password"
+ 1
+ 4 + passLen);
buf[p] = MESSAGE.USERAUTH_REQUEST;
buf.writeUInt32BE(userLen, ++p, true);
buf.write(username, p += 4, userLen, 'utf8');
buf.writeUInt32BE(14, p += userLen, true);
buf.write('ssh-connection', p += 4, 14, 'ascii');
buf.writeUInt32BE(8, p += 14, true);
buf.write('password', p += 4, 8, 'ascii');
buf[p += 8] = 0;
buf.writeUInt32BE(passLen, ++p, true);
buf.write(password, p += 4, passLen, 'utf8');
this._state.authsQueue.push('password');
this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (password)');
return send(this, buf);
}n/a
authSuccess = function () {
if (!this.server)
throw new Error('Server-only method called in client mode');
var authsQueue = this._state.authsQueue;
if (!authsQueue.length)
throw new Error('No auth in progress');
this._state.authsQueue.shift();
this.debug('DEBUG: Outgoing: Writing USERAUTH_SUCCESS');
return send(this, USERAUTH_SUCCESS_PACKET);
}n/a
cancelTcpipForward = function (bindAddr, bindPort, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
var addrlen = Buffer.byteLength(bindAddr);
var buf = new Buffer(1 + 4 + 20 + 1 + 4 + addrlen + 4);
buf[0] = MESSAGE.GLOBAL_REQUEST;
buf.writeUInt32BE(20, 1, true);
buf.write('cancel-tcpip-forward', 5, 20, 'ascii');
buf[25] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf.writeUInt32BE(addrlen, 26, true);
buf.write(bindAddr, 30, addrlen, 'ascii');
buf.writeUInt32BE(bindPort, 30 + addrlen, true);
this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (cancel-tcpip-forward)');
return send(this, buf);
}n/a
channelClose = function (chan) {
// Does not consume window space
var buf = new Buffer(1 + 4);
buf[0] = MESSAGE.CHANNEL_CLOSE;
buf.writeUInt32BE(chan, 1, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_CLOSE (' + chan + ')');
return send(this, buf);
}n/a
channelData = function (chan, data) {
var dataIsBuffer = Buffer.isBuffer(data);
var dataLen = (dataIsBuffer ? data.length : Buffer.byteLength(data));
var buf = new Buffer(1 + 4 + 4 + dataLen);
buf[0] = MESSAGE.CHANNEL_DATA;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(dataLen, 5, true);
if (dataIsBuffer)
data.copy(buf, 9);
else
buf.write(data, 9, dataLen, 'utf8');
this.debug('DEBUG: Outgoing: Writing CHANNEL_DATA (' + chan + ')');
return send(this, buf);
}n/a
channelEOF = function (chan) {
// Does not consume window space
var buf = new Buffer(1 + 4);
buf[0] = MESSAGE.CHANNEL_EOF;
buf.writeUInt32BE(chan, 1, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_EOF (' + chan + ')');
return send(this, buf);
}n/a
channelExtData = function (chan, data, type) {
var dataIsBuffer = Buffer.isBuffer(data);
var dataLen = (dataIsBuffer ? data.length : Buffer.byteLength(data));
var buf = new Buffer(1 + 4 + 4 + 4 + dataLen);
buf[0] = MESSAGE.CHANNEL_EXTENDED_DATA;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(type, 5, true);
buf.writeUInt32BE(dataLen, 9, true);
if (dataIsBuffer)
data.copy(buf, 13);
else
buf.write(data, 13, dataLen, 'utf8');
this.debug('DEBUG: Outgoing: Writing CHANNEL_EXTENDED_DATA (' + chan + ')');
return send(this, buf);
}n/a
channelFailure = function (chan) {
// Does not consume window space
var buf = new Buffer(1 + 4);
buf[0] = MESSAGE.CHANNEL_FAILURE;
buf.writeUInt32BE(chan, 1, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_FAILURE (' + chan + ')');
return send(this, buf);
}n/a
channelOpenConfirm = function (remoteChan, localChan, initWindow, maxPacket) {
var buf = new Buffer(1 + 4 + 4 + 4 + 4);
buf[0] = MESSAGE.CHANNEL_OPEN_CONFIRMATION;
buf.writeUInt32BE(remoteChan, 1, true);
buf.writeUInt32BE(localChan, 5, true);
buf.writeUInt32BE(initWindow, 9, true);
buf.writeUInt32BE(maxPacket, 13, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN_CONFIRMATION (r:'
+ remoteChan
+ ', l:'
+ localChan
+ ')');
return send(this, buf);
}n/a
channelOpenFail = function (remoteChan, reason, desc, lang) {
if (typeof desc !== 'string')
desc = '';
if (typeof lang !== 'string')
lang = '';
var descLen = Buffer.byteLength(desc);
var langLen = Buffer.byteLength(lang);
var p = 9;
var buf = new Buffer(1 + 4 + 4 + 4 + descLen + 4 + langLen);
buf[0] = MESSAGE.CHANNEL_OPEN_FAILURE;
buf.writeUInt32BE(remoteChan, 1, true);
buf.writeUInt32BE(reason, 5, true);
buf.writeUInt32BE(descLen, p, true);
p += 4;
if (descLen) {
buf.write(desc, p, descLen, 'utf8');
p += descLen;
}
buf.writeUInt32BE(langLen, p, true);
if (langLen)
buf.write(lang, p += 4, langLen, 'ascii');
this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN_FAILURE ('
+ remoteChan
+ ')');
return send(this, buf);
}n/a
channelSuccess = function (chan) {
// Does not consume window space
var buf = new Buffer(1 + 4);
buf[0] = MESSAGE.CHANNEL_SUCCESS;
buf.writeUInt32BE(chan, 1, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_SUCCESS (' + chan + ')');
return send(this, buf);
}n/a
channelWindowAdjust = function (chan, amount) {
// Does not consume window space
var buf = new Buffer(1 + 4 + 4);
buf[0] = MESSAGE.CHANNEL_WINDOW_ADJUST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(amount, 5, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_WINDOW_ADJUST ('
+ chan
+ ', '
+ amount
+ ')');
return send(this, buf);
}n/a
directTcpip = function (chan, initWindow, maxPacket, cfg) {
if (this.server)
throw new Error('Client-only method called in server mode');
var srclen = Buffer.byteLength(cfg.srcIP);
var dstlen = Buffer.byteLength(cfg.dstIP);
var p = 29;
var buf = new Buffer(1 + 4 + 12 + 4 + 4 + 4 + 4 + srclen + 4 + 4 + dstlen
+ 4);
buf[0] = MESSAGE.CHANNEL_OPEN;
buf.writeUInt32BE(12, 1, true);
buf.write('direct-tcpip', 5, 12, 'ascii');
buf.writeUInt32BE(chan, 17, true);
buf.writeUInt32BE(initWindow, 21, true);
buf.writeUInt32BE(maxPacket, 25, true);
buf.writeUInt32BE(dstlen, p, true);
buf.write(cfg.dstIP, p += 4, dstlen, 'ascii');
buf.writeUInt32BE(cfg.dstPort, p += dstlen, true);
buf.writeUInt32BE(srclen, p += 4, true);
buf.write(cfg.srcIP, p += 4, srclen, 'ascii');
buf.writeUInt32BE(cfg.srcPort, p += srclen, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
+ chan
+ ', direct-tcpip)');
return send(this, buf);
}n/a
disconnect = function (reason) {
/*
byte SSH_MSG_DISCONNECT
uint32 reason code
string description in ISO-10646 UTF-8 encoding
string language tag
*/
var buf = new Buffer(1 + 4 + 4 + 4);
buf.fill(0);
buf[0] = MESSAGE.DISCONNECT;
if (DISCONNECT_REASON[reason] === undefined)
reason = DISCONNECT_REASON.BY_APPLICATION;
buf.writeUInt32BE(reason, 1, true);
this.debug('DEBUG: Outgoing: Writing DISCONNECT ('
+ DISCONNECT_REASON[reason]
+ ')');
send(this, buf);
this.reset();
return false;
}n/a
env = function (chan, key, val, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
var keyLen = Buffer.byteLength(key);
var valLen = (Buffer.isBuffer(val) ? val.length : Buffer.byteLength(val));
var buf = new Buffer(1 + 4 + 4 + 3 + 1 + 4 + keyLen + 4 + valLen);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(3, 5, true);
buf.write('env', 9, 3, 'ascii');
buf[12] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf.writeUInt32BE(keyLen, 13, true);
buf.write(key, 17, keyLen, 'ascii');
buf.writeUInt32BE(valLen, 17 + keyLen, true);
if (Buffer.isBuffer(val))
val.copy(buf, 17 + keyLen + 4);
else
buf.write(val, 17 + keyLen + 4, valLen, 'utf8');
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', env)');
return send(this, buf);
}n/a
exec = function (chan, cmd, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
var cmdlen = (Buffer.isBuffer(cmd) ? cmd.length : Buffer.byteLength(cmd));
var buf = new Buffer(1 + 4 + 4 + 4 + 1 + 4 + cmdlen);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(4, 5, true);
buf.write('exec', 9, 4, 'ascii');
buf[13] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf.writeUInt32BE(cmdlen, 14, true);
if (Buffer.isBuffer(cmd))
cmd.copy(buf, 18);
else
buf.write(cmd, 18, cmdlen, 'utf8');
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', exec)');
return send(this, buf);
}...
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')) {
...exitSignal = function (chan, name, coreDumped, msg) {
if (!this.server)
throw new Error('Server-only method called in client mode');
// Does not consume window space
var nameLen = Buffer.byteLength(name);
var msgLen = (msg ? Buffer.byteLength(msg) : 0);
var p = 25 + nameLen;
var buf = new Buffer(1 + 4 + 4 + 11 + 1 + 4 + nameLen + 1 + 4 + msgLen + 4);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(11, 5, true);
buf.write('exit-signal', 9, 11, 'ascii');
buf[20] = 0;
buf.writeUInt32BE(nameLen, 21, true);
buf.write(name, 25, nameLen, 'utf8');
buf[p++] = (coreDumped ? 1 : 0);
buf.writeUInt32BE(msgLen, p, true);
p += 4;
if (msgLen) {
buf.write(msg, p, msgLen, 'utf8');
p += msgLen;
}
buf.writeUInt32BE(0, p, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', exit-signal)');
return send(this, buf);
}n/a
exitStatus = function (chan, status) {
if (!this.server)
throw new Error('Server-only method called in client mode');
// Does not consume window space
var buf = new Buffer(1 + 4 + 4 + 11 + 1 + 4);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(11, 5, true);
buf.write('exit-status', 9, 11, 'ascii');
buf[20] = 0;
buf.writeUInt32BE(status, 21, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', exit-status)');
return send(this, buf);
}n/a
forwardedTcpip = function (chan, initWindow, maxPacket, cfg) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var boundAddrLen = Buffer.byteLength(cfg.boundAddr);
var remoteAddrLen = Buffer.byteLength(cfg.remoteAddr);
var p = 36 + boundAddrLen;
var buf = new Buffer(1 + 4 + 15 + 4 + 4 + 4 + 4 + boundAddrLen + 4 + 4
+ remoteAddrLen + 4);
buf[0] = MESSAGE.CHANNEL_OPEN;
buf.writeUInt32BE(15, 1, true);
buf.write('forwarded-tcpip', 5, 15, 'ascii');
buf.writeUInt32BE(chan, 20, true);
buf.writeUInt32BE(initWindow, 24, true);
buf.writeUInt32BE(maxPacket, 28, true);
buf.writeUInt32BE(boundAddrLen, 32, true);
buf.write(cfg.boundAddr, 36, boundAddrLen, 'ascii');
buf.writeUInt32BE(cfg.boundPort, p, true);
buf.writeUInt32BE(remoteAddrLen, p += 4, true);
buf.write(cfg.remoteAddr, p += 4, remoteAddrLen, 'ascii');
buf.writeUInt32BE(cfg.remotePort, p += remoteAddrLen, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
+ chan
+ ', forwarded-tcpip)');
return send(this, buf);
}n/a
openssh_agentForward = function (chan, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
var buf = new Buffer(1 + 4 + 4 + 26 + 1);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(26, 5, true);
buf.write('auth-agent-req@openssh.com', 9, 26, 'ascii');
buf[35] = (wantReply === undefined || wantReply === true ? 1 : 0);
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', auth-agent-req@openssh.com)');
return send(this, buf);
}n/a
openssh_cancelStreamLocalForward = function (socketPath, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
var pathlen = Buffer.byteLength(pathlen);
var buf = new Buffer(1 + 4 + 38 + 1 + 4 + pathlen);
buf[0] = MESSAGE.GLOBAL_REQUEST;
buf.writeUInt32BE(38, 1, true);
buf.write('cancel-streamlocal-forward@openssh.com', 5, 38, 'ascii');
buf[43] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf.writeUInt32BE(pathlen, 44, true);
buf.write(socketPath, 48, pathlen, 'utf8');
this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (cancel-streamlocal-forward@openssh.com)');
return send(this, buf);
}n/a
openssh_directStreamLocal = function (chan, initWindow, maxPacket, cfg) {
if (this.server)
throw new Error('Client-only method called in server mode');
var pathlen = Buffer.byteLength(cfg.socketPath);
var p = 47;
var buf = new Buffer(1 + 4 + 30 + 4 + 4 + 4 + 4 + pathlen + 4);
buf[0] = MESSAGE.CHANNEL_OPEN;
buf.writeUInt32BE(30, 1, true);
buf.write('direct-streamlocal@openssh.com', 5, 30, 'ascii');
buf.writeUInt32BE(chan, 35, true);
buf.writeUInt32BE(initWindow, 39, true);
buf.writeUInt32BE(maxPacket, 43, true);
buf.writeUInt32BE(pathlen, p, true);
buf.write(cfg.socketPath, p += 4, pathlen, 'utf8');
this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
+ chan
+ ', direct-streamlocal@openssh.com)');
return send(this, buf);
}n/a
openssh_forwardedStreamLocal = function (chan, initWindow, maxPacket, cfg) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var pathlen = Buffer.byteLength(cfg.socketPath);
var buf = new Buffer(1 + 4 + 33 + 4 + 4 + 4 + 4 + pathlen + 4);
buf[0] = MESSAGE.CHANNEL_OPEN;
buf.writeUInt32BE(33, 1, true);
buf.write('forwarded-streamlocal@openssh.com', 5, 33, 'ascii');
buf.writeUInt32BE(chan, 38, true);
buf.writeUInt32BE(initWindow, 42, true);
buf.writeUInt32BE(maxPacket, 46, true);
buf.writeUInt32BE(pathlen, 50, true);
buf.write(cfg.socketPath, 54, pathlen, 'utf8');
buf.writeUInt32BE(0, 54 + pathlen, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
+ chan
+ ', forwarded-streamlocal@openssh.com)');
return send(this, buf);
}n/a
openssh_noMoreSessions = function (wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
var buf = new Buffer(1 + 4 + 28 + 1);
buf[0] = MESSAGE.GLOBAL_REQUEST;
buf.writeUInt32BE(28, 1, true);
buf.write('no-more-sessions@openssh.com', 5, 28, 'ascii');
buf[33] = (wantReply === undefined || wantReply === true ? 1 : 0);
this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (no-more-sessions@openssh.com)');
return send(this, buf);
}n/a
openssh_streamLocalForward = function (socketPath, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
var pathlen = Buffer.byteLength(pathlen);
var buf = new Buffer(1 + 4 + 31 + 1 + 4 + pathlen);
buf[0] = MESSAGE.GLOBAL_REQUEST;
buf.writeUInt32BE(31, 1, true);
buf.write('streamlocal-forward@openssh.com', 5, 31, 'ascii');
buf[36] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf.writeUInt32BE(pathlen, 37, true);
buf.write(socketPath, 41, pathlen, 'utf8');
this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (streamlocal-forward@openssh.com)');
return send(this, buf);
}n/a
ping = function () {
this.debug('DEBUG: Outgoing: Writing ping (GLOBAL_REQUEST: keepalive@openssh.com)');
return send(this, PING_PACKET);
}n/a
pty = function (chan, rows, cols, height, width, term, modes, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
if (!term || !term.length)
term = 'vt100';
if (modes
&& !Buffer.isBuffer(modes)
&& !Array.isArray(modes)
&& typeof modes === 'object')
modes = modesToBytes(modes);
if (!modes || !modes.length)
modes = NO_TERMINAL_MODES_BUFFER;
var termLen = term.length;
var modesLen = modes.length;
var p = 21;
var buf = new Buffer(1 + 4 + 4 + 7 + 1 + 4 + termLen + 4 + 4 + 4 + 4 + 4
+ modesLen);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(7, 5, true);
buf.write('pty-req', 9, 7, 'ascii');
buf[16] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf.writeUInt32BE(termLen, 17, true);
buf.write(term, 21, termLen, 'utf8');
buf.writeUInt32BE(cols, p += termLen, true);
buf.writeUInt32BE(rows, p += 4, true);
buf.writeUInt32BE(width, p += 4, true);
buf.writeUInt32BE(height, p += 4, true);
buf.writeUInt32BE(modesLen, p += 4, true);
p += 4;
if (Array.isArray(modes)) {
for (var i = 0; i < modesLen; ++i)
buf[p++] = modes[i];
} else if (Buffer.isBuffer(modes)) {
modes.copy(buf, p);
}
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', pty-req)');
return send(this, buf);
}n/a
push = function (chunk, encoding) {
var ret = this.__push(chunk, encoding);
this._needContinue = (ret === false);
return ret;
}...
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');
...rekey = function () {
var status = this._state.outgoing.status;
if (status === OUT_REKEYING)
throw new Error('A re-key is already in progress');
else if (status !== OUT_READY)
throw new Error('Cannot re-key yet');
this.debug('DEBUG: Outgoing: Starting re-key');
return KEXINIT(this);
}n/a
requestFailure = function () {
this.debug('DEBUG: Outgoing: Writing REQUEST_FAILURE');
return send(this, REQUEST_FAILURE_PACKET);
}n/a
requestSuccess = function (data) {
var buf;
if (Buffer.isBuffer(data)) {
buf = new Buffer(1 + data.length);
buf[0] = MESSAGE.REQUEST_SUCCESS;
data.copy(buf, 1);
} else
buf = REQUEST_SUCCESS_PACKET;
this.debug('DEBUG: Outgoing: Writing REQUEST_SUCCESS');
return send(this, buf);
}n/a
reset = function (noend) {
if (this._state) {
var state = this._state;
state.incoming.status = IN_INIT;
state.outgoing.status = OUT_INIT;
} else {
this._state = {
authsQueue: [],
hostkeyFormat: undefined,
kex: undefined,
kexdh: undefined,
incoming: {
status: IN_INIT,
expectedPacket: undefined,
search: undefined,
greeting: undefined,
seqno: 0,
pktLen: undefined,
padLen: undefined,
pktExtra: undefined,
payload: undefined,
packet: undefined,
kexinit: undefined,
identRaw: undefined,
rekeyQueue: [],
ignoreNext: false,
expect: {
amount: undefined,
type: undefined,
ptr: 0,
buf: undefined
},
decrypt: {
instance: false,
size: 8,
isGCM: false,
iv: undefined, // GCM
key: undefined, // GCM
buf: undefined,
type: undefined
},
hmac: {
size: undefined,
key: undefined,
buf: undefined,
bufCompute: new Buffer(9),
type: false
},
decompress: {
instance: false,
type: false
}
},
outgoing: {
status: OUT_INIT,
seqno: 0,
bufSeqno: new Buffer(4),
rekeyQueue: [],
kexinit: undefined,
kexsecret: undefined,
pubkey: undefined,
exchangeHash: undefined,
sessionId: undefined,
sentNEWKEYS: false,
encrypt: {
instance: false,
size: 8,
isGCM: false,
iv: undefined, // GCM
key: undefined, // GCM
type: undefined
},
hmac: {
size: undefined,
key: undefined,
buf: undefined,
type: false
},
compress: {
instance: false,
type: false
}
}
};
}
if (!noend) {
if (this.readable)
this.push(null);
}
}n/a
service = function (svcName) {
if (this.server)
throw new Error('Client-only method called in server mode');
var svcNameLen = Buffer.byteLength(svcName);
var buf = new Buffer(1 + 4 + svcNameLen);
buf[0] = MESSAGE.SERVICE_REQUEST;
buf.writeUInt32BE(svcNameLen, 1, true);
buf.write(svcName, 5, svcNameLen, 'ascii');
this.debug('DEBUG: Outgoing: Writing SERVICE_REQUEST (' + svcName + ')');
return send(this, buf);
}n/a
serviceAccept = function (svcName) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var svcNameLen = svcName.length;
var buf = new Buffer(1 + 4 + svcNameLen);
buf[0] = MESSAGE.SERVICE_ACCEPT;
buf.writeUInt32BE(svcNameLen, 1, true);
buf.write(svcName, 5, svcNameLen, 'ascii');
this.debug('DEBUG: Outgoing: Writing SERVICE_ACCEPT (' + svcName + ')');
send(this, buf);
if (this.server && this.banner && svcName === 'ssh-userauth') {
/*
byte SSH_MSG_USERAUTH_BANNER
string message in ISO-10646 UTF-8 encoding
string language tag
*/
var bannerLen = Buffer.byteLength(this.banner);
var packetLen = 1 + 4 + bannerLen + 4;
if (packetLen > BUFFER_MAX_LEN) {
bannerLen -= 1 + 4 + 4;
packetLen -= 1 + 4 + 4;
}
var packet = new Buffer(packetLen);
packet[0] = MESSAGE.USERAUTH_BANNER;
packet.writeUInt32BE(bannerLen, 1, true);
packet.write(this.banner, 5, bannerLen, 'utf8');
packet.fill(0, packetLen - 4); // Empty language tag
this.debug('DEBUG: Outgoing: Writing USERAUTH_BANNER');
send(this, packet);
this.banner = undefined; // Prevent banner from being displayed again
}
}n/a
session = function (chan, initWindow, maxPacket) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
var buf = new Buffer(1 + 4 + 7 + 4 + 4 + 4);
buf[0] = MESSAGE.CHANNEL_OPEN;
buf.writeUInt32BE(7, 1, true);
buf.write('session', 5, 7, 'ascii');
buf.writeUInt32BE(chan, 12, true);
buf.writeUInt32BE(initWindow, 16, true);
buf.writeUInt32BE(maxPacket, 20, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
+ chan
+ ', session)');
return send(this, buf);
}n/a
shell = function (chan, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
var buf = new Buffer(1 + 4 + 4 + 5 + 1);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(5, 5, true);
buf.write('shell', 9, 5, 'ascii');
buf[14] = (wantReply === undefined || wantReply === true ? 1 : 0);
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', shell)');
return send(this, buf);
}n/a
signal = function (chan, signal) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
signal = signal.toUpperCase();
if (signal.slice(0, 3) === 'SIG')
signal = signal.substring(3);
if (SIGNALS.indexOf(signal) === -1)
throw new Error('Invalid signal: ' + signal);
var signalLen = signal.length;
var buf = new Buffer(1 + 4 + 4 + 6 + 1 + 4 + signalLen);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(6, 5, true);
buf.write('signal', 9, 6, 'ascii');
buf[15] = 0;
buf.writeUInt32BE(signalLen, 16, true);
buf.write(signal, 20, signalLen, 'ascii');
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', signal)');
return send(this, buf);
}n/a
subsystem = function (chan, name, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
var nameLen = Buffer.byteLength(name);
var buf = new Buffer(1 + 4 + 4 + 9 + 1 + 4 + nameLen);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(9, 5, true);
buf.write('subsystem', 9, 9, 'ascii');
buf[18] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf.writeUInt32BE(nameLen, 19, true);
buf.write(name, 23, nameLen, 'ascii');
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', subsystem: '
+ name
+ ')');
return send(this, buf);
}n/a
tcpipForward = function (bindAddr, bindPort, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
var addrlen = Buffer.byteLength(bindAddr);
var buf = new Buffer(1 + 4 + 13 + 1 + 4 + addrlen + 4);
buf[0] = MESSAGE.GLOBAL_REQUEST;
buf.writeUInt32BE(13, 1, true);
buf.write('tcpip-forward', 5, 13, 'ascii');
buf[18] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf.writeUInt32BE(addrlen, 19, true);
buf.write(bindAddr, 23, addrlen, 'ascii');
buf.writeUInt32BE(bindPort, 23 + addrlen, true);
this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (tcpip-forward)');
return send(this, buf);
}n/a
windowChange = function (chan, rows, cols, height, width) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
var buf = new Buffer(1 + 4 + 4 + 13 + 1 + 4 + 4 + 4 + 4);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(13, 5, true);
buf.write('window-change', 9, 13, 'ascii');
buf[22] = 0;
buf.writeUInt32BE(cols, 23, true);
buf.writeUInt32BE(rows, 27, true);
buf.writeUInt32BE(width, 31, true);
buf.writeUInt32BE(height, 35, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', window-change)');
return send(this, buf);
}n/a
x11 = function (chan, initWindow, maxPacket, cfg) {
if (!this.server)
throw new Error('Server-only method called in client mode');
var addrLen = Buffer.byteLength(cfg.originAddr);
var p = 24 + addrLen;
var buf = new Buffer(1 + 4 + 3 + 4 + 4 + 4 + 4 + addrLen + 4);
buf[0] = MESSAGE.CHANNEL_OPEN;
buf.writeUInt32BE(3, 1, true);
buf.write('x11', 5, 3, 'ascii');
buf.writeUInt32BE(chan, 8, true);
buf.writeUInt32BE(initWindow, 12, true);
buf.writeUInt32BE(maxPacket, 16, true);
buf.writeUInt32BE(addrLen, 20, true);
buf.write(cfg.originAddr, 24, addrLen, 'ascii');
buf.writeUInt32BE(cfg.originPort, p, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
+ chan
+ ', x11)');
return send(this, buf);
}n/a
x11Forward = function (chan, cfg, wantReply) {
if (this.server)
throw new Error('Client-only method called in server mode');
// Does not consume window space
var protolen = Buffer.byteLength(cfg.protocol);
var cookielen = Buffer.byteLength(cfg.cookie);
var buf = new Buffer(1 + 4 + 4 + 7 + 1 + 1 + 4 + protolen + 4 + cookielen
+ 4);
buf[0] = MESSAGE.CHANNEL_REQUEST;
buf.writeUInt32BE(chan, 1, true);
buf.writeUInt32BE(7, 5, true);
buf.write('x11-req', 9, 7, 'ascii');
buf[16] = (wantReply === undefined || wantReply === true ? 1 : 0);
buf[17] = (cfg.single ? 1 : 0);
buf.writeUInt32BE(protolen, 18, true);
var bp = 22;
if (Buffer.isBuffer(cfg.protocol))
cfg.protocol.copy(buf, bp);
else
buf.write(cfg.protocol, bp, protolen, 'utf8');
bp += protolen;
buf.writeUInt32BE(cookielen, bp, true);
bp += 4;
if (Buffer.isBuffer(cfg.cookie))
cfg.cookie.copy(buf, bp);
else
buf.write(cfg.cookie, bp, cookielen, 'utf8');
bp += cookielen;
buf.writeUInt32BE((cfg.screen || 0), bp, true);
this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
+ chan
+ ', x11-req)');
return send(this, buf);
}n/a
function BigInteger(a, b, c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}n/a
function Barrett(m) {
// setup Barrett
this.r2 = nbi();
this.q3 = nbi();
BigInteger.ONE.dlShiftTo(2*m.t,this.r2);
this.mu = this.r2.divide(m);
this.m = m;
}n/a
function bnAbs() { return (this.s<0)?this.negate():this; }...
r.t = i;
r.clamp();
}
// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
...function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }...
if(ac) c.subTo(a,c);
d.subTo(b,d);
}
}
if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
if(d.compareTo(m) >= 0) return d.subtract(m);
if(d.signum() < 0) d.addTo(m,d); else return d;
if(d.signum() < 0) return d.add(m); else return d;
}
var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,
151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,
337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,
541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,
743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,
971,977,983,991,997];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];
// (public) test primality with certainty >= 1-.5^t
function bnIsProbablePrime(t) {
...function bnpAddTo(a, r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]+a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c += a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c += a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += a.s;
}
r.s = (c<0)?-1:0;
if(c > 0) r[i++] = c;
else if(c < -1) r[i++] = this.DV+c;
r.t = i;
r.clamp();
}...
if(c > 0) r[i++] = c;
else if(c < -1) r[i++] = this.DV+c;
r.t = i;
r.clamp();
}
// (public) this + a
function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }
// (public) this - a
function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }
// (public) this * a
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }
...function am3(i, x, w, j, c, n) {
var xl = x&0x3fff, xh = x>>14;
while(--n >= 0) {
var l = this[i]&0x3fff;
var h = this[i++]>>14;
var m = xh*l+h*xl;
l = xl*l+((m&0x3fff)<<14)+w[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w[j++] = l&0xfffffff;
}
return c;
}...
// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}
// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
...function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }n/a
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }n/a
function bnBitCount() {
var r = 0, x = this.s&this.DM;
for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x);
return r;
}n/a
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}...
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
}
}
}
else {
// new BigInteger(int,RNG)
var x = new Array(), t = a&7;
x.length = (a>>3)+1;
...function bnpBitwiseTo(a, op, r) {
var i, f, m = Math.min(a.t,this.t);
for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]);
if(a.t < this.t) {
f = a.s&this.DM;
for(i = m; i < this.t; ++i) r[i] = op(this[i],f);
r.t = this.t;
}
else {
f = this.s&this.DM;
for(i = m; i < a.t; ++i) r[i] = op(f,a[i]);
r.t = a.t;
}
r.s = op(this.s,a.s);
r.clamp();
}...
function bnpFromNumber(a,b,c) {
if("number" == typeof b) {
// new BigInteger(int,int,RNG)
if(a < 2) this.fromInt(1);
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
}
}
}
...function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; }n/a
function bnpChangeBit(n, op) {
var r = BigInteger.ONE.shiftLeft(n);
this.bitwiseTo(r,op,r);
return r;
}...
function bnpChangeBit(n,op) {
var r = BigInteger.ONE.shiftLeft(n);
this.bitwiseTo(r,op,r);
return r;
}
// (public) this | (1<<n)
function bnSetBit(n) { return this.changeBit(n,op_or); }
// (public) this & ~(1<<n)
function bnClearBit(n) { return this.changeBit(n,op_andnot); }
// (public) this ^ (1<<n)
function bnFlipBit(n) { return this.changeBit(n,op_xor); }
...function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }...
else return 1;
}
// (protected) convert to radix string
function bnpToRadix(b) {
if(b == null) b = 10;
if(this.signum() == 0 || b < 2 || b > 36) return "0";
var cs = this.chunkSize(b);
var a = Math.pow(b,cs);
var d = nbv(a), y = nbi(), z = nbi(), r = "";
this.divRemTo(d,y,z);
while(y.signum() > 0) {
r = (a+z.intValue()).toString(b).substr(1) + r;
y.divRemTo(d,y,z);
}
...function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}...
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}
// (protected) clamp off excess high words
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
...function bnClearBit(n) { return this.changeBit(n,op_andnot); }n/a
function bnClone() { var r = nbi(); this.copyTo(r); return r; }...
}
}
return z.revert(r);
}
// (public) gcd(this,a) (HAC 14.54)
function bnGCD(a) {
var x = (this.s<0)?this.negate():this.clone();
var y = (a.s<0)?a.negate():a.clone();
if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
var i = x.getLowestSetBit(), g = y.getLowestSetBit();
if(g < 0) return x;
if(i < g) g = i;
if(g > 0) {
x.rShiftTo(g,x);
...function bnCompareTo(a) {
var r = this.s-a.s;
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return (this.s<0)?-r:r;
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
return 0;
}...
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 0) {
...function bnpCopyTo(r) {
for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
r.t = this.t;
r.s = this.s;
}...
// r != q, this != m. q or r may be null.
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
...function bnpDAddOffset(n, w) {
if(n == 0) return;
while(this.t <= w) this[this.t++] = 0;
this[w] += n;
while(this[w] >= this.DV) {
this[w] -= this.DV;
if(++w >= this.t) this[this.t++] = 0;
++this[w];
}
}...
if(x < 0) {
if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
continue;
}
w = b*w+x;
if(++j >= cs) {
this.dMultiply(d);
this.dAddOffset(w,0);
j = 0;
w = 0;
}
}
if(j > 0) {
this.dMultiply(Math.pow(b,j));
this.dAddOffset(w,0);
...function bnpDMultiply(n) {
this[this.t] = this.am(0,n-1,this,0,0,this.t);
++this.t;
this.clamp();
}...
var x = intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
continue;
}
w = b*w+x;
if(++j >= cs) {
this.dMultiply(d);
this.dAddOffset(w,0);
j = 0;
w = 0;
}
}
if(j > 0) {
this.dMultiply(Math.pow(b,j));
...function bnpDivRemTo(m, q, r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 0) {
// Estimate quotient digit
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}...
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}
// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
...function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }...
// Barrett modular reduction
function Barrett(m) {
// setup Barrett
this.r2 = nbi();
this.q3 = nbi();
BigInteger.ONE.dlShiftTo(2*m.t,this.r2);
this.mu = this.r2.divide(m);
this.m = m;
}
function barrettConvert(x) {
if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
else if(x.compareTo(this.m) < 0) return x;
else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
...function bnDivideAndRemainder(a) {
var q = nbi(), r = nbi();
this.divRemTo(a,q,r);
return new Array(q,r);
}n/a
function bnpDLShiftTo(n, r) {
var i;
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
for(i = n-1; i >= 0; --i) r[i] = 0;
r.t = this.t+n;
r.s = this.s;
}...
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
...function bnpDRShiftTo(n, r) {
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}...
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
...function bnEquals(a) { return(this.compareTo(a)==0); }n/a
function bnpExp(e, z) {
if(e > 0xffffffff || e < 1) return BigInteger.ONE;
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
g.copyTo(r);
while(--i >= 0) {
z.sqrTo(r,r2);
if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
else { var t = r; r = r2; r2 = t; }
}
return z.revert(r);
}...
return z.revert(r);
}
// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}
// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.clamp = bnpClamp;
...function bnFlipBit(n) { return this.changeBit(n,op_xor); }n/a
function bnpFromInt(x) {
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
else if(x < -1) this[0] = x+this.DV;
else this.t = 0;
}...
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
else if(x < -1) this[0] = x+this.DV;
else this.t = 0;
}
// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
// (protected) set from string and radix
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
...function bnpFromNumber(a, b, c) {
if("number" == typeof b) {
// new BigInteger(int,int,RNG)
if(a < 2) this.fromInt(1);
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
}
}
}
else {
// new BigInteger(int,RNG)
var x = new Array(), t = a&7;
x.length = (a>>3)+1;
b.nextBytes(x);
if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0;
this.fromString(x,256);
}
}...
// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}
// return new, unset BigInteger
function nbi() { return new BigInteger(null); }
...function bnpFromRadix(s, b) {
this.fromInt(0);
if(b == null) b = 10;
var cs = this.chunkSize(b);
var d = Math.pow(b,cs), mi = false, j = 0, w = 0;
for(var i = 0; i < s.length; ++i) {
var x = intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
continue;
}
w = b*w+x;
if(++j >= cs) {
this.dMultiply(d);
this.dAddOffset(w,0);
j = 0;
w = 0;
}
}
if(j > 0) {
this.dMultiply(Math.pow(b,j));
this.dAddOffset(w,0);
}
if(mi) BigInteger.ZERO.subTo(this,this);
}...
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
...function bnpFromString(s, b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this[this.t++] = x;
else if(sh+k > this.DB) {
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
this[this.t++] = (x>>(this.DB-sh));
}
else
this[this.t-1] |= x<<sh;
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}...
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,
256);
else this.fromString(a,b);
}
// return new, unset BigInteger
function nbi() { return new BigInteger(null); }
// am: Compute w_j += (x*this_i), propagate carries,
...function bnGCD(a) {
var x = (this.s<0)?this.negate():this.clone();
var y = (a.s<0)?a.negate():a.clone();
if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
var i = x.getLowestSetBit(), g = y.getLowestSetBit();
if(g < 0) return x;
if(i < g) g = i;
if(g > 0) {
x.rShiftTo(g,x);
y.rShiftTo(g,y);
}
while(x.signum() > 0) {
if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);
if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);
if(x.compareTo(y) >= 0) {
x.subTo(y,x);
x.rShiftTo(1,x);
}
else {
y.subTo(x,y);
y.rShiftTo(1,y);
}
}
if(g > 0) y.lShiftTo(g,y);
return y;
}n/a
function bnGetLowestSetBit() {
for(var i = 0; i < this.t; ++i)
if(this[i] != 0) return i*this.DB+lbit(this[i]);
if(this.s < 0) return this.t*this.DB;
return -1;
}...
}
// (public) gcd(this,a) (HAC 14.54)
function bnGCD(a) {
var x = (this.s<0)?this.negate():this.clone();
var y = (a.s<0)?a.negate():a.clone();
if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
var i = x.getLowestSetBit(), g = y.getLowestSetBit();
if(g < 0) return x;
if(i < g) g = i;
if(g > 0) {
x.rShiftTo(g,x);
y.rShiftTo(g,y);
}
while(x.signum() > 0) {
...function bnIntValue() {
if(this.s < 0) {
if(this.t == 1) return this[0]-this.DV;
else if(this.t == 0) return -1;
}
else if(this.t == 1) return this[0];
else if(this.t == 0) return 0;
// assumes 16 < DB < 32
return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0];
}...
if(b == null) b = 10;
if(this.signum() == 0 || b < 2 || b > 36) return "0";
var cs = this.chunkSize(b);
var a = Math.pow(b,cs);
var d = nbv(a), y = nbi(), z = nbi(), r = "";
this.divRemTo(d,y,z);
while(y.signum() > 0) {
r = (a+z.intValue()).toString(b).substr(1) + r;
y.divRemTo(d,y,z);
}
return z.intValue().toString(b) + r;
}
// (protected) convert from radix string
function bnpFromRadix(s,b) {
...function bnpInvDigit() {
if(this.t < 1) return 0;
var x = this[0];
if((x&1) == 0) return 0;
var y = x&3; // y == 1/x mod 2^2
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
// last step - calculate inverse mod DV directly;
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}...
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}
// Montgomery reduction
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}
// xR mod m
...function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }...
}
return z.revert(r);
}
// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}
// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
...function bnIsProbablePrime(t) {
var i, x = this.abs();
if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) {
for(i = 0; i < lowprimes.length; ++i)
if(x[0] == lowprimes[i]) return true;
return false;
}
if(x.isEven()) return false;
i = 1;
while(i < lowprimes.length) {
var m = lowprimes[i], j = i+1;
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
m = x.modInt(m);
while(i < j) if(m%lowprimes[i++] == 0) return false;
}
return x.millerRabin(t);
}...
// new BigInteger(int,int,RNG)
if(a < 2) this.fromInt(1);
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
}
}
}
else {
// new BigInteger(int,RNG)
...function bnpLShiftTo(n, r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<cbs)-1;
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
for(i = this.t-1; i >= 0; --i) {
r[i+ds+1] = (this[i]>>cbs)|c;
c = (this[i]&bm)<<bs;
}
for(i = ds-1; i >= 0; --i) r[i] = 0;
r[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}...
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
...function bnMax(a) { return(this.compareTo(a)>0)?this:a; }...
r.t = this.t+n;
r.s = this.s;
}
// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}
// (protected) r = this << n
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
...function bnpMillerRabin(t) {
var n1 = this.subtract(BigInteger.ONE);
var k = n1.getLowestSetBit();
if(k <= 0) return false;
var r = n1.shiftRight(k);
t = (t+1)>>1;
if(t > lowprimes.length) t = lowprimes.length;
var a = nbi();
for(var i = 0; i < t; ++i) {
//Pick bases at random, instead of starting at 2
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
var y = a.modPow(r,this);
if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
var j = 1;
while(j++ < k && y.compareTo(n1) != 0) {
y = y.modPowInt(2,this);
if(y.compareTo(BigInteger.ONE) == 0) return false;
}
if(y.compareTo(n1) != 0) return false;
}
}
return true;
}...
i = 1;
while(i < lowprimes.length) {
var m = lowprimes[i], j = i+1;
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
m = x.modInt(m);
while(i < j) if(m%lowprimes[i++] == 0) return false;
}
return x.millerRabin(t);
}
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
function bnpMillerRabin(t) {
var n1 = this.subtract(BigInteger.ONE);
var k = n1.getLowestSetBit();
if(k <= 0) return false;
...function bnMin(a) { return(this.compareTo(a)<0)?this:a; }...
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}
// (protected) r = this - a
function bnpSubTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]-a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
...function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}...
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}
// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
...function bnpModInt(n) {
if(n <= 0) return 0;
var d = this.DV%n, r = (this.s<0)?n-1:0;
if(this.t > 0)
if(d == 0) r = this[0]%n;
else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n;
return r;
}...
return false;
}
if(x.isEven()) return false;
i = 1;
while(i < lowprimes.length) {
var m = lowprimes[i], j = i+1;
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
m = x.modInt(m);
while(i < j) if(m%lowprimes[i++] == 0) return false;
}
return x.millerRabin(t);
}
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
function bnpMillerRabin(t) {
...function bnModInverse(m) {
var ac = m.isEven();
if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;
var u = m.clone(), v = this.clone();
var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
while(u.signum() != 0) {
while(u.isEven()) {
u.rShiftTo(1,u);
if(ac) {
if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
a.rShiftTo(1,a);
}
else if(!b.isEven()) b.subTo(m,b);
b.rShiftTo(1,b);
}
while(v.isEven()) {
v.rShiftTo(1,v);
if(ac) {
if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
c.rShiftTo(1,c);
}
else if(!d.isEven()) d.subTo(m,d);
d.rShiftTo(1,d);
}
if(u.compareTo(v) >= 0) {
u.subTo(v,u);
if(ac) a.subTo(c,a);
b.subTo(d,b);
}
else {
v.subTo(u,v);
if(ac) c.subTo(a,c);
d.subTo(b,d);
}
}
if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
if(d.compareTo(m) >= 0) return d.subtract(m);
if(d.signum() < 0) d.addTo(m,d); else return d;
if(d.signum() < 0) return d.add(m); else return d;
}n/a
function bnModPow(e, m) {
var i = e.bitLength(), k, r = nbv(1), z;
if(i <= 0) return r;
else if(i < 18) k = 1;
else if(i < 48) k = 3;
else if(i < 144) k = 4;
else if(i < 768) k = 5;
else k = 6;
if(i < 8)
z = new Classic(m);
else if(m.isEven())
z = new Barrett(m);
else
z = new Montgomery(m);
// precomputation
var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1;
g[1] = z.convert(this);
if(k > 1) {
var g2 = nbi();
z.sqrTo(g[1],g2);
while(n <= km) {
g[n] = nbi();
z.mulTo(g2,g[n-2],g[n]);
n += 2;
}
}
var j = e.t-1, w, is1 = true, r2 = nbi(), t;
i = nbits(e[j])-1;
while(j >= 0) {
if(i >= k1) w = (e[j]>>(i-k1))&km;
else {
w = (e[j]&((1<<(i+1))-1))<<(k1-i);
if(j > 0) w |= e[j-1]>>(this.DB+i-k1);
}
n = k;
while((w&1) == 0) { w >>= 1; --n; }
if((i -= n) < 0) { i += this.DB; --j; }
if(is1) { // ret == 1, don't bother squaring or multiplying it
g[w].copyTo(r);
is1 = false;
}
else {
while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
z.mulTo(r2,g[w],r);
}
while(j >= 0 && (e[j]&(1<<i)) == 0) {
z.sqrTo(r,r2); t = r; r = r2; r2 = t;
if(--i < 0) { i = this.DB-1; --j; }
}
}
return z.revert(r);
}...
var r = n1.shiftRight(k);
t = (t+1)>>1;
if(t > lowprimes.length) t = lowprimes.length;
var a = nbi();
for(var i = 0; i < t; ++i) {
//Pick bases at random, instead of starting at 2
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
var y = a.modPow(r,this);
if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
var j = 1;
while(j++ < k && y.compareTo(n1) != 0) {
y = y.modPowInt(2,this);
if(y.compareTo(BigInteger.ONE) == 0) return false;
}
if(y.compareTo(n1) != 0) return false;
...function bnModPowInt(e, m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}...
for(var i = 0; i < t; ++i) {
//Pick bases at random, instead of starting at 2
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
var y = a.modPow(r,this);
if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
var j = 1;
while(j++ < k && y.compareTo(n1) != 0) {
y = y.modPowInt(2,this);
if(y.compareTo(BigInteger.ONE) == 0) return false;
}
if(y.compareTo(n1) != 0) return false;
}
}
return true;
}
...function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }n/a
function bnpMultiplyLowerTo(a, n, r) {
var i = Math.min(this.t+a.t,n);
r.s = 0; // assumes a,this >= 0
r.t = i;
while(i > 0) r[--i] = 0;
var j;
for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t);
for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i);
r.clamp();
}...
function barrettRevert(x) { return x; }
// x = x mod m (HAC 14.42)
function barrettReduce(x) {
x.drShiftTo(this.m.t-1,this.r2);
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
x.subTo(this.r2,x);
while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = x^2 mod m; x != r
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
...function bnpMultiplyTo(a, r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}...
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
...function bnpMultiplyUpperTo(a, n, r) {
--n;
var i = r.t = this.t+a.t-n;
r.s = 0; // assumes a,this >= 0
while(--i >= 0) r[i] = 0;
for(i = Math.max(n-this.t,0); i < a.t; ++i)
r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n);
r.clamp();
r.drShiftTo(1,r);
}...
function barrettRevert(x) { return x; }
// x = x mod m (HAC 14.42)
function barrettReduce(x) {
x.drShiftTo(this.m.t-1,this.r2);
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
x.subTo(this.r2,x);
while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = x^2 mod m; x != r
...function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }...
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}
// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
...function bnNot() {
var r = nbi();
for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i];
r.t = this.t;
r.s = ~this.s;
return r;
}n/a
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }n/a
function bnPow(e) { return this.exp(e,new NullExp()); }...
dbits = 28;
BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DV = (1<<dbits);
var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;
// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
...function bnpRShiftTo(n, r) {
r.s = this.s;
var ds = Math.floor(n/this.DB);
if(ds >= this.t) { r.t = 0; return; }
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<bs)-1;
r[0] = this[ds]>>bs;
for(var i = ds+1; i < this.t; ++i) {
r[i-ds-1] |= (this[i]&bm)<<cbs;
r[i-ds] = this[i]>>bs;
}
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}...
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
...function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }n/a
function bnSetBit(n) { return this.changeBit(n,op_or); }n/a
function bnShiftLeft(n) {
var r = nbi();
if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
return r;
}...
function bnpFromNumber(a,b,c) {
if("number" == typeof b) {
// new BigInteger(int,int,RNG)
if(a < 2) this.fromInt(1);
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
}
}
}
...function bnShiftRight(n) {
var r = nbi();
if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
return r;
}...
}
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
function bnpMillerRabin(t) {
var n1 = this.subtract(BigInteger.ONE);
var k = n1.getLowestSetBit();
if(k <= 0) return false;
var r = n1.shiftRight(k);
t = (t+1)>>1;
if(t > lowprimes.length) t = lowprimes.length;
var a = nbi();
for(var i = 0; i < t; ++i) {
//Pick bases at random, instead of starting at 2
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
var y = a.modPow(r,this);
...function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; }n/a
function bnSigNum() {
if(this.s < 0) return -1;
else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
else return 1;
}...
else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
else return 1;
}
// (protected) convert to radix string
function bnpToRadix(b) {
if(b == null) b = 10;
if(this.signum() == 0 || b < 2 || b > 36) return "0";
var cs = this.chunkSize(b);
var a = Math.pow(b,cs);
var d = nbv(a), y = nbi(), z = nbi(), r = "";
this.divRemTo(d,y,z);
while(y.signum() > 0) {
r = (a+z.intValue()).toString(b).substr(1) + r;
y.divRemTo(d,y,z);
...function bnSquare() { var r = nbi(); this.squareTo(r); return r; }n/a
function bnpSquareTo(r) {
var x = this.abs();
var i = r.t = 2*x.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < x.t-1; ++i) {
var c = x.am(i,x[i],r,2*i,0,1);
if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
r[i+x.t] -= x.DV;
r[i+x.t+1] = 1;
}
}
if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
r.s = 0;
r.clamp();
}...
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
...function bnpSubTo(a, r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]-a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c -= a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c -= a.s;
}
r.s = (c<0)?-1:0;
if(c < -1) r[i++] = this.DV+c;
else if(c > 0) r[i++] = c;
r.t = i;
r.clamp();
}...
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}
// (protected) clamp off excess high words
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}
...function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }...
else {
v.subTo(u,v);
if(ac) c.subTo(a,c);
d.subTo(b,d);
}
}
if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
if(d.compareTo(m) >= 0) return d.subtract(m);
if(d.signum() < 0) d.addTo(m,d); else return d;
if(d.signum() < 0) return d.add(m); else return d;
}
var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,
151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,
337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,
541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,
743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,
971,977,983,991,997];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];
...function bnTestBit(n) {
var j = Math.floor(n/this.DB);
if(j >= this.t) return(this.s!=0);
return((this[j]&(1<<(n%this.DB)))!=0);
}...
// (protected) alternate constructor
function bnpFromNumber(a,b,c) {
if("number" == typeof b) {
// new BigInteger(int,int,RNG)
if(a < 2) this.fromInt(1);
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
}
}
...function bnToByteArray() {
var i = this.t, r = new Array();
r[0] = this.s;
var p = this.DB-(i*this.DB)%8, d, k = 0;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p)
r[k++] = d|(this.s<<(this.DB-p));
while(i >= 0) {
if(p < 8) {
d = (this[i]&((1<<p)-1))<<(8-p);
d |= this[--i]>>(p+=this.DB-8);
}
else {
d = (this[i]>>(p-=8))&0xff;
if(p <= 0) { p += this.DB; --i; }
}
if((d&0x80) != 0) d |= -256;
if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
if(k > 0 || d != this.s) r[k++] = d;
}
}
return r;
}n/a
function bnpToRadix(b) {
if(b == null) b = 10;
if(this.signum() == 0 || b < 2 || b > 36) return "0";
var cs = this.chunkSize(b);
var a = Math.pow(b,cs);
var d = nbv(a), y = nbi(), z = nbi(), r = "";
this.divRemTo(d,y,z);
while(y.signum() > 0) {
r = (a+z.intValue()).toString(b).substr(1) + r;
y.divRemTo(d,y,z);
}
return z.intValue().toString(b) + r;
}...
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
var p = this.DB-(i*this.DB)%k;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this[i]&((1<<p)-1))<<(k-p);
...function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
var p = this.DB-(i*this.DB)%k;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this[i]&((1<<p)-1))<<(k-p);
d |= this[--i]>>(p+=this.DB-k);
}
else {
d = (this[i]>>(p-=k))&km;
if(p <= 0) { p += this.DB; --i; }
}
if(d > 0) m = true;
if(m) r += int2char(d);
}
}
return m?r:"0";
}...
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}
// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
...function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }n/a
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;
}n/a
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;
}n/a
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;
}n/a
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;
}n/a
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;
}n/a
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;
}n/a
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;
}n/a
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;
}...
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;
};
...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');
}
}n/a
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(); // Sk ......
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
...function isStreamCipher(name) {
return RE_STREAM.test(name);
}n/a
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);
}n/a
parseKey = 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].toLo ......
* **tcpipForward**(< _string_ >bindAddr, < _integer_ >bindPort[, < _boolean_ >wantReply]) - _boolean_
- Writes a tcpip forward global request packet. `wantReply` defaults to `true`. Returns `false` if you should wait for the `continue
` event before sending any more traffic.
* **cancelTcpipForward**(< _string_ >bindAddr, < _integer_ >bindPort[, < _boolean_ >wantReply]) -
_boolean_ - Writes a cancel tcpip forward global request packet. `wantReply` defaults to `true`. Returns `false` if you should wait
for the `continue` event before sending any more traffic.
* **authPassword**(< _string_ >username, < _string_ >password) - _boolean_ - Writes a password userauth request
packet. Returns `false` if you should wait for the `continue` event before sending any more traffic.
* **authPK**(< _string_ >username, < _object_ >pubKey[, < _function_ >cbSign]) - _boolean_ - Writes
a publickey userauth request packet. `pubKey` is the object returned from using `utils.parseKey
()` on a private or public key. If `cbSign` is not present, a pubkey check userauth packet is written. Otherwise `cbSign`
is called with `(blob, callback)`, where `blob` is the data to sign with the private key and the resulting signature _Buffer_ is
passed to `callback` as the first argument. Returns `false` if you should wait for the `continue` event before sending any more
traffic.
* **authHostbased**(< _string_ >username, < _object_ >pubKey, < _string_ >localHostname, < _string_
>localUsername, < _function_ >cbSign) - _boolean_ - Writes a hostbased userauth request packet. `pubKey` is the
object returned from using `utils.parseKey()` on a private or public key. `cbSign` is called with `(blob, callback)`, where `blob
` is the data to sign with the private key and the resulting signature _Buffer_ is passed to `callback` as the first argument. Returns
`false` if you should wait for the `continue` event before sending any more traffic.
* **authKeyboard**(< _string_ >username) - _boolean_ - Writes a keyboard-interactive userauth request packet. Returns `
false` if you should wait for the `continue` event before sending any more traffic.
* **authNone**(< _string_ >username) - _boolean_ - Writes a "none" userauth request packet. Returns `false`
if you should wait for the `continue` event before sending any more traffic.
...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);
}...
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':
...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);
}...
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
...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));
}...
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;
...