code:
/**
 * HTTP client-side implementation that uses forge.net sockets.
 *
 * @author Dave Longley
 *
 * Copyright (c) 2010-2014 Digital Bazaar, Inc. All rights reserved.
 */
var forge = require('./forge');
require('./tls');
require('./util');

// define http namespace
var http = module.exports = forge.http = forge.http || {};

// logging category
var cat = 'forge.http';

// normalizes an http header field name
var _normalize = function(name) {
  return name.toLowerCase().replace(/(^.)|(-.)/g,
    function(a) {return a.toUpperCase();});
};

/**
 * Gets the local storage ID for the given client.
 *
 * @param client the client to get the local storage ID for.
 *
 * @return the local storage ID to use.
 */
var _getStorageId = function(client) {
  // TODO: include browser in ID to avoid sharing cookies between
  // browsers (if this is undesirable)
  // navigator.userAgent
  return 'forge.http.' +
    client.url.protocol.slice(0, -1) + '.' +
    client.url.hostname + '.' +
    client.url.port;
};

/**
 * Loads persistent cookies from disk for the given client.
 *
 * @param client the client.
 */
var _loadCookies = function(client) {
  if(client.persistCookies) {
    try {
      var cookies = forge.util.getItem(
        client.socketPool.flashApi,
        _getStorageId(client), 'cookies');
      client.cookies = cookies || {};
    } catch(ex) {
      // no flash storage available, just silently fail
      // TODO: i assume we want this logged somewhere or
      // should it actually generate an error
      //forge.log.error(cat, ex);
    }
  }
};

/**
 * Saves persistent cookies on disk for the given client.
 *
 * @param client the client.
 */
var _saveCookies = function(client) {
  if(client.persistCookies) {
    try {
      forge.util.setItem(
        client.socketPool.flashApi,
        _getStorageId(client), 'cookies', client.cookies);
    } catch(ex) {
      // no flash storage available, just silently fail
      // TODO: i assume we want this logged somewhere or
      // should it actually generate an error
      //forge.log.error(cat, ex);
    }
  }

  // FIXME: remove me
  _loadCookies(client);
};

/**
 * Clears persistent cookies on disk for the given client.
 *
 * @param client the client.
 */
var _clearCookies = function(client) {
  if(client.persistCookies) {
    try {
      // only thing stored is 'cookies', so clear whole storage
      forge.util.clearItems(
        client.socketPool.flashApi,
        _getStorageId(client));
    } catch(ex) {
      // no flash storage available, just silently fail
      // TODO: i assume we want this logged somewhere or
      // should it actually generate an error
      //forge.log.error(cat, ex);
    }
  }
};

/**
 * Connects and sends a request.
 *
 * @param client the http client.
 * @param socket the socket to use.
 */
var _doRequest = function(client, socket) {
  if(socket.isConnected()) {
    // already connected
    socket.options.request.connectTime = +new Date();
    socket.connected({
      type: 'connect',
      id: socket.id
    });
  } else {
    // connect
    socket.options.request.connectTime = +new Date();
    socket.connect({
      host: client.url.hostname,
      port: client.url.port,
      policyPort: client.policyPort,
      policyUrl: client.policyUrl
    });
  }
};

/**
 * Handles the next request or marks a socket as idle.
 *
 * @param client the http client.
 * @param socket the socket.
 */
var _handleNextRequest = function(client, socket) {
  // clear buffer
  socket.buffer.clear();

  // get pending request
  var pending = null;
  while(pending === null && client.requests.length > 0) {
    pending = client.requests.shift();
    if(pending.request.aborted) {
      pending = null;
    }
  }

  // mark socket idle if no pending requests
  if(pending === null) {
    if(socket.options !== null) {
      socket.options = null;
    }
    client.idle.push(socket);
  } else {
    // handle pending request, allow 1 retry
    socket.retries = 1;
    socket.options = pending;
    _doRequest(client, socket);
  }
};

/**
 * Sets up a socket for use with an http client.
 *
 * @param client the parent http client.
 * @param socket the socket to set up.
 * @param tlsOptions if the socket must use TLS, the TLS options.
 */
var _initSocket = function(client, socket, tlsOptions) {
  // no socket options yet
  socket.options = null;

  // set up handlers
  socket.connected = function(e) {
    // socket primed by caching TLS session, handle next request
    if(socket.options === null) {
      _handleNextRequest(client, socket);
    } else {
      // socket in use
      var request = socket.options.request;
      request.connectTime = +new Date() - request.connectTime;
      e.socket = socket;
      socket.options.connected(e);
      if(request.aborted) {
        socket.close();
      } else {
        var out = request.toString();
        if(request.body) {
          out += request.body;
        }
        request.time = +new Date();
        socket.send(out);
        request.time = +new Date() - request.time;
        socket.options.response.time = +new Date();
        socket.sending = true;
      }
    }
  };
  socket.closed = function(e) {
    if(socket.sending) {
      socket.sending = false;
      if(socket.retries > 0) {
        --socket.retries;
        _doRequest(client, socket);
      } else {
        // error, closed during send
        socket.error({
          id: socket.id,
          type: 'ioError',
          message: 'Connection closed during send. Broken pipe.',
          bytesAvailable: 0
        });
      }
    } else {
      // handle unspecified content-length transfer
      var response = socket.options.response;
      if(response.readBodyUntilClose) {
        response.time = +new Date() - response.time;
        response.bodyReceived = true;
        socket.options.bodyReady({
          request: socket.options.request,
          response: response,
          socket: socket
        });
      }
      socket.options.closed(e);
      _handleNextRequest(client, socket);
    }
  };
  socket.data = function(e) {
    socket.sending = false;
    var request = socket.options.request;
    if(request.aborted) {
      socket.close();
    } else {
      // receive all bytes available
      var response = socket.options.response;
      var bytes = socket.receive(e.bytesAvailable);
      if(bytes !== null) {
        // receive header and then body
        socket.buffer.putBytes(bytes);
        if(!response.headerReceived) {
          response.readHeader(socket.buffer);
          if(response.headerReceived) {
            socket.options.headerReady({
              request: socket.options.request,
              response: response,
              socket: socket
            });
          }
        }
        if(response.headerReceived && !response.bodyReceived) {
          response.readBody(socket.buffer);
        }
        if(response.bodyReceived) {
          socket.options.bodyReady({
            request: socket.options.request,
            response: response,
            socket: socket
          });
          // close connection if requested or by default on http/1.0
          var value = response.getField('Connection') || '';
          if(value.indexOf('close') != -1 ||
            (response.version === 'HTTP/1.0' &&
            response.getField('Keep-Alive') === null)) {
            socket.close();
          } else {
            _handleNextRequest(client, socket);
          }
        }
      }
    }
  };
  socket.error = function(e) {
    // do error callback, include request
    socket.options.error({
      type: e.type,
      message: e.message,
      request: socket.options.request,
      response: socket.options.response,
      socket: socket
    });
    socket.close();
  };

  // wrap socket for TLS
  if(tlsOptions) {
    socket = forge.tls.wrapSocket({
      sessionId: null,
      sessionCache: {},
      caStore: tlsOptions.caStore,
      cipherSuites: tlsOptions.cipherSuites,
      socket: socket,
      virtualHost: tlsOptions.virtualHost,
      verify: tlsOptions.verify,
      getCertificate: tlsOptions.getCertificate,
      getPrivateKey: tlsOptions.getPrivateKey,
      getSignature: tlsOptions.getSignature,
      deflate: tlsOptions.deflate || null,
      inflate: tlsOptions.inflate || null
    });

    socket.options = null;
    socket.buffer = forge.util.createBuffer();
    client.sockets.push(socket);
    if(tlsOptions.prime) {
      // prime socket by connecting and caching TLS session, will do
      // next request from there
      socket.connect({
        host: client.url.hostname,
        port: client.url.port,
        policyPort: client.policyPort,
        policyUrl: client.policyUrl
      });
    } else {
      // do not prime socket, just add as idle
      client.idle.push(socket);
    }
  } else {
    // no need to prime non-TLS sockets
    socket.buffer = forge.util.createBuffer();
    client.sockets.push(socket);
    client.idle.push(socket);
  }
};

/**
 * Checks to see if the given cookie has expired. If the cookie's max-age
 * plus its created time is less than the time now, it has expired, unless
 * its max-age is set to -1 which indicates it will never expire.
 *
 * @param cookie the cookie to check.
 *
 * @return true if it has expired, false if not.
 */
var _hasCookieExpired = function(cookie) {
  var rval = false;

  if(cookie.maxAge !== -1) {
    var now = _getUtcTime(new Date());
    var expires = cookie.created + cookie.maxAge;
    if(expires <= now) {
      rval = true;
    }
  }

  return rval;
};

/**
 * Adds cookies in the given client to the given request.
 *
 * @param client the client.
 * @param request the request.
 */
var _writeCookies = function(client, request) {
  var expired = [];
  var url = client.url;
  var cookies = client.cookies;
  for(var name in cookies) {
    // get cookie paths
    var paths = cookies[name];
    for(var p in paths) {
      var cookie = paths[p];
      if(_hasCookieExpired(cookie)) {
        // store for clean up
        expired.push(cookie);
      } else if(request.path.indexOf(cookie.path) === 0) {
        // path or path's ancestor must match cookie.path
        request.addCookie(cookie);
      }
    }
  }

  // clean up expired cookies
  for(var i = 0; i < expired.length; ++i) {
    var cookie = expired[i];
    client.removeCookie(cookie.name, cookie.path);
  }
};

/**
 * Gets cookies from the given response and adds the to the given client.
 *
 * @param client the client.
 * @param response the response.
 */
var _readCookies = function(client, response) {
  var cookies = response.getCookies();
  for(var i = 0; i < cookies.length; ++i) {
    try {
      client.setCookie(cookies[i]);
    } catch(ex) {
      // ignore failure to add other-domain, etc. cookies
    }
  }
};

