text: (function(){// Copyright 2012 Joyent, Inc.  All rights reserved.
  
  var assert = require('assert-plus');
  var crypto = require('crypto');
  
  var asn1 = require('asn1');
  var ctype = require('ctype');
  
  
  
  ///--- Helpers
  
  function readNext(buffer, offset) {
    var len = ctype.ruint32(buffer, 'big', offset);
    offset += 4;
  
    var newOffset = offset + len;
  
    return {
      data: buffer.slice(offset, newOffset),
      offset: newOffset
    };
  }
  
  
  function writeInt(writer, buffer) {
    writer.writeByte(0x02); // ASN1.Integer
    writer.writeLength(buffer.length);
  
    for (var i = 0; i < buffer.length; i++)
      writer.writeByte(buffer[i]);
  
    return writer;
  }
  
  
  function rsaToPEM(key) {
    var buffer;
    var der;
    var exponent;
    var i;
    var modulus;
    var newKey = '';
    var offset = 0;
    var type;
    var tmp;
  
    try {
      buffer = new Buffer(key.split(' ')[1], 'base64');
  
      tmp = readNext(buffer, offset);
      type = tmp.data.toString();
      offset = tmp.offset;
  
      if (type !== 'ssh-rsa')
        throw new Error('Invalid ssh key type: ' + type);
  
      tmp = readNext(buffer, offset);
      exponent = tmp.data;
      offset = tmp.offset;
  
      tmp = readNext(buffer, offset);
      modulus = tmp.data;
    } catch (e) {
      throw new Error('Invalid ssh key: ' + key);
    }
  
    // DER is a subset of BER
    der = new asn1.BerWriter();
  
    der.startSequence();
  
    der.startSequence();
    der.writeOID('1.2.840.113549.1.1.1');
    der.writeNull();
    der.endSequence();
  
    der.startSequence(0x03); // bit string
    der.writeByte(0x00);
  
    // Actual key
    der.startSequence();
    writeInt(der, modulus);
    writeInt(der, exponent);
    der.endSequence();
  
    // bit string
    der.endSequence();
  
    der.endSequence();
  
    tmp = der.buffer.toString('base64');
    for (i = 0; i < tmp.length; i++) {
      if ((i % 64) === 0)
        newKey += '\n';
      newKey += tmp.charAt(i);
    }
  
    if (!/\\n$/.test(newKey))
      newKey += '\n';
  
    return '-----BEGIN PUBLIC KEY-----' + newKey + '-----END PUBLIC KEY-----\n';
  }
  
  
  function dsaToPEM(key) {
    var buffer;
    var offset = 0;
    var tmp;
    var der;
    var newKey = '';
  
    var type;
    var p;
    var q;
    var g;
    var y;
  
    try {
      buffer = new Buffer(key.split(' ')[1], 'base64');
  
      tmp = readNext(buffer, offset);
      type = tmp.data.toString();
      offset = tmp.offset;
  
      /* JSSTYLED */
      if (!/^ssh-ds[as].*/.test(type))
        throw new Error('Invalid ssh key type: ' + type);
  
      tmp = readNext(buffer, offset);
      p = tmp.data;
      offset = tmp.offset;
  
      tmp = readNext(buffer, offset);
      q = tmp.data;
      offset = tmp.offset;
  
      tmp = readNext(buffer, offset);
      g = tmp.data;
      offset = tmp.offset;
  
      tmp = readNext(buffer, offset);
      y = tmp.data;
    } catch (e) {
      console.log(e.stack);
      throw new Error('Invalid ssh key: ' + key);
    }
  
    // DER is a subset of BER
    der = new asn1.BerWriter();
  
    der.startSequence();
  
    der.startSequence();
    der.writeOID('1.2.840.10040.4.1');
  
    der.startSequence();
    writeInt(der, p);
    writeInt(der, q);
    writeInt(der, g);
    der.endSequence();
  
    der.endSequence();
  
    der.startSequence(0x03); // bit string
    der.writeByte(0x00);
    writeInt(der, y);
    der.endSequence();
  
    der.endSequence();
  
    tmp = der.buffer.toString('base64');
    for (var i = 0; i < tmp.length; i++) {
      if ((i % 64) === 0)
        newKey += '\n';
      newKey += tmp.charAt(i);
    }
  
    if (!/\\n$/.test(newKey))
      newKey += '\n';
  
    return '-----BEGIN PUBLIC KEY-----' + newKey + '-----END PUBLIC KEY-----\n';
  }
  
  
  ///--- API
  
  module.exports = {
  
    /**
     * Converts an OpenSSH public key (rsa only) to a PKCS#8 PEM file.
     *
     * The intent of this module is to interoperate with OpenSSL only,
     * specifically the node crypto module's `verify` method.
     *
     * @param {String} key an OpenSSH public key.
     * @return {String} PEM encoded form of the RSA public key.
     * @throws {TypeError} on bad input.
     * @throws {Error} on invalid ssh key formatted data.
     */
    sshKeyToPEM: function sshKeyToPEM(key) {
      assert.string(key, 'ssh_key');
  
      /* JSSTYLED */
      if (/^ssh-rsa.*/.test(key))
        return rsaToPEM(key);
  
      /* JSSTYLED */
      if (/^ssh-ds[as].*/.test(key))
        return dsaToPEM(key);
  
      throw new Error('Only RSA and DSA public keys are allowed');
    },
  
  
    /**
     * Generates an OpenSSH fingerprint from an ssh public key.
     *
     * @param {String} key an OpenSSH public key.
     * @return {String} key fingerprint.
     * @throws {TypeError} on bad input.
     * @throws {Error} if what you passed doesn't look like an ssh public key.
     */
    fingerprint: function fingerprint(key) {
      assert.string(key, 'ssh_key');
  
      var pieces = key.split(' ');
      if (!pieces || !pieces.length || pieces.length < 2)
        throw new Error('invalid ssh key');
  
      var data = new Buffer(pieces[1], 'base64');
  
      var hash = crypto.createHash('md5');
      hash.update(data);
      var digest = hash.digest('hex');
  
      var fp = '';
      for (var i = 0; i < digest.length; i++) {
        if (i && i % 2 === 0)
          fp += ':';
  
        fp += digest[i];
      }
  
      return fp;
    },
  
    /**
    * Converts a PKGCS#8 PEM file to an OpenSSH public key (rsa)
    *
    * The reverse of the above function.
    */
    pemToRsaSSHKey: function pemToRsaSSHKey(pem, comment) {
      assert.equal('string', typeof pem, 'typeof pem');
  
      // chop off the BEGIN PUBLIC KEY and END PUBLIC KEY portion
      var cleaned = pem.split('\n').slice(1, -2).join('');
  
      var buf = new Buffer(cleaned, 'base64');
  
      var der = new asn1.BerReader(buf);
  
      der.readSequence();
      der.readSequence();
  
      var oid = der.readOID();
      assert.equal(oid, '1.2.840.113549.1.1.1', 'pem not in RSA format');
  
      // Null -- XXX this probably isn't good practice
      der.readByte();
      der.readByte();
  
      // bit string sequence
      der.readSequence(0x03);
      der.readByte();
      der.readSequence();
  
      // modulus
      assert.equal(der.peek(), asn1.Ber.Integer, 'modulus not an integer');
      der._offset = der.readLength(der.offset + 1);
      var modulus = der._buf.slice(der.offset, der.offset + der.length);
      der._offset += der.length;
  
      // exponent
      assert.equal(der.peek(), asn1.Ber.Integer, 'exponent not an integer');
      der._offset = der.readLength(der.offset + 1);
      var exponent = der._buf.slice(der.offset, der.offset + der.length);
      der._offset += der.length;
  
      // now, make the key
      var type = new Buffer('ssh-rsa');
      var buffer = new Buffer(4 + type.length + 4 + modulus.length + 4 + exponent.length);
      var i = 0;
      buffer.writeUInt32BE(type.length, i);     i += 4;
      type.copy(buffer, i);                     i += type.length;
      buffer.writeUInt32BE(exponent.length, i); i += 4;
      exponent.copy(buffer, i);                 i += exponent.length;
      buffer.writeUInt32BE(modulus.length, i);  i += 4;
      modulus.copy(buffer, i);                  i += modulus.length;
  
      var s = type.toString() + ' ' + buffer.toString('base64') + ' ' + (comment || '');
      return s;
    }
  };
  
  })();
  