123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817 |
- var crypto = require('crypto');
- var Ber = require('asn1').Ber;
- var BigInteger = require('./jsbn'); // only for converting PPK -> OpenSSL format
- var SSH_TO_OPENSSL = require('./constants').SSH_TO_OPENSSL;
- var RE_STREAM = /^arcfour/i;
- var RE_KEY_LEN = /(.{64})/g;
- // XXX the value of 2400 from dropbear is only for certain strings, not all
- // strings. for example the list strings used during handshakes
- var MAX_STRING_LEN = Infinity;//2400; // taken from dropbear
- var PPK_IV = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
- module.exports = {
- iv_inc: iv_inc,
- isStreamCipher: isStreamCipher,
- readInt: readInt,
- readString: readString,
- parseKey: require('./keyParser'),
- genPublicKey: genPublicKey,
- convertPPKPrivate: convertPPKPrivate,
- verifyPPKMAC: verifyPPKMAC,
- decryptKey: decryptKey,
- DSASigBERToBare: DSASigBERToBare,
- DSASigBareToBER: DSASigBareToBER,
- ECDSASigASN1ToSSH: ECDSASigASN1ToSSH,
- ECDSASigSSHToASN1: ECDSASigSSHToASN1,
- RSAKeySSHToASN1: RSAKeySSHToASN1,
- DSAKeySSHToASN1: DSAKeySSHToASN1,
- ECDSAKeySSHToASN1: ECDSAKeySSHToASN1
- };
- function iv_inc(iv) {
- var n = 12;
- var c = 0;
- do {
- --n;
- c = iv[n];
- if (c === 255)
- iv[n] = 0;
- else {
- iv[n] = ++c;
- return;
- }
- } while (n > 4);
- }
- function isStreamCipher(name) {
- return RE_STREAM.test(name);
- }
- function readInt(buffer, start, stream, cb) {
- var bufferLen = buffer.length;
- if (start < 0 || start >= bufferLen || (bufferLen - start) < 4) {
- stream && stream._cleanup(cb);
- return false;
- }
- return buffer.readUInt32BE(start, true);
- }
- function DSASigBERToBare(signature) {
- if (signature.length <= 40)
- return signature;
- // This is a quick and dirty way to get from BER encoded r and s that
- // OpenSSL gives us, to just the bare values back to back (40 bytes
- // total) like OpenSSH (and possibly others) are expecting
- var asnReader = new Ber.Reader(signature);
- asnReader.readSequence();
- var r = asnReader.readString(Ber.Integer, true);
- var s = asnReader.readString(Ber.Integer, true);
- var rOffset = 0;
- var sOffset = 0;
- if (r.length < 20) {
- var rNew = new Buffer(20);
- r.copy(rNew, 1);
- r = rNew;
- r[0] = 0;
- }
- if (s.length < 20) {
- var sNew = new Buffer(20);
- s.copy(sNew, 1);
- s = sNew;
- s[0] = 0;
- }
- if (r.length > 20 && r[0] === 0x00)
- rOffset = 1;
- if (s.length > 20 && s[0] === 0x00)
- sOffset = 1;
- var newSig = new Buffer((r.length - rOffset) + (s.length - sOffset));
- r.copy(newSig, 0, rOffset);
- s.copy(newSig, r.length - rOffset, sOffset);
- return newSig;
- }
- function DSASigBareToBER(signature) {
- if (signature.length > 40)
- return signature;
- // Change bare signature r and s values to ASN.1 BER values for OpenSSL
- var asnWriter = new Ber.Writer();
- asnWriter.startSequence();
- var r = signature.slice(0, 20);
- var s = signature.slice(20);
- if (r[0] & 0x80) {
- var rNew = new Buffer(21);
- rNew[0] = 0x00;
- r.copy(rNew, 1);
- r = rNew;
- } else if (r[0] === 0x00 && !(r[1] & 0x80)) {
- r = r.slice(1);
- }
- if (s[0] & 0x80) {
- var sNew = new Buffer(21);
- sNew[0] = 0x00;
- s.copy(sNew, 1);
- s = sNew;
- } else if (s[0] === 0x00 && !(s[1] & 0x80)) {
- s = s.slice(1);
- }
- asnWriter.writeBuffer(r, Ber.Integer);
- asnWriter.writeBuffer(s, Ber.Integer);
- asnWriter.endSequence();
- return asnWriter.buffer;
- }
- function ECDSASigASN1ToSSH(signature) {
- if (signature[0] === 0x00)
- return signature;
- // Convert SSH signature parameters to ASN.1 BER values for OpenSSL
- var asnReader = new Ber.Reader(signature);
- asnReader.readSequence();
- var r = asnReader.readString(Ber.Integer, true);
- var s = asnReader.readString(Ber.Integer, true);
- if (r === null || s === null)
- throw new Error('Invalid signature');
- var newSig = new Buffer(4 + r.length + 4 + s.length);
- newSig.writeUInt32BE(r.length, 0, true);
- r.copy(newSig, 4);
- newSig.writeUInt32BE(s.length, 4 + r.length, true);
- s.copy(newSig, 4 + 4 + r.length);
- return newSig;
- }
- function ECDSASigSSHToASN1(signature, self, callback) {
- // Convert SSH signature parameters to ASN.1 BER values for OpenSSL
- var r = readString(signature, 0, self, callback);
- if (r === false)
- return false;
- var s = readString(signature, signature._pos, self, callback);
- if (s === false)
- return false;
- var asnWriter = new Ber.Writer();
- asnWriter.startSequence();
- asnWriter.writeBuffer(r, Ber.Integer);
- asnWriter.writeBuffer(s, Ber.Integer);
- asnWriter.endSequence();
- return asnWriter.buffer;
- }
- function RSAKeySSHToASN1(key, self, callback) {
- // Convert SSH key parameters to ASN.1 BER values for OpenSSL
- var e = readString(key, key._pos, self, callback);
- if (e === false)
- return false;
- var n = readString(key, key._pos, self, callback);
- if (n === false)
- return false;
- var asnWriter = new Ber.Writer();
- asnWriter.startSequence();
- // algorithm
- asnWriter.startSequence();
- asnWriter.writeOID('1.2.840.113549.1.1.1'); // rsaEncryption
- // algorithm parameters (RSA has none)
- asnWriter.writeNull();
- asnWriter.endSequence();
- // subjectPublicKey
- asnWriter.startSequence(Ber.BitString);
- asnWriter.writeByte(0x00);
- asnWriter.startSequence();
- asnWriter.writeBuffer(n, Ber.Integer);
- asnWriter.writeBuffer(e, Ber.Integer);
- asnWriter.endSequence();
- asnWriter.endSequence();
- asnWriter.endSequence();
- return asnWriter.buffer;
- }
- function DSAKeySSHToASN1(key, self, callback) {
- // Convert SSH key parameters to ASN.1 BER values for OpenSSL
- var p = readString(key, key._pos, self, callback);
- if (p === false)
- return false;
- var q = readString(key, key._pos, self, callback);
- if (q === false)
- return false;
- var g = readString(key, key._pos, self, callback);
- if (g === false)
- return false;
- var y = readString(key, key._pos, self, callback);
- if (y === false)
- return false;
- var asnWriter = new Ber.Writer();
- asnWriter.startSequence();
- // algorithm
- asnWriter.startSequence();
- asnWriter.writeOID('1.2.840.10040.4.1'); // id-dsa
- // algorithm parameters
- asnWriter.startSequence();
- asnWriter.writeBuffer(p, Ber.Integer);
- asnWriter.writeBuffer(q, Ber.Integer);
- asnWriter.writeBuffer(g, Ber.Integer);
- asnWriter.endSequence();
- asnWriter.endSequence();
- // subjectPublicKey
- asnWriter.startSequence(Ber.BitString);
- asnWriter.writeByte(0x00);
- asnWriter.writeBuffer(y, Ber.Integer);
- asnWriter.endSequence();
- asnWriter.endSequence();
- return asnWriter.buffer;
- }
- function ECDSAKeySSHToASN1(key, self, callback) {
- // Convert SSH key parameters to ASN.1 BER values for OpenSSL
- var curve = readString(key, key._pos, self, callback);
- if (curve === false)
- return false;
- var Q = readString(key, key._pos, self, callback);
- if (Q === false)
- return false;
- var ecCurveOID;
- switch (curve.toString('ascii')) {
- case 'nistp256':
- // prime256v1/secp256r1
- ecCurveOID = '1.2.840.10045.3.1.7';
- break;
- case 'nistp384':
- // secp384r1
- ecCurveOID = '1.3.132.0.34';
- break;
- case 'nistp521':
- // secp521r1
- ecCurveOID = '1.3.132.0.35';
- break;
- default:
- return false;
- }
- var asnWriter = new Ber.Writer();
- asnWriter.startSequence();
- // algorithm
- asnWriter.startSequence();
- asnWriter.writeOID('1.2.840.10045.2.1'); // id-ecPublicKey
- // algorithm parameters (namedCurve)
- asnWriter.writeOID(ecCurveOID);
- asnWriter.endSequence();
- // subjectPublicKey
- asnWriter.startSequence(Ber.BitString);
- asnWriter.writeByte(0x00);
- // XXX: hack to write a raw buffer without a tag -- yuck
- asnWriter._ensure(Q.length);
- Q.copy(asnWriter._buf, asnWriter._offset, 0, Q.length);
- asnWriter._offset += Q.length;
- // end hack
- asnWriter.endSequence();
- asnWriter.endSequence();
- return asnWriter.buffer;
- }
- function decryptKey(keyInfo, passphrase) {
- if (keyInfo._decrypted || !keyInfo.encryption)
- return;
- var keylen = 0;
- var key;
- var iv;
- var dc;
- keyInfo.encryption = (SSH_TO_OPENSSL[keyInfo.encryption]
- || keyInfo.encryption);
- switch (keyInfo.encryption) {
- case 'aes-256-cbc':
- case 'aes-256-ctr':
- keylen = 32;
- break;
- case 'des-ede3-cbc':
- case 'des-ede3':
- case 'aes-192-cbc':
- case 'aes-192-ctr':
- keylen = 24;
- break;
- case 'aes-128-cbc':
- case 'aes-128-ctr':
- case 'cast-cbc':
- case 'bf-cbc':
- keylen = 16;
- break;
- default:
- throw new Error('Unsupported cipher for encrypted key: '
- + keyInfo.encryption);
- }
- if (keyInfo.ppk) {
- iv = PPK_IV;
- key = Buffer.concat([
- crypto.createHash('sha1')
- .update('\x00\x00\x00\x00' + passphrase, 'utf8')
- .digest(),
- crypto.createHash('sha1')
- .update('\x00\x00\x00\x01' + passphrase, 'utf8')
- .digest()
- ]);
- key = key.slice(0, keylen);
- } else {
- iv = new Buffer(keyInfo.extra[0], 'hex');
- key = crypto.createHash('md5')
- .update(passphrase, 'utf8')
- .update(iv.slice(0, 8))
- .digest();
- while (keylen > key.length) {
- key = Buffer.concat([
- key,
- (crypto.createHash('md5')
- .update(key)
- .update(passphrase, 'utf8')
- .update(iv)
- .digest()).slice(0, 8)
- ]);
- }
- if (key.length > keylen)
- key = key.slice(0, keylen);
- }
- dc = crypto.createDecipheriv(keyInfo.encryption, key, iv);
- dc.setAutoPadding(false);
- keyInfo.private = Buffer.concat([ dc.update(keyInfo.private), dc.final() ]);
- keyInfo._decrypted = true;
- if (keyInfo.privateOrig) {
- // Update our original base64-encoded version of the private key
- var orig = keyInfo.privateOrig.toString('utf8');
- var newOrig = /^(.+(?:\r\n|\n))/.exec(orig)[1];
- var b64key = keyInfo.private.toString('base64');
- newOrig += b64key.match(/.{1,70}/g).join('\n');
- newOrig += /((?:\r\n|\n).+)$/.exec(orig)[1];
- keyInfo.privateOrig = newOrig;
- } else if (keyInfo.ppk) {
- var valid = verifyPPKMAC(keyInfo, passphrase, keyInfo.private);
- if (!valid)
- throw new Error('PPK MAC mismatch');
- // Automatically convert private key data to OpenSSL format
- // (including PEM)
- convertPPKPrivate(keyInfo);
- }
- // Fill in full key type
- // TODO: make DRY, we do this also in keyParser
- if (keyInfo.type !== 'ec') {
- keyInfo.fulltype = 'ssh-' + keyInfo.type;
- } else {
- // ECDSA
- var asnReader = new Ber.Reader(keyInfo.private);
- asnReader.readSequence();
- asnReader.readInt();
- asnReader.readString(Ber.OctetString, true);
- asnReader.readByte(); // Skip "complex" context type byte
- var offset = asnReader.readLength(); // Skip context length
- if (offset !== null) {
- asnReader._offset = offset;
- switch (asnReader.readOID()) {
- case '1.2.840.10045.3.1.7':
- // prime256v1/secp256r1
- keyInfo.fulltype = 'ecdsa-sha2-nistp256';
- break;
- case '1.3.132.0.34':
- // secp384r1
- keyInfo.fulltype = 'ecdsa-sha2-nistp384';
- break;
- case '1.3.132.0.35':
- // secp521r1
- keyInfo.fulltype = 'ecdsa-sha2-nistp521';
- break;
- }
- }
- if (keyInfo.fulltype === undefined)
- return new Error('Unsupported EC private key type');
- }
- }
- function genPublicKey(keyInfo) {
- var publicKey;
- var i;
- // RSA
- var n;
- var e;
- // DSA
- var p;
- var q;
- var g;
- var y;
- // ECDSA
- var d;
- var Q;
- var ecCurveOID;
- var ecCurveName;
- if (keyInfo.private) {
- // parsing private key in ASN.1 format in order to generate a public key
- var privKey = keyInfo.private;
- var asnReader = new Ber.Reader(privKey);
- var errMsg;
- if (asnReader.readSequence() === null) {
- errMsg = 'Malformed private key (expected sequence)';
- if (keyInfo._decrypted)
- errMsg += '. Bad passphrase?';
- throw new Error(errMsg);
- }
- // version (ignored)
- if (asnReader.readInt() === null) {
- errMsg = 'Malformed private key (expected version)';
- if (keyInfo._decrypted)
- errMsg += '. Bad passphrase?';
- throw new Error(errMsg);
- }
- if (keyInfo.type === 'rsa') {
- // modulus (n) -- integer
- n = asnReader.readString(Ber.Integer, true);
- if (n === null) {
- errMsg = 'Malformed private key (expected RSA n value)';
- if (keyInfo._decrypted)
- errMsg += '. Bad passphrase?';
- throw new Error(errMsg);
- }
- // public exponent (e) -- integer
- e = asnReader.readString(Ber.Integer, true);
- if (e === null) {
- errMsg = 'Malformed private key (expected RSA e value)';
- if (keyInfo._decrypted)
- errMsg += '. Bad passphrase?';
- throw new Error(errMsg);
- }
- publicKey = new Buffer(4 + 7 // ssh-rsa
- + 4 + n.length
- + 4 + e.length);
- publicKey.writeUInt32BE(7, 0, true);
- publicKey.write('ssh-rsa', 4, 7, 'ascii');
- i = 4 + 7;
- publicKey.writeUInt32BE(e.length, i, true);
- e.copy(publicKey, i += 4);
- publicKey.writeUInt32BE(n.length, i += e.length, true);
- n.copy(publicKey, i += 4);
- } else if (keyInfo.type === 'dss') { // DSA
- // prime (p) -- integer
- p = asnReader.readString(Ber.Integer, true);
- if (p === null) {
- errMsg = 'Malformed private key (expected DSA p value)';
- if (keyInfo._decrypted)
- errMsg += '. Bad passphrase?';
- throw new Error(errMsg);
- }
- // group order (q) -- integer
- q = asnReader.readString(Ber.Integer, true);
- if (q === null) {
- errMsg = 'Malformed private key (expected DSA q value)';
- if (keyInfo._decrypted)
- errMsg += '. Bad passphrase?';
- throw new Error(errMsg);
- }
- // group generator (g) -- integer
- g = asnReader.readString(Ber.Integer, true);
- if (g === null) {
- errMsg = 'Malformed private key (expected DSA g value)';
- if (keyInfo._decrypted)
- errMsg += '. Bad passphrase?';
- throw new Error(errMsg);
- }
- // public key value (y) -- integer
- y = asnReader.readString(Ber.Integer, true);
- if (y === null) {
- errMsg = 'Malformed private key (expected DSA y value)';
- if (keyInfo._decrypted)
- errMsg += '. Bad passphrase?';
- throw new Error(errMsg);
- }
- publicKey = new Buffer(4 + 7 // ssh-dss
- + 4 + p.length
- + 4 + q.length
- + 4 + g.length
- + 4 + y.length);
- publicKey.writeUInt32BE(7, 0, true);
- publicKey.write('ssh-dss', 4, 7, 'ascii');
- i = 4 + 7;
- publicKey.writeUInt32BE(p.length, i, true);
- p.copy(publicKey, i += 4);
- publicKey.writeUInt32BE(q.length, i += p.length, true);
- q.copy(publicKey, i += 4);
- publicKey.writeUInt32BE(g.length, i += q.length, true);
- g.copy(publicKey, i += 4);
- publicKey.writeUInt32BE(y.length, i += g.length, true);
- y.copy(publicKey, i += 4);
- } else { // ECDSA
- d = asnReader.readString(Ber.OctetString, true);
- if (d === null)
- throw new Error('Malformed private key (expected ECDSA private key)');
- asnReader.readByte(); // Skip "complex" context type byte
- var offset = asnReader.readLength(); // Skip context length
- if (offset === null)
- throw new Error('Malformed private key (expected ECDSA context value)');
- asnReader._offset = offset;
- ecCurveOID = asnReader.readOID();
- if (ecCurveOID === null)
- throw new Error('Malformed private key (expected ECDSA curve)');
- var tempECDH;
- switch (ecCurveOID) {
- case '1.2.840.10045.3.1.7':
- // prime256v1/secp256r1
- keyInfo.curve = ecCurveName = 'nistp256';
- tempECDH = crypto.createECDH('prime256v1');
- break;
- case '1.3.132.0.34':
- // secp384r1
- keyInfo.curve = ecCurveName = 'nistp384';
- tempECDH = crypto.createECDH('secp384r1');
- break;
- case '1.3.132.0.35':
- // secp521r1
- keyInfo.curve = ecCurveName = 'nistp521';
- tempECDH = crypto.createECDH('secp521r1');
- break;
- default:
- throw new Error('Malformed private key (unsupported EC curve)');
- }
- tempECDH.setPrivateKey(d);
- Q = tempECDH.getPublicKey();
- publicKey = new Buffer(4 + 19 // ecdsa-sha2-<curve name>
- + 4 + 8 // <curve name>
- + 4 + Q.length);
- publicKey.writeUInt32BE(19, 0, true);
- publicKey.write('ecdsa-sha2-' + ecCurveName, 4, 19, 'ascii');
- publicKey.writeUInt32BE(8, 23, true);
- publicKey.write(ecCurveName, 27, 8, 'ascii');
- publicKey.writeUInt32BE(Q.length, 35, true);
- Q.copy(publicKey, 39);
- }
- } else if (keyInfo.public) {
- publicKey = keyInfo.public;
- if (keyInfo.type === 'ec') {
- // TODO: support adding ecdsa-* prefix
- ecCurveName = keyInfo.curve;
- } else if (publicKey[0] !== 0
- // check for missing ssh-{dsa,rsa} prefix
- || publicKey[1] !== 0
- || publicKey[2] !== 0
- || publicKey[3] !== 7
- || publicKey[4] !== 115
- || publicKey[5] !== 115
- || publicKey[6] !== 104
- || publicKey[7] !== 45
- || ((publicKey[8] !== 114
- || publicKey[9] !== 115
- || publicKey[10] !== 97)
- &&
- ((publicKey[8] !== 100
- || publicKey[9] !== 115
- || publicKey[10] !== 115)))) {
- var newPK = new Buffer(4 + 7 + publicKey.length);
- publicKey.copy(newPK, 11);
- newPK.writeUInt32BE(7, 0, true);
- if (keyInfo.type === 'rsa')
- newPK.write('ssh-rsa', 4, 7, 'ascii');
- else
- newPK.write('ssh-dss', 4, 7, 'ascii');
- publicKey = newPK;
- }
- } else
- throw new Error('Missing data generated by parseKey()');
- // generate a public key format for use with OpenSSL
- i = 4 + 7;
- var fulltype;
- var asn1KeyBuf;
- if (keyInfo.type === 'rsa') {
- fulltype = 'ssh-rsa';
- asn1KeyBuf = RSAKeySSHToASN1(publicKey.slice(4 + 7));
- } else if (keyInfo.type === 'dss') {
- fulltype = 'ssh-dss';
- asn1KeyBuf = DSAKeySSHToASN1(publicKey.slice(4 + 7));
- } else { // ECDSA
- fulltype = 'ecdsa-sha2-' + ecCurveName;
- asn1KeyBuf = ECDSAKeySSHToASN1(publicKey.slice(4 + 19));
- }
- if (!asn1KeyBuf)
- throw new Error('Invalid SSH-formatted public key');
- var b64key = asn1KeyBuf.toString('base64').replace(RE_KEY_LEN, '$1\n');
- var fullkey = '-----BEGIN PUBLIC KEY-----\n'
- + b64key
- + (b64key[b64key.length - 1] === '\n' ? '' : '\n')
- + '-----END PUBLIC KEY-----';
- return {
- type: keyInfo.type,
- fulltype: fulltype,
- curve: ecCurveName,
- public: publicKey,
- publicOrig: new Buffer(fullkey)
- };
- }
- function verifyPPKMAC(keyInfo, passphrase, privateKey) {
- if (keyInfo._macresult !== undefined)
- return keyInfo._macresult;
- else if (!keyInfo.ppk)
- throw new Error("Key isn't a PPK");
- else if (!keyInfo.privateMAC)
- throw new Error('Missing MAC');
- else if (!privateKey)
- throw new Error('Missing raw private key data');
- else if (keyInfo.encryption && typeof passphrase !== 'string')
- throw new Error('Missing passphrase for encrypted PPK');
- else if (keyInfo.encryption && !keyInfo._decrypted)
- throw new Error('PPK must be decrypted before verifying MAC');
- var mac = keyInfo.privateMAC;
- var typelen = keyInfo.fulltype.length;
- // encryption algorithm is converted at this point for use with OpenSSL,
- // so we need to use the original value so that the MAC is calculated
- // correctly
- var enc = (keyInfo.encryption ? 'aes256-cbc' : 'none');
- var enclen = enc.length;
- var commlen = Buffer.byteLength(keyInfo.comment);
- var pub = keyInfo.public;
- var publen = pub.length;
- var privlen = privateKey.length;
- var macdata = new Buffer(4 + typelen
- + 4 + enclen
- + 4 + commlen
- + 4 + publen
- + 4 + privlen);
- var p = 0;
- macdata.writeUInt32BE(typelen, p, true);
- macdata.write(keyInfo.fulltype, p += 4, typelen, 'ascii');
- macdata.writeUInt32BE(enclen, p += typelen, true);
- macdata.write(enc, p += 4, enclen, 'ascii');
- macdata.writeUInt32BE(commlen, p += enclen, true);
- macdata.write(keyInfo.comment, p += 4, commlen, 'utf8');
- macdata.writeUInt32BE(publen, p += commlen, true);
- pub.copy(macdata, p += 4);
- macdata.writeUInt32BE(privlen, p += publen, true);
- privateKey.copy(macdata, p += 4);
- if (typeof passphrase !== 'string')
- passphrase = '';
- var mackey = crypto.createHash('sha1')
- .update('putty-private-key-file-mac-key', 'ascii')
- .update(passphrase, 'utf8')
- .digest();
- var calcMAC = crypto.createHmac('sha1', mackey)
- .update(macdata)
- .digest('hex');
- return (keyInfo._macresult = (calcMAC === mac));
- }
- function convertPPKPrivate(keyInfo) {
- if (!keyInfo.ppk || !keyInfo.public || !keyInfo.private)
- throw new Error("Key isn't a PPK");
- else if (keyInfo._converted)
- return false;
- var pub = keyInfo.public;
- var priv = keyInfo.private;
- var asnWriter = new Ber.Writer();
- var p;
- var q;
- if (keyInfo.type === 'rsa') {
- var e = readString(pub, 4 + 7);
- var n = readString(pub, pub._pos);
- var d = readString(priv, 0);
- p = readString(priv, priv._pos);
- q = readString(priv, priv._pos);
- var iqmp = readString(priv, priv._pos);
- var p1 = new BigInteger(p, 256);
- var q1 = new BigInteger(q, 256);
- var dmp1 = new BigInteger(d, 256);
- var dmq1 = new BigInteger(d, 256);
- dmp1 = new Buffer(dmp1.mod(p1.subtract(BigInteger.ONE)).toByteArray());
- dmq1 = new Buffer(dmq1.mod(q1.subtract(BigInteger.ONE)).toByteArray());
- asnWriter.startSequence();
- asnWriter.writeInt(0x00, Ber.Integer);
- asnWriter.writeBuffer(n, Ber.Integer);
- asnWriter.writeBuffer(e, Ber.Integer);
- asnWriter.writeBuffer(d, Ber.Integer);
- asnWriter.writeBuffer(p, Ber.Integer);
- asnWriter.writeBuffer(q, Ber.Integer);
- asnWriter.writeBuffer(dmp1, Ber.Integer);
- asnWriter.writeBuffer(dmq1, Ber.Integer);
- asnWriter.writeBuffer(iqmp, Ber.Integer);
- asnWriter.endSequence();
- } else {
- p = readString(pub, 4 + 7);
- q = readString(pub, pub._pos);
- var g = readString(pub, pub._pos);
- var y = readString(pub, pub._pos);
- var x = readString(priv, 0);
- asnWriter.startSequence();
- asnWriter.writeInt(0x00, Ber.Integer);
- asnWriter.writeBuffer(p, Ber.Integer);
- asnWriter.writeBuffer(q, Ber.Integer);
- asnWriter.writeBuffer(g, Ber.Integer);
- asnWriter.writeBuffer(y, Ber.Integer);
- asnWriter.writeBuffer(x, Ber.Integer);
- asnWriter.endSequence();
- }
- var b64key = asnWriter.buffer.toString('base64').replace(RE_KEY_LEN, '$1\n');
- var fullkey = '-----BEGIN '
- + (keyInfo.type === 'rsa' ? 'RSA' : 'DSA')
- + ' PRIVATE KEY-----\n'
- + b64key
- + (b64key[b64key.length - 1] === '\n' ? '' : '\n')
- + '-----END '
- + (keyInfo.type === 'rsa' ? 'RSA' : 'DSA')
- + ' PRIVATE KEY-----';
- keyInfo.private = asnWriter.buffer;
- keyInfo.privateOrig = new Buffer(fullkey);
- keyInfo._converted = true;
- return true;
- }
- function readString(buffer, start, encoding, stream, cb, maxLen) {
- if (encoding && !Buffer.isBuffer(encoding) && typeof encoding !== 'string') {
- if (typeof cb === 'number')
- maxLen = cb;
- cb = stream;
- stream = encoding;
- encoding = undefined;
- }
- start || (start = 0);
- var bufferLen = buffer.length;
- var left = (bufferLen - start);
- var len;
- var end;
- if (start < 0 || start >= bufferLen || left < 4) {
- stream && stream._cleanup(cb);
- return false;
- }
- len = buffer.readUInt32BE(start, true);
- if (len > (maxLen || MAX_STRING_LEN) || left < (4 + len)) {
- stream && stream._cleanup(cb);
- return false;
- }
- start += 4;
- end = start + len;
- buffer._pos = end;
- if (encoding) {
- if (Buffer.isBuffer(encoding)) {
- buffer.copy(encoding, 0, start, end);
- return encoding;
- } else
- return buffer.toString(encoding, start, end);
- } else
- return buffer.slice(start, end);
- }
|