/**
 * Creates an http client that uses forge.net sockets as a backend and
 * forge.tls for security.
 *
 * @param options:
 *   url: the url to connect to (scheme://host:port).
 *   socketPool: the flash socket pool to use.
 *   policyPort: the flash policy port to use (if other than the
 *     socket pool default), use 0 for flash default.
 *   policyUrl: the flash policy file URL to use (if provided will
 *     be used instead of a policy port).
 *   connections: number of connections to use to handle requests.
 *   caCerts: an array of certificates to trust for TLS, certs may
 *     be PEM-formatted or cert objects produced via forge.pki.
 *   cipherSuites: an optional array of cipher suites to use,
 *     see forge.tls.CipherSuites.
 *   virtualHost: the virtual server name to use in a TLS SNI
 *     extension, if not provided the url host will be used.
 *   verify: a custom TLS certificate verify callback to use.
 *   getCertificate: an optional callback used to get a client-side
 *     certificate (see forge.tls for details).
 *   getPrivateKey: an optional callback used to get a client-side
 *     private key (see forge.tls for details).
 *   getSignature: an optional callback used to get a client-side
 *     signature (see forge.tls for details).
 *   persistCookies: true to use persistent cookies via flash local
 *     storage, false to only keep cookies in javascript.
 *   primeTlsSockets: true to immediately connect TLS sockets on
 *     their creation so that they will cache TLS sessions for reuse.
 *
 * @return the client.
 */
http.createClient = function(options) {
  // create CA store to share with all TLS connections
  var caStore = null;
  if(options.caCerts) {
    caStore = forge.pki.createCaStore(options.caCerts);
  }

  // get scheme, host, and port from url
  options.url = (options.url ||
    window.location.protocol + '//' + window.location.host);
  var url;
  try {
    url = new URL(options.url);
  } catch(e) {
    var error = new Error('Invalid url.');
    error.details = {url: options.url};
    throw error;
  }

  // default to 1 connection
  options.connections = options.connections || 1;

  // create client
  var sp = options.socketPool;
  var client = {
    // url
    url: url,
    // socket pool
    socketPool: sp,
    // the policy port to use
    policyPort: options.policyPort,
    // policy url to use
    policyUrl: options.policyUrl,
    // queue of requests to service
    requests: [],
    // all sockets
    sockets: [],
    // idle sockets
    idle: [],
    // whether or not the connections are secure
    secure: (url.protocol === 'https:'),
    // cookie jar (key'd off of name and then path, there is only 1 domain
    // and one setting for secure per client so name+path is unique)
    cookies: {},
    // default to flash storage of cookies
    persistCookies: (typeof(options.persistCookies) === 'undefined') ?
      true : options.persistCookies
  };

  // load cookies from disk
  _loadCookies(client);

  /**
   * A default certificate verify function that checks a certificate common
   * name against the client's URL host.
   *
   * @param c the TLS connection.
   * @param verified true if cert is verified, otherwise alert number.
   * @param depth the chain depth.
   * @param certs the cert chain.
   *
   * @return true if verified and the common name matches the host, error
   *         otherwise.
   */
  var _defaultCertificateVerify = function(c, verified, depth, certs) {
    if(depth === 0 && verified === true) {
      // compare common name to url host
      var cn = certs[depth].subject.getField('CN');
      if(cn === null || client.url.hostname !== cn.value) {
        verified = {
          message: 'Certificate common name does not match url host.'
        };
      }
    }
    return verified;
  };

  // determine if TLS is used
  var tlsOptions = null;
  if(client.secure) {
    tlsOptions = {
      caStore: caStore,
      cipherSuites: options.cipherSuites || null,
      virtualHost: options.virtualHost || url.hostname,
      verify: options.verify || _defaultCertificateVerify,
      getCertificate: options.getCertificate || null,
      getPrivateKey: options.getPrivateKey || null,
      getSignature: options.getSignature || null,
      prime: options.primeTlsSockets || false
    };

    // if socket pool uses a flash api, then add deflate support to TLS
    if(sp.flashApi !== null) {
      tlsOptions.deflate = function(bytes) {
        // strip 2 byte zlib header and 4 byte trailer
        return forge.util.deflate(sp.flashApi, bytes, true);
      };
      tlsOptions.inflate = function(bytes) {
        return forge.util.inflate(sp.flashApi, bytes, true);
      };
    }
  }

  // create and initialize sockets
  for(var i = 0; i < options.connections; ++i) {
    _initSocket(client, sp.createSocket(), tlsOptions);
  }

  /**
   * Sends a request. A method 'abort' will be set on the request that
   * can be called to attempt to abort the request.
   *
   * @param options:
   *          request: the request to send.
   *          connected: a callback for when the connection is open.
   *          closed: a callback for when the connection is closed.
   *          headerReady: a callback for when the response header arrives.
   *          bodyReady: a callback for when the response body arrives.
   *          error: a callback for if an error occurs.
   */
  client.send = function(options) {
    // add host header if not set
    if(options.request.getField('Host') === null) {
      options.request.setField('Host', client.url.origin);
    }

    // set default dummy handlers
    var opts = {};
    opts.request = options.request;
    opts.connected = options.connected || function() {};
    opts.closed = options.close || function() {};
    opts.headerReady = function(e) {
      // read cookies
      _readCookies(client, e.response);
      if(options.headerReady) {
        options.headerReady(e);
      }
    };
    opts.bodyReady = options.bodyReady || function() {};
    opts.error = options.error || function() {};

    // create response
    opts.response = http.createResponse();
    opts.response.time = 0;
    opts.response.flashApi = client.socketPool.flashApi;
    opts.request.flashApi = client.socketPool.flashApi;

    // create abort function
    opts.request.abort = function() {
      // set aborted, clear handlers
      opts.request.aborted = true;
      opts.connected = function() {};
      opts.closed = function() {};
      opts.headerReady = function() {};
      opts.bodyReady = function() {};
      opts.error = function() {};
    };

    // add cookies to request
    _writeCookies(client, opts.request);

    // queue request options if there are no idle sockets
    if(client.idle.length === 0) {
      client.requests.push(opts);
    } else {
      // use an idle socket, prefer an idle *connected* socket first
      var socket = null;
      var len = client.idle.length;
      for(var i = 0; socket === null && i < len; ++i) {
        socket = client.idle[i];
        if(socket.isConnected()) {
          client.idle.splice(i, 1);
        } else {
          socket = null;
        }
      }
      // no connected socket available, get unconnected socket
      if(socket === null) {
        socket = client.idle.pop();
      }
      socket.options = opts;
      _doRequest(client, socket);
    }
  };

  /**
   * Destroys this client.
   */
  client.destroy = function() {
    // clear pending requests, close and destroy sockets
    client.requests = [];
    for(var i = 0; i < client.sockets.length; ++i) {
      client.sockets[i].close();
      client.sockets[i].destroy();
    }
    client.socketPool = null;
    client.sockets = [];
    client.idle = [];
  };

  /**
   * Sets a cookie for use with all connections made by this client. Any
   * cookie with the same name will be replaced. If the cookie's value
   * is undefined, null, or the blank string, the cookie will be removed.
   *
   * If the cookie's domain doesn't match this client's url host or the
   * cookie's secure flag doesn't match this client's url scheme, then
   * setting the cookie will fail with an exception.
   *
   * @param cookie the cookie with parameters:
   *   name: the name of the cookie.
   *   value: the value of the cookie.
   *   comment: an optional comment string.
   *   maxAge: the age of the cookie in seconds relative to created time.
   *   secure: true if the cookie must be sent over a secure protocol.
   *   httpOnly: true to restrict access to the cookie from javascript
   *     (inaffective since the cookies are stored in javascript).
   *   path: the path for the cookie.
   *   domain: optional domain the cookie belongs to (must start with dot).
   *   version: optional version of the cookie.
   *   created: creation time, in UTC seconds, of the cookie.
   */
  client.setCookie = function(cookie) {
    var rval;
    if(typeof(cookie.name) !== 'undefined') {
      if(cookie.value === null || typeof(cookie.value) === 'undefined' ||
        cookie.value === '') {
        // remove cookie
        rval = client.removeCookie(cookie.name, cookie.path);
      } else {
        // set cookie defaults
        cookie.comment = cookie.comment || '';
        cookie.maxAge = cookie.maxAge || 0;
        cookie.secure = (typeof(cookie.secure) === 'undefined') ?
          true : cookie.secure;
        cookie.httpOnly = cookie.httpOnly || true;
        cookie.path = cookie.path || '/';
        cookie.domain = cookie.domain || null;
        cookie.version = cookie.version || null;
        cookie.created = _getUtcTime(new Date());

        // do secure check
        if(cookie.secure !== client.secure) {
          var error = new Error('Http client url scheme is incompatible ' +
            'with cookie secure flag.');
          error.url = client.url;
          error.cookie = cookie;
          throw error;
        }
        // make sure url host is within cookie.domain
        if(!http.withinCookieDomain(client.url, cookie)) {
          var error = new Error('Http client url scheme is incompatible ' +
            'with cookie secure flag.');
          error.url = client.url;
          error.cookie = cookie;
          throw error;
        }

        // add new cookie
        if(!(cookie.name in client.cookies)) {
          client.cookies[cookie.name] = {};
        }
        client.cookies[cookie.name][cookie.path] = cookie;
        rval = true;

        // save cookies
        _saveCookies(client);
      }
    }

    return rval;
  };

  /**
   * Gets a cookie by its name.
   *
   * @param name the name of the cookie to retrieve.
   * @param path an optional path for the cookie (if there are multiple
   *          cookies with the same name but different paths).
   *
   * @return the cookie or null if not found.
   */
  client.getCookie = function(name, path) {
    var rval = null;
    if(name in client.cookies) {
      var paths = client.cookies[name];

      // get path-specific cookie
      if(path) {
        if(path in paths) {
          rval = paths[path];
        }
      } else {
        // get first cookie
        for(var p in paths) {
          rval = paths[p];
          break;
        }
      }
    }
    return rval;
  };

  /**
   * Removes a cookie.
   *
   * @param name the name of the cookie to remove.
   * @param path an optional path for the cookie (if there are multiple
   *          cookies with the same name but different paths).
   *
   * @return true if a cookie was removed, false if not.
   */
  client.removeCookie = function(name, path) {
    var rval = false;
    if(name in client.cookies) {
      // delete the specific path
      if(path) {
        var paths = client.cookies[name];
        if(path in paths) {
          rval = true;
          delete client.cookies[name][path];
          // clean up entry if empty
          var empty = true;
          for(var i in client.cookies[name]) {
            empty = false;
            break;
          }
          if(empty) {
            delete client.cookies[name];
          }
        }
      } else {
        // delete all cookies with the given name
        rval = true;
        delete client.cookies[name];
      }
    }
    if(rval) {
      // save cookies
      _saveCookies(client);
    }
    return rval;
  };

  /**
   * Clears all cookies stored in this client.
   */
  client.clearCookies = function() {
    client.cookies = {};
    _clearCookies(client);
  };

  if(forge.log) {
    forge.log.debug('forge.http', 'created client', options);
  }

  return client;
};

/**
 * Trims the whitespace off of the beginning and end of a string.
 *
 * @param str the string to trim.
 *
 * @return the trimmed string.
 */
var _trimString = function(str) {
  return str.replace(/^\s*/, '').replace(/\s*$/, '');
};

/**
 * Creates an http header object.
 *
 * @return the http header object.
 */
var _createHeader = function() {
  var header = {
    fields: {},
    setField: function(name, value) {
      // normalize field name, trim value
      header.fields[_normalize(name)] = [_trimString('' + value)];
    },
    appendField: function(name, value) {
      name = _normalize(name);
      if(!(name in header.fields)) {
        header.fields[name] = [];
      }
      header.fields[name].push(_trimString('' + value));
    },
    getField: function(name, index) {
      var rval = null;
      name = _normalize(name);
      if(name in header.fields) {
        index = index || 0;
        rval = header.fields[name][index];
      }
      return rval;
    }
  };
  return header;
};

/**
 * Gets the time in utc seconds given a date.
 *
 * @param d the date to use.
 *
 * @return the time in utc seconds.
 */
var _getUtcTime = function(d) {
  var utc = +d + d.getTimezoneOffset() * 60000;
  return Math.floor(+new Date() / 1000);
};

/**
 * Creates an http request.
 *
 * @param options:
 *          version: the version.
 *          method: the method.
 *          path: the path.
 *          body: the body.
 *          headers: custom header fields to add,
 *            eg: [{'Content-Length': 0}].
 *
 * @return the http request.
 */
http.createRequest = function(options) {
  options = options || {};
  var request = _createHeader();
  request.version = options.version || 'HTTP/1.1';
  request.method = options.method || null;
  request.path = options.path || null;
  request.body = options.body || null;
  request.bodyDeflated = false;
  request.flashApi = null;

  // add custom headers
  var headers = options.headers || [];
  if(!forge.util.isArray(headers)) {
    headers = [headers];
  }
  for(var i = 0; i < headers.length; ++i) {
    for(var name in headers[i]) {
      request.appendField(name, headers[i][name]);
    }
  }

  /**
   * Adds a cookie to the request 'Cookie' header.
   *
   * @param cookie a cookie to add.
   */
  request.addCookie = function(cookie) {
    var value = '';
    var field = request.getField('Cookie');
    if(field !== null) {
      // separate cookies by semi-colons
      value = field + '; ';
    }

    // get current time in utc seconds
    var now = _getUtcTime(new Date());

    // output cookie name and value
    value += cookie.name + '=' + cookie.value;
    request.setField('Cookie', value);
  };

  /**
   * Converts an http request into a string that can be sent as an
   * HTTP request. Does not include any data.
   *
   * @return the string representation of the request.
   */
  request.toString = function() {
    /* Sample request header:
      GET /some/path/?query HTTP/1.1
      Host: www.someurl.com
      Connection: close
      Accept-Encoding: deflate
      Accept: image/gif, text/html
      User-Agent: Mozilla 4.0
     */

    // set default headers
    if(request.getField('User-Agent') === null) {
      request.setField('User-Agent', 'forge.http 1.0');
    }
    if(request.getField('Accept') === null) {
      request.setField('Accept', '*/*');
    }
    if(request.getField('Connection') === null) {
      request.setField('Connection', 'keep-alive');
      request.setField('Keep-Alive', '115');
    }

    // add Accept-Encoding if not specified
    if(request.flashApi !== null &&
      request.getField('Accept-Encoding') === null) {
      request.setField('Accept-Encoding', 'deflate');
    }

    // if the body isn't null, deflate it if its larger than 100 bytes
    if(request.flashApi !== null && request.body !== null &&
      request.getField('Content-Encoding') === null &&
      !request.bodyDeflated && request.body.length > 100) {
      // use flash to compress data
      request.body = forge.util.deflate(request.flashApi, request.body);
      request.bodyDeflated = true;
      request.setField('Content-Encoding', 'deflate');
      request.setField('Content-Length', request.body.length);
    } else if(request.body !== null) {
      // set content length for body
      request.setField('Content-Length', request.body.length);
    }

    // build start line
    var rval =
      request.method.toUpperCase() + ' ' + request.path + ' ' +
      request.version + '\r\n';

    // add each header
    for(var name in request.fields) {
      var fields = request.fields[name];
      for(var i = 0; i < fields.length; ++i) {
        rval += name + ': ' + fields[i] + '\r\n';
      }
    }
    // final terminating CRLF
    rval += '\r\n';

    return rval;
  };

  return request;
};

/**
 * Creates an empty http response header.
 *
 * @return the empty http response header.
 */
http.createResponse = function() {
  // private vars
  var _first = true;
  var _chunkSize = 0;
  var _chunksFinished = false;

  // create response
  var response = _createHeader();
  response.version = null;
  response.code = 0;
  response.message = null;
  response.body = null;
  response.headerReceived = false;
  response.bodyReceived = false;
  response.flashApi = null;

  /**
   * Reads a line that ends in CRLF from a byte buffer.
   *
   * @param b the byte buffer.
   *
   * @return the line or null if none was found.
   */
  var _readCrlf = function(b) {
    var line = null;
    var i = b.data.indexOf('\r\n', b.read);
    if(i != -1) {
      // read line, skip CRLF
      line = b.getBytes(i - b.read);
      b.getBytes(2);
    }
    return line;
  };

  /**
   * Parses a header field and appends it to the response.
   *
   * @param line the header field line.
   */
  var _parseHeader = function(line) {
    var tmp = line.indexOf(':');
    var name = line.substring(0, tmp++);
    response.appendField(
      name, (tmp < line.length) ? line.substring(tmp) : '');
  };

  /**
   * Reads an http response header from a buffer of bytes.
   *
   * @param b the byte buffer to parse the header from.
   *
   * @return true if the whole header was read, false if not.
   */
  response.readHeader = function(b) {
    // read header lines (each ends in CRLF)
    var line = '';
    while(!response.headerReceived && line !== null) {
      line = _readCrlf(b);
      if(line !== null) {
        // parse first line
        if(_first) {
          _first = false;
          var tmp = line.split(' ');
          if(tmp.length >= 3) {
            response.version = tmp[0];
            response.code = parseInt(tmp[1], 10);
            response.message = tmp.slice(2).join(' ');
          } else {
            // invalid header
            var error = new Error('Invalid http response header.');
            error.details = {'line': line};
            throw error;
          }
        } else if(line.length === 0) {
          // handle final line, end of header
          response.headerReceived = true;
        } else {
          _parseHeader(line);
        }
      }
    }

    return response.headerReceived;
  };

  /**
   * Reads some chunked http response entity-body from the given buffer of
   * bytes.
   *
   * @param b the byte buffer to read from.
   *
   * @return true if the whole body was read, false if not.
   */
  var _readChunkedBody = function(b) {
    /* Chunked transfer-encoding sends data in a series of chunks,
      followed by a set of 0-N http trailers.
      The format is as follows:

      chunk-size (in hex) CRLF
      chunk data (with "chunk-size" many bytes) CRLF
      ... (N many chunks)
      chunk-size (of 0 indicating the last chunk) CRLF
      N many http trailers followed by CRLF
      blank line + CRLF (terminates the trailers)

      If there are no http trailers, then after the chunk-size of 0,
      there is still a single CRLF (indicating the blank line + CRLF
      that terminates the trailers). In other words, you always terminate
      the trailers with blank line + CRLF, regardless of 0-N trailers. */

      /* From RFC-2616, section 3.6.1, here is the pseudo-code for
      implementing chunked transfer-encoding:

      length := 0
      read chunk-size, chunk-extension (if any) and CRLF
      while (chunk-size > 0) {
        read chunk-data and CRLF
        append chunk-data to entity-body
        length := length + chunk-size
        read chunk-size and CRLF
      }
      read entity-header
      while (entity-header not empty) {
        append entity-header to existing header fields
        read entity-header
      }
      Content-Length := length
      Remove "chunked" from Transfer-Encoding
    */

    var line = '';
    while(line !== null && b.length() > 0) {
      // if in the process of reading a chunk
      if(_chunkSize > 0) {
        // if there are not enough bytes to read chunk and its
        // trailing CRLF,  we must wait for more data to be received
        if(_chunkSize + 2 > b.length()) {
          break;
        }

        // read chunk data, skip CRLF
        response.body += b.getBytes(_chunkSize);
        b.getBytes(2);
        _chunkSize = 0;
      } else if(!_chunksFinished) {
        // more chunks, read next chunk-size line
        line = _readCrlf(b);
        if(line !== null) {
          // parse chunk-size (ignore any chunk extension)
          _chunkSize = parseInt(line.split(';', 1)[0], 16);
          _chunksFinished = (_chunkSize === 0);
        }
      } else {
        // chunks finished, read next trailer
        line = _readCrlf(b);
        while(line !== null) {
          if(line.length > 0) {
            // parse trailer
            _parseHeader(line);
            // read next trailer
            line = _readCrlf(b);
          } else {
            // body received
            response.bodyReceived = true;
            line = null;
          }
        }
      }
    }

    return response.bodyReceived;
  };

  /**
   * Reads an http response body from a buffer of bytes.
   *
   * @param b the byte buffer to read from.
   *
   * @return true if the whole body was read, false if not.
   */
  response.readBody = function(b) {
    var contentLength = response.getField('Content-Length');
    var transferEncoding = response.getField('Transfer-Encoding');
    if(contentLength !== null) {
      contentLength = parseInt(contentLength);
    }

    // read specified length
    if(contentLength !== null && contentLength >= 0) {
      response.body = response.body || '';
      response.body += b.getBytes(contentLength);
      response.bodyReceived = (response.body.length === contentLength);
    } else if(transferEncoding !== null) {
      // read chunked encoding
      if(transferEncoding.indexOf('chunked') != -1) {
        response.body = response.body || '';
        _readChunkedBody(b);
      } else {
        var error = new Error('Unknown Transfer-Encoding.');
        error.details = {'transferEncoding': transferEncoding};
        throw error;
      }
    } else if((contentLength !== null && contentLength < 0) ||
      (contentLength === null &&
      response.getField('Content-Type') !== null)) {
      // read all data in the buffer
      response.body = response.body || '';
      response.body += b.getBytes();
      response.readBodyUntilClose = true;
    } else {
      // no body
      response.body = null;
      response.bodyReceived = true;
    }

    if(response.bodyReceived) {
      response.time = +new Date() - response.time;
    }

    if(response.flashApi !== null &&
      response.bodyReceived && response.body !== null &&
      response.getField('Content-Encoding') === 'deflate') {
      // inflate using flash api
      response.body = forge.util.inflate(
        response.flashApi, response.body);
    }

    return response.bodyReceived;
  };

   /**
    * Parses an array of cookies from the 'Set-Cookie' field, if present.
    *
    * @return the array of cookies.
    */
   response.getCookies = function() {
     var rval = [];

     // get Set-Cookie field
     if('Set-Cookie' in response.fields) {
       var field = response.fields['Set-Cookie'];

       // get current local time in seconds
       var now = +new Date() / 1000;

       // regex for parsing 'name1=value1; name2=value2; name3'
       var regex = /\s*([^=]*)=?([^;]*)(;|$)/g;

       // examples:
       // Set-Cookie: cookie1_name=cookie1_value; max-age=0; path=/
       // Set-Cookie: c2=v2; expires=Thu, 21-Aug-2008 23:47:25 GMT; path=/
       for(var i = 0; i < field.length; ++i) {
         var fv = field[i];
         var m;
         regex.lastIndex = 0;
         var first = true;
         var cookie = {};
         do {
           m = regex.exec(fv);
           if(m !== null) {
             var name = _trimString(m[1]);
             var value = _trimString(m[2]);

             // cookie_name=value
             if(first) {
               cookie.name = name;
               cookie.value = value;
               first = false;
             } else {
               // property_name=value
               name = name.toLowerCase();
               switch(name) {
               case 'expires':
                 // replace hyphens w/spaces so date will parse
                 value = value.replace(/-/g, ' ');
                 var secs = Date.parse(value) / 1000;
                 cookie.maxAge = Math.max(0, secs - now);
                 break;
               case 'max-age':
                 cookie.maxAge = parseInt(value, 10);
                 break;
               case 'secure':
                 cookie.secure = true;
                 break;
               case 'httponly':
                 cookie.httpOnly = true;
                 break;
               default:
                 if(name !== '') {
                   cookie[name] = value;
                 }
               }
             }
           }
         } while(m !== null && m[0] !== '');
         rval.push(cookie);
       }
     }

     return rval;
  };

  /**
   * Converts an http response into a string that can be sent as an
   * HTTP response. Does not include any data.
   *
   * @return the string representation of the response.
   */
  response.toString = function() {
    /* Sample response header:
      HTTP/1.0 200 OK
      Host: www.someurl.com
      Connection: close
     */

    // build start line
    var rval =
      response.version + ' ' + response.code + ' ' + response.message + '\r\n';

    // add each header
    for(var name in response.fields) {
      var fields = response.fields[name];
      for(var i = 0; i < fields.length; ++i) {
        rval += name + ': ' + fields[i] + '\r\n';
      }
    }
    // final terminating CRLF
    rval += '\r\n';

    return rval;
  };

  return response;
};

/**
 * Returns true if the given url is within the given cookie's domain.
 *
 * @param url the url to check.
 * @param cookie the cookie or cookie domain to check.
 */
http.withinCookieDomain = function(url, cookie) {
  var rval = false;

  // cookie may be null, a cookie object, or a domain string
  var domain = (cookie === null || typeof cookie === 'string') ?
    cookie : cookie.domain;

  // any domain will do
  if(domain === null) {
    rval = true;
  } else if(domain.charAt(0) === '.') {
    // ensure domain starts with a '.'
    // parse URL as necessary
    if(typeof url === 'string') {
      url = new URL(url);
    }

    // add '.' to front of URL hostname to match against domain
    var host = '.' + url.hostname;

    // if the host ends with domain then it falls within it
    var idx = host.lastIndexOf(domain);
    if(idx !== -1 && (idx + domain.length === host.length)) {
      rval = true;
    }
  }

  return rval;
};


code:
/**
 * Node.js module for Forge with extra utils and networking.
 *
 * @author Dave Longley
 *
 * Copyright 2011-2016 Digital Bazaar, Inc.
 */
module.exports = require('./forge');
// require core forge
require('./index');
// additional utils and networking support
require('./form');
require('./socket');
require('./tlssocket');
require('./http');
require('./xhr');

license: mit


code:
/**
 * Node.js module for Forge.
 *
 * @author Dave Longley
 *
 * Copyright 2011-2016 Digital Bazaar, Inc.
 */
module.exports = require('./forge');
require('./aes');
require('./aesCipherSuites');
require('./asn1');
require('./cipher');
require('./des');
require('./ed25519');
require('./hmac');
require('./kem');
require('./log');
require('./md.all');
require('./mgf1');
require('./pbkdf2');
require('./pem');
require('./pkcs1');
require('./pkcs12');
require('./pkcs7');
require('./pki');
require('./prime');
require('./prng');
require('./pss');
require('./random');
require('./rc2');
require('./ssh');
require('./tls');
require('./util');


code:
// Copyright (c) 2005  Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.

// Basic JavaScript BN library - subset useful for RSA encryption.

/*
Licensing (LICENSE)
-------------------

This software is covered under the following copyright:
*/
/*
 * Copyright (c) 2003-2005  Tom Wu
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
 * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * In addition, the following condition applies:
 *
 * All redistributions must retain an intact copy of this copyright notice
 * and disclaimer.
 */
/*
Address all questions regarding this license to:

  Tom Wu
  tjw@cs.Stanford.EDU
*/
var forge = require('./forge');

module.exports = forge.jsbn = forge.jsbn || {};

// Bits per digit
var dbits;

// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);

// (public) Constructor
function BigInteger(a,b,c) {
  this.data = [];
  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);
}
forge.jsbn.BigInteger = BigInteger;

// return new, unset BigInteger
function nbi() { return new BigInteger(null); }

// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.

// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
  while(--n >= 0) {
    var v = x*this.data[i++]+w.data[j]+c;
    c = Math.floor(v/0x4000000);
    w.data[j++] = v&0x3ffffff;
  }
  return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
  var xl = x&0x7fff, xh = x>>15;
  while(--n >= 0) {
    var l = this.data[i]&0x7fff;
    var h = this.data[i++]>>15;
    var m = xh*l+h*xl;
    l = xl*l+((m&0x7fff)<<15)+w.data[j]+(c&0x3fffffff);
    c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
    w.data[j++] = l&0x3fffffff;
  }
  return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
  var xl = x&0x3fff, xh = x>>14;
  while(--n >= 0) {
    var l = this.data[i]&0x3fff;
    var h = this.data[i++]>>14;
    var m = xh*l+h*xl;
    l = xl*l+((m&0x3fff)<<14)+w.data[j]+c;
    c = (l>>28)+(m>>14)+xh*h;
    w.data[j++] = l&0xfffffff;
  }
  return c;
}

// node.js (no browser)
if(typeof(navigator) === 'undefined')
{
   BigInteger.prototype.am = am3;
   dbits = 28;
} else if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
  BigInteger.prototype.am = am2;
  dbits = 30;
} else if(j_lm && (navigator.appName != "Netscape")) {
  BigInteger.prototype.am = am1;
  dbits = 26;
} else { // Mozilla/Netscape seems to prefer am3
  BigInteger.prototype.am = am3;
  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;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;

function int2char(n) { return BI_RM.charAt(n); }
function intAt(s,i) {
  var c = BI_RC[s.charCodeAt(i)];
  return (c==null)?-1:c;
}

// (protected) copy this to r
function bnpCopyTo(r) {
  for(var i = this.t-1; i >= 0; --i) r.data[i] = this.data[i];
  r.t = this.t;
  r.s = this.s;
}

// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
  this.t = 1;
  this.s = (x<0)?-1:0;
  if(x > 0) this.data[0] = x;
  else if(x < -1) this.data[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
  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.data[this.t++] = x;
    else if(sh+k > this.DB) {
      this.data[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
      this.data[this.t++] = (x>>(this.DB-sh));
    } else
      this.data[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.data[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.data[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);
  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.data[i]>>p) > 0) { m = true; r = int2char(d); }
    while(i >= 0) {
      if(p < k) {
        d = (this.data[i]&((1<<p)-1))<<(k-p);
        d |= this.data[--i]>>(p+=this.DB-k);
      } else {
        d = (this.data[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";
}

// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }

// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }

// (public) return + if this > a, - if this < a, 0 if equal
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.data[i]-a.data[i]) != 0) return r;
  return 0;
}

// returns bit length of the integer x
function nbits(x) {
  var r = 1, t;
  if((t=x>>>16) != 0) { x = t; r += 16; }
  if((t=x>>8) != 0) { x = t; r += 8; }
  if((t=x>>4) != 0) { x = t; r += 4; }
  if((t=x>>2) != 0) { x = t; r += 2; }
  if((t=x>>1) != 0) { x = t; r += 1; }
  return r;
}

// (public) return the number of bits in "this"
function bnBitLength() {
  if(this.t <= 0) return 0;
  return this.DB*(this.t-1)+nbits(this.data[this.t-1]^(this.s&this.DM));
}

// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
  var i;
  for(i = this.t-1; i >= 0; --i) r.data[i+n] = this.data[i];
  for(i = n-1; i >= 0; --i) r.data[i] = 0;
  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.data[i-n] = this.data[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;
  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.data[i+ds+1] = (this.data[i]>>cbs)|c;
    c = (this.data[i]&bm)<<bs;
  }
  for(i = ds-1; i >= 0; --i) r.data[i] = 0;
  r.data[ds] = c;
  r.t = this.t+ds+1;
  r.s = this.s;
  r.clamp();
}

// (protected) r = this >> n
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.data[0] = this.data[ds]>>bs;
  for(var i = ds+1; i < this.t; ++i) {
    r.data[i-ds-1] |= (this.data[i]&bm)<<cbs;
    r.data[i-ds] = this.data[i]>>bs;
  }
  if(bs > 0) r.data[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.data[i]-a.data[i];
    r.data[i++] = c&this.DM;
    c >>= this.DB;
  }
  if(a.t < this.t) {
    c -= a.s;
    while(i < this.t) {
      c += this.data[i];
      r.data[i++] = c&this.DM;
      c >>= this.DB;
    }
    c += this.s;
  } else {
    c += this.s;
    while(i < a.t) {
      c -= a.data[i];
      r.data[i++] = c&this.DM;
      c >>= this.DB;
    }
    c -= a.s;
  }
  r.s = (c<0)?-1:0;
  if(c < -1) r.data[i++] = this.DV+c;
  else if(c > 0) r.data[i++] = c;
  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.data[i] = 0;
  for(i = 0; i < y.t; ++i) r.data[i+x.t] = x.am(0,y.data[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) {
  var x = this.abs();
  var i = r.t = 2*x.t;
  while(--i >= 0) r.data[i] = 0;
  for(i = 0; i < x.t-1; ++i) {
    var c = x.am(i,x.data[i],r,2*i,0,1);
    if((r.data[i+x.t]+=x.am(i+1,2*x.data[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
      r.data[i+x.t] -= x.DV;
      r.data[i+x.t+1] = 1;
    }
  }
  if(r.t > 0) r.data[r.t-1] += x.am(i,x.data[i],r,2*i,0,1);
  r.s = 0;
  r.clamp();
}

// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// 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.data[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.data[ys-1];
  if(y0 == 0) return;
  var yt = y0*(1<<this.F1)+((ys>1)?y.data[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.data[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.data[y.t++] = 0;
  while(--j >= 0) {
    // Estimate quotient digit
    var qd = (r.data[--i]==y0)?this.DM:Math.floor(r.data[i]*d1+(r.data[i-1]+e)*d2);
    if((r.data[i]+=y.am(0,qd,r,j,0,ys)) < qd) {	// Try it out
      y.dlShiftTo(j,t);
      r.subTo(t,r);
      while(r.data[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);
}

// (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) {
  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;

// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
//         xy == 1 (mod m)
//         xy =  1+km
//   xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
  if(this.t < 1) return 0;
  var x = this.data[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;
}

// 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 montConvert(x) {
  var r = nbi();
  x.abs().dlShiftTo(this.m.t,r);
  r.divRemTo(this.m,null,r);
  if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
  return r;
}

// x/R mod m
function montRevert(x) {
  var r = nbi();
  x.copyTo(r);
  this.reduce(r);
  return r;
}

// x = x/R mod m (HAC 14.32)
function montReduce(x) {
  while(x.t <= this.mt2)	// pad x so am has enough room later
    x.data[x.t++] = 0;
  for(var i = 0; i < this.m.t; ++i) {
    // faster way of calculating u0 = x.data[i]*mp mod DV
    var j = x.data[i]&0x7fff;
    var u0 = (j*this.mpl+(((j*this.mph+(x.data[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
    // use am to combine the multiply-shift-add into one call
    j = i+this.m.t;
    x.data[j] += this.m.am(0,u0,x,i,0,this.m.t);
    // propagate carry
    while(x.data[j] >= x.DV) { x.data[j] -= x.DV; x.data[++j]++; }
  }
  x.clamp();
  x.drShiftTo(this.m.t,x);
  if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}

// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }

// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }

Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;

// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this.data[0]&1):this.s) == 0; }

// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
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);
}

// (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;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.multiplyTo = bnpMultiplyTo;
BigInteger.prototype.squareTo = bnpSquareTo;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.exp = bnpExp;

// public
BigInteger.prototype.toString = bnToString;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.bitLength = bnBitLength;
BigInteger.prototype.mod = bnMod;
BigInteger.prototype.modPowInt = bnModPowInt;

// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);

// jsbn2 lib

//Copyright (c) 2005-2009  Tom Wu
//All Rights Reserved.
//See "LICENSE" for details (See jsbn.js for LICENSE).

//Extended JavaScript BN functions, required for RSA private ops.

//Version 1.1: new BigInteger("0", 10) returns "proper" zero

//(public)
function bnClone() { var r = nbi(); this.copyTo(r); return r; }

//(public) return value as integer
function bnIntValue() {
if(this.s < 0) {
 if(this.t == 1) return this.data[0]-this.DV;
 else if(this.t == 0) return -1;
} else if(this.t == 1) return this.data[0];
else if(this.t == 0) return 0;
// assumes 16 < DB < 32
return ((this.data[1]&((1<<(32-this.DB))-1))<<this.DB)|this.data[0];
}

//(public) return value as byte
function bnByteValue() { return (this.t==0)?this.s:(this.data[0]<<24)>>24; }

//(public) return value as short (assumes DB>=16)
function bnShortValue() { return (this.t==0)?this.s:(this.data[0]<<16)>>16; }

//(protected) return x s.t. r^x < DV
function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }

//(public) 0 if this == 0, 1 if this > 0
function bnSigNum() {
if(this.s < 0) return -1;
else if(this.t <= 0 || (this.t == 1 && this.data[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);
}
return z.intValue().toString(b) + r;
}

//(protected) convert from radix string
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);
}

//(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);
   }
 }
} 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);
}
}

//(public) convert to bigendian byte array
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.data[i]>>p) != (this.s&this.DM)>>p)
   r[k++] = d|(this.s<<(this.DB-p));
 while(i >= 0) {
   if(p < 8) {
     d = (this.data[i]&((1<<p)-1))<<(8-p);
     d |= this.data[--i]>>(p+=this.DB-8);
   } else {
     d = (this.data[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;
}

function bnEquals(a) { return(this.compareTo(a)==0); }
function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
function bnMax(a) { return(this.compareTo(a)>0)?this:a; }

//(protected) r = this op a (bitwise)
function bnpBitwiseTo(a,op,r) {
var i, f, m = Math.min(a.t,this.t);
for(i = 0; i < m; ++i) r.data[i] = op(this.data[i],a.data[i]);
if(a.t < this.t) {
 f = a.s&this.DM;
 for(i = m; i < this.t; ++i) r.data[i] = op(this.data[i],f);
 r.t = this.t;
} else {
 f = this.s&this.DM;
 for(i = m; i < a.t; ++i) r.data[i] = op(f,a.data[i]);
 r.t = a.t;
}
r.s = op(this.s,a.s);
r.clamp();
}

//(public) this & a
function op_and(x,y) { return x&y; }
function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }

//(public) this | a
function op_or(x,y) { return x|y; }
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }

//(public) this ^ a
function op_xor(x,y) { return x^y; }
function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }

//(public) this & ~a
function op_andnot(x,y) { return x&~y; }
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }

//(public) ~this
function bnNot() {
var r = nbi();
for(var i = 0; i < this.t; ++i) r.data[i] = this.DM&~this.data[i];
r.t = this.t;
r.s = ~this.s;
return r;
}

//(public) this << n
function bnShiftLeft(n) {
var r = nbi();
if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
return r;
}

//(public) this >> n
function bnShiftRight(n) {
var r = nbi();
if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
return r;
}

//return index of lowest 1-bit in x, x < 2^31
function lbit(x) {
if(x == 0) return -1;
var r = 0;
if((x&0xffff) == 0) { x >>= 16; r += 16; }
if((x&0xff) == 0) { x >>= 8; r += 8; }
if((x&0xf) == 0) { x >>= 4; r += 4; }
if((x&3) == 0) { x >>= 2; r += 2; }
if((x&1) == 0) ++r;
return r;
}

//(public) returns index of lowest 1-bit (or -1 if none)
function bnGetLowestSetBit() {
for(var i = 0; i < this.t; ++i)
 if(this.data[i] != 0) return i*this.DB+lbit(this.data[i]);
if(this.s < 0) return this.t*this.DB;
return -1;
}

//return number of 1 bits in x
function cbit(x) {
var r = 0;
while(x != 0) { x &= x-1; ++r; }
return r;
}

//(public) return number of set bits
function bnBitCount() {
var r = 0, x = this.s&this.DM;
for(var i = 0; i < this.t; ++i) r += cbit(this.data[i]^x);
return r;
}

//(public) true iff nth bit is set
function bnTestBit(n) {
var j = Math.floor(n/this.DB);
if(j >= this.t) return(this.s!=0);
return((this.data[j]&(1<<(n%this.DB)))!=0);
}

//(protected) this op (1<<n)
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); }

//(protected) r = this + a
function bnpAddTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
 c += this.data[i]+a.data[i];
 r.data[i++] = c&this.DM;
 c >>= this.DB;
}
if(a.t < this.t) {
 c += a.s;
 while(i < this.t) {
   c += this.data[i];
   r.data[i++] = c&this.DM;
   c >>= this.DB;
 }
 c += this.s;
} else {
 c += this.s;
 while(i < a.t) {
   c += a.data[i];
   r.data[i++] = c&this.DM;
   c >>= this.DB;
 }
 c += a.s;
}
r.s = (c<0)?-1:0;
if(c > 0) r.data[i++] = c;
else if(c < -1) r.data[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; }

//(public) this / a
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }

//(public) this % a
function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }

//(public) [this/a,this%a]
function bnDivideAndRemainder(a) {
var q = nbi(), r = nbi();
this.divRemTo(a,q,r);
return new Array(q,r);
}

//(protected) this *= n, this >= 0, 1 < n < DV
function bnpDMultiply(n) {
this.data[this.t] = this.am(0,n-1,this,0,0,this.t);
++this.t;
this.clamp();
}

//(protected) this += n << w words, this >= 0
function bnpDAddOffset(n,w) {
if(n == 0) return;
while(this.t <= w) this.data[this.t++] = 0;
this.data[w] += n;
while(this.data[w] >= this.DV) {
 this.data[w] -= this.DV;
 if(++w >= this.t) this.data[this.t++] = 0;
 ++this.data[w];
}
}

//A "null" reducer
function NullExp() {}
function nNop(x) { return x; }
function nMulTo(x,y,r) { x.multiplyTo(y,r); }
function nSqrTo(x,r) { x.squareTo(r); }

NullExp.prototype.convert = nNop;
NullExp.prototype.revert = nNop;
NullExp.prototype.mulTo = nMulTo;
NullExp.prototype.sqrTo = nSqrTo;

//(public) this^e
function bnPow(e) { return this.exp(e,new NullExp()); }

//(protected) r = lower n words of "this * a", a.t <= n
//"this" should be the larger one if appropriate.
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.data[--i] = 0;
var j;
for(j = r.t-this.t; i < j; ++i) r.data[i+this.t] = this.am(0,a.data[i],r,i,0,this.t);
for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a.data[i],r,i,0,n-i);
r.clamp();
}

//(protected) r = "this * a" without lower n words, n > 0
//"this" should be the larger one if appropriate.
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.data[i] = 0;
for(i = Math.max(n-this.t,0); i < a.t; ++i)
 r.data[this.t+i-n] = this.am(n-i,a.data[i],r,0,0,this.t+i-n);
r.clamp();
r.drShiftTo(1,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 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); }

//r = x*y mod m; x,y != r
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }

Barrett.prototype.convert = barrettConvert;
Barrett.prototype.revert = barrettRevert;
Barrett.prototype.reduce = barrettReduce;
Barrett.prototype.mulTo = barrettMulTo;
Barrett.prototype.sqrTo = barrettSqrTo;

//(public) this^e % m (HAC 14.85)
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.data[j])-1;
while(j >= 0) {
 if(i >= k1) w = (e.data[j]>>(i-k1))&km;
 else {
   w = (e.data[j]&((1<<(i+1))-1))<<(k1-i);
   if(j > 0) w |= e.data[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.data[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);
}

//(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) {
 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;
}

//(protected) this % n, n < 2^26
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.data[0]%n;
 else for(var i = this.t-1; i >= 0; --i) r = (d*r+this.data[i])%n;
return r;
}

//(public) 1/this % m (HAC 14.61)
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;
}

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];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];

//(public) test primality with certainty >= 1-.5^t
function bnIsProbablePrime(t) {
var i, x = this.abs();
if(x.t == 1 && x.data[0] <= lowprimes[lowprimes.length-1]) {
 for(i = 0; i < lowprimes.length; ++i)
   if(x.data[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);
}

//(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);
var prng = bnGetPrng();
var a;
for(var i = 0; i < t; ++i) {
 // select witness 'a' at random from between 1 and n1
 do {
   a = new BigInteger(this.bitLength(), prng);
 }
 while(a.compareTo(BigInteger.ONE) <= 0 || a.compareTo(n1) >= 0);
 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;
}

// get pseudo random number generator
function bnGetPrng() {
  // create prng with api that matches BigInteger secure random
  return {
    // x is an array to fill with bytes
    nextBytes: function(x) {
      for(var i = 0; i < x.length; ++i) {
        x[i] = Math.floor(Math.random() * 0x0100);
      }
    }
  };
}

//protected
BigInteger.prototype.chunkSize = bnpChunkSize;
BigInteger.prototype.toRadix = bnpToRadix;
BigInteger.prototype.fromRadix = bnpFromRadix;
BigInteger.prototype.fromNumber = bnpFromNumber;
BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
BigInteger.prototype.changeBit = bnpChangeBit;
BigInteger.prototype.addTo = bnpAddTo;
BigInteger.prototype.dMultiply = bnpDMultiply;
BigInteger.prototype.dAddOffset = bnpDAddOffset;
BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
BigInteger.prototype.modInt = bnpModInt;
BigInteger.prototype.millerRabin = bnpMillerRabin;

//public
BigInteger.prototype.clone = bnClone;
BigInteger.prototype.intValue = bnIntValue;
BigInteger.prototype.byteValue = bnByteValue;
BigInteger.prototype.shortValue = bnShortValue;
BigInteger.prototype.signum = bnSigNum;
BigInteger.prototype.toByteArray = bnToByteArray;
BigInteger.prototype.equals = bnEquals;
BigInteger.prototype.min = bnMin;
BigInteger.prototype.max = bnMax;
BigInteger.prototype.and = bnAnd;
BigInteger.prototype.or = bnOr;
BigInteger.prototype.xor = bnXor;
BigInteger.prototype.andNot = bnAndNot;
BigInteger.prototype.not = bnNot;
BigInteger.prototype.shiftLeft = bnShiftLeft;
BigInteger.prototype.shiftRight = bnShiftRight;
BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
BigInteger.prototype.bitCount = bnBitCount;
BigInteger.prototype.testBit = bnTestBit;
BigInteger.prototype.setBit = bnSetBit;
BigInteger.prototype.clearBit = bnClearBit;
BigInteger.prototype.flipBit = bnFlipBit;
BigInteger.prototype.add = bnAdd;
BigInteger.prototype.subtract = bnSubtract;
BigInteger.prototype.multiply = bnMultiply;
BigInteger.prototype.divide = bnDivide;
BigInteger.prototype.remainder = bnRemainder;
BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
BigInteger.prototype.modPow = bnModPow;
BigInteger.prototype.modInverse = bnModInverse;
BigInteger.prototype.pow = bnPow;
BigInteger.prototype.gcd = bnGCD;
BigInteger.prototype.isProbablePrime = bnIsProbablePrime;

//BigInteger interfaces not implemented in jsbn:

//BigInteger(int signum, byte[] magnitude)
//double doubleValue()
//float floatValue()
//int hashCode()
//long longValue()
//static BigInteger valueOf(long val)


code:
/**
 * Javascript implementation of RSA-KEM.
 *
 * @author Lautaro Cozzani Rodriguez
 * @author Dave Longley
 *
 * Copyright (c) 2014 Lautaro Cozzani <lautaro.cozzani@scytl.com>
 * Copyright (c) 2014 Digital Bazaar, Inc.
 */
var forge = require('./forge');
require('./util');
require('./random');
require('./jsbn');

module.exports = forge.kem = forge.kem || {};

var BigInteger = forge.jsbn.BigInteger;

/**
 * The API for the RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2.
 */
forge.kem.rsa = {};

/**
 * Creates an RSA KEM API object for generating a secret asymmetric key.
 *
 * The symmetric key may be generated via a call to 'encrypt', which will
 * produce a ciphertext to be transmitted to the recipient and a key to be
 * kept secret. The ciphertext is a parameter to be passed to 'decrypt' which
 * will produce the same secret key for the recipient to use to decrypt a
 * message that was encrypted with the secret key.
 *
 * @param kdf the KDF API to use (eg: new forge.kem.kdf1()).
 * @param options the options to use.
 *          [prng] a custom crypto-secure pseudo-random number generator to use,
 *            that must define "getBytesSync".
 */
forge.kem.rsa.create = function(kdf, options) {
  options = options || {};
  var prng = options.prng || forge.random;

  var kem = {};

  /**
   * Generates a secret key and its encapsulation.
   *
   * @param publicKey the RSA public key to encrypt with.
   * @param keyLength the length, in bytes, of the secret key to generate.
   *
   * @return an object with:
   *   encapsulation: the ciphertext for generating the secret key, as a
   *     binary-encoded string of bytes.
   *   key: the secret key to use for encrypting a message.
   */
  kem.encrypt = function(publicKey, keyLength) {
    // generate a random r where 1 < r < n
    var byteLength = Math.ceil(publicKey.n.bitLength() / 8);
    var r;
    do {
      r = new BigInteger(
        forge.util.bytesToHex(prng.getBytesSync(byteLength)),
        16).mod(publicKey.n);
    } while(r.compareTo(BigInteger.ONE) <= 0);

    // prepend r with zeros
    r = forge.util.hexToBytes(r.toString(16));
    var zeros = byteLength - r.length;
    if(zeros > 0) {
      r = forge.util.fillString(String.fromCharCode(0), zeros) + r;
    }

    // encrypt the random
    var encapsulation = publicKey.encrypt(r, 'NONE');

    // generate the secret key
    var key = kdf.generate(r, keyLength);

    return {encapsulation: encapsulation, key: key};
  };

  /**
   * Decrypts an encapsulated secret key.
   *
   * @param privateKey the RSA private key to decrypt with.
   * @param encapsulation the ciphertext for generating the secret key, as
   *          a binary-encoded string of bytes.
   * @param keyLength the length, in bytes, of the secret key to generate.
   *
   * @return the secret key as a binary-encoded string of bytes.
   */
  kem.decrypt = function(privateKey, encapsulation, keyLength) {
    // decrypt the encapsulation and generate the secret key
    var r = privateKey.decrypt(encapsulation, 'NONE');
    return kdf.generate(r, keyLength);
  };

  return kem;
};

// TODO: add forge.kem.kdf.create('KDF1', {md: ..., ...}) API?

/**
 * Creates a key derivation API object that implements KDF1 per ISO 18033-2.
 *
 * @param md the hash API to use.
 * @param [digestLength] an optional digest length that must be positive and
 *          less than or equal to md.digestLength.
 *
 * @return a KDF1 API object.
 */
forge.kem.kdf1 = function(md, digestLength) {
  _createKDF(this, md, 0, digestLength || md.digestLength);
};

/**
 * Creates a key derivation API object that implements KDF2 per ISO 18033-2.
 *
 * @param md the hash API to use.
 * @param [digestLength] an optional digest length that must be positive and
 *          less than or equal to md.digestLength.
 *
 * @return a KDF2 API object.
 */
forge.kem.kdf2 = function(md, digestLength) {
  _createKDF(this, md, 1, digestLength || md.digestLength);
};

/**
 * Creates a KDF1 or KDF2 API object.
 *
 * @param md the hash API to use.
 * @param counterStart the starting index for the counter.
 * @param digestLength the digest length to use.
 *
 * @return the KDF API object.
 */
function _createKDF(kdf, md, counterStart, digestLength) {
  /**
   * Generate a key of the specified length.
   *
   * @param x the binary-encoded byte string to generate a key from.
   * @param length the number of bytes to generate (the size of the key).
   *
   * @return the key as a binary-encoded string.
   */
  kdf.generate = function(x, length) {
    var key = new forge.util.ByteBuffer();

    // run counter from counterStart to ceil(length / Hash.len)
    var k = Math.ceil(length / digestLength) + counterStart;

    var c = new forge.util.ByteBuffer();
    for(var i = counterStart; i < k; ++i) {
      // I2OSP(i, 4): convert counter to an octet string of 4 octets
      c.putInt32(i);

      // digest 'x' and the counter and add the result to the key
      md.start();
      md.update(x + c.getBytes());
      var hash = md.digest();
      key.putBytes(hash.getBytes(digestLength));
    }

    // truncate to the correct key length
    key.truncate(key.length() - length);
    return key.getBytes();
  };
}


code:
/**
 * Cross-browser support for logging in a web application.
 *
 * @author David I. Lehn <dlehn@digitalbazaar.com>
 *
 * Copyright (c) 2008-2013 Digital Bazaar, Inc.
 */
var forge = require('./forge');
require('./util');

/* LOG API */
module.exports = forge.log = forge.log || {};

/**
 * Application logging system.
 *
 * Each logger level available as it's own function of the form:
 *   forge.log.level(category, args...)
 * The category is an arbitrary string, and the args are the same as
 * Firebug's console.log API. By default the call will be output as:
 *   'LEVEL [category] <args[0]>, args[1], ...'
 * This enables proper % formatting via the first argument.
 * Each category is enabled by default but can be enabled or disabled with
 * the setCategoryEnabled() function.
 */
// list of known levels
forge.log.levels = [
  'none', 'error', 'warning', 'info', 'debug', 'verbose', 'max'];
// info on the levels indexed by name:
//   index: level index
//   name: uppercased display name
var sLevelInfo = {};
// list of loggers
var sLoggers = [];
/**
 * Standard console logger. If no console support is enabled this will
 * remain null. Check before using.
 */
var sConsoleLogger = null;

// logger flags
/**
 * Lock the level at the current value. Used in cases where user config may
 * set the level such that only critical messages are seen but more verbose
 * messages are needed for debugging or other purposes.
 */
forge.log.LEVEL_LOCKED = (1 << 1);
/**
 * Always call log function. By default, the logging system will check the
 * message level against logger.level before calling the log function. This
 * flag allows the function to do its own check.
 */
forge.log.NO_LEVEL_CHECK = (1 << 2);
/**
 * Perform message interpolation with the passed arguments. "%" style
 * fields in log messages will be replaced by arguments as needed. Some
 * loggers, such as Firebug, may do this automatically. The original log
 * message will be available as 'message' and the interpolated version will
 * be available as 'fullMessage'.
 */
forge.log.INTERPOLATE = (1 << 3);

// setup each log level
for(var i = 0; i < forge.log.levels.length; ++i) {
  var level = forge.log.levels[i];
  sLevelInfo[level] = {
    index: i,
    name: level.toUpperCase()
  };
}

/**
 * Message logger. Will dispatch a message to registered loggers as needed.
 *
 * @param message message object
 */
forge.log.logMessage = function(message) {
  var messageLevelIndex = sLevelInfo[message.level].index;
  for(var i = 0; i < sLoggers.length; ++i) {
    var logger = sLoggers[i];
    if(logger.flags & forge.log.NO_LEVEL_CHECK) {
      logger.f(message);
    } else {
      // get logger level
      var loggerLevelIndex = sLevelInfo[logger.level].index;
      // check level
      if(messageLevelIndex <= loggerLevelIndex) {
        // message critical enough, call logger
        logger.f(logger, message);
      }
    }
  }
};

/**
 * Sets the 'standard' key on a message object to:
 * "LEVEL [category] " + message
 *
 * @param message a message log object
 */
forge.log.prepareStandard = function(message) {
  if(!('standard' in message)) {
    message.standard =
      sLevelInfo[message.level].name +
      //' ' + +message.timestamp +
      ' [' + message.category + '] ' +
      message.message;
  }
};

/**
 * Sets the 'full' key on a message object to the original message
 * interpolated via % formatting with the message arguments.
 *
 * @param message a message log object.
 */
forge.log.prepareFull = function(message) {
  if(!('full' in message)) {
    // copy args and insert message at the front
    var args = [message.message];
    args = args.concat([] || message['arguments']);
    // format the message
    message.full = forge.util.format.apply(this, args);
  }
};

/**
 * Applies both preparseStandard() and prepareFull() to a message object and
 * store result in 'standardFull'.
 *
 * @param message a message log object.
 */
forge.log.prepareStandardFull = function(message) {
  if(!('standardFull' in message)) {
    // FIXME implement 'standardFull' logging
    forge.log.prepareStandard(message);
    message.standardFull = message.standard;
  }
};

// create log level functions
if(true) {
  // levels for which we want functions
  var levels = ['error', 'warning', 'info', 'debug', 'verbose'];
  for(var i = 0; i < levels.length; ++i) {
    // wrap in a function to ensure proper level var is passed
    (function(level) {
      // create function for this level
      forge.log[level] = function(category, message/*, args...*/) {
        // convert arguments to real array, remove category and message
        var args = Array.prototype.slice.call(arguments).slice(2);
        // create message object
        // Note: interpolation and standard formatting is done lazily
        var msg = {
          timestamp: new Date(),
          level: level,
          category: category,
          message: message,
          'arguments': args
          /*standard*/
          /*full*/
          /*fullMessage*/
        };
        // process this message
        forge.log.logMessage(msg);
      };
    })(levels[i]);
  }
}

/**
 * Creates a new logger with specified custom logging function.
 *
 * The logging function has a signature of:
 *   function(logger, message)
 * logger: current logger
 * message: object:
 *   level: level id
 *   category: category
 *   message: string message
 *   arguments: Array of extra arguments
 *   fullMessage: interpolated message and arguments if INTERPOLATE flag set
 *
 * @param logFunction a logging function which takes a log message object
 *          as a parameter.
 *
 * @return a logger object.
 */
forge.log.makeLogger = function(logFunction) {
  var logger = {
    flags: 0,
    f: logFunction
  };
  forge.log.setLevel(logger, 'none');
  return logger;
};

/**
 * Sets the current log level on a logger.
 *
 * @param logger the target logger.
 * @param level the new maximum log level as a string.
 *
 * @return true if set, false if not.
 */
forge.log.setLevel = function(logger, level) {
  var rval = false;
  if(logger && !(logger.flags & forge.log.LEVEL_LOCKED)) {
    for(var i = 0; i < forge.log.levels.length; ++i) {
      var aValidLevel = forge.log.levels[i];
      if(level == aValidLevel) {
        // set level
        logger.level = level;
        rval = true;
        break;
      }
    }
  }

  return rval;
};

/**
 * Locks the log level at its current value.
 *
 * @param logger the target logger.
 * @param lock boolean lock value, default to true.
 */
forge.log.lock = function(logger, lock) {
  if(typeof lock === 'undefined' || lock) {
    logger.flags |= forge.log.LEVEL_LOCKED;
  } else {
    logger.flags &= ~forge.log.LEVEL_LOCKED;
  }
};

/**
 * Adds a logger.
 *
 * @param logger the logger object.
 */
forge.log.addLogger = function(logger) {
  sLoggers.push(logger);
};

// setup the console logger if possible, else create fake console.log
if(typeof(console) !== 'undefined' && 'log' in console) {
  var logger;
  if(console.error && console.warn && console.info && console.debug) {
    // looks like Firebug-style logging is available
    // level handlers map
    var levelHandlers = {
      error: console.error,
      warning: console.warn,
      info: console.info,
      debug: console.debug,
      verbose: console.debug
    };
    var f = function(logger, message) {
      forge.log.prepareStandard(message);
      var handler = levelHandlers[message.level];
      // prepend standard message and concat args
      var args = [message.standard];
      args = args.concat(message['arguments'].slice());
      // apply to low-level console function
      handler.apply(console, args);
    };
    logger = forge.log.makeLogger(f);
  } else {
    // only appear to have basic console.log
    var f = function(logger, message) {
      forge.log.prepareStandardFull(message);
      console.log(message.standardFull);
    };
    logger = forge.log.makeLogger(f);
  }
  forge.log.setLevel(logger, 'debug');
  forge.log.addLogger(logger);
  sConsoleLogger = logger;
} else {
  // define fake console.log to avoid potential script errors on
  // browsers that do not have console logging
  console = {
    log: function() {}
  };
}

/*
 * Check for logging control query vars in current URL.
 *
 * console.level=<level-name>
 * Set's the console log level by name.  Useful to override defaults and
 * allow more verbose logging before a user config is loaded.
 *
 * console.lock=<true|false>
 * Lock the console log level at whatever level it is set at.  This is run
 * after console.level is processed.  Useful to force a level of verbosity
 * that could otherwise be limited by a user config.
 */
if(sConsoleLogger !== null &&
  typeof window !== 'undefined' && window.location
) {
  var query = new URL(window.location.href).searchParams;
  if(query.has('console.level')) {
    // set with last value
    forge.log.setLevel(
      sConsoleLogger, query.get('console.level').slice(-1)[0]);
  }
  if(query.has('console.lock')) {
    // set with last value
    var lock = query.get('console.lock').slice(-1)[0];
    if(lock == 'true') {
      forge.log.lock(sConsoleLogger);
    }
  }
}

// provide public access to console logger
forge.log.consoleLogger = sConsoleLogger;


code:
/**
 * Node.js module for all known Forge message digests.
 *
 * @author Dave Longley
 *
 * Copyright 2011-2017 Digital Bazaar, Inc.
 */
module.exports = require('./md');

require('./md5');
require('./sha1');
require('./sha256');
require('./sha512');


code:
/**
 * Node.js module for Forge message digests.
 *
 * @author Dave Longley
 *
 * Copyright 2011-2017 Digital Bazaar, Inc.
 */
var forge = require('./forge');

module.exports = forge.md = forge.md || {};
forge.md.algorithms = forge.md.algorithms || {};


code:
/**
 * Message Digest Algorithm 5 with 128-bit digest (MD5) implementation.
 *
 * @author Dave Longley
 *
 * Copyright (c) 2010-2014 Digital Bazaar, Inc.
 */
var forge = require('./forge');
require('./md');
require('./util');

var md5 = module.exports = forge.md5 = forge.md5 || {};
forge.md.md5 = forge.md.algorithms.md5 = md5;

/**
 * Creates an MD5 message digest object.
 *
 * @return a message digest object.
 */
md5.create = function() {
  // do initialization as necessary
  if(!_initialized) {
    _init();
  }

  // MD5 state contains four 32-bit integers
  var _state = null;

  // input buffer
  var _input = forge.util.createBuffer();

  // used for word storage
  var _w = new Array(16);

  // message digest object
  var md = {
    algorithm: 'md5',
    blockLength: 64,
    digestLength: 16,
    // 56-bit length of message so far (does not including padding)
    messageLength: 0,
    // true message length
    fullMessageLength: null,
    // size of message length in bytes
    messageLengthSize: 8
  };

  /**
   * Starts the digest.
   *
   * @return this digest object.
   */
  md.start = function() {
    // up to 56-bit message length for convenience
    md.messageLength = 0;

    // full message length (set md.messageLength64 for backwards-compatibility)
    md.fullMessageLength = md.messageLength64 = [];
    var int32s = md.messageLengthSize / 4;
    for(var i = 0; i < int32s; ++i) {
      md.fullMessageLength.push(0);
    }
    _input = forge.util.createBuffer();
    _state = {
      h0: 0x67452301,
      h1: 0xEFCDAB89,
      h2: 0x98BADCFE,
      h3: 0x10325476
    };
    return md;
  };
  // start digest automatically for first time
  md.start();

  /**
   * Updates the digest with the given message input. The given input can
   * treated as raw input (no encoding will be applied) or an encoding of
   * 'utf8' maybe given to encode the input using UTF-8.
   *
   * @param msg the message input to update with.
   * @param encoding the encoding to use (default: 'raw', other: 'utf8').
   *
   * @return this digest object.
   */
  md.update = function(msg, encoding) {
    if(encoding === 'utf8') {
      msg = forge.util.encodeUtf8(msg);
    }

    // update message length
    var len = msg.length;
    md.messageLength += len;
    len = [(len / 0x100000000) >>> 0, len >>> 0];
    for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
      md.fullMessageLength[i] += len[1];
      len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);
      md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;
      len[0] = (len[1] / 0x100000000) >>> 0;
    }

    // add bytes to input buffer
    _input.putBytes(msg);

    // process bytes
    _update(_state, _w, _input);

    // compact input buffer every 2K or if empty
    if(_input.read > 2048 || _input.length() === 0) {
      _input.compact();
    }

    return md;
  };

  /**
   * Produces the digest.
   *
   * @return a byte buffer containing the digest value.
   */
  md.digest = function() {
    /* Note: Here we copy the remaining bytes in the input buffer and
    add the appropriate MD5 padding. Then we do the final update
    on a copy of the state so that if the user wants to get
    intermediate digests they can do so. */

    /* Determine the number of bytes that must be added to the message
    to ensure its length is congruent to 448 mod 512. In other words,
    the data to be digested must be a multiple of 512 bits (or 128 bytes).
    This data includes the message, some padding, and the length of the
    message. Since the length of the message will be encoded as 8 bytes (64
    bits), that means that the last segment of the data must have 56 bytes
    (448 bits) of message and padding. Therefore, the length of the message
    plus the padding must be congruent to 448 mod 512 because
    512 - 128 = 448.

    In order to fill up the message length it must be filled with
    padding that begins with 1 bit followed by all 0 bits. Padding
    must *always* be present, so if the message length is already
    congruent to 448 mod 512, then 512 padding bits must be added. */

    var finalBlock = forge.util.createBuffer();
    finalBlock.putBytes(_input.bytes());

    // compute remaining size to be digested (include message length size)
    var remaining = (
      md.fullMessageLength[md.fullMessageLength.length - 1] +
      md.messageLengthSize);

    // add padding for overflow blockSize - overflow
    // _padding starts with 1 byte with first bit is set (byte value 128), then
    // there may be up to (blockSize - 1) other pad bytes
    var overflow = remaining & (md.blockLength - 1);
    finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));

    // serialize message length in bits in little-endian order; since length
    // is stored in bytes we multiply by 8 and add carry
    var bits, carry = 0;
    for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
      bits = md.fullMessageLength[i] * 8 + carry;
      carry = (bits / 0x100000000) >>> 0;
      finalBlock.putInt32Le(bits >>> 0);
    }

    var s2 = {
      h0: _state.h0,
      h1: _state.h1,
      h2: _state.h2,
      h3: _state.h3
    };
    _update(s2, _w, finalBlock);
    var rval = forge.util.createBuffer();
    rval.putInt32Le(s2.h0);
    rval.putInt32Le(s2.h1);
    rval.putInt32Le(s2.h2);
    rval.putInt32Le(s2.h3);
    return rval;
  };

  return md;
};

// padding, constant tables for calculating md5
var _padding = null;
var _g = null;
var _r = null;
var _k = null;
var _initialized = false;

/**
 * Initializes the constant tables.
 */
function _init() {
  // create padding
  _padding = String.fromCharCode(128);
  _padding += forge.util.fillString(String.fromCharCode(0x00), 64);

  // g values
  _g = [
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
    1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,
    5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,
    0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9];

  // rounds table
  _r = [
    7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,
    5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,
    4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,
    6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21];

  // get the result of abs(sin(i + 1)) as a 32-bit integer
  _k = new Array(64);
  for(var i = 0; i < 64; ++i) {
    _k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 0x100000000);
  }

  // now initialized
  _initialized = true;
}

/**
 * Updates an MD5 state with the given byte buffer.
 *
 * @param s the MD5 state to update.
 * @param w the array to use to store words.
 * @param bytes the byte buffer to update with.
 */
function _update(s, w, bytes) {
  // consume 512 bit (64 byte) chunks
  var t, a, b, c, d, f, r, i;
  var len = bytes.length();
  while(len >= 64) {
    // initialize hash value for this chunk
    a = s.h0;
    b = s.h1;
    c = s.h2;
    d = s.h3;

    // round 1
    for(i = 0; i < 16; ++i) {
      w[i] = bytes.getInt32Le();
      f = d ^ (b & (c ^ d));
      t = (a + f + _k[i] + w[i]);
      r = _r[i];
      a = d;
      d = c;
      c = b;
      b += (t << r) | (t >>> (32 - r));
    }
    // round 2
    for(; i < 32; ++i) {
      f = c ^ (d & (b ^ c));
      t = (a + f + _k[i] + w[_g[i]]);
      r = _r[i];
      a = d;
      d = c;
      c = b;
      b += (t << r) | (t >>> (32 - r));
    }
    // round 3
    for(; i < 48; ++i) {
      f = b ^ c ^ d;
      t = (a + f + _k[i] + w[_g[i]]);
      r = _r[i];
      a = d;
      d = c;
      c = b;
      b += (t << r) | (t >>> (32 - r));
    }
    // round 4
    for(; i < 64; ++i) {
      f = c ^ (b | ~d);
      t = (a + f + _k[i] + w[_g[i]]);
      r = _r[i];
      a = d;
      d = c;
      c = b;
      b += (t << r) | (t >>> (32 - r));
    }

    // update hash state
    s.h0 = (s.h0 + a) | 0;
    s.h1 = (s.h1 + b) | 0;
    s.h2 = (s.h2 + c) | 0;
    s.h3 = (s.h3 + d) | 0;

    len -= 64;
  }
}


code:
/**
 * Node.js module for Forge mask generation functions.
 *
 * @author Stefan Siegl
 *
 * Copyright 2012 Stefan Siegl <stesie@brokenpipe.de>
 */
var forge = require('./forge');
require('./mgf1');

module.exports = forge.mgf = forge.mgf || {};
forge.mgf.mgf1 = forge.mgf1;