Index: frontend/node_modules/node-forge/LICENSE
===================================================================
--- frontend/node_modules/node-forge/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,331 @@
+You may use the Forge project under the terms of either the BSD License or the
+GNU General Public License (GPL) Version 2.
+
+The BSD License is recommended for most projects. It is simple and easy to
+understand and it places almost no restrictions on what you can do with the
+Forge project.
+
+If the GPL suits your project better you are also free to use Forge under
+that license.
+
+You don't have to do anything special to choose one license or the other and
+you don't have to notify anyone which license you are using. You are free to
+use this project in commercial projects as long as the copyright header is
+left intact.
+
+If you are a commercial entity and use this set of libraries in your
+commercial software then reasonable payment to Digital Bazaar, if you can
+afford it, is not required but is expected and would be appreciated. If this
+library saves you time, then it's saving you money. The cost of developing
+the Forge software was on the order of several hundred hours and tens of
+thousands of dollars. We are attempting to strike a balance between helping
+the development community while not being taken advantage of by lucrative
+commercial entities for our efforts.
+
+-------------------------------------------------------------------------------
+New BSD License (3-clause)
+Copyright (c) 2010, Digital Bazaar, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of Digital Bazaar, Inc. nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL DIGITAL BAZAAR BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------------------
+        GNU GENERAL PUBLIC LICENSE
+           Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+          Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+        GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+          NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
Index: frontend/node_modules/node-forge/README.md
===================================================================
--- frontend/node_modules/node-forge/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,2071 @@
+# Forge
+
+[![NPM](https://nodei.co/npm/node-forge.svg?data=d)](https://nodei.co/npm/node-forge/)
+
+[![Main Checks](https://github.com/digitalbazaar/forge/actions/workflows/main.yaml/badge.svg)](https://github.com/digitalbazaar/forge/actions/workflows/main.yaml)
+
+A native implementation of [TLS][] (and various other cryptographic tools) in
+[JavaScript][].
+
+Introduction
+------------
+
+The Forge software is a fully native implementation of the [TLS][] protocol
+in JavaScript, a set of cryptography utilities, and a set of tools for
+developing Web Apps that utilize many network resources.
+
+Performance
+------------
+
+Forge is fast. Benchmarks against other popular JavaScript cryptography
+libraries can be found here:
+
+* http://dominictarr.github.io/crypto-bench/
+* http://cryptojs.altervista.org/test/simulate-threading-speed_test.html
+
+Documentation
+-------------
+
+* [Introduction](#introduction)
+* [Performance](#performance)
+* [Installation](#installation)
+* [Testing](#testing)
+* [Contributing](#contributing)
+
+### API
+
+* [Options](#options)
+
+### Transports
+
+* [TLS](#tls)
+* [HTTP](#http)
+* [SSH](#ssh)
+* [XHR](#xhr)
+* [Sockets](#socket)
+
+### Ciphers
+
+* [CIPHER](#cipher)
+* [AES](#aes)
+* [DES](#des)
+* [RC2](#rc2)
+
+### PKI
+
+* [ED25519](#ed25519)
+* [RSA](#rsa)
+* [RSA-KEM](#rsakem)
+* [X.509](#x509)
+* [PKCS#5](#pkcs5)
+* [PKCS#7](#pkcs7)
+* [PKCS#8](#pkcs8)
+* [PKCS#10](#pkcs10)
+* [PKCS#12](#pkcs12)
+* [ASN.1](#asn)
+
+### Message Digests
+
+* [SHA1](#sha1)
+* [SHA256](#sha256)
+* [SHA384](#sha384)
+* [SHA512](#sha512)
+* [MD5](#md5)
+* [HMAC](#hmac)
+
+### Utilities
+
+* [Prime](#prime)
+* [PRNG](#prng)
+* [Tasks](#task)
+* [Utilities](#util)
+* [Logging](#log)
+* [Flash Networking Support](#flash)
+
+### Other
+
+* [Security Considerations](#security-considerations)
+* [Library Background](#library-background)
+* [Contact](#contact)
+* [Donations](#donations)
+
+---------------------------------------
+
+Installation
+------------
+
+**Note**: Please see the [Security Considerations](#security-considerations)
+section before using packaging systems and pre-built files.
+
+Forge uses a [CommonJS][] module structure with a build process for browser
+bundles. The older [0.6.x][] branch with standalone files is available but will
+not be regularly updated.
+
+### Node.js
+
+If you want to use forge with [Node.js][], it is available through `npm`:
+
+https://www.npmjs.com/package/node-forge
+
+Installation:
+
+    npm install node-forge
+
+You can then use forge as a regular module:
+
+```js
+var forge = require('node-forge');
+```
+
+The npm package includes pre-built `forge.min.js`, `forge.all.min.js`, and
+`prime.worker.min.js` using the [UMD][] format.
+
+### jsDelivr CDN
+
+To use it via [jsDelivr](https://www.jsdelivr.com/package/npm/node-forge) include this in your html:
+
+```html
+<script src="https://cdn.jsdelivr.net/npm/node-forge@1.0.0/dist/forge.min.js"></script>
+```
+
+### unpkg CDN
+
+To use it via [unpkg](https://unpkg.com/#/) include this in your html:
+
+```html
+<script src="https://unpkg.com/node-forge@1.0.0/dist/forge.min.js"></script>
+```
+
+### Development Requirements
+
+The core JavaScript has the following requirements to build and test:
+
+* Building a browser bundle:
+  * Node.js
+  * npm
+* Testing
+  * Node.js
+  * npm
+  * Chrome, Firefox, Safari (optional)
+
+Some special networking features can optionally use a Flash component.  See the
+[Flash README](./flash/README.md) for details.
+
+### Building for a web browser
+
+To create single file bundles for use with browsers run the following:
+
+    npm install
+    npm run build
+
+This will create single non-minimized and minimized files that can be
+included in the browser:
+
+    dist/forge.js
+    dist/forge.min.js
+
+A bundle that adds some utilities and networking support is also available:
+
+    dist/forge.all.js
+    dist/forge.all.min.js
+
+Include the file via:
+
+```html
+<script src="YOUR_SCRIPT_PATH/forge.js"></script>
+```
+or
+```html
+<script src="YOUR_SCRIPT_PATH/forge.min.js"></script>
+```
+
+The above bundles will synchronously create a global 'forge' object.
+
+**Note**: These bundles will not include any WebWorker scripts (eg:
+`dist/prime.worker.js`), so these will need to be accessible from the browser
+if any WebWorkers are used.
+
+### Building a custom browser bundle
+
+The build process uses [webpack][] and the [config](./webpack.config.js) file
+can be modified to generate a file or files that only contain the parts of
+forge you need.
+
+[Browserify][] override support is also present in `package.json`.
+
+Testing
+-------
+
+### Prepare to run tests
+
+    npm install
+
+### Running automated tests with Node.js
+
+Forge natively runs in a [Node.js][] environment:
+
+    npm test
+
+### Running automated tests with Headless Chrome
+
+Automated testing is done via [Karma][]. By default it will run the tests with
+Headless Chrome.
+
+    npm run test-karma
+
+Is 'mocha' reporter output too verbose? Other reporters are available. Try
+'dots', 'progress', or 'tap'.
+
+    npm run test-karma -- --reporters progress
+
+By default [webpack][] is used. [Browserify][] can also be used.
+
+    BUNDLER=browserify npm run test-karma
+
+### Running automated tests with one or more browsers
+
+You can also specify one or more browsers to use.
+
+    npm run test-karma -- --browsers Chrome,Firefox,Safari,ChromeHeadless
+
+The reporter option and `BUNDLER` environment variable can also be used.
+
+### Running manual tests in a browser
+
+Testing in a browser uses [webpack][] to combine forge and all tests and then
+loading the result in a browser. A simple web server is provided that will
+output the HTTP or HTTPS URLs to load. It also will start a simple Flash Policy
+Server. Unit tests and older legacy tests are provided. Custom ports can be
+used by running `node tests/server.js` manually.
+
+To run the unit tests in a browser a special forge build is required:
+
+    npm run test-build
+
+To run legacy browser based tests the main forge build is required:
+
+    npm run build
+
+The tests are run with a custom server that prints out the URLs to use:
+
+    npm run test-server
+
+### Running other tests
+
+There are some other random tests and benchmarks available in the tests
+directory.
+
+### Coverage testing
+
+To perform coverage testing of the unit tests, run the following. The results
+will be put in the `coverage/` directory. Note that coverage testing can slow
+down some tests considerably.
+
+    npm install
+    npm run coverage
+
+Contributing
+------------
+
+Any contributions (eg: PRs) that are accepted will be brought under the same
+license used by the rest of the Forge project. This license allows Forge to
+be used under the terms of either the BSD License or the GNU General Public
+License (GPL) Version 2.
+
+See: [LICENSE](https://github.com/digitalbazaar/forge/blob/cbebca3780658703d925b61b2caffb1d263a6c1d/LICENSE)
+
+If a contribution contains 3rd party source code with its own license, it
+may retain it, so long as that license is compatible with the Forge license.
+
+API
+---
+
+<a name="options" />
+
+### Options
+
+If at any time you wish to disable the use of native code, where available,
+for particular forge features like its secure random number generator, you
+may set the ```forge.options.usePureJavaScript``` flag to ```true```. It is
+not recommended that you set this flag as native code is typically more
+performant and may have stronger security properties. It may be useful to
+set this flag to test certain features that you plan to run in environments
+that are different from your testing environment.
+
+To disable native code when including forge in the browser:
+
+```js
+// run this *after* including the forge script
+forge.options.usePureJavaScript = true;
+```
+
+To disable native code when using Node.js:
+
+```js
+var forge = require('node-forge');
+forge.options.usePureJavaScript = true;
+```
+
+Transports
+----------
+
+<a name="tls" />
+
+### TLS
+
+Provides a native javascript client and server-side [TLS][] implementation.
+
+__Examples__
+
+```js
+// create TLS client
+var client = forge.tls.createConnection({
+  server: false,
+  caStore: /* Array of PEM-formatted certs or a CA store object */,
+  sessionCache: {},
+  // supported cipher suites in order of preference
+  cipherSuites: [
+    forge.tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA,
+    forge.tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA],
+  virtualHost: 'example.com',
+  verify: function(connection, verified, depth, certs) {
+    if(depth === 0) {
+      var cn = certs[0].subject.getField('CN').value;
+      if(cn !== 'example.com') {
+        verified = {
+          alert: forge.tls.Alert.Description.bad_certificate,
+          message: 'Certificate common name does not match hostname.'
+        };
+      }
+    }
+    return verified;
+  },
+  connected: function(connection) {
+    console.log('connected');
+    // send message to server
+    connection.prepare(forge.util.encodeUtf8('Hi server!'));
+    /* NOTE: experimental, start heartbeat retransmission timer
+    myHeartbeatTimer = setInterval(function() {
+      connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
+    }, 5*60*1000);*/
+  },
+  /* provide a client-side cert if you want
+  getCertificate: function(connection, hint) {
+    return myClientCertificate;
+  },
+  /* the private key for the client-side cert if provided */
+  getPrivateKey: function(connection, cert) {
+    return myClientPrivateKey;
+  },
+  tlsDataReady: function(connection) {
+    // TLS data (encrypted) is ready to be sent to the server
+    sendToServerSomehow(connection.tlsData.getBytes());
+    // if you were communicating with the server below, you'd do:
+    // server.process(connection.tlsData.getBytes());
+  },
+  dataReady: function(connection) {
+    // clear data from the server is ready
+    console.log('the server sent: ' +
+      forge.util.decodeUtf8(connection.data.getBytes()));
+    // close connection
+    connection.close();
+  },
+  /* NOTE: experimental
+  heartbeatReceived: function(connection, payload) {
+    // restart retransmission timer, look at payload
+    clearInterval(myHeartbeatTimer);
+    myHeartbeatTimer = setInterval(function() {
+      connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
+    }, 5*60*1000);
+    payload.getBytes();
+  },*/
+  closed: function(connection) {
+    console.log('disconnected');
+  },
+  error: function(connection, error) {
+    console.log('uh oh', error);
+  }
+});
+
+// start the handshake process
+client.handshake();
+
+// when encrypted TLS data is received from the server, process it
+client.process(encryptedBytesFromServer);
+
+// create TLS server
+var server = forge.tls.createConnection({
+  server: true,
+  caStore: /* Array of PEM-formatted certs or a CA store object */,
+  sessionCache: {},
+  // supported cipher suites in order of preference
+  cipherSuites: [
+    forge.tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA,
+    forge.tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA],
+  // require a client-side certificate if you want
+  verifyClient: true,
+  verify: function(connection, verified, depth, certs) {
+    if(depth === 0) {
+      var cn = certs[0].subject.getField('CN').value;
+      if(cn !== 'the-client') {
+        verified = {
+          alert: forge.tls.Alert.Description.bad_certificate,
+          message: 'Certificate common name does not match expected client.'
+        };
+      }
+    }
+    return verified;
+  },
+  connected: function(connection) {
+    console.log('connected');
+    // send message to client
+    connection.prepare(forge.util.encodeUtf8('Hi client!'));
+    /* NOTE: experimental, start heartbeat retransmission timer
+    myHeartbeatTimer = setInterval(function() {
+      connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
+    }, 5*60*1000);*/
+  },
+  getCertificate: function(connection, hint) {
+    return myServerCertificate;
+  },
+  getPrivateKey: function(connection, cert) {
+    return myServerPrivateKey;
+  },
+  tlsDataReady: function(connection) {
+    // TLS data (encrypted) is ready to be sent to the client
+    sendToClientSomehow(connection.tlsData.getBytes());
+    // if you were communicating with the client above you'd do:
+    // client.process(connection.tlsData.getBytes());
+  },
+  dataReady: function(connection) {
+    // clear data from the client is ready
+    console.log('the client sent: ' +
+      forge.util.decodeUtf8(connection.data.getBytes()));
+    // close connection
+    connection.close();
+  },
+  /* NOTE: experimental
+  heartbeatReceived: function(connection, payload) {
+    // restart retransmission timer, look at payload
+    clearInterval(myHeartbeatTimer);
+    myHeartbeatTimer = setInterval(function() {
+      connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
+    }, 5*60*1000);
+    payload.getBytes();
+  },*/
+  closed: function(connection) {
+    console.log('disconnected');
+  },
+  error: function(connection, error) {
+    console.log('uh oh', error);
+  }
+});
+
+// when encrypted TLS data is received from the client, process it
+server.process(encryptedBytesFromClient);
+```
+
+Connect to a TLS server using node's net.Socket:
+
+```js
+var socket = new net.Socket();
+
+var client = forge.tls.createConnection({
+  server: false,
+  verify: function(connection, verified, depth, certs) {
+    // skip verification for testing
+    console.log('[tls] server certificate verified');
+    return true;
+  },
+  connected: function(connection) {
+    console.log('[tls] connected');
+    // prepare some data to send (note that the string is interpreted as
+    // 'binary' encoded, which works for HTTP which only uses ASCII, use
+    // forge.util.encodeUtf8(str) otherwise
+    client.prepare('GET / HTTP/1.0\r\n\r\n');
+  },
+  tlsDataReady: function(connection) {
+    // encrypted data is ready to be sent to the server
+    var data = connection.tlsData.getBytes();
+    socket.write(data, 'binary'); // encoding should be 'binary'
+  },
+  dataReady: function(connection) {
+    // clear data from the server is ready
+    var data = connection.data.getBytes();
+    console.log('[tls] data received from the server: ' + data);
+  },
+  closed: function() {
+    console.log('[tls] disconnected');
+  },
+  error: function(connection, error) {
+    console.log('[tls] error', error);
+  }
+});
+
+socket.on('connect', function() {
+  console.log('[socket] connected');
+  client.handshake();
+});
+socket.on('data', function(data) {
+  client.process(data.toString('binary')); // encoding should be 'binary'
+});
+socket.on('end', function() {
+  console.log('[socket] disconnected');
+});
+
+// connect to google.com
+socket.connect(443, 'google.com');
+
+// or connect to gmail's imap server (but don't send the HTTP header above)
+//socket.connect(993, 'imap.gmail.com');
+```
+
+<a name="http" />
+
+### HTTP
+
+Provides a native [JavaScript][] mini-implementation of an http client that
+uses pooled sockets.
+
+__Examples__
+
+```js
+// create an HTTP GET request
+var request = forge.http.createRequest({method: 'GET', path: url.path});
+
+// send the request somewhere
+sendSomehow(request.toString());
+
+// receive response
+var buffer = forge.util.createBuffer();
+var response = forge.http.createResponse();
+var someAsyncDataHandler = function(bytes) {
+  if(!response.bodyReceived) {
+    buffer.putBytes(bytes);
+    if(!response.headerReceived) {
+      if(response.readHeader(buffer)) {
+        console.log('HTTP response header: ' + response.toString());
+      }
+    }
+    if(response.headerReceived && !response.bodyReceived) {
+      if(response.readBody(buffer)) {
+        console.log('HTTP response body: ' + response.body);
+      }
+    }
+  }
+};
+```
+
+<a name="ssh" />
+
+### SSH
+
+Provides some SSH utility functions.
+
+__Examples__
+
+```js
+// encodes (and optionally encrypts) a private RSA key as a Putty PPK file
+forge.ssh.privateKeyToPutty(privateKey, passphrase, comment);
+
+// encodes a public RSA key as an OpenSSH file
+forge.ssh.publicKeyToOpenSSH(key, comment);
+
+// encodes a private RSA key as an OpenSSH file
+forge.ssh.privateKeyToOpenSSH(privateKey, passphrase);
+
+// gets the SSH public key fingerprint in a byte buffer
+forge.ssh.getPublicKeyFingerprint(key);
+
+// gets a hex-encoded, colon-delimited SSH public key fingerprint
+forge.ssh.getPublicKeyFingerprint(key, {encoding: 'hex', delimiter: ':'});
+```
+
+<a name="xhr" />
+
+### XHR
+
+Provides an XmlHttpRequest implementation using forge.http as a backend.
+
+__Examples__
+
+```js
+// TODO
+```
+
+<a name="socket" />
+
+### Sockets
+
+Provides an interface to create and use raw sockets provided via Flash.
+
+__Examples__
+
+```js
+// TODO
+```
+
+Ciphers
+-------
+
+<a name="cipher" />
+
+### CIPHER
+
+Provides a basic API for block encryption and decryption. There is built-in
+support for the ciphers: [AES][], [3DES][], and [DES][], and for the modes
+of operation: [ECB][], [CBC][], [CFB][], [OFB][], [CTR][], and [GCM][].
+
+These algorithms are currently supported:
+
+* AES-ECB
+* AES-CBC
+* AES-CFB
+* AES-OFB
+* AES-CTR
+* AES-GCM
+* 3DES-ECB
+* 3DES-CBC
+* DES-ECB
+* DES-CBC
+
+When using an [AES][] algorithm, the key size will determine whether
+AES-128, AES-192, or AES-256 is used (all are supported). When a [DES][]
+algorithm is used, the key size will determine whether [3DES][] or regular
+[DES][] is used. Use a [3DES][] algorithm to enforce Triple-DES.
+
+__Examples__
+
+```js
+// generate a random key and IV
+// Note: a key size of 16 bytes will use AES-128, 24 => AES-192, 32 => AES-256
+var key = forge.random.getBytesSync(16);
+var iv = forge.random.getBytesSync(16);
+
+/* alternatively, generate a password-based 16-byte key
+var salt = forge.random.getBytesSync(128);
+var key = forge.pkcs5.pbkdf2('password', salt, numIterations, 16);
+*/
+
+// encrypt some bytes using CBC mode
+// (other modes include: ECB, CFB, OFB, CTR, and GCM)
+// Note: CBC and ECB modes use PKCS#7 padding as default
+var cipher = forge.cipher.createCipher('AES-CBC', key);
+cipher.start({iv: iv});
+cipher.update(forge.util.createBuffer(someBytes));
+cipher.finish();
+var encrypted = cipher.output;
+// outputs encrypted hex
+console.log(encrypted.toHex());
+
+// decrypt some bytes using CBC mode
+// (other modes include: CFB, OFB, CTR, and GCM)
+var decipher = forge.cipher.createDecipher('AES-CBC', key);
+decipher.start({iv: iv});
+decipher.update(encrypted);
+var result = decipher.finish(); // check 'result' for true/false
+// outputs decrypted hex
+console.log(decipher.output.toHex());
+
+// decrypt bytes using CBC mode and streaming
+// Performance can suffer for large multi-MB inputs due to buffer
+// manipulations. Stream processing in chunks can offer significant
+// improvement. CPU intensive update() calls could also be performed with
+// setImmediate/setTimeout to avoid blocking the main browser UI thread (not
+// shown here). Optimal block size depends on the JavaScript VM and other
+// factors. Encryption can use a simple technique for increased performance.
+var encryptedBytes = encrypted.bytes();
+var decipher = forge.cipher.createDecipher('AES-CBC', key);
+decipher.start({iv: iv});
+var length = encryptedBytes.length;
+var chunkSize = 1024 * 64;
+var index = 0;
+var decrypted = '';
+do {
+  decrypted += decipher.output.getBytes();
+  var buf = forge.util.createBuffer(encryptedBytes.substr(index, chunkSize));
+  decipher.update(buf);
+  index += chunkSize;
+} while(index < length);
+var result = decipher.finish();
+assert(result);
+decrypted += decipher.output.getBytes();
+console.log(forge.util.bytesToHex(decrypted));
+
+// encrypt some bytes using GCM mode
+var cipher = forge.cipher.createCipher('AES-GCM', key);
+cipher.start({
+  iv: iv, // should be a 12-byte binary-encoded string or byte buffer
+  additionalData: 'binary-encoded string', // optional
+  tagLength: 128 // optional, defaults to 128 bits
+});
+cipher.update(forge.util.createBuffer(someBytes));
+cipher.finish();
+var encrypted = cipher.output;
+var tag = cipher.mode.tag;
+// outputs encrypted hex
+console.log(encrypted.toHex());
+// outputs authentication tag
+console.log(tag.toHex());
+
+// decrypt some bytes using GCM mode
+var decipher = forge.cipher.createDecipher('AES-GCM', key);
+decipher.start({
+  iv: iv,
+  additionalData: 'binary-encoded string', // optional
+  tagLength: 128, // optional, defaults to 128 bits
+  tag: tag // authentication tag from encryption
+});
+decipher.update(encrypted);
+var pass = decipher.finish();
+// pass is false if there was a failure (eg: authentication tag didn't match)
+if(pass) {
+  // outputs decrypted hex
+  console.log(decipher.output.toHex());
+}
+```
+
+Using forge in Node.js to match openssl's "enc" command line tool (**Note**: OpenSSL "enc" uses a non-standard file format with a custom key derivation function and a fixed iteration count of 1, which some consider less secure than alternatives such as [OpenPGP](https://tools.ietf.org/html/rfc4880)/[GnuPG](https://www.gnupg.org/)):
+
+```js
+var forge = require('node-forge');
+var fs = require('fs');
+
+// openssl enc -des3 -in input.txt -out input.enc
+function encrypt(password) {
+  var input = fs.readFileSync('input.txt', {encoding: 'binary'});
+
+  // 3DES key and IV sizes
+  var keySize = 24;
+  var ivSize = 8;
+
+  // get derived bytes
+  // Notes:
+  // 1. If using an alternative hash (eg: "-md sha1") pass
+  //   "forge.md.sha1.create()" as the final parameter.
+  // 2. If using "-nosalt", set salt to null.
+  var salt = forge.random.getBytesSync(8);
+  // var md = forge.md.sha1.create(); // "-md sha1"
+  var derivedBytes = forge.pbe.opensslDeriveBytes(
+    password, salt, keySize + ivSize/*, md*/);
+  var buffer = forge.util.createBuffer(derivedBytes);
+  var key = buffer.getBytes(keySize);
+  var iv = buffer.getBytes(ivSize);
+
+  var cipher = forge.cipher.createCipher('3DES-CBC', key);
+  cipher.start({iv: iv});
+  cipher.update(forge.util.createBuffer(input, 'binary'));
+  cipher.finish();
+
+  var output = forge.util.createBuffer();
+
+  // if using a salt, prepend this to the output:
+  if(salt !== null) {
+    output.putBytes('Salted__'); // (add to match openssl tool output)
+    output.putBytes(salt);
+  }
+  output.putBuffer(cipher.output);
+
+  fs.writeFileSync('input.enc', output.getBytes(), {encoding: 'binary'});
+}
+
+// openssl enc -d -des3 -in input.enc -out input.dec.txt
+function decrypt(password) {
+  var input = fs.readFileSync('input.enc', {encoding: 'binary'});
+
+  // parse salt from input
+  input = forge.util.createBuffer(input, 'binary');
+  // skip "Salted__" (if known to be present)
+  input.getBytes('Salted__'.length);
+  // read 8-byte salt
+  var salt = input.getBytes(8);
+
+  // Note: if using "-nosalt", skip above parsing and use
+  // var salt = null;
+
+  // 3DES key and IV sizes
+  var keySize = 24;
+  var ivSize = 8;
+
+  var derivedBytes = forge.pbe.opensslDeriveBytes(
+    password, salt, keySize + ivSize);
+  var buffer = forge.util.createBuffer(derivedBytes);
+  var key = buffer.getBytes(keySize);
+  var iv = buffer.getBytes(ivSize);
+
+  var decipher = forge.cipher.createDecipher('3DES-CBC', key);
+  decipher.start({iv: iv});
+  decipher.update(input);
+  var result = decipher.finish(); // check 'result' for true/false
+
+  fs.writeFileSync(
+    'input.dec.txt', decipher.output.getBytes(), {encoding: 'binary'});
+}
+```
+
+<a name="aes" />
+
+### AES
+
+Provides [AES][] encryption and decryption in [CBC][], [CFB][], [OFB][],
+[CTR][], and [GCM][] modes. See [CIPHER](#cipher) for examples.
+
+<a name="des" />
+
+### DES
+
+Provides [3DES][] and [DES][] encryption and decryption in [ECB][] and
+[CBC][] modes. See [CIPHER](#cipher) for examples.
+
+<a name="rc2" />
+
+### RC2
+
+__Examples__
+
+```js
+// generate a random key and IV
+var key = forge.random.getBytesSync(16);
+var iv = forge.random.getBytesSync(8);
+
+// encrypt some bytes
+var cipher = forge.rc2.createEncryptionCipher(key);
+cipher.start(iv);
+cipher.update(forge.util.createBuffer(someBytes));
+cipher.finish();
+var encrypted = cipher.output;
+// outputs encrypted hex
+console.log(encrypted.toHex());
+
+// decrypt some bytes
+var cipher = forge.rc2.createDecryptionCipher(key);
+cipher.start(iv);
+cipher.update(encrypted);
+cipher.finish();
+// outputs decrypted hex
+console.log(cipher.output.toHex());
+```
+
+PKI
+---
+
+Provides [X.509][] certificate support, ED25519 key generation and
+signing/verifying, and RSA public and private key encoding, decoding,
+encryption/decryption, and signing/verifying.
+
+<a name="ed25519" />
+
+### ED25519
+
+Special thanks to [TweetNaCl.js][] for providing the bulk of the implementation.
+
+__Examples__
+
+```js
+var ed25519 = forge.pki.ed25519;
+
+// generate a random ED25519 keypair
+var keypair = ed25519.generateKeyPair();
+// `keypair.publicKey` is a node.js Buffer or Uint8Array
+// `keypair.privateKey` is a node.js Buffer or Uint8Array
+
+// generate a random ED25519 keypair based on a random 32-byte seed
+var seed = forge.random.getBytesSync(32);
+var keypair = ed25519.generateKeyPair({seed: seed});
+
+// generate a random ED25519 keypair based on a "password" 32-byte seed
+var password = 'Mai9ohgh6ahxee0jutheew0pungoozil';
+var seed = new forge.util.ByteBuffer(password, 'utf8');
+var keypair = ed25519.generateKeyPair({seed: seed});
+
+// sign a UTF-8 message
+var signature = ED25519.sign({
+  message: 'test',
+  // also accepts `binary` if you want to pass a binary string
+  encoding: 'utf8',
+  // node.js Buffer, Uint8Array, forge ByteBuffer, binary string
+  privateKey: privateKey
+});
+// `signature` is a node.js Buffer or Uint8Array
+
+// sign a message passed as a buffer
+var signature = ED25519.sign({
+  // also accepts a forge ByteBuffer or Uint8Array
+  message: Buffer.from('test', 'utf8'),
+  privateKey: privateKey
+});
+
+// sign a message digest (shorter "message" == better performance)
+var md = forge.md.sha256.create();
+md.update('test', 'utf8');
+var signature = ED25519.sign({
+  md: md,
+  privateKey: privateKey
+});
+
+// verify a signature on a UTF-8 message
+var verified = ED25519.verify({
+  message: 'test',
+  encoding: 'utf8',
+  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
+  signature: signature,
+  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
+  publicKey: publicKey
+});
+// `verified` is true/false
+
+// sign a message passed as a buffer
+var verified = ED25519.verify({
+  // also accepts a forge ByteBuffer or Uint8Array
+  message: Buffer.from('test', 'utf8'),
+  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
+  signature: signature,
+  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
+  publicKey: publicKey
+});
+
+// verify a signature on a message digest
+var md = forge.md.sha256.create();
+md.update('test', 'utf8');
+var verified = ED25519.verify({
+  md: md,
+  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
+  signature: signature,
+  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
+  publicKey: publicKey
+});
+```
+
+<a name="rsa" />
+
+### RSA
+
+__Examples__
+
+```js
+var rsa = forge.pki.rsa;
+
+// generate an RSA key pair synchronously
+// *NOT RECOMMENDED*: Can be significantly slower than async and may block
+// JavaScript execution. Will use native Node.js 10.12.0+ API if possible.
+var keypair = rsa.generateKeyPair({bits: 2048, e: 0x10001});
+
+// generate an RSA key pair asynchronously (uses web workers if available)
+// use workers: -1 to run a fast core estimator to optimize # of workers
+// *RECOMMENDED*: Can be significantly faster than sync. Will use native
+// Node.js 10.12.0+ or WebCrypto API if possible.
+rsa.generateKeyPair({bits: 2048, workers: 2}, function(err, keypair) {
+  // keypair.privateKey, keypair.publicKey
+});
+
+// generate an RSA key pair in steps that attempt to run for a specified period
+// of time on the main JS thread
+var state = rsa.createKeyPairGenerationState(2048, 0x10001);
+var step = function() {
+  // run for 100 ms
+  if(!rsa.stepKeyPairGenerationState(state, 100)) {
+    setTimeout(step, 1);
+  }
+  else {
+    // done, turn off progress indicator, use state.keys
+  }
+};
+// turn on progress indicator, schedule generation to run
+setTimeout(step);
+
+// sign data with a private key and output DigestInfo DER-encoded bytes
+// (defaults to RSASSA PKCS#1 v1.5)
+var md = forge.md.sha1.create();
+md.update('sign this', 'utf8');
+var signature = privateKey.sign(md);
+
+// verify data with a public key
+// (defaults to RSASSA PKCS#1 v1.5)
+var verified = publicKey.verify(md.digest().bytes(), signature);
+
+// sign data using RSASSA-PSS where PSS uses a SHA-1 hash, a SHA-1 based
+// masking function MGF1, and a 20 byte salt
+var md = forge.md.sha1.create();
+md.update('sign this', 'utf8');
+var pss = forge.pss.create({
+  md: forge.md.sha1.create(),
+  mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
+  saltLength: 20
+  // optionally pass 'prng' with a custom PRNG implementation
+  // optionalls pass 'salt' with a forge.util.ByteBuffer w/custom salt
+});
+var signature = privateKey.sign(md, pss);
+
+// verify RSASSA-PSS signature
+var pss = forge.pss.create({
+  md: forge.md.sha1.create(),
+  mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
+  saltLength: 20
+  // optionally pass 'prng' with a custom PRNG implementation
+});
+var md = forge.md.sha1.create();
+md.update('sign this', 'utf8');
+publicKey.verify(md.digest().getBytes(), signature, pss);
+
+// encrypt data with a public key (defaults to RSAES PKCS#1 v1.5)
+var encrypted = publicKey.encrypt(bytes);
+
+// decrypt data with a private key (defaults to RSAES PKCS#1 v1.5)
+var decrypted = privateKey.decrypt(encrypted);
+
+// encrypt data with a public key using RSAES PKCS#1 v1.5
+var encrypted = publicKey.encrypt(bytes, 'RSAES-PKCS1-V1_5');
+
+// decrypt data with a private key using RSAES PKCS#1 v1.5
+var decrypted = privateKey.decrypt(encrypted, 'RSAES-PKCS1-V1_5');
+
+// encrypt data with a public key using RSAES-OAEP
+var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP');
+
+// decrypt data with a private key using RSAES-OAEP
+var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP');
+
+// encrypt data with a public key using RSAES-OAEP/SHA-256
+var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
+  md: forge.md.sha256.create()
+});
+
+// decrypt data with a private key using RSAES-OAEP/SHA-256
+var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP', {
+  md: forge.md.sha256.create()
+});
+
+// encrypt data with a public key using RSAES-OAEP/SHA-256/MGF1-SHA-1
+// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
+var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
+  md: forge.md.sha256.create(),
+  mgf1: {
+    md: forge.md.sha1.create()
+  }
+});
+
+// decrypt data with a private key using RSAES-OAEP/SHA-256/MGF1-SHA-1
+// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
+var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP', {
+  md: forge.md.sha256.create(),
+  mgf1: {
+    md: forge.md.sha1.create()
+  }
+});
+
+```
+
+<a name="rsakem" />
+
+### RSA-KEM
+
+__Examples__
+
+```js
+// generate an RSA key pair asynchronously (uses web workers if available)
+// use workers: -1 to run a fast core estimator to optimize # of workers
+forge.rsa.generateKeyPair({bits: 2048, workers: -1}, function(err, keypair) {
+  // keypair.privateKey, keypair.publicKey
+});
+
+// generate and encapsulate a 16-byte secret key
+var kdf1 = new forge.kem.kdf1(forge.md.sha1.create());
+var kem = forge.kem.rsa.create(kdf1);
+var result = kem.encrypt(keypair.publicKey, 16);
+// result has 'encapsulation' and 'key'
+
+// encrypt some bytes
+var iv = forge.random.getBytesSync(12);
+var someBytes = 'hello world!';
+var cipher = forge.cipher.createCipher('AES-GCM', result.key);
+cipher.start({iv: iv});
+cipher.update(forge.util.createBuffer(someBytes));
+cipher.finish();
+var encrypted = cipher.output.getBytes();
+var tag = cipher.mode.tag.getBytes();
+
+// send 'encrypted', 'iv', 'tag', and result.encapsulation to recipient
+
+// decrypt encapsulated 16-byte secret key
+var kdf1 = new forge.kem.kdf1(forge.md.sha1.create());
+var kem = forge.kem.rsa.create(kdf1);
+var key = kem.decrypt(keypair.privateKey, result.encapsulation, 16);
+
+// decrypt some bytes
+var decipher = forge.cipher.createDecipher('AES-GCM', key);
+decipher.start({iv: iv, tag: tag});
+decipher.update(forge.util.createBuffer(encrypted));
+var pass = decipher.finish();
+// pass is false if there was a failure (eg: authentication tag didn't match)
+if(pass) {
+  // outputs 'hello world!'
+  console.log(decipher.output.getBytes());
+}
+
+```
+
+<a name="x509" />
+
+### X.509
+
+__Examples__
+
+```js
+var pki = forge.pki;
+
+// convert a PEM-formatted public key to a Forge public key
+var publicKey = pki.publicKeyFromPem(pem);
+
+// convert a Forge public key to PEM-format
+var pem = pki.publicKeyToPem(publicKey);
+
+// convert an ASN.1 SubjectPublicKeyInfo to a Forge public key
+var publicKey = pki.publicKeyFromAsn1(subjectPublicKeyInfo);
+
+// convert a Forge public key to an ASN.1 SubjectPublicKeyInfo
+var subjectPublicKeyInfo = pki.publicKeyToAsn1(publicKey);
+
+// gets a SHA-1 RSAPublicKey fingerprint a byte buffer
+pki.getPublicKeyFingerprint(key);
+
+// gets a SHA-1 SubjectPublicKeyInfo fingerprint a byte buffer
+pki.getPublicKeyFingerprint(key, {type: 'SubjectPublicKeyInfo'});
+
+// gets a hex-encoded, colon-delimited SHA-1 RSAPublicKey public key fingerprint
+pki.getPublicKeyFingerprint(key, {encoding: 'hex', delimiter: ':'});
+
+// gets a hex-encoded, colon-delimited SHA-1 SubjectPublicKeyInfo public key fingerprint
+pki.getPublicKeyFingerprint(key, {
+  type: 'SubjectPublicKeyInfo',
+  encoding: 'hex',
+  delimiter: ':'
+});
+
+// gets a hex-encoded, colon-delimited MD5 RSAPublicKey public key fingerprint
+pki.getPublicKeyFingerprint(key, {
+  md: forge.md.md5.create(),
+  encoding: 'hex',
+  delimiter: ':'
+});
+
+// creates a CA store
+var caStore = pki.createCaStore([/* PEM-encoded cert */, ...]);
+
+// add a certificate to the CA store
+caStore.addCertificate(certObjectOrPemString);
+
+// gets the issuer (its certificate) for the given certificate
+var issuerCert = caStore.getIssuer(subjectCert);
+
+// verifies a certificate chain against a CA store
+pki.verifyCertificateChain(caStore, chain, customVerifyCallback);
+
+// signs a certificate using the given private key
+cert.sign(privateKey);
+
+// signs a certificate using SHA-256 instead of SHA-1
+cert.sign(privateKey, forge.md.sha256.create());
+
+// verifies an issued certificate using the certificates public key
+var verified = issuer.verify(issued);
+
+// generate a keypair and create an X.509v3 certificate
+var keys = pki.rsa.generateKeyPair(2048);
+var cert = pki.createCertificate();
+cert.publicKey = keys.publicKey;
+// alternatively set public key from a csr
+//cert.publicKey = csr.publicKey;
+// NOTE: serialNumber is the hex encoded value of an ASN.1 INTEGER.
+// Conforming CAs should ensure serialNumber is:
+// - no more than 20 octets
+// - non-negative (prefix a '00' if your value starts with a '1' bit)
+cert.serialNumber = '01';
+cert.validity.notBefore = new Date();
+cert.validity.notAfter = new Date();
+cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
+var attrs = [{
+  name: 'commonName',
+  value: 'example.org'
+}, {
+  name: 'countryName',
+  value: 'US'
+}, {
+  shortName: 'ST',
+  value: 'Virginia'
+}, {
+  name: 'localityName',
+  value: 'Blacksburg'
+}, {
+  name: 'organizationName',
+  value: 'Test'
+}, {
+  shortName: 'OU',
+  value: 'Test'
+}];
+cert.setSubject(attrs);
+// alternatively set subject from a csr
+//cert.setSubject(csr.subject.attributes);
+cert.setIssuer(attrs);
+cert.setExtensions([{
+  name: 'basicConstraints',
+  cA: true
+}, {
+  name: 'keyUsage',
+  keyCertSign: true,
+  digitalSignature: true,
+  nonRepudiation: true,
+  keyEncipherment: true,
+  dataEncipherment: true
+}, {
+  name: 'extKeyUsage',
+  serverAuth: true,
+  clientAuth: true,
+  codeSigning: true,
+  emailProtection: true,
+  timeStamping: true
+}, {
+  name: 'nsCertType',
+  client: true,
+  server: true,
+  email: true,
+  objsign: true,
+  sslCA: true,
+  emailCA: true,
+  objCA: true
+}, {
+  name: 'subjectAltName',
+  altNames: [{
+    type: 6, // URI
+    value: 'http://example.org/webid#me'
+  }, {
+    type: 7, // IP
+    ip: '127.0.0.1'
+  }]
+}, {
+  name: 'subjectKeyIdentifier'
+}]);
+/* alternatively set extensions from a csr
+var extensions = csr.getAttribute({name: 'extensionRequest'}).extensions;
+// optionally add more extensions
+extensions.push.apply(extensions, [{
+  name: 'basicConstraints',
+  cA: true
+}, {
+  name: 'keyUsage',
+  keyCertSign: true,
+  digitalSignature: true,
+  nonRepudiation: true,
+  keyEncipherment: true,
+  dataEncipherment: true
+}]);
+cert.setExtensions(extensions);
+*/
+// self-sign certificate
+cert.sign(keys.privateKey);
+
+// convert a Forge certificate to PEM
+var pem = pki.certificateToPem(cert);
+
+// convert a Forge certificate from PEM
+var cert = pki.certificateFromPem(pem);
+
+// convert an ASN.1 X.509x3 object to a Forge certificate
+var cert = pki.certificateFromAsn1(obj);
+
+// convert a Forge certificate to an ASN.1 X.509v3 object
+var asn1Cert = pki.certificateToAsn1(cert);
+```
+
+<a name="pkcs5" />
+
+### PKCS#5
+
+Provides the password-based key-derivation function from [PKCS#5][].
+
+__Examples__
+
+```js
+// generate a password-based 16-byte key
+// note an optional message digest can be passed as the final parameter
+var salt = forge.random.getBytesSync(128);
+var derivedKey = forge.pkcs5.pbkdf2('password', salt, numIterations, 16);
+
+// generate key asynchronously
+// note an optional message digest can be passed before the callback
+forge.pkcs5.pbkdf2('password', salt, numIterations, 16, function(err, derivedKey) {
+  // do something w/derivedKey
+});
+```
+
+<a name="pkcs7" />
+
+### PKCS#7
+
+Provides cryptographically protected messages from [PKCS#7][].
+
+__Examples__
+
+```js
+// convert a message from PEM
+var p7 = forge.pkcs7.messageFromPem(pem);
+// look at p7.recipients
+
+// find a recipient by the issuer of a certificate
+var recipient = p7.findRecipient(cert);
+
+// decrypt
+p7.decrypt(p7.recipients[0], privateKey);
+
+// create a p7 enveloped message
+var p7 = forge.pkcs7.createEnvelopedData();
+
+// add a recipient
+var cert = forge.pki.certificateFromPem(certPem);
+p7.addRecipient(cert);
+
+// set content
+p7.content = forge.util.createBuffer('Hello');
+
+// encrypt
+p7.encrypt();
+
+// convert message to PEM
+var pem = forge.pkcs7.messageToPem(p7);
+
+// create a degenerate PKCS#7 certificate container
+// (CRLs not currently supported, only certificates)
+var p7 = forge.pkcs7.createSignedData();
+p7.addCertificate(certOrCertPem1);
+p7.addCertificate(certOrCertPem2);
+var pem = forge.pkcs7.messageToPem(p7);
+
+// create PKCS#7 signed data with authenticatedAttributes
+// attributes include: PKCS#9 content-type, message-digest, and signing-time
+var p7 = forge.pkcs7.createSignedData();
+p7.content = forge.util.createBuffer('Some content to be signed.', 'utf8');
+p7.addCertificate(certOrCertPem);
+p7.addSigner({
+  key: privateKeyAssociatedWithCert,
+  certificate: certOrCertPem,
+  digestAlgorithm: forge.pki.oids.sha256,
+  authenticatedAttributes: [{
+    type: forge.pki.oids.contentType,
+    value: forge.pki.oids.data
+  }, {
+    type: forge.pki.oids.messageDigest
+    // value will be auto-populated at signing time
+  }, {
+    type: forge.pki.oids.signingTime,
+    // value can also be auto-populated at signing time
+    value: new Date()
+  }]
+});
+p7.sign();
+var pem = forge.pkcs7.messageToPem(p7);
+
+// PKCS#7 Sign in detached mode.
+// Includes the signature and certificate without the signed data.
+p7.sign({detached: true});
+
+```
+
+<a name="pkcs8" />
+
+### PKCS#8
+
+__Examples__
+
+```js
+var pki = forge.pki;
+
+// convert a PEM-formatted private key to a Forge private key
+var privateKey = pki.privateKeyFromPem(pem);
+
+// convert a Forge private key to PEM-format
+var pem = pki.privateKeyToPem(privateKey);
+
+// convert an ASN.1 PrivateKeyInfo or RSAPrivateKey to a Forge private key
+var privateKey = pki.privateKeyFromAsn1(rsaPrivateKey);
+
+// convert a Forge private key to an ASN.1 RSAPrivateKey
+var rsaPrivateKey = pki.privateKeyToAsn1(privateKey);
+
+// wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
+var privateKeyInfo = pki.wrapRsaPrivateKey(rsaPrivateKey);
+
+// convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
+var pem = pki.privateKeyInfoToPem(privateKeyInfo);
+
+// encrypts a PrivateKeyInfo using a custom password and
+// outputs an EncryptedPrivateKeyInfo
+var encryptedPrivateKeyInfo = pki.encryptPrivateKeyInfo(
+  privateKeyInfo, 'myCustomPasswordHere', {
+    algorithm: 'aes256', // 'aes128', 'aes192', 'aes256', '3des'
+  });
+
+// decrypts an ASN.1 EncryptedPrivateKeyInfo that was encrypted
+// with a custom password
+var privateKeyInfo = pki.decryptPrivateKeyInfo(
+  encryptedPrivateKeyInfo, 'myCustomPasswordHere');
+
+// converts an EncryptedPrivateKeyInfo to PEM
+var pem = pki.encryptedPrivateKeyToPem(encryptedPrivateKeyInfo);
+
+// converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format
+var encryptedPrivateKeyInfo = pki.encryptedPrivateKeyFromPem(pem);
+
+// wraps and encrypts a Forge private key and outputs it in PEM format
+var pem = pki.encryptRsaPrivateKey(privateKey, 'password');
+
+// encrypts a Forge private key and outputs it in PEM format using OpenSSL's
+// proprietary legacy format + encapsulated PEM headers (DEK-Info)
+var pem = pki.encryptRsaPrivateKey(privateKey, 'password', {legacy: true});
+
+// decrypts a PEM-formatted, encrypted private key
+var privateKey = pki.decryptRsaPrivateKey(pem, 'password');
+
+// sets an RSA public key from a private key
+var publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e);
+```
+
+<a name="pkcs10" />
+
+### PKCS#10
+
+Provides certification requests or certificate signing requests (CSR) from
+[PKCS#10][].
+
+__Examples__
+
+```js
+// generate a key pair
+var keys = forge.pki.rsa.generateKeyPair(2048);
+
+// create a certification request (CSR)
+var csr = forge.pki.createCertificationRequest();
+csr.publicKey = keys.publicKey;
+csr.setSubject([{
+  name: 'commonName',
+  value: 'example.org'
+}, {
+  name: 'countryName',
+  value: 'US'
+}, {
+  shortName: 'ST',
+  value: 'Virginia'
+}, {
+  name: 'localityName',
+  value: 'Blacksburg'
+}, {
+  name: 'organizationName',
+  value: 'Test'
+}, {
+  shortName: 'OU',
+  value: 'Test'
+}]);
+// set (optional) attributes
+csr.setAttributes([{
+  name: 'challengePassword',
+  value: 'password'
+}, {
+  name: 'unstructuredName',
+  value: 'My Company, Inc.'
+}, {
+  name: 'extensionRequest',
+  extensions: [{
+    name: 'subjectAltName',
+    altNames: [{
+      // 2 is DNS type
+      type: 2,
+      value: 'test.domain.com'
+    }, {
+      type: 2,
+      value: 'other.domain.com',
+    }, {
+      type: 2,
+      value: 'www.domain.net'
+    }]
+  }]
+}]);
+
+// sign certification request
+csr.sign(keys.privateKey);
+
+// verify certification request
+var verified = csr.verify();
+
+// convert certification request to PEM-format
+var pem = forge.pki.certificationRequestToPem(csr);
+
+// convert a Forge certification request from PEM-format
+var csr = forge.pki.certificationRequestFromPem(pem);
+
+// get an attribute
+csr.getAttribute({name: 'challengePassword'});
+
+// get extensions array
+csr.getAttribute({name: 'extensionRequest'}).extensions;
+
+```
+
+<a name="pkcs12" />
+
+### PKCS#12
+
+Provides the cryptographic archive file format from [PKCS#12][].
+
+**Note for Chrome/Firefox/iOS/similar users**: If you have trouble importing
+a PKCS#12 container, try using the TripleDES algorithm. It can be passed
+to `forge.pkcs12.toPkcs12Asn1` using the `{algorithm: '3des'}` option.
+
+__Examples__
+
+```js
+// decode p12 from base64
+var p12Der = forge.util.decode64(p12b64);
+// get p12 as ASN.1 object
+var p12Asn1 = forge.asn1.fromDer(p12Der);
+// decrypt p12 using the password 'password'
+var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, 'password');
+// decrypt p12 using non-strict parsing mode (resolves some ASN.1 parse errors)
+var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, 'password');
+// decrypt p12 using literally no password (eg: Mac OS X/apple push)
+var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1);
+// decrypt p12 using an "empty" password (eg: OpenSSL with no password input)
+var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, '');
+// p12.safeContents is an array of safe contents, each of
+// which contains an array of safeBags
+
+// get bags by friendlyName
+var bags = p12.getBags({friendlyName: 'test'});
+// bags are key'd by attribute type (here "friendlyName")
+// and the key values are an array of matching objects
+var cert = bags.friendlyName[0];
+
+// get bags by localKeyId
+var bags = p12.getBags({localKeyId: buffer});
+// bags are key'd by attribute type (here "localKeyId")
+// and the key values are an array of matching objects
+var cert = bags.localKeyId[0];
+
+// get bags by localKeyId (input in hex)
+var bags = p12.getBags({localKeyIdHex: '7b59377ff142d0be4565e9ac3d396c01401cd879'});
+// bags are key'd by attribute type (here "localKeyId", *not* "localKeyIdHex")
+// and the key values are an array of matching objects
+var cert = bags.localKeyId[0];
+
+// get bags by type
+var bags = p12.getBags({bagType: forge.pki.oids.certBag});
+// bags are key'd by bagType and each bagType key's value
+// is an array of matches (in this case, certificate objects)
+var cert = bags[forge.pki.oids.certBag][0];
+
+// get bags by friendlyName and filter on bag type
+var bags = p12.getBags({
+  friendlyName: 'test',
+  bagType: forge.pki.oids.certBag
+});
+
+// get key bags
+var bags = p12.getBags({bagType: forge.pki.oids.keyBag});
+// get key
+var bag = bags[forge.pki.oids.keyBag][0];
+var key = bag.key;
+// if the key is in a format unrecognized by forge then
+// bag.key will be `null`, use bag.asn1 to get the ASN.1
+// representation of the key
+if(bag.key === null) {
+  var keyAsn1 = bag.asn1;
+  // can now convert back to DER/PEM/etc for export
+}
+
+// generate a p12 using AES (default)
+var p12Asn1 = forge.pkcs12.toPkcs12Asn1(
+  privateKey, certificateChain, 'password');
+
+// generate a p12 that can be imported by Chrome/Firefox/iOS
+// (requires the use of Triple DES instead of AES)
+var p12Asn1 = forge.pkcs12.toPkcs12Asn1(
+  privateKey, certificateChain, 'password',
+  {algorithm: '3des'});
+
+// base64-encode p12
+var p12Der = forge.asn1.toDer(p12Asn1).getBytes();
+var p12b64 = forge.util.encode64(p12Der);
+
+// create download link for p12
+var a = document.createElement('a');
+a.download = 'example.p12';
+a.setAttribute('href', 'data:application/x-pkcs12;base64,' + p12b64);
+a.appendChild(document.createTextNode('Download'));
+```
+
+<a name="asn" />
+
+### ASN.1
+
+Provides [ASN.1][] DER encoding and decoding.
+
+__Examples__
+
+```js
+var asn1 = forge.asn1;
+
+// create a SubjectPublicKeyInfo
+var subjectPublicKeyInfo =
+  asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // AlgorithmIdentifier
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // algorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(pki.oids['rsaEncryption']).getBytes()),
+      // parameters (null)
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+    ]),
+    // subjectPublicKey
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, [
+      // RSAPublicKey
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // modulus (n)
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+          _bnToBytes(key.n)),
+        // publicExponent (e)
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+          _bnToBytes(key.e))
+      ])
+    ])
+  ]);
+
+// serialize an ASN.1 object to DER format
+var derBuffer = asn1.toDer(subjectPublicKeyInfo);
+
+// deserialize to an ASN.1 object from a byte buffer filled with DER data
+var object = asn1.fromDer(derBuffer);
+
+// convert an OID dot-separated string to a byte buffer
+var derOidBuffer = asn1.oidToDer('1.2.840.113549.1.1.5');
+
+// convert a byte buffer with a DER-encoded OID to a dot-separated string
+console.log(asn1.derToOid(derOidBuffer));
+// output: 1.2.840.113549.1.1.5
+
+// validates that an ASN.1 object matches a particular ASN.1 structure and
+// captures data of interest from that structure for easy access
+var publicKeyValidator = {
+  name: 'SubjectPublicKeyInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  captureAsn1: 'subjectPublicKeyInfo',
+  value: [{
+    name: 'SubjectPublicKeyInfo.AlgorithmIdentifier',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'AlgorithmIdentifier.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'publicKeyOid'
+    }]
+  }, {
+    // subjectPublicKey
+    name: 'SubjectPublicKeyInfo.subjectPublicKey',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.BITSTRING,
+    constructed: false,
+    value: [{
+      // RSAPublicKey
+      name: 'SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      optional: true,
+      captureAsn1: 'rsaPublicKey'
+    }]
+  }]
+};
+
+var capture = {};
+var errors = [];
+if(!asn1.validate(
+  publicKeyValidator, subjectPublicKeyInfo, validator, capture, errors)) {
+  throw 'ASN.1 object is not a SubjectPublicKeyInfo.';
+}
+// capture.subjectPublicKeyInfo contains the full ASN.1 object
+// capture.rsaPublicKey contains the full ASN.1 object for the RSA public key
+// capture.publicKeyOid only contains the value for the OID
+var oid = asn1.derToOid(capture.publicKeyOid);
+if(oid !== pki.oids['rsaEncryption']) {
+  throw 'Unsupported OID.';
+}
+
+// pretty print an ASN.1 object to a string for debugging purposes
+asn1.prettyPrint(object);
+```
+
+Message Digests
+----------------
+
+<a name="sha1" />
+
+### SHA1
+
+Provides [SHA-1][] message digests.
+
+__Examples__
+
+```js
+var md = forge.md.sha1.create();
+md.update('The quick brown fox jumps over the lazy dog');
+console.log(md.digest().toHex());
+// output: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
+```
+
+<a name="sha256" />
+
+### SHA256
+
+Provides [SHA-256][] message digests.
+
+__Examples__
+
+```js
+var md = forge.md.sha256.create();
+md.update('The quick brown fox jumps over the lazy dog');
+console.log(md.digest().toHex());
+// output: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
+```
+
+<a name="sha384" />
+
+### SHA384
+
+Provides [SHA-384][] message digests.
+
+__Examples__
+
+```js
+var md = forge.md.sha384.create();
+md.update('The quick brown fox jumps over the lazy dog');
+console.log(md.digest().toHex());
+// output: ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1
+```
+
+<a name="sha512" />
+
+### SHA512
+
+Provides [SHA-512][] message digests.
+
+__Examples__
+
+```js
+// SHA-512
+var md = forge.md.sha512.create();
+md.update('The quick brown fox jumps over the lazy dog');
+console.log(md.digest().toHex());
+// output: 07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6
+
+// SHA-512/224
+var md = forge.md.sha512.sha224.create();
+md.update('The quick brown fox jumps over the lazy dog');
+console.log(md.digest().toHex());
+// output: 944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37
+
+// SHA-512/256
+var md = forge.md.sha512.sha256.create();
+md.update('The quick brown fox jumps over the lazy dog');
+console.log(md.digest().toHex());
+// output: dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d
+```
+
+<a name="md5" />
+
+### MD5
+
+Provides [MD5][] message digests.
+
+__Examples__
+
+```js
+var md = forge.md.md5.create();
+md.update('The quick brown fox jumps over the lazy dog');
+console.log(md.digest().toHex());
+// output: 9e107d9d372bb6826bd81d3542a419d6
+```
+
+<a name="hmac" />
+
+### HMAC
+
+Provides [HMAC][] w/any supported message digest algorithm.
+
+__Examples__
+
+```js
+var hmac = forge.hmac.create();
+hmac.start('sha1', 'Jefe');
+hmac.update('what do ya want for nothing?');
+console.log(hmac.digest().toHex());
+// output: effcdf6ae5eb2fa2d27416d5f184df9c259a7c79
+```
+
+Utilities
+---------
+
+<a name="prime" />
+
+### Prime
+
+Provides an API for generating large, random, probable primes.
+
+__Examples__
+
+```js
+// generate a random prime on the main JS thread
+var bits = 1024;
+forge.prime.generateProbablePrime(bits, function(err, num) {
+  console.log('random prime', num.toString(16));
+});
+
+// generate a random prime using Web Workers (if available, otherwise
+// falls back to the main thread)
+var bits = 1024;
+var options = {
+  algorithm: {
+    name: 'PRIMEINC',
+    workers: -1 // auto-optimize # of workers
+  }
+};
+forge.prime.generateProbablePrime(bits, options, function(err, num) {
+  console.log('random prime', num.toString(16));
+});
+```
+
+<a name="prng" />
+
+### PRNG
+
+Provides a [Fortuna][]-based cryptographically-secure pseudo-random number
+generator, to be used with a cryptographic function backend, e.g. [AES][]. An
+implementation using [AES][] as a backend is provided. An API for collecting
+entropy is given, though if window.crypto.getRandomValues is available, it will
+be used automatically.
+
+__Examples__
+
+```js
+// get some random bytes synchronously
+var bytes = forge.random.getBytesSync(32);
+console.log(forge.util.bytesToHex(bytes));
+
+// get some random bytes asynchronously
+forge.random.getBytes(32, function(err, bytes) {
+  console.log(forge.util.bytesToHex(bytes));
+});
+
+// collect some entropy if you'd like
+forge.random.collect(someRandomBytes);
+jQuery().mousemove(function(e) {
+  forge.random.collectInt(e.clientX, 16);
+  forge.random.collectInt(e.clientY, 16);
+});
+
+// specify a seed file for use with the synchronous API if you'd like
+forge.random.seedFileSync = function(needed) {
+  // get 'needed' number of random bytes from somewhere
+  return fetchedRandomBytes;
+};
+
+// specify a seed file for use with the asynchronous API if you'd like
+forge.random.seedFile = function(needed, callback) {
+  // get the 'needed' number of random bytes from somewhere
+  callback(null, fetchedRandomBytes);
+});
+
+// register the main thread to send entropy or a Web Worker to receive
+// entropy on demand from the main thread
+forge.random.registerWorker(self);
+
+// generate a new instance of a PRNG with no collected entropy
+var myPrng = forge.random.createInstance();
+```
+
+<a name="task" />
+
+### Tasks
+
+Provides queuing and synchronizing tasks in a web application.
+
+__Examples__
+
+```js
+// TODO
+```
+
+<a name="util" />
+
+### Utilities
+
+Provides utility functions, including byte buffer support, base64,
+bytes to/from hex, zlib inflate/deflate, etc.
+
+__Examples__
+
+```js
+// encode/decode base64
+var encoded = forge.util.encode64(str);
+var str = forge.util.decode64(encoded);
+
+// encode/decode UTF-8
+var encoded = forge.util.encodeUtf8(str);
+var str = forge.util.decodeUtf8(encoded);
+
+// bytes to/from hex
+var bytes = forge.util.hexToBytes(hex);
+var hex = forge.util.bytesToHex(bytes);
+
+// create an empty byte buffer
+var buffer = forge.util.createBuffer();
+// create a byte buffer from raw binary bytes
+var buffer = forge.util.createBuffer(input, 'raw');
+// create a byte buffer from utf8 bytes
+var buffer = forge.util.createBuffer(input, 'utf8');
+
+// get the length of the buffer in bytes
+buffer.length();
+// put bytes into the buffer
+buffer.putBytes(bytes);
+// put a 32-bit integer into the buffer
+buffer.putInt32(10);
+// buffer to hex
+buffer.toHex();
+// get a copy of the bytes in the buffer
+bytes.bytes(/* count */);
+// empty this buffer and get its contents
+bytes.getBytes(/* count */);
+
+// convert a forge buffer into a Node.js Buffer
+// make sure you specify the encoding as 'binary'
+var forgeBuffer = forge.util.createBuffer();
+var nodeBuffer = Buffer.from(forgeBuffer.getBytes(), 'binary');
+
+// convert a Node.js Buffer into a forge buffer
+// make sure you specify the encoding as 'binary'
+var nodeBuffer = Buffer.from('CAFE', 'hex');
+var forgeBuffer = forge.util.createBuffer(nodeBuffer.toString('binary'));
+```
+
+<a name="log" />
+
+### Logging
+
+Provides logging to a javascript console using various categories and
+levels of verbosity.
+
+__Examples__
+
+```js
+// TODO
+```
+
+<a name="flash" />
+
+### Flash Networking Support
+
+The [flash README](./flash/README.md) provides details on rebuilding the
+optional Flash component used for networking. It also provides details on
+Policy Server support.
+
+Security Considerations
+-----------------------
+
+When using this code please keep the following in mind:
+
+- Cryptography is hard. Please review and test this code before depending on it
+  for critical functionality.
+- The nature of JavaScript is that execution of this code depends on trusting a
+  very large set of JavaScript tools and systems. Consider runtime variations,
+  runtime characteristics, runtime optimization, code optimization, code
+  minimization, code obfuscation, bundling tools, possible bugs, the Forge code
+  itself, and so on.
+- If using pre-built bundles from [NPM][], another CDN, or similar, be aware
+  someone else ran the tools to create those files.
+- Use a secure transport channel such as [TLS][] to load scripts and consider
+  using additional security mechanisms such as [Subresource Integrity][] script
+  attributes.
+- Use "native" functionality where possible. This can be critical when dealing
+  with performance and random number generation. Note that the JavaScript
+  random number algorithms should perform well if given suitable entropy.
+- Understand possible attacks against cryptographic systems. For instance side
+  channel and timing attacks may be possible due to the difficulty in
+  implementing constant time algorithms in pure JavaScript.
+- Certain features in this library are less susceptible to attacks depending on
+  usage. This primarily includes features that deal with data format
+  manipulation or those that are not involved in communication.
+
+Library Background
+------------------
+
+* https://digitalbazaar.com/2010/07/20/javascript-tls-1/
+* https://digitalbazaar.com/2010/07/20/javascript-tls-2/
+
+Contact
+-------
+
+* Code: https://github.com/digitalbazaar/forge
+* Bugs: https://github.com/digitalbazaar/forge/issues
+* Email: support@digitalbazaar.com
+* IRC: [#forgejs][] on [Libera.Chat][] (people may also be on [freenode][] for
+  historical reasons).
+
+Donations
+---------
+
+Financial support is welcome and helps contribute to further development:
+
+* For [PayPal][] please send to paypal@digitalbazaar.com.
+* Something else? Please contact support@digitalbazaar.com.
+
+[#forgejs]: https://webchat.freenode.net/?channels=#forgejs
+[0.6.x]: https://github.com/digitalbazaar/forge/tree/0.6.x
+[3DES]: https://en.wikipedia.org/wiki/Triple_DES
+[AES]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+[ASN.1]: https://en.wikipedia.org/wiki/ASN.1
+[Browserify]: http://browserify.org/
+[CBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+[CFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+[CTR]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+[CommonJS]: https://en.wikipedia.org/wiki/CommonJS
+[DES]: https://en.wikipedia.org/wiki/Data_Encryption_Standard
+[ECB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+[Fortuna]: https://en.wikipedia.org/wiki/Fortuna_(PRNG)
+[GCM]: https://en.wikipedia.org/wiki/GCM_mode
+[HMAC]: https://en.wikipedia.org/wiki/HMAC
+[JavaScript]: https://en.wikipedia.org/wiki/JavaScript
+[Karma]: https://karma-runner.github.io/
+[Libera.Chat]: https://libera.chat/
+[MD5]: https://en.wikipedia.org/wiki/MD5
+[NPM]: https://www.npmjs.com/
+[Node.js]: https://nodejs.org/
+[OFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+[PKCS#10]: https://en.wikipedia.org/wiki/Certificate_signing_request
+[PKCS#12]: https://en.wikipedia.org/wiki/PKCS_%E2%99%AF12
+[PKCS#5]: https://en.wikipedia.org/wiki/PKCS
+[PKCS#7]: https://en.wikipedia.org/wiki/Cryptographic_Message_Syntax
+[PayPal]: https://www.paypal.com/
+[RC2]: https://en.wikipedia.org/wiki/RC2
+[SHA-1]: https://en.wikipedia.org/wiki/SHA-1
+[SHA-256]: https://en.wikipedia.org/wiki/SHA-256
+[SHA-384]: https://en.wikipedia.org/wiki/SHA-384
+[SHA-512]: https://en.wikipedia.org/wiki/SHA-512
+[Subresource Integrity]: https://www.w3.org/TR/SRI/
+[TLS]: https://en.wikipedia.org/wiki/Transport_Layer_Security
+[UMD]: https://github.com/umdjs/umd
+[X.509]: https://en.wikipedia.org/wiki/X.509
+[freenode]: https://freenode.net/
+[unpkg]: https://unpkg.com/
+[webpack]: https://webpack.github.io/
+[TweetNaCl.js]: https://github.com/dchest/tweetnacl-js
Index: frontend/node_modules/node-forge/dist/forge.all.min.js
===================================================================
--- frontend/node_modules/node-forge/dist/forge.all.min.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/dist/forge.all.min.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,2 @@
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.forge=t():e.forge=t()}(window,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var a=t[n]={i:n,l:!1,exports:{}};return e[n].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)r.d(n,a,function(t){return e[t]}.bind(null,a));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=34)}([function(e,t){e.exports={options:{usePureJavaScript:!1}}},function(e,t,r){(function(t){var n=r(0),a=r(38),i=e.exports=n.util=n.util||{};function s(e){if(8!==e&&16!==e&&24!==e&&32!==e)throw new Error("Only 8, 16, 24, or 32 bits supported: "+e)}function o(e){if(this.data="",this.read=0,"string"==typeof e)this.data=e;else if(i.isArrayBuffer(e)||i.isArrayBufferView(e))if("undefined"!=typeof Buffer&&e instanceof Buffer)this.data=e.toString("binary");else{var t=new Uint8Array(e);try{this.data=String.fromCharCode.apply(null,t)}catch(e){for(var r=0;r<t.length;++r)this.putByte(t[r])}}else(e instanceof o||"object"==typeof e&&"string"==typeof e.data&&"number"==typeof e.read)&&(this.data=e.data,this.read=e.read);this._constructedStringLength=0}!function(){if("undefined"!=typeof process&&process.nextTick&&!process.browser)return i.nextTick=process.nextTick,void("function"==typeof setImmediate?i.setImmediate=setImmediate:i.setImmediate=i.nextTick);if("function"==typeof setImmediate)return i.setImmediate=function(){return setImmediate.apply(void 0,arguments)},void(i.nextTick=function(e){return setImmediate(e)});if(i.setImmediate=function(e){setTimeout(e,0)},"undefined"!=typeof window&&"function"==typeof window.postMessage){var e="forge.setImmediate",t=[];i.setImmediate=function(r){t.push(r),1===t.length&&window.postMessage(e,"*")},window.addEventListener("message",(function(r){if(r.source===window&&r.data===e){r.stopPropagation();var n=t.slice();t.length=0,n.forEach((function(e){e()}))}}),!0)}if("undefined"!=typeof MutationObserver){var r=Date.now(),n=!0,a=document.createElement("div");t=[];new MutationObserver((function(){var e=t.slice();t.length=0,e.forEach((function(e){e()}))})).observe(a,{attributes:!0});var s=i.setImmediate;i.setImmediate=function(e){Date.now()-r>15?(r=Date.now(),s(e)):(t.push(e),1===t.length&&a.setAttribute("a",n=!n))}}i.nextTick=i.setImmediate}(),i.isNodejs="undefined"!=typeof process&&process.versions&&process.versions.node,i.globalScope=i.isNodejs?t:"undefined"==typeof self?window:self,i.isArray=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i.isArrayBuffer=function(e){return"undefined"!=typeof ArrayBuffer&&e instanceof ArrayBuffer},i.isArrayBufferView=function(e){return e&&i.isArrayBuffer(e.buffer)&&void 0!==e.byteLength},i.ByteBuffer=o,i.ByteStringBuffer=o;i.ByteStringBuffer.prototype._optimizeConstructedString=function(e){this._constructedStringLength+=e,this._constructedStringLength>4096&&(this.data.substr(0,1),this._constructedStringLength=0)},i.ByteStringBuffer.prototype.length=function(){return this.data.length-this.read},i.ByteStringBuffer.prototype.isEmpty=function(){return this.length()<=0},i.ByteStringBuffer.prototype.putByte=function(e){return this.putBytes(String.fromCharCode(e))},i.ByteStringBuffer.prototype.fillWithByte=function(e,t){e=String.fromCharCode(e);for(var r=this.data;t>0;)1&t&&(r+=e),(t>>>=1)>0&&(e+=e);return this.data=r,this._optimizeConstructedString(t),this},i.ByteStringBuffer.prototype.putBytes=function(e){return this.data+=e,this._optimizeConstructedString(e.length),this},i.ByteStringBuffer.prototype.putString=function(e){return this.putBytes(i.encodeUtf8(e))},i.ByteStringBuffer.prototype.putInt16=function(e){return this.putBytes(String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt24=function(e){return this.putBytes(String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt32=function(e){return this.putBytes(String.fromCharCode(e>>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt16Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255))},i.ByteStringBuffer.prototype.putInt24Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255))},i.ByteStringBuffer.prototype.putInt32Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>24&255))},i.ByteStringBuffer.prototype.putInt=function(e,t){s(t);var r="";do{t-=8,r+=String.fromCharCode(e>>t&255)}while(t>0);return this.putBytes(r)},i.ByteStringBuffer.prototype.putSignedInt=function(e,t){return e<0&&(e+=2<<t-1),this.putInt(e,t)},i.ByteStringBuffer.prototype.putBuffer=function(e){return this.putBytes(e.getBytes())},i.ByteStringBuffer.prototype.getByte=function(){return this.data.charCodeAt(this.read++)},i.ByteStringBuffer.prototype.getInt16=function(){var e=this.data.charCodeAt(this.read)<<8^this.data.charCodeAt(this.read+1);return this.read+=2,e},i.ByteStringBuffer.prototype.getInt24=function(){var e=this.data.charCodeAt(this.read)<<16^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2);return this.read+=3,e},i.ByteStringBuffer.prototype.getInt32=function(){var e=this.data.charCodeAt(this.read)<<24^this.data.charCodeAt(this.read+1)<<16^this.data.charCodeAt(this.read+2)<<8^this.data.charCodeAt(this.read+3);return this.read+=4,e},i.ByteStringBuffer.prototype.getInt16Le=function(){var e=this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8;return this.read+=2,e},i.ByteStringBuffer.prototype.getInt24Le=function(){var e=this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2)<<16;return this.read+=3,e},i.ByteStringBuffer.prototype.getInt32Le=function(){var e=this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2)<<16^this.data.charCodeAt(this.read+3)<<24;return this.read+=4,e},i.ByteStringBuffer.prototype.getInt=function(e){s(e);var t=0;do{t=(t<<8)+this.data.charCodeAt(this.read++),e-=8}while(e>0);return t},i.ByteStringBuffer.prototype.getSignedInt=function(e){var t=this.getInt(e),r=2<<e-2;return t>=r&&(t-=r<<1),t},i.ByteStringBuffer.prototype.getBytes=function(e){var t;return e?(e=Math.min(this.length(),e),t=this.data.slice(this.read,this.read+e),this.read+=e):0===e?t="":(t=0===this.read?this.data:this.data.slice(this.read),this.clear()),t},i.ByteStringBuffer.prototype.bytes=function(e){return void 0===e?this.data.slice(this.read):this.data.slice(this.read,this.read+e)},i.ByteStringBuffer.prototype.at=function(e){return this.data.charCodeAt(this.read+e)},i.ByteStringBuffer.prototype.setAt=function(e,t){return this.data=this.data.substr(0,this.read+e)+String.fromCharCode(t)+this.data.substr(this.read+e+1),this},i.ByteStringBuffer.prototype.last=function(){return this.data.charCodeAt(this.data.length-1)},i.ByteStringBuffer.prototype.copy=function(){var e=i.createBuffer(this.data);return e.read=this.read,e},i.ByteStringBuffer.prototype.compact=function(){return this.read>0&&(this.data=this.data.slice(this.read),this.read=0),this},i.ByteStringBuffer.prototype.clear=function(){return this.data="",this.read=0,this},i.ByteStringBuffer.prototype.truncate=function(e){var t=Math.max(0,this.length()-e);return this.data=this.data.substr(this.read,t),this.read=0,this},i.ByteStringBuffer.prototype.toHex=function(){for(var e="",t=this.read;t<this.data.length;++t){var r=this.data.charCodeAt(t);r<16&&(e+="0"),e+=r.toString(16)}return e},i.ByteStringBuffer.prototype.toString=function(){return i.decodeUtf8(this.bytes())},i.DataBuffer=function(e,t){t=t||{},this.read=t.readOffset||0,this.growSize=t.growSize||1024;var r=i.isArrayBuffer(e),n=i.isArrayBufferView(e);if(r||n)return this.data=r?new DataView(e):new DataView(e.buffer,e.byteOffset,e.byteLength),void(this.write="writeOffset"in t?t.writeOffset:this.data.byteLength);this.data=new DataView(new ArrayBuffer(0)),this.write=0,null!=e&&this.putBytes(e),"writeOffset"in t&&(this.write=t.writeOffset)},i.DataBuffer.prototype.length=function(){return this.write-this.read},i.DataBuffer.prototype.isEmpty=function(){return this.length()<=0},i.DataBuffer.prototype.accommodate=function(e,t){if(this.length()>=e)return this;t=Math.max(t||this.growSize,e);var r=new Uint8Array(this.data.buffer,this.data.byteOffset,this.data.byteLength),n=new Uint8Array(this.length()+t);return n.set(r),this.data=new DataView(n.buffer),this},i.DataBuffer.prototype.putByte=function(e){return this.accommodate(1),this.data.setUint8(this.write++,e),this},i.DataBuffer.prototype.fillWithByte=function(e,t){this.accommodate(t);for(var r=0;r<t;++r)this.data.setUint8(e);return this},i.DataBuffer.prototype.putBytes=function(e,t){if(i.isArrayBufferView(e)){var r=(n=new Uint8Array(e.buffer,e.byteOffset,e.byteLength)).byteLength-n.byteOffset;return this.accommodate(r),new Uint8Array(this.data.buffer,this.write).set(n),this.write+=r,this}if(i.isArrayBuffer(e)){var n=new Uint8Array(e);return this.accommodate(n.byteLength),new Uint8Array(this.data.buffer).set(n,this.write),this.write+=n.byteLength,this}if(e instanceof i.DataBuffer||"object"==typeof e&&"number"==typeof e.read&&"number"==typeof e.write&&i.isArrayBufferView(e.data)){n=new Uint8Array(e.data.byteLength,e.read,e.length());return this.accommodate(n.byteLength),new Uint8Array(e.data.byteLength,this.write).set(n),this.write+=n.byteLength,this}if(e instanceof i.ByteStringBuffer&&(e=e.data,t="binary"),t=t||"binary","string"==typeof e){var a;if("hex"===t)return this.accommodate(Math.ceil(e.length/2)),a=new Uint8Array(this.data.buffer,this.write),this.write+=i.binary.hex.decode(e,a,this.write),this;if("base64"===t)return this.accommodate(3*Math.ceil(e.length/4)),a=new Uint8Array(this.data.buffer,this.write),this.write+=i.binary.base64.decode(e,a,this.write),this;if("utf8"===t&&(e=i.encodeUtf8(e),t="binary"),"binary"===t||"raw"===t)return this.accommodate(e.length),a=new Uint8Array(this.data.buffer,this.write),this.write+=i.binary.raw.decode(a),this;if("utf16"===t)return this.accommodate(2*e.length),a=new Uint16Array(this.data.buffer,this.write),this.write+=i.text.utf16.encode(a),this;throw new Error("Invalid encoding: "+t)}throw Error("Invalid parameter: "+e)},i.DataBuffer.prototype.putBuffer=function(e){return this.putBytes(e),e.clear(),this},i.DataBuffer.prototype.putString=function(e){return this.putBytes(e,"utf16")},i.DataBuffer.prototype.putInt16=function(e){return this.accommodate(2),this.data.setInt16(this.write,e),this.write+=2,this},i.DataBuffer.prototype.putInt24=function(e){return this.accommodate(3),this.data.setInt16(this.write,e>>8&65535),this.data.setInt8(this.write,e>>16&255),this.write+=3,this},i.DataBuffer.prototype.putInt32=function(e){return this.accommodate(4),this.data.setInt32(this.write,e),this.write+=4,this},i.DataBuffer.prototype.putInt16Le=function(e){return this.accommodate(2),this.data.setInt16(this.write,e,!0),this.write+=2,this},i.DataBuffer.prototype.putInt24Le=function(e){return this.accommodate(3),this.data.setInt8(this.write,e>>16&255),this.data.setInt16(this.write,e>>8&65535,!0),this.write+=3,this},i.DataBuffer.prototype.putInt32Le=function(e){return this.accommodate(4),this.data.setInt32(this.write,e,!0),this.write+=4,this},i.DataBuffer.prototype.putInt=function(e,t){s(t),this.accommodate(t/8);do{t-=8,this.data.setInt8(this.write++,e>>t&255)}while(t>0);return this},i.DataBuffer.prototype.putSignedInt=function(e,t){return s(t),this.accommodate(t/8),e<0&&(e+=2<<t-1),this.putInt(e,t)},i.DataBuffer.prototype.getByte=function(){return this.data.getInt8(this.read++)},i.DataBuffer.prototype.getInt16=function(){var e=this.data.getInt16(this.read);return this.read+=2,e},i.DataBuffer.prototype.getInt24=function(){var e=this.data.getInt16(this.read)<<8^this.data.getInt8(this.read+2);return this.read+=3,e},i.DataBuffer.prototype.getInt32=function(){var e=this.data.getInt32(this.read);return this.read+=4,e},i.DataBuffer.prototype.getInt16Le=function(){var e=this.data.getInt16(this.read,!0);return this.read+=2,e},i.DataBuffer.prototype.getInt24Le=function(){var e=this.data.getInt8(this.read)^this.data.getInt16(this.read+1,!0)<<8;return this.read+=3,e},i.DataBuffer.prototype.getInt32Le=function(){var e=this.data.getInt32(this.read,!0);return this.read+=4,e},i.DataBuffer.prototype.getInt=function(e){s(e);var t=0;do{t=(t<<8)+this.data.getInt8(this.read++),e-=8}while(e>0);return t},i.DataBuffer.prototype.getSignedInt=function(e){var t=this.getInt(e),r=2<<e-2;return t>=r&&(t-=r<<1),t},i.DataBuffer.prototype.getBytes=function(e){var t;return e?(e=Math.min(this.length(),e),t=this.data.slice(this.read,this.read+e),this.read+=e):0===e?t="":(t=0===this.read?this.data:this.data.slice(this.read),this.clear()),t},i.DataBuffer.prototype.bytes=function(e){return void 0===e?this.data.slice(this.read):this.data.slice(this.read,this.read+e)},i.DataBuffer.prototype.at=function(e){return this.data.getUint8(this.read+e)},i.DataBuffer.prototype.setAt=function(e,t){return this.data.setUint8(e,t),this},i.DataBuffer.prototype.last=function(){return this.data.getUint8(this.write-1)},i.DataBuffer.prototype.copy=function(){return new i.DataBuffer(this)},i.DataBuffer.prototype.compact=function(){if(this.read>0){var e=new Uint8Array(this.data.buffer,this.read),t=new Uint8Array(e.byteLength);t.set(e),this.data=new DataView(t),this.write-=this.read,this.read=0}return this},i.DataBuffer.prototype.clear=function(){return this.data=new DataView(new ArrayBuffer(0)),this.read=this.write=0,this},i.DataBuffer.prototype.truncate=function(e){return this.write=Math.max(0,this.length()-e),this.read=Math.min(this.read,this.write),this},i.DataBuffer.prototype.toHex=function(){for(var e="",t=this.read;t<this.data.byteLength;++t){var r=this.data.getUint8(t);r<16&&(e+="0"),e+=r.toString(16)}return e},i.DataBuffer.prototype.toString=function(e){var t=new Uint8Array(this.data,this.read,this.length());if("binary"===(e=e||"utf8")||"raw"===e)return i.binary.raw.encode(t);if("hex"===e)return i.binary.hex.encode(t);if("base64"===e)return i.binary.base64.encode(t);if("utf8"===e)return i.text.utf8.decode(t);if("utf16"===e)return i.text.utf16.decode(t);throw new Error("Invalid encoding: "+e)},i.createBuffer=function(e,t){return t=t||"raw",void 0!==e&&"utf8"===t&&(e=i.encodeUtf8(e)),new i.ByteBuffer(e)},i.fillString=function(e,t){for(var r="";t>0;)1&t&&(r+=e),(t>>>=1)>0&&(e+=e);return r},i.xorBytes=function(e,t,r){for(var n="",a="",i="",s=0,o=0;r>0;--r,++s)a=e.charCodeAt(s)^t.charCodeAt(s),o>=10&&(n+=i,i="",o=0),i+=String.fromCharCode(a),++o;return n+=i},i.hexToBytes=function(e){var t="",r=0;for(!0&e.length&&(r=1,t+=String.fromCharCode(parseInt(e[0],16)));r<e.length;r+=2)t+=String.fromCharCode(parseInt(e.substr(r,2),16));return t},i.bytesToHex=function(e){return i.createBuffer(e).toHex()},i.int32ToBytes=function(e){return String.fromCharCode(e>>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e)};var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",u=[62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],l="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";i.encode64=function(e,t){for(var r,n,a,i="",s="",o=0;o<e.length;)r=e.charCodeAt(o++),n=e.charCodeAt(o++),a=e.charCodeAt(o++),i+=c.charAt(r>>2),i+=c.charAt((3&r)<<4|n>>4),isNaN(n)?i+="==":(i+=c.charAt((15&n)<<2|a>>6),i+=isNaN(a)?"=":c.charAt(63&a)),t&&i.length>t&&(s+=i.substr(0,t)+"\r\n",i=i.substr(t));return s+=i},i.decode64=function(e){e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var t,r,n,a,i="",s=0;s<e.length;)t=u[e.charCodeAt(s++)-43],r=u[e.charCodeAt(s++)-43],n=u[e.charCodeAt(s++)-43],a=u[e.charCodeAt(s++)-43],i+=String.fromCharCode(t<<2|r>>4),64!==n&&(i+=String.fromCharCode((15&r)<<4|n>>2),64!==a&&(i+=String.fromCharCode((3&n)<<6|a)));return i},i.encodeUtf8=function(e){return unescape(encodeURIComponent(e))},i.decodeUtf8=function(e){return decodeURIComponent(escape(e))},i.binary={raw:{},hex:{},base64:{},base58:{},baseN:{encode:a.encode,decode:a.decode}},i.binary.raw.encode=function(e){return String.fromCharCode.apply(null,e)},i.binary.raw.decode=function(e,t,r){var n=t;n||(n=new Uint8Array(e.length));for(var a=r=r||0,i=0;i<e.length;++i)n[a++]=e.charCodeAt(i);return t?a-r:n},i.binary.hex.encode=i.bytesToHex,i.binary.hex.decode=function(e,t,r){var n=t;n||(n=new Uint8Array(Math.ceil(e.length/2)));var a=0,i=r=r||0;for(1&e.length&&(a=1,n[i++]=parseInt(e[0],16));a<e.length;a+=2)n[i++]=parseInt(e.substr(a,2),16);return t?i-r:n},i.binary.base64.encode=function(e,t){for(var r,n,a,i="",s="",o=0;o<e.byteLength;)r=e[o++],n=e[o++],a=e[o++],i+=c.charAt(r>>2),i+=c.charAt((3&r)<<4|n>>4),isNaN(n)?i+="==":(i+=c.charAt((15&n)<<2|a>>6),i+=isNaN(a)?"=":c.charAt(63&a)),t&&i.length>t&&(s+=i.substr(0,t)+"\r\n",i=i.substr(t));return s+=i},i.binary.base64.decode=function(e,t,r){var n,a,i,s,o=t;o||(o=new Uint8Array(3*Math.ceil(e.length/4))),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var c=0,l=r=r||0;c<e.length;)n=u[e.charCodeAt(c++)-43],a=u[e.charCodeAt(c++)-43],i=u[e.charCodeAt(c++)-43],s=u[e.charCodeAt(c++)-43],o[l++]=n<<2|a>>4,64!==i&&(o[l++]=(15&a)<<4|i>>2,64!==s&&(o[l++]=(3&i)<<6|s));return t?l-r:o.subarray(0,l)},i.binary.base58.encode=function(e,t){return i.binary.baseN.encode(e,l,t)},i.binary.base58.decode=function(e,t){return i.binary.baseN.decode(e,l,t)},i.text={utf8:{},utf16:{}},i.text.utf8.encode=function(e,t,r){e=i.encodeUtf8(e);var n=t;n||(n=new Uint8Array(e.length));for(var a=r=r||0,s=0;s<e.length;++s)n[a++]=e.charCodeAt(s);return t?a-r:n},i.text.utf8.decode=function(e){return i.decodeUtf8(String.fromCharCode.apply(null,e))},i.text.utf16.encode=function(e,t,r){var n=t;n||(n=new Uint8Array(2*e.length));for(var a=new Uint16Array(n.buffer),i=r=r||0,s=r,o=0;o<e.length;++o)a[s++]=e.charCodeAt(o),i+=2;return t?i-r:n},i.text.utf16.decode=function(e){return String.fromCharCode.apply(null,new Uint16Array(e.buffer))},i.deflate=function(e,t,r){if(t=i.decode64(e.deflate(i.encode64(t)).rval),r){var n=2;32&t.charCodeAt(1)&&(n=6),t=t.substring(n,t.length-4)}return t},i.inflate=function(e,t,r){var n=e.inflate(i.encode64(t)).rval;return null===n?null:i.decode64(n)};var p=function(e,t,r){if(!e)throw new Error("WebStorage not available.");var n;if(null===r?n=e.removeItem(t):(r=i.encode64(JSON.stringify(r)),n=e.setItem(t,r)),void 0!==n&&!0!==n.rval){var a=new Error(n.error.message);throw a.id=n.error.id,a.name=n.error.name,a}},f=function(e,t){if(!e)throw new Error("WebStorage not available.");var r=e.getItem(t);if(e.init)if(null===r.rval){if(r.error){var n=new Error(r.error.message);throw n.id=r.error.id,n.name=r.error.name,n}r=null}else r=r.rval;return null!==r&&(r=JSON.parse(i.decode64(r))),r},h=function(e,t,r,n){var a=f(e,t);null===a&&(a={}),a[r]=n,p(e,t,a)},d=function(e,t,r){var n=f(e,t);return null!==n&&(n=r in n?n[r]:null),n},y=function(e,t,r){var n=f(e,t);if(null!==n&&r in n){delete n[r];var a=!0;for(var i in n){a=!1;break}a&&(n=null),p(e,t,n)}},g=function(e,t){p(e,t,null)},v=function(e,t,r){var n,a=null;void 0===r&&(r=["web","flash"]);var i=!1,s=null;for(var o in r){n=r[o];try{if("flash"===n||"both"===n){if(null===t[0])throw new Error("Flash local storage not available.");a=e.apply(this,t),i="flash"===n}"web"!==n&&"both"!==n||(t[0]=localStorage,a=e.apply(this,t),i=!0)}catch(e){s=e}if(i)break}if(!i)throw s;return a};i.setItem=function(e,t,r,n,a){v(h,arguments,a)},i.getItem=function(e,t,r,n){return v(d,arguments,n)},i.removeItem=function(e,t,r,n){v(y,arguments,n)},i.clearItems=function(e,t,r){v(g,arguments,r)},i.isEmpty=function(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0},i.format=function(e){for(var t,r,n=/%./g,a=0,i=[],s=0;t=n.exec(e);){(r=e.substring(s,n.lastIndex-2)).length>0&&i.push(r),s=n.lastIndex;var o=t[0][1];switch(o){case"s":case"o":a<arguments.length?i.push(arguments[1+a++]):i.push("<?>");break;case"%":i.push("%");break;default:i.push("<%"+o+"?>")}}return i.push(e.substring(s)),i.join("")},i.formatNumber=function(e,t,r,n){var a=e,i=isNaN(t=Math.abs(t))?2:t,s=void 0===r?",":r,o=void 0===n?".":n,c=a<0?"-":"",u=parseInt(a=Math.abs(+a||0).toFixed(i),10)+"",l=u.length>3?u.length%3:0;return c+(l?u.substr(0,l)+o:"")+u.substr(l).replace(/(\d{3})(?=\d)/g,"$1"+o)+(i?s+Math.abs(a-u).toFixed(i).slice(2):"")},i.formatSize=function(e){return e=e>=1073741824?i.formatNumber(e/1073741824,2,".","")+" GiB":e>=1048576?i.formatNumber(e/1048576,2,".","")+" MiB":e>=1024?i.formatNumber(e/1024,0)+" KiB":i.formatNumber(e,0)+" bytes"},i.bytesFromIP=function(e){return-1!==e.indexOf(".")?i.bytesFromIPv4(e):-1!==e.indexOf(":")?i.bytesFromIPv6(e):null},i.bytesFromIPv4=function(e){if(4!==(e=e.split(".")).length)return null;for(var t=i.createBuffer(),r=0;r<e.length;++r){var n=parseInt(e[r],10);if(isNaN(n))return null;t.putByte(n)}return t.getBytes()},i.bytesFromIPv6=function(e){for(var t=0,r=2*(8-(e=e.split(":").filter((function(e){return 0===e.length&&++t,!0}))).length+t),n=i.createBuffer(),a=0;a<8;++a)if(e[a]&&0!==e[a].length){var s=i.hexToBytes(e[a]);s.length<2&&n.putByte(0),n.putBytes(s)}else n.fillWithByte(0,r),r=0;return n.getBytes()},i.bytesToIP=function(e){return 4===e.length?i.bytesToIPv4(e):16===e.length?i.bytesToIPv6(e):null},i.bytesToIPv4=function(e){if(4!==e.length)return null;for(var t=[],r=0;r<e.length;++r)t.push(e.charCodeAt(r));return t.join(".")},i.bytesToIPv6=function(e){if(16!==e.length)return null;for(var t=[],r=[],n=0,a=0;a<e.length;a+=2){for(var s=i.bytesToHex(e[a]+e[a+1]);"0"===s[0]&&"0"!==s;)s=s.substr(1);if("0"===s){var o=r[r.length-1],c=t.length;o&&c===o.end+1?(o.end=c,o.end-o.start>r[n].end-r[n].start&&(n=r.length-1)):r.push({start:c,end:c})}t.push(s)}if(r.length>0){var u=r[n];u.end-u.start>0&&(t.splice(u.start,u.end-u.start+1,""),0===u.start&&t.unshift(""),7===u.end&&t.push(""))}return t.join(":")},i.estimateCores=function(e,t){if("function"==typeof e&&(t=e,e={}),e=e||{},"cores"in i&&!e.update)return t(null,i.cores);if("undefined"!=typeof navigator&&"hardwareConcurrency"in navigator&&navigator.hardwareConcurrency>0)return i.cores=navigator.hardwareConcurrency,t(null,i.cores);if("undefined"==typeof Worker)return i.cores=1,t(null,i.cores);if("undefined"==typeof Blob)return i.cores=2,t(null,i.cores);var r=URL.createObjectURL(new Blob(["(",function(){self.addEventListener("message",(function(e){for(var t=Date.now(),r=t+4;Date.now()<r;);self.postMessage({st:t,et:r})}))}.toString(),")()"],{type:"application/javascript"}));!function e(n,a,s){if(0===a){var o=Math.floor(n.reduce((function(e,t){return e+t}),0)/n.length);return i.cores=Math.max(1,o),URL.revokeObjectURL(r),t(null,i.cores)}!function(e,t){for(var n=[],a=[],i=0;i<e;++i){var s=new Worker(r);s.addEventListener("message",(function(r){if(a.push(r.data),a.length===e){for(var i=0;i<e;++i)n[i].terminate();t(null,a)}})),n.push(s)}for(i=0;i<e;++i)n[i].postMessage(i)}(s,(function(t,r){n.push(function(e,t){for(var r=[],n=0;n<e;++n)for(var a=t[n],i=r[n]=[],s=0;s<e;++s)if(n!==s){var o=t[s];(a.st>o.st&&a.st<o.et||o.st>a.st&&o.st<a.et)&&i.push(s)}return r.reduce((function(e,t){return Math.max(e,t.length)}),0)}(s,r)),e(n,a-1,s)}))}([],5,16)}}).call(this,r(37))},function(e,t,r){var n=r(0);r(5),r(23),r(24),r(1),n.random&&n.random.getBytes?e.exports=n.random:function(t){var r={},a=new Array(4),i=n.util.createBuffer();function s(){var e=n.prng.create(r);return e.getBytes=function(t,r){return e.generate(t,r)},e.getBytesSync=function(t){return e.generate(t)},e}r.formatKey=function(e){var t=n.util.createBuffer(e);return(e=new Array(4))[0]=t.getInt32(),e[1]=t.getInt32(),e[2]=t.getInt32(),e[3]=t.getInt32(),n.aes._expandKey(e,!1)},r.formatSeed=function(e){var t=n.util.createBuffer(e);return(e=new Array(4))[0]=t.getInt32(),e[1]=t.getInt32(),e[2]=t.getInt32(),e[3]=t.getInt32(),e},r.cipher=function(e,t){return n.aes._updateBlock(e,t,a,!1),i.putInt32(a[0]),i.putInt32(a[1]),i.putInt32(a[2]),i.putInt32(a[3]),i.getBytes()},r.increment=function(e){return++e[3],e},r.md=n.md.sha256;var o=s(),c=null,u=n.util.globalScope,l=u.crypto||u.msCrypto;if(l&&l.getRandomValues&&(c=function(e){return l.getRandomValues(e)}),n.options.usePureJavaScript||!n.util.isNodejs&&!c){if("undefined"==typeof window||window.document,o.collectInt(+new Date,32),"undefined"!=typeof navigator){var p="";for(var f in navigator)try{"string"==typeof navigator[f]&&(p+=navigator[f])}catch(e){}o.collect(p),p=null}t&&(t().mousemove((function(e){o.collectInt(e.clientX,16),o.collectInt(e.clientY,16)})),t().keypress((function(e){o.collectInt(e.charCode,8)})))}if(n.random)for(var f in o)n.random[f]=o[f];else n.random=o;n.random.createInstance=s,e.exports=n.random}("undefined"!=typeof jQuery?jQuery:null)},function(e,t,r){var n=r(0);r(1),r(6);var a=e.exports=n.asn1=n.asn1||{};function i(e,t,r){if(r>t){var n=new Error("Too few bytes to parse DER.");throw n.available=e.length(),n.remaining=t,n.requested=r,n}}a.Class={UNIVERSAL:0,APPLICATION:64,CONTEXT_SPECIFIC:128,PRIVATE:192},a.Type={NONE:0,BOOLEAN:1,INTEGER:2,BITSTRING:3,OCTETSTRING:4,NULL:5,OID:6,ODESC:7,EXTERNAL:8,REAL:9,ENUMERATED:10,EMBEDDED:11,UTF8:12,ROID:13,SEQUENCE:16,SET:17,PRINTABLESTRING:19,IA5STRING:22,UTCTIME:23,GENERALIZEDTIME:24,BMPSTRING:30},a.maxDepth=256,a.create=function(e,t,r,i,s){if(n.util.isArray(i)){for(var o=[],c=0;c<i.length;++c)void 0!==i[c]&&o.push(i[c]);i=o}var u={tagClass:e,type:t,constructed:r,composed:r||n.util.isArray(i),value:i};return s&&"bitStringContents"in s&&(u.bitStringContents=s.bitStringContents,u.original=a.copy(u)),u},a.copy=function(e,t){var r;if(n.util.isArray(e)){r=[];for(var i=0;i<e.length;++i)r.push(a.copy(e[i],t));return r}return"string"==typeof e?e:(r={tagClass:e.tagClass,type:e.type,constructed:e.constructed,composed:e.composed,value:a.copy(e.value,t)},t&&!t.excludeBitStringContents&&(r.bitStringContents=e.bitStringContents),r)},a.equals=function(e,t,r){if(n.util.isArray(e)){if(!n.util.isArray(t))return!1;if(e.length!==t.length)return!1;for(var i=0;i<e.length;++i)if(!a.equals(e[i],t[i]))return!1;return!0}if(typeof e!=typeof t)return!1;if("string"==typeof e)return e===t;var s=e.tagClass===t.tagClass&&e.type===t.type&&e.constructed===t.constructed&&e.composed===t.composed&&a.equals(e.value,t.value);return r&&r.includeBitStringContents&&(s=s&&e.bitStringContents===t.bitStringContents),s},a.getBerValueLength=function(e){var t=e.getByte();if(128!==t)return 128&t?e.getInt((127&t)<<3):t};a.fromDer=function(e,t){void 0===t&&(t={strict:!0,parseAllBytes:!0,decodeBitStrings:!0}),"boolean"==typeof t&&(t={strict:t,parseAllBytes:!0,decodeBitStrings:!0}),"strict"in t||(t.strict=!0),"parseAllBytes"in t||(t.parseAllBytes=!0),"decodeBitStrings"in t||(t.decodeBitStrings=!0),"maxDepth"in t||(t.maxDepth=a.maxDepth),"string"==typeof e&&(e=n.util.createBuffer(e));var r=e.length(),s=function e(t,r,n,s){if(n>=s.maxDepth)throw new Error("ASN.1 parsing error: Max depth exceeded.");var o;i(t,r,2);var c=t.getByte();r--;var u=192&c,l=31&c;o=t.length();var p,f,h=function(e,t){var r=e.getByte();if(t--,128!==r){var n;if(128&r){var a=127&r;i(e,t,a),n=e.getInt(a<<3)}else n=r;if(n<0)throw new Error("Negative length: "+n);return n}}(t,r);if(r-=o-t.length(),void 0!==h&&h>r){if(s.strict){var d=new Error("Too few bytes to read ASN.1 value.");throw d.available=t.length(),d.remaining=r,d.requested=h,d}h=r}var y=32==(32&c);if(y)if(p=[],void 0===h)for(;;){if(i(t,r,2),t.bytes(2)===String.fromCharCode(0,0)){t.getBytes(2),r-=2;break}o=t.length(),p.push(e(t,r,n+1,s)),r-=o-t.length()}else for(;h>0;)o=t.length(),p.push(e(t,h,n+1,s)),r-=o-t.length(),h-=o-t.length();void 0===p&&u===a.Class.UNIVERSAL&&l===a.Type.BITSTRING&&(f=t.bytes(h));if(void 0===p&&s.decodeBitStrings&&u===a.Class.UNIVERSAL&&l===a.Type.BITSTRING&&h>1){var g=t.read,v=r,m=0;if(l===a.Type.BITSTRING&&(i(t,r,1),m=t.getByte(),r--),0===m)try{o=t.length();var C=e(t,r,n+1,{strict:!0,decodeBitStrings:!0}),E=o-t.length();r-=E,l==a.Type.BITSTRING&&E++;var S=C.tagClass;E!==h||S!==a.Class.UNIVERSAL&&S!==a.Class.CONTEXT_SPECIFIC||(p=[C])}catch(e){}void 0===p&&(t.read=g,r=v)}if(void 0===p){if(void 0===h){if(s.strict)throw new Error("Non-constructed ASN.1 object of indefinite length.");h=r}if(l===a.Type.BMPSTRING)for(p="";h>0;h-=2)i(t,r,2),p+=String.fromCharCode(t.getInt16()),r-=2;else p=t.getBytes(h),r-=h}var T=void 0===f?null:{bitStringContents:f};return a.create(u,l,y,p,T)}(e,e.length(),0,t);if(t.parseAllBytes&&0!==e.length()){var o=new Error("Unparsed DER bytes remain after ASN.1 parsing.");throw o.byteCount=r,o.remaining=e.length(),o}return s},a.toDer=function(e){var t=n.util.createBuffer(),r=e.tagClass|e.type,i=n.util.createBuffer(),s=!1;if("bitStringContents"in e&&(s=!0,e.original&&(s=a.equals(e,e.original))),s)i.putBytes(e.bitStringContents);else if(e.composed){e.constructed?r|=32:i.putByte(0);for(var o=0;o<e.value.length;++o)void 0!==e.value[o]&&i.putBuffer(a.toDer(e.value[o]))}else if(e.type===a.Type.BMPSTRING)for(o=0;o<e.value.length;++o)i.putInt16(e.value.charCodeAt(o));else e.type===a.Type.INTEGER&&e.value.length>1&&(0===e.value.charCodeAt(0)&&0==(128&e.value.charCodeAt(1))||255===e.value.charCodeAt(0)&&128==(128&e.value.charCodeAt(1)))?i.putBytes(e.value.substr(1)):i.putBytes(e.value);if(t.putByte(r),i.length()<=127)t.putByte(127&i.length());else{var c=i.length(),u="";do{u+=String.fromCharCode(255&c),c>>>=8}while(c>0);t.putByte(128|u.length);for(o=u.length-1;o>=0;--o)t.putByte(u.charCodeAt(o))}return t.putBuffer(i),t},a.oidToDer=function(e){var t,r,a,i,s=e.split("."),o=n.util.createBuffer();o.putByte(40*parseInt(s[0],10)+parseInt(s[1],10));for(var c=2;c<s.length;++c){if(t=!0,r=[],(a=parseInt(s[c],10))>4294967295)throw new Error("OID value too large; max is 32-bits.");do{i=127&a,a>>>=7,t||(i|=128),r.push(i),t=!1}while(a>0);for(var u=r.length-1;u>=0;--u)o.putByte(r[u])}return o},a.derToOid=function(e){var t;"string"==typeof e&&(e=n.util.createBuffer(e));var r=e.getByte();t=Math.floor(r/40)+"."+r%40;for(var a=0;e.length()>0;){if(a>70368744177663)throw new Error("OID value too large; max is 53-bits.");a*=128,128&(r=e.getByte())?a+=127&r:(t+="."+(a+r),a=0)}return t},a.utcTimeToDate=function(e){var t=new Date,r=parseInt(e.substr(0,2),10);r=r>=50?1900+r:2e3+r;var n=parseInt(e.substr(2,2),10)-1,a=parseInt(e.substr(4,2),10),i=parseInt(e.substr(6,2),10),s=parseInt(e.substr(8,2),10),o=0;if(e.length>11){var c=e.charAt(10),u=10;"+"!==c&&"-"!==c&&(o=parseInt(e.substr(10,2),10),u+=2)}if(t.setUTCFullYear(r,n,a),t.setUTCHours(i,s,o,0),u&&("+"===(c=e.charAt(u))||"-"===c)){var l=60*parseInt(e.substr(u+1,2),10)+parseInt(e.substr(u+4,2),10);l*=6e4,"+"===c?t.setTime(+t-l):t.setTime(+t+l)}return t},a.generalizedTimeToDate=function(e){var t=new Date,r=parseInt(e.substr(0,4),10),n=parseInt(e.substr(4,2),10)-1,a=parseInt(e.substr(6,2),10),i=parseInt(e.substr(8,2),10),s=parseInt(e.substr(10,2),10),o=parseInt(e.substr(12,2),10),c=0,u=0,l=!1;"Z"===e.charAt(e.length-1)&&(l=!0);var p=e.length-5,f=e.charAt(p);"+"!==f&&"-"!==f||(u=60*parseInt(e.substr(p+1,2),10)+parseInt(e.substr(p+4,2),10),u*=6e4,"+"===f&&(u*=-1),l=!0);return"."===e.charAt(14)&&(c=1e3*parseFloat(e.substr(14),10)),l?(t.setUTCFullYear(r,n,a),t.setUTCHours(i,s,o,c),t.setTime(+t+u)):(t.setFullYear(r,n,a),t.setHours(i,s,o,c)),t},a.dateToUtcTime=function(e){if("string"==typeof e)return e;var t="",r=[];r.push((""+e.getUTCFullYear()).substr(2)),r.push(""+(e.getUTCMonth()+1)),r.push(""+e.getUTCDate()),r.push(""+e.getUTCHours()),r.push(""+e.getUTCMinutes()),r.push(""+e.getUTCSeconds());for(var n=0;n<r.length;++n)r[n].length<2&&(t+="0"),t+=r[n];return t+="Z"},a.dateToGeneralizedTime=function(e){if("string"==typeof e)return e;var t="",r=[];r.push(""+e.getUTCFullYear()),r.push(""+(e.getUTCMonth()+1)),r.push(""+e.getUTCDate()),r.push(""+e.getUTCHours()),r.push(""+e.getUTCMinutes()),r.push(""+e.getUTCSeconds());for(var n=0;n<r.length;++n)r[n].length<2&&(t+="0"),t+=r[n];return t+="Z"},a.integerToDer=function(e){var t=n.util.createBuffer();if(e>=-128&&e<128)return t.putSignedInt(e,8);if(e>=-32768&&e<32768)return t.putSignedInt(e,16);if(e>=-8388608&&e<8388608)return t.putSignedInt(e,24);if(e>=-2147483648&&e<2147483648)return t.putSignedInt(e,32);var r=new Error("Integer too large; max is 32-bits.");throw r.integer=e,r},a.derToInteger=function(e){"string"==typeof e&&(e=n.util.createBuffer(e));var t=8*e.length();if(t>32)throw new Error("Integer too large; max is 32-bits.");return e.getSignedInt(t)},a.validate=function(e,t,r,i){var s=!1;if(e.tagClass!==t.tagClass&&void 0!==t.tagClass||e.type!==t.type&&void 0!==t.type)i&&(e.tagClass!==t.tagClass&&i.push("["+t.name+'] Expected tag class "'+t.tagClass+'", got "'+e.tagClass+'"'),e.type!==t.type&&i.push("["+t.name+'] Expected type "'+t.type+'", got "'+e.type+'"'));else if(e.constructed===t.constructed||void 0===t.constructed){if(s=!0,t.value&&n.util.isArray(t.value))for(var o=0,c=0;s&&c<t.value.length;++c){var u=t.value[c];s=!!u.optional;var l=e.value[o];if(l){if(void 0!==u.tagClass&&void 0!==u.type&&(l.tagClass!==u.tagClass||l.type!==u.type)){if(u.optional){s=!0;continue}s=!1,i&&i.push("["+t.name+"] Tag mismatch. Expected ("+u.tagClass+","+u.type+"), got ("+l.tagClass+","+l.type+")");break}if(a.validate(l,u,r,i))++o,s=!0;else{if(!u.optional){s=!1;break}s=!0}}else u.optional||(s=!1,i&&i.push("["+t.name+'] Missing required element. Expected tag class "'+u.tagClass+'", type "'+u.type+'"'))}if(s&&r)if(t.capture&&(r[t.capture]=e.value),t.captureAsn1&&(r[t.captureAsn1]=e),t.captureBitStringContents&&"bitStringContents"in e&&(r[t.captureBitStringContents]=e.bitStringContents),t.captureBitStringValue&&"bitStringContents"in e)if(e.bitStringContents.length<2)r[t.captureBitStringValue]="";else{if(0!==e.bitStringContents.charCodeAt(0))throw new Error("captureBitStringValue only supported for zero unused bits");r[t.captureBitStringValue]=e.bitStringContents.slice(1)}}else i&&i.push("["+t.name+'] Expected constructed "'+t.constructed+'", got "'+e.constructed+'"');return s};var s=/[^\\u0000-\\u00ff]/;a.prettyPrint=function(e,t,r){var i="";r=r||2,(t=t||0)>0&&(i+="\n");for(var o="",c=0;c<t*r;++c)o+=" ";switch(i+=o+"Tag: ",e.tagClass){case a.Class.UNIVERSAL:i+="Universal:";break;case a.Class.APPLICATION:i+="Application:";break;case a.Class.CONTEXT_SPECIFIC:i+="Context-Specific:";break;case a.Class.PRIVATE:i+="Private:"}if(e.tagClass===a.Class.UNIVERSAL)switch(i+=e.type,e.type){case a.Type.NONE:i+=" (None)";break;case a.Type.BOOLEAN:i+=" (Boolean)";break;case a.Type.INTEGER:i+=" (Integer)";break;case a.Type.BITSTRING:i+=" (Bit string)";break;case a.Type.OCTETSTRING:i+=" (Octet string)";break;case a.Type.NULL:i+=" (Null)";break;case a.Type.OID:i+=" (Object Identifier)";break;case a.Type.ODESC:i+=" (Object Descriptor)";break;case a.Type.EXTERNAL:i+=" (External or Instance of)";break;case a.Type.REAL:i+=" (Real)";break;case a.Type.ENUMERATED:i+=" (Enumerated)";break;case a.Type.EMBEDDED:i+=" (Embedded PDV)";break;case a.Type.UTF8:i+=" (UTF8)";break;case a.Type.ROID:i+=" (Relative Object Identifier)";break;case a.Type.SEQUENCE:i+=" (Sequence)";break;case a.Type.SET:i+=" (Set)";break;case a.Type.PRINTABLESTRING:i+=" (Printable String)";break;case a.Type.IA5String:i+=" (IA5String (ASCII))";break;case a.Type.UTCTIME:i+=" (UTC time)";break;case a.Type.GENERALIZEDTIME:i+=" (Generalized time)";break;case a.Type.BMPSTRING:i+=" (BMP String)"}else i+=e.type;if(i+="\n",i+=o+"Constructed: "+e.constructed+"\n",e.composed){var u=0,l="";for(c=0;c<e.value.length;++c)void 0!==e.value[c]&&(u+=1,l+=a.prettyPrint(e.value[c],t+1,r),c+1<e.value.length&&(l+=","));i+=o+"Sub values: "+u+l}else{if(i+=o+"Value: ",e.type===a.Type.OID){var p=a.derToOid(e.value);i+=p,n.pki&&n.pki.oids&&p in n.pki.oids&&(i+=" ("+n.pki.oids[p]+") ")}if(e.type===a.Type.INTEGER)try{i+=a.derToInteger(e.value)}catch(t){i+="0x"+n.util.bytesToHex(e.value)}else if(e.type===a.Type.BITSTRING){if(e.value.length>1?i+="0x"+n.util.bytesToHex(e.value.slice(1)):i+="(none)",e.value.length>0){var f=e.value.charCodeAt(0);1==f?i+=" (1 unused bit shown)":f>1&&(i+=" ("+f+" unused bits shown)")}}else if(e.type===a.Type.OCTETSTRING)s.test(e.value)||(i+="("+e.value+") "),i+="0x"+n.util.bytesToHex(e.value);else if(e.type===a.Type.UTF8)try{i+=n.util.decodeUtf8(e.value)}catch(t){if("URI malformed"!==t.message)throw t;i+="0x"+n.util.bytesToHex(e.value)+" (malformed UTF8)"}else e.type===a.Type.PRINTABLESTRING||e.type===a.Type.IA5String?i+=e.value:s.test(e.value)?i+="0x"+n.util.bytesToHex(e.value):0===e.value.length?i+="[null]":i+=e.value}return i}},function(e,t,r){var n=r(0);e.exports=n.md=n.md||{},n.md.algorithms=n.md.algorithms||{}},function(e,t,r){var n=r(0);function a(e,t){n.cipher.registerAlgorithm(e,(function(){return new n.aes.Algorithm(e,t)}))}r(14),r(20),r(1),e.exports=n.aes=n.aes||{},n.aes.startEncrypting=function(e,t,r,n){var a=d({key:e,output:r,decrypt:!1,mode:n});return a.start(t),a},n.aes.createEncryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!1,mode:t})},n.aes.startDecrypting=function(e,t,r,n){var a=d({key:e,output:r,decrypt:!0,mode:n});return a.start(t),a},n.aes.createDecryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!0,mode:t})},n.aes.Algorithm=function(e,t){l||p();var r=this;r.name=e,r.mode=new t({blockSize:16,cipher:{encrypt:function(e,t){return h(r._w,e,t,!1)},decrypt:function(e,t){return h(r._w,e,t,!0)}}}),r._init=!1},n.aes.Algorithm.prototype.initialize=function(e){if(!this._init){var t,r=e.key;if("string"!=typeof r||16!==r.length&&24!==r.length&&32!==r.length){if(n.util.isArray(r)&&(16===r.length||24===r.length||32===r.length)){t=r,r=n.util.createBuffer();for(var a=0;a<t.length;++a)r.putByte(t[a])}}else r=n.util.createBuffer(r);if(!n.util.isArray(r)){t=r,r=[];var i=t.length();if(16===i||24===i||32===i){i>>>=2;for(a=0;a<i;++a)r.push(t.getInt32())}}if(!n.util.isArray(r)||4!==r.length&&6!==r.length&&8!==r.length)throw new Error("Invalid key parameter.");var s=this.mode.name,o=-1!==["CFB","OFB","CTR","GCM"].indexOf(s);this._w=f(r,e.decrypt&&!o),this._init=!0}},n.aes._expandKey=function(e,t){return l||p(),f(e,t)},n.aes._updateBlock=h,a("AES-ECB",n.cipher.modes.ecb),a("AES-CBC",n.cipher.modes.cbc),a("AES-CFB",n.cipher.modes.cfb),a("AES-OFB",n.cipher.modes.ofb),a("AES-CTR",n.cipher.modes.ctr),a("AES-GCM",n.cipher.modes.gcm);var i,s,o,c,u,l=!1;function p(){l=!0,o=[0,1,2,4,8,16,32,64,128,27,54];for(var e=new Array(256),t=0;t<128;++t)e[t]=t<<1,e[t+128]=t+128<<1^283;i=new Array(256),s=new Array(256),c=new Array(4),u=new Array(4);for(t=0;t<4;++t)c[t]=new Array(256),u[t]=new Array(256);var r,n,a,p,f,h,d,y=0,g=0;for(t=0;t<256;++t){p=(p=g^g<<1^g<<2^g<<3^g<<4)>>8^255&p^99,i[y]=p,s[p]=y,h=(f=e[p])<<24^p<<16^p<<8^p^f,d=((r=e[y])^(n=e[r])^(a=e[n]))<<24^(y^a)<<16^(y^n^a)<<8^y^r^a;for(var v=0;v<4;++v)c[v][y]=h,u[v][p]=d,h=h<<24|h>>>8,d=d<<24|d>>>8;0===y?y=g=1:(y=r^e[e[e[r^a]]],g^=e[e[g]])}}function f(e,t){for(var r,n=e.slice(0),a=1,s=n.length,c=4*(s+6+1),l=s;l<c;++l)r=n[l-1],l%s==0?(r=i[r>>>16&255]<<24^i[r>>>8&255]<<16^i[255&r]<<8^i[r>>>24]^o[a]<<24,a++):s>6&&l%s==4&&(r=i[r>>>24]<<24^i[r>>>16&255]<<16^i[r>>>8&255]<<8^i[255&r]),n[l]=n[l-s]^r;if(t){for(var p,f=u[0],h=u[1],d=u[2],y=u[3],g=n.slice(0),v=(l=0,(c=n.length)-4);l<c;l+=4,v-=4)if(0===l||l===c-4)g[l]=n[v],g[l+1]=n[v+3],g[l+2]=n[v+2],g[l+3]=n[v+1];else for(var m=0;m<4;++m)p=n[v+m],g[l+(3&-m)]=f[i[p>>>24]]^h[i[p>>>16&255]]^d[i[p>>>8&255]]^y[i[255&p]];n=g}return n}function h(e,t,r,n){var a,o,l,p,f,h,d,y,g,v,m,C,E=e.length/4-1;n?(a=u[0],o=u[1],l=u[2],p=u[3],f=s):(a=c[0],o=c[1],l=c[2],p=c[3],f=i),h=t[0]^e[0],d=t[n?3:1]^e[1],y=t[2]^e[2],g=t[n?1:3]^e[3];for(var S=3,T=1;T<E;++T)v=a[h>>>24]^o[d>>>16&255]^l[y>>>8&255]^p[255&g]^e[++S],m=a[d>>>24]^o[y>>>16&255]^l[g>>>8&255]^p[255&h]^e[++S],C=a[y>>>24]^o[g>>>16&255]^l[h>>>8&255]^p[255&d]^e[++S],g=a[g>>>24]^o[h>>>16&255]^l[d>>>8&255]^p[255&y]^e[++S],h=v,d=m,y=C;r[0]=f[h>>>24]<<24^f[d>>>16&255]<<16^f[y>>>8&255]<<8^f[255&g]^e[++S],r[n?3:1]=f[d>>>24]<<24^f[y>>>16&255]<<16^f[g>>>8&255]<<8^f[255&h]^e[++S],r[2]=f[y>>>24]<<24^f[g>>>16&255]<<16^f[h>>>8&255]<<8^f[255&d]^e[++S],r[n?1:3]=f[g>>>24]<<24^f[h>>>16&255]<<16^f[d>>>8&255]<<8^f[255&y]^e[++S]}function d(e){var t,r="AES-"+((e=e||{}).mode||"CBC").toUpperCase(),a=(t=e.decrypt?n.cipher.createDecipher(r,e.key):n.cipher.createCipher(r,e.key)).start;return t.start=function(e,r){var i=null;r instanceof n.util.ByteBuffer&&(i=r,r={}),(r=r||{}).output=i,r.iv=e,a.call(t,r)},t}},function(e,t,r){var n=r(0);n.pki=n.pki||{};var a=e.exports=n.pki.oids=n.oids=n.oids||{};function i(e,t){a[e]=t,a[t]=e}function s(e,t){a[e]=t}i("1.2.840.113549.1.1.1","rsaEncryption"),i("1.2.840.113549.1.1.4","md5WithRSAEncryption"),i("1.2.840.113549.1.1.5","sha1WithRSAEncryption"),i("1.2.840.113549.1.1.7","RSAES-OAEP"),i("1.2.840.113549.1.1.8","mgf1"),i("1.2.840.113549.1.1.9","pSpecified"),i("1.2.840.113549.1.1.10","RSASSA-PSS"),i("1.2.840.113549.1.1.11","sha256WithRSAEncryption"),i("1.2.840.113549.1.1.12","sha384WithRSAEncryption"),i("1.2.840.113549.1.1.13","sha512WithRSAEncryption"),i("1.3.101.112","EdDSA25519"),i("1.2.840.10040.4.3","dsa-with-sha1"),i("1.3.14.3.2.7","desCBC"),i("1.3.14.3.2.26","sha1"),i("1.3.14.3.2.29","sha1WithRSASignature"),i("2.16.840.1.101.3.4.2.1","sha256"),i("2.16.840.1.101.3.4.2.2","sha384"),i("2.16.840.1.101.3.4.2.3","sha512"),i("2.16.840.1.101.3.4.2.4","sha224"),i("2.16.840.1.101.3.4.2.5","sha512-224"),i("2.16.840.1.101.3.4.2.6","sha512-256"),i("1.2.840.113549.2.2","md2"),i("1.2.840.113549.2.5","md5"),i("1.2.840.113549.1.7.1","data"),i("1.2.840.113549.1.7.2","signedData"),i("1.2.840.113549.1.7.3","envelopedData"),i("1.2.840.113549.1.7.4","signedAndEnvelopedData"),i("1.2.840.113549.1.7.5","digestedData"),i("1.2.840.113549.1.7.6","encryptedData"),i("1.2.840.113549.1.9.1","emailAddress"),i("1.2.840.113549.1.9.2","unstructuredName"),i("1.2.840.113549.1.9.3","contentType"),i("1.2.840.113549.1.9.4","messageDigest"),i("1.2.840.113549.1.9.5","signingTime"),i("1.2.840.113549.1.9.6","counterSignature"),i("1.2.840.113549.1.9.7","challengePassword"),i("1.2.840.113549.1.9.8","unstructuredAddress"),i("1.2.840.113549.1.9.14","extensionRequest"),i("1.2.840.113549.1.9.20","friendlyName"),i("1.2.840.113549.1.9.21","localKeyId"),i("1.2.840.113549.1.9.22.1","x509Certificate"),i("1.2.840.113549.1.12.10.1.1","keyBag"),i("1.2.840.113549.1.12.10.1.2","pkcs8ShroudedKeyBag"),i("1.2.840.113549.1.12.10.1.3","certBag"),i("1.2.840.113549.1.12.10.1.4","crlBag"),i("1.2.840.113549.1.12.10.1.5","secretBag"),i("1.2.840.113549.1.12.10.1.6","safeContentsBag"),i("1.2.840.113549.1.5.13","pkcs5PBES2"),i("1.2.840.113549.1.5.12","pkcs5PBKDF2"),i("1.2.840.113549.1.12.1.1","pbeWithSHAAnd128BitRC4"),i("1.2.840.113549.1.12.1.2","pbeWithSHAAnd40BitRC4"),i("1.2.840.113549.1.12.1.3","pbeWithSHAAnd3-KeyTripleDES-CBC"),i("1.2.840.113549.1.12.1.4","pbeWithSHAAnd2-KeyTripleDES-CBC"),i("1.2.840.113549.1.12.1.5","pbeWithSHAAnd128BitRC2-CBC"),i("1.2.840.113549.1.12.1.6","pbewithSHAAnd40BitRC2-CBC"),i("1.2.840.113549.2.7","hmacWithSHA1"),i("1.2.840.113549.2.8","hmacWithSHA224"),i("1.2.840.113549.2.9","hmacWithSHA256"),i("1.2.840.113549.2.10","hmacWithSHA384"),i("1.2.840.113549.2.11","hmacWithSHA512"),i("1.2.840.113549.3.7","des-EDE3-CBC"),i("2.16.840.1.101.3.4.1.2","aes128-CBC"),i("2.16.840.1.101.3.4.1.22","aes192-CBC"),i("2.16.840.1.101.3.4.1.42","aes256-CBC"),i("2.5.4.3","commonName"),i("2.5.4.4","surname"),i("2.5.4.5","serialNumber"),i("2.5.4.6","countryName"),i("2.5.4.7","localityName"),i("2.5.4.8","stateOrProvinceName"),i("2.5.4.9","streetAddress"),i("2.5.4.10","organizationName"),i("2.5.4.11","organizationalUnitName"),i("2.5.4.12","title"),i("2.5.4.13","description"),i("2.5.4.15","businessCategory"),i("2.5.4.17","postalCode"),i("2.5.4.42","givenName"),i("2.5.4.65","pseudonym"),i("1.3.6.1.4.1.311.60.2.1.2","jurisdictionOfIncorporationStateOrProvinceName"),i("1.3.6.1.4.1.311.60.2.1.3","jurisdictionOfIncorporationCountryName"),i("2.16.840.1.113730.1.1","nsCertType"),i("2.16.840.1.113730.1.13","nsComment"),s("2.5.29.1","authorityKeyIdentifier"),s("2.5.29.2","keyAttributes"),s("2.5.29.3","certificatePolicies"),s("2.5.29.4","keyUsageRestriction"),s("2.5.29.5","policyMapping"),s("2.5.29.6","subtreesConstraint"),s("2.5.29.7","subjectAltName"),s("2.5.29.8","issuerAltName"),s("2.5.29.9","subjectDirectoryAttributes"),s("2.5.29.10","basicConstraints"),s("2.5.29.11","nameConstraints"),s("2.5.29.12","policyConstraints"),s("2.5.29.13","basicConstraints"),i("2.5.29.14","subjectKeyIdentifier"),i("2.5.29.15","keyUsage"),s("2.5.29.16","privateKeyUsagePeriod"),i("2.5.29.17","subjectAltName"),i("2.5.29.18","issuerAltName"),i("2.5.29.19","basicConstraints"),s("2.5.29.20","cRLNumber"),s("2.5.29.21","cRLReason"),s("2.5.29.22","expirationDate"),s("2.5.29.23","instructionCode"),s("2.5.29.24","invalidityDate"),s("2.5.29.25","cRLDistributionPoints"),s("2.5.29.26","issuingDistributionPoint"),s("2.5.29.27","deltaCRLIndicator"),s("2.5.29.28","issuingDistributionPoint"),s("2.5.29.29","certificateIssuer"),s("2.5.29.30","nameConstraints"),i("2.5.29.31","cRLDistributionPoints"),i("2.5.29.32","certificatePolicies"),s("2.5.29.33","policyMappings"),s("2.5.29.34","policyConstraints"),i("2.5.29.35","authorityKeyIdentifier"),s("2.5.29.36","policyConstraints"),i("2.5.29.37","extKeyUsage"),s("2.5.29.46","freshestCRL"),s("2.5.29.54","inhibitAnyPolicy"),i("1.3.6.1.4.1.11129.2.4.2","timestampList"),i("1.3.6.1.5.5.7.1.1","authorityInfoAccess"),i("1.3.6.1.5.5.7.3.1","serverAuth"),i("1.3.6.1.5.5.7.3.2","clientAuth"),i("1.3.6.1.5.5.7.3.3","codeSigning"),i("1.3.6.1.5.5.7.3.4","emailProtection"),i("1.3.6.1.5.5.7.3.8","timeStamping")},function(e,t,r){var n=r(0);r(1);var a=e.exports=n.pem=n.pem||{};function i(e){for(var t=e.name+": ",r=[],n=function(e,t){return" "+t},a=0;a<e.values.length;++a)r.push(e.values[a].replace(/^(\S+\r\n)/,n));t+=r.join(",")+"\r\n";var i=0,s=-1;for(a=0;a<t.length;++a,++i)if(i>65&&-1!==s){var o=t[s];","===o?(++s,t=t.substr(0,s)+"\r\n "+t.substr(s)):t=t.substr(0,s)+"\r\n"+o+t.substr(s+1),i=a-s-1,s=-1,++a}else" "!==t[a]&&"\t"!==t[a]&&","!==t[a]||(s=a);return t}function s(e){return e.replace(/^\s+/,"")}a.encode=function(e,t){t=t||{};var r,a="-----BEGIN "+e.type+"-----\r\n";if(e.procType&&(a+=i(r={name:"Proc-Type",values:[String(e.procType.version),e.procType.type]})),e.contentDomain&&(a+=i(r={name:"Content-Domain",values:[e.contentDomain]})),e.dekInfo&&(r={name:"DEK-Info",values:[e.dekInfo.algorithm]},e.dekInfo.parameters&&r.values.push(e.dekInfo.parameters),a+=i(r)),e.headers)for(var s=0;s<e.headers.length;++s)a+=i(e.headers[s]);return e.procType&&(a+="\r\n"),a+=n.util.encode64(e.body,t.maxline||64)+"\r\n",a+="-----END "+e.type+"-----\r\n"},a.decode=function(e){for(var t,r=[],a=/\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g,i=/([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/,o=/\r?\n/;t=a.exec(e);){var c=t[1];"NEW CERTIFICATE REQUEST"===c&&(c="CERTIFICATE REQUEST");var u={type:c,procType:null,contentDomain:null,dekInfo:null,headers:[],body:n.util.decode64(t[3])};if(r.push(u),t[2]){for(var l=t[2].split(o),p=0;t&&p<l.length;){for(var f=l[p].replace(/\s+$/,""),h=p+1;h<l.length;++h){var d=l[h];if(!/\s/.test(d[0]))break;f+=d,p=h}if(t=f.match(i)){for(var y={name:t[1],values:[]},g=t[2].split(","),v=0;v<g.length;++v)y.values.push(s(g[v]));if(u.procType)if(u.contentDomain||"Content-Domain"!==y.name)if(u.dekInfo||"DEK-Info"!==y.name)u.headers.push(y);else{if(0===y.values.length)throw new Error('Invalid PEM formatted message. The "DEK-Info" header must have at least one subfield.');u.dekInfo={algorithm:g[0],parameters:g[1]||null}}else u.contentDomain=g[0]||"";else{if("Proc-Type"!==y.name)throw new Error('Invalid PEM formatted message. The first encapsulated header must be "Proc-Type".');if(2!==y.values.length)throw new Error('Invalid PEM formatted message. The "Proc-Type" header must have two subfields.');u.procType={version:g[0],type:g[1]}}}++p}if("ENCRYPTED"===u.procType&&!u.dekInfo)throw new Error('Invalid PEM formatted message. The "DEK-Info" header must be present if "Proc-Type" is "ENCRYPTED".')}}if(0===r.length)throw new Error("Invalid PEM formatted message.");return r}},function(e,t,r){var n=r(0);r(4),r(1),(e.exports=n.hmac=n.hmac||{}).create=function(){var e=null,t=null,r=null,a=null,i={start:function(i,s){if(null!==i)if("string"==typeof i){if(!((i=i.toLowerCase())in n.md.algorithms))throw new Error('Unknown hash algorithm "'+i+'"');t=n.md.algorithms[i].create()}else t=i;if(null===s)s=e;else{if("string"==typeof s)s=n.util.createBuffer(s);else if(n.util.isArray(s)){var o=s;s=n.util.createBuffer();for(var c=0;c<o.length;++c)s.putByte(o[c])}var u=s.length();u>t.blockLength&&(t.start(),t.update(s.bytes()),s=t.digest()),r=n.util.createBuffer(),a=n.util.createBuffer(),u=s.length();for(c=0;c<u;++c){o=s.at(c);r.putByte(54^o),a.putByte(92^o)}if(u<t.blockLength)for(o=t.blockLength-u,c=0;c<o;++c)r.putByte(54),a.putByte(92);e=s,r=r.bytes(),a=a.bytes()}t.start(),t.update(r)},update:function(e){t.update(e)},getMac:function(){var e=t.digest().bytes();return t.start(),t.update(a),t.update(e),t.digest()}};return i.digest=i.getMac,i}},function(e,t,r){var n=r(0);r(4),r(1);var a=e.exports=n.sha1=n.sha1||{};n.md.sha1=n.md.algorithms.sha1=a,a.create=function(){s||(i=String.fromCharCode(128),i+=n.util.fillString(String.fromCharCode(0),64),s=!0);var e=null,t=n.util.createBuffer(),r=new Array(80),a={algorithm:"sha1",blockLength:64,digestLength:20,messageLength:0,fullMessageLength:null,messageLengthSize:8,start:function(){a.messageLength=0,a.fullMessageLength=a.messageLength64=[];for(var r=a.messageLengthSize/4,i=0;i<r;++i)a.fullMessageLength.push(0);return t=n.util.createBuffer(),e={h0:1732584193,h1:4023233417,h2:2562383102,h3:271733878,h4:3285377520},a}};return a.start(),a.update=function(i,s){"utf8"===s&&(i=n.util.encodeUtf8(i));var c=i.length;a.messageLength+=c,c=[c/4294967296>>>0,c>>>0];for(var u=a.fullMessageLength.length-1;u>=0;--u)a.fullMessageLength[u]+=c[1],c[1]=c[0]+(a.fullMessageLength[u]/4294967296>>>0),a.fullMessageLength[u]=a.fullMessageLength[u]>>>0,c[0]=c[1]/4294967296>>>0;return t.putBytes(i),o(e,r,t),(t.read>2048||0===t.length())&&t.compact(),a},a.digest=function(){var s=n.util.createBuffer();s.putBytes(t.bytes());var c,u=a.fullMessageLength[a.fullMessageLength.length-1]+a.messageLengthSize&a.blockLength-1;s.putBytes(i.substr(0,a.blockLength-u));for(var l=8*a.fullMessageLength[0],p=0;p<a.fullMessageLength.length-1;++p)l+=(c=8*a.fullMessageLength[p+1])/4294967296>>>0,s.putInt32(l>>>0),l=c>>>0;s.putInt32(l);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3,h4:e.h4};o(f,r,s);var h=n.util.createBuffer();return h.putInt32(f.h0),h.putInt32(f.h1),h.putInt32(f.h2),h.putInt32(f.h3),h.putInt32(f.h4),h},a};var i=null,s=!1;function o(e,t,r){for(var n,a,i,s,o,c,u,l=r.length();l>=64;){for(a=e.h0,i=e.h1,s=e.h2,o=e.h3,c=e.h4,u=0;u<16;++u)n=r.getInt32(),t[u]=n,n=(a<<5|a>>>27)+(o^i&(s^o))+c+1518500249+n,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=a,a=n;for(;u<20;++u)n=(n=t[u-3]^t[u-8]^t[u-14]^t[u-16])<<1|n>>>31,t[u]=n,n=(a<<5|a>>>27)+(o^i&(s^o))+c+1518500249+n,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=a,a=n;for(;u<32;++u)n=(n=t[u-3]^t[u-8]^t[u-14]^t[u-16])<<1|n>>>31,t[u]=n,n=(a<<5|a>>>27)+(i^s^o)+c+1859775393+n,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=a,a=n;for(;u<40;++u)n=(n=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|n>>>30,t[u]=n,n=(a<<5|a>>>27)+(i^s^o)+c+1859775393+n,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=a,a=n;for(;u<60;++u)n=(n=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|n>>>30,t[u]=n,n=(a<<5|a>>>27)+(i&s|o&(i^s))+c+2400959708+n,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=a,a=n;for(;u<80;++u)n=(n=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|n>>>30,t[u]=n,n=(a<<5|a>>>27)+(i^s^o)+c+3395469782+n,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=a,a=n;e.h0=e.h0+a|0,e.h1=e.h1+i|0,e.h2=e.h2+s|0,e.h3=e.h3+o|0,e.h4=e.h4+c|0,l-=64}}},function(e,t,r){var n=r(0);r(3),r(8),r(15),r(7),r(21),r(2),r(9),r(1);var a=function(e,t,r,a){var i=n.util.createBuffer(),s=e.length>>1,o=s+(1&e.length),c=e.substr(0,o),u=e.substr(s,o),l=n.util.createBuffer(),p=n.hmac.create();r=t+r;var f=Math.ceil(a/16),h=Math.ceil(a/20);p.start("MD5",c);var d=n.util.createBuffer();l.putBytes(r);for(var y=0;y<f;++y)p.start(null,null),p.update(l.getBytes()),l.putBuffer(p.digest()),p.start(null,null),p.update(l.bytes()+r),d.putBuffer(p.digest());p.start("SHA1",u);var g=n.util.createBuffer();l.clear(),l.putBytes(r);for(y=0;y<h;++y)p.start(null,null),p.update(l.getBytes()),l.putBuffer(p.digest()),p.start(null,null),p.update(l.bytes()+r),g.putBuffer(p.digest());return i.putBytes(n.util.xorBytes(d.getBytes(),g.getBytes(),a)),i},i=function(e,t,r){var a=!1;try{var i=e.deflate(t.fragment.getBytes());t.fragment=n.util.createBuffer(i),t.length=i.length,a=!0}catch(e){}return a},s=function(e,t,r){var a=!1;try{var i=e.inflate(t.fragment.getBytes());t.fragment=n.util.createBuffer(i),t.length=i.length,a=!0}catch(e){}return a},o=function(e,t){var r=0;switch(t){case 1:r=e.getByte();break;case 2:r=e.getInt16();break;case 3:r=e.getInt24();break;case 4:r=e.getInt32()}return n.util.createBuffer(e.getBytes(r))},c=function(e,t,r){e.putInt(r.length(),t<<3),e.putBuffer(r)},u={Versions:{TLS_1_0:{major:3,minor:1},TLS_1_1:{major:3,minor:2},TLS_1_2:{major:3,minor:3}}};u.SupportedVersions=[u.Versions.TLS_1_1,u.Versions.TLS_1_0],u.Version=u.SupportedVersions[0],u.MaxFragment=15360,u.ConnectionEnd={server:0,client:1},u.PRFAlgorithm={tls_prf_sha256:0},u.BulkCipherAlgorithm={none:null,rc4:0,des3:1,aes:2},u.CipherType={stream:0,block:1,aead:2},u.MACAlgorithm={none:null,hmac_md5:0,hmac_sha1:1,hmac_sha256:2,hmac_sha384:3,hmac_sha512:4},u.CompressionMethod={none:0,deflate:1},u.ContentType={change_cipher_spec:20,alert:21,handshake:22,application_data:23,heartbeat:24},u.HandshakeType={hello_request:0,client_hello:1,server_hello:2,certificate:11,server_key_exchange:12,certificate_request:13,server_hello_done:14,certificate_verify:15,client_key_exchange:16,finished:20},u.Alert={},u.Alert.Level={warning:1,fatal:2},u.Alert.Description={close_notify:0,unexpected_message:10,bad_record_mac:20,decryption_failed:21,record_overflow:22,decompression_failure:30,handshake_failure:40,bad_certificate:42,unsupported_certificate:43,certificate_revoked:44,certificate_expired:45,certificate_unknown:46,illegal_parameter:47,unknown_ca:48,access_denied:49,decode_error:50,decrypt_error:51,export_restriction:60,protocol_version:70,insufficient_security:71,internal_error:80,user_canceled:90,no_renegotiation:100},u.HeartbeatMessageType={heartbeat_request:1,heartbeat_response:2},u.CipherSuites={},u.getCipherSuite=function(e){var t=null;for(var r in u.CipherSuites){var n=u.CipherSuites[r];if(n.id[0]===e.charCodeAt(0)&&n.id[1]===e.charCodeAt(1)){t=n;break}}return t},u.handleUnexpected=function(e,t){!e.open&&e.entity===u.ConnectionEnd.client||e.error(e,{message:"Unexpected message. Received TLS record out of order.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unexpected_message}})},u.handleHelloRequest=function(e,t,r){!e.handshaking&&e.handshakes>0&&(u.queue(e,u.createAlert(e,{level:u.Alert.Level.warning,description:u.Alert.Description.no_renegotiation})),u.flush(e)),e.process()},u.parseHelloMessage=function(e,t,r){var a=null,i=e.entity===u.ConnectionEnd.client;if(r<38)e.error(e,{message:i?"Invalid ServerHello message. Message too short.":"Invalid ClientHello message. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});else{var s=t.fragment,c=s.length();if(a={version:{major:s.getByte(),minor:s.getByte()},random:n.util.createBuffer(s.getBytes(32)),session_id:o(s,1),extensions:[]},i?(a.cipher_suite=s.getBytes(2),a.compression_method=s.getByte()):(a.cipher_suites=o(s,2),a.compression_methods=o(s,1)),(c=r-(c-s.length()))>0){for(var l=o(s,2);l.length()>0;)a.extensions.push({type:[l.getByte(),l.getByte()],data:o(l,2)});if(!i)for(var p=0;p<a.extensions.length;++p){var f=a.extensions[p];if(0===f.type[0]&&0===f.type[1])for(var h=o(f.data,2);h.length()>0;){if(0!==h.getByte())break;e.session.extensions.server_name.serverNameList.push(o(h,2).getBytes())}}}if(e.session.version&&(a.version.major!==e.session.version.major||a.version.minor!==e.session.version.minor))return e.error(e,{message:"TLS version change is disallowed during renegotiation.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}});if(i)e.session.cipherSuite=u.getCipherSuite(a.cipher_suite);else for(var d=n.util.createBuffer(a.cipher_suites.bytes());d.length()>0&&(e.session.cipherSuite=u.getCipherSuite(d.getBytes(2)),null===e.session.cipherSuite););if(null===e.session.cipherSuite)return e.error(e,{message:"No cipher suites in common.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.handshake_failure},cipherSuite:n.util.bytesToHex(a.cipher_suite)});e.session.compressionMethod=i?a.compression_method:u.CompressionMethod.none}return a},u.createSecurityParameters=function(e,t){var r=e.entity===u.ConnectionEnd.client,n=t.random.bytes(),a=r?e.session.sp.client_random:n,i=r?n:u.createRandom().getBytes();e.session.sp={entity:e.entity,prf_algorithm:u.PRFAlgorithm.tls_prf_sha256,bulk_cipher_algorithm:null,cipher_type:null,enc_key_length:null,block_length:null,fixed_iv_length:null,record_iv_length:null,mac_algorithm:null,mac_length:null,mac_key_length:null,compression_algorithm:e.session.compressionMethod,pre_master_secret:null,master_secret:null,client_random:a,server_random:i}},u.handleServerHello=function(e,t,r){var n=u.parseHelloMessage(e,t,r);if(!e.fail){if(!(n.version.minor<=e.version.minor))return e.error(e,{message:"Incompatible TLS version.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}});e.version.minor=n.version.minor,e.session.version=e.version;var a=n.session_id.bytes();a.length>0&&a===e.session.id?(e.expect=d,e.session.resuming=!0,e.session.sp.server_random=n.random.bytes()):(e.expect=l,e.session.resuming=!1,u.createSecurityParameters(e,n)),e.session.id=a,e.process()}},u.handleClientHello=function(e,t,r){var a=u.parseHelloMessage(e,t,r);if(!e.fail){var i=a.session_id.bytes(),s=null;if(e.sessionCache&&(null===(s=e.sessionCache.getSession(i))?i="":(s.version.major!==a.version.major||s.version.minor>a.version.minor)&&(s=null,i="")),0===i.length&&(i=n.random.getBytes(32)),e.session.id=i,e.session.clientHelloVersion=a.version,e.session.sp={},s)e.version=e.session.version=s.version,e.session.sp=s.sp;else{for(var o,c=1;c<u.SupportedVersions.length&&!((o=u.SupportedVersions[c]).minor<=a.version.minor);++c);e.version={major:o.major,minor:o.minor},e.session.version=e.version}null!==s?(e.expect=S,e.session.resuming=!0,e.session.sp.client_random=a.random.bytes()):(e.expect=!1!==e.verifyClient?m:C,e.session.resuming=!1,u.createSecurityParameters(e,a)),e.open=!0,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createServerHello(e)})),e.session.resuming?(u.queue(e,u.createRecord(e,{type:u.ContentType.change_cipher_spec,data:u.createChangeCipherSpec()})),e.state.pending=u.createConnectionState(e),e.state.current.write=e.state.pending.write,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createFinished(e)}))):(u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificate(e)})),e.fail||(u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createServerKeyExchange(e)})),!1!==e.verifyClient&&u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificateRequest(e)})),u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createServerHelloDone(e)})))),u.flush(e),e.process()}},u.handleCertificate=function(e,t,r){if(r<3)return e.error(e,{message:"Invalid Certificate message. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var a,i,s=t.fragment,c={certificate_list:o(s,3)},l=[];try{for(;c.certificate_list.length()>0;)a=o(c.certificate_list,3),i=n.asn1.fromDer(a),a=n.pki.certificateFromAsn1(i,!0),l.push(a)}catch(t){return e.error(e,{message:"Could not parse certificate list.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.bad_certificate}})}var f=e.entity===u.ConnectionEnd.client;!f&&!0!==e.verifyClient||0!==l.length?0===l.length?e.expect=f?p:C:(f?e.session.serverCertificate=l[0]:e.session.clientCertificate=l[0],u.verifyCertificateChain(e,l)&&(e.expect=f?p:C)):e.error(e,{message:f?"No server certificate provided.":"No client certificate provided.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}}),e.process()},u.handleServerKeyExchange=function(e,t,r){if(r>0)return e.error(e,{message:"Invalid key parameters. Only RSA is supported.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unsupported_certificate}});e.expect=f,e.process()},u.handleClientKeyExchange=function(e,t,r){if(r<48)return e.error(e,{message:"Invalid key parameters. Only RSA is supported.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unsupported_certificate}});var a=t.fragment,i={enc_pre_master_secret:o(a,2).getBytes()},s=null;if(e.getPrivateKey)try{s=e.getPrivateKey(e,e.session.serverCertificate),s=n.pki.privateKeyFromPem(s)}catch(t){e.error(e,{message:"Could not get private key.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}})}if(null===s)return e.error(e,{message:"No private key set.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}});try{var c=e.session.sp;c.pre_master_secret=s.decrypt(i.enc_pre_master_secret);var l=e.session.clientHelloVersion;if(l.major!==c.pre_master_secret.charCodeAt(0)||l.minor!==c.pre_master_secret.charCodeAt(1))throw new Error("TLS version rollback attack detected.")}catch(e){c.pre_master_secret=n.random.getBytes(48)}e.expect=S,null!==e.session.clientCertificate&&(e.expect=E),e.process()},u.handleCertificateRequest=function(e,t,r){if(r<3)return e.error(e,{message:"Invalid CertificateRequest. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var n=t.fragment,a={certificate_types:o(n,1),certificate_authorities:o(n,2)};e.session.certificateRequest=a,e.expect=h,e.process()},u.handleCertificateVerify=function(e,t,r){if(r<2)return e.error(e,{message:"Invalid CertificateVerify. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var a=t.fragment;a.read-=4;var i=a.bytes();a.read+=4;var s={signature:o(a,2).getBytes()},c=n.util.createBuffer();c.putBuffer(e.session.md5.digest()),c.putBuffer(e.session.sha1.digest()),c=c.getBytes();try{if(!e.session.clientCertificate.publicKey.verify(c,s.signature,"NONE"))throw new Error("CertificateVerify signature does not match.");e.session.md5.update(i),e.session.sha1.update(i)}catch(t){return e.error(e,{message:"Bad signature in CertificateVerify.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.handshake_failure}})}e.expect=S,e.process()},u.handleServerHelloDone=function(e,t,r){if(r>0)return e.error(e,{message:"Invalid ServerHelloDone message. Invalid length.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.record_overflow}});if(null===e.serverCertificate){var a={message:"No server certificate provided. Not enough security.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.insufficient_security}},i=e.verify(e,a.alert.description,0,[]);if(!0!==i)return(i||0===i)&&("object"!=typeof i||n.util.isArray(i)?"number"==typeof i&&(a.alert.description=i):(i.message&&(a.message=i.message),i.alert&&(a.alert.description=i.alert))),e.error(e,a)}null!==e.session.certificateRequest&&(t=u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificate(e)}),u.queue(e,t)),t=u.createRecord(e,{type:u.ContentType.handshake,data:u.createClientKeyExchange(e)}),u.queue(e,t),e.expect=v;var s=function(e,t){null!==e.session.certificateRequest&&null!==e.session.clientCertificate&&u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificateVerify(e,t)})),u.queue(e,u.createRecord(e,{type:u.ContentType.change_cipher_spec,data:u.createChangeCipherSpec()})),e.state.pending=u.createConnectionState(e),e.state.current.write=e.state.pending.write,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createFinished(e)})),e.expect=d,u.flush(e),e.process()};if(null===e.session.certificateRequest||null===e.session.clientCertificate)return s(e,null);u.getClientSignature(e,s)},u.handleChangeCipherSpec=function(e,t){if(1!==t.fragment.getByte())return e.error(e,{message:"Invalid ChangeCipherSpec message received.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var r=e.entity===u.ConnectionEnd.client;(e.session.resuming&&r||!e.session.resuming&&!r)&&(e.state.pending=u.createConnectionState(e)),e.state.current.read=e.state.pending.read,(!e.session.resuming&&r||e.session.resuming&&!r)&&(e.state.pending=null),e.expect=r?y:T,e.process()},u.handleFinished=function(e,t,r){var i=t.fragment;i.read-=4;var s=i.bytes();i.read+=4;var o=t.fragment.getBytes();(i=n.util.createBuffer()).putBuffer(e.session.md5.digest()),i.putBuffer(e.session.sha1.digest());var c=e.entity===u.ConnectionEnd.client,l=c?"server finished":"client finished",p=e.session.sp;if((i=a(p.master_secret,l,i.getBytes(),12)).getBytes()!==o)return e.error(e,{message:"Invalid verify_data in Finished message.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.decrypt_error}});e.session.md5.update(s),e.session.sha1.update(s),(e.session.resuming&&c||!e.session.resuming&&!c)&&(u.queue(e,u.createRecord(e,{type:u.ContentType.change_cipher_spec,data:u.createChangeCipherSpec()})),e.state.current.write=e.state.pending.write,e.state.pending=null,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createFinished(e)}))),e.expect=c?g:I,e.handshaking=!1,++e.handshakes,e.peerCertificate=c?e.session.serverCertificate:e.session.clientCertificate,u.flush(e),e.isConnected=!0,e.connected(e),e.process()},u.handleAlert=function(e,t){var r,n=t.fragment,a={level:n.getByte(),description:n.getByte()};switch(a.description){case u.Alert.Description.close_notify:r="Connection closed.";break;case u.Alert.Description.unexpected_message:r="Unexpected message.";break;case u.Alert.Description.bad_record_mac:r="Bad record MAC.";break;case u.Alert.Description.decryption_failed:r="Decryption failed.";break;case u.Alert.Description.record_overflow:r="Record overflow.";break;case u.Alert.Description.decompression_failure:r="Decompression failed.";break;case u.Alert.Description.handshake_failure:r="Handshake failure.";break;case u.Alert.Description.bad_certificate:r="Bad certificate.";break;case u.Alert.Description.unsupported_certificate:r="Unsupported certificate.";break;case u.Alert.Description.certificate_revoked:r="Certificate revoked.";break;case u.Alert.Description.certificate_expired:r="Certificate expired.";break;case u.Alert.Description.certificate_unknown:r="Certificate unknown.";break;case u.Alert.Description.illegal_parameter:r="Illegal parameter.";break;case u.Alert.Description.unknown_ca:r="Unknown certificate authority.";break;case u.Alert.Description.access_denied:r="Access denied.";break;case u.Alert.Description.decode_error:r="Decode error.";break;case u.Alert.Description.decrypt_error:r="Decrypt error.";break;case u.Alert.Description.export_restriction:r="Export restriction.";break;case u.Alert.Description.protocol_version:r="Unsupported protocol version.";break;case u.Alert.Description.insufficient_security:r="Insufficient security.";break;case u.Alert.Description.internal_error:r="Internal error.";break;case u.Alert.Description.user_canceled:r="User canceled.";break;case u.Alert.Description.no_renegotiation:r="Renegotiation not supported.";break;default:r="Unknown error."}if(a.description===u.Alert.Description.close_notify)return e.close();e.error(e,{message:r,send:!1,origin:e.entity===u.ConnectionEnd.client?"server":"client",alert:a}),e.process()},u.handleHandshake=function(e,t){var r=t.fragment,a=r.getByte(),i=r.getInt24();if(i>r.length())return e.fragmented=t,t.fragment=n.util.createBuffer(),r.read-=4,e.process();e.fragmented=null,r.read-=4;var s=r.bytes(i+4);r.read+=4,a in x[e.entity][e.expect]?(e.entity!==u.ConnectionEnd.server||e.open||e.fail||(e.handshaking=!0,e.session={version:null,extensions:{server_name:{serverNameList:[]}},cipherSuite:null,compressionMethod:null,serverCertificate:null,clientCertificate:null,md5:n.md.md5.create(),sha1:n.md.sha1.create()}),a!==u.HandshakeType.hello_request&&a!==u.HandshakeType.certificate_verify&&a!==u.HandshakeType.finished&&(e.session.md5.update(s),e.session.sha1.update(s)),x[e.entity][e.expect][a](e,t,i)):u.handleUnexpected(e,t)},u.handleApplicationData=function(e,t){e.data.putBuffer(t.fragment),e.dataReady(e),e.process()},u.handleHeartbeat=function(e,t){var r=t.fragment,a=r.getByte(),i=r.getInt16(),s=r.getBytes(i);if(a===u.HeartbeatMessageType.heartbeat_request){if(e.handshaking||i>s.length)return e.process();u.queue(e,u.createRecord(e,{type:u.ContentType.heartbeat,data:u.createHeartbeat(u.HeartbeatMessageType.heartbeat_response,s)})),u.flush(e)}else if(a===u.HeartbeatMessageType.heartbeat_response){if(s!==e.expectedHeartbeatPayload)return e.process();e.heartbeatReceived&&e.heartbeatReceived(e,n.util.createBuffer(s))}e.process()};var l=1,p=2,f=3,h=4,d=5,y=6,g=7,v=8,m=1,C=2,E=3,S=4,T=5,I=6,b=u.handleUnexpected,A=u.handleChangeCipherSpec,B=u.handleAlert,N=u.handleHandshake,k=u.handleApplicationData,w=u.handleHeartbeat,R=[];R[u.ConnectionEnd.client]=[[b,B,N,b,w],[b,B,N,b,w],[b,B,N,b,w],[b,B,N,b,w],[b,B,N,b,w],[A,B,b,b,w],[b,B,N,b,w],[b,B,N,k,w],[b,B,N,b,w]],R[u.ConnectionEnd.server]=[[b,B,N,b,w],[b,B,N,b,w],[b,B,N,b,w],[b,B,N,b,w],[A,B,b,b,w],[b,B,N,b,w],[b,B,N,k,w],[b,B,N,b,w]];var L=u.handleHelloRequest,_=u.handleServerHello,U=u.handleCertificate,D=u.handleServerKeyExchange,P=u.handleCertificateRequest,V=u.handleServerHelloDone,O=u.handleFinished,x=[];x[u.ConnectionEnd.client]=[[b,b,_,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b],[L,b,b,b,b,b,b,b,b,b,b,U,D,P,V,b,b,b,b,b,b],[L,b,b,b,b,b,b,b,b,b,b,b,D,P,V,b,b,b,b,b,b],[L,b,b,b,b,b,b,b,b,b,b,b,b,P,V,b,b,b,b,b,b],[L,b,b,b,b,b,b,b,b,b,b,b,b,b,V,b,b,b,b,b,b],[L,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b],[L,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,O],[L,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b],[L,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b]];var K=u.handleClientHello,M=u.handleClientKeyExchange,F=u.handleCertificateVerify;x[u.ConnectionEnd.server]=[[b,K,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b],[b,b,b,b,b,b,b,b,b,b,b,U,b,b,b,b,b,b,b,b,b],[b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,M,b,b,b,b],[b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,F,b,b,b,b,b],[b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b],[b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,O],[b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b],[b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b]],u.generateKeys=function(e,t){var r=a,n=t.client_random+t.server_random;e.session.resuming||(t.master_secret=r(t.pre_master_secret,"master secret",n,48).bytes(),t.pre_master_secret=null),n=t.server_random+t.client_random;var i=2*t.mac_key_length+2*t.enc_key_length,s=e.version.major===u.Versions.TLS_1_0.major&&e.version.minor===u.Versions.TLS_1_0.minor;s&&(i+=2*t.fixed_iv_length);var o=r(t.master_secret,"key expansion",n,i),c={client_write_MAC_key:o.getBytes(t.mac_key_length),server_write_MAC_key:o.getBytes(t.mac_key_length),client_write_key:o.getBytes(t.enc_key_length),server_write_key:o.getBytes(t.enc_key_length)};return s&&(c.client_write_IV=o.getBytes(t.fixed_iv_length),c.server_write_IV=o.getBytes(t.fixed_iv_length)),c},u.createConnectionState=function(e){var t=e.entity===u.ConnectionEnd.client,r=function(){var e={sequenceNumber:[0,0],macKey:null,macLength:0,macFunction:null,cipherState:null,cipherFunction:function(e){return!0},compressionState:null,compressFunction:function(e){return!0},updateSequenceNumber:function(){4294967295===e.sequenceNumber[1]?(e.sequenceNumber[1]=0,++e.sequenceNumber[0]):++e.sequenceNumber[1]}};return e},n={read:r(),write:r()};if(n.read.update=function(e,t){return n.read.cipherFunction(t,n.read)?n.read.compressFunction(e,t,n.read)||e.error(e,{message:"Could not decompress record.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.decompression_failure}}):e.error(e,{message:"Could not decrypt record or bad MAC.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.bad_record_mac}}),!e.fail},n.write.update=function(e,t){return n.write.compressFunction(e,t,n.write)?n.write.cipherFunction(t,n.write)||e.error(e,{message:"Could not encrypt record.",send:!1,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}}):e.error(e,{message:"Could not compress record.",send:!1,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}}),!e.fail},e.session){var a=e.session.sp;switch(e.session.cipherSuite.initSecurityParameters(a),a.keys=u.generateKeys(e,a),n.read.macKey=t?a.keys.server_write_MAC_key:a.keys.client_write_MAC_key,n.write.macKey=t?a.keys.client_write_MAC_key:a.keys.server_write_MAC_key,e.session.cipherSuite.initConnectionState(n,e,a),a.compression_algorithm){case u.CompressionMethod.none:break;case u.CompressionMethod.deflate:n.read.compressFunction=s,n.write.compressFunction=i;break;default:throw new Error("Unsupported compression algorithm.")}}return n},u.createRandom=function(){var e=new Date,t=+e+6e4*e.getTimezoneOffset(),r=n.util.createBuffer();return r.putInt32(t),r.putBytes(n.random.getBytes(28)),r},u.createRecord=function(e,t){return t.data?{type:t.type,version:{major:e.version.major,minor:e.version.minor},length:t.data.length(),fragment:t.data}:null},u.createAlert=function(e,t){var r=n.util.createBuffer();return r.putByte(t.level),r.putByte(t.description),u.createRecord(e,{type:u.ContentType.alert,data:r})},u.createClientHello=function(e){e.session.clientHelloVersion={major:e.version.major,minor:e.version.minor};for(var t=n.util.createBuffer(),r=0;r<e.cipherSuites.length;++r){var a=e.cipherSuites[r];t.putByte(a.id[0]),t.putByte(a.id[1])}var i=t.length(),s=n.util.createBuffer();s.putByte(u.CompressionMethod.none);var o=s.length(),l=n.util.createBuffer();if(e.virtualHost){var p=n.util.createBuffer();p.putByte(0),p.putByte(0);var f=n.util.createBuffer();f.putByte(0),c(f,2,n.util.createBuffer(e.virtualHost));var h=n.util.createBuffer();c(h,2,f),c(p,2,h),l.putBuffer(p)}var d=l.length();d>0&&(d+=2);var y=e.session.id,g=y.length+1+2+4+28+2+i+1+o+d,v=n.util.createBuffer();return v.putByte(u.HandshakeType.client_hello),v.putInt24(g),v.putByte(e.version.major),v.putByte(e.version.minor),v.putBytes(e.session.sp.client_random),c(v,1,n.util.createBuffer(y)),c(v,2,t),c(v,1,s),d>0&&c(v,2,l),v},u.createServerHello=function(e){var t=e.session.id,r=t.length+1+2+4+28+2+1,a=n.util.createBuffer();return a.putByte(u.HandshakeType.server_hello),a.putInt24(r),a.putByte(e.version.major),a.putByte(e.version.minor),a.putBytes(e.session.sp.server_random),c(a,1,n.util.createBuffer(t)),a.putByte(e.session.cipherSuite.id[0]),a.putByte(e.session.cipherSuite.id[1]),a.putByte(e.session.compressionMethod),a},u.createCertificate=function(e){var t,r=e.entity===u.ConnectionEnd.client,a=null;e.getCertificate&&(t=r?e.session.certificateRequest:e.session.extensions.server_name.serverNameList,a=e.getCertificate(e,t));var i=n.util.createBuffer();if(null!==a)try{n.util.isArray(a)||(a=[a]);for(var s=null,o=0;o<a.length;++o){var l=n.pem.decode(a[o])[0];if("CERTIFICATE"!==l.type&&"X509 CERTIFICATE"!==l.type&&"TRUSTED CERTIFICATE"!==l.type){var p=new Error('Could not convert certificate from PEM; PEM header type is not "CERTIFICATE", "X509 CERTIFICATE", or "TRUSTED CERTIFICATE".');throw p.headerType=l.type,p}if(l.procType&&"ENCRYPTED"===l.procType.type)throw new Error("Could not convert certificate from PEM; PEM is encrypted.");var f=n.util.createBuffer(l.body);null===s&&(s=n.asn1.fromDer(f.bytes(),!1));var h=n.util.createBuffer();c(h,3,f),i.putBuffer(h)}a=n.pki.certificateFromAsn1(s),r?e.session.clientCertificate=a:e.session.serverCertificate=a}catch(t){return e.error(e,{message:"Could not send certificate list.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.bad_certificate}})}var d=3+i.length(),y=n.util.createBuffer();return y.putByte(u.HandshakeType.certificate),y.putInt24(d),c(y,3,i),y},u.createClientKeyExchange=function(e){var t=n.util.createBuffer();t.putByte(e.session.clientHelloVersion.major),t.putByte(e.session.clientHelloVersion.minor),t.putBytes(n.random.getBytes(46));var r=e.session.sp;r.pre_master_secret=t.getBytes();var a=(t=e.session.serverCertificate.publicKey.encrypt(r.pre_master_secret)).length+2,i=n.util.createBuffer();return i.putByte(u.HandshakeType.client_key_exchange),i.putInt24(a),i.putInt16(t.length),i.putBytes(t),i},u.createServerKeyExchange=function(e){var t=n.util.createBuffer();return t},u.getClientSignature=function(e,t){var r=n.util.createBuffer();r.putBuffer(e.session.md5.digest()),r.putBuffer(e.session.sha1.digest()),r=r.getBytes(),e.getSignature=e.getSignature||function(e,t,r){var a=null;if(e.getPrivateKey)try{a=e.getPrivateKey(e,e.session.clientCertificate),a=n.pki.privateKeyFromPem(a)}catch(t){e.error(e,{message:"Could not get private key.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}})}null===a?e.error(e,{message:"No private key set.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}}):t=a.sign(t,null),r(e,t)},e.getSignature(e,r,t)},u.createCertificateVerify=function(e,t){var r=t.length+2,a=n.util.createBuffer();return a.putByte(u.HandshakeType.certificate_verify),a.putInt24(r),a.putInt16(t.length),a.putBytes(t),a},u.createCertificateRequest=function(e){var t=n.util.createBuffer();t.putByte(1);var r=n.util.createBuffer();for(var a in e.caStore.certs){var i=e.caStore.certs[a],s=n.pki.distinguishedNameToAsn1(i.subject),o=n.asn1.toDer(s);r.putInt16(o.length()),r.putBuffer(o)}var l=1+t.length()+2+r.length(),p=n.util.createBuffer();return p.putByte(u.HandshakeType.certificate_request),p.putInt24(l),c(p,1,t),c(p,2,r),p},u.createServerHelloDone=function(e){var t=n.util.createBuffer();return t.putByte(u.HandshakeType.server_hello_done),t.putInt24(0),t},u.createChangeCipherSpec=function(){var e=n.util.createBuffer();return e.putByte(1),e},u.createFinished=function(e){var t=n.util.createBuffer();t.putBuffer(e.session.md5.digest()),t.putBuffer(e.session.sha1.digest());var r=e.entity===u.ConnectionEnd.client,i=e.session.sp,s=r?"client finished":"server finished";t=a(i.master_secret,s,t.getBytes(),12);var o=n.util.createBuffer();return o.putByte(u.HandshakeType.finished),o.putInt24(t.length()),o.putBuffer(t),o},u.createHeartbeat=function(e,t,r){void 0===r&&(r=t.length);var a=n.util.createBuffer();a.putByte(e),a.putInt16(r),a.putBytes(t);var i=a.length(),s=Math.max(16,i-r-3);return a.putBytes(n.random.getBytes(s)),a},u.queue=function(e,t){if(t&&(0!==t.fragment.length()||t.type!==u.ContentType.handshake&&t.type!==u.ContentType.alert&&t.type!==u.ContentType.change_cipher_spec)){if(t.type===u.ContentType.handshake){var r=t.fragment.bytes();e.session.md5.update(r),e.session.sha1.update(r),r=null}var a;if(t.fragment.length()<=u.MaxFragment)a=[t];else{a=[];for(var i=t.fragment.bytes();i.length>u.MaxFragment;)a.push(u.createRecord(e,{type:t.type,data:n.util.createBuffer(i.slice(0,u.MaxFragment))})),i=i.slice(u.MaxFragment);i.length>0&&a.push(u.createRecord(e,{type:t.type,data:n.util.createBuffer(i)}))}for(var s=0;s<a.length&&!e.fail;++s){var o=a[s];e.state.current.write.update(e,o)&&e.records.push(o)}}},u.flush=function(e){for(var t=0;t<e.records.length;++t){var r=e.records[t];e.tlsData.putByte(r.type),e.tlsData.putByte(r.version.major),e.tlsData.putByte(r.version.minor),e.tlsData.putInt16(r.fragment.length()),e.tlsData.putBuffer(e.records[t].fragment)}return e.records=[],e.tlsDataReady(e)};var q=function(e){switch(e){case!0:return!0;case n.pki.certificateError.bad_certificate:return u.Alert.Description.bad_certificate;case n.pki.certificateError.unsupported_certificate:return u.Alert.Description.unsupported_certificate;case n.pki.certificateError.certificate_revoked:return u.Alert.Description.certificate_revoked;case n.pki.certificateError.certificate_expired:return u.Alert.Description.certificate_expired;case n.pki.certificateError.certificate_unknown:return u.Alert.Description.certificate_unknown;case n.pki.certificateError.unknown_ca:return u.Alert.Description.unknown_ca;default:return u.Alert.Description.bad_certificate}};for(var H in u.verifyCertificateChain=function(e,t){try{var r={};for(var a in e.verifyOptions)r[a]=e.verifyOptions[a];r.verify=function(t,r,a){q(t);var i=e.verify(e,t,r,a);if(!0!==i){if("object"==typeof i&&!n.util.isArray(i)){var s=new Error("The application rejected the certificate.");throw s.send=!0,s.alert={level:u.Alert.Level.fatal,description:u.Alert.Description.bad_certificate},i.message&&(s.message=i.message),i.alert&&(s.alert.description=i.alert),s}i!==t&&(i=function(e){switch(e){case!0:return!0;case u.Alert.Description.bad_certificate:return n.pki.certificateError.bad_certificate;case u.Alert.Description.unsupported_certificate:return n.pki.certificateError.unsupported_certificate;case u.Alert.Description.certificate_revoked:return n.pki.certificateError.certificate_revoked;case u.Alert.Description.certificate_expired:return n.pki.certificateError.certificate_expired;case u.Alert.Description.certificate_unknown:return n.pki.certificateError.certificate_unknown;case u.Alert.Description.unknown_ca:return n.pki.certificateError.unknown_ca;default:return n.pki.certificateError.bad_certificate}}(i))}return i},n.pki.verifyCertificateChain(e.caStore,t,r)}catch(t){var i=t;("object"!=typeof i||n.util.isArray(i))&&(i={send:!0,alert:{level:u.Alert.Level.fatal,description:q(t)}}),"send"in i||(i.send=!0),"alert"in i||(i.alert={level:u.Alert.Level.fatal,description:q(i.error)}),e.error(e,i)}return!e.fail},u.createSessionCache=function(e,t){var r=null;if(e&&e.getSession&&e.setSession&&e.order)r=e;else{for(var a in(r={}).cache=e||{},r.capacity=Math.max(t||100,1),r.order=[],e)r.order.length<=t?r.order.push(a):delete e[a];r.getSession=function(e){var t=null,a=null;if(e?a=n.util.bytesToHex(e):r.order.length>0&&(a=r.order[0]),null!==a&&a in r.cache)for(var i in t=r.cache[a],delete r.cache[a],r.order)if(r.order[i]===a){r.order.splice(i,1);break}return t},r.setSession=function(e,t){if(r.order.length===r.capacity){var a=r.order.shift();delete r.cache[a]}a=n.util.bytesToHex(e);r.order.push(a),r.cache[a]=t}}return r},u.createConnection=function(e){var t=null;t=e.caStore?n.util.isArray(e.caStore)?n.pki.createCaStore(e.caStore):e.caStore:n.pki.createCaStore();var r=e.cipherSuites||null;if(null===r)for(var a in r=[],u.CipherSuites)r.push(u.CipherSuites[a]);var i=e.server?u.ConnectionEnd.server:u.ConnectionEnd.client,s=e.sessionCache?u.createSessionCache(e.sessionCache):null,o={version:{major:u.Version.major,minor:u.Version.minor},entity:i,sessionId:e.sessionId,caStore:t,sessionCache:s,cipherSuites:r,connected:e.connected,virtualHost:e.virtualHost||null,verifyClient:e.verifyClient||!1,verify:e.verify||function(e,t,r,n){return t},verifyOptions:e.verifyOptions||{},getCertificate:e.getCertificate||null,getPrivateKey:e.getPrivateKey||null,getSignature:e.getSignature||null,input:n.util.createBuffer(),tlsData:n.util.createBuffer(),data:n.util.createBuffer(),tlsDataReady:e.tlsDataReady,dataReady:e.dataReady,heartbeatReceived:e.heartbeatReceived,closed:e.closed,error:function(t,r){r.origin=r.origin||(t.entity===u.ConnectionEnd.client?"client":"server"),r.send&&(u.queue(t,u.createAlert(t,r.alert)),u.flush(t));var n=!1!==r.fatal;n&&(t.fail=!0),e.error(t,r),n&&t.close(!1)},deflate:e.deflate||null,inflate:e.inflate||null,reset:function(e){o.version={major:u.Version.major,minor:u.Version.minor},o.record=null,o.session=null,o.peerCertificate=null,o.state={pending:null,current:null},o.expect=(o.entity,u.ConnectionEnd.client,0),o.fragmented=null,o.records=[],o.open=!1,o.handshakes=0,o.handshaking=!1,o.isConnected=!1,o.fail=!(e||void 0===e),o.input.clear(),o.tlsData.clear(),o.data.clear(),o.state.current=u.createConnectionState(o)}};o.reset();return o.handshake=function(e){if(o.entity!==u.ConnectionEnd.client)o.error(o,{message:"Cannot initiate handshake as a server.",fatal:!1});else if(o.handshaking)o.error(o,{message:"Handshake already in progress.",fatal:!1});else{o.fail&&!o.open&&0===o.handshakes&&(o.fail=!1),o.handshaking=!0;var t=null;(e=e||"").length>0&&(o.sessionCache&&(t=o.sessionCache.getSession(e)),null===t&&(e="")),0===e.length&&o.sessionCache&&null!==(t=o.sessionCache.getSession())&&(e=t.id),o.session={id:e,version:null,cipherSuite:null,compressionMethod:null,serverCertificate:null,certificateRequest:null,clientCertificate:null,sp:{},md5:n.md.md5.create(),sha1:n.md.sha1.create()},t&&(o.version=t.version,o.session.sp=t.sp),o.session.sp.client_random=u.createRandom().getBytes(),o.open=!0,u.queue(o,u.createRecord(o,{type:u.ContentType.handshake,data:u.createClientHello(o)})),u.flush(o)}},o.process=function(e){var t=0;return e&&o.input.putBytes(e),o.fail||(null!==o.record&&o.record.ready&&o.record.fragment.isEmpty()&&(o.record=null),null===o.record&&(t=function(e){var t=0,r=e.input,a=r.length();if(a<5)t=5-a;else{e.record={type:r.getByte(),version:{major:r.getByte(),minor:r.getByte()},length:r.getInt16(),fragment:n.util.createBuffer(),ready:!1};var i=e.record.version.major===e.version.major;i&&e.session&&e.session.version&&(i=e.record.version.minor===e.version.minor),i||e.error(e,{message:"Incompatible TLS version.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}})}return t}(o)),o.fail||null===o.record||o.record.ready||(t=function(e){var t=0,r=e.input,n=r.length();n<e.record.length?t=e.record.length-n:(e.record.fragment.putBytes(r.getBytes(e.record.length)),r.compact(),e.state.current.read.update(e,e.record)&&(null!==e.fragmented&&(e.fragmented.type===e.record.type?(e.fragmented.fragment.putBuffer(e.record.fragment),e.record=e.fragmented):e.error(e,{message:"Invalid fragmented record.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unexpected_message}})),e.record.ready=!0));return t}(o)),!o.fail&&null!==o.record&&o.record.ready&&function(e,t){var r=t.type-u.ContentType.change_cipher_spec,n=R[e.entity][e.expect];r in n?n[r](e,t):u.handleUnexpected(e,t)}(o,o.record)),t},o.prepare=function(e){return u.queue(o,u.createRecord(o,{type:u.ContentType.application_data,data:n.util.createBuffer(e)})),u.flush(o)},o.prepareHeartbeatRequest=function(e,t){return e instanceof n.util.ByteBuffer&&(e=e.bytes()),void 0===t&&(t=e.length),o.expectedHeartbeatPayload=e,u.queue(o,u.createRecord(o,{type:u.ContentType.heartbeat,data:u.createHeartbeat(u.HeartbeatMessageType.heartbeat_request,e,t)})),u.flush(o)},o.close=function(e){if(!o.fail&&o.sessionCache&&o.session){var t={id:o.session.id,version:o.session.version,sp:o.session.sp};t.sp.keys=null,o.sessionCache.setSession(t.id,t)}o.open&&(o.open=!1,o.input.clear(),(o.isConnected||o.handshaking)&&(o.isConnected=o.handshaking=!1,u.queue(o,u.createAlert(o,{level:u.Alert.Level.warning,description:u.Alert.Description.close_notify})),u.flush(o)),o.closed(o)),o.reset(e)},o},e.exports=n.tls=n.tls||{},u)"function"!=typeof u[H]&&(n.tls[H]=u[H]);n.tls.prf_tls1=a,n.tls.hmac_sha1=function(e,t,r){var a=n.hmac.create();a.start("SHA1",e);var i=n.util.createBuffer();return i.putInt32(t[0]),i.putInt32(t[1]),i.putByte(r.type),i.putByte(r.version.major),i.putByte(r.version.minor),i.putInt16(r.length),i.putBytes(r.fragment.bytes()),a.update(i.getBytes()),a.digest().getBytes()},n.tls.createSessionCache=u.createSessionCache,n.tls.createConnection=u.createConnection},function(e,t,r){var n=r(0);function a(e,t){n.cipher.registerAlgorithm(e,(function(){return new n.des.Algorithm(e,t)}))}r(14),r(20),r(1),e.exports=n.des=n.des||{},n.des.startEncrypting=function(e,t,r,n){var a=d({key:e,output:r,decrypt:!1,mode:n||(null===t?"ECB":"CBC")});return a.start(t),a},n.des.createEncryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!1,mode:t})},n.des.startDecrypting=function(e,t,r,n){var a=d({key:e,output:r,decrypt:!0,mode:n||(null===t?"ECB":"CBC")});return a.start(t),a},n.des.createDecryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!0,mode:t})},n.des.Algorithm=function(e,t){var r=this;r.name=e,r.mode=new t({blockSize:8,cipher:{encrypt:function(e,t){return h(r._keys,e,t,!1)},decrypt:function(e,t){return h(r._keys,e,t,!0)}}}),r._init=!1},n.des.Algorithm.prototype.initialize=function(e){if(!this._init){var t=n.util.createBuffer(e.key);if(0===this.name.indexOf("3DES")&&24!==t.length())throw new Error("Invalid Triple-DES key size: "+8*t.length());this._keys=function(e){for(var t,r=[0,4,536870912,536870916,65536,65540,536936448,536936452,512,516,536871424,536871428,66048,66052,536936960,536936964],n=[0,1,1048576,1048577,67108864,67108865,68157440,68157441,256,257,1048832,1048833,67109120,67109121,68157696,68157697],a=[0,8,2048,2056,16777216,16777224,16779264,16779272,0,8,2048,2056,16777216,16777224,16779264,16779272],i=[0,2097152,134217728,136314880,8192,2105344,134225920,136323072,131072,2228224,134348800,136445952,139264,2236416,134356992,136454144],s=[0,262144,16,262160,0,262144,16,262160,4096,266240,4112,266256,4096,266240,4112,266256],o=[0,1024,32,1056,0,1024,32,1056,33554432,33555456,33554464,33555488,33554432,33555456,33554464,33555488],c=[0,268435456,524288,268959744,2,268435458,524290,268959746,0,268435456,524288,268959744,2,268435458,524290,268959746],u=[0,65536,2048,67584,536870912,536936448,536872960,536938496,131072,196608,133120,198656,537001984,537067520,537004032,537069568],l=[0,262144,0,262144,2,262146,2,262146,33554432,33816576,33554432,33816576,33554434,33816578,33554434,33816578],p=[0,268435456,8,268435464,0,268435456,8,268435464,1024,268436480,1032,268436488,1024,268436480,1032,268436488],f=[0,32,0,32,1048576,1048608,1048576,1048608,8192,8224,8192,8224,1056768,1056800,1056768,1056800],h=[0,16777216,512,16777728,2097152,18874368,2097664,18874880,67108864,83886080,67109376,83886592,69206016,85983232,69206528,85983744],d=[0,4096,134217728,134221824,524288,528384,134742016,134746112,16,4112,134217744,134221840,524304,528400,134742032,134746128],y=[0,4,256,260,0,4,256,260,1,5,257,261,1,5,257,261],g=e.length()>8?3:1,v=[],m=[0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0],C=0,E=0;E<g;E++){var S=e.getInt32(),T=e.getInt32();S^=(t=252645135&(S>>>4^T))<<4,S^=t=65535&((T^=t)>>>-16^S),S^=(t=858993459&(S>>>2^(T^=t<<-16)))<<2,S^=t=65535&((T^=t)>>>-16^S),S^=(t=1431655765&(S>>>1^(T^=t<<-16)))<<1,S^=t=16711935&((T^=t)>>>8^S),t=(S^=(t=1431655765&(S>>>1^(T^=t<<8)))<<1)<<8|(T^=t)>>>20&240,S=T<<24|T<<8&16711680|T>>>8&65280|T>>>24&240,T=t;for(var I=0;I<m.length;++I){m[I]?(S=S<<2|S>>>26,T=T<<2|T>>>26):(S=S<<1|S>>>27,T=T<<1|T>>>27);var b=r[(S&=-15)>>>28]|n[S>>>24&15]|a[S>>>20&15]|i[S>>>16&15]|s[S>>>12&15]|o[S>>>8&15]|c[S>>>4&15],A=u[(T&=-15)>>>28]|l[T>>>24&15]|p[T>>>20&15]|f[T>>>16&15]|h[T>>>12&15]|d[T>>>8&15]|y[T>>>4&15];t=65535&(A>>>16^b),v[C++]=b^t,v[C++]=A^t<<16}}return v}(t),this._init=!0}},a("DES-ECB",n.cipher.modes.ecb),a("DES-CBC",n.cipher.modes.cbc),a("DES-CFB",n.cipher.modes.cfb),a("DES-OFB",n.cipher.modes.ofb),a("DES-CTR",n.cipher.modes.ctr),a("3DES-ECB",n.cipher.modes.ecb),a("3DES-CBC",n.cipher.modes.cbc),a("3DES-CFB",n.cipher.modes.cfb),a("3DES-OFB",n.cipher.modes.ofb),a("3DES-CTR",n.cipher.modes.ctr);var i=[16843776,0,65536,16843780,16842756,66564,4,65536,1024,16843776,16843780,1024,16778244,16842756,16777216,4,1028,16778240,16778240,66560,66560,16842752,16842752,16778244,65540,16777220,16777220,65540,0,1028,66564,16777216,65536,16843780,4,16842752,16843776,16777216,16777216,1024,16842756,65536,66560,16777220,1024,4,16778244,66564,16843780,65540,16842752,16778244,16777220,1028,66564,16843776,1028,16778240,16778240,0,65540,66560,0,16842756],s=[-2146402272,-2147450880,32768,1081376,1048576,32,-2146435040,-2147450848,-2147483616,-2146402272,-2146402304,-2147483648,-2147450880,1048576,32,-2146435040,1081344,1048608,-2147450848,0,-2147483648,32768,1081376,-2146435072,1048608,-2147483616,0,1081344,32800,-2146402304,-2146435072,32800,0,1081376,-2146435040,1048576,-2147450848,-2146435072,-2146402304,32768,-2146435072,-2147450880,32,-2146402272,1081376,32,32768,-2147483648,32800,-2146402304,1048576,-2147483616,1048608,-2147450848,-2147483616,1048608,1081344,0,-2147450880,32800,-2147483648,-2146435040,-2146402272,1081344],o=[520,134349312,0,134348808,134218240,0,131592,134218240,131080,134217736,134217736,131072,134349320,131080,134348800,520,134217728,8,134349312,512,131584,134348800,134348808,131592,134218248,131584,131072,134218248,8,134349320,512,134217728,134349312,134217728,131080,520,131072,134349312,134218240,0,512,131080,134349320,134218240,134217736,512,0,134348808,134218248,131072,134217728,134349320,8,131592,131584,134217736,134348800,134218248,520,134348800,131592,8,134348808,131584],c=[8396801,8321,8321,128,8396928,8388737,8388609,8193,0,8396800,8396800,8396929,129,0,8388736,8388609,1,8192,8388608,8396801,128,8388608,8193,8320,8388737,1,8320,8388736,8192,8396928,8396929,129,8388736,8388609,8396800,8396929,129,0,0,8396800,8320,8388736,8388737,1,8396801,8321,8321,128,8396929,129,1,8192,8388609,8193,8396928,8388737,8193,8320,8388608,8396801,128,8388608,8192,8396928],u=[256,34078976,34078720,1107296512,524288,256,1073741824,34078720,1074266368,524288,33554688,1074266368,1107296512,1107820544,524544,1073741824,33554432,1074266112,1074266112,0,1073742080,1107820800,1107820800,33554688,1107820544,1073742080,0,1107296256,34078976,33554432,1107296256,524544,524288,1107296512,256,33554432,1073741824,34078720,1107296512,1074266368,33554688,1073741824,1107820544,34078976,1074266368,256,33554432,1107820544,1107820800,524544,1107296256,1107820800,34078720,0,1074266112,1107296256,524544,33554688,1073742080,524288,0,1074266112,34078976,1073742080],l=[536870928,541065216,16384,541081616,541065216,16,541081616,4194304,536887296,4210704,4194304,536870928,4194320,536887296,536870912,16400,0,4194320,536887312,16384,4210688,536887312,16,541065232,541065232,0,4210704,541081600,16400,4210688,541081600,536870912,536887296,16,541065232,4210688,541081616,4194304,16400,536870928,4194304,536887296,536870912,16400,536870928,541081616,4210688,541065216,4210704,541081600,0,541065232,16,16384,541065216,4210704,16384,4194320,536887312,0,541081600,536870912,4194320,536887312],p=[2097152,69206018,67110914,0,2048,67110914,2099202,69208064,69208066,2097152,0,67108866,2,67108864,69206018,2050,67110912,2099202,2097154,67110912,67108866,69206016,69208064,2097154,69206016,2048,2050,69208066,2099200,2,67108864,2099200,67108864,2099200,2097152,67110914,67110914,69206018,69206018,2,2097154,67108864,67110912,2097152,69208064,2050,2099202,69208064,2050,67108866,69208066,69206016,2099200,0,2,69208066,0,2099202,69206016,2048,67108866,67110912,2048,2097154],f=[268439616,4096,262144,268701760,268435456,268439616,64,268435456,262208,268697600,268701760,266240,268701696,266304,4096,64,268697600,268435520,268439552,4160,266240,262208,268697664,268701696,4160,0,0,268697664,268435520,268439552,266304,262144,266304,262144,268701696,4096,64,268697664,4096,266304,268439552,64,268435520,268697600,268697664,268435456,262144,268439616,0,268701760,262208,268435520,268697600,268439552,268439616,0,268701760,266240,266240,4160,4160,262208,268435456,268701696];function h(e,t,r,n){var a,h,d=32===e.length?3:9;a=3===d?n?[30,-2,-2]:[0,32,2]:n?[94,62,-2,32,64,2,30,-2,-2]:[0,32,2,62,30,-2,64,96,2];var y=t[0],g=t[1];y^=(h=252645135&(y>>>4^g))<<4,y^=(h=65535&(y>>>16^(g^=h)))<<16,y^=h=858993459&((g^=h)>>>2^y),y^=h=16711935&((g^=h<<2)>>>8^y),y=(y^=(h=1431655765&(y>>>1^(g^=h<<8)))<<1)<<1|y>>>31,g=(g^=h)<<1|g>>>31;for(var v=0;v<d;v+=3){for(var m=a[v+1],C=a[v+2],E=a[v];E!=m;E+=C){var S=g^e[E],T=(g>>>4|g<<28)^e[E+1];h=y,y=g,g=h^(s[S>>>24&63]|c[S>>>16&63]|l[S>>>8&63]|f[63&S]|i[T>>>24&63]|o[T>>>16&63]|u[T>>>8&63]|p[63&T])}h=y,y=g,g=h}g=g>>>1|g<<31,g^=h=1431655765&((y=y>>>1|y<<31)>>>1^g),g^=(h=16711935&(g>>>8^(y^=h<<1)))<<8,g^=(h=858993459&(g>>>2^(y^=h)))<<2,g^=h=65535&((y^=h)>>>16^g),g^=h=252645135&((y^=h<<16)>>>4^g),y^=h<<4,r[0]=y,r[1]=g}function d(e){var t,r="DES-"+((e=e||{}).mode||"CBC").toUpperCase(),a=(t=e.decrypt?n.cipher.createDecipher(r,e.key):n.cipher.createCipher(r,e.key)).start;return t.start=function(e,r){var i=null;r instanceof n.util.ByteBuffer&&(i=r,r={}),(r=r||{}).output=i,r.iv=e,a.call(t,r)},t}},function(e,t,r){var n=r(0);if(r(3),r(13),r(6),r(26),r(27),r(2),r(1),void 0===a)var a=n.jsbn.BigInteger;var i=n.util.isNodejs?r(17):null,s=n.asn1,o=n.util;n.pki=n.pki||{},e.exports=n.pki.rsa=n.rsa=n.rsa||{};var c=n.pki,u=[6,4,2,4,2,4,6,2],l={name:"PrivateKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"PrivateKeyInfo.version",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"PrivateKeyInfo.privateKeyAlgorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"privateKeyOid"}]},{name:"PrivateKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.OCTETSTRING,constructed:!1,capture:"privateKey"}]},p={name:"RSAPrivateKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"RSAPrivateKey.version",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"RSAPrivateKey.modulus",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyModulus"},{name:"RSAPrivateKey.publicExponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPublicExponent"},{name:"RSAPrivateKey.privateExponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrivateExponent"},{name:"RSAPrivateKey.prime1",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrime1"},{name:"RSAPrivateKey.prime2",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrime2"},{name:"RSAPrivateKey.exponent1",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyExponent1"},{name:"RSAPrivateKey.exponent2",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyExponent2"},{name:"RSAPrivateKey.coefficient",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyCoefficient"}]},f={name:"RSAPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"RSAPublicKey.modulus",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"publicKeyModulus"},{name:"RSAPublicKey.exponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"publicKeyExponent"}]},h=n.pki.rsa.publicKeyValidator={name:"SubjectPublicKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,captureAsn1:"subjectPublicKeyInfo",value:[{name:"SubjectPublicKeyInfo.AlgorithmIdentifier",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"publicKeyOid"}]},{name:"SubjectPublicKeyInfo.subjectPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.BITSTRING,constructed:!1,value:[{name:"SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,optional:!0,captureAsn1:"rsaPublicKey"}]}]},d={name:"DigestInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"DigestInfo.DigestAlgorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"DigestInfo.DigestAlgorithm.algorithmIdentifier",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"algorithmIdentifier"},{name:"DigestInfo.DigestAlgorithm.parameters",tagClass:s.Class.UNIVERSAL,type:s.Type.NULL,capture:"parameters",optional:!0,constructed:!1}]},{name:"DigestInfo.digest",tagClass:s.Class.UNIVERSAL,type:s.Type.OCTETSTRING,constructed:!1,capture:"digest"}]},y=function(e){var t;if(!(e.algorithm in c.oids)){var r=new Error("Unknown message digest algorithm.");throw r.algorithm=e.algorithm,r}t=c.oids[e.algorithm];var n=s.oidToDer(t).getBytes(),a=s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[]),i=s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[]);i.value.push(s.create(s.Class.UNIVERSAL,s.Type.OID,!1,n)),i.value.push(s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,""));var o=s.create(s.Class.UNIVERSAL,s.Type.OCTETSTRING,!1,e.digest().getBytes());return a.value.push(i),a.value.push(o),s.toDer(a).getBytes()},g=function(e,t,r){if(r)return e.modPow(t.e,t.n);if(!t.p||!t.q)return e.modPow(t.d,t.n);var i;t.dP||(t.dP=t.d.mod(t.p.subtract(a.ONE))),t.dQ||(t.dQ=t.d.mod(t.q.subtract(a.ONE))),t.qInv||(t.qInv=t.q.modInverse(t.p));do{i=new a(n.util.bytesToHex(n.random.getBytes(t.n.bitLength()/8)),16)}while(i.compareTo(t.n)>=0||!i.gcd(t.n).equals(a.ONE));for(var s=(e=e.multiply(i.modPow(t.e,t.n)).mod(t.n)).mod(t.p).modPow(t.dP,t.p),o=e.mod(t.q).modPow(t.dQ,t.q);s.compareTo(o)<0;)s=s.add(t.p);var c=s.subtract(o).multiply(t.qInv).mod(t.p).multiply(t.q).add(o);return c=c.multiply(i.modInverse(t.n)).mod(t.n)};function v(e,t,r){var a=n.util.createBuffer(),i=Math.ceil(t.n.bitLength()/8);if(e.length>i-11){var s=new Error("Message is too long for PKCS#1 v1.5 padding.");throw s.length=e.length,s.max=i-11,s}a.putByte(0),a.putByte(r);var o,c=i-3-e.length;if(0===r||1===r){o=0===r?0:255;for(var u=0;u<c;++u)a.putByte(o)}else for(;c>0;){var l=0,p=n.random.getBytes(c);for(u=0;u<c;++u)0===(o=p.charCodeAt(u))?++l:a.putByte(o);c=l}return a.putByte(0),a.putBytes(e),a}function m(e,t,r,a,i){var s=Math.ceil(t.n.bitLength()/8),o=n.util.createBuffer(e),c=o.getByte(),u=o.getByte();if(0!==c||r&&0!==u&&1!==u||!r&&2!==u||r&&0===u&&void 0===a)throw new Error("Encryption block is invalid.");var l=0;if(0===u){l=s-3-a;for(var p=0;p<l;++p)if(0!==o.getByte())throw new Error("Encryption block is invalid.")}else if(1===u){for(l=0;o.length()>1;){if(255!==o.getByte()){--o.read;break}++l}if(l<8&&(!i||!i._skipPaddingChecks))throw new Error("Encryption block is invalid.")}else if(2===u){for(l=0;o.length()>1;){if(0===o.getByte()){--o.read;break}++l}if(l<8&&(!i||!i._skipPaddingChecks))throw new Error("Encryption block is invalid.")}if(0!==o.getByte()||l!==s-3-o.length())throw new Error("Encryption block is invalid.");return o.getBytes()}function C(e,t,r){"function"==typeof t&&(r=t,t={});var i={algorithm:{name:(t=t||{}).algorithm||"PRIMEINC",options:{workers:t.workers||2,workLoad:t.workLoad||100,workerScript:t.workerScript}}};function s(){o(e.pBits,(function(t,n){return t?r(t):(e.p=n,null!==e.q?u(t,e.q):void o(e.qBits,u))}))}function o(e,t){n.prime.generateProbablePrime(e,i,t)}function u(t,n){if(t)return r(t);if(e.q=n,e.p.compareTo(e.q)<0){var i=e.p;e.p=e.q,e.q=i}if(0!==e.p.subtract(a.ONE).gcd(e.e).compareTo(a.ONE))return e.p=null,void s();if(0!==e.q.subtract(a.ONE).gcd(e.e).compareTo(a.ONE))return e.q=null,void o(e.qBits,u);if(e.p1=e.p.subtract(a.ONE),e.q1=e.q.subtract(a.ONE),e.phi=e.p1.multiply(e.q1),0!==e.phi.gcd(e.e).compareTo(a.ONE))return e.p=e.q=null,void s();if(e.n=e.p.multiply(e.q),e.n.bitLength()!==e.bits)return e.q=null,void o(e.qBits,u);var l=e.e.modInverse(e.phi);e.keys={privateKey:c.rsa.setPrivateKey(e.n,e.e,l,e.p,e.q,l.mod(e.p1),l.mod(e.q1),e.q.modInverse(e.p)),publicKey:c.rsa.setPublicKey(e.n,e.e)},r(null,e.keys)}"prng"in t&&(i.prng=t.prng),s()}function E(e){var t=e.toString(16);t[0]>="8"&&(t="00"+t);var r=n.util.hexToBytes(t);return r.length>1&&(0===r.charCodeAt(0)&&0==(128&r.charCodeAt(1))||255===r.charCodeAt(0)&&128==(128&r.charCodeAt(1)))?r.substr(1):r}function S(e){return e<=100?27:e<=150?18:e<=200?15:e<=250?12:e<=300?9:e<=350?8:e<=400?7:e<=500?6:e<=600?5:e<=800?4:e<=1250?3:2}function T(e){return n.util.isNodejs&&"function"==typeof i[e]}function I(e){return void 0!==o.globalScope&&"object"==typeof o.globalScope.crypto&&"object"==typeof o.globalScope.crypto.subtle&&"function"==typeof o.globalScope.crypto.subtle[e]}function b(e){return void 0!==o.globalScope&&"object"==typeof o.globalScope.msCrypto&&"object"==typeof o.globalScope.msCrypto.subtle&&"function"==typeof o.globalScope.msCrypto.subtle[e]}function A(e){for(var t=n.util.hexToBytes(e.toString(16)),r=new Uint8Array(t.length),a=0;a<t.length;++a)r[a]=t.charCodeAt(a);return r}c.rsa.encrypt=function(e,t,r){var i,s=r,o=Math.ceil(t.n.bitLength()/8);!1!==r&&!0!==r?(s=2===r,i=v(e,t,r)):(i=n.util.createBuffer()).putBytes(e);for(var c=new a(i.toHex(),16),u=g(c,t,s).toString(16),l=n.util.createBuffer(),p=o-Math.ceil(u.length/2);p>0;)l.putByte(0),--p;return l.putBytes(n.util.hexToBytes(u)),l.getBytes()},c.rsa.decrypt=function(e,t,r,i){var s=Math.ceil(t.n.bitLength()/8);if(e.length!==s){var o=new Error("Encrypted message length is invalid.");throw o.length=e.length,o.expected=s,o}var c=new a(n.util.createBuffer(e).toHex(),16);if(c.compareTo(t.n)>=0)throw new Error("Encrypted message is invalid.");for(var u=g(c,t,r).toString(16),l=n.util.createBuffer(),p=s-Math.ceil(u.length/2);p>0;)l.putByte(0),--p;return l.putBytes(n.util.hexToBytes(u)),!1!==i?m(l.getBytes(),t,r):l.getBytes()},c.rsa.createKeyPairGenerationState=function(e,t,r){"string"==typeof e&&(e=parseInt(e,10)),e=e||2048;var i,s=(r=r||{}).prng||n.random,o={nextBytes:function(e){for(var t=s.getBytesSync(e.length),r=0;r<e.length;++r)e[r]=t.charCodeAt(r)}},c=r.algorithm||"PRIMEINC";if("PRIMEINC"!==c)throw new Error("Invalid key generation algorithm: "+c);return(i={algorithm:c,state:0,bits:e,rng:o,eInt:t||65537,e:new a(null),p:null,q:null,qBits:e>>1,pBits:e-(e>>1),pqState:0,num:null,keys:null}).e.fromInt(i.eInt),i},c.rsa.stepKeyPairGenerationState=function(e,t){"algorithm"in e||(e.algorithm="PRIMEINC");var r=new a(null);r.fromInt(30);for(var n,i=0,s=function(e,t){return e|t},o=+new Date,l=0;null===e.keys&&(t<=0||l<t);){if(0===e.state){var p=null===e.p?e.pBits:e.qBits,f=p-1;0===e.pqState?(e.num=new a(p,e.rng),e.num.testBit(f)||e.num.bitwiseTo(a.ONE.shiftLeft(f),s,e.num),e.num.dAddOffset(31-e.num.mod(r).byteValue(),0),i=0,++e.pqState):1===e.pqState?e.num.bitLength()>p?e.pqState=0:e.num.isProbablePrime(S(e.num.bitLength()))?++e.pqState:e.num.dAddOffset(u[i++%8],0):2===e.pqState?e.pqState=0===e.num.subtract(a.ONE).gcd(e.e).compareTo(a.ONE)?3:0:3===e.pqState&&(e.pqState=0,null===e.p?e.p=e.num:e.q=e.num,null!==e.p&&null!==e.q&&++e.state,e.num=null)}else if(1===e.state)e.p.compareTo(e.q)<0&&(e.num=e.p,e.p=e.q,e.q=e.num),++e.state;else if(2===e.state)e.p1=e.p.subtract(a.ONE),e.q1=e.q.subtract(a.ONE),e.phi=e.p1.multiply(e.q1),++e.state;else if(3===e.state)0===e.phi.gcd(e.e).compareTo(a.ONE)?++e.state:(e.p=null,e.q=null,e.state=0);else if(4===e.state)e.n=e.p.multiply(e.q),e.n.bitLength()===e.bits?++e.state:(e.q=null,e.state=0);else if(5===e.state){var h=e.e.modInverse(e.phi);e.keys={privateKey:c.rsa.setPrivateKey(e.n,e.e,h,e.p,e.q,h.mod(e.p1),h.mod(e.q1),e.q.modInverse(e.p)),publicKey:c.rsa.setPublicKey(e.n,e.e)}}l+=(n=+new Date)-o,o=n}return null!==e.keys},c.rsa.generateKeyPair=function(e,t,r,a){if(1===arguments.length?"object"==typeof e?(r=e,e=void 0):"function"==typeof e&&(a=e,e=void 0):2===arguments.length?"number"==typeof e?"function"==typeof t?(a=t,t=void 0):"number"!=typeof t&&(r=t,t=void 0):(r=e,a=t,e=void 0,t=void 0):3===arguments.length&&("number"==typeof t?"function"==typeof r&&(a=r,r=void 0):(a=r,r=t,t=void 0)),r=r||{},void 0===e&&(e=r.bits||2048),void 0===t&&(t=r.e||65537),!n.options.usePureJavaScript&&!r.prng&&e>=256&&e<=16384&&(65537===t||3===t))if(a){if(T("generateKeyPair"))return i.generateKeyPair("rsa",{modulusLength:e,publicExponent:t,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}},(function(e,t,r){if(e)return a(e);a(null,{privateKey:c.privateKeyFromPem(r),publicKey:c.publicKeyFromPem(t)})}));if(I("generateKey")&&I("exportKey"))return o.globalScope.crypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:e,publicExponent:A(t),hash:{name:"SHA-256"}},!0,["sign","verify"]).then((function(e){return o.globalScope.crypto.subtle.exportKey("pkcs8",e.privateKey)})).then(void 0,(function(e){a(e)})).then((function(e){if(e){var t=c.privateKeyFromAsn1(s.fromDer(n.util.createBuffer(e)));a(null,{privateKey:t,publicKey:c.setRsaPublicKey(t.n,t.e)})}}));if(b("generateKey")&&b("exportKey")){var u=o.globalScope.msCrypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:e,publicExponent:A(t),hash:{name:"SHA-256"}},!0,["sign","verify"]);return u.oncomplete=function(e){var t=e.target.result,r=o.globalScope.msCrypto.subtle.exportKey("pkcs8",t.privateKey);r.oncomplete=function(e){var t=e.target.result,r=c.privateKeyFromAsn1(s.fromDer(n.util.createBuffer(t)));a(null,{privateKey:r,publicKey:c.setRsaPublicKey(r.n,r.e)})},r.onerror=function(e){a(e)}},void(u.onerror=function(e){a(e)})}}else if(T("generateKeyPairSync")){var l=i.generateKeyPairSync("rsa",{modulusLength:e,publicExponent:t,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}});return{privateKey:c.privateKeyFromPem(l.privateKey),publicKey:c.publicKeyFromPem(l.publicKey)}}var p=c.rsa.createKeyPairGenerationState(e,t,r);if(!a)return c.rsa.stepKeyPairGenerationState(p,0),p.keys;C(p,r,a)},c.setRsaPublicKey=c.rsa.setPublicKey=function(e,t){var r={n:e,e:t,encrypt:function(e,t,a){if("string"==typeof t?t=t.toUpperCase():void 0===t&&(t="RSAES-PKCS1-V1_5"),"RSAES-PKCS1-V1_5"===t)t={encode:function(e,t,r){return v(e,t,2).getBytes()}};else if("RSA-OAEP"===t||"RSAES-OAEP"===t)t={encode:function(e,t){return n.pkcs1.encode_rsa_oaep(t,e,a)}};else if(-1!==["RAW","NONE","NULL",null].indexOf(t))t={encode:function(e){return e}};else if("string"==typeof t)throw new Error('Unsupported encryption scheme: "'+t+'".');var i=t.encode(e,r,!0);return c.rsa.encrypt(i,r,!0)},verify:function(e,t,a,i){"string"==typeof a?a=a.toUpperCase():void 0===a&&(a="RSASSA-PKCS1-V1_5"),void 0===i&&(i={_parseAllDigestBytes:!0,_skipPaddingChecks:!1}),"_parseAllDigestBytes"in i||(i._parseAllDigestBytes=!0),"_skipPaddingChecks"in i||(i._skipPaddingChecks=!1),"RSASSA-PKCS1-V1_5"===a?a={verify:function(e,t){t=m(t,r,!0,void 0,i);var a=s.fromDer(t,{parseAllBytes:i._parseAllDigestBytes}),o={},c=[];if(!s.validate(a,d,o,c)||2!==a.value.length)throw(u=new Error("ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value.")).errors=c,u;var u,l=s.derToOid(o.algorithmIdentifier);if(l!==n.oids.md2&&l!==n.oids.md5&&l!==n.oids.sha1&&l!==n.oids.sha224&&l!==n.oids.sha256&&l!==n.oids.sha384&&l!==n.oids.sha512&&l!==n.oids["sha512-224"]&&l!==n.oids["sha512-256"])throw(u=new Error("Unknown RSASSA-PKCS1-v1_5 DigestAlgorithm identifier.")).oid=l,u;if((l===n.oids.md2||l===n.oids.md5)&&!("parameters"in o))throw new Error("ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value. Missing algorithm identifier NULL parameters.");return e===o.digest}}:"NONE"!==a&&"NULL"!==a&&null!==a||(a={verify:function(e,t){return e===(t=m(t,r,!0,void 0,i))}});var o=c.rsa.decrypt(t,r,!0,!1);return a.verify(e,o,r.n.bitLength())}};return r},c.setRsaPrivateKey=c.rsa.setPrivateKey=function(e,t,r,a,i,s,o,u){var l={n:e,e:t,d:r,p:a,q:i,dP:s,dQ:o,qInv:u,decrypt:function(e,t,r){"string"==typeof t?t=t.toUpperCase():void 0===t&&(t="RSAES-PKCS1-V1_5");var a=c.rsa.decrypt(e,l,!1,!1);if("RSAES-PKCS1-V1_5"===t)t={decode:m};else if("RSA-OAEP"===t||"RSAES-OAEP"===t)t={decode:function(e,t){return n.pkcs1.decode_rsa_oaep(t,e,r)}};else{if(-1===["RAW","NONE","NULL",null].indexOf(t))throw new Error('Unsupported encryption scheme: "'+t+'".');t={decode:function(e){return e}}}return t.decode(a,l,!1)},sign:function(e,t){var r=!1;"string"==typeof t&&(t=t.toUpperCase()),void 0===t||"RSASSA-PKCS1-V1_5"===t?(t={encode:y},r=1):"NONE"!==t&&"NULL"!==t&&null!==t||(t={encode:function(){return e}},r=1);var n=t.encode(e,l.n.bitLength());return c.rsa.encrypt(n,l,r)}};return l},c.wrapRsaPrivateKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,s.integerToDer(0).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.OID,!1,s.oidToDer(c.oids.rsaEncryption).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,"")]),s.create(s.Class.UNIVERSAL,s.Type.OCTETSTRING,!1,s.toDer(e).getBytes())])},c.privateKeyFromAsn1=function(e){var t,r,i,o,u,f,h,d,y={},g=[];if(s.validate(e,l,y,g)&&(e=s.fromDer(n.util.createBuffer(y.privateKey))),y={},g=[],!s.validate(e,p,y,g)){var v=new Error("Cannot read private key. ASN.1 object does not contain an RSAPrivateKey.");throw v.errors=g,v}return t=n.util.createBuffer(y.privateKeyModulus).toHex(),r=n.util.createBuffer(y.privateKeyPublicExponent).toHex(),i=n.util.createBuffer(y.privateKeyPrivateExponent).toHex(),o=n.util.createBuffer(y.privateKeyPrime1).toHex(),u=n.util.createBuffer(y.privateKeyPrime2).toHex(),f=n.util.createBuffer(y.privateKeyExponent1).toHex(),h=n.util.createBuffer(y.privateKeyExponent2).toHex(),d=n.util.createBuffer(y.privateKeyCoefficient).toHex(),c.setRsaPrivateKey(new a(t,16),new a(r,16),new a(i,16),new a(o,16),new a(u,16),new a(f,16),new a(h,16),new a(d,16))},c.privateKeyToAsn1=c.privateKeyToRSAPrivateKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,s.integerToDer(0).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.n)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.e)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.d)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.p)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.q)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.dP)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.dQ)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.qInv))])},c.publicKeyFromAsn1=function(e){var t={},r=[];if(s.validate(e,h,t,r)){var i,o=s.derToOid(t.publicKeyOid);if(o!==c.oids.rsaEncryption)throw(i=new Error("Cannot read public key. Unknown OID.")).oid=o,i;e=t.rsaPublicKey}if(r=[],!s.validate(e,f,t,r))throw(i=new Error("Cannot read public key. ASN.1 object does not contain an RSAPublicKey.")).errors=r,i;var u=n.util.createBuffer(t.publicKeyModulus).toHex(),l=n.util.createBuffer(t.publicKeyExponent).toHex();return c.setRsaPublicKey(new a(u,16),new a(l,16))},c.publicKeyToAsn1=c.publicKeyToSubjectPublicKeyInfo=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.OID,!1,s.oidToDer(c.oids.rsaEncryption).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,"")]),s.create(s.Class.UNIVERSAL,s.Type.BITSTRING,!1,[c.publicKeyToRSAPublicKey(e)])])},c.publicKeyToRSAPublicKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.n)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.e))])}},function(e,t,r){var n,a=r(0);e.exports=a.jsbn=a.jsbn||{};function i(e,t,r){this.data=[],null!=e&&("number"==typeof e?this.fromNumber(e,t,r):null==t&&"string"!=typeof e?this.fromString(e,256):this.fromString(e,t))}function s(){return new i(null)}function o(e,t,r,n,a,i){for(var s=16383&t,o=t>>14;--i>=0;){var c=16383&this.data[e],u=this.data[e++]>>14,l=o*c+u*s;a=((c=s*c+((16383&l)<<14)+r.data[n]+a)>>28)+(l>>14)+o*u,r.data[n++]=268435455&c}return a}a.jsbn.BigInteger=i,"undefined"==typeof navigator?(i.prototype.am=o,n=28):"Microsoft Internet Explorer"==navigator.appName?(i.prototype.am=function(e,t,r,n,a,i){for(var s=32767&t,o=t>>15;--i>=0;){var c=32767&this.data[e],u=this.data[e++]>>15,l=o*c+u*s;a=((c=s*c+((32767&l)<<15)+r.data[n]+(1073741823&a))>>>30)+(l>>>15)+o*u+(a>>>30),r.data[n++]=1073741823&c}return a},n=30):"Netscape"!=navigator.appName?(i.prototype.am=function(e,t,r,n,a,i){for(;--i>=0;){var s=t*this.data[e++]+r.data[n]+a;a=Math.floor(s/67108864),r.data[n++]=67108863&s}return a},n=26):(i.prototype.am=o,n=28),i.prototype.DB=n,i.prototype.DM=(1<<n)-1,i.prototype.DV=1<<n;i.prototype.FV=Math.pow(2,52),i.prototype.F1=52-n,i.prototype.F2=2*n-52;var c,u,l=new Array;for(c="0".charCodeAt(0),u=0;u<=9;++u)l[c++]=u;for(c="a".charCodeAt(0),u=10;u<36;++u)l[c++]=u;for(c="A".charCodeAt(0),u=10;u<36;++u)l[c++]=u;function p(e){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(e)}function f(e,t){var r=l[e.charCodeAt(t)];return null==r?-1:r}function h(e){var t=s();return t.fromInt(e),t}function d(e){var t,r=1;return 0!=(t=e>>>16)&&(e=t,r+=16),0!=(t=e>>8)&&(e=t,r+=8),0!=(t=e>>4)&&(e=t,r+=4),0!=(t=e>>2)&&(e=t,r+=2),0!=(t=e>>1)&&(e=t,r+=1),r}function y(e){this.m=e}function g(e){this.m=e,this.mp=e.invDigit(),this.mpl=32767&this.mp,this.mph=this.mp>>15,this.um=(1<<e.DB-15)-1,this.mt2=2*e.t}function v(e,t){return e&t}function m(e,t){return e|t}function C(e,t){return e^t}function E(e,t){return e&~t}function S(e){if(0==e)return-1;var t=0;return 0==(65535&e)&&(e>>=16,t+=16),0==(255&e)&&(e>>=8,t+=8),0==(15&e)&&(e>>=4,t+=4),0==(3&e)&&(e>>=2,t+=2),0==(1&e)&&++t,t}function T(e){for(var t=0;0!=e;)e&=e-1,++t;return t}function I(){}function b(e){return e}function A(e){this.r2=s(),this.q3=s(),i.ONE.dlShiftTo(2*e.t,this.r2),this.mu=this.r2.divide(e),this.m=e}y.prototype.convert=function(e){return e.s<0||e.compareTo(this.m)>=0?e.mod(this.m):e},y.prototype.revert=function(e){return e},y.prototype.reduce=function(e){e.divRemTo(this.m,null,e)},y.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},y.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},g.prototype.convert=function(e){var t=s();return e.abs().dlShiftTo(this.m.t,t),t.divRemTo(this.m,null,t),e.s<0&&t.compareTo(i.ZERO)>0&&this.m.subTo(t,t),t},g.prototype.revert=function(e){var t=s();return e.copyTo(t),this.reduce(t),t},g.prototype.reduce=function(e){for(;e.t<=this.mt2;)e.data[e.t++]=0;for(var t=0;t<this.m.t;++t){var r=32767&e.data[t],n=r*this.mpl+((r*this.mph+(e.data[t]>>15)*this.mpl&this.um)<<15)&e.DM;for(r=t+this.m.t,e.data[r]+=this.m.am(0,n,e,t,0,this.m.t);e.data[r]>=e.DV;)e.data[r]-=e.DV,e.data[++r]++}e.clamp(),e.drShiftTo(this.m.t,e),e.compareTo(this.m)>=0&&e.subTo(this.m,e)},g.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},g.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},i.prototype.copyTo=function(e){for(var t=this.t-1;t>=0;--t)e.data[t]=this.data[t];e.t=this.t,e.s=this.s},i.prototype.fromInt=function(e){this.t=1,this.s=e<0?-1:0,e>0?this.data[0]=e:e<-1?this.data[0]=e+this.DV:this.t=0},i.prototype.fromString=function(e,t){var r;if(16==t)r=4;else if(8==t)r=3;else if(256==t)r=8;else if(2==t)r=1;else if(32==t)r=5;else{if(4!=t)return void this.fromRadix(e,t);r=2}this.t=0,this.s=0;for(var n=e.length,a=!1,s=0;--n>=0;){var o=8==r?255&e[n]:f(e,n);o<0?"-"==e.charAt(n)&&(a=!0):(a=!1,0==s?this.data[this.t++]=o:s+r>this.DB?(this.data[this.t-1]|=(o&(1<<this.DB-s)-1)<<s,this.data[this.t++]=o>>this.DB-s):this.data[this.t-1]|=o<<s,(s+=r)>=this.DB&&(s-=this.DB))}8==r&&0!=(128&e[0])&&(this.s=-1,s>0&&(this.data[this.t-1]|=(1<<this.DB-s)-1<<s)),this.clamp(),a&&i.ZERO.subTo(this,this)},i.prototype.clamp=function(){for(var e=this.s&this.DM;this.t>0&&this.data[this.t-1]==e;)--this.t},i.prototype.dlShiftTo=function(e,t){var r;for(r=this.t-1;r>=0;--r)t.data[r+e]=this.data[r];for(r=e-1;r>=0;--r)t.data[r]=0;t.t=this.t+e,t.s=this.s},i.prototype.drShiftTo=function(e,t){for(var r=e;r<this.t;++r)t.data[r-e]=this.data[r];t.t=Math.max(this.t-e,0),t.s=this.s},i.prototype.lShiftTo=function(e,t){var r,n=e%this.DB,a=this.DB-n,i=(1<<a)-1,s=Math.floor(e/this.DB),o=this.s<<n&this.DM;for(r=this.t-1;r>=0;--r)t.data[r+s+1]=this.data[r]>>a|o,o=(this.data[r]&i)<<n;for(r=s-1;r>=0;--r)t.data[r]=0;t.data[s]=o,t.t=this.t+s+1,t.s=this.s,t.clamp()},i.prototype.rShiftTo=function(e,t){t.s=this.s;var r=Math.floor(e/this.DB);if(r>=this.t)t.t=0;else{var n=e%this.DB,a=this.DB-n,i=(1<<n)-1;t.data[0]=this.data[r]>>n;for(var s=r+1;s<this.t;++s)t.data[s-r-1]|=(this.data[s]&i)<<a,t.data[s-r]=this.data[s]>>n;n>0&&(t.data[this.t-r-1]|=(this.s&i)<<a),t.t=this.t-r,t.clamp()}},i.prototype.subTo=function(e,t){for(var r=0,n=0,a=Math.min(e.t,this.t);r<a;)n+=this.data[r]-e.data[r],t.data[r++]=n&this.DM,n>>=this.DB;if(e.t<this.t){for(n-=e.s;r<this.t;)n+=this.data[r],t.data[r++]=n&this.DM,n>>=this.DB;n+=this.s}else{for(n+=this.s;r<e.t;)n-=e.data[r],t.data[r++]=n&this.DM,n>>=this.DB;n-=e.s}t.s=n<0?-1:0,n<-1?t.data[r++]=this.DV+n:n>0&&(t.data[r++]=n),t.t=r,t.clamp()},i.prototype.multiplyTo=function(e,t){var r=this.abs(),n=e.abs(),a=r.t;for(t.t=a+n.t;--a>=0;)t.data[a]=0;for(a=0;a<n.t;++a)t.data[a+r.t]=r.am(0,n.data[a],t,a,0,r.t);t.s=0,t.clamp(),this.s!=e.s&&i.ZERO.subTo(t,t)},i.prototype.squareTo=function(e){for(var t=this.abs(),r=e.t=2*t.t;--r>=0;)e.data[r]=0;for(r=0;r<t.t-1;++r){var n=t.am(r,t.data[r],e,2*r,0,1);(e.data[r+t.t]+=t.am(r+1,2*t.data[r],e,2*r+1,n,t.t-r-1))>=t.DV&&(e.data[r+t.t]-=t.DV,e.data[r+t.t+1]=1)}e.t>0&&(e.data[e.t-1]+=t.am(r,t.data[r],e,2*r,0,1)),e.s=0,e.clamp()},i.prototype.divRemTo=function(e,t,r){var n=e.abs();if(!(n.t<=0)){var a=this.abs();if(a.t<n.t)return null!=t&&t.fromInt(0),void(null!=r&&this.copyTo(r));null==r&&(r=s());var o=s(),c=this.s,u=e.s,l=this.DB-d(n.data[n.t-1]);l>0?(n.lShiftTo(l,o),a.lShiftTo(l,r)):(n.copyTo(o),a.copyTo(r));var p=o.t,f=o.data[p-1];if(0!=f){var h=f*(1<<this.F1)+(p>1?o.data[p-2]>>this.F2:0),y=this.FV/h,g=(1<<this.F1)/h,v=1<<this.F2,m=r.t,C=m-p,E=null==t?s():t;for(o.dlShiftTo(C,E),r.compareTo(E)>=0&&(r.data[r.t++]=1,r.subTo(E,r)),i.ONE.dlShiftTo(p,E),E.subTo(o,o);o.t<p;)o.data[o.t++]=0;for(;--C>=0;){var S=r.data[--m]==f?this.DM:Math.floor(r.data[m]*y+(r.data[m-1]+v)*g);if((r.data[m]+=o.am(0,S,r,C,0,p))<S)for(o.dlShiftTo(C,E),r.subTo(E,r);r.data[m]<--S;)r.subTo(E,r)}null!=t&&(r.drShiftTo(p,t),c!=u&&i.ZERO.subTo(t,t)),r.t=p,r.clamp(),l>0&&r.rShiftTo(l,r),c<0&&i.ZERO.subTo(r,r)}}},i.prototype.invDigit=function(){if(this.t<1)return 0;var e=this.data[0];if(0==(1&e))return 0;var t=3&e;return(t=(t=(t=(t=t*(2-(15&e)*t)&15)*(2-(255&e)*t)&255)*(2-((65535&e)*t&65535))&65535)*(2-e*t%this.DV)%this.DV)>0?this.DV-t:-t},i.prototype.isEven=function(){return 0==(this.t>0?1&this.data[0]:this.s)},i.prototype.exp=function(e,t){if(e>4294967295||e<1)return i.ONE;var r=s(),n=s(),a=t.convert(this),o=d(e)-1;for(a.copyTo(r);--o>=0;)if(t.sqrTo(r,n),(e&1<<o)>0)t.mulTo(n,a,r);else{var c=r;r=n,n=c}return t.revert(r)},i.prototype.toString=function(e){if(this.s<0)return"-"+this.negate().toString(e);var t;if(16==e)t=4;else if(8==e)t=3;else if(2==e)t=1;else if(32==e)t=5;else{if(4!=e)return this.toRadix(e);t=2}var r,n=(1<<t)-1,a=!1,i="",s=this.t,o=this.DB-s*this.DB%t;if(s-- >0)for(o<this.DB&&(r=this.data[s]>>o)>0&&(a=!0,i=p(r));s>=0;)o<t?(r=(this.data[s]&(1<<o)-1)<<t-o,r|=this.data[--s]>>(o+=this.DB-t)):(r=this.data[s]>>(o-=t)&n,o<=0&&(o+=this.DB,--s)),r>0&&(a=!0),a&&(i+=p(r));return a?i:"0"},i.prototype.negate=function(){var e=s();return i.ZERO.subTo(this,e),e},i.prototype.abs=function(){return this.s<0?this.negate():this},i.prototype.compareTo=function(e){var t=this.s-e.s;if(0!=t)return t;var r=this.t;if(0!=(t=r-e.t))return this.s<0?-t:t;for(;--r>=0;)if(0!=(t=this.data[r]-e.data[r]))return t;return 0},i.prototype.bitLength=function(){return this.t<=0?0:this.DB*(this.t-1)+d(this.data[this.t-1]^this.s&this.DM)},i.prototype.mod=function(e){var t=s();return this.abs().divRemTo(e,null,t),this.s<0&&t.compareTo(i.ZERO)>0&&e.subTo(t,t),t},i.prototype.modPowInt=function(e,t){var r;return r=e<256||t.isEven()?new y(t):new g(t),this.exp(e,r)},i.ZERO=h(0),i.ONE=h(1),I.prototype.convert=b,I.prototype.revert=b,I.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r)},I.prototype.sqrTo=function(e,t){e.squareTo(t)},A.prototype.convert=function(e){if(e.s<0||e.t>2*this.m.t)return e.mod(this.m);if(e.compareTo(this.m)<0)return e;var t=s();return e.copyTo(t),this.reduce(t),t},A.prototype.revert=function(e){return e},A.prototype.reduce=function(e){for(e.drShiftTo(this.m.t-1,this.r2),e.t>this.m.t+1&&(e.t=this.m.t+1,e.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);e.compareTo(this.r2)<0;)e.dAddOffset(1,this.m.t+1);for(e.subTo(this.r2,e);e.compareTo(this.m)>=0;)e.subTo(this.m,e)},A.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},A.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)};var B=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],N=(1<<26)/B[B.length-1];i.prototype.chunkSize=function(e){return Math.floor(Math.LN2*this.DB/Math.log(e))},i.prototype.toRadix=function(e){if(null==e&&(e=10),0==this.signum()||e<2||e>36)return"0";var t=this.chunkSize(e),r=Math.pow(e,t),n=h(r),a=s(),i=s(),o="";for(this.divRemTo(n,a,i);a.signum()>0;)o=(r+i.intValue()).toString(e).substr(1)+o,a.divRemTo(n,a,i);return i.intValue().toString(e)+o},i.prototype.fromRadix=function(e,t){this.fromInt(0),null==t&&(t=10);for(var r=this.chunkSize(t),n=Math.pow(t,r),a=!1,s=0,o=0,c=0;c<e.length;++c){var u=f(e,c);u<0?"-"==e.charAt(c)&&0==this.signum()&&(a=!0):(o=t*o+u,++s>=r&&(this.dMultiply(n),this.dAddOffset(o,0),s=0,o=0))}s>0&&(this.dMultiply(Math.pow(t,s)),this.dAddOffset(o,0)),a&&i.ZERO.subTo(this,this)},i.prototype.fromNumber=function(e,t,r){if("number"==typeof t)if(e<2)this.fromInt(1);else for(this.fromNumber(e,r),this.testBit(e-1)||this.bitwiseTo(i.ONE.shiftLeft(e-1),m,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(t);)this.dAddOffset(2,0),this.bitLength()>e&&this.subTo(i.ONE.shiftLeft(e-1),this);else{var n=new Array,a=7&e;n.length=1+(e>>3),t.nextBytes(n),a>0?n[0]&=(1<<a)-1:n[0]=0,this.fromString(n,256)}},i.prototype.bitwiseTo=function(e,t,r){var n,a,i=Math.min(e.t,this.t);for(n=0;n<i;++n)r.data[n]=t(this.data[n],e.data[n]);if(e.t<this.t){for(a=e.s&this.DM,n=i;n<this.t;++n)r.data[n]=t(this.data[n],a);r.t=this.t}else{for(a=this.s&this.DM,n=i;n<e.t;++n)r.data[n]=t(a,e.data[n]);r.t=e.t}r.s=t(this.s,e.s),r.clamp()},i.prototype.changeBit=function(e,t){var r=i.ONE.shiftLeft(e);return this.bitwiseTo(r,t,r),r},i.prototype.addTo=function(e,t){for(var r=0,n=0,a=Math.min(e.t,this.t);r<a;)n+=this.data[r]+e.data[r],t.data[r++]=n&this.DM,n>>=this.DB;if(e.t<this.t){for(n+=e.s;r<this.t;)n+=this.data[r],t.data[r++]=n&this.DM,n>>=this.DB;n+=this.s}else{for(n+=this.s;r<e.t;)n+=e.data[r],t.data[r++]=n&this.DM,n>>=this.DB;n+=e.s}t.s=n<0?-1:0,n>0?t.data[r++]=n:n<-1&&(t.data[r++]=this.DV+n),t.t=r,t.clamp()},i.prototype.dMultiply=function(e){this.data[this.t]=this.am(0,e-1,this,0,0,this.t),++this.t,this.clamp()},i.prototype.dAddOffset=function(e,t){if(0!=e){for(;this.t<=t;)this.data[this.t++]=0;for(this.data[t]+=e;this.data[t]>=this.DV;)this.data[t]-=this.DV,++t>=this.t&&(this.data[this.t++]=0),++this.data[t]}},i.prototype.multiplyLowerTo=function(e,t,r){var n,a=Math.min(this.t+e.t,t);for(r.s=0,r.t=a;a>0;)r.data[--a]=0;for(n=r.t-this.t;a<n;++a)r.data[a+this.t]=this.am(0,e.data[a],r,a,0,this.t);for(n=Math.min(e.t,t);a<n;++a)this.am(0,e.data[a],r,a,0,t-a);r.clamp()},i.prototype.multiplyUpperTo=function(e,t,r){--t;var n=r.t=this.t+e.t-t;for(r.s=0;--n>=0;)r.data[n]=0;for(n=Math.max(t-this.t,0);n<e.t;++n)r.data[this.t+n-t]=this.am(t-n,e.data[n],r,0,0,this.t+n-t);r.clamp(),r.drShiftTo(1,r)},i.prototype.modInt=function(e){if(e<=0)return 0;var t=this.DV%e,r=this.s<0?e-1:0;if(this.t>0)if(0==t)r=this.data[0]%e;else for(var n=this.t-1;n>=0;--n)r=(t*r+this.data[n])%e;return r},i.prototype.millerRabin=function(e){var t=this.subtract(i.ONE),r=t.getLowestSetBit();if(r<=0)return!1;for(var n,a=t.shiftRight(r),s={nextBytes:function(e){for(var t=0;t<e.length;++t)e[t]=Math.floor(256*Math.random())}},o=0;o<e;++o){do{n=new i(this.bitLength(),s)}while(n.compareTo(i.ONE)<=0||n.compareTo(t)>=0);var c=n.modPow(a,this);if(0!=c.compareTo(i.ONE)&&0!=c.compareTo(t)){for(var u=1;u++<r&&0!=c.compareTo(t);)if(0==(c=c.modPowInt(2,this)).compareTo(i.ONE))return!1;if(0!=c.compareTo(t))return!1}}return!0},i.prototype.clone=function(){var e=s();return this.copyTo(e),e},i.prototype.intValue=function(){if(this.s<0){if(1==this.t)return this.data[0]-this.DV;if(0==this.t)return-1}else{if(1==this.t)return this.data[0];if(0==this.t)return 0}return(this.data[1]&(1<<32-this.DB)-1)<<this.DB|this.data[0]},i.prototype.byteValue=function(){return 0==this.t?this.s:this.data[0]<<24>>24},i.prototype.shortValue=function(){return 0==this.t?this.s:this.data[0]<<16>>16},i.prototype.signum=function(){return this.s<0?-1:this.t<=0||1==this.t&&this.data[0]<=0?0:1},i.prototype.toByteArray=function(){var e=this.t,t=new Array;t[0]=this.s;var r,n=this.DB-e*this.DB%8,a=0;if(e-- >0)for(n<this.DB&&(r=this.data[e]>>n)!=(this.s&this.DM)>>n&&(t[a++]=r|this.s<<this.DB-n);e>=0;)n<8?(r=(this.data[e]&(1<<n)-1)<<8-n,r|=this.data[--e]>>(n+=this.DB-8)):(r=this.data[e]>>(n-=8)&255,n<=0&&(n+=this.DB,--e)),0!=(128&r)&&(r|=-256),0==a&&(128&this.s)!=(128&r)&&++a,(a>0||r!=this.s)&&(t[a++]=r);return t},i.prototype.equals=function(e){return 0==this.compareTo(e)},i.prototype.min=function(e){return this.compareTo(e)<0?this:e},i.prototype.max=function(e){return this.compareTo(e)>0?this:e},i.prototype.and=function(e){var t=s();return this.bitwiseTo(e,v,t),t},i.prototype.or=function(e){var t=s();return this.bitwiseTo(e,m,t),t},i.prototype.xor=function(e){var t=s();return this.bitwiseTo(e,C,t),t},i.prototype.andNot=function(e){var t=s();return this.bitwiseTo(e,E,t),t},i.prototype.not=function(){for(var e=s(),t=0;t<this.t;++t)e.data[t]=this.DM&~this.data[t];return e.t=this.t,e.s=~this.s,e},i.prototype.shiftLeft=function(e){var t=s();return e<0?this.rShiftTo(-e,t):this.lShiftTo(e,t),t},i.prototype.shiftRight=function(e){var t=s();return e<0?this.lShiftTo(-e,t):this.rShiftTo(e,t),t},i.prototype.getLowestSetBit=function(){for(var e=0;e<this.t;++e)if(0!=this.data[e])return e*this.DB+S(this.data[e]);return this.s<0?this.t*this.DB:-1},i.prototype.bitCount=function(){for(var e=0,t=this.s&this.DM,r=0;r<this.t;++r)e+=T(this.data[r]^t);return e},i.prototype.testBit=function(e){var t=Math.floor(e/this.DB);return t>=this.t?0!=this.s:0!=(this.data[t]&1<<e%this.DB)},i.prototype.setBit=function(e){return this.changeBit(e,m)},i.prototype.clearBit=function(e){return this.changeBit(e,E)},i.prototype.flipBit=function(e){return this.changeBit(e,C)},i.prototype.add=function(e){var t=s();return this.addTo(e,t),t},i.prototype.subtract=function(e){var t=s();return this.subTo(e,t),t},i.prototype.multiply=function(e){var t=s();return this.multiplyTo(e,t),t},i.prototype.divide=function(e){var t=s();return this.divRemTo(e,t,null),t},i.prototype.remainder=function(e){var t=s();return this.divRemTo(e,null,t),t},i.prototype.divideAndRemainder=function(e){var t=s(),r=s();return this.divRemTo(e,t,r),new Array(t,r)},i.prototype.modPow=function(e,t){var r,n,a=e.bitLength(),i=h(1);if(a<=0)return i;r=a<18?1:a<48?3:a<144?4:a<768?5:6,n=a<8?new y(t):t.isEven()?new A(t):new g(t);var o=new Array,c=3,u=r-1,l=(1<<r)-1;if(o[1]=n.convert(this),r>1){var p=s();for(n.sqrTo(o[1],p);c<=l;)o[c]=s(),n.mulTo(p,o[c-2],o[c]),c+=2}var f,v,m=e.t-1,C=!0,E=s();for(a=d(e.data[m])-1;m>=0;){for(a>=u?f=e.data[m]>>a-u&l:(f=(e.data[m]&(1<<a+1)-1)<<u-a,m>0&&(f|=e.data[m-1]>>this.DB+a-u)),c=r;0==(1&f);)f>>=1,--c;if((a-=c)<0&&(a+=this.DB,--m),C)o[f].copyTo(i),C=!1;else{for(;c>1;)n.sqrTo(i,E),n.sqrTo(E,i),c-=2;c>0?n.sqrTo(i,E):(v=i,i=E,E=v),n.mulTo(E,o[f],i)}for(;m>=0&&0==(e.data[m]&1<<a);)n.sqrTo(i,E),v=i,i=E,E=v,--a<0&&(a=this.DB-1,--m)}return n.revert(i)},i.prototype.modInverse=function(e){if(0==this.signum())return i.ZERO;var t=e.isEven();if(this.isEven()&&t||0==e.signum())return i.ZERO;for(var r=e.clone(),n=this.clone(),a=h(1),s=h(0),o=h(0),c=h(1);0!=r.signum();){for(;r.isEven();)r.rShiftTo(1,r),t?(a.isEven()&&s.isEven()||(a.addTo(this,a),s.subTo(e,s)),a.rShiftTo(1,a)):s.isEven()||s.subTo(e,s),s.rShiftTo(1,s);for(;n.isEven();)n.rShiftTo(1,n),t?(o.isEven()&&c.isEven()||(o.addTo(this,o),c.subTo(e,c)),o.rShiftTo(1,o)):c.isEven()||c.subTo(e,c),c.rShiftTo(1,c);r.compareTo(n)>=0?(r.subTo(n,r),t&&a.subTo(o,a),s.subTo(c,s)):(n.subTo(r,n),t&&o.subTo(a,o),c.subTo(s,c))}return 0!=n.compareTo(i.ONE)?i.ZERO:c.compareTo(e)>=0?c.subtract(e):c.signum()<0?(c.addTo(e,c),c.signum()<0?c.add(e):c):c},i.prototype.pow=function(e){return this.exp(e,new I)},i.prototype.gcd=function(e){var t=this.s<0?this.negate():this.clone(),r=e.s<0?e.negate():e.clone();if(t.compareTo(r)<0){var n=t;t=r,r=n}var a=t.getLowestSetBit(),i=r.getLowestSetBit();if(i<0)return t;for(a<i&&(i=a),i>0&&(t.rShiftTo(i,t),r.rShiftTo(i,r));t.signum()>0;)(a=t.getLowestSetBit())>0&&t.rShiftTo(a,t),(a=r.getLowestSetBit())>0&&r.rShiftTo(a,r),t.compareTo(r)>=0?(t.subTo(r,t),t.rShiftTo(1,t)):(r.subTo(t,r),r.rShiftTo(1,r));return i>0&&r.lShiftTo(i,r),r},i.prototype.isProbablePrime=function(e){var t,r=this.abs();if(1==r.t&&r.data[0]<=B[B.length-1]){for(t=0;t<B.length;++t)if(r.data[0]==B[t])return!0;return!1}if(r.isEven())return!1;for(t=1;t<B.length;){for(var n=B[t],a=t+1;a<B.length&&n<N;)n*=B[a++];for(n=r.modInt(n);t<a;)if(n%B[t++]==0)return!1}return r.millerRabin(e)},i.prototype.square=function(){var e=s();return this.squareTo(e),e}},function(e,t,r){var n=r(0);r(1),e.exports=n.cipher=n.cipher||{},n.cipher.algorithms=n.cipher.algorithms||{},n.cipher.createCipher=function(e,t){var r=e;if("string"==typeof r&&(r=n.cipher.getAlgorithm(r))&&(r=r()),!r)throw new Error("Unsupported algorithm: "+e);return new n.cipher.BlockCipher({algorithm:r,key:t,decrypt:!1})},n.cipher.createDecipher=function(e,t){var r=e;if("string"==typeof r&&(r=n.cipher.getAlgorithm(r))&&(r=r()),!r)throw new Error("Unsupported algorithm: "+e);return new n.cipher.BlockCipher({algorithm:r,key:t,decrypt:!0})},n.cipher.registerAlgorithm=function(e,t){e=e.toUpperCase(),n.cipher.algorithms[e]=t},n.cipher.getAlgorithm=function(e){return(e=e.toUpperCase())in n.cipher.algorithms?n.cipher.algorithms[e]:null};var a=n.cipher.BlockCipher=function(e){this.algorithm=e.algorithm,this.mode=this.algorithm.mode,this.blockSize=this.mode.blockSize,this._finish=!1,this._input=null,this.output=null,this._op=e.decrypt?this.mode.decrypt:this.mode.encrypt,this._decrypt=e.decrypt,this.algorithm.initialize(e)};a.prototype.start=function(e){e=e||{};var t={};for(var r in e)t[r]=e[r];t.decrypt=this._decrypt,this._finish=!1,this._input=n.util.createBuffer(),this.output=e.output||n.util.createBuffer(),this.mode.start(t)},a.prototype.update=function(e){for(e&&this._input.putBuffer(e);!this._op.call(this.mode,this._input,this.output,this._finish)&&!this._finish;);this._input.compact()},a.prototype.finish=function(e){!e||"ECB"!==this.mode.name&&"CBC"!==this.mode.name||(this.mode.pad=function(t){return e(this.blockSize,t,!1)},this.mode.unpad=function(t){return e(this.blockSize,t,!0)});var t={};return t.decrypt=this._decrypt,t.overflow=this._input.length()%this.blockSize,!(!this._decrypt&&this.mode.pad&&!this.mode.pad(this._input,t))&&(this._finish=!0,this.update(),!(this._decrypt&&this.mode.unpad&&!this.mode.unpad(this.output,t))&&!(this.mode.afterFinish&&!this.mode.afterFinish(this.output,t)))}},function(e,t,r){var n=r(0);r(4),r(1);var a=e.exports=n.md5=n.md5||{};n.md.md5=n.md.algorithms.md5=a,a.create=function(){u||function(){i=String.fromCharCode(128),i+=n.util.fillString(String.fromCharCode(0),64),s=[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],o=[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],c=new Array(64);for(var e=0;e<64;++e)c[e]=Math.floor(4294967296*Math.abs(Math.sin(e+1)));u=!0}();var e=null,t=n.util.createBuffer(),r=new Array(16),a={algorithm:"md5",blockLength:64,digestLength:16,messageLength:0,fullMessageLength:null,messageLengthSize:8,start:function(){a.messageLength=0,a.fullMessageLength=a.messageLength64=[];for(var r=a.messageLengthSize/4,i=0;i<r;++i)a.fullMessageLength.push(0);return t=n.util.createBuffer(),e={h0:1732584193,h1:4023233417,h2:2562383102,h3:271733878},a}};return a.start(),a.update=function(i,s){"utf8"===s&&(i=n.util.encodeUtf8(i));var o=i.length;a.messageLength+=o,o=[o/4294967296>>>0,o>>>0];for(var c=a.fullMessageLength.length-1;c>=0;--c)a.fullMessageLength[c]+=o[1],o[1]=o[0]+(a.fullMessageLength[c]/4294967296>>>0),a.fullMessageLength[c]=a.fullMessageLength[c]>>>0,o[0]=o[1]/4294967296>>>0;return t.putBytes(i),l(e,r,t),(t.read>2048||0===t.length())&&t.compact(),a},a.digest=function(){var s=n.util.createBuffer();s.putBytes(t.bytes());var o=a.fullMessageLength[a.fullMessageLength.length-1]+a.messageLengthSize&a.blockLength-1;s.putBytes(i.substr(0,a.blockLength-o));for(var c,u=0,p=a.fullMessageLength.length-1;p>=0;--p)u=(c=8*a.fullMessageLength[p]+u)/4294967296>>>0,s.putInt32Le(c>>>0);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3};l(f,r,s);var h=n.util.createBuffer();return h.putInt32Le(f.h0),h.putInt32Le(f.h1),h.putInt32Le(f.h2),h.putInt32Le(f.h3),h},a};var i=null,s=null,o=null,c=null,u=!1;function l(e,t,r){for(var n,a,i,u,l,p,f,h=r.length();h>=64;){for(a=e.h0,i=e.h1,u=e.h2,l=e.h3,f=0;f<16;++f)t[f]=r.getInt32Le(),n=a+(l^i&(u^l))+c[f]+t[f],a=l,l=u,u=i,i+=n<<(p=o[f])|n>>>32-p;for(;f<32;++f)n=a+(u^l&(i^u))+c[f]+t[s[f]],a=l,l=u,u=i,i+=n<<(p=o[f])|n>>>32-p;for(;f<48;++f)n=a+(i^u^l)+c[f]+t[s[f]],a=l,l=u,u=i,i+=n<<(p=o[f])|n>>>32-p;for(;f<64;++f)n=a+(u^(i|~l))+c[f]+t[s[f]],a=l,l=u,u=i,i+=n<<(p=o[f])|n>>>32-p;e.h0=e.h0+a|0,e.h1=e.h1+i|0,e.h2=e.h2+u|0,e.h3=e.h3+l|0,h-=64}}},function(e,t,r){var n=r(0);r(8),r(4),r(1);var a,i=n.pkcs5=n.pkcs5||{};n.util.isNodejs&&!n.options.usePureJavaScript&&(a=r(17)),e.exports=n.pbkdf2=i.pbkdf2=function(e,t,r,i,s,o){if("function"==typeof s&&(o=s,s=null),n.util.isNodejs&&!n.options.usePureJavaScript&&a.pbkdf2&&(null===s||"object"!=typeof s)&&(a.pbkdf2Sync.length>4||!s||"sha1"===s))return"string"!=typeof s&&(s="sha1"),e=Buffer.from(e,"binary"),t=Buffer.from(t,"binary"),o?4===a.pbkdf2Sync.length?a.pbkdf2(e,t,r,i,(function(e,t){if(e)return o(e);o(null,t.toString("binary"))})):a.pbkdf2(e,t,r,i,s,(function(e,t){if(e)return o(e);o(null,t.toString("binary"))})):4===a.pbkdf2Sync.length?a.pbkdf2Sync(e,t,r,i).toString("binary"):a.pbkdf2Sync(e,t,r,i,s).toString("binary");if(null==s&&(s="sha1"),"string"==typeof s){if(!(s in n.md.algorithms))throw new Error("Unknown hash algorithm: "+s);s=n.md[s].create()}var c=s.digestLength;if(i>4294967295*c){var u=new Error("Derived key is too long.");if(o)return o(u);throw u}var l=Math.ceil(i/c),p=i-(l-1)*c,f=n.hmac.create();f.start(s,e);var h,d,y,g="";if(!o){for(var v=1;v<=l;++v){f.start(null,null),f.update(t),f.update(n.util.int32ToBytes(v)),h=y=f.digest().getBytes();for(var m=2;m<=r;++m)f.start(null,null),f.update(y),d=f.digest().getBytes(),h=n.util.xorBytes(h,d,c),y=d;g+=v<l?h:h.substr(0,p)}return g}v=1;function C(){if(v>l)return o(null,g);f.start(null,null),f.update(t),f.update(n.util.int32ToBytes(v)),h=y=f.digest().getBytes(),m=2,E()}function E(){if(m<=r)return f.start(null,null),f.update(y),d=f.digest().getBytes(),h=n.util.xorBytes(h,d,c),y=d,++m,n.util.setImmediate(E);g+=v<l?h:h.substr(0,p),++v,C()}C()}},function(e,t){},function(e,t,r){var n=r(0);r(5),r(3),r(11),r(4),r(40),r(6),r(7),r(19),r(12),r(1);var a=n.asn1,i=e.exports=n.pki=n.pki||{},s=i.oids,o={};o.CN=s.commonName,o.commonName="CN",o.C=s.countryName,o.countryName="C",o.L=s.localityName,o.localityName="L",o.ST=s.stateOrProvinceName,o.stateOrProvinceName="ST",o.O=s.organizationName,o.organizationName="O",o.OU=s.organizationalUnitName,o.organizationalUnitName="OU",o.E=s.emailAddress,o.emailAddress="E";var c=n.pki.rsa.publicKeyValidator,u={name:"Certificate",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"Certificate.TBSCertificate",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"tbsCertificate",value:[{name:"Certificate.TBSCertificate.version",tagClass:a.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,value:[{name:"Certificate.TBSCertificate.version.integer",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"certVersion"}]},{name:"Certificate.TBSCertificate.serialNumber",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"certSerialNumber"},{name:"Certificate.TBSCertificate.signature",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"Certificate.TBSCertificate.signature.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"certinfoSignatureOid"},{name:"Certificate.TBSCertificate.signature.parameters",tagClass:a.Class.UNIVERSAL,optional:!0,captureAsn1:"certinfoSignatureParams"}]},{name:"Certificate.TBSCertificate.issuer",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"certIssuer"},{name:"Certificate.TBSCertificate.validity",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"Certificate.TBSCertificate.validity.notBefore (utc)",tagClass:a.Class.UNIVERSAL,type:a.Type.UTCTIME,constructed:!1,optional:!0,capture:"certValidity1UTCTime"},{name:"Certificate.TBSCertificate.validity.notBefore (generalized)",tagClass:a.Class.UNIVERSAL,type:a.Type.GENERALIZEDTIME,constructed:!1,optional:!0,capture:"certValidity2GeneralizedTime"},{name:"Certificate.TBSCertificate.validity.notAfter (utc)",tagClass:a.Class.UNIVERSAL,type:a.Type.UTCTIME,constructed:!1,optional:!0,capture:"certValidity3UTCTime"},{name:"Certificate.TBSCertificate.validity.notAfter (generalized)",tagClass:a.Class.UNIVERSAL,type:a.Type.GENERALIZEDTIME,constructed:!1,optional:!0,capture:"certValidity4GeneralizedTime"}]},{name:"Certificate.TBSCertificate.subject",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"certSubject"},c,{name:"Certificate.TBSCertificate.issuerUniqueID",tagClass:a.Class.CONTEXT_SPECIFIC,type:1,constructed:!0,optional:!0,value:[{name:"Certificate.TBSCertificate.issuerUniqueID.id",tagClass:a.Class.UNIVERSAL,type:a.Type.BITSTRING,constructed:!1,captureBitStringValue:"certIssuerUniqueId"}]},{name:"Certificate.TBSCertificate.subjectUniqueID",tagClass:a.Class.CONTEXT_SPECIFIC,type:2,constructed:!0,optional:!0,value:[{name:"Certificate.TBSCertificate.subjectUniqueID.id",tagClass:a.Class.UNIVERSAL,type:a.Type.BITSTRING,constructed:!1,captureBitStringValue:"certSubjectUniqueId"}]},{name:"Certificate.TBSCertificate.extensions",tagClass:a.Class.CONTEXT_SPECIFIC,type:3,constructed:!0,captureAsn1:"certExtensions",optional:!0}]},{name:"Certificate.signatureAlgorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"Certificate.signatureAlgorithm.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"certSignatureOid"},{name:"Certificate.TBSCertificate.signature.parameters",tagClass:a.Class.UNIVERSAL,optional:!0,captureAsn1:"certSignatureParams"}]},{name:"Certificate.signatureValue",tagClass:a.Class.UNIVERSAL,type:a.Type.BITSTRING,constructed:!1,captureBitStringValue:"certSignature"}]},l={name:"rsapss",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"rsapss.hashAlgorithm",tagClass:a.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,value:[{name:"rsapss.hashAlgorithm.AlgorithmIdentifier",tagClass:a.Class.UNIVERSAL,type:a.Class.SEQUENCE,constructed:!0,optional:!0,value:[{name:"rsapss.hashAlgorithm.AlgorithmIdentifier.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"hashOid"}]}]},{name:"rsapss.maskGenAlgorithm",tagClass:a.Class.CONTEXT_SPECIFIC,type:1,constructed:!0,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier",tagClass:a.Class.UNIVERSAL,type:a.Class.SEQUENCE,constructed:!0,optional:!0,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"maskGenOid"},{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.params",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.params.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"maskGenHashOid"}]}]}]},{name:"rsapss.saltLength",tagClass:a.Class.CONTEXT_SPECIFIC,type:2,optional:!0,value:[{name:"rsapss.saltLength.saltLength",tagClass:a.Class.UNIVERSAL,type:a.Class.INTEGER,constructed:!1,capture:"saltLength"}]},{name:"rsapss.trailerField",tagClass:a.Class.CONTEXT_SPECIFIC,type:3,optional:!0,value:[{name:"rsapss.trailer.trailer",tagClass:a.Class.UNIVERSAL,type:a.Class.INTEGER,constructed:!1,capture:"trailer"}]}]},p={name:"CertificationRequestInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"certificationRequestInfo",value:[{name:"CertificationRequestInfo.integer",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"certificationRequestInfoVersion"},{name:"CertificationRequestInfo.subject",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"certificationRequestInfoSubject"},c,{name:"CertificationRequestInfo.attributes",tagClass:a.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,capture:"certificationRequestInfoAttributes",value:[{name:"CertificationRequestInfo.attributes",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"CertificationRequestInfo.attributes.type",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1},{name:"CertificationRequestInfo.attributes.value",tagClass:a.Class.UNIVERSAL,type:a.Type.SET,constructed:!0}]}]}]},f={name:"CertificationRequest",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"csr",value:[p,{name:"CertificationRequest.signatureAlgorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"CertificationRequest.signatureAlgorithm.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"csrSignatureOid"},{name:"CertificationRequest.signatureAlgorithm.parameters",tagClass:a.Class.UNIVERSAL,optional:!0,captureAsn1:"csrSignatureParams"}]},{name:"CertificationRequest.signature",tagClass:a.Class.UNIVERSAL,type:a.Type.BITSTRING,constructed:!1,captureBitStringValue:"csrSignature"}]};function h(e,t){"string"==typeof t&&(t={shortName:t});for(var r,n=null,a=0;null===n&&a<e.attributes.length;++a)r=e.attributes[a],(t.type&&t.type===r.type||t.name&&t.name===r.name||t.shortName&&t.shortName===r.shortName)&&(n=r);return n}i.RDNAttributesAsArray=function(e,t){for(var r,n,i,c=[],u=0;u<e.value.length;++u){r=e.value[u];for(var l=0;l<r.value.length;++l)i={},n=r.value[l],i.type=a.derToOid(n.value[0].value),i.value=n.value[1].value,i.valueTagClass=n.value[1].type,i.type in s&&(i.name=s[i.type],i.name in o&&(i.shortName=o[i.name])),t&&(t.update(i.type),t.update(i.value)),c.push(i)}return c},i.CRIAttributesAsArray=function(e){for(var t=[],r=0;r<e.length;++r)for(var n=e[r],c=a.derToOid(n.value[0].value),u=n.value[1].value,l=0;l<u.length;++l){var p={};if(p.type=c,p.value=u[l].value,p.valueTagClass=u[l].type,p.type in s&&(p.name=s[p.type],p.name in o&&(p.shortName=o[p.name])),p.type===s.extensionRequest){p.extensions=[];for(var f=0;f<p.value.length;++f)p.extensions.push(i.certificateExtensionFromAsn1(p.value[f]))}t.push(p)}return t};var d=function(e,t,r){var n={};if(e!==s["RSASSA-PSS"])return n;r&&(n={hash:{algorithmOid:s.sha1},mgf:{algorithmOid:s.mgf1,hash:{algorithmOid:s.sha1}},saltLength:20});var i={},o=[];if(!a.validate(t,l,i,o)){var c=new Error("Cannot read RSASSA-PSS parameter block.");throw c.errors=o,c}return void 0!==i.hashOid&&(n.hash=n.hash||{},n.hash.algorithmOid=a.derToOid(i.hashOid)),void 0!==i.maskGenOid&&(n.mgf=n.mgf||{},n.mgf.algorithmOid=a.derToOid(i.maskGenOid),n.mgf.hash=n.mgf.hash||{},n.mgf.hash.algorithmOid=a.derToOid(i.maskGenHashOid)),void 0!==i.saltLength&&(n.saltLength=i.saltLength.charCodeAt(0)),n},y=function(e){switch(s[e.signatureOid]){case"sha1WithRSAEncryption":case"sha1WithRSASignature":return n.md.sha1.create();case"md5WithRSAEncryption":return n.md.md5.create();case"sha256WithRSAEncryption":return n.md.sha256.create();case"sha384WithRSAEncryption":return n.md.sha384.create();case"sha512WithRSAEncryption":return n.md.sha512.create();case"RSASSA-PSS":return n.md.sha256.create();default:var t=new Error("Could not compute "+e.type+" digest. Unknown signature OID.");throw t.signatureOid=e.signatureOid,t}},g=function(e){var t,r=e.certificate;switch(r.signatureOid){case s.sha1WithRSAEncryption:case s.sha1WithRSASignature:break;case s["RSASSA-PSS"]:var a,i,o;if(void 0===(a=s[r.signatureParameters.mgf.hash.algorithmOid])||void 0===n.md[a])throw(o=new Error("Unsupported MGF hash function.")).oid=r.signatureParameters.mgf.hash.algorithmOid,o.name=a,o;if(void 0===(i=s[r.signatureParameters.mgf.algorithmOid])||void 0===n.mgf[i])throw(o=new Error("Unsupported MGF function.")).oid=r.signatureParameters.mgf.algorithmOid,o.name=i,o;if(i=n.mgf[i].create(n.md[a].create()),void 0===(a=s[r.signatureParameters.hash.algorithmOid])||void 0===n.md[a])throw(o=new Error("Unsupported RSASSA-PSS hash function.")).oid=r.signatureParameters.hash.algorithmOid,o.name=a,o;t=n.pss.create(n.md[a].create(),i,r.signatureParameters.saltLength)}return r.publicKey.verify(e.md.digest().getBytes(),e.signature,t)};function v(e){for(var t,r,i=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]),s=e.attributes,o=0;o<s.length;++o){var c=(t=s[o]).value,u=a.Type.PRINTABLESTRING;"valueTagClass"in t&&(u=t.valueTagClass)===a.Type.UTF8&&(c=n.util.encodeUtf8(c)),r=a.create(a.Class.UNIVERSAL,a.Type.SET,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(t.type).getBytes()),a.create(a.Class.UNIVERSAL,u,!1,c)])]),i.value.push(r)}return i}function m(e){for(var t,r=0;r<e.length;++r){if(void 0===(t=e[r]).name&&(t.type&&t.type in i.oids?t.name=i.oids[t.type]:t.shortName&&t.shortName in o&&(t.name=i.oids[o[t.shortName]])),void 0===t.type){if(!t.name||!(t.name in i.oids))throw(c=new Error("Attribute type not specified.")).attribute=t,c;t.type=i.oids[t.name]}if(void 0===t.shortName&&t.name&&t.name in o&&(t.shortName=o[t.name]),t.type===s.extensionRequest&&(t.valueConstructed=!0,t.valueTagClass=a.Type.SEQUENCE,!t.value&&t.extensions)){t.value=[];for(var n=0;n<t.extensions.length;++n)t.value.push(i.certificateExtensionToAsn1(C(t.extensions[n])))}var c;if(void 0===t.value)throw(c=new Error("Attribute value not specified.")).attribute=t,c}}function C(e,t){if(t=t||{},void 0===e.name&&e.id&&e.id in i.oids&&(e.name=i.oids[e.id]),void 0===e.id){if(!e.name||!(e.name in i.oids))throw(S=new Error("Extension ID not specified.")).extension=e,S;e.id=i.oids[e.name]}if(void 0!==e.value)return e;if("keyUsage"===e.name){var r=0,o=0,c=0;e.digitalSignature&&(o|=128,r=7),e.nonRepudiation&&(o|=64,r=6),e.keyEncipherment&&(o|=32,r=5),e.dataEncipherment&&(o|=16,r=4),e.keyAgreement&&(o|=8,r=3),e.keyCertSign&&(o|=4,r=2),e.cRLSign&&(o|=2,r=1),e.encipherOnly&&(o|=1,r=0),e.decipherOnly&&(c|=128,r=7);var u=String.fromCharCode(r);0!==c?u+=String.fromCharCode(o)+String.fromCharCode(c):0!==o&&(u+=String.fromCharCode(o)),e.value=a.create(a.Class.UNIVERSAL,a.Type.BITSTRING,!1,u)}else if("basicConstraints"===e.name)e.value=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]),e.cA&&e.value.value.push(a.create(a.Class.UNIVERSAL,a.Type.BOOLEAN,!1,String.fromCharCode(255))),"pathLenConstraint"in e&&e.value.value.push(a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(e.pathLenConstraint).getBytes()));else if("extKeyUsage"===e.name){e.value=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]);var l=e.value.value;for(var p in e)!0===e[p]&&(p in s?l.push(a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(s[p]).getBytes())):-1!==p.indexOf(".")&&l.push(a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(p).getBytes())))}else if("nsCertType"===e.name){r=0,o=0;e.client&&(o|=128,r=7),e.server&&(o|=64,r=6),e.email&&(o|=32,r=5),e.objsign&&(o|=16,r=4),e.reserved&&(o|=8,r=3),e.sslCA&&(o|=4,r=2),e.emailCA&&(o|=2,r=1),e.objCA&&(o|=1,r=0);u=String.fromCharCode(r);0!==o&&(u+=String.fromCharCode(o)),e.value=a.create(a.Class.UNIVERSAL,a.Type.BITSTRING,!1,u)}else if("subjectAltName"===e.name||"issuerAltName"===e.name){e.value=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]);for(var f=0;f<e.altNames.length;++f){u=(m=e.altNames[f]).value;if(7===m.type&&m.ip){if(null===(u=n.util.bytesFromIP(m.ip)))throw(S=new Error('Extension "ip" value is not a valid IPv4 or IPv6 address.')).extension=e,S}else 8===m.type&&(u=m.oid?a.oidToDer(a.oidToDer(m.oid)):a.oidToDer(u));e.value.value.push(a.create(a.Class.CONTEXT_SPECIFIC,m.type,!1,u))}}else if("nsComment"===e.name&&t.cert){if(!/^[\x00-\x7F]*$/.test(e.comment)||e.comment.length<1||e.comment.length>128)throw new Error('Invalid "nsComment" content.');e.value=a.create(a.Class.UNIVERSAL,a.Type.IA5STRING,!1,e.comment)}else if("subjectKeyIdentifier"===e.name&&t.cert){var h=t.cert.generateSubjectKeyIdentifier();e.subjectKeyIdentifier=h.toHex(),e.value=a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,h.getBytes())}else if("authorityKeyIdentifier"===e.name&&t.cert){e.value=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]);l=e.value.value;if(e.keyIdentifier){var d=!0===e.keyIdentifier?t.cert.generateSubjectKeyIdentifier().getBytes():e.keyIdentifier;l.push(a.create(a.Class.CONTEXT_SPECIFIC,0,!1,d))}if(e.authorityCertIssuer){var y=[a.create(a.Class.CONTEXT_SPECIFIC,4,!0,[v(!0===e.authorityCertIssuer?t.cert.issuer:e.authorityCertIssuer)])];l.push(a.create(a.Class.CONTEXT_SPECIFIC,1,!0,y))}if(e.serialNumber){var g=n.util.hexToBytes(!0===e.serialNumber?t.cert.serialNumber:e.serialNumber);l.push(a.create(a.Class.CONTEXT_SPECIFIC,2,!1,g))}}else if("cRLDistributionPoints"===e.name){e.value=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]);l=e.value.value;var m,C=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]),E=a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[]);for(f=0;f<e.altNames.length;++f){u=(m=e.altNames[f]).value;if(7===m.type&&m.ip){if(null===(u=n.util.bytesFromIP(m.ip)))throw(S=new Error('Extension "ip" value is not a valid IPv4 or IPv6 address.')).extension=e,S}else 8===m.type&&(u=m.oid?a.oidToDer(a.oidToDer(m.oid)):a.oidToDer(u));E.value.push(a.create(a.Class.CONTEXT_SPECIFIC,m.type,!1,u))}C.value.push(a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[E])),l.push(C)}var S;if(void 0===e.value)throw(S=new Error("Extension value not specified.")).extension=e,S;return e}function E(e,t){switch(e){case s["RSASSA-PSS"]:var r=[];return void 0!==t.hash.algorithmOid&&r.push(a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(t.hash.algorithmOid).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")])])),void 0!==t.mgf.algorithmOid&&r.push(a.create(a.Class.CONTEXT_SPECIFIC,1,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(t.mgf.algorithmOid).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(t.mgf.hash.algorithmOid).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")])])])),void 0!==t.saltLength&&r.push(a.create(a.Class.CONTEXT_SPECIFIC,2,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(t.saltLength).getBytes())])),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,r);default:return a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")}}function S(e){var t=a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[]);if(0===e.attributes.length)return t;for(var r=e.attributes,i=0;i<r.length;++i){var s=r[i],o=s.value,c=a.Type.UTF8;"valueTagClass"in s&&(c=s.valueTagClass),c===a.Type.UTF8&&(o=n.util.encodeUtf8(o));var u=!1;"valueConstructed"in s&&(u=s.valueConstructed);var l=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(s.type).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SET,!0,[a.create(a.Class.UNIVERSAL,c,u,o)])]);t.value.push(l)}return t}i.certificateFromPem=function(e,t,r){var s=n.pem.decode(e)[0];if("CERTIFICATE"!==s.type&&"X509 CERTIFICATE"!==s.type&&"TRUSTED CERTIFICATE"!==s.type){var o=new Error('Could not convert certificate from PEM; PEM header type is not "CERTIFICATE", "X509 CERTIFICATE", or "TRUSTED CERTIFICATE".');throw o.headerType=s.type,o}if(s.procType&&"ENCRYPTED"===s.procType.type)throw new Error("Could not convert certificate from PEM; PEM is encrypted.");var c=a.fromDer(s.body,r);return i.certificateFromAsn1(c,t)},i.certificateToPem=function(e,t){var r={type:"CERTIFICATE",body:a.toDer(i.certificateToAsn1(e)).getBytes()};return n.pem.encode(r,{maxline:t})},i.publicKeyFromPem=function(e){var t=n.pem.decode(e)[0];if("PUBLIC KEY"!==t.type&&"RSA PUBLIC KEY"!==t.type){var r=new Error('Could not convert public key from PEM; PEM header type is not "PUBLIC KEY" or "RSA PUBLIC KEY".');throw r.headerType=t.type,r}if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert public key from PEM; PEM is encrypted.");var s=a.fromDer(t.body);return i.publicKeyFromAsn1(s)},i.publicKeyToPem=function(e,t){var r={type:"PUBLIC KEY",body:a.toDer(i.publicKeyToAsn1(e)).getBytes()};return n.pem.encode(r,{maxline:t})},i.publicKeyToRSAPublicKeyPem=function(e,t){var r={type:"RSA PUBLIC KEY",body:a.toDer(i.publicKeyToRSAPublicKey(e)).getBytes()};return n.pem.encode(r,{maxline:t})},i.getPublicKeyFingerprint=function(e,t){var r,s=(t=t||{}).md||n.md.sha1.create();switch(t.type||"RSAPublicKey"){case"RSAPublicKey":r=a.toDer(i.publicKeyToRSAPublicKey(e)).getBytes();break;case"SubjectPublicKeyInfo":r=a.toDer(i.publicKeyToAsn1(e)).getBytes();break;default:throw new Error('Unknown fingerprint type "'+t.type+'".')}s.start(),s.update(r);var o=s.digest();if("hex"===t.encoding){var c=o.toHex();return t.delimiter?c.match(/.{2}/g).join(t.delimiter):c}if("binary"===t.encoding)return o.getBytes();if(t.encoding)throw new Error('Unknown encoding "'+t.encoding+'".');return o},i.certificationRequestFromPem=function(e,t,r){var s=n.pem.decode(e)[0];if("CERTIFICATE REQUEST"!==s.type){var o=new Error('Could not convert certification request from PEM; PEM header type is not "CERTIFICATE REQUEST".');throw o.headerType=s.type,o}if(s.procType&&"ENCRYPTED"===s.procType.type)throw new Error("Could not convert certification request from PEM; PEM is encrypted.");var c=a.fromDer(s.body,r);return i.certificationRequestFromAsn1(c,t)},i.certificationRequestToPem=function(e,t){var r={type:"CERTIFICATE REQUEST",body:a.toDer(i.certificationRequestToAsn1(e)).getBytes()};return n.pem.encode(r,{maxline:t})},i.createCertificate=function(){var e={version:2,serialNumber:"00",signatureOid:null,signature:null,siginfo:{}};return e.siginfo.algorithmOid=null,e.validity={},e.validity.notBefore=new Date,e.validity.notAfter=new Date,e.issuer={},e.issuer.getField=function(t){return h(e.issuer,t)},e.issuer.addField=function(t){m([t]),e.issuer.attributes.push(t)},e.issuer.attributes=[],e.issuer.hash=null,e.subject={},e.subject.getField=function(t){return h(e.subject,t)},e.subject.addField=function(t){m([t]),e.subject.attributes.push(t)},e.subject.attributes=[],e.subject.hash=null,e.extensions=[],e.publicKey=null,e.md=null,e.setSubject=function(t,r){m(t),e.subject.attributes=t,delete e.subject.uniqueId,r&&(e.subject.uniqueId=r),e.subject.hash=null},e.setIssuer=function(t,r){m(t),e.issuer.attributes=t,delete e.issuer.uniqueId,r&&(e.issuer.uniqueId=r),e.issuer.hash=null},e.setExtensions=function(t){for(var r=0;r<t.length;++r)C(t[r],{cert:e});e.extensions=t},e.getExtension=function(t){"string"==typeof t&&(t={name:t});for(var r,n=null,a=0;null===n&&a<e.extensions.length;++a)r=e.extensions[a],(t.id&&r.id===t.id||t.name&&r.name===t.name)&&(n=r);return n},e.sign=function(t,r){e.md=r||n.md.sha1.create();var o=s[e.md.algorithm+"WithRSAEncryption"];if(!o){var c=new Error("Could not compute certificate digest. Unknown message digest algorithm OID.");throw c.algorithm=e.md.algorithm,c}e.signatureOid=e.siginfo.algorithmOid=o,e.tbsCertificate=i.getTBSCertificate(e);var u=a.toDer(e.tbsCertificate);e.md.update(u.getBytes()),e.signature=t.sign(e.md)},e.verify=function(t){var r=!1;if(!e.issued(t)){var n=t.issuer,s=e.subject,o=new Error("The parent certificate did not issue the given child certificate; the child certificate's issuer does not match the parent's subject.");throw o.expectedIssuer=s.attributes,o.actualIssuer=n.attributes,o}var c=t.md;if(null===c){c=y({signatureOid:t.signatureOid,type:"certificate"});var u=t.tbsCertificate||i.getTBSCertificate(t),l=a.toDer(u);c.update(l.getBytes())}return null!==c&&(r=g({certificate:e,md:c,signature:t.signature})),r},e.isIssuer=function(t){var r=!1,n=e.issuer,a=t.subject;if(n.hash&&a.hash)r=n.hash===a.hash;else if(n.attributes.length===a.attributes.length){var i,s;r=!0;for(var o=0;r&&o<n.attributes.length;++o)i=n.attributes[o],s=a.attributes[o],i.type===s.type&&i.value===s.value||(r=!1)}return r},e.issued=function(t){return t.isIssuer(e)},e.generateSubjectKeyIdentifier=function(){return i.getPublicKeyFingerprint(e.publicKey,{type:"RSAPublicKey"})},e.verifySubjectKeyIdentifier=function(){for(var t=s.subjectKeyIdentifier,r=0;r<e.extensions.length;++r){var a=e.extensions[r];if(a.id===t){var i=e.generateSubjectKeyIdentifier().getBytes();return n.util.hexToBytes(a.subjectKeyIdentifier)===i}}return!1},e},i.certificateFromAsn1=function(e,t){var r={},s=[];if(!a.validate(e,u,r,s)){var o=new Error("Cannot read X.509 certificate. ASN.1 object is not an X509v3 Certificate.");throw o.errors=s,o}if(a.derToOid(r.publicKeyOid)!==i.oids.rsaEncryption)throw new Error("Cannot read public key. OID is not RSA.");var c=i.createCertificate();c.version=r.certVersion?r.certVersion.charCodeAt(0):0;var l=n.util.createBuffer(r.certSerialNumber);c.serialNumber=l.toHex(),c.signatureOid=n.asn1.derToOid(r.certSignatureOid),c.signatureParameters=d(c.signatureOid,r.certSignatureParams,!0),c.siginfo.algorithmOid=n.asn1.derToOid(r.certinfoSignatureOid),c.siginfo.parameters=d(c.siginfo.algorithmOid,r.certinfoSignatureParams,!1),c.signature=r.certSignature;var p=[];if(void 0!==r.certValidity1UTCTime&&p.push(a.utcTimeToDate(r.certValidity1UTCTime)),void 0!==r.certValidity2GeneralizedTime&&p.push(a.generalizedTimeToDate(r.certValidity2GeneralizedTime)),void 0!==r.certValidity3UTCTime&&p.push(a.utcTimeToDate(r.certValidity3UTCTime)),void 0!==r.certValidity4GeneralizedTime&&p.push(a.generalizedTimeToDate(r.certValidity4GeneralizedTime)),p.length>2)throw new Error("Cannot read notBefore/notAfter validity times; more than two times were provided in the certificate.");if(p.length<2)throw new Error("Cannot read notBefore/notAfter validity times; they were not provided as either UTCTime or GeneralizedTime.");if(c.validity.notBefore=p[0],c.validity.notAfter=p[1],c.tbsCertificate=r.tbsCertificate,t){c.md=y({signatureOid:c.signatureOid,type:"certificate"});var f=a.toDer(c.tbsCertificate);c.md.update(f.getBytes())}var g=n.md.sha1.create(),v=a.toDer(r.certIssuer);g.update(v.getBytes()),c.issuer.getField=function(e){return h(c.issuer,e)},c.issuer.addField=function(e){m([e]),c.issuer.attributes.push(e)},c.issuer.attributes=i.RDNAttributesAsArray(r.certIssuer),r.certIssuerUniqueId&&(c.issuer.uniqueId=r.certIssuerUniqueId),c.issuer.hash=g.digest().toHex();var C=n.md.sha1.create(),E=a.toDer(r.certSubject);return C.update(E.getBytes()),c.subject.getField=function(e){return h(c.subject,e)},c.subject.addField=function(e){m([e]),c.subject.attributes.push(e)},c.subject.attributes=i.RDNAttributesAsArray(r.certSubject),r.certSubjectUniqueId&&(c.subject.uniqueId=r.certSubjectUniqueId),c.subject.hash=C.digest().toHex(),r.certExtensions?c.extensions=i.certificateExtensionsFromAsn1(r.certExtensions):c.extensions=[],c.publicKey=i.publicKeyFromAsn1(r.subjectPublicKeyInfo),c},i.certificateExtensionsFromAsn1=function(e){for(var t=[],r=0;r<e.value.length;++r)for(var n=e.value[r],a=0;a<n.value.length;++a)t.push(i.certificateExtensionFromAsn1(n.value[a]));return t},i.certificateExtensionFromAsn1=function(e){var t={};if(t.id=a.derToOid(e.value[0].value),t.critical=!1,e.value[1].type===a.Type.BOOLEAN?(t.critical=0!==e.value[1].value.charCodeAt(0),t.value=e.value[2].value):t.value=e.value[1].value,t.id in s)if(t.name=s[t.id],"keyUsage"===t.name){var r=0,i=0;(c=a.fromDer(t.value)).value.length>1&&(r=c.value.charCodeAt(1),i=c.value.length>2?c.value.charCodeAt(2):0),t.digitalSignature=128==(128&r),t.nonRepudiation=64==(64&r),t.keyEncipherment=32==(32&r),t.dataEncipherment=16==(16&r),t.keyAgreement=8==(8&r),t.keyCertSign=4==(4&r),t.cRLSign=2==(2&r),t.encipherOnly=1==(1&r),t.decipherOnly=128==(128&i)}else if("basicConstraints"===t.name){(c=a.fromDer(t.value)).value.length>0&&c.value[0].type===a.Type.BOOLEAN?t.cA=0!==c.value[0].value.charCodeAt(0):t.cA=!1;var o=null;c.value.length>0&&c.value[0].type===a.Type.INTEGER?o=c.value[0].value:c.value.length>1&&(o=c.value[1].value),null!==o&&(t.pathLenConstraint=a.derToInteger(o))}else if("extKeyUsage"===t.name)for(var c=a.fromDer(t.value),u=0;u<c.value.length;++u){var l=a.derToOid(c.value[u].value);l in s?t[s[l]]=!0:t[l]=!0}else if("nsCertType"===t.name){r=0;(c=a.fromDer(t.value)).value.length>1&&(r=c.value.charCodeAt(1)),t.client=128==(128&r),t.server=64==(64&r),t.email=32==(32&r),t.objsign=16==(16&r),t.reserved=8==(8&r),t.sslCA=4==(4&r),t.emailCA=2==(2&r),t.objCA=1==(1&r)}else if("subjectAltName"===t.name||"issuerAltName"===t.name){var p;t.altNames=[];c=a.fromDer(t.value);for(var f=0;f<c.value.length;++f){var h={type:(p=c.value[f]).type,value:p.value};switch(t.altNames.push(h),p.type){case 1:case 2:case 6:break;case 7:h.ip=n.util.bytesToIP(p.value);break;case 8:h.oid=a.derToOid(p.value)}}}else if("subjectKeyIdentifier"===t.name){c=a.fromDer(t.value);t.subjectKeyIdentifier=n.util.bytesToHex(c.value)}return t},i.certificationRequestFromAsn1=function(e,t){var r={},s=[];if(!a.validate(e,f,r,s)){var o=new Error("Cannot read PKCS#10 certificate request. ASN.1 object is not a PKCS#10 CertificationRequest.");throw o.errors=s,o}if(a.derToOid(r.publicKeyOid)!==i.oids.rsaEncryption)throw new Error("Cannot read public key. OID is not RSA.");var c=i.createCertificationRequest();if(c.version=r.csrVersion?r.csrVersion.charCodeAt(0):0,c.signatureOid=n.asn1.derToOid(r.csrSignatureOid),c.signatureParameters=d(c.signatureOid,r.csrSignatureParams,!0),c.siginfo.algorithmOid=n.asn1.derToOid(r.csrSignatureOid),c.siginfo.parameters=d(c.siginfo.algorithmOid,r.csrSignatureParams,!1),c.signature=r.csrSignature,c.certificationRequestInfo=r.certificationRequestInfo,t){c.md=y({signatureOid:c.signatureOid,type:"certification request"});var u=a.toDer(c.certificationRequestInfo);c.md.update(u.getBytes())}var l=n.md.sha1.create();return c.subject.getField=function(e){return h(c.subject,e)},c.subject.addField=function(e){m([e]),c.subject.attributes.push(e)},c.subject.attributes=i.RDNAttributesAsArray(r.certificationRequestInfoSubject,l),c.subject.hash=l.digest().toHex(),c.publicKey=i.publicKeyFromAsn1(r.subjectPublicKeyInfo),c.getAttribute=function(e){return h(c,e)},c.addAttribute=function(e){m([e]),c.attributes.push(e)},c.attributes=i.CRIAttributesAsArray(r.certificationRequestInfoAttributes||[]),c},i.createCertificationRequest=function(){var e={version:0,signatureOid:null,signature:null,siginfo:{}};return e.siginfo.algorithmOid=null,e.subject={},e.subject.getField=function(t){return h(e.subject,t)},e.subject.addField=function(t){m([t]),e.subject.attributes.push(t)},e.subject.attributes=[],e.subject.hash=null,e.publicKey=null,e.attributes=[],e.getAttribute=function(t){return h(e,t)},e.addAttribute=function(t){m([t]),e.attributes.push(t)},e.md=null,e.setSubject=function(t){m(t),e.subject.attributes=t,e.subject.hash=null},e.setAttributes=function(t){m(t),e.attributes=t},e.sign=function(t,r){e.md=r||n.md.sha1.create();var o=s[e.md.algorithm+"WithRSAEncryption"];if(!o){var c=new Error("Could not compute certification request digest. Unknown message digest algorithm OID.");throw c.algorithm=e.md.algorithm,c}e.signatureOid=e.siginfo.algorithmOid=o,e.certificationRequestInfo=i.getCertificationRequestInfo(e);var u=a.toDer(e.certificationRequestInfo);e.md.update(u.getBytes()),e.signature=t.sign(e.md)},e.verify=function(){var t=!1,r=e.md;if(null===r){r=y({signatureOid:e.signatureOid,type:"certification request"});var n=e.certificationRequestInfo||i.getCertificationRequestInfo(e),s=a.toDer(n);r.update(s.getBytes())}return null!==r&&(t=g({certificate:e,md:r,signature:e.signature})),t},e};var T=new Date("1950-01-01T00:00:00Z"),I=new Date("2050-01-01T00:00:00Z");function b(e){return e>=T&&e<I?a.create(a.Class.UNIVERSAL,a.Type.UTCTIME,!1,a.dateToUtcTime(e)):a.create(a.Class.UNIVERSAL,a.Type.GENERALIZEDTIME,!1,a.dateToGeneralizedTime(e))}i.getTBSCertificate=function(e){var t=b(e.validity.notBefore),r=b(e.validity.notAfter),s=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(e.version).getBytes())]),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,n.util.hexToBytes(e.serialNumber)),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.siginfo.algorithmOid).getBytes()),E(e.siginfo.algorithmOid,e.siginfo.parameters)]),v(e.issuer),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[t,r]),v(e.subject),i.publicKeyToAsn1(e.publicKey)]);return e.issuer.uniqueId&&s.value.push(a.create(a.Class.CONTEXT_SPECIFIC,1,!0,[a.create(a.Class.UNIVERSAL,a.Type.BITSTRING,!1,String.fromCharCode(0)+e.issuer.uniqueId)])),e.subject.uniqueId&&s.value.push(a.create(a.Class.CONTEXT_SPECIFIC,2,!0,[a.create(a.Class.UNIVERSAL,a.Type.BITSTRING,!1,String.fromCharCode(0)+e.subject.uniqueId)])),e.extensions.length>0&&s.value.push(i.certificateExtensionsToAsn1(e.extensions)),s},i.getCertificationRequestInfo=function(e){return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(e.version).getBytes()),v(e.subject),i.publicKeyToAsn1(e.publicKey),S(e)])},i.distinguishedNameToAsn1=function(e){return v(e)},i.certificateToAsn1=function(e){var t=e.tbsCertificate||i.getTBSCertificate(e);return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[t,a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.signatureOid).getBytes()),E(e.signatureOid,e.signatureParameters)]),a.create(a.Class.UNIVERSAL,a.Type.BITSTRING,!1,String.fromCharCode(0)+e.signature)])},i.certificateExtensionsToAsn1=function(e){var t=a.create(a.Class.CONTEXT_SPECIFIC,3,!0,[]),r=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]);t.value.push(r);for(var n=0;n<e.length;++n)r.value.push(i.certificateExtensionToAsn1(e[n]));return t},i.certificateExtensionToAsn1=function(e){var t=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[]);t.value.push(a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.id).getBytes())),e.critical&&t.value.push(a.create(a.Class.UNIVERSAL,a.Type.BOOLEAN,!1,String.fromCharCode(255)));var r=e.value;return"string"!=typeof e.value&&(r=a.toDer(r).getBytes()),t.value.push(a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,r)),t},i.certificationRequestToAsn1=function(e){var t=e.certificationRequestInfo||i.getCertificationRequestInfo(e);return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[t,a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.signatureOid).getBytes()),E(e.signatureOid,e.signatureParameters)]),a.create(a.Class.UNIVERSAL,a.Type.BITSTRING,!1,String.fromCharCode(0)+e.signature)])},i.createCaStore=function(e){var t={certs:{}};function r(e){return s(e),t.certs[e.hash]||null}function s(e){if(!e.hash){var t=n.md.sha1.create();e.attributes=i.RDNAttributesAsArray(v(e),t),e.hash=t.digest().toHex()}}if(t.getIssuer=function(e){return r(e.issuer)},t.addCertificate=function(e){if("string"==typeof e&&(e=n.pki.certificateFromPem(e)),s(e.subject),!t.hasCertificate(e))if(e.subject.hash in t.certs){var r=t.certs[e.subject.hash];n.util.isArray(r)||(r=[r]),r.push(e),t.certs[e.subject.hash]=r}else t.certs[e.subject.hash]=e},t.hasCertificate=function(e){"string"==typeof e&&(e=n.pki.certificateFromPem(e));var t=r(e.subject);if(!t)return!1;n.util.isArray(t)||(t=[t]);for(var s=a.toDer(i.certificateToAsn1(e)).getBytes(),o=0;o<t.length;++o){if(s===a.toDer(i.certificateToAsn1(t[o])).getBytes())return!0}return!1},t.listAllCertificates=function(){var e=[];for(var r in t.certs)if(t.certs.hasOwnProperty(r)){var a=t.certs[r];if(n.util.isArray(a))for(var i=0;i<a.length;++i)e.push(a[i]);else e.push(a)}return e},t.removeCertificate=function(e){var o;if("string"==typeof e&&(e=n.pki.certificateFromPem(e)),s(e.subject),!t.hasCertificate(e))return null;var c=r(e.subject);if(!n.util.isArray(c))return o=t.certs[e.subject.hash],delete t.certs[e.subject.hash],o;for(var u=a.toDer(i.certificateToAsn1(e)).getBytes(),l=0;l<c.length;++l){u===a.toDer(i.certificateToAsn1(c[l])).getBytes()&&(o=c[l],c.splice(l,1))}return 0===c.length&&delete t.certs[e.subject.hash],o},e)for(var o=0;o<e.length;++o){var c=e[o];t.addCertificate(c)}return t},i.certificateError={bad_certificate:"forge.pki.BadCertificate",unsupported_certificate:"forge.pki.UnsupportedCertificate",certificate_revoked:"forge.pki.CertificateRevoked",certificate_expired:"forge.pki.CertificateExpired",certificate_unknown:"forge.pki.CertificateUnknown",unknown_ca:"forge.pki.UnknownCertificateAuthority"},i.verifyCertificateChain=function(e,t,r){"function"==typeof r&&(r={verify:r}),r=r||{};var a=(t=t.slice(0)).slice(0),s=r.validityCheckDate;void 0===s&&(s=new Date);var o=!0,c=null,u=0;do{var l=t.shift(),p=null,f=!1;if(s&&(s<l.validity.notBefore||s>l.validity.notAfter)&&(c={message:"Certificate is not valid yet or has expired.",error:i.certificateError.certificate_expired,notBefore:l.validity.notBefore,notAfter:l.validity.notAfter,now:s}),null===c){if(null===(p=t[0]||e.getIssuer(l))&&l.isIssuer(l)&&(f=!0,p=l),p){var h=p;n.util.isArray(h)||(h=[h]);for(var d=!1;!d&&h.length>0;){p=h.shift();try{d=p.verify(l)}catch(e){}}d||(c={message:"Certificate signature is invalid.",error:i.certificateError.bad_certificate})}null!==c||p&&!f||e.hasCertificate(l)||(c={message:"Certificate is not trusted.",error:i.certificateError.unknown_ca})}if(null===c&&p&&!l.isIssuer(p)&&(c={message:"Certificate issuer is invalid.",error:i.certificateError.bad_certificate}),null===c)for(var y={keyUsage:!0,basicConstraints:!0},g=0;null===c&&g<l.extensions.length;++g){var v=l.extensions[g];v.critical&&!(v.name in y)&&(c={message:"Certificate has an unsupported critical extension.",error:i.certificateError.unsupported_certificate})}if(null===c&&(!o||0===t.length&&(!p||f))){var m=l.getExtension("basicConstraints"),C=l.getExtension("keyUsage");if(null!==C&&(C.keyCertSign&&null!==m||(c={message:"Certificate keyUsage or basicConstraints conflict or indicate that the certificate is not a CA. If the certificate is the only one in the chain or isn't the first then the certificate must be a valid CA.",error:i.certificateError.bad_certificate})),null===c&&null===m&&(c={message:"Certificate is missing basicConstraints extension and cannot be used as a CA.",error:i.certificateError.bad_certificate}),null!==c||null===m||m.cA||(c={message:"Certificate basicConstraints indicates the certificate is not a CA.",error:i.certificateError.bad_certificate}),null===c&&null!==C&&"pathLenConstraint"in m)u-1>m.pathLenConstraint&&(c={message:"Certificate basicConstraints pathLenConstraint violated.",error:i.certificateError.bad_certificate})}var E=null===c||c.error,S=r.verify?r.verify(E,u,a):E;if(!0!==S)throw!0===E&&(c={message:"The application rejected the certificate.",error:i.certificateError.bad_certificate}),(S||0===S)&&("object"!=typeof S||n.util.isArray(S)?"string"==typeof S&&(c.error=S):(S.message&&(c.message=S.message),S.error&&(c.error=S.error))),c;c=null,o=!1,++u}while(t.length>0);return!0}},function(e,t,r){var n=r(0);r(2),r(1),(e.exports=n.pss=n.pss||{}).create=function(e){3===arguments.length&&(e={md:arguments[0],mgf:arguments[1],saltLength:arguments[2]});var t,r=e.md,a=e.mgf,i=r.digestLength,s=e.salt||null;if("string"==typeof s&&(s=n.util.createBuffer(s)),"saltLength"in e)t=e.saltLength;else{if(null===s)throw new Error("Salt length not specified or specific salt not given.");t=s.length()}if(null!==s&&s.length()!==t)throw new Error("Given salt length does not match length of given salt.");var o=e.prng||n.random,c={encode:function(e,c){var u,l,p=c-1,f=Math.ceil(p/8),h=e.digest().getBytes();if(f<i+t+2)throw new Error("Message is too long to encrypt.");l=null===s?o.getBytesSync(t):s.bytes();var d=new n.util.ByteBuffer;d.fillWithByte(0,8),d.putBytes(h),d.putBytes(l),r.start(),r.update(d.getBytes());var y=r.digest().getBytes(),g=new n.util.ByteBuffer;g.fillWithByte(0,f-t-i-2),g.putByte(1),g.putBytes(l);var v=g.getBytes(),m=f-i-1,C=a.generate(y,m),E="";for(u=0;u<m;u++)E+=String.fromCharCode(v.charCodeAt(u)^C.charCodeAt(u));var S=65280>>8*f-p&255;return(E=String.fromCharCode(E.charCodeAt(0)&~S)+E.substr(1))+y+String.fromCharCode(188)},verify:function(e,s,o){var c,u=o-1,l=Math.ceil(u/8);if(s=s.substr(-l),l<i+t+2)throw new Error("Inconsistent parameters to PSS signature verification.");if(188!==s.charCodeAt(l-1))throw new Error("Encoded message does not end in 0xBC.");var p=l-i-1,f=s.substr(0,p),h=s.substr(p,i),d=65280>>8*l-u&255;if(0!=(f.charCodeAt(0)&d))throw new Error("Bits beyond keysize not zero as expected.");var y=a.generate(h,p),g="";for(c=0;c<p;c++)g+=String.fromCharCode(f.charCodeAt(c)^y.charCodeAt(c));g=String.fromCharCode(g.charCodeAt(0)&~d)+g.substr(1);var v=l-i-t-2;for(c=0;c<v;c++)if(0!==g.charCodeAt(c))throw new Error("Leftmost octets not zero as expected");if(1!==g.charCodeAt(v))throw new Error("Inconsistent PSS signature, 0x01 marker not found");var m=g.substr(-t),C=new n.util.ByteBuffer;return C.fillWithByte(0,8),C.putBytes(e),C.putBytes(m),r.start(),r.update(C.getBytes()),h===r.digest().getBytes()}};return c}},function(e,t,r){var n=r(0);r(1),n.cipher=n.cipher||{};var a=e.exports=n.cipher.modes=n.cipher.modes||{};function i(e,t){if("string"==typeof e&&(e=n.util.createBuffer(e)),n.util.isArray(e)&&e.length>4){var r=e;e=n.util.createBuffer();for(var a=0;a<r.length;++a)e.putByte(r[a])}if(e.length()<t)throw new Error("Invalid IV length; got "+e.length()+" bytes and expected "+t+" bytes.");if(!n.util.isArray(e)){var i=[],s=t/4;for(a=0;a<s;++a)i.push(e.getInt32());e=i}return e}function s(e){e[e.length-1]=e[e.length-1]+1&4294967295}function o(e){return[e/4294967296|0,4294967295&e]}a.ecb=function(e){e=e||{},this.name="ECB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints)},a.ecb.prototype.start=function(e){},a.ecb.prototype.encrypt=function(e,t,r){if(e.length()<this.blockSize&&!(r&&e.length()>0))return!0;for(var n=0;n<this._ints;++n)this._inBlock[n]=e.getInt32();this.cipher.encrypt(this._inBlock,this._outBlock);for(n=0;n<this._ints;++n)t.putInt32(this._outBlock[n])},a.ecb.prototype.decrypt=function(e,t,r){if(e.length()<this.blockSize&&!(r&&e.length()>0))return!0;for(var n=0;n<this._ints;++n)this._inBlock[n]=e.getInt32();this.cipher.decrypt(this._inBlock,this._outBlock);for(n=0;n<this._ints;++n)t.putInt32(this._outBlock[n])},a.ecb.prototype.pad=function(e,t){var r=e.length()===this.blockSize?this.blockSize:this.blockSize-e.length();return e.fillWithByte(r,r),!0},a.ecb.prototype.unpad=function(e,t){if(t.overflow>0)return!1;var r=e.length(),n=e.at(r-1);return!(n>this.blockSize<<2)&&(e.truncate(n),!0)},a.cbc=function(e){e=e||{},this.name="CBC",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints)},a.cbc.prototype.start=function(e){if(null===e.iv){if(!this._prev)throw new Error("Invalid IV parameter.");this._iv=this._prev.slice(0)}else{if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._prev=this._iv.slice(0)}},a.cbc.prototype.encrypt=function(e,t,r){if(e.length()<this.blockSize&&!(r&&e.length()>0))return!0;for(var n=0;n<this._ints;++n)this._inBlock[n]=this._prev[n]^e.getInt32();this.cipher.encrypt(this._inBlock,this._outBlock);for(n=0;n<this._ints;++n)t.putInt32(this._outBlock[n]);this._prev=this._outBlock},a.cbc.prototype.decrypt=function(e,t,r){if(e.length()<this.blockSize&&!(r&&e.length()>0))return!0;for(var n=0;n<this._ints;++n)this._inBlock[n]=e.getInt32();this.cipher.decrypt(this._inBlock,this._outBlock);for(n=0;n<this._ints;++n)t.putInt32(this._prev[n]^this._outBlock[n]);this._prev=this._inBlock.slice(0)},a.cbc.prototype.pad=function(e,t){var r=e.length()===this.blockSize?this.blockSize:this.blockSize-e.length();return e.fillWithByte(r,r),!0},a.cbc.prototype.unpad=function(e,t){if(t.overflow>0)return!1;var r=e.length(),n=e.at(r-1);return!(n>this.blockSize<<2)&&(e.truncate(n),!0)},a.cfb=function(e){e=e||{},this.name="CFB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialBlock=new Array(this._ints),this._partialOutput=n.util.createBuffer(),this._partialBytes=0},a.cfb.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},a.cfb.prototype.encrypt=function(e,t,r){var n=e.length();if(0===n)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&n>=this.blockSize)for(var a=0;a<this._ints;++a)this._inBlock[a]=e.getInt32()^this._outBlock[a],t.putInt32(this._inBlock[a]);else{var i=(this.blockSize-n)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(a=0;a<this._ints;++a)this._partialBlock[a]=e.getInt32()^this._outBlock[a],this._partialOutput.putInt32(this._partialBlock[a]);if(i>0)e.read-=this.blockSize;else for(a=0;a<this._ints;++a)this._inBlock[a]=this._partialBlock[a];if(this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0}},a.cfb.prototype.decrypt=function(e,t,r){var n=e.length();if(0===n)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&n>=this.blockSize)for(var a=0;a<this._ints;++a)this._inBlock[a]=e.getInt32(),t.putInt32(this._inBlock[a]^this._outBlock[a]);else{var i=(this.blockSize-n)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(a=0;a<this._ints;++a)this._partialBlock[a]=e.getInt32(),this._partialOutput.putInt32(this._partialBlock[a]^this._outBlock[a]);if(i>0)e.read-=this.blockSize;else for(a=0;a<this._ints;++a)this._inBlock[a]=this._partialBlock[a];if(this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0}},a.ofb=function(e){e=e||{},this.name="OFB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=n.util.createBuffer(),this._partialBytes=0},a.ofb.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},a.ofb.prototype.encrypt=function(e,t,r){var n=e.length();if(0===e.length())return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&n>=this.blockSize)for(var a=0;a<this._ints;++a)t.putInt32(e.getInt32()^this._outBlock[a]),this._inBlock[a]=this._outBlock[a];else{var i=(this.blockSize-n)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(a=0;a<this._ints;++a)this._partialOutput.putInt32(e.getInt32()^this._outBlock[a]);if(i>0)e.read-=this.blockSize;else for(a=0;a<this._ints;++a)this._inBlock[a]=this._outBlock[a];if(this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0}},a.ofb.prototype.decrypt=a.ofb.prototype.encrypt,a.ctr=function(e){e=e||{},this.name="CTR",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=n.util.createBuffer(),this._partialBytes=0},a.ctr.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},a.ctr.prototype.encrypt=function(e,t,r){var n=e.length();if(0===n)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&n>=this.blockSize)for(var a=0;a<this._ints;++a)t.putInt32(e.getInt32()^this._outBlock[a]);else{var i=(this.blockSize-n)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(a=0;a<this._ints;++a)this._partialOutput.putInt32(e.getInt32()^this._outBlock[a]);if(i>0&&(e.read-=this.blockSize),this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0}s(this._inBlock)},a.ctr.prototype.decrypt=a.ctr.prototype.encrypt,a.gcm=function(e){e=e||{},this.name="GCM",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints),this._partialOutput=n.util.createBuffer(),this._partialBytes=0,this._R=3774873600},a.gcm.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");var t,r=n.util.createBuffer(e.iv);if(this._cipherLength=0,t="additionalData"in e?n.util.createBuffer(e.additionalData):n.util.createBuffer(),this._tagLength="tagLength"in e?e.tagLength:128,this._tag=null,e.decrypt&&(this._tag=n.util.createBuffer(e.tag).getBytes(),this._tag.length!==this._tagLength/8))throw new Error("Authentication tag does not match tag length.");this._hashBlock=new Array(this._ints),this.tag=null,this._hashSubkey=new Array(this._ints),this.cipher.encrypt([0,0,0,0],this._hashSubkey),this.componentBits=4,this._m=this.generateHashTable(this._hashSubkey,this.componentBits);var a=r.length();if(12===a)this._j0=[r.getInt32(),r.getInt32(),r.getInt32(),1];else{for(this._j0=[0,0,0,0];r.length()>0;)this._j0=this.ghash(this._hashSubkey,this._j0,[r.getInt32(),r.getInt32(),r.getInt32(),r.getInt32()]);this._j0=this.ghash(this._hashSubkey,this._j0,[0,0].concat(o(8*a)))}this._inBlock=this._j0.slice(0),s(this._inBlock),this._partialBytes=0,t=n.util.createBuffer(t),this._aDataLength=o(8*t.length());var i=t.length()%this.blockSize;for(i&&t.fillWithByte(0,this.blockSize-i),this._s=[0,0,0,0];t.length()>0;)this._s=this.ghash(this._hashSubkey,this._s,[t.getInt32(),t.getInt32(),t.getInt32(),t.getInt32()])},a.gcm.prototype.encrypt=function(e,t,r){var n=e.length();if(0===n)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&n>=this.blockSize){for(var a=0;a<this._ints;++a)t.putInt32(this._outBlock[a]^=e.getInt32());this._cipherLength+=this.blockSize}else{var i=(this.blockSize-n)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(a=0;a<this._ints;++a)this._partialOutput.putInt32(e.getInt32()^this._outBlock[a]);if(i<=0||r){if(r){var o=n%this.blockSize;this._cipherLength+=o,this._partialOutput.truncate(this.blockSize-o)}else this._cipherLength+=this.blockSize;for(a=0;a<this._ints;++a)this._outBlock[a]=this._partialOutput.getInt32();this._partialOutput.read-=this.blockSize}if(this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return e.read-=this.blockSize,t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0}this._s=this.ghash(this._hashSubkey,this._s,this._outBlock),s(this._inBlock)},a.gcm.prototype.decrypt=function(e,t,r){var n=e.length();if(n<this.blockSize&&!(r&&n>0))return!0;this.cipher.encrypt(this._inBlock,this._outBlock),s(this._inBlock),this._hashBlock[0]=e.getInt32(),this._hashBlock[1]=e.getInt32(),this._hashBlock[2]=e.getInt32(),this._hashBlock[3]=e.getInt32(),this._s=this.ghash(this._hashSubkey,this._s,this._hashBlock);for(var a=0;a<this._ints;++a)t.putInt32(this._outBlock[a]^this._hashBlock[a]);n<this.blockSize?this._cipherLength+=n%this.blockSize:this._cipherLength+=this.blockSize},a.gcm.prototype.afterFinish=function(e,t){var r=!0;t.decrypt&&t.overflow&&e.truncate(this.blockSize-t.overflow),this.tag=n.util.createBuffer();var a=this._aDataLength.concat(o(8*this._cipherLength));this._s=this.ghash(this._hashSubkey,this._s,a);var i=[];this.cipher.encrypt(this._j0,i);for(var s=0;s<this._ints;++s)this.tag.putInt32(this._s[s]^i[s]);return this.tag.truncate(this.tag.length()%(this._tagLength/8)),t.decrypt&&this.tag.bytes()!==this._tag&&(r=!1),r},a.gcm.prototype.multiply=function(e,t){for(var r=[0,0,0,0],n=t.slice(0),a=0;a<128;++a){e[a/32|0]&1<<31-a%32&&(r[0]^=n[0],r[1]^=n[1],r[2]^=n[2],r[3]^=n[3]),this.pow(n,n)}return r},a.gcm.prototype.pow=function(e,t){for(var r=1&e[3],n=3;n>0;--n)t[n]=e[n]>>>1|(1&e[n-1])<<31;t[0]=e[0]>>>1,r&&(t[0]^=this._R)},a.gcm.prototype.tableMultiply=function(e){for(var t=[0,0,0,0],r=0;r<32;++r){var n=e[r/8|0]>>>4*(7-r%8)&15,a=this._m[r][n];t[0]^=a[0],t[1]^=a[1],t[2]^=a[2],t[3]^=a[3]}return t},a.gcm.prototype.ghash=function(e,t,r){return t[0]^=r[0],t[1]^=r[1],t[2]^=r[2],t[3]^=r[3],this.tableMultiply(t)},a.gcm.prototype.generateHashTable=function(e,t){for(var r=8/t,n=4*r,a=16*r,i=new Array(a),s=0;s<a;++s){var o=[0,0,0,0],c=(n-1-s%n)*t;o[s/n|0]=1<<t-1<<c,i[s]=this.generateSubHashTable(this.multiply(o,e),t)}return i},a.gcm.prototype.generateSubHashTable=function(e,t){var r=1<<t,n=r>>>1,a=new Array(r);a[n]=e.slice(0);for(var i=n>>>1;i>0;)this.pow(a[2*i],a[i]=[]),i>>=1;for(i=2;i<n;){for(var s=1;s<i;++s){var o=a[i],c=a[s];a[i+s]=[o[0]^c[0],o[1]^c[1],o[2]^c[2],o[3]^c[3]]}i*=2}for(a[0]=[0,0,0,0],i=n+1;i<r;++i){var u=a[i^n];a[i]=[e[0]^u[0],e[1]^u[1],e[2]^u[2],e[3]^u[3]]}return a}},function(e,t,r){var n=r(0);r(3),r(6),r(22),r(7),r(16),r(28),r(19),r(12),r(1),r(18);var a=n.asn1,i=e.exports=n.pki=n.pki||{};i.pemToDer=function(e){var t=n.pem.decode(e)[0];if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert PEM to DER; PEM is encrypted.");return n.util.createBuffer(t.body)},i.privateKeyFromPem=function(e){var t=n.pem.decode(e)[0];if("PRIVATE KEY"!==t.type&&"RSA PRIVATE KEY"!==t.type){var r=new Error('Could not convert private key from PEM; PEM header type is not "PRIVATE KEY" or "RSA PRIVATE KEY".');throw r.headerType=t.type,r}if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert private key from PEM; PEM is encrypted.");var s=a.fromDer(t.body);return i.privateKeyFromAsn1(s)},i.privateKeyToPem=function(e,t){var r={type:"RSA PRIVATE KEY",body:a.toDer(i.privateKeyToAsn1(e)).getBytes()};return n.pem.encode(r,{maxline:t})},i.privateKeyInfoToPem=function(e,t){var r={type:"PRIVATE KEY",body:a.toDer(e).getBytes()};return n.pem.encode(r,{maxline:t})}},function(e,t,r){var n=r(0);if(r(5),r(3),r(11),r(4),r(6),r(16),r(7),r(2),r(25),r(12),r(1),void 0===a)var a=n.jsbn.BigInteger;var i=n.asn1,s=n.pki=n.pki||{};e.exports=s.pbe=n.pbe=n.pbe||{};var o=s.oids,c={name:"EncryptedPrivateKeyInfo",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedPrivateKeyInfo.encryptionAlgorithm",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:i.Class.UNIVERSAL,type:i.Type.OID,constructed:!1,capture:"encryptionOid"},{name:"AlgorithmIdentifier.parameters",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,captureAsn1:"encryptionParams"}]},{name:"EncryptedPrivateKeyInfo.encryptedData",tagClass:i.Class.UNIVERSAL,type:i.Type.OCTETSTRING,constructed:!1,capture:"encryptedData"}]},u={name:"PBES2Algorithms",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.keyDerivationFunc",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.keyDerivationFunc.oid",tagClass:i.Class.UNIVERSAL,type:i.Type.OID,constructed:!1,capture:"kdfOid"},{name:"PBES2Algorithms.params",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.params.salt",tagClass:i.Class.UNIVERSAL,type:i.Type.OCTETSTRING,constructed:!1,capture:"kdfSalt"},{name:"PBES2Algorithms.params.iterationCount",tagClass:i.Class.UNIVERSAL,type:i.Type.INTEGER,constructed:!1,capture:"kdfIterationCount"},{name:"PBES2Algorithms.params.keyLength",tagClass:i.Class.UNIVERSAL,type:i.Type.INTEGER,constructed:!1,optional:!0,capture:"keyLength"},{name:"PBES2Algorithms.params.prf",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,optional:!0,value:[{name:"PBES2Algorithms.params.prf.algorithm",tagClass:i.Class.UNIVERSAL,type:i.Type.OID,constructed:!1,capture:"prfOid"}]}]}]},{name:"PBES2Algorithms.encryptionScheme",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.encryptionScheme.oid",tagClass:i.Class.UNIVERSAL,type:i.Type.OID,constructed:!1,capture:"encOid"},{name:"PBES2Algorithms.encryptionScheme.iv",tagClass:i.Class.UNIVERSAL,type:i.Type.OCTETSTRING,constructed:!1,capture:"encIv"}]}]},l={name:"pkcs-12PbeParams",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"pkcs-12PbeParams.salt",tagClass:i.Class.UNIVERSAL,type:i.Type.OCTETSTRING,constructed:!1,capture:"salt"},{name:"pkcs-12PbeParams.iterations",tagClass:i.Class.UNIVERSAL,type:i.Type.INTEGER,constructed:!1,capture:"iterations"}]};function p(e,t){return e.start().update(t).digest().getBytes()}function f(e){var t;if(e){if(!(t=s.oids[i.derToOid(e)])){var r=new Error("Unsupported PRF OID.");throw r.oid=e,r.supported=["hmacWithSHA1","hmacWithSHA224","hmacWithSHA256","hmacWithSHA384","hmacWithSHA512"],r}}else t="hmacWithSHA1";return h(t)}function h(e){var t=n.md;switch(e){case"hmacWithSHA224":t=n.md.sha512;case"hmacWithSHA1":case"hmacWithSHA256":case"hmacWithSHA384":case"hmacWithSHA512":e=e.substr(8).toLowerCase();break;default:var r=new Error("Unsupported PRF algorithm.");throw r.algorithm=e,r.supported=["hmacWithSHA1","hmacWithSHA224","hmacWithSHA256","hmacWithSHA384","hmacWithSHA512"],r}if(!t||!(e in t))throw new Error("Unknown hash algorithm: "+e);return t[e].create()}s.encryptPrivateKeyInfo=function(e,t,r){(r=r||{}).saltSize=r.saltSize||8,r.count=r.count||2048,r.algorithm=r.algorithm||"aes128",r.prfAlgorithm=r.prfAlgorithm||"sha1";var a,c,u,l=n.random.getBytesSync(r.saltSize),p=r.count,f=i.integerToDer(p);if(0===r.algorithm.indexOf("aes")||"des"===r.algorithm){var d,y,g;switch(r.algorithm){case"aes128":a=16,d=16,y=o["aes128-CBC"],g=n.aes.createEncryptionCipher;break;case"aes192":a=24,d=16,y=o["aes192-CBC"],g=n.aes.createEncryptionCipher;break;case"aes256":a=32,d=16,y=o["aes256-CBC"],g=n.aes.createEncryptionCipher;break;case"des":a=8,d=8,y=o.desCBC,g=n.des.createEncryptionCipher;break;default:throw(T=new Error("Cannot encrypt private key. Unknown encryption algorithm.")).algorithm=r.algorithm,T}var v="hmacWith"+r.prfAlgorithm.toUpperCase(),m=h(v),C=n.pkcs5.pbkdf2(t,l,p,a,m),E=n.random.getBytesSync(d);(I=g(C)).start(E),I.update(i.toDer(e)),I.finish(),u=I.output.getBytes();var S=function(e,t,r,a){var o=i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OCTETSTRING,!1,e),i.create(i.Class.UNIVERSAL,i.Type.INTEGER,!1,t.getBytes())]);"hmacWithSHA1"!==a&&o.value.push(i.create(i.Class.UNIVERSAL,i.Type.INTEGER,!1,n.util.hexToBytes(r.toString(16))),i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(s.oids[a]).getBytes()),i.create(i.Class.UNIVERSAL,i.Type.NULL,!1,"")]));return o}(l,f,a,v);c=i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(o.pkcs5PBES2).getBytes()),i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(o.pkcs5PBKDF2).getBytes()),S]),i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(y).getBytes()),i.create(i.Class.UNIVERSAL,i.Type.OCTETSTRING,!1,E)])])])}else{var T;if("3des"!==r.algorithm)throw(T=new Error("Cannot encrypt private key. Unknown encryption algorithm.")).algorithm=r.algorithm,T;a=24;var I,b=new n.util.ByteBuffer(l);C=s.pbe.generatePkcs12Key(t,b,1,p,a),E=s.pbe.generatePkcs12Key(t,b,2,p,a);(I=n.des.createEncryptionCipher(C)).start(E),I.update(i.toDer(e)),I.finish(),u=I.output.getBytes(),c=i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(o["pbeWithSHAAnd3-KeyTripleDES-CBC"]).getBytes()),i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OCTETSTRING,!1,l),i.create(i.Class.UNIVERSAL,i.Type.INTEGER,!1,f.getBytes())])])}return i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[c,i.create(i.Class.UNIVERSAL,i.Type.OCTETSTRING,!1,u)])},s.decryptPrivateKeyInfo=function(e,t){var r=null,a={},o=[];if(!i.validate(e,c,a,o)){var u=new Error("Cannot read encrypted private key. ASN.1 object is not a supported EncryptedPrivateKeyInfo.");throw u.errors=o,u}var l=i.derToOid(a.encryptionOid),p=s.pbe.getCipher(l,a.encryptionParams,t),f=n.util.createBuffer(a.encryptedData);return p.update(f),p.finish()&&(r=i.fromDer(p.output)),r},s.encryptedPrivateKeyToPem=function(e,t){var r={type:"ENCRYPTED PRIVATE KEY",body:i.toDer(e).getBytes()};return n.pem.encode(r,{maxline:t})},s.encryptedPrivateKeyFromPem=function(e){var t=n.pem.decode(e)[0];if("ENCRYPTED PRIVATE KEY"!==t.type){var r=new Error('Could not convert encrypted private key from PEM; PEM header type is "ENCRYPTED PRIVATE KEY".');throw r.headerType=t.type,r}if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert encrypted private key from PEM; PEM is encrypted.");return i.fromDer(t.body)},s.encryptRsaPrivateKey=function(e,t,r){if(!(r=r||{}).legacy){var a=s.wrapRsaPrivateKey(s.privateKeyToAsn1(e));return a=s.encryptPrivateKeyInfo(a,t,r),s.encryptedPrivateKeyToPem(a)}var o,c,u,l;switch(r.algorithm){case"aes128":o="AES-128-CBC",u=16,c=n.random.getBytesSync(16),l=n.aes.createEncryptionCipher;break;case"aes192":o="AES-192-CBC",u=24,c=n.random.getBytesSync(16),l=n.aes.createEncryptionCipher;break;case"aes256":o="AES-256-CBC",u=32,c=n.random.getBytesSync(16),l=n.aes.createEncryptionCipher;break;case"3des":o="DES-EDE3-CBC",u=24,c=n.random.getBytesSync(8),l=n.des.createEncryptionCipher;break;case"des":o="DES-CBC",u=8,c=n.random.getBytesSync(8),l=n.des.createEncryptionCipher;break;default:var p=new Error('Could not encrypt RSA private key; unsupported encryption algorithm "'+r.algorithm+'".');throw p.algorithm=r.algorithm,p}var f=l(n.pbe.opensslDeriveBytes(t,c.substr(0,8),u));f.start(c),f.update(i.toDer(s.privateKeyToAsn1(e))),f.finish();var h={type:"RSA PRIVATE KEY",procType:{version:"4",type:"ENCRYPTED"},dekInfo:{algorithm:o,parameters:n.util.bytesToHex(c).toUpperCase()},body:f.output.getBytes()};return n.pem.encode(h)},s.decryptRsaPrivateKey=function(e,t){var r=null,a=n.pem.decode(e)[0];if("ENCRYPTED PRIVATE KEY"!==a.type&&"PRIVATE KEY"!==a.type&&"RSA PRIVATE KEY"!==a.type)throw(u=new Error('Could not convert private key from PEM; PEM header type is not "ENCRYPTED PRIVATE KEY", "PRIVATE KEY", or "RSA PRIVATE KEY".')).headerType=u,u;if(a.procType&&"ENCRYPTED"===a.procType.type){var o,c;switch(a.dekInfo.algorithm){case"DES-CBC":o=8,c=n.des.createDecryptionCipher;break;case"DES-EDE3-CBC":o=24,c=n.des.createDecryptionCipher;break;case"AES-128-CBC":o=16,c=n.aes.createDecryptionCipher;break;case"AES-192-CBC":o=24,c=n.aes.createDecryptionCipher;break;case"AES-256-CBC":o=32,c=n.aes.createDecryptionCipher;break;case"RC2-40-CBC":o=5,c=function(e){return n.rc2.createDecryptionCipher(e,40)};break;case"RC2-64-CBC":o=8,c=function(e){return n.rc2.createDecryptionCipher(e,64)};break;case"RC2-128-CBC":o=16,c=function(e){return n.rc2.createDecryptionCipher(e,128)};break;default:var u;throw(u=new Error('Could not decrypt private key; unsupported encryption algorithm "'+a.dekInfo.algorithm+'".')).algorithm=a.dekInfo.algorithm,u}var l=n.util.hexToBytes(a.dekInfo.parameters),p=c(n.pbe.opensslDeriveBytes(t,l.substr(0,8),o));if(p.start(l),p.update(n.util.createBuffer(a.body)),!p.finish())return r;r=p.output.getBytes()}else r=a.body;return null!==(r="ENCRYPTED PRIVATE KEY"===a.type?s.decryptPrivateKeyInfo(i.fromDer(r),t):i.fromDer(r))&&(r=s.privateKeyFromAsn1(r)),r},s.pbe.generatePkcs12Key=function(e,t,r,a,i,s){var o,c;if(null==s){if(!("sha1"in n.md))throw new Error('"sha1" hash algorithm unavailable.');s=n.md.sha1.create()}var u=s.digestLength,l=s.blockLength,p=new n.util.ByteBuffer,f=new n.util.ByteBuffer;if(null!=e){for(c=0;c<e.length;c++)f.putInt16(e.charCodeAt(c));f.putInt16(0)}var h=f.length(),d=t.length(),y=new n.util.ByteBuffer;y.fillWithByte(r,l);var g=l*Math.ceil(d/l),v=new n.util.ByteBuffer;for(c=0;c<g;c++)v.putByte(t.at(c%d));var m=l*Math.ceil(h/l),C=new n.util.ByteBuffer;for(c=0;c<m;c++)C.putByte(f.at(c%h));var E=v;E.putBuffer(C);for(var S=Math.ceil(i/u),T=1;T<=S;T++){var I=new n.util.ByteBuffer;I.putBytes(y.bytes()),I.putBytes(E.bytes());for(var b=0;b<a;b++)s.start(),s.update(I.getBytes()),I=s.digest();var A=new n.util.ByteBuffer;for(c=0;c<l;c++)A.putByte(I.at(c%u));var B=Math.ceil(d/l)+Math.ceil(h/l),N=new n.util.ByteBuffer;for(o=0;o<B;o++){var k=new n.util.ByteBuffer(E.getBytes(l)),w=511;for(c=A.length()-1;c>=0;c--)w>>=8,w+=A.at(c)+k.at(c),k.setAt(c,255&w);N.putBuffer(k)}E=N,p.putBuffer(I)}return p.truncate(p.length()-i),p},s.pbe.getCipher=function(e,t,r){switch(e){case s.oids.pkcs5PBES2:return s.pbe.getCipherForPBES2(e,t,r);case s.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:case s.oids["pbewithSHAAnd40BitRC2-CBC"]:return s.pbe.getCipherForPKCS12PBE(e,t,r);default:var n=new Error("Cannot read encrypted PBE data block. Unsupported OID.");throw n.oid=e,n.supportedOids=["pkcs5PBES2","pbeWithSHAAnd3-KeyTripleDES-CBC","pbewithSHAAnd40BitRC2-CBC"],n}},s.pbe.getCipherForPBES2=function(e,t,r){var a,o={},c=[];if(!i.validate(t,u,o,c))throw(a=new Error("Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.")).errors=c,a;if((e=i.derToOid(o.kdfOid))!==s.oids.pkcs5PBKDF2)throw(a=new Error("Cannot read encrypted private key. Unsupported key derivation function OID.")).oid=e,a.supportedOids=["pkcs5PBKDF2"],a;if((e=i.derToOid(o.encOid))!==s.oids["aes128-CBC"]&&e!==s.oids["aes192-CBC"]&&e!==s.oids["aes256-CBC"]&&e!==s.oids["des-EDE3-CBC"]&&e!==s.oids.desCBC)throw(a=new Error("Cannot read encrypted private key. Unsupported encryption scheme OID.")).oid=e,a.supportedOids=["aes128-CBC","aes192-CBC","aes256-CBC","des-EDE3-CBC","desCBC"],a;var l,p,h=o.kdfSalt,d=n.util.createBuffer(o.kdfIterationCount);switch(d=d.getInt(d.length()<<3),s.oids[e]){case"aes128-CBC":l=16,p=n.aes.createDecryptionCipher;break;case"aes192-CBC":l=24,p=n.aes.createDecryptionCipher;break;case"aes256-CBC":l=32,p=n.aes.createDecryptionCipher;break;case"des-EDE3-CBC":l=24,p=n.des.createDecryptionCipher;break;case"desCBC":l=8,p=n.des.createDecryptionCipher}var y=f(o.prfOid),g=n.pkcs5.pbkdf2(r,h,d,l,y),v=o.encIv,m=p(g);return m.start(v),m},s.pbe.getCipherForPKCS12PBE=function(e,t,r){var a={},o=[];if(!i.validate(t,l,a,o))throw(y=new Error("Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.")).errors=o,y;var c,u,p,h=n.util.createBuffer(a.salt),d=n.util.createBuffer(a.iterations);switch(d=d.getInt(d.length()<<3),e){case s.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:c=24,u=8,p=n.des.startDecrypting;break;case s.oids["pbewithSHAAnd40BitRC2-CBC"]:c=5,u=8,p=function(e,t){var r=n.rc2.createDecryptionCipher(e,40);return r.start(t,null),r};break;default:var y;throw(y=new Error("Cannot read PKCS #12 PBE data block. Unsupported OID.")).oid=e,y}var g=f(a.prfOid),v=s.pbe.generatePkcs12Key(r,h,1,d,c,g);return g.start(),p(v,s.pbe.generatePkcs12Key(r,h,2,d,u,g))},s.pbe.opensslDeriveBytes=function(e,t,r,a){if(null==a){if(!("md5"in n.md))throw new Error('"md5" hash algorithm unavailable.');a=n.md.md5.create()}null===t&&(t="");for(var i=[p(a,e+t)],s=16,o=1;s<r;++o,s+=16)i.push(p(a,i[o-1]+e+t));return i.join("").substr(0,r)}},function(e,t,r){var n=r(0);r(4),r(1);var a=e.exports=n.sha256=n.sha256||{};n.md.sha256=n.md.algorithms.sha256=a,a.create=function(){s||(i=String.fromCharCode(128),i+=n.util.fillString(String.fromCharCode(0),64),o=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],s=!0);var e=null,t=n.util.createBuffer(),r=new Array(64),a={algorithm:"sha256",blockLength:64,digestLength:32,messageLength:0,fullMessageLength:null,messageLengthSize:8,start:function(){a.messageLength=0,a.fullMessageLength=a.messageLength64=[];for(var r=a.messageLengthSize/4,i=0;i<r;++i)a.fullMessageLength.push(0);return t=n.util.createBuffer(),e={h0:1779033703,h1:3144134277,h2:1013904242,h3:2773480762,h4:1359893119,h5:2600822924,h6:528734635,h7:1541459225},a}};return a.start(),a.update=function(i,s){"utf8"===s&&(i=n.util.encodeUtf8(i));var o=i.length;a.messageLength+=o,o=[o/4294967296>>>0,o>>>0];for(var u=a.fullMessageLength.length-1;u>=0;--u)a.fullMessageLength[u]+=o[1],o[1]=o[0]+(a.fullMessageLength[u]/4294967296>>>0),a.fullMessageLength[u]=a.fullMessageLength[u]>>>0,o[0]=o[1]/4294967296>>>0;return t.putBytes(i),c(e,r,t),(t.read>2048||0===t.length())&&t.compact(),a},a.digest=function(){var s=n.util.createBuffer();s.putBytes(t.bytes());var o,u=a.fullMessageLength[a.fullMessageLength.length-1]+a.messageLengthSize&a.blockLength-1;s.putBytes(i.substr(0,a.blockLength-u));for(var l=8*a.fullMessageLength[0],p=0;p<a.fullMessageLength.length-1;++p)l+=(o=8*a.fullMessageLength[p+1])/4294967296>>>0,s.putInt32(l>>>0),l=o>>>0;s.putInt32(l);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3,h4:e.h4,h5:e.h5,h6:e.h6,h7:e.h7};c(f,r,s);var h=n.util.createBuffer();return h.putInt32(f.h0),h.putInt32(f.h1),h.putInt32(f.h2),h.putInt32(f.h3),h.putInt32(f.h4),h.putInt32(f.h5),h.putInt32(f.h6),h.putInt32(f.h7),h},a};var i=null,s=!1,o=null;function c(e,t,r){for(var n,a,i,s,c,u,l,p,f,h,d,y,g,v=r.length();v>=64;){for(c=0;c<16;++c)t[c]=r.getInt32();for(;c<64;++c)n=((n=t[c-2])>>>17|n<<15)^(n>>>19|n<<13)^n>>>10,a=((a=t[c-15])>>>7|a<<25)^(a>>>18|a<<14)^a>>>3,t[c]=n+t[c-7]+a+t[c-16]|0;for(u=e.h0,l=e.h1,p=e.h2,f=e.h3,h=e.h4,d=e.h5,y=e.h6,g=e.h7,c=0;c<64;++c)i=(u>>>2|u<<30)^(u>>>13|u<<19)^(u>>>22|u<<10),s=u&l|p&(u^l),n=g+((h>>>6|h<<26)^(h>>>11|h<<21)^(h>>>25|h<<7))+(y^h&(d^y))+o[c]+t[c],g=y,y=d,d=h,h=f+n>>>0,f=p,p=l,l=u,u=n+(a=i+s)>>>0;e.h0=e.h0+u|0,e.h1=e.h1+l|0,e.h2=e.h2+p|0,e.h3=e.h3+f|0,e.h4=e.h4+h|0,e.h5=e.h5+d|0,e.h6=e.h6+y|0,e.h7=e.h7+g|0,v-=64}}},function(e,t,r){var n=r(0);r(1);var a=null;!n.util.isNodejs||n.options.usePureJavaScript||process.versions["node-webkit"]||(a=r(17)),(e.exports=n.prng=n.prng||{}).create=function(e){for(var t={plugin:e,key:null,seed:null,time:null,reseeds:0,generated:0,keyBytes:""},r=e.md,i=new Array(32),s=0;s<32;++s)i[s]=r.create();function o(){if(t.pools[0].messageLength>=32)return c();var e=32-t.pools[0].messageLength<<5;t.collect(t.seedFileSync(e)),c()}function c(){t.reseeds=4294967295===t.reseeds?0:t.reseeds+1;var e=t.plugin.md.create();e.update(t.keyBytes);for(var r=1,n=0;n<32;++n)t.reseeds%r==0&&(e.update(t.pools[n].digest().getBytes()),t.pools[n].start()),r<<=1;t.keyBytes=e.digest().getBytes(),e.start(),e.update(t.keyBytes);var a=e.digest().getBytes();t.key=t.plugin.formatKey(t.keyBytes),t.seed=t.plugin.formatSeed(a),t.generated=0}function u(e){var t=null,r=n.util.globalScope,a=r.crypto||r.msCrypto;a&&a.getRandomValues&&(t=function(e){return a.getRandomValues(e)});var i=n.util.createBuffer();if(t)for(;i.length()<e;){var s=Math.max(1,Math.min(e-i.length(),65536)/4),o=new Uint32Array(Math.floor(s));try{t(o);for(var c=0;c<o.length;++c)i.putInt32(o[c])}catch(e){if(!("undefined"!=typeof QuotaExceededError&&e instanceof QuotaExceededError))throw e}}if(i.length()<e)for(var u,l,p,f=Math.floor(65536*Math.random());i.length()<e;){l=16807*(65535&f),l+=(32767&(u=16807*(f>>16)))<<16,f=4294967295&(l=(2147483647&(l+=u>>15))+(l>>31));for(c=0;c<3;++c)p=f>>>(c<<3),p^=Math.floor(256*Math.random()),i.putByte(255&p)}return i.getBytes(e)}return t.pools=i,t.pool=0,t.generate=function(e,r){if(!r)return t.generateSync(e);var a=t.plugin.cipher,i=t.plugin.increment,s=t.plugin.formatKey,o=t.plugin.formatSeed,u=n.util.createBuffer();t.key=null,function l(p){if(p)return r(p);if(u.length()>=e)return r(null,u.getBytes(e));t.generated>1048575&&(t.key=null);if(null===t.key)return n.util.nextTick((function(){!function(e){if(t.pools[0].messageLength>=32)return c(),e();var r=32-t.pools[0].messageLength<<5;t.seedFile(r,(function(r,n){if(r)return e(r);t.collect(n),c(),e()}))}(l)}));var f=a(t.key,t.seed);t.generated+=f.length,u.putBytes(f),t.key=s(a(t.key,i(t.seed))),t.seed=o(a(t.key,t.seed)),n.util.setImmediate(l)}()},t.generateSync=function(e){var r=t.plugin.cipher,a=t.plugin.increment,i=t.plugin.formatKey,s=t.plugin.formatSeed;t.key=null;for(var c=n.util.createBuffer();c.length()<e;){t.generated>1048575&&(t.key=null),null===t.key&&o();var u=r(t.key,t.seed);t.generated+=u.length,c.putBytes(u),t.key=i(r(t.key,a(t.seed))),t.seed=s(r(t.key,t.seed))}return c.getBytes(e)},a?(t.seedFile=function(e,t){a.randomBytes(e,(function(e,r){if(e)return t(e);t(null,r.toString())}))},t.seedFileSync=function(e){return a.randomBytes(e).toString()}):(t.seedFile=function(e,t){try{t(null,u(e))}catch(e){t(e)}},t.seedFileSync=u),t.collect=function(e){for(var r=e.length,n=0;n<r;++n)t.pools[t.pool].update(e.substr(n,1)),t.pool=31===t.pool?0:t.pool+1},t.collectInt=function(e,r){for(var n="",a=0;a<r;a+=8)n+=String.fromCharCode(e>>a&255);t.collect(n)},t.registerWorker=function(e){if(e===self)t.seedFile=function(e,t){self.addEventListener("message",(function e(r){var n=r.data;n.forge&&n.forge.prng&&(self.removeEventListener("message",e),t(n.forge.prng.err,n.forge.prng.bytes))})),self.postMessage({forge:{prng:{needed:e}}})};else{e.addEventListener("message",(function(r){var n=r.data;n.forge&&n.forge.prng&&t.seedFile(n.forge.prng.needed,(function(t,r){e.postMessage({forge:{prng:{err:t,bytes:r}}})}))}))}},t}},function(e,t,r){var n=r(0);r(1);var a=[217,120,249,196,25,221,181,237,40,233,253,121,74,160,216,157,198,126,55,131,43,118,83,142,98,76,100,136,68,139,251,162,23,154,89,245,135,179,79,19,97,69,109,141,9,129,125,50,189,143,64,235,134,183,123,11,240,149,33,34,92,107,78,130,84,214,101,147,206,96,178,28,115,86,192,20,167,140,241,220,18,117,202,31,59,190,228,209,66,61,212,48,163,60,182,38,111,191,14,218,70,105,7,87,39,242,29,155,188,148,67,3,248,17,199,246,144,239,62,231,6,195,213,47,200,102,30,215,8,232,234,222,128,82,238,247,132,170,114,172,53,77,106,42,150,26,210,113,90,21,73,116,75,159,208,94,4,24,164,236,194,224,65,110,15,81,203,204,36,145,175,80,161,244,112,57,153,124,58,133,35,184,180,122,252,2,54,91,37,85,151,49,45,93,250,152,227,138,146,174,5,223,41,16,103,108,186,201,211,0,230,207,225,158,168,44,99,22,1,63,88,226,137,169,13,56,52,27,171,51,255,176,187,72,12,95,185,177,205,46,197,243,219,71,229,165,156,119,10,166,32,104,254,127,193,173],i=[1,2,3,5],s=function(e,t){return e<<t&65535|(65535&e)>>16-t},o=function(e,t){return(65535&e)>>t|e<<16-t&65535};e.exports=n.rc2=n.rc2||{},n.rc2.expandKey=function(e,t){"string"==typeof e&&(e=n.util.createBuffer(e)),t=t||128;var r,i=e,s=e.length(),o=t,c=Math.ceil(o/8),u=255>>(7&o);for(r=s;r<128;r++)i.putByte(a[i.at(r-1)+i.at(r-s)&255]);for(i.setAt(128-c,a[i.at(128-c)&u]),r=127-c;r>=0;r--)i.setAt(r,a[i.at(r+1)^i.at(r+c)]);return i};var c=function(e,t,r){var a,c,u,l,p=!1,f=null,h=null,d=null,y=[];for(e=n.rc2.expandKey(e,t),u=0;u<64;u++)y.push(e.getInt16Le());r?(a=function(e){for(u=0;u<4;u++)e[u]+=y[l]+(e[(u+3)%4]&e[(u+2)%4])+(~e[(u+3)%4]&e[(u+1)%4]),e[u]=s(e[u],i[u]),l++},c=function(e){for(u=0;u<4;u++)e[u]+=y[63&e[(u+3)%4]]}):(a=function(e){for(u=3;u>=0;u--)e[u]=o(e[u],i[u]),e[u]-=y[l]+(e[(u+3)%4]&e[(u+2)%4])+(~e[(u+3)%4]&e[(u+1)%4]),l--},c=function(e){for(u=3;u>=0;u--)e[u]-=y[63&e[(u+3)%4]]});var g=function(e){var t=[];for(u=0;u<4;u++){var n=f.getInt16Le();null!==d&&(r?n^=d.getInt16Le():d.putInt16Le(n)),t.push(65535&n)}l=r?0:63;for(var a=0;a<e.length;a++)for(var i=0;i<e[a][0];i++)e[a][1](t);for(u=0;u<4;u++)null!==d&&(r?d.putInt16Le(t[u]):t[u]^=d.getInt16Le()),h.putInt16Le(t[u])},v=null;return v={start:function(e,t){e&&"string"==typeof e&&(e=n.util.createBuffer(e)),p=!1,f=n.util.createBuffer(),h=t||new n.util.createBuffer,d=e,v.output=h},update:function(e){for(p||f.putBuffer(e);f.length()>=8;)g([[5,a],[1,c],[6,a],[1,c],[5,a]])},finish:function(e){var t=!0;if(r)if(e)t=e(8,f,!r);else{var n=8===f.length()?8:8-f.length();f.fillWithByte(n,n)}if(t&&(p=!0,v.update()),!r&&(t=0===f.length()))if(e)t=e(8,h,!r);else{var a=h.length(),i=h.at(a-1);i>a?t=!1:h.truncate(i)}return t}}};n.rc2.startEncrypting=function(e,t,r){var a=n.rc2.createEncryptionCipher(e,128);return a.start(t,r),a},n.rc2.createEncryptionCipher=function(e,t){return c(e,t,!0)},n.rc2.startDecrypting=function(e,t,r){var a=n.rc2.createDecryptionCipher(e,128);return a.start(t,r),a},n.rc2.createDecryptionCipher=function(e,t){return c(e,t,!1)}},function(e,t,r){var n=r(0);r(1),r(2),r(9);var a=e.exports=n.pkcs1=n.pkcs1||{};function i(e,t,r){r||(r=n.md.sha1.create());for(var a="",i=Math.ceil(t/r.digestLength),s=0;s<i;++s){var o=String.fromCharCode(s>>24&255,s>>16&255,s>>8&255,255&s);r.start(),r.update(e+o),a+=r.digest().getBytes()}return a.substring(0,t)}a.encode_rsa_oaep=function(e,t,r){var a,s,o,c;"string"==typeof r?(a=r,s=arguments[3]||void 0,o=arguments[4]||void 0):r&&(a=r.label||void 0,s=r.seed||void 0,o=r.md||void 0,r.mgf1&&r.mgf1.md&&(c=r.mgf1.md)),o?o.start():o=n.md.sha1.create(),c||(c=o);var u=Math.ceil(e.n.bitLength()/8),l=u-2*o.digestLength-2;if(t.length>l)throw(g=new Error("RSAES-OAEP input message length is too long.")).length=t.length,g.maxLength=l,g;a||(a=""),o.update(a,"raw");for(var p=o.digest(),f="",h=l-t.length,d=0;d<h;d++)f+="\0";var y=p.getBytes()+f+""+t;if(s){if(s.length!==o.digestLength){var g;throw(g=new Error("Invalid RSAES-OAEP seed. The seed length must match the digest length.")).seedLength=s.length,g.digestLength=o.digestLength,g}}else s=n.random.getBytes(o.digestLength);var v=i(s,u-o.digestLength-1,c),m=n.util.xorBytes(y,v,y.length),C=i(m,o.digestLength,c),E=n.util.xorBytes(s,C,s.length);return"\0"+E+m},a.decode_rsa_oaep=function(e,t,r){var a,s,o;"string"==typeof r?(a=r,s=arguments[3]||void 0):r&&(a=r.label||void 0,s=r.md||void 0,r.mgf1&&r.mgf1.md&&(o=r.mgf1.md));var c=Math.ceil(e.n.bitLength()/8);if(t.length!==c)throw(m=new Error("RSAES-OAEP encoded message length is invalid.")).length=t.length,m.expectedLength=c,m;if(void 0===s?s=n.md.sha1.create():s.start(),o||(o=s),c<2*s.digestLength+2)throw new Error("RSAES-OAEP key is too short for the hash function.");a||(a=""),s.update(a,"raw");for(var u=s.digest().getBytes(),l=t.charAt(0),p=t.substring(1,s.digestLength+1),f=t.substring(1+s.digestLength),h=i(f,s.digestLength,o),d=n.util.xorBytes(p,h,p.length),y=i(d,c-s.digestLength-1,o),g=n.util.xorBytes(f,y,f.length),v=g.substring(0,s.digestLength),m="\0"!==l,C=0;C<s.digestLength;++C)m|=u.charAt(C)!==v.charAt(C);for(var E=1,S=s.digestLength,T=s.digestLength;T<g.length;T++){var I=g.charCodeAt(T),b=1&I^1,A=E?65534:0;m|=I&A,S+=E&=b}if(m||1!==g.charCodeAt(S))throw new Error("Invalid RSAES-OAEP padding.");return g.substring(S+1)}},function(e,t,r){var n=r(0);r(1),r(13),r(2),function(){if(n.prime)e.exports=n.prime;else{var t=e.exports=n.prime=n.prime||{},r=n.jsbn.BigInteger,a=[6,4,2,4,2,4,6,2],i=new r(null);i.fromInt(30);var s=function(e,t){return e|t};t.generateProbablePrime=function(e,t,a){"function"==typeof t&&(a=t,t={});var i=(t=t||{}).algorithm||"PRIMEINC";"string"==typeof i&&(i={name:i}),i.options=i.options||{};var s=t.prng||n.random,u={nextBytes:function(e){for(var t=s.getBytesSync(e.length),r=0;r<e.length;++r)e[r]=t.charCodeAt(r)}};if("PRIMEINC"===i.name)return function(e,t,a,i){if("workers"in a)return function(e,t,a,i){if("undefined"==typeof Worker)return o(e,t,a,i);var s=c(e,t),u=a.workers,l=a.workLoad||100,p=30*l/8,f=a.workerScript||"forge/prime.worker.js";if(-1===u)return n.util.estimateCores((function(e,t){e&&(t=2),u=t-1,h()}));function h(){u=Math.max(1,u);for(var n=[],a=0;a<u;++a)n[a]=new Worker(f);for(a=0;a<u;++a)n[a].addEventListener("message",h);var o=!1;function h(a){if(!o){0;var u=a.data;if(u.found){for(var f=0;f<n.length;++f)n[f].terminate();return o=!0,i(null,new r(u.prime,16))}s.bitLength()>e&&(s=c(e,t));var h=s.toString(16);a.target.postMessage({hex:h,workLoad:l}),s.dAddOffset(p,0)}}}h()}(e,t,a,i);return o(e,t,a,i)}(e,u,i.options,a);throw new Error("Invalid prime generation algorithm: "+i.name)}}function o(e,t,r,i){var s=c(e,t),o=function(e){return e<=100?27:e<=150?18:e<=200?15:e<=250?12:e<=300?9:e<=350?8:e<=400?7:e<=500?6:e<=600?5:e<=800?4:e<=1250?3:2}(s.bitLength());"millerRabinTests"in r&&(o=r.millerRabinTests);var u=10;"maxBlockTime"in r&&(u=r.maxBlockTime),function e(t,r,i,s,o,u,l){var p=+new Date;do{if(t.bitLength()>r&&(t=c(r,i)),t.isProbablePrime(o))return l(null,t);t.dAddOffset(a[s++%8],0)}while(u<0||+new Date-p<u);n.util.setImmediate((function(){e(t,r,i,s,o,u,l)}))}(s,e,t,0,o,u,i)}function c(e,t){var n=new r(e,t),a=e-1;return n.testBit(a)||n.bitwiseTo(r.ONE.shiftLeft(a),s,n),n.dAddOffset(31-n.mod(i).byteValue(),0),n}}()},function(e,t,r){var n=r(0);r(3),r(8),r(6),r(29),r(22),r(2),r(12),r(9),r(1),r(18);var a=n.asn1,i=n.pki,s=e.exports=n.pkcs12=n.pkcs12||{},o={name:"ContentInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"ContentInfo.contentType",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"contentType"},{name:"ContentInfo.content",tagClass:a.Class.CONTEXT_SPECIFIC,constructed:!0,captureAsn1:"content"}]},c={name:"PFX",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.version",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"version"},o,{name:"PFX.macData",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,optional:!0,captureAsn1:"mac",value:[{name:"PFX.macData.mac",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.macData.mac.digestAlgorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.macData.mac.digestAlgorithm.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"macAlgorithm"},{name:"PFX.macData.mac.digestAlgorithm.parameters",optional:!0,tagClass:a.Class.UNIVERSAL,captureAsn1:"macAlgorithmParameters"}]},{name:"PFX.macData.mac.digest",tagClass:a.Class.UNIVERSAL,type:a.Type.OCTETSTRING,constructed:!1,capture:"macDigest"}]},{name:"PFX.macData.macSalt",tagClass:a.Class.UNIVERSAL,type:a.Type.OCTETSTRING,constructed:!1,capture:"macSalt"},{name:"PFX.macData.iterations",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,optional:!0,capture:"macIterations"}]}]},u={name:"SafeBag",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"SafeBag.bagId",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"bagId"},{name:"SafeBag.bagValue",tagClass:a.Class.CONTEXT_SPECIFIC,constructed:!0,captureAsn1:"bagValue"},{name:"SafeBag.bagAttributes",tagClass:a.Class.UNIVERSAL,type:a.Type.SET,constructed:!0,optional:!0,capture:"bagAttributes"}]},l={name:"Attribute",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"Attribute.attrId",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"oid"},{name:"Attribute.attrValues",tagClass:a.Class.UNIVERSAL,type:a.Type.SET,constructed:!0,capture:"values"}]},p={name:"CertBag",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"CertBag.certId",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"certId"},{name:"CertBag.certValue",tagClass:a.Class.CONTEXT_SPECIFIC,constructed:!0,value:[{name:"CertBag.certValue[0]",tagClass:a.Class.UNIVERSAL,type:a.Class.OCTETSTRING,constructed:!1,capture:"cert"}]}]};function f(e,t,r,n){for(var a=[],i=0;i<e.length;i++)for(var s=0;s<e[i].safeBags.length;s++){var o=e[i].safeBags[s];void 0!==n&&o.type!==n||(null!==t?void 0!==o.attributes[t]&&o.attributes[t].indexOf(r)>=0&&a.push(o):a.push(o))}return a}function h(e){if(e.composed||e.constructed){for(var t=n.util.createBuffer(),r=0;r<e.value.length;++r)t.putBytes(e.value[r].value);e.composed=e.constructed=!1,e.value=t.getBytes()}return e}function d(e,t){var r={},s=[];if(!a.validate(e,n.pkcs7.asn1.encryptedDataValidator,r,s))throw(o=new Error("Cannot read EncryptedContentInfo.")).errors=s,o;var o,c=a.derToOid(r.contentType);if(c!==i.oids.data)throw(o=new Error("PKCS#12 EncryptedContentInfo ContentType is not Data.")).oid=c,o;c=a.derToOid(r.encAlgorithm);var u=i.pbe.getCipher(c,r.encParameter,t),l=h(r.encryptedContentAsn1),p=n.util.createBuffer(l.value);if(u.update(p),!u.finish())throw new Error("Failed to decrypt PKCS#12 SafeContents.");return u.output.getBytes()}function y(e,t,r){if(!t&&0===e.length)return[];if((e=a.fromDer(e,t)).tagClass!==a.Class.UNIVERSAL||e.type!==a.Type.SEQUENCE||!0!==e.constructed)throw new Error("PKCS#12 SafeContents expected to be a SEQUENCE OF SafeBag.");for(var n=[],s=0;s<e.value.length;s++){var o=e.value[s],c={},l=[];if(!a.validate(o,u,c,l))throw(v=new Error("Cannot read SafeBag.")).errors=l,v;var f,h,d={type:a.derToOid(c.bagId),attributes:g(c.bagAttributes)};n.push(d);var y=c.bagValue.value[0];switch(d.type){case i.oids.pkcs8ShroudedKeyBag:if(null===(y=i.decryptPrivateKeyInfo(y,r)))throw new Error("Unable to decrypt PKCS#8 ShroudedKeyBag, wrong password?");case i.oids.keyBag:try{d.key=i.privateKeyFromAsn1(y)}catch(e){d.key=null,d.asn1=y}continue;case i.oids.certBag:f=p,h=function(){if(a.derToOid(c.certId)!==i.oids.x509Certificate){var e=new Error("Unsupported certificate type, only X.509 supported.");throw e.oid=a.derToOid(c.certId),e}var r=a.fromDer(c.cert,t);try{d.cert=i.certificateFromAsn1(r,!0)}catch(e){d.cert=null,d.asn1=r}};break;default:var v;throw(v=new Error("Unsupported PKCS#12 SafeBag type.")).oid=d.type,v}if(void 0!==f&&!a.validate(y,f,c,l))throw(v=new Error("Cannot read PKCS#12 "+f.name)).errors=l,v;h()}return n}function g(e){var t={};if(void 0!==e)for(var r=0;r<e.length;++r){var n={},s=[];if(!a.validate(e[r],l,n,s)){var o=new Error("Cannot read PKCS#12 BagAttribute.");throw o.errors=s,o}var c=a.derToOid(n.oid);if(void 0!==i.oids[c]){t[i.oids[c]]=[];for(var u=0;u<n.values.length;++u)t[i.oids[c]].push(n.values[u].value)}}return t}s.pkcs12FromAsn1=function(e,t,r){"string"==typeof t?(r=t,t=!0):void 0===t&&(t=!0);var u={};if(!a.validate(e,c,u,[]))throw(l=new Error("Cannot read PKCS#12 PFX. ASN.1 object is not an PKCS#12 PFX.")).errors=l,l;var l,p={version:u.version.charCodeAt(0),safeContents:[],getBags:function(e){var t,r={};return"localKeyId"in e?t=e.localKeyId:"localKeyIdHex"in e&&(t=n.util.hexToBytes(e.localKeyIdHex)),void 0===t&&!("friendlyName"in e)&&"bagType"in e&&(r[e.bagType]=f(p.safeContents,null,null,e.bagType)),void 0!==t&&(r.localKeyId=f(p.safeContents,"localKeyId",t,e.bagType)),"friendlyName"in e&&(r.friendlyName=f(p.safeContents,"friendlyName",e.friendlyName,e.bagType)),r},getBagsByFriendlyName:function(e,t){return f(p.safeContents,"friendlyName",e,t)},getBagsByLocalKeyId:function(e,t){return f(p.safeContents,"localKeyId",e,t)}};if(3!==u.version.charCodeAt(0))throw(l=new Error("PKCS#12 PFX of version other than 3 not supported.")).version=u.version.charCodeAt(0),l;if(a.derToOid(u.contentType)!==i.oids.data)throw(l=new Error("Only PKCS#12 PFX in password integrity mode supported.")).oid=a.derToOid(u.contentType),l;var g=u.content.value[0];if(g.tagClass!==a.Class.UNIVERSAL||g.type!==a.Type.OCTETSTRING)throw new Error("PKCS#12 authSafe content data is not an OCTET STRING.");if(g=h(g),u.mac){var v=null,m=0,C=a.derToOid(u.macAlgorithm);switch(C){case i.oids.sha1:v=n.md.sha1.create(),m=20;break;case i.oids.sha256:v=n.md.sha256.create(),m=32;break;case i.oids.sha384:v=n.md.sha384.create(),m=48;break;case i.oids.sha512:v=n.md.sha512.create(),m=64;break;case i.oids.md5:v=n.md.md5.create(),m=16}if(null===v)throw new Error("PKCS#12 uses unsupported MAC algorithm: "+C);var E=new n.util.ByteBuffer(u.macSalt),S="macIterations"in u?parseInt(n.util.bytesToHex(u.macIterations),16):1,T=s.generateKey(r,E,3,S,m,v),I=n.hmac.create();if(I.start(v,T),I.update(g.value),I.getMac().getBytes()!==u.macDigest)throw new Error("PKCS#12 MAC could not be verified. Invalid password?")}else if(Array.isArray(e.value)&&e.value.length>2)throw new Error("Invalid PKCS#12. macData field present but MAC was not validated.");return function(e,t,r,n){if((t=a.fromDer(t,r)).tagClass!==a.Class.UNIVERSAL||t.type!==a.Type.SEQUENCE||!0!==t.constructed)throw new Error("PKCS#12 AuthenticatedSafe expected to be a SEQUENCE OF ContentInfo");for(var s=0;s<t.value.length;s++){var c=t.value[s],u={},l=[];if(!a.validate(c,o,u,l))throw(v=new Error("Cannot read ContentInfo.")).errors=l,v;var p={encrypted:!1},f=null,g=u.content.value[0];switch(a.derToOid(u.contentType)){case i.oids.data:if(g.tagClass!==a.Class.UNIVERSAL||g.type!==a.Type.OCTETSTRING)throw new Error("PKCS#12 SafeContents Data is not an OCTET STRING.");f=h(g).value;break;case i.oids.encryptedData:f=d(g,n),p.encrypted=!0;break;default:var v;throw(v=new Error("Unsupported PKCS#12 contentType.")).contentType=a.derToOid(u.contentType),v}p.safeBags=y(f,r,n),e.safeContents.push(p)}}(p,g.value,t,r),p},s.toPkcs12Asn1=function(e,t,r,o){(o=o||{}).saltSize=o.saltSize||8,o.count=o.count||2048,o.algorithm=o.algorithm||o.encAlgorithm||"aes128","useMac"in o||(o.useMac=!0),"localKeyId"in o||(o.localKeyId=null),"generateLocalKeyId"in o||(o.generateLocalKeyId=!0);var c,u=o.localKeyId;if(null!==u)u=n.util.hexToBytes(u);else if(o.generateLocalKeyId)if(t){var l=n.util.isArray(t)?t[0]:t;"string"==typeof l&&(l=i.certificateFromPem(l)),(N=n.md.sha1.create()).update(a.toDer(i.certificateToAsn1(l)).getBytes()),u=N.digest().getBytes()}else u=n.random.getBytes(20);var p=[];null!==u&&p.push(a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.localKeyId).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SET,!0,[a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,u)])])),"friendlyName"in o&&p.push(a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.friendlyName).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SET,!0,[a.create(a.Class.UNIVERSAL,a.Type.BMPSTRING,!1,o.friendlyName)])])),p.length>0&&(c=a.create(a.Class.UNIVERSAL,a.Type.SET,!0,p));var f=[],h=[];null!==t&&(h=n.util.isArray(t)?t:[t]);for(var d=[],y=0;y<h.length;++y){"string"==typeof(t=h[y])&&(t=i.certificateFromPem(t));var g=0===y?c:void 0,v=i.certificateToAsn1(t),m=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.certBag).getBytes()),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.x509Certificate).getBytes()),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,a.toDer(v).getBytes())])])]),g]);d.push(m)}if(d.length>0){var C=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,d),E=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.data).getBytes()),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,a.toDer(C).getBytes())])]);f.push(E)}var S=null;if(null!==e){var T=i.wrapRsaPrivateKey(i.privateKeyToAsn1(e));S=null===r?a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.keyBag).getBytes()),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[T]),c]):a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.pkcs8ShroudedKeyBag).getBytes()),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[i.encryptPrivateKeyInfo(T,r,o)]),c]);var I=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[S]),b=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.data).getBytes()),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,a.toDer(I).getBytes())])]);f.push(b)}var A,B=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,f);if(o.useMac){var N=n.md.sha1.create(),k=new n.util.ByteBuffer(n.random.getBytes(o.saltSize)),w=o.count,R=(e=s.generateKey(r,k,3,w,20),n.hmac.create());R.start(N,e),R.update(a.toDer(B).getBytes());var L=R.getMac();A=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.sha1).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")]),a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,L.getBytes())]),a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,k.getBytes()),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(w).getBytes())])}return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(3).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(i.oids.data).getBytes()),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,a.toDer(B).getBytes())])]),A])},s.generateKey=n.pbe.generatePkcs12Key},function(e,t,r){var n=r(0);r(3),r(1);var a=n.asn1,i=e.exports=n.pkcs7asn1=n.pkcs7asn1||{};n.pkcs7=n.pkcs7||{},n.pkcs7.asn1=i;var s={name:"ContentInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"ContentInfo.ContentType",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"contentType"},{name:"ContentInfo.content",tagClass:a.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,captureAsn1:"content"}]};i.contentInfoValidator=s;var o={name:"EncryptedContentInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedContentInfo.contentType",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"contentType"},{name:"EncryptedContentInfo.contentEncryptionAlgorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedContentInfo.contentEncryptionAlgorithm.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"encAlgorithm"},{name:"EncryptedContentInfo.contentEncryptionAlgorithm.parameter",tagClass:a.Class.UNIVERSAL,captureAsn1:"encParameter"}]},{name:"EncryptedContentInfo.encryptedContent",tagClass:a.Class.CONTEXT_SPECIFIC,type:0,capture:"encryptedContent",captureAsn1:"encryptedContentAsn1"}]};i.envelopedDataValidator={name:"EnvelopedData",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"EnvelopedData.Version",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"version"},{name:"EnvelopedData.RecipientInfos",tagClass:a.Class.UNIVERSAL,type:a.Type.SET,constructed:!0,captureAsn1:"recipientInfos"}].concat(o)},i.encryptedDataValidator={name:"EncryptedData",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedData.Version",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"version"}].concat(o)};var c={name:"SignerInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.version",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1},{name:"SignerInfo.issuerAndSerialNumber",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.issuerAndSerialNumber.issuer",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"issuer"},{name:"SignerInfo.issuerAndSerialNumber.serialNumber",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"serial"}]},{name:"SignerInfo.digestAlgorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.digestAlgorithm.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"digestAlgorithm"},{name:"SignerInfo.digestAlgorithm.parameter",tagClass:a.Class.UNIVERSAL,constructed:!1,captureAsn1:"digestParameter",optional:!0}]},{name:"SignerInfo.authenticatedAttributes",tagClass:a.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,capture:"authenticatedAttributes"},{name:"SignerInfo.digestEncryptionAlgorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,capture:"signatureAlgorithm"},{name:"SignerInfo.encryptedDigest",tagClass:a.Class.UNIVERSAL,type:a.Type.OCTETSTRING,constructed:!1,capture:"signature"},{name:"SignerInfo.unauthenticatedAttributes",tagClass:a.Class.CONTEXT_SPECIFIC,type:1,constructed:!0,optional:!0,capture:"unauthenticatedAttributes"}]};i.signedDataValidator={name:"SignedData",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"SignedData.Version",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"version"},{name:"SignedData.DigestAlgorithms",tagClass:a.Class.UNIVERSAL,type:a.Type.SET,constructed:!0,captureAsn1:"digestAlgorithms"},s,{name:"SignedData.Certificates",tagClass:a.Class.CONTEXT_SPECIFIC,type:0,optional:!0,captureAsn1:"certificates"},{name:"SignedData.CertificateRevocationLists",tagClass:a.Class.CONTEXT_SPECIFIC,type:1,optional:!0,captureAsn1:"crls"},{name:"SignedData.SignerInfos",tagClass:a.Class.UNIVERSAL,type:a.Type.SET,capture:"signerInfos",optional:!0,value:[c]}]},i.recipientInfoValidator={name:"RecipientInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.version",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"version"},{name:"RecipientInfo.issuerAndSerial",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.issuerAndSerial.issuer",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"issuer"},{name:"RecipientInfo.issuerAndSerial.serialNumber",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"serial"}]},{name:"RecipientInfo.keyEncryptionAlgorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.keyEncryptionAlgorithm.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"encAlgorithm"},{name:"RecipientInfo.keyEncryptionAlgorithm.parameter",tagClass:a.Class.UNIVERSAL,constructed:!1,captureAsn1:"encParameter",optional:!0}]},{name:"RecipientInfo.encryptedKey",tagClass:a.Class.UNIVERSAL,type:a.Type.OCTETSTRING,constructed:!1,capture:"encKey"}]}},function(e,t,r){var n=r(0);r(1),n.mgf=n.mgf||{},(e.exports=n.mgf.mgf1=n.mgf1=n.mgf1||{}).create=function(e){return{generate:function(t,r){for(var a=new n.util.ByteBuffer,i=Math.ceil(r/e.digestLength),s=0;s<i;s++){var o=new n.util.ByteBuffer;o.putInt32(s),e.start(),e.update(t+o.getBytes()),a.putBuffer(e.digest())}return a.truncate(a.length()-r),a.getBytes()}}}},function(e,t,r){var n=r(0);r(4),r(1);var a=e.exports=n.sha512=n.sha512||{};n.md.sha512=n.md.algorithms.sha512=a;var i=n.sha384=n.sha512.sha384=n.sha512.sha384||{};i.create=function(){return a.create("SHA-384")},n.md.sha384=n.md.algorithms.sha384=i,n.sha512.sha256=n.sha512.sha256||{create:function(){return a.create("SHA-512/256")}},n.md["sha512/256"]=n.md.algorithms["sha512/256"]=n.sha512.sha256,n.sha512.sha224=n.sha512.sha224||{create:function(){return a.create("SHA-512/224")}},n.md["sha512/224"]=n.md.algorithms["sha512/224"]=n.sha512.sha224,a.create=function(e){if(o||(s=String.fromCharCode(128),s+=n.util.fillString(String.fromCharCode(0),128),c=[[1116352408,3609767458],[1899447441,602891725],[3049323471,3964484399],[3921009573,2173295548],[961987163,4081628472],[1508970993,3053834265],[2453635748,2937671579],[2870763221,3664609560],[3624381080,2734883394],[310598401,1164996542],[607225278,1323610764],[1426881987,3590304994],[1925078388,4068182383],[2162078206,991336113],[2614888103,633803317],[3248222580,3479774868],[3835390401,2666613458],[4022224774,944711139],[264347078,2341262773],[604807628,2007800933],[770255983,1495990901],[1249150122,1856431235],[1555081692,3175218132],[1996064986,2198950837],[2554220882,3999719339],[2821834349,766784016],[2952996808,2566594879],[3210313671,3203337956],[3336571891,1034457026],[3584528711,2466948901],[113926993,3758326383],[338241895,168717936],[666307205,1188179964],[773529912,1546045734],[1294757372,1522805485],[1396182291,2643833823],[1695183700,2343527390],[1986661051,1014477480],[2177026350,1206759142],[2456956037,344077627],[2730485921,1290863460],[2820302411,3158454273],[3259730800,3505952657],[3345764771,106217008],[3516065817,3606008344],[3600352804,1432725776],[4094571909,1467031594],[275423344,851169720],[430227734,3100823752],[506948616,1363258195],[659060556,3750685593],[883997877,3785050280],[958139571,3318307427],[1322822218,3812723403],[1537002063,2003034995],[1747873779,3602036899],[1955562222,1575990012],[2024104815,1125592928],[2227730452,2716904306],[2361852424,442776044],[2428436474,593698344],[2756734187,3733110249],[3204031479,2999351573],[3329325298,3815920427],[3391569614,3928383900],[3515267271,566280711],[3940187606,3454069534],[4118630271,4000239992],[116418474,1914138554],[174292421,2731055270],[289380356,3203993006],[460393269,320620315],[685471733,587496836],[852142971,1086792851],[1017036298,365543100],[1126000580,2618297676],[1288033470,3409855158],[1501505948,4234509866],[1607167915,987167468],[1816402316,1246189591]],(u={})["SHA-512"]=[[1779033703,4089235720],[3144134277,2227873595],[1013904242,4271175723],[2773480762,1595750129],[1359893119,2917565137],[2600822924,725511199],[528734635,4215389547],[1541459225,327033209]],u["SHA-384"]=[[3418070365,3238371032],[1654270250,914150663],[2438529370,812702999],[355462360,4144912697],[1731405415,4290775857],[2394180231,1750603025],[3675008525,1694076839],[1203062813,3204075428]],u["SHA-512/256"]=[[573645204,4230739756],[2673172387,3360449730],[596883563,1867755857],[2520282905,1497426621],[2519219938,2827943907],[3193839141,1401305490],[721525244,746961066],[246885852,2177182882]],u["SHA-512/224"]=[[2352822216,424955298],[1944164710,2312950998],[502970286,855612546],[1738396948,1479516111],[258812777,2077511080],[2011393907,79989058],[1067287976,1780299464],[286451373,2446758561]],o=!0),void 0===e&&(e="SHA-512"),!(e in u))throw new Error("Invalid SHA-512 algorithm: "+e);for(var t=u[e],r=null,a=n.util.createBuffer(),i=new Array(80),p=0;p<80;++p)i[p]=new Array(2);var f=64;switch(e){case"SHA-384":f=48;break;case"SHA-512/256":f=32;break;case"SHA-512/224":f=28}var h={algorithm:e.replace("-","").toLowerCase(),blockLength:128,digestLength:f,messageLength:0,fullMessageLength:null,messageLengthSize:16,start:function(){h.messageLength=0,h.fullMessageLength=h.messageLength128=[];for(var e=h.messageLengthSize/4,i=0;i<e;++i)h.fullMessageLength.push(0);a=n.util.createBuffer(),r=new Array(t.length);for(i=0;i<t.length;++i)r[i]=t[i].slice(0);return h}};return h.start(),h.update=function(e,t){"utf8"===t&&(e=n.util.encodeUtf8(e));var s=e.length;h.messageLength+=s,s=[s/4294967296>>>0,s>>>0];for(var o=h.fullMessageLength.length-1;o>=0;--o)h.fullMessageLength[o]+=s[1],s[1]=s[0]+(h.fullMessageLength[o]/4294967296>>>0),h.fullMessageLength[o]=h.fullMessageLength[o]>>>0,s[0]=s[1]/4294967296>>>0;return a.putBytes(e),l(r,i,a),(a.read>2048||0===a.length())&&a.compact(),h},h.digest=function(){var t=n.util.createBuffer();t.putBytes(a.bytes());var o,c=h.fullMessageLength[h.fullMessageLength.length-1]+h.messageLengthSize&h.blockLength-1;t.putBytes(s.substr(0,h.blockLength-c));for(var u=8*h.fullMessageLength[0],p=0;p<h.fullMessageLength.length-1;++p)u+=(o=8*h.fullMessageLength[p+1])/4294967296>>>0,t.putInt32(u>>>0),u=o>>>0;t.putInt32(u);var f=new Array(r.length);for(p=0;p<r.length;++p)f[p]=r[p].slice(0);l(f,i,t);var d,y=n.util.createBuffer();d="SHA-512"===e?f.length:"SHA-384"===e?f.length-2:f.length-4;for(p=0;p<d;++p)y.putInt32(f[p][0]),p===d-1&&"SHA-512/224"===e||y.putInt32(f[p][1]);return y},h};var s=null,o=!1,c=null,u=null;function l(e,t,r){for(var n,a,i,s,o,u,l,p,f,h,d,y,g,v,m,C,E,S,T,I,b,A,B,N,k,w,R,L,_,U,D,P,V,O=r.length();O>=128;){for(R=0;R<16;++R)t[R][0]=r.getInt32()>>>0,t[R][1]=r.getInt32()>>>0;for(;R<80;++R)n=(((L=(U=t[R-2])[0])>>>19|(_=U[1])<<13)^(_>>>29|L<<3)^L>>>6)>>>0,a=((L<<13|_>>>19)^(_<<3|L>>>29)^(L<<26|_>>>6))>>>0,i=(((L=(P=t[R-15])[0])>>>1|(_=P[1])<<31)^(L>>>8|_<<24)^L>>>7)>>>0,s=((L<<31|_>>>1)^(L<<24|_>>>8)^(L<<25|_>>>7))>>>0,D=t[R-7],V=t[R-16],_=a+D[1]+s+V[1],t[R][0]=n+D[0]+i+V[0]+(_/4294967296>>>0)>>>0,t[R][1]=_>>>0;for(d=e[0][0],y=e[0][1],g=e[1][0],v=e[1][1],m=e[2][0],C=e[2][1],E=e[3][0],S=e[3][1],T=e[4][0],I=e[4][1],b=e[5][0],A=e[5][1],B=e[6][0],N=e[6][1],k=e[7][0],w=e[7][1],R=0;R<80;++R)l=((T>>>14|I<<18)^(T>>>18|I<<14)^(I>>>9|T<<23))>>>0,p=(B^T&(b^B))>>>0,o=((d>>>28|y<<4)^(y>>>2|d<<30)^(y>>>7|d<<25))>>>0,u=((d<<4|y>>>28)^(y<<30|d>>>2)^(y<<25|d>>>7))>>>0,f=(d&g|m&(d^g))>>>0,h=(y&v|C&(y^v))>>>0,_=w+(((T<<18|I>>>14)^(T<<14|I>>>18)^(I<<23|T>>>9))>>>0)+((N^I&(A^N))>>>0)+c[R][1]+t[R][1],n=k+l+p+c[R][0]+t[R][0]+(_/4294967296>>>0)>>>0,a=_>>>0,i=o+f+((_=u+h)/4294967296>>>0)>>>0,s=_>>>0,k=B,w=N,B=b,N=A,b=T,A=I,T=E+n+((_=S+a)/4294967296>>>0)>>>0,I=_>>>0,E=m,S=C,m=g,C=v,g=d,v=y,d=n+i+((_=a+s)/4294967296>>>0)>>>0,y=_>>>0;_=e[0][1]+y,e[0][0]=e[0][0]+d+(_/4294967296>>>0)>>>0,e[0][1]=_>>>0,_=e[1][1]+v,e[1][0]=e[1][0]+g+(_/4294967296>>>0)>>>0,e[1][1]=_>>>0,_=e[2][1]+C,e[2][0]=e[2][0]+m+(_/4294967296>>>0)>>>0,e[2][1]=_>>>0,_=e[3][1]+S,e[3][0]=e[3][0]+E+(_/4294967296>>>0)>>>0,e[3][1]=_>>>0,_=e[4][1]+I,e[4][0]=e[4][0]+T+(_/4294967296>>>0)>>>0,e[4][1]=_>>>0,_=e[5][1]+A,e[5][0]=e[5][0]+b+(_/4294967296>>>0)>>>0,e[5][1]=_>>>0,_=e[6][1]+N,e[6][0]=e[6][0]+B+(_/4294967296>>>0)>>>0,e[6][1]=_>>>0,_=e[7][1]+w,e[7][0]=e[7][0]+k+(_/4294967296>>>0)>>>0,e[7][1]=_>>>0,O-=128}}},function(e,t,r){var n=r(0);r(1);var a=e.exports=n.net=n.net||{};a.socketPools={},a.createSocketPool=function(e){e.msie=e.msie||!1;var t=e.flashId,r=document.getElementById(t);r.init({marshallExceptions:!e.msie});var i={id:t,flashApi:r,sockets:{},policyPort:e.policyPort||0,policyUrl:e.policyUrl||null};a.socketPools[t]=i,!0===e.msie?i.handler=function(e){if(e.id in i.sockets){var t;switch(e.type){case"connect":t="connected";break;case"close":t="closed";break;case"socketData":t="data";break;default:t="error"}setTimeout((function(){i.sockets[e.id][t](e)}),0)}}:i.handler=function(e){if(e.id in i.sockets){var t;switch(e.type){case"connect":t="connected";break;case"close":t="closed";break;case"socketData":t="data";break;default:t="error"}i.sockets[e.id][t](e)}};var s="forge.net.socketPools['"+t+"'].handler";return r.subscribe("connect",s),r.subscribe("close",s),r.subscribe("socketData",s),r.subscribe("ioError",s),r.subscribe("securityError",s),i.destroy=function(){for(var t in delete a.socketPools[e.flashId],i.sockets)i.sockets[t].destroy();i.sockets={},r.cleanup()},i.createSocket=function(e){e=e||{};var t=r.create(),a={id:t,connected:e.connected||function(e){},closed:e.closed||function(e){},data:e.data||function(e){},error:e.error||function(e){},destroy:function(){r.destroy(t),delete i.sockets[t]},connect:function(e){var n=e.policyUrl||null,a=0;null===n&&0!==e.policyPort&&(a=e.policyPort||i.policyPort),r.connect(t,e.host,e.port,a,n)},close:function(){r.close(t),a.closed({id:a.id,type:"close",bytesAvailable:0})},isConnected:function(){return r.isConnected(t)},send:function(e){return r.send(t,n.util.encode64(e))},receive:function(e){var a=r.receive(t,e).rval;return null===a?null:n.util.decode64(a)},bytesAvailable:function(){return r.getBytesAvailable(t)}};return i.sockets[t]=a,a},i},a.destroySocketPool=function(e){e.flashId in a.socketPools&&a.socketPools[e.flashId].destroy()},a.createSocket=function(e){var t=null;e.flashId in a.socketPools&&(t=a.socketPools[e.flashId].createSocket(e));return t}},function(e,t,r){var n=r(0);r(10),r(1);var a=e.exports=n.http=n.http||{},i=function(e){return e.toLowerCase().replace(/(^.)|(-.)/g,(function(e){return e.toUpperCase()}))},s=function(e){return"forge.http."+e.url.protocol.slice(0,-1)+"."+e.url.hostname+"."+e.url.port},o=function(e){if(e.persistCookies)try{var t=n.util.getItem(e.socketPool.flashApi,s(e),"cookies");e.cookies=t||{}}catch(e){}},c=function(e){if(e.persistCookies)try{n.util.setItem(e.socketPool.flashApi,s(e),"cookies",e.cookies)}catch(e){}o(e)},u=function(e,t){t.isConnected()?(t.options.request.connectTime=+new Date,t.connected({type:"connect",id:t.id})):(t.options.request.connectTime=+new Date,t.connect({host:e.url.hostname,port:e.url.port,policyPort:e.policyPort,policyUrl:e.policyUrl}))},l=function(e,t){t.buffer.clear();for(var r=null;null===r&&e.requests.length>0;)(r=e.requests.shift()).request.aborted&&(r=null);null===r?(null!==t.options&&(t.options=null),e.idle.push(t)):(t.retries=1,t.options=r,u(e,t))},p=function(e,t,r){t.options=null,t.connected=function(r){if(null===t.options)l(e,t);else{var n=t.options.request;if(n.connectTime=+new Date-n.connectTime,r.socket=t,t.options.connected(r),n.aborted)t.close();else{var a=n.toString();n.body&&(a+=n.body),n.time=+new Date,t.send(a),n.time=+new Date-n.time,t.options.response.time=+new Date,t.sending=!0}}},t.closed=function(r){if(t.sending)t.sending=!1,t.retries>0?(--t.retries,u(e,t)):t.error({id:t.id,type:"ioError",message:"Connection closed during send. Broken pipe.",bytesAvailable:0});else{var n=t.options.response;n.readBodyUntilClose&&(n.time=+new Date-n.time,n.bodyReceived=!0,t.options.bodyReady({request:t.options.request,response:n,socket:t})),t.options.closed(r),l(e,t)}},t.data=function(r){if(t.sending=!1,t.options.request.aborted)t.close();else{var n=t.options.response,a=t.receive(r.bytesAvailable);if(null!==a)if(t.buffer.putBytes(a),n.headerReceived||(n.readHeader(t.buffer),n.headerReceived&&t.options.headerReady({request:t.options.request,response:n,socket:t})),n.headerReceived&&!n.bodyReceived&&n.readBody(t.buffer),n.bodyReceived)t.options.bodyReady({request:t.options.request,response:n,socket:t}),-1!=(n.getField("Connection")||"").indexOf("close")||"HTTP/1.0"===n.version&&null===n.getField("Keep-Alive")?t.close():l(e,t)}},t.error=function(e){t.options.error({type:e.type,message:e.message,request:t.options.request,response:t.options.response,socket:t}),t.close()},r?((t=n.tls.wrapSocket({sessionId:null,sessionCache:{},caStore:r.caStore,cipherSuites:r.cipherSuites,socket:t,virtualHost:r.virtualHost,verify:r.verify,getCertificate:r.getCertificate,getPrivateKey:r.getPrivateKey,getSignature:r.getSignature,deflate:r.deflate||null,inflate:r.inflate||null})).options=null,t.buffer=n.util.createBuffer(),e.sockets.push(t),r.prime?t.connect({host:e.url.hostname,port:e.url.port,policyPort:e.policyPort,policyUrl:e.policyUrl}):e.idle.push(t)):(t.buffer=n.util.createBuffer(),e.sockets.push(t),e.idle.push(t))},f=function(e){var t=!1;if(-1!==e.maxAge){var r=y(new Date);e.created+e.maxAge<=r&&(t=!0)}return t};a.createClient=function(e){var t,r=null;e.caCerts&&(r=n.pki.createCaStore(e.caCerts)),e.url=e.url||window.location.protocol+"//"+window.location.host;try{t=new URL(e.url)}catch(t){var i=new Error("Invalid url.");throw i.details={url:e.url},i}e.connections=e.connections||1;var l=e.socketPool,h={url:t,socketPool:l,policyPort:e.policyPort,policyUrl:e.policyUrl,requests:[],sockets:[],idle:[],secure:"https:"===t.protocol,cookies:{},persistCookies:void 0===e.persistCookies||e.persistCookies};o(h);var d=null;h.secure&&(d={caStore:r,cipherSuites:e.cipherSuites||null,virtualHost:e.virtualHost||t.hostname,verify:e.verify||function(e,t,r,n){if(0===r&&!0===t){var a=n[r].subject.getField("CN");null!==a&&h.url.hostname===a.value||(t={message:"Certificate common name does not match url host."})}return t},getCertificate:e.getCertificate||null,getPrivateKey:e.getPrivateKey||null,getSignature:e.getSignature||null,prime:e.primeTlsSockets||!1},null!==l.flashApi&&(d.deflate=function(e){return n.util.deflate(l.flashApi,e,!0)},d.inflate=function(e){return n.util.inflate(l.flashApi,e,!0)}));for(var g=0;g<e.connections;++g)p(h,l.createSocket(),d);return h.send=function(e){null===e.request.getField("Host")&&e.request.setField("Host",h.url.origin);var t={};if(t.request=e.request,t.connected=e.connected||function(){},t.closed=e.close||function(){},t.headerReady=function(t){!function(e,t){for(var r=t.getCookies(),n=0;n<r.length;++n)try{e.setCookie(r[n])}catch(e){}}(h,t.response),e.headerReady&&e.headerReady(t)},t.bodyReady=e.bodyReady||function(){},t.error=e.error||function(){},t.response=a.createResponse(),t.response.time=0,t.response.flashApi=h.socketPool.flashApi,t.request.flashApi=h.socketPool.flashApi,t.request.abort=function(){t.request.aborted=!0,t.connected=function(){},t.closed=function(){},t.headerReady=function(){},t.bodyReady=function(){},t.error=function(){}},function(e,t){var r=[],n=(e.url,e.cookies);for(var a in n){var i=n[a];for(var s in i){var o=i[s];f(o)?r.push(o):0===t.path.indexOf(o.path)&&t.addCookie(o)}}for(var c=0;c<r.length;++c){o=r[c];e.removeCookie(o.name,o.path)}}(h,t.request),0===h.idle.length)h.requests.push(t);else{for(var r=null,n=h.idle.length,i=0;null===r&&i<n;++i)(r=h.idle[i]).isConnected()?h.idle.splice(i,1):r=null;null===r&&(r=h.idle.pop()),r.options=t,u(h,r)}},h.destroy=function(){h.requests=[];for(var e=0;e<h.sockets.length;++e)h.sockets[e].close(),h.sockets[e].destroy();h.socketPool=null,h.sockets=[],h.idle=[]},h.setCookie=function(e){var t;if(void 0!==e.name)if(null===e.value||void 0===e.value||""===e.value)t=h.removeCookie(e.name,e.path);else{var r;if(e.comment=e.comment||"",e.maxAge=e.maxAge||0,e.secure=void 0===e.secure||e.secure,e.httpOnly=e.httpOnly||!0,e.path=e.path||"/",e.domain=e.domain||null,e.version=e.version||null,e.created=y(new Date),e.secure!==h.secure)throw(r=new Error("Http client url scheme is incompatible with cookie secure flag.")).url=h.url,r.cookie=e,r;if(!a.withinCookieDomain(h.url,e))throw(r=new Error("Http client url scheme is incompatible with cookie secure flag.")).url=h.url,r.cookie=e,r;e.name in h.cookies||(h.cookies[e.name]={}),h.cookies[e.name][e.path]=e,t=!0,c(h)}return t},h.getCookie=function(e,t){var r=null;if(e in h.cookies){var n=h.cookies[e];if(t)t in n&&(r=n[t]);else for(var a in n){r=n[a];break}}return r},h.removeCookie=function(e,t){var r=!1;if(e in h.cookies)if(t){if(t in h.cookies[e]){r=!0,delete h.cookies[e][t];var n=!0;for(var a in h.cookies[e]){n=!1;break}n&&delete h.cookies[e]}}else r=!0,delete h.cookies[e];return r&&c(h),r},h.clearCookies=function(){h.cookies={},function(e){if(e.persistCookies)try{n.util.clearItems(e.socketPool.flashApi,s(e))}catch(e){}}(h)},n.log&&n.log.debug("forge.http","created client",e),h};var h=function(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")},d=function(){var e={fields:{},setField:function(t,r){e.fields[i(t)]=[h(""+r)]},appendField:function(t,r){(t=i(t))in e.fields||(e.fields[t]=[]),e.fields[t].push(h(""+r))},getField:function(t,r){var n=null;return(t=i(t))in e.fields&&(r=r||0,n=e.fields[t][r]),n}};return e},y=function(e){e.getTimezoneOffset();return Math.floor(+new Date/1e3)};a.createRequest=function(e){e=e||{};var t=d();t.version=e.version||"HTTP/1.1",t.method=e.method||null,t.path=e.path||null,t.body=e.body||null,t.bodyDeflated=!1,t.flashApi=null;var r=e.headers||[];n.util.isArray(r)||(r=[r]);for(var a=0;a<r.length;++a)for(var i in r[a])t.appendField(i,r[a][i]);return t.addCookie=function(e){var r="",n=t.getField("Cookie");null!==n&&(r=n+"; ");y(new Date);r+=e.name+"="+e.value,t.setField("Cookie",r)},t.toString=function(){null===t.getField("User-Agent")&&t.setField("User-Agent","forge.http 1.0"),null===t.getField("Accept")&&t.setField("Accept","*/*"),null===t.getField("Connection")&&(t.setField("Connection","keep-alive"),t.setField("Keep-Alive","115")),null!==t.flashApi&&null===t.getField("Accept-Encoding")&&t.setField("Accept-Encoding","deflate"),null!==t.flashApi&&null!==t.body&&null===t.getField("Content-Encoding")&&!t.bodyDeflated&&t.body.length>100?(t.body=n.util.deflate(t.flashApi,t.body),t.bodyDeflated=!0,t.setField("Content-Encoding","deflate"),t.setField("Content-Length",t.body.length)):null!==t.body&&t.setField("Content-Length",t.body.length);var e=t.method.toUpperCase()+" "+t.path+" "+t.version+"\r\n";for(var r in t.fields)for(var a=t.fields[r],i=0;i<a.length;++i)e+=r+": "+a[i]+"\r\n";return e+="\r\n"},t},a.createResponse=function(){var e=!0,t=0,r=!1,a=d();a.version=null,a.code=0,a.message=null,a.body=null,a.headerReceived=!1,a.bodyReceived=!1,a.flashApi=null;var i=function(e){var t=null,r=e.data.indexOf("\r\n",e.read);return-1!=r&&(t=e.getBytes(r-e.read),e.getBytes(2)),t},s=function(e){var t=e.indexOf(":"),r=e.substring(0,t++);a.appendField(r,t<e.length?e.substring(t):"")};a.readHeader=function(t){for(var r="";!a.headerReceived&&null!==r;)if(null!==(r=i(t)))if(e){e=!1;var n=r.split(" ");if(!(n.length>=3)){var o=new Error("Invalid http response header.");throw o.details={line:r},o}a.version=n[0],a.code=parseInt(n[1],10),a.message=n.slice(2).join(" ")}else 0===r.length?a.headerReceived=!0:s(r);return a.headerReceived};return a.readBody=function(e){var o=a.getField("Content-Length"),c=a.getField("Transfer-Encoding");if(null!==o&&(o=parseInt(o)),null!==o&&o>=0)a.body=a.body||"",a.body+=e.getBytes(o),a.bodyReceived=a.body.length===o;else if(null!==c){if(-1==c.indexOf("chunked")){var u=new Error("Unknown Transfer-Encoding.");throw u.details={transferEncoding:c},u}a.body=a.body||"",function(e){for(var n="";null!==n&&e.length()>0;)if(t>0){if(t+2>e.length())break;a.body+=e.getBytes(t),e.getBytes(2),t=0}else if(r)for(n=i(e);null!==n;)n.length>0?(s(n),n=i(e)):(a.bodyReceived=!0,n=null);else null!==(n=i(e))&&(t=parseInt(n.split(";",1)[0],16),r=0===t);a.bodyReceived}(e)}else null!==o&&o<0||null===o&&null!==a.getField("Content-Type")?(a.body=a.body||"",a.body+=e.getBytes(),a.readBodyUntilClose=!0):(a.body=null,a.bodyReceived=!0);return a.bodyReceived&&(a.time=+new Date-a.time),null!==a.flashApi&&a.bodyReceived&&null!==a.body&&"deflate"===a.getField("Content-Encoding")&&(a.body=n.util.inflate(a.flashApi,a.body)),a.bodyReceived},a.getCookies=function(){var e=[];if("Set-Cookie"in a.fields)for(var t=a.fields["Set-Cookie"],r=+new Date/1e3,n=/\s*([^=]*)=?([^;]*)(;|$)/g,i=0;i<t.length;++i){var s,o=t[i];n.lastIndex=0;var c=!0,u={};do{if(null!==(s=n.exec(o))){var l=h(s[1]),p=h(s[2]);if(c)u.name=l,u.value=p,c=!1;else switch(l=l.toLowerCase()){case"expires":p=p.replace(/-/g," ");var f=Date.parse(p)/1e3;u.maxAge=Math.max(0,f-r);break;case"max-age":u.maxAge=parseInt(p,10);break;case"secure":u.secure=!0;break;case"httponly":u.httpOnly=!0;break;default:""!==l&&(u[l]=p)}}}while(null!==s&&""!==s[0]);e.push(u)}return e},a.toString=function(){var e=a.version+" "+a.code+" "+a.message+"\r\n";for(var t in a.fields)for(var r=a.fields[t],n=0;n<r.length;++n)e+=t+": "+r[n]+"\r\n";return e+="\r\n"},a},a.withinCookieDomain=function(e,t){var r=!1,n=null===t||"string"==typeof t?t:t.domain;if(null===n)r=!0;else if("."===n.charAt(0)){"string"==typeof e&&(e=new URL(e));var a="."+e.hostname,i=a.lastIndexOf(n);-1!==i&&i+n.length===a.length&&(r=!0)}return r}},function(e,t,r){e.exports=r(35)},function(e,t,r){e.exports=r(0),r(36),r(48),r(32),r(49),r(33),r(50)},function(e,t,r){e.exports=r(0),r(5),r(39),r(3),r(14),r(11),r(41),r(8),r(43),r(44),r(45),r(30),r(16),r(7),r(26),r(28),r(46),r(21),r(27),r(24),r(19),r(2),r(25),r(47),r(10),r(1)},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t){var r={};e.exports=r;var n={};r.encode=function(e,t,r){if("string"!=typeof t)throw new TypeError('"alphabet" must be a string.');if(void 0!==r&&"number"!=typeof r)throw new TypeError('"maxline" must be a number.');var n="";if(e instanceof Uint8Array){var a=0,i=t.length,s=t.charAt(0),o=[0];for(a=0;a<e.length;++a){for(var c=0,u=e[a];c<o.length;++c)u+=o[c]<<8,o[c]=u%i,u=u/i|0;for(;u>0;)o.push(u%i),u=u/i|0}for(a=0;0===e[a]&&a<e.length-1;++a)n+=s;for(a=o.length-1;a>=0;--a)n+=t[o[a]]}else n=function(e,t){var r=0,n=t.length,a=t.charAt(0),i=[0];for(r=0;r<e.length();++r){for(var s=0,o=e.at(r);s<i.length;++s)o+=i[s]<<8,i[s]=o%n,o=o/n|0;for(;o>0;)i.push(o%n),o=o/n|0}var c="";for(r=0;0===e.at(r)&&r<e.length()-1;++r)c+=a;for(r=i.length-1;r>=0;--r)c+=t[i[r]];return c}(e,t);if(r){var l=new RegExp(".{1,"+r+"}","g");n=n.match(l).join("\r\n")}return n},r.decode=function(e,t){if("string"!=typeof e)throw new TypeError('"input" must be a string.');if("string"!=typeof t)throw new TypeError('"alphabet" must be a string.');var r=n[t];if(!r){r=n[t]=[];for(var a=0;a<t.length;++a)r[t.charCodeAt(a)]=a}e=e.replace(/\s/g,"");var i=t.length,s=t.charAt(0),o=[0];for(a=0;a<e.length;a++){var c=r[e.charCodeAt(a)];if(void 0===c)return;for(var u=0,l=c;u<o.length;++u)l+=o[u]*i,o[u]=255&l,l>>=8;for(;l>0;)o.push(255&l),l>>=8}for(var p=0;e[p]===s&&p<e.length-1;++p)o.push(0);return"undefined"!=typeof Buffer?Buffer.from(o.reverse()):new Uint8Array(o.reverse())}},function(e,t,r){var n=r(0);r(5),r(10);var a=e.exports=n.tls;function i(e,t,r){var i=t.entity===n.tls.ConnectionEnd.client;e.read.cipherState={init:!1,cipher:n.cipher.createDecipher("AES-CBC",i?r.keys.server_write_key:r.keys.client_write_key),iv:i?r.keys.server_write_IV:r.keys.client_write_IV},e.write.cipherState={init:!1,cipher:n.cipher.createCipher("AES-CBC",i?r.keys.client_write_key:r.keys.server_write_key),iv:i?r.keys.client_write_IV:r.keys.server_write_IV},e.read.cipherFunction=u,e.write.cipherFunction=s,e.read.macLength=e.write.macLength=r.mac_length,e.read.macFunction=e.write.macFunction=a.hmac_sha1}function s(e,t){var r,i=!1,s=t.macFunction(t.macKey,t.sequenceNumber,e);e.fragment.putBytes(s),t.updateSequenceNumber(),r=e.version.minor===a.Versions.TLS_1_0.minor?t.cipherState.init?null:t.cipherState.iv:n.random.getBytesSync(16),t.cipherState.init=!0;var c=t.cipherState.cipher;return c.start({iv:r}),e.version.minor>=a.Versions.TLS_1_1.minor&&c.output.putBytes(r),c.update(e.fragment),c.finish(o)&&(e.fragment=c.output,e.length=e.fragment.length(),i=!0),i}function o(e,t,r){if(!r){var n=e-t.length()%e;t.fillWithByte(n-1,n)}return!0}function c(e,t,r){var n=!0;if(r){for(var a=t.length(),i=t.last(),s=a-1-i;s<a-1;++s)n=n&&t.at(s)==i;n&&t.truncate(i+1)}return n}function u(e,t){var r,i=!1;r=e.version.minor===a.Versions.TLS_1_0.minor?t.cipherState.init?null:t.cipherState.iv:e.fragment.getBytes(16),t.cipherState.init=!0;var s=t.cipherState.cipher;s.start({iv:r}),s.update(e.fragment),i=s.finish(c);var o=t.macLength,u=n.random.getBytesSync(o),l=s.output.length();l>=o?(e.fragment=s.output.getBytes(l-o),u=s.output.getBytes(o)):e.fragment=s.output.getBytes(),e.fragment=n.util.createBuffer(e.fragment),e.length=e.fragment.length();var p=t.macFunction(t.macKey,t.sequenceNumber,e);return t.updateSequenceNumber(),i=function(e,t,r){var a=n.hmac.create();return a.start("SHA1",e),a.update(t),t=a.digest().getBytes(),a.start(null,null),a.update(r),r=a.digest().getBytes(),t===r}(t.macKey,u,p)&&i}a.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA={id:[0,47],name:"TLS_RSA_WITH_AES_128_CBC_SHA",initSecurityParameters:function(e){e.bulk_cipher_algorithm=a.BulkCipherAlgorithm.aes,e.cipher_type=a.CipherType.block,e.enc_key_length=16,e.block_length=16,e.fixed_iv_length=16,e.record_iv_length=16,e.mac_algorithm=a.MACAlgorithm.hmac_sha1,e.mac_length=20,e.mac_key_length=20},initConnectionState:i},a.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA={id:[0,53],name:"TLS_RSA_WITH_AES_256_CBC_SHA",initSecurityParameters:function(e){e.bulk_cipher_algorithm=a.BulkCipherAlgorithm.aes,e.cipher_type=a.CipherType.block,e.enc_key_length=32,e.block_length=16,e.fixed_iv_length=16,e.record_iv_length=16,e.mac_algorithm=a.MACAlgorithm.hmac_sha1,e.mac_length=20,e.mac_key_length=20},initConnectionState:i}},function(e,t,r){var n=r(0);r(30),e.exports=n.mgf=n.mgf||{},n.mgf.mgf1=n.mgf1},function(e,t,r){var n=r(0);r(13),r(2),r(31),r(1);var a=r(42),i=a.publicKeyValidator,s=a.privateKeyValidator;if(void 0===o)var o=n.jsbn.BigInteger;var c=n.util.ByteBuffer,u="undefined"==typeof Buffer?Uint8Array:Buffer;n.pki=n.pki||{},e.exports=n.pki.ed25519=n.ed25519=n.ed25519||{};var l=n.ed25519;function p(e){var t=e.message;if(t instanceof Uint8Array||t instanceof u)return t;var r=e.encoding;if(void 0===t){if(!e.md)throw new TypeError('"options.message" or "options.md" not specified.');t=e.md.digest().getBytes(),r="binary"}if("string"==typeof t&&!r)throw new TypeError('"options.encoding" must be "binary" or "utf8".');if("string"==typeof t){if("undefined"!=typeof Buffer)return Buffer.from(t,r);t=new c(t,r)}else if(!(t instanceof c))throw new TypeError('"options.message" must be a node.js Buffer, a Uint8Array, a forge ByteBuffer, or a string with "options.encoding" specifying its encoding.');for(var n=new u(t.length()),a=0;a<n.length;++a)n[a]=t.at(a);return n}l.constants={},l.constants.PUBLIC_KEY_BYTE_LENGTH=32,l.constants.PRIVATE_KEY_BYTE_LENGTH=64,l.constants.SEED_BYTE_LENGTH=32,l.constants.SIGN_BYTE_LENGTH=64,l.constants.HASH_BYTE_LENGTH=64,l.generateKeyPair=function(e){var t=(e=e||{}).seed;if(void 0===t)t=n.random.getBytesSync(l.constants.SEED_BYTE_LENGTH);else if("string"==typeof t){if(t.length!==l.constants.SEED_BYTE_LENGTH)throw new TypeError('"seed" must be '+l.constants.SEED_BYTE_LENGTH+" bytes in length.")}else if(!(t instanceof Uint8Array))throw new TypeError('"seed" must be a node.js Buffer, Uint8Array, or a binary string.');t=p({message:t,encoding:"binary"});for(var r=new u(l.constants.PUBLIC_KEY_BYTE_LENGTH),a=new u(l.constants.PRIVATE_KEY_BYTE_LENGTH),i=0;i<32;++i)a[i]=t[i];return function(e,t){var r,n=[P(),P(),P(),P()],a=E(t,32);for(a[0]&=248,a[31]&=127,a[31]|=64,L(n,a),A(e,n),r=0;r<32;++r)t[r+32]=e[r]}(r,a),{publicKey:r,privateKey:a}},l.privateKeyFromAsn1=function(e){var t={},r=[];if(!n.asn1.validate(e,s,t,r)){var a=new Error("Invalid Key.");throw a.errors=r,a}var i=n.asn1.derToOid(t.privateKeyOid),o=n.oids.EdDSA25519;if(i!==o)throw new Error('Invalid OID "'+i+'"; OID must be "'+o+'".');var c=t.privateKey;return{privateKeyBytes:p({message:n.asn1.fromDer(c).value,encoding:"binary"})}},l.publicKeyFromAsn1=function(e){var t={},r=[];if(!n.asn1.validate(e,i,t,r)){var a=new Error("Invalid Key.");throw a.errors=r,a}var s=n.asn1.derToOid(t.publicKeyOid),o=n.oids.EdDSA25519;if(s!==o)throw new Error('Invalid OID "'+s+'"; OID must be "'+o+'".');var c=t.ed25519PublicKey;if(c.length!==l.constants.PUBLIC_KEY_BYTE_LENGTH)throw new Error("Key length is invalid.");return p({message:c,encoding:"binary"})},l.publicKeyFromPrivateKey=function(e){var t=p({message:(e=e||{}).privateKey,encoding:"binary"});if(t.length!==l.constants.PRIVATE_KEY_BYTE_LENGTH)throw new TypeError('"options.privateKey" must have a byte length of '+l.constants.PRIVATE_KEY_BYTE_LENGTH);for(var r=new u(l.constants.PUBLIC_KEY_BYTE_LENGTH),n=0;n<r.length;++n)r[n]=t[32+n];return r},l.sign=function(e){var t=p(e=e||{}),r=p({message:e.privateKey,encoding:"binary"});if(r.length===l.constants.SEED_BYTE_LENGTH)r=l.generateKeyPair({seed:r}).privateKey;else if(r.length!==l.constants.PRIVATE_KEY_BYTE_LENGTH)throw new TypeError('"options.privateKey" must have a byte length of '+l.constants.SEED_BYTE_LENGTH+" or "+l.constants.PRIVATE_KEY_BYTE_LENGTH);var n=new u(l.constants.SIGN_BYTE_LENGTH+t.length);!function(e,t,r,n){var a,i,s=new Float64Array(64),o=[P(),P(),P(),P()],c=E(n,32);c[0]&=248,c[31]&=127,c[31]|=64;var u=r+64;for(a=0;a<r;++a)e[64+a]=t[a];for(a=0;a<32;++a)e[32+a]=c[32+a];var l=E(e.subarray(32),r+32);for(T(l),L(o,l),A(e,o),a=32;a<64;++a)e[a]=n[a];var p=E(e,r+64);for(T(p),a=32;a<64;++a)s[a]=0;for(a=0;a<32;++a)s[a]=l[a];for(a=0;a<32;++a)for(i=0;i<32;i++)s[a+i]+=p[a]*c[i];S(e.subarray(32),s)}(n,t,t.length,r);for(var a=new u(l.constants.SIGN_BYTE_LENGTH),i=0;i<a.length;++i)a[i]=n[i];return a},l.verify=function(e){var t=p(e=e||{});if(void 0===e.signature)throw new TypeError('"options.signature" must be a node.js Buffer, a Uint8Array, a forge ByteBuffer, or a binary string.');var r=p({message:e.signature,encoding:"binary"});if(r.length!==l.constants.SIGN_BYTE_LENGTH)throw new TypeError('"options.signature" must have a byte length of '+l.constants.SIGN_BYTE_LENGTH);var n=p({message:e.publicKey,encoding:"binary"});if(n.length!==l.constants.PUBLIC_KEY_BYTE_LENGTH)throw new TypeError('"options.publicKey" must have a byte length of '+l.constants.PUBLIC_KEY_BYTE_LENGTH);var a,i=new u(l.constants.SIGN_BYTE_LENGTH+t.length),s=new u(l.constants.SIGN_BYTE_LENGTH+t.length);for(a=0;a<l.constants.SIGN_BYTE_LENGTH;++a)i[a]=r[a];for(a=0;a<t.length;++a)i[a+l.constants.SIGN_BYTE_LENGTH]=t[a];return function(e,t,r,n){var a,i=new u(32),s=[P(),P(),P(),P()],o=[P(),P(),P(),P()];if(-1,r<64)return-1;if(function(e,t){var r=P(),n=P(),a=P(),i=P(),s=P(),o=P(),c=P();_(e[2],h),function(e,t){var r;for(r=0;r<16;++r)e[r]=t[2*r]+(t[2*r+1]<<8);e[15]&=32767}(e[1],t),x(a,e[1]),K(i,a,d),O(a,a,e[2]),V(i,e[2],i),x(s,i),x(o,s),K(c,o,s),K(r,c,a),K(r,r,i),function(e,t){var r,n=P();for(r=0;r<16;++r)n[r]=t[r];for(r=250;r>=0;--r)x(n,n),1!==r&&K(n,n,t);for(r=0;r<16;++r)e[r]=n[r]}(r,r),K(r,r,a),K(r,r,i),K(r,r,i),K(e[0],r,i),x(n,e[0]),K(n,n,i),N(n,a)&&K(e[0],e[0],C);if(x(n,e[0]),K(n,n,i),N(n,a))return-1;w(e[0])===t[31]>>7&&O(e[0],f,e[0]);return K(e[3],e[0],e[1]),0}(o,n))return-1;if(!function(e,t){var r;for(r=31;r>=0;--r){if(e[t+r]<m[r])return!0;if(e[t+r]>m[r])return!1}return!1}(t,32))return-1;for(a=0;a<r;++a)e[a]=t[a];for(a=0;a<32;++a)e[a+32]=n[a];var c=E(e,r);if(T(c),R(s,o,c),L(o,t.subarray(32)),I(s,o),A(i,s),r-=64,k(t,0,i,0)){for(a=0;a<r;++a)e[a]=0;return-1}for(a=0;a<r;++a)e[a]=t[a+64];return r}(s,i,i.length,n)>=0};var f=P(),h=P([1]),d=P([30883,4953,19914,30187,55467,16705,2637,112,59544,30585,16505,36039,65139,11119,27886,20995]),y=P([61785,9906,39828,60374,45398,33411,5274,224,53552,61171,33010,6542,64743,22239,55772,9222]),g=P([54554,36645,11616,51542,42930,38181,51040,26924,56412,64982,57905,49316,21502,52590,14035,8553]),v=P([26200,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214]),m=new Float64Array([237,211,245,92,26,99,18,88,214,156,247,162,222,249,222,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16]),C=P([41136,18958,6951,50414,58488,44335,6150,12099,55207,15867,153,11085,57099,20417,9344,11139]);function E(e,t){var r=n.md.sha512.create(),a=new c(e);r.update(a.getBytes(t),"binary");var i=r.digest().getBytes();if("undefined"!=typeof Buffer)return Buffer.from(i,"binary");for(var s=new u(l.constants.HASH_BYTE_LENGTH),o=0;o<64;++o)s[o]=i.charCodeAt(o);return s}function S(e,t){var r,n,a,i;for(n=63;n>=32;--n){for(r=0,a=n-32,i=n-12;a<i;++a)t[a]+=r-16*t[n]*m[a-(n-32)],r=t[a]+128>>8,t[a]-=256*r;t[a]+=r,t[n]=0}for(r=0,a=0;a<32;++a)t[a]+=r-(t[31]>>4)*m[a],r=t[a]>>8,t[a]&=255;for(a=0;a<32;++a)t[a]-=r*m[a];for(n=0;n<32;++n)t[n+1]+=t[n]>>8,e[n]=255&t[n]}function T(e){for(var t=new Float64Array(64),r=0;r<64;++r)t[r]=e[r],e[r]=0;S(e,t)}function I(e,t){var r=P(),n=P(),a=P(),i=P(),s=P(),o=P(),c=P(),u=P(),l=P();O(r,e[1],e[0]),O(l,t[1],t[0]),K(r,r,l),V(n,e[0],e[1]),V(l,t[0],t[1]),K(n,n,l),K(a,e[3],t[3]),K(a,a,y),K(i,e[2],t[2]),V(i,i,i),O(s,n,r),O(o,i,a),V(c,i,a),V(u,n,r),K(e[0],s,o),K(e[1],u,c),K(e[2],c,o),K(e[3],s,u)}function b(e,t,r){for(var n=0;n<4;++n)D(e[n],t[n],r)}function A(e,t){var r=P(),n=P(),a=P();!function(e,t){var r,n=P();for(r=0;r<16;++r)n[r]=t[r];for(r=253;r>=0;--r)x(n,n),2!==r&&4!==r&&K(n,n,t);for(r=0;r<16;++r)e[r]=n[r]}(a,t[2]),K(r,t[0],a),K(n,t[1],a),B(e,n),e[31]^=w(r)<<7}function B(e,t){var r,n,a,i=P(),s=P();for(r=0;r<16;++r)s[r]=t[r];for(U(s),U(s),U(s),n=0;n<2;++n){for(i[0]=s[0]-65517,r=1;r<15;++r)i[r]=s[r]-65535-(i[r-1]>>16&1),i[r-1]&=65535;i[15]=s[15]-32767-(i[14]>>16&1),a=i[15]>>16&1,i[14]&=65535,D(s,i,1-a)}for(r=0;r<16;r++)e[2*r]=255&s[r],e[2*r+1]=s[r]>>8}function N(e,t){var r=new u(32),n=new u(32);return B(r,e),B(n,t),k(r,0,n,0)}function k(e,t,r,n){return function(e,t,r,n,a){var i,s=0;for(i=0;i<a;++i)s|=e[t+i]^r[n+i];return(1&s-1>>>8)-1}(e,t,r,n,32)}function w(e){var t=new u(32);return B(t,e),1&t[0]}function R(e,t,r){var n,a;for(_(e[0],f),_(e[1],h),_(e[2],h),_(e[3],f),a=255;a>=0;--a)b(e,t,n=r[a/8|0]>>(7&a)&1),I(t,e),I(e,e),b(e,t,n)}function L(e,t){var r=[P(),P(),P(),P()];_(r[0],g),_(r[1],v),_(r[2],h),K(r[3],g,v),R(e,r,t)}function _(e,t){var r;for(r=0;r<16;r++)e[r]=0|t[r]}function U(e){var t,r,n=1;for(t=0;t<16;++t)r=e[t]+n+65535,n=Math.floor(r/65536),e[t]=r-65536*n;e[0]+=n-1+37*(n-1)}function D(e,t,r){for(var n,a=~(r-1),i=0;i<16;++i)n=a&(e[i]^t[i]),e[i]^=n,t[i]^=n}function P(e){var t,r=new Float64Array(16);if(e)for(t=0;t<e.length;++t)r[t]=e[t];return r}function V(e,t,r){for(var n=0;n<16;++n)e[n]=t[n]+r[n]}function O(e,t,r){for(var n=0;n<16;++n)e[n]=t[n]-r[n]}function x(e,t){K(e,t,t)}function K(e,t,r){var n,a,i=0,s=0,o=0,c=0,u=0,l=0,p=0,f=0,h=0,d=0,y=0,g=0,v=0,m=0,C=0,E=0,S=0,T=0,I=0,b=0,A=0,B=0,N=0,k=0,w=0,R=0,L=0,_=0,U=0,D=0,P=0,V=r[0],O=r[1],x=r[2],K=r[3],M=r[4],F=r[5],q=r[6],H=r[7],j=r[8],G=r[9],Q=r[10],z=r[11],X=r[12],Y=r[13],W=r[14],Z=r[15];i+=(n=t[0])*V,s+=n*O,o+=n*x,c+=n*K,u+=n*M,l+=n*F,p+=n*q,f+=n*H,h+=n*j,d+=n*G,y+=n*Q,g+=n*z,v+=n*X,m+=n*Y,C+=n*W,E+=n*Z,s+=(n=t[1])*V,o+=n*O,c+=n*x,u+=n*K,l+=n*M,p+=n*F,f+=n*q,h+=n*H,d+=n*j,y+=n*G,g+=n*Q,v+=n*z,m+=n*X,C+=n*Y,E+=n*W,S+=n*Z,o+=(n=t[2])*V,c+=n*O,u+=n*x,l+=n*K,p+=n*M,f+=n*F,h+=n*q,d+=n*H,y+=n*j,g+=n*G,v+=n*Q,m+=n*z,C+=n*X,E+=n*Y,S+=n*W,T+=n*Z,c+=(n=t[3])*V,u+=n*O,l+=n*x,p+=n*K,f+=n*M,h+=n*F,d+=n*q,y+=n*H,g+=n*j,v+=n*G,m+=n*Q,C+=n*z,E+=n*X,S+=n*Y,T+=n*W,I+=n*Z,u+=(n=t[4])*V,l+=n*O,p+=n*x,f+=n*K,h+=n*M,d+=n*F,y+=n*q,g+=n*H,v+=n*j,m+=n*G,C+=n*Q,E+=n*z,S+=n*X,T+=n*Y,I+=n*W,b+=n*Z,l+=(n=t[5])*V,p+=n*O,f+=n*x,h+=n*K,d+=n*M,y+=n*F,g+=n*q,v+=n*H,m+=n*j,C+=n*G,E+=n*Q,S+=n*z,T+=n*X,I+=n*Y,b+=n*W,A+=n*Z,p+=(n=t[6])*V,f+=n*O,h+=n*x,d+=n*K,y+=n*M,g+=n*F,v+=n*q,m+=n*H,C+=n*j,E+=n*G,S+=n*Q,T+=n*z,I+=n*X,b+=n*Y,A+=n*W,B+=n*Z,f+=(n=t[7])*V,h+=n*O,d+=n*x,y+=n*K,g+=n*M,v+=n*F,m+=n*q,C+=n*H,E+=n*j,S+=n*G,T+=n*Q,I+=n*z,b+=n*X,A+=n*Y,B+=n*W,N+=n*Z,h+=(n=t[8])*V,d+=n*O,y+=n*x,g+=n*K,v+=n*M,m+=n*F,C+=n*q,E+=n*H,S+=n*j,T+=n*G,I+=n*Q,b+=n*z,A+=n*X,B+=n*Y,N+=n*W,k+=n*Z,d+=(n=t[9])*V,y+=n*O,g+=n*x,v+=n*K,m+=n*M,C+=n*F,E+=n*q,S+=n*H,T+=n*j,I+=n*G,b+=n*Q,A+=n*z,B+=n*X,N+=n*Y,k+=n*W,w+=n*Z,y+=(n=t[10])*V,g+=n*O,v+=n*x,m+=n*K,C+=n*M,E+=n*F,S+=n*q,T+=n*H,I+=n*j,b+=n*G,A+=n*Q,B+=n*z,N+=n*X,k+=n*Y,w+=n*W,R+=n*Z,g+=(n=t[11])*V,v+=n*O,m+=n*x,C+=n*K,E+=n*M,S+=n*F,T+=n*q,I+=n*H,b+=n*j,A+=n*G,B+=n*Q,N+=n*z,k+=n*X,w+=n*Y,R+=n*W,L+=n*Z,v+=(n=t[12])*V,m+=n*O,C+=n*x,E+=n*K,S+=n*M,T+=n*F,I+=n*q,b+=n*H,A+=n*j,B+=n*G,N+=n*Q,k+=n*z,w+=n*X,R+=n*Y,L+=n*W,_+=n*Z,m+=(n=t[13])*V,C+=n*O,E+=n*x,S+=n*K,T+=n*M,I+=n*F,b+=n*q,A+=n*H,B+=n*j,N+=n*G,k+=n*Q,w+=n*z,R+=n*X,L+=n*Y,_+=n*W,U+=n*Z,C+=(n=t[14])*V,E+=n*O,S+=n*x,T+=n*K,I+=n*M,b+=n*F,A+=n*q,B+=n*H,N+=n*j,k+=n*G,w+=n*Q,R+=n*z,L+=n*X,_+=n*Y,U+=n*W,D+=n*Z,E+=(n=t[15])*V,s+=38*(T+=n*x),o+=38*(I+=n*K),c+=38*(b+=n*M),u+=38*(A+=n*F),l+=38*(B+=n*q),p+=38*(N+=n*H),f+=38*(k+=n*j),h+=38*(w+=n*G),d+=38*(R+=n*Q),y+=38*(L+=n*z),g+=38*(_+=n*X),v+=38*(U+=n*Y),m+=38*(D+=n*W),C+=38*(P+=n*Z),i=(n=(i+=38*(S+=n*O))+(a=1)+65535)-65536*(a=Math.floor(n/65536)),s=(n=s+a+65535)-65536*(a=Math.floor(n/65536)),o=(n=o+a+65535)-65536*(a=Math.floor(n/65536)),c=(n=c+a+65535)-65536*(a=Math.floor(n/65536)),u=(n=u+a+65535)-65536*(a=Math.floor(n/65536)),l=(n=l+a+65535)-65536*(a=Math.floor(n/65536)),p=(n=p+a+65535)-65536*(a=Math.floor(n/65536)),f=(n=f+a+65535)-65536*(a=Math.floor(n/65536)),h=(n=h+a+65535)-65536*(a=Math.floor(n/65536)),d=(n=d+a+65535)-65536*(a=Math.floor(n/65536)),y=(n=y+a+65535)-65536*(a=Math.floor(n/65536)),g=(n=g+a+65535)-65536*(a=Math.floor(n/65536)),v=(n=v+a+65535)-65536*(a=Math.floor(n/65536)),m=(n=m+a+65535)-65536*(a=Math.floor(n/65536)),C=(n=C+a+65535)-65536*(a=Math.floor(n/65536)),E=(n=E+a+65535)-65536*(a=Math.floor(n/65536)),i=(n=(i+=a-1+37*(a-1))+(a=1)+65535)-65536*(a=Math.floor(n/65536)),s=(n=s+a+65535)-65536*(a=Math.floor(n/65536)),o=(n=o+a+65535)-65536*(a=Math.floor(n/65536)),c=(n=c+a+65535)-65536*(a=Math.floor(n/65536)),u=(n=u+a+65535)-65536*(a=Math.floor(n/65536)),l=(n=l+a+65535)-65536*(a=Math.floor(n/65536)),p=(n=p+a+65535)-65536*(a=Math.floor(n/65536)),f=(n=f+a+65535)-65536*(a=Math.floor(n/65536)),h=(n=h+a+65535)-65536*(a=Math.floor(n/65536)),d=(n=d+a+65535)-65536*(a=Math.floor(n/65536)),y=(n=y+a+65535)-65536*(a=Math.floor(n/65536)),g=(n=g+a+65535)-65536*(a=Math.floor(n/65536)),v=(n=v+a+65535)-65536*(a=Math.floor(n/65536)),m=(n=m+a+65535)-65536*(a=Math.floor(n/65536)),C=(n=C+a+65535)-65536*(a=Math.floor(n/65536)),E=(n=E+a+65535)-65536*(a=Math.floor(n/65536)),i+=a-1+37*(a-1),e[0]=i,e[1]=s,e[2]=o,e[3]=c,e[4]=u,e[5]=l,e[6]=p,e[7]=f,e[8]=h,e[9]=d,e[10]=y,e[11]=g,e[12]=v,e[13]=m,e[14]=C,e[15]=E}},function(e,t,r){var n=r(0);r(3);var a=n.asn1;t.privateKeyValidator={name:"PrivateKeyInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"PrivateKeyInfo.version",tagClass:a.Class.UNIVERSAL,type:a.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"PrivateKeyInfo.privateKeyAlgorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"privateKeyOid"}]},{name:"PrivateKeyInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.OCTETSTRING,constructed:!1,capture:"privateKey"}]},t.publicKeyValidator={name:"SubjectPublicKeyInfo",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,captureAsn1:"subjectPublicKeyInfo",value:[{name:"SubjectPublicKeyInfo.AlgorithmIdentifier",tagClass:a.Class.UNIVERSAL,type:a.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:a.Class.UNIVERSAL,type:a.Type.OID,constructed:!1,capture:"publicKeyOid"}]},{tagClass:a.Class.UNIVERSAL,type:a.Type.BITSTRING,constructed:!1,composed:!0,captureBitStringValue:"ed25519PublicKey"}]}},function(e,t,r){var n=r(0);r(1),r(2),r(13),e.exports=n.kem=n.kem||{};var a=n.jsbn.BigInteger;function i(e,t,r,a){e.generate=function(e,i){for(var s=new n.util.ByteBuffer,o=Math.ceil(i/a)+r,c=new n.util.ByteBuffer,u=r;u<o;++u){c.putInt32(u),t.start(),t.update(e+c.getBytes());var l=t.digest();s.putBytes(l.getBytes(a))}return s.truncate(s.length()-i),s.getBytes()}}n.kem.rsa={},n.kem.rsa.create=function(e,t){var r=(t=t||{}).prng||n.random,i={encrypt:function(t,i){var s,o=Math.ceil(t.n.bitLength()/8);do{s=new a(n.util.bytesToHex(r.getBytesSync(o)),16).mod(t.n)}while(s.compareTo(a.ONE)<=0);var c=o-(s=n.util.hexToBytes(s.toString(16))).length;return c>0&&(s=n.util.fillString(String.fromCharCode(0),c)+s),{encapsulation:t.encrypt(s,"NONE"),key:e.generate(s,i)}},decrypt:function(t,r,n){var a=t.decrypt(r,"NONE");return e.generate(a,n)}};return i},n.kem.kdf1=function(e,t){i(this,e,0,t||e.digestLength)},n.kem.kdf2=function(e,t){i(this,e,1,t||e.digestLength)}},function(e,t,r){var n=r(0);r(1),e.exports=n.log=n.log||{},n.log.levels=["none","error","warning","info","debug","verbose","max"];var a={},i=[],s=null;n.log.LEVEL_LOCKED=2,n.log.NO_LEVEL_CHECK=4,n.log.INTERPOLATE=8;for(var o=0;o<n.log.levels.length;++o){var c=n.log.levels[o];a[c]={index:o,name:c.toUpperCase()}}n.log.logMessage=function(e){for(var t=a[e.level].index,r=0;r<i.length;++r){var s=i[r];if(s.flags&n.log.NO_LEVEL_CHECK)s.f(e);else t<=a[s.level].index&&s.f(s,e)}},n.log.prepareStandard=function(e){"standard"in e||(e.standard=a[e.level].name+" ["+e.category+"] "+e.message)},n.log.prepareFull=function(e){if(!("full"in e)){var t=[e.message];t=t.concat([]||!1),e.full=n.util.format.apply(this,t)}},n.log.prepareStandardFull=function(e){"standardFull"in e||(n.log.prepareStandard(e),e.standardFull=e.standard)};var u=["error","warning","info","debug","verbose"];for(o=0;o<u.length;++o)!function(e){n.log[e]=function(t,r){var a=Array.prototype.slice.call(arguments).slice(2),i={timestamp:new Date,level:e,category:t,message:r,arguments:a};n.log.logMessage(i)}}(u[o]);if(n.log.makeLogger=function(e){var t={flags:0,f:e};return n.log.setLevel(t,"none"),t},n.log.setLevel=function(e,t){var r=!1;if(e&&!(e.flags&n.log.LEVEL_LOCKED))for(var a=0;a<n.log.levels.length;++a){if(t==n.log.levels[a]){e.level=t,r=!0;break}}return r},n.log.lock=function(e,t){void 0===t||t?e.flags|=n.log.LEVEL_LOCKED:e.flags&=~n.log.LEVEL_LOCKED},n.log.addLogger=function(e){i.push(e)},"undefined"!=typeof console&&"log"in console){var l;if(console.error&&console.warn&&console.info&&console.debug){var p={error:console.error,warning:console.warn,info:console.info,debug:console.debug,verbose:console.debug},f=function(e,t){n.log.prepareStandard(t);var r=p[t.level],a=[t.standard];a=a.concat(t.arguments.slice()),r.apply(console,a)};l=n.log.makeLogger(f)}else{f=function(e,t){n.log.prepareStandardFull(t),console.log(t.standardFull)};l=n.log.makeLogger(f)}n.log.setLevel(l,"debug"),n.log.addLogger(l),s=l}else console={log:function(){}};if(null!==s&&"undefined"!=typeof window&&window.location){var h=new URL(window.location.href).searchParams;if(h.has("console.level")&&n.log.setLevel(s,h.get("console.level").slice(-1)[0]),h.has("console.lock"))"true"==h.get("console.lock").slice(-1)[0]&&n.log.lock(s)}n.log.consoleLogger=s},function(e,t,r){e.exports=r(4),r(15),r(9),r(23),r(31)},function(e,t,r){var n=r(0);r(5),r(3),r(11),r(6),r(7),r(29),r(2),r(1),r(18);var a=n.asn1,i=e.exports=n.pkcs7=n.pkcs7||{};function s(e){var t={},r=[];if(!a.validate(e,i.asn1.recipientInfoValidator,t,r)){var s=new Error("Cannot read PKCS#7 RecipientInfo. ASN.1 object is not an PKCS#7 RecipientInfo.");throw s.errors=r,s}return{version:t.version.charCodeAt(0),issuer:n.pki.RDNAttributesAsArray(t.issuer),serialNumber:n.util.createBuffer(t.serial).toHex(),encryptedContent:{algorithm:a.derToOid(t.encAlgorithm),parameter:t.encParameter?t.encParameter.value:void 0,content:t.encKey}}}function o(e){for(var t,r=[],i=0;i<e.length;++i)r.push((t=e[i],a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(t.version).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[n.pki.distinguishedNameToAsn1({attributes:t.issuer}),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,n.util.hexToBytes(t.serialNumber))]),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(t.encryptedContent.algorithm).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")]),a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,t.encryptedContent.content)])));return r}function c(e){var t=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(e.version).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[n.pki.distinguishedNameToAsn1({attributes:e.issuer}),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,n.util.hexToBytes(e.serialNumber))]),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.digestAlgorithm).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")])]);if(e.authenticatedAttributesAsn1&&t.value.push(e.authenticatedAttributesAsn1),t.value.push(a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.signatureAlgorithm).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")])),t.value.push(a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,e.signature)),e.unauthenticatedAttributes.length>0){for(var r=a.create(a.Class.CONTEXT_SPECIFIC,1,!0,[]),i=0;i<e.unauthenticatedAttributes.length;++i){var s=e.unauthenticatedAttributes[i];r.values.push(u(s))}t.value.push(r)}return t}function u(e){var t;if(e.type===n.pki.oids.contentType)t=a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.value).getBytes());else if(e.type===n.pki.oids.messageDigest)t=a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,e.value.bytes());else if(e.type===n.pki.oids.signingTime){var r=new Date("1950-01-01T00:00:00Z"),i=new Date("2050-01-01T00:00:00Z"),s=e.value;if("string"==typeof s){var o=Date.parse(s);s=isNaN(o)?13===s.length?a.utcTimeToDate(s):a.generalizedTimeToDate(s):new Date(o)}t=s>=r&&s<i?a.create(a.Class.UNIVERSAL,a.Type.UTCTIME,!1,a.dateToUtcTime(s)):a.create(a.Class.UNIVERSAL,a.Type.GENERALIZEDTIME,!1,a.dateToGeneralizedTime(s))}return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.type).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SET,!0,[t])])}function l(e,t,r){var i={};if(!a.validate(t,r,i,[])){var s=new Error("Cannot read PKCS#7 message. ASN.1 object is not a supported PKCS#7 message.");throw s.errors=s,s}if(a.derToOid(i.contentType)!==n.pki.oids.data)throw new Error("Unsupported PKCS#7 message. Only wrapped ContentType Data supported.");if(i.encryptedContent){var o="";if(n.util.isArray(i.encryptedContent))for(var c=0;c<i.encryptedContent.length;++c){if(i.encryptedContent[c].type!==a.Type.OCTETSTRING)throw new Error("Malformed PKCS#7 message, expecting encrypted content constructed of only OCTET STRING objects.");o+=i.encryptedContent[c].value}else o=i.encryptedContent;e.encryptedContent={algorithm:a.derToOid(i.encAlgorithm),parameter:n.util.createBuffer(i.encParameter.value),content:n.util.createBuffer(o)}}if(i.content){o="";if(n.util.isArray(i.content))for(c=0;c<i.content.length;++c){if(i.content[c].type!==a.Type.OCTETSTRING)throw new Error("Malformed PKCS#7 message, expecting content constructed of only OCTET STRING objects.");o+=i.content[c].value}else o=i.content;e.content=n.util.createBuffer(o)}return e.version=i.version.charCodeAt(0),e.rawCapture=i,i}function p(e){if(void 0===e.encryptedContent.key)throw new Error("Symmetric key not available.");if(void 0===e.content){var t;switch(e.encryptedContent.algorithm){case n.pki.oids["aes128-CBC"]:case n.pki.oids["aes192-CBC"]:case n.pki.oids["aes256-CBC"]:t=n.aes.createDecryptionCipher(e.encryptedContent.key);break;case n.pki.oids.desCBC:case n.pki.oids["des-EDE3-CBC"]:t=n.des.createDecryptionCipher(e.encryptedContent.key);break;default:throw new Error("Unsupported symmetric cipher, OID "+e.encryptedContent.algorithm)}if(t.start(e.encryptedContent.parameter),t.update(e.encryptedContent.content),!t.finish())throw new Error("Symmetric decryption failed.");e.content=t.output}}i.messageFromPem=function(e){var t=n.pem.decode(e)[0];if("PKCS7"!==t.type){var r=new Error('Could not convert PKCS#7 message from PEM; PEM header type is not "PKCS#7".');throw r.headerType=t.type,r}if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert PKCS#7 message from PEM; PEM is encrypted.");var s=a.fromDer(t.body);return i.messageFromAsn1(s)},i.messageToPem=function(e,t){var r={type:"PKCS7",body:a.toDer(e.toAsn1()).getBytes()};return n.pem.encode(r,{maxline:t})},i.messageFromAsn1=function(e){var t={},r=[];if(!a.validate(e,i.asn1.contentInfoValidator,t,r)){var s=new Error("Cannot read PKCS#7 message. ASN.1 object is not an PKCS#7 ContentInfo.");throw s.errors=r,s}var o,c=a.derToOid(t.contentType);switch(c){case n.pki.oids.envelopedData:o=i.createEnvelopedData();break;case n.pki.oids.encryptedData:o=i.createEncryptedData();break;case n.pki.oids.signedData:o=i.createSignedData();break;default:throw new Error("Cannot read PKCS#7 message. ContentType with OID "+c+" is not (yet) supported.")}return o.fromAsn1(t.content.value[0]),o},i.createSignedData=function(){var e=null;return e={type:n.pki.oids.signedData,version:1,certificates:[],crls:[],signers:[],digestAlgorithmIdentifiers:[],contentInfo:null,signerInfos:[],fromAsn1:function(t){if(l(e,t,i.asn1.signedDataValidator),e.certificates=[],e.crls=[],e.digestAlgorithmIdentifiers=[],e.contentInfo=null,e.signerInfos=[],e.rawCapture.certificates)for(var r=e.rawCapture.certificates.value,a=0;a<r.length;++a)e.certificates.push(n.pki.certificateFromAsn1(r[a]))},toAsn1:function(){e.contentInfo||e.sign();for(var t=[],r=0;r<e.certificates.length;++r)t.push(n.pki.certificateToAsn1(e.certificates[r]));var i=[],s=a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(e.version).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SET,!0,e.digestAlgorithmIdentifiers),e.contentInfo])]);return t.length>0&&s.value[0].value.push(a.create(a.Class.CONTEXT_SPECIFIC,0,!0,t)),i.length>0&&s.value[0].value.push(a.create(a.Class.CONTEXT_SPECIFIC,1,!0,i)),s.value[0].value.push(a.create(a.Class.UNIVERSAL,a.Type.SET,!0,e.signerInfos)),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.type).getBytes()),s])},addSigner:function(t){var r=t.issuer,a=t.serialNumber;if(t.certificate){var i=t.certificate;"string"==typeof i&&(i=n.pki.certificateFromPem(i)),r=i.issuer.attributes,a=i.serialNumber}var s=t.key;if(!s)throw new Error("Could not add PKCS#7 signer; no private key specified.");"string"==typeof s&&(s=n.pki.privateKeyFromPem(s));var o=t.digestAlgorithm||n.pki.oids.sha1;switch(o){case n.pki.oids.sha1:case n.pki.oids.sha256:case n.pki.oids.sha384:case n.pki.oids.sha512:case n.pki.oids.md5:break;default:throw new Error("Could not add PKCS#7 signer; unknown message digest algorithm: "+o)}var c=t.authenticatedAttributes||[];if(c.length>0){for(var u=!1,l=!1,p=0;p<c.length;++p){var f=c[p];if(u||f.type!==n.pki.oids.contentType){if(l||f.type!==n.pki.oids.messageDigest);else if(l=!0,u)break}else if(u=!0,l)break}if(!u||!l)throw new Error("Invalid signer.authenticatedAttributes. If signer.authenticatedAttributes is specified, then it must contain at least two attributes, PKCS #9 content-type and PKCS #9 message-digest.")}e.signers.push({key:s,version:1,issuer:r,serialNumber:a,digestAlgorithm:o,signatureAlgorithm:n.pki.oids.rsaEncryption,signature:null,authenticatedAttributes:c,unauthenticatedAttributes:[]})},sign:function(t){var r;(t=t||{},"object"!=typeof e.content||null===e.contentInfo)&&(e.contentInfo=a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(n.pki.oids.data).getBytes())]),"content"in e&&(e.content instanceof n.util.ByteBuffer?r=e.content.bytes():"string"==typeof e.content&&(r=n.util.encodeUtf8(e.content)),t.detached?e.detachedContent=a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,r):e.contentInfo.value.push(a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,r)]))));0!==e.signers.length&&function(t){var r;r=e.detachedContent?e.detachedContent:(r=e.contentInfo.value[1]).value[0];if(!r)throw new Error("Could not sign PKCS#7 message; there is no content to sign.");var i=a.derToOid(e.contentInfo.value[0].value),s=a.toDer(r);for(var o in s.getByte(),a.getBerValueLength(s),s=s.getBytes(),t)t[o].start().update(s);for(var l=new Date,p=0;p<e.signers.length;++p){var f=e.signers[p];if(0===f.authenticatedAttributes.length){if(i!==n.pki.oids.data)throw new Error("Invalid signer; authenticatedAttributes must be present when the ContentInfo content type is not PKCS#7 Data.")}else{f.authenticatedAttributesAsn1=a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[]);for(var h=a.create(a.Class.UNIVERSAL,a.Type.SET,!0,[]),d=0;d<f.authenticatedAttributes.length;++d){var y=f.authenticatedAttributes[d];y.type===n.pki.oids.messageDigest?y.value=t[f.digestAlgorithm].digest():y.type===n.pki.oids.signingTime&&(y.value||(y.value=l)),h.value.push(u(y)),f.authenticatedAttributesAsn1.value.push(u(y))}s=a.toDer(h).getBytes(),f.md.start().update(s)}f.signature=f.key.sign(f.md,"RSASSA-PKCS1-V1_5")}e.signerInfos=function(e){for(var t=[],r=0;r<e.length;++r)t.push(c(e[r]));return t}(e.signers)}(function(){for(var t={},r=0;r<e.signers.length;++r){var i=e.signers[r];(s=i.digestAlgorithm)in t||(t[s]=n.md[n.pki.oids[s]].create()),0===i.authenticatedAttributes.length?i.md=t[s]:i.md=n.md[n.pki.oids[s]].create()}for(var s in e.digestAlgorithmIdentifiers=[],t)e.digestAlgorithmIdentifiers.push(a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(s).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")]));return t}())},verify:function(){throw new Error("PKCS#7 signature verification not yet implemented.")},addCertificate:function(t){"string"==typeof t&&(t=n.pki.certificateFromPem(t)),e.certificates.push(t)},addCertificateRevokationList:function(e){throw new Error("PKCS#7 CRL support not yet implemented.")}}},i.createEncryptedData=function(){var e=null;return e={type:n.pki.oids.encryptedData,version:0,encryptedContent:{algorithm:n.pki.oids["aes256-CBC"]},fromAsn1:function(t){l(e,t,i.asn1.encryptedDataValidator)},decrypt:function(t){void 0!==t&&(e.encryptedContent.key=t),p(e)}}},i.createEnvelopedData=function(){var e=null;return e={type:n.pki.oids.envelopedData,version:0,recipients:[],encryptedContent:{algorithm:n.pki.oids["aes256-CBC"]},fromAsn1:function(t){var r=l(e,t,i.asn1.envelopedDataValidator);e.recipients=function(e){for(var t=[],r=0;r<e.length;++r)t.push(s(e[r]));return t}(r.recipientInfos.value)},toAsn1:function(){return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(e.type).getBytes()),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(e.version).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SET,!0,o(e.recipients)),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,(t=e.encryptedContent,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(n.pki.oids.data).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(t.algorithm).getBytes()),t.parameter?a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,t.parameter.getBytes()):void 0]),a.create(a.Class.CONTEXT_SPECIFIC,0,!0,[a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,t.content.getBytes())])]))])])]);var t},findRecipient:function(t){for(var r=t.issuer.attributes,n=0;n<e.recipients.length;++n){var a=e.recipients[n],i=a.issuer;if(a.serialNumber===t.serialNumber&&i.length===r.length){for(var s=!0,o=0;o<r.length;++o)if(i[o].type!==r[o].type||i[o].value!==r[o].value){s=!1;break}if(s)return a}}return null},decrypt:function(t,r){if(void 0===e.encryptedContent.key&&void 0!==t&&void 0!==r)switch(t.encryptedContent.algorithm){case n.pki.oids.rsaEncryption:case n.pki.oids.desCBC:var a=r.decrypt(t.encryptedContent.content);e.encryptedContent.key=n.util.createBuffer(a);break;default:throw new Error("Unsupported asymmetric cipher, OID "+t.encryptedContent.algorithm)}p(e)},addRecipient:function(t){e.recipients.push({version:0,issuer:t.issuer.attributes,serialNumber:t.serialNumber,encryptedContent:{algorithm:n.pki.oids.rsaEncryption,key:t.publicKey}})},encrypt:function(t,r){if(void 0===e.encryptedContent.content){var a,i,s;switch(r=r||e.encryptedContent.algorithm,t=t||e.encryptedContent.key,r){case n.pki.oids["aes128-CBC"]:a=16,i=16,s=n.aes.createEncryptionCipher;break;case n.pki.oids["aes192-CBC"]:a=24,i=16,s=n.aes.createEncryptionCipher;break;case n.pki.oids["aes256-CBC"]:a=32,i=16,s=n.aes.createEncryptionCipher;break;case n.pki.oids["des-EDE3-CBC"]:a=24,i=8,s=n.des.createEncryptionCipher;break;default:throw new Error("Unsupported symmetric cipher, OID "+r)}if(void 0===t)t=n.util.createBuffer(n.random.getBytes(a));else if(t.length()!=a)throw new Error("Symmetric key has wrong length; got "+t.length()+" bytes, expected "+a+".");e.encryptedContent.algorithm=r,e.encryptedContent.key=t,e.encryptedContent.parameter=n.util.createBuffer(n.random.getBytes(i));var o=s(t);if(o.start(e.encryptedContent.parameter.copy()),o.update(e.content),!o.finish())throw new Error("Symmetric encryption failed.");e.encryptedContent.content=o.output}for(var c=0;c<e.recipients.length;++c){var u=e.recipients[c];if(void 0===u.encryptedContent.content)switch(u.encryptedContent.algorithm){case n.pki.oids.rsaEncryption:u.encryptedContent.content=u.encryptedContent.key.encrypt(e.encryptedContent.key.data);break;default:throw new Error("Unsupported asymmetric cipher, OID "+u.encryptedContent.algorithm)}}}}}},function(e,t,r){var n=r(0);r(5),r(8),r(15),r(9),r(1);var a=e.exports=n.ssh=n.ssh||{};function i(e,t){var r=t.toString(16);r[0]>="8"&&(r="00"+r);var a=n.util.hexToBytes(r);e.putInt32(a.length),e.putBytes(a)}function s(e,t){e.putInt32(t.length),e.putString(t)}function o(){for(var e=n.md.sha1.create(),t=arguments.length,r=0;r<t;++r)e.update(arguments[r]);return e.digest()}a.privateKeyToPutty=function(e,t,r){var a=""===(t=t||"")?"none":"aes256-cbc",c="PuTTY-User-Key-File-2: ssh-rsa\r\n";c+="Encryption: "+a+"\r\n",c+="Comment: "+(r=r||"")+"\r\n";var u=n.util.createBuffer();s(u,"ssh-rsa"),i(u,e.e),i(u,e.n);var l=n.util.encode64(u.bytes(),64),p=Math.floor(l.length/66)+1;c+="Public-Lines: "+p+"\r\n",c+=l;var f,h=n.util.createBuffer();if(i(h,e.d),i(h,e.p),i(h,e.q),i(h,e.qInv),t){var d=h.length()+16-1;d-=d%16;var y=o(h.bytes());y.truncate(y.length()-d+h.length()),h.putBuffer(y);var g=n.util.createBuffer();g.putBuffer(o("\0\0\0\0",t)),g.putBuffer(o("\0\0\0",t));var v=n.aes.createEncryptionCipher(g.truncate(8),"CBC");v.start(n.util.createBuffer().fillWithByte(0,16)),v.update(h.copy()),v.finish();var m=v.output;m.truncate(16),f=n.util.encode64(m.bytes(),64)}else f=n.util.encode64(h.bytes(),64);c+="\r\nPrivate-Lines: "+(p=Math.floor(f.length/66)+1)+"\r\n",c+=f;var C=o("putty-private-key-file-mac-key",t),E=n.util.createBuffer();s(E,"ssh-rsa"),s(E,a),s(E,r),E.putInt32(u.length()),E.putBuffer(u),E.putInt32(h.length()),E.putBuffer(h);var S=n.hmac.create();return S.start("sha1",C),S.update(E.bytes()),c+="\r\nPrivate-MAC: "+S.digest().toHex()+"\r\n"},a.publicKeyToOpenSSH=function(e,t){t=t||"";var r=n.util.createBuffer();return s(r,"ssh-rsa"),i(r,e.e),i(r,e.n),"ssh-rsa "+n.util.encode64(r.bytes())+" "+t},a.privateKeyToOpenSSH=function(e,t){return t?n.pki.encryptRsaPrivateKey(e,t,{legacy:!0,algorithm:"aes128"}):n.pki.privateKeyToPem(e)},a.getPublicKeyFingerprint=function(e,t){var r=(t=t||{}).md||n.md.md5.create(),a=n.util.createBuffer();s(a,"ssh-rsa"),i(a,e.e),i(a,e.n),r.start(),r.update(a.getBytes());var o=r.digest();if("hex"===t.encoding){var c=o.toHex();return t.delimiter?c.match(/.{2}/g).join(t.delimiter):c}if("binary"===t.encoding)return o.getBytes();if(t.encoding)throw new Error('Unknown encoding "'+t.encoding+'".');return o}},function(e,t,r){var n,a,i,s=r(0),o=e.exports=s.form=s.form||{};n=jQuery,a=/([^\[]*?)\[(.*?)\]/g,i=function(e,t,r,i){for(var s=[],o=0;o<t.length;++o){var c=t[o];if(-1!==c.indexOf("[")&&-1===c.indexOf("]")&&o<t.length-1)do{c+="."+t[++o]}while(o<t.length-1&&-1===t[o].indexOf("]"));s.push(c)}t=s,s=[],n.each(t,(function(e,t){s=s.concat(function(e){for(var t,r=[];t=a.exec(e);)t[1].length>0&&r.push(t[1]),t.length>=2&&r.push(t[2]);return 0===r.length&&r.push(e),r}(t))})),t=s,n.each(t,(function(a,s){if(i&&0!==s.length&&s in i&&(s=i[s]),0===s.length&&(s=e.length),e[s])a==t.length-1?(n.isArray(e[s])||(e[s]=[e[s]]),e[s].push(r)):e=e[s];else if(a==t.length-1)e[s]=r;else{var o=t[a+1];if(0===o.length)e[s]=[];else{var c=o-0==o&&o.length>0;e[s]=c?[]:{}}e=e[s]}}))},o.serialize=function(e,t,r){var a={};return t=t||".",n.each(e.serializeArray(),(function(){i(a,this.name.split(t),this.value||"",r)})),a}},function(e,t,r){var n=r(0);r(10),n.tls.wrapSocket=function(e){var t=e.socket,r={id:t.id,connected:t.connected||function(e){},closed:t.closed||function(e){},data:t.data||function(e){},error:t.error||function(e){}},a=n.tls.createConnection({server:!1,sessionId:e.sessionId||null,caStore:e.caStore||[],sessionCache:e.sessionCache||null,cipherSuites:e.cipherSuites||null,virtualHost:e.virtualHost,verify:e.verify,getCertificate:e.getCertificate,getPrivateKey:e.getPrivateKey,getSignature:e.getSignature,deflate:e.deflate,inflate:e.inflate,connected:function(e){1===e.handshakes&&r.connected({id:t.id,type:"connect",bytesAvailable:e.data.length()})},tlsDataReady:function(e){return t.send(e.tlsData.getBytes())},dataReady:function(e){r.data({id:t.id,type:"socketData",bytesAvailable:e.data.length()})},closed:function(e){t.close()},error:function(e,n){r.error({id:t.id,type:"tlsError",message:n.message,bytesAvailable:0,error:n}),t.close()}});t.connected=function(t){a.handshake(e.sessionId)},t.closed=function(e){a.open&&a.handshaking&&r.error({id:t.id,type:"ioError",message:"Connection closed during handshake.",bytesAvailable:0}),a.close(),r.closed({id:t.id,type:"close",bytesAvailable:0})},t.error=function(e){r.error({id:t.id,type:e.type,message:e.message,bytesAvailable:0}),a.close()};var i=0;return t.data=function(e){if(a.open){if(e.bytesAvailable>=i){var r=Math.max(e.bytesAvailable,i),n=t.receive(r);null!==n&&(i=a.process(n))}}else t.receive(e.bytesAvailable)},r.destroy=function(){t.destroy()},r.setSessionCache=function(e){a.sessionCache=tls.createSessionCache(e)},r.connect=function(e){t.connect(e)},r.close=function(){a.close()},r.isConnected=function(){return a.isConnected&&t.isConnected()},r.send=function(e){return a.prepare(e)},r.receive=function(e){return a.data.getBytes(e)},r.bytesAvailable=function(){return a.data.length()},r}},function(e,t,r){var n=r(0);r(32),r(33);var a,i,s,o,c,u,l,p,f,h,d=e.exports=n.xhr=n.xhr||{};a=jQuery,i="forge.xhr",s=null,o=0,c=null,u=null,l={},p=10,f=n.net,h=n.http,d.init=function(e){n.log.debug(i,"initializing",e),o=e.policyPort||o,c=e.policyUrl||c,p=e.connections||p,s=f.createSocketPool({flashId:e.flashId,policyPort:o,policyUrl:c,msie:e.msie||!1}),u=h.createClient({url:e.url||window.location.protocol+"//"+window.location.host,socketPool:s,policyPort:o,policyUrl:c,connections:e.connections||p,caCerts:e.caCerts,cipherSuites:e.cipherSuites,persistCookies:e.persistCookies||!0,primeTlsSockets:e.primeTlsSockets||!1,verify:e.verify,getCertificate:e.getCertificate,getPrivateKey:e.getPrivateKey,getSignature:e.getSignature}),l[u.url.origin]=u,n.log.debug(i,"ready")},d.cleanup=function(){for(var e in l)l[e].destroy();l={},u=null,s.destroy(),s=null},d.setCookie=function(e){if(e.maxAge=e.maxAge||-1,e.domain)for(var t in l){var r=l[t];h.withinCookieDomain(r.url,e)&&r.secure===e.secure&&r.setCookie(e)}else u.setCookie(e)},d.getCookie=function(e,t,r){var a=null;if(r)for(var i in l){var s=l[i];if(h.withinCookieDomain(s.url,r)){var o=s.getCookie(e,t);null!==o&&(null===a?a=o:n.util.isArray(a)?a.push(o):a=[a,o])}}else a=u.getCookie(e,t);return a},d.removeCookie=function(e,t,r){var n=!1;if(r)for(var a in l){var i=l[a];h.withinCookieDomain(i.url,r)&&i.removeCookie(e,t)&&(n=!0)}else n=u.removeCookie(e,t);return n},d.create=function(e){e=a.extend({logWarningOnError:!0,verbose:!1,logError:function(){},logWarning:function(){},logDebug:function(){},logVerbose:function(){},url:null},e||{});var t={client:null,request:null,response:null,asynchronous:!0,sendFlag:!1,errorFlag:!1},r={error:e.logError||n.log.error,warning:e.logWarning||n.log.warning,debug:e.logDebug||n.log.debug,verbose:e.logVerbose||n.log.verbose},f={onreadystatechange:null,readyState:0,responseText:"",responseXML:null,status:0,statusText:""};if(null===e.url)t.client=u;else{var d;try{d=new URL(e.url)}catch(t){new Error("Invalid url.").details={url:e.url}}d.origin in l?t.client=l[d.origin]:(t.client=h.createClient({url:e.url,socketPool:s,policyPort:e.policyPort||o,policyUrl:e.policyUrl||c,connections:e.connections||p,caCerts:e.caCerts,cipherSuites:e.cipherSuites,persistCookies:e.persistCookies||!0,primeTlsSockets:e.primeTlsSockets||!1,verify:e.verify,getCertificate:e.getCertificate,getPrivateKey:e.getPrivateKey,getSignature:e.getSignature}),l[d.origin]=t.client)}return f.open=function(e,r,n,a,i){switch(e){case"DELETE":case"GET":case"HEAD":case"OPTIONS":case"PATCH":case"POST":case"PUT":break;case"CONNECT":case"TRACE":case"TRACK":throw new Error("CONNECT, TRACE and TRACK methods are disallowed");default:throw new Error("Invalid method: "+e)}t.sendFlag=!1,f.responseText="",f.responseXML=null,f.status=0,f.statusText="",t.request=h.createRequest({method:e,path:r}),f.readyState=1,f.onreadystatechange&&f.onreadystatechange()},f.setRequestHeader=function(e,r){if(1!=f.readyState||t.sendFlag)throw new Error("XHR not open or sending");t.request.setField(e,r)},f.send=function(e){if(1!=f.readyState||t.sendFlag)throw new Error("XHR not open or sending");if(e&&"GET"!==t.request.method&&"HEAD"!==t.request.method)if("undefined"!=typeof XMLSerializer)if(e instanceof Document){var n=new XMLSerializer;t.request.body=n.serializeToString(e)}else t.request.body=e;else void 0!==e.xml?t.request.body=e.xml:t.request.body=e;t.errorFlag=!1,t.sendFlag=!0,f.onreadystatechange&&f.onreadystatechange();var a={};a.request=t.request,a.headerReady=function(e){f.cookies=t.client.cookies,f.readyState=2,f.status=e.response.code,f.statusText=e.response.message,t.response=e.response,f.onreadystatechange&&f.onreadystatechange(),t.response.aborted||(f.readyState=3,f.onreadystatechange&&f.onreadystatechange())},a.bodyReady=function(e){f.readyState=4;var n=e.response.getField("Content-Type");if(n&&(0===n.indexOf("text/xml")||0===n.indexOf("application/xml")||-1!==n.indexOf("+xml")))try{var s=new ActiveXObject("MicrosoftXMLDOM");s.async=!1,s.loadXML(e.response.body),f.responseXML=s}catch(e){var o=new DOMParser;f.responseXML=o.parseFromString(e.body,"text/xml")}var c=0;null!==e.response.body&&(f.responseText=e.response.body,c=e.response.body.length);var u=t.request,l=u.method+" "+u.path+" "+f.status+" "+f.statusText+" "+c+"B "+(e.request.connectTime+e.request.time+e.response.time)+"ms";a.verbose?(f.status>=400&&a.logWarningOnError?r.warning:r.verbose)(i,l,e,e.response.body?"\n"+e.response.body:"\nNo content"):(f.status>=400&&a.logWarningOnError?r.warning:r.debug)(i,l),f.onreadystatechange&&f.onreadystatechange()},a.error=function(e){var n=t.request;r.error(i,n.method+" "+n.path,e),f.responseText="",f.responseXML=null,t.errorFlag=!0,f.status=0,f.statusText="",f.readyState=4,f.onreadystatechange&&f.onreadystatechange()},t.client.send(a)},f.abort=function(){t.request.abort(),f.responseText="",f.responseXML=null,t.errorFlag=!0,f.status=0,f.statusText="",t.request=null,t.response=null,4===f.readyState||0===f.readyState||1===f.readyState&&!t.sendFlag||(f.readyState=4,t.sendFlag=!1,f.onreadystatechange&&f.onreadystatechange()),f.readyState=0},f.getAllResponseHeaders=function(){var e="";if(null!==t.response){var r=t.response.fields;a.each(r,(function(t,r){a.each(r,(function(r,n){e+=t+": "+n+"\r\n"}))}))}return e},f.getResponseHeader=function(e){var r=null;return null!==t.response&&e in t.response.fields&&(r=t.response.fields[e],n.util.isArray(r)&&(r=r.join())),r},f}}])}));
+//# sourceMappingURL=forge.all.min.js.map
Index: frontend/node_modules/node-forge/dist/forge.all.min.js.map
===================================================================
--- frontend/node_modules/node-forge/dist/forge.all.min.js.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/dist/forge.all.min.js.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"forge.all.min.js","sources":["webpack://[name]/forge.all.min.js"],"mappings":"AAAA","sourceRoot":""}
Index: frontend/node_modules/node-forge/dist/forge.min.js
===================================================================
--- frontend/node_modules/node-forge/dist/forge.min.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/dist/forge.min.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,2 @@
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.forge=t():e.forge=t()}(window,(function(){return function(e){var t={};function r(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,a){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(a,n,function(t){return e[t]}.bind(null,n));return a},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=32)}([function(e,t){e.exports={options:{usePureJavaScript:!1}}},function(e,t,r){(function(t){var a=r(0),n=r(35),i=e.exports=a.util=a.util||{};function s(e){if(8!==e&&16!==e&&24!==e&&32!==e)throw new Error("Only 8, 16, 24, or 32 bits supported: "+e)}function o(e){if(this.data="",this.read=0,"string"==typeof e)this.data=e;else if(i.isArrayBuffer(e)||i.isArrayBufferView(e))if("undefined"!=typeof Buffer&&e instanceof Buffer)this.data=e.toString("binary");else{var t=new Uint8Array(e);try{this.data=String.fromCharCode.apply(null,t)}catch(e){for(var r=0;r<t.length;++r)this.putByte(t[r])}}else(e instanceof o||"object"==typeof e&&"string"==typeof e.data&&"number"==typeof e.read)&&(this.data=e.data,this.read=e.read);this._constructedStringLength=0}!function(){if("undefined"!=typeof process&&process.nextTick&&!process.browser)return i.nextTick=process.nextTick,void("function"==typeof setImmediate?i.setImmediate=setImmediate:i.setImmediate=i.nextTick);if("function"==typeof setImmediate)return i.setImmediate=function(){return setImmediate.apply(void 0,arguments)},void(i.nextTick=function(e){return setImmediate(e)});if(i.setImmediate=function(e){setTimeout(e,0)},"undefined"!=typeof window&&"function"==typeof window.postMessage){var e="forge.setImmediate",t=[];i.setImmediate=function(r){t.push(r),1===t.length&&window.postMessage(e,"*")},window.addEventListener("message",(function(r){if(r.source===window&&r.data===e){r.stopPropagation();var a=t.slice();t.length=0,a.forEach((function(e){e()}))}}),!0)}if("undefined"!=typeof MutationObserver){var r=Date.now(),a=!0,n=document.createElement("div");t=[];new MutationObserver((function(){var e=t.slice();t.length=0,e.forEach((function(e){e()}))})).observe(n,{attributes:!0});var s=i.setImmediate;i.setImmediate=function(e){Date.now()-r>15?(r=Date.now(),s(e)):(t.push(e),1===t.length&&n.setAttribute("a",a=!a))}}i.nextTick=i.setImmediate}(),i.isNodejs="undefined"!=typeof process&&process.versions&&process.versions.node,i.globalScope=i.isNodejs?t:"undefined"==typeof self?window:self,i.isArray=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i.isArrayBuffer=function(e){return"undefined"!=typeof ArrayBuffer&&e instanceof ArrayBuffer},i.isArrayBufferView=function(e){return e&&i.isArrayBuffer(e.buffer)&&void 0!==e.byteLength},i.ByteBuffer=o,i.ByteStringBuffer=o;i.ByteStringBuffer.prototype._optimizeConstructedString=function(e){this._constructedStringLength+=e,this._constructedStringLength>4096&&(this.data.substr(0,1),this._constructedStringLength=0)},i.ByteStringBuffer.prototype.length=function(){return this.data.length-this.read},i.ByteStringBuffer.prototype.isEmpty=function(){return this.length()<=0},i.ByteStringBuffer.prototype.putByte=function(e){return this.putBytes(String.fromCharCode(e))},i.ByteStringBuffer.prototype.fillWithByte=function(e,t){e=String.fromCharCode(e);for(var r=this.data;t>0;)1&t&&(r+=e),(t>>>=1)>0&&(e+=e);return this.data=r,this._optimizeConstructedString(t),this},i.ByteStringBuffer.prototype.putBytes=function(e){return this.data+=e,this._optimizeConstructedString(e.length),this},i.ByteStringBuffer.prototype.putString=function(e){return this.putBytes(i.encodeUtf8(e))},i.ByteStringBuffer.prototype.putInt16=function(e){return this.putBytes(String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt24=function(e){return this.putBytes(String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt32=function(e){return this.putBytes(String.fromCharCode(e>>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt16Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255))},i.ByteStringBuffer.prototype.putInt24Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255))},i.ByteStringBuffer.prototype.putInt32Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>24&255))},i.ByteStringBuffer.prototype.putInt=function(e,t){s(t);var r="";do{t-=8,r+=String.fromCharCode(e>>t&255)}while(t>0);return this.putBytes(r)},i.ByteStringBuffer.prototype.putSignedInt=function(e,t){return e<0&&(e+=2<<t-1),this.putInt(e,t)},i.ByteStringBuffer.prototype.putBuffer=function(e){return this.putBytes(e.getBytes())},i.ByteStringBuffer.prototype.getByte=function(){return this.data.charCodeAt(this.read++)},i.ByteStringBuffer.prototype.getInt16=function(){var e=this.data.charCodeAt(this.read)<<8^this.data.charCodeAt(this.read+1);return this.read+=2,e},i.ByteStringBuffer.prototype.getInt24=function(){var e=this.data.charCodeAt(this.read)<<16^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2);return this.read+=3,e},i.ByteStringBuffer.prototype.getInt32=function(){var e=this.data.charCodeAt(this.read)<<24^this.data.charCodeAt(this.read+1)<<16^this.data.charCodeAt(this.read+2)<<8^this.data.charCodeAt(this.read+3);return this.read+=4,e},i.ByteStringBuffer.prototype.getInt16Le=function(){var e=this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8;return this.read+=2,e},i.ByteStringBuffer.prototype.getInt24Le=function(){var e=this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2)<<16;return this.read+=3,e},i.ByteStringBuffer.prototype.getInt32Le=function(){var e=this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2)<<16^this.data.charCodeAt(this.read+3)<<24;return this.read+=4,e},i.ByteStringBuffer.prototype.getInt=function(e){s(e);var t=0;do{t=(t<<8)+this.data.charCodeAt(this.read++),e-=8}while(e>0);return t},i.ByteStringBuffer.prototype.getSignedInt=function(e){var t=this.getInt(e),r=2<<e-2;return t>=r&&(t-=r<<1),t},i.ByteStringBuffer.prototype.getBytes=function(e){var t;return e?(e=Math.min(this.length(),e),t=this.data.slice(this.read,this.read+e),this.read+=e):0===e?t="":(t=0===this.read?this.data:this.data.slice(this.read),this.clear()),t},i.ByteStringBuffer.prototype.bytes=function(e){return void 0===e?this.data.slice(this.read):this.data.slice(this.read,this.read+e)},i.ByteStringBuffer.prototype.at=function(e){return this.data.charCodeAt(this.read+e)},i.ByteStringBuffer.prototype.setAt=function(e,t){return this.data=this.data.substr(0,this.read+e)+String.fromCharCode(t)+this.data.substr(this.read+e+1),this},i.ByteStringBuffer.prototype.last=function(){return this.data.charCodeAt(this.data.length-1)},i.ByteStringBuffer.prototype.copy=function(){var e=i.createBuffer(this.data);return e.read=this.read,e},i.ByteStringBuffer.prototype.compact=function(){return this.read>0&&(this.data=this.data.slice(this.read),this.read=0),this},i.ByteStringBuffer.prototype.clear=function(){return this.data="",this.read=0,this},i.ByteStringBuffer.prototype.truncate=function(e){var t=Math.max(0,this.length()-e);return this.data=this.data.substr(this.read,t),this.read=0,this},i.ByteStringBuffer.prototype.toHex=function(){for(var e="",t=this.read;t<this.data.length;++t){var r=this.data.charCodeAt(t);r<16&&(e+="0"),e+=r.toString(16)}return e},i.ByteStringBuffer.prototype.toString=function(){return i.decodeUtf8(this.bytes())},i.DataBuffer=function(e,t){t=t||{},this.read=t.readOffset||0,this.growSize=t.growSize||1024;var r=i.isArrayBuffer(e),a=i.isArrayBufferView(e);if(r||a)return this.data=r?new DataView(e):new DataView(e.buffer,e.byteOffset,e.byteLength),void(this.write="writeOffset"in t?t.writeOffset:this.data.byteLength);this.data=new DataView(new ArrayBuffer(0)),this.write=0,null!=e&&this.putBytes(e),"writeOffset"in t&&(this.write=t.writeOffset)},i.DataBuffer.prototype.length=function(){return this.write-this.read},i.DataBuffer.prototype.isEmpty=function(){return this.length()<=0},i.DataBuffer.prototype.accommodate=function(e,t){if(this.length()>=e)return this;t=Math.max(t||this.growSize,e);var r=new Uint8Array(this.data.buffer,this.data.byteOffset,this.data.byteLength),a=new Uint8Array(this.length()+t);return a.set(r),this.data=new DataView(a.buffer),this},i.DataBuffer.prototype.putByte=function(e){return this.accommodate(1),this.data.setUint8(this.write++,e),this},i.DataBuffer.prototype.fillWithByte=function(e,t){this.accommodate(t);for(var r=0;r<t;++r)this.data.setUint8(e);return this},i.DataBuffer.prototype.putBytes=function(e,t){if(i.isArrayBufferView(e)){var r=(a=new Uint8Array(e.buffer,e.byteOffset,e.byteLength)).byteLength-a.byteOffset;return this.accommodate(r),new Uint8Array(this.data.buffer,this.write).set(a),this.write+=r,this}if(i.isArrayBuffer(e)){var a=new Uint8Array(e);return this.accommodate(a.byteLength),new Uint8Array(this.data.buffer).set(a,this.write),this.write+=a.byteLength,this}if(e instanceof i.DataBuffer||"object"==typeof e&&"number"==typeof e.read&&"number"==typeof e.write&&i.isArrayBufferView(e.data)){a=new Uint8Array(e.data.byteLength,e.read,e.length());return this.accommodate(a.byteLength),new Uint8Array(e.data.byteLength,this.write).set(a),this.write+=a.byteLength,this}if(e instanceof i.ByteStringBuffer&&(e=e.data,t="binary"),t=t||"binary","string"==typeof e){var n;if("hex"===t)return this.accommodate(Math.ceil(e.length/2)),n=new Uint8Array(this.data.buffer,this.write),this.write+=i.binary.hex.decode(e,n,this.write),this;if("base64"===t)return this.accommodate(3*Math.ceil(e.length/4)),n=new Uint8Array(this.data.buffer,this.write),this.write+=i.binary.base64.decode(e,n,this.write),this;if("utf8"===t&&(e=i.encodeUtf8(e),t="binary"),"binary"===t||"raw"===t)return this.accommodate(e.length),n=new Uint8Array(this.data.buffer,this.write),this.write+=i.binary.raw.decode(n),this;if("utf16"===t)return this.accommodate(2*e.length),n=new Uint16Array(this.data.buffer,this.write),this.write+=i.text.utf16.encode(n),this;throw new Error("Invalid encoding: "+t)}throw Error("Invalid parameter: "+e)},i.DataBuffer.prototype.putBuffer=function(e){return this.putBytes(e),e.clear(),this},i.DataBuffer.prototype.putString=function(e){return this.putBytes(e,"utf16")},i.DataBuffer.prototype.putInt16=function(e){return this.accommodate(2),this.data.setInt16(this.write,e),this.write+=2,this},i.DataBuffer.prototype.putInt24=function(e){return this.accommodate(3),this.data.setInt16(this.write,e>>8&65535),this.data.setInt8(this.write,e>>16&255),this.write+=3,this},i.DataBuffer.prototype.putInt32=function(e){return this.accommodate(4),this.data.setInt32(this.write,e),this.write+=4,this},i.DataBuffer.prototype.putInt16Le=function(e){return this.accommodate(2),this.data.setInt16(this.write,e,!0),this.write+=2,this},i.DataBuffer.prototype.putInt24Le=function(e){return this.accommodate(3),this.data.setInt8(this.write,e>>16&255),this.data.setInt16(this.write,e>>8&65535,!0),this.write+=3,this},i.DataBuffer.prototype.putInt32Le=function(e){return this.accommodate(4),this.data.setInt32(this.write,e,!0),this.write+=4,this},i.DataBuffer.prototype.putInt=function(e,t){s(t),this.accommodate(t/8);do{t-=8,this.data.setInt8(this.write++,e>>t&255)}while(t>0);return this},i.DataBuffer.prototype.putSignedInt=function(e,t){return s(t),this.accommodate(t/8),e<0&&(e+=2<<t-1),this.putInt(e,t)},i.DataBuffer.prototype.getByte=function(){return this.data.getInt8(this.read++)},i.DataBuffer.prototype.getInt16=function(){var e=this.data.getInt16(this.read);return this.read+=2,e},i.DataBuffer.prototype.getInt24=function(){var e=this.data.getInt16(this.read)<<8^this.data.getInt8(this.read+2);return this.read+=3,e},i.DataBuffer.prototype.getInt32=function(){var e=this.data.getInt32(this.read);return this.read+=4,e},i.DataBuffer.prototype.getInt16Le=function(){var e=this.data.getInt16(this.read,!0);return this.read+=2,e},i.DataBuffer.prototype.getInt24Le=function(){var e=this.data.getInt8(this.read)^this.data.getInt16(this.read+1,!0)<<8;return this.read+=3,e},i.DataBuffer.prototype.getInt32Le=function(){var e=this.data.getInt32(this.read,!0);return this.read+=4,e},i.DataBuffer.prototype.getInt=function(e){s(e);var t=0;do{t=(t<<8)+this.data.getInt8(this.read++),e-=8}while(e>0);return t},i.DataBuffer.prototype.getSignedInt=function(e){var t=this.getInt(e),r=2<<e-2;return t>=r&&(t-=r<<1),t},i.DataBuffer.prototype.getBytes=function(e){var t;return e?(e=Math.min(this.length(),e),t=this.data.slice(this.read,this.read+e),this.read+=e):0===e?t="":(t=0===this.read?this.data:this.data.slice(this.read),this.clear()),t},i.DataBuffer.prototype.bytes=function(e){return void 0===e?this.data.slice(this.read):this.data.slice(this.read,this.read+e)},i.DataBuffer.prototype.at=function(e){return this.data.getUint8(this.read+e)},i.DataBuffer.prototype.setAt=function(e,t){return this.data.setUint8(e,t),this},i.DataBuffer.prototype.last=function(){return this.data.getUint8(this.write-1)},i.DataBuffer.prototype.copy=function(){return new i.DataBuffer(this)},i.DataBuffer.prototype.compact=function(){if(this.read>0){var e=new Uint8Array(this.data.buffer,this.read),t=new Uint8Array(e.byteLength);t.set(e),this.data=new DataView(t),this.write-=this.read,this.read=0}return this},i.DataBuffer.prototype.clear=function(){return this.data=new DataView(new ArrayBuffer(0)),this.read=this.write=0,this},i.DataBuffer.prototype.truncate=function(e){return this.write=Math.max(0,this.length()-e),this.read=Math.min(this.read,this.write),this},i.DataBuffer.prototype.toHex=function(){for(var e="",t=this.read;t<this.data.byteLength;++t){var r=this.data.getUint8(t);r<16&&(e+="0"),e+=r.toString(16)}return e},i.DataBuffer.prototype.toString=function(e){var t=new Uint8Array(this.data,this.read,this.length());if("binary"===(e=e||"utf8")||"raw"===e)return i.binary.raw.encode(t);if("hex"===e)return i.binary.hex.encode(t);if("base64"===e)return i.binary.base64.encode(t);if("utf8"===e)return i.text.utf8.decode(t);if("utf16"===e)return i.text.utf16.decode(t);throw new Error("Invalid encoding: "+e)},i.createBuffer=function(e,t){return t=t||"raw",void 0!==e&&"utf8"===t&&(e=i.encodeUtf8(e)),new i.ByteBuffer(e)},i.fillString=function(e,t){for(var r="";t>0;)1&t&&(r+=e),(t>>>=1)>0&&(e+=e);return r},i.xorBytes=function(e,t,r){for(var a="",n="",i="",s=0,o=0;r>0;--r,++s)n=e.charCodeAt(s)^t.charCodeAt(s),o>=10&&(a+=i,i="",o=0),i+=String.fromCharCode(n),++o;return a+=i},i.hexToBytes=function(e){var t="",r=0;for(!0&e.length&&(r=1,t+=String.fromCharCode(parseInt(e[0],16)));r<e.length;r+=2)t+=String.fromCharCode(parseInt(e.substr(r,2),16));return t},i.bytesToHex=function(e){return i.createBuffer(e).toHex()},i.int32ToBytes=function(e){return String.fromCharCode(e>>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e)};var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",u=[62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],l="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";i.encode64=function(e,t){for(var r,a,n,i="",s="",o=0;o<e.length;)r=e.charCodeAt(o++),a=e.charCodeAt(o++),n=e.charCodeAt(o++),i+=c.charAt(r>>2),i+=c.charAt((3&r)<<4|a>>4),isNaN(a)?i+="==":(i+=c.charAt((15&a)<<2|n>>6),i+=isNaN(n)?"=":c.charAt(63&n)),t&&i.length>t&&(s+=i.substr(0,t)+"\r\n",i=i.substr(t));return s+=i},i.decode64=function(e){e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var t,r,a,n,i="",s=0;s<e.length;)t=u[e.charCodeAt(s++)-43],r=u[e.charCodeAt(s++)-43],a=u[e.charCodeAt(s++)-43],n=u[e.charCodeAt(s++)-43],i+=String.fromCharCode(t<<2|r>>4),64!==a&&(i+=String.fromCharCode((15&r)<<4|a>>2),64!==n&&(i+=String.fromCharCode((3&a)<<6|n)));return i},i.encodeUtf8=function(e){return unescape(encodeURIComponent(e))},i.decodeUtf8=function(e){return decodeURIComponent(escape(e))},i.binary={raw:{},hex:{},base64:{},base58:{},baseN:{encode:n.encode,decode:n.decode}},i.binary.raw.encode=function(e){return String.fromCharCode.apply(null,e)},i.binary.raw.decode=function(e,t,r){var a=t;a||(a=new Uint8Array(e.length));for(var n=r=r||0,i=0;i<e.length;++i)a[n++]=e.charCodeAt(i);return t?n-r:a},i.binary.hex.encode=i.bytesToHex,i.binary.hex.decode=function(e,t,r){var a=t;a||(a=new Uint8Array(Math.ceil(e.length/2)));var n=0,i=r=r||0;for(1&e.length&&(n=1,a[i++]=parseInt(e[0],16));n<e.length;n+=2)a[i++]=parseInt(e.substr(n,2),16);return t?i-r:a},i.binary.base64.encode=function(e,t){for(var r,a,n,i="",s="",o=0;o<e.byteLength;)r=e[o++],a=e[o++],n=e[o++],i+=c.charAt(r>>2),i+=c.charAt((3&r)<<4|a>>4),isNaN(a)?i+="==":(i+=c.charAt((15&a)<<2|n>>6),i+=isNaN(n)?"=":c.charAt(63&n)),t&&i.length>t&&(s+=i.substr(0,t)+"\r\n",i=i.substr(t));return s+=i},i.binary.base64.decode=function(e,t,r){var a,n,i,s,o=t;o||(o=new Uint8Array(3*Math.ceil(e.length/4))),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var c=0,l=r=r||0;c<e.length;)a=u[e.charCodeAt(c++)-43],n=u[e.charCodeAt(c++)-43],i=u[e.charCodeAt(c++)-43],s=u[e.charCodeAt(c++)-43],o[l++]=a<<2|n>>4,64!==i&&(o[l++]=(15&n)<<4|i>>2,64!==s&&(o[l++]=(3&i)<<6|s));return t?l-r:o.subarray(0,l)},i.binary.base58.encode=function(e,t){return i.binary.baseN.encode(e,l,t)},i.binary.base58.decode=function(e,t){return i.binary.baseN.decode(e,l,t)},i.text={utf8:{},utf16:{}},i.text.utf8.encode=function(e,t,r){e=i.encodeUtf8(e);var a=t;a||(a=new Uint8Array(e.length));for(var n=r=r||0,s=0;s<e.length;++s)a[n++]=e.charCodeAt(s);return t?n-r:a},i.text.utf8.decode=function(e){return i.decodeUtf8(String.fromCharCode.apply(null,e))},i.text.utf16.encode=function(e,t,r){var a=t;a||(a=new Uint8Array(2*e.length));for(var n=new Uint16Array(a.buffer),i=r=r||0,s=r,o=0;o<e.length;++o)n[s++]=e.charCodeAt(o),i+=2;return t?i-r:a},i.text.utf16.decode=function(e){return String.fromCharCode.apply(null,new Uint16Array(e.buffer))},i.deflate=function(e,t,r){if(t=i.decode64(e.deflate(i.encode64(t)).rval),r){var a=2;32&t.charCodeAt(1)&&(a=6),t=t.substring(a,t.length-4)}return t},i.inflate=function(e,t,r){var a=e.inflate(i.encode64(t)).rval;return null===a?null:i.decode64(a)};var p=function(e,t,r){if(!e)throw new Error("WebStorage not available.");var a;if(null===r?a=e.removeItem(t):(r=i.encode64(JSON.stringify(r)),a=e.setItem(t,r)),void 0!==a&&!0!==a.rval){var n=new Error(a.error.message);throw n.id=a.error.id,n.name=a.error.name,n}},f=function(e,t){if(!e)throw new Error("WebStorage not available.");var r=e.getItem(t);if(e.init)if(null===r.rval){if(r.error){var a=new Error(r.error.message);throw a.id=r.error.id,a.name=r.error.name,a}r=null}else r=r.rval;return null!==r&&(r=JSON.parse(i.decode64(r))),r},h=function(e,t,r,a){var n=f(e,t);null===n&&(n={}),n[r]=a,p(e,t,n)},d=function(e,t,r){var a=f(e,t);return null!==a&&(a=r in a?a[r]:null),a},y=function(e,t,r){var a=f(e,t);if(null!==a&&r in a){delete a[r];var n=!0;for(var i in a){n=!1;break}n&&(a=null),p(e,t,a)}},g=function(e,t){p(e,t,null)},v=function(e,t,r){var a,n=null;void 0===r&&(r=["web","flash"]);var i=!1,s=null;for(var o in r){a=r[o];try{if("flash"===a||"both"===a){if(null===t[0])throw new Error("Flash local storage not available.");n=e.apply(this,t),i="flash"===a}"web"!==a&&"both"!==a||(t[0]=localStorage,n=e.apply(this,t),i=!0)}catch(e){s=e}if(i)break}if(!i)throw s;return n};i.setItem=function(e,t,r,a,n){v(h,arguments,n)},i.getItem=function(e,t,r,a){return v(d,arguments,a)},i.removeItem=function(e,t,r,a){v(y,arguments,a)},i.clearItems=function(e,t,r){v(g,arguments,r)},i.isEmpty=function(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0},i.format=function(e){for(var t,r,a=/%./g,n=0,i=[],s=0;t=a.exec(e);){(r=e.substring(s,a.lastIndex-2)).length>0&&i.push(r),s=a.lastIndex;var o=t[0][1];switch(o){case"s":case"o":n<arguments.length?i.push(arguments[1+n++]):i.push("<?>");break;case"%":i.push("%");break;default:i.push("<%"+o+"?>")}}return i.push(e.substring(s)),i.join("")},i.formatNumber=function(e,t,r,a){var n=e,i=isNaN(t=Math.abs(t))?2:t,s=void 0===r?",":r,o=void 0===a?".":a,c=n<0?"-":"",u=parseInt(n=Math.abs(+n||0).toFixed(i),10)+"",l=u.length>3?u.length%3:0;return c+(l?u.substr(0,l)+o:"")+u.substr(l).replace(/(\d{3})(?=\d)/g,"$1"+o)+(i?s+Math.abs(n-u).toFixed(i).slice(2):"")},i.formatSize=function(e){return e=e>=1073741824?i.formatNumber(e/1073741824,2,".","")+" GiB":e>=1048576?i.formatNumber(e/1048576,2,".","")+" MiB":e>=1024?i.formatNumber(e/1024,0)+" KiB":i.formatNumber(e,0)+" bytes"},i.bytesFromIP=function(e){return-1!==e.indexOf(".")?i.bytesFromIPv4(e):-1!==e.indexOf(":")?i.bytesFromIPv6(e):null},i.bytesFromIPv4=function(e){if(4!==(e=e.split(".")).length)return null;for(var t=i.createBuffer(),r=0;r<e.length;++r){var a=parseInt(e[r],10);if(isNaN(a))return null;t.putByte(a)}return t.getBytes()},i.bytesFromIPv6=function(e){for(var t=0,r=2*(8-(e=e.split(":").filter((function(e){return 0===e.length&&++t,!0}))).length+t),a=i.createBuffer(),n=0;n<8;++n)if(e[n]&&0!==e[n].length){var s=i.hexToBytes(e[n]);s.length<2&&a.putByte(0),a.putBytes(s)}else a.fillWithByte(0,r),r=0;return a.getBytes()},i.bytesToIP=function(e){return 4===e.length?i.bytesToIPv4(e):16===e.length?i.bytesToIPv6(e):null},i.bytesToIPv4=function(e){if(4!==e.length)return null;for(var t=[],r=0;r<e.length;++r)t.push(e.charCodeAt(r));return t.join(".")},i.bytesToIPv6=function(e){if(16!==e.length)return null;for(var t=[],r=[],a=0,n=0;n<e.length;n+=2){for(var s=i.bytesToHex(e[n]+e[n+1]);"0"===s[0]&&"0"!==s;)s=s.substr(1);if("0"===s){var o=r[r.length-1],c=t.length;o&&c===o.end+1?(o.end=c,o.end-o.start>r[a].end-r[a].start&&(a=r.length-1)):r.push({start:c,end:c})}t.push(s)}if(r.length>0){var u=r[a];u.end-u.start>0&&(t.splice(u.start,u.end-u.start+1,""),0===u.start&&t.unshift(""),7===u.end&&t.push(""))}return t.join(":")},i.estimateCores=function(e,t){if("function"==typeof e&&(t=e,e={}),e=e||{},"cores"in i&&!e.update)return t(null,i.cores);if("undefined"!=typeof navigator&&"hardwareConcurrency"in navigator&&navigator.hardwareConcurrency>0)return i.cores=navigator.hardwareConcurrency,t(null,i.cores);if("undefined"==typeof Worker)return i.cores=1,t(null,i.cores);if("undefined"==typeof Blob)return i.cores=2,t(null,i.cores);var r=URL.createObjectURL(new Blob(["(",function(){self.addEventListener("message",(function(e){for(var t=Date.now(),r=t+4;Date.now()<r;);self.postMessage({st:t,et:r})}))}.toString(),")()"],{type:"application/javascript"}));!function e(a,n,s){if(0===n){var o=Math.floor(a.reduce((function(e,t){return e+t}),0)/a.length);return i.cores=Math.max(1,o),URL.revokeObjectURL(r),t(null,i.cores)}!function(e,t){for(var a=[],n=[],i=0;i<e;++i){var s=new Worker(r);s.addEventListener("message",(function(r){if(n.push(r.data),n.length===e){for(var i=0;i<e;++i)a[i].terminate();t(null,n)}})),a.push(s)}for(i=0;i<e;++i)a[i].postMessage(i)}(s,(function(t,r){a.push(function(e,t){for(var r=[],a=0;a<e;++a)for(var n=t[a],i=r[a]=[],s=0;s<e;++s)if(a!==s){var o=t[s];(n.st>o.st&&n.st<o.et||o.st>n.st&&o.st<n.et)&&i.push(s)}return r.reduce((function(e,t){return Math.max(e,t.length)}),0)}(s,r)),e(a,n-1,s)}))}([],5,16)}}).call(this,r(34))},function(e,t,r){var a=r(0);r(5),r(23),r(24),r(1),a.random&&a.random.getBytes?e.exports=a.random:function(t){var r={},n=new Array(4),i=a.util.createBuffer();function s(){var e=a.prng.create(r);return e.getBytes=function(t,r){return e.generate(t,r)},e.getBytesSync=function(t){return e.generate(t)},e}r.formatKey=function(e){var t=a.util.createBuffer(e);return(e=new Array(4))[0]=t.getInt32(),e[1]=t.getInt32(),e[2]=t.getInt32(),e[3]=t.getInt32(),a.aes._expandKey(e,!1)},r.formatSeed=function(e){var t=a.util.createBuffer(e);return(e=new Array(4))[0]=t.getInt32(),e[1]=t.getInt32(),e[2]=t.getInt32(),e[3]=t.getInt32(),e},r.cipher=function(e,t){return a.aes._updateBlock(e,t,n,!1),i.putInt32(n[0]),i.putInt32(n[1]),i.putInt32(n[2]),i.putInt32(n[3]),i.getBytes()},r.increment=function(e){return++e[3],e},r.md=a.md.sha256;var o=s(),c=null,u=a.util.globalScope,l=u.crypto||u.msCrypto;if(l&&l.getRandomValues&&(c=function(e){return l.getRandomValues(e)}),a.options.usePureJavaScript||!a.util.isNodejs&&!c){if("undefined"==typeof window||window.document,o.collectInt(+new Date,32),"undefined"!=typeof navigator){var p="";for(var f in navigator)try{"string"==typeof navigator[f]&&(p+=navigator[f])}catch(e){}o.collect(p),p=null}t&&(t().mousemove((function(e){o.collectInt(e.clientX,16),o.collectInt(e.clientY,16)})),t().keypress((function(e){o.collectInt(e.charCode,8)})))}if(a.random)for(var f in o)a.random[f]=o[f];else a.random=o;a.random.createInstance=s,e.exports=a.random}("undefined"!=typeof jQuery?jQuery:null)},function(e,t,r){var a=r(0);r(1),r(6);var n=e.exports=a.asn1=a.asn1||{};function i(e,t,r){if(r>t){var a=new Error("Too few bytes to parse DER.");throw a.available=e.length(),a.remaining=t,a.requested=r,a}}n.Class={UNIVERSAL:0,APPLICATION:64,CONTEXT_SPECIFIC:128,PRIVATE:192},n.Type={NONE:0,BOOLEAN:1,INTEGER:2,BITSTRING:3,OCTETSTRING:4,NULL:5,OID:6,ODESC:7,EXTERNAL:8,REAL:9,ENUMERATED:10,EMBEDDED:11,UTF8:12,ROID:13,SEQUENCE:16,SET:17,PRINTABLESTRING:19,IA5STRING:22,UTCTIME:23,GENERALIZEDTIME:24,BMPSTRING:30},n.maxDepth=256,n.create=function(e,t,r,i,s){if(a.util.isArray(i)){for(var o=[],c=0;c<i.length;++c)void 0!==i[c]&&o.push(i[c]);i=o}var u={tagClass:e,type:t,constructed:r,composed:r||a.util.isArray(i),value:i};return s&&"bitStringContents"in s&&(u.bitStringContents=s.bitStringContents,u.original=n.copy(u)),u},n.copy=function(e,t){var r;if(a.util.isArray(e)){r=[];for(var i=0;i<e.length;++i)r.push(n.copy(e[i],t));return r}return"string"==typeof e?e:(r={tagClass:e.tagClass,type:e.type,constructed:e.constructed,composed:e.composed,value:n.copy(e.value,t)},t&&!t.excludeBitStringContents&&(r.bitStringContents=e.bitStringContents),r)},n.equals=function(e,t,r){if(a.util.isArray(e)){if(!a.util.isArray(t))return!1;if(e.length!==t.length)return!1;for(var i=0;i<e.length;++i)if(!n.equals(e[i],t[i]))return!1;return!0}if(typeof e!=typeof t)return!1;if("string"==typeof e)return e===t;var s=e.tagClass===t.tagClass&&e.type===t.type&&e.constructed===t.constructed&&e.composed===t.composed&&n.equals(e.value,t.value);return r&&r.includeBitStringContents&&(s=s&&e.bitStringContents===t.bitStringContents),s},n.getBerValueLength=function(e){var t=e.getByte();if(128!==t)return 128&t?e.getInt((127&t)<<3):t};n.fromDer=function(e,t){void 0===t&&(t={strict:!0,parseAllBytes:!0,decodeBitStrings:!0}),"boolean"==typeof t&&(t={strict:t,parseAllBytes:!0,decodeBitStrings:!0}),"strict"in t||(t.strict=!0),"parseAllBytes"in t||(t.parseAllBytes=!0),"decodeBitStrings"in t||(t.decodeBitStrings=!0),"maxDepth"in t||(t.maxDepth=n.maxDepth),"string"==typeof e&&(e=a.util.createBuffer(e));var r=e.length(),s=function e(t,r,a,s){if(a>=s.maxDepth)throw new Error("ASN.1 parsing error: Max depth exceeded.");var o;i(t,r,2);var c=t.getByte();r--;var u=192&c,l=31&c;o=t.length();var p,f,h=function(e,t){var r=e.getByte();if(t--,128!==r){var a;if(128&r){var n=127&r;i(e,t,n),a=e.getInt(n<<3)}else a=r;if(a<0)throw new Error("Negative length: "+a);return a}}(t,r);if(r-=o-t.length(),void 0!==h&&h>r){if(s.strict){var d=new Error("Too few bytes to read ASN.1 value.");throw d.available=t.length(),d.remaining=r,d.requested=h,d}h=r}var y=32==(32&c);if(y)if(p=[],void 0===h)for(;;){if(i(t,r,2),t.bytes(2)===String.fromCharCode(0,0)){t.getBytes(2),r-=2;break}o=t.length(),p.push(e(t,r,a+1,s)),r-=o-t.length()}else for(;h>0;)o=t.length(),p.push(e(t,h,a+1,s)),r-=o-t.length(),h-=o-t.length();void 0===p&&u===n.Class.UNIVERSAL&&l===n.Type.BITSTRING&&(f=t.bytes(h));if(void 0===p&&s.decodeBitStrings&&u===n.Class.UNIVERSAL&&l===n.Type.BITSTRING&&h>1){var g=t.read,v=r,m=0;if(l===n.Type.BITSTRING&&(i(t,r,1),m=t.getByte(),r--),0===m)try{o=t.length();var C=e(t,r,a+1,{strict:!0,decodeBitStrings:!0}),E=o-t.length();r-=E,l==n.Type.BITSTRING&&E++;var S=C.tagClass;E!==h||S!==n.Class.UNIVERSAL&&S!==n.Class.CONTEXT_SPECIFIC||(p=[C])}catch(e){}void 0===p&&(t.read=g,r=v)}if(void 0===p){if(void 0===h){if(s.strict)throw new Error("Non-constructed ASN.1 object of indefinite length.");h=r}if(l===n.Type.BMPSTRING)for(p="";h>0;h-=2)i(t,r,2),p+=String.fromCharCode(t.getInt16()),r-=2;else p=t.getBytes(h),r-=h}var T=void 0===f?null:{bitStringContents:f};return n.create(u,l,y,p,T)}(e,e.length(),0,t);if(t.parseAllBytes&&0!==e.length()){var o=new Error("Unparsed DER bytes remain after ASN.1 parsing.");throw o.byteCount=r,o.remaining=e.length(),o}return s},n.toDer=function(e){var t=a.util.createBuffer(),r=e.tagClass|e.type,i=a.util.createBuffer(),s=!1;if("bitStringContents"in e&&(s=!0,e.original&&(s=n.equals(e,e.original))),s)i.putBytes(e.bitStringContents);else if(e.composed){e.constructed?r|=32:i.putByte(0);for(var o=0;o<e.value.length;++o)void 0!==e.value[o]&&i.putBuffer(n.toDer(e.value[o]))}else if(e.type===n.Type.BMPSTRING)for(o=0;o<e.value.length;++o)i.putInt16(e.value.charCodeAt(o));else e.type===n.Type.INTEGER&&e.value.length>1&&(0===e.value.charCodeAt(0)&&0==(128&e.value.charCodeAt(1))||255===e.value.charCodeAt(0)&&128==(128&e.value.charCodeAt(1)))?i.putBytes(e.value.substr(1)):i.putBytes(e.value);if(t.putByte(r),i.length()<=127)t.putByte(127&i.length());else{var c=i.length(),u="";do{u+=String.fromCharCode(255&c),c>>>=8}while(c>0);t.putByte(128|u.length);for(o=u.length-1;o>=0;--o)t.putByte(u.charCodeAt(o))}return t.putBuffer(i),t},n.oidToDer=function(e){var t,r,n,i,s=e.split("."),o=a.util.createBuffer();o.putByte(40*parseInt(s[0],10)+parseInt(s[1],10));for(var c=2;c<s.length;++c){if(t=!0,r=[],(n=parseInt(s[c],10))>4294967295)throw new Error("OID value too large; max is 32-bits.");do{i=127&n,n>>>=7,t||(i|=128),r.push(i),t=!1}while(n>0);for(var u=r.length-1;u>=0;--u)o.putByte(r[u])}return o},n.derToOid=function(e){var t;"string"==typeof e&&(e=a.util.createBuffer(e));var r=e.getByte();t=Math.floor(r/40)+"."+r%40;for(var n=0;e.length()>0;){if(n>70368744177663)throw new Error("OID value too large; max is 53-bits.");n*=128,128&(r=e.getByte())?n+=127&r:(t+="."+(n+r),n=0)}return t},n.utcTimeToDate=function(e){var t=new Date,r=parseInt(e.substr(0,2),10);r=r>=50?1900+r:2e3+r;var a=parseInt(e.substr(2,2),10)-1,n=parseInt(e.substr(4,2),10),i=parseInt(e.substr(6,2),10),s=parseInt(e.substr(8,2),10),o=0;if(e.length>11){var c=e.charAt(10),u=10;"+"!==c&&"-"!==c&&(o=parseInt(e.substr(10,2),10),u+=2)}if(t.setUTCFullYear(r,a,n),t.setUTCHours(i,s,o,0),u&&("+"===(c=e.charAt(u))||"-"===c)){var l=60*parseInt(e.substr(u+1,2),10)+parseInt(e.substr(u+4,2),10);l*=6e4,"+"===c?t.setTime(+t-l):t.setTime(+t+l)}return t},n.generalizedTimeToDate=function(e){var t=new Date,r=parseInt(e.substr(0,4),10),a=parseInt(e.substr(4,2),10)-1,n=parseInt(e.substr(6,2),10),i=parseInt(e.substr(8,2),10),s=parseInt(e.substr(10,2),10),o=parseInt(e.substr(12,2),10),c=0,u=0,l=!1;"Z"===e.charAt(e.length-1)&&(l=!0);var p=e.length-5,f=e.charAt(p);"+"!==f&&"-"!==f||(u=60*parseInt(e.substr(p+1,2),10)+parseInt(e.substr(p+4,2),10),u*=6e4,"+"===f&&(u*=-1),l=!0);return"."===e.charAt(14)&&(c=1e3*parseFloat(e.substr(14),10)),l?(t.setUTCFullYear(r,a,n),t.setUTCHours(i,s,o,c),t.setTime(+t+u)):(t.setFullYear(r,a,n),t.setHours(i,s,o,c)),t},n.dateToUtcTime=function(e){if("string"==typeof e)return e;var t="",r=[];r.push((""+e.getUTCFullYear()).substr(2)),r.push(""+(e.getUTCMonth()+1)),r.push(""+e.getUTCDate()),r.push(""+e.getUTCHours()),r.push(""+e.getUTCMinutes()),r.push(""+e.getUTCSeconds());for(var a=0;a<r.length;++a)r[a].length<2&&(t+="0"),t+=r[a];return t+="Z"},n.dateToGeneralizedTime=function(e){if("string"==typeof e)return e;var t="",r=[];r.push(""+e.getUTCFullYear()),r.push(""+(e.getUTCMonth()+1)),r.push(""+e.getUTCDate()),r.push(""+e.getUTCHours()),r.push(""+e.getUTCMinutes()),r.push(""+e.getUTCSeconds());for(var a=0;a<r.length;++a)r[a].length<2&&(t+="0"),t+=r[a];return t+="Z"},n.integerToDer=function(e){var t=a.util.createBuffer();if(e>=-128&&e<128)return t.putSignedInt(e,8);if(e>=-32768&&e<32768)return t.putSignedInt(e,16);if(e>=-8388608&&e<8388608)return t.putSignedInt(e,24);if(e>=-2147483648&&e<2147483648)return t.putSignedInt(e,32);var r=new Error("Integer too large; max is 32-bits.");throw r.integer=e,r},n.derToInteger=function(e){"string"==typeof e&&(e=a.util.createBuffer(e));var t=8*e.length();if(t>32)throw new Error("Integer too large; max is 32-bits.");return e.getSignedInt(t)},n.validate=function(e,t,r,i){var s=!1;if(e.tagClass!==t.tagClass&&void 0!==t.tagClass||e.type!==t.type&&void 0!==t.type)i&&(e.tagClass!==t.tagClass&&i.push("["+t.name+'] Expected tag class "'+t.tagClass+'", got "'+e.tagClass+'"'),e.type!==t.type&&i.push("["+t.name+'] Expected type "'+t.type+'", got "'+e.type+'"'));else if(e.constructed===t.constructed||void 0===t.constructed){if(s=!0,t.value&&a.util.isArray(t.value))for(var o=0,c=0;s&&c<t.value.length;++c){var u=t.value[c];s=!!u.optional;var l=e.value[o];if(l){if(void 0!==u.tagClass&&void 0!==u.type&&(l.tagClass!==u.tagClass||l.type!==u.type)){if(u.optional){s=!0;continue}s=!1,i&&i.push("["+t.name+"] Tag mismatch. Expected ("+u.tagClass+","+u.type+"), got ("+l.tagClass+","+l.type+")");break}if(n.validate(l,u,r,i))++o,s=!0;else{if(!u.optional){s=!1;break}s=!0}}else u.optional||(s=!1,i&&i.push("["+t.name+'] Missing required element. Expected tag class "'+u.tagClass+'", type "'+u.type+'"'))}if(s&&r)if(t.capture&&(r[t.capture]=e.value),t.captureAsn1&&(r[t.captureAsn1]=e),t.captureBitStringContents&&"bitStringContents"in e&&(r[t.captureBitStringContents]=e.bitStringContents),t.captureBitStringValue&&"bitStringContents"in e)if(e.bitStringContents.length<2)r[t.captureBitStringValue]="";else{if(0!==e.bitStringContents.charCodeAt(0))throw new Error("captureBitStringValue only supported for zero unused bits");r[t.captureBitStringValue]=e.bitStringContents.slice(1)}}else i&&i.push("["+t.name+'] Expected constructed "'+t.constructed+'", got "'+e.constructed+'"');return s};var s=/[^\\u0000-\\u00ff]/;n.prettyPrint=function(e,t,r){var i="";r=r||2,(t=t||0)>0&&(i+="\n");for(var o="",c=0;c<t*r;++c)o+=" ";switch(i+=o+"Tag: ",e.tagClass){case n.Class.UNIVERSAL:i+="Universal:";break;case n.Class.APPLICATION:i+="Application:";break;case n.Class.CONTEXT_SPECIFIC:i+="Context-Specific:";break;case n.Class.PRIVATE:i+="Private:"}if(e.tagClass===n.Class.UNIVERSAL)switch(i+=e.type,e.type){case n.Type.NONE:i+=" (None)";break;case n.Type.BOOLEAN:i+=" (Boolean)";break;case n.Type.INTEGER:i+=" (Integer)";break;case n.Type.BITSTRING:i+=" (Bit string)";break;case n.Type.OCTETSTRING:i+=" (Octet string)";break;case n.Type.NULL:i+=" (Null)";break;case n.Type.OID:i+=" (Object Identifier)";break;case n.Type.ODESC:i+=" (Object Descriptor)";break;case n.Type.EXTERNAL:i+=" (External or Instance of)";break;case n.Type.REAL:i+=" (Real)";break;case n.Type.ENUMERATED:i+=" (Enumerated)";break;case n.Type.EMBEDDED:i+=" (Embedded PDV)";break;case n.Type.UTF8:i+=" (UTF8)";break;case n.Type.ROID:i+=" (Relative Object Identifier)";break;case n.Type.SEQUENCE:i+=" (Sequence)";break;case n.Type.SET:i+=" (Set)";break;case n.Type.PRINTABLESTRING:i+=" (Printable String)";break;case n.Type.IA5String:i+=" (IA5String (ASCII))";break;case n.Type.UTCTIME:i+=" (UTC time)";break;case n.Type.GENERALIZEDTIME:i+=" (Generalized time)";break;case n.Type.BMPSTRING:i+=" (BMP String)"}else i+=e.type;if(i+="\n",i+=o+"Constructed: "+e.constructed+"\n",e.composed){var u=0,l="";for(c=0;c<e.value.length;++c)void 0!==e.value[c]&&(u+=1,l+=n.prettyPrint(e.value[c],t+1,r),c+1<e.value.length&&(l+=","));i+=o+"Sub values: "+u+l}else{if(i+=o+"Value: ",e.type===n.Type.OID){var p=n.derToOid(e.value);i+=p,a.pki&&a.pki.oids&&p in a.pki.oids&&(i+=" ("+a.pki.oids[p]+") ")}if(e.type===n.Type.INTEGER)try{i+=n.derToInteger(e.value)}catch(t){i+="0x"+a.util.bytesToHex(e.value)}else if(e.type===n.Type.BITSTRING){if(e.value.length>1?i+="0x"+a.util.bytesToHex(e.value.slice(1)):i+="(none)",e.value.length>0){var f=e.value.charCodeAt(0);1==f?i+=" (1 unused bit shown)":f>1&&(i+=" ("+f+" unused bits shown)")}}else if(e.type===n.Type.OCTETSTRING)s.test(e.value)||(i+="("+e.value+") "),i+="0x"+a.util.bytesToHex(e.value);else if(e.type===n.Type.UTF8)try{i+=a.util.decodeUtf8(e.value)}catch(t){if("URI malformed"!==t.message)throw t;i+="0x"+a.util.bytesToHex(e.value)+" (malformed UTF8)"}else e.type===n.Type.PRINTABLESTRING||e.type===n.Type.IA5String?i+=e.value:s.test(e.value)?i+="0x"+a.util.bytesToHex(e.value):0===e.value.length?i+="[null]":i+=e.value}return i}},function(e,t,r){var a=r(0);e.exports=a.md=a.md||{},a.md.algorithms=a.md.algorithms||{}},function(e,t,r){var a=r(0);function n(e,t){a.cipher.registerAlgorithm(e,(function(){return new a.aes.Algorithm(e,t)}))}r(13),r(19),r(1),e.exports=a.aes=a.aes||{},a.aes.startEncrypting=function(e,t,r,a){var n=d({key:e,output:r,decrypt:!1,mode:a});return n.start(t),n},a.aes.createEncryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!1,mode:t})},a.aes.startDecrypting=function(e,t,r,a){var n=d({key:e,output:r,decrypt:!0,mode:a});return n.start(t),n},a.aes.createDecryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!0,mode:t})},a.aes.Algorithm=function(e,t){l||p();var r=this;r.name=e,r.mode=new t({blockSize:16,cipher:{encrypt:function(e,t){return h(r._w,e,t,!1)},decrypt:function(e,t){return h(r._w,e,t,!0)}}}),r._init=!1},a.aes.Algorithm.prototype.initialize=function(e){if(!this._init){var t,r=e.key;if("string"!=typeof r||16!==r.length&&24!==r.length&&32!==r.length){if(a.util.isArray(r)&&(16===r.length||24===r.length||32===r.length)){t=r,r=a.util.createBuffer();for(var n=0;n<t.length;++n)r.putByte(t[n])}}else r=a.util.createBuffer(r);if(!a.util.isArray(r)){t=r,r=[];var i=t.length();if(16===i||24===i||32===i){i>>>=2;for(n=0;n<i;++n)r.push(t.getInt32())}}if(!a.util.isArray(r)||4!==r.length&&6!==r.length&&8!==r.length)throw new Error("Invalid key parameter.");var s=this.mode.name,o=-1!==["CFB","OFB","CTR","GCM"].indexOf(s);this._w=f(r,e.decrypt&&!o),this._init=!0}},a.aes._expandKey=function(e,t){return l||p(),f(e,t)},a.aes._updateBlock=h,n("AES-ECB",a.cipher.modes.ecb),n("AES-CBC",a.cipher.modes.cbc),n("AES-CFB",a.cipher.modes.cfb),n("AES-OFB",a.cipher.modes.ofb),n("AES-CTR",a.cipher.modes.ctr),n("AES-GCM",a.cipher.modes.gcm);var i,s,o,c,u,l=!1;function p(){l=!0,o=[0,1,2,4,8,16,32,64,128,27,54];for(var e=new Array(256),t=0;t<128;++t)e[t]=t<<1,e[t+128]=t+128<<1^283;i=new Array(256),s=new Array(256),c=new Array(4),u=new Array(4);for(t=0;t<4;++t)c[t]=new Array(256),u[t]=new Array(256);var r,a,n,p,f,h,d,y=0,g=0;for(t=0;t<256;++t){p=(p=g^g<<1^g<<2^g<<3^g<<4)>>8^255&p^99,i[y]=p,s[p]=y,h=(f=e[p])<<24^p<<16^p<<8^p^f,d=((r=e[y])^(a=e[r])^(n=e[a]))<<24^(y^n)<<16^(y^a^n)<<8^y^r^n;for(var v=0;v<4;++v)c[v][y]=h,u[v][p]=d,h=h<<24|h>>>8,d=d<<24|d>>>8;0===y?y=g=1:(y=r^e[e[e[r^n]]],g^=e[e[g]])}}function f(e,t){for(var r,a=e.slice(0),n=1,s=a.length,c=4*(s+6+1),l=s;l<c;++l)r=a[l-1],l%s==0?(r=i[r>>>16&255]<<24^i[r>>>8&255]<<16^i[255&r]<<8^i[r>>>24]^o[n]<<24,n++):s>6&&l%s==4&&(r=i[r>>>24]<<24^i[r>>>16&255]<<16^i[r>>>8&255]<<8^i[255&r]),a[l]=a[l-s]^r;if(t){for(var p,f=u[0],h=u[1],d=u[2],y=u[3],g=a.slice(0),v=(l=0,(c=a.length)-4);l<c;l+=4,v-=4)if(0===l||l===c-4)g[l]=a[v],g[l+1]=a[v+3],g[l+2]=a[v+2],g[l+3]=a[v+1];else for(var m=0;m<4;++m)p=a[v+m],g[l+(3&-m)]=f[i[p>>>24]]^h[i[p>>>16&255]]^d[i[p>>>8&255]]^y[i[255&p]];a=g}return a}function h(e,t,r,a){var n,o,l,p,f,h,d,y,g,v,m,C,E=e.length/4-1;a?(n=u[0],o=u[1],l=u[2],p=u[3],f=s):(n=c[0],o=c[1],l=c[2],p=c[3],f=i),h=t[0]^e[0],d=t[a?3:1]^e[1],y=t[2]^e[2],g=t[a?1:3]^e[3];for(var S=3,T=1;T<E;++T)v=n[h>>>24]^o[d>>>16&255]^l[y>>>8&255]^p[255&g]^e[++S],m=n[d>>>24]^o[y>>>16&255]^l[g>>>8&255]^p[255&h]^e[++S],C=n[y>>>24]^o[g>>>16&255]^l[h>>>8&255]^p[255&d]^e[++S],g=n[g>>>24]^o[h>>>16&255]^l[d>>>8&255]^p[255&y]^e[++S],h=v,d=m,y=C;r[0]=f[h>>>24]<<24^f[d>>>16&255]<<16^f[y>>>8&255]<<8^f[255&g]^e[++S],r[a?3:1]=f[d>>>24]<<24^f[y>>>16&255]<<16^f[g>>>8&255]<<8^f[255&h]^e[++S],r[2]=f[y>>>24]<<24^f[g>>>16&255]<<16^f[h>>>8&255]<<8^f[255&d]^e[++S],r[a?1:3]=f[g>>>24]<<24^f[h>>>16&255]<<16^f[d>>>8&255]<<8^f[255&y]^e[++S]}function d(e){var t,r="AES-"+((e=e||{}).mode||"CBC").toUpperCase(),n=(t=e.decrypt?a.cipher.createDecipher(r,e.key):a.cipher.createCipher(r,e.key)).start;return t.start=function(e,r){var i=null;r instanceof a.util.ByteBuffer&&(i=r,r={}),(r=r||{}).output=i,r.iv=e,n.call(t,r)},t}},function(e,t,r){var a=r(0);a.pki=a.pki||{};var n=e.exports=a.pki.oids=a.oids=a.oids||{};function i(e,t){n[e]=t,n[t]=e}function s(e,t){n[e]=t}i("1.2.840.113549.1.1.1","rsaEncryption"),i("1.2.840.113549.1.1.4","md5WithRSAEncryption"),i("1.2.840.113549.1.1.5","sha1WithRSAEncryption"),i("1.2.840.113549.1.1.7","RSAES-OAEP"),i("1.2.840.113549.1.1.8","mgf1"),i("1.2.840.113549.1.1.9","pSpecified"),i("1.2.840.113549.1.1.10","RSASSA-PSS"),i("1.2.840.113549.1.1.11","sha256WithRSAEncryption"),i("1.2.840.113549.1.1.12","sha384WithRSAEncryption"),i("1.2.840.113549.1.1.13","sha512WithRSAEncryption"),i("1.3.101.112","EdDSA25519"),i("1.2.840.10040.4.3","dsa-with-sha1"),i("1.3.14.3.2.7","desCBC"),i("1.3.14.3.2.26","sha1"),i("1.3.14.3.2.29","sha1WithRSASignature"),i("2.16.840.1.101.3.4.2.1","sha256"),i("2.16.840.1.101.3.4.2.2","sha384"),i("2.16.840.1.101.3.4.2.3","sha512"),i("2.16.840.1.101.3.4.2.4","sha224"),i("2.16.840.1.101.3.4.2.5","sha512-224"),i("2.16.840.1.101.3.4.2.6","sha512-256"),i("1.2.840.113549.2.2","md2"),i("1.2.840.113549.2.5","md5"),i("1.2.840.113549.1.7.1","data"),i("1.2.840.113549.1.7.2","signedData"),i("1.2.840.113549.1.7.3","envelopedData"),i("1.2.840.113549.1.7.4","signedAndEnvelopedData"),i("1.2.840.113549.1.7.5","digestedData"),i("1.2.840.113549.1.7.6","encryptedData"),i("1.2.840.113549.1.9.1","emailAddress"),i("1.2.840.113549.1.9.2","unstructuredName"),i("1.2.840.113549.1.9.3","contentType"),i("1.2.840.113549.1.9.4","messageDigest"),i("1.2.840.113549.1.9.5","signingTime"),i("1.2.840.113549.1.9.6","counterSignature"),i("1.2.840.113549.1.9.7","challengePassword"),i("1.2.840.113549.1.9.8","unstructuredAddress"),i("1.2.840.113549.1.9.14","extensionRequest"),i("1.2.840.113549.1.9.20","friendlyName"),i("1.2.840.113549.1.9.21","localKeyId"),i("1.2.840.113549.1.9.22.1","x509Certificate"),i("1.2.840.113549.1.12.10.1.1","keyBag"),i("1.2.840.113549.1.12.10.1.2","pkcs8ShroudedKeyBag"),i("1.2.840.113549.1.12.10.1.3","certBag"),i("1.2.840.113549.1.12.10.1.4","crlBag"),i("1.2.840.113549.1.12.10.1.5","secretBag"),i("1.2.840.113549.1.12.10.1.6","safeContentsBag"),i("1.2.840.113549.1.5.13","pkcs5PBES2"),i("1.2.840.113549.1.5.12","pkcs5PBKDF2"),i("1.2.840.113549.1.12.1.1","pbeWithSHAAnd128BitRC4"),i("1.2.840.113549.1.12.1.2","pbeWithSHAAnd40BitRC4"),i("1.2.840.113549.1.12.1.3","pbeWithSHAAnd3-KeyTripleDES-CBC"),i("1.2.840.113549.1.12.1.4","pbeWithSHAAnd2-KeyTripleDES-CBC"),i("1.2.840.113549.1.12.1.5","pbeWithSHAAnd128BitRC2-CBC"),i("1.2.840.113549.1.12.1.6","pbewithSHAAnd40BitRC2-CBC"),i("1.2.840.113549.2.7","hmacWithSHA1"),i("1.2.840.113549.2.8","hmacWithSHA224"),i("1.2.840.113549.2.9","hmacWithSHA256"),i("1.2.840.113549.2.10","hmacWithSHA384"),i("1.2.840.113549.2.11","hmacWithSHA512"),i("1.2.840.113549.3.7","des-EDE3-CBC"),i("2.16.840.1.101.3.4.1.2","aes128-CBC"),i("2.16.840.1.101.3.4.1.22","aes192-CBC"),i("2.16.840.1.101.3.4.1.42","aes256-CBC"),i("2.5.4.3","commonName"),i("2.5.4.4","surname"),i("2.5.4.5","serialNumber"),i("2.5.4.6","countryName"),i("2.5.4.7","localityName"),i("2.5.4.8","stateOrProvinceName"),i("2.5.4.9","streetAddress"),i("2.5.4.10","organizationName"),i("2.5.4.11","organizationalUnitName"),i("2.5.4.12","title"),i("2.5.4.13","description"),i("2.5.4.15","businessCategory"),i("2.5.4.17","postalCode"),i("2.5.4.42","givenName"),i("2.5.4.65","pseudonym"),i("1.3.6.1.4.1.311.60.2.1.2","jurisdictionOfIncorporationStateOrProvinceName"),i("1.3.6.1.4.1.311.60.2.1.3","jurisdictionOfIncorporationCountryName"),i("2.16.840.1.113730.1.1","nsCertType"),i("2.16.840.1.113730.1.13","nsComment"),s("2.5.29.1","authorityKeyIdentifier"),s("2.5.29.2","keyAttributes"),s("2.5.29.3","certificatePolicies"),s("2.5.29.4","keyUsageRestriction"),s("2.5.29.5","policyMapping"),s("2.5.29.6","subtreesConstraint"),s("2.5.29.7","subjectAltName"),s("2.5.29.8","issuerAltName"),s("2.5.29.9","subjectDirectoryAttributes"),s("2.5.29.10","basicConstraints"),s("2.5.29.11","nameConstraints"),s("2.5.29.12","policyConstraints"),s("2.5.29.13","basicConstraints"),i("2.5.29.14","subjectKeyIdentifier"),i("2.5.29.15","keyUsage"),s("2.5.29.16","privateKeyUsagePeriod"),i("2.5.29.17","subjectAltName"),i("2.5.29.18","issuerAltName"),i("2.5.29.19","basicConstraints"),s("2.5.29.20","cRLNumber"),s("2.5.29.21","cRLReason"),s("2.5.29.22","expirationDate"),s("2.5.29.23","instructionCode"),s("2.5.29.24","invalidityDate"),s("2.5.29.25","cRLDistributionPoints"),s("2.5.29.26","issuingDistributionPoint"),s("2.5.29.27","deltaCRLIndicator"),s("2.5.29.28","issuingDistributionPoint"),s("2.5.29.29","certificateIssuer"),s("2.5.29.30","nameConstraints"),i("2.5.29.31","cRLDistributionPoints"),i("2.5.29.32","certificatePolicies"),s("2.5.29.33","policyMappings"),s("2.5.29.34","policyConstraints"),i("2.5.29.35","authorityKeyIdentifier"),s("2.5.29.36","policyConstraints"),i("2.5.29.37","extKeyUsage"),s("2.5.29.46","freshestCRL"),s("2.5.29.54","inhibitAnyPolicy"),i("1.3.6.1.4.1.11129.2.4.2","timestampList"),i("1.3.6.1.5.5.7.1.1","authorityInfoAccess"),i("1.3.6.1.5.5.7.3.1","serverAuth"),i("1.3.6.1.5.5.7.3.2","clientAuth"),i("1.3.6.1.5.5.7.3.3","codeSigning"),i("1.3.6.1.5.5.7.3.4","emailProtection"),i("1.3.6.1.5.5.7.3.8","timeStamping")},function(e,t,r){var a=r(0);r(1);var n=e.exports=a.pem=a.pem||{};function i(e){for(var t=e.name+": ",r=[],a=function(e,t){return" "+t},n=0;n<e.values.length;++n)r.push(e.values[n].replace(/^(\S+\r\n)/,a));t+=r.join(",")+"\r\n";var i=0,s=-1;for(n=0;n<t.length;++n,++i)if(i>65&&-1!==s){var o=t[s];","===o?(++s,t=t.substr(0,s)+"\r\n "+t.substr(s)):t=t.substr(0,s)+"\r\n"+o+t.substr(s+1),i=n-s-1,s=-1,++n}else" "!==t[n]&&"\t"!==t[n]&&","!==t[n]||(s=n);return t}function s(e){return e.replace(/^\s+/,"")}n.encode=function(e,t){t=t||{};var r,n="-----BEGIN "+e.type+"-----\r\n";if(e.procType&&(n+=i(r={name:"Proc-Type",values:[String(e.procType.version),e.procType.type]})),e.contentDomain&&(n+=i(r={name:"Content-Domain",values:[e.contentDomain]})),e.dekInfo&&(r={name:"DEK-Info",values:[e.dekInfo.algorithm]},e.dekInfo.parameters&&r.values.push(e.dekInfo.parameters),n+=i(r)),e.headers)for(var s=0;s<e.headers.length;++s)n+=i(e.headers[s]);return e.procType&&(n+="\r\n"),n+=a.util.encode64(e.body,t.maxline||64)+"\r\n",n+="-----END "+e.type+"-----\r\n"},n.decode=function(e){for(var t,r=[],n=/\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g,i=/([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/,o=/\r?\n/;t=n.exec(e);){var c=t[1];"NEW CERTIFICATE REQUEST"===c&&(c="CERTIFICATE REQUEST");var u={type:c,procType:null,contentDomain:null,dekInfo:null,headers:[],body:a.util.decode64(t[3])};if(r.push(u),t[2]){for(var l=t[2].split(o),p=0;t&&p<l.length;){for(var f=l[p].replace(/\s+$/,""),h=p+1;h<l.length;++h){var d=l[h];if(!/\s/.test(d[0]))break;f+=d,p=h}if(t=f.match(i)){for(var y={name:t[1],values:[]},g=t[2].split(","),v=0;v<g.length;++v)y.values.push(s(g[v]));if(u.procType)if(u.contentDomain||"Content-Domain"!==y.name)if(u.dekInfo||"DEK-Info"!==y.name)u.headers.push(y);else{if(0===y.values.length)throw new Error('Invalid PEM formatted message. The "DEK-Info" header must have at least one subfield.');u.dekInfo={algorithm:g[0],parameters:g[1]||null}}else u.contentDomain=g[0]||"";else{if("Proc-Type"!==y.name)throw new Error('Invalid PEM formatted message. The first encapsulated header must be "Proc-Type".');if(2!==y.values.length)throw new Error('Invalid PEM formatted message. The "Proc-Type" header must have two subfields.');u.procType={version:g[0],type:g[1]}}}++p}if("ENCRYPTED"===u.procType&&!u.dekInfo)throw new Error('Invalid PEM formatted message. The "DEK-Info" header must be present if "Proc-Type" is "ENCRYPTED".')}}if(0===r.length)throw new Error("Invalid PEM formatted message.");return r}},function(e,t,r){var a=r(0);r(4),r(1),(e.exports=a.hmac=a.hmac||{}).create=function(){var e=null,t=null,r=null,n=null,i={start:function(i,s){if(null!==i)if("string"==typeof i){if(!((i=i.toLowerCase())in a.md.algorithms))throw new Error('Unknown hash algorithm "'+i+'"');t=a.md.algorithms[i].create()}else t=i;if(null===s)s=e;else{if("string"==typeof s)s=a.util.createBuffer(s);else if(a.util.isArray(s)){var o=s;s=a.util.createBuffer();for(var c=0;c<o.length;++c)s.putByte(o[c])}var u=s.length();u>t.blockLength&&(t.start(),t.update(s.bytes()),s=t.digest()),r=a.util.createBuffer(),n=a.util.createBuffer(),u=s.length();for(c=0;c<u;++c){o=s.at(c);r.putByte(54^o),n.putByte(92^o)}if(u<t.blockLength)for(o=t.blockLength-u,c=0;c<o;++c)r.putByte(54),n.putByte(92);e=s,r=r.bytes(),n=n.bytes()}t.start(),t.update(r)},update:function(e){t.update(e)},getMac:function(){var e=t.digest().bytes();return t.start(),t.update(n),t.update(e),t.digest()}};return i.digest=i.getMac,i}},function(e,t,r){var a=r(0);r(4),r(1);var n=e.exports=a.sha1=a.sha1||{};a.md.sha1=a.md.algorithms.sha1=n,n.create=function(){s||(i=String.fromCharCode(128),i+=a.util.fillString(String.fromCharCode(0),64),s=!0);var e=null,t=a.util.createBuffer(),r=new Array(80),n={algorithm:"sha1",blockLength:64,digestLength:20,messageLength:0,fullMessageLength:null,messageLengthSize:8,start:function(){n.messageLength=0,n.fullMessageLength=n.messageLength64=[];for(var r=n.messageLengthSize/4,i=0;i<r;++i)n.fullMessageLength.push(0);return t=a.util.createBuffer(),e={h0:1732584193,h1:4023233417,h2:2562383102,h3:271733878,h4:3285377520},n}};return n.start(),n.update=function(i,s){"utf8"===s&&(i=a.util.encodeUtf8(i));var c=i.length;n.messageLength+=c,c=[c/4294967296>>>0,c>>>0];for(var u=n.fullMessageLength.length-1;u>=0;--u)n.fullMessageLength[u]+=c[1],c[1]=c[0]+(n.fullMessageLength[u]/4294967296>>>0),n.fullMessageLength[u]=n.fullMessageLength[u]>>>0,c[0]=c[1]/4294967296>>>0;return t.putBytes(i),o(e,r,t),(t.read>2048||0===t.length())&&t.compact(),n},n.digest=function(){var s=a.util.createBuffer();s.putBytes(t.bytes());var c,u=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize&n.blockLength-1;s.putBytes(i.substr(0,n.blockLength-u));for(var l=8*n.fullMessageLength[0],p=0;p<n.fullMessageLength.length-1;++p)l+=(c=8*n.fullMessageLength[p+1])/4294967296>>>0,s.putInt32(l>>>0),l=c>>>0;s.putInt32(l);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3,h4:e.h4};o(f,r,s);var h=a.util.createBuffer();return h.putInt32(f.h0),h.putInt32(f.h1),h.putInt32(f.h2),h.putInt32(f.h3),h.putInt32(f.h4),h},n};var i=null,s=!1;function o(e,t,r){for(var a,n,i,s,o,c,u,l=r.length();l>=64;){for(n=e.h0,i=e.h1,s=e.h2,o=e.h3,c=e.h4,u=0;u<16;++u)a=r.getInt32(),t[u]=a,a=(n<<5|n>>>27)+(o^i&(s^o))+c+1518500249+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<20;++u)a=(a=t[u-3]^t[u-8]^t[u-14]^t[u-16])<<1|a>>>31,t[u]=a,a=(n<<5|n>>>27)+(o^i&(s^o))+c+1518500249+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<32;++u)a=(a=t[u-3]^t[u-8]^t[u-14]^t[u-16])<<1|a>>>31,t[u]=a,a=(n<<5|n>>>27)+(i^s^o)+c+1859775393+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<40;++u)a=(a=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|a>>>30,t[u]=a,a=(n<<5|n>>>27)+(i^s^o)+c+1859775393+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<60;++u)a=(a=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|a>>>30,t[u]=a,a=(n<<5|n>>>27)+(i&s|o&(i^s))+c+2400959708+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<80;++u)a=(a=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|a>>>30,t[u]=a,a=(n<<5|n>>>27)+(i^s^o)+c+3395469782+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;e.h0=e.h0+n|0,e.h1=e.h1+i|0,e.h2=e.h2+s|0,e.h3=e.h3+o|0,e.h4=e.h4+c|0,l-=64}}},function(e,t,r){var a=r(0);function n(e,t){a.cipher.registerAlgorithm(e,(function(){return new a.des.Algorithm(e,t)}))}r(13),r(19),r(1),e.exports=a.des=a.des||{},a.des.startEncrypting=function(e,t,r,a){var n=d({key:e,output:r,decrypt:!1,mode:a||(null===t?"ECB":"CBC")});return n.start(t),n},a.des.createEncryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!1,mode:t})},a.des.startDecrypting=function(e,t,r,a){var n=d({key:e,output:r,decrypt:!0,mode:a||(null===t?"ECB":"CBC")});return n.start(t),n},a.des.createDecryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!0,mode:t})},a.des.Algorithm=function(e,t){var r=this;r.name=e,r.mode=new t({blockSize:8,cipher:{encrypt:function(e,t){return h(r._keys,e,t,!1)},decrypt:function(e,t){return h(r._keys,e,t,!0)}}}),r._init=!1},a.des.Algorithm.prototype.initialize=function(e){if(!this._init){var t=a.util.createBuffer(e.key);if(0===this.name.indexOf("3DES")&&24!==t.length())throw new Error("Invalid Triple-DES key size: "+8*t.length());this._keys=function(e){for(var t,r=[0,4,536870912,536870916,65536,65540,536936448,536936452,512,516,536871424,536871428,66048,66052,536936960,536936964],a=[0,1,1048576,1048577,67108864,67108865,68157440,68157441,256,257,1048832,1048833,67109120,67109121,68157696,68157697],n=[0,8,2048,2056,16777216,16777224,16779264,16779272,0,8,2048,2056,16777216,16777224,16779264,16779272],i=[0,2097152,134217728,136314880,8192,2105344,134225920,136323072,131072,2228224,134348800,136445952,139264,2236416,134356992,136454144],s=[0,262144,16,262160,0,262144,16,262160,4096,266240,4112,266256,4096,266240,4112,266256],o=[0,1024,32,1056,0,1024,32,1056,33554432,33555456,33554464,33555488,33554432,33555456,33554464,33555488],c=[0,268435456,524288,268959744,2,268435458,524290,268959746,0,268435456,524288,268959744,2,268435458,524290,268959746],u=[0,65536,2048,67584,536870912,536936448,536872960,536938496,131072,196608,133120,198656,537001984,537067520,537004032,537069568],l=[0,262144,0,262144,2,262146,2,262146,33554432,33816576,33554432,33816576,33554434,33816578,33554434,33816578],p=[0,268435456,8,268435464,0,268435456,8,268435464,1024,268436480,1032,268436488,1024,268436480,1032,268436488],f=[0,32,0,32,1048576,1048608,1048576,1048608,8192,8224,8192,8224,1056768,1056800,1056768,1056800],h=[0,16777216,512,16777728,2097152,18874368,2097664,18874880,67108864,83886080,67109376,83886592,69206016,85983232,69206528,85983744],d=[0,4096,134217728,134221824,524288,528384,134742016,134746112,16,4112,134217744,134221840,524304,528400,134742032,134746128],y=[0,4,256,260,0,4,256,260,1,5,257,261,1,5,257,261],g=e.length()>8?3:1,v=[],m=[0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0],C=0,E=0;E<g;E++){var S=e.getInt32(),T=e.getInt32();S^=(t=252645135&(S>>>4^T))<<4,S^=t=65535&((T^=t)>>>-16^S),S^=(t=858993459&(S>>>2^(T^=t<<-16)))<<2,S^=t=65535&((T^=t)>>>-16^S),S^=(t=1431655765&(S>>>1^(T^=t<<-16)))<<1,S^=t=16711935&((T^=t)>>>8^S),t=(S^=(t=1431655765&(S>>>1^(T^=t<<8)))<<1)<<8|(T^=t)>>>20&240,S=T<<24|T<<8&16711680|T>>>8&65280|T>>>24&240,T=t;for(var I=0;I<m.length;++I){m[I]?(S=S<<2|S>>>26,T=T<<2|T>>>26):(S=S<<1|S>>>27,T=T<<1|T>>>27);var A=r[(S&=-15)>>>28]|a[S>>>24&15]|n[S>>>20&15]|i[S>>>16&15]|s[S>>>12&15]|o[S>>>8&15]|c[S>>>4&15],B=u[(T&=-15)>>>28]|l[T>>>24&15]|p[T>>>20&15]|f[T>>>16&15]|h[T>>>12&15]|d[T>>>8&15]|y[T>>>4&15];t=65535&(B>>>16^A),v[C++]=A^t,v[C++]=B^t<<16}}return v}(t),this._init=!0}},n("DES-ECB",a.cipher.modes.ecb),n("DES-CBC",a.cipher.modes.cbc),n("DES-CFB",a.cipher.modes.cfb),n("DES-OFB",a.cipher.modes.ofb),n("DES-CTR",a.cipher.modes.ctr),n("3DES-ECB",a.cipher.modes.ecb),n("3DES-CBC",a.cipher.modes.cbc),n("3DES-CFB",a.cipher.modes.cfb),n("3DES-OFB",a.cipher.modes.ofb),n("3DES-CTR",a.cipher.modes.ctr);var i=[16843776,0,65536,16843780,16842756,66564,4,65536,1024,16843776,16843780,1024,16778244,16842756,16777216,4,1028,16778240,16778240,66560,66560,16842752,16842752,16778244,65540,16777220,16777220,65540,0,1028,66564,16777216,65536,16843780,4,16842752,16843776,16777216,16777216,1024,16842756,65536,66560,16777220,1024,4,16778244,66564,16843780,65540,16842752,16778244,16777220,1028,66564,16843776,1028,16778240,16778240,0,65540,66560,0,16842756],s=[-2146402272,-2147450880,32768,1081376,1048576,32,-2146435040,-2147450848,-2147483616,-2146402272,-2146402304,-2147483648,-2147450880,1048576,32,-2146435040,1081344,1048608,-2147450848,0,-2147483648,32768,1081376,-2146435072,1048608,-2147483616,0,1081344,32800,-2146402304,-2146435072,32800,0,1081376,-2146435040,1048576,-2147450848,-2146435072,-2146402304,32768,-2146435072,-2147450880,32,-2146402272,1081376,32,32768,-2147483648,32800,-2146402304,1048576,-2147483616,1048608,-2147450848,-2147483616,1048608,1081344,0,-2147450880,32800,-2147483648,-2146435040,-2146402272,1081344],o=[520,134349312,0,134348808,134218240,0,131592,134218240,131080,134217736,134217736,131072,134349320,131080,134348800,520,134217728,8,134349312,512,131584,134348800,134348808,131592,134218248,131584,131072,134218248,8,134349320,512,134217728,134349312,134217728,131080,520,131072,134349312,134218240,0,512,131080,134349320,134218240,134217736,512,0,134348808,134218248,131072,134217728,134349320,8,131592,131584,134217736,134348800,134218248,520,134348800,131592,8,134348808,131584],c=[8396801,8321,8321,128,8396928,8388737,8388609,8193,0,8396800,8396800,8396929,129,0,8388736,8388609,1,8192,8388608,8396801,128,8388608,8193,8320,8388737,1,8320,8388736,8192,8396928,8396929,129,8388736,8388609,8396800,8396929,129,0,0,8396800,8320,8388736,8388737,1,8396801,8321,8321,128,8396929,129,1,8192,8388609,8193,8396928,8388737,8193,8320,8388608,8396801,128,8388608,8192,8396928],u=[256,34078976,34078720,1107296512,524288,256,1073741824,34078720,1074266368,524288,33554688,1074266368,1107296512,1107820544,524544,1073741824,33554432,1074266112,1074266112,0,1073742080,1107820800,1107820800,33554688,1107820544,1073742080,0,1107296256,34078976,33554432,1107296256,524544,524288,1107296512,256,33554432,1073741824,34078720,1107296512,1074266368,33554688,1073741824,1107820544,34078976,1074266368,256,33554432,1107820544,1107820800,524544,1107296256,1107820800,34078720,0,1074266112,1107296256,524544,33554688,1073742080,524288,0,1074266112,34078976,1073742080],l=[536870928,541065216,16384,541081616,541065216,16,541081616,4194304,536887296,4210704,4194304,536870928,4194320,536887296,536870912,16400,0,4194320,536887312,16384,4210688,536887312,16,541065232,541065232,0,4210704,541081600,16400,4210688,541081600,536870912,536887296,16,541065232,4210688,541081616,4194304,16400,536870928,4194304,536887296,536870912,16400,536870928,541081616,4210688,541065216,4210704,541081600,0,541065232,16,16384,541065216,4210704,16384,4194320,536887312,0,541081600,536870912,4194320,536887312],p=[2097152,69206018,67110914,0,2048,67110914,2099202,69208064,69208066,2097152,0,67108866,2,67108864,69206018,2050,67110912,2099202,2097154,67110912,67108866,69206016,69208064,2097154,69206016,2048,2050,69208066,2099200,2,67108864,2099200,67108864,2099200,2097152,67110914,67110914,69206018,69206018,2,2097154,67108864,67110912,2097152,69208064,2050,2099202,69208064,2050,67108866,69208066,69206016,2099200,0,2,69208066,0,2099202,69206016,2048,67108866,67110912,2048,2097154],f=[268439616,4096,262144,268701760,268435456,268439616,64,268435456,262208,268697600,268701760,266240,268701696,266304,4096,64,268697600,268435520,268439552,4160,266240,262208,268697664,268701696,4160,0,0,268697664,268435520,268439552,266304,262144,266304,262144,268701696,4096,64,268697664,4096,266304,268439552,64,268435520,268697600,268697664,268435456,262144,268439616,0,268701760,262208,268435520,268697600,268439552,268439616,0,268701760,266240,266240,4160,4160,262208,268435456,268701696];function h(e,t,r,a){var n,h,d=32===e.length?3:9;n=3===d?a?[30,-2,-2]:[0,32,2]:a?[94,62,-2,32,64,2,30,-2,-2]:[0,32,2,62,30,-2,64,96,2];var y=t[0],g=t[1];y^=(h=252645135&(y>>>4^g))<<4,y^=(h=65535&(y>>>16^(g^=h)))<<16,y^=h=858993459&((g^=h)>>>2^y),y^=h=16711935&((g^=h<<2)>>>8^y),y=(y^=(h=1431655765&(y>>>1^(g^=h<<8)))<<1)<<1|y>>>31,g=(g^=h)<<1|g>>>31;for(var v=0;v<d;v+=3){for(var m=n[v+1],C=n[v+2],E=n[v];E!=m;E+=C){var S=g^e[E],T=(g>>>4|g<<28)^e[E+1];h=y,y=g,g=h^(s[S>>>24&63]|c[S>>>16&63]|l[S>>>8&63]|f[63&S]|i[T>>>24&63]|o[T>>>16&63]|u[T>>>8&63]|p[63&T])}h=y,y=g,g=h}g=g>>>1|g<<31,g^=h=1431655765&((y=y>>>1|y<<31)>>>1^g),g^=(h=16711935&(g>>>8^(y^=h<<1)))<<8,g^=(h=858993459&(g>>>2^(y^=h)))<<2,g^=h=65535&((y^=h)>>>16^g),g^=h=252645135&((y^=h<<16)>>>4^g),y^=h<<4,r[0]=y,r[1]=g}function d(e){var t,r="DES-"+((e=e||{}).mode||"CBC").toUpperCase(),n=(t=e.decrypt?a.cipher.createDecipher(r,e.key):a.cipher.createCipher(r,e.key)).start;return t.start=function(e,r){var i=null;r instanceof a.util.ByteBuffer&&(i=r,r={}),(r=r||{}).output=i,r.iv=e,n.call(t,r)},t}},function(e,t,r){var a=r(0);if(r(3),r(12),r(6),r(26),r(27),r(2),r(1),void 0===n)var n=a.jsbn.BigInteger;var i=a.util.isNodejs?r(16):null,s=a.asn1,o=a.util;a.pki=a.pki||{},e.exports=a.pki.rsa=a.rsa=a.rsa||{};var c=a.pki,u=[6,4,2,4,2,4,6,2],l={name:"PrivateKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"PrivateKeyInfo.version",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"PrivateKeyInfo.privateKeyAlgorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"privateKeyOid"}]},{name:"PrivateKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.OCTETSTRING,constructed:!1,capture:"privateKey"}]},p={name:"RSAPrivateKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"RSAPrivateKey.version",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"RSAPrivateKey.modulus",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyModulus"},{name:"RSAPrivateKey.publicExponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPublicExponent"},{name:"RSAPrivateKey.privateExponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrivateExponent"},{name:"RSAPrivateKey.prime1",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrime1"},{name:"RSAPrivateKey.prime2",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrime2"},{name:"RSAPrivateKey.exponent1",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyExponent1"},{name:"RSAPrivateKey.exponent2",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyExponent2"},{name:"RSAPrivateKey.coefficient",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyCoefficient"}]},f={name:"RSAPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"RSAPublicKey.modulus",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"publicKeyModulus"},{name:"RSAPublicKey.exponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"publicKeyExponent"}]},h=a.pki.rsa.publicKeyValidator={name:"SubjectPublicKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,captureAsn1:"subjectPublicKeyInfo",value:[{name:"SubjectPublicKeyInfo.AlgorithmIdentifier",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"publicKeyOid"}]},{name:"SubjectPublicKeyInfo.subjectPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.BITSTRING,constructed:!1,value:[{name:"SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,optional:!0,captureAsn1:"rsaPublicKey"}]}]},d={name:"DigestInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"DigestInfo.DigestAlgorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"DigestInfo.DigestAlgorithm.algorithmIdentifier",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"algorithmIdentifier"},{name:"DigestInfo.DigestAlgorithm.parameters",tagClass:s.Class.UNIVERSAL,type:s.Type.NULL,capture:"parameters",optional:!0,constructed:!1}]},{name:"DigestInfo.digest",tagClass:s.Class.UNIVERSAL,type:s.Type.OCTETSTRING,constructed:!1,capture:"digest"}]},y=function(e){var t;if(!(e.algorithm in c.oids)){var r=new Error("Unknown message digest algorithm.");throw r.algorithm=e.algorithm,r}t=c.oids[e.algorithm];var a=s.oidToDer(t).getBytes(),n=s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[]),i=s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[]);i.value.push(s.create(s.Class.UNIVERSAL,s.Type.OID,!1,a)),i.value.push(s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,""));var o=s.create(s.Class.UNIVERSAL,s.Type.OCTETSTRING,!1,e.digest().getBytes());return n.value.push(i),n.value.push(o),s.toDer(n).getBytes()},g=function(e,t,r){if(r)return e.modPow(t.e,t.n);if(!t.p||!t.q)return e.modPow(t.d,t.n);var i;t.dP||(t.dP=t.d.mod(t.p.subtract(n.ONE))),t.dQ||(t.dQ=t.d.mod(t.q.subtract(n.ONE))),t.qInv||(t.qInv=t.q.modInverse(t.p));do{i=new n(a.util.bytesToHex(a.random.getBytes(t.n.bitLength()/8)),16)}while(i.compareTo(t.n)>=0||!i.gcd(t.n).equals(n.ONE));for(var s=(e=e.multiply(i.modPow(t.e,t.n)).mod(t.n)).mod(t.p).modPow(t.dP,t.p),o=e.mod(t.q).modPow(t.dQ,t.q);s.compareTo(o)<0;)s=s.add(t.p);var c=s.subtract(o).multiply(t.qInv).mod(t.p).multiply(t.q).add(o);return c=c.multiply(i.modInverse(t.n)).mod(t.n)};function v(e,t,r){var n=a.util.createBuffer(),i=Math.ceil(t.n.bitLength()/8);if(e.length>i-11){var s=new Error("Message is too long for PKCS#1 v1.5 padding.");throw s.length=e.length,s.max=i-11,s}n.putByte(0),n.putByte(r);var o,c=i-3-e.length;if(0===r||1===r){o=0===r?0:255;for(var u=0;u<c;++u)n.putByte(o)}else for(;c>0;){var l=0,p=a.random.getBytes(c);for(u=0;u<c;++u)0===(o=p.charCodeAt(u))?++l:n.putByte(o);c=l}return n.putByte(0),n.putBytes(e),n}function m(e,t,r,n,i){var s=Math.ceil(t.n.bitLength()/8),o=a.util.createBuffer(e),c=o.getByte(),u=o.getByte();if(0!==c||r&&0!==u&&1!==u||!r&&2!==u||r&&0===u&&void 0===n)throw new Error("Encryption block is invalid.");var l=0;if(0===u){l=s-3-n;for(var p=0;p<l;++p)if(0!==o.getByte())throw new Error("Encryption block is invalid.")}else if(1===u){for(l=0;o.length()>1;){if(255!==o.getByte()){--o.read;break}++l}if(l<8&&(!i||!i._skipPaddingChecks))throw new Error("Encryption block is invalid.")}else if(2===u){for(l=0;o.length()>1;){if(0===o.getByte()){--o.read;break}++l}if(l<8&&(!i||!i._skipPaddingChecks))throw new Error("Encryption block is invalid.")}if(0!==o.getByte()||l!==s-3-o.length())throw new Error("Encryption block is invalid.");return o.getBytes()}function C(e,t,r){"function"==typeof t&&(r=t,t={});var i={algorithm:{name:(t=t||{}).algorithm||"PRIMEINC",options:{workers:t.workers||2,workLoad:t.workLoad||100,workerScript:t.workerScript}}};function s(){o(e.pBits,(function(t,a){return t?r(t):(e.p=a,null!==e.q?u(t,e.q):void o(e.qBits,u))}))}function o(e,t){a.prime.generateProbablePrime(e,i,t)}function u(t,a){if(t)return r(t);if(e.q=a,e.p.compareTo(e.q)<0){var i=e.p;e.p=e.q,e.q=i}if(0!==e.p.subtract(n.ONE).gcd(e.e).compareTo(n.ONE))return e.p=null,void s();if(0!==e.q.subtract(n.ONE).gcd(e.e).compareTo(n.ONE))return e.q=null,void o(e.qBits,u);if(e.p1=e.p.subtract(n.ONE),e.q1=e.q.subtract(n.ONE),e.phi=e.p1.multiply(e.q1),0!==e.phi.gcd(e.e).compareTo(n.ONE))return e.p=e.q=null,void s();if(e.n=e.p.multiply(e.q),e.n.bitLength()!==e.bits)return e.q=null,void o(e.qBits,u);var l=e.e.modInverse(e.phi);e.keys={privateKey:c.rsa.setPrivateKey(e.n,e.e,l,e.p,e.q,l.mod(e.p1),l.mod(e.q1),e.q.modInverse(e.p)),publicKey:c.rsa.setPublicKey(e.n,e.e)},r(null,e.keys)}"prng"in t&&(i.prng=t.prng),s()}function E(e){var t=e.toString(16);t[0]>="8"&&(t="00"+t);var r=a.util.hexToBytes(t);return r.length>1&&(0===r.charCodeAt(0)&&0==(128&r.charCodeAt(1))||255===r.charCodeAt(0)&&128==(128&r.charCodeAt(1)))?r.substr(1):r}function S(e){return e<=100?27:e<=150?18:e<=200?15:e<=250?12:e<=300?9:e<=350?8:e<=400?7:e<=500?6:e<=600?5:e<=800?4:e<=1250?3:2}function T(e){return a.util.isNodejs&&"function"==typeof i[e]}function I(e){return void 0!==o.globalScope&&"object"==typeof o.globalScope.crypto&&"object"==typeof o.globalScope.crypto.subtle&&"function"==typeof o.globalScope.crypto.subtle[e]}function A(e){return void 0!==o.globalScope&&"object"==typeof o.globalScope.msCrypto&&"object"==typeof o.globalScope.msCrypto.subtle&&"function"==typeof o.globalScope.msCrypto.subtle[e]}function B(e){for(var t=a.util.hexToBytes(e.toString(16)),r=new Uint8Array(t.length),n=0;n<t.length;++n)r[n]=t.charCodeAt(n);return r}c.rsa.encrypt=function(e,t,r){var i,s=r,o=Math.ceil(t.n.bitLength()/8);!1!==r&&!0!==r?(s=2===r,i=v(e,t,r)):(i=a.util.createBuffer()).putBytes(e);for(var c=new n(i.toHex(),16),u=g(c,t,s).toString(16),l=a.util.createBuffer(),p=o-Math.ceil(u.length/2);p>0;)l.putByte(0),--p;return l.putBytes(a.util.hexToBytes(u)),l.getBytes()},c.rsa.decrypt=function(e,t,r,i){var s=Math.ceil(t.n.bitLength()/8);if(e.length!==s){var o=new Error("Encrypted message length is invalid.");throw o.length=e.length,o.expected=s,o}var c=new n(a.util.createBuffer(e).toHex(),16);if(c.compareTo(t.n)>=0)throw new Error("Encrypted message is invalid.");for(var u=g(c,t,r).toString(16),l=a.util.createBuffer(),p=s-Math.ceil(u.length/2);p>0;)l.putByte(0),--p;return l.putBytes(a.util.hexToBytes(u)),!1!==i?m(l.getBytes(),t,r):l.getBytes()},c.rsa.createKeyPairGenerationState=function(e,t,r){"string"==typeof e&&(e=parseInt(e,10)),e=e||2048;var i,s=(r=r||{}).prng||a.random,o={nextBytes:function(e){for(var t=s.getBytesSync(e.length),r=0;r<e.length;++r)e[r]=t.charCodeAt(r)}},c=r.algorithm||"PRIMEINC";if("PRIMEINC"!==c)throw new Error("Invalid key generation algorithm: "+c);return(i={algorithm:c,state:0,bits:e,rng:o,eInt:t||65537,e:new n(null),p:null,q:null,qBits:e>>1,pBits:e-(e>>1),pqState:0,num:null,keys:null}).e.fromInt(i.eInt),i},c.rsa.stepKeyPairGenerationState=function(e,t){"algorithm"in e||(e.algorithm="PRIMEINC");var r=new n(null);r.fromInt(30);for(var a,i=0,s=function(e,t){return e|t},o=+new Date,l=0;null===e.keys&&(t<=0||l<t);){if(0===e.state){var p=null===e.p?e.pBits:e.qBits,f=p-1;0===e.pqState?(e.num=new n(p,e.rng),e.num.testBit(f)||e.num.bitwiseTo(n.ONE.shiftLeft(f),s,e.num),e.num.dAddOffset(31-e.num.mod(r).byteValue(),0),i=0,++e.pqState):1===e.pqState?e.num.bitLength()>p?e.pqState=0:e.num.isProbablePrime(S(e.num.bitLength()))?++e.pqState:e.num.dAddOffset(u[i++%8],0):2===e.pqState?e.pqState=0===e.num.subtract(n.ONE).gcd(e.e).compareTo(n.ONE)?3:0:3===e.pqState&&(e.pqState=0,null===e.p?e.p=e.num:e.q=e.num,null!==e.p&&null!==e.q&&++e.state,e.num=null)}else if(1===e.state)e.p.compareTo(e.q)<0&&(e.num=e.p,e.p=e.q,e.q=e.num),++e.state;else if(2===e.state)e.p1=e.p.subtract(n.ONE),e.q1=e.q.subtract(n.ONE),e.phi=e.p1.multiply(e.q1),++e.state;else if(3===e.state)0===e.phi.gcd(e.e).compareTo(n.ONE)?++e.state:(e.p=null,e.q=null,e.state=0);else if(4===e.state)e.n=e.p.multiply(e.q),e.n.bitLength()===e.bits?++e.state:(e.q=null,e.state=0);else if(5===e.state){var h=e.e.modInverse(e.phi);e.keys={privateKey:c.rsa.setPrivateKey(e.n,e.e,h,e.p,e.q,h.mod(e.p1),h.mod(e.q1),e.q.modInverse(e.p)),publicKey:c.rsa.setPublicKey(e.n,e.e)}}l+=(a=+new Date)-o,o=a}return null!==e.keys},c.rsa.generateKeyPair=function(e,t,r,n){if(1===arguments.length?"object"==typeof e?(r=e,e=void 0):"function"==typeof e&&(n=e,e=void 0):2===arguments.length?"number"==typeof e?"function"==typeof t?(n=t,t=void 0):"number"!=typeof t&&(r=t,t=void 0):(r=e,n=t,e=void 0,t=void 0):3===arguments.length&&("number"==typeof t?"function"==typeof r&&(n=r,r=void 0):(n=r,r=t,t=void 0)),r=r||{},void 0===e&&(e=r.bits||2048),void 0===t&&(t=r.e||65537),!a.options.usePureJavaScript&&!r.prng&&e>=256&&e<=16384&&(65537===t||3===t))if(n){if(T("generateKeyPair"))return i.generateKeyPair("rsa",{modulusLength:e,publicExponent:t,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}},(function(e,t,r){if(e)return n(e);n(null,{privateKey:c.privateKeyFromPem(r),publicKey:c.publicKeyFromPem(t)})}));if(I("generateKey")&&I("exportKey"))return o.globalScope.crypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:e,publicExponent:B(t),hash:{name:"SHA-256"}},!0,["sign","verify"]).then((function(e){return o.globalScope.crypto.subtle.exportKey("pkcs8",e.privateKey)})).then(void 0,(function(e){n(e)})).then((function(e){if(e){var t=c.privateKeyFromAsn1(s.fromDer(a.util.createBuffer(e)));n(null,{privateKey:t,publicKey:c.setRsaPublicKey(t.n,t.e)})}}));if(A("generateKey")&&A("exportKey")){var u=o.globalScope.msCrypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:e,publicExponent:B(t),hash:{name:"SHA-256"}},!0,["sign","verify"]);return u.oncomplete=function(e){var t=e.target.result,r=o.globalScope.msCrypto.subtle.exportKey("pkcs8",t.privateKey);r.oncomplete=function(e){var t=e.target.result,r=c.privateKeyFromAsn1(s.fromDer(a.util.createBuffer(t)));n(null,{privateKey:r,publicKey:c.setRsaPublicKey(r.n,r.e)})},r.onerror=function(e){n(e)}},void(u.onerror=function(e){n(e)})}}else if(T("generateKeyPairSync")){var l=i.generateKeyPairSync("rsa",{modulusLength:e,publicExponent:t,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}});return{privateKey:c.privateKeyFromPem(l.privateKey),publicKey:c.publicKeyFromPem(l.publicKey)}}var p=c.rsa.createKeyPairGenerationState(e,t,r);if(!n)return c.rsa.stepKeyPairGenerationState(p,0),p.keys;C(p,r,n)},c.setRsaPublicKey=c.rsa.setPublicKey=function(e,t){var r={n:e,e:t,encrypt:function(e,t,n){if("string"==typeof t?t=t.toUpperCase():void 0===t&&(t="RSAES-PKCS1-V1_5"),"RSAES-PKCS1-V1_5"===t)t={encode:function(e,t,r){return v(e,t,2).getBytes()}};else if("RSA-OAEP"===t||"RSAES-OAEP"===t)t={encode:function(e,t){return a.pkcs1.encode_rsa_oaep(t,e,n)}};else if(-1!==["RAW","NONE","NULL",null].indexOf(t))t={encode:function(e){return e}};else if("string"==typeof t)throw new Error('Unsupported encryption scheme: "'+t+'".');var i=t.encode(e,r,!0);return c.rsa.encrypt(i,r,!0)},verify:function(e,t,n,i){"string"==typeof n?n=n.toUpperCase():void 0===n&&(n="RSASSA-PKCS1-V1_5"),void 0===i&&(i={_parseAllDigestBytes:!0,_skipPaddingChecks:!1}),"_parseAllDigestBytes"in i||(i._parseAllDigestBytes=!0),"_skipPaddingChecks"in i||(i._skipPaddingChecks=!1),"RSASSA-PKCS1-V1_5"===n?n={verify:function(e,t){t=m(t,r,!0,void 0,i);var n=s.fromDer(t,{parseAllBytes:i._parseAllDigestBytes}),o={},c=[];if(!s.validate(n,d,o,c)||2!==n.value.length)throw(u=new Error("ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value.")).errors=c,u;var u,l=s.derToOid(o.algorithmIdentifier);if(l!==a.oids.md2&&l!==a.oids.md5&&l!==a.oids.sha1&&l!==a.oids.sha224&&l!==a.oids.sha256&&l!==a.oids.sha384&&l!==a.oids.sha512&&l!==a.oids["sha512-224"]&&l!==a.oids["sha512-256"])throw(u=new Error("Unknown RSASSA-PKCS1-v1_5 DigestAlgorithm identifier.")).oid=l,u;if((l===a.oids.md2||l===a.oids.md5)&&!("parameters"in o))throw new Error("ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value. Missing algorithm identifier NULL parameters.");return e===o.digest}}:"NONE"!==n&&"NULL"!==n&&null!==n||(n={verify:function(e,t){return e===(t=m(t,r,!0,void 0,i))}});var o=c.rsa.decrypt(t,r,!0,!1);return n.verify(e,o,r.n.bitLength())}};return r},c.setRsaPrivateKey=c.rsa.setPrivateKey=function(e,t,r,n,i,s,o,u){var l={n:e,e:t,d:r,p:n,q:i,dP:s,dQ:o,qInv:u,decrypt:function(e,t,r){"string"==typeof t?t=t.toUpperCase():void 0===t&&(t="RSAES-PKCS1-V1_5");var n=c.rsa.decrypt(e,l,!1,!1);if("RSAES-PKCS1-V1_5"===t)t={decode:m};else if("RSA-OAEP"===t||"RSAES-OAEP"===t)t={decode:function(e,t){return a.pkcs1.decode_rsa_oaep(t,e,r)}};else{if(-1===["RAW","NONE","NULL",null].indexOf(t))throw new Error('Unsupported encryption scheme: "'+t+'".');t={decode:function(e){return e}}}return t.decode(n,l,!1)},sign:function(e,t){var r=!1;"string"==typeof t&&(t=t.toUpperCase()),void 0===t||"RSASSA-PKCS1-V1_5"===t?(t={encode:y},r=1):"NONE"!==t&&"NULL"!==t&&null!==t||(t={encode:function(){return e}},r=1);var a=t.encode(e,l.n.bitLength());return c.rsa.encrypt(a,l,r)}};return l},c.wrapRsaPrivateKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,s.integerToDer(0).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.OID,!1,s.oidToDer(c.oids.rsaEncryption).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,"")]),s.create(s.Class.UNIVERSAL,s.Type.OCTETSTRING,!1,s.toDer(e).getBytes())])},c.privateKeyFromAsn1=function(e){var t,r,i,o,u,f,h,d,y={},g=[];if(s.validate(e,l,y,g)&&(e=s.fromDer(a.util.createBuffer(y.privateKey))),y={},g=[],!s.validate(e,p,y,g)){var v=new Error("Cannot read private key. ASN.1 object does not contain an RSAPrivateKey.");throw v.errors=g,v}return t=a.util.createBuffer(y.privateKeyModulus).toHex(),r=a.util.createBuffer(y.privateKeyPublicExponent).toHex(),i=a.util.createBuffer(y.privateKeyPrivateExponent).toHex(),o=a.util.createBuffer(y.privateKeyPrime1).toHex(),u=a.util.createBuffer(y.privateKeyPrime2).toHex(),f=a.util.createBuffer(y.privateKeyExponent1).toHex(),h=a.util.createBuffer(y.privateKeyExponent2).toHex(),d=a.util.createBuffer(y.privateKeyCoefficient).toHex(),c.setRsaPrivateKey(new n(t,16),new n(r,16),new n(i,16),new n(o,16),new n(u,16),new n(f,16),new n(h,16),new n(d,16))},c.privateKeyToAsn1=c.privateKeyToRSAPrivateKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,s.integerToDer(0).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.n)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.e)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.d)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.p)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.q)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.dP)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.dQ)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.qInv))])},c.publicKeyFromAsn1=function(e){var t={},r=[];if(s.validate(e,h,t,r)){var i,o=s.derToOid(t.publicKeyOid);if(o!==c.oids.rsaEncryption)throw(i=new Error("Cannot read public key. Unknown OID.")).oid=o,i;e=t.rsaPublicKey}if(r=[],!s.validate(e,f,t,r))throw(i=new Error("Cannot read public key. ASN.1 object does not contain an RSAPublicKey.")).errors=r,i;var u=a.util.createBuffer(t.publicKeyModulus).toHex(),l=a.util.createBuffer(t.publicKeyExponent).toHex();return c.setRsaPublicKey(new n(u,16),new n(l,16))},c.publicKeyToAsn1=c.publicKeyToSubjectPublicKeyInfo=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.OID,!1,s.oidToDer(c.oids.rsaEncryption).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,"")]),s.create(s.Class.UNIVERSAL,s.Type.BITSTRING,!1,[c.publicKeyToRSAPublicKey(e)])])},c.publicKeyToRSAPublicKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.n)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.e))])}},function(e,t,r){var a,n=r(0);e.exports=n.jsbn=n.jsbn||{};function i(e,t,r){this.data=[],null!=e&&("number"==typeof e?this.fromNumber(e,t,r):null==t&&"string"!=typeof e?this.fromString(e,256):this.fromString(e,t))}function s(){return new i(null)}function o(e,t,r,a,n,i){for(var s=16383&t,o=t>>14;--i>=0;){var c=16383&this.data[e],u=this.data[e++]>>14,l=o*c+u*s;n=((c=s*c+((16383&l)<<14)+r.data[a]+n)>>28)+(l>>14)+o*u,r.data[a++]=268435455&c}return n}n.jsbn.BigInteger=i,"undefined"==typeof navigator?(i.prototype.am=o,a=28):"Microsoft Internet Explorer"==navigator.appName?(i.prototype.am=function(e,t,r,a,n,i){for(var s=32767&t,o=t>>15;--i>=0;){var c=32767&this.data[e],u=this.data[e++]>>15,l=o*c+u*s;n=((c=s*c+((32767&l)<<15)+r.data[a]+(1073741823&n))>>>30)+(l>>>15)+o*u+(n>>>30),r.data[a++]=1073741823&c}return n},a=30):"Netscape"!=navigator.appName?(i.prototype.am=function(e,t,r,a,n,i){for(;--i>=0;){var s=t*this.data[e++]+r.data[a]+n;n=Math.floor(s/67108864),r.data[a++]=67108863&s}return n},a=26):(i.prototype.am=o,a=28),i.prototype.DB=a,i.prototype.DM=(1<<a)-1,i.prototype.DV=1<<a;i.prototype.FV=Math.pow(2,52),i.prototype.F1=52-a,i.prototype.F2=2*a-52;var c,u,l=new Array;for(c="0".charCodeAt(0),u=0;u<=9;++u)l[c++]=u;for(c="a".charCodeAt(0),u=10;u<36;++u)l[c++]=u;for(c="A".charCodeAt(0),u=10;u<36;++u)l[c++]=u;function p(e){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(e)}function f(e,t){var r=l[e.charCodeAt(t)];return null==r?-1:r}function h(e){var t=s();return t.fromInt(e),t}function d(e){var t,r=1;return 0!=(t=e>>>16)&&(e=t,r+=16),0!=(t=e>>8)&&(e=t,r+=8),0!=(t=e>>4)&&(e=t,r+=4),0!=(t=e>>2)&&(e=t,r+=2),0!=(t=e>>1)&&(e=t,r+=1),r}function y(e){this.m=e}function g(e){this.m=e,this.mp=e.invDigit(),this.mpl=32767&this.mp,this.mph=this.mp>>15,this.um=(1<<e.DB-15)-1,this.mt2=2*e.t}function v(e,t){return e&t}function m(e,t){return e|t}function C(e,t){return e^t}function E(e,t){return e&~t}function S(e){if(0==e)return-1;var t=0;return 0==(65535&e)&&(e>>=16,t+=16),0==(255&e)&&(e>>=8,t+=8),0==(15&e)&&(e>>=4,t+=4),0==(3&e)&&(e>>=2,t+=2),0==(1&e)&&++t,t}function T(e){for(var t=0;0!=e;)e&=e-1,++t;return t}function I(){}function A(e){return e}function B(e){this.r2=s(),this.q3=s(),i.ONE.dlShiftTo(2*e.t,this.r2),this.mu=this.r2.divide(e),this.m=e}y.prototype.convert=function(e){return e.s<0||e.compareTo(this.m)>=0?e.mod(this.m):e},y.prototype.revert=function(e){return e},y.prototype.reduce=function(e){e.divRemTo(this.m,null,e)},y.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},y.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},g.prototype.convert=function(e){var t=s();return e.abs().dlShiftTo(this.m.t,t),t.divRemTo(this.m,null,t),e.s<0&&t.compareTo(i.ZERO)>0&&this.m.subTo(t,t),t},g.prototype.revert=function(e){var t=s();return e.copyTo(t),this.reduce(t),t},g.prototype.reduce=function(e){for(;e.t<=this.mt2;)e.data[e.t++]=0;for(var t=0;t<this.m.t;++t){var r=32767&e.data[t],a=r*this.mpl+((r*this.mph+(e.data[t]>>15)*this.mpl&this.um)<<15)&e.DM;for(r=t+this.m.t,e.data[r]+=this.m.am(0,a,e,t,0,this.m.t);e.data[r]>=e.DV;)e.data[r]-=e.DV,e.data[++r]++}e.clamp(),e.drShiftTo(this.m.t,e),e.compareTo(this.m)>=0&&e.subTo(this.m,e)},g.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},g.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},i.prototype.copyTo=function(e){for(var t=this.t-1;t>=0;--t)e.data[t]=this.data[t];e.t=this.t,e.s=this.s},i.prototype.fromInt=function(e){this.t=1,this.s=e<0?-1:0,e>0?this.data[0]=e:e<-1?this.data[0]=e+this.DV:this.t=0},i.prototype.fromString=function(e,t){var r;if(16==t)r=4;else if(8==t)r=3;else if(256==t)r=8;else if(2==t)r=1;else if(32==t)r=5;else{if(4!=t)return void this.fromRadix(e,t);r=2}this.t=0,this.s=0;for(var a=e.length,n=!1,s=0;--a>=0;){var o=8==r?255&e[a]:f(e,a);o<0?"-"==e.charAt(a)&&(n=!0):(n=!1,0==s?this.data[this.t++]=o:s+r>this.DB?(this.data[this.t-1]|=(o&(1<<this.DB-s)-1)<<s,this.data[this.t++]=o>>this.DB-s):this.data[this.t-1]|=o<<s,(s+=r)>=this.DB&&(s-=this.DB))}8==r&&0!=(128&e[0])&&(this.s=-1,s>0&&(this.data[this.t-1]|=(1<<this.DB-s)-1<<s)),this.clamp(),n&&i.ZERO.subTo(this,this)},i.prototype.clamp=function(){for(var e=this.s&this.DM;this.t>0&&this.data[this.t-1]==e;)--this.t},i.prototype.dlShiftTo=function(e,t){var r;for(r=this.t-1;r>=0;--r)t.data[r+e]=this.data[r];for(r=e-1;r>=0;--r)t.data[r]=0;t.t=this.t+e,t.s=this.s},i.prototype.drShiftTo=function(e,t){for(var r=e;r<this.t;++r)t.data[r-e]=this.data[r];t.t=Math.max(this.t-e,0),t.s=this.s},i.prototype.lShiftTo=function(e,t){var r,a=e%this.DB,n=this.DB-a,i=(1<<n)-1,s=Math.floor(e/this.DB),o=this.s<<a&this.DM;for(r=this.t-1;r>=0;--r)t.data[r+s+1]=this.data[r]>>n|o,o=(this.data[r]&i)<<a;for(r=s-1;r>=0;--r)t.data[r]=0;t.data[s]=o,t.t=this.t+s+1,t.s=this.s,t.clamp()},i.prototype.rShiftTo=function(e,t){t.s=this.s;var r=Math.floor(e/this.DB);if(r>=this.t)t.t=0;else{var a=e%this.DB,n=this.DB-a,i=(1<<a)-1;t.data[0]=this.data[r]>>a;for(var s=r+1;s<this.t;++s)t.data[s-r-1]|=(this.data[s]&i)<<n,t.data[s-r]=this.data[s]>>a;a>0&&(t.data[this.t-r-1]|=(this.s&i)<<n),t.t=this.t-r,t.clamp()}},i.prototype.subTo=function(e,t){for(var r=0,a=0,n=Math.min(e.t,this.t);r<n;)a+=this.data[r]-e.data[r],t.data[r++]=a&this.DM,a>>=this.DB;if(e.t<this.t){for(a-=e.s;r<this.t;)a+=this.data[r],t.data[r++]=a&this.DM,a>>=this.DB;a+=this.s}else{for(a+=this.s;r<e.t;)a-=e.data[r],t.data[r++]=a&this.DM,a>>=this.DB;a-=e.s}t.s=a<0?-1:0,a<-1?t.data[r++]=this.DV+a:a>0&&(t.data[r++]=a),t.t=r,t.clamp()},i.prototype.multiplyTo=function(e,t){var r=this.abs(),a=e.abs(),n=r.t;for(t.t=n+a.t;--n>=0;)t.data[n]=0;for(n=0;n<a.t;++n)t.data[n+r.t]=r.am(0,a.data[n],t,n,0,r.t);t.s=0,t.clamp(),this.s!=e.s&&i.ZERO.subTo(t,t)},i.prototype.squareTo=function(e){for(var t=this.abs(),r=e.t=2*t.t;--r>=0;)e.data[r]=0;for(r=0;r<t.t-1;++r){var a=t.am(r,t.data[r],e,2*r,0,1);(e.data[r+t.t]+=t.am(r+1,2*t.data[r],e,2*r+1,a,t.t-r-1))>=t.DV&&(e.data[r+t.t]-=t.DV,e.data[r+t.t+1]=1)}e.t>0&&(e.data[e.t-1]+=t.am(r,t.data[r],e,2*r,0,1)),e.s=0,e.clamp()},i.prototype.divRemTo=function(e,t,r){var a=e.abs();if(!(a.t<=0)){var n=this.abs();if(n.t<a.t)return null!=t&&t.fromInt(0),void(null!=r&&this.copyTo(r));null==r&&(r=s());var o=s(),c=this.s,u=e.s,l=this.DB-d(a.data[a.t-1]);l>0?(a.lShiftTo(l,o),n.lShiftTo(l,r)):(a.copyTo(o),n.copyTo(r));var p=o.t,f=o.data[p-1];if(0!=f){var h=f*(1<<this.F1)+(p>1?o.data[p-2]>>this.F2:0),y=this.FV/h,g=(1<<this.F1)/h,v=1<<this.F2,m=r.t,C=m-p,E=null==t?s():t;for(o.dlShiftTo(C,E),r.compareTo(E)>=0&&(r.data[r.t++]=1,r.subTo(E,r)),i.ONE.dlShiftTo(p,E),E.subTo(o,o);o.t<p;)o.data[o.t++]=0;for(;--C>=0;){var S=r.data[--m]==f?this.DM:Math.floor(r.data[m]*y+(r.data[m-1]+v)*g);if((r.data[m]+=o.am(0,S,r,C,0,p))<S)for(o.dlShiftTo(C,E),r.subTo(E,r);r.data[m]<--S;)r.subTo(E,r)}null!=t&&(r.drShiftTo(p,t),c!=u&&i.ZERO.subTo(t,t)),r.t=p,r.clamp(),l>0&&r.rShiftTo(l,r),c<0&&i.ZERO.subTo(r,r)}}},i.prototype.invDigit=function(){if(this.t<1)return 0;var e=this.data[0];if(0==(1&e))return 0;var t=3&e;return(t=(t=(t=(t=t*(2-(15&e)*t)&15)*(2-(255&e)*t)&255)*(2-((65535&e)*t&65535))&65535)*(2-e*t%this.DV)%this.DV)>0?this.DV-t:-t},i.prototype.isEven=function(){return 0==(this.t>0?1&this.data[0]:this.s)},i.prototype.exp=function(e,t){if(e>4294967295||e<1)return i.ONE;var r=s(),a=s(),n=t.convert(this),o=d(e)-1;for(n.copyTo(r);--o>=0;)if(t.sqrTo(r,a),(e&1<<o)>0)t.mulTo(a,n,r);else{var c=r;r=a,a=c}return t.revert(r)},i.prototype.toString=function(e){if(this.s<0)return"-"+this.negate().toString(e);var t;if(16==e)t=4;else if(8==e)t=3;else if(2==e)t=1;else if(32==e)t=5;else{if(4!=e)return this.toRadix(e);t=2}var r,a=(1<<t)-1,n=!1,i="",s=this.t,o=this.DB-s*this.DB%t;if(s-- >0)for(o<this.DB&&(r=this.data[s]>>o)>0&&(n=!0,i=p(r));s>=0;)o<t?(r=(this.data[s]&(1<<o)-1)<<t-o,r|=this.data[--s]>>(o+=this.DB-t)):(r=this.data[s]>>(o-=t)&a,o<=0&&(o+=this.DB,--s)),r>0&&(n=!0),n&&(i+=p(r));return n?i:"0"},i.prototype.negate=function(){var e=s();return i.ZERO.subTo(this,e),e},i.prototype.abs=function(){return this.s<0?this.negate():this},i.prototype.compareTo=function(e){var t=this.s-e.s;if(0!=t)return t;var r=this.t;if(0!=(t=r-e.t))return this.s<0?-t:t;for(;--r>=0;)if(0!=(t=this.data[r]-e.data[r]))return t;return 0},i.prototype.bitLength=function(){return this.t<=0?0:this.DB*(this.t-1)+d(this.data[this.t-1]^this.s&this.DM)},i.prototype.mod=function(e){var t=s();return this.abs().divRemTo(e,null,t),this.s<0&&t.compareTo(i.ZERO)>0&&e.subTo(t,t),t},i.prototype.modPowInt=function(e,t){var r;return r=e<256||t.isEven()?new y(t):new g(t),this.exp(e,r)},i.ZERO=h(0),i.ONE=h(1),I.prototype.convert=A,I.prototype.revert=A,I.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r)},I.prototype.sqrTo=function(e,t){e.squareTo(t)},B.prototype.convert=function(e){if(e.s<0||e.t>2*this.m.t)return e.mod(this.m);if(e.compareTo(this.m)<0)return e;var t=s();return e.copyTo(t),this.reduce(t),t},B.prototype.revert=function(e){return e},B.prototype.reduce=function(e){for(e.drShiftTo(this.m.t-1,this.r2),e.t>this.m.t+1&&(e.t=this.m.t+1,e.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);e.compareTo(this.r2)<0;)e.dAddOffset(1,this.m.t+1);for(e.subTo(this.r2,e);e.compareTo(this.m)>=0;)e.subTo(this.m,e)},B.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},B.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)};var b=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],N=(1<<26)/b[b.length-1];i.prototype.chunkSize=function(e){return Math.floor(Math.LN2*this.DB/Math.log(e))},i.prototype.toRadix=function(e){if(null==e&&(e=10),0==this.signum()||e<2||e>36)return"0";var t=this.chunkSize(e),r=Math.pow(e,t),a=h(r),n=s(),i=s(),o="";for(this.divRemTo(a,n,i);n.signum()>0;)o=(r+i.intValue()).toString(e).substr(1)+o,n.divRemTo(a,n,i);return i.intValue().toString(e)+o},i.prototype.fromRadix=function(e,t){this.fromInt(0),null==t&&(t=10);for(var r=this.chunkSize(t),a=Math.pow(t,r),n=!1,s=0,o=0,c=0;c<e.length;++c){var u=f(e,c);u<0?"-"==e.charAt(c)&&0==this.signum()&&(n=!0):(o=t*o+u,++s>=r&&(this.dMultiply(a),this.dAddOffset(o,0),s=0,o=0))}s>0&&(this.dMultiply(Math.pow(t,s)),this.dAddOffset(o,0)),n&&i.ZERO.subTo(this,this)},i.prototype.fromNumber=function(e,t,r){if("number"==typeof t)if(e<2)this.fromInt(1);else for(this.fromNumber(e,r),this.testBit(e-1)||this.bitwiseTo(i.ONE.shiftLeft(e-1),m,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(t);)this.dAddOffset(2,0),this.bitLength()>e&&this.subTo(i.ONE.shiftLeft(e-1),this);else{var a=new Array,n=7&e;a.length=1+(e>>3),t.nextBytes(a),n>0?a[0]&=(1<<n)-1:a[0]=0,this.fromString(a,256)}},i.prototype.bitwiseTo=function(e,t,r){var a,n,i=Math.min(e.t,this.t);for(a=0;a<i;++a)r.data[a]=t(this.data[a],e.data[a]);if(e.t<this.t){for(n=e.s&this.DM,a=i;a<this.t;++a)r.data[a]=t(this.data[a],n);r.t=this.t}else{for(n=this.s&this.DM,a=i;a<e.t;++a)r.data[a]=t(n,e.data[a]);r.t=e.t}r.s=t(this.s,e.s),r.clamp()},i.prototype.changeBit=function(e,t){var r=i.ONE.shiftLeft(e);return this.bitwiseTo(r,t,r),r},i.prototype.addTo=function(e,t){for(var r=0,a=0,n=Math.min(e.t,this.t);r<n;)a+=this.data[r]+e.data[r],t.data[r++]=a&this.DM,a>>=this.DB;if(e.t<this.t){for(a+=e.s;r<this.t;)a+=this.data[r],t.data[r++]=a&this.DM,a>>=this.DB;a+=this.s}else{for(a+=this.s;r<e.t;)a+=e.data[r],t.data[r++]=a&this.DM,a>>=this.DB;a+=e.s}t.s=a<0?-1:0,a>0?t.data[r++]=a:a<-1&&(t.data[r++]=this.DV+a),t.t=r,t.clamp()},i.prototype.dMultiply=function(e){this.data[this.t]=this.am(0,e-1,this,0,0,this.t),++this.t,this.clamp()},i.prototype.dAddOffset=function(e,t){if(0!=e){for(;this.t<=t;)this.data[this.t++]=0;for(this.data[t]+=e;this.data[t]>=this.DV;)this.data[t]-=this.DV,++t>=this.t&&(this.data[this.t++]=0),++this.data[t]}},i.prototype.multiplyLowerTo=function(e,t,r){var a,n=Math.min(this.t+e.t,t);for(r.s=0,r.t=n;n>0;)r.data[--n]=0;for(a=r.t-this.t;n<a;++n)r.data[n+this.t]=this.am(0,e.data[n],r,n,0,this.t);for(a=Math.min(e.t,t);n<a;++n)this.am(0,e.data[n],r,n,0,t-n);r.clamp()},i.prototype.multiplyUpperTo=function(e,t,r){--t;var a=r.t=this.t+e.t-t;for(r.s=0;--a>=0;)r.data[a]=0;for(a=Math.max(t-this.t,0);a<e.t;++a)r.data[this.t+a-t]=this.am(t-a,e.data[a],r,0,0,this.t+a-t);r.clamp(),r.drShiftTo(1,r)},i.prototype.modInt=function(e){if(e<=0)return 0;var t=this.DV%e,r=this.s<0?e-1:0;if(this.t>0)if(0==t)r=this.data[0]%e;else for(var a=this.t-1;a>=0;--a)r=(t*r+this.data[a])%e;return r},i.prototype.millerRabin=function(e){var t=this.subtract(i.ONE),r=t.getLowestSetBit();if(r<=0)return!1;for(var a,n=t.shiftRight(r),s={nextBytes:function(e){for(var t=0;t<e.length;++t)e[t]=Math.floor(256*Math.random())}},o=0;o<e;++o){do{a=new i(this.bitLength(),s)}while(a.compareTo(i.ONE)<=0||a.compareTo(t)>=0);var c=a.modPow(n,this);if(0!=c.compareTo(i.ONE)&&0!=c.compareTo(t)){for(var u=1;u++<r&&0!=c.compareTo(t);)if(0==(c=c.modPowInt(2,this)).compareTo(i.ONE))return!1;if(0!=c.compareTo(t))return!1}}return!0},i.prototype.clone=function(){var e=s();return this.copyTo(e),e},i.prototype.intValue=function(){if(this.s<0){if(1==this.t)return this.data[0]-this.DV;if(0==this.t)return-1}else{if(1==this.t)return this.data[0];if(0==this.t)return 0}return(this.data[1]&(1<<32-this.DB)-1)<<this.DB|this.data[0]},i.prototype.byteValue=function(){return 0==this.t?this.s:this.data[0]<<24>>24},i.prototype.shortValue=function(){return 0==this.t?this.s:this.data[0]<<16>>16},i.prototype.signum=function(){return this.s<0?-1:this.t<=0||1==this.t&&this.data[0]<=0?0:1},i.prototype.toByteArray=function(){var e=this.t,t=new Array;t[0]=this.s;var r,a=this.DB-e*this.DB%8,n=0;if(e-- >0)for(a<this.DB&&(r=this.data[e]>>a)!=(this.s&this.DM)>>a&&(t[n++]=r|this.s<<this.DB-a);e>=0;)a<8?(r=(this.data[e]&(1<<a)-1)<<8-a,r|=this.data[--e]>>(a+=this.DB-8)):(r=this.data[e]>>(a-=8)&255,a<=0&&(a+=this.DB,--e)),0!=(128&r)&&(r|=-256),0==n&&(128&this.s)!=(128&r)&&++n,(n>0||r!=this.s)&&(t[n++]=r);return t},i.prototype.equals=function(e){return 0==this.compareTo(e)},i.prototype.min=function(e){return this.compareTo(e)<0?this:e},i.prototype.max=function(e){return this.compareTo(e)>0?this:e},i.prototype.and=function(e){var t=s();return this.bitwiseTo(e,v,t),t},i.prototype.or=function(e){var t=s();return this.bitwiseTo(e,m,t),t},i.prototype.xor=function(e){var t=s();return this.bitwiseTo(e,C,t),t},i.prototype.andNot=function(e){var t=s();return this.bitwiseTo(e,E,t),t},i.prototype.not=function(){for(var e=s(),t=0;t<this.t;++t)e.data[t]=this.DM&~this.data[t];return e.t=this.t,e.s=~this.s,e},i.prototype.shiftLeft=function(e){var t=s();return e<0?this.rShiftTo(-e,t):this.lShiftTo(e,t),t},i.prototype.shiftRight=function(e){var t=s();return e<0?this.lShiftTo(-e,t):this.rShiftTo(e,t),t},i.prototype.getLowestSetBit=function(){for(var e=0;e<this.t;++e)if(0!=this.data[e])return e*this.DB+S(this.data[e]);return this.s<0?this.t*this.DB:-1},i.prototype.bitCount=function(){for(var e=0,t=this.s&this.DM,r=0;r<this.t;++r)e+=T(this.data[r]^t);return e},i.prototype.testBit=function(e){var t=Math.floor(e/this.DB);return t>=this.t?0!=this.s:0!=(this.data[t]&1<<e%this.DB)},i.prototype.setBit=function(e){return this.changeBit(e,m)},i.prototype.clearBit=function(e){return this.changeBit(e,E)},i.prototype.flipBit=function(e){return this.changeBit(e,C)},i.prototype.add=function(e){var t=s();return this.addTo(e,t),t},i.prototype.subtract=function(e){var t=s();return this.subTo(e,t),t},i.prototype.multiply=function(e){var t=s();return this.multiplyTo(e,t),t},i.prototype.divide=function(e){var t=s();return this.divRemTo(e,t,null),t},i.prototype.remainder=function(e){var t=s();return this.divRemTo(e,null,t),t},i.prototype.divideAndRemainder=function(e){var t=s(),r=s();return this.divRemTo(e,t,r),new Array(t,r)},i.prototype.modPow=function(e,t){var r,a,n=e.bitLength(),i=h(1);if(n<=0)return i;r=n<18?1:n<48?3:n<144?4:n<768?5:6,a=n<8?new y(t):t.isEven()?new B(t):new g(t);var o=new Array,c=3,u=r-1,l=(1<<r)-1;if(o[1]=a.convert(this),r>1){var p=s();for(a.sqrTo(o[1],p);c<=l;)o[c]=s(),a.mulTo(p,o[c-2],o[c]),c+=2}var f,v,m=e.t-1,C=!0,E=s();for(n=d(e.data[m])-1;m>=0;){for(n>=u?f=e.data[m]>>n-u&l:(f=(e.data[m]&(1<<n+1)-1)<<u-n,m>0&&(f|=e.data[m-1]>>this.DB+n-u)),c=r;0==(1&f);)f>>=1,--c;if((n-=c)<0&&(n+=this.DB,--m),C)o[f].copyTo(i),C=!1;else{for(;c>1;)a.sqrTo(i,E),a.sqrTo(E,i),c-=2;c>0?a.sqrTo(i,E):(v=i,i=E,E=v),a.mulTo(E,o[f],i)}for(;m>=0&&0==(e.data[m]&1<<n);)a.sqrTo(i,E),v=i,i=E,E=v,--n<0&&(n=this.DB-1,--m)}return a.revert(i)},i.prototype.modInverse=function(e){if(0==this.signum())return i.ZERO;var t=e.isEven();if(this.isEven()&&t||0==e.signum())return i.ZERO;for(var r=e.clone(),a=this.clone(),n=h(1),s=h(0),o=h(0),c=h(1);0!=r.signum();){for(;r.isEven();)r.rShiftTo(1,r),t?(n.isEven()&&s.isEven()||(n.addTo(this,n),s.subTo(e,s)),n.rShiftTo(1,n)):s.isEven()||s.subTo(e,s),s.rShiftTo(1,s);for(;a.isEven();)a.rShiftTo(1,a),t?(o.isEven()&&c.isEven()||(o.addTo(this,o),c.subTo(e,c)),o.rShiftTo(1,o)):c.isEven()||c.subTo(e,c),c.rShiftTo(1,c);r.compareTo(a)>=0?(r.subTo(a,r),t&&n.subTo(o,n),s.subTo(c,s)):(a.subTo(r,a),t&&o.subTo(n,o),c.subTo(s,c))}return 0!=a.compareTo(i.ONE)?i.ZERO:c.compareTo(e)>=0?c.subtract(e):c.signum()<0?(c.addTo(e,c),c.signum()<0?c.add(e):c):c},i.prototype.pow=function(e){return this.exp(e,new I)},i.prototype.gcd=function(e){var t=this.s<0?this.negate():this.clone(),r=e.s<0?e.negate():e.clone();if(t.compareTo(r)<0){var a=t;t=r,r=a}var n=t.getLowestSetBit(),i=r.getLowestSetBit();if(i<0)return t;for(n<i&&(i=n),i>0&&(t.rShiftTo(i,t),r.rShiftTo(i,r));t.signum()>0;)(n=t.getLowestSetBit())>0&&t.rShiftTo(n,t),(n=r.getLowestSetBit())>0&&r.rShiftTo(n,r),t.compareTo(r)>=0?(t.subTo(r,t),t.rShiftTo(1,t)):(r.subTo(t,r),r.rShiftTo(1,r));return i>0&&r.lShiftTo(i,r),r},i.prototype.isProbablePrime=function(e){var t,r=this.abs();if(1==r.t&&r.data[0]<=b[b.length-1]){for(t=0;t<b.length;++t)if(r.data[0]==b[t])return!0;return!1}if(r.isEven())return!1;for(t=1;t<b.length;){for(var a=b[t],n=t+1;n<b.length&&a<N;)a*=b[n++];for(a=r.modInt(a);t<n;)if(a%b[t++]==0)return!1}return r.millerRabin(e)},i.prototype.square=function(){var e=s();return this.squareTo(e),e}},function(e,t,r){var a=r(0);r(1),e.exports=a.cipher=a.cipher||{},a.cipher.algorithms=a.cipher.algorithms||{},a.cipher.createCipher=function(e,t){var r=e;if("string"==typeof r&&(r=a.cipher.getAlgorithm(r))&&(r=r()),!r)throw new Error("Unsupported algorithm: "+e);return new a.cipher.BlockCipher({algorithm:r,key:t,decrypt:!1})},a.cipher.createDecipher=function(e,t){var r=e;if("string"==typeof r&&(r=a.cipher.getAlgorithm(r))&&(r=r()),!r)throw new Error("Unsupported algorithm: "+e);return new a.cipher.BlockCipher({algorithm:r,key:t,decrypt:!0})},a.cipher.registerAlgorithm=function(e,t){e=e.toUpperCase(),a.cipher.algorithms[e]=t},a.cipher.getAlgorithm=function(e){return(e=e.toUpperCase())in a.cipher.algorithms?a.cipher.algorithms[e]:null};var n=a.cipher.BlockCipher=function(e){this.algorithm=e.algorithm,this.mode=this.algorithm.mode,this.blockSize=this.mode.blockSize,this._finish=!1,this._input=null,this.output=null,this._op=e.decrypt?this.mode.decrypt:this.mode.encrypt,this._decrypt=e.decrypt,this.algorithm.initialize(e)};n.prototype.start=function(e){e=e||{};var t={};for(var r in e)t[r]=e[r];t.decrypt=this._decrypt,this._finish=!1,this._input=a.util.createBuffer(),this.output=e.output||a.util.createBuffer(),this.mode.start(t)},n.prototype.update=function(e){for(e&&this._input.putBuffer(e);!this._op.call(this.mode,this._input,this.output,this._finish)&&!this._finish;);this._input.compact()},n.prototype.finish=function(e){!e||"ECB"!==this.mode.name&&"CBC"!==this.mode.name||(this.mode.pad=function(t){return e(this.blockSize,t,!1)},this.mode.unpad=function(t){return e(this.blockSize,t,!0)});var t={};return t.decrypt=this._decrypt,t.overflow=this._input.length()%this.blockSize,!(!this._decrypt&&this.mode.pad&&!this.mode.pad(this._input,t))&&(this._finish=!0,this.update(),!(this._decrypt&&this.mode.unpad&&!this.mode.unpad(this.output,t))&&!(this.mode.afterFinish&&!this.mode.afterFinish(this.output,t)))}},function(e,t,r){var a=r(0);r(4),r(1);var n=e.exports=a.md5=a.md5||{};a.md.md5=a.md.algorithms.md5=n,n.create=function(){u||function(){i=String.fromCharCode(128),i+=a.util.fillString(String.fromCharCode(0),64),s=[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],o=[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],c=new Array(64);for(var e=0;e<64;++e)c[e]=Math.floor(4294967296*Math.abs(Math.sin(e+1)));u=!0}();var e=null,t=a.util.createBuffer(),r=new Array(16),n={algorithm:"md5",blockLength:64,digestLength:16,messageLength:0,fullMessageLength:null,messageLengthSize:8,start:function(){n.messageLength=0,n.fullMessageLength=n.messageLength64=[];for(var r=n.messageLengthSize/4,i=0;i<r;++i)n.fullMessageLength.push(0);return t=a.util.createBuffer(),e={h0:1732584193,h1:4023233417,h2:2562383102,h3:271733878},n}};return n.start(),n.update=function(i,s){"utf8"===s&&(i=a.util.encodeUtf8(i));var o=i.length;n.messageLength+=o,o=[o/4294967296>>>0,o>>>0];for(var c=n.fullMessageLength.length-1;c>=0;--c)n.fullMessageLength[c]+=o[1],o[1]=o[0]+(n.fullMessageLength[c]/4294967296>>>0),n.fullMessageLength[c]=n.fullMessageLength[c]>>>0,o[0]=o[1]/4294967296>>>0;return t.putBytes(i),l(e,r,t),(t.read>2048||0===t.length())&&t.compact(),n},n.digest=function(){var s=a.util.createBuffer();s.putBytes(t.bytes());var o=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize&n.blockLength-1;s.putBytes(i.substr(0,n.blockLength-o));for(var c,u=0,p=n.fullMessageLength.length-1;p>=0;--p)u=(c=8*n.fullMessageLength[p]+u)/4294967296>>>0,s.putInt32Le(c>>>0);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3};l(f,r,s);var h=a.util.createBuffer();return h.putInt32Le(f.h0),h.putInt32Le(f.h1),h.putInt32Le(f.h2),h.putInt32Le(f.h3),h},n};var i=null,s=null,o=null,c=null,u=!1;function l(e,t,r){for(var a,n,i,u,l,p,f,h=r.length();h>=64;){for(n=e.h0,i=e.h1,u=e.h2,l=e.h3,f=0;f<16;++f)t[f]=r.getInt32Le(),a=n+(l^i&(u^l))+c[f]+t[f],n=l,l=u,u=i,i+=a<<(p=o[f])|a>>>32-p;for(;f<32;++f)a=n+(u^l&(i^u))+c[f]+t[s[f]],n=l,l=u,u=i,i+=a<<(p=o[f])|a>>>32-p;for(;f<48;++f)a=n+(i^u^l)+c[f]+t[s[f]],n=l,l=u,u=i,i+=a<<(p=o[f])|a>>>32-p;for(;f<64;++f)a=n+(u^(i|~l))+c[f]+t[s[f]],n=l,l=u,u=i,i+=a<<(p=o[f])|a>>>32-p;e.h0=e.h0+n|0,e.h1=e.h1+i|0,e.h2=e.h2+u|0,e.h3=e.h3+l|0,h-=64}}},function(e,t,r){var a=r(0);r(8),r(4),r(1);var n,i=a.pkcs5=a.pkcs5||{};a.util.isNodejs&&!a.options.usePureJavaScript&&(n=r(16)),e.exports=a.pbkdf2=i.pbkdf2=function(e,t,r,i,s,o){if("function"==typeof s&&(o=s,s=null),a.util.isNodejs&&!a.options.usePureJavaScript&&n.pbkdf2&&(null===s||"object"!=typeof s)&&(n.pbkdf2Sync.length>4||!s||"sha1"===s))return"string"!=typeof s&&(s="sha1"),e=Buffer.from(e,"binary"),t=Buffer.from(t,"binary"),o?4===n.pbkdf2Sync.length?n.pbkdf2(e,t,r,i,(function(e,t){if(e)return o(e);o(null,t.toString("binary"))})):n.pbkdf2(e,t,r,i,s,(function(e,t){if(e)return o(e);o(null,t.toString("binary"))})):4===n.pbkdf2Sync.length?n.pbkdf2Sync(e,t,r,i).toString("binary"):n.pbkdf2Sync(e,t,r,i,s).toString("binary");if(null==s&&(s="sha1"),"string"==typeof s){if(!(s in a.md.algorithms))throw new Error("Unknown hash algorithm: "+s);s=a.md[s].create()}var c=s.digestLength;if(i>4294967295*c){var u=new Error("Derived key is too long.");if(o)return o(u);throw u}var l=Math.ceil(i/c),p=i-(l-1)*c,f=a.hmac.create();f.start(s,e);var h,d,y,g="";if(!o){for(var v=1;v<=l;++v){f.start(null,null),f.update(t),f.update(a.util.int32ToBytes(v)),h=y=f.digest().getBytes();for(var m=2;m<=r;++m)f.start(null,null),f.update(y),d=f.digest().getBytes(),h=a.util.xorBytes(h,d,c),y=d;g+=v<l?h:h.substr(0,p)}return g}v=1;function C(){if(v>l)return o(null,g);f.start(null,null),f.update(t),f.update(a.util.int32ToBytes(v)),h=y=f.digest().getBytes(),m=2,E()}function E(){if(m<=r)return f.start(null,null),f.update(y),d=f.digest().getBytes(),h=a.util.xorBytes(h,d,c),y=d,++m,a.util.setImmediate(E);g+=v<l?h:h.substr(0,p),++v,C()}C()}},function(e,t){},function(e,t,r){var a=r(0);r(5),r(3),r(10),r(4),r(37),r(6),r(7),r(18),r(11),r(1);var n=a.asn1,i=e.exports=a.pki=a.pki||{},s=i.oids,o={};o.CN=s.commonName,o.commonName="CN",o.C=s.countryName,o.countryName="C",o.L=s.localityName,o.localityName="L",o.ST=s.stateOrProvinceName,o.stateOrProvinceName="ST",o.O=s.organizationName,o.organizationName="O",o.OU=s.organizationalUnitName,o.organizationalUnitName="OU",o.E=s.emailAddress,o.emailAddress="E";var c=a.pki.rsa.publicKeyValidator,u={name:"Certificate",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"Certificate.TBSCertificate",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"tbsCertificate",value:[{name:"Certificate.TBSCertificate.version",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,value:[{name:"Certificate.TBSCertificate.version.integer",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"certVersion"}]},{name:"Certificate.TBSCertificate.serialNumber",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"certSerialNumber"},{name:"Certificate.TBSCertificate.signature",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"Certificate.TBSCertificate.signature.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"certinfoSignatureOid"},{name:"Certificate.TBSCertificate.signature.parameters",tagClass:n.Class.UNIVERSAL,optional:!0,captureAsn1:"certinfoSignatureParams"}]},{name:"Certificate.TBSCertificate.issuer",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"certIssuer"},{name:"Certificate.TBSCertificate.validity",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"Certificate.TBSCertificate.validity.notBefore (utc)",tagClass:n.Class.UNIVERSAL,type:n.Type.UTCTIME,constructed:!1,optional:!0,capture:"certValidity1UTCTime"},{name:"Certificate.TBSCertificate.validity.notBefore (generalized)",tagClass:n.Class.UNIVERSAL,type:n.Type.GENERALIZEDTIME,constructed:!1,optional:!0,capture:"certValidity2GeneralizedTime"},{name:"Certificate.TBSCertificate.validity.notAfter (utc)",tagClass:n.Class.UNIVERSAL,type:n.Type.UTCTIME,constructed:!1,optional:!0,capture:"certValidity3UTCTime"},{name:"Certificate.TBSCertificate.validity.notAfter (generalized)",tagClass:n.Class.UNIVERSAL,type:n.Type.GENERALIZEDTIME,constructed:!1,optional:!0,capture:"certValidity4GeneralizedTime"}]},{name:"Certificate.TBSCertificate.subject",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"certSubject"},c,{name:"Certificate.TBSCertificate.issuerUniqueID",tagClass:n.Class.CONTEXT_SPECIFIC,type:1,constructed:!0,optional:!0,value:[{name:"Certificate.TBSCertificate.issuerUniqueID.id",tagClass:n.Class.UNIVERSAL,type:n.Type.BITSTRING,constructed:!1,captureBitStringValue:"certIssuerUniqueId"}]},{name:"Certificate.TBSCertificate.subjectUniqueID",tagClass:n.Class.CONTEXT_SPECIFIC,type:2,constructed:!0,optional:!0,value:[{name:"Certificate.TBSCertificate.subjectUniqueID.id",tagClass:n.Class.UNIVERSAL,type:n.Type.BITSTRING,constructed:!1,captureBitStringValue:"certSubjectUniqueId"}]},{name:"Certificate.TBSCertificate.extensions",tagClass:n.Class.CONTEXT_SPECIFIC,type:3,constructed:!0,captureAsn1:"certExtensions",optional:!0}]},{name:"Certificate.signatureAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"Certificate.signatureAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"certSignatureOid"},{name:"Certificate.TBSCertificate.signature.parameters",tagClass:n.Class.UNIVERSAL,optional:!0,captureAsn1:"certSignatureParams"}]},{name:"Certificate.signatureValue",tagClass:n.Class.UNIVERSAL,type:n.Type.BITSTRING,constructed:!1,captureBitStringValue:"certSignature"}]},l={name:"rsapss",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"rsapss.hashAlgorithm",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,value:[{name:"rsapss.hashAlgorithm.AlgorithmIdentifier",tagClass:n.Class.UNIVERSAL,type:n.Class.SEQUENCE,constructed:!0,optional:!0,value:[{name:"rsapss.hashAlgorithm.AlgorithmIdentifier.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"hashOid"}]}]},{name:"rsapss.maskGenAlgorithm",tagClass:n.Class.CONTEXT_SPECIFIC,type:1,constructed:!0,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier",tagClass:n.Class.UNIVERSAL,type:n.Class.SEQUENCE,constructed:!0,optional:!0,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"maskGenOid"},{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.params",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.params.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"maskGenHashOid"}]}]}]},{name:"rsapss.saltLength",tagClass:n.Class.CONTEXT_SPECIFIC,type:2,optional:!0,value:[{name:"rsapss.saltLength.saltLength",tagClass:n.Class.UNIVERSAL,type:n.Class.INTEGER,constructed:!1,capture:"saltLength"}]},{name:"rsapss.trailerField",tagClass:n.Class.CONTEXT_SPECIFIC,type:3,optional:!0,value:[{name:"rsapss.trailer.trailer",tagClass:n.Class.UNIVERSAL,type:n.Class.INTEGER,constructed:!1,capture:"trailer"}]}]},p={name:"CertificationRequestInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"certificationRequestInfo",value:[{name:"CertificationRequestInfo.integer",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"certificationRequestInfoVersion"},{name:"CertificationRequestInfo.subject",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"certificationRequestInfoSubject"},c,{name:"CertificationRequestInfo.attributes",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,capture:"certificationRequestInfoAttributes",value:[{name:"CertificationRequestInfo.attributes",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"CertificationRequestInfo.attributes.type",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1},{name:"CertificationRequestInfo.attributes.value",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,constructed:!0}]}]}]},f={name:"CertificationRequest",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"csr",value:[p,{name:"CertificationRequest.signatureAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"CertificationRequest.signatureAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"csrSignatureOid"},{name:"CertificationRequest.signatureAlgorithm.parameters",tagClass:n.Class.UNIVERSAL,optional:!0,captureAsn1:"csrSignatureParams"}]},{name:"CertificationRequest.signature",tagClass:n.Class.UNIVERSAL,type:n.Type.BITSTRING,constructed:!1,captureBitStringValue:"csrSignature"}]};function h(e,t){"string"==typeof t&&(t={shortName:t});for(var r,a=null,n=0;null===a&&n<e.attributes.length;++n)r=e.attributes[n],(t.type&&t.type===r.type||t.name&&t.name===r.name||t.shortName&&t.shortName===r.shortName)&&(a=r);return a}i.RDNAttributesAsArray=function(e,t){for(var r,a,i,c=[],u=0;u<e.value.length;++u){r=e.value[u];for(var l=0;l<r.value.length;++l)i={},a=r.value[l],i.type=n.derToOid(a.value[0].value),i.value=a.value[1].value,i.valueTagClass=a.value[1].type,i.type in s&&(i.name=s[i.type],i.name in o&&(i.shortName=o[i.name])),t&&(t.update(i.type),t.update(i.value)),c.push(i)}return c},i.CRIAttributesAsArray=function(e){for(var t=[],r=0;r<e.length;++r)for(var a=e[r],c=n.derToOid(a.value[0].value),u=a.value[1].value,l=0;l<u.length;++l){var p={};if(p.type=c,p.value=u[l].value,p.valueTagClass=u[l].type,p.type in s&&(p.name=s[p.type],p.name in o&&(p.shortName=o[p.name])),p.type===s.extensionRequest){p.extensions=[];for(var f=0;f<p.value.length;++f)p.extensions.push(i.certificateExtensionFromAsn1(p.value[f]))}t.push(p)}return t};var d=function(e,t,r){var a={};if(e!==s["RSASSA-PSS"])return a;r&&(a={hash:{algorithmOid:s.sha1},mgf:{algorithmOid:s.mgf1,hash:{algorithmOid:s.sha1}},saltLength:20});var i={},o=[];if(!n.validate(t,l,i,o)){var c=new Error("Cannot read RSASSA-PSS parameter block.");throw c.errors=o,c}return void 0!==i.hashOid&&(a.hash=a.hash||{},a.hash.algorithmOid=n.derToOid(i.hashOid)),void 0!==i.maskGenOid&&(a.mgf=a.mgf||{},a.mgf.algorithmOid=n.derToOid(i.maskGenOid),a.mgf.hash=a.mgf.hash||{},a.mgf.hash.algorithmOid=n.derToOid(i.maskGenHashOid)),void 0!==i.saltLength&&(a.saltLength=i.saltLength.charCodeAt(0)),a},y=function(e){switch(s[e.signatureOid]){case"sha1WithRSAEncryption":case"sha1WithRSASignature":return a.md.sha1.create();case"md5WithRSAEncryption":return a.md.md5.create();case"sha256WithRSAEncryption":return a.md.sha256.create();case"sha384WithRSAEncryption":return a.md.sha384.create();case"sha512WithRSAEncryption":return a.md.sha512.create();case"RSASSA-PSS":return a.md.sha256.create();default:var t=new Error("Could not compute "+e.type+" digest. Unknown signature OID.");throw t.signatureOid=e.signatureOid,t}},g=function(e){var t,r=e.certificate;switch(r.signatureOid){case s.sha1WithRSAEncryption:case s.sha1WithRSASignature:break;case s["RSASSA-PSS"]:var n,i,o;if(void 0===(n=s[r.signatureParameters.mgf.hash.algorithmOid])||void 0===a.md[n])throw(o=new Error("Unsupported MGF hash function.")).oid=r.signatureParameters.mgf.hash.algorithmOid,o.name=n,o;if(void 0===(i=s[r.signatureParameters.mgf.algorithmOid])||void 0===a.mgf[i])throw(o=new Error("Unsupported MGF function.")).oid=r.signatureParameters.mgf.algorithmOid,o.name=i,o;if(i=a.mgf[i].create(a.md[n].create()),void 0===(n=s[r.signatureParameters.hash.algorithmOid])||void 0===a.md[n])throw(o=new Error("Unsupported RSASSA-PSS hash function.")).oid=r.signatureParameters.hash.algorithmOid,o.name=n,o;t=a.pss.create(a.md[n].create(),i,r.signatureParameters.saltLength)}return r.publicKey.verify(e.md.digest().getBytes(),e.signature,t)};function v(e){for(var t,r,i=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]),s=e.attributes,o=0;o<s.length;++o){var c=(t=s[o]).value,u=n.Type.PRINTABLESTRING;"valueTagClass"in t&&(u=t.valueTagClass)===n.Type.UTF8&&(c=a.util.encodeUtf8(c)),r=n.create(n.Class.UNIVERSAL,n.Type.SET,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(t.type).getBytes()),n.create(n.Class.UNIVERSAL,u,!1,c)])]),i.value.push(r)}return i}function m(e){for(var t,r=0;r<e.length;++r){if(void 0===(t=e[r]).name&&(t.type&&t.type in i.oids?t.name=i.oids[t.type]:t.shortName&&t.shortName in o&&(t.name=i.oids[o[t.shortName]])),void 0===t.type){if(!t.name||!(t.name in i.oids))throw(c=new Error("Attribute type not specified.")).attribute=t,c;t.type=i.oids[t.name]}if(void 0===t.shortName&&t.name&&t.name in o&&(t.shortName=o[t.name]),t.type===s.extensionRequest&&(t.valueConstructed=!0,t.valueTagClass=n.Type.SEQUENCE,!t.value&&t.extensions)){t.value=[];for(var a=0;a<t.extensions.length;++a)t.value.push(i.certificateExtensionToAsn1(C(t.extensions[a])))}var c;if(void 0===t.value)throw(c=new Error("Attribute value not specified.")).attribute=t,c}}function C(e,t){if(t=t||{},void 0===e.name&&e.id&&e.id in i.oids&&(e.name=i.oids[e.id]),void 0===e.id){if(!e.name||!(e.name in i.oids))throw(S=new Error("Extension ID not specified.")).extension=e,S;e.id=i.oids[e.name]}if(void 0!==e.value)return e;if("keyUsage"===e.name){var r=0,o=0,c=0;e.digitalSignature&&(o|=128,r=7),e.nonRepudiation&&(o|=64,r=6),e.keyEncipherment&&(o|=32,r=5),e.dataEncipherment&&(o|=16,r=4),e.keyAgreement&&(o|=8,r=3),e.keyCertSign&&(o|=4,r=2),e.cRLSign&&(o|=2,r=1),e.encipherOnly&&(o|=1,r=0),e.decipherOnly&&(c|=128,r=7);var u=String.fromCharCode(r);0!==c?u+=String.fromCharCode(o)+String.fromCharCode(c):0!==o&&(u+=String.fromCharCode(o)),e.value=n.create(n.Class.UNIVERSAL,n.Type.BITSTRING,!1,u)}else if("basicConstraints"===e.name)e.value=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]),e.cA&&e.value.value.push(n.create(n.Class.UNIVERSAL,n.Type.BOOLEAN,!1,String.fromCharCode(255))),"pathLenConstraint"in e&&e.value.value.push(n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(e.pathLenConstraint).getBytes()));else if("extKeyUsage"===e.name){e.value=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);var l=e.value.value;for(var p in e)!0===e[p]&&(p in s?l.push(n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(s[p]).getBytes())):-1!==p.indexOf(".")&&l.push(n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(p).getBytes())))}else if("nsCertType"===e.name){r=0,o=0;e.client&&(o|=128,r=7),e.server&&(o|=64,r=6),e.email&&(o|=32,r=5),e.objsign&&(o|=16,r=4),e.reserved&&(o|=8,r=3),e.sslCA&&(o|=4,r=2),e.emailCA&&(o|=2,r=1),e.objCA&&(o|=1,r=0);u=String.fromCharCode(r);0!==o&&(u+=String.fromCharCode(o)),e.value=n.create(n.Class.UNIVERSAL,n.Type.BITSTRING,!1,u)}else if("subjectAltName"===e.name||"issuerAltName"===e.name){e.value=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);for(var f=0;f<e.altNames.length;++f){u=(m=e.altNames[f]).value;if(7===m.type&&m.ip){if(null===(u=a.util.bytesFromIP(m.ip)))throw(S=new Error('Extension "ip" value is not a valid IPv4 or IPv6 address.')).extension=e,S}else 8===m.type&&(u=m.oid?n.oidToDer(n.oidToDer(m.oid)):n.oidToDer(u));e.value.value.push(n.create(n.Class.CONTEXT_SPECIFIC,m.type,!1,u))}}else if("nsComment"===e.name&&t.cert){if(!/^[\x00-\x7F]*$/.test(e.comment)||e.comment.length<1||e.comment.length>128)throw new Error('Invalid "nsComment" content.');e.value=n.create(n.Class.UNIVERSAL,n.Type.IA5STRING,!1,e.comment)}else if("subjectKeyIdentifier"===e.name&&t.cert){var h=t.cert.generateSubjectKeyIdentifier();e.subjectKeyIdentifier=h.toHex(),e.value=n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,h.getBytes())}else if("authorityKeyIdentifier"===e.name&&t.cert){e.value=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);l=e.value.value;if(e.keyIdentifier){var d=!0===e.keyIdentifier?t.cert.generateSubjectKeyIdentifier().getBytes():e.keyIdentifier;l.push(n.create(n.Class.CONTEXT_SPECIFIC,0,!1,d))}if(e.authorityCertIssuer){var y=[n.create(n.Class.CONTEXT_SPECIFIC,4,!0,[v(!0===e.authorityCertIssuer?t.cert.issuer:e.authorityCertIssuer)])];l.push(n.create(n.Class.CONTEXT_SPECIFIC,1,!0,y))}if(e.serialNumber){var g=a.util.hexToBytes(!0===e.serialNumber?t.cert.serialNumber:e.serialNumber);l.push(n.create(n.Class.CONTEXT_SPECIFIC,2,!1,g))}}else if("cRLDistributionPoints"===e.name){e.value=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);l=e.value.value;var m,C=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]),E=n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[]);for(f=0;f<e.altNames.length;++f){u=(m=e.altNames[f]).value;if(7===m.type&&m.ip){if(null===(u=a.util.bytesFromIP(m.ip)))throw(S=new Error('Extension "ip" value is not a valid IPv4 or IPv6 address.')).extension=e,S}else 8===m.type&&(u=m.oid?n.oidToDer(n.oidToDer(m.oid)):n.oidToDer(u));E.value.push(n.create(n.Class.CONTEXT_SPECIFIC,m.type,!1,u))}C.value.push(n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[E])),l.push(C)}var S;if(void 0===e.value)throw(S=new Error("Extension value not specified.")).extension=e,S;return e}function E(e,t){switch(e){case s["RSASSA-PSS"]:var r=[];return void 0!==t.hash.algorithmOid&&r.push(n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(t.hash.algorithmOid).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")])])),void 0!==t.mgf.algorithmOid&&r.push(n.create(n.Class.CONTEXT_SPECIFIC,1,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(t.mgf.algorithmOid).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(t.mgf.hash.algorithmOid).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")])])])),void 0!==t.saltLength&&r.push(n.create(n.Class.CONTEXT_SPECIFIC,2,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(t.saltLength).getBytes())])),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,r);default:return n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")}}function S(e){var t=n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[]);if(0===e.attributes.length)return t;for(var r=e.attributes,i=0;i<r.length;++i){var s=r[i],o=s.value,c=n.Type.UTF8;"valueTagClass"in s&&(c=s.valueTagClass),c===n.Type.UTF8&&(o=a.util.encodeUtf8(o));var u=!1;"valueConstructed"in s&&(u=s.valueConstructed);var l=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(s.type).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SET,!0,[n.create(n.Class.UNIVERSAL,c,u,o)])]);t.value.push(l)}return t}i.certificateFromPem=function(e,t,r){var s=a.pem.decode(e)[0];if("CERTIFICATE"!==s.type&&"X509 CERTIFICATE"!==s.type&&"TRUSTED CERTIFICATE"!==s.type){var o=new Error('Could not convert certificate from PEM; PEM header type is not "CERTIFICATE", "X509 CERTIFICATE", or "TRUSTED CERTIFICATE".');throw o.headerType=s.type,o}if(s.procType&&"ENCRYPTED"===s.procType.type)throw new Error("Could not convert certificate from PEM; PEM is encrypted.");var c=n.fromDer(s.body,r);return i.certificateFromAsn1(c,t)},i.certificateToPem=function(e,t){var r={type:"CERTIFICATE",body:n.toDer(i.certificateToAsn1(e)).getBytes()};return a.pem.encode(r,{maxline:t})},i.publicKeyFromPem=function(e){var t=a.pem.decode(e)[0];if("PUBLIC KEY"!==t.type&&"RSA PUBLIC KEY"!==t.type){var r=new Error('Could not convert public key from PEM; PEM header type is not "PUBLIC KEY" or "RSA PUBLIC KEY".');throw r.headerType=t.type,r}if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert public key from PEM; PEM is encrypted.");var s=n.fromDer(t.body);return i.publicKeyFromAsn1(s)},i.publicKeyToPem=function(e,t){var r={type:"PUBLIC KEY",body:n.toDer(i.publicKeyToAsn1(e)).getBytes()};return a.pem.encode(r,{maxline:t})},i.publicKeyToRSAPublicKeyPem=function(e,t){var r={type:"RSA PUBLIC KEY",body:n.toDer(i.publicKeyToRSAPublicKey(e)).getBytes()};return a.pem.encode(r,{maxline:t})},i.getPublicKeyFingerprint=function(e,t){var r,s=(t=t||{}).md||a.md.sha1.create();switch(t.type||"RSAPublicKey"){case"RSAPublicKey":r=n.toDer(i.publicKeyToRSAPublicKey(e)).getBytes();break;case"SubjectPublicKeyInfo":r=n.toDer(i.publicKeyToAsn1(e)).getBytes();break;default:throw new Error('Unknown fingerprint type "'+t.type+'".')}s.start(),s.update(r);var o=s.digest();if("hex"===t.encoding){var c=o.toHex();return t.delimiter?c.match(/.{2}/g).join(t.delimiter):c}if("binary"===t.encoding)return o.getBytes();if(t.encoding)throw new Error('Unknown encoding "'+t.encoding+'".');return o},i.certificationRequestFromPem=function(e,t,r){var s=a.pem.decode(e)[0];if("CERTIFICATE REQUEST"!==s.type){var o=new Error('Could not convert certification request from PEM; PEM header type is not "CERTIFICATE REQUEST".');throw o.headerType=s.type,o}if(s.procType&&"ENCRYPTED"===s.procType.type)throw new Error("Could not convert certification request from PEM; PEM is encrypted.");var c=n.fromDer(s.body,r);return i.certificationRequestFromAsn1(c,t)},i.certificationRequestToPem=function(e,t){var r={type:"CERTIFICATE REQUEST",body:n.toDer(i.certificationRequestToAsn1(e)).getBytes()};return a.pem.encode(r,{maxline:t})},i.createCertificate=function(){var e={version:2,serialNumber:"00",signatureOid:null,signature:null,siginfo:{}};return e.siginfo.algorithmOid=null,e.validity={},e.validity.notBefore=new Date,e.validity.notAfter=new Date,e.issuer={},e.issuer.getField=function(t){return h(e.issuer,t)},e.issuer.addField=function(t){m([t]),e.issuer.attributes.push(t)},e.issuer.attributes=[],e.issuer.hash=null,e.subject={},e.subject.getField=function(t){return h(e.subject,t)},e.subject.addField=function(t){m([t]),e.subject.attributes.push(t)},e.subject.attributes=[],e.subject.hash=null,e.extensions=[],e.publicKey=null,e.md=null,e.setSubject=function(t,r){m(t),e.subject.attributes=t,delete e.subject.uniqueId,r&&(e.subject.uniqueId=r),e.subject.hash=null},e.setIssuer=function(t,r){m(t),e.issuer.attributes=t,delete e.issuer.uniqueId,r&&(e.issuer.uniqueId=r),e.issuer.hash=null},e.setExtensions=function(t){for(var r=0;r<t.length;++r)C(t[r],{cert:e});e.extensions=t},e.getExtension=function(t){"string"==typeof t&&(t={name:t});for(var r,a=null,n=0;null===a&&n<e.extensions.length;++n)r=e.extensions[n],(t.id&&r.id===t.id||t.name&&r.name===t.name)&&(a=r);return a},e.sign=function(t,r){e.md=r||a.md.sha1.create();var o=s[e.md.algorithm+"WithRSAEncryption"];if(!o){var c=new Error("Could not compute certificate digest. Unknown message digest algorithm OID.");throw c.algorithm=e.md.algorithm,c}e.signatureOid=e.siginfo.algorithmOid=o,e.tbsCertificate=i.getTBSCertificate(e);var u=n.toDer(e.tbsCertificate);e.md.update(u.getBytes()),e.signature=t.sign(e.md)},e.verify=function(t){var r=!1;if(!e.issued(t)){var a=t.issuer,s=e.subject,o=new Error("The parent certificate did not issue the given child certificate; the child certificate's issuer does not match the parent's subject.");throw o.expectedIssuer=s.attributes,o.actualIssuer=a.attributes,o}var c=t.md;if(null===c){c=y({signatureOid:t.signatureOid,type:"certificate"});var u=t.tbsCertificate||i.getTBSCertificate(t),l=n.toDer(u);c.update(l.getBytes())}return null!==c&&(r=g({certificate:e,md:c,signature:t.signature})),r},e.isIssuer=function(t){var r=!1,a=e.issuer,n=t.subject;if(a.hash&&n.hash)r=a.hash===n.hash;else if(a.attributes.length===n.attributes.length){var i,s;r=!0;for(var o=0;r&&o<a.attributes.length;++o)i=a.attributes[o],s=n.attributes[o],i.type===s.type&&i.value===s.value||(r=!1)}return r},e.issued=function(t){return t.isIssuer(e)},e.generateSubjectKeyIdentifier=function(){return i.getPublicKeyFingerprint(e.publicKey,{type:"RSAPublicKey"})},e.verifySubjectKeyIdentifier=function(){for(var t=s.subjectKeyIdentifier,r=0;r<e.extensions.length;++r){var n=e.extensions[r];if(n.id===t){var i=e.generateSubjectKeyIdentifier().getBytes();return a.util.hexToBytes(n.subjectKeyIdentifier)===i}}return!1},e},i.certificateFromAsn1=function(e,t){var r={},s=[];if(!n.validate(e,u,r,s)){var o=new Error("Cannot read X.509 certificate. ASN.1 object is not an X509v3 Certificate.");throw o.errors=s,o}if(n.derToOid(r.publicKeyOid)!==i.oids.rsaEncryption)throw new Error("Cannot read public key. OID is not RSA.");var c=i.createCertificate();c.version=r.certVersion?r.certVersion.charCodeAt(0):0;var l=a.util.createBuffer(r.certSerialNumber);c.serialNumber=l.toHex(),c.signatureOid=a.asn1.derToOid(r.certSignatureOid),c.signatureParameters=d(c.signatureOid,r.certSignatureParams,!0),c.siginfo.algorithmOid=a.asn1.derToOid(r.certinfoSignatureOid),c.siginfo.parameters=d(c.siginfo.algorithmOid,r.certinfoSignatureParams,!1),c.signature=r.certSignature;var p=[];if(void 0!==r.certValidity1UTCTime&&p.push(n.utcTimeToDate(r.certValidity1UTCTime)),void 0!==r.certValidity2GeneralizedTime&&p.push(n.generalizedTimeToDate(r.certValidity2GeneralizedTime)),void 0!==r.certValidity3UTCTime&&p.push(n.utcTimeToDate(r.certValidity3UTCTime)),void 0!==r.certValidity4GeneralizedTime&&p.push(n.generalizedTimeToDate(r.certValidity4GeneralizedTime)),p.length>2)throw new Error("Cannot read notBefore/notAfter validity times; more than two times were provided in the certificate.");if(p.length<2)throw new Error("Cannot read notBefore/notAfter validity times; they were not provided as either UTCTime or GeneralizedTime.");if(c.validity.notBefore=p[0],c.validity.notAfter=p[1],c.tbsCertificate=r.tbsCertificate,t){c.md=y({signatureOid:c.signatureOid,type:"certificate"});var f=n.toDer(c.tbsCertificate);c.md.update(f.getBytes())}var g=a.md.sha1.create(),v=n.toDer(r.certIssuer);g.update(v.getBytes()),c.issuer.getField=function(e){return h(c.issuer,e)},c.issuer.addField=function(e){m([e]),c.issuer.attributes.push(e)},c.issuer.attributes=i.RDNAttributesAsArray(r.certIssuer),r.certIssuerUniqueId&&(c.issuer.uniqueId=r.certIssuerUniqueId),c.issuer.hash=g.digest().toHex();var C=a.md.sha1.create(),E=n.toDer(r.certSubject);return C.update(E.getBytes()),c.subject.getField=function(e){return h(c.subject,e)},c.subject.addField=function(e){m([e]),c.subject.attributes.push(e)},c.subject.attributes=i.RDNAttributesAsArray(r.certSubject),r.certSubjectUniqueId&&(c.subject.uniqueId=r.certSubjectUniqueId),c.subject.hash=C.digest().toHex(),r.certExtensions?c.extensions=i.certificateExtensionsFromAsn1(r.certExtensions):c.extensions=[],c.publicKey=i.publicKeyFromAsn1(r.subjectPublicKeyInfo),c},i.certificateExtensionsFromAsn1=function(e){for(var t=[],r=0;r<e.value.length;++r)for(var a=e.value[r],n=0;n<a.value.length;++n)t.push(i.certificateExtensionFromAsn1(a.value[n]));return t},i.certificateExtensionFromAsn1=function(e){var t={};if(t.id=n.derToOid(e.value[0].value),t.critical=!1,e.value[1].type===n.Type.BOOLEAN?(t.critical=0!==e.value[1].value.charCodeAt(0),t.value=e.value[2].value):t.value=e.value[1].value,t.id in s)if(t.name=s[t.id],"keyUsage"===t.name){var r=0,i=0;(c=n.fromDer(t.value)).value.length>1&&(r=c.value.charCodeAt(1),i=c.value.length>2?c.value.charCodeAt(2):0),t.digitalSignature=128==(128&r),t.nonRepudiation=64==(64&r),t.keyEncipherment=32==(32&r),t.dataEncipherment=16==(16&r),t.keyAgreement=8==(8&r),t.keyCertSign=4==(4&r),t.cRLSign=2==(2&r),t.encipherOnly=1==(1&r),t.decipherOnly=128==(128&i)}else if("basicConstraints"===t.name){(c=n.fromDer(t.value)).value.length>0&&c.value[0].type===n.Type.BOOLEAN?t.cA=0!==c.value[0].value.charCodeAt(0):t.cA=!1;var o=null;c.value.length>0&&c.value[0].type===n.Type.INTEGER?o=c.value[0].value:c.value.length>1&&(o=c.value[1].value),null!==o&&(t.pathLenConstraint=n.derToInteger(o))}else if("extKeyUsage"===t.name)for(var c=n.fromDer(t.value),u=0;u<c.value.length;++u){var l=n.derToOid(c.value[u].value);l in s?t[s[l]]=!0:t[l]=!0}else if("nsCertType"===t.name){r=0;(c=n.fromDer(t.value)).value.length>1&&(r=c.value.charCodeAt(1)),t.client=128==(128&r),t.server=64==(64&r),t.email=32==(32&r),t.objsign=16==(16&r),t.reserved=8==(8&r),t.sslCA=4==(4&r),t.emailCA=2==(2&r),t.objCA=1==(1&r)}else if("subjectAltName"===t.name||"issuerAltName"===t.name){var p;t.altNames=[];c=n.fromDer(t.value);for(var f=0;f<c.value.length;++f){var h={type:(p=c.value[f]).type,value:p.value};switch(t.altNames.push(h),p.type){case 1:case 2:case 6:break;case 7:h.ip=a.util.bytesToIP(p.value);break;case 8:h.oid=n.derToOid(p.value)}}}else if("subjectKeyIdentifier"===t.name){c=n.fromDer(t.value);t.subjectKeyIdentifier=a.util.bytesToHex(c.value)}return t},i.certificationRequestFromAsn1=function(e,t){var r={},s=[];if(!n.validate(e,f,r,s)){var o=new Error("Cannot read PKCS#10 certificate request. ASN.1 object is not a PKCS#10 CertificationRequest.");throw o.errors=s,o}if(n.derToOid(r.publicKeyOid)!==i.oids.rsaEncryption)throw new Error("Cannot read public key. OID is not RSA.");var c=i.createCertificationRequest();if(c.version=r.csrVersion?r.csrVersion.charCodeAt(0):0,c.signatureOid=a.asn1.derToOid(r.csrSignatureOid),c.signatureParameters=d(c.signatureOid,r.csrSignatureParams,!0),c.siginfo.algorithmOid=a.asn1.derToOid(r.csrSignatureOid),c.siginfo.parameters=d(c.siginfo.algorithmOid,r.csrSignatureParams,!1),c.signature=r.csrSignature,c.certificationRequestInfo=r.certificationRequestInfo,t){c.md=y({signatureOid:c.signatureOid,type:"certification request"});var u=n.toDer(c.certificationRequestInfo);c.md.update(u.getBytes())}var l=a.md.sha1.create();return c.subject.getField=function(e){return h(c.subject,e)},c.subject.addField=function(e){m([e]),c.subject.attributes.push(e)},c.subject.attributes=i.RDNAttributesAsArray(r.certificationRequestInfoSubject,l),c.subject.hash=l.digest().toHex(),c.publicKey=i.publicKeyFromAsn1(r.subjectPublicKeyInfo),c.getAttribute=function(e){return h(c,e)},c.addAttribute=function(e){m([e]),c.attributes.push(e)},c.attributes=i.CRIAttributesAsArray(r.certificationRequestInfoAttributes||[]),c},i.createCertificationRequest=function(){var e={version:0,signatureOid:null,signature:null,siginfo:{}};return e.siginfo.algorithmOid=null,e.subject={},e.subject.getField=function(t){return h(e.subject,t)},e.subject.addField=function(t){m([t]),e.subject.attributes.push(t)},e.subject.attributes=[],e.subject.hash=null,e.publicKey=null,e.attributes=[],e.getAttribute=function(t){return h(e,t)},e.addAttribute=function(t){m([t]),e.attributes.push(t)},e.md=null,e.setSubject=function(t){m(t),e.subject.attributes=t,e.subject.hash=null},e.setAttributes=function(t){m(t),e.attributes=t},e.sign=function(t,r){e.md=r||a.md.sha1.create();var o=s[e.md.algorithm+"WithRSAEncryption"];if(!o){var c=new Error("Could not compute certification request digest. Unknown message digest algorithm OID.");throw c.algorithm=e.md.algorithm,c}e.signatureOid=e.siginfo.algorithmOid=o,e.certificationRequestInfo=i.getCertificationRequestInfo(e);var u=n.toDer(e.certificationRequestInfo);e.md.update(u.getBytes()),e.signature=t.sign(e.md)},e.verify=function(){var t=!1,r=e.md;if(null===r){r=y({signatureOid:e.signatureOid,type:"certification request"});var a=e.certificationRequestInfo||i.getCertificationRequestInfo(e),s=n.toDer(a);r.update(s.getBytes())}return null!==r&&(t=g({certificate:e,md:r,signature:e.signature})),t},e};var T=new Date("1950-01-01T00:00:00Z"),I=new Date("2050-01-01T00:00:00Z");function A(e){return e>=T&&e<I?n.create(n.Class.UNIVERSAL,n.Type.UTCTIME,!1,n.dateToUtcTime(e)):n.create(n.Class.UNIVERSAL,n.Type.GENERALIZEDTIME,!1,n.dateToGeneralizedTime(e))}i.getTBSCertificate=function(e){var t=A(e.validity.notBefore),r=A(e.validity.notAfter),s=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(e.version).getBytes())]),n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,a.util.hexToBytes(e.serialNumber)),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.siginfo.algorithmOid).getBytes()),E(e.siginfo.algorithmOid,e.siginfo.parameters)]),v(e.issuer),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[t,r]),v(e.subject),i.publicKeyToAsn1(e.publicKey)]);return e.issuer.uniqueId&&s.value.push(n.create(n.Class.CONTEXT_SPECIFIC,1,!0,[n.create(n.Class.UNIVERSAL,n.Type.BITSTRING,!1,String.fromCharCode(0)+e.issuer.uniqueId)])),e.subject.uniqueId&&s.value.push(n.create(n.Class.CONTEXT_SPECIFIC,2,!0,[n.create(n.Class.UNIVERSAL,n.Type.BITSTRING,!1,String.fromCharCode(0)+e.subject.uniqueId)])),e.extensions.length>0&&s.value.push(i.certificateExtensionsToAsn1(e.extensions)),s},i.getCertificationRequestInfo=function(e){return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(e.version).getBytes()),v(e.subject),i.publicKeyToAsn1(e.publicKey),S(e)])},i.distinguishedNameToAsn1=function(e){return v(e)},i.certificateToAsn1=function(e){var t=e.tbsCertificate||i.getTBSCertificate(e);return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[t,n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.signatureOid).getBytes()),E(e.signatureOid,e.signatureParameters)]),n.create(n.Class.UNIVERSAL,n.Type.BITSTRING,!1,String.fromCharCode(0)+e.signature)])},i.certificateExtensionsToAsn1=function(e){var t=n.create(n.Class.CONTEXT_SPECIFIC,3,!0,[]),r=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);t.value.push(r);for(var a=0;a<e.length;++a)r.value.push(i.certificateExtensionToAsn1(e[a]));return t},i.certificateExtensionToAsn1=function(e){var t=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);t.value.push(n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.id).getBytes())),e.critical&&t.value.push(n.create(n.Class.UNIVERSAL,n.Type.BOOLEAN,!1,String.fromCharCode(255)));var r=e.value;return"string"!=typeof e.value&&(r=n.toDer(r).getBytes()),t.value.push(n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,r)),t},i.certificationRequestToAsn1=function(e){var t=e.certificationRequestInfo||i.getCertificationRequestInfo(e);return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[t,n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.signatureOid).getBytes()),E(e.signatureOid,e.signatureParameters)]),n.create(n.Class.UNIVERSAL,n.Type.BITSTRING,!1,String.fromCharCode(0)+e.signature)])},i.createCaStore=function(e){var t={certs:{}};function r(e){return s(e),t.certs[e.hash]||null}function s(e){if(!e.hash){var t=a.md.sha1.create();e.attributes=i.RDNAttributesAsArray(v(e),t),e.hash=t.digest().toHex()}}if(t.getIssuer=function(e){return r(e.issuer)},t.addCertificate=function(e){if("string"==typeof e&&(e=a.pki.certificateFromPem(e)),s(e.subject),!t.hasCertificate(e))if(e.subject.hash in t.certs){var r=t.certs[e.subject.hash];a.util.isArray(r)||(r=[r]),r.push(e),t.certs[e.subject.hash]=r}else t.certs[e.subject.hash]=e},t.hasCertificate=function(e){"string"==typeof e&&(e=a.pki.certificateFromPem(e));var t=r(e.subject);if(!t)return!1;a.util.isArray(t)||(t=[t]);for(var s=n.toDer(i.certificateToAsn1(e)).getBytes(),o=0;o<t.length;++o){if(s===n.toDer(i.certificateToAsn1(t[o])).getBytes())return!0}return!1},t.listAllCertificates=function(){var e=[];for(var r in t.certs)if(t.certs.hasOwnProperty(r)){var n=t.certs[r];if(a.util.isArray(n))for(var i=0;i<n.length;++i)e.push(n[i]);else e.push(n)}return e},t.removeCertificate=function(e){var o;if("string"==typeof e&&(e=a.pki.certificateFromPem(e)),s(e.subject),!t.hasCertificate(e))return null;var c=r(e.subject);if(!a.util.isArray(c))return o=t.certs[e.subject.hash],delete t.certs[e.subject.hash],o;for(var u=n.toDer(i.certificateToAsn1(e)).getBytes(),l=0;l<c.length;++l){u===n.toDer(i.certificateToAsn1(c[l])).getBytes()&&(o=c[l],c.splice(l,1))}return 0===c.length&&delete t.certs[e.subject.hash],o},e)for(var o=0;o<e.length;++o){var c=e[o];t.addCertificate(c)}return t},i.certificateError={bad_certificate:"forge.pki.BadCertificate",unsupported_certificate:"forge.pki.UnsupportedCertificate",certificate_revoked:"forge.pki.CertificateRevoked",certificate_expired:"forge.pki.CertificateExpired",certificate_unknown:"forge.pki.CertificateUnknown",unknown_ca:"forge.pki.UnknownCertificateAuthority"},i.verifyCertificateChain=function(e,t,r){"function"==typeof r&&(r={verify:r}),r=r||{};var n=(t=t.slice(0)).slice(0),s=r.validityCheckDate;void 0===s&&(s=new Date);var o=!0,c=null,u=0;do{var l=t.shift(),p=null,f=!1;if(s&&(s<l.validity.notBefore||s>l.validity.notAfter)&&(c={message:"Certificate is not valid yet or has expired.",error:i.certificateError.certificate_expired,notBefore:l.validity.notBefore,notAfter:l.validity.notAfter,now:s}),null===c){if(null===(p=t[0]||e.getIssuer(l))&&l.isIssuer(l)&&(f=!0,p=l),p){var h=p;a.util.isArray(h)||(h=[h]);for(var d=!1;!d&&h.length>0;){p=h.shift();try{d=p.verify(l)}catch(e){}}d||(c={message:"Certificate signature is invalid.",error:i.certificateError.bad_certificate})}null!==c||p&&!f||e.hasCertificate(l)||(c={message:"Certificate is not trusted.",error:i.certificateError.unknown_ca})}if(null===c&&p&&!l.isIssuer(p)&&(c={message:"Certificate issuer is invalid.",error:i.certificateError.bad_certificate}),null===c)for(var y={keyUsage:!0,basicConstraints:!0},g=0;null===c&&g<l.extensions.length;++g){var v=l.extensions[g];v.critical&&!(v.name in y)&&(c={message:"Certificate has an unsupported critical extension.",error:i.certificateError.unsupported_certificate})}if(null===c&&(!o||0===t.length&&(!p||f))){var m=l.getExtension("basicConstraints"),C=l.getExtension("keyUsage");if(null!==C&&(C.keyCertSign&&null!==m||(c={message:"Certificate keyUsage or basicConstraints conflict or indicate that the certificate is not a CA. If the certificate is the only one in the chain or isn't the first then the certificate must be a valid CA.",error:i.certificateError.bad_certificate})),null===c&&null===m&&(c={message:"Certificate is missing basicConstraints extension and cannot be used as a CA.",error:i.certificateError.bad_certificate}),null!==c||null===m||m.cA||(c={message:"Certificate basicConstraints indicates the certificate is not a CA.",error:i.certificateError.bad_certificate}),null===c&&null!==C&&"pathLenConstraint"in m)u-1>m.pathLenConstraint&&(c={message:"Certificate basicConstraints pathLenConstraint violated.",error:i.certificateError.bad_certificate})}var E=null===c||c.error,S=r.verify?r.verify(E,u,n):E;if(!0!==S)throw!0===E&&(c={message:"The application rejected the certificate.",error:i.certificateError.bad_certificate}),(S||0===S)&&("object"!=typeof S||a.util.isArray(S)?"string"==typeof S&&(c.error=S):(S.message&&(c.message=S.message),S.error&&(c.error=S.error))),c;c=null,o=!1,++u}while(t.length>0);return!0}},function(e,t,r){var a=r(0);r(2),r(1),(e.exports=a.pss=a.pss||{}).create=function(e){3===arguments.length&&(e={md:arguments[0],mgf:arguments[1],saltLength:arguments[2]});var t,r=e.md,n=e.mgf,i=r.digestLength,s=e.salt||null;if("string"==typeof s&&(s=a.util.createBuffer(s)),"saltLength"in e)t=e.saltLength;else{if(null===s)throw new Error("Salt length not specified or specific salt not given.");t=s.length()}if(null!==s&&s.length()!==t)throw new Error("Given salt length does not match length of given salt.");var o=e.prng||a.random,c={encode:function(e,c){var u,l,p=c-1,f=Math.ceil(p/8),h=e.digest().getBytes();if(f<i+t+2)throw new Error("Message is too long to encrypt.");l=null===s?o.getBytesSync(t):s.bytes();var d=new a.util.ByteBuffer;d.fillWithByte(0,8),d.putBytes(h),d.putBytes(l),r.start(),r.update(d.getBytes());var y=r.digest().getBytes(),g=new a.util.ByteBuffer;g.fillWithByte(0,f-t-i-2),g.putByte(1),g.putBytes(l);var v=g.getBytes(),m=f-i-1,C=n.generate(y,m),E="";for(u=0;u<m;u++)E+=String.fromCharCode(v.charCodeAt(u)^C.charCodeAt(u));var S=65280>>8*f-p&255;return(E=String.fromCharCode(E.charCodeAt(0)&~S)+E.substr(1))+y+String.fromCharCode(188)},verify:function(e,s,o){var c,u=o-1,l=Math.ceil(u/8);if(s=s.substr(-l),l<i+t+2)throw new Error("Inconsistent parameters to PSS signature verification.");if(188!==s.charCodeAt(l-1))throw new Error("Encoded message does not end in 0xBC.");var p=l-i-1,f=s.substr(0,p),h=s.substr(p,i),d=65280>>8*l-u&255;if(0!=(f.charCodeAt(0)&d))throw new Error("Bits beyond keysize not zero as expected.");var y=n.generate(h,p),g="";for(c=0;c<p;c++)g+=String.fromCharCode(f.charCodeAt(c)^y.charCodeAt(c));g=String.fromCharCode(g.charCodeAt(0)&~d)+g.substr(1);var v=l-i-t-2;for(c=0;c<v;c++)if(0!==g.charCodeAt(c))throw new Error("Leftmost octets not zero as expected");if(1!==g.charCodeAt(v))throw new Error("Inconsistent PSS signature, 0x01 marker not found");var m=g.substr(-t),C=new a.util.ByteBuffer;return C.fillWithByte(0,8),C.putBytes(e),C.putBytes(m),r.start(),r.update(C.getBytes()),h===r.digest().getBytes()}};return c}},function(e,t,r){var a=r(0);r(1),a.cipher=a.cipher||{};var n=e.exports=a.cipher.modes=a.cipher.modes||{};function i(e,t){if("string"==typeof e&&(e=a.util.createBuffer(e)),a.util.isArray(e)&&e.length>4){var r=e;e=a.util.createBuffer();for(var n=0;n<r.length;++n)e.putByte(r[n])}if(e.length()<t)throw new Error("Invalid IV length; got "+e.length()+" bytes and expected "+t+" bytes.");if(!a.util.isArray(e)){var i=[],s=t/4;for(n=0;n<s;++n)i.push(e.getInt32());e=i}return e}function s(e){e[e.length-1]=e[e.length-1]+1&4294967295}function o(e){return[e/4294967296|0,4294967295&e]}n.ecb=function(e){e=e||{},this.name="ECB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints)},n.ecb.prototype.start=function(e){},n.ecb.prototype.encrypt=function(e,t,r){if(e.length()<this.blockSize&&!(r&&e.length()>0))return!0;for(var a=0;a<this._ints;++a)this._inBlock[a]=e.getInt32();this.cipher.encrypt(this._inBlock,this._outBlock);for(a=0;a<this._ints;++a)t.putInt32(this._outBlock[a])},n.ecb.prototype.decrypt=function(e,t,r){if(e.length()<this.blockSize&&!(r&&e.length()>0))return!0;for(var a=0;a<this._ints;++a)this._inBlock[a]=e.getInt32();this.cipher.decrypt(this._inBlock,this._outBlock);for(a=0;a<this._ints;++a)t.putInt32(this._outBlock[a])},n.ecb.prototype.pad=function(e,t){var r=e.length()===this.blockSize?this.blockSize:this.blockSize-e.length();return e.fillWithByte(r,r),!0},n.ecb.prototype.unpad=function(e,t){if(t.overflow>0)return!1;var r=e.length(),a=e.at(r-1);return!(a>this.blockSize<<2)&&(e.truncate(a),!0)},n.cbc=function(e){e=e||{},this.name="CBC",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints)},n.cbc.prototype.start=function(e){if(null===e.iv){if(!this._prev)throw new Error("Invalid IV parameter.");this._iv=this._prev.slice(0)}else{if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._prev=this._iv.slice(0)}},n.cbc.prototype.encrypt=function(e,t,r){if(e.length()<this.blockSize&&!(r&&e.length()>0))return!0;for(var a=0;a<this._ints;++a)this._inBlock[a]=this._prev[a]^e.getInt32();this.cipher.encrypt(this._inBlock,this._outBlock);for(a=0;a<this._ints;++a)t.putInt32(this._outBlock[a]);this._prev=this._outBlock},n.cbc.prototype.decrypt=function(e,t,r){if(e.length()<this.blockSize&&!(r&&e.length()>0))return!0;for(var a=0;a<this._ints;++a)this._inBlock[a]=e.getInt32();this.cipher.decrypt(this._inBlock,this._outBlock);for(a=0;a<this._ints;++a)t.putInt32(this._prev[a]^this._outBlock[a]);this._prev=this._inBlock.slice(0)},n.cbc.prototype.pad=function(e,t){var r=e.length()===this.blockSize?this.blockSize:this.blockSize-e.length();return e.fillWithByte(r,r),!0},n.cbc.prototype.unpad=function(e,t){if(t.overflow>0)return!1;var r=e.length(),a=e.at(r-1);return!(a>this.blockSize<<2)&&(e.truncate(a),!0)},n.cfb=function(e){e=e||{},this.name="CFB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialBlock=new Array(this._ints),this._partialOutput=a.util.createBuffer(),this._partialBytes=0},n.cfb.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},n.cfb.prototype.encrypt=function(e,t,r){var a=e.length();if(0===a)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize)for(var n=0;n<this._ints;++n)this._inBlock[n]=e.getInt32()^this._outBlock[n],t.putInt32(this._inBlock[n]);else{var i=(this.blockSize-a)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n<this._ints;++n)this._partialBlock[n]=e.getInt32()^this._outBlock[n],this._partialOutput.putInt32(this._partialBlock[n]);if(i>0)e.read-=this.blockSize;else for(n=0;n<this._ints;++n)this._inBlock[n]=this._partialBlock[n];if(this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}},n.cfb.prototype.decrypt=function(e,t,r){var a=e.length();if(0===a)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize)for(var n=0;n<this._ints;++n)this._inBlock[n]=e.getInt32(),t.putInt32(this._inBlock[n]^this._outBlock[n]);else{var i=(this.blockSize-a)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n<this._ints;++n)this._partialBlock[n]=e.getInt32(),this._partialOutput.putInt32(this._partialBlock[n]^this._outBlock[n]);if(i>0)e.read-=this.blockSize;else for(n=0;n<this._ints;++n)this._inBlock[n]=this._partialBlock[n];if(this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}},n.ofb=function(e){e=e||{},this.name="OFB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=a.util.createBuffer(),this._partialBytes=0},n.ofb.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},n.ofb.prototype.encrypt=function(e,t,r){var a=e.length();if(0===e.length())return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize)for(var n=0;n<this._ints;++n)t.putInt32(e.getInt32()^this._outBlock[n]),this._inBlock[n]=this._outBlock[n];else{var i=(this.blockSize-a)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n<this._ints;++n)this._partialOutput.putInt32(e.getInt32()^this._outBlock[n]);if(i>0)e.read-=this.blockSize;else for(n=0;n<this._ints;++n)this._inBlock[n]=this._outBlock[n];if(this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}},n.ofb.prototype.decrypt=n.ofb.prototype.encrypt,n.ctr=function(e){e=e||{},this.name="CTR",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=a.util.createBuffer(),this._partialBytes=0},n.ctr.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},n.ctr.prototype.encrypt=function(e,t,r){var a=e.length();if(0===a)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize)for(var n=0;n<this._ints;++n)t.putInt32(e.getInt32()^this._outBlock[n]);else{var i=(this.blockSize-a)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n<this._ints;++n)this._partialOutput.putInt32(e.getInt32()^this._outBlock[n]);if(i>0&&(e.read-=this.blockSize),this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}s(this._inBlock)},n.ctr.prototype.decrypt=n.ctr.prototype.encrypt,n.gcm=function(e){e=e||{},this.name="GCM",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints),this._partialOutput=a.util.createBuffer(),this._partialBytes=0,this._R=3774873600},n.gcm.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");var t,r=a.util.createBuffer(e.iv);if(this._cipherLength=0,t="additionalData"in e?a.util.createBuffer(e.additionalData):a.util.createBuffer(),this._tagLength="tagLength"in e?e.tagLength:128,this._tag=null,e.decrypt&&(this._tag=a.util.createBuffer(e.tag).getBytes(),this._tag.length!==this._tagLength/8))throw new Error("Authentication tag does not match tag length.");this._hashBlock=new Array(this._ints),this.tag=null,this._hashSubkey=new Array(this._ints),this.cipher.encrypt([0,0,0,0],this._hashSubkey),this.componentBits=4,this._m=this.generateHashTable(this._hashSubkey,this.componentBits);var n=r.length();if(12===n)this._j0=[r.getInt32(),r.getInt32(),r.getInt32(),1];else{for(this._j0=[0,0,0,0];r.length()>0;)this._j0=this.ghash(this._hashSubkey,this._j0,[r.getInt32(),r.getInt32(),r.getInt32(),r.getInt32()]);this._j0=this.ghash(this._hashSubkey,this._j0,[0,0].concat(o(8*n)))}this._inBlock=this._j0.slice(0),s(this._inBlock),this._partialBytes=0,t=a.util.createBuffer(t),this._aDataLength=o(8*t.length());var i=t.length()%this.blockSize;for(i&&t.fillWithByte(0,this.blockSize-i),this._s=[0,0,0,0];t.length()>0;)this._s=this.ghash(this._hashSubkey,this._s,[t.getInt32(),t.getInt32(),t.getInt32(),t.getInt32()])},n.gcm.prototype.encrypt=function(e,t,r){var a=e.length();if(0===a)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize){for(var n=0;n<this._ints;++n)t.putInt32(this._outBlock[n]^=e.getInt32());this._cipherLength+=this.blockSize}else{var i=(this.blockSize-a)%this.blockSize;i>0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n<this._ints;++n)this._partialOutput.putInt32(e.getInt32()^this._outBlock[n]);if(i<=0||r){if(r){var o=a%this.blockSize;this._cipherLength+=o,this._partialOutput.truncate(this.blockSize-o)}else this._cipherLength+=this.blockSize;for(n=0;n<this._ints;++n)this._outBlock[n]=this._partialOutput.getInt32();this._partialOutput.read-=this.blockSize}if(this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return e.read-=this.blockSize,t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}this._s=this.ghash(this._hashSubkey,this._s,this._outBlock),s(this._inBlock)},n.gcm.prototype.decrypt=function(e,t,r){var a=e.length();if(a<this.blockSize&&!(r&&a>0))return!0;this.cipher.encrypt(this._inBlock,this._outBlock),s(this._inBlock),this._hashBlock[0]=e.getInt32(),this._hashBlock[1]=e.getInt32(),this._hashBlock[2]=e.getInt32(),this._hashBlock[3]=e.getInt32(),this._s=this.ghash(this._hashSubkey,this._s,this._hashBlock);for(var n=0;n<this._ints;++n)t.putInt32(this._outBlock[n]^this._hashBlock[n]);a<this.blockSize?this._cipherLength+=a%this.blockSize:this._cipherLength+=this.blockSize},n.gcm.prototype.afterFinish=function(e,t){var r=!0;t.decrypt&&t.overflow&&e.truncate(this.blockSize-t.overflow),this.tag=a.util.createBuffer();var n=this._aDataLength.concat(o(8*this._cipherLength));this._s=this.ghash(this._hashSubkey,this._s,n);var i=[];this.cipher.encrypt(this._j0,i);for(var s=0;s<this._ints;++s)this.tag.putInt32(this._s[s]^i[s]);return this.tag.truncate(this.tag.length()%(this._tagLength/8)),t.decrypt&&this.tag.bytes()!==this._tag&&(r=!1),r},n.gcm.prototype.multiply=function(e,t){for(var r=[0,0,0,0],a=t.slice(0),n=0;n<128;++n){e[n/32|0]&1<<31-n%32&&(r[0]^=a[0],r[1]^=a[1],r[2]^=a[2],r[3]^=a[3]),this.pow(a,a)}return r},n.gcm.prototype.pow=function(e,t){for(var r=1&e[3],a=3;a>0;--a)t[a]=e[a]>>>1|(1&e[a-1])<<31;t[0]=e[0]>>>1,r&&(t[0]^=this._R)},n.gcm.prototype.tableMultiply=function(e){for(var t=[0,0,0,0],r=0;r<32;++r){var a=e[r/8|0]>>>4*(7-r%8)&15,n=this._m[r][a];t[0]^=n[0],t[1]^=n[1],t[2]^=n[2],t[3]^=n[3]}return t},n.gcm.prototype.ghash=function(e,t,r){return t[0]^=r[0],t[1]^=r[1],t[2]^=r[2],t[3]^=r[3],this.tableMultiply(t)},n.gcm.prototype.generateHashTable=function(e,t){for(var r=8/t,a=4*r,n=16*r,i=new Array(n),s=0;s<n;++s){var o=[0,0,0,0],c=(a-1-s%a)*t;o[s/a|0]=1<<t-1<<c,i[s]=this.generateSubHashTable(this.multiply(o,e),t)}return i},n.gcm.prototype.generateSubHashTable=function(e,t){var r=1<<t,a=r>>>1,n=new Array(r);n[a]=e.slice(0);for(var i=a>>>1;i>0;)this.pow(n[2*i],n[i]=[]),i>>=1;for(i=2;i<a;){for(var s=1;s<i;++s){var o=n[i],c=n[s];n[i+s]=[o[0]^c[0],o[1]^c[1],o[2]^c[2],o[3]^c[3]]}i*=2}for(n[0]=[0,0,0,0],i=a+1;i<r;++i){var u=n[i^a];n[i]=[e[0]^u[0],e[1]^u[1],e[2]^u[2],e[3]^u[3]]}return n}},function(e,t,r){var a=r(0);r(3),r(8),r(14),r(7),r(21),r(2),r(9),r(1);var n=function(e,t,r,n){var i=a.util.createBuffer(),s=e.length>>1,o=s+(1&e.length),c=e.substr(0,o),u=e.substr(s,o),l=a.util.createBuffer(),p=a.hmac.create();r=t+r;var f=Math.ceil(n/16),h=Math.ceil(n/20);p.start("MD5",c);var d=a.util.createBuffer();l.putBytes(r);for(var y=0;y<f;++y)p.start(null,null),p.update(l.getBytes()),l.putBuffer(p.digest()),p.start(null,null),p.update(l.bytes()+r),d.putBuffer(p.digest());p.start("SHA1",u);var g=a.util.createBuffer();l.clear(),l.putBytes(r);for(y=0;y<h;++y)p.start(null,null),p.update(l.getBytes()),l.putBuffer(p.digest()),p.start(null,null),p.update(l.bytes()+r),g.putBuffer(p.digest());return i.putBytes(a.util.xorBytes(d.getBytes(),g.getBytes(),n)),i},i=function(e,t,r){var n=!1;try{var i=e.deflate(t.fragment.getBytes());t.fragment=a.util.createBuffer(i),t.length=i.length,n=!0}catch(e){}return n},s=function(e,t,r){var n=!1;try{var i=e.inflate(t.fragment.getBytes());t.fragment=a.util.createBuffer(i),t.length=i.length,n=!0}catch(e){}return n},o=function(e,t){var r=0;switch(t){case 1:r=e.getByte();break;case 2:r=e.getInt16();break;case 3:r=e.getInt24();break;case 4:r=e.getInt32()}return a.util.createBuffer(e.getBytes(r))},c=function(e,t,r){e.putInt(r.length(),t<<3),e.putBuffer(r)},u={Versions:{TLS_1_0:{major:3,minor:1},TLS_1_1:{major:3,minor:2},TLS_1_2:{major:3,minor:3}}};u.SupportedVersions=[u.Versions.TLS_1_1,u.Versions.TLS_1_0],u.Version=u.SupportedVersions[0],u.MaxFragment=15360,u.ConnectionEnd={server:0,client:1},u.PRFAlgorithm={tls_prf_sha256:0},u.BulkCipherAlgorithm={none:null,rc4:0,des3:1,aes:2},u.CipherType={stream:0,block:1,aead:2},u.MACAlgorithm={none:null,hmac_md5:0,hmac_sha1:1,hmac_sha256:2,hmac_sha384:3,hmac_sha512:4},u.CompressionMethod={none:0,deflate:1},u.ContentType={change_cipher_spec:20,alert:21,handshake:22,application_data:23,heartbeat:24},u.HandshakeType={hello_request:0,client_hello:1,server_hello:2,certificate:11,server_key_exchange:12,certificate_request:13,server_hello_done:14,certificate_verify:15,client_key_exchange:16,finished:20},u.Alert={},u.Alert.Level={warning:1,fatal:2},u.Alert.Description={close_notify:0,unexpected_message:10,bad_record_mac:20,decryption_failed:21,record_overflow:22,decompression_failure:30,handshake_failure:40,bad_certificate:42,unsupported_certificate:43,certificate_revoked:44,certificate_expired:45,certificate_unknown:46,illegal_parameter:47,unknown_ca:48,access_denied:49,decode_error:50,decrypt_error:51,export_restriction:60,protocol_version:70,insufficient_security:71,internal_error:80,user_canceled:90,no_renegotiation:100},u.HeartbeatMessageType={heartbeat_request:1,heartbeat_response:2},u.CipherSuites={},u.getCipherSuite=function(e){var t=null;for(var r in u.CipherSuites){var a=u.CipherSuites[r];if(a.id[0]===e.charCodeAt(0)&&a.id[1]===e.charCodeAt(1)){t=a;break}}return t},u.handleUnexpected=function(e,t){!e.open&&e.entity===u.ConnectionEnd.client||e.error(e,{message:"Unexpected message. Received TLS record out of order.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unexpected_message}})},u.handleHelloRequest=function(e,t,r){!e.handshaking&&e.handshakes>0&&(u.queue(e,u.createAlert(e,{level:u.Alert.Level.warning,description:u.Alert.Description.no_renegotiation})),u.flush(e)),e.process()},u.parseHelloMessage=function(e,t,r){var n=null,i=e.entity===u.ConnectionEnd.client;if(r<38)e.error(e,{message:i?"Invalid ServerHello message. Message too short.":"Invalid ClientHello message. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});else{var s=t.fragment,c=s.length();if(n={version:{major:s.getByte(),minor:s.getByte()},random:a.util.createBuffer(s.getBytes(32)),session_id:o(s,1),extensions:[]},i?(n.cipher_suite=s.getBytes(2),n.compression_method=s.getByte()):(n.cipher_suites=o(s,2),n.compression_methods=o(s,1)),(c=r-(c-s.length()))>0){for(var l=o(s,2);l.length()>0;)n.extensions.push({type:[l.getByte(),l.getByte()],data:o(l,2)});if(!i)for(var p=0;p<n.extensions.length;++p){var f=n.extensions[p];if(0===f.type[0]&&0===f.type[1])for(var h=o(f.data,2);h.length()>0;){if(0!==h.getByte())break;e.session.extensions.server_name.serverNameList.push(o(h,2).getBytes())}}}if(e.session.version&&(n.version.major!==e.session.version.major||n.version.minor!==e.session.version.minor))return e.error(e,{message:"TLS version change is disallowed during renegotiation.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}});if(i)e.session.cipherSuite=u.getCipherSuite(n.cipher_suite);else for(var d=a.util.createBuffer(n.cipher_suites.bytes());d.length()>0&&(e.session.cipherSuite=u.getCipherSuite(d.getBytes(2)),null===e.session.cipherSuite););if(null===e.session.cipherSuite)return e.error(e,{message:"No cipher suites in common.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.handshake_failure},cipherSuite:a.util.bytesToHex(n.cipher_suite)});e.session.compressionMethod=i?n.compression_method:u.CompressionMethod.none}return n},u.createSecurityParameters=function(e,t){var r=e.entity===u.ConnectionEnd.client,a=t.random.bytes(),n=r?e.session.sp.client_random:a,i=r?a:u.createRandom().getBytes();e.session.sp={entity:e.entity,prf_algorithm:u.PRFAlgorithm.tls_prf_sha256,bulk_cipher_algorithm:null,cipher_type:null,enc_key_length:null,block_length:null,fixed_iv_length:null,record_iv_length:null,mac_algorithm:null,mac_length:null,mac_key_length:null,compression_algorithm:e.session.compressionMethod,pre_master_secret:null,master_secret:null,client_random:n,server_random:i}},u.handleServerHello=function(e,t,r){var a=u.parseHelloMessage(e,t,r);if(!e.fail){if(!(a.version.minor<=e.version.minor))return e.error(e,{message:"Incompatible TLS version.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}});e.version.minor=a.version.minor,e.session.version=e.version;var n=a.session_id.bytes();n.length>0&&n===e.session.id?(e.expect=d,e.session.resuming=!0,e.session.sp.server_random=a.random.bytes()):(e.expect=l,e.session.resuming=!1,u.createSecurityParameters(e,a)),e.session.id=n,e.process()}},u.handleClientHello=function(e,t,r){var n=u.parseHelloMessage(e,t,r);if(!e.fail){var i=n.session_id.bytes(),s=null;if(e.sessionCache&&(null===(s=e.sessionCache.getSession(i))?i="":(s.version.major!==n.version.major||s.version.minor>n.version.minor)&&(s=null,i="")),0===i.length&&(i=a.random.getBytes(32)),e.session.id=i,e.session.clientHelloVersion=n.version,e.session.sp={},s)e.version=e.session.version=s.version,e.session.sp=s.sp;else{for(var o,c=1;c<u.SupportedVersions.length&&!((o=u.SupportedVersions[c]).minor<=n.version.minor);++c);e.version={major:o.major,minor:o.minor},e.session.version=e.version}null!==s?(e.expect=S,e.session.resuming=!0,e.session.sp.client_random=n.random.bytes()):(e.expect=!1!==e.verifyClient?m:C,e.session.resuming=!1,u.createSecurityParameters(e,n)),e.open=!0,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createServerHello(e)})),e.session.resuming?(u.queue(e,u.createRecord(e,{type:u.ContentType.change_cipher_spec,data:u.createChangeCipherSpec()})),e.state.pending=u.createConnectionState(e),e.state.current.write=e.state.pending.write,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createFinished(e)}))):(u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificate(e)})),e.fail||(u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createServerKeyExchange(e)})),!1!==e.verifyClient&&u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificateRequest(e)})),u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createServerHelloDone(e)})))),u.flush(e),e.process()}},u.handleCertificate=function(e,t,r){if(r<3)return e.error(e,{message:"Invalid Certificate message. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var n,i,s=t.fragment,c={certificate_list:o(s,3)},l=[];try{for(;c.certificate_list.length()>0;)n=o(c.certificate_list,3),i=a.asn1.fromDer(n),n=a.pki.certificateFromAsn1(i,!0),l.push(n)}catch(t){return e.error(e,{message:"Could not parse certificate list.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.bad_certificate}})}var f=e.entity===u.ConnectionEnd.client;!f&&!0!==e.verifyClient||0!==l.length?0===l.length?e.expect=f?p:C:(f?e.session.serverCertificate=l[0]:e.session.clientCertificate=l[0],u.verifyCertificateChain(e,l)&&(e.expect=f?p:C)):e.error(e,{message:f?"No server certificate provided.":"No client certificate provided.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}}),e.process()},u.handleServerKeyExchange=function(e,t,r){if(r>0)return e.error(e,{message:"Invalid key parameters. Only RSA is supported.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unsupported_certificate}});e.expect=f,e.process()},u.handleClientKeyExchange=function(e,t,r){if(r<48)return e.error(e,{message:"Invalid key parameters. Only RSA is supported.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unsupported_certificate}});var n=t.fragment,i={enc_pre_master_secret:o(n,2).getBytes()},s=null;if(e.getPrivateKey)try{s=e.getPrivateKey(e,e.session.serverCertificate),s=a.pki.privateKeyFromPem(s)}catch(t){e.error(e,{message:"Could not get private key.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}})}if(null===s)return e.error(e,{message:"No private key set.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}});try{var c=e.session.sp;c.pre_master_secret=s.decrypt(i.enc_pre_master_secret);var l=e.session.clientHelloVersion;if(l.major!==c.pre_master_secret.charCodeAt(0)||l.minor!==c.pre_master_secret.charCodeAt(1))throw new Error("TLS version rollback attack detected.")}catch(e){c.pre_master_secret=a.random.getBytes(48)}e.expect=S,null!==e.session.clientCertificate&&(e.expect=E),e.process()},u.handleCertificateRequest=function(e,t,r){if(r<3)return e.error(e,{message:"Invalid CertificateRequest. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var a=t.fragment,n={certificate_types:o(a,1),certificate_authorities:o(a,2)};e.session.certificateRequest=n,e.expect=h,e.process()},u.handleCertificateVerify=function(e,t,r){if(r<2)return e.error(e,{message:"Invalid CertificateVerify. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var n=t.fragment;n.read-=4;var i=n.bytes();n.read+=4;var s={signature:o(n,2).getBytes()},c=a.util.createBuffer();c.putBuffer(e.session.md5.digest()),c.putBuffer(e.session.sha1.digest()),c=c.getBytes();try{if(!e.session.clientCertificate.publicKey.verify(c,s.signature,"NONE"))throw new Error("CertificateVerify signature does not match.");e.session.md5.update(i),e.session.sha1.update(i)}catch(t){return e.error(e,{message:"Bad signature in CertificateVerify.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.handshake_failure}})}e.expect=S,e.process()},u.handleServerHelloDone=function(e,t,r){if(r>0)return e.error(e,{message:"Invalid ServerHelloDone message. Invalid length.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.record_overflow}});if(null===e.serverCertificate){var n={message:"No server certificate provided. Not enough security.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.insufficient_security}},i=e.verify(e,n.alert.description,0,[]);if(!0!==i)return(i||0===i)&&("object"!=typeof i||a.util.isArray(i)?"number"==typeof i&&(n.alert.description=i):(i.message&&(n.message=i.message),i.alert&&(n.alert.description=i.alert))),e.error(e,n)}null!==e.session.certificateRequest&&(t=u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificate(e)}),u.queue(e,t)),t=u.createRecord(e,{type:u.ContentType.handshake,data:u.createClientKeyExchange(e)}),u.queue(e,t),e.expect=v;var s=function(e,t){null!==e.session.certificateRequest&&null!==e.session.clientCertificate&&u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificateVerify(e,t)})),u.queue(e,u.createRecord(e,{type:u.ContentType.change_cipher_spec,data:u.createChangeCipherSpec()})),e.state.pending=u.createConnectionState(e),e.state.current.write=e.state.pending.write,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createFinished(e)})),e.expect=d,u.flush(e),e.process()};if(null===e.session.certificateRequest||null===e.session.clientCertificate)return s(e,null);u.getClientSignature(e,s)},u.handleChangeCipherSpec=function(e,t){if(1!==t.fragment.getByte())return e.error(e,{message:"Invalid ChangeCipherSpec message received.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var r=e.entity===u.ConnectionEnd.client;(e.session.resuming&&r||!e.session.resuming&&!r)&&(e.state.pending=u.createConnectionState(e)),e.state.current.read=e.state.pending.read,(!e.session.resuming&&r||e.session.resuming&&!r)&&(e.state.pending=null),e.expect=r?y:T,e.process()},u.handleFinished=function(e,t,r){var i=t.fragment;i.read-=4;var s=i.bytes();i.read+=4;var o=t.fragment.getBytes();(i=a.util.createBuffer()).putBuffer(e.session.md5.digest()),i.putBuffer(e.session.sha1.digest());var c=e.entity===u.ConnectionEnd.client,l=c?"server finished":"client finished",p=e.session.sp;if((i=n(p.master_secret,l,i.getBytes(),12)).getBytes()!==o)return e.error(e,{message:"Invalid verify_data in Finished message.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.decrypt_error}});e.session.md5.update(s),e.session.sha1.update(s),(e.session.resuming&&c||!e.session.resuming&&!c)&&(u.queue(e,u.createRecord(e,{type:u.ContentType.change_cipher_spec,data:u.createChangeCipherSpec()})),e.state.current.write=e.state.pending.write,e.state.pending=null,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createFinished(e)}))),e.expect=c?g:I,e.handshaking=!1,++e.handshakes,e.peerCertificate=c?e.session.serverCertificate:e.session.clientCertificate,u.flush(e),e.isConnected=!0,e.connected(e),e.process()},u.handleAlert=function(e,t){var r,a=t.fragment,n={level:a.getByte(),description:a.getByte()};switch(n.description){case u.Alert.Description.close_notify:r="Connection closed.";break;case u.Alert.Description.unexpected_message:r="Unexpected message.";break;case u.Alert.Description.bad_record_mac:r="Bad record MAC.";break;case u.Alert.Description.decryption_failed:r="Decryption failed.";break;case u.Alert.Description.record_overflow:r="Record overflow.";break;case u.Alert.Description.decompression_failure:r="Decompression failed.";break;case u.Alert.Description.handshake_failure:r="Handshake failure.";break;case u.Alert.Description.bad_certificate:r="Bad certificate.";break;case u.Alert.Description.unsupported_certificate:r="Unsupported certificate.";break;case u.Alert.Description.certificate_revoked:r="Certificate revoked.";break;case u.Alert.Description.certificate_expired:r="Certificate expired.";break;case u.Alert.Description.certificate_unknown:r="Certificate unknown.";break;case u.Alert.Description.illegal_parameter:r="Illegal parameter.";break;case u.Alert.Description.unknown_ca:r="Unknown certificate authority.";break;case u.Alert.Description.access_denied:r="Access denied.";break;case u.Alert.Description.decode_error:r="Decode error.";break;case u.Alert.Description.decrypt_error:r="Decrypt error.";break;case u.Alert.Description.export_restriction:r="Export restriction.";break;case u.Alert.Description.protocol_version:r="Unsupported protocol version.";break;case u.Alert.Description.insufficient_security:r="Insufficient security.";break;case u.Alert.Description.internal_error:r="Internal error.";break;case u.Alert.Description.user_canceled:r="User canceled.";break;case u.Alert.Description.no_renegotiation:r="Renegotiation not supported.";break;default:r="Unknown error."}if(n.description===u.Alert.Description.close_notify)return e.close();e.error(e,{message:r,send:!1,origin:e.entity===u.ConnectionEnd.client?"server":"client",alert:n}),e.process()},u.handleHandshake=function(e,t){var r=t.fragment,n=r.getByte(),i=r.getInt24();if(i>r.length())return e.fragmented=t,t.fragment=a.util.createBuffer(),r.read-=4,e.process();e.fragmented=null,r.read-=4;var s=r.bytes(i+4);r.read+=4,n in K[e.entity][e.expect]?(e.entity!==u.ConnectionEnd.server||e.open||e.fail||(e.handshaking=!0,e.session={version:null,extensions:{server_name:{serverNameList:[]}},cipherSuite:null,compressionMethod:null,serverCertificate:null,clientCertificate:null,md5:a.md.md5.create(),sha1:a.md.sha1.create()}),n!==u.HandshakeType.hello_request&&n!==u.HandshakeType.certificate_verify&&n!==u.HandshakeType.finished&&(e.session.md5.update(s),e.session.sha1.update(s)),K[e.entity][e.expect][n](e,t,i)):u.handleUnexpected(e,t)},u.handleApplicationData=function(e,t){e.data.putBuffer(t.fragment),e.dataReady(e),e.process()},u.handleHeartbeat=function(e,t){var r=t.fragment,n=r.getByte(),i=r.getInt16(),s=r.getBytes(i);if(n===u.HeartbeatMessageType.heartbeat_request){if(e.handshaking||i>s.length)return e.process();u.queue(e,u.createRecord(e,{type:u.ContentType.heartbeat,data:u.createHeartbeat(u.HeartbeatMessageType.heartbeat_response,s)})),u.flush(e)}else if(n===u.HeartbeatMessageType.heartbeat_response){if(s!==e.expectedHeartbeatPayload)return e.process();e.heartbeatReceived&&e.heartbeatReceived(e,a.util.createBuffer(s))}e.process()};var l=1,p=2,f=3,h=4,d=5,y=6,g=7,v=8,m=1,C=2,E=3,S=4,T=5,I=6,A=u.handleUnexpected,B=u.handleChangeCipherSpec,b=u.handleAlert,N=u.handleHandshake,R=u.handleApplicationData,w=u.handleHeartbeat,_=[];_[u.ConnectionEnd.client]=[[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[B,b,A,A,w],[A,b,N,A,w],[A,b,N,R,w],[A,b,N,A,w]],_[u.ConnectionEnd.server]=[[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[B,b,A,A,w],[A,b,N,A,w],[A,b,N,R,w],[A,b,N,A,w]];var k=u.handleHelloRequest,L=u.handleServerHello,U=u.handleCertificate,D=u.handleServerKeyExchange,P=u.handleCertificateRequest,V=u.handleServerHelloDone,O=u.handleFinished,K=[];K[u.ConnectionEnd.client]=[[A,A,L,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[k,A,A,A,A,A,A,A,A,A,A,U,D,P,V,A,A,A,A,A,A],[k,A,A,A,A,A,A,A,A,A,A,A,D,P,V,A,A,A,A,A,A],[k,A,A,A,A,A,A,A,A,A,A,A,A,P,V,A,A,A,A,A,A],[k,A,A,A,A,A,A,A,A,A,A,A,A,A,V,A,A,A,A,A,A],[k,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[k,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,O],[k,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[k,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A]];var x=u.handleClientHello,M=u.handleClientKeyExchange,F=u.handleCertificateVerify;K[u.ConnectionEnd.server]=[[A,x,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,U,A,A,A,A,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,M,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,F,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,O],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A]],u.generateKeys=function(e,t){var r=n,a=t.client_random+t.server_random;e.session.resuming||(t.master_secret=r(t.pre_master_secret,"master secret",a,48).bytes(),t.pre_master_secret=null),a=t.server_random+t.client_random;var i=2*t.mac_key_length+2*t.enc_key_length,s=e.version.major===u.Versions.TLS_1_0.major&&e.version.minor===u.Versions.TLS_1_0.minor;s&&(i+=2*t.fixed_iv_length);var o=r(t.master_secret,"key expansion",a,i),c={client_write_MAC_key:o.getBytes(t.mac_key_length),server_write_MAC_key:o.getBytes(t.mac_key_length),client_write_key:o.getBytes(t.enc_key_length),server_write_key:o.getBytes(t.enc_key_length)};return s&&(c.client_write_IV=o.getBytes(t.fixed_iv_length),c.server_write_IV=o.getBytes(t.fixed_iv_length)),c},u.createConnectionState=function(e){var t=e.entity===u.ConnectionEnd.client,r=function(){var e={sequenceNumber:[0,0],macKey:null,macLength:0,macFunction:null,cipherState:null,cipherFunction:function(e){return!0},compressionState:null,compressFunction:function(e){return!0},updateSequenceNumber:function(){4294967295===e.sequenceNumber[1]?(e.sequenceNumber[1]=0,++e.sequenceNumber[0]):++e.sequenceNumber[1]}};return e},a={read:r(),write:r()};if(a.read.update=function(e,t){return a.read.cipherFunction(t,a.read)?a.read.compressFunction(e,t,a.read)||e.error(e,{message:"Could not decompress record.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.decompression_failure}}):e.error(e,{message:"Could not decrypt record or bad MAC.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.bad_record_mac}}),!e.fail},a.write.update=function(e,t){return a.write.compressFunction(e,t,a.write)?a.write.cipherFunction(t,a.write)||e.error(e,{message:"Could not encrypt record.",send:!1,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}}):e.error(e,{message:"Could not compress record.",send:!1,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}}),!e.fail},e.session){var n=e.session.sp;switch(e.session.cipherSuite.initSecurityParameters(n),n.keys=u.generateKeys(e,n),a.read.macKey=t?n.keys.server_write_MAC_key:n.keys.client_write_MAC_key,a.write.macKey=t?n.keys.client_write_MAC_key:n.keys.server_write_MAC_key,e.session.cipherSuite.initConnectionState(a,e,n),n.compression_algorithm){case u.CompressionMethod.none:break;case u.CompressionMethod.deflate:a.read.compressFunction=s,a.write.compressFunction=i;break;default:throw new Error("Unsupported compression algorithm.")}}return a},u.createRandom=function(){var e=new Date,t=+e+6e4*e.getTimezoneOffset(),r=a.util.createBuffer();return r.putInt32(t),r.putBytes(a.random.getBytes(28)),r},u.createRecord=function(e,t){return t.data?{type:t.type,version:{major:e.version.major,minor:e.version.minor},length:t.data.length(),fragment:t.data}:null},u.createAlert=function(e,t){var r=a.util.createBuffer();return r.putByte(t.level),r.putByte(t.description),u.createRecord(e,{type:u.ContentType.alert,data:r})},u.createClientHello=function(e){e.session.clientHelloVersion={major:e.version.major,minor:e.version.minor};for(var t=a.util.createBuffer(),r=0;r<e.cipherSuites.length;++r){var n=e.cipherSuites[r];t.putByte(n.id[0]),t.putByte(n.id[1])}var i=t.length(),s=a.util.createBuffer();s.putByte(u.CompressionMethod.none);var o=s.length(),l=a.util.createBuffer();if(e.virtualHost){var p=a.util.createBuffer();p.putByte(0),p.putByte(0);var f=a.util.createBuffer();f.putByte(0),c(f,2,a.util.createBuffer(e.virtualHost));var h=a.util.createBuffer();c(h,2,f),c(p,2,h),l.putBuffer(p)}var d=l.length();d>0&&(d+=2);var y=e.session.id,g=y.length+1+2+4+28+2+i+1+o+d,v=a.util.createBuffer();return v.putByte(u.HandshakeType.client_hello),v.putInt24(g),v.putByte(e.version.major),v.putByte(e.version.minor),v.putBytes(e.session.sp.client_random),c(v,1,a.util.createBuffer(y)),c(v,2,t),c(v,1,s),d>0&&c(v,2,l),v},u.createServerHello=function(e){var t=e.session.id,r=t.length+1+2+4+28+2+1,n=a.util.createBuffer();return n.putByte(u.HandshakeType.server_hello),n.putInt24(r),n.putByte(e.version.major),n.putByte(e.version.minor),n.putBytes(e.session.sp.server_random),c(n,1,a.util.createBuffer(t)),n.putByte(e.session.cipherSuite.id[0]),n.putByte(e.session.cipherSuite.id[1]),n.putByte(e.session.compressionMethod),n},u.createCertificate=function(e){var t,r=e.entity===u.ConnectionEnd.client,n=null;e.getCertificate&&(t=r?e.session.certificateRequest:e.session.extensions.server_name.serverNameList,n=e.getCertificate(e,t));var i=a.util.createBuffer();if(null!==n)try{a.util.isArray(n)||(n=[n]);for(var s=null,o=0;o<n.length;++o){var l=a.pem.decode(n[o])[0];if("CERTIFICATE"!==l.type&&"X509 CERTIFICATE"!==l.type&&"TRUSTED CERTIFICATE"!==l.type){var p=new Error('Could not convert certificate from PEM; PEM header type is not "CERTIFICATE", "X509 CERTIFICATE", or "TRUSTED CERTIFICATE".');throw p.headerType=l.type,p}if(l.procType&&"ENCRYPTED"===l.procType.type)throw new Error("Could not convert certificate from PEM; PEM is encrypted.");var f=a.util.createBuffer(l.body);null===s&&(s=a.asn1.fromDer(f.bytes(),!1));var h=a.util.createBuffer();c(h,3,f),i.putBuffer(h)}n=a.pki.certificateFromAsn1(s),r?e.session.clientCertificate=n:e.session.serverCertificate=n}catch(t){return e.error(e,{message:"Could not send certificate list.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.bad_certificate}})}var d=3+i.length(),y=a.util.createBuffer();return y.putByte(u.HandshakeType.certificate),y.putInt24(d),c(y,3,i),y},u.createClientKeyExchange=function(e){var t=a.util.createBuffer();t.putByte(e.session.clientHelloVersion.major),t.putByte(e.session.clientHelloVersion.minor),t.putBytes(a.random.getBytes(46));var r=e.session.sp;r.pre_master_secret=t.getBytes();var n=(t=e.session.serverCertificate.publicKey.encrypt(r.pre_master_secret)).length+2,i=a.util.createBuffer();return i.putByte(u.HandshakeType.client_key_exchange),i.putInt24(n),i.putInt16(t.length),i.putBytes(t),i},u.createServerKeyExchange=function(e){var t=a.util.createBuffer();return t},u.getClientSignature=function(e,t){var r=a.util.createBuffer();r.putBuffer(e.session.md5.digest()),r.putBuffer(e.session.sha1.digest()),r=r.getBytes(),e.getSignature=e.getSignature||function(e,t,r){var n=null;if(e.getPrivateKey)try{n=e.getPrivateKey(e,e.session.clientCertificate),n=a.pki.privateKeyFromPem(n)}catch(t){e.error(e,{message:"Could not get private key.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}})}null===n?e.error(e,{message:"No private key set.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}}):t=n.sign(t,null),r(e,t)},e.getSignature(e,r,t)},u.createCertificateVerify=function(e,t){var r=t.length+2,n=a.util.createBuffer();return n.putByte(u.HandshakeType.certificate_verify),n.putInt24(r),n.putInt16(t.length),n.putBytes(t),n},u.createCertificateRequest=function(e){var t=a.util.createBuffer();t.putByte(1);var r=a.util.createBuffer();for(var n in e.caStore.certs){var i=e.caStore.certs[n],s=a.pki.distinguishedNameToAsn1(i.subject),o=a.asn1.toDer(s);r.putInt16(o.length()),r.putBuffer(o)}var l=1+t.length()+2+r.length(),p=a.util.createBuffer();return p.putByte(u.HandshakeType.certificate_request),p.putInt24(l),c(p,1,t),c(p,2,r),p},u.createServerHelloDone=function(e){var t=a.util.createBuffer();return t.putByte(u.HandshakeType.server_hello_done),t.putInt24(0),t},u.createChangeCipherSpec=function(){var e=a.util.createBuffer();return e.putByte(1),e},u.createFinished=function(e){var t=a.util.createBuffer();t.putBuffer(e.session.md5.digest()),t.putBuffer(e.session.sha1.digest());var r=e.entity===u.ConnectionEnd.client,i=e.session.sp,s=r?"client finished":"server finished";t=n(i.master_secret,s,t.getBytes(),12);var o=a.util.createBuffer();return o.putByte(u.HandshakeType.finished),o.putInt24(t.length()),o.putBuffer(t),o},u.createHeartbeat=function(e,t,r){void 0===r&&(r=t.length);var n=a.util.createBuffer();n.putByte(e),n.putInt16(r),n.putBytes(t);var i=n.length(),s=Math.max(16,i-r-3);return n.putBytes(a.random.getBytes(s)),n},u.queue=function(e,t){if(t&&(0!==t.fragment.length()||t.type!==u.ContentType.handshake&&t.type!==u.ContentType.alert&&t.type!==u.ContentType.change_cipher_spec)){if(t.type===u.ContentType.handshake){var r=t.fragment.bytes();e.session.md5.update(r),e.session.sha1.update(r),r=null}var n;if(t.fragment.length()<=u.MaxFragment)n=[t];else{n=[];for(var i=t.fragment.bytes();i.length>u.MaxFragment;)n.push(u.createRecord(e,{type:t.type,data:a.util.createBuffer(i.slice(0,u.MaxFragment))})),i=i.slice(u.MaxFragment);i.length>0&&n.push(u.createRecord(e,{type:t.type,data:a.util.createBuffer(i)}))}for(var s=0;s<n.length&&!e.fail;++s){var o=n[s];e.state.current.write.update(e,o)&&e.records.push(o)}}},u.flush=function(e){for(var t=0;t<e.records.length;++t){var r=e.records[t];e.tlsData.putByte(r.type),e.tlsData.putByte(r.version.major),e.tlsData.putByte(r.version.minor),e.tlsData.putInt16(r.fragment.length()),e.tlsData.putBuffer(e.records[t].fragment)}return e.records=[],e.tlsDataReady(e)};var j=function(e){switch(e){case!0:return!0;case a.pki.certificateError.bad_certificate:return u.Alert.Description.bad_certificate;case a.pki.certificateError.unsupported_certificate:return u.Alert.Description.unsupported_certificate;case a.pki.certificateError.certificate_revoked:return u.Alert.Description.certificate_revoked;case a.pki.certificateError.certificate_expired:return u.Alert.Description.certificate_expired;case a.pki.certificateError.certificate_unknown:return u.Alert.Description.certificate_unknown;case a.pki.certificateError.unknown_ca:return u.Alert.Description.unknown_ca;default:return u.Alert.Description.bad_certificate}};for(var G in u.verifyCertificateChain=function(e,t){try{var r={};for(var n in e.verifyOptions)r[n]=e.verifyOptions[n];r.verify=function(t,r,n){j(t);var i=e.verify(e,t,r,n);if(!0!==i){if("object"==typeof i&&!a.util.isArray(i)){var s=new Error("The application rejected the certificate.");throw s.send=!0,s.alert={level:u.Alert.Level.fatal,description:u.Alert.Description.bad_certificate},i.message&&(s.message=i.message),i.alert&&(s.alert.description=i.alert),s}i!==t&&(i=function(e){switch(e){case!0:return!0;case u.Alert.Description.bad_certificate:return a.pki.certificateError.bad_certificate;case u.Alert.Description.unsupported_certificate:return a.pki.certificateError.unsupported_certificate;case u.Alert.Description.certificate_revoked:return a.pki.certificateError.certificate_revoked;case u.Alert.Description.certificate_expired:return a.pki.certificateError.certificate_expired;case u.Alert.Description.certificate_unknown:return a.pki.certificateError.certificate_unknown;case u.Alert.Description.unknown_ca:return a.pki.certificateError.unknown_ca;default:return a.pki.certificateError.bad_certificate}}(i))}return i},a.pki.verifyCertificateChain(e.caStore,t,r)}catch(t){var i=t;("object"!=typeof i||a.util.isArray(i))&&(i={send:!0,alert:{level:u.Alert.Level.fatal,description:j(t)}}),"send"in i||(i.send=!0),"alert"in i||(i.alert={level:u.Alert.Level.fatal,description:j(i.error)}),e.error(e,i)}return!e.fail},u.createSessionCache=function(e,t){var r=null;if(e&&e.getSession&&e.setSession&&e.order)r=e;else{for(var n in(r={}).cache=e||{},r.capacity=Math.max(t||100,1),r.order=[],e)r.order.length<=t?r.order.push(n):delete e[n];r.getSession=function(e){var t=null,n=null;if(e?n=a.util.bytesToHex(e):r.order.length>0&&(n=r.order[0]),null!==n&&n in r.cache)for(var i in t=r.cache[n],delete r.cache[n],r.order)if(r.order[i]===n){r.order.splice(i,1);break}return t},r.setSession=function(e,t){if(r.order.length===r.capacity){var n=r.order.shift();delete r.cache[n]}n=a.util.bytesToHex(e);r.order.push(n),r.cache[n]=t}}return r},u.createConnection=function(e){var t=null;t=e.caStore?a.util.isArray(e.caStore)?a.pki.createCaStore(e.caStore):e.caStore:a.pki.createCaStore();var r=e.cipherSuites||null;if(null===r)for(var n in r=[],u.CipherSuites)r.push(u.CipherSuites[n]);var i=e.server?u.ConnectionEnd.server:u.ConnectionEnd.client,s=e.sessionCache?u.createSessionCache(e.sessionCache):null,o={version:{major:u.Version.major,minor:u.Version.minor},entity:i,sessionId:e.sessionId,caStore:t,sessionCache:s,cipherSuites:r,connected:e.connected,virtualHost:e.virtualHost||null,verifyClient:e.verifyClient||!1,verify:e.verify||function(e,t,r,a){return t},verifyOptions:e.verifyOptions||{},getCertificate:e.getCertificate||null,getPrivateKey:e.getPrivateKey||null,getSignature:e.getSignature||null,input:a.util.createBuffer(),tlsData:a.util.createBuffer(),data:a.util.createBuffer(),tlsDataReady:e.tlsDataReady,dataReady:e.dataReady,heartbeatReceived:e.heartbeatReceived,closed:e.closed,error:function(t,r){r.origin=r.origin||(t.entity===u.ConnectionEnd.client?"client":"server"),r.send&&(u.queue(t,u.createAlert(t,r.alert)),u.flush(t));var a=!1!==r.fatal;a&&(t.fail=!0),e.error(t,r),a&&t.close(!1)},deflate:e.deflate||null,inflate:e.inflate||null,reset:function(e){o.version={major:u.Version.major,minor:u.Version.minor},o.record=null,o.session=null,o.peerCertificate=null,o.state={pending:null,current:null},o.expect=(o.entity,u.ConnectionEnd.client,0),o.fragmented=null,o.records=[],o.open=!1,o.handshakes=0,o.handshaking=!1,o.isConnected=!1,o.fail=!(e||void 0===e),o.input.clear(),o.tlsData.clear(),o.data.clear(),o.state.current=u.createConnectionState(o)}};o.reset();return o.handshake=function(e){if(o.entity!==u.ConnectionEnd.client)o.error(o,{message:"Cannot initiate handshake as a server.",fatal:!1});else if(o.handshaking)o.error(o,{message:"Handshake already in progress.",fatal:!1});else{o.fail&&!o.open&&0===o.handshakes&&(o.fail=!1),o.handshaking=!0;var t=null;(e=e||"").length>0&&(o.sessionCache&&(t=o.sessionCache.getSession(e)),null===t&&(e="")),0===e.length&&o.sessionCache&&null!==(t=o.sessionCache.getSession())&&(e=t.id),o.session={id:e,version:null,cipherSuite:null,compressionMethod:null,serverCertificate:null,certificateRequest:null,clientCertificate:null,sp:{},md5:a.md.md5.create(),sha1:a.md.sha1.create()},t&&(o.version=t.version,o.session.sp=t.sp),o.session.sp.client_random=u.createRandom().getBytes(),o.open=!0,u.queue(o,u.createRecord(o,{type:u.ContentType.handshake,data:u.createClientHello(o)})),u.flush(o)}},o.process=function(e){var t=0;return e&&o.input.putBytes(e),o.fail||(null!==o.record&&o.record.ready&&o.record.fragment.isEmpty()&&(o.record=null),null===o.record&&(t=function(e){var t=0,r=e.input,n=r.length();if(n<5)t=5-n;else{e.record={type:r.getByte(),version:{major:r.getByte(),minor:r.getByte()},length:r.getInt16(),fragment:a.util.createBuffer(),ready:!1};var i=e.record.version.major===e.version.major;i&&e.session&&e.session.version&&(i=e.record.version.minor===e.version.minor),i||e.error(e,{message:"Incompatible TLS version.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}})}return t}(o)),o.fail||null===o.record||o.record.ready||(t=function(e){var t=0,r=e.input,a=r.length();a<e.record.length?t=e.record.length-a:(e.record.fragment.putBytes(r.getBytes(e.record.length)),r.compact(),e.state.current.read.update(e,e.record)&&(null!==e.fragmented&&(e.fragmented.type===e.record.type?(e.fragmented.fragment.putBuffer(e.record.fragment),e.record=e.fragmented):e.error(e,{message:"Invalid fragmented record.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unexpected_message}})),e.record.ready=!0));return t}(o)),!o.fail&&null!==o.record&&o.record.ready&&function(e,t){var r=t.type-u.ContentType.change_cipher_spec,a=_[e.entity][e.expect];r in a?a[r](e,t):u.handleUnexpected(e,t)}(o,o.record)),t},o.prepare=function(e){return u.queue(o,u.createRecord(o,{type:u.ContentType.application_data,data:a.util.createBuffer(e)})),u.flush(o)},o.prepareHeartbeatRequest=function(e,t){return e instanceof a.util.ByteBuffer&&(e=e.bytes()),void 0===t&&(t=e.length),o.expectedHeartbeatPayload=e,u.queue(o,u.createRecord(o,{type:u.ContentType.heartbeat,data:u.createHeartbeat(u.HeartbeatMessageType.heartbeat_request,e,t)})),u.flush(o)},o.close=function(e){if(!o.fail&&o.sessionCache&&o.session){var t={id:o.session.id,version:o.session.version,sp:o.session.sp};t.sp.keys=null,o.sessionCache.setSession(t.id,t)}o.open&&(o.open=!1,o.input.clear(),(o.isConnected||o.handshaking)&&(o.isConnected=o.handshaking=!1,u.queue(o,u.createAlert(o,{level:u.Alert.Level.warning,description:u.Alert.Description.close_notify})),u.flush(o)),o.closed(o)),o.reset(e)},o},e.exports=a.tls=a.tls||{},u)"function"!=typeof u[G]&&(a.tls[G]=u[G]);a.tls.prf_tls1=n,a.tls.hmac_sha1=function(e,t,r){var n=a.hmac.create();n.start("SHA1",e);var i=a.util.createBuffer();return i.putInt32(t[0]),i.putInt32(t[1]),i.putByte(r.type),i.putByte(r.version.major),i.putByte(r.version.minor),i.putInt16(r.length),i.putBytes(r.fragment.bytes()),n.update(i.getBytes()),n.digest().getBytes()},a.tls.createSessionCache=u.createSessionCache,a.tls.createConnection=u.createConnection},function(e,t,r){var a=r(0);r(3),r(6),r(22),r(7),r(15),r(28),r(18),r(11),r(1),r(17);var n=a.asn1,i=e.exports=a.pki=a.pki||{};i.pemToDer=function(e){var t=a.pem.decode(e)[0];if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert PEM to DER; PEM is encrypted.");return a.util.createBuffer(t.body)},i.privateKeyFromPem=function(e){var t=a.pem.decode(e)[0];if("PRIVATE KEY"!==t.type&&"RSA PRIVATE KEY"!==t.type){var r=new Error('Could not convert private key from PEM; PEM header type is not "PRIVATE KEY" or "RSA PRIVATE KEY".');throw r.headerType=t.type,r}if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert private key from PEM; PEM is encrypted.");var s=n.fromDer(t.body);return i.privateKeyFromAsn1(s)},i.privateKeyToPem=function(e,t){var r={type:"RSA PRIVATE KEY",body:n.toDer(i.privateKeyToAsn1(e)).getBytes()};return a.pem.encode(r,{maxline:t})},i.privateKeyInfoToPem=function(e,t){var r={type:"PRIVATE KEY",body:n.toDer(e).getBytes()};return a.pem.encode(r,{maxline:t})}},function(e,t,r){var a=r(0);if(r(5),r(3),r(10),r(4),r(6),r(15),r(7),r(2),r(25),r(11),r(1),void 0===n)var n=a.jsbn.BigInteger;var i=a.asn1,s=a.pki=a.pki||{};e.exports=s.pbe=a.pbe=a.pbe||{};var o=s.oids,c={name:"EncryptedPrivateKeyInfo",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedPrivateKeyInfo.encryptionAlgorithm",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:i.Class.UNIVERSAL,type:i.Type.OID,constructed:!1,capture:"encryptionOid"},{name:"AlgorithmIdentifier.parameters",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,captureAsn1:"encryptionParams"}]},{name:"EncryptedPrivateKeyInfo.encryptedData",tagClass:i.Class.UNIVERSAL,type:i.Type.OCTETSTRING,constructed:!1,capture:"encryptedData"}]},u={name:"PBES2Algorithms",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.keyDerivationFunc",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.keyDerivationFunc.oid",tagClass:i.Class.UNIVERSAL,type:i.Type.OID,constructed:!1,capture:"kdfOid"},{name:"PBES2Algorithms.params",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.params.salt",tagClass:i.Class.UNIVERSAL,type:i.Type.OCTETSTRING,constructed:!1,capture:"kdfSalt"},{name:"PBES2Algorithms.params.iterationCount",tagClass:i.Class.UNIVERSAL,type:i.Type.INTEGER,constructed:!1,capture:"kdfIterationCount"},{name:"PBES2Algorithms.params.keyLength",tagClass:i.Class.UNIVERSAL,type:i.Type.INTEGER,constructed:!1,optional:!0,capture:"keyLength"},{name:"PBES2Algorithms.params.prf",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,optional:!0,value:[{name:"PBES2Algorithms.params.prf.algorithm",tagClass:i.Class.UNIVERSAL,type:i.Type.OID,constructed:!1,capture:"prfOid"}]}]}]},{name:"PBES2Algorithms.encryptionScheme",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.encryptionScheme.oid",tagClass:i.Class.UNIVERSAL,type:i.Type.OID,constructed:!1,capture:"encOid"},{name:"PBES2Algorithms.encryptionScheme.iv",tagClass:i.Class.UNIVERSAL,type:i.Type.OCTETSTRING,constructed:!1,capture:"encIv"}]}]},l={name:"pkcs-12PbeParams",tagClass:i.Class.UNIVERSAL,type:i.Type.SEQUENCE,constructed:!0,value:[{name:"pkcs-12PbeParams.salt",tagClass:i.Class.UNIVERSAL,type:i.Type.OCTETSTRING,constructed:!1,capture:"salt"},{name:"pkcs-12PbeParams.iterations",tagClass:i.Class.UNIVERSAL,type:i.Type.INTEGER,constructed:!1,capture:"iterations"}]};function p(e,t){return e.start().update(t).digest().getBytes()}function f(e){var t;if(e){if(!(t=s.oids[i.derToOid(e)])){var r=new Error("Unsupported PRF OID.");throw r.oid=e,r.supported=["hmacWithSHA1","hmacWithSHA224","hmacWithSHA256","hmacWithSHA384","hmacWithSHA512"],r}}else t="hmacWithSHA1";return h(t)}function h(e){var t=a.md;switch(e){case"hmacWithSHA224":t=a.md.sha512;case"hmacWithSHA1":case"hmacWithSHA256":case"hmacWithSHA384":case"hmacWithSHA512":e=e.substr(8).toLowerCase();break;default:var r=new Error("Unsupported PRF algorithm.");throw r.algorithm=e,r.supported=["hmacWithSHA1","hmacWithSHA224","hmacWithSHA256","hmacWithSHA384","hmacWithSHA512"],r}if(!t||!(e in t))throw new Error("Unknown hash algorithm: "+e);return t[e].create()}s.encryptPrivateKeyInfo=function(e,t,r){(r=r||{}).saltSize=r.saltSize||8,r.count=r.count||2048,r.algorithm=r.algorithm||"aes128",r.prfAlgorithm=r.prfAlgorithm||"sha1";var n,c,u,l=a.random.getBytesSync(r.saltSize),p=r.count,f=i.integerToDer(p);if(0===r.algorithm.indexOf("aes")||"des"===r.algorithm){var d,y,g;switch(r.algorithm){case"aes128":n=16,d=16,y=o["aes128-CBC"],g=a.aes.createEncryptionCipher;break;case"aes192":n=24,d=16,y=o["aes192-CBC"],g=a.aes.createEncryptionCipher;break;case"aes256":n=32,d=16,y=o["aes256-CBC"],g=a.aes.createEncryptionCipher;break;case"des":n=8,d=8,y=o.desCBC,g=a.des.createEncryptionCipher;break;default:throw(T=new Error("Cannot encrypt private key. Unknown encryption algorithm.")).algorithm=r.algorithm,T}var v="hmacWith"+r.prfAlgorithm.toUpperCase(),m=h(v),C=a.pkcs5.pbkdf2(t,l,p,n,m),E=a.random.getBytesSync(d);(I=g(C)).start(E),I.update(i.toDer(e)),I.finish(),u=I.output.getBytes();var S=function(e,t,r,n){var o=i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OCTETSTRING,!1,e),i.create(i.Class.UNIVERSAL,i.Type.INTEGER,!1,t.getBytes())]);"hmacWithSHA1"!==n&&o.value.push(i.create(i.Class.UNIVERSAL,i.Type.INTEGER,!1,a.util.hexToBytes(r.toString(16))),i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(s.oids[n]).getBytes()),i.create(i.Class.UNIVERSAL,i.Type.NULL,!1,"")]));return o}(l,f,n,v);c=i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(o.pkcs5PBES2).getBytes()),i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(o.pkcs5PBKDF2).getBytes()),S]),i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(y).getBytes()),i.create(i.Class.UNIVERSAL,i.Type.OCTETSTRING,!1,E)])])])}else{var T;if("3des"!==r.algorithm)throw(T=new Error("Cannot encrypt private key. Unknown encryption algorithm.")).algorithm=r.algorithm,T;n=24;var I,A=new a.util.ByteBuffer(l);C=s.pbe.generatePkcs12Key(t,A,1,p,n),E=s.pbe.generatePkcs12Key(t,A,2,p,n);(I=a.des.createEncryptionCipher(C)).start(E),I.update(i.toDer(e)),I.finish(),u=I.output.getBytes(),c=i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OID,!1,i.oidToDer(o["pbeWithSHAAnd3-KeyTripleDES-CBC"]).getBytes()),i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[i.create(i.Class.UNIVERSAL,i.Type.OCTETSTRING,!1,l),i.create(i.Class.UNIVERSAL,i.Type.INTEGER,!1,f.getBytes())])])}return i.create(i.Class.UNIVERSAL,i.Type.SEQUENCE,!0,[c,i.create(i.Class.UNIVERSAL,i.Type.OCTETSTRING,!1,u)])},s.decryptPrivateKeyInfo=function(e,t){var r=null,n={},o=[];if(!i.validate(e,c,n,o)){var u=new Error("Cannot read encrypted private key. ASN.1 object is not a supported EncryptedPrivateKeyInfo.");throw u.errors=o,u}var l=i.derToOid(n.encryptionOid),p=s.pbe.getCipher(l,n.encryptionParams,t),f=a.util.createBuffer(n.encryptedData);return p.update(f),p.finish()&&(r=i.fromDer(p.output)),r},s.encryptedPrivateKeyToPem=function(e,t){var r={type:"ENCRYPTED PRIVATE KEY",body:i.toDer(e).getBytes()};return a.pem.encode(r,{maxline:t})},s.encryptedPrivateKeyFromPem=function(e){var t=a.pem.decode(e)[0];if("ENCRYPTED PRIVATE KEY"!==t.type){var r=new Error('Could not convert encrypted private key from PEM; PEM header type is "ENCRYPTED PRIVATE KEY".');throw r.headerType=t.type,r}if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert encrypted private key from PEM; PEM is encrypted.");return i.fromDer(t.body)},s.encryptRsaPrivateKey=function(e,t,r){if(!(r=r||{}).legacy){var n=s.wrapRsaPrivateKey(s.privateKeyToAsn1(e));return n=s.encryptPrivateKeyInfo(n,t,r),s.encryptedPrivateKeyToPem(n)}var o,c,u,l;switch(r.algorithm){case"aes128":o="AES-128-CBC",u=16,c=a.random.getBytesSync(16),l=a.aes.createEncryptionCipher;break;case"aes192":o="AES-192-CBC",u=24,c=a.random.getBytesSync(16),l=a.aes.createEncryptionCipher;break;case"aes256":o="AES-256-CBC",u=32,c=a.random.getBytesSync(16),l=a.aes.createEncryptionCipher;break;case"3des":o="DES-EDE3-CBC",u=24,c=a.random.getBytesSync(8),l=a.des.createEncryptionCipher;break;case"des":o="DES-CBC",u=8,c=a.random.getBytesSync(8),l=a.des.createEncryptionCipher;break;default:var p=new Error('Could not encrypt RSA private key; unsupported encryption algorithm "'+r.algorithm+'".');throw p.algorithm=r.algorithm,p}var f=l(a.pbe.opensslDeriveBytes(t,c.substr(0,8),u));f.start(c),f.update(i.toDer(s.privateKeyToAsn1(e))),f.finish();var h={type:"RSA PRIVATE KEY",procType:{version:"4",type:"ENCRYPTED"},dekInfo:{algorithm:o,parameters:a.util.bytesToHex(c).toUpperCase()},body:f.output.getBytes()};return a.pem.encode(h)},s.decryptRsaPrivateKey=function(e,t){var r=null,n=a.pem.decode(e)[0];if("ENCRYPTED PRIVATE KEY"!==n.type&&"PRIVATE KEY"!==n.type&&"RSA PRIVATE KEY"!==n.type)throw(u=new Error('Could not convert private key from PEM; PEM header type is not "ENCRYPTED PRIVATE KEY", "PRIVATE KEY", or "RSA PRIVATE KEY".')).headerType=u,u;if(n.procType&&"ENCRYPTED"===n.procType.type){var o,c;switch(n.dekInfo.algorithm){case"DES-CBC":o=8,c=a.des.createDecryptionCipher;break;case"DES-EDE3-CBC":o=24,c=a.des.createDecryptionCipher;break;case"AES-128-CBC":o=16,c=a.aes.createDecryptionCipher;break;case"AES-192-CBC":o=24,c=a.aes.createDecryptionCipher;break;case"AES-256-CBC":o=32,c=a.aes.createDecryptionCipher;break;case"RC2-40-CBC":o=5,c=function(e){return a.rc2.createDecryptionCipher(e,40)};break;case"RC2-64-CBC":o=8,c=function(e){return a.rc2.createDecryptionCipher(e,64)};break;case"RC2-128-CBC":o=16,c=function(e){return a.rc2.createDecryptionCipher(e,128)};break;default:var u;throw(u=new Error('Could not decrypt private key; unsupported encryption algorithm "'+n.dekInfo.algorithm+'".')).algorithm=n.dekInfo.algorithm,u}var l=a.util.hexToBytes(n.dekInfo.parameters),p=c(a.pbe.opensslDeriveBytes(t,l.substr(0,8),o));if(p.start(l),p.update(a.util.createBuffer(n.body)),!p.finish())return r;r=p.output.getBytes()}else r=n.body;return null!==(r="ENCRYPTED PRIVATE KEY"===n.type?s.decryptPrivateKeyInfo(i.fromDer(r),t):i.fromDer(r))&&(r=s.privateKeyFromAsn1(r)),r},s.pbe.generatePkcs12Key=function(e,t,r,n,i,s){var o,c;if(null==s){if(!("sha1"in a.md))throw new Error('"sha1" hash algorithm unavailable.');s=a.md.sha1.create()}var u=s.digestLength,l=s.blockLength,p=new a.util.ByteBuffer,f=new a.util.ByteBuffer;if(null!=e){for(c=0;c<e.length;c++)f.putInt16(e.charCodeAt(c));f.putInt16(0)}var h=f.length(),d=t.length(),y=new a.util.ByteBuffer;y.fillWithByte(r,l);var g=l*Math.ceil(d/l),v=new a.util.ByteBuffer;for(c=0;c<g;c++)v.putByte(t.at(c%d));var m=l*Math.ceil(h/l),C=new a.util.ByteBuffer;for(c=0;c<m;c++)C.putByte(f.at(c%h));var E=v;E.putBuffer(C);for(var S=Math.ceil(i/u),T=1;T<=S;T++){var I=new a.util.ByteBuffer;I.putBytes(y.bytes()),I.putBytes(E.bytes());for(var A=0;A<n;A++)s.start(),s.update(I.getBytes()),I=s.digest();var B=new a.util.ByteBuffer;for(c=0;c<l;c++)B.putByte(I.at(c%u));var b=Math.ceil(d/l)+Math.ceil(h/l),N=new a.util.ByteBuffer;for(o=0;o<b;o++){var R=new a.util.ByteBuffer(E.getBytes(l)),w=511;for(c=B.length()-1;c>=0;c--)w>>=8,w+=B.at(c)+R.at(c),R.setAt(c,255&w);N.putBuffer(R)}E=N,p.putBuffer(I)}return p.truncate(p.length()-i),p},s.pbe.getCipher=function(e,t,r){switch(e){case s.oids.pkcs5PBES2:return s.pbe.getCipherForPBES2(e,t,r);case s.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:case s.oids["pbewithSHAAnd40BitRC2-CBC"]:return s.pbe.getCipherForPKCS12PBE(e,t,r);default:var a=new Error("Cannot read encrypted PBE data block. Unsupported OID.");throw a.oid=e,a.supportedOids=["pkcs5PBES2","pbeWithSHAAnd3-KeyTripleDES-CBC","pbewithSHAAnd40BitRC2-CBC"],a}},s.pbe.getCipherForPBES2=function(e,t,r){var n,o={},c=[];if(!i.validate(t,u,o,c))throw(n=new Error("Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.")).errors=c,n;if((e=i.derToOid(o.kdfOid))!==s.oids.pkcs5PBKDF2)throw(n=new Error("Cannot read encrypted private key. Unsupported key derivation function OID.")).oid=e,n.supportedOids=["pkcs5PBKDF2"],n;if((e=i.derToOid(o.encOid))!==s.oids["aes128-CBC"]&&e!==s.oids["aes192-CBC"]&&e!==s.oids["aes256-CBC"]&&e!==s.oids["des-EDE3-CBC"]&&e!==s.oids.desCBC)throw(n=new Error("Cannot read encrypted private key. Unsupported encryption scheme OID.")).oid=e,n.supportedOids=["aes128-CBC","aes192-CBC","aes256-CBC","des-EDE3-CBC","desCBC"],n;var l,p,h=o.kdfSalt,d=a.util.createBuffer(o.kdfIterationCount);switch(d=d.getInt(d.length()<<3),s.oids[e]){case"aes128-CBC":l=16,p=a.aes.createDecryptionCipher;break;case"aes192-CBC":l=24,p=a.aes.createDecryptionCipher;break;case"aes256-CBC":l=32,p=a.aes.createDecryptionCipher;break;case"des-EDE3-CBC":l=24,p=a.des.createDecryptionCipher;break;case"desCBC":l=8,p=a.des.createDecryptionCipher}var y=f(o.prfOid),g=a.pkcs5.pbkdf2(r,h,d,l,y),v=o.encIv,m=p(g);return m.start(v),m},s.pbe.getCipherForPKCS12PBE=function(e,t,r){var n={},o=[];if(!i.validate(t,l,n,o))throw(y=new Error("Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.")).errors=o,y;var c,u,p,h=a.util.createBuffer(n.salt),d=a.util.createBuffer(n.iterations);switch(d=d.getInt(d.length()<<3),e){case s.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:c=24,u=8,p=a.des.startDecrypting;break;case s.oids["pbewithSHAAnd40BitRC2-CBC"]:c=5,u=8,p=function(e,t){var r=a.rc2.createDecryptionCipher(e,40);return r.start(t,null),r};break;default:var y;throw(y=new Error("Cannot read PKCS #12 PBE data block. Unsupported OID.")).oid=e,y}var g=f(n.prfOid),v=s.pbe.generatePkcs12Key(r,h,1,d,c,g);return g.start(),p(v,s.pbe.generatePkcs12Key(r,h,2,d,u,g))},s.pbe.opensslDeriveBytes=function(e,t,r,n){if(null==n){if(!("md5"in a.md))throw new Error('"md5" hash algorithm unavailable.');n=a.md.md5.create()}null===t&&(t="");for(var i=[p(n,e+t)],s=16,o=1;s<r;++o,s+=16)i.push(p(n,i[o-1]+e+t));return i.join("").substr(0,r)}},function(e,t,r){var a=r(0);r(4),r(1);var n=e.exports=a.sha256=a.sha256||{};a.md.sha256=a.md.algorithms.sha256=n,n.create=function(){s||(i=String.fromCharCode(128),i+=a.util.fillString(String.fromCharCode(0),64),o=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],s=!0);var e=null,t=a.util.createBuffer(),r=new Array(64),n={algorithm:"sha256",blockLength:64,digestLength:32,messageLength:0,fullMessageLength:null,messageLengthSize:8,start:function(){n.messageLength=0,n.fullMessageLength=n.messageLength64=[];for(var r=n.messageLengthSize/4,i=0;i<r;++i)n.fullMessageLength.push(0);return t=a.util.createBuffer(),e={h0:1779033703,h1:3144134277,h2:1013904242,h3:2773480762,h4:1359893119,h5:2600822924,h6:528734635,h7:1541459225},n}};return n.start(),n.update=function(i,s){"utf8"===s&&(i=a.util.encodeUtf8(i));var o=i.length;n.messageLength+=o,o=[o/4294967296>>>0,o>>>0];for(var u=n.fullMessageLength.length-1;u>=0;--u)n.fullMessageLength[u]+=o[1],o[1]=o[0]+(n.fullMessageLength[u]/4294967296>>>0),n.fullMessageLength[u]=n.fullMessageLength[u]>>>0,o[0]=o[1]/4294967296>>>0;return t.putBytes(i),c(e,r,t),(t.read>2048||0===t.length())&&t.compact(),n},n.digest=function(){var s=a.util.createBuffer();s.putBytes(t.bytes());var o,u=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize&n.blockLength-1;s.putBytes(i.substr(0,n.blockLength-u));for(var l=8*n.fullMessageLength[0],p=0;p<n.fullMessageLength.length-1;++p)l+=(o=8*n.fullMessageLength[p+1])/4294967296>>>0,s.putInt32(l>>>0),l=o>>>0;s.putInt32(l);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3,h4:e.h4,h5:e.h5,h6:e.h6,h7:e.h7};c(f,r,s);var h=a.util.createBuffer();return h.putInt32(f.h0),h.putInt32(f.h1),h.putInt32(f.h2),h.putInt32(f.h3),h.putInt32(f.h4),h.putInt32(f.h5),h.putInt32(f.h6),h.putInt32(f.h7),h},n};var i=null,s=!1,o=null;function c(e,t,r){for(var a,n,i,s,c,u,l,p,f,h,d,y,g,v=r.length();v>=64;){for(c=0;c<16;++c)t[c]=r.getInt32();for(;c<64;++c)a=((a=t[c-2])>>>17|a<<15)^(a>>>19|a<<13)^a>>>10,n=((n=t[c-15])>>>7|n<<25)^(n>>>18|n<<14)^n>>>3,t[c]=a+t[c-7]+n+t[c-16]|0;for(u=e.h0,l=e.h1,p=e.h2,f=e.h3,h=e.h4,d=e.h5,y=e.h6,g=e.h7,c=0;c<64;++c)i=(u>>>2|u<<30)^(u>>>13|u<<19)^(u>>>22|u<<10),s=u&l|p&(u^l),a=g+((h>>>6|h<<26)^(h>>>11|h<<21)^(h>>>25|h<<7))+(y^h&(d^y))+o[c]+t[c],g=y,y=d,d=h,h=f+a>>>0,f=p,p=l,l=u,u=a+(n=i+s)>>>0;e.h0=e.h0+u|0,e.h1=e.h1+l|0,e.h2=e.h2+p|0,e.h3=e.h3+f|0,e.h4=e.h4+h|0,e.h5=e.h5+d|0,e.h6=e.h6+y|0,e.h7=e.h7+g|0,v-=64}}},function(e,t,r){var a=r(0);r(1);var n=null;!a.util.isNodejs||a.options.usePureJavaScript||process.versions["node-webkit"]||(n=r(16)),(e.exports=a.prng=a.prng||{}).create=function(e){for(var t={plugin:e,key:null,seed:null,time:null,reseeds:0,generated:0,keyBytes:""},r=e.md,i=new Array(32),s=0;s<32;++s)i[s]=r.create();function o(){if(t.pools[0].messageLength>=32)return c();var e=32-t.pools[0].messageLength<<5;t.collect(t.seedFileSync(e)),c()}function c(){t.reseeds=4294967295===t.reseeds?0:t.reseeds+1;var e=t.plugin.md.create();e.update(t.keyBytes);for(var r=1,a=0;a<32;++a)t.reseeds%r==0&&(e.update(t.pools[a].digest().getBytes()),t.pools[a].start()),r<<=1;t.keyBytes=e.digest().getBytes(),e.start(),e.update(t.keyBytes);var n=e.digest().getBytes();t.key=t.plugin.formatKey(t.keyBytes),t.seed=t.plugin.formatSeed(n),t.generated=0}function u(e){var t=null,r=a.util.globalScope,n=r.crypto||r.msCrypto;n&&n.getRandomValues&&(t=function(e){return n.getRandomValues(e)});var i=a.util.createBuffer();if(t)for(;i.length()<e;){var s=Math.max(1,Math.min(e-i.length(),65536)/4),o=new Uint32Array(Math.floor(s));try{t(o);for(var c=0;c<o.length;++c)i.putInt32(o[c])}catch(e){if(!("undefined"!=typeof QuotaExceededError&&e instanceof QuotaExceededError))throw e}}if(i.length()<e)for(var u,l,p,f=Math.floor(65536*Math.random());i.length()<e;){l=16807*(65535&f),l+=(32767&(u=16807*(f>>16)))<<16,f=4294967295&(l=(2147483647&(l+=u>>15))+(l>>31));for(c=0;c<3;++c)p=f>>>(c<<3),p^=Math.floor(256*Math.random()),i.putByte(255&p)}return i.getBytes(e)}return t.pools=i,t.pool=0,t.generate=function(e,r){if(!r)return t.generateSync(e);var n=t.plugin.cipher,i=t.plugin.increment,s=t.plugin.formatKey,o=t.plugin.formatSeed,u=a.util.createBuffer();t.key=null,function l(p){if(p)return r(p);if(u.length()>=e)return r(null,u.getBytes(e));t.generated>1048575&&(t.key=null);if(null===t.key)return a.util.nextTick((function(){!function(e){if(t.pools[0].messageLength>=32)return c(),e();var r=32-t.pools[0].messageLength<<5;t.seedFile(r,(function(r,a){if(r)return e(r);t.collect(a),c(),e()}))}(l)}));var f=n(t.key,t.seed);t.generated+=f.length,u.putBytes(f),t.key=s(n(t.key,i(t.seed))),t.seed=o(n(t.key,t.seed)),a.util.setImmediate(l)}()},t.generateSync=function(e){var r=t.plugin.cipher,n=t.plugin.increment,i=t.plugin.formatKey,s=t.plugin.formatSeed;t.key=null;for(var c=a.util.createBuffer();c.length()<e;){t.generated>1048575&&(t.key=null),null===t.key&&o();var u=r(t.key,t.seed);t.generated+=u.length,c.putBytes(u),t.key=i(r(t.key,n(t.seed))),t.seed=s(r(t.key,t.seed))}return c.getBytes(e)},n?(t.seedFile=function(e,t){n.randomBytes(e,(function(e,r){if(e)return t(e);t(null,r.toString())}))},t.seedFileSync=function(e){return n.randomBytes(e).toString()}):(t.seedFile=function(e,t){try{t(null,u(e))}catch(e){t(e)}},t.seedFileSync=u),t.collect=function(e){for(var r=e.length,a=0;a<r;++a)t.pools[t.pool].update(e.substr(a,1)),t.pool=31===t.pool?0:t.pool+1},t.collectInt=function(e,r){for(var a="",n=0;n<r;n+=8)a+=String.fromCharCode(e>>n&255);t.collect(a)},t.registerWorker=function(e){if(e===self)t.seedFile=function(e,t){self.addEventListener("message",(function e(r){var a=r.data;a.forge&&a.forge.prng&&(self.removeEventListener("message",e),t(a.forge.prng.err,a.forge.prng.bytes))})),self.postMessage({forge:{prng:{needed:e}}})};else{e.addEventListener("message",(function(r){var a=r.data;a.forge&&a.forge.prng&&t.seedFile(a.forge.prng.needed,(function(t,r){e.postMessage({forge:{prng:{err:t,bytes:r}}})}))}))}},t}},function(e,t,r){var a=r(0);r(1);var n=[217,120,249,196,25,221,181,237,40,233,253,121,74,160,216,157,198,126,55,131,43,118,83,142,98,76,100,136,68,139,251,162,23,154,89,245,135,179,79,19,97,69,109,141,9,129,125,50,189,143,64,235,134,183,123,11,240,149,33,34,92,107,78,130,84,214,101,147,206,96,178,28,115,86,192,20,167,140,241,220,18,117,202,31,59,190,228,209,66,61,212,48,163,60,182,38,111,191,14,218,70,105,7,87,39,242,29,155,188,148,67,3,248,17,199,246,144,239,62,231,6,195,213,47,200,102,30,215,8,232,234,222,128,82,238,247,132,170,114,172,53,77,106,42,150,26,210,113,90,21,73,116,75,159,208,94,4,24,164,236,194,224,65,110,15,81,203,204,36,145,175,80,161,244,112,57,153,124,58,133,35,184,180,122,252,2,54,91,37,85,151,49,45,93,250,152,227,138,146,174,5,223,41,16,103,108,186,201,211,0,230,207,225,158,168,44,99,22,1,63,88,226,137,169,13,56,52,27,171,51,255,176,187,72,12,95,185,177,205,46,197,243,219,71,229,165,156,119,10,166,32,104,254,127,193,173],i=[1,2,3,5],s=function(e,t){return e<<t&65535|(65535&e)>>16-t},o=function(e,t){return(65535&e)>>t|e<<16-t&65535};e.exports=a.rc2=a.rc2||{},a.rc2.expandKey=function(e,t){"string"==typeof e&&(e=a.util.createBuffer(e)),t=t||128;var r,i=e,s=e.length(),o=t,c=Math.ceil(o/8),u=255>>(7&o);for(r=s;r<128;r++)i.putByte(n[i.at(r-1)+i.at(r-s)&255]);for(i.setAt(128-c,n[i.at(128-c)&u]),r=127-c;r>=0;r--)i.setAt(r,n[i.at(r+1)^i.at(r+c)]);return i};var c=function(e,t,r){var n,c,u,l,p=!1,f=null,h=null,d=null,y=[];for(e=a.rc2.expandKey(e,t),u=0;u<64;u++)y.push(e.getInt16Le());r?(n=function(e){for(u=0;u<4;u++)e[u]+=y[l]+(e[(u+3)%4]&e[(u+2)%4])+(~e[(u+3)%4]&e[(u+1)%4]),e[u]=s(e[u],i[u]),l++},c=function(e){for(u=0;u<4;u++)e[u]+=y[63&e[(u+3)%4]]}):(n=function(e){for(u=3;u>=0;u--)e[u]=o(e[u],i[u]),e[u]-=y[l]+(e[(u+3)%4]&e[(u+2)%4])+(~e[(u+3)%4]&e[(u+1)%4]),l--},c=function(e){for(u=3;u>=0;u--)e[u]-=y[63&e[(u+3)%4]]});var g=function(e){var t=[];for(u=0;u<4;u++){var a=f.getInt16Le();null!==d&&(r?a^=d.getInt16Le():d.putInt16Le(a)),t.push(65535&a)}l=r?0:63;for(var n=0;n<e.length;n++)for(var i=0;i<e[n][0];i++)e[n][1](t);for(u=0;u<4;u++)null!==d&&(r?d.putInt16Le(t[u]):t[u]^=d.getInt16Le()),h.putInt16Le(t[u])},v=null;return v={start:function(e,t){e&&"string"==typeof e&&(e=a.util.createBuffer(e)),p=!1,f=a.util.createBuffer(),h=t||new a.util.createBuffer,d=e,v.output=h},update:function(e){for(p||f.putBuffer(e);f.length()>=8;)g([[5,n],[1,c],[6,n],[1,c],[5,n]])},finish:function(e){var t=!0;if(r)if(e)t=e(8,f,!r);else{var a=8===f.length()?8:8-f.length();f.fillWithByte(a,a)}if(t&&(p=!0,v.update()),!r&&(t=0===f.length()))if(e)t=e(8,h,!r);else{var n=h.length(),i=h.at(n-1);i>n?t=!1:h.truncate(i)}return t}}};a.rc2.startEncrypting=function(e,t,r){var n=a.rc2.createEncryptionCipher(e,128);return n.start(t,r),n},a.rc2.createEncryptionCipher=function(e,t){return c(e,t,!0)},a.rc2.startDecrypting=function(e,t,r){var n=a.rc2.createDecryptionCipher(e,128);return n.start(t,r),n},a.rc2.createDecryptionCipher=function(e,t){return c(e,t,!1)}},function(e,t,r){var a=r(0);r(1),r(2),r(9);var n=e.exports=a.pkcs1=a.pkcs1||{};function i(e,t,r){r||(r=a.md.sha1.create());for(var n="",i=Math.ceil(t/r.digestLength),s=0;s<i;++s){var o=String.fromCharCode(s>>24&255,s>>16&255,s>>8&255,255&s);r.start(),r.update(e+o),n+=r.digest().getBytes()}return n.substring(0,t)}n.encode_rsa_oaep=function(e,t,r){var n,s,o,c;"string"==typeof r?(n=r,s=arguments[3]||void 0,o=arguments[4]||void 0):r&&(n=r.label||void 0,s=r.seed||void 0,o=r.md||void 0,r.mgf1&&r.mgf1.md&&(c=r.mgf1.md)),o?o.start():o=a.md.sha1.create(),c||(c=o);var u=Math.ceil(e.n.bitLength()/8),l=u-2*o.digestLength-2;if(t.length>l)throw(g=new Error("RSAES-OAEP input message length is too long.")).length=t.length,g.maxLength=l,g;n||(n=""),o.update(n,"raw");for(var p=o.digest(),f="",h=l-t.length,d=0;d<h;d++)f+="\0";var y=p.getBytes()+f+""+t;if(s){if(s.length!==o.digestLength){var g;throw(g=new Error("Invalid RSAES-OAEP seed. The seed length must match the digest length.")).seedLength=s.length,g.digestLength=o.digestLength,g}}else s=a.random.getBytes(o.digestLength);var v=i(s,u-o.digestLength-1,c),m=a.util.xorBytes(y,v,y.length),C=i(m,o.digestLength,c),E=a.util.xorBytes(s,C,s.length);return"\0"+E+m},n.decode_rsa_oaep=function(e,t,r){var n,s,o;"string"==typeof r?(n=r,s=arguments[3]||void 0):r&&(n=r.label||void 0,s=r.md||void 0,r.mgf1&&r.mgf1.md&&(o=r.mgf1.md));var c=Math.ceil(e.n.bitLength()/8);if(t.length!==c)throw(m=new Error("RSAES-OAEP encoded message length is invalid.")).length=t.length,m.expectedLength=c,m;if(void 0===s?s=a.md.sha1.create():s.start(),o||(o=s),c<2*s.digestLength+2)throw new Error("RSAES-OAEP key is too short for the hash function.");n||(n=""),s.update(n,"raw");for(var u=s.digest().getBytes(),l=t.charAt(0),p=t.substring(1,s.digestLength+1),f=t.substring(1+s.digestLength),h=i(f,s.digestLength,o),d=a.util.xorBytes(p,h,p.length),y=i(d,c-s.digestLength-1,o),g=a.util.xorBytes(f,y,f.length),v=g.substring(0,s.digestLength),m="\0"!==l,C=0;C<s.digestLength;++C)m|=u.charAt(C)!==v.charAt(C);for(var E=1,S=s.digestLength,T=s.digestLength;T<g.length;T++){var I=g.charCodeAt(T),A=1&I^1,B=E?65534:0;m|=I&B,S+=E&=A}if(m||1!==g.charCodeAt(S))throw new Error("Invalid RSAES-OAEP padding.");return g.substring(S+1)}},function(e,t,r){var a=r(0);r(1),r(12),r(2),function(){if(a.prime)e.exports=a.prime;else{var t=e.exports=a.prime=a.prime||{},r=a.jsbn.BigInteger,n=[6,4,2,4,2,4,6,2],i=new r(null);i.fromInt(30);var s=function(e,t){return e|t};t.generateProbablePrime=function(e,t,n){"function"==typeof t&&(n=t,t={});var i=(t=t||{}).algorithm||"PRIMEINC";"string"==typeof i&&(i={name:i}),i.options=i.options||{};var s=t.prng||a.random,u={nextBytes:function(e){for(var t=s.getBytesSync(e.length),r=0;r<e.length;++r)e[r]=t.charCodeAt(r)}};if("PRIMEINC"===i.name)return function(e,t,n,i){if("workers"in n)return function(e,t,n,i){if("undefined"==typeof Worker)return o(e,t,n,i);var s=c(e,t),u=n.workers,l=n.workLoad||100,p=30*l/8,f=n.workerScript||"forge/prime.worker.js";if(-1===u)return a.util.estimateCores((function(e,t){e&&(t=2),u=t-1,h()}));function h(){u=Math.max(1,u);for(var a=[],n=0;n<u;++n)a[n]=new Worker(f);for(n=0;n<u;++n)a[n].addEventListener("message",h);var o=!1;function h(n){if(!o){0;var u=n.data;if(u.found){for(var f=0;f<a.length;++f)a[f].terminate();return o=!0,i(null,new r(u.prime,16))}s.bitLength()>e&&(s=c(e,t));var h=s.toString(16);n.target.postMessage({hex:h,workLoad:l}),s.dAddOffset(p,0)}}}h()}(e,t,n,i);return o(e,t,n,i)}(e,u,i.options,n);throw new Error("Invalid prime generation algorithm: "+i.name)}}function o(e,t,r,i){var s=c(e,t),o=function(e){return e<=100?27:e<=150?18:e<=200?15:e<=250?12:e<=300?9:e<=350?8:e<=400?7:e<=500?6:e<=600?5:e<=800?4:e<=1250?3:2}(s.bitLength());"millerRabinTests"in r&&(o=r.millerRabinTests);var u=10;"maxBlockTime"in r&&(u=r.maxBlockTime),function e(t,r,i,s,o,u,l){var p=+new Date;do{if(t.bitLength()>r&&(t=c(r,i)),t.isProbablePrime(o))return l(null,t);t.dAddOffset(n[s++%8],0)}while(u<0||+new Date-p<u);a.util.setImmediate((function(){e(t,r,i,s,o,u,l)}))}(s,e,t,0,o,u,i)}function c(e,t){var a=new r(e,t),n=e-1;return a.testBit(n)||a.bitwiseTo(r.ONE.shiftLeft(n),s,a),a.dAddOffset(31-a.mod(i).byteValue(),0),a}}()},function(e,t,r){var a=r(0);r(3),r(8),r(6),r(29),r(22),r(2),r(11),r(9),r(1),r(17);var n=a.asn1,i=a.pki,s=e.exports=a.pkcs12=a.pkcs12||{},o={name:"ContentInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"ContentInfo.contentType",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"contentType"},{name:"ContentInfo.content",tagClass:n.Class.CONTEXT_SPECIFIC,constructed:!0,captureAsn1:"content"}]},c={name:"PFX",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"},o,{name:"PFX.macData",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,optional:!0,captureAsn1:"mac",value:[{name:"PFX.macData.mac",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.macData.mac.digestAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.macData.mac.digestAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"macAlgorithm"},{name:"PFX.macData.mac.digestAlgorithm.parameters",optional:!0,tagClass:n.Class.UNIVERSAL,captureAsn1:"macAlgorithmParameters"}]},{name:"PFX.macData.mac.digest",tagClass:n.Class.UNIVERSAL,type:n.Type.OCTETSTRING,constructed:!1,capture:"macDigest"}]},{name:"PFX.macData.macSalt",tagClass:n.Class.UNIVERSAL,type:n.Type.OCTETSTRING,constructed:!1,capture:"macSalt"},{name:"PFX.macData.iterations",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,optional:!0,capture:"macIterations"}]}]},u={name:"SafeBag",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SafeBag.bagId",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"bagId"},{name:"SafeBag.bagValue",tagClass:n.Class.CONTEXT_SPECIFIC,constructed:!0,captureAsn1:"bagValue"},{name:"SafeBag.bagAttributes",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,constructed:!0,optional:!0,capture:"bagAttributes"}]},l={name:"Attribute",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"Attribute.attrId",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"oid"},{name:"Attribute.attrValues",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,constructed:!0,capture:"values"}]},p={name:"CertBag",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"CertBag.certId",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"certId"},{name:"CertBag.certValue",tagClass:n.Class.CONTEXT_SPECIFIC,constructed:!0,value:[{name:"CertBag.certValue[0]",tagClass:n.Class.UNIVERSAL,type:n.Class.OCTETSTRING,constructed:!1,capture:"cert"}]}]};function f(e,t,r,a){for(var n=[],i=0;i<e.length;i++)for(var s=0;s<e[i].safeBags.length;s++){var o=e[i].safeBags[s];void 0!==a&&o.type!==a||(null!==t?void 0!==o.attributes[t]&&o.attributes[t].indexOf(r)>=0&&n.push(o):n.push(o))}return n}function h(e){if(e.composed||e.constructed){for(var t=a.util.createBuffer(),r=0;r<e.value.length;++r)t.putBytes(e.value[r].value);e.composed=e.constructed=!1,e.value=t.getBytes()}return e}function d(e,t){var r={},s=[];if(!n.validate(e,a.pkcs7.asn1.encryptedDataValidator,r,s))throw(o=new Error("Cannot read EncryptedContentInfo.")).errors=s,o;var o,c=n.derToOid(r.contentType);if(c!==i.oids.data)throw(o=new Error("PKCS#12 EncryptedContentInfo ContentType is not Data.")).oid=c,o;c=n.derToOid(r.encAlgorithm);var u=i.pbe.getCipher(c,r.encParameter,t),l=h(r.encryptedContentAsn1),p=a.util.createBuffer(l.value);if(u.update(p),!u.finish())throw new Error("Failed to decrypt PKCS#12 SafeContents.");return u.output.getBytes()}function y(e,t,r){if(!t&&0===e.length)return[];if((e=n.fromDer(e,t)).tagClass!==n.Class.UNIVERSAL||e.type!==n.Type.SEQUENCE||!0!==e.constructed)throw new Error("PKCS#12 SafeContents expected to be a SEQUENCE OF SafeBag.");for(var a=[],s=0;s<e.value.length;s++){var o=e.value[s],c={},l=[];if(!n.validate(o,u,c,l))throw(v=new Error("Cannot read SafeBag.")).errors=l,v;var f,h,d={type:n.derToOid(c.bagId),attributes:g(c.bagAttributes)};a.push(d);var y=c.bagValue.value[0];switch(d.type){case i.oids.pkcs8ShroudedKeyBag:if(null===(y=i.decryptPrivateKeyInfo(y,r)))throw new Error("Unable to decrypt PKCS#8 ShroudedKeyBag, wrong password?");case i.oids.keyBag:try{d.key=i.privateKeyFromAsn1(y)}catch(e){d.key=null,d.asn1=y}continue;case i.oids.certBag:f=p,h=function(){if(n.derToOid(c.certId)!==i.oids.x509Certificate){var e=new Error("Unsupported certificate type, only X.509 supported.");throw e.oid=n.derToOid(c.certId),e}var r=n.fromDer(c.cert,t);try{d.cert=i.certificateFromAsn1(r,!0)}catch(e){d.cert=null,d.asn1=r}};break;default:var v;throw(v=new Error("Unsupported PKCS#12 SafeBag type.")).oid=d.type,v}if(void 0!==f&&!n.validate(y,f,c,l))throw(v=new Error("Cannot read PKCS#12 "+f.name)).errors=l,v;h()}return a}function g(e){var t={};if(void 0!==e)for(var r=0;r<e.length;++r){var a={},s=[];if(!n.validate(e[r],l,a,s)){var o=new Error("Cannot read PKCS#12 BagAttribute.");throw o.errors=s,o}var c=n.derToOid(a.oid);if(void 0!==i.oids[c]){t[i.oids[c]]=[];for(var u=0;u<a.values.length;++u)t[i.oids[c]].push(a.values[u].value)}}return t}s.pkcs12FromAsn1=function(e,t,r){"string"==typeof t?(r=t,t=!0):void 0===t&&(t=!0);var u={};if(!n.validate(e,c,u,[]))throw(l=new Error("Cannot read PKCS#12 PFX. ASN.1 object is not an PKCS#12 PFX.")).errors=l,l;var l,p={version:u.version.charCodeAt(0),safeContents:[],getBags:function(e){var t,r={};return"localKeyId"in e?t=e.localKeyId:"localKeyIdHex"in e&&(t=a.util.hexToBytes(e.localKeyIdHex)),void 0===t&&!("friendlyName"in e)&&"bagType"in e&&(r[e.bagType]=f(p.safeContents,null,null,e.bagType)),void 0!==t&&(r.localKeyId=f(p.safeContents,"localKeyId",t,e.bagType)),"friendlyName"in e&&(r.friendlyName=f(p.safeContents,"friendlyName",e.friendlyName,e.bagType)),r},getBagsByFriendlyName:function(e,t){return f(p.safeContents,"friendlyName",e,t)},getBagsByLocalKeyId:function(e,t){return f(p.safeContents,"localKeyId",e,t)}};if(3!==u.version.charCodeAt(0))throw(l=new Error("PKCS#12 PFX of version other than 3 not supported.")).version=u.version.charCodeAt(0),l;if(n.derToOid(u.contentType)!==i.oids.data)throw(l=new Error("Only PKCS#12 PFX in password integrity mode supported.")).oid=n.derToOid(u.contentType),l;var g=u.content.value[0];if(g.tagClass!==n.Class.UNIVERSAL||g.type!==n.Type.OCTETSTRING)throw new Error("PKCS#12 authSafe content data is not an OCTET STRING.");if(g=h(g),u.mac){var v=null,m=0,C=n.derToOid(u.macAlgorithm);switch(C){case i.oids.sha1:v=a.md.sha1.create(),m=20;break;case i.oids.sha256:v=a.md.sha256.create(),m=32;break;case i.oids.sha384:v=a.md.sha384.create(),m=48;break;case i.oids.sha512:v=a.md.sha512.create(),m=64;break;case i.oids.md5:v=a.md.md5.create(),m=16}if(null===v)throw new Error("PKCS#12 uses unsupported MAC algorithm: "+C);var E=new a.util.ByteBuffer(u.macSalt),S="macIterations"in u?parseInt(a.util.bytesToHex(u.macIterations),16):1,T=s.generateKey(r,E,3,S,m,v),I=a.hmac.create();if(I.start(v,T),I.update(g.value),I.getMac().getBytes()!==u.macDigest)throw new Error("PKCS#12 MAC could not be verified. Invalid password?")}else if(Array.isArray(e.value)&&e.value.length>2)throw new Error("Invalid PKCS#12. macData field present but MAC was not validated.");return function(e,t,r,a){if((t=n.fromDer(t,r)).tagClass!==n.Class.UNIVERSAL||t.type!==n.Type.SEQUENCE||!0!==t.constructed)throw new Error("PKCS#12 AuthenticatedSafe expected to be a SEQUENCE OF ContentInfo");for(var s=0;s<t.value.length;s++){var c=t.value[s],u={},l=[];if(!n.validate(c,o,u,l))throw(v=new Error("Cannot read ContentInfo.")).errors=l,v;var p={encrypted:!1},f=null,g=u.content.value[0];switch(n.derToOid(u.contentType)){case i.oids.data:if(g.tagClass!==n.Class.UNIVERSAL||g.type!==n.Type.OCTETSTRING)throw new Error("PKCS#12 SafeContents Data is not an OCTET STRING.");f=h(g).value;break;case i.oids.encryptedData:f=d(g,a),p.encrypted=!0;break;default:var v;throw(v=new Error("Unsupported PKCS#12 contentType.")).contentType=n.derToOid(u.contentType),v}p.safeBags=y(f,r,a),e.safeContents.push(p)}}(p,g.value,t,r),p},s.toPkcs12Asn1=function(e,t,r,o){(o=o||{}).saltSize=o.saltSize||8,o.count=o.count||2048,o.algorithm=o.algorithm||o.encAlgorithm||"aes128","useMac"in o||(o.useMac=!0),"localKeyId"in o||(o.localKeyId=null),"generateLocalKeyId"in o||(o.generateLocalKeyId=!0);var c,u=o.localKeyId;if(null!==u)u=a.util.hexToBytes(u);else if(o.generateLocalKeyId)if(t){var l=a.util.isArray(t)?t[0]:t;"string"==typeof l&&(l=i.certificateFromPem(l)),(N=a.md.sha1.create()).update(n.toDer(i.certificateToAsn1(l)).getBytes()),u=N.digest().getBytes()}else u=a.random.getBytes(20);var p=[];null!==u&&p.push(n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.localKeyId).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SET,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,u)])])),"friendlyName"in o&&p.push(n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.friendlyName).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SET,!0,[n.create(n.Class.UNIVERSAL,n.Type.BMPSTRING,!1,o.friendlyName)])])),p.length>0&&(c=n.create(n.Class.UNIVERSAL,n.Type.SET,!0,p));var f=[],h=[];null!==t&&(h=a.util.isArray(t)?t:[t]);for(var d=[],y=0;y<h.length;++y){"string"==typeof(t=h[y])&&(t=i.certificateFromPem(t));var g=0===y?c:void 0,v=i.certificateToAsn1(t),m=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.certBag).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.x509Certificate).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,n.toDer(v).getBytes())])])]),g]);d.push(m)}if(d.length>0){var C=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,d),E=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.data).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,n.toDer(C).getBytes())])]);f.push(E)}var S=null;if(null!==e){var T=i.wrapRsaPrivateKey(i.privateKeyToAsn1(e));S=null===r?n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.keyBag).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[T]),c]):n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.pkcs8ShroudedKeyBag).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[i.encryptPrivateKeyInfo(T,r,o)]),c]);var I=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[S]),A=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.data).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,n.toDer(I).getBytes())])]);f.push(A)}var B,b=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,f);if(o.useMac){var N=a.md.sha1.create(),R=new a.util.ByteBuffer(a.random.getBytes(o.saltSize)),w=o.count,_=(e=s.generateKey(r,R,3,w,20),a.hmac.create());_.start(N,e),_.update(n.toDer(b).getBytes());var k=_.getMac();B=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.sha1).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")]),n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,k.getBytes())]),n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,R.getBytes()),n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(w).getBytes())])}return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(3).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.data).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,n.toDer(b).getBytes())])]),B])},s.generateKey=a.pbe.generatePkcs12Key},function(e,t,r){var a=r(0);r(3),r(1);var n=a.asn1,i=e.exports=a.pkcs7asn1=a.pkcs7asn1||{};a.pkcs7=a.pkcs7||{},a.pkcs7.asn1=i;var s={name:"ContentInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"ContentInfo.ContentType",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"contentType"},{name:"ContentInfo.content",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,captureAsn1:"content"}]};i.contentInfoValidator=s;var o={name:"EncryptedContentInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedContentInfo.contentType",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"contentType"},{name:"EncryptedContentInfo.contentEncryptionAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedContentInfo.contentEncryptionAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"encAlgorithm"},{name:"EncryptedContentInfo.contentEncryptionAlgorithm.parameter",tagClass:n.Class.UNIVERSAL,captureAsn1:"encParameter"}]},{name:"EncryptedContentInfo.encryptedContent",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,capture:"encryptedContent",captureAsn1:"encryptedContentAsn1"}]};i.envelopedDataValidator={name:"EnvelopedData",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"EnvelopedData.Version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"},{name:"EnvelopedData.RecipientInfos",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,constructed:!0,captureAsn1:"recipientInfos"}].concat(o)},i.encryptedDataValidator={name:"EncryptedData",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedData.Version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"}].concat(o)};var c={name:"SignerInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1},{name:"SignerInfo.issuerAndSerialNumber",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.issuerAndSerialNumber.issuer",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"issuer"},{name:"SignerInfo.issuerAndSerialNumber.serialNumber",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"serial"}]},{name:"SignerInfo.digestAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.digestAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"digestAlgorithm"},{name:"SignerInfo.digestAlgorithm.parameter",tagClass:n.Class.UNIVERSAL,constructed:!1,captureAsn1:"digestParameter",optional:!0}]},{name:"SignerInfo.authenticatedAttributes",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,capture:"authenticatedAttributes"},{name:"SignerInfo.digestEncryptionAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,capture:"signatureAlgorithm"},{name:"SignerInfo.encryptedDigest",tagClass:n.Class.UNIVERSAL,type:n.Type.OCTETSTRING,constructed:!1,capture:"signature"},{name:"SignerInfo.unauthenticatedAttributes",tagClass:n.Class.CONTEXT_SPECIFIC,type:1,constructed:!0,optional:!0,capture:"unauthenticatedAttributes"}]};i.signedDataValidator={name:"SignedData",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SignedData.Version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"},{name:"SignedData.DigestAlgorithms",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,constructed:!0,captureAsn1:"digestAlgorithms"},s,{name:"SignedData.Certificates",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,optional:!0,captureAsn1:"certificates"},{name:"SignedData.CertificateRevocationLists",tagClass:n.Class.CONTEXT_SPECIFIC,type:1,optional:!0,captureAsn1:"crls"},{name:"SignedData.SignerInfos",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,capture:"signerInfos",optional:!0,value:[c]}]},i.recipientInfoValidator={name:"RecipientInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"},{name:"RecipientInfo.issuerAndSerial",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.issuerAndSerial.issuer",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"issuer"},{name:"RecipientInfo.issuerAndSerial.serialNumber",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"serial"}]},{name:"RecipientInfo.keyEncryptionAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.keyEncryptionAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"encAlgorithm"},{name:"RecipientInfo.keyEncryptionAlgorithm.parameter",tagClass:n.Class.UNIVERSAL,constructed:!1,captureAsn1:"encParameter",optional:!0}]},{name:"RecipientInfo.encryptedKey",tagClass:n.Class.UNIVERSAL,type:n.Type.OCTETSTRING,constructed:!1,capture:"encKey"}]}},function(e,t,r){var a=r(0);r(1),a.mgf=a.mgf||{},(e.exports=a.mgf.mgf1=a.mgf1=a.mgf1||{}).create=function(e){return{generate:function(t,r){for(var n=new a.util.ByteBuffer,i=Math.ceil(r/e.digestLength),s=0;s<i;s++){var o=new a.util.ByteBuffer;o.putInt32(s),e.start(),e.update(t+o.getBytes()),n.putBuffer(e.digest())}return n.truncate(n.length()-r),n.getBytes()}}}},function(e,t,r){var a=r(0);r(4),r(1);var n=e.exports=a.sha512=a.sha512||{};a.md.sha512=a.md.algorithms.sha512=n;var i=a.sha384=a.sha512.sha384=a.sha512.sha384||{};i.create=function(){return n.create("SHA-384")},a.md.sha384=a.md.algorithms.sha384=i,a.sha512.sha256=a.sha512.sha256||{create:function(){return n.create("SHA-512/256")}},a.md["sha512/256"]=a.md.algorithms["sha512/256"]=a.sha512.sha256,a.sha512.sha224=a.sha512.sha224||{create:function(){return n.create("SHA-512/224")}},a.md["sha512/224"]=a.md.algorithms["sha512/224"]=a.sha512.sha224,n.create=function(e){if(o||(s=String.fromCharCode(128),s+=a.util.fillString(String.fromCharCode(0),128),c=[[1116352408,3609767458],[1899447441,602891725],[3049323471,3964484399],[3921009573,2173295548],[961987163,4081628472],[1508970993,3053834265],[2453635748,2937671579],[2870763221,3664609560],[3624381080,2734883394],[310598401,1164996542],[607225278,1323610764],[1426881987,3590304994],[1925078388,4068182383],[2162078206,991336113],[2614888103,633803317],[3248222580,3479774868],[3835390401,2666613458],[4022224774,944711139],[264347078,2341262773],[604807628,2007800933],[770255983,1495990901],[1249150122,1856431235],[1555081692,3175218132],[1996064986,2198950837],[2554220882,3999719339],[2821834349,766784016],[2952996808,2566594879],[3210313671,3203337956],[3336571891,1034457026],[3584528711,2466948901],[113926993,3758326383],[338241895,168717936],[666307205,1188179964],[773529912,1546045734],[1294757372,1522805485],[1396182291,2643833823],[1695183700,2343527390],[1986661051,1014477480],[2177026350,1206759142],[2456956037,344077627],[2730485921,1290863460],[2820302411,3158454273],[3259730800,3505952657],[3345764771,106217008],[3516065817,3606008344],[3600352804,1432725776],[4094571909,1467031594],[275423344,851169720],[430227734,3100823752],[506948616,1363258195],[659060556,3750685593],[883997877,3785050280],[958139571,3318307427],[1322822218,3812723403],[1537002063,2003034995],[1747873779,3602036899],[1955562222,1575990012],[2024104815,1125592928],[2227730452,2716904306],[2361852424,442776044],[2428436474,593698344],[2756734187,3733110249],[3204031479,2999351573],[3329325298,3815920427],[3391569614,3928383900],[3515267271,566280711],[3940187606,3454069534],[4118630271,4000239992],[116418474,1914138554],[174292421,2731055270],[289380356,3203993006],[460393269,320620315],[685471733,587496836],[852142971,1086792851],[1017036298,365543100],[1126000580,2618297676],[1288033470,3409855158],[1501505948,4234509866],[1607167915,987167468],[1816402316,1246189591]],(u={})["SHA-512"]=[[1779033703,4089235720],[3144134277,2227873595],[1013904242,4271175723],[2773480762,1595750129],[1359893119,2917565137],[2600822924,725511199],[528734635,4215389547],[1541459225,327033209]],u["SHA-384"]=[[3418070365,3238371032],[1654270250,914150663],[2438529370,812702999],[355462360,4144912697],[1731405415,4290775857],[2394180231,1750603025],[3675008525,1694076839],[1203062813,3204075428]],u["SHA-512/256"]=[[573645204,4230739756],[2673172387,3360449730],[596883563,1867755857],[2520282905,1497426621],[2519219938,2827943907],[3193839141,1401305490],[721525244,746961066],[246885852,2177182882]],u["SHA-512/224"]=[[2352822216,424955298],[1944164710,2312950998],[502970286,855612546],[1738396948,1479516111],[258812777,2077511080],[2011393907,79989058],[1067287976,1780299464],[286451373,2446758561]],o=!0),void 0===e&&(e="SHA-512"),!(e in u))throw new Error("Invalid SHA-512 algorithm: "+e);for(var t=u[e],r=null,n=a.util.createBuffer(),i=new Array(80),p=0;p<80;++p)i[p]=new Array(2);var f=64;switch(e){case"SHA-384":f=48;break;case"SHA-512/256":f=32;break;case"SHA-512/224":f=28}var h={algorithm:e.replace("-","").toLowerCase(),blockLength:128,digestLength:f,messageLength:0,fullMessageLength:null,messageLengthSize:16,start:function(){h.messageLength=0,h.fullMessageLength=h.messageLength128=[];for(var e=h.messageLengthSize/4,i=0;i<e;++i)h.fullMessageLength.push(0);n=a.util.createBuffer(),r=new Array(t.length);for(i=0;i<t.length;++i)r[i]=t[i].slice(0);return h}};return h.start(),h.update=function(e,t){"utf8"===t&&(e=a.util.encodeUtf8(e));var s=e.length;h.messageLength+=s,s=[s/4294967296>>>0,s>>>0];for(var o=h.fullMessageLength.length-1;o>=0;--o)h.fullMessageLength[o]+=s[1],s[1]=s[0]+(h.fullMessageLength[o]/4294967296>>>0),h.fullMessageLength[o]=h.fullMessageLength[o]>>>0,s[0]=s[1]/4294967296>>>0;return n.putBytes(e),l(r,i,n),(n.read>2048||0===n.length())&&n.compact(),h},h.digest=function(){var t=a.util.createBuffer();t.putBytes(n.bytes());var o,c=h.fullMessageLength[h.fullMessageLength.length-1]+h.messageLengthSize&h.blockLength-1;t.putBytes(s.substr(0,h.blockLength-c));for(var u=8*h.fullMessageLength[0],p=0;p<h.fullMessageLength.length-1;++p)u+=(o=8*h.fullMessageLength[p+1])/4294967296>>>0,t.putInt32(u>>>0),u=o>>>0;t.putInt32(u);var f=new Array(r.length);for(p=0;p<r.length;++p)f[p]=r[p].slice(0);l(f,i,t);var d,y=a.util.createBuffer();d="SHA-512"===e?f.length:"SHA-384"===e?f.length-2:f.length-4;for(p=0;p<d;++p)y.putInt32(f[p][0]),p===d-1&&"SHA-512/224"===e||y.putInt32(f[p][1]);return y},h};var s=null,o=!1,c=null,u=null;function l(e,t,r){for(var a,n,i,s,o,u,l,p,f,h,d,y,g,v,m,C,E,S,T,I,A,B,b,N,R,w,_,k,L,U,D,P,V,O=r.length();O>=128;){for(_=0;_<16;++_)t[_][0]=r.getInt32()>>>0,t[_][1]=r.getInt32()>>>0;for(;_<80;++_)a=(((k=(U=t[_-2])[0])>>>19|(L=U[1])<<13)^(L>>>29|k<<3)^k>>>6)>>>0,n=((k<<13|L>>>19)^(L<<3|k>>>29)^(k<<26|L>>>6))>>>0,i=(((k=(P=t[_-15])[0])>>>1|(L=P[1])<<31)^(k>>>8|L<<24)^k>>>7)>>>0,s=((k<<31|L>>>1)^(k<<24|L>>>8)^(k<<25|L>>>7))>>>0,D=t[_-7],V=t[_-16],L=n+D[1]+s+V[1],t[_][0]=a+D[0]+i+V[0]+(L/4294967296>>>0)>>>0,t[_][1]=L>>>0;for(d=e[0][0],y=e[0][1],g=e[1][0],v=e[1][1],m=e[2][0],C=e[2][1],E=e[3][0],S=e[3][1],T=e[4][0],I=e[4][1],A=e[5][0],B=e[5][1],b=e[6][0],N=e[6][1],R=e[7][0],w=e[7][1],_=0;_<80;++_)l=((T>>>14|I<<18)^(T>>>18|I<<14)^(I>>>9|T<<23))>>>0,p=(b^T&(A^b))>>>0,o=((d>>>28|y<<4)^(y>>>2|d<<30)^(y>>>7|d<<25))>>>0,u=((d<<4|y>>>28)^(y<<30|d>>>2)^(y<<25|d>>>7))>>>0,f=(d&g|m&(d^g))>>>0,h=(y&v|C&(y^v))>>>0,L=w+(((T<<18|I>>>14)^(T<<14|I>>>18)^(I<<23|T>>>9))>>>0)+((N^I&(B^N))>>>0)+c[_][1]+t[_][1],a=R+l+p+c[_][0]+t[_][0]+(L/4294967296>>>0)>>>0,n=L>>>0,i=o+f+((L=u+h)/4294967296>>>0)>>>0,s=L>>>0,R=b,w=N,b=A,N=B,A=T,B=I,T=E+a+((L=S+n)/4294967296>>>0)>>>0,I=L>>>0,E=m,S=C,m=g,C=v,g=d,v=y,d=a+i+((L=n+s)/4294967296>>>0)>>>0,y=L>>>0;L=e[0][1]+y,e[0][0]=e[0][0]+d+(L/4294967296>>>0)>>>0,e[0][1]=L>>>0,L=e[1][1]+v,e[1][0]=e[1][0]+g+(L/4294967296>>>0)>>>0,e[1][1]=L>>>0,L=e[2][1]+C,e[2][0]=e[2][0]+m+(L/4294967296>>>0)>>>0,e[2][1]=L>>>0,L=e[3][1]+S,e[3][0]=e[3][0]+E+(L/4294967296>>>0)>>>0,e[3][1]=L>>>0,L=e[4][1]+I,e[4][0]=e[4][0]+T+(L/4294967296>>>0)>>>0,e[4][1]=L>>>0,L=e[5][1]+B,e[5][0]=e[5][0]+A+(L/4294967296>>>0)>>>0,e[5][1]=L>>>0,L=e[6][1]+N,e[6][0]=e[6][0]+b+(L/4294967296>>>0)>>>0,e[6][1]=L>>>0,L=e[7][1]+w,e[7][0]=e[7][0]+R+(L/4294967296>>>0)>>>0,e[7][1]=L>>>0,O-=128}}},function(e,t,r){e.exports=r(33)},function(e,t,r){e.exports=r(0),r(5),r(36),r(3),r(13),r(10),r(38),r(8),r(40),r(41),r(42),r(30),r(15),r(7),r(26),r(28),r(43),r(21),r(27),r(24),r(18),r(2),r(25),r(44),r(20),r(1)},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t){var r={};e.exports=r;var a={};r.encode=function(e,t,r){if("string"!=typeof t)throw new TypeError('"alphabet" must be a string.');if(void 0!==r&&"number"!=typeof r)throw new TypeError('"maxline" must be a number.');var a="";if(e instanceof Uint8Array){var n=0,i=t.length,s=t.charAt(0),o=[0];for(n=0;n<e.length;++n){for(var c=0,u=e[n];c<o.length;++c)u+=o[c]<<8,o[c]=u%i,u=u/i|0;for(;u>0;)o.push(u%i),u=u/i|0}for(n=0;0===e[n]&&n<e.length-1;++n)a+=s;for(n=o.length-1;n>=0;--n)a+=t[o[n]]}else a=function(e,t){var r=0,a=t.length,n=t.charAt(0),i=[0];for(r=0;r<e.length();++r){for(var s=0,o=e.at(r);s<i.length;++s)o+=i[s]<<8,i[s]=o%a,o=o/a|0;for(;o>0;)i.push(o%a),o=o/a|0}var c="";for(r=0;0===e.at(r)&&r<e.length()-1;++r)c+=n;for(r=i.length-1;r>=0;--r)c+=t[i[r]];return c}(e,t);if(r){var l=new RegExp(".{1,"+r+"}","g");a=a.match(l).join("\r\n")}return a},r.decode=function(e,t){if("string"!=typeof e)throw new TypeError('"input" must be a string.');if("string"!=typeof t)throw new TypeError('"alphabet" must be a string.');var r=a[t];if(!r){r=a[t]=[];for(var n=0;n<t.length;++n)r[t.charCodeAt(n)]=n}e=e.replace(/\s/g,"");var i=t.length,s=t.charAt(0),o=[0];for(n=0;n<e.length;n++){var c=r[e.charCodeAt(n)];if(void 0===c)return;for(var u=0,l=c;u<o.length;++u)l+=o[u]*i,o[u]=255&l,l>>=8;for(;l>0;)o.push(255&l),l>>=8}for(var p=0;e[p]===s&&p<e.length-1;++p)o.push(0);return"undefined"!=typeof Buffer?Buffer.from(o.reverse()):new Uint8Array(o.reverse())}},function(e,t,r){var a=r(0);r(5),r(20);var n=e.exports=a.tls;function i(e,t,r){var i=t.entity===a.tls.ConnectionEnd.client;e.read.cipherState={init:!1,cipher:a.cipher.createDecipher("AES-CBC",i?r.keys.server_write_key:r.keys.client_write_key),iv:i?r.keys.server_write_IV:r.keys.client_write_IV},e.write.cipherState={init:!1,cipher:a.cipher.createCipher("AES-CBC",i?r.keys.client_write_key:r.keys.server_write_key),iv:i?r.keys.client_write_IV:r.keys.server_write_IV},e.read.cipherFunction=u,e.write.cipherFunction=s,e.read.macLength=e.write.macLength=r.mac_length,e.read.macFunction=e.write.macFunction=n.hmac_sha1}function s(e,t){var r,i=!1,s=t.macFunction(t.macKey,t.sequenceNumber,e);e.fragment.putBytes(s),t.updateSequenceNumber(),r=e.version.minor===n.Versions.TLS_1_0.minor?t.cipherState.init?null:t.cipherState.iv:a.random.getBytesSync(16),t.cipherState.init=!0;var c=t.cipherState.cipher;return c.start({iv:r}),e.version.minor>=n.Versions.TLS_1_1.minor&&c.output.putBytes(r),c.update(e.fragment),c.finish(o)&&(e.fragment=c.output,e.length=e.fragment.length(),i=!0),i}function o(e,t,r){if(!r){var a=e-t.length()%e;t.fillWithByte(a-1,a)}return!0}function c(e,t,r){var a=!0;if(r){for(var n=t.length(),i=t.last(),s=n-1-i;s<n-1;++s)a=a&&t.at(s)==i;a&&t.truncate(i+1)}return a}function u(e,t){var r,i=!1;r=e.version.minor===n.Versions.TLS_1_0.minor?t.cipherState.init?null:t.cipherState.iv:e.fragment.getBytes(16),t.cipherState.init=!0;var s=t.cipherState.cipher;s.start({iv:r}),s.update(e.fragment),i=s.finish(c);var o=t.macLength,u=a.random.getBytesSync(o),l=s.output.length();l>=o?(e.fragment=s.output.getBytes(l-o),u=s.output.getBytes(o)):e.fragment=s.output.getBytes(),e.fragment=a.util.createBuffer(e.fragment),e.length=e.fragment.length();var p=t.macFunction(t.macKey,t.sequenceNumber,e);return t.updateSequenceNumber(),i=function(e,t,r){var n=a.hmac.create();return n.start("SHA1",e),n.update(t),t=n.digest().getBytes(),n.start(null,null),n.update(r),r=n.digest().getBytes(),t===r}(t.macKey,u,p)&&i}n.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA={id:[0,47],name:"TLS_RSA_WITH_AES_128_CBC_SHA",initSecurityParameters:function(e){e.bulk_cipher_algorithm=n.BulkCipherAlgorithm.aes,e.cipher_type=n.CipherType.block,e.enc_key_length=16,e.block_length=16,e.fixed_iv_length=16,e.record_iv_length=16,e.mac_algorithm=n.MACAlgorithm.hmac_sha1,e.mac_length=20,e.mac_key_length=20},initConnectionState:i},n.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA={id:[0,53],name:"TLS_RSA_WITH_AES_256_CBC_SHA",initSecurityParameters:function(e){e.bulk_cipher_algorithm=n.BulkCipherAlgorithm.aes,e.cipher_type=n.CipherType.block,e.enc_key_length=32,e.block_length=16,e.fixed_iv_length=16,e.record_iv_length=16,e.mac_algorithm=n.MACAlgorithm.hmac_sha1,e.mac_length=20,e.mac_key_length=20},initConnectionState:i}},function(e,t,r){var a=r(0);r(30),e.exports=a.mgf=a.mgf||{},a.mgf.mgf1=a.mgf1},function(e,t,r){var a=r(0);r(12),r(2),r(31),r(1);var n=r(39),i=n.publicKeyValidator,s=n.privateKeyValidator;if(void 0===o)var o=a.jsbn.BigInteger;var c=a.util.ByteBuffer,u="undefined"==typeof Buffer?Uint8Array:Buffer;a.pki=a.pki||{},e.exports=a.pki.ed25519=a.ed25519=a.ed25519||{};var l=a.ed25519;function p(e){var t=e.message;if(t instanceof Uint8Array||t instanceof u)return t;var r=e.encoding;if(void 0===t){if(!e.md)throw new TypeError('"options.message" or "options.md" not specified.');t=e.md.digest().getBytes(),r="binary"}if("string"==typeof t&&!r)throw new TypeError('"options.encoding" must be "binary" or "utf8".');if("string"==typeof t){if("undefined"!=typeof Buffer)return Buffer.from(t,r);t=new c(t,r)}else if(!(t instanceof c))throw new TypeError('"options.message" must be a node.js Buffer, a Uint8Array, a forge ByteBuffer, or a string with "options.encoding" specifying its encoding.');for(var a=new u(t.length()),n=0;n<a.length;++n)a[n]=t.at(n);return a}l.constants={},l.constants.PUBLIC_KEY_BYTE_LENGTH=32,l.constants.PRIVATE_KEY_BYTE_LENGTH=64,l.constants.SEED_BYTE_LENGTH=32,l.constants.SIGN_BYTE_LENGTH=64,l.constants.HASH_BYTE_LENGTH=64,l.generateKeyPair=function(e){var t=(e=e||{}).seed;if(void 0===t)t=a.random.getBytesSync(l.constants.SEED_BYTE_LENGTH);else if("string"==typeof t){if(t.length!==l.constants.SEED_BYTE_LENGTH)throw new TypeError('"seed" must be '+l.constants.SEED_BYTE_LENGTH+" bytes in length.")}else if(!(t instanceof Uint8Array))throw new TypeError('"seed" must be a node.js Buffer, Uint8Array, or a binary string.');t=p({message:t,encoding:"binary"});for(var r=new u(l.constants.PUBLIC_KEY_BYTE_LENGTH),n=new u(l.constants.PRIVATE_KEY_BYTE_LENGTH),i=0;i<32;++i)n[i]=t[i];return function(e,t){var r,a=[P(),P(),P(),P()],n=E(t,32);for(n[0]&=248,n[31]&=127,n[31]|=64,k(a,n),B(e,a),r=0;r<32;++r)t[r+32]=e[r]}(r,n),{publicKey:r,privateKey:n}},l.privateKeyFromAsn1=function(e){var t={},r=[];if(!a.asn1.validate(e,s,t,r)){var n=new Error("Invalid Key.");throw n.errors=r,n}var i=a.asn1.derToOid(t.privateKeyOid),o=a.oids.EdDSA25519;if(i!==o)throw new Error('Invalid OID "'+i+'"; OID must be "'+o+'".');var c=t.privateKey;return{privateKeyBytes:p({message:a.asn1.fromDer(c).value,encoding:"binary"})}},l.publicKeyFromAsn1=function(e){var t={},r=[];if(!a.asn1.validate(e,i,t,r)){var n=new Error("Invalid Key.");throw n.errors=r,n}var s=a.asn1.derToOid(t.publicKeyOid),o=a.oids.EdDSA25519;if(s!==o)throw new Error('Invalid OID "'+s+'"; OID must be "'+o+'".');var c=t.ed25519PublicKey;if(c.length!==l.constants.PUBLIC_KEY_BYTE_LENGTH)throw new Error("Key length is invalid.");return p({message:c,encoding:"binary"})},l.publicKeyFromPrivateKey=function(e){var t=p({message:(e=e||{}).privateKey,encoding:"binary"});if(t.length!==l.constants.PRIVATE_KEY_BYTE_LENGTH)throw new TypeError('"options.privateKey" must have a byte length of '+l.constants.PRIVATE_KEY_BYTE_LENGTH);for(var r=new u(l.constants.PUBLIC_KEY_BYTE_LENGTH),a=0;a<r.length;++a)r[a]=t[32+a];return r},l.sign=function(e){var t=p(e=e||{}),r=p({message:e.privateKey,encoding:"binary"});if(r.length===l.constants.SEED_BYTE_LENGTH)r=l.generateKeyPair({seed:r}).privateKey;else if(r.length!==l.constants.PRIVATE_KEY_BYTE_LENGTH)throw new TypeError('"options.privateKey" must have a byte length of '+l.constants.SEED_BYTE_LENGTH+" or "+l.constants.PRIVATE_KEY_BYTE_LENGTH);var a=new u(l.constants.SIGN_BYTE_LENGTH+t.length);!function(e,t,r,a){var n,i,s=new Float64Array(64),o=[P(),P(),P(),P()],c=E(a,32);c[0]&=248,c[31]&=127,c[31]|=64;var u=r+64;for(n=0;n<r;++n)e[64+n]=t[n];for(n=0;n<32;++n)e[32+n]=c[32+n];var l=E(e.subarray(32),r+32);for(T(l),k(o,l),B(e,o),n=32;n<64;++n)e[n]=a[n];var p=E(e,r+64);for(T(p),n=32;n<64;++n)s[n]=0;for(n=0;n<32;++n)s[n]=l[n];for(n=0;n<32;++n)for(i=0;i<32;i++)s[n+i]+=p[n]*c[i];S(e.subarray(32),s)}(a,t,t.length,r);for(var n=new u(l.constants.SIGN_BYTE_LENGTH),i=0;i<n.length;++i)n[i]=a[i];return n},l.verify=function(e){var t=p(e=e||{});if(void 0===e.signature)throw new TypeError('"options.signature" must be a node.js Buffer, a Uint8Array, a forge ByteBuffer, or a binary string.');var r=p({message:e.signature,encoding:"binary"});if(r.length!==l.constants.SIGN_BYTE_LENGTH)throw new TypeError('"options.signature" must have a byte length of '+l.constants.SIGN_BYTE_LENGTH);var a=p({message:e.publicKey,encoding:"binary"});if(a.length!==l.constants.PUBLIC_KEY_BYTE_LENGTH)throw new TypeError('"options.publicKey" must have a byte length of '+l.constants.PUBLIC_KEY_BYTE_LENGTH);var n,i=new u(l.constants.SIGN_BYTE_LENGTH+t.length),s=new u(l.constants.SIGN_BYTE_LENGTH+t.length);for(n=0;n<l.constants.SIGN_BYTE_LENGTH;++n)i[n]=r[n];for(n=0;n<t.length;++n)i[n+l.constants.SIGN_BYTE_LENGTH]=t[n];return function(e,t,r,a){var n,i=new u(32),s=[P(),P(),P(),P()],o=[P(),P(),P(),P()];if(-1,r<64)return-1;if(function(e,t){var r=P(),a=P(),n=P(),i=P(),s=P(),o=P(),c=P();L(e[2],h),function(e,t){var r;for(r=0;r<16;++r)e[r]=t[2*r]+(t[2*r+1]<<8);e[15]&=32767}(e[1],t),K(n,e[1]),x(i,n,d),O(n,n,e[2]),V(i,e[2],i),K(s,i),K(o,s),x(c,o,s),x(r,c,n),x(r,r,i),function(e,t){var r,a=P();for(r=0;r<16;++r)a[r]=t[r];for(r=250;r>=0;--r)K(a,a),1!==r&&x(a,a,t);for(r=0;r<16;++r)e[r]=a[r]}(r,r),x(r,r,n),x(r,r,i),x(r,r,i),x(e[0],r,i),K(a,e[0]),x(a,a,i),N(a,n)&&x(e[0],e[0],C);if(K(a,e[0]),x(a,a,i),N(a,n))return-1;w(e[0])===t[31]>>7&&O(e[0],f,e[0]);return x(e[3],e[0],e[1]),0}(o,a))return-1;if(!function(e,t){var r;for(r=31;r>=0;--r){if(e[t+r]<m[r])return!0;if(e[t+r]>m[r])return!1}return!1}(t,32))return-1;for(n=0;n<r;++n)e[n]=t[n];for(n=0;n<32;++n)e[n+32]=a[n];var c=E(e,r);if(T(c),_(s,o,c),k(o,t.subarray(32)),I(s,o),B(i,s),r-=64,R(t,0,i,0)){for(n=0;n<r;++n)e[n]=0;return-1}for(n=0;n<r;++n)e[n]=t[n+64];return r}(s,i,i.length,a)>=0};var f=P(),h=P([1]),d=P([30883,4953,19914,30187,55467,16705,2637,112,59544,30585,16505,36039,65139,11119,27886,20995]),y=P([61785,9906,39828,60374,45398,33411,5274,224,53552,61171,33010,6542,64743,22239,55772,9222]),g=P([54554,36645,11616,51542,42930,38181,51040,26924,56412,64982,57905,49316,21502,52590,14035,8553]),v=P([26200,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214]),m=new Float64Array([237,211,245,92,26,99,18,88,214,156,247,162,222,249,222,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16]),C=P([41136,18958,6951,50414,58488,44335,6150,12099,55207,15867,153,11085,57099,20417,9344,11139]);function E(e,t){var r=a.md.sha512.create(),n=new c(e);r.update(n.getBytes(t),"binary");var i=r.digest().getBytes();if("undefined"!=typeof Buffer)return Buffer.from(i,"binary");for(var s=new u(l.constants.HASH_BYTE_LENGTH),o=0;o<64;++o)s[o]=i.charCodeAt(o);return s}function S(e,t){var r,a,n,i;for(a=63;a>=32;--a){for(r=0,n=a-32,i=a-12;n<i;++n)t[n]+=r-16*t[a]*m[n-(a-32)],r=t[n]+128>>8,t[n]-=256*r;t[n]+=r,t[a]=0}for(r=0,n=0;n<32;++n)t[n]+=r-(t[31]>>4)*m[n],r=t[n]>>8,t[n]&=255;for(n=0;n<32;++n)t[n]-=r*m[n];for(a=0;a<32;++a)t[a+1]+=t[a]>>8,e[a]=255&t[a]}function T(e){for(var t=new Float64Array(64),r=0;r<64;++r)t[r]=e[r],e[r]=0;S(e,t)}function I(e,t){var r=P(),a=P(),n=P(),i=P(),s=P(),o=P(),c=P(),u=P(),l=P();O(r,e[1],e[0]),O(l,t[1],t[0]),x(r,r,l),V(a,e[0],e[1]),V(l,t[0],t[1]),x(a,a,l),x(n,e[3],t[3]),x(n,n,y),x(i,e[2],t[2]),V(i,i,i),O(s,a,r),O(o,i,n),V(c,i,n),V(u,a,r),x(e[0],s,o),x(e[1],u,c),x(e[2],c,o),x(e[3],s,u)}function A(e,t,r){for(var a=0;a<4;++a)D(e[a],t[a],r)}function B(e,t){var r=P(),a=P(),n=P();!function(e,t){var r,a=P();for(r=0;r<16;++r)a[r]=t[r];for(r=253;r>=0;--r)K(a,a),2!==r&&4!==r&&x(a,a,t);for(r=0;r<16;++r)e[r]=a[r]}(n,t[2]),x(r,t[0],n),x(a,t[1],n),b(e,a),e[31]^=w(r)<<7}function b(e,t){var r,a,n,i=P(),s=P();for(r=0;r<16;++r)s[r]=t[r];for(U(s),U(s),U(s),a=0;a<2;++a){for(i[0]=s[0]-65517,r=1;r<15;++r)i[r]=s[r]-65535-(i[r-1]>>16&1),i[r-1]&=65535;i[15]=s[15]-32767-(i[14]>>16&1),n=i[15]>>16&1,i[14]&=65535,D(s,i,1-n)}for(r=0;r<16;r++)e[2*r]=255&s[r],e[2*r+1]=s[r]>>8}function N(e,t){var r=new u(32),a=new u(32);return b(r,e),b(a,t),R(r,0,a,0)}function R(e,t,r,a){return function(e,t,r,a,n){var i,s=0;for(i=0;i<n;++i)s|=e[t+i]^r[a+i];return(1&s-1>>>8)-1}(e,t,r,a,32)}function w(e){var t=new u(32);return b(t,e),1&t[0]}function _(e,t,r){var a,n;for(L(e[0],f),L(e[1],h),L(e[2],h),L(e[3],f),n=255;n>=0;--n)A(e,t,a=r[n/8|0]>>(7&n)&1),I(t,e),I(e,e),A(e,t,a)}function k(e,t){var r=[P(),P(),P(),P()];L(r[0],g),L(r[1],v),L(r[2],h),x(r[3],g,v),_(e,r,t)}function L(e,t){var r;for(r=0;r<16;r++)e[r]=0|t[r]}function U(e){var t,r,a=1;for(t=0;t<16;++t)r=e[t]+a+65535,a=Math.floor(r/65536),e[t]=r-65536*a;e[0]+=a-1+37*(a-1)}function D(e,t,r){for(var a,n=~(r-1),i=0;i<16;++i)a=n&(e[i]^t[i]),e[i]^=a,t[i]^=a}function P(e){var t,r=new Float64Array(16);if(e)for(t=0;t<e.length;++t)r[t]=e[t];return r}function V(e,t,r){for(var a=0;a<16;++a)e[a]=t[a]+r[a]}function O(e,t,r){for(var a=0;a<16;++a)e[a]=t[a]-r[a]}function K(e,t){x(e,t,t)}function x(e,t,r){var a,n,i=0,s=0,o=0,c=0,u=0,l=0,p=0,f=0,h=0,d=0,y=0,g=0,v=0,m=0,C=0,E=0,S=0,T=0,I=0,A=0,B=0,b=0,N=0,R=0,w=0,_=0,k=0,L=0,U=0,D=0,P=0,V=r[0],O=r[1],K=r[2],x=r[3],M=r[4],F=r[5],j=r[6],G=r[7],q=r[8],H=r[9],Q=r[10],z=r[11],Y=r[12],X=r[13],W=r[14],Z=r[15];i+=(a=t[0])*V,s+=a*O,o+=a*K,c+=a*x,u+=a*M,l+=a*F,p+=a*j,f+=a*G,h+=a*q,d+=a*H,y+=a*Q,g+=a*z,v+=a*Y,m+=a*X,C+=a*W,E+=a*Z,s+=(a=t[1])*V,o+=a*O,c+=a*K,u+=a*x,l+=a*M,p+=a*F,f+=a*j,h+=a*G,d+=a*q,y+=a*H,g+=a*Q,v+=a*z,m+=a*Y,C+=a*X,E+=a*W,S+=a*Z,o+=(a=t[2])*V,c+=a*O,u+=a*K,l+=a*x,p+=a*M,f+=a*F,h+=a*j,d+=a*G,y+=a*q,g+=a*H,v+=a*Q,m+=a*z,C+=a*Y,E+=a*X,S+=a*W,T+=a*Z,c+=(a=t[3])*V,u+=a*O,l+=a*K,p+=a*x,f+=a*M,h+=a*F,d+=a*j,y+=a*G,g+=a*q,v+=a*H,m+=a*Q,C+=a*z,E+=a*Y,S+=a*X,T+=a*W,I+=a*Z,u+=(a=t[4])*V,l+=a*O,p+=a*K,f+=a*x,h+=a*M,d+=a*F,y+=a*j,g+=a*G,v+=a*q,m+=a*H,C+=a*Q,E+=a*z,S+=a*Y,T+=a*X,I+=a*W,A+=a*Z,l+=(a=t[5])*V,p+=a*O,f+=a*K,h+=a*x,d+=a*M,y+=a*F,g+=a*j,v+=a*G,m+=a*q,C+=a*H,E+=a*Q,S+=a*z,T+=a*Y,I+=a*X,A+=a*W,B+=a*Z,p+=(a=t[6])*V,f+=a*O,h+=a*K,d+=a*x,y+=a*M,g+=a*F,v+=a*j,m+=a*G,C+=a*q,E+=a*H,S+=a*Q,T+=a*z,I+=a*Y,A+=a*X,B+=a*W,b+=a*Z,f+=(a=t[7])*V,h+=a*O,d+=a*K,y+=a*x,g+=a*M,v+=a*F,m+=a*j,C+=a*G,E+=a*q,S+=a*H,T+=a*Q,I+=a*z,A+=a*Y,B+=a*X,b+=a*W,N+=a*Z,h+=(a=t[8])*V,d+=a*O,y+=a*K,g+=a*x,v+=a*M,m+=a*F,C+=a*j,E+=a*G,S+=a*q,T+=a*H,I+=a*Q,A+=a*z,B+=a*Y,b+=a*X,N+=a*W,R+=a*Z,d+=(a=t[9])*V,y+=a*O,g+=a*K,v+=a*x,m+=a*M,C+=a*F,E+=a*j,S+=a*G,T+=a*q,I+=a*H,A+=a*Q,B+=a*z,b+=a*Y,N+=a*X,R+=a*W,w+=a*Z,y+=(a=t[10])*V,g+=a*O,v+=a*K,m+=a*x,C+=a*M,E+=a*F,S+=a*j,T+=a*G,I+=a*q,A+=a*H,B+=a*Q,b+=a*z,N+=a*Y,R+=a*X,w+=a*W,_+=a*Z,g+=(a=t[11])*V,v+=a*O,m+=a*K,C+=a*x,E+=a*M,S+=a*F,T+=a*j,I+=a*G,A+=a*q,B+=a*H,b+=a*Q,N+=a*z,R+=a*Y,w+=a*X,_+=a*W,k+=a*Z,v+=(a=t[12])*V,m+=a*O,C+=a*K,E+=a*x,S+=a*M,T+=a*F,I+=a*j,A+=a*G,B+=a*q,b+=a*H,N+=a*Q,R+=a*z,w+=a*Y,_+=a*X,k+=a*W,L+=a*Z,m+=(a=t[13])*V,C+=a*O,E+=a*K,S+=a*x,T+=a*M,I+=a*F,A+=a*j,B+=a*G,b+=a*q,N+=a*H,R+=a*Q,w+=a*z,_+=a*Y,k+=a*X,L+=a*W,U+=a*Z,C+=(a=t[14])*V,E+=a*O,S+=a*K,T+=a*x,I+=a*M,A+=a*F,B+=a*j,b+=a*G,N+=a*q,R+=a*H,w+=a*Q,_+=a*z,k+=a*Y,L+=a*X,U+=a*W,D+=a*Z,E+=(a=t[15])*V,s+=38*(T+=a*K),o+=38*(I+=a*x),c+=38*(A+=a*M),u+=38*(B+=a*F),l+=38*(b+=a*j),p+=38*(N+=a*G),f+=38*(R+=a*q),h+=38*(w+=a*H),d+=38*(_+=a*Q),y+=38*(k+=a*z),g+=38*(L+=a*Y),v+=38*(U+=a*X),m+=38*(D+=a*W),C+=38*(P+=a*Z),i=(a=(i+=38*(S+=a*O))+(n=1)+65535)-65536*(n=Math.floor(a/65536)),s=(a=s+n+65535)-65536*(n=Math.floor(a/65536)),o=(a=o+n+65535)-65536*(n=Math.floor(a/65536)),c=(a=c+n+65535)-65536*(n=Math.floor(a/65536)),u=(a=u+n+65535)-65536*(n=Math.floor(a/65536)),l=(a=l+n+65535)-65536*(n=Math.floor(a/65536)),p=(a=p+n+65535)-65536*(n=Math.floor(a/65536)),f=(a=f+n+65535)-65536*(n=Math.floor(a/65536)),h=(a=h+n+65535)-65536*(n=Math.floor(a/65536)),d=(a=d+n+65535)-65536*(n=Math.floor(a/65536)),y=(a=y+n+65535)-65536*(n=Math.floor(a/65536)),g=(a=g+n+65535)-65536*(n=Math.floor(a/65536)),v=(a=v+n+65535)-65536*(n=Math.floor(a/65536)),m=(a=m+n+65535)-65536*(n=Math.floor(a/65536)),C=(a=C+n+65535)-65536*(n=Math.floor(a/65536)),E=(a=E+n+65535)-65536*(n=Math.floor(a/65536)),i=(a=(i+=n-1+37*(n-1))+(n=1)+65535)-65536*(n=Math.floor(a/65536)),s=(a=s+n+65535)-65536*(n=Math.floor(a/65536)),o=(a=o+n+65535)-65536*(n=Math.floor(a/65536)),c=(a=c+n+65535)-65536*(n=Math.floor(a/65536)),u=(a=u+n+65535)-65536*(n=Math.floor(a/65536)),l=(a=l+n+65535)-65536*(n=Math.floor(a/65536)),p=(a=p+n+65535)-65536*(n=Math.floor(a/65536)),f=(a=f+n+65535)-65536*(n=Math.floor(a/65536)),h=(a=h+n+65535)-65536*(n=Math.floor(a/65536)),d=(a=d+n+65535)-65536*(n=Math.floor(a/65536)),y=(a=y+n+65535)-65536*(n=Math.floor(a/65536)),g=(a=g+n+65535)-65536*(n=Math.floor(a/65536)),v=(a=v+n+65535)-65536*(n=Math.floor(a/65536)),m=(a=m+n+65535)-65536*(n=Math.floor(a/65536)),C=(a=C+n+65535)-65536*(n=Math.floor(a/65536)),E=(a=E+n+65535)-65536*(n=Math.floor(a/65536)),i+=n-1+37*(n-1),e[0]=i,e[1]=s,e[2]=o,e[3]=c,e[4]=u,e[5]=l,e[6]=p,e[7]=f,e[8]=h,e[9]=d,e[10]=y,e[11]=g,e[12]=v,e[13]=m,e[14]=C,e[15]=E}},function(e,t,r){var a=r(0);r(3);var n=a.asn1;t.privateKeyValidator={name:"PrivateKeyInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"PrivateKeyInfo.version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"PrivateKeyInfo.privateKeyAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"privateKeyOid"}]},{name:"PrivateKeyInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.OCTETSTRING,constructed:!1,capture:"privateKey"}]},t.publicKeyValidator={name:"SubjectPublicKeyInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"subjectPublicKeyInfo",value:[{name:"SubjectPublicKeyInfo.AlgorithmIdentifier",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"publicKeyOid"}]},{tagClass:n.Class.UNIVERSAL,type:n.Type.BITSTRING,constructed:!1,composed:!0,captureBitStringValue:"ed25519PublicKey"}]}},function(e,t,r){var a=r(0);r(1),r(2),r(12),e.exports=a.kem=a.kem||{};var n=a.jsbn.BigInteger;function i(e,t,r,n){e.generate=function(e,i){for(var s=new a.util.ByteBuffer,o=Math.ceil(i/n)+r,c=new a.util.ByteBuffer,u=r;u<o;++u){c.putInt32(u),t.start(),t.update(e+c.getBytes());var l=t.digest();s.putBytes(l.getBytes(n))}return s.truncate(s.length()-i),s.getBytes()}}a.kem.rsa={},a.kem.rsa.create=function(e,t){var r=(t=t||{}).prng||a.random,i={encrypt:function(t,i){var s,o=Math.ceil(t.n.bitLength()/8);do{s=new n(a.util.bytesToHex(r.getBytesSync(o)),16).mod(t.n)}while(s.compareTo(n.ONE)<=0);var c=o-(s=a.util.hexToBytes(s.toString(16))).length;return c>0&&(s=a.util.fillString(String.fromCharCode(0),c)+s),{encapsulation:t.encrypt(s,"NONE"),key:e.generate(s,i)}},decrypt:function(t,r,a){var n=t.decrypt(r,"NONE");return e.generate(n,a)}};return i},a.kem.kdf1=function(e,t){i(this,e,0,t||e.digestLength)},a.kem.kdf2=function(e,t){i(this,e,1,t||e.digestLength)}},function(e,t,r){var a=r(0);r(1),e.exports=a.log=a.log||{},a.log.levels=["none","error","warning","info","debug","verbose","max"];var n={},i=[],s=null;a.log.LEVEL_LOCKED=2,a.log.NO_LEVEL_CHECK=4,a.log.INTERPOLATE=8;for(var o=0;o<a.log.levels.length;++o){var c=a.log.levels[o];n[c]={index:o,name:c.toUpperCase()}}a.log.logMessage=function(e){for(var t=n[e.level].index,r=0;r<i.length;++r){var s=i[r];if(s.flags&a.log.NO_LEVEL_CHECK)s.f(e);else t<=n[s.level].index&&s.f(s,e)}},a.log.prepareStandard=function(e){"standard"in e||(e.standard=n[e.level].name+" ["+e.category+"] "+e.message)},a.log.prepareFull=function(e){if(!("full"in e)){var t=[e.message];t=t.concat([]||!1),e.full=a.util.format.apply(this,t)}},a.log.prepareStandardFull=function(e){"standardFull"in e||(a.log.prepareStandard(e),e.standardFull=e.standard)};var u=["error","warning","info","debug","verbose"];for(o=0;o<u.length;++o)!function(e){a.log[e]=function(t,r){var n=Array.prototype.slice.call(arguments).slice(2),i={timestamp:new Date,level:e,category:t,message:r,arguments:n};a.log.logMessage(i)}}(u[o]);if(a.log.makeLogger=function(e){var t={flags:0,f:e};return a.log.setLevel(t,"none"),t},a.log.setLevel=function(e,t){var r=!1;if(e&&!(e.flags&a.log.LEVEL_LOCKED))for(var n=0;n<a.log.levels.length;++n){if(t==a.log.levels[n]){e.level=t,r=!0;break}}return r},a.log.lock=function(e,t){void 0===t||t?e.flags|=a.log.LEVEL_LOCKED:e.flags&=~a.log.LEVEL_LOCKED},a.log.addLogger=function(e){i.push(e)},"undefined"!=typeof console&&"log"in console){var l;if(console.error&&console.warn&&console.info&&console.debug){var p={error:console.error,warning:console.warn,info:console.info,debug:console.debug,verbose:console.debug},f=function(e,t){a.log.prepareStandard(t);var r=p[t.level],n=[t.standard];n=n.concat(t.arguments.slice()),r.apply(console,n)};l=a.log.makeLogger(f)}else{f=function(e,t){a.log.prepareStandardFull(t),console.log(t.standardFull)};l=a.log.makeLogger(f)}a.log.setLevel(l,"debug"),a.log.addLogger(l),s=l}else console={log:function(){}};if(null!==s&&"undefined"!=typeof window&&window.location){var h=new URL(window.location.href).searchParams;if(h.has("console.level")&&a.log.setLevel(s,h.get("console.level").slice(-1)[0]),h.has("console.lock"))"true"==h.get("console.lock").slice(-1)[0]&&a.log.lock(s)}a.log.consoleLogger=s},function(e,t,r){e.exports=r(4),r(14),r(9),r(23),r(31)},function(e,t,r){var a=r(0);r(5),r(3),r(10),r(6),r(7),r(29),r(2),r(1),r(17);var n=a.asn1,i=e.exports=a.pkcs7=a.pkcs7||{};function s(e){var t={},r=[];if(!n.validate(e,i.asn1.recipientInfoValidator,t,r)){var s=new Error("Cannot read PKCS#7 RecipientInfo. ASN.1 object is not an PKCS#7 RecipientInfo.");throw s.errors=r,s}return{version:t.version.charCodeAt(0),issuer:a.pki.RDNAttributesAsArray(t.issuer),serialNumber:a.util.createBuffer(t.serial).toHex(),encryptedContent:{algorithm:n.derToOid(t.encAlgorithm),parameter:t.encParameter?t.encParameter.value:void 0,content:t.encKey}}}function o(e){for(var t,r=[],i=0;i<e.length;++i)r.push((t=e[i],n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(t.version).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[a.pki.distinguishedNameToAsn1({attributes:t.issuer}),n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,a.util.hexToBytes(t.serialNumber))]),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(t.encryptedContent.algorithm).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")]),n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,t.encryptedContent.content)])));return r}function c(e){var t=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(e.version).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[a.pki.distinguishedNameToAsn1({attributes:e.issuer}),n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,a.util.hexToBytes(e.serialNumber))]),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.digestAlgorithm).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")])]);if(e.authenticatedAttributesAsn1&&t.value.push(e.authenticatedAttributesAsn1),t.value.push(n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.signatureAlgorithm).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")])),t.value.push(n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,e.signature)),e.unauthenticatedAttributes.length>0){for(var r=n.create(n.Class.CONTEXT_SPECIFIC,1,!0,[]),i=0;i<e.unauthenticatedAttributes.length;++i){var s=e.unauthenticatedAttributes[i];r.values.push(u(s))}t.value.push(r)}return t}function u(e){var t;if(e.type===a.pki.oids.contentType)t=n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.value).getBytes());else if(e.type===a.pki.oids.messageDigest)t=n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,e.value.bytes());else if(e.type===a.pki.oids.signingTime){var r=new Date("1950-01-01T00:00:00Z"),i=new Date("2050-01-01T00:00:00Z"),s=e.value;if("string"==typeof s){var o=Date.parse(s);s=isNaN(o)?13===s.length?n.utcTimeToDate(s):n.generalizedTimeToDate(s):new Date(o)}t=s>=r&&s<i?n.create(n.Class.UNIVERSAL,n.Type.UTCTIME,!1,n.dateToUtcTime(s)):n.create(n.Class.UNIVERSAL,n.Type.GENERALIZEDTIME,!1,n.dateToGeneralizedTime(s))}return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.type).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SET,!0,[t])])}function l(e,t,r){var i={};if(!n.validate(t,r,i,[])){var s=new Error("Cannot read PKCS#7 message. ASN.1 object is not a supported PKCS#7 message.");throw s.errors=s,s}if(n.derToOid(i.contentType)!==a.pki.oids.data)throw new Error("Unsupported PKCS#7 message. Only wrapped ContentType Data supported.");if(i.encryptedContent){var o="";if(a.util.isArray(i.encryptedContent))for(var c=0;c<i.encryptedContent.length;++c){if(i.encryptedContent[c].type!==n.Type.OCTETSTRING)throw new Error("Malformed PKCS#7 message, expecting encrypted content constructed of only OCTET STRING objects.");o+=i.encryptedContent[c].value}else o=i.encryptedContent;e.encryptedContent={algorithm:n.derToOid(i.encAlgorithm),parameter:a.util.createBuffer(i.encParameter.value),content:a.util.createBuffer(o)}}if(i.content){o="";if(a.util.isArray(i.content))for(c=0;c<i.content.length;++c){if(i.content[c].type!==n.Type.OCTETSTRING)throw new Error("Malformed PKCS#7 message, expecting content constructed of only OCTET STRING objects.");o+=i.content[c].value}else o=i.content;e.content=a.util.createBuffer(o)}return e.version=i.version.charCodeAt(0),e.rawCapture=i,i}function p(e){if(void 0===e.encryptedContent.key)throw new Error("Symmetric key not available.");if(void 0===e.content){var t;switch(e.encryptedContent.algorithm){case a.pki.oids["aes128-CBC"]:case a.pki.oids["aes192-CBC"]:case a.pki.oids["aes256-CBC"]:t=a.aes.createDecryptionCipher(e.encryptedContent.key);break;case a.pki.oids.desCBC:case a.pki.oids["des-EDE3-CBC"]:t=a.des.createDecryptionCipher(e.encryptedContent.key);break;default:throw new Error("Unsupported symmetric cipher, OID "+e.encryptedContent.algorithm)}if(t.start(e.encryptedContent.parameter),t.update(e.encryptedContent.content),!t.finish())throw new Error("Symmetric decryption failed.");e.content=t.output}}i.messageFromPem=function(e){var t=a.pem.decode(e)[0];if("PKCS7"!==t.type){var r=new Error('Could not convert PKCS#7 message from PEM; PEM header type is not "PKCS#7".');throw r.headerType=t.type,r}if(t.procType&&"ENCRYPTED"===t.procType.type)throw new Error("Could not convert PKCS#7 message from PEM; PEM is encrypted.");var s=n.fromDer(t.body);return i.messageFromAsn1(s)},i.messageToPem=function(e,t){var r={type:"PKCS7",body:n.toDer(e.toAsn1()).getBytes()};return a.pem.encode(r,{maxline:t})},i.messageFromAsn1=function(e){var t={},r=[];if(!n.validate(e,i.asn1.contentInfoValidator,t,r)){var s=new Error("Cannot read PKCS#7 message. ASN.1 object is not an PKCS#7 ContentInfo.");throw s.errors=r,s}var o,c=n.derToOid(t.contentType);switch(c){case a.pki.oids.envelopedData:o=i.createEnvelopedData();break;case a.pki.oids.encryptedData:o=i.createEncryptedData();break;case a.pki.oids.signedData:o=i.createSignedData();break;default:throw new Error("Cannot read PKCS#7 message. ContentType with OID "+c+" is not (yet) supported.")}return o.fromAsn1(t.content.value[0]),o},i.createSignedData=function(){var e=null;return e={type:a.pki.oids.signedData,version:1,certificates:[],crls:[],signers:[],digestAlgorithmIdentifiers:[],contentInfo:null,signerInfos:[],fromAsn1:function(t){if(l(e,t,i.asn1.signedDataValidator),e.certificates=[],e.crls=[],e.digestAlgorithmIdentifiers=[],e.contentInfo=null,e.signerInfos=[],e.rawCapture.certificates)for(var r=e.rawCapture.certificates.value,n=0;n<r.length;++n)e.certificates.push(a.pki.certificateFromAsn1(r[n]))},toAsn1:function(){e.contentInfo||e.sign();for(var t=[],r=0;r<e.certificates.length;++r)t.push(a.pki.certificateToAsn1(e.certificates[r]));var i=[],s=n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(e.version).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SET,!0,e.digestAlgorithmIdentifiers),e.contentInfo])]);return t.length>0&&s.value[0].value.push(n.create(n.Class.CONTEXT_SPECIFIC,0,!0,t)),i.length>0&&s.value[0].value.push(n.create(n.Class.CONTEXT_SPECIFIC,1,!0,i)),s.value[0].value.push(n.create(n.Class.UNIVERSAL,n.Type.SET,!0,e.signerInfos)),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.type).getBytes()),s])},addSigner:function(t){var r=t.issuer,n=t.serialNumber;if(t.certificate){var i=t.certificate;"string"==typeof i&&(i=a.pki.certificateFromPem(i)),r=i.issuer.attributes,n=i.serialNumber}var s=t.key;if(!s)throw new Error("Could not add PKCS#7 signer; no private key specified.");"string"==typeof s&&(s=a.pki.privateKeyFromPem(s));var o=t.digestAlgorithm||a.pki.oids.sha1;switch(o){case a.pki.oids.sha1:case a.pki.oids.sha256:case a.pki.oids.sha384:case a.pki.oids.sha512:case a.pki.oids.md5:break;default:throw new Error("Could not add PKCS#7 signer; unknown message digest algorithm: "+o)}var c=t.authenticatedAttributes||[];if(c.length>0){for(var u=!1,l=!1,p=0;p<c.length;++p){var f=c[p];if(u||f.type!==a.pki.oids.contentType){if(l||f.type!==a.pki.oids.messageDigest);else if(l=!0,u)break}else if(u=!0,l)break}if(!u||!l)throw new Error("Invalid signer.authenticatedAttributes. If signer.authenticatedAttributes is specified, then it must contain at least two attributes, PKCS #9 content-type and PKCS #9 message-digest.")}e.signers.push({key:s,version:1,issuer:r,serialNumber:n,digestAlgorithm:o,signatureAlgorithm:a.pki.oids.rsaEncryption,signature:null,authenticatedAttributes:c,unauthenticatedAttributes:[]})},sign:function(t){var r;(t=t||{},"object"!=typeof e.content||null===e.contentInfo)&&(e.contentInfo=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(a.pki.oids.data).getBytes())]),"content"in e&&(e.content instanceof a.util.ByteBuffer?r=e.content.bytes():"string"==typeof e.content&&(r=a.util.encodeUtf8(e.content)),t.detached?e.detachedContent=n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,r):e.contentInfo.value.push(n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,r)]))));0!==e.signers.length&&function(t){var r;r=e.detachedContent?e.detachedContent:(r=e.contentInfo.value[1]).value[0];if(!r)throw new Error("Could not sign PKCS#7 message; there is no content to sign.");var i=n.derToOid(e.contentInfo.value[0].value),s=n.toDer(r);for(var o in s.getByte(),n.getBerValueLength(s),s=s.getBytes(),t)t[o].start().update(s);for(var l=new Date,p=0;p<e.signers.length;++p){var f=e.signers[p];if(0===f.authenticatedAttributes.length){if(i!==a.pki.oids.data)throw new Error("Invalid signer; authenticatedAttributes must be present when the ContentInfo content type is not PKCS#7 Data.")}else{f.authenticatedAttributesAsn1=n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[]);for(var h=n.create(n.Class.UNIVERSAL,n.Type.SET,!0,[]),d=0;d<f.authenticatedAttributes.length;++d){var y=f.authenticatedAttributes[d];y.type===a.pki.oids.messageDigest?y.value=t[f.digestAlgorithm].digest():y.type===a.pki.oids.signingTime&&(y.value||(y.value=l)),h.value.push(u(y)),f.authenticatedAttributesAsn1.value.push(u(y))}s=n.toDer(h).getBytes(),f.md.start().update(s)}f.signature=f.key.sign(f.md,"RSASSA-PKCS1-V1_5")}e.signerInfos=function(e){for(var t=[],r=0;r<e.length;++r)t.push(c(e[r]));return t}(e.signers)}(function(){for(var t={},r=0;r<e.signers.length;++r){var i=e.signers[r];(s=i.digestAlgorithm)in t||(t[s]=a.md[a.pki.oids[s]].create()),0===i.authenticatedAttributes.length?i.md=t[s]:i.md=a.md[a.pki.oids[s]].create()}for(var s in e.digestAlgorithmIdentifiers=[],t)e.digestAlgorithmIdentifiers.push(n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(s).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")]));return t}())},verify:function(){throw new Error("PKCS#7 signature verification not yet implemented.")},addCertificate:function(t){"string"==typeof t&&(t=a.pki.certificateFromPem(t)),e.certificates.push(t)},addCertificateRevokationList:function(e){throw new Error("PKCS#7 CRL support not yet implemented.")}}},i.createEncryptedData=function(){var e=null;return e={type:a.pki.oids.encryptedData,version:0,encryptedContent:{algorithm:a.pki.oids["aes256-CBC"]},fromAsn1:function(t){l(e,t,i.asn1.encryptedDataValidator)},decrypt:function(t){void 0!==t&&(e.encryptedContent.key=t),p(e)}}},i.createEnvelopedData=function(){var e=null;return e={type:a.pki.oids.envelopedData,version:0,recipients:[],encryptedContent:{algorithm:a.pki.oids["aes256-CBC"]},fromAsn1:function(t){var r=l(e,t,i.asn1.envelopedDataValidator);e.recipients=function(e){for(var t=[],r=0;r<e.length;++r)t.push(s(e[r]));return t}(r.recipientInfos.value)},toAsn1:function(){return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.type).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(e.version).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SET,!0,o(e.recipients)),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,(t=e.encryptedContent,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(a.pki.oids.data).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(t.algorithm).getBytes()),t.parameter?n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,t.parameter.getBytes()):void 0]),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,t.content.getBytes())])]))])])]);var t},findRecipient:function(t){for(var r=t.issuer.attributes,a=0;a<e.recipients.length;++a){var n=e.recipients[a],i=n.issuer;if(n.serialNumber===t.serialNumber&&i.length===r.length){for(var s=!0,o=0;o<r.length;++o)if(i[o].type!==r[o].type||i[o].value!==r[o].value){s=!1;break}if(s)return n}}return null},decrypt:function(t,r){if(void 0===e.encryptedContent.key&&void 0!==t&&void 0!==r)switch(t.encryptedContent.algorithm){case a.pki.oids.rsaEncryption:case a.pki.oids.desCBC:var n=r.decrypt(t.encryptedContent.content);e.encryptedContent.key=a.util.createBuffer(n);break;default:throw new Error("Unsupported asymmetric cipher, OID "+t.encryptedContent.algorithm)}p(e)},addRecipient:function(t){e.recipients.push({version:0,issuer:t.issuer.attributes,serialNumber:t.serialNumber,encryptedContent:{algorithm:a.pki.oids.rsaEncryption,key:t.publicKey}})},encrypt:function(t,r){if(void 0===e.encryptedContent.content){var n,i,s;switch(r=r||e.encryptedContent.algorithm,t=t||e.encryptedContent.key,r){case a.pki.oids["aes128-CBC"]:n=16,i=16,s=a.aes.createEncryptionCipher;break;case a.pki.oids["aes192-CBC"]:n=24,i=16,s=a.aes.createEncryptionCipher;break;case a.pki.oids["aes256-CBC"]:n=32,i=16,s=a.aes.createEncryptionCipher;break;case a.pki.oids["des-EDE3-CBC"]:n=24,i=8,s=a.des.createEncryptionCipher;break;default:throw new Error("Unsupported symmetric cipher, OID "+r)}if(void 0===t)t=a.util.createBuffer(a.random.getBytes(n));else if(t.length()!=n)throw new Error("Symmetric key has wrong length; got "+t.length()+" bytes, expected "+n+".");e.encryptedContent.algorithm=r,e.encryptedContent.key=t,e.encryptedContent.parameter=a.util.createBuffer(a.random.getBytes(i));var o=s(t);if(o.start(e.encryptedContent.parameter.copy()),o.update(e.content),!o.finish())throw new Error("Symmetric encryption failed.");e.encryptedContent.content=o.output}for(var c=0;c<e.recipients.length;++c){var u=e.recipients[c];if(void 0===u.encryptedContent.content)switch(u.encryptedContent.algorithm){case a.pki.oids.rsaEncryption:u.encryptedContent.content=u.encryptedContent.key.encrypt(e.encryptedContent.key.data);break;default:throw new Error("Unsupported asymmetric cipher, OID "+u.encryptedContent.algorithm)}}}}}},function(e,t,r){var a=r(0);r(5),r(8),r(14),r(9),r(1);var n=e.exports=a.ssh=a.ssh||{};function i(e,t){var r=t.toString(16);r[0]>="8"&&(r="00"+r);var n=a.util.hexToBytes(r);e.putInt32(n.length),e.putBytes(n)}function s(e,t){e.putInt32(t.length),e.putString(t)}function o(){for(var e=a.md.sha1.create(),t=arguments.length,r=0;r<t;++r)e.update(arguments[r]);return e.digest()}n.privateKeyToPutty=function(e,t,r){var n=""===(t=t||"")?"none":"aes256-cbc",c="PuTTY-User-Key-File-2: ssh-rsa\r\n";c+="Encryption: "+n+"\r\n",c+="Comment: "+(r=r||"")+"\r\n";var u=a.util.createBuffer();s(u,"ssh-rsa"),i(u,e.e),i(u,e.n);var l=a.util.encode64(u.bytes(),64),p=Math.floor(l.length/66)+1;c+="Public-Lines: "+p+"\r\n",c+=l;var f,h=a.util.createBuffer();if(i(h,e.d),i(h,e.p),i(h,e.q),i(h,e.qInv),t){var d=h.length()+16-1;d-=d%16;var y=o(h.bytes());y.truncate(y.length()-d+h.length()),h.putBuffer(y);var g=a.util.createBuffer();g.putBuffer(o("\0\0\0\0",t)),g.putBuffer(o("\0\0\0",t));var v=a.aes.createEncryptionCipher(g.truncate(8),"CBC");v.start(a.util.createBuffer().fillWithByte(0,16)),v.update(h.copy()),v.finish();var m=v.output;m.truncate(16),f=a.util.encode64(m.bytes(),64)}else f=a.util.encode64(h.bytes(),64);c+="\r\nPrivate-Lines: "+(p=Math.floor(f.length/66)+1)+"\r\n",c+=f;var C=o("putty-private-key-file-mac-key",t),E=a.util.createBuffer();s(E,"ssh-rsa"),s(E,n),s(E,r),E.putInt32(u.length()),E.putBuffer(u),E.putInt32(h.length()),E.putBuffer(h);var S=a.hmac.create();return S.start("sha1",C),S.update(E.bytes()),c+="\r\nPrivate-MAC: "+S.digest().toHex()+"\r\n"},n.publicKeyToOpenSSH=function(e,t){t=t||"";var r=a.util.createBuffer();return s(r,"ssh-rsa"),i(r,e.e),i(r,e.n),"ssh-rsa "+a.util.encode64(r.bytes())+" "+t},n.privateKeyToOpenSSH=function(e,t){return t?a.pki.encryptRsaPrivateKey(e,t,{legacy:!0,algorithm:"aes128"}):a.pki.privateKeyToPem(e)},n.getPublicKeyFingerprint=function(e,t){var r=(t=t||{}).md||a.md.md5.create(),n=a.util.createBuffer();s(n,"ssh-rsa"),i(n,e.e),i(n,e.n),r.start(),r.update(n.getBytes());var o=r.digest();if("hex"===t.encoding){var c=o.toHex();return t.delimiter?c.match(/.{2}/g).join(t.delimiter):c}if("binary"===t.encoding)return o.getBytes();if(t.encoding)throw new Error('Unknown encoding "'+t.encoding+'".');return o}}])}));
+//# sourceMappingURL=forge.min.js.map
Index: frontend/node_modules/node-forge/dist/forge.min.js.map
===================================================================
--- frontend/node_modules/node-forge/dist/forge.min.js.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/dist/forge.min.js.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"forge.min.js","sources":["webpack://[name]/forge.min.js"],"mappings":"AAAA","sourceRoot":""}
Index: frontend/node_modules/node-forge/dist/prime.worker.min.js
===================================================================
--- frontend/node_modules/node-forge/dist/prime.worker.min.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/dist/prime.worker.min.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,2 @@
+!function(t){var i={};function r(o){if(i[o])return i[o].exports;var s=i[o]={i:o,l:!1,exports:{}};return t[o].call(s.exports,s,s.exports,r),s.l=!0,s.exports}r.m=t,r.c=i,r.d=function(t,i,o){r.o(t,i)||Object.defineProperty(t,i,{enumerable:!0,get:o})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,i){if(1&i&&(t=r(t)),8&i)return t;if(4&i&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(r.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&i&&"string"!=typeof t)for(var s in t)r.d(o,s,function(i){return t[i]}.bind(null,s));return o},r.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(i,"a",i),i},r.o=function(t,i){return Object.prototype.hasOwnProperty.call(t,i)},r.p="",r(r.s=1)}([function(t,i){t.exports={options:{usePureJavaScript:!1}}},function(t,i,r){r(2),t.exports=r(0)},function(t,i,r){var o=r(0);r(3);var s=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],e=(1<<26)/s[s.length-1],a=o.jsbn.BigInteger;new a(null).fromInt(2),self.addEventListener("message",(function(t){var i=function(t){for(var i=new a(t.hex,16),r=0,o=t.workLoad,s=0;s<o;++s){if(h(i))return{found:!0,prime:i.toString(16)};i.dAddOffset(n[r++%8],0)}return{found:!1}}(t.data);self.postMessage(i)})),self.postMessage({found:!1});var n=[6,4,2,4,2,4,6,2];function h(t){for(var i=1;i<s.length;){for(var r=s[i],o=i+1;o<s.length&&r<e;)r*=s[o++];for(r=t.modInt(r);i<o;)if(r%s[i++]==0)return!1}return function(t){var i=t.subtract(a.ONE),r=i.getLowestSetBit();if(r<=0)return!1;for(var o,s=i.shiftRight(r),e=(p=t.bitLength(),p<=100?27:p<=150?18:p<=200?15:p<=250?12:p<=300?9:p<=350?8:p<=400?7:p<=500?6:p<=600?5:p<=800?4:p<=1250?3:2),n={nextBytes:function(t){for(var i=0;i<t.length;++i)t[i]=Math.floor(255*Math.random())}},h=0;h<e;++h){do{o=new a(t.bitLength(),n)}while(o.compareTo(a.ONE)<=0||o.compareTo(i)>=0);var u=o.modPow(s,t);if(0!==u.compareTo(a.ONE)&&0!==u.compareTo(i)){for(var f=r;--f;){if(0===(u=u.modPowInt(2,t)).compareTo(a.ONE))return!1;if(0===u.compareTo(i))break}if(0===f)return!1}}var p;return!0}(t)}},function(t,i,r){var o,s=r(0);t.exports=s.jsbn=s.jsbn||{};function e(t,i,r){this.data=[],null!=t&&("number"==typeof t?this.fromNumber(t,i,r):null==i&&"string"!=typeof t?this.fromString(t,256):this.fromString(t,i))}function a(){return new e(null)}function n(t,i,r,o,s,e){for(var a=16383&i,n=i>>14;--e>=0;){var h=16383&this.data[t],u=this.data[t++]>>14,f=n*h+u*a;s=((h=a*h+((16383&f)<<14)+r.data[o]+s)>>28)+(f>>14)+n*u,r.data[o++]=268435455&h}return s}s.jsbn.BigInteger=e,"undefined"==typeof navigator?(e.prototype.am=n,o=28):"Microsoft Internet Explorer"==navigator.appName?(e.prototype.am=function(t,i,r,o,s,e){for(var a=32767&i,n=i>>15;--e>=0;){var h=32767&this.data[t],u=this.data[t++]>>15,f=n*h+u*a;s=((h=a*h+((32767&f)<<15)+r.data[o]+(1073741823&s))>>>30)+(f>>>15)+n*u+(s>>>30),r.data[o++]=1073741823&h}return s},o=30):"Netscape"!=navigator.appName?(e.prototype.am=function(t,i,r,o,s,e){for(;--e>=0;){var a=i*this.data[t++]+r.data[o]+s;s=Math.floor(a/67108864),r.data[o++]=67108863&a}return s},o=26):(e.prototype.am=n,o=28),e.prototype.DB=o,e.prototype.DM=(1<<o)-1,e.prototype.DV=1<<o;e.prototype.FV=Math.pow(2,52),e.prototype.F1=52-o,e.prototype.F2=2*o-52;var h,u,f=new Array;for(h="0".charCodeAt(0),u=0;u<=9;++u)f[h++]=u;for(h="a".charCodeAt(0),u=10;u<36;++u)f[h++]=u;for(h="A".charCodeAt(0),u=10;u<36;++u)f[h++]=u;function p(t){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(t)}function d(t,i){var r=f[t.charCodeAt(i)];return null==r?-1:r}function c(t){var i=a();return i.fromInt(t),i}function m(t){var i,r=1;return 0!=(i=t>>>16)&&(t=i,r+=16),0!=(i=t>>8)&&(t=i,r+=8),0!=(i=t>>4)&&(t=i,r+=4),0!=(i=t>>2)&&(t=i,r+=2),0!=(i=t>>1)&&(t=i,r+=1),r}function l(t){this.m=t}function v(t){this.m=t,this.mp=t.invDigit(),this.mpl=32767&this.mp,this.mph=this.mp>>15,this.um=(1<<t.DB-15)-1,this.mt2=2*t.t}function T(t,i){return t&i}function y(t,i){return t|i}function b(t,i){return t^i}function g(t,i){return t&~i}function D(t){if(0==t)return-1;var i=0;return 0==(65535&t)&&(t>>=16,i+=16),0==(255&t)&&(t>>=8,i+=8),0==(15&t)&&(t>>=4,i+=4),0==(3&t)&&(t>>=2,i+=2),0==(1&t)&&++i,i}function B(t){for(var i=0;0!=t;)t&=t-1,++i;return i}function S(){}function M(t){return t}function w(t){this.r2=a(),this.q3=a(),e.ONE.dlShiftTo(2*t.t,this.r2),this.mu=this.r2.divide(t),this.m=t}l.prototype.convert=function(t){return t.s<0||t.compareTo(this.m)>=0?t.mod(this.m):t},l.prototype.revert=function(t){return t},l.prototype.reduce=function(t){t.divRemTo(this.m,null,t)},l.prototype.mulTo=function(t,i,r){t.multiplyTo(i,r),this.reduce(r)},l.prototype.sqrTo=function(t,i){t.squareTo(i),this.reduce(i)},v.prototype.convert=function(t){var i=a();return t.abs().dlShiftTo(this.m.t,i),i.divRemTo(this.m,null,i),t.s<0&&i.compareTo(e.ZERO)>0&&this.m.subTo(i,i),i},v.prototype.revert=function(t){var i=a();return t.copyTo(i),this.reduce(i),i},v.prototype.reduce=function(t){for(;t.t<=this.mt2;)t.data[t.t++]=0;for(var i=0;i<this.m.t;++i){var r=32767&t.data[i],o=r*this.mpl+((r*this.mph+(t.data[i]>>15)*this.mpl&this.um)<<15)&t.DM;for(r=i+this.m.t,t.data[r]+=this.m.am(0,o,t,i,0,this.m.t);t.data[r]>=t.DV;)t.data[r]-=t.DV,t.data[++r]++}t.clamp(),t.drShiftTo(this.m.t,t),t.compareTo(this.m)>=0&&t.subTo(this.m,t)},v.prototype.mulTo=function(t,i,r){t.multiplyTo(i,r),this.reduce(r)},v.prototype.sqrTo=function(t,i){t.squareTo(i),this.reduce(i)},e.prototype.copyTo=function(t){for(var i=this.t-1;i>=0;--i)t.data[i]=this.data[i];t.t=this.t,t.s=this.s},e.prototype.fromInt=function(t){this.t=1,this.s=t<0?-1:0,t>0?this.data[0]=t:t<-1?this.data[0]=t+this.DV:this.t=0},e.prototype.fromString=function(t,i){var r;if(16==i)r=4;else if(8==i)r=3;else if(256==i)r=8;else if(2==i)r=1;else if(32==i)r=5;else{if(4!=i)return void this.fromRadix(t,i);r=2}this.t=0,this.s=0;for(var o=t.length,s=!1,a=0;--o>=0;){var n=8==r?255&t[o]:d(t,o);n<0?"-"==t.charAt(o)&&(s=!0):(s=!1,0==a?this.data[this.t++]=n:a+r>this.DB?(this.data[this.t-1]|=(n&(1<<this.DB-a)-1)<<a,this.data[this.t++]=n>>this.DB-a):this.data[this.t-1]|=n<<a,(a+=r)>=this.DB&&(a-=this.DB))}8==r&&0!=(128&t[0])&&(this.s=-1,a>0&&(this.data[this.t-1]|=(1<<this.DB-a)-1<<a)),this.clamp(),s&&e.ZERO.subTo(this,this)},e.prototype.clamp=function(){for(var t=this.s&this.DM;this.t>0&&this.data[this.t-1]==t;)--this.t},e.prototype.dlShiftTo=function(t,i){var r;for(r=this.t-1;r>=0;--r)i.data[r+t]=this.data[r];for(r=t-1;r>=0;--r)i.data[r]=0;i.t=this.t+t,i.s=this.s},e.prototype.drShiftTo=function(t,i){for(var r=t;r<this.t;++r)i.data[r-t]=this.data[r];i.t=Math.max(this.t-t,0),i.s=this.s},e.prototype.lShiftTo=function(t,i){var r,o=t%this.DB,s=this.DB-o,e=(1<<s)-1,a=Math.floor(t/this.DB),n=this.s<<o&this.DM;for(r=this.t-1;r>=0;--r)i.data[r+a+1]=this.data[r]>>s|n,n=(this.data[r]&e)<<o;for(r=a-1;r>=0;--r)i.data[r]=0;i.data[a]=n,i.t=this.t+a+1,i.s=this.s,i.clamp()},e.prototype.rShiftTo=function(t,i){i.s=this.s;var r=Math.floor(t/this.DB);if(r>=this.t)i.t=0;else{var o=t%this.DB,s=this.DB-o,e=(1<<o)-1;i.data[0]=this.data[r]>>o;for(var a=r+1;a<this.t;++a)i.data[a-r-1]|=(this.data[a]&e)<<s,i.data[a-r]=this.data[a]>>o;o>0&&(i.data[this.t-r-1]|=(this.s&e)<<s),i.t=this.t-r,i.clamp()}},e.prototype.subTo=function(t,i){for(var r=0,o=0,s=Math.min(t.t,this.t);r<s;)o+=this.data[r]-t.data[r],i.data[r++]=o&this.DM,o>>=this.DB;if(t.t<this.t){for(o-=t.s;r<this.t;)o+=this.data[r],i.data[r++]=o&this.DM,o>>=this.DB;o+=this.s}else{for(o+=this.s;r<t.t;)o-=t.data[r],i.data[r++]=o&this.DM,o>>=this.DB;o-=t.s}i.s=o<0?-1:0,o<-1?i.data[r++]=this.DV+o:o>0&&(i.data[r++]=o),i.t=r,i.clamp()},e.prototype.multiplyTo=function(t,i){var r=this.abs(),o=t.abs(),s=r.t;for(i.t=s+o.t;--s>=0;)i.data[s]=0;for(s=0;s<o.t;++s)i.data[s+r.t]=r.am(0,o.data[s],i,s,0,r.t);i.s=0,i.clamp(),this.s!=t.s&&e.ZERO.subTo(i,i)},e.prototype.squareTo=function(t){for(var i=this.abs(),r=t.t=2*i.t;--r>=0;)t.data[r]=0;for(r=0;r<i.t-1;++r){var o=i.am(r,i.data[r],t,2*r,0,1);(t.data[r+i.t]+=i.am(r+1,2*i.data[r],t,2*r+1,o,i.t-r-1))>=i.DV&&(t.data[r+i.t]-=i.DV,t.data[r+i.t+1]=1)}t.t>0&&(t.data[t.t-1]+=i.am(r,i.data[r],t,2*r,0,1)),t.s=0,t.clamp()},e.prototype.divRemTo=function(t,i,r){var o=t.abs();if(!(o.t<=0)){var s=this.abs();if(s.t<o.t)return null!=i&&i.fromInt(0),void(null!=r&&this.copyTo(r));null==r&&(r=a());var n=a(),h=this.s,u=t.s,f=this.DB-m(o.data[o.t-1]);f>0?(o.lShiftTo(f,n),s.lShiftTo(f,r)):(o.copyTo(n),s.copyTo(r));var p=n.t,d=n.data[p-1];if(0!=d){var c=d*(1<<this.F1)+(p>1?n.data[p-2]>>this.F2:0),l=this.FV/c,v=(1<<this.F1)/c,T=1<<this.F2,y=r.t,b=y-p,g=null==i?a():i;for(n.dlShiftTo(b,g),r.compareTo(g)>=0&&(r.data[r.t++]=1,r.subTo(g,r)),e.ONE.dlShiftTo(p,g),g.subTo(n,n);n.t<p;)n.data[n.t++]=0;for(;--b>=0;){var D=r.data[--y]==d?this.DM:Math.floor(r.data[y]*l+(r.data[y-1]+T)*v);if((r.data[y]+=n.am(0,D,r,b,0,p))<D)for(n.dlShiftTo(b,g),r.subTo(g,r);r.data[y]<--D;)r.subTo(g,r)}null!=i&&(r.drShiftTo(p,i),h!=u&&e.ZERO.subTo(i,i)),r.t=p,r.clamp(),f>0&&r.rShiftTo(f,r),h<0&&e.ZERO.subTo(r,r)}}},e.prototype.invDigit=function(){if(this.t<1)return 0;var t=this.data[0];if(0==(1&t))return 0;var i=3&t;return(i=(i=(i=(i=i*(2-(15&t)*i)&15)*(2-(255&t)*i)&255)*(2-((65535&t)*i&65535))&65535)*(2-t*i%this.DV)%this.DV)>0?this.DV-i:-i},e.prototype.isEven=function(){return 0==(this.t>0?1&this.data[0]:this.s)},e.prototype.exp=function(t,i){if(t>4294967295||t<1)return e.ONE;var r=a(),o=a(),s=i.convert(this),n=m(t)-1;for(s.copyTo(r);--n>=0;)if(i.sqrTo(r,o),(t&1<<n)>0)i.mulTo(o,s,r);else{var h=r;r=o,o=h}return i.revert(r)},e.prototype.toString=function(t){if(this.s<0)return"-"+this.negate().toString(t);var i;if(16==t)i=4;else if(8==t)i=3;else if(2==t)i=1;else if(32==t)i=5;else{if(4!=t)return this.toRadix(t);i=2}var r,o=(1<<i)-1,s=!1,e="",a=this.t,n=this.DB-a*this.DB%i;if(a-- >0)for(n<this.DB&&(r=this.data[a]>>n)>0&&(s=!0,e=p(r));a>=0;)n<i?(r=(this.data[a]&(1<<n)-1)<<i-n,r|=this.data[--a]>>(n+=this.DB-i)):(r=this.data[a]>>(n-=i)&o,n<=0&&(n+=this.DB,--a)),r>0&&(s=!0),s&&(e+=p(r));return s?e:"0"},e.prototype.negate=function(){var t=a();return e.ZERO.subTo(this,t),t},e.prototype.abs=function(){return this.s<0?this.negate():this},e.prototype.compareTo=function(t){var i=this.s-t.s;if(0!=i)return i;var r=this.t;if(0!=(i=r-t.t))return this.s<0?-i:i;for(;--r>=0;)if(0!=(i=this.data[r]-t.data[r]))return i;return 0},e.prototype.bitLength=function(){return this.t<=0?0:this.DB*(this.t-1)+m(this.data[this.t-1]^this.s&this.DM)},e.prototype.mod=function(t){var i=a();return this.abs().divRemTo(t,null,i),this.s<0&&i.compareTo(e.ZERO)>0&&t.subTo(i,i),i},e.prototype.modPowInt=function(t,i){var r;return r=t<256||i.isEven()?new l(i):new v(i),this.exp(t,r)},e.ZERO=c(0),e.ONE=c(1),S.prototype.convert=M,S.prototype.revert=M,S.prototype.mulTo=function(t,i,r){t.multiplyTo(i,r)},S.prototype.sqrTo=function(t,i){t.squareTo(i)},w.prototype.convert=function(t){if(t.s<0||t.t>2*this.m.t)return t.mod(this.m);if(t.compareTo(this.m)<0)return t;var i=a();return t.copyTo(i),this.reduce(i),i},w.prototype.revert=function(t){return t},w.prototype.reduce=function(t){for(t.drShiftTo(this.m.t-1,this.r2),t.t>this.m.t+1&&(t.t=this.m.t+1,t.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);t.compareTo(this.r2)<0;)t.dAddOffset(1,this.m.t+1);for(t.subTo(this.r2,t);t.compareTo(this.m)>=0;)t.subTo(this.m,t)},w.prototype.mulTo=function(t,i,r){t.multiplyTo(i,r),this.reduce(r)},w.prototype.sqrTo=function(t,i){t.squareTo(i),this.reduce(i)};var E=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],O=(1<<26)/E[E.length-1];e.prototype.chunkSize=function(t){return Math.floor(Math.LN2*this.DB/Math.log(t))},e.prototype.toRadix=function(t){if(null==t&&(t=10),0==this.signum()||t<2||t>36)return"0";var i=this.chunkSize(t),r=Math.pow(t,i),o=c(r),s=a(),e=a(),n="";for(this.divRemTo(o,s,e);s.signum()>0;)n=(r+e.intValue()).toString(t).substr(1)+n,s.divRemTo(o,s,e);return e.intValue().toString(t)+n},e.prototype.fromRadix=function(t,i){this.fromInt(0),null==i&&(i=10);for(var r=this.chunkSize(i),o=Math.pow(i,r),s=!1,a=0,n=0,h=0;h<t.length;++h){var u=d(t,h);u<0?"-"==t.charAt(h)&&0==this.signum()&&(s=!0):(n=i*n+u,++a>=r&&(this.dMultiply(o),this.dAddOffset(n,0),a=0,n=0))}a>0&&(this.dMultiply(Math.pow(i,a)),this.dAddOffset(n,0)),s&&e.ZERO.subTo(this,this)},e.prototype.fromNumber=function(t,i,r){if("number"==typeof i)if(t<2)this.fromInt(1);else for(this.fromNumber(t,r),this.testBit(t-1)||this.bitwiseTo(e.ONE.shiftLeft(t-1),y,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(i);)this.dAddOffset(2,0),this.bitLength()>t&&this.subTo(e.ONE.shiftLeft(t-1),this);else{var o=new Array,s=7&t;o.length=1+(t>>3),i.nextBytes(o),s>0?o[0]&=(1<<s)-1:o[0]=0,this.fromString(o,256)}},e.prototype.bitwiseTo=function(t,i,r){var o,s,e=Math.min(t.t,this.t);for(o=0;o<e;++o)r.data[o]=i(this.data[o],t.data[o]);if(t.t<this.t){for(s=t.s&this.DM,o=e;o<this.t;++o)r.data[o]=i(this.data[o],s);r.t=this.t}else{for(s=this.s&this.DM,o=e;o<t.t;++o)r.data[o]=i(s,t.data[o]);r.t=t.t}r.s=i(this.s,t.s),r.clamp()},e.prototype.changeBit=function(t,i){var r=e.ONE.shiftLeft(t);return this.bitwiseTo(r,i,r),r},e.prototype.addTo=function(t,i){for(var r=0,o=0,s=Math.min(t.t,this.t);r<s;)o+=this.data[r]+t.data[r],i.data[r++]=o&this.DM,o>>=this.DB;if(t.t<this.t){for(o+=t.s;r<this.t;)o+=this.data[r],i.data[r++]=o&this.DM,o>>=this.DB;o+=this.s}else{for(o+=this.s;r<t.t;)o+=t.data[r],i.data[r++]=o&this.DM,o>>=this.DB;o+=t.s}i.s=o<0?-1:0,o>0?i.data[r++]=o:o<-1&&(i.data[r++]=this.DV+o),i.t=r,i.clamp()},e.prototype.dMultiply=function(t){this.data[this.t]=this.am(0,t-1,this,0,0,this.t),++this.t,this.clamp()},e.prototype.dAddOffset=function(t,i){if(0!=t){for(;this.t<=i;)this.data[this.t++]=0;for(this.data[i]+=t;this.data[i]>=this.DV;)this.data[i]-=this.DV,++i>=this.t&&(this.data[this.t++]=0),++this.data[i]}},e.prototype.multiplyLowerTo=function(t,i,r){var o,s=Math.min(this.t+t.t,i);for(r.s=0,r.t=s;s>0;)r.data[--s]=0;for(o=r.t-this.t;s<o;++s)r.data[s+this.t]=this.am(0,t.data[s],r,s,0,this.t);for(o=Math.min(t.t,i);s<o;++s)this.am(0,t.data[s],r,s,0,i-s);r.clamp()},e.prototype.multiplyUpperTo=function(t,i,r){--i;var o=r.t=this.t+t.t-i;for(r.s=0;--o>=0;)r.data[o]=0;for(o=Math.max(i-this.t,0);o<t.t;++o)r.data[this.t+o-i]=this.am(i-o,t.data[o],r,0,0,this.t+o-i);r.clamp(),r.drShiftTo(1,r)},e.prototype.modInt=function(t){if(t<=0)return 0;var i=this.DV%t,r=this.s<0?t-1:0;if(this.t>0)if(0==i)r=this.data[0]%t;else for(var o=this.t-1;o>=0;--o)r=(i*r+this.data[o])%t;return r},e.prototype.millerRabin=function(t){var i=this.subtract(e.ONE),r=i.getLowestSetBit();if(r<=0)return!1;for(var o,s=i.shiftRight(r),a={nextBytes:function(t){for(var i=0;i<t.length;++i)t[i]=Math.floor(256*Math.random())}},n=0;n<t;++n){do{o=new e(this.bitLength(),a)}while(o.compareTo(e.ONE)<=0||o.compareTo(i)>=0);var h=o.modPow(s,this);if(0!=h.compareTo(e.ONE)&&0!=h.compareTo(i)){for(var u=1;u++<r&&0!=h.compareTo(i);)if(0==(h=h.modPowInt(2,this)).compareTo(e.ONE))return!1;if(0!=h.compareTo(i))return!1}}return!0},e.prototype.clone=function(){var t=a();return this.copyTo(t),t},e.prototype.intValue=function(){if(this.s<0){if(1==this.t)return this.data[0]-this.DV;if(0==this.t)return-1}else{if(1==this.t)return this.data[0];if(0==this.t)return 0}return(this.data[1]&(1<<32-this.DB)-1)<<this.DB|this.data[0]},e.prototype.byteValue=function(){return 0==this.t?this.s:this.data[0]<<24>>24},e.prototype.shortValue=function(){return 0==this.t?this.s:this.data[0]<<16>>16},e.prototype.signum=function(){return this.s<0?-1:this.t<=0||1==this.t&&this.data[0]<=0?0:1},e.prototype.toByteArray=function(){var t=this.t,i=new Array;i[0]=this.s;var r,o=this.DB-t*this.DB%8,s=0;if(t-- >0)for(o<this.DB&&(r=this.data[t]>>o)!=(this.s&this.DM)>>o&&(i[s++]=r|this.s<<this.DB-o);t>=0;)o<8?(r=(this.data[t]&(1<<o)-1)<<8-o,r|=this.data[--t]>>(o+=this.DB-8)):(r=this.data[t]>>(o-=8)&255,o<=0&&(o+=this.DB,--t)),0!=(128&r)&&(r|=-256),0==s&&(128&this.s)!=(128&r)&&++s,(s>0||r!=this.s)&&(i[s++]=r);return i},e.prototype.equals=function(t){return 0==this.compareTo(t)},e.prototype.min=function(t){return this.compareTo(t)<0?this:t},e.prototype.max=function(t){return this.compareTo(t)>0?this:t},e.prototype.and=function(t){var i=a();return this.bitwiseTo(t,T,i),i},e.prototype.or=function(t){var i=a();return this.bitwiseTo(t,y,i),i},e.prototype.xor=function(t){var i=a();return this.bitwiseTo(t,b,i),i},e.prototype.andNot=function(t){var i=a();return this.bitwiseTo(t,g,i),i},e.prototype.not=function(){for(var t=a(),i=0;i<this.t;++i)t.data[i]=this.DM&~this.data[i];return t.t=this.t,t.s=~this.s,t},e.prototype.shiftLeft=function(t){var i=a();return t<0?this.rShiftTo(-t,i):this.lShiftTo(t,i),i},e.prototype.shiftRight=function(t){var i=a();return t<0?this.lShiftTo(-t,i):this.rShiftTo(t,i),i},e.prototype.getLowestSetBit=function(){for(var t=0;t<this.t;++t)if(0!=this.data[t])return t*this.DB+D(this.data[t]);return this.s<0?this.t*this.DB:-1},e.prototype.bitCount=function(){for(var t=0,i=this.s&this.DM,r=0;r<this.t;++r)t+=B(this.data[r]^i);return t},e.prototype.testBit=function(t){var i=Math.floor(t/this.DB);return i>=this.t?0!=this.s:0!=(this.data[i]&1<<t%this.DB)},e.prototype.setBit=function(t){return this.changeBit(t,y)},e.prototype.clearBit=function(t){return this.changeBit(t,g)},e.prototype.flipBit=function(t){return this.changeBit(t,b)},e.prototype.add=function(t){var i=a();return this.addTo(t,i),i},e.prototype.subtract=function(t){var i=a();return this.subTo(t,i),i},e.prototype.multiply=function(t){var i=a();return this.multiplyTo(t,i),i},e.prototype.divide=function(t){var i=a();return this.divRemTo(t,i,null),i},e.prototype.remainder=function(t){var i=a();return this.divRemTo(t,null,i),i},e.prototype.divideAndRemainder=function(t){var i=a(),r=a();return this.divRemTo(t,i,r),new Array(i,r)},e.prototype.modPow=function(t,i){var r,o,s=t.bitLength(),e=c(1);if(s<=0)return e;r=s<18?1:s<48?3:s<144?4:s<768?5:6,o=s<8?new l(i):i.isEven()?new w(i):new v(i);var n=new Array,h=3,u=r-1,f=(1<<r)-1;if(n[1]=o.convert(this),r>1){var p=a();for(o.sqrTo(n[1],p);h<=f;)n[h]=a(),o.mulTo(p,n[h-2],n[h]),h+=2}var d,T,y=t.t-1,b=!0,g=a();for(s=m(t.data[y])-1;y>=0;){for(s>=u?d=t.data[y]>>s-u&f:(d=(t.data[y]&(1<<s+1)-1)<<u-s,y>0&&(d|=t.data[y-1]>>this.DB+s-u)),h=r;0==(1&d);)d>>=1,--h;if((s-=h)<0&&(s+=this.DB,--y),b)n[d].copyTo(e),b=!1;else{for(;h>1;)o.sqrTo(e,g),o.sqrTo(g,e),h-=2;h>0?o.sqrTo(e,g):(T=e,e=g,g=T),o.mulTo(g,n[d],e)}for(;y>=0&&0==(t.data[y]&1<<s);)o.sqrTo(e,g),T=e,e=g,g=T,--s<0&&(s=this.DB-1,--y)}return o.revert(e)},e.prototype.modInverse=function(t){if(0==this.signum())return e.ZERO;var i=t.isEven();if(this.isEven()&&i||0==t.signum())return e.ZERO;for(var r=t.clone(),o=this.clone(),s=c(1),a=c(0),n=c(0),h=c(1);0!=r.signum();){for(;r.isEven();)r.rShiftTo(1,r),i?(s.isEven()&&a.isEven()||(s.addTo(this,s),a.subTo(t,a)),s.rShiftTo(1,s)):a.isEven()||a.subTo(t,a),a.rShiftTo(1,a);for(;o.isEven();)o.rShiftTo(1,o),i?(n.isEven()&&h.isEven()||(n.addTo(this,n),h.subTo(t,h)),n.rShiftTo(1,n)):h.isEven()||h.subTo(t,h),h.rShiftTo(1,h);r.compareTo(o)>=0?(r.subTo(o,r),i&&s.subTo(n,s),a.subTo(h,a)):(o.subTo(r,o),i&&n.subTo(s,n),h.subTo(a,h))}return 0!=o.compareTo(e.ONE)?e.ZERO:h.compareTo(t)>=0?h.subtract(t):h.signum()<0?(h.addTo(t,h),h.signum()<0?h.add(t):h):h},e.prototype.pow=function(t){return this.exp(t,new S)},e.prototype.gcd=function(t){var i=this.s<0?this.negate():this.clone(),r=t.s<0?t.negate():t.clone();if(i.compareTo(r)<0){var o=i;i=r,r=o}var s=i.getLowestSetBit(),e=r.getLowestSetBit();if(e<0)return i;for(s<e&&(e=s),e>0&&(i.rShiftTo(e,i),r.rShiftTo(e,r));i.signum()>0;)(s=i.getLowestSetBit())>0&&i.rShiftTo(s,i),(s=r.getLowestSetBit())>0&&r.rShiftTo(s,r),i.compareTo(r)>=0?(i.subTo(r,i),i.rShiftTo(1,i)):(r.subTo(i,r),r.rShiftTo(1,r));return e>0&&r.lShiftTo(e,r),r},e.prototype.isProbablePrime=function(t){var i,r=this.abs();if(1==r.t&&r.data[0]<=E[E.length-1]){for(i=0;i<E.length;++i)if(r.data[0]==E[i])return!0;return!1}if(r.isEven())return!1;for(i=1;i<E.length;){for(var o=E[i],s=i+1;s<E.length&&o<O;)o*=E[s++];for(o=r.modInt(o);i<s;)if(o%E[i++]==0)return!1}return r.millerRabin(t)},e.prototype.square=function(){var t=a();return this.squareTo(t),t}}]);
+//# sourceMappingURL=prime.worker.min.js.map
Index: frontend/node_modules/node-forge/dist/prime.worker.min.js.map
===================================================================
--- frontend/node_modules/node-forge/dist/prime.worker.min.js.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/dist/prime.worker.min.js.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"prime.worker.min.js","sources":["webpack:///prime.worker.min.js"],"mappings":"AAAA","sourceRoot":""}
Index: frontend/node_modules/node-forge/lib/aes.js
===================================================================
--- frontend/node_modules/node-forge/lib/aes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/aes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1091 @@
+/**
+ * Advanced Encryption Standard (AES) implementation.
+ *
+ * This implementation is based on the public domain library 'jscrypto' which
+ * was written by:
+ *
+ * Emily Stark (estark@stanford.edu)
+ * Mike Hamburg (mhamburg@stanford.edu)
+ * Dan Boneh (dabo@cs.stanford.edu)
+ *
+ * Parts of this code are based on the OpenSSL implementation of AES:
+ * http://www.openssl.org
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./cipher');
+require('./cipherModes');
+require('./util');
+
+/* AES API */
+module.exports = forge.aes = forge.aes || {};
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * var cipher = forge.cipher.createCipher('AES-<mode>', key);
+ * cipher.start({iv: iv});
+ *
+ * Creates an AES cipher object to encrypt data using the given symmetric key.
+ * The output will be stored in the 'output' member of the returned cipher.
+ *
+ * The key and iv may be given as a string of bytes, an array of bytes,
+ * a byte buffer, or an array of 32-bit words.
+ *
+ * @param key the symmetric key to use.
+ * @param iv the initialization vector to use.
+ * @param output the buffer to write to, null to create one.
+ * @param mode the cipher mode to use (default: 'CBC').
+ *
+ * @return the cipher.
+ */
+forge.aes.startEncrypting = function(key, iv, output, mode) {
+  var cipher = _createCipher({
+    key: key,
+    output: output,
+    decrypt: false,
+    mode: mode
+  });
+  cipher.start(iv);
+  return cipher;
+};
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * var cipher = forge.cipher.createCipher('AES-<mode>', key);
+ *
+ * Creates an AES cipher object to encrypt data using the given symmetric key.
+ *
+ * The key may be given as a string of bytes, an array of bytes, a
+ * byte buffer, or an array of 32-bit words.
+ *
+ * @param key the symmetric key to use.
+ * @param mode the cipher mode to use (default: 'CBC').
+ *
+ * @return the cipher.
+ */
+forge.aes.createEncryptionCipher = function(key, mode) {
+  return _createCipher({
+    key: key,
+    output: null,
+    decrypt: false,
+    mode: mode
+  });
+};
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * var decipher = forge.cipher.createDecipher('AES-<mode>', key);
+ * decipher.start({iv: iv});
+ *
+ * Creates an AES cipher object to decrypt data using the given symmetric key.
+ * The output will be stored in the 'output' member of the returned cipher.
+ *
+ * The key and iv may be given as a string of bytes, an array of bytes,
+ * a byte buffer, or an array of 32-bit words.
+ *
+ * @param key the symmetric key to use.
+ * @param iv the initialization vector to use.
+ * @param output the buffer to write to, null to create one.
+ * @param mode the cipher mode to use (default: 'CBC').
+ *
+ * @return the cipher.
+ */
+forge.aes.startDecrypting = function(key, iv, output, mode) {
+  var cipher = _createCipher({
+    key: key,
+    output: output,
+    decrypt: true,
+    mode: mode
+  });
+  cipher.start(iv);
+  return cipher;
+};
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * var decipher = forge.cipher.createDecipher('AES-<mode>', key);
+ *
+ * Creates an AES cipher object to decrypt data using the given symmetric key.
+ *
+ * The key may be given as a string of bytes, an array of bytes, a
+ * byte buffer, or an array of 32-bit words.
+ *
+ * @param key the symmetric key to use.
+ * @param mode the cipher mode to use (default: 'CBC').
+ *
+ * @return the cipher.
+ */
+forge.aes.createDecryptionCipher = function(key, mode) {
+  return _createCipher({
+    key: key,
+    output: null,
+    decrypt: true,
+    mode: mode
+  });
+};
+
+/**
+ * Creates a new AES cipher algorithm object.
+ *
+ * @param name the name of the algorithm.
+ * @param mode the mode factory function.
+ *
+ * @return the AES algorithm object.
+ */
+forge.aes.Algorithm = function(name, mode) {
+  if(!init) {
+    initialize();
+  }
+  var self = this;
+  self.name = name;
+  self.mode = new mode({
+    blockSize: 16,
+    cipher: {
+      encrypt: function(inBlock, outBlock) {
+        return _updateBlock(self._w, inBlock, outBlock, false);
+      },
+      decrypt: function(inBlock, outBlock) {
+        return _updateBlock(self._w, inBlock, outBlock, true);
+      }
+    }
+  });
+  self._init = false;
+};
+
+/**
+ * Initializes this AES algorithm by expanding its key.
+ *
+ * @param options the options to use.
+ *          key the key to use with this algorithm.
+ *          decrypt true if the algorithm should be initialized for decryption,
+ *            false for encryption.
+ */
+forge.aes.Algorithm.prototype.initialize = function(options) {
+  if(this._init) {
+    return;
+  }
+
+  var key = options.key;
+  var tmp;
+
+  /* Note: The key may be a string of bytes, an array of bytes, a byte
+    buffer, or an array of 32-bit integers. If the key is in bytes, then
+    it must be 16, 24, or 32 bytes in length. If it is in 32-bit
+    integers, it must be 4, 6, or 8 integers long. */
+
+  if(typeof key === 'string' &&
+    (key.length === 16 || key.length === 24 || key.length === 32)) {
+    // convert key string into byte buffer
+    key = forge.util.createBuffer(key);
+  } else if(forge.util.isArray(key) &&
+    (key.length === 16 || key.length === 24 || key.length === 32)) {
+    // convert key integer array into byte buffer
+    tmp = key;
+    key = forge.util.createBuffer();
+    for(var i = 0; i < tmp.length; ++i) {
+      key.putByte(tmp[i]);
+    }
+  }
+
+  // convert key byte buffer into 32-bit integer array
+  if(!forge.util.isArray(key)) {
+    tmp = key;
+    key = [];
+
+    // key lengths of 16, 24, 32 bytes allowed
+    var len = tmp.length();
+    if(len === 16 || len === 24 || len === 32) {
+      len = len >>> 2;
+      for(var i = 0; i < len; ++i) {
+        key.push(tmp.getInt32());
+      }
+    }
+  }
+
+  // key must be an array of 32-bit integers by now
+  if(!forge.util.isArray(key) ||
+    !(key.length === 4 || key.length === 6 || key.length === 8)) {
+    throw new Error('Invalid key parameter.');
+  }
+
+  // encryption operation is always used for these modes
+  var mode = this.mode.name;
+  var encryptOp = (['CFB', 'OFB', 'CTR', 'GCM'].indexOf(mode) !== -1);
+
+  // do key expansion
+  this._w = _expandKey(key, options.decrypt && !encryptOp);
+  this._init = true;
+};
+
+/**
+ * Expands a key. Typically only used for testing.
+ *
+ * @param key the symmetric key to expand, as an array of 32-bit words.
+ * @param decrypt true to expand for decryption, false for encryption.
+ *
+ * @return the expanded key.
+ */
+forge.aes._expandKey = function(key, decrypt) {
+  if(!init) {
+    initialize();
+  }
+  return _expandKey(key, decrypt);
+};
+
+/**
+ * Updates a single block. Typically only used for testing.
+ *
+ * @param w the expanded key to use.
+ * @param input an array of block-size 32-bit words.
+ * @param output an array of block-size 32-bit words.
+ * @param decrypt true to decrypt, false to encrypt.
+ */
+forge.aes._updateBlock = _updateBlock;
+
+/** Register AES algorithms **/
+
+registerAlgorithm('AES-ECB', forge.cipher.modes.ecb);
+registerAlgorithm('AES-CBC', forge.cipher.modes.cbc);
+registerAlgorithm('AES-CFB', forge.cipher.modes.cfb);
+registerAlgorithm('AES-OFB', forge.cipher.modes.ofb);
+registerAlgorithm('AES-CTR', forge.cipher.modes.ctr);
+registerAlgorithm('AES-GCM', forge.cipher.modes.gcm);
+
+function registerAlgorithm(name, mode) {
+  var factory = function() {
+    return new forge.aes.Algorithm(name, mode);
+  };
+  forge.cipher.registerAlgorithm(name, factory);
+}
+
+/** AES implementation **/
+
+var init = false; // not yet initialized
+var Nb = 4;       // number of words comprising the state (AES = 4)
+var sbox;         // non-linear substitution table used in key expansion
+var isbox;        // inversion of sbox
+var rcon;         // round constant word array
+var mix;          // mix-columns table
+var imix;         // inverse mix-columns table
+
+/**
+ * Performs initialization, ie: precomputes tables to optimize for speed.
+ *
+ * One way to understand how AES works is to imagine that 'addition' and
+ * 'multiplication' are interfaces that require certain mathematical
+ * properties to hold true (ie: they are associative) but they might have
+ * different implementations and produce different kinds of results ...
+ * provided that their mathematical properties remain true. AES defines
+ * its own methods of addition and multiplication but keeps some important
+ * properties the same, ie: associativity and distributivity. The
+ * explanation below tries to shed some light on how AES defines addition
+ * and multiplication of bytes and 32-bit words in order to perform its
+ * encryption and decryption algorithms.
+ *
+ * The basics:
+ *
+ * The AES algorithm views bytes as binary representations of polynomials
+ * that have either 1 or 0 as the coefficients. It defines the addition
+ * or subtraction of two bytes as the XOR operation. It also defines the
+ * multiplication of two bytes as a finite field referred to as GF(2^8)
+ * (Note: 'GF' means "Galois Field" which is a field that contains a finite
+ * number of elements so GF(2^8) has 256 elements).
+ *
+ * This means that any two bytes can be represented as binary polynomials;
+ * when they multiplied together and modularly reduced by an irreducible
+ * polynomial of the 8th degree, the results are the field GF(2^8). The
+ * specific irreducible polynomial that AES uses in hexadecimal is 0x11b.
+ * This multiplication is associative with 0x01 as the identity:
+ *
+ * (b * 0x01 = GF(b, 0x01) = b).
+ *
+ * The operation GF(b, 0x02) can be performed at the byte level by left
+ * shifting b once and then XOR'ing it (to perform the modular reduction)
+ * with 0x11b if b is >= 128. Repeated application of the multiplication
+ * of 0x02 can be used to implement the multiplication of any two bytes.
+ *
+ * For instance, multiplying 0x57 and 0x13, denoted as GF(0x57, 0x13), can
+ * be performed by factoring 0x13 into 0x01, 0x02, and 0x10. Then these
+ * factors can each be multiplied by 0x57 and then added together. To do
+ * the multiplication, values for 0x57 multiplied by each of these 3 factors
+ * can be precomputed and stored in a table. To add them, the values from
+ * the table are XOR'd together.
+ *
+ * AES also defines addition and multiplication of words, that is 4-byte
+ * numbers represented as polynomials of 3 degrees where the coefficients
+ * are the values of the bytes.
+ *
+ * The word [a0, a1, a2, a3] is a polynomial a3x^3 + a2x^2 + a1x + a0.
+ *
+ * Addition is performed by XOR'ing like powers of x. Multiplication
+ * is performed in two steps, the first is an algebraic expansion as
+ * you would do normally (where addition is XOR). But the result is
+ * a polynomial larger than 3 degrees and thus it cannot fit in a word. So
+ * next the result is modularly reduced by an AES-specific polynomial of
+ * degree 4 which will always produce a polynomial of less than 4 degrees
+ * such that it will fit in a word. In AES, this polynomial is x^4 + 1.
+ *
+ * The modular product of two polynomials 'a' and 'b' is thus:
+ *
+ * d(x) = d3x^3 + d2x^2 + d1x + d0
+ * with
+ * d0 = GF(a0, b0) ^ GF(a3, b1) ^ GF(a2, b2) ^ GF(a1, b3)
+ * d1 = GF(a1, b0) ^ GF(a0, b1) ^ GF(a3, b2) ^ GF(a2, b3)
+ * d2 = GF(a2, b0) ^ GF(a1, b1) ^ GF(a0, b2) ^ GF(a3, b3)
+ * d3 = GF(a3, b0) ^ GF(a2, b1) ^ GF(a1, b2) ^ GF(a0, b3)
+ *
+ * As a matrix:
+ *
+ * [d0] = [a0 a3 a2 a1][b0]
+ * [d1]   [a1 a0 a3 a2][b1]
+ * [d2]   [a2 a1 a0 a3][b2]
+ * [d3]   [a3 a2 a1 a0][b3]
+ *
+ * Special polynomials defined by AES (0x02 == {02}):
+ * a(x)    = {03}x^3 + {01}x^2 + {01}x + {02}
+ * a^-1(x) = {0b}x^3 + {0d}x^2 + {09}x + {0e}.
+ *
+ * These polynomials are used in the MixColumns() and InverseMixColumns()
+ * operations, respectively, to cause each element in the state to affect
+ * the output (referred to as diffusing).
+ *
+ * RotWord() uses: a0 = a1 = a2 = {00} and a3 = {01}, which is the
+ * polynomial x3.
+ *
+ * The ShiftRows() method modifies the last 3 rows in the state (where
+ * the state is 4 words with 4 bytes per word) by shifting bytes cyclically.
+ * The 1st byte in the second row is moved to the end of the row. The 1st
+ * and 2nd bytes in the third row are moved to the end of the row. The 1st,
+ * 2nd, and 3rd bytes are moved in the fourth row.
+ *
+ * More details on how AES arithmetic works:
+ *
+ * In the polynomial representation of binary numbers, XOR performs addition
+ * and subtraction and multiplication in GF(2^8) denoted as GF(a, b)
+ * corresponds with the multiplication of polynomials modulo an irreducible
+ * polynomial of degree 8. In other words, for AES, GF(a, b) will multiply
+ * polynomial 'a' with polynomial 'b' and then do a modular reduction by
+ * an AES-specific irreducible polynomial of degree 8.
+ *
+ * A polynomial is irreducible if its only divisors are one and itself. For
+ * the AES algorithm, this irreducible polynomial is:
+ *
+ * m(x) = x^8 + x^4 + x^3 + x + 1,
+ *
+ * or {01}{1b} in hexadecimal notation, where each coefficient is a bit:
+ * 100011011 = 283 = 0x11b.
+ *
+ * For example, GF(0x57, 0x83) = 0xc1 because
+ *
+ * 0x57 = 87  = 01010111 = x^6 + x^4 + x^2 + x + 1
+ * 0x85 = 131 = 10000101 = x^7 + x + 1
+ *
+ * (x^6 + x^4 + x^2 + x + 1) * (x^7 + x + 1)
+ * =  x^13 + x^11 + x^9 + x^8 + x^7 +
+ *    x^7 + x^5 + x^3 + x^2 + x +
+ *    x^6 + x^4 + x^2 + x + 1
+ * =  x^13 + x^11 + x^9 + x^8 + x^6 + x^5 + x^4 + x^3 + 1 = y
+ *    y modulo (x^8 + x^4 + x^3 + x + 1)
+ * =  x^7 + x^6 + 1.
+ *
+ * The modular reduction by m(x) guarantees the result will be a binary
+ * polynomial of less than degree 8, so that it can fit in a byte.
+ *
+ * The operation to multiply a binary polynomial b with x (the polynomial
+ * x in binary representation is 00000010) is:
+ *
+ * b_7x^8 + b_6x^7 + b_5x^6 + b_4x^5 + b_3x^4 + b_2x^3 + b_1x^2 + b_0x^1
+ *
+ * To get GF(b, x) we must reduce that by m(x). If b_7 is 0 (that is the
+ * most significant bit is 0 in b) then the result is already reduced. If
+ * it is 1, then we can reduce it by subtracting m(x) via an XOR.
+ *
+ * It follows that multiplication by x (00000010 or 0x02) can be implemented
+ * by performing a left shift followed by a conditional bitwise XOR with
+ * 0x1b. This operation on bytes is denoted by xtime(). Multiplication by
+ * higher powers of x can be implemented by repeated application of xtime().
+ *
+ * By adding intermediate results, multiplication by any constant can be
+ * implemented. For instance:
+ *
+ * GF(0x57, 0x13) = 0xfe because:
+ *
+ * xtime(b) = (b & 128) ? (b << 1 ^ 0x11b) : (b << 1)
+ *
+ * Note: We XOR with 0x11b instead of 0x1b because in javascript our
+ * datatype for b can be larger than 1 byte, so a left shift will not
+ * automatically eliminate bits that overflow a byte ... by XOR'ing the
+ * overflow bit with 1 (the extra one from 0x11b) we zero it out.
+ *
+ * GF(0x57, 0x02) = xtime(0x57) = 0xae
+ * GF(0x57, 0x04) = xtime(0xae) = 0x47
+ * GF(0x57, 0x08) = xtime(0x47) = 0x8e
+ * GF(0x57, 0x10) = xtime(0x8e) = 0x07
+ *
+ * GF(0x57, 0x13) = GF(0x57, (0x01 ^ 0x02 ^ 0x10))
+ *
+ * And by the distributive property (since XOR is addition and GF() is
+ * multiplication):
+ *
+ * = GF(0x57, 0x01) ^ GF(0x57, 0x02) ^ GF(0x57, 0x10)
+ * = 0x57 ^ 0xae ^ 0x07
+ * = 0xfe.
+ */
+function initialize() {
+  init = true;
+
+  /* Populate the Rcon table. These are the values given by
+    [x^(i-1),{00},{00},{00}] where x^(i-1) are powers of x (and x = 0x02)
+    in the field of GF(2^8), where i starts at 1.
+
+    rcon[0] = [0x00, 0x00, 0x00, 0x00]
+    rcon[1] = [0x01, 0x00, 0x00, 0x00] 2^(1-1) = 2^0 = 1
+    rcon[2] = [0x02, 0x00, 0x00, 0x00] 2^(2-1) = 2^1 = 2
+    ...
+    rcon[9]  = [0x1B, 0x00, 0x00, 0x00] 2^(9-1)  = 2^8 = 0x1B
+    rcon[10] = [0x36, 0x00, 0x00, 0x00] 2^(10-1) = 2^9 = 0x36
+
+    We only store the first byte because it is the only one used.
+  */
+  rcon = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36];
+
+  // compute xtime table which maps i onto GF(i, 0x02)
+  var xtime = new Array(256);
+  for(var i = 0; i < 128; ++i) {
+    xtime[i] = i << 1;
+    xtime[i + 128] = (i + 128) << 1 ^ 0x11B;
+  }
+
+  // compute all other tables
+  sbox = new Array(256);
+  isbox = new Array(256);
+  mix = new Array(4);
+  imix = new Array(4);
+  for(var i = 0; i < 4; ++i) {
+    mix[i] = new Array(256);
+    imix[i] = new Array(256);
+  }
+  var e = 0, ei = 0, e2, e4, e8, sx, sx2, me, ime;
+  for(var i = 0; i < 256; ++i) {
+    /* We need to generate the SubBytes() sbox and isbox tables so that
+      we can perform byte substitutions. This requires us to traverse
+      all of the elements in GF, find their multiplicative inverses,
+      and apply to each the following affine transformation:
+
+      bi' = bi ^ b(i + 4) mod 8 ^ b(i + 5) mod 8 ^ b(i + 6) mod 8 ^
+            b(i + 7) mod 8 ^ ci
+      for 0 <= i < 8, where bi is the ith bit of the byte, and ci is the
+      ith bit of a byte c with the value {63} or {01100011}.
+
+      It is possible to traverse every possible value in a Galois field
+      using what is referred to as a 'generator'. There are many
+      generators (128 out of 256): 3,5,6,9,11,82 to name a few. To fully
+      traverse GF we iterate 255 times, multiplying by our generator
+      each time.
+
+      On each iteration we can determine the multiplicative inverse for
+      the current element.
+
+      Suppose there is an element in GF 'e'. For a given generator 'g',
+      e = g^x. The multiplicative inverse of e is g^(255 - x). It turns
+      out that if use the inverse of a generator as another generator
+      it will produce all of the corresponding multiplicative inverses
+      at the same time. For this reason, we choose 5 as our inverse
+      generator because it only requires 2 multiplies and 1 add and its
+      inverse, 82, requires relatively few operations as well.
+
+      In order to apply the affine transformation, the multiplicative
+      inverse 'ei' of 'e' can be repeatedly XOR'd (4 times) with a
+      bit-cycling of 'ei'. To do this 'ei' is first stored in 's' and
+      'x'. Then 's' is left shifted and the high bit of 's' is made the
+      low bit. The resulting value is stored in 's'. Then 'x' is XOR'd
+      with 's' and stored in 'x'. On each subsequent iteration the same
+      operation is performed. When 4 iterations are complete, 'x' is
+      XOR'd with 'c' (0x63) and the transformed value is stored in 'x'.
+      For example:
+
+      s = 01000001
+      x = 01000001
+
+      iteration 1: s = 10000010, x ^= s
+      iteration 2: s = 00000101, x ^= s
+      iteration 3: s = 00001010, x ^= s
+      iteration 4: s = 00010100, x ^= s
+      x ^= 0x63
+
+      This can be done with a loop where s = (s << 1) | (s >> 7). However,
+      it can also be done by using a single 16-bit (in this case 32-bit)
+      number 'sx'. Since XOR is an associative operation, we can set 'sx'
+      to 'ei' and then XOR it with 'sx' left-shifted 1,2,3, and 4 times.
+      The most significant bits will flow into the high 8 bit positions
+      and be correctly XOR'd with one another. All that remains will be
+      to cycle the high 8 bits by XOR'ing them all with the lower 8 bits
+      afterwards.
+
+      At the same time we're populating sbox and isbox we can precompute
+      the multiplication we'll need to do to do MixColumns() later.
+    */
+
+    // apply affine transformation
+    sx = ei ^ (ei << 1) ^ (ei << 2) ^ (ei << 3) ^ (ei << 4);
+    sx = (sx >> 8) ^ (sx & 255) ^ 0x63;
+
+    // update tables
+    sbox[e] = sx;
+    isbox[sx] = e;
+
+    /* Mixing columns is done using matrix multiplication. The columns
+      that are to be mixed are each a single word in the current state.
+      The state has Nb columns (4 columns). Therefore each column is a
+      4 byte word. So to mix the columns in a single column 'c' where
+      its rows are r0, r1, r2, and r3, we use the following matrix
+      multiplication:
+
+      [2 3 1 1]*[r0,c]=[r'0,c]
+      [1 2 3 1] [r1,c] [r'1,c]
+      [1 1 2 3] [r2,c] [r'2,c]
+      [3 1 1 2] [r3,c] [r'3,c]
+
+      r0, r1, r2, and r3 are each 1 byte of one of the words in the
+      state (a column). To do matrix multiplication for each mixed
+      column c' we multiply the corresponding row from the left matrix
+      with the corresponding column from the right matrix. In total, we
+      get 4 equations:
+
+      r0,c' = 2*r0,c + 3*r1,c + 1*r2,c + 1*r3,c
+      r1,c' = 1*r0,c + 2*r1,c + 3*r2,c + 1*r3,c
+      r2,c' = 1*r0,c + 1*r1,c + 2*r2,c + 3*r3,c
+      r3,c' = 3*r0,c + 1*r1,c + 1*r2,c + 2*r3,c
+
+      As usual, the multiplication is as previously defined and the
+      addition is XOR. In order to optimize mixing columns we can store
+      the multiplication results in tables. If you think of the whole
+      column as a word (it might help to visualize by mentally rotating
+      the equations above by counterclockwise 90 degrees) then you can
+      see that it would be useful to map the multiplications performed on
+      each byte (r0, r1, r2, r3) onto a word as well. For instance, we
+      could map 2*r0,1*r0,1*r0,3*r0 onto a word by storing 2*r0 in the
+      highest 8 bits and 3*r0 in the lowest 8 bits (with the other two
+      respectively in the middle). This means that a table can be
+      constructed that uses r0 as an index to the word. We can do the
+      same with r1, r2, and r3, creating a total of 4 tables.
+
+      To construct a full c', we can just look up each byte of c in
+      their respective tables and XOR the results together.
+
+      Also, to build each table we only have to calculate the word
+      for 2,1,1,3 for every byte ... which we can do on each iteration
+      of this loop since we will iterate over every byte. After we have
+      calculated 2,1,1,3 we can get the results for the other tables
+      by cycling the byte at the end to the beginning. For instance
+      we can take the result of table 2,1,1,3 and produce table 3,2,1,1
+      by moving the right most byte to the left most position just like
+      how you can imagine the 3 moved out of 2,1,1,3 and to the front
+      to produce 3,2,1,1.
+
+      There is another optimization in that the same multiples of
+      the current element we need in order to advance our generator
+      to the next iteration can be reused in performing the 2,1,1,3
+      calculation. We also calculate the inverse mix column tables,
+      with e,9,d,b being the inverse of 2,1,1,3.
+
+      When we're done, and we need to actually mix columns, the first
+      byte of each state word should be put through mix[0] (2,1,1,3),
+      the second through mix[1] (3,2,1,1) and so forth. Then they should
+      be XOR'd together to produce the fully mixed column.
+    */
+
+    // calculate mix and imix table values
+    sx2 = xtime[sx];
+    e2 = xtime[e];
+    e4 = xtime[e2];
+    e8 = xtime[e4];
+    me =
+      (sx2 << 24) ^  // 2
+      (sx << 16) ^   // 1
+      (sx << 8) ^    // 1
+      (sx ^ sx2);    // 3
+    ime =
+      (e2 ^ e4 ^ e8) << 24 ^  // E (14)
+      (e ^ e8) << 16 ^        // 9
+      (e ^ e4 ^ e8) << 8 ^    // D (13)
+      (e ^ e2 ^ e8);          // B (11)
+    // produce each of the mix tables by rotating the 2,1,1,3 value
+    for(var n = 0; n < 4; ++n) {
+      mix[n][e] = me;
+      imix[n][sx] = ime;
+      // cycle the right most byte to the left most position
+      // ie: 2,1,1,3 becomes 3,2,1,1
+      me = me << 24 | me >>> 8;
+      ime = ime << 24 | ime >>> 8;
+    }
+
+    // get next element and inverse
+    if(e === 0) {
+      // 1 is the inverse of 1
+      e = ei = 1;
+    } else {
+      // e = 2e + 2*2*2*(10e)) = multiply e by 82 (chosen generator)
+      // ei = ei + 2*2*ei = multiply ei by 5 (inverse generator)
+      e = e2 ^ xtime[xtime[xtime[e2 ^ e8]]];
+      ei ^= xtime[xtime[ei]];
+    }
+  }
+}
+
+/**
+ * Generates a key schedule using the AES key expansion algorithm.
+ *
+ * The AES algorithm takes the Cipher Key, K, and performs a Key Expansion
+ * routine to generate a key schedule. The Key Expansion generates a total
+ * of Nb*(Nr + 1) words: the algorithm requires an initial set of Nb words,
+ * and each of the Nr rounds requires Nb words of key data. The resulting
+ * key schedule consists of a linear array of 4-byte words, denoted [wi ],
+ * with i in the range 0 <= i < Nb(Nr + 1).
+ *
+ * KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk)
+ * AES-128 (Nb=4, Nk=4, Nr=10)
+ * AES-192 (Nb=4, Nk=6, Nr=12)
+ * AES-256 (Nb=4, Nk=8, Nr=14)
+ * Note: Nr=Nk+6.
+ *
+ * Nb is the number of columns (32-bit words) comprising the State (or
+ * number of bytes in a block). For AES, Nb=4.
+ *
+ * @param key the key to schedule (as an array of 32-bit words).
+ * @param decrypt true to modify the key schedule to decrypt, false not to.
+ *
+ * @return the generated key schedule.
+ */
+function _expandKey(key, decrypt) {
+  // copy the key's words to initialize the key schedule
+  var w = key.slice(0);
+
+  /* RotWord() will rotate a word, moving the first byte to the last
+    byte's position (shifting the other bytes left).
+
+    We will be getting the value of Rcon at i / Nk. 'i' will iterate
+    from Nk to (Nb * Nr+1). Nk = 4 (4 byte key), Nb = 4 (4 words in
+    a block), Nr = Nk + 6 (10). Therefore 'i' will iterate from
+    4 to 44 (exclusive). Each time we iterate 4 times, i / Nk will
+    increase by 1. We use a counter iNk to keep track of this.
+   */
+
+  // go through the rounds expanding the key
+  var temp, iNk = 1;
+  var Nk = w.length;
+  var Nr1 = Nk + 6 + 1;
+  var end = Nb * Nr1;
+  for(var i = Nk; i < end; ++i) {
+    temp = w[i - 1];
+    if(i % Nk === 0) {
+      // temp = SubWord(RotWord(temp)) ^ Rcon[i / Nk]
+      temp =
+        sbox[temp >>> 16 & 255] << 24 ^
+        sbox[temp >>> 8 & 255] << 16 ^
+        sbox[temp & 255] << 8 ^
+        sbox[temp >>> 24] ^ (rcon[iNk] << 24);
+      iNk++;
+    } else if(Nk > 6 && (i % Nk === 4)) {
+      // temp = SubWord(temp)
+      temp =
+        sbox[temp >>> 24] << 24 ^
+        sbox[temp >>> 16 & 255] << 16 ^
+        sbox[temp >>> 8 & 255] << 8 ^
+        sbox[temp & 255];
+    }
+    w[i] = w[i - Nk] ^ temp;
+  }
+
+  /* When we are updating a cipher block we always use the code path for
+     encryption whether we are decrypting or not (to shorten code and
+     simplify the generation of look up tables). However, because there
+     are differences in the decryption algorithm, other than just swapping
+     in different look up tables, we must transform our key schedule to
+     account for these changes:
+
+     1. The decryption algorithm gets its key rounds in reverse order.
+     2. The decryption algorithm adds the round key before mixing columns
+       instead of afterwards.
+
+     We don't need to modify our key schedule to handle the first case,
+     we can just traverse the key schedule in reverse order when decrypting.
+
+     The second case requires a little work.
+
+     The tables we built for performing rounds will take an input and then
+     perform SubBytes() and MixColumns() or, for the decrypt version,
+     InvSubBytes() and InvMixColumns(). But the decrypt algorithm requires
+     us to AddRoundKey() before InvMixColumns(). This means we'll need to
+     apply some transformations to the round key to inverse-mix its columns
+     so they'll be correct for moving AddRoundKey() to after the state has
+     had its columns inverse-mixed.
+
+     To inverse-mix the columns of the state when we're decrypting we use a
+     lookup table that will apply InvSubBytes() and InvMixColumns() at the
+     same time. However, the round key's bytes are not inverse-substituted
+     in the decryption algorithm. To get around this problem, we can first
+     substitute the bytes in the round key so that when we apply the
+     transformation via the InvSubBytes()+InvMixColumns() table, it will
+     undo our substitution leaving us with the original value that we
+     want -- and then inverse-mix that value.
+
+     This change will correctly alter our key schedule so that we can XOR
+     each round key with our already transformed decryption state. This
+     allows us to use the same code path as the encryption algorithm.
+
+     We make one more change to the decryption key. Since the decryption
+     algorithm runs in reverse from the encryption algorithm, we reverse
+     the order of the round keys to avoid having to iterate over the key
+     schedule backwards when running the encryption algorithm later in
+     decryption mode. In addition to reversing the order of the round keys,
+     we also swap each round key's 2nd and 4th rows. See the comments
+     section where rounds are performed for more details about why this is
+     done. These changes are done inline with the other substitution
+     described above.
+  */
+  if(decrypt) {
+    var tmp;
+    var m0 = imix[0];
+    var m1 = imix[1];
+    var m2 = imix[2];
+    var m3 = imix[3];
+    var wnew = w.slice(0);
+    end = w.length;
+    for(var i = 0, wi = end - Nb; i < end; i += Nb, wi -= Nb) {
+      // do not sub the first or last round key (round keys are Nb
+      // words) as no column mixing is performed before they are added,
+      // but do change the key order
+      if(i === 0 || i === (end - Nb)) {
+        wnew[i] = w[wi];
+        wnew[i + 1] = w[wi + 3];
+        wnew[i + 2] = w[wi + 2];
+        wnew[i + 3] = w[wi + 1];
+      } else {
+        // substitute each round key byte because the inverse-mix
+        // table will inverse-substitute it (effectively cancel the
+        // substitution because round key bytes aren't sub'd in
+        // decryption mode) and swap indexes 3 and 1
+        for(var n = 0; n < Nb; ++n) {
+          tmp = w[wi + n];
+          wnew[i + (3&-n)] =
+            m0[sbox[tmp >>> 24]] ^
+            m1[sbox[tmp >>> 16 & 255]] ^
+            m2[sbox[tmp >>> 8 & 255]] ^
+            m3[sbox[tmp & 255]];
+        }
+      }
+    }
+    w = wnew;
+  }
+
+  return w;
+}
+
+/**
+ * Updates a single block (16 bytes) using AES. The update will either
+ * encrypt or decrypt the block.
+ *
+ * @param w the key schedule.
+ * @param input the input block (an array of 32-bit words).
+ * @param output the updated output block.
+ * @param decrypt true to decrypt the block, false to encrypt it.
+ */
+function _updateBlock(w, input, output, decrypt) {
+  /*
+  Cipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
+  begin
+    byte state[4,Nb]
+    state = in
+    AddRoundKey(state, w[0, Nb-1])
+    for round = 1 step 1 to Nr-1
+      SubBytes(state)
+      ShiftRows(state)
+      MixColumns(state)
+      AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
+    end for
+    SubBytes(state)
+    ShiftRows(state)
+    AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
+    out = state
+  end
+
+  InvCipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
+  begin
+    byte state[4,Nb]
+    state = in
+    AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
+    for round = Nr-1 step -1 downto 1
+      InvShiftRows(state)
+      InvSubBytes(state)
+      AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
+      InvMixColumns(state)
+    end for
+    InvShiftRows(state)
+    InvSubBytes(state)
+    AddRoundKey(state, w[0, Nb-1])
+    out = state
+  end
+  */
+
+  // Encrypt: AddRoundKey(state, w[0, Nb-1])
+  // Decrypt: AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
+  var Nr = w.length / 4 - 1;
+  var m0, m1, m2, m3, sub;
+  if(decrypt) {
+    m0 = imix[0];
+    m1 = imix[1];
+    m2 = imix[2];
+    m3 = imix[3];
+    sub = isbox;
+  } else {
+    m0 = mix[0];
+    m1 = mix[1];
+    m2 = mix[2];
+    m3 = mix[3];
+    sub = sbox;
+  }
+  var a, b, c, d, a2, b2, c2;
+  a = input[0] ^ w[0];
+  b = input[decrypt ? 3 : 1] ^ w[1];
+  c = input[2] ^ w[2];
+  d = input[decrypt ? 1 : 3] ^ w[3];
+  var i = 3;
+
+  /* In order to share code we follow the encryption algorithm when both
+    encrypting and decrypting. To account for the changes required in the
+    decryption algorithm, we use different lookup tables when decrypting
+    and use a modified key schedule to account for the difference in the
+    order of transformations applied when performing rounds. We also get
+    key rounds in reverse order (relative to encryption). */
+  for(var round = 1; round < Nr; ++round) {
+    /* As described above, we'll be using table lookups to perform the
+      column mixing. Each column is stored as a word in the state (the
+      array 'input' has one column as a word at each index). In order to
+      mix a column, we perform these transformations on each row in c,
+      which is 1 byte in each word. The new column for c0 is c'0:
+
+               m0      m1      m2      m3
+      r0,c'0 = 2*r0,c0 + 3*r1,c0 + 1*r2,c0 + 1*r3,c0
+      r1,c'0 = 1*r0,c0 + 2*r1,c0 + 3*r2,c0 + 1*r3,c0
+      r2,c'0 = 1*r0,c0 + 1*r1,c0 + 2*r2,c0 + 3*r3,c0
+      r3,c'0 = 3*r0,c0 + 1*r1,c0 + 1*r2,c0 + 2*r3,c0
+
+      So using mix tables where c0 is a word with r0 being its upper
+      8 bits and r3 being its lower 8 bits:
+
+      m0[c0 >> 24] will yield this word: [2*r0,1*r0,1*r0,3*r0]
+      ...
+      m3[c0 & 255] will yield this word: [1*r3,1*r3,3*r3,2*r3]
+
+      Therefore to mix the columns in each word in the state we
+      do the following (& 255 omitted for brevity):
+      c'0,r0 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3]
+      c'0,r1 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3]
+      c'0,r2 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3]
+      c'0,r3 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3]
+
+      However, before mixing, the algorithm requires us to perform
+      ShiftRows(). The ShiftRows() transformation cyclically shifts the
+      last 3 rows of the state over different offsets. The first row
+      (r = 0) is not shifted.
+
+      s'_r,c = s_r,(c + shift(r, Nb) mod Nb
+      for 0 < r < 4 and 0 <= c < Nb and
+      shift(1, 4) = 1
+      shift(2, 4) = 2
+      shift(3, 4) = 3.
+
+      This causes the first byte in r = 1 to be moved to the end of
+      the row, the first 2 bytes in r = 2 to be moved to the end of
+      the row, the first 3 bytes in r = 3 to be moved to the end of
+      the row:
+
+      r1: [c0 c1 c2 c3] => [c1 c2 c3 c0]
+      r2: [c0 c1 c2 c3]    [c2 c3 c0 c1]
+      r3: [c0 c1 c2 c3]    [c3 c0 c1 c2]
+
+      We can make these substitutions inline with our column mixing to
+      generate an updated set of equations to produce each word in the
+      state (note the columns have changed positions):
+
+      c0 c1 c2 c3 => c0 c1 c2 c3
+      c0 c1 c2 c3    c1 c2 c3 c0  (cycled 1 byte)
+      c0 c1 c2 c3    c2 c3 c0 c1  (cycled 2 bytes)
+      c0 c1 c2 c3    c3 c0 c1 c2  (cycled 3 bytes)
+
+      Therefore:
+
+      c'0 = 2*r0,c0 + 3*r1,c1 + 1*r2,c2 + 1*r3,c3
+      c'0 = 1*r0,c0 + 2*r1,c1 + 3*r2,c2 + 1*r3,c3
+      c'0 = 1*r0,c0 + 1*r1,c1 + 2*r2,c2 + 3*r3,c3
+      c'0 = 3*r0,c0 + 1*r1,c1 + 1*r2,c2 + 2*r3,c3
+
+      c'1 = 2*r0,c1 + 3*r1,c2 + 1*r2,c3 + 1*r3,c0
+      c'1 = 1*r0,c1 + 2*r1,c2 + 3*r2,c3 + 1*r3,c0
+      c'1 = 1*r0,c1 + 1*r1,c2 + 2*r2,c3 + 3*r3,c0
+      c'1 = 3*r0,c1 + 1*r1,c2 + 1*r2,c3 + 2*r3,c0
+
+      ... and so forth for c'2 and c'3. The important distinction is
+      that the columns are cycling, with c0 being used with the m0
+      map when calculating c0, but c1 being used with the m0 map when
+      calculating c1 ... and so forth.
+
+      When performing the inverse we transform the mirror image and
+      skip the bottom row, instead of the top one, and move upwards:
+
+      c3 c2 c1 c0 => c0 c3 c2 c1  (cycled 3 bytes) *same as encryption
+      c3 c2 c1 c0    c1 c0 c3 c2  (cycled 2 bytes)
+      c3 c2 c1 c0    c2 c1 c0 c3  (cycled 1 byte)  *same as encryption
+      c3 c2 c1 c0    c3 c2 c1 c0
+
+      If you compare the resulting matrices for ShiftRows()+MixColumns()
+      and for InvShiftRows()+InvMixColumns() the 2nd and 4th columns are
+      different (in encrypt mode vs. decrypt mode). So in order to use
+      the same code to handle both encryption and decryption, we will
+      need to do some mapping.
+
+      If in encryption mode we let a=c0, b=c1, c=c2, d=c3, and r<N> be
+      a row number in the state, then the resulting matrix in encryption
+      mode for applying the above transformations would be:
+
+      r1: a b c d
+      r2: b c d a
+      r3: c d a b
+      r4: d a b c
+
+      If we did the same in decryption mode we would get:
+
+      r1: a d c b
+      r2: b a d c
+      r3: c b a d
+      r4: d c b a
+
+      If instead we swap d and b (set b=c3 and d=c1), then we get:
+
+      r1: a b c d
+      r2: d a b c
+      r3: c d a b
+      r4: b c d a
+
+      Now the 1st and 3rd rows are the same as the encryption matrix. All
+      we need to do then to make the mapping exactly the same is to swap
+      the 2nd and 4th rows when in decryption mode. To do this without
+      having to do it on each iteration, we swapped the 2nd and 4th rows
+      in the decryption key schedule. We also have to do the swap above
+      when we first pull in the input and when we set the final output. */
+    a2 =
+      m0[a >>> 24] ^
+      m1[b >>> 16 & 255] ^
+      m2[c >>> 8 & 255] ^
+      m3[d & 255] ^ w[++i];
+    b2 =
+      m0[b >>> 24] ^
+      m1[c >>> 16 & 255] ^
+      m2[d >>> 8 & 255] ^
+      m3[a & 255] ^ w[++i];
+    c2 =
+      m0[c >>> 24] ^
+      m1[d >>> 16 & 255] ^
+      m2[a >>> 8 & 255] ^
+      m3[b & 255] ^ w[++i];
+    d =
+      m0[d >>> 24] ^
+      m1[a >>> 16 & 255] ^
+      m2[b >>> 8 & 255] ^
+      m3[c & 255] ^ w[++i];
+    a = a2;
+    b = b2;
+    c = c2;
+  }
+
+  /*
+    Encrypt:
+    SubBytes(state)
+    ShiftRows(state)
+    AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
+
+    Decrypt:
+    InvShiftRows(state)
+    InvSubBytes(state)
+    AddRoundKey(state, w[0, Nb-1])
+   */
+  // Note: rows are shifted inline
+  output[0] =
+    (sub[a >>> 24] << 24) ^
+    (sub[b >>> 16 & 255] << 16) ^
+    (sub[c >>> 8 & 255] << 8) ^
+    (sub[d & 255]) ^ w[++i];
+  output[decrypt ? 3 : 1] =
+    (sub[b >>> 24] << 24) ^
+    (sub[c >>> 16 & 255] << 16) ^
+    (sub[d >>> 8 & 255] << 8) ^
+    (sub[a & 255]) ^ w[++i];
+  output[2] =
+    (sub[c >>> 24] << 24) ^
+    (sub[d >>> 16 & 255] << 16) ^
+    (sub[a >>> 8 & 255] << 8) ^
+    (sub[b & 255]) ^ w[++i];
+  output[decrypt ? 1 : 3] =
+    (sub[d >>> 24] << 24) ^
+    (sub[a >>> 16 & 255] << 16) ^
+    (sub[b >>> 8 & 255] << 8) ^
+    (sub[c & 255]) ^ w[++i];
+}
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * forge.cipher.createCipher('AES-<mode>', key);
+ * forge.cipher.createDecipher('AES-<mode>', key);
+ *
+ * Creates a deprecated AES cipher object. This object's mode will default to
+ * CBC (cipher-block-chaining).
+ *
+ * The key and iv may be given as a string of bytes, an array of bytes, a
+ * byte buffer, or an array of 32-bit words.
+ *
+ * @param options the options to use.
+ *          key the symmetric key to use.
+ *          output the buffer to write to.
+ *          decrypt true for decryption, false for encryption.
+ *          mode the cipher mode to use (default: 'CBC').
+ *
+ * @return the cipher.
+ */
+function _createCipher(options) {
+  options = options || {};
+  var mode = (options.mode || 'CBC').toUpperCase();
+  var algorithm = 'AES-' + mode;
+
+  var cipher;
+  if(options.decrypt) {
+    cipher = forge.cipher.createDecipher(algorithm, options.key);
+  } else {
+    cipher = forge.cipher.createCipher(algorithm, options.key);
+  }
+
+  // backwards compatible start API
+  var start = cipher.start;
+  cipher.start = function(iv, options) {
+    // backwards compatibility: support second arg as output buffer
+    var output = null;
+    if(options instanceof forge.util.ByteBuffer) {
+      output = options;
+      options = {};
+    }
+    options = options || {};
+    options.output = output;
+    options.iv = iv;
+    start.call(cipher, options);
+  };
+
+  return cipher;
+}
Index: frontend/node_modules/node-forge/lib/aesCipherSuites.js
===================================================================
--- frontend/node_modules/node-forge/lib/aesCipherSuites.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/aesCipherSuites.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,282 @@
+/**
+ * A Javascript implementation of AES Cipher Suites for TLS.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2009-2015 Digital Bazaar, Inc.
+ *
+ */
+var forge = require('./forge');
+require('./aes');
+require('./tls');
+
+var tls = module.exports = forge.tls;
+
+/**
+ * Supported cipher suites.
+ */
+tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA'] = {
+  id: [0x00, 0x2f],
+  name: 'TLS_RSA_WITH_AES_128_CBC_SHA',
+  initSecurityParameters: function(sp) {
+    sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;
+    sp.cipher_type = tls.CipherType.block;
+    sp.enc_key_length = 16;
+    sp.block_length = 16;
+    sp.fixed_iv_length = 16;
+    sp.record_iv_length = 16;
+    sp.mac_algorithm = tls.MACAlgorithm.hmac_sha1;
+    sp.mac_length = 20;
+    sp.mac_key_length = 20;
+  },
+  initConnectionState: initConnectionState
+};
+tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA'] = {
+  id: [0x00, 0x35],
+  name: 'TLS_RSA_WITH_AES_256_CBC_SHA',
+  initSecurityParameters: function(sp) {
+    sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;
+    sp.cipher_type = tls.CipherType.block;
+    sp.enc_key_length = 32;
+    sp.block_length = 16;
+    sp.fixed_iv_length = 16;
+    sp.record_iv_length = 16;
+    sp.mac_algorithm = tls.MACAlgorithm.hmac_sha1;
+    sp.mac_length = 20;
+    sp.mac_key_length = 20;
+  },
+  initConnectionState: initConnectionState
+};
+
+function initConnectionState(state, c, sp) {
+  var client = (c.entity === forge.tls.ConnectionEnd.client);
+
+  // cipher setup
+  state.read.cipherState = {
+    init: false,
+    cipher: forge.cipher.createDecipher('AES-CBC', client ?
+      sp.keys.server_write_key : sp.keys.client_write_key),
+    iv: client ? sp.keys.server_write_IV : sp.keys.client_write_IV
+  };
+  state.write.cipherState = {
+    init: false,
+    cipher: forge.cipher.createCipher('AES-CBC', client ?
+      sp.keys.client_write_key : sp.keys.server_write_key),
+    iv: client ? sp.keys.client_write_IV : sp.keys.server_write_IV
+  };
+  state.read.cipherFunction = decrypt_aes_cbc_sha1;
+  state.write.cipherFunction = encrypt_aes_cbc_sha1;
+
+  // MAC setup
+  state.read.macLength = state.write.macLength = sp.mac_length;
+  state.read.macFunction = state.write.macFunction = tls.hmac_sha1;
+}
+
+/**
+ * Encrypts the TLSCompressed record into a TLSCipherText record using AES
+ * in CBC mode.
+ *
+ * @param record the TLSCompressed record to encrypt.
+ * @param s the ConnectionState to use.
+ *
+ * @return true on success, false on failure.
+ */
+function encrypt_aes_cbc_sha1(record, s) {
+  var rval = false;
+
+  // append MAC to fragment, update sequence number
+  var mac = s.macFunction(s.macKey, s.sequenceNumber, record);
+  record.fragment.putBytes(mac);
+  s.updateSequenceNumber();
+
+  // TLS 1.1+ use an explicit IV every time to protect against CBC attacks
+  var iv;
+  if(record.version.minor === tls.Versions.TLS_1_0.minor) {
+    // use the pre-generated IV when initializing for TLS 1.0, otherwise use
+    // the residue from the previous encryption
+    iv = s.cipherState.init ? null : s.cipherState.iv;
+  } else {
+    iv = forge.random.getBytesSync(16);
+  }
+
+  s.cipherState.init = true;
+
+  // start cipher
+  var cipher = s.cipherState.cipher;
+  cipher.start({iv: iv});
+
+  // TLS 1.1+ write IV into output
+  if(record.version.minor >= tls.Versions.TLS_1_1.minor) {
+    cipher.output.putBytes(iv);
+  }
+
+  // do encryption (default padding is appropriate)
+  cipher.update(record.fragment);
+  if(cipher.finish(encrypt_aes_cbc_sha1_padding)) {
+    // set record fragment to encrypted output
+    record.fragment = cipher.output;
+    record.length = record.fragment.length();
+    rval = true;
+  }
+
+  return rval;
+}
+
+/**
+ * Handles padding for aes_cbc_sha1 in encrypt mode.
+ *
+ * @param blockSize the block size.
+ * @param input the input buffer.
+ * @param decrypt true in decrypt mode, false in encrypt mode.
+ *
+ * @return true on success, false on failure.
+ */
+function encrypt_aes_cbc_sha1_padding(blockSize, input, decrypt) {
+  /* The encrypted data length (TLSCiphertext.length) is one more than the sum
+   of SecurityParameters.block_length, TLSCompressed.length,
+   SecurityParameters.mac_length, and padding_length.
+
+   The padding may be any length up to 255 bytes long, as long as it results in
+   the TLSCiphertext.length being an integral multiple of the block length.
+   Lengths longer than necessary might be desirable to frustrate attacks on a
+   protocol based on analysis of the lengths of exchanged messages. Each uint8
+   in the padding data vector must be filled with the padding length value.
+
+   The padding length should be such that the total size of the
+   GenericBlockCipher structure is a multiple of the cipher's block length.
+   Legal values range from zero to 255, inclusive. This length specifies the
+   length of the padding field exclusive of the padding_length field itself.
+
+   This is slightly different from PKCS#7 because the padding value is 1
+   less than the actual number of padding bytes if you include the
+   padding_length uint8 itself as a padding byte. */
+  if(!decrypt) {
+    // get the number of padding bytes required to reach the blockSize and
+    // subtract 1 for the padding value (to make room for the padding_length
+    // uint8)
+    var padding = blockSize - (input.length() % blockSize);
+    input.fillWithByte(padding - 1, padding);
+  }
+  return true;
+}
+
+/**
+ * Handles padding for aes_cbc_sha1 in decrypt mode.
+ *
+ * @param blockSize the block size.
+ * @param output the output buffer.
+ * @param decrypt true in decrypt mode, false in encrypt mode.
+ *
+ * @return true on success, false on failure.
+ */
+function decrypt_aes_cbc_sha1_padding(blockSize, output, decrypt) {
+  var rval = true;
+  if(decrypt) {
+    /* The last byte in the output specifies the number of padding bytes not
+      including itself. Each of the padding bytes has the same value as that
+      last byte (known as the padding_length). Here we check all padding
+      bytes to ensure they have the value of padding_length even if one of
+      them is bad in order to ward-off timing attacks. */
+    var len = output.length();
+    var paddingLength = output.last();
+    for(var i = len - 1 - paddingLength; i < len - 1; ++i) {
+      rval = rval && (output.at(i) == paddingLength);
+    }
+    if(rval) {
+      // trim off padding bytes and last padding length byte
+      output.truncate(paddingLength + 1);
+    }
+  }
+  return rval;
+}
+
+/**
+ * Decrypts a TLSCipherText record into a TLSCompressed record using
+ * AES in CBC mode.
+ *
+ * @param record the TLSCipherText record to decrypt.
+ * @param s the ConnectionState to use.
+ *
+ * @return true on success, false on failure.
+ */
+function decrypt_aes_cbc_sha1(record, s) {
+  var rval = false;
+
+  var iv;
+  if(record.version.minor === tls.Versions.TLS_1_0.minor) {
+    // use pre-generated IV when initializing for TLS 1.0, otherwise use the
+    // residue from the previous decryption
+    iv = s.cipherState.init ? null : s.cipherState.iv;
+  } else {
+    // TLS 1.1+ use an explicit IV every time to protect against CBC attacks
+    // that is appended to the record fragment
+    iv = record.fragment.getBytes(16);
+  }
+
+  s.cipherState.init = true;
+
+  // start cipher
+  var cipher = s.cipherState.cipher;
+  cipher.start({iv: iv});
+
+  // do decryption
+  cipher.update(record.fragment);
+  rval = cipher.finish(decrypt_aes_cbc_sha1_padding);
+
+  // even if decryption fails, keep going to minimize timing attacks
+
+  // decrypted data:
+  // first (len - 20) bytes = application data
+  // last 20 bytes          = MAC
+  var macLen = s.macLength;
+
+  // create a random MAC to check against should the mac length check fail
+  // Note: do this regardless of the failure to keep timing consistent
+  var mac = forge.random.getBytesSync(macLen);
+
+  // get fragment and mac
+  var len = cipher.output.length();
+  if(len >= macLen) {
+    record.fragment = cipher.output.getBytes(len - macLen);
+    mac = cipher.output.getBytes(macLen);
+  } else {
+    // bad data, but get bytes anyway to try to keep timing consistent
+    record.fragment = cipher.output.getBytes();
+  }
+  record.fragment = forge.util.createBuffer(record.fragment);
+  record.length = record.fragment.length();
+
+  // see if data integrity checks out, update sequence number
+  var mac2 = s.macFunction(s.macKey, s.sequenceNumber, record);
+  s.updateSequenceNumber();
+  rval = compareMacs(s.macKey, mac, mac2) && rval;
+  return rval;
+}
+
+/**
+ * Safely compare two MACs. This function will compare two MACs in a way
+ * that protects against timing attacks.
+ *
+ * TODO: Expose elsewhere as a utility API.
+ *
+ * See: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
+ *
+ * @param key the MAC key to use.
+ * @param mac1 as a binary-encoded string of bytes.
+ * @param mac2 as a binary-encoded string of bytes.
+ *
+ * @return true if the MACs are the same, false if not.
+ */
+function compareMacs(key, mac1, mac2) {
+  var hmac = forge.hmac.create();
+
+  hmac.start('SHA1', key);
+  hmac.update(mac1);
+  mac1 = hmac.digest().getBytes();
+
+  hmac.start(null, null);
+  hmac.update(mac2);
+  mac2 = hmac.digest().getBytes();
+
+  return mac1 === mac2;
+}
Index: frontend/node_modules/node-forge/lib/asn1-validator.js
===================================================================
--- frontend/node_modules/node-forge/lib/asn1-validator.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/asn1-validator.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2019 Digital Bazaar, Inc.
+ */
+
+var forge = require('./forge');
+require('./asn1');
+var asn1 = forge.asn1;
+
+exports.privateKeyValidator = {
+  // PrivateKeyInfo
+  name: 'PrivateKeyInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    // Version (INTEGER)
+    name: 'PrivateKeyInfo.version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyVersion'
+  }, {
+    // privateKeyAlgorithm
+    name: 'PrivateKeyInfo.privateKeyAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'AlgorithmIdentifier.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'privateKeyOid'
+    }]
+  }, {
+    // PrivateKey
+    name: 'PrivateKeyInfo',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OCTETSTRING,
+    constructed: false,
+    capture: 'privateKey'
+  }]
+};
+
+exports.publicKeyValidator = {
+  name: 'SubjectPublicKeyInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  captureAsn1: 'subjectPublicKeyInfo',
+  value: [{
+    name: 'SubjectPublicKeyInfo.AlgorithmIdentifier',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'AlgorithmIdentifier.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'publicKeyOid'
+    }]
+  },
+  // capture group for ed25519PublicKey
+  {
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.BITSTRING,
+    constructed: false,
+    composed: true,
+    captureBitStringValue: 'ed25519PublicKey'
+  }
+  // FIXME: this is capture group for rsaPublicKey, use it in this API or
+  // discard?
+  /* {
+    // subjectPublicKey
+    name: 'SubjectPublicKeyInfo.subjectPublicKey',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.BITSTRING,
+    constructed: false,
+    value: [{
+      // RSAPublicKey
+      name: 'SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      optional: true,
+      captureAsn1: 'rsaPublicKey'
+    }]
+  } */
+  ]
+};
Index: frontend/node_modules/node-forge/lib/asn1.js
===================================================================
--- frontend/node_modules/node-forge/lib/asn1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/asn1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1503 @@
+/**
+ * Javascript implementation of Abstract Syntax Notation Number One.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2015 Digital Bazaar, Inc.
+ *
+ * An API for storing data using the Abstract Syntax Notation Number One
+ * format using DER (Distinguished Encoding Rules) encoding. This encoding is
+ * commonly used to store data for PKI, i.e. X.509 Certificates, and this
+ * implementation exists for that purpose.
+ *
+ * Abstract Syntax Notation Number One (ASN.1) is used to define the abstract
+ * syntax of information without restricting the way the information is encoded
+ * for transmission. It provides a standard that allows for open systems
+ * communication. ASN.1 defines the syntax of information data and a number of
+ * simple data types as well as a notation for describing them and specifying
+ * values for them.
+ *
+ * The RSA algorithm creates public and private keys that are often stored in
+ * X.509 or PKCS#X formats -- which use ASN.1 (encoded in DER format). This
+ * class provides the most basic functionality required to store and load DSA
+ * keys that are encoded according to ASN.1.
+ *
+ * The most common binary encodings for ASN.1 are BER (Basic Encoding Rules)
+ * and DER (Distinguished Encoding Rules). DER is just a subset of BER that
+ * has stricter requirements for how data must be encoded.
+ *
+ * Each ASN.1 structure has a tag (a byte identifying the ASN.1 structure type)
+ * and a byte array for the value of this ASN1 structure which may be data or a
+ * list of ASN.1 structures.
+ *
+ * Each ASN.1 structure using BER is (Tag-Length-Value):
+ *
+ * | byte 0 | bytes X | bytes Y |
+ * |--------|---------|----------
+ * |  tag   | length  |  value  |
+ *
+ * ASN.1 allows for tags to be of "High-tag-number form" which allows a tag to
+ * be two or more octets, but that is not supported by this class. A tag is
+ * only 1 byte. Bits 1-5 give the tag number (ie the data type within a
+ * particular 'class'), 6 indicates whether or not the ASN.1 value is
+ * constructed from other ASN.1 values, and bits 7 and 8 give the 'class'. If
+ * bits 7 and 8 are both zero, the class is UNIVERSAL. If only bit 7 is set,
+ * then the class is APPLICATION. If only bit 8 is set, then the class is
+ * CONTEXT_SPECIFIC. If both bits 7 and 8 are set, then the class is PRIVATE.
+ * The tag numbers for the data types for the class UNIVERSAL are listed below:
+ *
+ * UNIVERSAL 0 Reserved for use by the encoding rules
+ * UNIVERSAL 1 Boolean type
+ * UNIVERSAL 2 Integer type
+ * UNIVERSAL 3 Bitstring type
+ * UNIVERSAL 4 Octetstring type
+ * UNIVERSAL 5 Null type
+ * UNIVERSAL 6 Object identifier type
+ * UNIVERSAL 7 Object descriptor type
+ * UNIVERSAL 8 External type and Instance-of type
+ * UNIVERSAL 9 Real type
+ * UNIVERSAL 10 Enumerated type
+ * UNIVERSAL 11 Embedded-pdv type
+ * UNIVERSAL 12 UTF8String type
+ * UNIVERSAL 13 Relative object identifier type
+ * UNIVERSAL 14-15 Reserved for future editions
+ * UNIVERSAL 16 Sequence and Sequence-of types
+ * UNIVERSAL 17 Set and Set-of types
+ * UNIVERSAL 18-22, 25-30 Character string types
+ * UNIVERSAL 23-24 Time types
+ *
+ * The length of an ASN.1 structure is specified after the tag identifier.
+ * There is a definite form and an indefinite form. The indefinite form may
+ * be used if the encoding is constructed and not all immediately available.
+ * The indefinite form is encoded using a length byte with only the 8th bit
+ * set. The end of the constructed object is marked using end-of-contents
+ * octets (two zero bytes).
+ *
+ * The definite form looks like this:
+ *
+ * The length may take up 1 or more bytes, it depends on the length of the
+ * value of the ASN.1 structure. DER encoding requires that if the ASN.1
+ * structure has a value that has a length greater than 127, more than 1 byte
+ * will be used to store its length, otherwise just one byte will be used.
+ * This is strict.
+ *
+ * In the case that the length of the ASN.1 value is less than 127, 1 octet
+ * (byte) is used to store the "short form" length. The 8th bit has a value of
+ * 0 indicating the length is "short form" and not "long form" and bits 7-1
+ * give the length of the data. (The 8th bit is the left-most, most significant
+ * bit: also known as big endian or network format).
+ *
+ * In the case that the length of the ASN.1 value is greater than 127, 2 to
+ * 127 octets (bytes) are used to store the "long form" length. The first
+ * byte's 8th bit is set to 1 to indicate the length is "long form." Bits 7-1
+ * give the number of additional octets. All following octets are in base 256
+ * with the most significant digit first (typical big-endian binary unsigned
+ * integer storage). So, for instance, if the length of a value was 257, the
+ * first byte would be set to:
+ *
+ * 10000010 = 130 = 0x82.
+ *
+ * This indicates there are 2 octets (base 256) for the length. The second and
+ * third bytes (the octets just mentioned) would store the length in base 256:
+ *
+ * octet 2: 00000001 = 1 * 256^1 = 256
+ * octet 3: 00000001 = 1 * 256^0 = 1
+ * total = 257
+ *
+ * The algorithm for converting a js integer value of 257 to base-256 is:
+ *
+ * var value = 257;
+ * var bytes = [];
+ * bytes[0] = (value >>> 8) & 0xFF; // most significant byte first
+ * bytes[1] = value & 0xFF;        // least significant byte last
+ *
+ * On the ASN.1 UNIVERSAL Object Identifier (OID) type:
+ *
+ * An OID can be written like: "value1.value2.value3...valueN"
+ *
+ * The DER encoding rules:
+ *
+ * The first byte has the value 40 * value1 + value2.
+ * The following bytes, if any, encode the remaining values. Each value is
+ * encoded in base 128, most significant digit first (big endian), with as
+ * few digits as possible, and the most significant bit of each byte set
+ * to 1 except the last in each value's encoding. For example: Given the
+ * OID "1.2.840.113549", its DER encoding is (remember each byte except the
+ * last one in each encoding is OR'd with 0x80):
+ *
+ * byte 1: 40 * 1 + 2 = 42 = 0x2A.
+ * bytes 2-3: 128 * 6 + 72 = 840 = 6 72 = 6 72 = 0x0648 = 0x8648
+ * bytes 4-6: 16384 * 6 + 128 * 119 + 13 = 6 119 13 = 0x06770D = 0x86F70D
+ *
+ * The final value is: 0x2A864886F70D.
+ * The full OID (including ASN.1 tag and length of 6 bytes) is:
+ * 0x06062A864886F70D
+ */
+var forge = require('./forge');
+require('./util');
+require('./oids');
+
+/* ASN.1 API */
+var asn1 = module.exports = forge.asn1 = forge.asn1 || {};
+
+/**
+ * ASN.1 classes.
+ */
+asn1.Class = {
+  UNIVERSAL:        0x00,
+  APPLICATION:      0x40,
+  CONTEXT_SPECIFIC: 0x80,
+  PRIVATE:          0xC0
+};
+
+/**
+ * ASN.1 types. Not all types are supported by this implementation, only
+ * those necessary to implement a simple PKI are implemented.
+ */
+asn1.Type = {
+  NONE:             0,
+  BOOLEAN:          1,
+  INTEGER:          2,
+  BITSTRING:        3,
+  OCTETSTRING:      4,
+  NULL:             5,
+  OID:              6,
+  ODESC:            7,
+  EXTERNAL:         8,
+  REAL:             9,
+  ENUMERATED:      10,
+  EMBEDDED:        11,
+  UTF8:            12,
+  ROID:            13,
+  SEQUENCE:        16,
+  SET:             17,
+  PRINTABLESTRING: 19,
+  IA5STRING:       22,
+  UTCTIME:         23,
+  GENERALIZEDTIME: 24,
+  BMPSTRING:       30
+};
+
+/**
+ * Sets the default maximum recursion depth when parsing ASN.1 structures.
+ */
+asn1.maxDepth = 256;
+
+/**
+ * Creates a new asn1 object.
+ *
+ * @param tagClass the tag class for the object.
+ * @param type the data type (tag number) for the object.
+ * @param constructed true if the asn1 object is in constructed form.
+ * @param value the value for the object, if it is not constructed.
+ * @param [options] the options to use:
+ *          [bitStringContents] the plain BIT STRING content including padding
+ *            byte.
+ *
+ * @return the asn1 object.
+ */
+asn1.create = function(tagClass, type, constructed, value, options) {
+  /* An asn1 object has a tagClass, a type, a constructed flag, and a
+    value. The value's type depends on the constructed flag. If
+    constructed, it will contain a list of other asn1 objects. If not,
+    it will contain the ASN.1 value as an array of bytes formatted
+    according to the ASN.1 data type. */
+
+  // remove undefined values
+  if(forge.util.isArray(value)) {
+    var tmp = [];
+    for(var i = 0; i < value.length; ++i) {
+      if(value[i] !== undefined) {
+        tmp.push(value[i]);
+      }
+    }
+    value = tmp;
+  }
+
+  var obj = {
+    tagClass: tagClass,
+    type: type,
+    constructed: constructed,
+    composed: constructed || forge.util.isArray(value),
+    value: value
+  };
+  if(options && 'bitStringContents' in options) {
+    // TODO: copy byte buffer if it's a buffer not a string
+    obj.bitStringContents = options.bitStringContents;
+    // TODO: add readonly flag to avoid this overhead
+    // save copy to detect changes
+    obj.original = asn1.copy(obj);
+  }
+  return obj;
+};
+
+/**
+ * Copies an asn1 object.
+ *
+ * @param obj the asn1 object.
+ * @param [options] copy options:
+ *          [excludeBitStringContents] true to not copy bitStringContents
+ *
+ * @return the a copy of the asn1 object.
+ */
+asn1.copy = function(obj, options) {
+  var copy;
+
+  if(forge.util.isArray(obj)) {
+    copy = [];
+    for(var i = 0; i < obj.length; ++i) {
+      copy.push(asn1.copy(obj[i], options));
+    }
+    return copy;
+  }
+
+  if(typeof obj === 'string') {
+    // TODO: copy byte buffer if it's a buffer not a string
+    return obj;
+  }
+
+  copy = {
+    tagClass: obj.tagClass,
+    type: obj.type,
+    constructed: obj.constructed,
+    composed: obj.composed,
+    value: asn1.copy(obj.value, options)
+  };
+  if(options && !options.excludeBitStringContents) {
+    // TODO: copy byte buffer if it's a buffer not a string
+    copy.bitStringContents = obj.bitStringContents;
+  }
+  return copy;
+};
+
+/**
+ * Compares asn1 objects for equality.
+ *
+ * Note this function does not run in constant time.
+ *
+ * @param obj1 the first asn1 object.
+ * @param obj2 the second asn1 object.
+ * @param [options] compare options:
+ *          [includeBitStringContents] true to compare bitStringContents
+ *
+ * @return true if the asn1 objects are equal.
+ */
+asn1.equals = function(obj1, obj2, options) {
+  if(forge.util.isArray(obj1)) {
+    if(!forge.util.isArray(obj2)) {
+      return false;
+    }
+    if(obj1.length !== obj2.length) {
+      return false;
+    }
+    for(var i = 0; i < obj1.length; ++i) {
+      if(!asn1.equals(obj1[i], obj2[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  if(typeof obj1 !== typeof obj2) {
+    return false;
+  }
+
+  if(typeof obj1 === 'string') {
+    return obj1 === obj2;
+  }
+
+  var equal = obj1.tagClass === obj2.tagClass &&
+    obj1.type === obj2.type &&
+    obj1.constructed === obj2.constructed &&
+    obj1.composed === obj2.composed &&
+    asn1.equals(obj1.value, obj2.value);
+  if(options && options.includeBitStringContents) {
+    equal = equal && (obj1.bitStringContents === obj2.bitStringContents);
+  }
+
+  return equal;
+};
+
+/**
+ * Gets the length of a BER-encoded ASN.1 value.
+ *
+ * In case the length is not specified, undefined is returned.
+ *
+ * @param b the BER-encoded ASN.1 byte buffer, starting with the first
+ *          length byte.
+ *
+ * @return the length of the BER-encoded ASN.1 value or undefined.
+ */
+asn1.getBerValueLength = function(b) {
+  // TODO: move this function and related DER/BER functions to a der.js
+  // file; better abstract ASN.1 away from der/ber.
+  var b2 = b.getByte();
+  if(b2 === 0x80) {
+    return undefined;
+  }
+
+  // see if the length is "short form" or "long form" (bit 8 set)
+  var length;
+  var longForm = b2 & 0x80;
+  if(!longForm) {
+    // length is just the first byte
+    length = b2;
+  } else {
+    // the number of bytes the length is specified in bits 7 through 1
+    // and each length byte is in big-endian base-256
+    length = b.getInt((b2 & 0x7F) << 3);
+  }
+  return length;
+};
+
+/**
+ * Check if the byte buffer has enough bytes. Throws an Error if not.
+ *
+ * @param bytes the byte buffer to parse from.
+ * @param remaining the bytes remaining in the current parsing state.
+ * @param n the number of bytes the buffer must have.
+ */
+function _checkBufferLength(bytes, remaining, n) {
+  if(n > remaining) {
+    var error = new Error('Too few bytes to parse DER.');
+    error.available = bytes.length();
+    error.remaining = remaining;
+    error.requested = n;
+    throw error;
+  }
+}
+
+/**
+ * Gets the length of a BER-encoded ASN.1 value.
+ *
+ * In case the length is not specified, undefined is returned.
+ *
+ * @param bytes the byte buffer to parse from.
+ * @param remaining the bytes remaining in the current parsing state.
+ *
+ * @return the length of the BER-encoded ASN.1 value or undefined.
+ */
+var _getValueLength = function(bytes, remaining) {
+  // TODO: move this function and related DER/BER functions to a der.js
+  // file; better abstract ASN.1 away from der/ber.
+  // fromDer already checked that this byte exists
+  var b2 = bytes.getByte();
+  remaining--;
+  if(b2 === 0x80) {
+    return undefined;
+  }
+
+  // see if the length is "short form" or "long form" (bit 8 set)
+  var length;
+  var longForm = b2 & 0x80;
+  if(!longForm) {
+    // length is just the first byte
+    length = b2;
+  } else {
+    // the number of bytes the length is specified in bits 7 through 1
+    // and each length byte is in big-endian base-256
+    var longFormBytes = b2 & 0x7F;
+    _checkBufferLength(bytes, remaining, longFormBytes);
+    length = bytes.getInt(longFormBytes << 3);
+  }
+  // FIXME: this will only happen for 32 bit getInt with high bit set
+  if(length < 0) {
+    throw new Error('Negative length: ' + length);
+  }
+  return length;
+};
+
+/**
+ * Parses an asn1 object from a byte buffer in DER format.
+ *
+ * @param bytes the byte buffer to parse from.
+ * @param [strict] true to be strict when checking value lengths, false to
+ *          allow truncated values (default: true).
+ * @param [options] object with options or boolean strict flag
+ *          [strict] true to be strict when checking value lengths, false to
+ *            allow truncated values (default: true).
+ *          [parseAllBytes] true to ensure all bytes are parsed
+ *            (default: true)
+ *          [decodeBitStrings] true to attempt to decode the content of
+ *            BIT STRINGs (not OCTET STRINGs) using strict mode. Note that
+ *            without schema support to understand the data context this can
+ *            erroneously decode values that happen to be valid ASN.1. This
+ *            flag will be deprecated or removed as soon as schema support is
+ *            available. (default: true)
+ *          [maxDepth] override asn1.maxDepth recursion limit
+ *            (default: asn1.maxDepth)
+ *
+ * @throws Will throw an error for various malformed input conditions.
+ *
+ * @return the parsed asn1 object.
+ */
+asn1.fromDer = function(bytes, options) {
+  if(options === undefined) {
+    options = {
+      strict: true,
+      parseAllBytes: true,
+      decodeBitStrings: true
+    };
+  }
+  if(typeof options === 'boolean') {
+    options = {
+      strict: options,
+      parseAllBytes: true,
+      decodeBitStrings: true
+    };
+  }
+  if(!('strict' in options)) {
+    options.strict = true;
+  }
+  if(!('parseAllBytes' in options)) {
+    options.parseAllBytes = true;
+  }
+  if(!('decodeBitStrings' in options)) {
+    options.decodeBitStrings = true;
+  }
+  if(!('maxDepth' in options)) {
+    options.maxDepth = asn1.maxDepth;
+  }
+
+  // wrap in buffer if needed
+  if(typeof bytes === 'string') {
+    bytes = forge.util.createBuffer(bytes);
+  }
+
+  var byteCount = bytes.length();
+  var value = _fromDer(bytes, bytes.length(), 0, options);
+  if(options.parseAllBytes && bytes.length() !== 0) {
+    var error = new Error('Unparsed DER bytes remain after ASN.1 parsing.');
+    error.byteCount = byteCount;
+    error.remaining = bytes.length();
+    throw error;
+  }
+  return value;
+};
+
+/**
+ * Internal function to parse an asn1 object from a byte buffer in DER format.
+ *
+ * @param bytes the byte buffer to parse from.
+ * @param remaining the number of bytes remaining for this chunk.
+ * @param depth the current parsing depth.
+ * @param options object with same options as fromDer().
+ *
+ * @return the parsed asn1 object.
+ */
+function _fromDer(bytes, remaining, depth, options) {
+
+  // check depth limit
+  if(depth >= options.maxDepth) {
+    throw new Error('ASN.1 parsing error: Max depth exceeded.');
+  }
+
+  // temporary storage for consumption calculations
+  var start;
+
+  // minimum length for ASN.1 DER structure is 2
+  _checkBufferLength(bytes, remaining, 2);
+
+  // get the first byte
+  var b1 = bytes.getByte();
+  // consumed one byte
+  remaining--;
+
+  // get the tag class
+  var tagClass = (b1 & 0xC0);
+
+  // get the type (bits 1-5)
+  var type = b1 & 0x1F;
+
+  // get the variable value length and adjust remaining bytes
+  start = bytes.length();
+  var length = _getValueLength(bytes, remaining);
+  remaining -= start - bytes.length();
+
+  // ensure there are enough bytes to get the value
+  if(length !== undefined && length > remaining) {
+    if(options.strict) {
+      var error = new Error('Too few bytes to read ASN.1 value.');
+      error.available = bytes.length();
+      error.remaining = remaining;
+      error.requested = length;
+      throw error;
+    }
+    // Note: be lenient with truncated values and use remaining state bytes
+    length = remaining;
+  }
+
+  // value storage
+  var value;
+  // possible BIT STRING contents storage
+  var bitStringContents;
+
+  // constructed flag is bit 6 (32 = 0x20) of the first byte
+  var constructed = ((b1 & 0x20) === 0x20);
+  if(constructed) {
+    // parse child asn1 objects from the value
+    value = [];
+    if(length === undefined) {
+      // asn1 object of indefinite length, read until end tag
+      for(;;) {
+        _checkBufferLength(bytes, remaining, 2);
+        if(bytes.bytes(2) === String.fromCharCode(0, 0)) {
+          bytes.getBytes(2);
+          remaining -= 2;
+          break;
+        }
+        start = bytes.length();
+        value.push(_fromDer(bytes, remaining, depth + 1, options));
+        remaining -= start - bytes.length();
+      }
+    } else {
+      // parsing asn1 object of definite length
+      while(length > 0) {
+        start = bytes.length();
+        value.push(_fromDer(bytes, length, depth + 1, options));
+        remaining -= start - bytes.length();
+        length -= start - bytes.length();
+      }
+    }
+  }
+
+  // if a BIT STRING, save the contents including padding
+  if(value === undefined && tagClass === asn1.Class.UNIVERSAL &&
+    type === asn1.Type.BITSTRING) {
+    bitStringContents = bytes.bytes(length);
+  }
+
+  // determine if a non-constructed value should be decoded as a composed
+  // value that contains other ASN.1 objects. BIT STRINGs (and OCTET STRINGs)
+  // can be used this way.
+  if(value === undefined && options.decodeBitStrings &&
+    tagClass === asn1.Class.UNIVERSAL &&
+    // FIXME: OCTET STRINGs not yet supported here
+    // .. other parts of forge expect to decode OCTET STRINGs manually
+    (type === asn1.Type.BITSTRING /*|| type === asn1.Type.OCTETSTRING*/) &&
+    length > 1) {
+    // save read position
+    var savedRead = bytes.read;
+    var savedRemaining = remaining;
+    var unused = 0;
+    if(type === asn1.Type.BITSTRING) {
+      /* The first octet gives the number of bits by which the length of the
+        bit string is less than the next multiple of eight (this is called
+        the "number of unused bits").
+
+        The second and following octets give the value of the bit string
+        converted to an octet string. */
+      _checkBufferLength(bytes, remaining, 1);
+      unused = bytes.getByte();
+      remaining--;
+    }
+    // if all bits are used, maybe the BIT/OCTET STRING holds ASN.1 objs
+    if(unused === 0) {
+      try {
+        // attempt to parse child asn1 object from the value
+        // (stored in array to signal composed value)
+        start = bytes.length();
+        var subOptions = {
+          // enforce strict mode to avoid parsing ASN.1 from plain data
+          strict: true,
+          decodeBitStrings: true
+        };
+        var composed = _fromDer(bytes, remaining, depth + 1, subOptions);
+        var used = start - bytes.length();
+        remaining -= used;
+        if(type == asn1.Type.BITSTRING) {
+          used++;
+        }
+
+        // if the data all decoded and the class indicates UNIVERSAL or
+        // CONTEXT_SPECIFIC then assume we've got an encapsulated ASN.1 object
+        var tc = composed.tagClass;
+        if(used === length &&
+          (tc === asn1.Class.UNIVERSAL || tc === asn1.Class.CONTEXT_SPECIFIC)) {
+          value = [composed];
+        }
+      } catch(ex) {
+      }
+    }
+    if(value === undefined) {
+      // restore read position
+      bytes.read = savedRead;
+      remaining = savedRemaining;
+    }
+  }
+
+  if(value === undefined) {
+    // asn1 not constructed or composed, get raw value
+    // TODO: do DER to OID conversion and vice-versa in .toDer?
+
+    if(length === undefined) {
+      if(options.strict) {
+        throw new Error('Non-constructed ASN.1 object of indefinite length.');
+      }
+      // be lenient and use remaining state bytes
+      length = remaining;
+    }
+
+    if(type === asn1.Type.BMPSTRING) {
+      value = '';
+      for(; length > 0; length -= 2) {
+        _checkBufferLength(bytes, remaining, 2);
+        value += String.fromCharCode(bytes.getInt16());
+        remaining -= 2;
+      }
+    } else {
+      value = bytes.getBytes(length);
+      remaining -= length;
+    }
+  }
+
+  // add BIT STRING contents if available
+  var asn1Options = bitStringContents === undefined ? null : {
+    bitStringContents: bitStringContents
+  };
+
+  // create and return asn1 object
+  return asn1.create(tagClass, type, constructed, value, asn1Options);
+}
+
+/**
+ * Converts the given asn1 object to a buffer of bytes in DER format.
+ *
+ * @param asn1 the asn1 object to convert to bytes.
+ *
+ * @return the buffer of bytes.
+ */
+asn1.toDer = function(obj) {
+  var bytes = forge.util.createBuffer();
+
+  // build the first byte
+  var b1 = obj.tagClass | obj.type;
+
+  // for storing the ASN.1 value
+  var value = forge.util.createBuffer();
+
+  // use BIT STRING contents if available and data not changed
+  var useBitStringContents = false;
+  if('bitStringContents' in obj) {
+    useBitStringContents = true;
+    if(obj.original) {
+      useBitStringContents = asn1.equals(obj, obj.original);
+    }
+  }
+
+  if(useBitStringContents) {
+    value.putBytes(obj.bitStringContents);
+  } else if(obj.composed) {
+    // if composed, use each child asn1 object's DER bytes as value
+    // turn on 6th bit (0x20 = 32) to indicate asn1 is constructed
+    // from other asn1 objects
+    if(obj.constructed) {
+      b1 |= 0x20;
+    } else {
+      // type is a bit string, add unused bits of 0x00
+      value.putByte(0x00);
+    }
+
+    // add all of the child DER bytes together
+    for(var i = 0; i < obj.value.length; ++i) {
+      if(obj.value[i] !== undefined) {
+        value.putBuffer(asn1.toDer(obj.value[i]));
+      }
+    }
+  } else {
+    // use asn1.value directly
+    if(obj.type === asn1.Type.BMPSTRING) {
+      for(var i = 0; i < obj.value.length; ++i) {
+        value.putInt16(obj.value.charCodeAt(i));
+      }
+    } else {
+      // ensure integer is minimally-encoded
+      // TODO: should all leading bytes be stripped vs just one?
+      // .. ex '00 00 01' => '01'?
+      if(obj.type === asn1.Type.INTEGER &&
+        obj.value.length > 1 &&
+        // leading 0x00 for positive integer
+        ((obj.value.charCodeAt(0) === 0 &&
+        (obj.value.charCodeAt(1) & 0x80) === 0) ||
+        // leading 0xFF for negative integer
+        (obj.value.charCodeAt(0) === 0xFF &&
+        (obj.value.charCodeAt(1) & 0x80) === 0x80))) {
+        value.putBytes(obj.value.substr(1));
+      } else {
+        value.putBytes(obj.value);
+      }
+    }
+  }
+
+  // add tag byte
+  bytes.putByte(b1);
+
+  // use "short form" encoding
+  if(value.length() <= 127) {
+    // one byte describes the length
+    // bit 8 = 0 and bits 7-1 = length
+    bytes.putByte(value.length() & 0x7F);
+  } else {
+    // use "long form" encoding
+    // 2 to 127 bytes describe the length
+    // first byte: bit 8 = 1 and bits 7-1 = # of additional bytes
+    // other bytes: length in base 256, big-endian
+    var len = value.length();
+    var lenBytes = '';
+    do {
+      lenBytes += String.fromCharCode(len & 0xFF);
+      len = len >>> 8;
+    } while(len > 0);
+
+    // set first byte to # bytes used to store the length and turn on
+    // bit 8 to indicate long-form length is used
+    bytes.putByte(lenBytes.length | 0x80);
+
+    // concatenate length bytes in reverse since they were generated
+    // little endian and we need big endian
+    for(var i = lenBytes.length - 1; i >= 0; --i) {
+      bytes.putByte(lenBytes.charCodeAt(i));
+    }
+  }
+
+  // concatenate value bytes
+  bytes.putBuffer(value);
+  return bytes;
+};
+
+/**
+ * Converts an OID dot-separated string to a byte buffer. The byte buffer
+ * contains only the DER-encoded value, not any tag or length bytes.
+ *
+ * @param oid the OID dot-separated string.
+ *
+ * @return the byte buffer.
+ */
+asn1.oidToDer = function(oid) {
+  // split OID into individual values
+  var values = oid.split('.');
+  var bytes = forge.util.createBuffer();
+
+  // first byte is 40 * value1 + value2
+  bytes.putByte(40 * parseInt(values[0], 10) + parseInt(values[1], 10));
+  // other bytes are each value in base 128 with 8th bit set except for
+  // the last byte for each value
+  var last, valueBytes, value, b;
+  for(var i = 2; i < values.length; ++i) {
+    // produce value bytes in reverse because we don't know how many
+    // bytes it will take to store the value
+    last = true;
+    valueBytes = [];
+    value = parseInt(values[i], 10);
+    // TODO: Change bitwise logic to allow larger values.
+    if(value > 0xffffffff) {
+      throw new Error('OID value too large; max is 32-bits.');
+    }
+    do {
+      b = value & 0x7F;
+      value = value >>> 7;
+      // if value is not last, then turn on 8th bit
+      if(!last) {
+        b |= 0x80;
+      }
+      valueBytes.push(b);
+      last = false;
+    } while(value > 0);
+
+    // add value bytes in reverse (needs to be in big endian)
+    for(var n = valueBytes.length - 1; n >= 0; --n) {
+      bytes.putByte(valueBytes[n]);
+    }
+  }
+
+  return bytes;
+};
+
+/**
+ * Converts a DER-encoded byte buffer to an OID dot-separated string. The
+ * byte buffer should contain only the DER-encoded value, not any tag or
+ * length bytes.
+ *
+ * @param bytes the byte buffer.
+ *
+ * @return the OID dot-separated string.
+ */
+asn1.derToOid = function(bytes) {
+  var oid;
+
+  // wrap in buffer if needed
+  if(typeof bytes === 'string') {
+    bytes = forge.util.createBuffer(bytes);
+  }
+
+  // first byte is 40 * value1 + value2
+  var b = bytes.getByte();
+  oid = Math.floor(b / 40) + '.' + (b % 40);
+
+  // other bytes are each value in base 128 with 8th bit set except for
+  // the last byte for each value
+  var value = 0;
+  while(bytes.length() > 0) {
+    // error if 7b shift would exceed Number.MAX_SAFE_INTEGER
+    // (Number.MAX_SAFE_INTEGER / 128)
+    if(value > 0x3fffffffffff) {
+      throw new Error('OID value too large; max is 53-bits.');
+    }
+    b = bytes.getByte();
+    value = value * 128;
+    // not the last byte for the value
+    if(b & 0x80) {
+      value += b & 0x7F;
+    } else {
+      // last byte
+      oid += '.' + (value + b);
+      value = 0;
+    }
+  }
+
+  return oid;
+};
+
+/**
+ * Converts a UTCTime value to a date.
+ *
+ * Note: GeneralizedTime has 4 digits for the year and is used for X.509
+ * dates past 2049. Parsing that structure hasn't been implemented yet.
+ *
+ * @param utc the UTCTime value to convert.
+ *
+ * @return the date.
+ */
+asn1.utcTimeToDate = function(utc) {
+  /* The following formats can be used:
+
+    YYMMDDhhmmZ
+    YYMMDDhhmm+hh'mm'
+    YYMMDDhhmm-hh'mm'
+    YYMMDDhhmmssZ
+    YYMMDDhhmmss+hh'mm'
+    YYMMDDhhmmss-hh'mm'
+
+    Where:
+
+    YY is the least significant two digits of the year
+    MM is the month (01 to 12)
+    DD is the day (01 to 31)
+    hh is the hour (00 to 23)
+    mm are the minutes (00 to 59)
+    ss are the seconds (00 to 59)
+    Z indicates that local time is GMT, + indicates that local time is
+    later than GMT, and - indicates that local time is earlier than GMT
+    hh' is the absolute value of the offset from GMT in hours
+    mm' is the absolute value of the offset from GMT in minutes */
+  var date = new Date();
+
+  // if YY >= 50 use 19xx, if YY < 50 use 20xx
+  var year = parseInt(utc.substr(0, 2), 10);
+  year = (year >= 50) ? 1900 + year : 2000 + year;
+  var MM = parseInt(utc.substr(2, 2), 10) - 1; // use 0-11 for month
+  var DD = parseInt(utc.substr(4, 2), 10);
+  var hh = parseInt(utc.substr(6, 2), 10);
+  var mm = parseInt(utc.substr(8, 2), 10);
+  var ss = 0;
+
+  // not just YYMMDDhhmmZ
+  if(utc.length > 11) {
+    // get character after minutes
+    var c = utc.charAt(10);
+    var end = 10;
+
+    // see if seconds are present
+    if(c !== '+' && c !== '-') {
+      // get seconds
+      ss = parseInt(utc.substr(10, 2), 10);
+      end += 2;
+    }
+  }
+
+  // update date
+  date.setUTCFullYear(year, MM, DD);
+  date.setUTCHours(hh, mm, ss, 0);
+
+  if(end) {
+    // get +/- after end of time
+    c = utc.charAt(end);
+    if(c === '+' || c === '-') {
+      // get hours+minutes offset
+      var hhoffset = parseInt(utc.substr(end + 1, 2), 10);
+      var mmoffset = parseInt(utc.substr(end + 4, 2), 10);
+
+      // calculate offset in milliseconds
+      var offset = hhoffset * 60 + mmoffset;
+      offset *= 60000;
+
+      // apply offset
+      if(c === '+') {
+        date.setTime(+date - offset);
+      } else {
+        date.setTime(+date + offset);
+      }
+    }
+  }
+
+  return date;
+};
+
+/**
+ * Converts a GeneralizedTime value to a date.
+ *
+ * @param gentime the GeneralizedTime value to convert.
+ *
+ * @return the date.
+ */
+asn1.generalizedTimeToDate = function(gentime) {
+  /* The following formats can be used:
+
+    YYYYMMDDHHMMSS
+    YYYYMMDDHHMMSS.fff
+    YYYYMMDDHHMMSSZ
+    YYYYMMDDHHMMSS.fffZ
+    YYYYMMDDHHMMSS+hh'mm'
+    YYYYMMDDHHMMSS.fff+hh'mm'
+    YYYYMMDDHHMMSS-hh'mm'
+    YYYYMMDDHHMMSS.fff-hh'mm'
+
+    Where:
+
+    YYYY is the year
+    MM is the month (01 to 12)
+    DD is the day (01 to 31)
+    hh is the hour (00 to 23)
+    mm are the minutes (00 to 59)
+    ss are the seconds (00 to 59)
+    .fff is the second fraction, accurate to three decimal places
+    Z indicates that local time is GMT, + indicates that local time is
+    later than GMT, and - indicates that local time is earlier than GMT
+    hh' is the absolute value of the offset from GMT in hours
+    mm' is the absolute value of the offset from GMT in minutes */
+  var date = new Date();
+
+  var YYYY = parseInt(gentime.substr(0, 4), 10);
+  var MM = parseInt(gentime.substr(4, 2), 10) - 1; // use 0-11 for month
+  var DD = parseInt(gentime.substr(6, 2), 10);
+  var hh = parseInt(gentime.substr(8, 2), 10);
+  var mm = parseInt(gentime.substr(10, 2), 10);
+  var ss = parseInt(gentime.substr(12, 2), 10);
+  var fff = 0;
+  var offset = 0;
+  var isUTC = false;
+
+  if(gentime.charAt(gentime.length - 1) === 'Z') {
+    isUTC = true;
+  }
+
+  var end = gentime.length - 5, c = gentime.charAt(end);
+  if(c === '+' || c === '-') {
+    // get hours+minutes offset
+    var hhoffset = parseInt(gentime.substr(end + 1, 2), 10);
+    var mmoffset = parseInt(gentime.substr(end + 4, 2), 10);
+
+    // calculate offset in milliseconds
+    offset = hhoffset * 60 + mmoffset;
+    offset *= 60000;
+
+    // apply offset
+    if(c === '+') {
+      offset *= -1;
+    }
+
+    isUTC = true;
+  }
+
+  // check for second fraction
+  if(gentime.charAt(14) === '.') {
+    fff = parseFloat(gentime.substr(14), 10) * 1000;
+  }
+
+  if(isUTC) {
+    date.setUTCFullYear(YYYY, MM, DD);
+    date.setUTCHours(hh, mm, ss, fff);
+
+    // apply offset
+    date.setTime(+date + offset);
+  } else {
+    date.setFullYear(YYYY, MM, DD);
+    date.setHours(hh, mm, ss, fff);
+  }
+
+  return date;
+};
+
+/**
+ * Converts a date to a UTCTime value.
+ *
+ * Note: GeneralizedTime has 4 digits for the year and is used for X.509
+ * dates past 2049. Converting to a GeneralizedTime hasn't been
+ * implemented yet.
+ *
+ * @param date the date to convert.
+ *
+ * @return the UTCTime value.
+ */
+asn1.dateToUtcTime = function(date) {
+  // TODO: validate; currently assumes proper format
+  if(typeof date === 'string') {
+    return date;
+  }
+
+  var rval = '';
+
+  // create format YYMMDDhhmmssZ
+  var format = [];
+  format.push(('' + date.getUTCFullYear()).substr(2));
+  format.push('' + (date.getUTCMonth() + 1));
+  format.push('' + date.getUTCDate());
+  format.push('' + date.getUTCHours());
+  format.push('' + date.getUTCMinutes());
+  format.push('' + date.getUTCSeconds());
+
+  // ensure 2 digits are used for each format entry
+  for(var i = 0; i < format.length; ++i) {
+    if(format[i].length < 2) {
+      rval += '0';
+    }
+    rval += format[i];
+  }
+  rval += 'Z';
+
+  return rval;
+};
+
+/**
+ * Converts a date to a GeneralizedTime value.
+ *
+ * @param date the date to convert.
+ *
+ * @return the GeneralizedTime value as a string.
+ */
+asn1.dateToGeneralizedTime = function(date) {
+  // TODO: validate; currently assumes proper format
+  if(typeof date === 'string') {
+    return date;
+  }
+
+  var rval = '';
+
+  // create format YYYYMMDDHHMMSSZ
+  var format = [];
+  format.push('' + date.getUTCFullYear());
+  format.push('' + (date.getUTCMonth() + 1));
+  format.push('' + date.getUTCDate());
+  format.push('' + date.getUTCHours());
+  format.push('' + date.getUTCMinutes());
+  format.push('' + date.getUTCSeconds());
+
+  // ensure 2 digits are used for each format entry
+  for(var i = 0; i < format.length; ++i) {
+    if(format[i].length < 2) {
+      rval += '0';
+    }
+    rval += format[i];
+  }
+  rval += 'Z';
+
+  return rval;
+};
+
+/**
+ * Converts a javascript integer to a DER-encoded byte buffer to be used
+ * as the value for an INTEGER type.
+ *
+ * @param x the integer.
+ *
+ * @return the byte buffer.
+ */
+asn1.integerToDer = function(x) {
+  var rval = forge.util.createBuffer();
+  if(x >= -0x80 && x < 0x80) {
+    return rval.putSignedInt(x, 8);
+  }
+  if(x >= -0x8000 && x < 0x8000) {
+    return rval.putSignedInt(x, 16);
+  }
+  if(x >= -0x800000 && x < 0x800000) {
+    return rval.putSignedInt(x, 24);
+  }
+  if(x >= -0x80000000 && x < 0x80000000) {
+    return rval.putSignedInt(x, 32);
+  }
+  var error = new Error('Integer too large; max is 32-bits.');
+  error.integer = x;
+  throw error;
+};
+
+/**
+ * Converts a DER-encoded byte buffer to a javascript integer. This is
+ * typically used to decode the value of an INTEGER type.
+ *
+ * @param bytes the byte buffer.
+ *
+ * @return the integer.
+ */
+asn1.derToInteger = function(bytes) {
+  // wrap in buffer if needed
+  if(typeof bytes === 'string') {
+    bytes = forge.util.createBuffer(bytes);
+  }
+
+  var n = bytes.length() * 8;
+  if(n > 32) {
+    throw new Error('Integer too large; max is 32-bits.');
+  }
+  return bytes.getSignedInt(n);
+};
+
+/**
+ * Validates that the given ASN.1 object is at least a super set of the
+ * given ASN.1 structure. Only tag classes and types are checked. An
+ * optional map may also be provided to capture ASN.1 values while the
+ * structure is checked.
+ *
+ * To capture an ASN.1 value, set an object in the validator's 'capture'
+ * parameter to the key to use in the capture map. To capture the full
+ * ASN.1 object, specify 'captureAsn1'. To capture BIT STRING bytes, including
+ * the leading unused bits counter byte, specify 'captureBitStringContents'.
+ * To capture BIT STRING bytes, without the leading unused bits counter byte,
+ * specify 'captureBitStringValue'.
+ *
+ * Objects in the validator may set a field 'optional' to true to indicate
+ * that it isn't necessary to pass validation.
+ *
+ * @param obj the ASN.1 object to validate.
+ * @param v the ASN.1 structure validator.
+ * @param capture an optional map to capture values in.
+ * @param errors an optional array for storing validation errors.
+ *
+ * @return true on success, false on failure.
+ */
+asn1.validate = function(obj, v, capture, errors) {
+  var rval = false;
+
+  // ensure tag class and type are the same if specified
+  if((obj.tagClass === v.tagClass || typeof(v.tagClass) === 'undefined') &&
+    (obj.type === v.type || typeof(v.type) === 'undefined')) {
+    // ensure constructed flag is the same if specified
+    if(obj.constructed === v.constructed ||
+      typeof(v.constructed) === 'undefined') {
+      rval = true;
+
+      // handle sub values
+      if(v.value && forge.util.isArray(v.value)) {
+        var j = 0;
+        for(var i = 0; rval && i < v.value.length; ++i) {
+          var schemaItem = v.value[i];
+          rval = !!schemaItem.optional;
+
+          // current child in the object
+          var objChild = obj.value[j];
+
+          // if there is no child left to match
+          if(!objChild) {
+            // if optional, ok (rval already true), else fail below
+            if(!schemaItem.optional) {
+              rval = false;
+              if(errors) {
+                errors.push('[' + v.name + '] ' +
+                  'Missing required element. Expected tag class "' +
+                  schemaItem.tagClass + '", type "' + schemaItem.type + '"');
+              }
+            }
+            continue;
+          }
+
+          // If schema explicitly specifies tagClass/type, do a quick structural check
+          // to avoid unnecessary recursion/side-effects when tags clearly don't match.
+          var schemaHasTag = (typeof schemaItem.tagClass !== 'undefined' &&
+            typeof schemaItem.type !== 'undefined');
+
+          if(schemaHasTag &&
+            (objChild.tagClass !== schemaItem.tagClass || objChild.type !== schemaItem.type)) {
+            // Tags do not match.
+            if(schemaItem.optional) {
+              // Skip this schema element (don't consume objChild; don't call recursive validate).
+              rval = true;
+              continue;
+            } else {
+              // Required schema item mismatched - fail.
+              rval = false;
+              if(errors) {
+                errors.push('[' + v.name + '] ' +
+                  'Tag mismatch. Expected (' +
+                  schemaItem.tagClass + ',' + schemaItem.type + '), got (' +
+                  objChild.tagClass + ',' + objChild.type + ')');
+              }
+              break;
+            }
+          }
+
+          // Tags are compatible (or schema did not declare tags) - dive into recursive validate.
+          var childRval = asn1.validate(objChild, schemaItem, capture, errors);
+          if(childRval) {
+            // consume this child
+            ++j;
+            rval = true;
+          } else if(schemaItem.optional) {
+            // validation failed but element is optional => skip schema item (don't consume child)
+            rval = true;
+          } else {
+            // required item failed
+            rval = false;
+            // errors should already be populated by recursive call; keep failing
+            break;
+          }
+        }
+      }
+
+      if(rval && capture) {
+        if(v.capture) {
+          capture[v.capture] = obj.value;
+        }
+        if(v.captureAsn1) {
+          capture[v.captureAsn1] = obj;
+        }
+        if(v.captureBitStringContents && 'bitStringContents' in obj) {
+          capture[v.captureBitStringContents] = obj.bitStringContents;
+        }
+        if(v.captureBitStringValue && 'bitStringContents' in obj) {
+          var value;
+          if(obj.bitStringContents.length < 2) {
+            capture[v.captureBitStringValue] = '';
+          } else {
+            // FIXME: support unused bits with data shifting
+            var unused = obj.bitStringContents.charCodeAt(0);
+            if(unused !== 0) {
+              throw new Error(
+                'captureBitStringValue only supported for zero unused bits');
+            }
+            capture[v.captureBitStringValue] = obj.bitStringContents.slice(1);
+          }
+        }
+      }
+    } else if(errors) {
+      errors.push(
+        '[' + v.name + '] ' +
+        'Expected constructed "' + v.constructed + '", got "' +
+        obj.constructed + '"');
+    }
+  } else if(errors) {
+    if(obj.tagClass !== v.tagClass) {
+      errors.push(
+        '[' + v.name + '] ' +
+        'Expected tag class "' + v.tagClass + '", got "' +
+        obj.tagClass + '"');
+    }
+    if(obj.type !== v.type) {
+      errors.push(
+        '[' + v.name + '] ' +
+        'Expected type "' + v.type + '", got "' +
+        obj.type + '"');
+    }
+  }
+  return rval;
+};
+
+// regex for testing for non-latin characters
+var _nonLatinRegex = /[^\\u0000-\\u00ff]/;
+
+/**
+ * Pretty prints an ASN.1 object to a string.
+ *
+ * @param obj the object to write out.
+ * @param level the level in the tree.
+ * @param indentation the indentation to use.
+ *
+ * @return the string.
+ */
+asn1.prettyPrint = function(obj, level, indentation) {
+  var rval = '';
+
+  // set default level and indentation
+  level = level || 0;
+  indentation = indentation || 2;
+
+  // start new line for deep levels
+  if(level > 0) {
+    rval += '\n';
+  }
+
+  // create indent
+  var indent = '';
+  for(var i = 0; i < level * indentation; ++i) {
+    indent += ' ';
+  }
+
+  // print class:type
+  rval += indent + 'Tag: ';
+  switch(obj.tagClass) {
+  case asn1.Class.UNIVERSAL:
+    rval += 'Universal:';
+    break;
+  case asn1.Class.APPLICATION:
+    rval += 'Application:';
+    break;
+  case asn1.Class.CONTEXT_SPECIFIC:
+    rval += 'Context-Specific:';
+    break;
+  case asn1.Class.PRIVATE:
+    rval += 'Private:';
+    break;
+  }
+
+  if(obj.tagClass === asn1.Class.UNIVERSAL) {
+    rval += obj.type;
+
+    // known types
+    switch(obj.type) {
+    case asn1.Type.NONE:
+      rval += ' (None)';
+      break;
+    case asn1.Type.BOOLEAN:
+      rval += ' (Boolean)';
+      break;
+    case asn1.Type.INTEGER:
+      rval += ' (Integer)';
+      break;
+    case asn1.Type.BITSTRING:
+      rval += ' (Bit string)';
+      break;
+    case asn1.Type.OCTETSTRING:
+      rval += ' (Octet string)';
+      break;
+    case asn1.Type.NULL:
+      rval += ' (Null)';
+      break;
+    case asn1.Type.OID:
+      rval += ' (Object Identifier)';
+      break;
+    case asn1.Type.ODESC:
+      rval += ' (Object Descriptor)';
+      break;
+    case asn1.Type.EXTERNAL:
+      rval += ' (External or Instance of)';
+      break;
+    case asn1.Type.REAL:
+      rval += ' (Real)';
+      break;
+    case asn1.Type.ENUMERATED:
+      rval += ' (Enumerated)';
+      break;
+    case asn1.Type.EMBEDDED:
+      rval += ' (Embedded PDV)';
+      break;
+    case asn1.Type.UTF8:
+      rval += ' (UTF8)';
+      break;
+    case asn1.Type.ROID:
+      rval += ' (Relative Object Identifier)';
+      break;
+    case asn1.Type.SEQUENCE:
+      rval += ' (Sequence)';
+      break;
+    case asn1.Type.SET:
+      rval += ' (Set)';
+      break;
+    case asn1.Type.PRINTABLESTRING:
+      rval += ' (Printable String)';
+      break;
+    case asn1.Type.IA5String:
+      rval += ' (IA5String (ASCII))';
+      break;
+    case asn1.Type.UTCTIME:
+      rval += ' (UTC time)';
+      break;
+    case asn1.Type.GENERALIZEDTIME:
+      rval += ' (Generalized time)';
+      break;
+    case asn1.Type.BMPSTRING:
+      rval += ' (BMP String)';
+      break;
+    }
+  } else {
+    rval += obj.type;
+  }
+
+  rval += '\n';
+  rval += indent + 'Constructed: ' + obj.constructed + '\n';
+
+  if(obj.composed) {
+    var subvalues = 0;
+    var sub = '';
+    for(var i = 0; i < obj.value.length; ++i) {
+      if(obj.value[i] !== undefined) {
+        subvalues += 1;
+        sub += asn1.prettyPrint(obj.value[i], level + 1, indentation);
+        if((i + 1) < obj.value.length) {
+          sub += ',';
+        }
+      }
+    }
+    rval += indent + 'Sub values: ' + subvalues + sub;
+  } else {
+    rval += indent + 'Value: ';
+    if(obj.type === asn1.Type.OID) {
+      var oid = asn1.derToOid(obj.value);
+      rval += oid;
+      if(forge.pki && forge.pki.oids) {
+        if(oid in forge.pki.oids) {
+          rval += ' (' + forge.pki.oids[oid] + ') ';
+        }
+      }
+    }
+    if(obj.type === asn1.Type.INTEGER) {
+      try {
+        rval += asn1.derToInteger(obj.value);
+      } catch(ex) {
+        rval += '0x' + forge.util.bytesToHex(obj.value);
+      }
+    } else if(obj.type === asn1.Type.BITSTRING) {
+      // TODO: shift bits as needed to display without padding
+      if(obj.value.length > 1) {
+        // remove unused bits field
+        rval += '0x' + forge.util.bytesToHex(obj.value.slice(1));
+      } else {
+        rval += '(none)';
+      }
+      // show unused bit count
+      if(obj.value.length > 0) {
+        var unused = obj.value.charCodeAt(0);
+        if(unused == 1) {
+          rval += ' (1 unused bit shown)';
+        } else if(unused > 1) {
+          rval += ' (' + unused + ' unused bits shown)';
+        }
+      }
+    } else if(obj.type === asn1.Type.OCTETSTRING) {
+      if(!_nonLatinRegex.test(obj.value)) {
+        rval += '(' + obj.value + ') ';
+      }
+      rval += '0x' + forge.util.bytesToHex(obj.value);
+    } else if(obj.type === asn1.Type.UTF8) {
+      try {
+        rval += forge.util.decodeUtf8(obj.value);
+      } catch(e) {
+        if(e.message === 'URI malformed') {
+          rval +=
+            '0x' + forge.util.bytesToHex(obj.value) + ' (malformed UTF8)';
+        } else {
+          throw e;
+        }
+      }
+    } else if(obj.type === asn1.Type.PRINTABLESTRING ||
+      obj.type === asn1.Type.IA5String) {
+      rval += obj.value;
+    } else if(_nonLatinRegex.test(obj.value)) {
+      rval += '0x' + forge.util.bytesToHex(obj.value);
+    } else if(obj.value.length === 0) {
+      rval += '[null]';
+    } else {
+      rval += obj.value;
+    }
+  }
+
+  return rval;
+};
Index: frontend/node_modules/node-forge/lib/baseN.js
===================================================================
--- frontend/node_modules/node-forge/lib/baseN.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/baseN.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,186 @@
+/**
+ * Base-N/Base-X encoding/decoding functions.
+ *
+ * Original implementation from base-x:
+ * https://github.com/cryptocoinjs/base-x
+ *
+ * Which is MIT licensed:
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright base-x contributors (c) 2016
+ *
+ * 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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+var api = {};
+module.exports = api;
+
+// baseN alphabet indexes
+var _reverseAlphabets = {};
+
+/**
+ * BaseN-encodes a Uint8Array using the given alphabet.
+ *
+ * @param input the Uint8Array to encode.
+ * @param maxline the maximum number of encoded characters per line to use,
+ *          defaults to none.
+ *
+ * @return the baseN-encoded output string.
+ */
+api.encode = function(input, alphabet, maxline) {
+  if(typeof alphabet !== 'string') {
+    throw new TypeError('"alphabet" must be a string.');
+  }
+  if(maxline !== undefined && typeof maxline !== 'number') {
+    throw new TypeError('"maxline" must be a number.');
+  }
+
+  var output = '';
+
+  if(!(input instanceof Uint8Array)) {
+    // assume forge byte buffer
+    output = _encodeWithByteBuffer(input, alphabet);
+  } else {
+    var i = 0;
+    var base = alphabet.length;
+    var first = alphabet.charAt(0);
+    var digits = [0];
+    for(i = 0; i < input.length; ++i) {
+      for(var j = 0, carry = input[i]; j < digits.length; ++j) {
+        carry += digits[j] << 8;
+        digits[j] = carry % base;
+        carry = (carry / base) | 0;
+      }
+
+      while(carry > 0) {
+        digits.push(carry % base);
+        carry = (carry / base) | 0;
+      }
+    }
+
+    // deal with leading zeros
+    for(i = 0; input[i] === 0 && i < input.length - 1; ++i) {
+      output += first;
+    }
+    // convert digits to a string
+    for(i = digits.length - 1; i >= 0; --i) {
+      output += alphabet[digits[i]];
+    }
+  }
+
+  if(maxline) {
+    var regex = new RegExp('.{1,' + maxline + '}', 'g');
+    output = output.match(regex).join('\r\n');
+  }
+
+  return output;
+};
+
+/**
+ * Decodes a baseN-encoded (using the given alphabet) string to a
+ * Uint8Array.
+ *
+ * @param input the baseN-encoded input string.
+ *
+ * @return the Uint8Array.
+ */
+api.decode = function(input, alphabet) {
+  if(typeof input !== 'string') {
+    throw new TypeError('"input" must be a string.');
+  }
+  if(typeof alphabet !== 'string') {
+    throw new TypeError('"alphabet" must be a string.');
+  }
+
+  var table = _reverseAlphabets[alphabet];
+  if(!table) {
+    // compute reverse alphabet
+    table = _reverseAlphabets[alphabet] = [];
+    for(var i = 0; i < alphabet.length; ++i) {
+      table[alphabet.charCodeAt(i)] = i;
+    }
+  }
+
+  // remove whitespace characters
+  input = input.replace(/\s/g, '');
+
+  var base = alphabet.length;
+  var first = alphabet.charAt(0);
+  var bytes = [0];
+  for(var i = 0; i < input.length; i++) {
+    var value = table[input.charCodeAt(i)];
+    if(value === undefined) {
+      return;
+    }
+
+    for(var j = 0, carry = value; j < bytes.length; ++j) {
+      carry += bytes[j] * base;
+      bytes[j] = carry & 0xff;
+      carry >>= 8;
+    }
+
+    while(carry > 0) {
+      bytes.push(carry & 0xff);
+      carry >>= 8;
+    }
+  }
+
+  // deal with leading zeros
+  for(var k = 0; input[k] === first && k < input.length - 1; ++k) {
+    bytes.push(0);
+  }
+
+  if(typeof Buffer !== 'undefined') {
+    return Buffer.from(bytes.reverse());
+  }
+
+  return new Uint8Array(bytes.reverse());
+};
+
+function _encodeWithByteBuffer(input, alphabet) {
+  var i = 0;
+  var base = alphabet.length;
+  var first = alphabet.charAt(0);
+  var digits = [0];
+  for(i = 0; i < input.length(); ++i) {
+    for(var j = 0, carry = input.at(i); j < digits.length; ++j) {
+      carry += digits[j] << 8;
+      digits[j] = carry % base;
+      carry = (carry / base) | 0;
+    }
+
+    while(carry > 0) {
+      digits.push(carry % base);
+      carry = (carry / base) | 0;
+    }
+  }
+
+  var output = '';
+
+  // deal with leading zeros
+  for(i = 0; input.at(i) === 0 && i < input.length() - 1; ++i) {
+    output += first;
+  }
+  // convert digits to a string
+  for(i = digits.length - 1; i >= 0; --i) {
+    output += alphabet[digits[i]];
+  }
+
+  return output;
+}
Index: frontend/node_modules/node-forge/lib/cipher.js
===================================================================
--- frontend/node_modules/node-forge/lib/cipher.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/cipher.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,230 @@
+/**
+ * Cipher base API.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./util');
+
+module.exports = forge.cipher = forge.cipher || {};
+
+// registered algorithms
+forge.cipher.algorithms = forge.cipher.algorithms || {};
+
+/**
+ * Creates a cipher object that can be used to encrypt data using the given
+ * algorithm and key. The algorithm may be provided as a string value for a
+ * previously registered algorithm or it may be given as a cipher algorithm
+ * API object.
+ *
+ * @param algorithm the algorithm to use, either a string or an algorithm API
+ *          object.
+ * @param key the key to use, as a binary-encoded string of bytes or a
+ *          byte buffer.
+ *
+ * @return the cipher.
+ */
+forge.cipher.createCipher = function(algorithm, key) {
+  var api = algorithm;
+  if(typeof api === 'string') {
+    api = forge.cipher.getAlgorithm(api);
+    if(api) {
+      api = api();
+    }
+  }
+  if(!api) {
+    throw new Error('Unsupported algorithm: ' + algorithm);
+  }
+
+  // assume block cipher
+  return new forge.cipher.BlockCipher({
+    algorithm: api,
+    key: key,
+    decrypt: false
+  });
+};
+
+/**
+ * Creates a decipher object that can be used to decrypt data using the given
+ * algorithm and key. The algorithm may be provided as a string value for a
+ * previously registered algorithm or it may be given as a cipher algorithm
+ * API object.
+ *
+ * @param algorithm the algorithm to use, either a string or an algorithm API
+ *          object.
+ * @param key the key to use, as a binary-encoded string of bytes or a
+ *          byte buffer.
+ *
+ * @return the cipher.
+ */
+forge.cipher.createDecipher = function(algorithm, key) {
+  var api = algorithm;
+  if(typeof api === 'string') {
+    api = forge.cipher.getAlgorithm(api);
+    if(api) {
+      api = api();
+    }
+  }
+  if(!api) {
+    throw new Error('Unsupported algorithm: ' + algorithm);
+  }
+
+  // assume block cipher
+  return new forge.cipher.BlockCipher({
+    algorithm: api,
+    key: key,
+    decrypt: true
+  });
+};
+
+/**
+ * Registers an algorithm by name. If the name was already registered, the
+ * algorithm API object will be overwritten.
+ *
+ * @param name the name of the algorithm.
+ * @param algorithm the algorithm API object.
+ */
+forge.cipher.registerAlgorithm = function(name, algorithm) {
+  name = name.toUpperCase();
+  forge.cipher.algorithms[name] = algorithm;
+};
+
+/**
+ * Gets a registered algorithm by name.
+ *
+ * @param name the name of the algorithm.
+ *
+ * @return the algorithm, if found, null if not.
+ */
+forge.cipher.getAlgorithm = function(name) {
+  name = name.toUpperCase();
+  if(name in forge.cipher.algorithms) {
+    return forge.cipher.algorithms[name];
+  }
+  return null;
+};
+
+var BlockCipher = forge.cipher.BlockCipher = function(options) {
+  this.algorithm = options.algorithm;
+  this.mode = this.algorithm.mode;
+  this.blockSize = this.mode.blockSize;
+  this._finish = false;
+  this._input = null;
+  this.output = null;
+  this._op = options.decrypt ? this.mode.decrypt : this.mode.encrypt;
+  this._decrypt = options.decrypt;
+  this.algorithm.initialize(options);
+};
+
+/**
+ * Starts or restarts the encryption or decryption process, whichever
+ * was previously configured.
+ *
+ * For non-GCM mode, the IV may be a binary-encoded string of bytes, an array
+ * of bytes, a byte buffer, or an array of 32-bit integers. If the IV is in
+ * bytes, then it must be Nb (16) bytes in length. If the IV is given in as
+ * 32-bit integers, then it must be 4 integers long.
+ *
+ * Note: an IV is not required or used in ECB mode.
+ *
+ * For GCM-mode, the IV must be given as a binary-encoded string of bytes or
+ * a byte buffer. The number of bytes should be 12 (96 bits) as recommended
+ * by NIST SP-800-38D but another length may be given.
+ *
+ * @param options the options to use:
+ *          iv the initialization vector to use as a binary-encoded string of
+ *            bytes, null to reuse the last ciphered block from a previous
+ *            update() (this "residue" method is for legacy support only).
+ *          additionalData additional authentication data as a binary-encoded
+ *            string of bytes, for 'GCM' mode, (default: none).
+ *          tagLength desired length of authentication tag, in bits, for
+ *            'GCM' mode (0-128, default: 128).
+ *          tag the authentication tag to check if decrypting, as a
+ *             binary-encoded string of bytes.
+ *          output the output the buffer to write to, null to create one.
+ */
+BlockCipher.prototype.start = function(options) {
+  options = options || {};
+  var opts = {};
+  for(var key in options) {
+    opts[key] = options[key];
+  }
+  opts.decrypt = this._decrypt;
+  this._finish = false;
+  this._input = forge.util.createBuffer();
+  this.output = options.output || forge.util.createBuffer();
+  this.mode.start(opts);
+};
+
+/**
+ * Updates the next block according to the cipher mode.
+ *
+ * @param input the buffer to read from.
+ */
+BlockCipher.prototype.update = function(input) {
+  if(input) {
+    // input given, so empty it into the input buffer
+    this._input.putBuffer(input);
+  }
+
+  // do cipher operation until it needs more input and not finished
+  while(!this._op.call(this.mode, this._input, this.output, this._finish) &&
+    !this._finish) {}
+
+  // free consumed memory from input buffer
+  this._input.compact();
+};
+
+/**
+ * Finishes encrypting or decrypting.
+ *
+ * @param pad a padding function to use in CBC mode, null for default,
+ *          signature(blockSize, buffer, decrypt).
+ *
+ * @return true if successful, false on error.
+ */
+BlockCipher.prototype.finish = function(pad) {
+  // backwards-compatibility w/deprecated padding API
+  // Note: will overwrite padding functions even after another start() call
+  if(pad && (this.mode.name === 'ECB' || this.mode.name === 'CBC')) {
+    this.mode.pad = function(input) {
+      return pad(this.blockSize, input, false);
+    };
+    this.mode.unpad = function(output) {
+      return pad(this.blockSize, output, true);
+    };
+  }
+
+  // build options for padding and afterFinish functions
+  var options = {};
+  options.decrypt = this._decrypt;
+
+  // get # of bytes that won't fill a block
+  options.overflow = this._input.length() % this.blockSize;
+
+  if(!this._decrypt && this.mode.pad) {
+    if(!this.mode.pad(this._input, options)) {
+      return false;
+    }
+  }
+
+  // do final update
+  this._finish = true;
+  this.update();
+
+  if(this._decrypt && this.mode.unpad) {
+    if(!this.mode.unpad(this.output, options)) {
+      return false;
+    }
+  }
+
+  if(this.mode.afterFinish) {
+    if(!this.mode.afterFinish(this.output, options)) {
+      return false;
+    }
+  }
+
+  return true;
+};
Index: frontend/node_modules/node-forge/lib/cipherModes.js
===================================================================
--- frontend/node_modules/node-forge/lib/cipherModes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/cipherModes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,999 @@
+/**
+ * Supported cipher modes.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./util');
+
+forge.cipher = forge.cipher || {};
+
+// supported cipher modes
+var modes = module.exports = forge.cipher.modes = forge.cipher.modes || {};
+
+/** Electronic codebook (ECB) (Don't use this; it's not secure) **/
+
+modes.ecb = function(options) {
+  options = options || {};
+  this.name = 'ECB';
+  this.cipher = options.cipher;
+  this.blockSize = options.blockSize || 16;
+  this._ints = this.blockSize / 4;
+  this._inBlock = new Array(this._ints);
+  this._outBlock = new Array(this._ints);
+};
+
+modes.ecb.prototype.start = function(options) {};
+
+modes.ecb.prototype.encrypt = function(input, output, finish) {
+  // not enough input to encrypt
+  if(input.length() < this.blockSize && !(finish && input.length() > 0)) {
+    return true;
+  }
+
+  // get next block
+  for(var i = 0; i < this._ints; ++i) {
+    this._inBlock[i] = input.getInt32();
+  }
+
+  // encrypt block
+  this.cipher.encrypt(this._inBlock, this._outBlock);
+
+  // write output
+  for(var i = 0; i < this._ints; ++i) {
+    output.putInt32(this._outBlock[i]);
+  }
+};
+
+modes.ecb.prototype.decrypt = function(input, output, finish) {
+  // not enough input to decrypt
+  if(input.length() < this.blockSize && !(finish && input.length() > 0)) {
+    return true;
+  }
+
+  // get next block
+  for(var i = 0; i < this._ints; ++i) {
+    this._inBlock[i] = input.getInt32();
+  }
+
+  // decrypt block
+  this.cipher.decrypt(this._inBlock, this._outBlock);
+
+  // write output
+  for(var i = 0; i < this._ints; ++i) {
+    output.putInt32(this._outBlock[i]);
+  }
+};
+
+modes.ecb.prototype.pad = function(input, options) {
+  // add PKCS#7 padding to block (each pad byte is the
+  // value of the number of pad bytes)
+  var padding = (input.length() === this.blockSize ?
+    this.blockSize : (this.blockSize - input.length()));
+  input.fillWithByte(padding, padding);
+  return true;
+};
+
+modes.ecb.prototype.unpad = function(output, options) {
+  // check for error: input data not a multiple of blockSize
+  if(options.overflow > 0) {
+    return false;
+  }
+
+  // ensure padding byte count is valid
+  var len = output.length();
+  var count = output.at(len - 1);
+  if(count > (this.blockSize << 2)) {
+    return false;
+  }
+
+  // trim off padding bytes
+  output.truncate(count);
+  return true;
+};
+
+/** Cipher-block Chaining (CBC) **/
+
+modes.cbc = function(options) {
+  options = options || {};
+  this.name = 'CBC';
+  this.cipher = options.cipher;
+  this.blockSize = options.blockSize || 16;
+  this._ints = this.blockSize / 4;
+  this._inBlock = new Array(this._ints);
+  this._outBlock = new Array(this._ints);
+};
+
+modes.cbc.prototype.start = function(options) {
+  // Note: legacy support for using IV residue (has security flaws)
+  // if IV is null, reuse block from previous processing
+  if(options.iv === null) {
+    // must have a previous block
+    if(!this._prev) {
+      throw new Error('Invalid IV parameter.');
+    }
+    this._iv = this._prev.slice(0);
+  } else if(!('iv' in options)) {
+    throw new Error('Invalid IV parameter.');
+  } else {
+    // save IV as "previous" block
+    this._iv = transformIV(options.iv, this.blockSize);
+    this._prev = this._iv.slice(0);
+  }
+};
+
+modes.cbc.prototype.encrypt = function(input, output, finish) {
+  // not enough input to encrypt
+  if(input.length() < this.blockSize && !(finish && input.length() > 0)) {
+    return true;
+  }
+
+  // get next block
+  // CBC XOR's IV (or previous block) with plaintext
+  for(var i = 0; i < this._ints; ++i) {
+    this._inBlock[i] = this._prev[i] ^ input.getInt32();
+  }
+
+  // encrypt block
+  this.cipher.encrypt(this._inBlock, this._outBlock);
+
+  // write output, save previous block
+  for(var i = 0; i < this._ints; ++i) {
+    output.putInt32(this._outBlock[i]);
+  }
+  this._prev = this._outBlock;
+};
+
+modes.cbc.prototype.decrypt = function(input, output, finish) {
+  // not enough input to decrypt
+  if(input.length() < this.blockSize && !(finish && input.length() > 0)) {
+    return true;
+  }
+
+  // get next block
+  for(var i = 0; i < this._ints; ++i) {
+    this._inBlock[i] = input.getInt32();
+  }
+
+  // decrypt block
+  this.cipher.decrypt(this._inBlock, this._outBlock);
+
+  // write output, save previous ciphered block
+  // CBC XOR's IV (or previous block) with ciphertext
+  for(var i = 0; i < this._ints; ++i) {
+    output.putInt32(this._prev[i] ^ this._outBlock[i]);
+  }
+  this._prev = this._inBlock.slice(0);
+};
+
+modes.cbc.prototype.pad = function(input, options) {
+  // add PKCS#7 padding to block (each pad byte is the
+  // value of the number of pad bytes)
+  var padding = (input.length() === this.blockSize ?
+    this.blockSize : (this.blockSize - input.length()));
+  input.fillWithByte(padding, padding);
+  return true;
+};
+
+modes.cbc.prototype.unpad = function(output, options) {
+  // check for error: input data not a multiple of blockSize
+  if(options.overflow > 0) {
+    return false;
+  }
+
+  // ensure padding byte count is valid
+  var len = output.length();
+  var count = output.at(len - 1);
+  if(count > (this.blockSize << 2)) {
+    return false;
+  }
+
+  // trim off padding bytes
+  output.truncate(count);
+  return true;
+};
+
+/** Cipher feedback (CFB) **/
+
+modes.cfb = function(options) {
+  options = options || {};
+  this.name = 'CFB';
+  this.cipher = options.cipher;
+  this.blockSize = options.blockSize || 16;
+  this._ints = this.blockSize / 4;
+  this._inBlock = null;
+  this._outBlock = new Array(this._ints);
+  this._partialBlock = new Array(this._ints);
+  this._partialOutput = forge.util.createBuffer();
+  this._partialBytes = 0;
+};
+
+modes.cfb.prototype.start = function(options) {
+  if(!('iv' in options)) {
+    throw new Error('Invalid IV parameter.');
+  }
+  // use IV as first input
+  this._iv = transformIV(options.iv, this.blockSize);
+  this._inBlock = this._iv.slice(0);
+  this._partialBytes = 0;
+};
+
+modes.cfb.prototype.encrypt = function(input, output, finish) {
+  // not enough input to encrypt
+  var inputLength = input.length();
+  if(inputLength === 0) {
+    return true;
+  }
+
+  // encrypt block
+  this.cipher.encrypt(this._inBlock, this._outBlock);
+
+  // handle full block
+  if(this._partialBytes === 0 && inputLength >= this.blockSize) {
+    // XOR input with output, write input as output
+    for(var i = 0; i < this._ints; ++i) {
+      this._inBlock[i] = input.getInt32() ^ this._outBlock[i];
+      output.putInt32(this._inBlock[i]);
+    }
+    return;
+  }
+
+  // handle partial block
+  var partialBytes = (this.blockSize - inputLength) % this.blockSize;
+  if(partialBytes > 0) {
+    partialBytes = this.blockSize - partialBytes;
+  }
+
+  // XOR input with output, write input as partial output
+  this._partialOutput.clear();
+  for(var i = 0; i < this._ints; ++i) {
+    this._partialBlock[i] = input.getInt32() ^ this._outBlock[i];
+    this._partialOutput.putInt32(this._partialBlock[i]);
+  }
+
+  if(partialBytes > 0) {
+    // block still incomplete, restore input buffer
+    input.read -= this.blockSize;
+  } else {
+    // block complete, update input block
+    for(var i = 0; i < this._ints; ++i) {
+      this._inBlock[i] = this._partialBlock[i];
+    }
+  }
+
+  // skip any previous partial bytes
+  if(this._partialBytes > 0) {
+    this._partialOutput.getBytes(this._partialBytes);
+  }
+
+  if(partialBytes > 0 && !finish) {
+    output.putBytes(this._partialOutput.getBytes(
+      partialBytes - this._partialBytes));
+    this._partialBytes = partialBytes;
+    return true;
+  }
+
+  output.putBytes(this._partialOutput.getBytes(
+    inputLength - this._partialBytes));
+  this._partialBytes = 0;
+};
+
+modes.cfb.prototype.decrypt = function(input, output, finish) {
+  // not enough input to decrypt
+  var inputLength = input.length();
+  if(inputLength === 0) {
+    return true;
+  }
+
+  // encrypt block (CFB always uses encryption mode)
+  this.cipher.encrypt(this._inBlock, this._outBlock);
+
+  // handle full block
+  if(this._partialBytes === 0 && inputLength >= this.blockSize) {
+    // XOR input with output, write input as output
+    for(var i = 0; i < this._ints; ++i) {
+      this._inBlock[i] = input.getInt32();
+      output.putInt32(this._inBlock[i] ^ this._outBlock[i]);
+    }
+    return;
+  }
+
+  // handle partial block
+  var partialBytes = (this.blockSize - inputLength) % this.blockSize;
+  if(partialBytes > 0) {
+    partialBytes = this.blockSize - partialBytes;
+  }
+
+  // XOR input with output, write input as partial output
+  this._partialOutput.clear();
+  for(var i = 0; i < this._ints; ++i) {
+    this._partialBlock[i] = input.getInt32();
+    this._partialOutput.putInt32(this._partialBlock[i] ^ this._outBlock[i]);
+  }
+
+  if(partialBytes > 0) {
+    // block still incomplete, restore input buffer
+    input.read -= this.blockSize;
+  } else {
+    // block complete, update input block
+    for(var i = 0; i < this._ints; ++i) {
+      this._inBlock[i] = this._partialBlock[i];
+    }
+  }
+
+  // skip any previous partial bytes
+  if(this._partialBytes > 0) {
+    this._partialOutput.getBytes(this._partialBytes);
+  }
+
+  if(partialBytes > 0 && !finish) {
+    output.putBytes(this._partialOutput.getBytes(
+      partialBytes - this._partialBytes));
+    this._partialBytes = partialBytes;
+    return true;
+  }
+
+  output.putBytes(this._partialOutput.getBytes(
+    inputLength - this._partialBytes));
+  this._partialBytes = 0;
+};
+
+/** Output feedback (OFB) **/
+
+modes.ofb = function(options) {
+  options = options || {};
+  this.name = 'OFB';
+  this.cipher = options.cipher;
+  this.blockSize = options.blockSize || 16;
+  this._ints = this.blockSize / 4;
+  this._inBlock = null;
+  this._outBlock = new Array(this._ints);
+  this._partialOutput = forge.util.createBuffer();
+  this._partialBytes = 0;
+};
+
+modes.ofb.prototype.start = function(options) {
+  if(!('iv' in options)) {
+    throw new Error('Invalid IV parameter.');
+  }
+  // use IV as first input
+  this._iv = transformIV(options.iv, this.blockSize);
+  this._inBlock = this._iv.slice(0);
+  this._partialBytes = 0;
+};
+
+modes.ofb.prototype.encrypt = function(input, output, finish) {
+  // not enough input to encrypt
+  var inputLength = input.length();
+  if(input.length() === 0) {
+    return true;
+  }
+
+  // encrypt block (OFB always uses encryption mode)
+  this.cipher.encrypt(this._inBlock, this._outBlock);
+
+  // handle full block
+  if(this._partialBytes === 0 && inputLength >= this.blockSize) {
+    // XOR input with output and update next input
+    for(var i = 0; i < this._ints; ++i) {
+      output.putInt32(input.getInt32() ^ this._outBlock[i]);
+      this._inBlock[i] = this._outBlock[i];
+    }
+    return;
+  }
+
+  // handle partial block
+  var partialBytes = (this.blockSize - inputLength) % this.blockSize;
+  if(partialBytes > 0) {
+    partialBytes = this.blockSize - partialBytes;
+  }
+
+  // XOR input with output
+  this._partialOutput.clear();
+  for(var i = 0; i < this._ints; ++i) {
+    this._partialOutput.putInt32(input.getInt32() ^ this._outBlock[i]);
+  }
+
+  if(partialBytes > 0) {
+    // block still incomplete, restore input buffer
+    input.read -= this.blockSize;
+  } else {
+    // block complete, update input block
+    for(var i = 0; i < this._ints; ++i) {
+      this._inBlock[i] = this._outBlock[i];
+    }
+  }
+
+  // skip any previous partial bytes
+  if(this._partialBytes > 0) {
+    this._partialOutput.getBytes(this._partialBytes);
+  }
+
+  if(partialBytes > 0 && !finish) {
+    output.putBytes(this._partialOutput.getBytes(
+      partialBytes - this._partialBytes));
+    this._partialBytes = partialBytes;
+    return true;
+  }
+
+  output.putBytes(this._partialOutput.getBytes(
+    inputLength - this._partialBytes));
+  this._partialBytes = 0;
+};
+
+modes.ofb.prototype.decrypt = modes.ofb.prototype.encrypt;
+
+/** Counter (CTR) **/
+
+modes.ctr = function(options) {
+  options = options || {};
+  this.name = 'CTR';
+  this.cipher = options.cipher;
+  this.blockSize = options.blockSize || 16;
+  this._ints = this.blockSize / 4;
+  this._inBlock = null;
+  this._outBlock = new Array(this._ints);
+  this._partialOutput = forge.util.createBuffer();
+  this._partialBytes = 0;
+};
+
+modes.ctr.prototype.start = function(options) {
+  if(!('iv' in options)) {
+    throw new Error('Invalid IV parameter.');
+  }
+  // use IV as first input
+  this._iv = transformIV(options.iv, this.blockSize);
+  this._inBlock = this._iv.slice(0);
+  this._partialBytes = 0;
+};
+
+modes.ctr.prototype.encrypt = function(input, output, finish) {
+  // not enough input to encrypt
+  var inputLength = input.length();
+  if(inputLength === 0) {
+    return true;
+  }
+
+  // encrypt block (CTR always uses encryption mode)
+  this.cipher.encrypt(this._inBlock, this._outBlock);
+
+  // handle full block
+  if(this._partialBytes === 0 && inputLength >= this.blockSize) {
+    // XOR input with output
+    for(var i = 0; i < this._ints; ++i) {
+      output.putInt32(input.getInt32() ^ this._outBlock[i]);
+    }
+  } else {
+    // handle partial block
+    var partialBytes = (this.blockSize - inputLength) % this.blockSize;
+    if(partialBytes > 0) {
+      partialBytes = this.blockSize - partialBytes;
+    }
+
+    // XOR input with output
+    this._partialOutput.clear();
+    for(var i = 0; i < this._ints; ++i) {
+      this._partialOutput.putInt32(input.getInt32() ^ this._outBlock[i]);
+    }
+
+    if(partialBytes > 0) {
+      // block still incomplete, restore input buffer
+      input.read -= this.blockSize;
+    }
+
+    // skip any previous partial bytes
+    if(this._partialBytes > 0) {
+      this._partialOutput.getBytes(this._partialBytes);
+    }
+
+    if(partialBytes > 0 && !finish) {
+      output.putBytes(this._partialOutput.getBytes(
+        partialBytes - this._partialBytes));
+      this._partialBytes = partialBytes;
+      return true;
+    }
+
+    output.putBytes(this._partialOutput.getBytes(
+      inputLength - this._partialBytes));
+    this._partialBytes = 0;
+  }
+
+  // block complete, increment counter (input block)
+  inc32(this._inBlock);
+};
+
+modes.ctr.prototype.decrypt = modes.ctr.prototype.encrypt;
+
+/** Galois/Counter Mode (GCM) **/
+
+modes.gcm = function(options) {
+  options = options || {};
+  this.name = 'GCM';
+  this.cipher = options.cipher;
+  this.blockSize = options.blockSize || 16;
+  this._ints = this.blockSize / 4;
+  this._inBlock = new Array(this._ints);
+  this._outBlock = new Array(this._ints);
+  this._partialOutput = forge.util.createBuffer();
+  this._partialBytes = 0;
+
+  // R is actually this value concatenated with 120 more zero bits, but
+  // we only XOR against R so the other zeros have no effect -- we just
+  // apply this value to the first integer in a block
+  this._R = 0xE1000000;
+};
+
+modes.gcm.prototype.start = function(options) {
+  if(!('iv' in options)) {
+    throw new Error('Invalid IV parameter.');
+  }
+  // ensure IV is a byte buffer
+  var iv = forge.util.createBuffer(options.iv);
+
+  // no ciphered data processed yet
+  this._cipherLength = 0;
+
+  // default additional data is none
+  var additionalData;
+  if('additionalData' in options) {
+    additionalData = forge.util.createBuffer(options.additionalData);
+  } else {
+    additionalData = forge.util.createBuffer();
+  }
+
+  // default tag length is 128 bits
+  if('tagLength' in options) {
+    this._tagLength = options.tagLength;
+  } else {
+    this._tagLength = 128;
+  }
+
+  // if tag is given, ensure tag matches tag length
+  this._tag = null;
+  if(options.decrypt) {
+    // save tag to check later
+    this._tag = forge.util.createBuffer(options.tag).getBytes();
+    if(this._tag.length !== (this._tagLength / 8)) {
+      throw new Error('Authentication tag does not match tag length.');
+    }
+  }
+
+  // create tmp storage for hash calculation
+  this._hashBlock = new Array(this._ints);
+
+  // no tag generated yet
+  this.tag = null;
+
+  // generate hash subkey
+  // (apply block cipher to "zero" block)
+  this._hashSubkey = new Array(this._ints);
+  this.cipher.encrypt([0, 0, 0, 0], this._hashSubkey);
+
+  // generate table M
+  // use 4-bit tables (32 component decomposition of a 16 byte value)
+  // 8-bit tables take more space and are known to have security
+  // vulnerabilities (in native implementations)
+  this.componentBits = 4;
+  this._m = this.generateHashTable(this._hashSubkey, this.componentBits);
+
+  // Note: support IV length different from 96 bits? (only supporting
+  // 96 bits is recommended by NIST SP-800-38D)
+  // generate J_0
+  var ivLength = iv.length();
+  if(ivLength === 12) {
+    // 96-bit IV
+    this._j0 = [iv.getInt32(), iv.getInt32(), iv.getInt32(), 1];
+  } else {
+    // IV is NOT 96-bits
+    this._j0 = [0, 0, 0, 0];
+    while(iv.length() > 0) {
+      this._j0 = this.ghash(
+        this._hashSubkey, this._j0,
+        [iv.getInt32(), iv.getInt32(), iv.getInt32(), iv.getInt32()]);
+    }
+    this._j0 = this.ghash(
+      this._hashSubkey, this._j0, [0, 0].concat(from64To32(ivLength * 8)));
+  }
+
+  // generate ICB (initial counter block)
+  this._inBlock = this._j0.slice(0);
+  inc32(this._inBlock);
+  this._partialBytes = 0;
+
+  // consume authentication data
+  additionalData = forge.util.createBuffer(additionalData);
+  // save additional data length as a BE 64-bit number
+  this._aDataLength = from64To32(additionalData.length() * 8);
+  // pad additional data to 128 bit (16 byte) block size
+  var overflow = additionalData.length() % this.blockSize;
+  if(overflow) {
+    additionalData.fillWithByte(0, this.blockSize - overflow);
+  }
+  this._s = [0, 0, 0, 0];
+  while(additionalData.length() > 0) {
+    this._s = this.ghash(this._hashSubkey, this._s, [
+      additionalData.getInt32(),
+      additionalData.getInt32(),
+      additionalData.getInt32(),
+      additionalData.getInt32()
+    ]);
+  }
+};
+
+modes.gcm.prototype.encrypt = function(input, output, finish) {
+  // not enough input to encrypt
+  var inputLength = input.length();
+  if(inputLength === 0) {
+    return true;
+  }
+
+  // encrypt block
+  this.cipher.encrypt(this._inBlock, this._outBlock);
+
+  // handle full block
+  if(this._partialBytes === 0 && inputLength >= this.blockSize) {
+    // XOR input with output
+    for(var i = 0; i < this._ints; ++i) {
+      output.putInt32(this._outBlock[i] ^= input.getInt32());
+    }
+    this._cipherLength += this.blockSize;
+  } else {
+    // handle partial block
+    var partialBytes = (this.blockSize - inputLength) % this.blockSize;
+    if(partialBytes > 0) {
+      partialBytes = this.blockSize - partialBytes;
+    }
+
+    // XOR input with output
+    this._partialOutput.clear();
+    for(var i = 0; i < this._ints; ++i) {
+      this._partialOutput.putInt32(input.getInt32() ^ this._outBlock[i]);
+    }
+
+    if(partialBytes <= 0 || finish) {
+      // handle overflow prior to hashing
+      if(finish) {
+        // get block overflow
+        var overflow = inputLength % this.blockSize;
+        this._cipherLength += overflow;
+        // truncate for hash function
+        this._partialOutput.truncate(this.blockSize - overflow);
+      } else {
+        this._cipherLength += this.blockSize;
+      }
+
+      // get output block for hashing
+      for(var i = 0; i < this._ints; ++i) {
+        this._outBlock[i] = this._partialOutput.getInt32();
+      }
+      this._partialOutput.read -= this.blockSize;
+    }
+
+    // skip any previous partial bytes
+    if(this._partialBytes > 0) {
+      this._partialOutput.getBytes(this._partialBytes);
+    }
+
+    if(partialBytes > 0 && !finish) {
+      // block still incomplete, restore input buffer, get partial output,
+      // and return early
+      input.read -= this.blockSize;
+      output.putBytes(this._partialOutput.getBytes(
+        partialBytes - this._partialBytes));
+      this._partialBytes = partialBytes;
+      return true;
+    }
+
+    output.putBytes(this._partialOutput.getBytes(
+      inputLength - this._partialBytes));
+    this._partialBytes = 0;
+  }
+
+  // update hash block S
+  this._s = this.ghash(this._hashSubkey, this._s, this._outBlock);
+
+  // increment counter (input block)
+  inc32(this._inBlock);
+};
+
+modes.gcm.prototype.decrypt = function(input, output, finish) {
+  // not enough input to decrypt
+  var inputLength = input.length();
+  if(inputLength < this.blockSize && !(finish && inputLength > 0)) {
+    return true;
+  }
+
+  // encrypt block (GCM always uses encryption mode)
+  this.cipher.encrypt(this._inBlock, this._outBlock);
+
+  // increment counter (input block)
+  inc32(this._inBlock);
+
+  // update hash block S
+  this._hashBlock[0] = input.getInt32();
+  this._hashBlock[1] = input.getInt32();
+  this._hashBlock[2] = input.getInt32();
+  this._hashBlock[3] = input.getInt32();
+  this._s = this.ghash(this._hashSubkey, this._s, this._hashBlock);
+
+  // XOR hash input with output
+  for(var i = 0; i < this._ints; ++i) {
+    output.putInt32(this._outBlock[i] ^ this._hashBlock[i]);
+  }
+
+  // increment cipher data length
+  if(inputLength < this.blockSize) {
+    this._cipherLength += inputLength % this.blockSize;
+  } else {
+    this._cipherLength += this.blockSize;
+  }
+};
+
+modes.gcm.prototype.afterFinish = function(output, options) {
+  var rval = true;
+
+  // handle overflow
+  if(options.decrypt && options.overflow) {
+    output.truncate(this.blockSize - options.overflow);
+  }
+
+  // handle authentication tag
+  this.tag = forge.util.createBuffer();
+
+  // concatenate additional data length with cipher length
+  var lengths = this._aDataLength.concat(from64To32(this._cipherLength * 8));
+
+  // include lengths in hash
+  this._s = this.ghash(this._hashSubkey, this._s, lengths);
+
+  // do GCTR(J_0, S)
+  var tag = [];
+  this.cipher.encrypt(this._j0, tag);
+  for(var i = 0; i < this._ints; ++i) {
+    this.tag.putInt32(this._s[i] ^ tag[i]);
+  }
+
+  // trim tag to length
+  this.tag.truncate(this.tag.length() % (this._tagLength / 8));
+
+  // check authentication tag
+  if(options.decrypt && this.tag.bytes() !== this._tag) {
+    rval = false;
+  }
+
+  return rval;
+};
+
+/**
+ * See NIST SP-800-38D 6.3 (Algorithm 1). This function performs Galois
+ * field multiplication. The field, GF(2^128), is defined by the polynomial:
+ *
+ * x^128 + x^7 + x^2 + x + 1
+ *
+ * Which is represented in little-endian binary form as: 11100001 (0xe1). When
+ * the value of a coefficient is 1, a bit is set. The value R, is the
+ * concatenation of this value and 120 zero bits, yielding a 128-bit value
+ * which matches the block size.
+ *
+ * This function will multiply two elements (vectors of bytes), X and Y, in
+ * the field GF(2^128). The result is initialized to zero. For each bit of
+ * X (out of 128), x_i, if x_i is set, then the result is multiplied (XOR'd)
+ * by the current value of Y. For each bit, the value of Y will be raised by
+ * a power of x (multiplied by the polynomial x). This can be achieved by
+ * shifting Y once to the right. If the current value of Y, prior to being
+ * multiplied by x, has 0 as its LSB, then it is a 127th degree polynomial.
+ * Otherwise, we must divide by R after shifting to find the remainder.
+ *
+ * @param x the first block to multiply by the second.
+ * @param y the second block to multiply by the first.
+ *
+ * @return the block result of the multiplication.
+ */
+modes.gcm.prototype.multiply = function(x, y) {
+  var z_i = [0, 0, 0, 0];
+  var v_i = y.slice(0);
+
+  // calculate Z_128 (block has 128 bits)
+  for(var i = 0; i < 128; ++i) {
+    // if x_i is 0, Z_{i+1} = Z_i (unchanged)
+    // else Z_{i+1} = Z_i ^ V_i
+    // get x_i by finding 32-bit int position, then left shift 1 by remainder
+    var x_i = x[(i / 32) | 0] & (1 << (31 - i % 32));
+    if(x_i) {
+      z_i[0] ^= v_i[0];
+      z_i[1] ^= v_i[1];
+      z_i[2] ^= v_i[2];
+      z_i[3] ^= v_i[3];
+    }
+
+    // if LSB(V_i) is 1, V_i = V_i >> 1
+    // else V_i = (V_i >> 1) ^ R
+    this.pow(v_i, v_i);
+  }
+
+  return z_i;
+};
+
+modes.gcm.prototype.pow = function(x, out) {
+  // if LSB(x) is 1, x = x >>> 1
+  // else x = (x >>> 1) ^ R
+  var lsb = x[3] & 1;
+
+  // always do x >>> 1:
+  // starting with the rightmost integer, shift each integer to the right
+  // one bit, pulling in the bit from the integer to the left as its top
+  // most bit (do this for the last 3 integers)
+  for(var i = 3; i > 0; --i) {
+    out[i] = (x[i] >>> 1) | ((x[i - 1] & 1) << 31);
+  }
+  // shift the first integer normally
+  out[0] = x[0] >>> 1;
+
+  // if lsb was not set, then polynomial had a degree of 127 and doesn't
+  // need to divided; otherwise, XOR with R to find the remainder; we only
+  // need to XOR the first integer since R technically ends w/120 zero bits
+  if(lsb) {
+    out[0] ^= this._R;
+  }
+};
+
+modes.gcm.prototype.tableMultiply = function(x) {
+  // assumes 4-bit tables are used
+  var z = [0, 0, 0, 0];
+  for(var i = 0; i < 32; ++i) {
+    var idx = (i / 8) | 0;
+    var x_i = (x[idx] >>> ((7 - (i % 8)) * 4)) & 0xF;
+    var ah = this._m[i][x_i];
+    z[0] ^= ah[0];
+    z[1] ^= ah[1];
+    z[2] ^= ah[2];
+    z[3] ^= ah[3];
+  }
+  return z;
+};
+
+/**
+ * A continuing version of the GHASH algorithm that operates on a single
+ * block. The hash block, last hash value (Ym) and the new block to hash
+ * are given.
+ *
+ * @param h the hash block.
+ * @param y the previous value for Ym, use [0, 0, 0, 0] for a new hash.
+ * @param x the block to hash.
+ *
+ * @return the hashed value (Ym).
+ */
+modes.gcm.prototype.ghash = function(h, y, x) {
+  y[0] ^= x[0];
+  y[1] ^= x[1];
+  y[2] ^= x[2];
+  y[3] ^= x[3];
+  return this.tableMultiply(y);
+  //return this.multiply(y, h);
+};
+
+/**
+ * Precomputes a table for multiplying against the hash subkey. This
+ * mechanism provides a substantial speed increase over multiplication
+ * performed without a table. The table-based multiplication this table is
+ * for solves X * H by multiplying each component of X by H and then
+ * composing the results together using XOR.
+ *
+ * This function can be used to generate tables with different bit sizes
+ * for the components, however, this implementation assumes there are
+ * 32 components of X (which is a 16 byte vector), therefore each component
+ * takes 4-bits (so the table is constructed with bits=4).
+ *
+ * @param h the hash subkey.
+ * @param bits the bit size for a component.
+ */
+modes.gcm.prototype.generateHashTable = function(h, bits) {
+  // TODO: There are further optimizations that would use only the
+  // first table M_0 (or some variant) along with a remainder table;
+  // this can be explored in the future
+  var multiplier = 8 / bits;
+  var perInt = 4 * multiplier;
+  var size = 16 * multiplier;
+  var m = new Array(size);
+  for(var i = 0; i < size; ++i) {
+    var tmp = [0, 0, 0, 0];
+    var idx = (i / perInt) | 0;
+    var shft = ((perInt - 1 - (i % perInt)) * bits);
+    tmp[idx] = (1 << (bits - 1)) << shft;
+    m[i] = this.generateSubHashTable(this.multiply(tmp, h), bits);
+  }
+  return m;
+};
+
+/**
+ * Generates a table for multiplying against the hash subkey for one
+ * particular component (out of all possible component values).
+ *
+ * @param mid the pre-multiplied value for the middle key of the table.
+ * @param bits the bit size for a component.
+ */
+modes.gcm.prototype.generateSubHashTable = function(mid, bits) {
+  // compute the table quickly by minimizing the number of
+  // POW operations -- they only need to be performed for powers of 2,
+  // all other entries can be composed from those powers using XOR
+  var size = 1 << bits;
+  var half = size >>> 1;
+  var m = new Array(size);
+  m[half] = mid.slice(0);
+  var i = half >>> 1;
+  while(i > 0) {
+    // raise m0[2 * i] and store in m0[i]
+    this.pow(m[2 * i], m[i] = []);
+    i >>= 1;
+  }
+  i = 2;
+  while(i < half) {
+    for(var j = 1; j < i; ++j) {
+      var m_i = m[i];
+      var m_j = m[j];
+      m[i + j] = [
+        m_i[0] ^ m_j[0],
+        m_i[1] ^ m_j[1],
+        m_i[2] ^ m_j[2],
+        m_i[3] ^ m_j[3]
+      ];
+    }
+    i *= 2;
+  }
+  m[0] = [0, 0, 0, 0];
+  /* Note: We could avoid storing these by doing composition during multiply
+  calculate top half using composition by speed is preferred. */
+  for(i = half + 1; i < size; ++i) {
+    var c = m[i ^ half];
+    m[i] = [mid[0] ^ c[0], mid[1] ^ c[1], mid[2] ^ c[2], mid[3] ^ c[3]];
+  }
+  return m;
+};
+
+/** Utility functions */
+
+function transformIV(iv, blockSize) {
+  if(typeof iv === 'string') {
+    // convert iv string into byte buffer
+    iv = forge.util.createBuffer(iv);
+  }
+
+  if(forge.util.isArray(iv) && iv.length > 4) {
+    // convert iv byte array into byte buffer
+    var tmp = iv;
+    iv = forge.util.createBuffer();
+    for(var i = 0; i < tmp.length; ++i) {
+      iv.putByte(tmp[i]);
+    }
+  }
+
+  if(iv.length() < blockSize) {
+    throw new Error(
+      'Invalid IV length; got ' + iv.length() +
+      ' bytes and expected ' + blockSize + ' bytes.');
+  }
+
+  if(!forge.util.isArray(iv)) {
+    // convert iv byte buffer into 32-bit integer array
+    var ints = [];
+    var blocks = blockSize / 4;
+    for(var i = 0; i < blocks; ++i) {
+      ints.push(iv.getInt32());
+    }
+    iv = ints;
+  }
+
+  return iv;
+}
+
+function inc32(block) {
+  // increment last 32 bits of block only
+  block[block.length - 1] = (block[block.length - 1] + 1) & 0xFFFFFFFF;
+}
+
+function from64To32(num) {
+  // convert 64-bit number to two BE Int32s
+  return [(num / 0x100000000) | 0, num & 0xFFFFFFFF];
+}
Index: frontend/node_modules/node-forge/lib/des.js
===================================================================
--- frontend/node_modules/node-forge/lib/des.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/des.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,496 @@
+/**
+ * DES (Data Encryption Standard) implementation.
+ *
+ * This implementation supports DES as well as 3DES-EDE in ECB and CBC mode.
+ * It is based on the BSD-licensed implementation by Paul Tero:
+ *
+ * Paul Tero, July 2001
+ * http://www.tero.co.uk/des/
+ *
+ * Optimised for performance with large blocks by
+ * Michael Hayworth, November 2001
+ * http://www.netdealing.com
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @author Stefan Siegl
+ * @author Dave Longley
+ *
+ * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
+ * Copyright (c) 2012-2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./cipher');
+require('./cipherModes');
+require('./util');
+
+/* DES API */
+module.exports = forge.des = forge.des || {};
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * var cipher = forge.cipher.createCipher('DES-<mode>', key);
+ * cipher.start({iv: iv});
+ *
+ * Creates an DES cipher object to encrypt data using the given symmetric key.
+ * The output will be stored in the 'output' member of the returned cipher.
+ *
+ * The key and iv may be given as binary-encoded strings of bytes or
+ * byte buffers.
+ *
+ * @param key the symmetric key to use (64 or 192 bits).
+ * @param iv the initialization vector to use.
+ * @param output the buffer to write to, null to create one.
+ * @param mode the cipher mode to use (default: 'CBC' if IV is
+ *          given, 'ECB' if null).
+ *
+ * @return the cipher.
+ */
+forge.des.startEncrypting = function(key, iv, output, mode) {
+  var cipher = _createCipher({
+    key: key,
+    output: output,
+    decrypt: false,
+    mode: mode || (iv === null ? 'ECB' : 'CBC')
+  });
+  cipher.start(iv);
+  return cipher;
+};
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * var cipher = forge.cipher.createCipher('DES-<mode>', key);
+ *
+ * Creates an DES cipher object to encrypt data using the given symmetric key.
+ *
+ * The key may be given as a binary-encoded string of bytes or a byte buffer.
+ *
+ * @param key the symmetric key to use (64 or 192 bits).
+ * @param mode the cipher mode to use (default: 'CBC').
+ *
+ * @return the cipher.
+ */
+forge.des.createEncryptionCipher = function(key, mode) {
+  return _createCipher({
+    key: key,
+    output: null,
+    decrypt: false,
+    mode: mode
+  });
+};
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * var decipher = forge.cipher.createDecipher('DES-<mode>', key);
+ * decipher.start({iv: iv});
+ *
+ * Creates an DES cipher object to decrypt data using the given symmetric key.
+ * The output will be stored in the 'output' member of the returned cipher.
+ *
+ * The key and iv may be given as binary-encoded strings of bytes or
+ * byte buffers.
+ *
+ * @param key the symmetric key to use (64 or 192 bits).
+ * @param iv the initialization vector to use.
+ * @param output the buffer to write to, null to create one.
+ * @param mode the cipher mode to use (default: 'CBC' if IV is
+ *          given, 'ECB' if null).
+ *
+ * @return the cipher.
+ */
+forge.des.startDecrypting = function(key, iv, output, mode) {
+  var cipher = _createCipher({
+    key: key,
+    output: output,
+    decrypt: true,
+    mode: mode || (iv === null ? 'ECB' : 'CBC')
+  });
+  cipher.start(iv);
+  return cipher;
+};
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * var decipher = forge.cipher.createDecipher('DES-<mode>', key);
+ *
+ * Creates an DES cipher object to decrypt data using the given symmetric key.
+ *
+ * The key may be given as a binary-encoded string of bytes or a byte buffer.
+ *
+ * @param key the symmetric key to use (64 or 192 bits).
+ * @param mode the cipher mode to use (default: 'CBC').
+ *
+ * @return the cipher.
+ */
+forge.des.createDecryptionCipher = function(key, mode) {
+  return _createCipher({
+    key: key,
+    output: null,
+    decrypt: true,
+    mode: mode
+  });
+};
+
+/**
+ * Creates a new DES cipher algorithm object.
+ *
+ * @param name the name of the algorithm.
+ * @param mode the mode factory function.
+ *
+ * @return the DES algorithm object.
+ */
+forge.des.Algorithm = function(name, mode) {
+  var self = this;
+  self.name = name;
+  self.mode = new mode({
+    blockSize: 8,
+    cipher: {
+      encrypt: function(inBlock, outBlock) {
+        return _updateBlock(self._keys, inBlock, outBlock, false);
+      },
+      decrypt: function(inBlock, outBlock) {
+        return _updateBlock(self._keys, inBlock, outBlock, true);
+      }
+    }
+  });
+  self._init = false;
+};
+
+/**
+ * Initializes this DES algorithm by expanding its key.
+ *
+ * @param options the options to use.
+ *          key the key to use with this algorithm.
+ *          decrypt true if the algorithm should be initialized for decryption,
+ *            false for encryption.
+ */
+forge.des.Algorithm.prototype.initialize = function(options) {
+  if(this._init) {
+    return;
+  }
+
+  var key = forge.util.createBuffer(options.key);
+  if(this.name.indexOf('3DES') === 0) {
+    if(key.length() !== 24) {
+      throw new Error('Invalid Triple-DES key size: ' + key.length() * 8);
+    }
+  }
+
+  // do key expansion to 16 or 48 subkeys (single or triple DES)
+  this._keys = _createKeys(key);
+  this._init = true;
+};
+
+/** Register DES algorithms **/
+
+registerAlgorithm('DES-ECB', forge.cipher.modes.ecb);
+registerAlgorithm('DES-CBC', forge.cipher.modes.cbc);
+registerAlgorithm('DES-CFB', forge.cipher.modes.cfb);
+registerAlgorithm('DES-OFB', forge.cipher.modes.ofb);
+registerAlgorithm('DES-CTR', forge.cipher.modes.ctr);
+
+registerAlgorithm('3DES-ECB', forge.cipher.modes.ecb);
+registerAlgorithm('3DES-CBC', forge.cipher.modes.cbc);
+registerAlgorithm('3DES-CFB', forge.cipher.modes.cfb);
+registerAlgorithm('3DES-OFB', forge.cipher.modes.ofb);
+registerAlgorithm('3DES-CTR', forge.cipher.modes.ctr);
+
+function registerAlgorithm(name, mode) {
+  var factory = function() {
+    return new forge.des.Algorithm(name, mode);
+  };
+  forge.cipher.registerAlgorithm(name, factory);
+}
+
+/** DES implementation **/
+
+var spfunction1 = [0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004];
+var spfunction2 = [-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000];
+var spfunction3 = [0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200];
+var spfunction4 = [0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080];
+var spfunction5 = [0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100];
+var spfunction6 = [0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010];
+var spfunction7 = [0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002];
+var spfunction8 = [0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000];
+
+/**
+ * Create necessary sub keys.
+ *
+ * @param key the 64-bit or 192-bit key.
+ *
+ * @return the expanded keys.
+ */
+function _createKeys(key) {
+  var pc2bytes0  = [0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204],
+      pc2bytes1  = [0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101],
+      pc2bytes2  = [0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808],
+      pc2bytes3  = [0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000],
+      pc2bytes4  = [0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010],
+      pc2bytes5  = [0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420],
+      pc2bytes6  = [0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002],
+      pc2bytes7  = [0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800],
+      pc2bytes8  = [0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002],
+      pc2bytes9  = [0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408],
+      pc2bytes10 = [0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020],
+      pc2bytes11 = [0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200],
+      pc2bytes12 = [0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010],
+      pc2bytes13 = [0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105];
+
+  // how many iterations (1 for des, 3 for triple des)
+  // changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys
+  var iterations = key.length() > 8 ? 3 : 1;
+
+  // stores the return keys
+  var keys = [];
+
+  // now define the left shifts which need to be done
+  var shifts = [0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0];
+
+  var n = 0, tmp;
+  for(var j = 0; j < iterations; j++) {
+    var left = key.getInt32();
+    var right = key.getInt32();
+
+    tmp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+    right ^= tmp;
+    left ^= (tmp << 4);
+
+    tmp = ((right >>> -16) ^ left) & 0x0000ffff;
+    left ^= tmp;
+    right ^= (tmp << -16);
+
+    tmp = ((left >>> 2) ^ right) & 0x33333333;
+    right ^= tmp;
+    left ^= (tmp << 2);
+
+    tmp = ((right >>> -16) ^ left) & 0x0000ffff;
+    left ^= tmp;
+    right ^= (tmp << -16);
+
+    tmp = ((left >>> 1) ^ right) & 0x55555555;
+    right ^= tmp;
+    left ^= (tmp << 1);
+
+    tmp = ((right >>> 8) ^ left) & 0x00ff00ff;
+    left ^= tmp;
+    right ^= (tmp << 8);
+
+    tmp = ((left >>> 1) ^ right) & 0x55555555;
+    right ^= tmp;
+    left ^= (tmp << 1);
+
+    // right needs to be shifted and OR'd with last four bits of left
+    tmp = (left << 8) | ((right >>> 20) & 0x000000f0);
+
+    // left needs to be put upside down
+    left = ((right << 24) | ((right << 8) & 0xff0000) |
+      ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0));
+    right = tmp;
+
+    // now go through and perform these shifts on the left and right keys
+    for(var i = 0; i < shifts.length; ++i) {
+      //shift the keys either one or two bits to the left
+      if(shifts[i]) {
+        left = (left << 2) | (left >>> 26);
+        right = (right << 2) | (right >>> 26);
+      } else {
+        left = (left << 1) | (left >>> 27);
+        right = (right << 1) | (right >>> 27);
+      }
+      left &= -0xf;
+      right &= -0xf;
+
+      // now apply PC-2, in such a way that E is easier when encrypting or
+      // decrypting this conversion will look like PC-2 except only the last 6
+      // bits of each byte are used rather than 48 consecutive bits and the
+      // order of lines will be according to how the S selection functions will
+      // be applied: S2, S4, S6, S8, S1, S3, S5, S7
+      var lefttmp = (
+        pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf] |
+        pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf] |
+        pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf] |
+        pc2bytes6[(left >>> 4) & 0xf]);
+      var righttmp = (
+        pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf] |
+        pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf] |
+        pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf] |
+        pc2bytes13[(right >>> 4) & 0xf]);
+      tmp = ((righttmp >>> 16) ^ lefttmp) & 0x0000ffff;
+      keys[n++] = lefttmp ^ tmp;
+      keys[n++] = righttmp ^ (tmp << 16);
+    }
+  }
+
+  return keys;
+}
+
+/**
+ * Updates a single block (1 byte) using DES. The update will either
+ * encrypt or decrypt the block.
+ *
+ * @param keys the expanded keys.
+ * @param input the input block (an array of 32-bit words).
+ * @param output the updated output block.
+ * @param decrypt true to decrypt the block, false to encrypt it.
+ */
+function _updateBlock(keys, input, output, decrypt) {
+  // set up loops for single or triple DES
+  var iterations = keys.length === 32 ? 3 : 9;
+  var looping;
+  if(iterations === 3) {
+    looping = decrypt ? [30, -2, -2] : [0, 32, 2];
+  } else {
+    looping = (decrypt ?
+      [94, 62, -2, 32, 64, 2, 30, -2, -2] :
+      [0, 32, 2, 62, 30, -2, 64, 96, 2]);
+  }
+
+  var tmp;
+
+  var left = input[0];
+  var right = input[1];
+
+  // first each 64 bit chunk of the message must be permuted according to IP
+  tmp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+  right ^= tmp;
+  left ^= (tmp << 4);
+
+  tmp = ((left >>> 16) ^ right) & 0x0000ffff;
+  right ^= tmp;
+  left ^= (tmp << 16);
+
+  tmp = ((right >>> 2) ^ left) & 0x33333333;
+  left ^= tmp;
+  right ^= (tmp << 2);
+
+  tmp = ((right >>> 8) ^ left) & 0x00ff00ff;
+  left ^= tmp;
+  right ^= (tmp << 8);
+
+  tmp = ((left >>> 1) ^ right) & 0x55555555;
+  right ^= tmp;
+  left ^= (tmp << 1);
+
+  // rotate left 1 bit
+  left = ((left << 1) | (left >>> 31));
+  right = ((right << 1) | (right >>> 31));
+
+  for(var j = 0; j < iterations; j += 3) {
+    var endloop = looping[j + 1];
+    var loopinc = looping[j + 2];
+
+    // now go through and perform the encryption or decryption
+    for(var i = looping[j]; i != endloop; i += loopinc) {
+      var right1 = right ^ keys[i];
+      var right2 = ((right >>> 4) | (right << 28)) ^ keys[i + 1];
+
+      // passing these bytes through the S selection functions
+      tmp = left;
+      left = right;
+      right = tmp ^ (
+        spfunction2[(right1 >>> 24) & 0x3f] |
+        spfunction4[(right1 >>> 16) & 0x3f] |
+        spfunction6[(right1 >>>  8) & 0x3f] |
+        spfunction8[right1 & 0x3f] |
+        spfunction1[(right2 >>> 24) & 0x3f] |
+        spfunction3[(right2 >>> 16) & 0x3f] |
+        spfunction5[(right2 >>>  8) & 0x3f] |
+        spfunction7[right2 & 0x3f]);
+    }
+    // unreverse left and right
+    tmp = left;
+    left = right;
+    right = tmp;
+  }
+
+  // rotate right 1 bit
+  left = ((left >>> 1) | (left << 31));
+  right = ((right >>> 1) | (right << 31));
+
+  // now perform IP-1, which is IP in the opposite direction
+  tmp = ((left >>> 1) ^ right) & 0x55555555;
+  right ^= tmp;
+  left ^= (tmp << 1);
+
+  tmp = ((right >>> 8) ^ left) & 0x00ff00ff;
+  left ^= tmp;
+  right ^= (tmp << 8);
+
+  tmp = ((right >>> 2) ^ left) & 0x33333333;
+  left ^= tmp;
+  right ^= (tmp << 2);
+
+  tmp = ((left >>> 16) ^ right) & 0x0000ffff;
+  right ^= tmp;
+  left ^= (tmp << 16);
+
+  tmp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+  right ^= tmp;
+  left ^= (tmp << 4);
+
+  output[0] = left;
+  output[1] = right;
+}
+
+/**
+ * Deprecated. Instead, use:
+ *
+ * forge.cipher.createCipher('DES-<mode>', key);
+ * forge.cipher.createDecipher('DES-<mode>', key);
+ *
+ * Creates a deprecated DES cipher object. This object's mode will default to
+ * CBC (cipher-block-chaining).
+ *
+ * The key may be given as a binary-encoded string of bytes or a byte buffer.
+ *
+ * @param options the options to use.
+ *          key the symmetric key to use (64 or 192 bits).
+ *          output the buffer to write to.
+ *          decrypt true for decryption, false for encryption.
+ *          mode the cipher mode to use (default: 'CBC').
+ *
+ * @return the cipher.
+ */
+function _createCipher(options) {
+  options = options || {};
+  var mode = (options.mode || 'CBC').toUpperCase();
+  var algorithm = 'DES-' + mode;
+
+  var cipher;
+  if(options.decrypt) {
+    cipher = forge.cipher.createDecipher(algorithm, options.key);
+  } else {
+    cipher = forge.cipher.createCipher(algorithm, options.key);
+  }
+
+  // backwards compatible start API
+  var start = cipher.start;
+  cipher.start = function(iv, options) {
+    // backwards compatibility: support second arg as output buffer
+    var output = null;
+    if(options instanceof forge.util.ByteBuffer) {
+      output = options;
+      options = {};
+    }
+    options = options || {};
+    options.output = output;
+    options.iv = iv;
+    start.call(cipher, options);
+  };
+
+  return cipher;
+}
Index: frontend/node_modules/node-forge/lib/ed25519.js
===================================================================
--- frontend/node_modules/node-forge/lib/ed25519.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/ed25519.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1091 @@
+/**
+ * JavaScript implementation of Ed25519.
+ *
+ * Copyright (c) 2017-2019 Digital Bazaar, Inc.
+ *
+ * This implementation is based on the most excellent TweetNaCl which is
+ * in the public domain. Many thanks to its contributors:
+ *
+ * https://github.com/dchest/tweetnacl-js
+ */
+var forge = require('./forge');
+require('./jsbn');
+require('./random');
+require('./sha512');
+require('./util');
+var asn1Validator = require('./asn1-validator');
+var publicKeyValidator = asn1Validator.publicKeyValidator;
+var privateKeyValidator = asn1Validator.privateKeyValidator;
+
+if(typeof BigInteger === 'undefined') {
+  var BigInteger = forge.jsbn.BigInteger;
+}
+
+var ByteBuffer = forge.util.ByteBuffer;
+var NativeBuffer = typeof Buffer === 'undefined' ? Uint8Array : Buffer;
+
+/*
+ * Ed25519 algorithms, see RFC 8032:
+ * https://tools.ietf.org/html/rfc8032
+ */
+forge.pki = forge.pki || {};
+module.exports = forge.pki.ed25519 = forge.ed25519 = forge.ed25519 || {};
+var ed25519 = forge.ed25519;
+
+ed25519.constants = {};
+ed25519.constants.PUBLIC_KEY_BYTE_LENGTH = 32;
+ed25519.constants.PRIVATE_KEY_BYTE_LENGTH = 64;
+ed25519.constants.SEED_BYTE_LENGTH = 32;
+ed25519.constants.SIGN_BYTE_LENGTH = 64;
+ed25519.constants.HASH_BYTE_LENGTH = 64;
+
+ed25519.generateKeyPair = function(options) {
+  options = options || {};
+  var seed = options.seed;
+  if(seed === undefined) {
+    // generate seed
+    seed = forge.random.getBytesSync(ed25519.constants.SEED_BYTE_LENGTH);
+  } else if(typeof seed === 'string') {
+    if(seed.length !== ed25519.constants.SEED_BYTE_LENGTH) {
+      throw new TypeError(
+        '"seed" must be ' + ed25519.constants.SEED_BYTE_LENGTH +
+        ' bytes in length.');
+    }
+  } else if(!(seed instanceof Uint8Array)) {
+    throw new TypeError(
+      '"seed" must be a node.js Buffer, Uint8Array, or a binary string.');
+  }
+
+  seed = messageToNativeBuffer({message: seed, encoding: 'binary'});
+
+  var pk = new NativeBuffer(ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);
+  var sk = new NativeBuffer(ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);
+  for(var i = 0; i < 32; ++i) {
+    sk[i] = seed[i];
+  }
+  crypto_sign_keypair(pk, sk);
+  return {publicKey: pk, privateKey: sk};
+};
+
+/**
+ * Converts a private key from a RFC8410 ASN.1 encoding.
+ *
+ * @param obj - The asn1 representation of a private key.
+ *
+ * @returns {Object} keyInfo - The key information.
+ * @returns {Buffer|Uint8Array} keyInfo.privateKeyBytes - 32 private key bytes.
+ */
+ed25519.privateKeyFromAsn1 = function(obj) {
+  var capture = {};
+  var errors = [];
+  var valid = forge.asn1.validate(obj, privateKeyValidator, capture, errors);
+  if(!valid) {
+    var error = new Error('Invalid Key.');
+    error.errors = errors;
+    throw error;
+  }
+  var oid = forge.asn1.derToOid(capture.privateKeyOid);
+  var ed25519Oid = forge.oids.EdDSA25519;
+  if(oid !== ed25519Oid) {
+    throw new Error('Invalid OID "' + oid + '"; OID must be "' +
+      ed25519Oid + '".');
+  }
+  var privateKey = capture.privateKey;
+  // manually extract the private key bytes from nested octet string, see FIXME:
+  // https://github.com/digitalbazaar/forge/blob/master/lib/asn1.js#L542
+  var privateKeyBytes = messageToNativeBuffer({
+    message: forge.asn1.fromDer(privateKey).value,
+    encoding: 'binary'
+  });
+  // TODO: RFC8410 specifies a format for encoding the public key bytes along
+  // with the private key bytes. `publicKeyBytes` can be returned in the
+  // future. https://tools.ietf.org/html/rfc8410#section-10.3
+  return {privateKeyBytes: privateKeyBytes};
+};
+
+/**
+ * Converts a public key from a RFC8410 ASN.1 encoding.
+ *
+ * @param obj - The asn1 representation of a public key.
+ *
+ * @return {Buffer|Uint8Array} - 32 public key bytes.
+ */
+ed25519.publicKeyFromAsn1 = function(obj) {
+  // get SubjectPublicKeyInfo
+  var capture = {};
+  var errors = [];
+  var valid = forge.asn1.validate(obj, publicKeyValidator, capture, errors);
+  if(!valid) {
+    var error = new Error('Invalid Key.');
+    error.errors = errors;
+    throw error;
+  }
+  var oid = forge.asn1.derToOid(capture.publicKeyOid);
+  var ed25519Oid = forge.oids.EdDSA25519;
+  if(oid !== ed25519Oid) {
+    throw new Error('Invalid OID "' + oid + '"; OID must be "' +
+      ed25519Oid + '".');
+  }
+  var publicKeyBytes = capture.ed25519PublicKey;
+  if(publicKeyBytes.length !== ed25519.constants.PUBLIC_KEY_BYTE_LENGTH) {
+    throw new Error('Key length is invalid.');
+  }
+  return messageToNativeBuffer({
+    message: publicKeyBytes,
+    encoding: 'binary'
+  });
+};
+
+ed25519.publicKeyFromPrivateKey = function(options) {
+  options = options || {};
+  var privateKey = messageToNativeBuffer({
+    message: options.privateKey, encoding: 'binary'
+  });
+  if(privateKey.length !== ed25519.constants.PRIVATE_KEY_BYTE_LENGTH) {
+    throw new TypeError(
+      '"options.privateKey" must have a byte length of ' +
+      ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);
+  }
+
+  var pk = new NativeBuffer(ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);
+  for(var i = 0; i < pk.length; ++i) {
+    pk[i] = privateKey[32 + i];
+  }
+  return pk;
+};
+
+ed25519.sign = function(options) {
+  options = options || {};
+  var msg = messageToNativeBuffer(options);
+  var privateKey = messageToNativeBuffer({
+    message: options.privateKey,
+    encoding: 'binary'
+  });
+  if(privateKey.length === ed25519.constants.SEED_BYTE_LENGTH) {
+    var keyPair = ed25519.generateKeyPair({seed: privateKey});
+    privateKey = keyPair.privateKey;
+  } else if(privateKey.length !== ed25519.constants.PRIVATE_KEY_BYTE_LENGTH) {
+    throw new TypeError(
+      '"options.privateKey" must have a byte length of ' +
+      ed25519.constants.SEED_BYTE_LENGTH + ' or ' +
+      ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);
+  }
+
+  var signedMsg = new NativeBuffer(
+    ed25519.constants.SIGN_BYTE_LENGTH + msg.length);
+  crypto_sign(signedMsg, msg, msg.length, privateKey);
+
+  var sig = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH);
+  for(var i = 0; i < sig.length; ++i) {
+    sig[i] = signedMsg[i];
+  }
+  return sig;
+};
+
+ed25519.verify = function(options) {
+  options = options || {};
+  var msg = messageToNativeBuffer(options);
+  if(options.signature === undefined) {
+    throw new TypeError(
+      '"options.signature" must be a node.js Buffer, a Uint8Array, a forge ' +
+      'ByteBuffer, or a binary string.');
+  }
+  var sig = messageToNativeBuffer({
+    message: options.signature,
+    encoding: 'binary'
+  });
+  if(sig.length !== ed25519.constants.SIGN_BYTE_LENGTH) {
+    throw new TypeError(
+      '"options.signature" must have a byte length of ' +
+      ed25519.constants.SIGN_BYTE_LENGTH);
+  }
+  var publicKey = messageToNativeBuffer({
+    message: options.publicKey,
+    encoding: 'binary'
+  });
+  if(publicKey.length !== ed25519.constants.PUBLIC_KEY_BYTE_LENGTH) {
+    throw new TypeError(
+      '"options.publicKey" must have a byte length of ' +
+      ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);
+  }
+
+  var sm = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH + msg.length);
+  var m = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH + msg.length);
+  var i;
+  for(i = 0; i < ed25519.constants.SIGN_BYTE_LENGTH; ++i) {
+    sm[i] = sig[i];
+  }
+  for(i = 0; i < msg.length; ++i) {
+    sm[i + ed25519.constants.SIGN_BYTE_LENGTH] = msg[i];
+  }
+  return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0);
+};
+
+function messageToNativeBuffer(options) {
+  var message = options.message;
+  if(message instanceof Uint8Array || message instanceof NativeBuffer) {
+    return message;
+  }
+
+  var encoding = options.encoding;
+  if(message === undefined) {
+    if(options.md) {
+      // TODO: more rigorous validation that `md` is a MessageDigest
+      message = options.md.digest().getBytes();
+      encoding = 'binary';
+    } else {
+      throw new TypeError('"options.message" or "options.md" not specified.');
+    }
+  }
+
+  if(typeof message === 'string' && !encoding) {
+    throw new TypeError('"options.encoding" must be "binary" or "utf8".');
+  }
+
+  if(typeof message === 'string') {
+    if(typeof Buffer !== 'undefined') {
+      return Buffer.from(message, encoding);
+    }
+    message = new ByteBuffer(message, encoding);
+  } else if(!(message instanceof ByteBuffer)) {
+    throw new TypeError(
+      '"options.message" must be a node.js Buffer, a Uint8Array, a forge ' +
+      'ByteBuffer, or a string with "options.encoding" specifying its ' +
+      'encoding.');
+  }
+
+  // convert to native buffer
+  var buffer = new NativeBuffer(message.length());
+  for(var i = 0; i < buffer.length; ++i) {
+    buffer[i] = message.at(i);
+  }
+  return buffer;
+}
+
+var gf0 = gf();
+var gf1 = gf([1]);
+var D = gf([
+  0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,
+  0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]);
+var D2 = gf([
+  0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0,
+  0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]);
+var X = gf([
+  0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c,
+  0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]);
+var Y = gf([
+  0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
+  0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]);
+var L = new Float64Array([
+  0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
+  0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]);
+var I = gf([
+  0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,
+  0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);
+
+// TODO: update forge buffer implementation to use `Buffer` or `Uint8Array`,
+// whichever is available, to improve performance
+function sha512(msg, msgLen) {
+  // Note: `out` and `msg` are NativeBuffer
+  var md = forge.md.sha512.create();
+  var buffer = new ByteBuffer(msg);
+  md.update(buffer.getBytes(msgLen), 'binary');
+  var hash = md.digest().getBytes();
+  if(typeof Buffer !== 'undefined') {
+    return Buffer.from(hash, 'binary');
+  }
+  var out = new NativeBuffer(ed25519.constants.HASH_BYTE_LENGTH);
+  for(var i = 0; i < 64; ++i) {
+    out[i] = hash.charCodeAt(i);
+  }
+  return out;
+}
+
+function crypto_sign_keypair(pk, sk) {
+  var p = [gf(), gf(), gf(), gf()];
+  var i;
+
+  var d = sha512(sk, 32);
+  d[0] &= 248;
+  d[31] &= 127;
+  d[31] |= 64;
+
+  scalarbase(p, d);
+  pack(pk, p);
+
+  for(i = 0; i < 32; ++i) {
+    sk[i + 32] = pk[i];
+  }
+  return 0;
+}
+
+// Note: difference from C - smlen returned, not passed as argument.
+function crypto_sign(sm, m, n, sk) {
+  var i, j, x = new Float64Array(64);
+  var p = [gf(), gf(), gf(), gf()];
+
+  var d = sha512(sk, 32);
+  d[0] &= 248;
+  d[31] &= 127;
+  d[31] |= 64;
+
+  var smlen = n + 64;
+  for(i = 0; i < n; ++i) {
+    sm[64 + i] = m[i];
+  }
+  for(i = 0; i < 32; ++i) {
+    sm[32 + i] = d[32 + i];
+  }
+
+  var r = sha512(sm.subarray(32), n + 32);
+  reduce(r);
+  scalarbase(p, r);
+  pack(sm, p);
+
+  for(i = 32; i < 64; ++i) {
+    sm[i] = sk[i];
+  }
+  var h = sha512(sm, n + 64);
+  reduce(h);
+
+  for(i = 32; i < 64; ++i) {
+    x[i] = 0;
+  }
+  for(i = 0; i < 32; ++i) {
+    x[i] = r[i];
+  }
+  for(i = 0; i < 32; ++i) {
+    for(j = 0; j < 32; j++) {
+      x[i + j] += h[i] * d[j];
+    }
+  }
+
+  modL(sm.subarray(32), x);
+  return smlen;
+}
+
+function crypto_sign_open(m, sm, n, pk) {
+  var i, mlen;
+  var t = new NativeBuffer(32);
+  var p = [gf(), gf(), gf(), gf()],
+      q = [gf(), gf(), gf(), gf()];
+
+  mlen = -1;
+  if(n < 64) {
+    return -1;
+  }
+
+  if(unpackneg(q, pk)) {
+    return -1;
+  }
+
+  if(!_isCanonicalSignatureScalar(sm, 32)) {
+    return -1;
+  }
+
+  for(i = 0; i < n; ++i) {
+    m[i] = sm[i];
+  }
+  for(i = 0; i < 32; ++i) {
+    m[i + 32] = pk[i];
+  }
+  var h = sha512(m, n);
+  reduce(h);
+  scalarmult(p, q, h);
+
+  scalarbase(q, sm.subarray(32));
+  add(p, q);
+  pack(t, p);
+
+  n -= 64;
+  if(crypto_verify_32(sm, 0, t, 0)) {
+    for(i = 0; i < n; ++i) {
+      m[i] = 0;
+    }
+    return -1;
+  }
+
+  for(i = 0; i < n; ++i) {
+    m[i] = sm[i + 64];
+  }
+  mlen = n;
+  return mlen;
+}
+
+function _isCanonicalSignatureScalar(bytes, offset) {
+  var i;
+  // Compare little-endian scalar S against group order L and require S < L.
+  for(i = 31; i >= 0; --i) {
+    if(bytes[offset + i] < L[i]) {
+      return true;
+    }
+    if(bytes[offset + i] > L[i]) {
+      return false;
+    }
+  }
+  // S == L is non-canonical.
+  return false;
+}
+
+function modL(r, x) {
+  var carry, i, j, k;
+  for(i = 63; i >= 32; --i) {
+    carry = 0;
+    for(j = i - 32, k = i - 12; j < k; ++j) {
+      x[j] += carry - 16 * x[i] * L[j - (i - 32)];
+      carry = (x[j] + 128) >> 8;
+      x[j] -= carry * 256;
+    }
+    x[j] += carry;
+    x[i] = 0;
+  }
+  carry = 0;
+  for(j = 0; j < 32; ++j) {
+    x[j] += carry - (x[31] >> 4) * L[j];
+    carry = x[j] >> 8;
+    x[j] &= 255;
+  }
+  for(j = 0; j < 32; ++j) {
+    x[j] -= carry * L[j];
+  }
+  for(i = 0; i < 32; ++i) {
+    x[i + 1] += x[i] >> 8;
+    r[i] = x[i] & 255;
+  }
+}
+
+function reduce(r) {
+  var x = new Float64Array(64);
+  for(var i = 0; i < 64; ++i) {
+    x[i] = r[i];
+    r[i] = 0;
+  }
+  modL(r, x);
+}
+
+function add(p, q) {
+  var a = gf(), b = gf(), c = gf(),
+      d = gf(), e = gf(), f = gf(),
+      g = gf(), h = gf(), t = gf();
+
+  Z(a, p[1], p[0]);
+  Z(t, q[1], q[0]);
+  M(a, a, t);
+  A(b, p[0], p[1]);
+  A(t, q[0], q[1]);
+  M(b, b, t);
+  M(c, p[3], q[3]);
+  M(c, c, D2);
+  M(d, p[2], q[2]);
+  A(d, d, d);
+  Z(e, b, a);
+  Z(f, d, c);
+  A(g, d, c);
+  A(h, b, a);
+
+  M(p[0], e, f);
+  M(p[1], h, g);
+  M(p[2], g, f);
+  M(p[3], e, h);
+}
+
+function cswap(p, q, b) {
+  for(var i = 0; i < 4; ++i) {
+    sel25519(p[i], q[i], b);
+  }
+}
+
+function pack(r, p) {
+  var tx = gf(), ty = gf(), zi = gf();
+  inv25519(zi, p[2]);
+  M(tx, p[0], zi);
+  M(ty, p[1], zi);
+  pack25519(r, ty);
+  r[31] ^= par25519(tx) << 7;
+}
+
+function pack25519(o, n) {
+  var i, j, b;
+  var m = gf(), t = gf();
+  for(i = 0; i < 16; ++i) {
+    t[i] = n[i];
+  }
+  car25519(t);
+  car25519(t);
+  car25519(t);
+  for(j = 0; j < 2; ++j) {
+    m[0] = t[0] - 0xffed;
+    for(i = 1; i < 15; ++i) {
+      m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
+      m[i-1] &= 0xffff;
+    }
+    m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
+    b = (m[15] >> 16) & 1;
+    m[14] &= 0xffff;
+    sel25519(t, m, 1 - b);
+  }
+  for (i = 0; i < 16; i++) {
+    o[2 * i] = t[i] & 0xff;
+    o[2 * i + 1] = t[i] >> 8;
+  }
+}
+
+function unpackneg(r, p) {
+  var t = gf(), chk = gf(), num = gf(),
+      den = gf(), den2 = gf(), den4 = gf(),
+      den6 = gf();
+
+  set25519(r[2], gf1);
+  unpack25519(r[1], p);
+  S(num, r[1]);
+  M(den, num, D);
+  Z(num, num, r[2]);
+  A(den, r[2], den);
+
+  S(den2, den);
+  S(den4, den2);
+  M(den6, den4, den2);
+  M(t, den6, num);
+  M(t, t, den);
+
+  pow2523(t, t);
+  M(t, t, num);
+  M(t, t, den);
+  M(t, t, den);
+  M(r[0], t, den);
+
+  S(chk, r[0]);
+  M(chk, chk, den);
+  if(neq25519(chk, num)) {
+    M(r[0], r[0], I);
+  }
+
+  S(chk, r[0]);
+  M(chk, chk, den);
+  if(neq25519(chk, num)) {
+    return -1;
+  }
+
+  if(par25519(r[0]) === (p[31] >> 7)) {
+    Z(r[0], gf0, r[0]);
+  }
+
+  M(r[3], r[0], r[1]);
+  return 0;
+}
+
+function unpack25519(o, n) {
+  var i;
+  for(i = 0; i < 16; ++i) {
+    o[i] = n[2 * i] + (n[2 * i + 1] << 8);
+  }
+  o[15] &= 0x7fff;
+}
+
+function pow2523(o, i) {
+  var c = gf();
+  var a;
+  for(a = 0; a < 16; ++a) {
+    c[a] = i[a];
+  }
+  for(a = 250; a >= 0; --a) {
+    S(c, c);
+    if(a !== 1) {
+      M(c, c, i);
+    }
+  }
+  for(a = 0; a < 16; ++a) {
+    o[a] = c[a];
+  }
+}
+
+function neq25519(a, b) {
+  var c = new NativeBuffer(32);
+  var d = new NativeBuffer(32);
+  pack25519(c, a);
+  pack25519(d, b);
+  return crypto_verify_32(c, 0, d, 0);
+}
+
+function crypto_verify_32(x, xi, y, yi) {
+  return vn(x, xi, y, yi, 32);
+}
+
+function vn(x, xi, y, yi, n) {
+  var i, d = 0;
+  for(i = 0; i < n; ++i) {
+    d |= x[xi + i] ^ y[yi + i];
+  }
+  return (1 & ((d - 1) >>> 8)) - 1;
+}
+
+function par25519(a) {
+  var d = new NativeBuffer(32);
+  pack25519(d, a);
+  return d[0] & 1;
+}
+
+function scalarmult(p, q, s) {
+  var b, i;
+  set25519(p[0], gf0);
+  set25519(p[1], gf1);
+  set25519(p[2], gf1);
+  set25519(p[3], gf0);
+  for(i = 255; i >= 0; --i) {
+    b = (s[(i / 8)|0] >> (i & 7)) & 1;
+    cswap(p, q, b);
+    add(q, p);
+    add(p, p);
+    cswap(p, q, b);
+  }
+}
+
+function scalarbase(p, s) {
+  var q = [gf(), gf(), gf(), gf()];
+  set25519(q[0], X);
+  set25519(q[1], Y);
+  set25519(q[2], gf1);
+  M(q[3], X, Y);
+  scalarmult(p, q, s);
+}
+
+function set25519(r, a) {
+  var i;
+  for(i = 0; i < 16; i++) {
+    r[i] = a[i] | 0;
+  }
+}
+
+function inv25519(o, i) {
+  var c = gf();
+  var a;
+  for(a = 0; a < 16; ++a) {
+    c[a] = i[a];
+  }
+  for(a = 253; a >= 0; --a) {
+    S(c, c);
+    if(a !== 2 && a !== 4) {
+      M(c, c, i);
+    }
+  }
+  for(a = 0; a < 16; ++a) {
+    o[a] = c[a];
+  }
+}
+
+function car25519(o) {
+  var i, v, c = 1;
+  for(i = 0; i < 16; ++i) {
+    v = o[i] + c + 65535;
+    c = Math.floor(v / 65536);
+    o[i] = v - c * 65536;
+  }
+  o[0] += c - 1 + 37 * (c - 1);
+}
+
+function sel25519(p, q, b) {
+  var t, c = ~(b - 1);
+  for(var i = 0; i < 16; ++i) {
+    t = c & (p[i] ^ q[i]);
+    p[i] ^= t;
+    q[i] ^= t;
+  }
+}
+
+function gf(init) {
+  var i, r = new Float64Array(16);
+  if(init) {
+    for(i = 0; i < init.length; ++i) {
+      r[i] = init[i];
+    }
+  }
+  return r;
+}
+
+function A(o, a, b) {
+  for(var i = 0; i < 16; ++i) {
+    o[i] = a[i] + b[i];
+  }
+}
+
+function Z(o, a, b) {
+  for(var i = 0; i < 16; ++i) {
+    o[i] = a[i] - b[i];
+  }
+}
+
+function S(o, a) {
+  M(o, a, a);
+}
+
+function M(o, a, b) {
+  var v, c,
+     t0 = 0,  t1 = 0,  t2 = 0,  t3 = 0,  t4 = 0,  t5 = 0,  t6 = 0,  t7 = 0,
+     t8 = 0,  t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
+    t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
+    t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0,
+    b0 = b[0],
+    b1 = b[1],
+    b2 = b[2],
+    b3 = b[3],
+    b4 = b[4],
+    b5 = b[5],
+    b6 = b[6],
+    b7 = b[7],
+    b8 = b[8],
+    b9 = b[9],
+    b10 = b[10],
+    b11 = b[11],
+    b12 = b[12],
+    b13 = b[13],
+    b14 = b[14],
+    b15 = b[15];
+
+  v = a[0];
+  t0 += v * b0;
+  t1 += v * b1;
+  t2 += v * b2;
+  t3 += v * b3;
+  t4 += v * b4;
+  t5 += v * b5;
+  t6 += v * b6;
+  t7 += v * b7;
+  t8 += v * b8;
+  t9 += v * b9;
+  t10 += v * b10;
+  t11 += v * b11;
+  t12 += v * b12;
+  t13 += v * b13;
+  t14 += v * b14;
+  t15 += v * b15;
+  v = a[1];
+  t1 += v * b0;
+  t2 += v * b1;
+  t3 += v * b2;
+  t4 += v * b3;
+  t5 += v * b4;
+  t6 += v * b5;
+  t7 += v * b6;
+  t8 += v * b7;
+  t9 += v * b8;
+  t10 += v * b9;
+  t11 += v * b10;
+  t12 += v * b11;
+  t13 += v * b12;
+  t14 += v * b13;
+  t15 += v * b14;
+  t16 += v * b15;
+  v = a[2];
+  t2 += v * b0;
+  t3 += v * b1;
+  t4 += v * b2;
+  t5 += v * b3;
+  t6 += v * b4;
+  t7 += v * b5;
+  t8 += v * b6;
+  t9 += v * b7;
+  t10 += v * b8;
+  t11 += v * b9;
+  t12 += v * b10;
+  t13 += v * b11;
+  t14 += v * b12;
+  t15 += v * b13;
+  t16 += v * b14;
+  t17 += v * b15;
+  v = a[3];
+  t3 += v * b0;
+  t4 += v * b1;
+  t5 += v * b2;
+  t6 += v * b3;
+  t7 += v * b4;
+  t8 += v * b5;
+  t9 += v * b6;
+  t10 += v * b7;
+  t11 += v * b8;
+  t12 += v * b9;
+  t13 += v * b10;
+  t14 += v * b11;
+  t15 += v * b12;
+  t16 += v * b13;
+  t17 += v * b14;
+  t18 += v * b15;
+  v = a[4];
+  t4 += v * b0;
+  t5 += v * b1;
+  t6 += v * b2;
+  t7 += v * b3;
+  t8 += v * b4;
+  t9 += v * b5;
+  t10 += v * b6;
+  t11 += v * b7;
+  t12 += v * b8;
+  t13 += v * b9;
+  t14 += v * b10;
+  t15 += v * b11;
+  t16 += v * b12;
+  t17 += v * b13;
+  t18 += v * b14;
+  t19 += v * b15;
+  v = a[5];
+  t5 += v * b0;
+  t6 += v * b1;
+  t7 += v * b2;
+  t8 += v * b3;
+  t9 += v * b4;
+  t10 += v * b5;
+  t11 += v * b6;
+  t12 += v * b7;
+  t13 += v * b8;
+  t14 += v * b9;
+  t15 += v * b10;
+  t16 += v * b11;
+  t17 += v * b12;
+  t18 += v * b13;
+  t19 += v * b14;
+  t20 += v * b15;
+  v = a[6];
+  t6 += v * b0;
+  t7 += v * b1;
+  t8 += v * b2;
+  t9 += v * b3;
+  t10 += v * b4;
+  t11 += v * b5;
+  t12 += v * b6;
+  t13 += v * b7;
+  t14 += v * b8;
+  t15 += v * b9;
+  t16 += v * b10;
+  t17 += v * b11;
+  t18 += v * b12;
+  t19 += v * b13;
+  t20 += v * b14;
+  t21 += v * b15;
+  v = a[7];
+  t7 += v * b0;
+  t8 += v * b1;
+  t9 += v * b2;
+  t10 += v * b3;
+  t11 += v * b4;
+  t12 += v * b5;
+  t13 += v * b6;
+  t14 += v * b7;
+  t15 += v * b8;
+  t16 += v * b9;
+  t17 += v * b10;
+  t18 += v * b11;
+  t19 += v * b12;
+  t20 += v * b13;
+  t21 += v * b14;
+  t22 += v * b15;
+  v = a[8];
+  t8 += v * b0;
+  t9 += v * b1;
+  t10 += v * b2;
+  t11 += v * b3;
+  t12 += v * b4;
+  t13 += v * b5;
+  t14 += v * b6;
+  t15 += v * b7;
+  t16 += v * b8;
+  t17 += v * b9;
+  t18 += v * b10;
+  t19 += v * b11;
+  t20 += v * b12;
+  t21 += v * b13;
+  t22 += v * b14;
+  t23 += v * b15;
+  v = a[9];
+  t9 += v * b0;
+  t10 += v * b1;
+  t11 += v * b2;
+  t12 += v * b3;
+  t13 += v * b4;
+  t14 += v * b5;
+  t15 += v * b6;
+  t16 += v * b7;
+  t17 += v * b8;
+  t18 += v * b9;
+  t19 += v * b10;
+  t20 += v * b11;
+  t21 += v * b12;
+  t22 += v * b13;
+  t23 += v * b14;
+  t24 += v * b15;
+  v = a[10];
+  t10 += v * b0;
+  t11 += v * b1;
+  t12 += v * b2;
+  t13 += v * b3;
+  t14 += v * b4;
+  t15 += v * b5;
+  t16 += v * b6;
+  t17 += v * b7;
+  t18 += v * b8;
+  t19 += v * b9;
+  t20 += v * b10;
+  t21 += v * b11;
+  t22 += v * b12;
+  t23 += v * b13;
+  t24 += v * b14;
+  t25 += v * b15;
+  v = a[11];
+  t11 += v * b0;
+  t12 += v * b1;
+  t13 += v * b2;
+  t14 += v * b3;
+  t15 += v * b4;
+  t16 += v * b5;
+  t17 += v * b6;
+  t18 += v * b7;
+  t19 += v * b8;
+  t20 += v * b9;
+  t21 += v * b10;
+  t22 += v * b11;
+  t23 += v * b12;
+  t24 += v * b13;
+  t25 += v * b14;
+  t26 += v * b15;
+  v = a[12];
+  t12 += v * b0;
+  t13 += v * b1;
+  t14 += v * b2;
+  t15 += v * b3;
+  t16 += v * b4;
+  t17 += v * b5;
+  t18 += v * b6;
+  t19 += v * b7;
+  t20 += v * b8;
+  t21 += v * b9;
+  t22 += v * b10;
+  t23 += v * b11;
+  t24 += v * b12;
+  t25 += v * b13;
+  t26 += v * b14;
+  t27 += v * b15;
+  v = a[13];
+  t13 += v * b0;
+  t14 += v * b1;
+  t15 += v * b2;
+  t16 += v * b3;
+  t17 += v * b4;
+  t18 += v * b5;
+  t19 += v * b6;
+  t20 += v * b7;
+  t21 += v * b8;
+  t22 += v * b9;
+  t23 += v * b10;
+  t24 += v * b11;
+  t25 += v * b12;
+  t26 += v * b13;
+  t27 += v * b14;
+  t28 += v * b15;
+  v = a[14];
+  t14 += v * b0;
+  t15 += v * b1;
+  t16 += v * b2;
+  t17 += v * b3;
+  t18 += v * b4;
+  t19 += v * b5;
+  t20 += v * b6;
+  t21 += v * b7;
+  t22 += v * b8;
+  t23 += v * b9;
+  t24 += v * b10;
+  t25 += v * b11;
+  t26 += v * b12;
+  t27 += v * b13;
+  t28 += v * b14;
+  t29 += v * b15;
+  v = a[15];
+  t15 += v * b0;
+  t16 += v * b1;
+  t17 += v * b2;
+  t18 += v * b3;
+  t19 += v * b4;
+  t20 += v * b5;
+  t21 += v * b6;
+  t22 += v * b7;
+  t23 += v * b8;
+  t24 += v * b9;
+  t25 += v * b10;
+  t26 += v * b11;
+  t27 += v * b12;
+  t28 += v * b13;
+  t29 += v * b14;
+  t30 += v * b15;
+
+  t0  += 38 * t16;
+  t1  += 38 * t17;
+  t2  += 38 * t18;
+  t3  += 38 * t19;
+  t4  += 38 * t20;
+  t5  += 38 * t21;
+  t6  += 38 * t22;
+  t7  += 38 * t23;
+  t8  += 38 * t24;
+  t9  += 38 * t25;
+  t10 += 38 * t26;
+  t11 += 38 * t27;
+  t12 += 38 * t28;
+  t13 += 38 * t29;
+  t14 += 38 * t30;
+  // t15 left as is
+
+  // first car
+  c = 1;
+  v =  t0 + c + 65535; c = Math.floor(v / 65536);  t0 = v - c * 65536;
+  v =  t1 + c + 65535; c = Math.floor(v / 65536);  t1 = v - c * 65536;
+  v =  t2 + c + 65535; c = Math.floor(v / 65536);  t2 = v - c * 65536;
+  v =  t3 + c + 65535; c = Math.floor(v / 65536);  t3 = v - c * 65536;
+  v =  t4 + c + 65535; c = Math.floor(v / 65536);  t4 = v - c * 65536;
+  v =  t5 + c + 65535; c = Math.floor(v / 65536);  t5 = v - c * 65536;
+  v =  t6 + c + 65535; c = Math.floor(v / 65536);  t6 = v - c * 65536;
+  v =  t7 + c + 65535; c = Math.floor(v / 65536);  t7 = v - c * 65536;
+  v =  t8 + c + 65535; c = Math.floor(v / 65536);  t8 = v - c * 65536;
+  v =  t9 + c + 65535; c = Math.floor(v / 65536);  t9 = v - c * 65536;
+  v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
+  v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
+  v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
+  v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
+  v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
+  v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
+  t0 += c-1 + 37 * (c-1);
+
+  // second car
+  c = 1;
+  v =  t0 + c + 65535; c = Math.floor(v / 65536);  t0 = v - c * 65536;
+  v =  t1 + c + 65535; c = Math.floor(v / 65536);  t1 = v - c * 65536;
+  v =  t2 + c + 65535; c = Math.floor(v / 65536);  t2 = v - c * 65536;
+  v =  t3 + c + 65535; c = Math.floor(v / 65536);  t3 = v - c * 65536;
+  v =  t4 + c + 65535; c = Math.floor(v / 65536);  t4 = v - c * 65536;
+  v =  t5 + c + 65535; c = Math.floor(v / 65536);  t5 = v - c * 65536;
+  v =  t6 + c + 65535; c = Math.floor(v / 65536);  t6 = v - c * 65536;
+  v =  t7 + c + 65535; c = Math.floor(v / 65536);  t7 = v - c * 65536;
+  v =  t8 + c + 65535; c = Math.floor(v / 65536);  t8 = v - c * 65536;
+  v =  t9 + c + 65535; c = Math.floor(v / 65536);  t9 = v - c * 65536;
+  v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
+  v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
+  v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
+  v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
+  v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
+  v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
+  t0 += c-1 + 37 * (c-1);
+
+  o[ 0] = t0;
+  o[ 1] = t1;
+  o[ 2] = t2;
+  o[ 3] = t3;
+  o[ 4] = t4;
+  o[ 5] = t5;
+  o[ 6] = t6;
+  o[ 7] = t7;
+  o[ 8] = t8;
+  o[ 9] = t9;
+  o[10] = t10;
+  o[11] = t11;
+  o[12] = t12;
+  o[13] = t13;
+  o[14] = t14;
+  o[15] = t15;
+}
Index: frontend/node_modules/node-forge/lib/forge.js
===================================================================
--- frontend/node_modules/node-forge/lib/forge.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/forge.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+/**
+ * Node.js module for Forge.
+ *
+ * @author Dave Longley
+ *
+ * Copyright 2011-2016 Digital Bazaar, Inc.
+ */
+module.exports = {
+  // default options
+  options: {
+    usePureJavaScript: false
+  }
+};
Index: frontend/node_modules/node-forge/lib/form.js
===================================================================
--- frontend/node_modules/node-forge/lib/form.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/form.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,149 @@
+/**
+ * Functions for manipulating web forms.
+ *
+ * @author David I. Lehn <dlehn@digitalbazaar.com>
+ * @author Dave Longley
+ * @author Mike Johnson
+ *
+ * Copyright (c) 2011-2014 Digital Bazaar, Inc. All rights reserved.
+ */
+var forge = require('./forge');
+
+/* Form API */
+var form = module.exports = forge.form = forge.form || {};
+
+(function($) {
+
+/**
+ * Regex for parsing a single name property (handles array brackets).
+ */
+var _regex = /([^\[]*?)\[(.*?)\]/g;
+
+/**
+ * Parses a single name property into an array with the name and any
+ * array indices.
+ *
+ * @param name the name to parse.
+ *
+ * @return the array of the name and its array indices in order.
+ */
+var _parseName = function(name) {
+  var rval = [];
+
+  var matches;
+  while(!!(matches = _regex.exec(name))) {
+    if(matches[1].length > 0) {
+      rval.push(matches[1]);
+    }
+    if(matches.length >= 2) {
+      rval.push(matches[2]);
+    }
+  }
+  if(rval.length === 0) {
+    rval.push(name);
+  }
+
+  return rval;
+};
+
+/**
+ * Adds a field from the given form to the given object.
+ *
+ * @param obj the object.
+ * @param names the field as an array of object property names.
+ * @param value the value of the field.
+ * @param dict a dictionary of names to replace.
+ */
+var _addField = function(obj, names, value, dict) {
+  // combine array names that fall within square brackets
+  var tmp = [];
+  for(var i = 0; i < names.length; ++i) {
+    // check name for starting square bracket but no ending one
+    var name = names[i];
+    if(name.indexOf('[') !== -1 && name.indexOf(']') === -1 &&
+      i < names.length - 1) {
+      do {
+        name += '.' + names[++i];
+      } while(i < names.length - 1 && names[i].indexOf(']') === -1);
+    }
+    tmp.push(name);
+  }
+  names = tmp;
+
+  // split out array indexes
+  var tmp = [];
+  $.each(names, function(n, name) {
+    tmp = tmp.concat(_parseName(name));
+  });
+  names = tmp;
+
+  // iterate over object property names until value is set
+  $.each(names, function(n, name) {
+    // do dictionary name replacement
+    if(dict && name.length !== 0 && name in dict) {
+       name = dict[name];
+    }
+
+    // blank name indicates appending to an array, set name to
+    // new last index of array
+    if(name.length === 0) {
+       name = obj.length;
+    }
+
+    // value already exists, append value
+    if(obj[name]) {
+      // last name in the field
+      if(n == names.length - 1) {
+        // more than one value, so convert into an array
+        if(!$.isArray(obj[name])) {
+          obj[name] = [obj[name]];
+        }
+        obj[name].push(value);
+      } else {
+        // not last name, go deeper into object
+        obj = obj[name];
+      }
+    } else if(n == names.length - 1) {
+      // new value, last name in the field, set value
+      obj[name] = value;
+    } else {
+      // new value, not last name, go deeper
+      // get next name
+      var next = names[n + 1];
+
+      // blank next value indicates array-appending, so create array
+      if(next.length === 0) {
+         obj[name] = [];
+      } else {
+        // if next name is a number create an array, otherwise a map
+        var isNum = ((next - 0) == next && next.length > 0);
+        obj[name] = isNum ? [] : {};
+      }
+      obj = obj[name];
+    }
+  });
+};
+
+/**
+ * Serializes a form to a JSON object. Object properties will be separated
+ * using the given separator (defaults to '.') and by square brackets.
+ *
+ * @param input the jquery form to serialize.
+ * @param sep the object-property separator (defaults to '.').
+ * @param dict a dictionary of names to replace (name=replace).
+ *
+ * @return the JSON-serialized form.
+ */
+form.serialize = function(input, sep, dict) {
+  var rval = {};
+
+  // add all fields in the form to the object
+  sep = sep || '.';
+  $.each(input.serializeArray(), function() {
+    _addField(rval, this.name.split(sep), this.value || '', dict);
+  });
+
+  return rval;
+};
+
+})(jQuery);
Index: frontend/node_modules/node-forge/lib/hmac.js
===================================================================
--- frontend/node_modules/node-forge/lib/hmac.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/hmac.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,146 @@
+/**
+ * Hash-based Message Authentication Code implementation. Requires a message
+ * digest object that can be obtained, for example, from forge.md.sha1 or
+ * forge.md.md5.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2012 Digital Bazaar, Inc. All rights reserved.
+ */
+var forge = require('./forge');
+require('./md');
+require('./util');
+
+/* HMAC API */
+var hmac = module.exports = forge.hmac = forge.hmac || {};
+
+/**
+ * Creates an HMAC object that uses the given message digest object.
+ *
+ * @return an HMAC object.
+ */
+hmac.create = function() {
+  // the hmac key to use
+  var _key = null;
+
+  // the message digest to use
+  var _md = null;
+
+  // the inner padding
+  var _ipadding = null;
+
+  // the outer padding
+  var _opadding = null;
+
+  // hmac context
+  var ctx = {};
+
+  /**
+   * Starts or restarts the HMAC with the given key and message digest.
+   *
+   * @param md the message digest to use, null to reuse the previous one,
+   *           a string to use builtin 'sha1', 'md5', 'sha256'.
+   * @param key the key to use as a string, array of bytes, byte buffer,
+   *           or null to reuse the previous key.
+   */
+  ctx.start = function(md, key) {
+    if(md !== null) {
+      if(typeof md === 'string') {
+        // create builtin message digest
+        md = md.toLowerCase();
+        if(md in forge.md.algorithms) {
+          _md = forge.md.algorithms[md].create();
+        } else {
+          throw new Error('Unknown hash algorithm "' + md + '"');
+        }
+      } else {
+        // store message digest
+        _md = md;
+      }
+    }
+
+    if(key === null) {
+      // reuse previous key
+      key = _key;
+    } else {
+      if(typeof key === 'string') {
+        // convert string into byte buffer
+        key = forge.util.createBuffer(key);
+      } else if(forge.util.isArray(key)) {
+        // convert byte array into byte buffer
+        var tmp = key;
+        key = forge.util.createBuffer();
+        for(var i = 0; i < tmp.length; ++i) {
+          key.putByte(tmp[i]);
+        }
+      }
+
+      // if key is longer than blocksize, hash it
+      var keylen = key.length();
+      if(keylen > _md.blockLength) {
+        _md.start();
+        _md.update(key.bytes());
+        key = _md.digest();
+      }
+
+      // mix key into inner and outer padding
+      // ipadding = [0x36 * blocksize] ^ key
+      // opadding = [0x5C * blocksize] ^ key
+      _ipadding = forge.util.createBuffer();
+      _opadding = forge.util.createBuffer();
+      keylen = key.length();
+      for(var i = 0; i < keylen; ++i) {
+        var tmp = key.at(i);
+        _ipadding.putByte(0x36 ^ tmp);
+        _opadding.putByte(0x5C ^ tmp);
+      }
+
+      // if key is shorter than blocksize, add additional padding
+      if(keylen < _md.blockLength) {
+        var tmp = _md.blockLength - keylen;
+        for(var i = 0; i < tmp; ++i) {
+          _ipadding.putByte(0x36);
+          _opadding.putByte(0x5C);
+        }
+      }
+      _key = key;
+      _ipadding = _ipadding.bytes();
+      _opadding = _opadding.bytes();
+    }
+
+    // digest is done like so: hash(opadding | hash(ipadding | message))
+
+    // prepare to do inner hash
+    // hash(ipadding | message)
+    _md.start();
+    _md.update(_ipadding);
+  };
+
+  /**
+   * Updates the HMAC with the given message bytes.
+   *
+   * @param bytes the bytes to update with.
+   */
+  ctx.update = function(bytes) {
+    _md.update(bytes);
+  };
+
+  /**
+   * Produces the Message Authentication Code (MAC).
+   *
+   * @return a byte buffer containing the digest value.
+   */
+  ctx.getMac = function() {
+    // digest is done like so: hash(opadding | hash(ipadding | message))
+    // here we do the outer hashing
+    var inner = _md.digest().bytes();
+    _md.start();
+    _md.update(_opadding);
+    _md.update(inner);
+    return _md.digest();
+  };
+  // alias for getMac
+  ctx.digest = ctx.getMac;
+
+  return ctx;
+};
Index: frontend/node_modules/node-forge/lib/http.js
===================================================================
--- frontend/node_modules/node-forge/lib/http.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/http.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1346 @@
+/**
+ * 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;
+};
Index: frontend/node_modules/node-forge/lib/index.all.js
===================================================================
--- frontend/node_modules/node-forge/lib/index.all.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/index.all.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,16 @@
+/**
+ * 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');
Index: frontend/node_modules/node-forge/lib/index.js
===================================================================
--- frontend/node_modules/node-forge/lib/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,33 @@
+/**
+ * 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');
Index: frontend/node_modules/node-forge/lib/jsbn.js
===================================================================
--- frontend/node_modules/node-forge/lib/jsbn.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/jsbn.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1294 @@
+// 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
+// Version 1.2: square() API, isProbablePrime fix
+
+// (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^2
+function bnSquare() { var r = nbi(); this.squareTo(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) {
+  // FORGE: jsbn fix
+  // avoid infinite loop
+  if(this.signum() == 0) {
+    // returning zero to align with similar behavior when no multiplicative
+    // inverse module m is found.
+    return BigInteger.ZERO;
+  }
+  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,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
+var lplim = (1<<26)/lowprimes[lowprimes.length-1];
+
+// (public) test primality with certainty >= 1-.5^t
+function bnIsProbablePrime(t) {
+  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;
+
+// JSBN-specific extension
+BigInteger.prototype.square = bnSquare;
+
+// BigInteger interfaces not implemented in jsbn:
+
+// BigInteger(int signum, byte[] magnitude)
+// double doubleValue()
+// float floatValue()
+// int hashCode()
+// long longValue()
+// static BigInteger valueOf(long val)
Index: frontend/node_modules/node-forge/lib/kem.js
===================================================================
--- frontend/node_modules/node-forge/lib/kem.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/kem.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,168 @@
+/**
+ * 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();
+  };
+}
Index: frontend/node_modules/node-forge/lib/log.js
===================================================================
--- frontend/node_modules/node-forge/lib/log.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/log.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,319 @@
+/**
+ * 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;
Index: frontend/node_modules/node-forge/lib/md.all.js
===================================================================
--- frontend/node_modules/node-forge/lib/md.all.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/md.all.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+/**
+ * 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');
Index: frontend/node_modules/node-forge/lib/md.js
===================================================================
--- frontend/node_modules/node-forge/lib/md.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/md.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,11 @@
+/**
+ * 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 || {};
Index: frontend/node_modules/node-forge/lib/md5.js
===================================================================
--- frontend/node_modules/node-forge/lib/md5.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/md5.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,289 @@
+/**
+ * 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;
+  }
+}
Index: frontend/node_modules/node-forge/lib/mgf.js
===================================================================
--- frontend/node_modules/node-forge/lib/mgf.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/mgf.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,12 @@
+/**
+ * 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;
Index: frontend/node_modules/node-forge/lib/mgf1.js
===================================================================
--- frontend/node_modules/node-forge/lib/mgf1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/mgf1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,57 @@
+/**
+ * Javascript implementation of mask generation function MGF1.
+ *
+ * @author Stefan Siegl
+ * @author Dave Longley
+ *
+ * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
+ * Copyright (c) 2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./util');
+
+forge.mgf = forge.mgf || {};
+var mgf1 = module.exports = forge.mgf.mgf1 = forge.mgf1 = forge.mgf1 || {};
+
+/**
+ * Creates a MGF1 mask generation function object.
+ *
+ * @param md the message digest API to use (eg: forge.md.sha1.create()).
+ *
+ * @return a mask generation function object.
+ */
+mgf1.create = function(md) {
+  var mgf = {
+    /**
+     * Generate mask of specified length.
+     *
+     * @param {String} seed The seed for mask generation.
+     * @param maskLen Number of bytes to generate.
+     * @return {String} The generated mask.
+     */
+    generate: function(seed, maskLen) {
+      /* 2. Let T be the empty octet string. */
+      var t = new forge.util.ByteBuffer();
+
+      /* 3. For counter from 0 to ceil(maskLen / hLen), do the following: */
+      var len = Math.ceil(maskLen / md.digestLength);
+      for(var i = 0; i < len; i++) {
+        /* a. Convert counter to an octet string C of length 4 octets */
+        var c = new forge.util.ByteBuffer();
+        c.putInt32(i);
+
+        /* b. Concatenate the hash of the seed mgfSeed and C to the octet
+         * string T: */
+        md.start();
+        md.update(seed + c.getBytes());
+        t.putBuffer(md.digest());
+      }
+
+      /* Output the leading maskLen octets of T as the octet string mask. */
+      t.truncate(t.length() - maskLen);
+      return t.getBytes();
+    }
+  };
+
+  return mgf;
+};
Index: frontend/node_modules/node-forge/lib/oids.js
===================================================================
--- frontend/node_modules/node-forge/lib/oids.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/oids.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,180 @@
+/**
+ * Object IDs for ASN.1.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2013 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+
+forge.pki = forge.pki || {};
+var oids = module.exports = forge.pki.oids = forge.oids = forge.oids || {};
+
+// set id to name mapping and name to id mapping
+function _IN(id, name) {
+  oids[id] = name;
+  oids[name] = id;
+}
+// set id to name mapping only
+function _I_(id, name) {
+  oids[id] = name;
+}
+
+// algorithm OIDs
+_IN('1.2.840.113549.1.1.1', 'rsaEncryption');
+// Note: md2 & md4 not implemented
+//_IN('1.2.840.113549.1.1.2', 'md2WithRSAEncryption');
+//_IN('1.2.840.113549.1.1.3', 'md4WithRSAEncryption');
+_IN('1.2.840.113549.1.1.4', 'md5WithRSAEncryption');
+_IN('1.2.840.113549.1.1.5', 'sha1WithRSAEncryption');
+_IN('1.2.840.113549.1.1.7', 'RSAES-OAEP');
+_IN('1.2.840.113549.1.1.8', 'mgf1');
+_IN('1.2.840.113549.1.1.9', 'pSpecified');
+_IN('1.2.840.113549.1.1.10', 'RSASSA-PSS');
+_IN('1.2.840.113549.1.1.11', 'sha256WithRSAEncryption');
+_IN('1.2.840.113549.1.1.12', 'sha384WithRSAEncryption');
+_IN('1.2.840.113549.1.1.13', 'sha512WithRSAEncryption');
+// Edwards-curve Digital Signature Algorithm (EdDSA) Ed25519
+_IN('1.3.101.112', 'EdDSA25519');
+
+_IN('1.2.840.10040.4.3', 'dsa-with-sha1');
+
+_IN('1.3.14.3.2.7', 'desCBC');
+
+_IN('1.3.14.3.2.26', 'sha1');
+// Deprecated equivalent of sha1WithRSAEncryption
+_IN('1.3.14.3.2.29', 'sha1WithRSASignature');
+_IN('2.16.840.1.101.3.4.2.1', 'sha256');
+_IN('2.16.840.1.101.3.4.2.2', 'sha384');
+_IN('2.16.840.1.101.3.4.2.3', 'sha512');
+_IN('2.16.840.1.101.3.4.2.4', 'sha224');
+_IN('2.16.840.1.101.3.4.2.5', 'sha512-224');
+_IN('2.16.840.1.101.3.4.2.6', 'sha512-256');
+_IN('1.2.840.113549.2.2', 'md2');
+_IN('1.2.840.113549.2.5', 'md5');
+
+// pkcs#7 content types
+_IN('1.2.840.113549.1.7.1', 'data');
+_IN('1.2.840.113549.1.7.2', 'signedData');
+_IN('1.2.840.113549.1.7.3', 'envelopedData');
+_IN('1.2.840.113549.1.7.4', 'signedAndEnvelopedData');
+_IN('1.2.840.113549.1.7.5', 'digestedData');
+_IN('1.2.840.113549.1.7.6', 'encryptedData');
+
+// pkcs#9 oids
+_IN('1.2.840.113549.1.9.1', 'emailAddress');
+_IN('1.2.840.113549.1.9.2', 'unstructuredName');
+_IN('1.2.840.113549.1.9.3', 'contentType');
+_IN('1.2.840.113549.1.9.4', 'messageDigest');
+_IN('1.2.840.113549.1.9.5', 'signingTime');
+_IN('1.2.840.113549.1.9.6', 'counterSignature');
+_IN('1.2.840.113549.1.9.7', 'challengePassword');
+_IN('1.2.840.113549.1.9.8', 'unstructuredAddress');
+_IN('1.2.840.113549.1.9.14', 'extensionRequest');
+
+_IN('1.2.840.113549.1.9.20', 'friendlyName');
+_IN('1.2.840.113549.1.9.21', 'localKeyId');
+_IN('1.2.840.113549.1.9.22.1', 'x509Certificate');
+
+// pkcs#12 safe bags
+_IN('1.2.840.113549.1.12.10.1.1', 'keyBag');
+_IN('1.2.840.113549.1.12.10.1.2', 'pkcs8ShroudedKeyBag');
+_IN('1.2.840.113549.1.12.10.1.3', 'certBag');
+_IN('1.2.840.113549.1.12.10.1.4', 'crlBag');
+_IN('1.2.840.113549.1.12.10.1.5', 'secretBag');
+_IN('1.2.840.113549.1.12.10.1.6', 'safeContentsBag');
+
+// password-based-encryption for pkcs#12
+_IN('1.2.840.113549.1.5.13', 'pkcs5PBES2');
+_IN('1.2.840.113549.1.5.12', 'pkcs5PBKDF2');
+
+_IN('1.2.840.113549.1.12.1.1', 'pbeWithSHAAnd128BitRC4');
+_IN('1.2.840.113549.1.12.1.2', 'pbeWithSHAAnd40BitRC4');
+_IN('1.2.840.113549.1.12.1.3', 'pbeWithSHAAnd3-KeyTripleDES-CBC');
+_IN('1.2.840.113549.1.12.1.4', 'pbeWithSHAAnd2-KeyTripleDES-CBC');
+_IN('1.2.840.113549.1.12.1.5', 'pbeWithSHAAnd128BitRC2-CBC');
+_IN('1.2.840.113549.1.12.1.6', 'pbewithSHAAnd40BitRC2-CBC');
+
+// hmac OIDs
+_IN('1.2.840.113549.2.7', 'hmacWithSHA1');
+_IN('1.2.840.113549.2.8', 'hmacWithSHA224');
+_IN('1.2.840.113549.2.9', 'hmacWithSHA256');
+_IN('1.2.840.113549.2.10', 'hmacWithSHA384');
+_IN('1.2.840.113549.2.11', 'hmacWithSHA512');
+
+// symmetric key algorithm oids
+_IN('1.2.840.113549.3.7', 'des-EDE3-CBC');
+_IN('2.16.840.1.101.3.4.1.2', 'aes128-CBC');
+_IN('2.16.840.1.101.3.4.1.22', 'aes192-CBC');
+_IN('2.16.840.1.101.3.4.1.42', 'aes256-CBC');
+
+// certificate issuer/subject OIDs
+_IN('2.5.4.3', 'commonName');
+_IN('2.5.4.4', 'surname');
+_IN('2.5.4.5', 'serialNumber');
+_IN('2.5.4.6', 'countryName');
+_IN('2.5.4.7', 'localityName');
+_IN('2.5.4.8', 'stateOrProvinceName');
+_IN('2.5.4.9', 'streetAddress');
+_IN('2.5.4.10', 'organizationName');
+_IN('2.5.4.11', 'organizationalUnitName');
+_IN('2.5.4.12', 'title');
+_IN('2.5.4.13', 'description');
+_IN('2.5.4.15', 'businessCategory');
+_IN('2.5.4.17', 'postalCode');
+_IN('2.5.4.42', 'givenName');
+_IN('2.5.4.65', 'pseudonym');
+_IN('1.3.6.1.4.1.311.60.2.1.2', 'jurisdictionOfIncorporationStateOrProvinceName');
+_IN('1.3.6.1.4.1.311.60.2.1.3', 'jurisdictionOfIncorporationCountryName');
+
+// X.509 extension OIDs
+_IN('2.16.840.1.113730.1.1', 'nsCertType');
+_IN('2.16.840.1.113730.1.13', 'nsComment'); // deprecated in theory; still widely used
+_I_('2.5.29.1', 'authorityKeyIdentifier'); // deprecated, use .35
+_I_('2.5.29.2', 'keyAttributes'); // obsolete use .37 or .15
+_I_('2.5.29.3', 'certificatePolicies'); // deprecated, use .32
+_I_('2.5.29.4', 'keyUsageRestriction'); // obsolete use .37 or .15
+_I_('2.5.29.5', 'policyMapping'); // deprecated use .33
+_I_('2.5.29.6', 'subtreesConstraint'); // obsolete use .30
+_I_('2.5.29.7', 'subjectAltName'); // deprecated use .17
+_I_('2.5.29.8', 'issuerAltName'); // deprecated use .18
+_I_('2.5.29.9', 'subjectDirectoryAttributes');
+_I_('2.5.29.10', 'basicConstraints'); // deprecated use .19
+_I_('2.5.29.11', 'nameConstraints'); // deprecated use .30
+_I_('2.5.29.12', 'policyConstraints'); // deprecated use .36
+_I_('2.5.29.13', 'basicConstraints'); // deprecated use .19
+_IN('2.5.29.14', 'subjectKeyIdentifier');
+_IN('2.5.29.15', 'keyUsage');
+_I_('2.5.29.16', 'privateKeyUsagePeriod');
+_IN('2.5.29.17', 'subjectAltName');
+_IN('2.5.29.18', 'issuerAltName');
+_IN('2.5.29.19', 'basicConstraints');
+_I_('2.5.29.20', 'cRLNumber');
+_I_('2.5.29.21', 'cRLReason');
+_I_('2.5.29.22', 'expirationDate');
+_I_('2.5.29.23', 'instructionCode');
+_I_('2.5.29.24', 'invalidityDate');
+_I_('2.5.29.25', 'cRLDistributionPoints'); // deprecated use .31
+_I_('2.5.29.26', 'issuingDistributionPoint'); // deprecated use .28
+_I_('2.5.29.27', 'deltaCRLIndicator');
+_I_('2.5.29.28', 'issuingDistributionPoint');
+_I_('2.5.29.29', 'certificateIssuer');
+_I_('2.5.29.30', 'nameConstraints');
+_IN('2.5.29.31', 'cRLDistributionPoints');
+_IN('2.5.29.32', 'certificatePolicies');
+_I_('2.5.29.33', 'policyMappings');
+_I_('2.5.29.34', 'policyConstraints'); // deprecated use .36
+_IN('2.5.29.35', 'authorityKeyIdentifier');
+_I_('2.5.29.36', 'policyConstraints');
+_IN('2.5.29.37', 'extKeyUsage');
+_I_('2.5.29.46', 'freshestCRL');
+_I_('2.5.29.54', 'inhibitAnyPolicy');
+
+// extKeyUsage purposes
+_IN('1.3.6.1.4.1.11129.2.4.2', 'timestampList');
+_IN('1.3.6.1.5.5.7.1.1', 'authorityInfoAccess');
+_IN('1.3.6.1.5.5.7.3.1', 'serverAuth');
+_IN('1.3.6.1.5.5.7.3.2', 'clientAuth');
+_IN('1.3.6.1.5.5.7.3.3', 'codeSigning');
+_IN('1.3.6.1.5.5.7.3.4', 'emailProtection');
+_IN('1.3.6.1.5.5.7.3.8', 'timeStamping');
Index: frontend/node_modules/node-forge/lib/pbe.js
===================================================================
--- frontend/node_modules/node-forge/lib/pbe.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pbe.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1023 @@
+/**
+ * Password-based encryption functions.
+ *
+ * @author Dave Longley
+ * @author Stefan Siegl <stesie@brokenpipe.de>
+ *
+ * Copyright (c) 2010-2013 Digital Bazaar, Inc.
+ * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
+ *
+ * An EncryptedPrivateKeyInfo:
+ *
+ * EncryptedPrivateKeyInfo ::= SEQUENCE {
+ *   encryptionAlgorithm  EncryptionAlgorithmIdentifier,
+ *   encryptedData        EncryptedData }
+ *
+ * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * EncryptedData ::= OCTET STRING
+ */
+var forge = require('./forge');
+require('./aes');
+require('./asn1');
+require('./des');
+require('./md');
+require('./oids');
+require('./pbkdf2');
+require('./pem');
+require('./random');
+require('./rc2');
+require('./rsa');
+require('./util');
+
+if(typeof BigInteger === 'undefined') {
+  var BigInteger = forge.jsbn.BigInteger;
+}
+
+// shortcut for asn.1 API
+var asn1 = forge.asn1;
+
+/* Password-based encryption implementation. */
+var pki = forge.pki = forge.pki || {};
+module.exports = pki.pbe = forge.pbe = forge.pbe || {};
+var oids = pki.oids;
+
+// validator for an EncryptedPrivateKeyInfo structure
+// Note: Currently only works w/algorithm params
+var encryptedPrivateKeyValidator = {
+  name: 'EncryptedPrivateKeyInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'EncryptedPrivateKeyInfo.encryptionAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'AlgorithmIdentifier.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'encryptionOid'
+    }, {
+      name: 'AlgorithmIdentifier.parameters',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      captureAsn1: 'encryptionParams'
+    }]
+  }, {
+    // encryptedData
+    name: 'EncryptedPrivateKeyInfo.encryptedData',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OCTETSTRING,
+    constructed: false,
+    capture: 'encryptedData'
+  }]
+};
+
+// validator for a PBES2Algorithms structure
+// Note: Currently only works w/PBKDF2 + AES encryption schemes
+var PBES2AlgorithmsValidator = {
+  name: 'PBES2Algorithms',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'PBES2Algorithms.keyDerivationFunc',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'PBES2Algorithms.keyDerivationFunc.oid',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'kdfOid'
+    }, {
+      name: 'PBES2Algorithms.params',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      value: [{
+        name: 'PBES2Algorithms.params.salt',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.OCTETSTRING,
+        constructed: false,
+        capture: 'kdfSalt'
+      }, {
+        name: 'PBES2Algorithms.params.iterationCount',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.INTEGER,
+        constructed: false,
+        capture: 'kdfIterationCount'
+      }, {
+        name: 'PBES2Algorithms.params.keyLength',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.INTEGER,
+        constructed: false,
+        optional: true,
+        capture: 'keyLength'
+      }, {
+        // prf
+        name: 'PBES2Algorithms.params.prf',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.SEQUENCE,
+        constructed: true,
+        optional: true,
+        value: [{
+          name: 'PBES2Algorithms.params.prf.algorithm',
+          tagClass: asn1.Class.UNIVERSAL,
+          type: asn1.Type.OID,
+          constructed: false,
+          capture: 'prfOid'
+        }]
+      }]
+    }]
+  }, {
+    name: 'PBES2Algorithms.encryptionScheme',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'PBES2Algorithms.encryptionScheme.oid',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'encOid'
+    }, {
+      name: 'PBES2Algorithms.encryptionScheme.iv',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OCTETSTRING,
+      constructed: false,
+      capture: 'encIv'
+    }]
+  }]
+};
+
+var pkcs12PbeParamsValidator = {
+  name: 'pkcs-12PbeParams',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'pkcs-12PbeParams.salt',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OCTETSTRING,
+    constructed: false,
+    capture: 'salt'
+  }, {
+    name: 'pkcs-12PbeParams.iterations',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'iterations'
+  }]
+};
+
+/**
+ * Encrypts a ASN.1 PrivateKeyInfo object, producing an EncryptedPrivateKeyInfo.
+ *
+ * PBES2Algorithms ALGORITHM-IDENTIFIER ::=
+ *   { {PBES2-params IDENTIFIED BY id-PBES2}, ...}
+ *
+ * id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
+ *
+ * PBES2-params ::= SEQUENCE {
+ *   keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
+ *   encryptionScheme AlgorithmIdentifier {{PBES2-Encs}}
+ * }
+ *
+ * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
+ *   { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
+ *
+ * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
+ *
+ * PBKDF2-params ::= SEQUENCE {
+ *   salt CHOICE {
+ *     specified OCTET STRING,
+ *     otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
+ *   },
+ *   iterationCount INTEGER (1..MAX),
+ *   keyLength INTEGER (1..MAX) OPTIONAL,
+ *   prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
+ * }
+ *
+ * @param obj the ASN.1 PrivateKeyInfo object.
+ * @param password the password to encrypt with.
+ * @param options:
+ *          algorithm the encryption algorithm to use
+ *            ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.
+ *          count the iteration count to use.
+ *          saltSize the salt size to use.
+ *          prfAlgorithm the PRF message digest algorithm to use
+ *            ('sha1', 'sha224', 'sha256', 'sha384', 'sha512')
+ *
+ * @return the ASN.1 EncryptedPrivateKeyInfo.
+ */
+pki.encryptPrivateKeyInfo = function(obj, password, options) {
+  // set default options
+  options = options || {};
+  options.saltSize = options.saltSize || 8;
+  options.count = options.count || 2048;
+  options.algorithm = options.algorithm || 'aes128';
+  options.prfAlgorithm = options.prfAlgorithm || 'sha1';
+
+  // generate PBE params
+  var salt = forge.random.getBytesSync(options.saltSize);
+  var count = options.count;
+  var countBytes = asn1.integerToDer(count);
+  var dkLen;
+  var encryptionAlgorithm;
+  var encryptedData;
+  if(options.algorithm.indexOf('aes') === 0 || options.algorithm === 'des') {
+    // do PBES2
+    var ivLen, encOid, cipherFn;
+    switch(options.algorithm) {
+    case 'aes128':
+      dkLen = 16;
+      ivLen = 16;
+      encOid = oids['aes128-CBC'];
+      cipherFn = forge.aes.createEncryptionCipher;
+      break;
+    case 'aes192':
+      dkLen = 24;
+      ivLen = 16;
+      encOid = oids['aes192-CBC'];
+      cipherFn = forge.aes.createEncryptionCipher;
+      break;
+    case 'aes256':
+      dkLen = 32;
+      ivLen = 16;
+      encOid = oids['aes256-CBC'];
+      cipherFn = forge.aes.createEncryptionCipher;
+      break;
+    case 'des':
+      dkLen = 8;
+      ivLen = 8;
+      encOid = oids['desCBC'];
+      cipherFn = forge.des.createEncryptionCipher;
+      break;
+    default:
+      var error = new Error('Cannot encrypt private key. Unknown encryption algorithm.');
+      error.algorithm = options.algorithm;
+      throw error;
+    }
+
+    // get PRF message digest
+    var prfAlgorithm = 'hmacWith' + options.prfAlgorithm.toUpperCase();
+    var md = prfAlgorithmToMessageDigest(prfAlgorithm);
+
+    // encrypt private key using pbe SHA-1 and AES/DES
+    var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen, md);
+    var iv = forge.random.getBytesSync(ivLen);
+    var cipher = cipherFn(dk);
+    cipher.start(iv);
+    cipher.update(asn1.toDer(obj));
+    cipher.finish();
+    encryptedData = cipher.output.getBytes();
+
+    // get PBKDF2-params
+    var params = createPbkdf2Params(salt, countBytes, dkLen, prfAlgorithm);
+
+    encryptionAlgorithm = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(oids['pkcs5PBES2']).getBytes()),
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // keyDerivationFunc
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+            asn1.oidToDer(oids['pkcs5PBKDF2']).getBytes()),
+          // PBKDF2-params
+          params
+        ]),
+        // encryptionScheme
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+            asn1.oidToDer(encOid).getBytes()),
+          // iv
+          asn1.create(
+            asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, iv)
+        ])
+      ])
+    ]);
+  } else if(options.algorithm === '3des') {
+    // Do PKCS12 PBE
+    dkLen = 24;
+
+    var saltBytes = new forge.util.ByteBuffer(salt);
+    var dk = pki.pbe.generatePkcs12Key(password, saltBytes, 1, count, dkLen);
+    var iv = pki.pbe.generatePkcs12Key(password, saltBytes, 2, count, dkLen);
+    var cipher = forge.des.createEncryptionCipher(dk);
+    cipher.start(iv);
+    cipher.update(asn1.toDer(obj));
+    cipher.finish();
+    encryptedData = cipher.output.getBytes();
+
+    encryptionAlgorithm = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(oids['pbeWithSHAAnd3-KeyTripleDES-CBC']).getBytes()),
+      // pkcs-12PbeParams
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // salt
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
+        // iteration count
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+          countBytes.getBytes())
+      ])
+    ]);
+  } else {
+    var error = new Error('Cannot encrypt private key. Unknown encryption algorithm.');
+    error.algorithm = options.algorithm;
+    throw error;
+  }
+
+  // EncryptedPrivateKeyInfo
+  var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // encryptionAlgorithm
+    encryptionAlgorithm,
+    // encryptedData
+    asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, encryptedData)
+  ]);
+  return rval;
+};
+
+/**
+ * Decrypts a ASN.1 PrivateKeyInfo object.
+ *
+ * @param obj the ASN.1 EncryptedPrivateKeyInfo object.
+ * @param password the password to decrypt with.
+ *
+ * @return the ASN.1 PrivateKeyInfo on success, null on failure.
+ */
+pki.decryptPrivateKeyInfo = function(obj, password) {
+  var rval = null;
+
+  // get PBE params
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, encryptedPrivateKeyValidator, capture, errors)) {
+    var error = new Error('Cannot read encrypted private key. ' +
+      'ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
+    error.errors = errors;
+    throw error;
+  }
+
+  // get cipher
+  var oid = asn1.derToOid(capture.encryptionOid);
+  var cipher = pki.pbe.getCipher(oid, capture.encryptionParams, password);
+
+  // get encrypted data
+  var encrypted = forge.util.createBuffer(capture.encryptedData);
+
+  cipher.update(encrypted);
+  if(cipher.finish()) {
+    rval = asn1.fromDer(cipher.output);
+  }
+
+  return rval;
+};
+
+/**
+ * Converts a EncryptedPrivateKeyInfo to PEM format.
+ *
+ * @param epki the EncryptedPrivateKeyInfo.
+ * @param maxline the maximum characters per line, defaults to 64.
+ *
+ * @return the PEM-formatted encrypted private key.
+ */
+pki.encryptedPrivateKeyToPem = function(epki, maxline) {
+  // convert to DER, then PEM-encode
+  var msg = {
+    type: 'ENCRYPTED PRIVATE KEY',
+    body: asn1.toDer(epki).getBytes()
+  };
+  return forge.pem.encode(msg, {maxline: maxline});
+};
+
+/**
+ * Converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format. Decryption
+ * is not performed.
+ *
+ * @param pem the EncryptedPrivateKeyInfo in PEM-format.
+ *
+ * @return the ASN.1 EncryptedPrivateKeyInfo.
+ */
+pki.encryptedPrivateKeyFromPem = function(pem) {
+  var msg = forge.pem.decode(pem)[0];
+
+  if(msg.type !== 'ENCRYPTED PRIVATE KEY') {
+    var error = new Error('Could not convert encrypted private key from PEM; ' +
+      'PEM header type is "ENCRYPTED PRIVATE KEY".');
+    error.headerType = msg.type;
+    throw error;
+  }
+  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+    throw new Error('Could not convert encrypted private key from PEM; ' +
+      'PEM is encrypted.');
+  }
+
+  // convert DER to ASN.1 object
+  return asn1.fromDer(msg.body);
+};
+
+/**
+ * Encrypts an RSA private key. By default, the key will be wrapped in
+ * a PrivateKeyInfo and encrypted to produce a PKCS#8 EncryptedPrivateKeyInfo.
+ * This is the standard, preferred way to encrypt a private key.
+ *
+ * To produce a non-standard PEM-encrypted private key that uses encapsulated
+ * headers to indicate the encryption algorithm (old-style non-PKCS#8 OpenSSL
+ * private key encryption), set the 'legacy' option to true. Note: Using this
+ * option will cause the iteration count to be forced to 1.
+ *
+ * Note: The 'des' algorithm is supported, but it is not considered to be
+ * secure because it only uses a single 56-bit key. If possible, it is highly
+ * recommended that a different algorithm be used.
+ *
+ * @param rsaKey the RSA key to encrypt.
+ * @param password the password to use.
+ * @param options:
+ *          algorithm: the encryption algorithm to use
+ *            ('aes128', 'aes192', 'aes256', '3des', 'des').
+ *          count: the iteration count to use.
+ *          saltSize: the salt size to use.
+ *          legacy: output an old non-PKCS#8 PEM-encrypted+encapsulated
+ *            headers (DEK-Info) private key.
+ *
+ * @return the PEM-encoded ASN.1 EncryptedPrivateKeyInfo.
+ */
+pki.encryptRsaPrivateKey = function(rsaKey, password, options) {
+  // standard PKCS#8
+  options = options || {};
+  if(!options.legacy) {
+    // encrypt PrivateKeyInfo
+    var rval = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(rsaKey));
+    rval = pki.encryptPrivateKeyInfo(rval, password, options);
+    return pki.encryptedPrivateKeyToPem(rval);
+  }
+
+  // legacy non-PKCS#8
+  var algorithm;
+  var iv;
+  var dkLen;
+  var cipherFn;
+  switch(options.algorithm) {
+  case 'aes128':
+    algorithm = 'AES-128-CBC';
+    dkLen = 16;
+    iv = forge.random.getBytesSync(16);
+    cipherFn = forge.aes.createEncryptionCipher;
+    break;
+  case 'aes192':
+    algorithm = 'AES-192-CBC';
+    dkLen = 24;
+    iv = forge.random.getBytesSync(16);
+    cipherFn = forge.aes.createEncryptionCipher;
+    break;
+  case 'aes256':
+    algorithm = 'AES-256-CBC';
+    dkLen = 32;
+    iv = forge.random.getBytesSync(16);
+    cipherFn = forge.aes.createEncryptionCipher;
+    break;
+  case '3des':
+    algorithm = 'DES-EDE3-CBC';
+    dkLen = 24;
+    iv = forge.random.getBytesSync(8);
+    cipherFn = forge.des.createEncryptionCipher;
+    break;
+  case 'des':
+    algorithm = 'DES-CBC';
+    dkLen = 8;
+    iv = forge.random.getBytesSync(8);
+    cipherFn = forge.des.createEncryptionCipher;
+    break;
+  default:
+    var error = new Error('Could not encrypt RSA private key; unsupported ' +
+      'encryption algorithm "' + options.algorithm + '".');
+    error.algorithm = options.algorithm;
+    throw error;
+  }
+
+  // encrypt private key using OpenSSL legacy key derivation
+  var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);
+  var cipher = cipherFn(dk);
+  cipher.start(iv);
+  cipher.update(asn1.toDer(pki.privateKeyToAsn1(rsaKey)));
+  cipher.finish();
+
+  var msg = {
+    type: 'RSA PRIVATE KEY',
+    procType: {
+      version: '4',
+      type: 'ENCRYPTED'
+    },
+    dekInfo: {
+      algorithm: algorithm,
+      parameters: forge.util.bytesToHex(iv).toUpperCase()
+    },
+    body: cipher.output.getBytes()
+  };
+  return forge.pem.encode(msg);
+};
+
+/**
+ * Decrypts an RSA private key.
+ *
+ * @param pem the PEM-formatted EncryptedPrivateKeyInfo to decrypt.
+ * @param password the password to use.
+ *
+ * @return the RSA key on success, null on failure.
+ */
+pki.decryptRsaPrivateKey = function(pem, password) {
+  var rval = null;
+
+  var msg = forge.pem.decode(pem)[0];
+
+  if(msg.type !== 'ENCRYPTED PRIVATE KEY' &&
+    msg.type !== 'PRIVATE KEY' &&
+    msg.type !== 'RSA PRIVATE KEY') {
+    var error = new Error('Could not convert private key from PEM; PEM header type ' +
+      'is not "ENCRYPTED PRIVATE KEY", "PRIVATE KEY", or "RSA PRIVATE KEY".');
+    error.headerType = error;
+    throw error;
+  }
+
+  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+    var dkLen;
+    var cipherFn;
+    switch(msg.dekInfo.algorithm) {
+    case 'DES-CBC':
+      dkLen = 8;
+      cipherFn = forge.des.createDecryptionCipher;
+      break;
+    case 'DES-EDE3-CBC':
+      dkLen = 24;
+      cipherFn = forge.des.createDecryptionCipher;
+      break;
+    case 'AES-128-CBC':
+      dkLen = 16;
+      cipherFn = forge.aes.createDecryptionCipher;
+      break;
+    case 'AES-192-CBC':
+      dkLen = 24;
+      cipherFn = forge.aes.createDecryptionCipher;
+      break;
+    case 'AES-256-CBC':
+      dkLen = 32;
+      cipherFn = forge.aes.createDecryptionCipher;
+      break;
+    case 'RC2-40-CBC':
+      dkLen = 5;
+      cipherFn = function(key) {
+        return forge.rc2.createDecryptionCipher(key, 40);
+      };
+      break;
+    case 'RC2-64-CBC':
+      dkLen = 8;
+      cipherFn = function(key) {
+        return forge.rc2.createDecryptionCipher(key, 64);
+      };
+      break;
+    case 'RC2-128-CBC':
+      dkLen = 16;
+      cipherFn = function(key) {
+        return forge.rc2.createDecryptionCipher(key, 128);
+      };
+      break;
+    default:
+      var error = new Error('Could not decrypt private key; unsupported ' +
+        'encryption algorithm "' + msg.dekInfo.algorithm + '".');
+      error.algorithm = msg.dekInfo.algorithm;
+      throw error;
+    }
+
+    // use OpenSSL legacy key derivation
+    var iv = forge.util.hexToBytes(msg.dekInfo.parameters);
+    var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);
+    var cipher = cipherFn(dk);
+    cipher.start(iv);
+    cipher.update(forge.util.createBuffer(msg.body));
+    if(cipher.finish()) {
+      rval = cipher.output.getBytes();
+    } else {
+      return rval;
+    }
+  } else {
+    rval = msg.body;
+  }
+
+  if(msg.type === 'ENCRYPTED PRIVATE KEY') {
+    rval = pki.decryptPrivateKeyInfo(asn1.fromDer(rval), password);
+  } else {
+    // decryption already performed above
+    rval = asn1.fromDer(rval);
+  }
+
+  if(rval !== null) {
+    rval = pki.privateKeyFromAsn1(rval);
+  }
+
+  return rval;
+};
+
+/**
+ * Derives a PKCS#12 key.
+ *
+ * @param password the password to derive the key material from, null or
+ *          undefined for none.
+ * @param salt the salt, as a ByteBuffer, to use.
+ * @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).
+ * @param iter the iteration count.
+ * @param n the number of bytes to derive from the password.
+ * @param md the message digest to use, defaults to SHA-1.
+ *
+ * @return a ByteBuffer with the bytes derived from the password.
+ */
+pki.pbe.generatePkcs12Key = function(password, salt, id, iter, n, md) {
+  var j, l;
+
+  if(typeof md === 'undefined' || md === null) {
+    if(!('sha1' in forge.md)) {
+      throw new Error('"sha1" hash algorithm unavailable.');
+    }
+    md = forge.md.sha1.create();
+  }
+
+  var u = md.digestLength;
+  var v = md.blockLength;
+  var result = new forge.util.ByteBuffer();
+
+  /* Convert password to Unicode byte buffer + trailing 0-byte. */
+  var passBuf = new forge.util.ByteBuffer();
+  if(password !== null && password !== undefined) {
+    for(l = 0; l < password.length; l++) {
+      passBuf.putInt16(password.charCodeAt(l));
+    }
+    passBuf.putInt16(0);
+  }
+
+  /* Length of salt and password in BYTES. */
+  var p = passBuf.length();
+  var s = salt.length();
+
+  /* 1. Construct a string, D (the "diversifier"), by concatenating
+        v copies of ID. */
+  var D = new forge.util.ByteBuffer();
+  D.fillWithByte(id, v);
+
+  /* 2. Concatenate copies of the salt together to create a string S of length
+        v * ceil(s / v) bytes (the final copy of the salt may be truncated
+        to create S).
+        Note that if the salt is the empty string, then so is S. */
+  var Slen = v * Math.ceil(s / v);
+  var S = new forge.util.ByteBuffer();
+  for(l = 0; l < Slen; l++) {
+    S.putByte(salt.at(l % s));
+  }
+
+  /* 3. Concatenate copies of the password together to create a string P of
+        length v * ceil(p / v) bytes (the final copy of the password may be
+        truncated to create P).
+        Note that if the password is the empty string, then so is P. */
+  var Plen = v * Math.ceil(p / v);
+  var P = new forge.util.ByteBuffer();
+  for(l = 0; l < Plen; l++) {
+    P.putByte(passBuf.at(l % p));
+  }
+
+  /* 4. Set I=S||P to be the concatenation of S and P. */
+  var I = S;
+  I.putBuffer(P);
+
+  /* 5. Set c=ceil(n / u). */
+  var c = Math.ceil(n / u);
+
+  /* 6. For i=1, 2, ..., c, do the following: */
+  for(var i = 1; i <= c; i++) {
+    /* a) Set Ai=H^r(D||I). (l.e. the rth hash of D||I, H(H(H(...H(D||I)))) */
+    var buf = new forge.util.ByteBuffer();
+    buf.putBytes(D.bytes());
+    buf.putBytes(I.bytes());
+    for(var round = 0; round < iter; round++) {
+      md.start();
+      md.update(buf.getBytes());
+      buf = md.digest();
+    }
+
+    /* b) Concatenate copies of Ai to create a string B of length v bytes (the
+          final copy of Ai may be truncated to create B). */
+    var B = new forge.util.ByteBuffer();
+    for(l = 0; l < v; l++) {
+      B.putByte(buf.at(l % u));
+    }
+
+    /* c) Treating I as a concatenation I0, I1, ..., Ik-1 of v-byte blocks,
+          where k=ceil(s / v) + ceil(p / v), modify I by setting
+          Ij=(Ij+B+1) mod 2v for each j.  */
+    var k = Math.ceil(s / v) + Math.ceil(p / v);
+    var Inew = new forge.util.ByteBuffer();
+    for(j = 0; j < k; j++) {
+      var chunk = new forge.util.ByteBuffer(I.getBytes(v));
+      var x = 0x1ff;
+      for(l = B.length() - 1; l >= 0; l--) {
+        x = x >> 8;
+        x += B.at(l) + chunk.at(l);
+        chunk.setAt(l, x & 0xff);
+      }
+      Inew.putBuffer(chunk);
+    }
+    I = Inew;
+
+    /* Add Ai to A. */
+    result.putBuffer(buf);
+  }
+
+  result.truncate(result.length() - n);
+  return result;
+};
+
+/**
+ * Get new Forge cipher object instance.
+ *
+ * @param oid the OID (in string notation).
+ * @param params the ASN.1 params object.
+ * @param password the password to decrypt with.
+ *
+ * @return new cipher object instance.
+ */
+pki.pbe.getCipher = function(oid, params, password) {
+  switch(oid) {
+  case pki.oids['pkcs5PBES2']:
+    return pki.pbe.getCipherForPBES2(oid, params, password);
+
+  case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
+  case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
+    return pki.pbe.getCipherForPKCS12PBE(oid, params, password);
+
+  default:
+    var error = new Error('Cannot read encrypted PBE data block. Unsupported OID.');
+    error.oid = oid;
+    error.supportedOids = [
+      'pkcs5PBES2',
+      'pbeWithSHAAnd3-KeyTripleDES-CBC',
+      'pbewithSHAAnd40BitRC2-CBC'
+    ];
+    throw error;
+  }
+};
+
+/**
+ * Get new Forge cipher object instance according to PBES2 params block.
+ *
+ * The returned cipher instance is already started using the IV
+ * from PBES2 parameter block.
+ *
+ * @param oid the PKCS#5 PBKDF2 OID (in string notation).
+ * @param params the ASN.1 PBES2-params object.
+ * @param password the password to decrypt with.
+ *
+ * @return new cipher object instance.
+ */
+pki.pbe.getCipherForPBES2 = function(oid, params, password) {
+  // get PBE params
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(params, PBES2AlgorithmsValidator, capture, errors)) {
+    var error = new Error('Cannot read password-based-encryption algorithm ' +
+      'parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
+    error.errors = errors;
+    throw error;
+  }
+
+  // check oids
+  oid = asn1.derToOid(capture.kdfOid);
+  if(oid !== pki.oids['pkcs5PBKDF2']) {
+    var error = new Error('Cannot read encrypted private key. ' +
+      'Unsupported key derivation function OID.');
+    error.oid = oid;
+    error.supportedOids = ['pkcs5PBKDF2'];
+    throw error;
+  }
+  oid = asn1.derToOid(capture.encOid);
+  if(oid !== pki.oids['aes128-CBC'] &&
+    oid !== pki.oids['aes192-CBC'] &&
+    oid !== pki.oids['aes256-CBC'] &&
+    oid !== pki.oids['des-EDE3-CBC'] &&
+    oid !== pki.oids['desCBC']) {
+    var error = new Error('Cannot read encrypted private key. ' +
+      'Unsupported encryption scheme OID.');
+    error.oid = oid;
+    error.supportedOids = [
+      'aes128-CBC', 'aes192-CBC', 'aes256-CBC', 'des-EDE3-CBC', 'desCBC'];
+    throw error;
+  }
+
+  // set PBE params
+  var salt = capture.kdfSalt;
+  var count = forge.util.createBuffer(capture.kdfIterationCount);
+  count = count.getInt(count.length() << 3);
+  var dkLen;
+  var cipherFn;
+  switch(pki.oids[oid]) {
+  case 'aes128-CBC':
+    dkLen = 16;
+    cipherFn = forge.aes.createDecryptionCipher;
+    break;
+  case 'aes192-CBC':
+    dkLen = 24;
+    cipherFn = forge.aes.createDecryptionCipher;
+    break;
+  case 'aes256-CBC':
+    dkLen = 32;
+    cipherFn = forge.aes.createDecryptionCipher;
+    break;
+  case 'des-EDE3-CBC':
+    dkLen = 24;
+    cipherFn = forge.des.createDecryptionCipher;
+    break;
+  case 'desCBC':
+    dkLen = 8;
+    cipherFn = forge.des.createDecryptionCipher;
+    break;
+  }
+
+  // get PRF message digest
+  var md = prfOidToMessageDigest(capture.prfOid);
+
+  // decrypt private key using pbe with chosen PRF and AES/DES
+  var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen, md);
+  var iv = capture.encIv;
+  var cipher = cipherFn(dk);
+  cipher.start(iv);
+
+  return cipher;
+};
+
+/**
+ * Get new Forge cipher object instance for PKCS#12 PBE.
+ *
+ * The returned cipher instance is already started using the key & IV
+ * derived from the provided password and PKCS#12 PBE salt.
+ *
+ * @param oid The PKCS#12 PBE OID (in string notation).
+ * @param params The ASN.1 PKCS#12 PBE-params object.
+ * @param password The password to decrypt with.
+ *
+ * @return the new cipher object instance.
+ */
+pki.pbe.getCipherForPKCS12PBE = function(oid, params, password) {
+  // get PBE params
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(params, pkcs12PbeParamsValidator, capture, errors)) {
+    var error = new Error('Cannot read password-based-encryption algorithm ' +
+      'parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
+    error.errors = errors;
+    throw error;
+  }
+
+  var salt = forge.util.createBuffer(capture.salt);
+  var count = forge.util.createBuffer(capture.iterations);
+  count = count.getInt(count.length() << 3);
+
+  var dkLen, dIvLen, cipherFn;
+  switch(oid) {
+    case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
+      dkLen = 24;
+      dIvLen = 8;
+      cipherFn = forge.des.startDecrypting;
+      break;
+
+    case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
+      dkLen = 5;
+      dIvLen = 8;
+      cipherFn = function(key, iv) {
+        var cipher = forge.rc2.createDecryptionCipher(key, 40);
+        cipher.start(iv, null);
+        return cipher;
+      };
+      break;
+
+    default:
+      var error = new Error('Cannot read PKCS #12 PBE data block. Unsupported OID.');
+      error.oid = oid;
+      throw error;
+  }
+
+  // get PRF message digest
+  var md = prfOidToMessageDigest(capture.prfOid);
+  var key = pki.pbe.generatePkcs12Key(password, salt, 1, count, dkLen, md);
+  md.start();
+  var iv = pki.pbe.generatePkcs12Key(password, salt, 2, count, dIvLen, md);
+
+  return cipherFn(key, iv);
+};
+
+/**
+ * OpenSSL's legacy key derivation function.
+ *
+ * See: http://www.openssl.org/docs/crypto/EVP_BytesToKey.html
+ *
+ * @param password the password to derive the key from.
+ * @param salt the salt to use, null for none.
+ * @param dkLen the number of bytes needed for the derived key.
+ * @param [options] the options to use:
+ *          [md] an optional message digest object to use.
+ */
+pki.pbe.opensslDeriveBytes = function(password, salt, dkLen, md) {
+  if(typeof md === 'undefined' || md === null) {
+    if(!('md5' in forge.md)) {
+      throw new Error('"md5" hash algorithm unavailable.');
+    }
+    md = forge.md.md5.create();
+  }
+  if(salt === null) {
+    salt = '';
+  }
+  var digests = [hash(md, password + salt)];
+  for(var length = 16, i = 1; length < dkLen; ++i, length += 16) {
+    digests.push(hash(md, digests[i - 1] + password + salt));
+  }
+  return digests.join('').substr(0, dkLen);
+};
+
+function hash(md, bytes) {
+  return md.start().update(bytes).digest().getBytes();
+}
+
+function prfOidToMessageDigest(prfOid) {
+  // get PRF algorithm, default to SHA-1
+  var prfAlgorithm;
+  if(!prfOid) {
+    prfAlgorithm = 'hmacWithSHA1';
+  } else {
+    prfAlgorithm = pki.oids[asn1.derToOid(prfOid)];
+    if(!prfAlgorithm) {
+      var error = new Error('Unsupported PRF OID.');
+      error.oid = prfOid;
+      error.supported = [
+        'hmacWithSHA1', 'hmacWithSHA224', 'hmacWithSHA256', 'hmacWithSHA384',
+        'hmacWithSHA512'];
+      throw error;
+    }
+  }
+  return prfAlgorithmToMessageDigest(prfAlgorithm);
+}
+
+function prfAlgorithmToMessageDigest(prfAlgorithm) {
+  var factory = forge.md;
+  switch(prfAlgorithm) {
+  case 'hmacWithSHA224':
+    factory = forge.md.sha512;
+  case 'hmacWithSHA1':
+  case 'hmacWithSHA256':
+  case 'hmacWithSHA384':
+  case 'hmacWithSHA512':
+    prfAlgorithm = prfAlgorithm.substr(8).toLowerCase();
+    break;
+  default:
+    var error = new Error('Unsupported PRF algorithm.');
+    error.algorithm = prfAlgorithm;
+    error.supported = [
+      'hmacWithSHA1', 'hmacWithSHA224', 'hmacWithSHA256', 'hmacWithSHA384',
+      'hmacWithSHA512'];
+    throw error;
+  }
+  if(!factory || !(prfAlgorithm in factory)) {
+    throw new Error('Unknown hash algorithm: ' + prfAlgorithm);
+  }
+  return factory[prfAlgorithm].create();
+}
+
+function createPbkdf2Params(salt, countBytes, dkLen, prfAlgorithm) {
+  var params = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // salt
+    asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
+    // iteration count
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      countBytes.getBytes())
+  ]);
+  // when PRF algorithm is not SHA-1 default, add key length and PRF algorithm
+  if(prfAlgorithm !== 'hmacWithSHA1') {
+    params.value.push(
+      // key length
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+        forge.util.hexToBytes(dkLen.toString(16))),
+      // AlgorithmIdentifier
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // algorithm
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          asn1.oidToDer(pki.oids[prfAlgorithm]).getBytes()),
+        // parameters (null)
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+      ]));
+  }
+  return params;
+}
Index: frontend/node_modules/node-forge/lib/pbkdf2.js
===================================================================
--- frontend/node_modules/node-forge/lib/pbkdf2.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pbkdf2.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,211 @@
+/**
+ * Password-Based Key-Derivation Function #2 implementation.
+ *
+ * See RFC 2898 for details.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2013 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./hmac');
+require('./md');
+require('./util');
+
+var pkcs5 = forge.pkcs5 = forge.pkcs5 || {};
+
+var crypto;
+if(forge.util.isNodejs && !forge.options.usePureJavaScript) {
+  crypto = require('crypto');
+}
+
+/**
+ * Derives a key from a password.
+ *
+ * @param p the password as a binary-encoded string of bytes.
+ * @param s the salt as a binary-encoded string of bytes.
+ * @param c the iteration count, a positive integer.
+ * @param dkLen the intended length, in bytes, of the derived key,
+ *          (max: 2^32 - 1) * hash length of the PRF.
+ * @param [md] the message digest (or algorithm identifier as a string) to use
+ *          in the PRF, defaults to SHA-1.
+ * @param [callback(err, key)] presence triggers asynchronous version, called
+ *          once the operation completes.
+ *
+ * @return the derived key, as a binary-encoded string of bytes, for the
+ *           synchronous version (if no callback is specified).
+ */
+module.exports = forge.pbkdf2 = pkcs5.pbkdf2 = function(
+  p, s, c, dkLen, md, callback) {
+  if(typeof md === 'function') {
+    callback = md;
+    md = null;
+  }
+
+  // use native implementation if possible and not disabled, note that
+  // some node versions only support SHA-1, others allow digest to be changed
+  if(forge.util.isNodejs && !forge.options.usePureJavaScript &&
+    crypto.pbkdf2 && (md === null || typeof md !== 'object') &&
+    (crypto.pbkdf2Sync.length > 4 || (!md || md === 'sha1'))) {
+    if(typeof md !== 'string') {
+      // default prf to SHA-1
+      md = 'sha1';
+    }
+    p = Buffer.from(p, 'binary');
+    s = Buffer.from(s, 'binary');
+    if(!callback) {
+      if(crypto.pbkdf2Sync.length === 4) {
+        return crypto.pbkdf2Sync(p, s, c, dkLen).toString('binary');
+      }
+      return crypto.pbkdf2Sync(p, s, c, dkLen, md).toString('binary');
+    }
+    if(crypto.pbkdf2Sync.length === 4) {
+      return crypto.pbkdf2(p, s, c, dkLen, function(err, key) {
+        if(err) {
+          return callback(err);
+        }
+        callback(null, key.toString('binary'));
+      });
+    }
+    return crypto.pbkdf2(p, s, c, dkLen, md, function(err, key) {
+      if(err) {
+        return callback(err);
+      }
+      callback(null, key.toString('binary'));
+    });
+  }
+
+  if(typeof md === 'undefined' || md === null) {
+    // default prf to SHA-1
+    md = 'sha1';
+  }
+  if(typeof md === 'string') {
+    if(!(md in forge.md.algorithms)) {
+      throw new Error('Unknown hash algorithm: ' + md);
+    }
+    md = forge.md[md].create();
+  }
+
+  var hLen = md.digestLength;
+
+  /* 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and
+    stop. */
+  if(dkLen > (0xFFFFFFFF * hLen)) {
+    var err = new Error('Derived key is too long.');
+    if(callback) {
+      return callback(err);
+    }
+    throw err;
+  }
+
+  /* 2. Let len be the number of hLen-octet blocks in the derived key,
+    rounding up, and let r be the number of octets in the last
+    block:
+
+    len = CEIL(dkLen / hLen),
+    r = dkLen - (len - 1) * hLen. */
+  var len = Math.ceil(dkLen / hLen);
+  var r = dkLen - (len - 1) * hLen;
+
+  /* 3. For each block of the derived key apply the function F defined
+    below to the password P, the salt S, the iteration count c, and
+    the block index to compute the block:
+
+    T_1 = F(P, S, c, 1),
+    T_2 = F(P, S, c, 2),
+    ...
+    T_len = F(P, S, c, len),
+
+    where the function F is defined as the exclusive-or sum of the
+    first c iterates of the underlying pseudorandom function PRF
+    applied to the password P and the concatenation of the salt S
+    and the block index i:
+
+    F(P, S, c, i) = u_1 XOR u_2 XOR ... XOR u_c
+
+    where
+
+    u_1 = PRF(P, S || INT(i)),
+    u_2 = PRF(P, u_1),
+    ...
+    u_c = PRF(P, u_{c-1}).
+
+    Here, INT(i) is a four-octet encoding of the integer i, most
+    significant octet first. */
+  var prf = forge.hmac.create();
+  prf.start(md, p);
+  var dk = '';
+  var xor, u_c, u_c1;
+
+  // sync version
+  if(!callback) {
+    for(var i = 1; i <= len; ++i) {
+      // PRF(P, S || INT(i)) (first iteration)
+      prf.start(null, null);
+      prf.update(s);
+      prf.update(forge.util.int32ToBytes(i));
+      xor = u_c1 = prf.digest().getBytes();
+
+      // PRF(P, u_{c-1}) (other iterations)
+      for(var j = 2; j <= c; ++j) {
+        prf.start(null, null);
+        prf.update(u_c1);
+        u_c = prf.digest().getBytes();
+        // F(p, s, c, i)
+        xor = forge.util.xorBytes(xor, u_c, hLen);
+        u_c1 = u_c;
+      }
+
+      /* 4. Concatenate the blocks and extract the first dkLen octets to
+        produce a derived key DK:
+
+        DK = T_1 || T_2 ||  ...  || T_len<0..r-1> */
+      dk += (i < len) ? xor : xor.substr(0, r);
+    }
+    /* 5. Output the derived key DK. */
+    return dk;
+  }
+
+  // async version
+  var i = 1, j;
+  function outer() {
+    if(i > len) {
+      // done
+      return callback(null, dk);
+    }
+
+    // PRF(P, S || INT(i)) (first iteration)
+    prf.start(null, null);
+    prf.update(s);
+    prf.update(forge.util.int32ToBytes(i));
+    xor = u_c1 = prf.digest().getBytes();
+
+    // PRF(P, u_{c-1}) (other iterations)
+    j = 2;
+    inner();
+  }
+
+  function inner() {
+    if(j <= c) {
+      prf.start(null, null);
+      prf.update(u_c1);
+      u_c = prf.digest().getBytes();
+      // F(p, s, c, i)
+      xor = forge.util.xorBytes(xor, u_c, hLen);
+      u_c1 = u_c;
+      ++j;
+      return forge.util.setImmediate(inner);
+    }
+
+    /* 4. Concatenate the blocks and extract the first dkLen octets to
+      produce a derived key DK:
+
+      DK = T_1 || T_2 ||  ...  || T_len<0..r-1> */
+    dk += (i < len) ? xor : xor.substr(0, r);
+
+    ++i;
+    outer();
+  }
+
+  outer();
+};
Index: frontend/node_modules/node-forge/lib/pem.js
===================================================================
--- frontend/node_modules/node-forge/lib/pem.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pem.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,237 @@
+/**
+ * Javascript implementation of basic PEM (Privacy Enhanced Mail) algorithms.
+ *
+ * See: RFC 1421.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2013-2014 Digital Bazaar, Inc.
+ *
+ * A Forge PEM object has the following fields:
+ *
+ * type: identifies the type of message (eg: "RSA PRIVATE KEY").
+ *
+ * procType: identifies the type of processing performed on the message,
+ *   it has two subfields: version and type, eg: 4,ENCRYPTED.
+ *
+ * contentDomain: identifies the type of content in the message, typically
+ *   only uses the value: "RFC822".
+ *
+ * dekInfo: identifies the message encryption algorithm and mode and includes
+ *   any parameters for the algorithm, it has two subfields: algorithm and
+ *   parameters, eg: DES-CBC,F8143EDE5960C597.
+ *
+ * headers: contains all other PEM encapsulated headers -- where order is
+ *   significant (for pairing data like recipient ID + key info).
+ *
+ * body: the binary-encoded body.
+ */
+var forge = require('./forge');
+require('./util');
+
+// shortcut for pem API
+var pem = module.exports = forge.pem = forge.pem || {};
+
+/**
+ * Encodes (serializes) the given PEM object.
+ *
+ * @param msg the PEM message object to encode.
+ * @param options the options to use:
+ *          maxline the maximum characters per line for the body, (default: 64).
+ *
+ * @return the PEM-formatted string.
+ */
+pem.encode = function(msg, options) {
+  options = options || {};
+  var rval = '-----BEGIN ' + msg.type + '-----\r\n';
+
+  // encode special headers
+  var header;
+  if(msg.procType) {
+    header = {
+      name: 'Proc-Type',
+      values: [String(msg.procType.version), msg.procType.type]
+    };
+    rval += foldHeader(header);
+  }
+  if(msg.contentDomain) {
+    header = {name: 'Content-Domain', values: [msg.contentDomain]};
+    rval += foldHeader(header);
+  }
+  if(msg.dekInfo) {
+    header = {name: 'DEK-Info', values: [msg.dekInfo.algorithm]};
+    if(msg.dekInfo.parameters) {
+      header.values.push(msg.dekInfo.parameters);
+    }
+    rval += foldHeader(header);
+  }
+
+  if(msg.headers) {
+    // encode all other headers
+    for(var i = 0; i < msg.headers.length; ++i) {
+      rval += foldHeader(msg.headers[i]);
+    }
+  }
+
+  // terminate header
+  if(msg.procType) {
+    rval += '\r\n';
+  }
+
+  // add body
+  rval += forge.util.encode64(msg.body, options.maxline || 64) + '\r\n';
+
+  rval += '-----END ' + msg.type + '-----\r\n';
+  return rval;
+};
+
+/**
+ * Decodes (deserializes) all PEM messages found in the given string.
+ *
+ * @param str the PEM-formatted string to decode.
+ *
+ * @return the PEM message objects in an array.
+ */
+pem.decode = function(str) {
+  var rval = [];
+
+  // split string into PEM messages (be lenient w/EOF on BEGIN line)
+  var rMessage = /\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g;
+  var rHeader = /([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/;
+  var rCRLF = /\r?\n/;
+  var match;
+  while(true) {
+    match = rMessage.exec(str);
+    if(!match) {
+      break;
+    }
+
+    // accept "NEW CERTIFICATE REQUEST" as "CERTIFICATE REQUEST"
+    // https://datatracker.ietf.org/doc/html/rfc7468#section-7
+    var type = match[1];
+    if(type === 'NEW CERTIFICATE REQUEST') {
+      type = 'CERTIFICATE REQUEST';
+    }
+
+    var msg = {
+      type: type,
+      procType: null,
+      contentDomain: null,
+      dekInfo: null,
+      headers: [],
+      body: forge.util.decode64(match[3])
+    };
+    rval.push(msg);
+
+    // no headers
+    if(!match[2]) {
+      continue;
+    }
+
+    // parse headers
+    var lines = match[2].split(rCRLF);
+    var li = 0;
+    while(match && li < lines.length) {
+      // get line, trim any rhs whitespace
+      var line = lines[li].replace(/\s+$/, '');
+
+      // RFC2822 unfold any following folded lines
+      for(var nl = li + 1; nl < lines.length; ++nl) {
+        var next = lines[nl];
+        if(!/\s/.test(next[0])) {
+          break;
+        }
+        line += next;
+        li = nl;
+      }
+
+      // parse header
+      match = line.match(rHeader);
+      if(match) {
+        var header = {name: match[1], values: []};
+        var values = match[2].split(',');
+        for(var vi = 0; vi < values.length; ++vi) {
+          header.values.push(ltrim(values[vi]));
+        }
+
+        // Proc-Type must be the first header
+        if(!msg.procType) {
+          if(header.name !== 'Proc-Type') {
+            throw new Error('Invalid PEM formatted message. The first ' +
+              'encapsulated header must be "Proc-Type".');
+          } else if(header.values.length !== 2) {
+            throw new Error('Invalid PEM formatted message. The "Proc-Type" ' +
+              'header must have two subfields.');
+          }
+          msg.procType = {version: values[0], type: values[1]};
+        } else if(!msg.contentDomain && header.name === 'Content-Domain') {
+          // special-case Content-Domain
+          msg.contentDomain = values[0] || '';
+        } else if(!msg.dekInfo && header.name === 'DEK-Info') {
+          // special-case DEK-Info
+          if(header.values.length === 0) {
+            throw new Error('Invalid PEM formatted message. The "DEK-Info" ' +
+              'header must have at least one subfield.');
+          }
+          msg.dekInfo = {algorithm: values[0], parameters: values[1] || null};
+        } else {
+          msg.headers.push(header);
+        }
+      }
+
+      ++li;
+    }
+
+    if(msg.procType === 'ENCRYPTED' && !msg.dekInfo) {
+      throw new Error('Invalid PEM formatted message. The "DEK-Info" ' +
+        'header must be present if "Proc-Type" is "ENCRYPTED".');
+    }
+  }
+
+  if(rval.length === 0) {
+    throw new Error('Invalid PEM formatted message.');
+  }
+
+  return rval;
+};
+
+function foldHeader(header) {
+  var rval = header.name + ': ';
+
+  // ensure values with CRLF are folded
+  var values = [];
+  var insertSpace = function(match, $1) {
+    return ' ' + $1;
+  };
+  for(var i = 0; i < header.values.length; ++i) {
+    values.push(header.values[i].replace(/^(\S+\r\n)/, insertSpace));
+  }
+  rval += values.join(',') + '\r\n';
+
+  // do folding
+  var length = 0;
+  var candidate = -1;
+  for(var i = 0; i < rval.length; ++i, ++length) {
+    if(length > 65 && candidate !== -1) {
+      var insert = rval[candidate];
+      if(insert === ',') {
+        ++candidate;
+        rval = rval.substr(0, candidate) + '\r\n ' + rval.substr(candidate);
+      } else {
+        rval = rval.substr(0, candidate) +
+          '\r\n' + insert + rval.substr(candidate + 1);
+      }
+      length = (i - candidate - 1);
+      candidate = -1;
+      ++i;
+    } else if(rval[i] === ' ' || rval[i] === '\t' || rval[i] === ',') {
+      candidate = i;
+    }
+  }
+
+  return rval;
+}
+
+function ltrim(str) {
+  return str.replace(/^\s+/, '');
+}
Index: frontend/node_modules/node-forge/lib/pkcs1.js
===================================================================
--- frontend/node_modules/node-forge/lib/pkcs1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pkcs1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,276 @@
+/**
+ * Partial implementation of PKCS#1 v2.2: RSA-OEAP
+ *
+ * Modified but based on the following MIT and BSD licensed code:
+ *
+ * https://github.com/kjur/jsjws/blob/master/rsa.js:
+ *
+ * The 'jsjws'(JSON Web Signature JavaScript Library) License
+ *
+ * Copyright (c) 2012 Kenji Urushima
+ *
+ * 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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * http://webrsa.cvs.sourceforge.net/viewvc/webrsa/Client/RSAES-OAEP.js?content-type=text%2Fplain:
+ *
+ * RSAES-OAEP.js
+ * $Id: RSAES-OAEP.js,v 1.1.1.1 2003/03/19 15:37:20 ellispritchard Exp $
+ * JavaScript Implementation of PKCS #1 v2.1 RSA CRYPTOGRAPHY STANDARD (RSA Laboratories, June 14, 2002)
+ * Copyright (C) Ellis Pritchard, Guardian Unlimited 2003.
+ * Contact: ellis@nukinetics.com
+ * Distributed under the BSD License.
+ *
+ * Official documentation: http://www.rsa.com/rsalabs/node.asp?id=2125
+ *
+ * @author Evan Jones (http://evanjones.ca/)
+ * @author Dave Longley
+ *
+ * Copyright (c) 2013-2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./util');
+require('./random');
+require('./sha1');
+
+// shortcut for PKCS#1 API
+var pkcs1 = module.exports = forge.pkcs1 = forge.pkcs1 || {};
+
+/**
+ * Encode the given RSAES-OAEP message (M) using key, with optional label (L)
+ * and seed.
+ *
+ * This method does not perform RSA encryption, it only encodes the message
+ * using RSAES-OAEP.
+ *
+ * @param key the RSA key to use.
+ * @param message the message to encode.
+ * @param options the options to use:
+ *          label an optional label to use.
+ *          seed the seed to use.
+ *          md the message digest object to use, undefined for SHA-1.
+ *          mgf1 optional mgf1 parameters:
+ *            md the message digest object to use for MGF1.
+ *
+ * @return the encoded message bytes.
+ */
+pkcs1.encode_rsa_oaep = function(key, message, options) {
+  // parse arguments
+  var label;
+  var seed;
+  var md;
+  var mgf1Md;
+  // legacy args (label, seed, md)
+  if(typeof options === 'string') {
+    label = options;
+    seed = arguments[3] || undefined;
+    md = arguments[4] || undefined;
+  } else if(options) {
+    label = options.label || undefined;
+    seed = options.seed || undefined;
+    md = options.md || undefined;
+    if(options.mgf1 && options.mgf1.md) {
+      mgf1Md = options.mgf1.md;
+    }
+  }
+
+  // default OAEP to SHA-1 message digest
+  if(!md) {
+    md = forge.md.sha1.create();
+  } else {
+    md.start();
+  }
+
+  // default MGF-1 to same as OAEP
+  if(!mgf1Md) {
+    mgf1Md = md;
+  }
+
+  // compute length in bytes and check output
+  var keyLength = Math.ceil(key.n.bitLength() / 8);
+  var maxLength = keyLength - 2 * md.digestLength - 2;
+  if(message.length > maxLength) {
+    var error = new Error('RSAES-OAEP input message length is too long.');
+    error.length = message.length;
+    error.maxLength = maxLength;
+    throw error;
+  }
+
+  if(!label) {
+    label = '';
+  }
+  md.update(label, 'raw');
+  var lHash = md.digest();
+
+  var PS = '';
+  var PS_length = maxLength - message.length;
+  for(var i = 0; i < PS_length; i++) {
+    PS += '\x00';
+  }
+
+  var DB = lHash.getBytes() + PS + '\x01' + message;
+
+  if(!seed) {
+    seed = forge.random.getBytes(md.digestLength);
+  } else if(seed.length !== md.digestLength) {
+    var error = new Error('Invalid RSAES-OAEP seed. The seed length must ' +
+      'match the digest length.');
+    error.seedLength = seed.length;
+    error.digestLength = md.digestLength;
+    throw error;
+  }
+
+  var dbMask = rsa_mgf1(seed, keyLength - md.digestLength - 1, mgf1Md);
+  var maskedDB = forge.util.xorBytes(DB, dbMask, DB.length);
+
+  var seedMask = rsa_mgf1(maskedDB, md.digestLength, mgf1Md);
+  var maskedSeed = forge.util.xorBytes(seed, seedMask, seed.length);
+
+  // return encoded message
+  return '\x00' + maskedSeed + maskedDB;
+};
+
+/**
+ * Decode the given RSAES-OAEP encoded message (EM) using key, with optional
+ * label (L).
+ *
+ * This method does not perform RSA decryption, it only decodes the message
+ * using RSAES-OAEP.
+ *
+ * @param key the RSA key to use.
+ * @param em the encoded message to decode.
+ * @param options the options to use:
+ *          label an optional label to use.
+ *          md the message digest object to use for OAEP, undefined for SHA-1.
+ *          mgf1 optional mgf1 parameters:
+ *            md the message digest object to use for MGF1.
+ *
+ * @return the decoded message bytes.
+ */
+pkcs1.decode_rsa_oaep = function(key, em, options) {
+  // parse args
+  var label;
+  var md;
+  var mgf1Md;
+  // legacy args
+  if(typeof options === 'string') {
+    label = options;
+    md = arguments[3] || undefined;
+  } else if(options) {
+    label = options.label || undefined;
+    md = options.md || undefined;
+    if(options.mgf1 && options.mgf1.md) {
+      mgf1Md = options.mgf1.md;
+    }
+  }
+
+  // compute length in bytes
+  var keyLength = Math.ceil(key.n.bitLength() / 8);
+
+  if(em.length !== keyLength) {
+    var error = new Error('RSAES-OAEP encoded message length is invalid.');
+    error.length = em.length;
+    error.expectedLength = keyLength;
+    throw error;
+  }
+
+  // default OAEP to SHA-1 message digest
+  if(md === undefined) {
+    md = forge.md.sha1.create();
+  } else {
+    md.start();
+  }
+
+  // default MGF-1 to same as OAEP
+  if(!mgf1Md) {
+    mgf1Md = md;
+  }
+
+  if(keyLength < 2 * md.digestLength + 2) {
+    throw new Error('RSAES-OAEP key is too short for the hash function.');
+  }
+
+  if(!label) {
+    label = '';
+  }
+  md.update(label, 'raw');
+  var lHash = md.digest().getBytes();
+
+  // split the message into its parts
+  var y = em.charAt(0);
+  var maskedSeed = em.substring(1, md.digestLength + 1);
+  var maskedDB = em.substring(1 + md.digestLength);
+
+  var seedMask = rsa_mgf1(maskedDB, md.digestLength, mgf1Md);
+  var seed = forge.util.xorBytes(maskedSeed, seedMask, maskedSeed.length);
+
+  var dbMask = rsa_mgf1(seed, keyLength - md.digestLength - 1, mgf1Md);
+  var db = forge.util.xorBytes(maskedDB, dbMask, maskedDB.length);
+
+  var lHashPrime = db.substring(0, md.digestLength);
+
+  // constant time check that all values match what is expected
+  var error = (y !== '\x00');
+
+  // constant time check lHash vs lHashPrime
+  for(var i = 0; i < md.digestLength; ++i) {
+    error |= (lHash.charAt(i) !== lHashPrime.charAt(i));
+  }
+
+  // "constant time" find the 0x1 byte separating the padding (zeros) from the
+  // message
+  // TODO: It must be possible to do this in a better/smarter way?
+  var in_ps = 1;
+  var index = md.digestLength;
+  for(var j = md.digestLength; j < db.length; j++) {
+    var code = db.charCodeAt(j);
+
+    var is_0 = (code & 0x1) ^ 0x1;
+
+    // non-zero if not 0 or 1 in the ps section
+    var error_mask = in_ps ? 0xfffe : 0x0000;
+    error |= (code & error_mask);
+
+    // latch in_ps to zero after we find 0x1
+    in_ps = in_ps & is_0;
+    index += in_ps;
+  }
+
+  if(error || db.charCodeAt(index) !== 0x1) {
+    throw new Error('Invalid RSAES-OAEP padding.');
+  }
+
+  return db.substring(index + 1);
+};
+
+function rsa_mgf1(seed, maskLength, hash) {
+  // default to SHA-1 message digest
+  if(!hash) {
+    hash = forge.md.sha1.create();
+  }
+  var t = '';
+  var count = Math.ceil(maskLength / hash.digestLength);
+  for(var i = 0; i < count; ++i) {
+    var c = String.fromCharCode(
+      (i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
+    hash.start();
+    hash.update(seed + c);
+    t += hash.digest().getBytes();
+  }
+  return t.substring(0, maskLength);
+}
Index: frontend/node_modules/node-forge/lib/pkcs12.js
===================================================================
--- frontend/node_modules/node-forge/lib/pkcs12.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pkcs12.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1078 @@
+/**
+ * Javascript implementation of PKCS#12.
+ *
+ * @author Dave Longley
+ * @author Stefan Siegl <stesie@brokenpipe.de>
+ *
+ * Copyright (c) 2010-2014 Digital Bazaar, Inc.
+ * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
+ *
+ * The ASN.1 representation of PKCS#12 is as follows
+ * (see ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12-tc1.pdf for details)
+ *
+ * PFX ::= SEQUENCE {
+ *   version  INTEGER {v3(3)}(v3,...),
+ *   authSafe ContentInfo,
+ *   macData  MacData OPTIONAL
+ * }
+ *
+ * MacData ::= SEQUENCE {
+ *   mac DigestInfo,
+ *   macSalt OCTET STRING,
+ *   iterations INTEGER DEFAULT 1
+ * }
+ * Note: The iterations default is for historical reasons and its use is
+ * deprecated. A higher value, like 1024, is recommended.
+ *
+ * DigestInfo is defined in PKCS#7 as follows:
+ *
+ * DigestInfo ::= SEQUENCE {
+ *   digestAlgorithm DigestAlgorithmIdentifier,
+ *   digest Digest
+ * }
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
+ * for the algorithm, if any. In the case of SHA1 there is none.
+ *
+ * AlgorithmIdentifer ::= SEQUENCE {
+ *    algorithm OBJECT IDENTIFIER,
+ *    parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ *
+ * Digest ::= OCTET STRING
+ *
+ *
+ * ContentInfo ::= SEQUENCE {
+ *   contentType ContentType,
+ *   content     [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
+ * }
+ *
+ * ContentType ::= OBJECT IDENTIFIER
+ *
+ * AuthenticatedSafe ::= SEQUENCE OF ContentInfo
+ * -- Data if unencrypted
+ * -- EncryptedData if password-encrypted
+ * -- EnvelopedData if public key-encrypted
+ *
+ *
+ * SafeContents ::= SEQUENCE OF SafeBag
+ *
+ * SafeBag ::= SEQUENCE {
+ *   bagId     BAG-TYPE.&id ({PKCS12BagSet})
+ *   bagValue  [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
+ *   bagAttributes SET OF PKCS12Attribute OPTIONAL
+ * }
+ *
+ * PKCS12Attribute ::= SEQUENCE {
+ *   attrId ATTRIBUTE.&id ({PKCS12AttrSet}),
+ *   attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId})
+ * } -- This type is compatible with the X.500 type 'Attribute'
+ *
+ * PKCS12AttrSet ATTRIBUTE ::= {
+ *   friendlyName | -- from PKCS #9
+ *   localKeyId, -- from PKCS #9
+ *   ... -- Other attributes are allowed
+ * }
+ *
+ * CertBag ::= SEQUENCE {
+ *   certId    BAG-TYPE.&id   ({CertTypes}),
+ *   certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId})
+ * }
+ *
+ * x509Certificate BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}}
+ *   -- DER-encoded X.509 certificate stored in OCTET STRING
+ *
+ * sdsiCertificate BAG-TYPE ::= {IA5String IDENTIFIED BY {certTypes 2}}
+ * -- Base64-encoded SDSI certificate stored in IA5String
+ *
+ * CertTypes BAG-TYPE ::= {
+ *   x509Certificate |
+ *   sdsiCertificate,
+ *   ... -- For future extensions
+ * }
+ */
+var forge = require('./forge');
+require('./asn1');
+require('./hmac');
+require('./oids');
+require('./pkcs7asn1');
+require('./pbe');
+require('./random');
+require('./rsa');
+require('./sha1');
+require('./util');
+require('./x509');
+
+// shortcut for asn.1 & PKI API
+var asn1 = forge.asn1;
+var pki = forge.pki;
+
+// shortcut for PKCS#12 API
+var p12 = module.exports = forge.pkcs12 = forge.pkcs12 || {};
+
+var contentInfoValidator = {
+  name: 'ContentInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,  // a ContentInfo
+  constructed: true,
+  value: [{
+    name: 'ContentInfo.contentType',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OID,
+    constructed: false,
+    capture: 'contentType'
+  }, {
+    name: 'ContentInfo.content',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    constructed: true,
+    captureAsn1: 'content'
+  }]
+};
+
+var pfxValidator = {
+  name: 'PFX',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'PFX.version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'version'
+  },
+  contentInfoValidator, {
+    name: 'PFX.macData',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    optional: true,
+    captureAsn1: 'mac',
+    value: [{
+      name: 'PFX.macData.mac',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,  // DigestInfo
+      constructed: true,
+      value: [{
+        name: 'PFX.macData.mac.digestAlgorithm',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.SEQUENCE,  // DigestAlgorithmIdentifier
+        constructed: true,
+        value: [{
+          name: 'PFX.macData.mac.digestAlgorithm.algorithm',
+          tagClass: asn1.Class.UNIVERSAL,
+          type: asn1.Type.OID,
+          constructed: false,
+          capture: 'macAlgorithm'
+        }, {
+          name: 'PFX.macData.mac.digestAlgorithm.parameters',
+          optional: true,
+          tagClass: asn1.Class.UNIVERSAL,
+          captureAsn1: 'macAlgorithmParameters'
+        }]
+      }, {
+        name: 'PFX.macData.mac.digest',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.OCTETSTRING,
+        constructed: false,
+        capture: 'macDigest'
+      }]
+    }, {
+      name: 'PFX.macData.macSalt',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OCTETSTRING,
+      constructed: false,
+      capture: 'macSalt'
+    }, {
+      name: 'PFX.macData.iterations',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.INTEGER,
+      constructed: false,
+      optional: true,
+      capture: 'macIterations'
+    }]
+  }]
+};
+
+var safeBagValidator = {
+  name: 'SafeBag',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'SafeBag.bagId',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OID,
+    constructed: false,
+    capture: 'bagId'
+  }, {
+    name: 'SafeBag.bagValue',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    constructed: true,
+    captureAsn1: 'bagValue'
+  }, {
+    name: 'SafeBag.bagAttributes',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SET,
+    constructed: true,
+    optional: true,
+    capture: 'bagAttributes'
+  }]
+};
+
+var attributeValidator = {
+  name: 'Attribute',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'Attribute.attrId',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OID,
+    constructed: false,
+    capture: 'oid'
+  }, {
+    name: 'Attribute.attrValues',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SET,
+    constructed: true,
+    capture: 'values'
+  }]
+};
+
+var certBagValidator = {
+  name: 'CertBag',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'CertBag.certId',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OID,
+    constructed: false,
+    capture: 'certId'
+  }, {
+    name: 'CertBag.certValue',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    constructed: true,
+    /* So far we only support X.509 certificates (which are wrapped in
+       an OCTET STRING, hence hard code that here). */
+    value: [{
+      name: 'CertBag.certValue[0]',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Class.OCTETSTRING,
+      constructed: false,
+      capture: 'cert'
+    }]
+  }]
+};
+
+/**
+ * Search SafeContents structure for bags with matching attributes.
+ *
+ * The search can optionally be narrowed by a certain bag type.
+ *
+ * @param safeContents the SafeContents structure to search in.
+ * @param attrName the name of the attribute to compare against.
+ * @param attrValue the attribute value to search for.
+ * @param [bagType] bag type to narrow search by.
+ *
+ * @return an array of matching bags.
+ */
+function _getBagsByAttribute(safeContents, attrName, attrValue, bagType) {
+  var result = [];
+
+  for(var i = 0; i < safeContents.length; i++) {
+    for(var j = 0; j < safeContents[i].safeBags.length; j++) {
+      var bag = safeContents[i].safeBags[j];
+      if(bagType !== undefined && bag.type !== bagType) {
+        continue;
+      }
+      // only filter by bag type, no attribute specified
+      if(attrName === null) {
+        result.push(bag);
+        continue;
+      }
+      if(bag.attributes[attrName] !== undefined &&
+        bag.attributes[attrName].indexOf(attrValue) >= 0) {
+        result.push(bag);
+      }
+    }
+  }
+
+  return result;
+}
+
+/**
+ * Converts a PKCS#12 PFX in ASN.1 notation into a PFX object.
+ *
+ * @param obj The PKCS#12 PFX in ASN.1 notation.
+ * @param strict true to use strict DER decoding, false not to (default: true).
+ * @param {String} password Password to decrypt with (optional).
+ *
+ * @return PKCS#12 PFX object.
+ */
+p12.pkcs12FromAsn1 = function(obj, strict, password) {
+  // handle args
+  if(typeof strict === 'string') {
+    password = strict;
+    strict = true;
+  } else if(strict === undefined) {
+    strict = true;
+  }
+
+  // validate PFX and capture data
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, pfxValidator, capture, errors)) {
+    var error = new Error('Cannot read PKCS#12 PFX. ' +
+      'ASN.1 object is not an PKCS#12 PFX.');
+    error.errors = error;
+    throw error;
+  }
+
+  var pfx = {
+    version: capture.version.charCodeAt(0),
+    safeContents: [],
+
+    /**
+     * Gets bags with matching attributes.
+     *
+     * @param filter the attributes to filter by:
+     *          [localKeyId] the localKeyId to search for.
+     *          [localKeyIdHex] the localKeyId in hex to search for.
+     *          [friendlyName] the friendly name to search for.
+     *          [bagType] bag type to narrow each attribute search by.
+     *
+     * @return a map of attribute type to an array of matching bags or, if no
+     *           attribute was given but a bag type, the map key will be the
+     *           bag type.
+     */
+    getBags: function(filter) {
+      var rval = {};
+
+      var localKeyId;
+      if('localKeyId' in filter) {
+        localKeyId = filter.localKeyId;
+      } else if('localKeyIdHex' in filter) {
+        localKeyId = forge.util.hexToBytes(filter.localKeyIdHex);
+      }
+
+      // filter on bagType only
+      if(localKeyId === undefined && !('friendlyName' in filter) &&
+        'bagType' in filter) {
+        rval[filter.bagType] = _getBagsByAttribute(
+          pfx.safeContents, null, null, filter.bagType);
+      }
+
+      if(localKeyId !== undefined) {
+        rval.localKeyId = _getBagsByAttribute(
+          pfx.safeContents, 'localKeyId',
+          localKeyId, filter.bagType);
+      }
+      if('friendlyName' in filter) {
+        rval.friendlyName = _getBagsByAttribute(
+          pfx.safeContents, 'friendlyName',
+          filter.friendlyName, filter.bagType);
+      }
+
+      return rval;
+    },
+
+    /**
+     * DEPRECATED: use getBags() instead.
+     *
+     * Get bags with matching friendlyName attribute.
+     *
+     * @param friendlyName the friendly name to search for.
+     * @param [bagType] bag type to narrow search by.
+     *
+     * @return an array of bags with matching friendlyName attribute.
+     */
+    getBagsByFriendlyName: function(friendlyName, bagType) {
+      return _getBagsByAttribute(
+        pfx.safeContents, 'friendlyName', friendlyName, bagType);
+    },
+
+    /**
+     * DEPRECATED: use getBags() instead.
+     *
+     * Get bags with matching localKeyId attribute.
+     *
+     * @param localKeyId the localKeyId to search for.
+     * @param [bagType] bag type to narrow search by.
+     *
+     * @return an array of bags with matching localKeyId attribute.
+     */
+    getBagsByLocalKeyId: function(localKeyId, bagType) {
+      return _getBagsByAttribute(
+        pfx.safeContents, 'localKeyId', localKeyId, bagType);
+    }
+  };
+
+  if(capture.version.charCodeAt(0) !== 3) {
+    var error = new Error('PKCS#12 PFX of version other than 3 not supported.');
+    error.version = capture.version.charCodeAt(0);
+    throw error;
+  }
+
+  if(asn1.derToOid(capture.contentType) !== pki.oids.data) {
+    var error = new Error('Only PKCS#12 PFX in password integrity mode supported.');
+    error.oid = asn1.derToOid(capture.contentType);
+    throw error;
+  }
+
+  var data = capture.content.value[0];
+  if(data.tagClass !== asn1.Class.UNIVERSAL ||
+     data.type !== asn1.Type.OCTETSTRING) {
+    throw new Error('PKCS#12 authSafe content data is not an OCTET STRING.');
+  }
+  data = _decodePkcs7Data(data);
+
+  // check for MAC
+  if(capture.mac) {
+    var md = null;
+    var macKeyBytes = 0;
+    var macAlgorithm = asn1.derToOid(capture.macAlgorithm);
+    switch(macAlgorithm) {
+    case pki.oids.sha1:
+      md = forge.md.sha1.create();
+      macKeyBytes = 20;
+      break;
+    case pki.oids.sha256:
+      md = forge.md.sha256.create();
+      macKeyBytes = 32;
+      break;
+    case pki.oids.sha384:
+      md = forge.md.sha384.create();
+      macKeyBytes = 48;
+      break;
+    case pki.oids.sha512:
+      md = forge.md.sha512.create();
+      macKeyBytes = 64;
+      break;
+    case pki.oids.md5:
+      md = forge.md.md5.create();
+      macKeyBytes = 16;
+      break;
+    }
+    if(md === null) {
+      throw new Error('PKCS#12 uses unsupported MAC algorithm: ' + macAlgorithm);
+    }
+
+    // verify MAC (iterations default to 1)
+    var macSalt = new forge.util.ByteBuffer(capture.macSalt);
+    var macIterations = (('macIterations' in capture) ?
+      parseInt(forge.util.bytesToHex(capture.macIterations), 16) : 1);
+    var macKey = p12.generateKey(
+      password, macSalt, 3, macIterations, macKeyBytes, md);
+    var mac = forge.hmac.create();
+    mac.start(md, macKey);
+    mac.update(data.value);
+    var macValue = mac.getMac();
+    if(macValue.getBytes() !== capture.macDigest) {
+      throw new Error('PKCS#12 MAC could not be verified. Invalid password?');
+    }
+  } else if(Array.isArray(obj.value) && obj.value.length > 2) {
+    /* This is pfx data that should have mac and verify macDigest */
+    throw new Error('Invalid PKCS#12. macData field present but MAC was not validated.');
+  }
+
+  _decodeAuthenticatedSafe(pfx, data.value, strict, password);
+  return pfx;
+};
+
+/**
+ * Decodes PKCS#7 Data. PKCS#7 (RFC 2315) defines "Data" as an OCTET STRING,
+ * but it is sometimes an OCTET STRING that is composed/constructed of chunks,
+ * each its own OCTET STRING. This is BER-encoding vs. DER-encoding. This
+ * function transforms this corner-case into the usual simple,
+ * non-composed/constructed OCTET STRING.
+ *
+ * This function may be moved to ASN.1 at some point to better deal with
+ * more BER-encoding issues, should they arise.
+ *
+ * @param data the ASN.1 Data object to transform.
+ */
+function _decodePkcs7Data(data) {
+  // handle special case of "chunked" data content: an octet string composed
+  // of other octet strings
+  if(data.composed || data.constructed) {
+    var value = forge.util.createBuffer();
+    for(var i = 0; i < data.value.length; ++i) {
+      value.putBytes(data.value[i].value);
+    }
+    data.composed = data.constructed = false;
+    data.value = value.getBytes();
+  }
+  return data;
+}
+
+/**
+ * Decode PKCS#12 AuthenticatedSafe (BER encoded) into PFX object.
+ *
+ * The AuthenticatedSafe is a BER-encoded SEQUENCE OF ContentInfo.
+ *
+ * @param pfx The PKCS#12 PFX object to fill.
+ * @param {String} authSafe BER-encoded AuthenticatedSafe.
+ * @param strict true to use strict DER decoding, false not to.
+ * @param {String} password Password to decrypt with (optional).
+ */
+function _decodeAuthenticatedSafe(pfx, authSafe, strict, password) {
+  authSafe = asn1.fromDer(authSafe, strict);  /* actually it's BER encoded */
+
+  if(authSafe.tagClass !== asn1.Class.UNIVERSAL ||
+     authSafe.type !== asn1.Type.SEQUENCE ||
+     authSafe.constructed !== true) {
+    throw new Error('PKCS#12 AuthenticatedSafe expected to be a ' +
+      'SEQUENCE OF ContentInfo');
+  }
+
+  for(var i = 0; i < authSafe.value.length; i++) {
+    var contentInfo = authSafe.value[i];
+
+    // validate contentInfo and capture data
+    var capture = {};
+    var errors = [];
+    if(!asn1.validate(contentInfo, contentInfoValidator, capture, errors)) {
+      var error = new Error('Cannot read ContentInfo.');
+      error.errors = errors;
+      throw error;
+    }
+
+    var obj = {
+      encrypted: false
+    };
+    var safeContents = null;
+    var data = capture.content.value[0];
+    switch(asn1.derToOid(capture.contentType)) {
+    case pki.oids.data:
+      if(data.tagClass !== asn1.Class.UNIVERSAL ||
+         data.type !== asn1.Type.OCTETSTRING) {
+        throw new Error('PKCS#12 SafeContents Data is not an OCTET STRING.');
+      }
+      safeContents = _decodePkcs7Data(data).value;
+      break;
+    case pki.oids.encryptedData:
+      safeContents = _decryptSafeContents(data, password);
+      obj.encrypted = true;
+      break;
+    default:
+      var error = new Error('Unsupported PKCS#12 contentType.');
+      error.contentType = asn1.derToOid(capture.contentType);
+      throw error;
+    }
+
+    obj.safeBags = _decodeSafeContents(safeContents, strict, password);
+    pfx.safeContents.push(obj);
+  }
+}
+
+/**
+ * Decrypt PKCS#7 EncryptedData structure.
+ *
+ * @param data ASN.1 encoded EncryptedContentInfo object.
+ * @param password The user-provided password.
+ *
+ * @return The decrypted SafeContents (ASN.1 object).
+ */
+function _decryptSafeContents(data, password) {
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(
+    data, forge.pkcs7.asn1.encryptedDataValidator, capture, errors)) {
+    var error = new Error('Cannot read EncryptedContentInfo.');
+    error.errors = errors;
+    throw error;
+  }
+
+  var oid = asn1.derToOid(capture.contentType);
+  if(oid !== pki.oids.data) {
+    var error = new Error(
+      'PKCS#12 EncryptedContentInfo ContentType is not Data.');
+    error.oid = oid;
+    throw error;
+  }
+
+  // get cipher
+  oid = asn1.derToOid(capture.encAlgorithm);
+  var cipher = pki.pbe.getCipher(oid, capture.encParameter, password);
+
+  // get encrypted data
+  var encryptedContentAsn1 = _decodePkcs7Data(capture.encryptedContentAsn1);
+  var encrypted = forge.util.createBuffer(encryptedContentAsn1.value);
+
+  cipher.update(encrypted);
+  if(!cipher.finish()) {
+    throw new Error('Failed to decrypt PKCS#12 SafeContents.');
+  }
+
+  return cipher.output.getBytes();
+}
+
+/**
+ * Decode PKCS#12 SafeContents (BER-encoded) into array of Bag objects.
+ *
+ * The safeContents is a BER-encoded SEQUENCE OF SafeBag.
+ *
+ * @param {String} safeContents BER-encoded safeContents.
+ * @param strict true to use strict DER decoding, false not to.
+ * @param {String} password Password to decrypt with (optional).
+ *
+ * @return {Array} Array of Bag objects.
+ */
+function _decodeSafeContents(safeContents, strict, password) {
+  // if strict and no safe contents, return empty safes
+  if(!strict && safeContents.length === 0) {
+    return [];
+  }
+
+  // actually it's BER-encoded
+  safeContents = asn1.fromDer(safeContents, strict);
+
+  if(safeContents.tagClass !== asn1.Class.UNIVERSAL ||
+    safeContents.type !== asn1.Type.SEQUENCE ||
+    safeContents.constructed !== true) {
+    throw new Error(
+      'PKCS#12 SafeContents expected to be a SEQUENCE OF SafeBag.');
+  }
+
+  var res = [];
+  for(var i = 0; i < safeContents.value.length; i++) {
+    var safeBag = safeContents.value[i];
+
+    // validate SafeBag and capture data
+    var capture = {};
+    var errors = [];
+    if(!asn1.validate(safeBag, safeBagValidator, capture, errors)) {
+      var error = new Error('Cannot read SafeBag.');
+      error.errors = errors;
+      throw error;
+    }
+
+    /* Create bag object and push to result array. */
+    var bag = {
+      type: asn1.derToOid(capture.bagId),
+      attributes: _decodeBagAttributes(capture.bagAttributes)
+    };
+    res.push(bag);
+
+    var validator, decoder;
+    var bagAsn1 = capture.bagValue.value[0];
+    switch(bag.type) {
+      case pki.oids.pkcs8ShroudedKeyBag:
+        /* bagAsn1 has a EncryptedPrivateKeyInfo, which we need to decrypt.
+           Afterwards we can handle it like a keyBag,
+           which is a PrivateKeyInfo. */
+        bagAsn1 = pki.decryptPrivateKeyInfo(bagAsn1, password);
+        if(bagAsn1 === null) {
+          throw new Error(
+            'Unable to decrypt PKCS#8 ShroudedKeyBag, wrong password?');
+        }
+
+        /* fall through */
+      case pki.oids.keyBag:
+        /* A PKCS#12 keyBag is a simple PrivateKeyInfo as understood by our
+           PKI module, hence we don't have to do validation/capturing here,
+           just pass what we already got. */
+        try {
+          bag.key = pki.privateKeyFromAsn1(bagAsn1);
+        } catch(e) {
+          // ignore unknown key type, pass asn1 value
+          bag.key = null;
+          bag.asn1 = bagAsn1;
+        }
+        continue;  /* Nothing more to do. */
+
+      case pki.oids.certBag:
+        /* A PKCS#12 certBag can wrap both X.509 and sdsi certificates.
+           Therefore put the SafeBag content through another validator to
+           capture the fields.  Afterwards check & store the results. */
+        validator = certBagValidator;
+        decoder = function() {
+          if(asn1.derToOid(capture.certId) !== pki.oids.x509Certificate) {
+            var error = new Error(
+              'Unsupported certificate type, only X.509 supported.');
+            error.oid = asn1.derToOid(capture.certId);
+            throw error;
+          }
+
+          // true=produce cert hash
+          var certAsn1 = asn1.fromDer(capture.cert, strict);
+          try {
+            bag.cert = pki.certificateFromAsn1(certAsn1, true);
+          } catch(e) {
+            // ignore unknown cert type, pass asn1 value
+            bag.cert = null;
+            bag.asn1 = certAsn1;
+          }
+        };
+        break;
+
+      default:
+        var error = new Error('Unsupported PKCS#12 SafeBag type.');
+        error.oid = bag.type;
+        throw error;
+    }
+
+    /* Validate SafeBag value (i.e. CertBag, etc.) and capture data if needed. */
+    if(validator !== undefined &&
+       !asn1.validate(bagAsn1, validator, capture, errors)) {
+      var error = new Error('Cannot read PKCS#12 ' + validator.name);
+      error.errors = errors;
+      throw error;
+    }
+
+    /* Call decoder function from above to store the results. */
+    decoder();
+  }
+
+  return res;
+}
+
+/**
+ * Decode PKCS#12 SET OF PKCS12Attribute into JavaScript object.
+ *
+ * @param attributes SET OF PKCS12Attribute (ASN.1 object).
+ *
+ * @return the decoded attributes.
+ */
+function _decodeBagAttributes(attributes) {
+  var decodedAttrs = {};
+
+  if(attributes !== undefined) {
+    for(var i = 0; i < attributes.length; ++i) {
+      var capture = {};
+      var errors = [];
+      if(!asn1.validate(attributes[i], attributeValidator, capture, errors)) {
+        var error = new Error('Cannot read PKCS#12 BagAttribute.');
+        error.errors = errors;
+        throw error;
+      }
+
+      var oid = asn1.derToOid(capture.oid);
+      if(pki.oids[oid] === undefined) {
+        // unsupported attribute type, ignore.
+        continue;
+      }
+
+      decodedAttrs[pki.oids[oid]] = [];
+      for(var j = 0; j < capture.values.length; ++j) {
+        decodedAttrs[pki.oids[oid]].push(capture.values[j].value);
+      }
+    }
+  }
+
+  return decodedAttrs;
+}
+
+/**
+ * Wraps a private key and certificate in a PKCS#12 PFX wrapper. If a
+ * password is provided then the private key will be encrypted.
+ *
+ * An entire certificate chain may also be included. To do this, pass
+ * an array for the "cert" parameter where the first certificate is
+ * the one that is paired with the private key and each subsequent one
+ * verifies the previous one. The certificates may be in PEM format or
+ * have been already parsed by Forge.
+ *
+ * @todo implement password-based-encryption for the whole package
+ *
+ * @param key the private key.
+ * @param cert the certificate (may be an array of certificates in order
+ *          to specify a certificate chain).
+ * @param password the password to use, null for none.
+ * @param options:
+ *          algorithm the encryption algorithm to use
+ *            ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.
+ *          count the iteration count to use.
+ *          saltSize the salt size to use.
+ *          useMac true to include a MAC, false not to, defaults to true.
+ *          localKeyId the local key ID to use, in hex.
+ *          friendlyName the friendly name to use.
+ *          generateLocalKeyId true to generate a random local key ID,
+ *            false not to, defaults to true.
+ *
+ * @return the PKCS#12 PFX ASN.1 object.
+ */
+p12.toPkcs12Asn1 = function(key, cert, password, options) {
+  // set default options
+  options = options || {};
+  options.saltSize = options.saltSize || 8;
+  options.count = options.count || 2048;
+  options.algorithm = options.algorithm || options.encAlgorithm || 'aes128';
+  if(!('useMac' in options)) {
+    options.useMac = true;
+  }
+  if(!('localKeyId' in options)) {
+    options.localKeyId = null;
+  }
+  if(!('generateLocalKeyId' in options)) {
+    options.generateLocalKeyId = true;
+  }
+
+  var localKeyId = options.localKeyId;
+  var bagAttrs;
+  if(localKeyId !== null) {
+    localKeyId = forge.util.hexToBytes(localKeyId);
+  } else if(options.generateLocalKeyId) {
+    // use SHA-1 of paired cert, if available
+    if(cert) {
+      var pairedCert = forge.util.isArray(cert) ? cert[0] : cert;
+      if(typeof pairedCert === 'string') {
+        pairedCert = pki.certificateFromPem(pairedCert);
+      }
+      var sha1 = forge.md.sha1.create();
+      sha1.update(asn1.toDer(pki.certificateToAsn1(pairedCert)).getBytes());
+      localKeyId = sha1.digest().getBytes();
+    } else {
+      // FIXME: consider using SHA-1 of public key (which can be generated
+      // from private key components), see: cert.generateSubjectKeyIdentifier
+      // generate random bytes
+      localKeyId = forge.random.getBytes(20);
+    }
+  }
+
+  var attrs = [];
+  if(localKeyId !== null) {
+    attrs.push(
+      // localKeyID
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // attrId
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          asn1.oidToDer(pki.oids.localKeyId).getBytes()),
+        // attrValues
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+            localKeyId)
+        ])
+      ]));
+  }
+  if('friendlyName' in options) {
+    attrs.push(
+      // friendlyName
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // attrId
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          asn1.oidToDer(pki.oids.friendlyName).getBytes()),
+        // attrValues
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BMPSTRING, false,
+            options.friendlyName)
+        ])
+      ]));
+  }
+
+  if(attrs.length > 0) {
+    bagAttrs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs);
+  }
+
+  // collect contents for AuthenticatedSafe
+  var contents = [];
+
+  // create safe bag(s) for certificate chain
+  var chain = [];
+  if(cert !== null) {
+    if(forge.util.isArray(cert)) {
+      chain = cert;
+    } else {
+      chain = [cert];
+    }
+  }
+
+  var certSafeBags = [];
+  for(var i = 0; i < chain.length; ++i) {
+    // convert cert from PEM as necessary
+    cert = chain[i];
+    if(typeof cert === 'string') {
+      cert = pki.certificateFromPem(cert);
+    }
+
+    // SafeBag
+    var certBagAttrs = (i === 0) ? bagAttrs : undefined;
+    var certAsn1 = pki.certificateToAsn1(cert);
+    var certSafeBag =
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // bagId
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          asn1.oidToDer(pki.oids.certBag).getBytes()),
+        // bagValue
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+          // CertBag
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+            // certId
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+              asn1.oidToDer(pki.oids.x509Certificate).getBytes()),
+            // certValue (x509Certificate)
+            asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+              asn1.create(
+                asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+                asn1.toDer(certAsn1).getBytes())
+            ])])]),
+        // bagAttributes (OPTIONAL)
+        certBagAttrs
+      ]);
+    certSafeBags.push(certSafeBag);
+  }
+
+  if(certSafeBags.length > 0) {
+    // SafeContents
+    var certSafeContents = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, certSafeBags);
+
+    // ContentInfo
+    var certCI =
+      // PKCS#7 ContentInfo
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // contentType
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          // OID for the content type is 'data'
+          asn1.oidToDer(pki.oids.data).getBytes()),
+        // content
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+          asn1.create(
+            asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+            asn1.toDer(certSafeContents).getBytes())
+        ])
+      ]);
+    contents.push(certCI);
+  }
+
+  // create safe contents for private key
+  var keyBag = null;
+  if(key !== null) {
+    // SafeBag
+    var pkAsn1 = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(key));
+    if(password === null) {
+      // no encryption
+      keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // bagId
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          asn1.oidToDer(pki.oids.keyBag).getBytes()),
+        // bagValue
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+          // PrivateKeyInfo
+          pkAsn1
+        ]),
+        // bagAttributes (OPTIONAL)
+        bagAttrs
+      ]);
+    } else {
+      // encrypted PrivateKeyInfo
+      keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // bagId
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          asn1.oidToDer(pki.oids.pkcs8ShroudedKeyBag).getBytes()),
+        // bagValue
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+          // EncryptedPrivateKeyInfo
+          pki.encryptPrivateKeyInfo(pkAsn1, password, options)
+        ]),
+        // bagAttributes (OPTIONAL)
+        bagAttrs
+      ]);
+    }
+
+    // SafeContents
+    var keySafeContents =
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [keyBag]);
+
+    // ContentInfo
+    var keyCI =
+      // PKCS#7 ContentInfo
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // contentType
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          // OID for the content type is 'data'
+          asn1.oidToDer(pki.oids.data).getBytes()),
+        // content
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+          asn1.create(
+            asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+            asn1.toDer(keySafeContents).getBytes())
+        ])
+      ]);
+    contents.push(keyCI);
+  }
+
+  // create AuthenticatedSafe by stringing together the contents
+  var safe = asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, contents);
+
+  var macData;
+  if(options.useMac) {
+    // MacData
+    var sha1 = forge.md.sha1.create();
+    var macSalt = new forge.util.ByteBuffer(
+      forge.random.getBytes(options.saltSize));
+    var count = options.count;
+    // 160-bit key
+    var key = p12.generateKey(password, macSalt, 3, count, 20);
+    var mac = forge.hmac.create();
+    mac.start(sha1, key);
+    mac.update(asn1.toDer(safe).getBytes());
+    var macValue = mac.getMac();
+    macData = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // mac DigestInfo
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // digestAlgorithm
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+          // algorithm = SHA-1
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+            asn1.oidToDer(pki.oids.sha1).getBytes()),
+          // parameters = Null
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+        ]),
+        // digest
+        asn1.create(
+          asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING,
+          false, macValue.getBytes())
+      ]),
+      // macSalt OCTET STRING
+      asn1.create(
+        asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, macSalt.getBytes()),
+      // iterations INTEGER (XXX: Only support count < 65536)
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+        asn1.integerToDer(count).getBytes()
+      )
+    ]);
+  }
+
+  // PFX
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // version (3)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      asn1.integerToDer(3).getBytes()),
+    // PKCS#7 ContentInfo
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // contentType
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        // OID for the content type is 'data'
+        asn1.oidToDer(pki.oids.data).getBytes()),
+      // content
+      asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+        asn1.create(
+          asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+          asn1.toDer(safe).getBytes())
+      ])
+    ]),
+    macData
+  ]);
+};
+
+/**
+ * Derives a PKCS#12 key.
+ *
+ * @param password the password to derive the key material from, null or
+ *          undefined for none.
+ * @param salt the salt, as a ByteBuffer, to use.
+ * @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).
+ * @param iter the iteration count.
+ * @param n the number of bytes to derive from the password.
+ * @param md the message digest to use, defaults to SHA-1.
+ *
+ * @return a ByteBuffer with the bytes derived from the password.
+ */
+p12.generateKey = forge.pbe.generatePkcs12Key;
Index: frontend/node_modules/node-forge/lib/pkcs7.js
===================================================================
--- frontend/node_modules/node-forge/lib/pkcs7.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pkcs7.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1260 @@
+/**
+ * Javascript implementation of PKCS#7 v1.5.
+ *
+ * @author Stefan Siegl
+ * @author Dave Longley
+ *
+ * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
+ * Copyright (c) 2012-2015 Digital Bazaar, Inc.
+ *
+ * Currently this implementation only supports ContentType of EnvelopedData,
+ * EncryptedData, or SignedData at the root level. The top level elements may
+ * contain only a ContentInfo of ContentType Data, i.e. plain data. Further
+ * nesting is not (yet) supported.
+ *
+ * The Forge validators for PKCS #7's ASN.1 structures are available from
+ * a separate file pkcs7asn1.js, since those are referenced from other
+ * PKCS standards like PKCS #12.
+ */
+var forge = require('./forge');
+require('./aes');
+require('./asn1');
+require('./des');
+require('./oids');
+require('./pem');
+require('./pkcs7asn1');
+require('./random');
+require('./util');
+require('./x509');
+
+// shortcut for ASN.1 API
+var asn1 = forge.asn1;
+
+// shortcut for PKCS#7 API
+var p7 = module.exports = forge.pkcs7 = forge.pkcs7 || {};
+
+/**
+ * Converts a PKCS#7 message from PEM format.
+ *
+ * @param pem the PEM-formatted PKCS#7 message.
+ *
+ * @return the PKCS#7 message.
+ */
+p7.messageFromPem = function(pem) {
+  var msg = forge.pem.decode(pem)[0];
+
+  if(msg.type !== 'PKCS7') {
+    var error = new Error('Could not convert PKCS#7 message from PEM; PEM ' +
+      'header type is not "PKCS#7".');
+    error.headerType = msg.type;
+    throw error;
+  }
+  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+    throw new Error('Could not convert PKCS#7 message from PEM; PEM is encrypted.');
+  }
+
+  // convert DER to ASN.1 object
+  var obj = asn1.fromDer(msg.body);
+
+  return p7.messageFromAsn1(obj);
+};
+
+/**
+ * Converts a PKCS#7 message to PEM format.
+ *
+ * @param msg The PKCS#7 message object
+ * @param maxline The maximum characters per line, defaults to 64.
+ *
+ * @return The PEM-formatted PKCS#7 message.
+ */
+p7.messageToPem = function(msg, maxline) {
+  // convert to ASN.1, then DER, then PEM-encode
+  var pemObj = {
+    type: 'PKCS7',
+    body: asn1.toDer(msg.toAsn1()).getBytes()
+  };
+  return forge.pem.encode(pemObj, {maxline: maxline});
+};
+
+/**
+ * Converts a PKCS#7 message from an ASN.1 object.
+ *
+ * @param obj the ASN.1 representation of a ContentInfo.
+ *
+ * @return the PKCS#7 message.
+ */
+p7.messageFromAsn1 = function(obj) {
+  // validate root level ContentInfo and capture data
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, p7.asn1.contentInfoValidator, capture, errors)) {
+    var error = new Error('Cannot read PKCS#7 message. ' +
+      'ASN.1 object is not an PKCS#7 ContentInfo.');
+    error.errors = errors;
+    throw error;
+  }
+
+  var contentType = asn1.derToOid(capture.contentType);
+  var msg;
+
+  switch(contentType) {
+    case forge.pki.oids.envelopedData:
+      msg = p7.createEnvelopedData();
+      break;
+
+    case forge.pki.oids.encryptedData:
+      msg = p7.createEncryptedData();
+      break;
+
+    case forge.pki.oids.signedData:
+      msg = p7.createSignedData();
+      break;
+
+    default:
+      throw new Error('Cannot read PKCS#7 message. ContentType with OID ' +
+        contentType + ' is not (yet) supported.');
+  }
+
+  msg.fromAsn1(capture.content.value[0]);
+  return msg;
+};
+
+p7.createSignedData = function() {
+  var msg = null;
+  msg = {
+    type: forge.pki.oids.signedData,
+    version: 1,
+    certificates: [],
+    crls: [],
+    // TODO: add json-formatted signer stuff here?
+    signers: [],
+    // populated during sign()
+    digestAlgorithmIdentifiers: [],
+    contentInfo: null,
+    signerInfos: [],
+
+    fromAsn1: function(obj) {
+      // validate SignedData content block and capture data.
+      _fromAsn1(msg, obj, p7.asn1.signedDataValidator);
+      msg.certificates = [];
+      msg.crls = [];
+      msg.digestAlgorithmIdentifiers = [];
+      msg.contentInfo = null;
+      msg.signerInfos = [];
+
+      if(msg.rawCapture.certificates) {
+        var certs = msg.rawCapture.certificates.value;
+        for(var i = 0; i < certs.length; ++i) {
+          msg.certificates.push(forge.pki.certificateFromAsn1(certs[i]));
+        }
+      }
+
+      // TODO: parse crls
+    },
+
+    toAsn1: function() {
+      // degenerate case with no content
+      if(!msg.contentInfo) {
+        msg.sign();
+      }
+
+      var certs = [];
+      for(var i = 0; i < msg.certificates.length; ++i) {
+        certs.push(forge.pki.certificateToAsn1(msg.certificates[i]));
+      }
+
+      var crls = [];
+      // TODO: implement CRLs
+
+      // [0] SignedData
+      var signedData = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+          // Version
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+            asn1.integerToDer(msg.version).getBytes()),
+          // DigestAlgorithmIdentifiers
+          asn1.create(
+            asn1.Class.UNIVERSAL, asn1.Type.SET, true,
+            msg.digestAlgorithmIdentifiers),
+          // ContentInfo
+          msg.contentInfo
+        ])
+      ]);
+      if(certs.length > 0) {
+        // [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL
+        signedData.value[0].value.push(
+          asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, certs));
+      }
+      if(crls.length > 0) {
+        // [1] IMPLICIT CertificateRevocationLists OPTIONAL
+        signedData.value[0].value.push(
+          asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, crls));
+      }
+      // SignerInfos
+      signedData.value[0].value.push(
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
+          msg.signerInfos));
+
+      // ContentInfo
+      return asn1.create(
+        asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+          // ContentType
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+            asn1.oidToDer(msg.type).getBytes()),
+          // [0] SignedData
+          signedData
+        ]);
+    },
+
+    /**
+     * Add (another) entity to list of signers.
+     *
+     * Note: If authenticatedAttributes are provided, then, per RFC 2315,
+     * they must include at least two attributes: content type and
+     * message digest. The message digest attribute value will be
+     * auto-calculated during signing and will be ignored if provided.
+     *
+     * Here's an example of providing these two attributes:
+     *
+     * forge.pkcs7.createSignedData();
+     * p7.addSigner({
+     *   issuer: cert.issuer.attributes,
+     *   serialNumber: cert.serialNumber,
+     *   key: privateKey,
+     *   digestAlgorithm: forge.pki.oids.sha1,
+     *   authenticatedAttributes: [{
+     *     type: forge.pki.oids.contentType,
+     *     value: forge.pki.oids.data
+     *   }, {
+     *     type: forge.pki.oids.messageDigest
+     *   }]
+     * });
+     *
+     * TODO: Support [subjectKeyIdentifier] as signer's ID.
+     *
+     * @param signer the signer information:
+     *          key the signer's private key.
+     *          [certificate] a certificate containing the public key
+     *            associated with the signer's private key; use this option as
+     *            an alternative to specifying signer.issuer and
+     *            signer.serialNumber.
+     *          [issuer] the issuer attributes (eg: cert.issuer.attributes).
+     *          [serialNumber] the signer's certificate's serial number in
+     *           hexadecimal (eg: cert.serialNumber).
+     *          [digestAlgorithm] the message digest OID, as a string, to use
+     *            (eg: forge.pki.oids.sha1).
+     *          [authenticatedAttributes] an optional array of attributes
+     *            to also sign along with the content.
+     */
+    addSigner: function(signer) {
+      var issuer = signer.issuer;
+      var serialNumber = signer.serialNumber;
+      if(signer.certificate) {
+        var cert = signer.certificate;
+        if(typeof cert === 'string') {
+          cert = forge.pki.certificateFromPem(cert);
+        }
+        issuer = cert.issuer.attributes;
+        serialNumber = cert.serialNumber;
+      }
+      var key = signer.key;
+      if(!key) {
+        throw new Error(
+          'Could not add PKCS#7 signer; no private key specified.');
+      }
+      if(typeof key === 'string') {
+        key = forge.pki.privateKeyFromPem(key);
+      }
+
+      // ensure OID known for digest algorithm
+      var digestAlgorithm = signer.digestAlgorithm || forge.pki.oids.sha1;
+      switch(digestAlgorithm) {
+      case forge.pki.oids.sha1:
+      case forge.pki.oids.sha256:
+      case forge.pki.oids.sha384:
+      case forge.pki.oids.sha512:
+      case forge.pki.oids.md5:
+        break;
+      default:
+        throw new Error(
+          'Could not add PKCS#7 signer; unknown message digest algorithm: ' +
+          digestAlgorithm);
+      }
+
+      // if authenticatedAttributes is present, then the attributes
+      // must contain at least PKCS #9 content-type and message-digest
+      var authenticatedAttributes = signer.authenticatedAttributes || [];
+      if(authenticatedAttributes.length > 0) {
+        var contentType = false;
+        var messageDigest = false;
+        for(var i = 0; i < authenticatedAttributes.length; ++i) {
+          var attr = authenticatedAttributes[i];
+          if(!contentType && attr.type === forge.pki.oids.contentType) {
+            contentType = true;
+            if(messageDigest) {
+              break;
+            }
+            continue;
+          }
+          if(!messageDigest && attr.type === forge.pki.oids.messageDigest) {
+            messageDigest = true;
+            if(contentType) {
+              break;
+            }
+            continue;
+          }
+        }
+
+        if(!contentType || !messageDigest) {
+          throw new Error('Invalid signer.authenticatedAttributes. If ' +
+            'signer.authenticatedAttributes is specified, then it must ' +
+            'contain at least two attributes, PKCS #9 content-type and ' +
+            'PKCS #9 message-digest.');
+        }
+      }
+
+      msg.signers.push({
+        key: key,
+        version: 1,
+        issuer: issuer,
+        serialNumber: serialNumber,
+        digestAlgorithm: digestAlgorithm,
+        signatureAlgorithm: forge.pki.oids.rsaEncryption,
+        signature: null,
+        authenticatedAttributes: authenticatedAttributes,
+        unauthenticatedAttributes: []
+      });
+    },
+
+    /**
+     * Signs the content.
+     * @param options Options to apply when signing:
+     *    [detached] boolean. If signing should be done in detached mode. Defaults to false.
+     */
+    sign: function(options) {
+      options = options || {};
+      // auto-generate content info
+      if(typeof msg.content !== 'object' || msg.contentInfo === null) {
+        // use Data ContentInfo
+        msg.contentInfo = asn1.create(
+          asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+            // ContentType
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+              asn1.oidToDer(forge.pki.oids.data).getBytes())
+          ]);
+
+        // add actual content, if present
+        if('content' in msg) {
+          var content;
+          if(msg.content instanceof forge.util.ByteBuffer) {
+            content = msg.content.bytes();
+          } else if(typeof msg.content === 'string') {
+            content = forge.util.encodeUtf8(msg.content);
+          }
+
+          if (options.detached) {
+            msg.detachedContent = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, content);
+          } else {
+            msg.contentInfo.value.push(
+              // [0] EXPLICIT content
+              asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+                asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+                  content)
+              ]));
+          }
+        }
+      }
+
+      // no signers, return early (degenerate case for certificate container)
+      if(msg.signers.length === 0) {
+        return;
+      }
+
+      // generate digest algorithm identifiers
+      var mds = addDigestAlgorithmIds();
+
+      // generate signerInfos
+      addSignerInfos(mds);
+    },
+
+    verify: function() {
+      throw new Error('PKCS#7 signature verification not yet implemented.');
+    },
+
+    /**
+     * Add a certificate.
+     *
+     * @param cert the certificate to add.
+     */
+    addCertificate: function(cert) {
+      // convert from PEM
+      if(typeof cert === 'string') {
+        cert = forge.pki.certificateFromPem(cert);
+      }
+      msg.certificates.push(cert);
+    },
+
+    /**
+     * Add a certificate revokation list.
+     *
+     * @param crl the certificate revokation list to add.
+     */
+    addCertificateRevokationList: function(crl) {
+      throw new Error('PKCS#7 CRL support not yet implemented.');
+    }
+  };
+  return msg;
+
+  function addDigestAlgorithmIds() {
+    var mds = {};
+
+    for(var i = 0; i < msg.signers.length; ++i) {
+      var signer = msg.signers[i];
+      var oid = signer.digestAlgorithm;
+      if(!(oid in mds)) {
+        // content digest
+        mds[oid] = forge.md[forge.pki.oids[oid]].create();
+      }
+      if(signer.authenticatedAttributes.length === 0) {
+        // no custom attributes to digest; use content message digest
+        signer.md = mds[oid];
+      } else {
+        // custom attributes to be digested; use own message digest
+        // TODO: optimize to just copy message digest state if that
+        // feature is ever supported with message digests
+        signer.md = forge.md[forge.pki.oids[oid]].create();
+      }
+    }
+
+    // add unique digest algorithm identifiers
+    msg.digestAlgorithmIdentifiers = [];
+    for(var oid in mds) {
+      msg.digestAlgorithmIdentifiers.push(
+        // AlgorithmIdentifier
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+          // algorithm
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+            asn1.oidToDer(oid).getBytes()),
+          // parameters (null)
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+        ]));
+    }
+
+    return mds;
+  }
+
+  function addSignerInfos(mds) {
+    var content;
+
+    if (msg.detachedContent) {
+      // Signature has been made in detached mode.
+      content = msg.detachedContent;
+    } else {
+      // Note: ContentInfo is a SEQUENCE with 2 values, second value is
+      // the content field and is optional for a ContentInfo but required here
+      // since signers are present
+      // get ContentInfo content
+      content = msg.contentInfo.value[1];
+      // skip [0] EXPLICIT content wrapper
+      content = content.value[0];
+    }
+
+    if(!content) {
+      throw new Error(
+        'Could not sign PKCS#7 message; there is no content to sign.');
+    }
+
+    // get ContentInfo content type
+    var contentType = asn1.derToOid(msg.contentInfo.value[0].value);
+
+    // serialize content
+    var bytes = asn1.toDer(content);
+
+    // skip identifier and length per RFC 2315 9.3
+    // skip identifier (1 byte)
+    bytes.getByte();
+    // read and discard length bytes
+    asn1.getBerValueLength(bytes);
+    bytes = bytes.getBytes();
+
+    // digest content DER value bytes
+    for(var oid in mds) {
+      mds[oid].start().update(bytes);
+    }
+
+    // sign content
+    var signingTime = new Date();
+    for(var i = 0; i < msg.signers.length; ++i) {
+      var signer = msg.signers[i];
+
+      if(signer.authenticatedAttributes.length === 0) {
+        // if ContentInfo content type is not "Data", then
+        // authenticatedAttributes must be present per RFC 2315
+        if(contentType !== forge.pki.oids.data) {
+          throw new Error(
+            'Invalid signer; authenticatedAttributes must be present ' +
+            'when the ContentInfo content type is not PKCS#7 Data.');
+        }
+      } else {
+        // process authenticated attributes
+        // [0] IMPLICIT
+        signer.authenticatedAttributesAsn1 = asn1.create(
+          asn1.Class.CONTEXT_SPECIFIC, 0, true, []);
+
+        // per RFC 2315, attributes are to be digested using a SET container
+        // not the above [0] IMPLICIT container
+        var attrsAsn1 = asn1.create(
+          asn1.Class.UNIVERSAL, asn1.Type.SET, true, []);
+
+        for(var ai = 0; ai < signer.authenticatedAttributes.length; ++ai) {
+          var attr = signer.authenticatedAttributes[ai];
+          if(attr.type === forge.pki.oids.messageDigest) {
+            // use content message digest as value
+            attr.value = mds[signer.digestAlgorithm].digest();
+          } else if(attr.type === forge.pki.oids.signingTime) {
+            // auto-populate signing time if not already set
+            if(!attr.value) {
+              attr.value = signingTime;
+            }
+          }
+
+          // convert to ASN.1 and push onto Attributes SET (for signing) and
+          // onto authenticatedAttributesAsn1 to complete SignedData ASN.1
+          // TODO: optimize away duplication
+          attrsAsn1.value.push(_attributeToAsn1(attr));
+          signer.authenticatedAttributesAsn1.value.push(_attributeToAsn1(attr));
+        }
+
+        // DER-serialize and digest SET OF attributes only
+        bytes = asn1.toDer(attrsAsn1).getBytes();
+        signer.md.start().update(bytes);
+      }
+
+      // sign digest
+      signer.signature = signer.key.sign(signer.md, 'RSASSA-PKCS1-V1_5');
+    }
+
+    // add signer info
+    msg.signerInfos = _signersToAsn1(msg.signers);
+  }
+};
+
+/**
+ * Creates an empty PKCS#7 message of type EncryptedData.
+ *
+ * @return the message.
+ */
+p7.createEncryptedData = function() {
+  var msg = null;
+  msg = {
+    type: forge.pki.oids.encryptedData,
+    version: 0,
+    encryptedContent: {
+      algorithm: forge.pki.oids['aes256-CBC']
+    },
+
+    /**
+     * Reads an EncryptedData content block (in ASN.1 format)
+     *
+     * @param obj The ASN.1 representation of the EncryptedData content block
+     */
+    fromAsn1: function(obj) {
+      // Validate EncryptedData content block and capture data.
+      _fromAsn1(msg, obj, p7.asn1.encryptedDataValidator);
+    },
+
+    /**
+     * Decrypt encrypted content
+     *
+     * @param key The (symmetric) key as a byte buffer
+     */
+    decrypt: function(key) {
+      if(key !== undefined) {
+        msg.encryptedContent.key = key;
+      }
+      _decryptContent(msg);
+    }
+  };
+  return msg;
+};
+
+/**
+ * Creates an empty PKCS#7 message of type EnvelopedData.
+ *
+ * @return the message.
+ */
+p7.createEnvelopedData = function() {
+  var msg = null;
+  msg = {
+    type: forge.pki.oids.envelopedData,
+    version: 0,
+    recipients: [],
+    encryptedContent: {
+      algorithm: forge.pki.oids['aes256-CBC']
+    },
+
+    /**
+     * Reads an EnvelopedData content block (in ASN.1 format)
+     *
+     * @param obj the ASN.1 representation of the EnvelopedData content block.
+     */
+    fromAsn1: function(obj) {
+      // validate EnvelopedData content block and capture data
+      var capture = _fromAsn1(msg, obj, p7.asn1.envelopedDataValidator);
+      msg.recipients = _recipientsFromAsn1(capture.recipientInfos.value);
+    },
+
+    toAsn1: function() {
+      // ContentInfo
+      return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // ContentType
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          asn1.oidToDer(msg.type).getBytes()),
+        // [0] EnvelopedData
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+            // Version
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+              asn1.integerToDer(msg.version).getBytes()),
+            // RecipientInfos
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
+              _recipientsToAsn1(msg.recipients)),
+            // EncryptedContentInfo
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true,
+              _encryptedContentToAsn1(msg.encryptedContent))
+          ])
+        ])
+      ]);
+    },
+
+    /**
+     * Find recipient by X.509 certificate's issuer.
+     *
+     * @param cert the certificate with the issuer to look for.
+     *
+     * @return the recipient object.
+     */
+    findRecipient: function(cert) {
+      var sAttr = cert.issuer.attributes;
+
+      for(var i = 0; i < msg.recipients.length; ++i) {
+        var r = msg.recipients[i];
+        var rAttr = r.issuer;
+
+        if(r.serialNumber !== cert.serialNumber) {
+          continue;
+        }
+
+        if(rAttr.length !== sAttr.length) {
+          continue;
+        }
+
+        var match = true;
+        for(var j = 0; j < sAttr.length; ++j) {
+          if(rAttr[j].type !== sAttr[j].type ||
+            rAttr[j].value !== sAttr[j].value) {
+            match = false;
+            break;
+          }
+        }
+
+        if(match) {
+          return r;
+        }
+      }
+
+      return null;
+    },
+
+    /**
+     * Decrypt enveloped content
+     *
+     * @param recipient The recipient object related to the private key
+     * @param privKey The (RSA) private key object
+     */
+    decrypt: function(recipient, privKey) {
+      if(msg.encryptedContent.key === undefined && recipient !== undefined &&
+        privKey !== undefined) {
+        switch(recipient.encryptedContent.algorithm) {
+          case forge.pki.oids.rsaEncryption:
+          case forge.pki.oids.desCBC:
+            var key = privKey.decrypt(recipient.encryptedContent.content);
+            msg.encryptedContent.key = forge.util.createBuffer(key);
+            break;
+
+          default:
+            throw new Error('Unsupported asymmetric cipher, ' +
+              'OID ' + recipient.encryptedContent.algorithm);
+        }
+      }
+
+      _decryptContent(msg);
+    },
+
+    /**
+     * Add (another) entity to list of recipients.
+     *
+     * @param cert The certificate of the entity to add.
+     */
+    addRecipient: function(cert) {
+      msg.recipients.push({
+        version: 0,
+        issuer: cert.issuer.attributes,
+        serialNumber: cert.serialNumber,
+        encryptedContent: {
+          // We simply assume rsaEncryption here, since forge.pki only
+          // supports RSA so far.  If the PKI module supports other
+          // ciphers one day, we need to modify this one as well.
+          algorithm: forge.pki.oids.rsaEncryption,
+          key: cert.publicKey
+        }
+      });
+    },
+
+    /**
+     * Encrypt enveloped content.
+     *
+     * This function supports two optional arguments, cipher and key, which
+     * can be used to influence symmetric encryption.  Unless cipher is
+     * provided, the cipher specified in encryptedContent.algorithm is used
+     * (defaults to AES-256-CBC).  If no key is provided, encryptedContent.key
+     * is (re-)used.  If that one's not set, a random key will be generated
+     * automatically.
+     *
+     * @param [key] The key to be used for symmetric encryption.
+     * @param [cipher] The OID of the symmetric cipher to use.
+     */
+    encrypt: function(key, cipher) {
+      // Part 1: Symmetric encryption
+      if(msg.encryptedContent.content === undefined) {
+        cipher = cipher || msg.encryptedContent.algorithm;
+        key = key || msg.encryptedContent.key;
+
+        var keyLen, ivLen, ciphFn;
+        switch(cipher) {
+          case forge.pki.oids['aes128-CBC']:
+            keyLen = 16;
+            ivLen = 16;
+            ciphFn = forge.aes.createEncryptionCipher;
+            break;
+
+          case forge.pki.oids['aes192-CBC']:
+            keyLen = 24;
+            ivLen = 16;
+            ciphFn = forge.aes.createEncryptionCipher;
+            break;
+
+          case forge.pki.oids['aes256-CBC']:
+            keyLen = 32;
+            ivLen = 16;
+            ciphFn = forge.aes.createEncryptionCipher;
+            break;
+
+          case forge.pki.oids['des-EDE3-CBC']:
+            keyLen = 24;
+            ivLen = 8;
+            ciphFn = forge.des.createEncryptionCipher;
+            break;
+
+          default:
+            throw new Error('Unsupported symmetric cipher, OID ' + cipher);
+        }
+
+        if(key === undefined) {
+          key = forge.util.createBuffer(forge.random.getBytes(keyLen));
+        } else if(key.length() != keyLen) {
+          throw new Error('Symmetric key has wrong length; ' +
+            'got ' + key.length() + ' bytes, expected ' + keyLen + '.');
+        }
+
+        // Keep a copy of the key & IV in the object, so the caller can
+        // use it for whatever reason.
+        msg.encryptedContent.algorithm = cipher;
+        msg.encryptedContent.key = key;
+        msg.encryptedContent.parameter = forge.util.createBuffer(
+          forge.random.getBytes(ivLen));
+
+        var ciph = ciphFn(key);
+        ciph.start(msg.encryptedContent.parameter.copy());
+        ciph.update(msg.content);
+
+        // The finish function does PKCS#7 padding by default, therefore
+        // no action required by us.
+        if(!ciph.finish()) {
+          throw new Error('Symmetric encryption failed.');
+        }
+
+        msg.encryptedContent.content = ciph.output;
+      }
+
+      // Part 2: asymmetric encryption for each recipient
+      for(var i = 0; i < msg.recipients.length; ++i) {
+        var recipient = msg.recipients[i];
+
+        // Nothing to do, encryption already done.
+        if(recipient.encryptedContent.content !== undefined) {
+          continue;
+        }
+
+        switch(recipient.encryptedContent.algorithm) {
+          case forge.pki.oids.rsaEncryption:
+            recipient.encryptedContent.content =
+              recipient.encryptedContent.key.encrypt(
+                msg.encryptedContent.key.data);
+            break;
+
+          default:
+            throw new Error('Unsupported asymmetric cipher, OID ' +
+              recipient.encryptedContent.algorithm);
+        }
+      }
+    }
+  };
+  return msg;
+};
+
+/**
+ * Converts a single recipient from an ASN.1 object.
+ *
+ * @param obj the ASN.1 RecipientInfo.
+ *
+ * @return the recipient object.
+ */
+function _recipientFromAsn1(obj) {
+  // validate EnvelopedData content block and capture data
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, p7.asn1.recipientInfoValidator, capture, errors)) {
+    var error = new Error('Cannot read PKCS#7 RecipientInfo. ' +
+      'ASN.1 object is not an PKCS#7 RecipientInfo.');
+    error.errors = errors;
+    throw error;
+  }
+
+  return {
+    version: capture.version.charCodeAt(0),
+    issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
+    serialNumber: forge.util.createBuffer(capture.serial).toHex(),
+    encryptedContent: {
+      algorithm: asn1.derToOid(capture.encAlgorithm),
+      parameter: capture.encParameter ? capture.encParameter.value : undefined,
+      content: capture.encKey
+    }
+  };
+}
+
+/**
+ * Converts a single recipient object to an ASN.1 object.
+ *
+ * @param obj the recipient object.
+ *
+ * @return the ASN.1 RecipientInfo.
+ */
+function _recipientToAsn1(obj) {
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // Version
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      asn1.integerToDer(obj.version).getBytes()),
+    // IssuerAndSerialNumber
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // Name
+      forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),
+      // Serial
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+        forge.util.hexToBytes(obj.serialNumber))
+    ]),
+    // KeyEncryptionAlgorithmIdentifier
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // Algorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(obj.encryptedContent.algorithm).getBytes()),
+      // Parameter, force NULL, only RSA supported for now.
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+    ]),
+    // EncryptedKey
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+      obj.encryptedContent.content)
+  ]);
+}
+
+/**
+ * Map a set of RecipientInfo ASN.1 objects to recipient objects.
+ *
+ * @param infos an array of ASN.1 representations RecipientInfo (i.e. SET OF).
+ *
+ * @return an array of recipient objects.
+ */
+function _recipientsFromAsn1(infos) {
+  var ret = [];
+  for(var i = 0; i < infos.length; ++i) {
+    ret.push(_recipientFromAsn1(infos[i]));
+  }
+  return ret;
+}
+
+/**
+ * Map an array of recipient objects to ASN.1 RecipientInfo objects.
+ *
+ * @param recipients an array of recipientInfo objects.
+ *
+ * @return an array of ASN.1 RecipientInfos.
+ */
+function _recipientsToAsn1(recipients) {
+  var ret = [];
+  for(var i = 0; i < recipients.length; ++i) {
+    ret.push(_recipientToAsn1(recipients[i]));
+  }
+  return ret;
+}
+
+/**
+ * Converts a single signer from an ASN.1 object.
+ *
+ * @param obj the ASN.1 representation of a SignerInfo.
+ *
+ * @return the signer object.
+ */
+function _signerFromAsn1(obj) {
+  // validate EnvelopedData content block and capture data
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, p7.asn1.signerInfoValidator, capture, errors)) {
+    var error = new Error('Cannot read PKCS#7 SignerInfo. ' +
+      'ASN.1 object is not an PKCS#7 SignerInfo.');
+    error.errors = errors;
+    throw error;
+  }
+
+  var rval = {
+    version: capture.version.charCodeAt(0),
+    issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
+    serialNumber: forge.util.createBuffer(capture.serial).toHex(),
+    digestAlgorithm: asn1.derToOid(capture.digestAlgorithm),
+    signatureAlgorithm: asn1.derToOid(capture.signatureAlgorithm),
+    signature: capture.signature,
+    authenticatedAttributes: [],
+    unauthenticatedAttributes: []
+  };
+
+  // TODO: convert attributes
+  var authenticatedAttributes = capture.authenticatedAttributes || [];
+  var unauthenticatedAttributes = capture.unauthenticatedAttributes || [];
+
+  return rval;
+}
+
+/**
+ * Converts a single signerInfo object to an ASN.1 object.
+ *
+ * @param obj the signerInfo object.
+ *
+ * @return the ASN.1 representation of a SignerInfo.
+ */
+function _signerToAsn1(obj) {
+  // SignerInfo
+  var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // version
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      asn1.integerToDer(obj.version).getBytes()),
+    // issuerAndSerialNumber
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // name
+      forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),
+      // serial
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+        forge.util.hexToBytes(obj.serialNumber))
+    ]),
+    // digestAlgorithm
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // algorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(obj.digestAlgorithm).getBytes()),
+      // parameters (null)
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+    ])
+  ]);
+
+  // authenticatedAttributes (OPTIONAL)
+  if(obj.authenticatedAttributesAsn1) {
+    // add ASN.1 previously generated during signing
+    rval.value.push(obj.authenticatedAttributesAsn1);
+  }
+
+  // digestEncryptionAlgorithm
+  rval.value.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // algorithm
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+      asn1.oidToDer(obj.signatureAlgorithm).getBytes()),
+    // parameters (null)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+  ]));
+
+  // encryptedDigest
+  rval.value.push(asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, obj.signature));
+
+  // unauthenticatedAttributes (OPTIONAL)
+  if(obj.unauthenticatedAttributes.length > 0) {
+    // [1] IMPLICIT
+    var attrsAsn1 = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, []);
+    for(var i = 0; i < obj.unauthenticatedAttributes.length; ++i) {
+      var attr = obj.unauthenticatedAttributes[i];
+      attrsAsn1.values.push(_attributeToAsn1(attr));
+    }
+    rval.value.push(attrsAsn1);
+  }
+
+  return rval;
+}
+
+/**
+ * Map a set of SignerInfo ASN.1 objects to an array of signer objects.
+ *
+ * @param signerInfoAsn1s an array of ASN.1 SignerInfos (i.e. SET OF).
+ *
+ * @return an array of signers objects.
+ */
+function _signersFromAsn1(signerInfoAsn1s) {
+  var ret = [];
+  for(var i = 0; i < signerInfoAsn1s.length; ++i) {
+    ret.push(_signerFromAsn1(signerInfoAsn1s[i]));
+  }
+  return ret;
+}
+
+/**
+ * Map an array of signer objects to ASN.1 objects.
+ *
+ * @param signers an array of signer objects.
+ *
+ * @return an array of ASN.1 SignerInfos.
+ */
+function _signersToAsn1(signers) {
+  var ret = [];
+  for(var i = 0; i < signers.length; ++i) {
+    ret.push(_signerToAsn1(signers[i]));
+  }
+  return ret;
+}
+
+/**
+ * Convert an attribute object to an ASN.1 Attribute.
+ *
+ * @param attr the attribute object.
+ *
+ * @return the ASN.1 Attribute.
+ */
+function _attributeToAsn1(attr) {
+  var value;
+
+  // TODO: generalize to support more attributes
+  if(attr.type === forge.pki.oids.contentType) {
+    value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+      asn1.oidToDer(attr.value).getBytes());
+  } else if(attr.type === forge.pki.oids.messageDigest) {
+    value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+      attr.value.bytes());
+  } else if(attr.type === forge.pki.oids.signingTime) {
+    /* Note per RFC 2985: Dates between 1 January 1950 and 31 December 2049
+      (inclusive) MUST be encoded as UTCTime. Any dates with year values
+      before 1950 or after 2049 MUST be encoded as GeneralizedTime. [Further,]
+      UTCTime values MUST be expressed in Greenwich Mean Time (Zulu) and MUST
+      include seconds (i.e., times are YYMMDDHHMMSSZ), even where the
+      number of seconds is zero.  Midnight (GMT) must be represented as
+      "YYMMDD000000Z". */
+    // TODO: make these module-level constants
+    var jan_1_1950 = new Date('1950-01-01T00:00:00Z');
+    var jan_1_2050 = new Date('2050-01-01T00:00:00Z');
+    var date = attr.value;
+    if(typeof date === 'string') {
+      // try to parse date
+      var timestamp = Date.parse(date);
+      if(!isNaN(timestamp)) {
+        date = new Date(timestamp);
+      } else if(date.length === 13) {
+        // YYMMDDHHMMSSZ (13 chars for UTCTime)
+        date = asn1.utcTimeToDate(date);
+      } else {
+        // assume generalized time
+        date = asn1.generalizedTimeToDate(date);
+      }
+    }
+
+    if(date >= jan_1_1950 && date < jan_1_2050) {
+      value = asn1.create(
+        asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,
+        asn1.dateToUtcTime(date));
+    } else {
+      value = asn1.create(
+        asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false,
+        asn1.dateToGeneralizedTime(date));
+    }
+  }
+
+  // TODO: expose as common API call
+  // create a RelativeDistinguishedName set
+  // each value in the set is an AttributeTypeAndValue first
+  // containing the type (an OID) and second the value
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // AttributeType
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+      asn1.oidToDer(attr.type).getBytes()),
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
+      // AttributeValue
+      value
+    ])
+  ]);
+}
+
+/**
+ * Map messages encrypted content to ASN.1 objects.
+ *
+ * @param ec The encryptedContent object of the message.
+ *
+ * @return ASN.1 representation of the encryptedContent object (SEQUENCE).
+ */
+function _encryptedContentToAsn1(ec) {
+  return [
+    // ContentType, always Data for the moment
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+      asn1.oidToDer(forge.pki.oids.data).getBytes()),
+    // ContentEncryptionAlgorithmIdentifier
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // Algorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(ec.algorithm).getBytes()),
+      // Parameters (IV)
+      !ec.parameter ?
+        undefined :
+        asn1.create(
+          asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+          ec.parameter.getBytes())
+    ]),
+    // [0] EncryptedContent
+    asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+        ec.content.getBytes())
+    ])
+  ];
+}
+
+/**
+ * Reads the "common part" of an PKCS#7 content block (in ASN.1 format)
+ *
+ * This function reads the "common part" of the PKCS#7 content blocks
+ * EncryptedData and EnvelopedData, i.e. version number and symmetrically
+ * encrypted content block.
+ *
+ * The result of the ASN.1 validate and capture process is returned
+ * to allow the caller to extract further data, e.g. the list of recipients
+ * in case of a EnvelopedData object.
+ *
+ * @param msg the PKCS#7 object to read the data to.
+ * @param obj the ASN.1 representation of the content block.
+ * @param validator the ASN.1 structure validator object to use.
+ *
+ * @return the value map captured by validator object.
+ */
+function _fromAsn1(msg, obj, validator) {
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, validator, capture, errors)) {
+    var error = new Error('Cannot read PKCS#7 message. ' +
+      'ASN.1 object is not a supported PKCS#7 message.');
+    error.errors = error;
+    throw error;
+  }
+
+  // Check contentType, so far we only support (raw) Data.
+  var contentType = asn1.derToOid(capture.contentType);
+  if(contentType !== forge.pki.oids.data) {
+    throw new Error('Unsupported PKCS#7 message. ' +
+      'Only wrapped ContentType Data supported.');
+  }
+
+  if(capture.encryptedContent) {
+    var content = '';
+    if(forge.util.isArray(capture.encryptedContent)) {
+      for(var i = 0; i < capture.encryptedContent.length; ++i) {
+        if(capture.encryptedContent[i].type !== asn1.Type.OCTETSTRING) {
+          throw new Error('Malformed PKCS#7 message, expecting encrypted ' +
+            'content constructed of only OCTET STRING objects.');
+        }
+        content += capture.encryptedContent[i].value;
+      }
+    } else {
+      content = capture.encryptedContent;
+    }
+    msg.encryptedContent = {
+      algorithm: asn1.derToOid(capture.encAlgorithm),
+      parameter: forge.util.createBuffer(capture.encParameter.value),
+      content: forge.util.createBuffer(content)
+    };
+  }
+
+  if(capture.content) {
+    var content = '';
+    if(forge.util.isArray(capture.content)) {
+      for(var i = 0; i < capture.content.length; ++i) {
+        if(capture.content[i].type !== asn1.Type.OCTETSTRING) {
+          throw new Error('Malformed PKCS#7 message, expecting ' +
+            'content constructed of only OCTET STRING objects.');
+        }
+        content += capture.content[i].value;
+      }
+    } else {
+      content = capture.content;
+    }
+    msg.content = forge.util.createBuffer(content);
+  }
+
+  msg.version = capture.version.charCodeAt(0);
+  msg.rawCapture = capture;
+
+  return capture;
+}
+
+/**
+ * Decrypt the symmetrically encrypted content block of the PKCS#7 message.
+ *
+ * Decryption is skipped in case the PKCS#7 message object already has a
+ * (decrypted) content attribute.  The algorithm, key and cipher parameters
+ * (probably the iv) are taken from the encryptedContent attribute of the
+ * message object.
+ *
+ * @param The PKCS#7 message object.
+ */
+function _decryptContent(msg) {
+  if(msg.encryptedContent.key === undefined) {
+    throw new Error('Symmetric key not available.');
+  }
+
+  if(msg.content === undefined) {
+    var ciph;
+
+    switch(msg.encryptedContent.algorithm) {
+      case forge.pki.oids['aes128-CBC']:
+      case forge.pki.oids['aes192-CBC']:
+      case forge.pki.oids['aes256-CBC']:
+        ciph = forge.aes.createDecryptionCipher(msg.encryptedContent.key);
+        break;
+
+      case forge.pki.oids['desCBC']:
+      case forge.pki.oids['des-EDE3-CBC']:
+        ciph = forge.des.createDecryptionCipher(msg.encryptedContent.key);
+        break;
+
+      default:
+        throw new Error('Unsupported symmetric cipher, OID ' +
+          msg.encryptedContent.algorithm);
+    }
+    ciph.start(msg.encryptedContent.parameter);
+    ciph.update(msg.encryptedContent.content);
+
+    if(!ciph.finish()) {
+      throw new Error('Symmetric decryption failed.');
+    }
+
+    msg.content = ciph.output;
+  }
+}
Index: frontend/node_modules/node-forge/lib/pkcs7asn1.js
===================================================================
--- frontend/node_modules/node-forge/lib/pkcs7asn1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pkcs7asn1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,410 @@
+/**
+ * Javascript implementation of ASN.1 validators for PKCS#7 v1.5.
+ *
+ * @author Dave Longley
+ * @author Stefan Siegl
+ *
+ * Copyright (c) 2012-2015 Digital Bazaar, Inc.
+ * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
+ *
+ * The ASN.1 representation of PKCS#7 is as follows
+ * (see RFC #2315 for details, http://www.ietf.org/rfc/rfc2315.txt):
+ *
+ * A PKCS#7 message consists of a ContentInfo on root level, which may
+ * contain any number of further ContentInfo nested into it.
+ *
+ * ContentInfo ::= SEQUENCE {
+ *   contentType                ContentType,
+ *   content               [0]  EXPLICIT ANY DEFINED BY contentType OPTIONAL
+ * }
+ *
+ * ContentType ::= OBJECT IDENTIFIER
+ *
+ * EnvelopedData ::= SEQUENCE {
+ *   version                    Version,
+ *   recipientInfos             RecipientInfos,
+ *   encryptedContentInfo       EncryptedContentInfo
+ * }
+ *
+ * EncryptedData ::= SEQUENCE {
+ *   version                    Version,
+ *   encryptedContentInfo       EncryptedContentInfo
+ * }
+ *
+ * id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ *   us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
+ *
+ * SignedData ::= SEQUENCE {
+ *   version           INTEGER,
+ *   digestAlgorithms  DigestAlgorithmIdentifiers,
+ *   contentInfo       ContentInfo,
+ *   certificates      [0] IMPLICIT Certificates OPTIONAL,
+ *   crls              [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+ *   signerInfos       SignerInfos
+ * }
+ *
+ * SignerInfos ::= SET OF SignerInfo
+ *
+ * SignerInfo ::= SEQUENCE {
+ *   version                    Version,
+ *   issuerAndSerialNumber      IssuerAndSerialNumber,
+ *   digestAlgorithm            DigestAlgorithmIdentifier,
+ *   authenticatedAttributes    [0] IMPLICIT Attributes OPTIONAL,
+ *   digestEncryptionAlgorithm  DigestEncryptionAlgorithmIdentifier,
+ *   encryptedDigest            EncryptedDigest,
+ *   unauthenticatedAttributes  [1] IMPLICIT Attributes OPTIONAL
+ * }
+ *
+ * EncryptedDigest ::= OCTET STRING
+ *
+ * Attributes ::= SET OF Attribute
+ *
+ * Attribute ::= SEQUENCE {
+ *   attrType    OBJECT IDENTIFIER,
+ *   attrValues  SET OF AttributeValue
+ * }
+ *
+ * AttributeValue ::= ANY
+ *
+ * Version ::= INTEGER
+ *
+ * RecipientInfos ::= SET OF RecipientInfo
+ *
+ * EncryptedContentInfo ::= SEQUENCE {
+ *   contentType                 ContentType,
+ *   contentEncryptionAlgorithm  ContentEncryptionAlgorithmIdentifier,
+ *   encryptedContent       [0]  IMPLICIT EncryptedContent OPTIONAL
+ * }
+ *
+ * ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
+ * for the algorithm, if any. In the case of AES and DES3, there is only one,
+ * the IV.
+ *
+ * AlgorithmIdentifer ::= SEQUENCE {
+ *    algorithm OBJECT IDENTIFIER,
+ *    parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ *
+ * EncryptedContent ::= OCTET STRING
+ *
+ * RecipientInfo ::= SEQUENCE {
+ *   version                     Version,
+ *   issuerAndSerialNumber       IssuerAndSerialNumber,
+ *   keyEncryptionAlgorithm      KeyEncryptionAlgorithmIdentifier,
+ *   encryptedKey                EncryptedKey
+ * }
+ *
+ * IssuerAndSerialNumber ::= SEQUENCE {
+ *   issuer                      Name,
+ *   serialNumber                CertificateSerialNumber
+ * }
+ *
+ * CertificateSerialNumber ::= INTEGER
+ *
+ * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * EncryptedKey ::= OCTET STRING
+ */
+var forge = require('./forge');
+require('./asn1');
+require('./util');
+
+// shortcut for ASN.1 API
+var asn1 = forge.asn1;
+
+// shortcut for PKCS#7 API
+var p7v = module.exports = forge.pkcs7asn1 = forge.pkcs7asn1 || {};
+forge.pkcs7 = forge.pkcs7 || {};
+forge.pkcs7.asn1 = p7v;
+
+var contentInfoValidator = {
+  name: 'ContentInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'ContentInfo.ContentType',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OID,
+    constructed: false,
+    capture: 'contentType'
+  }, {
+    name: 'ContentInfo.content',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 0,
+    constructed: true,
+    optional: true,
+    captureAsn1: 'content'
+  }]
+};
+p7v.contentInfoValidator = contentInfoValidator;
+
+var encryptedContentInfoValidator = {
+  name: 'EncryptedContentInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'EncryptedContentInfo.contentType',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OID,
+    constructed: false,
+    capture: 'contentType'
+  }, {
+    name: 'EncryptedContentInfo.contentEncryptionAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'EncryptedContentInfo.contentEncryptionAlgorithm.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'encAlgorithm'
+    }, {
+      name: 'EncryptedContentInfo.contentEncryptionAlgorithm.parameter',
+      tagClass: asn1.Class.UNIVERSAL,
+      captureAsn1: 'encParameter'
+    }]
+  }, {
+    name: 'EncryptedContentInfo.encryptedContent',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 0,
+    /* The PKCS#7 structure output by OpenSSL somewhat differs from what
+     * other implementations do generate.
+     *
+     * OpenSSL generates a structure like this:
+     * SEQUENCE {
+     *    ...
+     *    [0]
+     *       26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38
+     *       C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45
+     *       ...
+     * }
+     *
+     * Whereas other implementations (and this PKCS#7 module) generate:
+     * SEQUENCE {
+     *    ...
+     *    [0] {
+     *       OCTET STRING
+     *          26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38
+     *          C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45
+     *          ...
+     *    }
+     * }
+     *
+     * In order to support both, we just capture the context specific
+     * field here.  The OCTET STRING bit is removed below.
+     */
+    capture: 'encryptedContent',
+    captureAsn1: 'encryptedContentAsn1'
+  }]
+};
+
+p7v.envelopedDataValidator = {
+  name: 'EnvelopedData',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'EnvelopedData.Version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'version'
+  }, {
+    name: 'EnvelopedData.RecipientInfos',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SET,
+    constructed: true,
+    captureAsn1: 'recipientInfos'
+  }].concat(encryptedContentInfoValidator)
+};
+
+p7v.encryptedDataValidator = {
+  name: 'EncryptedData',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'EncryptedData.Version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'version'
+  }].concat(encryptedContentInfoValidator)
+};
+
+var signerValidator = {
+  name: 'SignerInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'SignerInfo.version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false
+  }, {
+    name: 'SignerInfo.issuerAndSerialNumber',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'SignerInfo.issuerAndSerialNumber.issuer',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      captureAsn1: 'issuer'
+    }, {
+      name: 'SignerInfo.issuerAndSerialNumber.serialNumber',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.INTEGER,
+      constructed: false,
+      capture: 'serial'
+    }]
+  }, {
+    name: 'SignerInfo.digestAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'SignerInfo.digestAlgorithm.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'digestAlgorithm'
+    }, {
+      name: 'SignerInfo.digestAlgorithm.parameter',
+      tagClass: asn1.Class.UNIVERSAL,
+      constructed: false,
+      captureAsn1: 'digestParameter',
+      optional: true
+    }]
+  }, {
+    name: 'SignerInfo.authenticatedAttributes',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 0,
+    constructed: true,
+    optional: true,
+    capture: 'authenticatedAttributes'
+  }, {
+    name: 'SignerInfo.digestEncryptionAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    capture: 'signatureAlgorithm'
+  }, {
+    name: 'SignerInfo.encryptedDigest',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OCTETSTRING,
+    constructed: false,
+    capture: 'signature'
+  }, {
+    name: 'SignerInfo.unauthenticatedAttributes',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 1,
+    constructed: true,
+    optional: true,
+    capture: 'unauthenticatedAttributes'
+  }]
+};
+
+p7v.signedDataValidator = {
+  name: 'SignedData',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'SignedData.Version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'version'
+  }, {
+    name: 'SignedData.DigestAlgorithms',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SET,
+    constructed: true,
+    captureAsn1: 'digestAlgorithms'
+  },
+  contentInfoValidator,
+  {
+    name: 'SignedData.Certificates',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 0,
+    optional: true,
+    captureAsn1: 'certificates'
+  }, {
+    name: 'SignedData.CertificateRevocationLists',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 1,
+    optional: true,
+    captureAsn1: 'crls'
+  }, {
+    name: 'SignedData.SignerInfos',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SET,
+    capture: 'signerInfos',
+    optional: true,
+    value: [signerValidator]
+  }]
+};
+
+p7v.recipientInfoValidator = {
+  name: 'RecipientInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'RecipientInfo.version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'version'
+  }, {
+    name: 'RecipientInfo.issuerAndSerial',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'RecipientInfo.issuerAndSerial.issuer',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      captureAsn1: 'issuer'
+    }, {
+      name: 'RecipientInfo.issuerAndSerial.serialNumber',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.INTEGER,
+      constructed: false,
+      capture: 'serial'
+    }]
+  }, {
+    name: 'RecipientInfo.keyEncryptionAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'RecipientInfo.keyEncryptionAlgorithm.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'encAlgorithm'
+    }, {
+      name: 'RecipientInfo.keyEncryptionAlgorithm.parameter',
+      tagClass: asn1.Class.UNIVERSAL,
+      constructed: false,
+      captureAsn1: 'encParameter',
+      optional: true
+    }]
+  }, {
+    name: 'RecipientInfo.encryptedKey',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OCTETSTRING,
+    constructed: false,
+    capture: 'encKey'
+  }]
+};
Index: frontend/node_modules/node-forge/lib/pki.js
===================================================================
--- frontend/node_modules/node-forge/lib/pki.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pki.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,102 @@
+/**
+ * Javascript implementation of a basic Public Key Infrastructure, including
+ * support for RSA public and private keys.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2013 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./asn1');
+require('./oids');
+require('./pbe');
+require('./pem');
+require('./pbkdf2');
+require('./pkcs12');
+require('./pss');
+require('./rsa');
+require('./util');
+require('./x509');
+
+// shortcut for asn.1 API
+var asn1 = forge.asn1;
+
+/* Public Key Infrastructure (PKI) implementation. */
+var pki = module.exports = forge.pki = forge.pki || {};
+
+/**
+ * NOTE: THIS METHOD IS DEPRECATED. Use pem.decode() instead.
+ *
+ * Converts PEM-formatted data to DER.
+ *
+ * @param pem the PEM-formatted data.
+ *
+ * @return the DER-formatted data.
+ */
+pki.pemToDer = function(pem) {
+  var msg = forge.pem.decode(pem)[0];
+  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+    throw new Error('Could not convert PEM to DER; PEM is encrypted.');
+  }
+  return forge.util.createBuffer(msg.body);
+};
+
+/**
+ * Converts an RSA private key from PEM format.
+ *
+ * @param pem the PEM-formatted private key.
+ *
+ * @return the private key.
+ */
+pki.privateKeyFromPem = function(pem) {
+  var msg = forge.pem.decode(pem)[0];
+
+  if(msg.type !== 'PRIVATE KEY' && msg.type !== 'RSA PRIVATE KEY') {
+    var error = new Error('Could not convert private key from PEM; PEM ' +
+      'header type is not "PRIVATE KEY" or "RSA PRIVATE KEY".');
+    error.headerType = msg.type;
+    throw error;
+  }
+  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+    throw new Error('Could not convert private key from PEM; PEM is encrypted.');
+  }
+
+  // convert DER to ASN.1 object
+  var obj = asn1.fromDer(msg.body);
+
+  return pki.privateKeyFromAsn1(obj);
+};
+
+/**
+ * Converts an RSA private key to PEM format.
+ *
+ * @param key the private key.
+ * @param maxline the maximum characters per line, defaults to 64.
+ *
+ * @return the PEM-formatted private key.
+ */
+pki.privateKeyToPem = function(key, maxline) {
+  // convert to ASN.1, then DER, then PEM-encode
+  var msg = {
+    type: 'RSA PRIVATE KEY',
+    body: asn1.toDer(pki.privateKeyToAsn1(key)).getBytes()
+  };
+  return forge.pem.encode(msg, {maxline: maxline});
+};
+
+/**
+ * Converts a PrivateKeyInfo to PEM format.
+ *
+ * @param pki the PrivateKeyInfo.
+ * @param maxline the maximum characters per line, defaults to 64.
+ *
+ * @return the PEM-formatted private key.
+ */
+pki.privateKeyInfoToPem = function(pki, maxline) {
+  // convert to DER, then PEM-encode
+  var msg = {
+    type: 'PRIVATE KEY',
+    body: asn1.toDer(pki).getBytes()
+  };
+  return forge.pem.encode(msg, {maxline: maxline});
+};
Index: frontend/node_modules/node-forge/lib/prime.js
===================================================================
--- frontend/node_modules/node-forge/lib/prime.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/prime.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,297 @@
+/**
+ * Prime number generation API.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./util');
+require('./jsbn');
+require('./random');
+
+(function() {
+
+// forge.prime already defined
+if(forge.prime) {
+  module.exports = forge.prime;
+  return;
+}
+
+/* PRIME API */
+var prime = module.exports = forge.prime = forge.prime || {};
+
+var BigInteger = forge.jsbn.BigInteger;
+
+// primes are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29
+var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];
+var THIRTY = new BigInteger(null);
+THIRTY.fromInt(30);
+var op_or = function(x, y) {return x|y;};
+
+/**
+ * Generates a random probable prime with the given number of bits.
+ *
+ * Alternative algorithms can be specified by name as a string or as an
+ * object with custom options like so:
+ *
+ * {
+ *   name: 'PRIMEINC',
+ *   options: {
+ *     maxBlockTime: <the maximum amount of time to block the main
+ *       thread before allowing I/O other JS to run>,
+ *     millerRabinTests: <the number of miller-rabin tests to run>,
+ *     workerScript: <the worker script URL>,
+ *     workers: <the number of web workers (if supported) to use,
+ *       -1 to use estimated cores minus one>.
+ *     workLoad: the size of the work load, ie: number of possible prime
+ *       numbers for each web worker to check per work assignment,
+ *       (default: 100).
+ *   }
+ * }
+ *
+ * @param bits the number of bits for the prime number.
+ * @param options the options to use.
+ *          [algorithm] the algorithm to use (default: 'PRIMEINC').
+ *          [prng] a custom crypto-secure pseudo-random number generator to use,
+ *            that must define "getBytesSync".
+ *
+ * @return callback(err, num) called once the operation completes.
+ */
+prime.generateProbablePrime = function(bits, options, callback) {
+  if(typeof options === 'function') {
+    callback = options;
+    options = {};
+  }
+  options = options || {};
+
+  // default to PRIMEINC algorithm
+  var algorithm = options.algorithm || 'PRIMEINC';
+  if(typeof algorithm === 'string') {
+    algorithm = {name: algorithm};
+  }
+  algorithm.options = algorithm.options || {};
+
+  // create prng with api that matches BigInteger secure random
+  var prng = options.prng || forge.random;
+  var rng = {
+    // x is an array to fill with bytes
+    nextBytes: function(x) {
+      var b = prng.getBytesSync(x.length);
+      for(var i = 0; i < x.length; ++i) {
+        x[i] = b.charCodeAt(i);
+      }
+    }
+  };
+
+  if(algorithm.name === 'PRIMEINC') {
+    return primeincFindPrime(bits, rng, algorithm.options, callback);
+  }
+
+  throw new Error('Invalid prime generation algorithm: ' + algorithm.name);
+};
+
+function primeincFindPrime(bits, rng, options, callback) {
+  if('workers' in options) {
+    return primeincFindPrimeWithWorkers(bits, rng, options, callback);
+  }
+  return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);
+}
+
+function primeincFindPrimeWithoutWorkers(bits, rng, options, callback) {
+  // initialize random number
+  var num = generateRandom(bits, rng);
+
+  /* Note: All primes are of the form 30k+i for i < 30 and gcd(30, i)=1. The
+  number we are given is always aligned at 30k + 1. Each time the number is
+  determined not to be prime we add to get to the next 'i', eg: if the number
+  was at 30k + 1 we add 6. */
+  var deltaIdx = 0;
+
+  // get required number of MR tests
+  var mrTests = getMillerRabinTests(num.bitLength());
+  if('millerRabinTests' in options) {
+    mrTests = options.millerRabinTests;
+  }
+
+  // find prime nearest to 'num' for maxBlockTime ms
+  // 10 ms gives 5ms of leeway for other calculations before dropping
+  // below 60fps (1000/60 == 16.67), but in reality, the number will
+  // likely be higher due to an 'atomic' big int modPow
+  var maxBlockTime = 10;
+  if('maxBlockTime' in options) {
+    maxBlockTime = options.maxBlockTime;
+  }
+
+  _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback);
+}
+
+function _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback) {
+  var start = +new Date();
+  do {
+    // overflow, regenerate random number
+    if(num.bitLength() > bits) {
+      num = generateRandom(bits, rng);
+    }
+    // do primality test
+    if(num.isProbablePrime(mrTests)) {
+      return callback(null, num);
+    }
+    // get next potential prime
+    num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);
+  } while(maxBlockTime < 0 || (+new Date() - start < maxBlockTime));
+
+  // keep trying later
+  forge.util.setImmediate(function() {
+    _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback);
+  });
+}
+
+// NOTE: This algorithm is indeterminate in nature because workers
+// run in parallel looking at different segments of numbers. Even if this
+// algorithm is run twice with the same input from a predictable RNG, it
+// may produce different outputs.
+function primeincFindPrimeWithWorkers(bits, rng, options, callback) {
+  // web workers unavailable
+  if(typeof Worker === 'undefined') {
+    return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);
+  }
+
+  // initialize random number
+  var num = generateRandom(bits, rng);
+
+  // use web workers to generate keys
+  var numWorkers = options.workers;
+  var workLoad = options.workLoad || 100;
+  var range = workLoad * 30 / 8;
+  var workerScript = options.workerScript || 'forge/prime.worker.js';
+  if(numWorkers === -1) {
+    return forge.util.estimateCores(function(err, cores) {
+      if(err) {
+        // default to 2
+        cores = 2;
+      }
+      numWorkers = cores - 1;
+      generate();
+    });
+  }
+  generate();
+
+  function generate() {
+    // require at least 1 worker
+    numWorkers = Math.max(1, numWorkers);
+
+    // TODO: consider optimizing by starting workers outside getPrime() ...
+    // note that in order to clean up they will have to be made internally
+    // asynchronous which may actually be slower
+
+    // start workers immediately
+    var workers = [];
+    for(var i = 0; i < numWorkers; ++i) {
+      // FIXME: fix path or use blob URLs
+      workers[i] = new Worker(workerScript);
+    }
+    var running = numWorkers;
+
+    // listen for requests from workers and assign ranges to find prime
+    for(var i = 0; i < numWorkers; ++i) {
+      workers[i].addEventListener('message', workerMessage);
+    }
+
+    /* Note: The distribution of random numbers is unknown. Therefore, each
+    web worker is continuously allocated a range of numbers to check for a
+    random number until one is found.
+
+    Every 30 numbers will be checked just 8 times, because prime numbers
+    have the form:
+
+    30k+i, for i < 30 and gcd(30, i)=1 (there are 8 values of i for this)
+
+    Therefore, if we want a web worker to run N checks before asking for
+    a new range of numbers, each range must contain N*30/8 numbers.
+
+    For 100 checks (workLoad), this is a range of 375. */
+
+    var found = false;
+    function workerMessage(e) {
+      // ignore message, prime already found
+      if(found) {
+        return;
+      }
+
+      --running;
+      var data = e.data;
+      if(data.found) {
+        // terminate all workers
+        for(var i = 0; i < workers.length; ++i) {
+          workers[i].terminate();
+        }
+        found = true;
+        return callback(null, new BigInteger(data.prime, 16));
+      }
+
+      // overflow, regenerate random number
+      if(num.bitLength() > bits) {
+        num = generateRandom(bits, rng);
+      }
+
+      // assign new range to check
+      var hex = num.toString(16);
+
+      // start prime search
+      e.target.postMessage({
+        hex: hex,
+        workLoad: workLoad
+      });
+
+      num.dAddOffset(range, 0);
+    }
+  }
+}
+
+/**
+ * Generates a random number using the given number of bits and RNG.
+ *
+ * @param bits the number of bits for the number.
+ * @param rng the random number generator to use.
+ *
+ * @return the random number.
+ */
+function generateRandom(bits, rng) {
+  var num = new BigInteger(bits, rng);
+  // force MSB set
+  var bits1 = bits - 1;
+  if(!num.testBit(bits1)) {
+    num.bitwiseTo(BigInteger.ONE.shiftLeft(bits1), op_or, num);
+  }
+  // align number on 30k+1 boundary
+  num.dAddOffset(31 - num.mod(THIRTY).byteValue(), 0);
+  return num;
+}
+
+/**
+ * Returns the required number of Miller-Rabin tests to generate a
+ * prime with an error probability of (1/2)^80.
+ *
+ * See Handbook of Applied Cryptography Chapter 4, Table 4.4.
+ *
+ * @param bits the bit size.
+ *
+ * @return the required number of iterations.
+ */
+function getMillerRabinTests(bits) {
+  if(bits <= 100) return 27;
+  if(bits <= 150) return 18;
+  if(bits <= 200) return 15;
+  if(bits <= 250) return 12;
+  if(bits <= 300) return 9;
+  if(bits <= 350) return 8;
+  if(bits <= 400) return 7;
+  if(bits <= 500) return 6;
+  if(bits <= 600) return 5;
+  if(bits <= 800) return 4;
+  if(bits <= 1250) return 3;
+  return 2;
+}
+
+})();
Index: frontend/node_modules/node-forge/lib/prime.worker.js
===================================================================
--- frontend/node_modules/node-forge/lib/prime.worker.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/prime.worker.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,168 @@
+/**
+ * RSA Key Generation Worker.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2013 Digital Bazaar, Inc.
+ */
+// worker is built using CommonJS syntax to include all code in one worker file
+//importScripts('jsbn.js');
+var forge = require('./forge');
+require('./jsbn');
+
+// prime constants
+var LOW_PRIMES = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
+var LP_LIMIT = (1 << 26) / LOW_PRIMES[LOW_PRIMES.length - 1];
+
+var BigInteger = forge.jsbn.BigInteger;
+var BIG_TWO = new BigInteger(null);
+BIG_TWO.fromInt(2);
+
+self.addEventListener('message', function(e) {
+  var result = findPrime(e.data);
+  self.postMessage(result);
+});
+
+// start receiving ranges to check
+self.postMessage({found: false});
+
+// primes are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29
+var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];
+
+function findPrime(data) {
+  // TODO: abstract based on data.algorithm (PRIMEINC vs. others)
+
+  // create BigInteger from given random bytes
+  var num = new BigInteger(data.hex, 16);
+
+  /* Note: All primes are of the form 30k+i for i < 30 and gcd(30, i)=1. The
+    number we are given is always aligned at 30k + 1. Each time the number is
+    determined not to be prime we add to get to the next 'i', eg: if the number
+    was at 30k + 1 we add 6. */
+  var deltaIdx = 0;
+
+  // find nearest prime
+  var workLoad = data.workLoad;
+  for(var i = 0; i < workLoad; ++i) {
+    // do primality test
+    if(isProbablePrime(num)) {
+      return {found: true, prime: num.toString(16)};
+    }
+    // get next potential prime
+    num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);
+  }
+
+  return {found: false};
+}
+
+function isProbablePrime(n) {
+  // divide by low primes, ignore even checks, etc (n already aligned properly)
+  var i = 1;
+  while(i < LOW_PRIMES.length) {
+    var m = LOW_PRIMES[i];
+    var j = i + 1;
+    while(j < LOW_PRIMES.length && m < LP_LIMIT) {
+      m *= LOW_PRIMES[j++];
+    }
+    m = n.modInt(m);
+    while(i < j) {
+      if(m % LOW_PRIMES[i++] === 0) {
+        return false;
+      }
+    }
+  }
+  return runMillerRabin(n);
+}
+
+// HAC 4.24, Miller-Rabin
+function runMillerRabin(n) {
+  // n1 = n - 1
+  var n1 = n.subtract(BigInteger.ONE);
+
+  // get s and d such that n1 = 2^s * d
+  var s = n1.getLowestSetBit();
+  if(s <= 0) {
+    return false;
+  }
+  var d = n1.shiftRight(s);
+
+  var k = _getMillerRabinTests(n.bitLength());
+  var prng = getPrng();
+  var a;
+  for(var i = 0; i < k; ++i) {
+    // select witness 'a' at random from between 1 and n - 1
+    do {
+      a = new BigInteger(n.bitLength(), prng);
+    } while(a.compareTo(BigInteger.ONE) <= 0 || a.compareTo(n1) >= 0);
+
+    /* See if 'a' is a composite witness. */
+
+    // x = a^d mod n
+    var x = a.modPow(d, n);
+
+    // probably prime
+    if(x.compareTo(BigInteger.ONE) === 0 || x.compareTo(n1) === 0) {
+      continue;
+    }
+
+    var j = s;
+    while(--j) {
+      // x = x^2 mod a
+      x = x.modPowInt(2, n);
+
+      // 'n' is composite because no previous x == -1 mod n
+      if(x.compareTo(BigInteger.ONE) === 0) {
+        return false;
+      }
+      // x == -1 mod n, so probably prime
+      if(x.compareTo(n1) === 0) {
+        break;
+      }
+    }
+
+    // 'x' is first_x^(n1/2) and is not +/- 1, so 'n' is not prime
+    if(j === 0) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// get pseudo random number generator
+function getPrng() {
+  // 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() * 0xFF);
+      }
+    }
+  };
+}
+
+/**
+ * Returns the required number of Miller-Rabin tests to generate a
+ * prime with an error probability of (1/2)^80.
+ *
+ * See Handbook of Applied Cryptography Chapter 4, Table 4.4.
+ *
+ * @param bits the bit size.
+ *
+ * @return the required number of iterations.
+ */
+function _getMillerRabinTests(bits) {
+  if(bits <= 100) return 27;
+  if(bits <= 150) return 18;
+  if(bits <= 200) return 15;
+  if(bits <= 250) return 12;
+  if(bits <= 300) return 9;
+  if(bits <= 350) return 8;
+  if(bits <= 400) return 7;
+  if(bits <= 500) return 6;
+  if(bits <= 600) return 5;
+  if(bits <= 800) return 4;
+  if(bits <= 1250) return 3;
+  return 2;
+}
Index: frontend/node_modules/node-forge/lib/prng.js
===================================================================
--- frontend/node_modules/node-forge/lib/prng.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/prng.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,419 @@
+/**
+ * A javascript implementation of a cryptographically-secure
+ * Pseudo Random Number Generator (PRNG). The Fortuna algorithm is followed
+ * here though the use of SHA-256 is not enforced; when generating an
+ * a PRNG context, the hashing algorithm and block cipher used for
+ * the generator are specified via a plugin.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./util');
+
+var _crypto = null;
+if(forge.util.isNodejs && !forge.options.usePureJavaScript &&
+  !process.versions['node-webkit']) {
+  _crypto = require('crypto');
+}
+
+/* PRNG API */
+var prng = module.exports = forge.prng = forge.prng || {};
+
+/**
+ * Creates a new PRNG context.
+ *
+ * A PRNG plugin must be passed in that will provide:
+ *
+ * 1. A function that initializes the key and seed of a PRNG context. It
+ *   will be given a 16 byte key and a 16 byte seed. Any key expansion
+ *   or transformation of the seed from a byte string into an array of
+ *   integers (or similar) should be performed.
+ * 2. The cryptographic function used by the generator. It takes a key and
+ *   a seed.
+ * 3. A seed increment function. It takes the seed and returns seed + 1.
+ * 4. An api to create a message digest.
+ *
+ * For an example, see random.js.
+ *
+ * @param plugin the PRNG plugin to use.
+ */
+prng.create = function(plugin) {
+  var ctx = {
+    plugin: plugin,
+    key: null,
+    seed: null,
+    time: null,
+    // number of reseeds so far
+    reseeds: 0,
+    // amount of data generated so far
+    generated: 0,
+    // no initial key bytes
+    keyBytes: ''
+  };
+
+  // create 32 entropy pools (each is a message digest)
+  var md = plugin.md;
+  var pools = new Array(32);
+  for(var i = 0; i < 32; ++i) {
+    pools[i] = md.create();
+  }
+  ctx.pools = pools;
+
+  // entropy pools are written to cyclically, starting at index 0
+  ctx.pool = 0;
+
+  /**
+   * Generates random bytes. The bytes may be generated synchronously or
+   * asynchronously. Web workers must use the asynchronous interface or
+   * else the behavior is undefined.
+   *
+   * @param count the number of random bytes to generate.
+   * @param [callback(err, bytes)] called once the operation completes.
+   *
+   * @return count random bytes as a string.
+   */
+  ctx.generate = function(count, callback) {
+    // do synchronously
+    if(!callback) {
+      return ctx.generateSync(count);
+    }
+
+    // simple generator using counter-based CBC
+    var cipher = ctx.plugin.cipher;
+    var increment = ctx.plugin.increment;
+    var formatKey = ctx.plugin.formatKey;
+    var formatSeed = ctx.plugin.formatSeed;
+    var b = forge.util.createBuffer();
+
+    // paranoid deviation from Fortuna:
+    // reset key for every request to protect previously
+    // generated random bytes should the key be discovered;
+    // there is no 100ms based reseeding because of this
+    // forced reseed for every `generate` call
+    ctx.key = null;
+
+    generate();
+
+    function generate(err) {
+      if(err) {
+        return callback(err);
+      }
+
+      // sufficient bytes generated
+      if(b.length() >= count) {
+        return callback(null, b.getBytes(count));
+      }
+
+      // if amount of data generated is greater than 1 MiB, trigger reseed
+      if(ctx.generated > 0xfffff) {
+        ctx.key = null;
+      }
+
+      if(ctx.key === null) {
+        // prevent stack overflow
+        return forge.util.nextTick(function() {
+          _reseed(generate);
+        });
+      }
+
+      // generate the random bytes
+      var bytes = cipher(ctx.key, ctx.seed);
+      ctx.generated += bytes.length;
+      b.putBytes(bytes);
+
+      // generate bytes for a new key and seed
+      ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
+      ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
+
+      forge.util.setImmediate(generate);
+    }
+  };
+
+  /**
+   * Generates random bytes synchronously.
+   *
+   * @param count the number of random bytes to generate.
+   *
+   * @return count random bytes as a string.
+   */
+  ctx.generateSync = function(count) {
+    // simple generator using counter-based CBC
+    var cipher = ctx.plugin.cipher;
+    var increment = ctx.plugin.increment;
+    var formatKey = ctx.plugin.formatKey;
+    var formatSeed = ctx.plugin.formatSeed;
+
+    // paranoid deviation from Fortuna:
+    // reset key for every request to protect previously
+    // generated random bytes should the key be discovered;
+    // there is no 100ms based reseeding because of this
+    // forced reseed for every `generateSync` call
+    ctx.key = null;
+
+    var b = forge.util.createBuffer();
+    while(b.length() < count) {
+      // if amount of data generated is greater than 1 MiB, trigger reseed
+      if(ctx.generated > 0xfffff) {
+        ctx.key = null;
+      }
+
+      if(ctx.key === null) {
+        _reseedSync();
+      }
+
+      // generate the random bytes
+      var bytes = cipher(ctx.key, ctx.seed);
+      ctx.generated += bytes.length;
+      b.putBytes(bytes);
+
+      // generate bytes for a new key and seed
+      ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
+      ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
+    }
+
+    return b.getBytes(count);
+  };
+
+  /**
+   * Private function that asynchronously reseeds a generator.
+   *
+   * @param callback(err) called once the operation completes.
+   */
+  function _reseed(callback) {
+    if(ctx.pools[0].messageLength >= 32) {
+      _seed();
+      return callback();
+    }
+    // not enough seed data...
+    var needed = (32 - ctx.pools[0].messageLength) << 5;
+    ctx.seedFile(needed, function(err, bytes) {
+      if(err) {
+        return callback(err);
+      }
+      ctx.collect(bytes);
+      _seed();
+      callback();
+    });
+  }
+
+  /**
+   * Private function that synchronously reseeds a generator.
+   */
+  function _reseedSync() {
+    if(ctx.pools[0].messageLength >= 32) {
+      return _seed();
+    }
+    // not enough seed data...
+    var needed = (32 - ctx.pools[0].messageLength) << 5;
+    ctx.collect(ctx.seedFileSync(needed));
+    _seed();
+  }
+
+  /**
+   * Private function that seeds a generator once enough bytes are available.
+   */
+  function _seed() {
+    // update reseed count
+    ctx.reseeds = (ctx.reseeds === 0xffffffff) ? 0 : ctx.reseeds + 1;
+
+    // goal is to update `key` via:
+    // key = hash(key + s)
+    //   where 's' is all collected entropy from selected pools, then...
+
+    // create a plugin-based message digest
+    var md = ctx.plugin.md.create();
+
+    // consume current key bytes
+    md.update(ctx.keyBytes);
+
+    // digest the entropy of pools whose index k meet the
+    // condition 'n mod 2^k == 0' where n is the number of reseeds
+    var _2powK = 1;
+    for(var k = 0; k < 32; ++k) {
+      if(ctx.reseeds % _2powK === 0) {
+        md.update(ctx.pools[k].digest().getBytes());
+        ctx.pools[k].start();
+      }
+      _2powK = _2powK << 1;
+    }
+
+    // get digest for key bytes
+    ctx.keyBytes = md.digest().getBytes();
+
+    // paranoid deviation from Fortuna:
+    // update `seed` via `seed = hash(key)`
+    // instead of initializing to zero once and only
+    // ever incrementing it
+    md.start();
+    md.update(ctx.keyBytes);
+    var seedBytes = md.digest().getBytes();
+
+    // update state
+    ctx.key = ctx.plugin.formatKey(ctx.keyBytes);
+    ctx.seed = ctx.plugin.formatSeed(seedBytes);
+    ctx.generated = 0;
+  }
+
+  /**
+   * The built-in default seedFile. This seedFile is used when entropy
+   * is needed immediately.
+   *
+   * @param needed the number of bytes that are needed.
+   *
+   * @return the random bytes.
+   */
+  function defaultSeedFile(needed) {
+    // use window.crypto.getRandomValues strong source of entropy if available
+    var getRandomValues = null;
+    var globalScope = forge.util.globalScope;
+    var _crypto = globalScope.crypto || globalScope.msCrypto;
+    if(_crypto && _crypto.getRandomValues) {
+      getRandomValues = function(arr) {
+        return _crypto.getRandomValues(arr);
+      };
+    }
+
+    var b = forge.util.createBuffer();
+    if(getRandomValues) {
+      while(b.length() < needed) {
+        // max byte length is 65536 before QuotaExceededError is thrown
+        // http://www.w3.org/TR/WebCryptoAPI/#RandomSource-method-getRandomValues
+        var count = Math.max(1, Math.min(needed - b.length(), 65536) / 4);
+        var entropy = new Uint32Array(Math.floor(count));
+        try {
+          getRandomValues(entropy);
+          for(var i = 0; i < entropy.length; ++i) {
+            b.putInt32(entropy[i]);
+          }
+        } catch(e) {
+          /* only ignore QuotaExceededError */
+          if(!(typeof QuotaExceededError !== 'undefined' &&
+            e instanceof QuotaExceededError)) {
+            throw e;
+          }
+        }
+      }
+    }
+
+    // be sad and add some weak random data
+    if(b.length() < needed) {
+      /* Draws from Park-Miller "minimal standard" 31 bit PRNG,
+      implemented with David G. Carta's optimization: with 32 bit math
+      and without division (Public Domain). */
+      var hi, lo, next;
+      var seed = Math.floor(Math.random() * 0x010000);
+      while(b.length() < needed) {
+        lo = 16807 * (seed & 0xFFFF);
+        hi = 16807 * (seed >> 16);
+        lo += (hi & 0x7FFF) << 16;
+        lo += hi >> 15;
+        lo = (lo & 0x7FFFFFFF) + (lo >> 31);
+        seed = lo & 0xFFFFFFFF;
+
+        // consume lower 3 bytes of seed
+        for(var i = 0; i < 3; ++i) {
+          // throw in more pseudo random
+          next = seed >>> (i << 3);
+          next ^= Math.floor(Math.random() * 0x0100);
+          b.putByte(next & 0xFF);
+        }
+      }
+    }
+
+    return b.getBytes(needed);
+  }
+  // initialize seed file APIs
+  if(_crypto) {
+    // use nodejs async API
+    ctx.seedFile = function(needed, callback) {
+      _crypto.randomBytes(needed, function(err, bytes) {
+        if(err) {
+          return callback(err);
+        }
+        callback(null, bytes.toString());
+      });
+    };
+    // use nodejs sync API
+    ctx.seedFileSync = function(needed) {
+      return _crypto.randomBytes(needed).toString();
+    };
+  } else {
+    ctx.seedFile = function(needed, callback) {
+      try {
+        callback(null, defaultSeedFile(needed));
+      } catch(e) {
+        callback(e);
+      }
+    };
+    ctx.seedFileSync = defaultSeedFile;
+  }
+
+  /**
+   * Adds entropy to a prng ctx's accumulator.
+   *
+   * @param bytes the bytes of entropy as a string.
+   */
+  ctx.collect = function(bytes) {
+    // iterate over pools distributing entropy cyclically
+    var count = bytes.length;
+    for(var i = 0; i < count; ++i) {
+      ctx.pools[ctx.pool].update(bytes.substr(i, 1));
+      ctx.pool = (ctx.pool === 31) ? 0 : ctx.pool + 1;
+    }
+  };
+
+  /**
+   * Collects an integer of n bits.
+   *
+   * @param i the integer entropy.
+   * @param n the number of bits in the integer.
+   */
+  ctx.collectInt = function(i, n) {
+    var bytes = '';
+    for(var x = 0; x < n; x += 8) {
+      bytes += String.fromCharCode((i >> x) & 0xFF);
+    }
+    ctx.collect(bytes);
+  };
+
+  /**
+   * Registers a Web Worker to receive immediate entropy from the main thread.
+   * This method is required until Web Workers can access the native crypto
+   * API. This method should be called twice for each created worker, once in
+   * the main thread, and once in the worker itself.
+   *
+   * @param worker the worker to register.
+   */
+  ctx.registerWorker = function(worker) {
+    // worker receives random bytes
+    if(worker === self) {
+      ctx.seedFile = function(needed, callback) {
+        function listener(e) {
+          var data = e.data;
+          if(data.forge && data.forge.prng) {
+            self.removeEventListener('message', listener);
+            callback(data.forge.prng.err, data.forge.prng.bytes);
+          }
+        }
+        self.addEventListener('message', listener);
+        self.postMessage({forge: {prng: {needed: needed}}});
+      };
+    } else {
+      // main thread sends random bytes upon request
+      var listener = function(e) {
+        var data = e.data;
+        if(data.forge && data.forge.prng) {
+          ctx.seedFile(data.forge.prng.needed, function(err, bytes) {
+            worker.postMessage({forge: {prng: {err: err, bytes: bytes}}});
+          });
+        }
+      };
+      // TODO: do we need to remove the event listener when the worker dies?
+      worker.addEventListener('message', listener);
+    }
+  };
+
+  return ctx;
+};
Index: frontend/node_modules/node-forge/lib/pss.js
===================================================================
--- frontend/node_modules/node-forge/lib/pss.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/pss.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,241 @@
+/**
+ * Javascript implementation of PKCS#1 PSS signature padding.
+ *
+ * @author Stefan Siegl
+ *
+ * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
+ */
+var forge = require('./forge');
+require('./random');
+require('./util');
+
+// shortcut for PSS API
+var pss = module.exports = forge.pss = forge.pss || {};
+
+/**
+ * Creates a PSS signature scheme object.
+ *
+ * There are several ways to provide a salt for encoding:
+ *
+ * 1. Specify the saltLength only and the built-in PRNG will generate it.
+ * 2. Specify the saltLength and a custom PRNG with 'getBytesSync' defined that
+ *   will be used.
+ * 3. Specify the salt itself as a forge.util.ByteBuffer.
+ *
+ * @param options the options to use:
+ *          md the message digest object to use, a forge md instance.
+ *          mgf the mask generation function to use, a forge mgf instance.
+ *          [saltLength] the length of the salt in octets.
+ *          [prng] the pseudo-random number generator to use to produce a salt.
+ *          [salt] the salt to use when encoding.
+ *
+ * @return a signature scheme object.
+ */
+pss.create = function(options) {
+  // backwards compatibility w/legacy args: hash, mgf, sLen
+  if(arguments.length === 3) {
+    options = {
+      md: arguments[0],
+      mgf: arguments[1],
+      saltLength: arguments[2]
+    };
+  }
+
+  var hash = options.md;
+  var mgf = options.mgf;
+  var hLen = hash.digestLength;
+
+  var salt_ = options.salt || null;
+  if(typeof salt_ === 'string') {
+    // assume binary-encoded string
+    salt_ = forge.util.createBuffer(salt_);
+  }
+
+  var sLen;
+  if('saltLength' in options) {
+    sLen = options.saltLength;
+  } else if(salt_ !== null) {
+    sLen = salt_.length();
+  } else {
+    throw new Error('Salt length not specified or specific salt not given.');
+  }
+
+  if(salt_ !== null && salt_.length() !== sLen) {
+    throw new Error('Given salt length does not match length of given salt.');
+  }
+
+  var prng = options.prng || forge.random;
+
+  var pssobj = {};
+
+  /**
+   * Encodes a PSS signature.
+   *
+   * This function implements EMSA-PSS-ENCODE as per RFC 3447, section 9.1.1.
+   *
+   * @param md the message digest object with the hash to sign.
+   * @param modsBits the length of the RSA modulus in bits.
+   *
+   * @return the encoded message as a binary-encoded string of length
+   *           ceil((modBits - 1) / 8).
+   */
+  pssobj.encode = function(md, modBits) {
+    var i;
+    var emBits = modBits - 1;
+    var emLen = Math.ceil(emBits / 8);
+
+    /* 2. Let mHash = Hash(M), an octet string of length hLen. */
+    var mHash = md.digest().getBytes();
+
+    /* 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. */
+    if(emLen < hLen + sLen + 2) {
+      throw new Error('Message is too long to encrypt.');
+    }
+
+    /* 4. Generate a random octet string salt of length sLen; if sLen = 0,
+     *    then salt is the empty string. */
+    var salt;
+    if(salt_ === null) {
+      salt = prng.getBytesSync(sLen);
+    } else {
+      salt = salt_.bytes();
+    }
+
+    /* 5. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt; */
+    var m_ = new forge.util.ByteBuffer();
+    m_.fillWithByte(0, 8);
+    m_.putBytes(mHash);
+    m_.putBytes(salt);
+
+    /* 6. Let H = Hash(M'), an octet string of length hLen. */
+    hash.start();
+    hash.update(m_.getBytes());
+    var h = hash.digest().getBytes();
+
+    /* 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
+     *    zero octets.  The length of PS may be 0. */
+    var ps = new forge.util.ByteBuffer();
+    ps.fillWithByte(0, emLen - sLen - hLen - 2);
+
+    /* 8. Let DB = PS || 0x01 || salt; DB is an octet string of length
+     *    emLen - hLen - 1. */
+    ps.putByte(0x01);
+    ps.putBytes(salt);
+    var db = ps.getBytes();
+
+    /* 9. Let dbMask = MGF(H, emLen - hLen - 1). */
+    var maskLen = emLen - hLen - 1;
+    var dbMask = mgf.generate(h, maskLen);
+
+    /* 10. Let maskedDB = DB \xor dbMask. */
+    var maskedDB = '';
+    for(i = 0; i < maskLen; i++) {
+      maskedDB += String.fromCharCode(db.charCodeAt(i) ^ dbMask.charCodeAt(i));
+    }
+
+    /* 11. Set the leftmost 8emLen - emBits bits of the leftmost octet in
+     *     maskedDB to zero. */
+    var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;
+    maskedDB = String.fromCharCode(maskedDB.charCodeAt(0) & ~mask) +
+      maskedDB.substr(1);
+
+    /* 12. Let EM = maskedDB || H || 0xbc.
+     * 13. Output EM. */
+    return maskedDB + h + String.fromCharCode(0xbc);
+  };
+
+  /**
+   * Verifies a PSS signature.
+   *
+   * This function implements EMSA-PSS-VERIFY as per RFC 3447, section 9.1.2.
+   *
+   * @param mHash the message digest hash, as a binary-encoded string, to
+   *         compare against the signature.
+   * @param em the encoded message, as a binary-encoded string
+   *          (RSA decryption result).
+   * @param modsBits the length of the RSA modulus in bits.
+   *
+   * @return true if the signature was verified, false if not.
+   */
+  pssobj.verify = function(mHash, em, modBits) {
+    var i;
+    var emBits = modBits - 1;
+    var emLen = Math.ceil(emBits / 8);
+
+    /* c. Convert the message representative m to an encoded message EM
+     *    of length emLen = ceil((modBits - 1) / 8) octets, where modBits
+     *    is the length in bits of the RSA modulus n */
+    em = em.substr(-emLen);
+
+    /* 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. */
+    if(emLen < hLen + sLen + 2) {
+      throw new Error('Inconsistent parameters to PSS signature verification.');
+    }
+
+    /* 4. If the rightmost octet of EM does not have hexadecimal value
+     *    0xbc, output "inconsistent" and stop. */
+    if(em.charCodeAt(emLen - 1) !== 0xbc) {
+      throw new Error('Encoded message does not end in 0xBC.');
+    }
+
+    /* 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
+     *    let H be the next hLen octets. */
+    var maskLen = emLen - hLen - 1;
+    var maskedDB = em.substr(0, maskLen);
+    var h = em.substr(maskLen, hLen);
+
+    /* 6. If the leftmost 8emLen - emBits bits of the leftmost octet in
+     *    maskedDB are not all equal to zero, output "inconsistent" and stop. */
+    var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;
+    if((maskedDB.charCodeAt(0) & mask) !== 0) {
+      throw new Error('Bits beyond keysize not zero as expected.');
+    }
+
+    /* 7. Let dbMask = MGF(H, emLen - hLen - 1). */
+    var dbMask = mgf.generate(h, maskLen);
+
+    /* 8. Let DB = maskedDB \xor dbMask. */
+    var db = '';
+    for(i = 0; i < maskLen; i++) {
+      db += String.fromCharCode(maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i));
+    }
+
+    /* 9. Set the leftmost 8emLen - emBits bits of the leftmost octet
+     * in DB to zero. */
+    db = String.fromCharCode(db.charCodeAt(0) & ~mask) + db.substr(1);
+
+    /* 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
+     * or if the octet at position emLen - hLen - sLen - 1 (the leftmost
+     * position is "position 1") does not have hexadecimal value 0x01,
+     * output "inconsistent" and stop. */
+    var checkLen = emLen - hLen - sLen - 2;
+    for(i = 0; i < checkLen; i++) {
+      if(db.charCodeAt(i) !== 0x00) {
+        throw new Error('Leftmost octets not zero as expected');
+      }
+    }
+
+    if(db.charCodeAt(checkLen) !== 0x01) {
+      throw new Error('Inconsistent PSS signature, 0x01 marker not found');
+    }
+
+    /* 11. Let salt be the last sLen octets of DB. */
+    var salt = db.substr(-sLen);
+
+    /* 12.  Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */
+    var m_ = new forge.util.ByteBuffer();
+    m_.fillWithByte(0, 8);
+    m_.putBytes(mHash);
+    m_.putBytes(salt);
+
+    /* 13. Let H' = Hash(M'), an octet string of length hLen. */
+    hash.start();
+    hash.update(m_.getBytes());
+    var h_ = hash.digest().getBytes();
+
+    /* 14. If H = H', output "consistent." Otherwise, output "inconsistent." */
+    return h === h_;
+  };
+
+  return pssobj;
+};
Index: frontend/node_modules/node-forge/lib/random.js
===================================================================
--- frontend/node_modules/node-forge/lib/random.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/random.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,191 @@
+/**
+ * An API for getting cryptographically-secure random bytes. The bytes are
+ * generated using the Fortuna algorithm devised by Bruce Schneier and
+ * Niels Ferguson.
+ *
+ * Getting strong random bytes is not yet easy to do in javascript. The only
+ * truish random entropy that can be collected is from the mouse, keyboard, or
+ * from timing with respect to page loads, etc. This generator makes a poor
+ * attempt at providing random bytes when those sources haven't yet provided
+ * enough entropy to initially seed or to reseed the PRNG.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2009-2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./aes');
+require('./sha256');
+require('./prng');
+require('./util');
+
+(function() {
+
+// forge.random already defined
+if(forge.random && forge.random.getBytes) {
+  module.exports = forge.random;
+  return;
+}
+
+(function(jQuery) {
+
+// the default prng plugin, uses AES-128
+var prng_aes = {};
+var _prng_aes_output = new Array(4);
+var _prng_aes_buffer = forge.util.createBuffer();
+prng_aes.formatKey = function(key) {
+  // convert the key into 32-bit integers
+  var tmp = forge.util.createBuffer(key);
+  key = new Array(4);
+  key[0] = tmp.getInt32();
+  key[1] = tmp.getInt32();
+  key[2] = tmp.getInt32();
+  key[3] = tmp.getInt32();
+
+  // return the expanded key
+  return forge.aes._expandKey(key, false);
+};
+prng_aes.formatSeed = function(seed) {
+  // convert seed into 32-bit integers
+  var tmp = forge.util.createBuffer(seed);
+  seed = new Array(4);
+  seed[0] = tmp.getInt32();
+  seed[1] = tmp.getInt32();
+  seed[2] = tmp.getInt32();
+  seed[3] = tmp.getInt32();
+  return seed;
+};
+prng_aes.cipher = function(key, seed) {
+  forge.aes._updateBlock(key, seed, _prng_aes_output, false);
+  _prng_aes_buffer.putInt32(_prng_aes_output[0]);
+  _prng_aes_buffer.putInt32(_prng_aes_output[1]);
+  _prng_aes_buffer.putInt32(_prng_aes_output[2]);
+  _prng_aes_buffer.putInt32(_prng_aes_output[3]);
+  return _prng_aes_buffer.getBytes();
+};
+prng_aes.increment = function(seed) {
+  // FIXME: do we care about carry or signed issues?
+  ++seed[3];
+  return seed;
+};
+prng_aes.md = forge.md.sha256;
+
+/**
+ * Creates a new PRNG.
+ */
+function spawnPrng() {
+  var ctx = forge.prng.create(prng_aes);
+
+  /**
+   * Gets random bytes. If a native secure crypto API is unavailable, this
+   * method tries to make the bytes more unpredictable by drawing from data that
+   * can be collected from the user of the browser, eg: mouse movement.
+   *
+   * If a callback is given, this method will be called asynchronously.
+   *
+   * @param count the number of random bytes to get.
+   * @param [callback(err, bytes)] called once the operation completes.
+   *
+   * @return the random bytes in a string.
+   */
+  ctx.getBytes = function(count, callback) {
+    return ctx.generate(count, callback);
+  };
+
+  /**
+   * Gets random bytes asynchronously. If a native secure crypto API is
+   * unavailable, this method tries to make the bytes more unpredictable by
+   * drawing from data that can be collected from the user of the browser,
+   * eg: mouse movement.
+   *
+   * @param count the number of random bytes to get.
+   *
+   * @return the random bytes in a string.
+   */
+  ctx.getBytesSync = function(count) {
+    return ctx.generate(count);
+  };
+
+  return ctx;
+}
+
+// create default prng context
+var _ctx = spawnPrng();
+
+// add other sources of entropy only if window.crypto.getRandomValues is not
+// available -- otherwise this source will be automatically used by the prng
+var getRandomValues = null;
+var globalScope = forge.util.globalScope;
+var _crypto = globalScope.crypto || globalScope.msCrypto;
+if(_crypto && _crypto.getRandomValues) {
+  getRandomValues = function(arr) {
+    return _crypto.getRandomValues(arr);
+  };
+}
+
+if(forge.options.usePureJavaScript ||
+  (!forge.util.isNodejs && !getRandomValues)) {
+  // if this is a web worker, do not use weak entropy, instead register to
+  // receive strong entropy asynchronously from the main thread
+  if(typeof window === 'undefined' || window.document === undefined) {
+    // FIXME:
+  }
+
+  // get load time entropy
+  _ctx.collectInt(+new Date(), 32);
+
+  // add some entropy from navigator object
+  if(typeof(navigator) !== 'undefined') {
+    var _navBytes = '';
+    for(var key in navigator) {
+      try {
+        if(typeof(navigator[key]) == 'string') {
+          _navBytes += navigator[key];
+        }
+      } catch(e) {
+        /* Some navigator keys might not be accessible, e.g. the geolocation
+          attribute throws an exception if touched in Mozilla chrome://
+          context.
+
+          Silently ignore this and just don't use this as a source of
+          entropy. */
+      }
+    }
+    _ctx.collect(_navBytes);
+    _navBytes = null;
+  }
+
+  // add mouse and keyboard collectors if jquery is available
+  if(jQuery) {
+    // set up mouse entropy capture
+    jQuery().mousemove(function(e) {
+      // add mouse coords
+      _ctx.collectInt(e.clientX, 16);
+      _ctx.collectInt(e.clientY, 16);
+    });
+
+    // set up keyboard entropy capture
+    jQuery().keypress(function(e) {
+      _ctx.collectInt(e.charCode, 8);
+    });
+  }
+}
+
+/* Random API */
+if(!forge.random) {
+  forge.random = _ctx;
+} else {
+  // extend forge.random with _ctx
+  for(var key in _ctx) {
+    forge.random[key] = _ctx[key];
+  }
+}
+
+// expose spawn PRNG
+forge.random.createInstance = spawnPrng;
+
+module.exports = forge.random;
+
+})(typeof(jQuery) !== 'undefined' ? jQuery : null);
+
+})();
Index: frontend/node_modules/node-forge/lib/rc2.js
===================================================================
--- frontend/node_modules/node-forge/lib/rc2.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/rc2.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,410 @@
+/**
+ * RC2 implementation.
+ *
+ * @author Stefan Siegl
+ *
+ * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
+ *
+ * Information on the RC2 cipher is available from RFC #2268,
+ * http://www.ietf.org/rfc/rfc2268.txt
+ */
+var forge = require('./forge');
+require('./util');
+
+var piTable = [
+  0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
+  0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
+  0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
+  0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
+  0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
+  0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
+  0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
+  0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
+  0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
+  0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
+  0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
+  0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
+  0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
+  0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
+  0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
+  0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad
+];
+
+var s = [1, 2, 3, 5];
+
+/**
+ * Rotate a word left by given number of bits.
+ *
+ * Bits that are shifted out on the left are put back in on the right
+ * hand side.
+ *
+ * @param word The word to shift left.
+ * @param bits The number of bits to shift by.
+ * @return The rotated word.
+ */
+var rol = function(word, bits) {
+  return ((word << bits) & 0xffff) | ((word & 0xffff) >> (16 - bits));
+};
+
+/**
+ * Rotate a word right by given number of bits.
+ *
+ * Bits that are shifted out on the right are put back in on the left
+ * hand side.
+ *
+ * @param word The word to shift right.
+ * @param bits The number of bits to shift by.
+ * @return The rotated word.
+ */
+var ror = function(word, bits) {
+  return ((word & 0xffff) >> bits) | ((word << (16 - bits)) & 0xffff);
+};
+
+/* RC2 API */
+module.exports = forge.rc2 = forge.rc2 || {};
+
+/**
+ * Perform RC2 key expansion as per RFC #2268, section 2.
+ *
+ * @param key variable-length user key (between 1 and 128 bytes)
+ * @param effKeyBits number of effective key bits (default: 128)
+ * @return the expanded RC2 key (ByteBuffer of 128 bytes)
+ */
+forge.rc2.expandKey = function(key, effKeyBits) {
+  if(typeof key === 'string') {
+    key = forge.util.createBuffer(key);
+  }
+  effKeyBits = effKeyBits || 128;
+
+  /* introduce variables that match the names used in RFC #2268 */
+  var L = key;
+  var T = key.length();
+  var T1 = effKeyBits;
+  var T8 = Math.ceil(T1 / 8);
+  var TM = 0xff >> (T1 & 0x07);
+  var i;
+
+  for(i = T; i < 128; i++) {
+    L.putByte(piTable[(L.at(i - 1) + L.at(i - T)) & 0xff]);
+  }
+
+  L.setAt(128 - T8, piTable[L.at(128 - T8) & TM]);
+
+  for(i = 127 - T8; i >= 0; i--) {
+    L.setAt(i, piTable[L.at(i + 1) ^ L.at(i + T8)]);
+  }
+
+  return L;
+};
+
+/**
+ * Creates a RC2 cipher object.
+ *
+ * @param key the symmetric key to use (as base for key generation).
+ * @param bits the number of effective key bits.
+ * @param encrypt false for decryption, true for encryption.
+ *
+ * @return the cipher.
+ */
+var createCipher = function(key, bits, encrypt) {
+  var _finish = false, _input = null, _output = null, _iv = null;
+  var mixRound, mashRound;
+  var i, j, K = [];
+
+  /* Expand key and fill into K[] Array */
+  key = forge.rc2.expandKey(key, bits);
+  for(i = 0; i < 64; i++) {
+    K.push(key.getInt16Le());
+  }
+
+  if(encrypt) {
+    /**
+     * Perform one mixing round "in place".
+     *
+     * @param R Array of four words to perform mixing on.
+     */
+    mixRound = function(R) {
+      for(i = 0; i < 4; i++) {
+        R[i] += K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) +
+          ((~R[(i + 3) % 4]) & R[(i + 1) % 4]);
+        R[i] = rol(R[i], s[i]);
+        j++;
+      }
+    };
+
+    /**
+     * Perform one mashing round "in place".
+     *
+     * @param R Array of four words to perform mashing on.
+     */
+    mashRound = function(R) {
+      for(i = 0; i < 4; i++) {
+        R[i] += K[R[(i + 3) % 4] & 63];
+      }
+    };
+  } else {
+    /**
+     * Perform one r-mixing round "in place".
+     *
+     * @param R Array of four words to perform mixing on.
+     */
+    mixRound = function(R) {
+      for(i = 3; i >= 0; i--) {
+        R[i] = ror(R[i], s[i]);
+        R[i] -= K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) +
+          ((~R[(i + 3) % 4]) & R[(i + 1) % 4]);
+        j--;
+      }
+    };
+
+    /**
+     * Perform one r-mashing round "in place".
+     *
+     * @param R Array of four words to perform mashing on.
+     */
+    mashRound = function(R) {
+      for(i = 3; i >= 0; i--) {
+        R[i] -= K[R[(i + 3) % 4] & 63];
+      }
+    };
+  }
+
+  /**
+   * Run the specified cipher execution plan.
+   *
+   * This function takes four words from the input buffer, applies the IV on
+   * it (if requested) and runs the provided execution plan.
+   *
+   * The plan must be put together in form of a array of arrays.  Where the
+   * outer one is simply a list of steps to perform and the inner one needs
+   * to have two elements: the first one telling how many rounds to perform,
+   * the second one telling what to do (i.e. the function to call).
+   *
+   * @param {Array} plan The plan to execute.
+   */
+  var runPlan = function(plan) {
+    var R = [];
+
+    /* Get data from input buffer and fill the four words into R */
+    for(i = 0; i < 4; i++) {
+      var val = _input.getInt16Le();
+
+      if(_iv !== null) {
+        if(encrypt) {
+          /* We're encrypting, apply the IV first. */
+          val ^= _iv.getInt16Le();
+        } else {
+          /* We're decryption, keep cipher text for next block. */
+          _iv.putInt16Le(val);
+        }
+      }
+
+      R.push(val & 0xffff);
+    }
+
+    /* Reset global "j" variable as per spec. */
+    j = encrypt ? 0 : 63;
+
+    /* Run execution plan. */
+    for(var ptr = 0; ptr < plan.length; ptr++) {
+      for(var ctr = 0; ctr < plan[ptr][0]; ctr++) {
+        plan[ptr][1](R);
+      }
+    }
+
+    /* Write back result to output buffer. */
+    for(i = 0; i < 4; i++) {
+      if(_iv !== null) {
+        if(encrypt) {
+          /* We're encrypting in CBC-mode, feed back encrypted bytes into
+             IV buffer to carry it forward to next block. */
+          _iv.putInt16Le(R[i]);
+        } else {
+          R[i] ^= _iv.getInt16Le();
+        }
+      }
+
+      _output.putInt16Le(R[i]);
+    }
+  };
+
+  /* Create cipher object */
+  var cipher = null;
+  cipher = {
+    /**
+     * Starts or restarts the encryption or decryption process, whichever
+     * was previously configured.
+     *
+     * To use the cipher in CBC mode, iv may be given either as a string
+     * of bytes, or as a byte buffer.  For ECB mode, give null as iv.
+     *
+     * @param iv the initialization vector to use, null for ECB mode.
+     * @param output the output the buffer to write to, null to create one.
+     */
+    start: function(iv, output) {
+      if(iv) {
+        /* CBC mode */
+        if(typeof iv === 'string') {
+          iv = forge.util.createBuffer(iv);
+        }
+      }
+
+      _finish = false;
+      _input = forge.util.createBuffer();
+      _output = output || new forge.util.createBuffer();
+      _iv = iv;
+
+      cipher.output = _output;
+    },
+
+    /**
+     * Updates the next block.
+     *
+     * @param input the buffer to read from.
+     */
+    update: function(input) {
+      if(!_finish) {
+        // not finishing, so fill the input buffer with more input
+        _input.putBuffer(input);
+      }
+
+      while(_input.length() >= 8) {
+        runPlan([
+            [ 5, mixRound ],
+            [ 1, mashRound ],
+            [ 6, mixRound ],
+            [ 1, mashRound ],
+            [ 5, mixRound ]
+          ]);
+      }
+    },
+
+    /**
+     * Finishes encrypting or decrypting.
+     *
+     * @param pad a padding function to use, null for PKCS#7 padding,
+     *           signature(blockSize, buffer, decrypt).
+     *
+     * @return true if successful, false on error.
+     */
+    finish: function(pad) {
+      var rval = true;
+
+      if(encrypt) {
+        if(pad) {
+          rval = pad(8, _input, !encrypt);
+        } else {
+          // add PKCS#7 padding to block (each pad byte is the
+          // value of the number of pad bytes)
+          var padding = (_input.length() === 8) ? 8 : (8 - _input.length());
+          _input.fillWithByte(padding, padding);
+        }
+      }
+
+      if(rval) {
+        // do final update
+        _finish = true;
+        cipher.update();
+      }
+
+      if(!encrypt) {
+        // check for error: input data not a multiple of block size
+        rval = (_input.length() === 0);
+        if(rval) {
+          if(pad) {
+            rval = pad(8, _output, !encrypt);
+          } else {
+            // ensure padding byte count is valid
+            var len = _output.length();
+            var count = _output.at(len - 1);
+
+            if(count > len) {
+              rval = false;
+            } else {
+              // trim off padding bytes
+              _output.truncate(count);
+            }
+          }
+        }
+      }
+
+      return rval;
+    }
+  };
+
+  return cipher;
+};
+
+/**
+ * Creates an RC2 cipher object to encrypt data in ECB or CBC mode using the
+ * given symmetric key. The output will be stored in the 'output' member
+ * of the returned cipher.
+ *
+ * The key and iv may be given as a string of bytes or a byte buffer.
+ * The cipher is initialized to use 128 effective key bits.
+ *
+ * @param key the symmetric key to use.
+ * @param iv the initialization vector to use.
+ * @param output the buffer to write to, null to create one.
+ *
+ * @return the cipher.
+ */
+forge.rc2.startEncrypting = function(key, iv, output) {
+  var cipher = forge.rc2.createEncryptionCipher(key, 128);
+  cipher.start(iv, output);
+  return cipher;
+};
+
+/**
+ * Creates an RC2 cipher object to encrypt data in ECB or CBC mode using the
+ * given symmetric key.
+ *
+ * The key may be given as a string of bytes or a byte buffer.
+ *
+ * To start encrypting call start() on the cipher with an iv and optional
+ * output buffer.
+ *
+ * @param key the symmetric key to use.
+ *
+ * @return the cipher.
+ */
+forge.rc2.createEncryptionCipher = function(key, bits) {
+  return createCipher(key, bits, true);
+};
+
+/**
+ * Creates an RC2 cipher object to decrypt data in ECB or CBC mode using the
+ * given symmetric key. The output will be stored in the 'output' member
+ * of the returned cipher.
+ *
+ * The key and iv may be given as a string of bytes or a byte buffer.
+ * The cipher is initialized to use 128 effective key bits.
+ *
+ * @param key the symmetric key to use.
+ * @param iv the initialization vector to use.
+ * @param output the buffer to write to, null to create one.
+ *
+ * @return the cipher.
+ */
+forge.rc2.startDecrypting = function(key, iv, output) {
+  var cipher = forge.rc2.createDecryptionCipher(key, 128);
+  cipher.start(iv, output);
+  return cipher;
+};
+
+/**
+ * Creates an RC2 cipher object to decrypt data in ECB or CBC mode using the
+ * given symmetric key.
+ *
+ * The key may be given as a string of bytes or a byte buffer.
+ *
+ * To start decrypting call start() on the cipher with an iv and optional
+ * output buffer.
+ *
+ * @param key the symmetric key to use.
+ *
+ * @return the cipher.
+ */
+forge.rc2.createDecryptionCipher = function(key, bits) {
+  return createCipher(key, bits, false);
+};
Index: frontend/node_modules/node-forge/lib/rsa.js
===================================================================
--- frontend/node_modules/node-forge/lib/rsa.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/rsa.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1968 @@
+/**
+ * Javascript implementation of basic RSA algorithms.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2014 Digital Bazaar, Inc.
+ *
+ * The only algorithm currently supported for PKI is RSA.
+ *
+ * An RSA key is often stored in ASN.1 DER format. The SubjectPublicKeyInfo
+ * ASN.1 structure is composed of an algorithm of type AlgorithmIdentifier
+ * and a subjectPublicKey of type bit string.
+ *
+ * The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
+ * for the algorithm, if any. In the case of RSA, there aren't any.
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ *   algorithm AlgorithmIdentifier,
+ *   subjectPublicKey BIT STRING
+ * }
+ *
+ * AlgorithmIdentifer ::= SEQUENCE {
+ *   algorithm OBJECT IDENTIFIER,
+ *   parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ *
+ * For an RSA public key, the subjectPublicKey is:
+ *
+ * RSAPublicKey ::= SEQUENCE {
+ *   modulus            INTEGER,    -- n
+ *   publicExponent     INTEGER     -- e
+ * }
+ *
+ * PrivateKeyInfo ::= SEQUENCE {
+ *   version                   Version,
+ *   privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
+ *   privateKey                PrivateKey,
+ *   attributes           [0]  IMPLICIT Attributes OPTIONAL
+ * }
+ *
+ * Version ::= INTEGER
+ * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+ * PrivateKey ::= OCTET STRING
+ * Attributes ::= SET OF Attribute
+ *
+ * An RSA private key as the following structure:
+ *
+ * RSAPrivateKey ::= SEQUENCE {
+ *   version Version,
+ *   modulus INTEGER, -- n
+ *   publicExponent INTEGER, -- e
+ *   privateExponent INTEGER, -- d
+ *   prime1 INTEGER, -- p
+ *   prime2 INTEGER, -- q
+ *   exponent1 INTEGER, -- d mod (p-1)
+ *   exponent2 INTEGER, -- d mod (q-1)
+ *   coefficient INTEGER -- (inverse of q) mod p
+ * }
+ *
+ * Version ::= INTEGER
+ *
+ * The OID for the RSA key algorithm is: 1.2.840.113549.1.1.1
+ */
+var forge = require('./forge');
+require('./asn1');
+require('./jsbn');
+require('./oids');
+require('./pkcs1');
+require('./prime');
+require('./random');
+require('./util');
+
+if(typeof BigInteger === 'undefined') {
+  var BigInteger = forge.jsbn.BigInteger;
+}
+
+var _crypto = forge.util.isNodejs ? require('crypto') : null;
+
+// shortcut for asn.1 API
+var asn1 = forge.asn1;
+
+// shortcut for util API
+var util = forge.util;
+
+/*
+ * RSA encryption and decryption, see RFC 2313.
+ */
+forge.pki = forge.pki || {};
+module.exports = forge.pki.rsa = forge.rsa = forge.rsa || {};
+var pki = forge.pki;
+
+// for finding primes, which are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29
+var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];
+
+// validator for a PrivateKeyInfo structure
+var privateKeyValidator = {
+  // PrivateKeyInfo
+  name: 'PrivateKeyInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    // Version (INTEGER)
+    name: 'PrivateKeyInfo.version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyVersion'
+  }, {
+    // privateKeyAlgorithm
+    name: 'PrivateKeyInfo.privateKeyAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'AlgorithmIdentifier.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'privateKeyOid'
+    }]
+  }, {
+    // PrivateKey
+    name: 'PrivateKeyInfo',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OCTETSTRING,
+    constructed: false,
+    capture: 'privateKey'
+  }]
+};
+
+// validator for an RSA private key
+var rsaPrivateKeyValidator = {
+  // RSAPrivateKey
+  name: 'RSAPrivateKey',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    // Version (INTEGER)
+    name: 'RSAPrivateKey.version',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyVersion'
+  }, {
+    // modulus (n)
+    name: 'RSAPrivateKey.modulus',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyModulus'
+  }, {
+    // publicExponent (e)
+    name: 'RSAPrivateKey.publicExponent',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyPublicExponent'
+  }, {
+    // privateExponent (d)
+    name: 'RSAPrivateKey.privateExponent',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyPrivateExponent'
+  }, {
+    // prime1 (p)
+    name: 'RSAPrivateKey.prime1',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyPrime1'
+  }, {
+    // prime2 (q)
+    name: 'RSAPrivateKey.prime2',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyPrime2'
+  }, {
+    // exponent1 (d mod (p-1))
+    name: 'RSAPrivateKey.exponent1',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyExponent1'
+  }, {
+    // exponent2 (d mod (q-1))
+    name: 'RSAPrivateKey.exponent2',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyExponent2'
+  }, {
+    // coefficient ((inverse of q) mod p)
+    name: 'RSAPrivateKey.coefficient',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'privateKeyCoefficient'
+  }]
+};
+
+// validator for an RSA public key
+var rsaPublicKeyValidator = {
+  // RSAPublicKey
+  name: 'RSAPublicKey',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    // modulus (n)
+    name: 'RSAPublicKey.modulus',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'publicKeyModulus'
+  }, {
+    // publicExponent (e)
+    name: 'RSAPublicKey.exponent',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'publicKeyExponent'
+  }]
+};
+
+// validator for an SubjectPublicKeyInfo structure
+// Note: Currently only works with an RSA public key
+var publicKeyValidator = forge.pki.rsa.publicKeyValidator = {
+  name: 'SubjectPublicKeyInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  captureAsn1: 'subjectPublicKeyInfo',
+  value: [{
+    name: 'SubjectPublicKeyInfo.AlgorithmIdentifier',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'AlgorithmIdentifier.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'publicKeyOid'
+    }]
+  }, {
+    // subjectPublicKey
+    name: 'SubjectPublicKeyInfo.subjectPublicKey',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.BITSTRING,
+    constructed: false,
+    value: [{
+      // RSAPublicKey
+      name: 'SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      optional: true,
+      captureAsn1: 'rsaPublicKey'
+    }]
+  }]
+};
+
+// validator for a DigestInfo structure
+var digestInfoValidator = {
+  name: 'DigestInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'DigestInfo.DigestAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      name: 'DigestInfo.DigestAlgorithm.algorithmIdentifier',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'algorithmIdentifier'
+    }, {
+      // NULL parameters
+      name: 'DigestInfo.DigestAlgorithm.parameters',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.NULL,
+      // captured only to check existence for md2 and md5
+      capture: 'parameters',
+      optional: true,
+      constructed: false
+    }]
+  }, {
+    // digest
+    name: 'DigestInfo.digest',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.OCTETSTRING,
+    constructed: false,
+    capture: 'digest'
+  }]
+};
+
+/**
+ * Wrap digest in DigestInfo object.
+ *
+ * This function implements EMSA-PKCS1-v1_5-ENCODE as per RFC 3447.
+ *
+ * DigestInfo ::= SEQUENCE {
+ *   digestAlgorithm DigestAlgorithmIdentifier,
+ *   digest Digest
+ * }
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ * Digest ::= OCTET STRING
+ *
+ * @param md the message digest object with the hash to sign.
+ *
+ * @return the encoded message (ready for RSA encryption)
+ */
+var emsaPkcs1v15encode = function(md) {
+  // get the oid for the algorithm
+  var oid;
+  if(md.algorithm in pki.oids) {
+    oid = pki.oids[md.algorithm];
+  } else {
+    var error = new Error('Unknown message digest algorithm.');
+    error.algorithm = md.algorithm;
+    throw error;
+  }
+  var oidBytes = asn1.oidToDer(oid).getBytes();
+
+  // create the digest info
+  var digestInfo = asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+  var digestAlgorithm = asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+  digestAlgorithm.value.push(asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.OID, false, oidBytes));
+  digestAlgorithm.value.push(asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.NULL, false, ''));
+  var digest = asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING,
+    false, md.digest().getBytes());
+  digestInfo.value.push(digestAlgorithm);
+  digestInfo.value.push(digest);
+
+  // encode digest info
+  return asn1.toDer(digestInfo).getBytes();
+};
+
+/**
+ * Performs x^c mod n (RSA encryption or decryption operation).
+ *
+ * @param x the number to raise and mod.
+ * @param key the key to use.
+ * @param pub true if the key is public, false if private.
+ *
+ * @return the result of x^c mod n.
+ */
+var _modPow = function(x, key, pub) {
+  if(pub) {
+    return x.modPow(key.e, key.n);
+  }
+
+  if(!key.p || !key.q) {
+    // allow calculation without CRT params (slow)
+    return x.modPow(key.d, key.n);
+  }
+
+  // pre-compute dP, dQ, and qInv if necessary
+  if(!key.dP) {
+    key.dP = key.d.mod(key.p.subtract(BigInteger.ONE));
+  }
+  if(!key.dQ) {
+    key.dQ = key.d.mod(key.q.subtract(BigInteger.ONE));
+  }
+  if(!key.qInv) {
+    key.qInv = key.q.modInverse(key.p);
+  }
+
+  /* Chinese remainder theorem (CRT) states:
+
+    Suppose n1, n2, ..., nk are positive integers which are pairwise
+    coprime (n1 and n2 have no common factors other than 1). For any
+    integers x1, x2, ..., xk there exists an integer x solving the
+    system of simultaneous congruences (where ~= means modularly
+    congruent so a ~= b mod n means a mod n = b mod n):
+
+    x ~= x1 mod n1
+    x ~= x2 mod n2
+    ...
+    x ~= xk mod nk
+
+    This system of congruences has a single simultaneous solution x
+    between 0 and n - 1. Furthermore, each xk solution and x itself
+    is congruent modulo the product n = n1*n2*...*nk.
+    So x1 mod n = x2 mod n = xk mod n = x mod n.
+
+    The single simultaneous solution x can be solved with the following
+    equation:
+
+    x = sum(xi*ri*si) mod n where ri = n/ni and si = ri^-1 mod ni.
+
+    Where x is less than n, xi = x mod ni.
+
+    For RSA we are only concerned with k = 2. The modulus n = pq, where
+    p and q are coprime. The RSA decryption algorithm is:
+
+    y = x^d mod n
+
+    Given the above:
+
+    x1 = x^d mod p
+    r1 = n/p = q
+    s1 = q^-1 mod p
+    x2 = x^d mod q
+    r2 = n/q = p
+    s2 = p^-1 mod q
+
+    So y = (x1r1s1 + x2r2s2) mod n
+         = ((x^d mod p)q(q^-1 mod p) + (x^d mod q)p(p^-1 mod q)) mod n
+
+    According to Fermat's Little Theorem, if the modulus P is prime,
+    for any integer A not evenly divisible by P, A^(P-1) ~= 1 mod P.
+    Since A is not divisible by P it follows that if:
+    N ~= M mod (P - 1), then A^N mod P = A^M mod P. Therefore:
+
+    A^N mod P = A^(M mod (P - 1)) mod P. (The latter takes less effort
+    to calculate). In order to calculate x^d mod p more quickly the
+    exponent d mod (p - 1) is stored in the RSA private key (the same
+    is done for x^d mod q). These values are referred to as dP and dQ
+    respectively. Therefore we now have:
+
+    y = ((x^dP mod p)q(q^-1 mod p) + (x^dQ mod q)p(p^-1 mod q)) mod n
+
+    Since we'll be reducing x^dP by modulo p (same for q) we can also
+    reduce x by p (and q respectively) before hand. Therefore, let
+
+    xp = ((x mod p)^dP mod p), and
+    xq = ((x mod q)^dQ mod q), yielding:
+
+    y = (xp*q*(q^-1 mod p) + xq*p*(p^-1 mod q)) mod n
+
+    This can be further reduced to a simple algorithm that only
+    requires 1 inverse (the q inverse is used) to be used and stored.
+    The algorithm is called Garner's algorithm. If qInv is the
+    inverse of q, we simply calculate:
+
+    y = (qInv*(xp - xq) mod p) * q + xq
+
+    However, there are two further complications. First, we need to
+    ensure that xp > xq to prevent signed BigIntegers from being used
+    so we add p until this is true (since we will be mod'ing with
+    p anyway). Then, there is a known timing attack on algorithms
+    using the CRT. To mitigate this risk, "cryptographic blinding"
+    should be used. This requires simply generating a random number r
+    between 0 and n-1 and its inverse and multiplying x by r^e before
+    calculating y and then multiplying y by r^-1 afterwards. Note that
+    r must be coprime with n (gcd(r, n) === 1) in order to have an
+    inverse.
+  */
+
+  // cryptographic blinding
+  var r;
+  do {
+    r = new BigInteger(
+      forge.util.bytesToHex(forge.random.getBytes(key.n.bitLength() / 8)),
+      16);
+  } while(r.compareTo(key.n) >= 0 || !r.gcd(key.n).equals(BigInteger.ONE));
+  x = x.multiply(r.modPow(key.e, key.n)).mod(key.n);
+
+  // calculate xp and xq
+  var xp = x.mod(key.p).modPow(key.dP, key.p);
+  var xq = x.mod(key.q).modPow(key.dQ, key.q);
+
+  // xp must be larger than xq to avoid signed bit usage
+  while(xp.compareTo(xq) < 0) {
+    xp = xp.add(key.p);
+  }
+
+  // do last step
+  var y = xp.subtract(xq)
+    .multiply(key.qInv).mod(key.p)
+    .multiply(key.q).add(xq);
+
+  // remove effect of random for cryptographic blinding
+  y = y.multiply(r.modInverse(key.n)).mod(key.n);
+
+  return y;
+};
+
+/**
+ * NOTE: THIS METHOD IS DEPRECATED, use 'sign' on a private key object or
+ * 'encrypt' on a public key object instead.
+ *
+ * Performs RSA encryption.
+ *
+ * The parameter bt controls whether to put padding bytes before the
+ * message passed in. Set bt to either true or false to disable padding
+ * completely (in order to handle e.g. EMSA-PSS encoding separately before),
+ * signaling whether the encryption operation is a public key operation
+ * (i.e. encrypting data) or not, i.e. private key operation (data signing).
+ *
+ * For PKCS#1 v1.5 padding pass in the block type to use, i.e. either 0x01
+ * (for signing) or 0x02 (for encryption). The key operation mode (private
+ * or public) is derived from this flag in that case).
+ *
+ * @param m the message to encrypt as a byte string.
+ * @param key the RSA key to use.
+ * @param bt for PKCS#1 v1.5 padding, the block type to use
+ *   (0x01 for private key, 0x02 for public),
+ *   to disable padding: true = public key, false = private key.
+ *
+ * @return the encrypted bytes as a string.
+ */
+pki.rsa.encrypt = function(m, key, bt) {
+  var pub = bt;
+  var eb;
+
+  // get the length of the modulus in bytes
+  var k = Math.ceil(key.n.bitLength() / 8);
+
+  if(bt !== false && bt !== true) {
+    // legacy, default to PKCS#1 v1.5 padding
+    pub = (bt === 0x02);
+    eb = _encodePkcs1_v1_5(m, key, bt);
+  } else {
+    eb = forge.util.createBuffer();
+    eb.putBytes(m);
+  }
+
+  // load encryption block as big integer 'x'
+  // FIXME: hex conversion inefficient, get BigInteger w/byte strings
+  var x = new BigInteger(eb.toHex(), 16);
+
+  // do RSA encryption
+  var y = _modPow(x, key, pub);
+
+  // convert y into the encrypted data byte string, if y is shorter in
+  // bytes than k, then prepend zero bytes to fill up ed
+  // FIXME: hex conversion inefficient, get BigInteger w/byte strings
+  var yhex = y.toString(16);
+  var ed = forge.util.createBuffer();
+  var zeros = k - Math.ceil(yhex.length / 2);
+  while(zeros > 0) {
+    ed.putByte(0x00);
+    --zeros;
+  }
+  ed.putBytes(forge.util.hexToBytes(yhex));
+  return ed.getBytes();
+};
+
+/**
+ * NOTE: THIS METHOD IS DEPRECATED, use 'decrypt' on a private key object or
+ * 'verify' on a public key object instead.
+ *
+ * Performs RSA decryption.
+ *
+ * The parameter ml controls whether to apply PKCS#1 v1.5 padding
+ * or not.  Set ml = false to disable padding removal completely
+ * (in order to handle e.g. EMSA-PSS later on) and simply pass back
+ * the RSA encryption block.
+ *
+ * @param ed the encrypted data to decrypt in as a byte string.
+ * @param key the RSA key to use.
+ * @param pub true for a public key operation, false for private.
+ * @param ml the message length, if known, false to disable padding.
+ *
+ * @return the decrypted message as a byte string.
+ */
+pki.rsa.decrypt = function(ed, key, pub, ml) {
+  // get the length of the modulus in bytes
+  var k = Math.ceil(key.n.bitLength() / 8);
+
+  // error if the length of the encrypted data ED is not k
+  if(ed.length !== k) {
+    var error = new Error('Encrypted message length is invalid.');
+    error.length = ed.length;
+    error.expected = k;
+    throw error;
+  }
+
+  // convert encrypted data into a big integer
+  // FIXME: hex conversion inefficient, get BigInteger w/byte strings
+  var y = new BigInteger(forge.util.createBuffer(ed).toHex(), 16);
+
+  // y must be less than the modulus or it wasn't the result of
+  // a previous mod operation (encryption) using that modulus
+  if(y.compareTo(key.n) >= 0) {
+    throw new Error('Encrypted message is invalid.');
+  }
+
+  // do RSA decryption
+  var x = _modPow(y, key, pub);
+
+  // create the encryption block, if x is shorter in bytes than k, then
+  // prepend zero bytes to fill up eb
+  // FIXME: hex conversion inefficient, get BigInteger w/byte strings
+  var xhex = x.toString(16);
+  var eb = forge.util.createBuffer();
+  var zeros = k - Math.ceil(xhex.length / 2);
+  while(zeros > 0) {
+    eb.putByte(0x00);
+    --zeros;
+  }
+  eb.putBytes(forge.util.hexToBytes(xhex));
+
+  if(ml !== false) {
+    // legacy, default to PKCS#1 v1.5 padding
+    return _decodePkcs1_v1_5(eb.getBytes(), key, pub);
+  }
+
+  // return message
+  return eb.getBytes();
+};
+
+/**
+ * Creates an RSA key-pair generation state object. It is used to allow
+ * key-generation to be performed in steps. It also allows for a UI to
+ * display progress updates.
+ *
+ * @param bits the size for the private key in bits, defaults to 2048.
+ * @param e the public exponent to use, defaults to 65537 (0x10001).
+ * @param [options] the options to use.
+ *          prng a custom crypto-secure pseudo-random number generator to use,
+ *            that must define "getBytesSync".
+ *          algorithm the algorithm to use (default: 'PRIMEINC').
+ *
+ * @return the state object to use to generate the key-pair.
+ */
+pki.rsa.createKeyPairGenerationState = function(bits, e, options) {
+  // TODO: migrate step-based prime generation code to forge.prime
+
+  // set default bits
+  if(typeof(bits) === 'string') {
+    bits = parseInt(bits, 10);
+  }
+  bits = bits || 2048;
+
+  // create prng with api that matches BigInteger secure random
+  options = options || {};
+  var prng = options.prng || forge.random;
+  var rng = {
+    // x is an array to fill with bytes
+    nextBytes: function(x) {
+      var b = prng.getBytesSync(x.length);
+      for(var i = 0; i < x.length; ++i) {
+        x[i] = b.charCodeAt(i);
+      }
+    }
+  };
+
+  var algorithm = options.algorithm || 'PRIMEINC';
+
+  // create PRIMEINC algorithm state
+  var rval;
+  if(algorithm === 'PRIMEINC') {
+    rval = {
+      algorithm: algorithm,
+      state: 0,
+      bits: bits,
+      rng: rng,
+      eInt: e || 65537,
+      e: new BigInteger(null),
+      p: null,
+      q: null,
+      qBits: bits >> 1,
+      pBits: bits - (bits >> 1),
+      pqState: 0,
+      num: null,
+      keys: null
+    };
+    rval.e.fromInt(rval.eInt);
+  } else {
+    throw new Error('Invalid key generation algorithm: ' + algorithm);
+  }
+
+  return rval;
+};
+
+/**
+ * Attempts to runs the key-generation algorithm for at most n seconds
+ * (approximately) using the given state. When key-generation has completed,
+ * the keys will be stored in state.keys.
+ *
+ * To use this function to update a UI while generating a key or to prevent
+ * causing browser lockups/warnings, set "n" to a value other than 0. A
+ * simple pattern for generating a key and showing a progress indicator is:
+ *
+ * var state = pki.rsa.createKeyPairGenerationState(2048);
+ * var step = function() {
+ *   // step key-generation, run algorithm for 100 ms, repeat
+ *   if(!forge.pki.rsa.stepKeyPairGenerationState(state, 100)) {
+ *     setTimeout(step, 1);
+ *   } else {
+ *     // key-generation complete
+ *     // TODO: turn off progress indicator here
+ *     // TODO: use the generated key-pair in "state.keys"
+ *   }
+ * };
+ * // TODO: turn on progress indicator here
+ * setTimeout(step, 0);
+ *
+ * @param state the state to use.
+ * @param n the maximum number of milliseconds to run the algorithm for, 0
+ *          to run the algorithm to completion.
+ *
+ * @return true if the key-generation completed, false if not.
+ */
+pki.rsa.stepKeyPairGenerationState = function(state, n) {
+  // set default algorithm if not set
+  if(!('algorithm' in state)) {
+    state.algorithm = 'PRIMEINC';
+  }
+
+  // TODO: migrate step-based prime generation code to forge.prime
+  // TODO: abstract as PRIMEINC algorithm
+
+  // do key generation (based on Tom Wu's rsa.js, see jsbn.js license)
+  // with some minor optimizations and designed to run in steps
+
+  // local state vars
+  var THIRTY = new BigInteger(null);
+  THIRTY.fromInt(30);
+  var deltaIdx = 0;
+  var op_or = function(x, y) {return x | y;};
+
+  // keep stepping until time limit is reached or done
+  var t1 = +new Date();
+  var t2;
+  var total = 0;
+  while(state.keys === null && (n <= 0 || total < n)) {
+    // generate p or q
+    if(state.state === 0) {
+      /* Note: All primes are of the form:
+
+        30k+i, for i < 30 and gcd(30, i)=1, where there are 8 values for i
+
+        When we generate a random number, we always align it at 30k + 1. Each
+        time the number is determined not to be prime we add to get to the
+        next 'i', eg: if the number was at 30k + 1 we add 6. */
+      var bits = (state.p === null) ? state.pBits : state.qBits;
+      var bits1 = bits - 1;
+
+      // get a random number
+      if(state.pqState === 0) {
+        state.num = new BigInteger(bits, state.rng);
+        // force MSB set
+        if(!state.num.testBit(bits1)) {
+          state.num.bitwiseTo(
+            BigInteger.ONE.shiftLeft(bits1), op_or, state.num);
+        }
+        // align number on 30k+1 boundary
+        state.num.dAddOffset(31 - state.num.mod(THIRTY).byteValue(), 0);
+        deltaIdx = 0;
+
+        ++state.pqState;
+      } else if(state.pqState === 1) {
+        // try to make the number a prime
+        if(state.num.bitLength() > bits) {
+          // overflow, try again
+          state.pqState = 0;
+          // do primality test
+        } else if(state.num.isProbablePrime(
+          _getMillerRabinTests(state.num.bitLength()))) {
+          ++state.pqState;
+        } else {
+          // get next potential prime
+          state.num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);
+        }
+      } else if(state.pqState === 2) {
+        // ensure number is coprime with e
+        state.pqState =
+          (state.num.subtract(BigInteger.ONE).gcd(state.e)
+            .compareTo(BigInteger.ONE) === 0) ? 3 : 0;
+      } else if(state.pqState === 3) {
+        // store p or q
+        state.pqState = 0;
+        if(state.p === null) {
+          state.p = state.num;
+        } else {
+          state.q = state.num;
+        }
+
+        // advance state if both p and q are ready
+        if(state.p !== null && state.q !== null) {
+          ++state.state;
+        }
+        state.num = null;
+      }
+    } else if(state.state === 1) {
+      // ensure p is larger than q (swap them if not)
+      if(state.p.compareTo(state.q) < 0) {
+        state.num = state.p;
+        state.p = state.q;
+        state.q = state.num;
+      }
+      ++state.state;
+    } else if(state.state === 2) {
+      // compute phi: (p - 1)(q - 1) (Euler's totient function)
+      state.p1 = state.p.subtract(BigInteger.ONE);
+      state.q1 = state.q.subtract(BigInteger.ONE);
+      state.phi = state.p1.multiply(state.q1);
+      ++state.state;
+    } else if(state.state === 3) {
+      // ensure e and phi are coprime
+      if(state.phi.gcd(state.e).compareTo(BigInteger.ONE) === 0) {
+        // phi and e are coprime, advance
+        ++state.state;
+      } else {
+        // phi and e aren't coprime, so generate a new p and q
+        state.p = null;
+        state.q = null;
+        state.state = 0;
+      }
+    } else if(state.state === 4) {
+      // create n, ensure n is has the right number of bits
+      state.n = state.p.multiply(state.q);
+
+      // ensure n is right number of bits
+      if(state.n.bitLength() === state.bits) {
+        // success, advance
+        ++state.state;
+      } else {
+        // failed, get new q
+        state.q = null;
+        state.state = 0;
+      }
+    } else if(state.state === 5) {
+      // set keys
+      var d = state.e.modInverse(state.phi);
+      state.keys = {
+        privateKey: pki.rsa.setPrivateKey(
+          state.n, state.e, d, state.p, state.q,
+          d.mod(state.p1), d.mod(state.q1),
+          state.q.modInverse(state.p)),
+        publicKey: pki.rsa.setPublicKey(state.n, state.e)
+      };
+    }
+
+    // update timing
+    t2 = +new Date();
+    total += t2 - t1;
+    t1 = t2;
+  }
+
+  return state.keys !== null;
+};
+
+/**
+ * Generates an RSA public-private key pair in a single call.
+ *
+ * To generate a key-pair in steps (to allow for progress updates and to
+ * prevent blocking or warnings in slow browsers) then use the key-pair
+ * generation state functions.
+ *
+ * To generate a key-pair asynchronously (either through web-workers, if
+ * available, or by breaking up the work on the main thread), pass a
+ * callback function.
+ *
+ * @param [bits] the size for the private key in bits, defaults to 2048.
+ * @param [e] the public exponent to use, defaults to 65537.
+ * @param [options] options for key-pair generation, if given then 'bits'
+ *            and 'e' must *not* be given:
+ *          bits the size for the private key in bits, (default: 2048).
+ *          e the public exponent to use, (default: 65537 (0x10001)).
+ *          workerScript the worker script URL.
+ *          workers the number of web workers (if supported) to use,
+ *            (default: 2).
+ *          workLoad the size of the work load, ie: number of possible prime
+ *            numbers for each web worker to check per work assignment,
+ *            (default: 100).
+ *          prng a custom crypto-secure pseudo-random number generator to use,
+ *            that must define "getBytesSync". Disables use of native APIs.
+ *          algorithm the algorithm to use (default: 'PRIMEINC').
+ * @param [callback(err, keypair)] called once the operation completes.
+ *
+ * @return an object with privateKey and publicKey properties.
+ */
+pki.rsa.generateKeyPair = function(bits, e, options, callback) {
+  // (bits), (options), (callback)
+  if(arguments.length === 1) {
+    if(typeof bits === 'object') {
+      options = bits;
+      bits = undefined;
+    } else if(typeof bits === 'function') {
+      callback = bits;
+      bits = undefined;
+    }
+  } else if(arguments.length === 2) {
+    // (bits, e), (bits, options), (bits, callback), (options, callback)
+    if(typeof bits === 'number') {
+      if(typeof e === 'function') {
+        callback = e;
+        e = undefined;
+      } else if(typeof e !== 'number') {
+        options = e;
+        e = undefined;
+      }
+    } else {
+      options = bits;
+      callback = e;
+      bits = undefined;
+      e = undefined;
+    }
+  } else if(arguments.length === 3) {
+    // (bits, e, options), (bits, e, callback), (bits, options, callback)
+    if(typeof e === 'number') {
+      if(typeof options === 'function') {
+        callback = options;
+        options = undefined;
+      }
+    } else {
+      callback = options;
+      options = e;
+      e = undefined;
+    }
+  }
+  options = options || {};
+  if(bits === undefined) {
+    bits = options.bits || 2048;
+  }
+  if(e === undefined) {
+    e = options.e || 0x10001;
+  }
+
+  // use native code if permitted, available, and parameters are acceptable
+  if(!forge.options.usePureJavaScript && !options.prng &&
+    bits >= 256 && bits <= 16384 && (e === 0x10001 || e === 3)) {
+    if(callback) {
+      // try native async
+      if(_detectNodeCrypto('generateKeyPair')) {
+        return _crypto.generateKeyPair('rsa', {
+          modulusLength: bits,
+          publicExponent: e,
+          publicKeyEncoding: {
+            type: 'spki',
+            format: 'pem'
+          },
+          privateKeyEncoding: {
+            type: 'pkcs8',
+            format: 'pem'
+          }
+        }, function(err, pub, priv) {
+          if(err) {
+            return callback(err);
+          }
+          callback(null, {
+            privateKey: pki.privateKeyFromPem(priv),
+            publicKey: pki.publicKeyFromPem(pub)
+          });
+        });
+      }
+      if(_detectSubtleCrypto('generateKey') &&
+        _detectSubtleCrypto('exportKey')) {
+        // use standard native generateKey
+        return util.globalScope.crypto.subtle.generateKey({
+          name: 'RSASSA-PKCS1-v1_5',
+          modulusLength: bits,
+          publicExponent: _intToUint8Array(e),
+          hash: {name: 'SHA-256'}
+        }, true /* key can be exported*/, ['sign', 'verify'])
+        .then(function(pair) {
+          return util.globalScope.crypto.subtle.exportKey(
+            'pkcs8', pair.privateKey);
+        // avoiding catch(function(err) {...}) to support IE <= 8
+        }).then(undefined, function(err) {
+          callback(err);
+        }).then(function(pkcs8) {
+          if(pkcs8) {
+            var privateKey = pki.privateKeyFromAsn1(
+              asn1.fromDer(forge.util.createBuffer(pkcs8)));
+            callback(null, {
+              privateKey: privateKey,
+              publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e)
+            });
+          }
+        });
+      }
+      if(_detectSubtleMsCrypto('generateKey') &&
+        _detectSubtleMsCrypto('exportKey')) {
+        var genOp = util.globalScope.msCrypto.subtle.generateKey({
+          name: 'RSASSA-PKCS1-v1_5',
+          modulusLength: bits,
+          publicExponent: _intToUint8Array(e),
+          hash: {name: 'SHA-256'}
+        }, true /* key can be exported*/, ['sign', 'verify']);
+        genOp.oncomplete = function(e) {
+          var pair = e.target.result;
+          var exportOp = util.globalScope.msCrypto.subtle.exportKey(
+            'pkcs8', pair.privateKey);
+          exportOp.oncomplete = function(e) {
+            var pkcs8 = e.target.result;
+            var privateKey = pki.privateKeyFromAsn1(
+              asn1.fromDer(forge.util.createBuffer(pkcs8)));
+            callback(null, {
+              privateKey: privateKey,
+              publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e)
+            });
+          };
+          exportOp.onerror = function(err) {
+            callback(err);
+          };
+        };
+        genOp.onerror = function(err) {
+          callback(err);
+        };
+        return;
+      }
+    } else {
+      // try native sync
+      if(_detectNodeCrypto('generateKeyPairSync')) {
+        var keypair = _crypto.generateKeyPairSync('rsa', {
+          modulusLength: bits,
+          publicExponent: e,
+          publicKeyEncoding: {
+            type: 'spki',
+            format: 'pem'
+          },
+          privateKeyEncoding: {
+            type: 'pkcs8',
+            format: 'pem'
+          }
+        });
+        return {
+          privateKey: pki.privateKeyFromPem(keypair.privateKey),
+          publicKey: pki.publicKeyFromPem(keypair.publicKey)
+        };
+      }
+    }
+  }
+
+  // use JavaScript implementation
+  var state = pki.rsa.createKeyPairGenerationState(bits, e, options);
+  if(!callback) {
+    pki.rsa.stepKeyPairGenerationState(state, 0);
+    return state.keys;
+  }
+  _generateKeyPair(state, options, callback);
+};
+
+/**
+ * Sets an RSA public key from BigIntegers modulus and exponent.
+ *
+ * @param n the modulus.
+ * @param e the exponent.
+ *
+ * @return the public key.
+ */
+pki.setRsaPublicKey = pki.rsa.setPublicKey = function(n, e) {
+  var key = {
+    n: n,
+    e: e
+  };
+
+  /**
+   * Encrypts the given data with this public key. Newer applications
+   * should use the 'RSA-OAEP' decryption scheme, 'RSAES-PKCS1-V1_5' is for
+   * legacy applications.
+   *
+   * @param data the byte string to encrypt.
+   * @param scheme the encryption scheme to use:
+   *          'RSAES-PKCS1-V1_5' (default),
+   *          'RSA-OAEP',
+   *          'RAW', 'NONE', or null to perform raw RSA encryption,
+   *          an object with an 'encode' property set to a function
+   *          with the signature 'function(data, key)' that returns
+   *          a binary-encoded string representing the encoded data.
+   * @param schemeOptions any scheme-specific options.
+   *
+   * @return the encrypted byte string.
+   */
+  key.encrypt = function(data, scheme, schemeOptions) {
+    if(typeof scheme === 'string') {
+      scheme = scheme.toUpperCase();
+    } else if(scheme === undefined) {
+      scheme = 'RSAES-PKCS1-V1_5';
+    }
+
+    if(scheme === 'RSAES-PKCS1-V1_5') {
+      scheme = {
+        encode: function(m, key, pub) {
+          return _encodePkcs1_v1_5(m, key, 0x02).getBytes();
+        }
+      };
+    } else if(scheme === 'RSA-OAEP' || scheme === 'RSAES-OAEP') {
+      scheme = {
+        encode: function(m, key) {
+          return forge.pkcs1.encode_rsa_oaep(key, m, schemeOptions);
+        }
+      };
+    } else if(['RAW', 'NONE', 'NULL', null].indexOf(scheme) !== -1) {
+      scheme = {encode: function(e) {return e;}};
+    } else if(typeof scheme === 'string') {
+      throw new Error('Unsupported encryption scheme: "' + scheme + '".');
+    }
+
+    // do scheme-based encoding then rsa encryption
+    var e = scheme.encode(data, key, true);
+    return pki.rsa.encrypt(e, key, true);
+  };
+
+  /**
+   * Verifies the given signature against the given digest.
+   *
+   * PKCS#1 supports multiple (currently two) signature schemes:
+   * RSASSA-PKCS1-V1_5 and RSASSA-PSS.
+   *
+   * By default this implementation uses the "old scheme", i.e.
+   * RSASSA-PKCS1-V1_5, in which case once RSA-decrypted, the
+   * signature is an OCTET STRING that holds a DigestInfo.
+   *
+   * DigestInfo ::= SEQUENCE {
+   *   digestAlgorithm DigestAlgorithmIdentifier,
+   *   digest Digest
+   * }
+   * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+   * Digest ::= OCTET STRING
+   *
+   * To perform PSS signature verification, provide an instance
+   * of Forge PSS object as the scheme parameter.
+   *
+   * @param digest the message digest hash to compare against the signature,
+   *          as a binary-encoded string.
+   * @param signature the signature to verify, as a binary-encoded string.
+   * @param scheme signature verification scheme to use:
+   *          'RSASSA-PKCS1-V1_5' or undefined for RSASSA PKCS#1 v1.5,
+   *          a Forge PSS object for RSASSA-PSS,
+   *          'NONE' or null for none, DigestInfo will not be expected, but
+   *            PKCS#1 v1.5 padding will still be used.
+   * @param options optional verify options
+   *          _parseAllDigestBytes testing flag to control parsing of all
+   *            digest bytes. Unsupported and not for general usage.
+   *            (default: true)
+   *          _skipPaddingChecks testing flag to skip some padding checks to
+   *            test other checks. Unsupported and not for general usage.
+   *            (default: false)
+   *
+   * @return true if the signature was verified, false if not.
+   */
+  key.verify = function(digest, signature, scheme, options) {
+    if(typeof scheme === 'string') {
+      scheme = scheme.toUpperCase();
+    } else if(scheme === undefined) {
+      scheme = 'RSASSA-PKCS1-V1_5';
+    }
+    if(options === undefined) {
+      options = {
+        _parseAllDigestBytes: true,
+        _skipPaddingChecks: false
+      };
+    }
+    if(!('_parseAllDigestBytes' in options)) {
+      options._parseAllDigestBytes = true;
+    }
+    if(!('_skipPaddingChecks' in options)) {
+      options._skipPaddingChecks = false;
+    }
+
+    if(scheme === 'RSASSA-PKCS1-V1_5') {
+      scheme = {
+        verify: function(digest, d) {
+          // remove padding
+          d = _decodePkcs1_v1_5(d, key, true, undefined, options);
+          // d is ASN.1 BER-encoded DigestInfo
+          var obj = asn1.fromDer(d, {
+            parseAllBytes: options._parseAllDigestBytes
+          });
+
+          // validate DigestInfo structure and element count
+          var capture = {};
+          var errors = [];
+          if(!asn1.validate(obj, digestInfoValidator, capture, errors) ||
+            obj.value.length !== 2) {
+            var error = new Error(
+              'ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 ' +
+              'DigestInfo value.');
+            error.errors = errors;
+            throw error;
+          }
+          // check hash algorithm identifier
+          // see PKCS1-v1-5DigestAlgorithms in RFC 8017
+          // FIXME: add support to validator for strict value choices
+          var oid = asn1.derToOid(capture.algorithmIdentifier);
+          if(!(oid === forge.oids.md2 ||
+            oid === forge.oids.md5 ||
+            oid === forge.oids.sha1 ||
+            oid === forge.oids.sha224 ||
+            oid === forge.oids.sha256 ||
+            oid === forge.oids.sha384 ||
+            oid === forge.oids.sha512 ||
+            oid === forge.oids['sha512-224'] ||
+            oid === forge.oids['sha512-256'])) {
+            var error = new Error(
+              'Unknown RSASSA-PKCS1-v1_5 DigestAlgorithm identifier.');
+            error.oid = oid;
+            throw error;
+          }
+
+          // special check for md2 and md5 that NULL parameters exist
+          if(oid === forge.oids.md2 || oid === forge.oids.md5) {
+            if(!('parameters' in capture)) {
+              throw new Error(
+                'ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 ' +
+                'DigestInfo value. ' +
+                'Missing algorithm identifier NULL parameters.');
+            }
+          }
+
+          // compare the given digest to the decrypted one
+          return digest === capture.digest;
+        }
+      };
+    } else if(scheme === 'NONE' || scheme === 'NULL' || scheme === null) {
+      scheme = {
+        verify: function(digest, d) {
+          // remove padding
+          d = _decodePkcs1_v1_5(d, key, true, undefined, options);
+          return digest === d;
+        }
+      };
+    }
+
+    // do rsa decryption w/o any decoding, then verify -- which does decoding
+    var d = pki.rsa.decrypt(signature, key, true, false);
+    return scheme.verify(digest, d, key.n.bitLength());
+  };
+
+  return key;
+};
+
+/**
+ * Sets an RSA private key from BigIntegers modulus, exponent, primes,
+ * prime exponents, and modular multiplicative inverse.
+ *
+ * @param n the modulus.
+ * @param e the public exponent.
+ * @param d the private exponent ((inverse of e) mod n).
+ * @param p the first prime.
+ * @param q the second prime.
+ * @param dP exponent1 (d mod (p-1)).
+ * @param dQ exponent2 (d mod (q-1)).
+ * @param qInv ((inverse of q) mod p)
+ *
+ * @return the private key.
+ */
+pki.setRsaPrivateKey = pki.rsa.setPrivateKey = function(
+  n, e, d, p, q, dP, dQ, qInv) {
+  var key = {
+    n: n,
+    e: e,
+    d: d,
+    p: p,
+    q: q,
+    dP: dP,
+    dQ: dQ,
+    qInv: qInv
+  };
+
+  /**
+   * Decrypts the given data with this private key. The decryption scheme
+   * must match the one used to encrypt the data.
+   *
+   * @param data the byte string to decrypt.
+   * @param scheme the decryption scheme to use:
+   *          'RSAES-PKCS1-V1_5' (default),
+   *          'RSA-OAEP',
+   *          'RAW', 'NONE', or null to perform raw RSA decryption.
+   * @param schemeOptions any scheme-specific options.
+   *
+   * @return the decrypted byte string.
+   */
+  key.decrypt = function(data, scheme, schemeOptions) {
+    if(typeof scheme === 'string') {
+      scheme = scheme.toUpperCase();
+    } else if(scheme === undefined) {
+      scheme = 'RSAES-PKCS1-V1_5';
+    }
+
+    // do rsa decryption w/o any decoding
+    var d = pki.rsa.decrypt(data, key, false, false);
+
+    if(scheme === 'RSAES-PKCS1-V1_5') {
+      scheme = {decode: _decodePkcs1_v1_5};
+    } else if(scheme === 'RSA-OAEP' || scheme === 'RSAES-OAEP') {
+      scheme = {
+        decode: function(d, key) {
+          return forge.pkcs1.decode_rsa_oaep(key, d, schemeOptions);
+        }
+      };
+    } else if(['RAW', 'NONE', 'NULL', null].indexOf(scheme) !== -1) {
+      scheme = {decode: function(d) {return d;}};
+    } else {
+      throw new Error('Unsupported encryption scheme: "' + scheme + '".');
+    }
+
+    // decode according to scheme
+    return scheme.decode(d, key, false);
+  };
+
+  /**
+   * Signs the given digest, producing a signature.
+   *
+   * PKCS#1 supports multiple (currently two) signature schemes:
+   * RSASSA-PKCS1-V1_5 and RSASSA-PSS.
+   *
+   * By default this implementation uses the "old scheme", i.e.
+   * RSASSA-PKCS1-V1_5. In order to generate a PSS signature, provide
+   * an instance of Forge PSS object as the scheme parameter.
+   *
+   * @param md the message digest object with the hash to sign.
+   * @param scheme the signature scheme to use:
+   *          'RSASSA-PKCS1-V1_5' or undefined for RSASSA PKCS#1 v1.5,
+   *          a Forge PSS object for RSASSA-PSS,
+   *          'NONE' or null for none, DigestInfo will not be used but
+   *            PKCS#1 v1.5 padding will still be used.
+   *
+   * @return the signature as a byte string.
+   */
+  key.sign = function(md, scheme) {
+    /* Note: The internal implementation of RSA operations is being
+      transitioned away from a PKCS#1 v1.5 hard-coded scheme. Some legacy
+      code like the use of an encoding block identifier 'bt' will eventually
+      be removed. */
+
+    // private key operation
+    var bt = false;
+
+    if(typeof scheme === 'string') {
+      scheme = scheme.toUpperCase();
+    }
+
+    if(scheme === undefined || scheme === 'RSASSA-PKCS1-V1_5') {
+      scheme = {encode: emsaPkcs1v15encode};
+      bt = 0x01;
+    } else if(scheme === 'NONE' || scheme === 'NULL' || scheme === null) {
+      scheme = {encode: function() {return md;}};
+      bt = 0x01;
+    }
+
+    // encode and then encrypt
+    var d = scheme.encode(md, key.n.bitLength());
+    return pki.rsa.encrypt(d, key, bt);
+  };
+
+  return key;
+};
+
+/**
+ * Wraps an RSAPrivateKey ASN.1 object in an ASN.1 PrivateKeyInfo object.
+ *
+ * @param rsaKey the ASN.1 RSAPrivateKey.
+ *
+ * @return the ASN.1 PrivateKeyInfo.
+ */
+pki.wrapRsaPrivateKey = function(rsaKey) {
+  // PrivateKeyInfo
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // version (0)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      asn1.integerToDer(0).getBytes()),
+    // privateKeyAlgorithm
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      asn1.create(
+        asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(pki.oids.rsaEncryption).getBytes()),
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+    ]),
+    // PrivateKey
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
+      asn1.toDer(rsaKey).getBytes())
+  ]);
+};
+
+/**
+ * Converts a private key from an ASN.1 object.
+ *
+ * @param obj the ASN.1 representation of a PrivateKeyInfo containing an
+ *          RSAPrivateKey or an RSAPrivateKey.
+ *
+ * @return the private key.
+ */
+pki.privateKeyFromAsn1 = function(obj) {
+  // get PrivateKeyInfo
+  var capture = {};
+  var errors = [];
+  if(asn1.validate(obj, privateKeyValidator, capture, errors)) {
+    obj = asn1.fromDer(forge.util.createBuffer(capture.privateKey));
+  }
+
+  // get RSAPrivateKey
+  capture = {};
+  errors = [];
+  if(!asn1.validate(obj, rsaPrivateKeyValidator, capture, errors)) {
+    var error = new Error('Cannot read private key. ' +
+      'ASN.1 object does not contain an RSAPrivateKey.');
+    error.errors = errors;
+    throw error;
+  }
+
+  // Note: Version is currently ignored.
+  // capture.privateKeyVersion
+  // FIXME: inefficient, get a BigInteger that uses byte strings
+  var n, e, d, p, q, dP, dQ, qInv;
+  n = forge.util.createBuffer(capture.privateKeyModulus).toHex();
+  e = forge.util.createBuffer(capture.privateKeyPublicExponent).toHex();
+  d = forge.util.createBuffer(capture.privateKeyPrivateExponent).toHex();
+  p = forge.util.createBuffer(capture.privateKeyPrime1).toHex();
+  q = forge.util.createBuffer(capture.privateKeyPrime2).toHex();
+  dP = forge.util.createBuffer(capture.privateKeyExponent1).toHex();
+  dQ = forge.util.createBuffer(capture.privateKeyExponent2).toHex();
+  qInv = forge.util.createBuffer(capture.privateKeyCoefficient).toHex();
+
+  // set private key
+  return pki.setRsaPrivateKey(
+    new BigInteger(n, 16),
+    new BigInteger(e, 16),
+    new BigInteger(d, 16),
+    new BigInteger(p, 16),
+    new BigInteger(q, 16),
+    new BigInteger(dP, 16),
+    new BigInteger(dQ, 16),
+    new BigInteger(qInv, 16));
+};
+
+/**
+ * Converts a private key to an ASN.1 RSAPrivateKey.
+ *
+ * @param key the private key.
+ *
+ * @return the ASN.1 representation of an RSAPrivateKey.
+ */
+pki.privateKeyToAsn1 = pki.privateKeyToRSAPrivateKey = function(key) {
+  // RSAPrivateKey
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // version (0 = only 2 primes, 1 multiple primes)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      asn1.integerToDer(0).getBytes()),
+    // modulus (n)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.n)),
+    // publicExponent (e)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.e)),
+    // privateExponent (d)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.d)),
+    // privateKeyPrime1 (p)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.p)),
+    // privateKeyPrime2 (q)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.q)),
+    // privateKeyExponent1 (dP)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.dP)),
+    // privateKeyExponent2 (dQ)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.dQ)),
+    // coefficient (qInv)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.qInv))
+  ]);
+};
+
+/**
+ * Converts a public key from an ASN.1 SubjectPublicKeyInfo or RSAPublicKey.
+ *
+ * @param obj the asn1 representation of a SubjectPublicKeyInfo or RSAPublicKey.
+ *
+ * @return the public key.
+ */
+pki.publicKeyFromAsn1 = function(obj) {
+  // get SubjectPublicKeyInfo
+  var capture = {};
+  var errors = [];
+  if(asn1.validate(obj, publicKeyValidator, capture, errors)) {
+    // get oid
+    var oid = asn1.derToOid(capture.publicKeyOid);
+    if(oid !== pki.oids.rsaEncryption) {
+      var error = new Error('Cannot read public key. Unknown OID.');
+      error.oid = oid;
+      throw error;
+    }
+    obj = capture.rsaPublicKey;
+  }
+
+  // get RSA params
+  errors = [];
+  if(!asn1.validate(obj, rsaPublicKeyValidator, capture, errors)) {
+    var error = new Error('Cannot read public key. ' +
+      'ASN.1 object does not contain an RSAPublicKey.');
+    error.errors = errors;
+    throw error;
+  }
+
+  // FIXME: inefficient, get a BigInteger that uses byte strings
+  var n = forge.util.createBuffer(capture.publicKeyModulus).toHex();
+  var e = forge.util.createBuffer(capture.publicKeyExponent).toHex();
+
+  // set public key
+  return pki.setRsaPublicKey(
+    new BigInteger(n, 16),
+    new BigInteger(e, 16));
+};
+
+/**
+ * Converts a public key to an ASN.1 SubjectPublicKeyInfo.
+ *
+ * @param key the public key.
+ *
+ * @return the asn1 representation of a SubjectPublicKeyInfo.
+ */
+pki.publicKeyToAsn1 = pki.publicKeyToSubjectPublicKeyInfo = function(key) {
+  // SubjectPublicKeyInfo
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // AlgorithmIdentifier
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // algorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(pki.oids.rsaEncryption).getBytes()),
+      // parameters (null)
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+    ]),
+    // subjectPublicKey
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, [
+      pki.publicKeyToRSAPublicKey(key)
+    ])
+  ]);
+};
+
+/**
+ * Converts a public key to an ASN.1 RSAPublicKey.
+ *
+ * @param key the public key.
+ *
+ * @return the asn1 representation of a RSAPublicKey.
+ */
+pki.publicKeyToRSAPublicKey = function(key) {
+  // RSAPublicKey
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // modulus (n)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.n)),
+    // publicExponent (e)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      _bnToBytes(key.e))
+  ]);
+};
+
+/**
+ * Encodes a message using PKCS#1 v1.5 padding.
+ *
+ * @param m the message to encode.
+ * @param key the RSA key to use.
+ * @param bt the block type to use, i.e. either 0x01 (for signing) or 0x02
+ *          (for encryption).
+ *
+ * @return the padded byte buffer.
+ */
+function _encodePkcs1_v1_5(m, key, bt) {
+  var eb = forge.util.createBuffer();
+
+  // get the length of the modulus in bytes
+  var k = Math.ceil(key.n.bitLength() / 8);
+
+  /* use PKCS#1 v1.5 padding */
+  if(m.length > (k - 11)) {
+    var error = new Error('Message is too long for PKCS#1 v1.5 padding.');
+    error.length = m.length;
+    error.max = k - 11;
+    throw error;
+  }
+
+  /* A block type BT, a padding string PS, and the data D shall be
+    formatted into an octet string EB, the encryption block:
+
+    EB = 00 || BT || PS || 00 || D
+
+    The block type BT shall be a single octet indicating the structure of
+    the encryption block. For this version of the document it shall have
+    value 00, 01, or 02. For a private-key operation, the block type
+    shall be 00 or 01. For a public-key operation, it shall be 02.
+
+    The padding string PS shall consist of k-3-||D|| octets. For block
+    type 00, the octets shall have value 00; for block type 01, they
+    shall have value FF; and for block type 02, they shall be
+    pseudorandomly generated and nonzero. This makes the length of the
+    encryption block EB equal to k. */
+
+  // build the encryption block
+  eb.putByte(0x00);
+  eb.putByte(bt);
+
+  // create the padding
+  var padNum = k - 3 - m.length;
+  var padByte;
+  // private key op
+  if(bt === 0x00 || bt === 0x01) {
+    padByte = (bt === 0x00) ? 0x00 : 0xFF;
+    for(var i = 0; i < padNum; ++i) {
+      eb.putByte(padByte);
+    }
+  } else {
+    // public key op
+    // pad with random non-zero values
+    while(padNum > 0) {
+      var numZeros = 0;
+      var padBytes = forge.random.getBytes(padNum);
+      for(var i = 0; i < padNum; ++i) {
+        padByte = padBytes.charCodeAt(i);
+        if(padByte === 0) {
+          ++numZeros;
+        } else {
+          eb.putByte(padByte);
+        }
+      }
+      padNum = numZeros;
+    }
+  }
+
+  // zero followed by message
+  eb.putByte(0x00);
+  eb.putBytes(m);
+
+  return eb;
+}
+
+/**
+ * Decodes a message using PKCS#1 v1.5 padding.
+ *
+ * @param em the message to decode.
+ * @param key the RSA key to use.
+ * @param pub true if the key is a public key, false if it is private.
+ * @param ml the message length, if specified.
+ * @param options testing options.
+ *
+ * @return the decoded bytes.
+ */
+function _decodePkcs1_v1_5(em, key, pub, ml, options) {
+  // get the length of the modulus in bytes
+  var k = Math.ceil(key.n.bitLength() / 8);
+
+  /* It is an error if any of the following conditions occurs:
+
+    1. The encryption block EB cannot be parsed unambiguously.
+    2. The padding string PS consists of fewer than eight octets
+      or is inconsistent with the block type BT.
+    3. The decryption process is a public-key operation and the block
+      type BT is not 00 or 01, or the decryption process is a
+      private-key operation and the block type is not 02.
+   */
+
+  // parse the encryption block
+  var eb = forge.util.createBuffer(em);
+  var first = eb.getByte();
+  var bt = eb.getByte();
+  if(first !== 0x00 ||
+    (pub && bt !== 0x00 && bt !== 0x01) ||
+    (!pub && bt !== 0x02) ||
+    (pub && bt === 0x00 && typeof(ml) === 'undefined')) {
+    throw new Error('Encryption block is invalid.');
+  }
+
+  var padNum = 0;
+  if(bt === 0x00) {
+    // check all padding bytes for 0x00
+    padNum = k - 3 - ml;
+    for(var i = 0; i < padNum; ++i) {
+      if(eb.getByte() !== 0x00) {
+        throw new Error('Encryption block is invalid.');
+      }
+    }
+  } else if(bt === 0x01) {
+    // find the first byte that isn't 0xFF, should be after all padding
+    padNum = 0;
+    while(eb.length() > 1) {
+      if(eb.getByte() !== 0xFF) {
+        --eb.read;
+        break;
+      }
+      ++padNum;
+    }
+
+    // RFC 2313 8.1 note 6
+    if(padNum < 8 && !(options ? options._skipPaddingChecks : false)) {
+      throw new Error('Encryption block is invalid.');
+    }
+  } else if(bt === 0x02) {
+    // look for 0x00 byte
+    padNum = 0;
+    while(eb.length() > 1) {
+      if(eb.getByte() === 0x00) {
+        --eb.read;
+        break;
+      }
+      ++padNum;
+    }
+
+    // RFC 2313 8.1 note 6
+    if(padNum < 8 && !(options ? options._skipPaddingChecks : false)) {
+      throw new Error('Encryption block is invalid.');
+    }
+  }
+
+  // zero must be 0x00 and padNum must be (k - 3 - message length)
+  var zero = eb.getByte();
+  if(zero !== 0x00 || padNum !== (k - 3 - eb.length())) {
+    throw new Error('Encryption block is invalid.');
+  }
+
+  return eb.getBytes();
+}
+
+/**
+ * Runs the key-generation algorithm asynchronously, either in the background
+ * via Web Workers, or using the main thread and setImmediate.
+ *
+ * @param state the key-pair generation state.
+ * @param [options] options for key-pair generation:
+ *          workerScript the worker script URL.
+ *          workers the number of web workers (if supported) to use,
+ *            (default: 2, -1 to use estimated cores minus one).
+ *          workLoad the size of the work load, ie: number of possible prime
+ *            numbers for each web worker to check per work assignment,
+ *            (default: 100).
+ * @param callback(err, keypair) called once the operation completes.
+ */
+function _generateKeyPair(state, options, callback) {
+  if(typeof options === 'function') {
+    callback = options;
+    options = {};
+  }
+  options = options || {};
+
+  var opts = {
+    algorithm: {
+      name: options.algorithm || 'PRIMEINC',
+      options: {
+        workers: options.workers || 2,
+        workLoad: options.workLoad || 100,
+        workerScript: options.workerScript
+      }
+    }
+  };
+  if('prng' in options) {
+    opts.prng = options.prng;
+  }
+
+  generate();
+
+  function generate() {
+    // find p and then q (done in series to simplify)
+    getPrime(state.pBits, function(err, num) {
+      if(err) {
+        return callback(err);
+      }
+      state.p = num;
+      if(state.q !== null) {
+        return finish(err, state.q);
+      }
+      getPrime(state.qBits, finish);
+    });
+  }
+
+  function getPrime(bits, callback) {
+    forge.prime.generateProbablePrime(bits, opts, callback);
+  }
+
+  function finish(err, num) {
+    if(err) {
+      return callback(err);
+    }
+
+    // set q
+    state.q = num;
+
+    // ensure p is larger than q (swap them if not)
+    if(state.p.compareTo(state.q) < 0) {
+      var tmp = state.p;
+      state.p = state.q;
+      state.q = tmp;
+    }
+
+    // ensure p is coprime with e
+    if(state.p.subtract(BigInteger.ONE).gcd(state.e)
+      .compareTo(BigInteger.ONE) !== 0) {
+      state.p = null;
+      generate();
+      return;
+    }
+
+    // ensure q is coprime with e
+    if(state.q.subtract(BigInteger.ONE).gcd(state.e)
+      .compareTo(BigInteger.ONE) !== 0) {
+      state.q = null;
+      getPrime(state.qBits, finish);
+      return;
+    }
+
+    // compute phi: (p - 1)(q - 1) (Euler's totient function)
+    state.p1 = state.p.subtract(BigInteger.ONE);
+    state.q1 = state.q.subtract(BigInteger.ONE);
+    state.phi = state.p1.multiply(state.q1);
+
+    // ensure e and phi are coprime
+    if(state.phi.gcd(state.e).compareTo(BigInteger.ONE) !== 0) {
+      // phi and e aren't coprime, so generate a new p and q
+      state.p = state.q = null;
+      generate();
+      return;
+    }
+
+    // create n, ensure n is has the right number of bits
+    state.n = state.p.multiply(state.q);
+    if(state.n.bitLength() !== state.bits) {
+      // failed, get new q
+      state.q = null;
+      getPrime(state.qBits, finish);
+      return;
+    }
+
+    // set keys
+    var d = state.e.modInverse(state.phi);
+    state.keys = {
+      privateKey: pki.rsa.setPrivateKey(
+        state.n, state.e, d, state.p, state.q,
+        d.mod(state.p1), d.mod(state.q1),
+        state.q.modInverse(state.p)),
+      publicKey: pki.rsa.setPublicKey(state.n, state.e)
+    };
+
+    callback(null, state.keys);
+  }
+}
+
+/**
+ * Converts a positive BigInteger into 2's-complement big-endian bytes.
+ *
+ * @param b the big integer to convert.
+ *
+ * @return the bytes.
+ */
+function _bnToBytes(b) {
+  // prepend 0x00 if first byte >= 0x80
+  var hex = b.toString(16);
+  if(hex[0] >= '8') {
+    hex = '00' + hex;
+  }
+  var bytes = forge.util.hexToBytes(hex);
+
+  // ensure integer is minimally-encoded
+  if(bytes.length > 1 &&
+    // leading 0x00 for positive integer
+    ((bytes.charCodeAt(0) === 0 &&
+    (bytes.charCodeAt(1) & 0x80) === 0) ||
+    // leading 0xFF for negative integer
+    (bytes.charCodeAt(0) === 0xFF &&
+    (bytes.charCodeAt(1) & 0x80) === 0x80))) {
+    return bytes.substr(1);
+  }
+  return bytes;
+}
+
+/**
+ * Returns the required number of Miller-Rabin tests to generate a
+ * prime with an error probability of (1/2)^80.
+ *
+ * See Handbook of Applied Cryptography Chapter 4, Table 4.4.
+ *
+ * @param bits the bit size.
+ *
+ * @return the required number of iterations.
+ */
+function _getMillerRabinTests(bits) {
+  if(bits <= 100) return 27;
+  if(bits <= 150) return 18;
+  if(bits <= 200) return 15;
+  if(bits <= 250) return 12;
+  if(bits <= 300) return 9;
+  if(bits <= 350) return 8;
+  if(bits <= 400) return 7;
+  if(bits <= 500) return 6;
+  if(bits <= 600) return 5;
+  if(bits <= 800) return 4;
+  if(bits <= 1250) return 3;
+  return 2;
+}
+
+/**
+ * Performs feature detection on the Node crypto interface.
+ *
+ * @param fn the feature (function) to detect.
+ *
+ * @return true if detected, false if not.
+ */
+function _detectNodeCrypto(fn) {
+  return forge.util.isNodejs && typeof _crypto[fn] === 'function';
+}
+
+/**
+ * Performs feature detection on the SubtleCrypto interface.
+ *
+ * @param fn the feature (function) to detect.
+ *
+ * @return true if detected, false if not.
+ */
+function _detectSubtleCrypto(fn) {
+  return (typeof util.globalScope !== 'undefined' &&
+    typeof util.globalScope.crypto === 'object' &&
+    typeof util.globalScope.crypto.subtle === 'object' &&
+    typeof util.globalScope.crypto.subtle[fn] === 'function');
+}
+
+/**
+ * Performs feature detection on the deprecated Microsoft Internet Explorer
+ * outdated SubtleCrypto interface. This function should only be used after
+ * checking for the modern, standard SubtleCrypto interface.
+ *
+ * @param fn the feature (function) to detect.
+ *
+ * @return true if detected, false if not.
+ */
+function _detectSubtleMsCrypto(fn) {
+  return (typeof util.globalScope !== 'undefined' &&
+    typeof util.globalScope.msCrypto === 'object' &&
+    typeof util.globalScope.msCrypto.subtle === 'object' &&
+    typeof util.globalScope.msCrypto.subtle[fn] === 'function');
+}
+
+function _intToUint8Array(x) {
+  var bytes = forge.util.hexToBytes(x.toString(16));
+  var buffer = new Uint8Array(bytes.length);
+  for(var i = 0; i < bytes.length; ++i) {
+    buffer[i] = bytes.charCodeAt(i);
+  }
+  return buffer;
+}
+
+function _privateKeyFromJwk(jwk) {
+  if(jwk.kty !== 'RSA') {
+    throw new Error(
+      'Unsupported key algorithm "' + jwk.kty + '"; algorithm must be "RSA".');
+  }
+  return pki.setRsaPrivateKey(
+    _base64ToBigInt(jwk.n),
+    _base64ToBigInt(jwk.e),
+    _base64ToBigInt(jwk.d),
+    _base64ToBigInt(jwk.p),
+    _base64ToBigInt(jwk.q),
+    _base64ToBigInt(jwk.dp),
+    _base64ToBigInt(jwk.dq),
+    _base64ToBigInt(jwk.qi));
+}
+
+function _publicKeyFromJwk(jwk) {
+  if(jwk.kty !== 'RSA') {
+    throw new Error('Key algorithm must be "RSA".');
+  }
+  return pki.setRsaPublicKey(
+    _base64ToBigInt(jwk.n),
+    _base64ToBigInt(jwk.e));
+}
+
+function _base64ToBigInt(b64) {
+  return new BigInteger(forge.util.bytesToHex(forge.util.decode64(b64)), 16);
+}
Index: frontend/node_modules/node-forge/lib/sha1.js
===================================================================
--- frontend/node_modules/node-forge/lib/sha1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/sha1.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,319 @@
+/**
+ * Secure Hash Algorithm with 160-bit digest (SHA-1) implementation.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2015 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./md');
+require('./util');
+
+var sha1 = module.exports = forge.sha1 = forge.sha1 || {};
+forge.md.sha1 = forge.md.algorithms.sha1 = sha1;
+
+/**
+ * Creates a SHA-1 message digest object.
+ *
+ * @return a message digest object.
+ */
+sha1.create = function() {
+  // do initialization as necessary
+  if(!_initialized) {
+    _init();
+  }
+
+  // SHA-1 state contains five 32-bit integers
+  var _state = null;
+
+  // input buffer
+  var _input = forge.util.createBuffer();
+
+  // used for word storage
+  var _w = new Array(80);
+
+  // message digest object
+  var md = {
+    algorithm: 'sha1',
+    blockLength: 64,
+    digestLength: 20,
+    // 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,
+      h4: 0xC3D2E1F0
+    };
+    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 SHA-1 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 big-endian order; since length
+    // is stored in bytes we multiply by 8 and add carry from next int
+    var next, carry;
+    var bits = md.fullMessageLength[0] * 8;
+    for(var i = 0; i < md.fullMessageLength.length - 1; ++i) {
+      next = md.fullMessageLength[i + 1] * 8;
+      carry = (next / 0x100000000) >>> 0;
+      bits += carry;
+      finalBlock.putInt32(bits >>> 0);
+      bits = next >>> 0;
+    }
+    finalBlock.putInt32(bits);
+
+    var s2 = {
+      h0: _state.h0,
+      h1: _state.h1,
+      h2: _state.h2,
+      h3: _state.h3,
+      h4: _state.h4
+    };
+    _update(s2, _w, finalBlock);
+    var rval = forge.util.createBuffer();
+    rval.putInt32(s2.h0);
+    rval.putInt32(s2.h1);
+    rval.putInt32(s2.h2);
+    rval.putInt32(s2.h3);
+    rval.putInt32(s2.h4);
+    return rval;
+  };
+
+  return md;
+};
+
+// sha-1 padding bytes not initialized yet
+var _padding = null;
+var _initialized = false;
+
+/**
+ * Initializes the constant tables.
+ */
+function _init() {
+  // create padding
+  _padding = String.fromCharCode(128);
+  _padding += forge.util.fillString(String.fromCharCode(0x00), 64);
+
+  // now initialized
+  _initialized = true;
+}
+
+/**
+ * Updates a SHA-1 state with the given byte buffer.
+ *
+ * @param s the SHA-1 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, e, f, i;
+  var len = bytes.length();
+  while(len >= 64) {
+    // the w array will be populated with sixteen 32-bit big-endian words
+    // and then extended into 80 32-bit words according to SHA-1 algorithm
+    // and for 32-79 using Max Locktyukhin's optimization
+
+    // initialize hash value for this chunk
+    a = s.h0;
+    b = s.h1;
+    c = s.h2;
+    d = s.h3;
+    e = s.h4;
+
+    // round 1
+    for(i = 0; i < 16; ++i) {
+      t = bytes.getInt32();
+      w[i] = t;
+      f = d ^ (b & (c ^ d));
+      t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t;
+      e = d;
+      d = c;
+      // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
+      c = ((b << 30) | (b >>> 2)) >>> 0;
+      b = a;
+      a = t;
+    }
+    for(; i < 20; ++i) {
+      t = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]);
+      t = (t << 1) | (t >>> 31);
+      w[i] = t;
+      f = d ^ (b & (c ^ d));
+      t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t;
+      e = d;
+      d = c;
+      // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
+      c = ((b << 30) | (b >>> 2)) >>> 0;
+      b = a;
+      a = t;
+    }
+    // round 2
+    for(; i < 32; ++i) {
+      t = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]);
+      t = (t << 1) | (t >>> 31);
+      w[i] = t;
+      f = b ^ c ^ d;
+      t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t;
+      e = d;
+      d = c;
+      // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
+      c = ((b << 30) | (b >>> 2)) >>> 0;
+      b = a;
+      a = t;
+    }
+    for(; i < 40; ++i) {
+      t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
+      t = (t << 2) | (t >>> 30);
+      w[i] = t;
+      f = b ^ c ^ d;
+      t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t;
+      e = d;
+      d = c;
+      // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
+      c = ((b << 30) | (b >>> 2)) >>> 0;
+      b = a;
+      a = t;
+    }
+    // round 3
+    for(; i < 60; ++i) {
+      t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
+      t = (t << 2) | (t >>> 30);
+      w[i] = t;
+      f = (b & c) | (d & (b ^ c));
+      t = ((a << 5) | (a >>> 27)) + f + e + 0x8F1BBCDC + t;
+      e = d;
+      d = c;
+      // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
+      c = ((b << 30) | (b >>> 2)) >>> 0;
+      b = a;
+      a = t;
+    }
+    // round 4
+    for(; i < 80; ++i) {
+      t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
+      t = (t << 2) | (t >>> 30);
+      w[i] = t;
+      f = b ^ c ^ d;
+      t = ((a << 5) | (a >>> 27)) + f + e + 0xCA62C1D6 + t;
+      e = d;
+      d = c;
+      // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
+      c = ((b << 30) | (b >>> 2)) >>> 0;
+      b = a;
+      a = t;
+    }
+
+    // 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;
+    s.h4 = (s.h4 + e) | 0;
+
+    len -= 64;
+  }
+}
Index: frontend/node_modules/node-forge/lib/sha256.js
===================================================================
--- frontend/node_modules/node-forge/lib/sha256.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/sha256.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,327 @@
+/**
+ * Secure Hash Algorithm with 256-bit digest (SHA-256) implementation.
+ *
+ * See FIPS 180-2 for details.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2015 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./md');
+require('./util');
+
+var sha256 = module.exports = forge.sha256 = forge.sha256 || {};
+forge.md.sha256 = forge.md.algorithms.sha256 = sha256;
+
+/**
+ * Creates a SHA-256 message digest object.
+ *
+ * @return a message digest object.
+ */
+sha256.create = function() {
+  // do initialization as necessary
+  if(!_initialized) {
+    _init();
+  }
+
+  // SHA-256 state contains eight 32-bit integers
+  var _state = null;
+
+  // input buffer
+  var _input = forge.util.createBuffer();
+
+  // used for word storage
+  var _w = new Array(64);
+
+  // message digest object
+  var md = {
+    algorithm: 'sha256',
+    blockLength: 64,
+    digestLength: 32,
+    // 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: 0x6A09E667,
+      h1: 0xBB67AE85,
+      h2: 0x3C6EF372,
+      h3: 0xA54FF53A,
+      h4: 0x510E527F,
+      h5: 0x9B05688C,
+      h6: 0x1F83D9AB,
+      h7: 0x5BE0CD19
+    };
+    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 SHA-256 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 big-endian order; since length
+    // is stored in bytes we multiply by 8 and add carry from next int
+    var next, carry;
+    var bits = md.fullMessageLength[0] * 8;
+    for(var i = 0; i < md.fullMessageLength.length - 1; ++i) {
+      next = md.fullMessageLength[i + 1] * 8;
+      carry = (next / 0x100000000) >>> 0;
+      bits += carry;
+      finalBlock.putInt32(bits >>> 0);
+      bits = next >>> 0;
+    }
+    finalBlock.putInt32(bits);
+
+    var s2 = {
+      h0: _state.h0,
+      h1: _state.h1,
+      h2: _state.h2,
+      h3: _state.h3,
+      h4: _state.h4,
+      h5: _state.h5,
+      h6: _state.h6,
+      h7: _state.h7
+    };
+    _update(s2, _w, finalBlock);
+    var rval = forge.util.createBuffer();
+    rval.putInt32(s2.h0);
+    rval.putInt32(s2.h1);
+    rval.putInt32(s2.h2);
+    rval.putInt32(s2.h3);
+    rval.putInt32(s2.h4);
+    rval.putInt32(s2.h5);
+    rval.putInt32(s2.h6);
+    rval.putInt32(s2.h7);
+    return rval;
+  };
+
+  return md;
+};
+
+// sha-256 padding bytes not initialized yet
+var _padding = null;
+var _initialized = false;
+
+// table of constants
+var _k = null;
+
+/**
+ * Initializes the constant tables.
+ */
+function _init() {
+  // create padding
+  _padding = String.fromCharCode(128);
+  _padding += forge.util.fillString(String.fromCharCode(0x00), 64);
+
+  // create K table for SHA-256
+  _k = [
+    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
+
+  // now initialized
+  _initialized = true;
+}
+
+/**
+ * Updates a SHA-256 state with the given byte buffer.
+ *
+ * @param s the SHA-256 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 t1, t2, s0, s1, ch, maj, i, a, b, c, d, e, f, g, h;
+  var len = bytes.length();
+  while(len >= 64) {
+    // the w array will be populated with sixteen 32-bit big-endian words
+    // and then extended into 64 32-bit words according to SHA-256
+    for(i = 0; i < 16; ++i) {
+      w[i] = bytes.getInt32();
+    }
+    for(; i < 64; ++i) {
+      // XOR word 2 words ago rot right 17, rot right 19, shft right 10
+      t1 = w[i - 2];
+      t1 =
+        ((t1 >>> 17) | (t1 << 15)) ^
+        ((t1 >>> 19) | (t1 << 13)) ^
+        (t1 >>> 10);
+      // XOR word 15 words ago rot right 7, rot right 18, shft right 3
+      t2 = w[i - 15];
+      t2 =
+        ((t2 >>> 7) | (t2 << 25)) ^
+        ((t2 >>> 18) | (t2 << 14)) ^
+        (t2 >>> 3);
+      // sum(t1, word 7 ago, t2, word 16 ago) modulo 2^32
+      w[i] = (t1 + w[i - 7] + t2 + w[i - 16]) | 0;
+    }
+
+    // initialize hash value for this chunk
+    a = s.h0;
+    b = s.h1;
+    c = s.h2;
+    d = s.h3;
+    e = s.h4;
+    f = s.h5;
+    g = s.h6;
+    h = s.h7;
+
+    // round function
+    for(i = 0; i < 64; ++i) {
+      // Sum1(e)
+      s1 =
+        ((e >>> 6) | (e << 26)) ^
+        ((e >>> 11) | (e << 21)) ^
+        ((e >>> 25) | (e << 7));
+      // Ch(e, f, g) (optimized the same way as SHA-1)
+      ch = g ^ (e & (f ^ g));
+      // Sum0(a)
+      s0 =
+        ((a >>> 2) | (a << 30)) ^
+        ((a >>> 13) | (a << 19)) ^
+        ((a >>> 22) | (a << 10));
+      // Maj(a, b, c) (optimized the same way as SHA-1)
+      maj = (a & b) | (c & (a ^ b));
+
+      // main algorithm
+      t1 = h + s1 + ch + _k[i] + w[i];
+      t2 = s0 + maj;
+      h = g;
+      g = f;
+      f = e;
+      // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
+      // can't truncate with `| 0`
+      e = (d + t1) >>> 0;
+      d = c;
+      c = b;
+      b = a;
+      // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug
+      // can't truncate with `| 0`
+      a = (t1 + t2) >>> 0;
+    }
+
+    // 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;
+    s.h4 = (s.h4 + e) | 0;
+    s.h5 = (s.h5 + f) | 0;
+    s.h6 = (s.h6 + g) | 0;
+    s.h7 = (s.h7 + h) | 0;
+    len -= 64;
+  }
+}
Index: frontend/node_modules/node-forge/lib/sha512.js
===================================================================
--- frontend/node_modules/node-forge/lib/sha512.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/sha512.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,561 @@
+/**
+ * Secure Hash Algorithm with a 1024-bit block size implementation.
+ *
+ * This includes: SHA-512, SHA-384, SHA-512/224, and SHA-512/256. For
+ * SHA-256 (block size 512 bits), see sha256.js.
+ *
+ * See FIPS 180-4 for details.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2014-2015 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./md');
+require('./util');
+
+var sha512 = module.exports = forge.sha512 = forge.sha512 || {};
+
+// SHA-512
+forge.md.sha512 = forge.md.algorithms.sha512 = sha512;
+
+// SHA-384
+var sha384 = forge.sha384 = forge.sha512.sha384 = forge.sha512.sha384 || {};
+sha384.create = function() {
+  return sha512.create('SHA-384');
+};
+forge.md.sha384 = forge.md.algorithms.sha384 = sha384;
+
+// SHA-512/256
+forge.sha512.sha256 = forge.sha512.sha256 || {
+  create: function() {
+    return sha512.create('SHA-512/256');
+  }
+};
+forge.md['sha512/256'] = forge.md.algorithms['sha512/256'] =
+  forge.sha512.sha256;
+
+// SHA-512/224
+forge.sha512.sha224 = forge.sha512.sha224 || {
+  create: function() {
+    return sha512.create('SHA-512/224');
+  }
+};
+forge.md['sha512/224'] = forge.md.algorithms['sha512/224'] =
+  forge.sha512.sha224;
+
+/**
+ * Creates a SHA-2 message digest object.
+ *
+ * @param algorithm the algorithm to use (SHA-512, SHA-384, SHA-512/224,
+ *          SHA-512/256).
+ *
+ * @return a message digest object.
+ */
+sha512.create = function(algorithm) {
+  // do initialization as necessary
+  if(!_initialized) {
+    _init();
+  }
+
+  if(typeof algorithm === 'undefined') {
+    algorithm = 'SHA-512';
+  }
+
+  if(!(algorithm in _states)) {
+    throw new Error('Invalid SHA-512 algorithm: ' + algorithm);
+  }
+
+  // SHA-512 state contains eight 64-bit integers (each as two 32-bit ints)
+  var _state = _states[algorithm];
+  var _h = null;
+
+  // input buffer
+  var _input = forge.util.createBuffer();
+
+  // used for 64-bit word storage
+  var _w = new Array(80);
+  for(var wi = 0; wi < 80; ++wi) {
+    _w[wi] = new Array(2);
+  }
+
+  // determine digest length by algorithm name (default)
+  var digestLength = 64;
+  switch(algorithm) {
+    case 'SHA-384':
+      digestLength = 48;
+      break;
+    case 'SHA-512/256':
+      digestLength = 32;
+      break;
+    case 'SHA-512/224':
+      digestLength = 28;
+      break;
+  }
+
+  // message digest object
+  var md = {
+    // SHA-512 => sha512
+    algorithm: algorithm.replace('-', '').toLowerCase(),
+    blockLength: 128,
+    digestLength: digestLength,
+    // 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: 16
+  };
+
+  /**
+   * 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.messageLength128 for backwards-compatibility)
+    md.fullMessageLength = md.messageLength128 = [];
+    var int32s = md.messageLengthSize / 4;
+    for(var i = 0; i < int32s; ++i) {
+      md.fullMessageLength.push(0);
+    }
+    _input = forge.util.createBuffer();
+    _h = new Array(_state.length);
+    for(var i = 0; i < _state.length; ++i) {
+      _h[i] = _state[i].slice(0);
+    }
+    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(_h, _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 SHA-512 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 896 mod 1024. In other words,
+    the data to be digested must be a multiple of 1024 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 16 bytes (128
+    bits), that means that the last segment of the data must have 112 bytes
+    (896 bits) of message and padding. Therefore, the length of the message
+    plus the padding must be congruent to 896 mod 1024 because
+    1024 - 128 = 896.
+
+    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 896 mod 1024, then 1024 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 big-endian order; since length
+    // is stored in bytes we multiply by 8 and add carry from next int
+    var next, carry;
+    var bits = md.fullMessageLength[0] * 8;
+    for(var i = 0; i < md.fullMessageLength.length - 1; ++i) {
+      next = md.fullMessageLength[i + 1] * 8;
+      carry = (next / 0x100000000) >>> 0;
+      bits += carry;
+      finalBlock.putInt32(bits >>> 0);
+      bits = next >>> 0;
+    }
+    finalBlock.putInt32(bits);
+
+    var h = new Array(_h.length);
+    for(var i = 0; i < _h.length; ++i) {
+      h[i] = _h[i].slice(0);
+    }
+    _update(h, _w, finalBlock);
+    var rval = forge.util.createBuffer();
+    var hlen;
+    if(algorithm === 'SHA-512') {
+      hlen = h.length;
+    } else if(algorithm === 'SHA-384') {
+      hlen = h.length - 2;
+    } else {
+      hlen = h.length - 4;
+    }
+    for(var i = 0; i < hlen; ++i) {
+      rval.putInt32(h[i][0]);
+      if(i !== hlen - 1 || algorithm !== 'SHA-512/224') {
+        rval.putInt32(h[i][1]);
+      }
+    }
+    return rval;
+  };
+
+  return md;
+};
+
+// sha-512 padding bytes not initialized yet
+var _padding = null;
+var _initialized = false;
+
+// table of constants
+var _k = null;
+
+// initial hash states
+var _states = null;
+
+/**
+ * Initializes the constant tables.
+ */
+function _init() {
+  // create padding
+  _padding = String.fromCharCode(128);
+  _padding += forge.util.fillString(String.fromCharCode(0x00), 128);
+
+  // create K table for SHA-512
+  _k = [
+    [0x428a2f98, 0xd728ae22], [0x71374491, 0x23ef65cd],
+    [0xb5c0fbcf, 0xec4d3b2f], [0xe9b5dba5, 0x8189dbbc],
+    [0x3956c25b, 0xf348b538], [0x59f111f1, 0xb605d019],
+    [0x923f82a4, 0xaf194f9b], [0xab1c5ed5, 0xda6d8118],
+    [0xd807aa98, 0xa3030242], [0x12835b01, 0x45706fbe],
+    [0x243185be, 0x4ee4b28c], [0x550c7dc3, 0xd5ffb4e2],
+    [0x72be5d74, 0xf27b896f], [0x80deb1fe, 0x3b1696b1],
+    [0x9bdc06a7, 0x25c71235], [0xc19bf174, 0xcf692694],
+    [0xe49b69c1, 0x9ef14ad2], [0xefbe4786, 0x384f25e3],
+    [0x0fc19dc6, 0x8b8cd5b5], [0x240ca1cc, 0x77ac9c65],
+    [0x2de92c6f, 0x592b0275], [0x4a7484aa, 0x6ea6e483],
+    [0x5cb0a9dc, 0xbd41fbd4], [0x76f988da, 0x831153b5],
+    [0x983e5152, 0xee66dfab], [0xa831c66d, 0x2db43210],
+    [0xb00327c8, 0x98fb213f], [0xbf597fc7, 0xbeef0ee4],
+    [0xc6e00bf3, 0x3da88fc2], [0xd5a79147, 0x930aa725],
+    [0x06ca6351, 0xe003826f], [0x14292967, 0x0a0e6e70],
+    [0x27b70a85, 0x46d22ffc], [0x2e1b2138, 0x5c26c926],
+    [0x4d2c6dfc, 0x5ac42aed], [0x53380d13, 0x9d95b3df],
+    [0x650a7354, 0x8baf63de], [0x766a0abb, 0x3c77b2a8],
+    [0x81c2c92e, 0x47edaee6], [0x92722c85, 0x1482353b],
+    [0xa2bfe8a1, 0x4cf10364], [0xa81a664b, 0xbc423001],
+    [0xc24b8b70, 0xd0f89791], [0xc76c51a3, 0x0654be30],
+    [0xd192e819, 0xd6ef5218], [0xd6990624, 0x5565a910],
+    [0xf40e3585, 0x5771202a], [0x106aa070, 0x32bbd1b8],
+    [0x19a4c116, 0xb8d2d0c8], [0x1e376c08, 0x5141ab53],
+    [0x2748774c, 0xdf8eeb99], [0x34b0bcb5, 0xe19b48a8],
+    [0x391c0cb3, 0xc5c95a63], [0x4ed8aa4a, 0xe3418acb],
+    [0x5b9cca4f, 0x7763e373], [0x682e6ff3, 0xd6b2b8a3],
+    [0x748f82ee, 0x5defb2fc], [0x78a5636f, 0x43172f60],
+    [0x84c87814, 0xa1f0ab72], [0x8cc70208, 0x1a6439ec],
+    [0x90befffa, 0x23631e28], [0xa4506ceb, 0xde82bde9],
+    [0xbef9a3f7, 0xb2c67915], [0xc67178f2, 0xe372532b],
+    [0xca273ece, 0xea26619c], [0xd186b8c7, 0x21c0c207],
+    [0xeada7dd6, 0xcde0eb1e], [0xf57d4f7f, 0xee6ed178],
+    [0x06f067aa, 0x72176fba], [0x0a637dc5, 0xa2c898a6],
+    [0x113f9804, 0xbef90dae], [0x1b710b35, 0x131c471b],
+    [0x28db77f5, 0x23047d84], [0x32caab7b, 0x40c72493],
+    [0x3c9ebe0a, 0x15c9bebc], [0x431d67c4, 0x9c100d4c],
+    [0x4cc5d4be, 0xcb3e42b6], [0x597f299c, 0xfc657e2a],
+    [0x5fcb6fab, 0x3ad6faec], [0x6c44198c, 0x4a475817]
+  ];
+
+  // initial hash states
+  _states = {};
+  _states['SHA-512'] = [
+    [0x6a09e667, 0xf3bcc908],
+    [0xbb67ae85, 0x84caa73b],
+    [0x3c6ef372, 0xfe94f82b],
+    [0xa54ff53a, 0x5f1d36f1],
+    [0x510e527f, 0xade682d1],
+    [0x9b05688c, 0x2b3e6c1f],
+    [0x1f83d9ab, 0xfb41bd6b],
+    [0x5be0cd19, 0x137e2179]
+  ];
+  _states['SHA-384'] = [
+    [0xcbbb9d5d, 0xc1059ed8],
+    [0x629a292a, 0x367cd507],
+    [0x9159015a, 0x3070dd17],
+    [0x152fecd8, 0xf70e5939],
+    [0x67332667, 0xffc00b31],
+    [0x8eb44a87, 0x68581511],
+    [0xdb0c2e0d, 0x64f98fa7],
+    [0x47b5481d, 0xbefa4fa4]
+  ];
+  _states['SHA-512/256'] = [
+    [0x22312194, 0xFC2BF72C],
+    [0x9F555FA3, 0xC84C64C2],
+    [0x2393B86B, 0x6F53B151],
+    [0x96387719, 0x5940EABD],
+    [0x96283EE2, 0xA88EFFE3],
+    [0xBE5E1E25, 0x53863992],
+    [0x2B0199FC, 0x2C85B8AA],
+    [0x0EB72DDC, 0x81C52CA2]
+  ];
+  _states['SHA-512/224'] = [
+    [0x8C3D37C8, 0x19544DA2],
+    [0x73E19966, 0x89DCD4D6],
+    [0x1DFAB7AE, 0x32FF9C82],
+    [0x679DD514, 0x582F9FCF],
+    [0x0F6D2B69, 0x7BD44DA8],
+    [0x77E36F73, 0x04C48942],
+    [0x3F9D85A8, 0x6A1D36C8],
+    [0x1112E6AD, 0x91D692A1]
+  ];
+
+  // now initialized
+  _initialized = true;
+}
+
+/**
+ * Updates a SHA-512 state with the given byte buffer.
+ *
+ * @param s the SHA-512 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 (128 byte) chunks
+  var t1_hi, t1_lo;
+  var t2_hi, t2_lo;
+  var s0_hi, s0_lo;
+  var s1_hi, s1_lo;
+  var ch_hi, ch_lo;
+  var maj_hi, maj_lo;
+  var a_hi, a_lo;
+  var b_hi, b_lo;
+  var c_hi, c_lo;
+  var d_hi, d_lo;
+  var e_hi, e_lo;
+  var f_hi, f_lo;
+  var g_hi, g_lo;
+  var h_hi, h_lo;
+  var i, hi, lo, w2, w7, w15, w16;
+  var len = bytes.length();
+  while(len >= 128) {
+    // the w array will be populated with sixteen 64-bit big-endian words
+    // and then extended into 64 64-bit words according to SHA-512
+    for(i = 0; i < 16; ++i) {
+      w[i][0] = bytes.getInt32() >>> 0;
+      w[i][1] = bytes.getInt32() >>> 0;
+    }
+    for(; i < 80; ++i) {
+      // for word 2 words ago: ROTR 19(x) ^ ROTR 61(x) ^ SHR 6(x)
+      w2 = w[i - 2];
+      hi = w2[0];
+      lo = w2[1];
+
+      // high bits
+      t1_hi = (
+        ((hi >>> 19) | (lo << 13)) ^ // ROTR 19
+        ((lo >>> 29) | (hi << 3)) ^ // ROTR 61/(swap + ROTR 29)
+        (hi >>> 6)) >>> 0; // SHR 6
+      // low bits
+      t1_lo = (
+        ((hi << 13) | (lo >>> 19)) ^ // ROTR 19
+        ((lo << 3) | (hi >>> 29)) ^ // ROTR 61/(swap + ROTR 29)
+        ((hi << 26) | (lo >>> 6))) >>> 0; // SHR 6
+
+      // for word 15 words ago: ROTR 1(x) ^ ROTR 8(x) ^ SHR 7(x)
+      w15 = w[i - 15];
+      hi = w15[0];
+      lo = w15[1];
+
+      // high bits
+      t2_hi = (
+        ((hi >>> 1) | (lo << 31)) ^ // ROTR 1
+        ((hi >>> 8) | (lo << 24)) ^ // ROTR 8
+        (hi >>> 7)) >>> 0; // SHR 7
+      // low bits
+      t2_lo = (
+        ((hi << 31) | (lo >>> 1)) ^ // ROTR 1
+        ((hi << 24) | (lo >>> 8)) ^ // ROTR 8
+        ((hi << 25) | (lo >>> 7))) >>> 0; // SHR 7
+
+      // sum(t1, word 7 ago, t2, word 16 ago) modulo 2^64 (carry lo overflow)
+      w7 = w[i - 7];
+      w16 = w[i - 16];
+      lo = (t1_lo + w7[1] + t2_lo + w16[1]);
+      w[i][0] = (t1_hi + w7[0] + t2_hi + w16[0] +
+        ((lo / 0x100000000) >>> 0)) >>> 0;
+      w[i][1] = lo >>> 0;
+    }
+
+    // initialize hash value for this chunk
+    a_hi = s[0][0];
+    a_lo = s[0][1];
+    b_hi = s[1][0];
+    b_lo = s[1][1];
+    c_hi = s[2][0];
+    c_lo = s[2][1];
+    d_hi = s[3][0];
+    d_lo = s[3][1];
+    e_hi = s[4][0];
+    e_lo = s[4][1];
+    f_hi = s[5][0];
+    f_lo = s[5][1];
+    g_hi = s[6][0];
+    g_lo = s[6][1];
+    h_hi = s[7][0];
+    h_lo = s[7][1];
+
+    // round function
+    for(i = 0; i < 80; ++i) {
+      // Sum1(e) = ROTR 14(e) ^ ROTR 18(e) ^ ROTR 41(e)
+      s1_hi = (
+        ((e_hi >>> 14) | (e_lo << 18)) ^ // ROTR 14
+        ((e_hi >>> 18) | (e_lo << 14)) ^ // ROTR 18
+        ((e_lo >>> 9) | (e_hi << 23))) >>> 0; // ROTR 41/(swap + ROTR 9)
+      s1_lo = (
+        ((e_hi << 18) | (e_lo >>> 14)) ^ // ROTR 14
+        ((e_hi << 14) | (e_lo >>> 18)) ^ // ROTR 18
+        ((e_lo << 23) | (e_hi >>> 9))) >>> 0; // ROTR 41/(swap + ROTR 9)
+
+      // Ch(e, f, g) (optimized the same way as SHA-1)
+      ch_hi = (g_hi ^ (e_hi & (f_hi ^ g_hi))) >>> 0;
+      ch_lo = (g_lo ^ (e_lo & (f_lo ^ g_lo))) >>> 0;
+
+      // Sum0(a) = ROTR 28(a) ^ ROTR 34(a) ^ ROTR 39(a)
+      s0_hi = (
+        ((a_hi >>> 28) | (a_lo << 4)) ^ // ROTR 28
+        ((a_lo >>> 2) | (a_hi << 30)) ^ // ROTR 34/(swap + ROTR 2)
+        ((a_lo >>> 7) | (a_hi << 25))) >>> 0; // ROTR 39/(swap + ROTR 7)
+      s0_lo = (
+        ((a_hi << 4) | (a_lo >>> 28)) ^ // ROTR 28
+        ((a_lo << 30) | (a_hi >>> 2)) ^ // ROTR 34/(swap + ROTR 2)
+        ((a_lo << 25) | (a_hi >>> 7))) >>> 0; // ROTR 39/(swap + ROTR 7)
+
+      // Maj(a, b, c) (optimized the same way as SHA-1)
+      maj_hi = ((a_hi & b_hi) | (c_hi & (a_hi ^ b_hi))) >>> 0;
+      maj_lo = ((a_lo & b_lo) | (c_lo & (a_lo ^ b_lo))) >>> 0;
+
+      // main algorithm
+      // t1 = (h + s1 + ch + _k[i] + _w[i]) modulo 2^64 (carry lo overflow)
+      lo = (h_lo + s1_lo + ch_lo + _k[i][1] + w[i][1]);
+      t1_hi = (h_hi + s1_hi + ch_hi + _k[i][0] + w[i][0] +
+        ((lo / 0x100000000) >>> 0)) >>> 0;
+      t1_lo = lo >>> 0;
+
+      // t2 = s0 + maj modulo 2^64 (carry lo overflow)
+      lo = s0_lo + maj_lo;
+      t2_hi = (s0_hi + maj_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+      t2_lo = lo >>> 0;
+
+      h_hi = g_hi;
+      h_lo = g_lo;
+
+      g_hi = f_hi;
+      g_lo = f_lo;
+
+      f_hi = e_hi;
+      f_lo = e_lo;
+
+      // e = (d + t1) modulo 2^64 (carry lo overflow)
+      lo = d_lo + t1_lo;
+      e_hi = (d_hi + t1_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+      e_lo = lo >>> 0;
+
+      d_hi = c_hi;
+      d_lo = c_lo;
+
+      c_hi = b_hi;
+      c_lo = b_lo;
+
+      b_hi = a_hi;
+      b_lo = a_lo;
+
+      // a = (t1 + t2) modulo 2^64 (carry lo overflow)
+      lo = t1_lo + t2_lo;
+      a_hi = (t1_hi + t2_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+      a_lo = lo >>> 0;
+    }
+
+    // update hash state (additional modulo 2^64)
+    lo = s[0][1] + a_lo;
+    s[0][0] = (s[0][0] + a_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+    s[0][1] = lo >>> 0;
+
+    lo = s[1][1] + b_lo;
+    s[1][0] = (s[1][0] + b_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+    s[1][1] = lo >>> 0;
+
+    lo = s[2][1] + c_lo;
+    s[2][0] = (s[2][0] + c_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+    s[2][1] = lo >>> 0;
+
+    lo = s[3][1] + d_lo;
+    s[3][0] = (s[3][0] + d_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+    s[3][1] = lo >>> 0;
+
+    lo = s[4][1] + e_lo;
+    s[4][0] = (s[4][0] + e_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+    s[4][1] = lo >>> 0;
+
+    lo = s[5][1] + f_lo;
+    s[5][0] = (s[5][0] + f_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+    s[5][1] = lo >>> 0;
+
+    lo = s[6][1] + g_lo;
+    s[6][0] = (s[6][0] + g_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+    s[6][1] = lo >>> 0;
+
+    lo = s[7][1] + h_lo;
+    s[7][0] = (s[7][0] + h_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
+    s[7][1] = lo >>> 0;
+
+    len -= 128;
+  }
+}
Index: frontend/node_modules/node-forge/lib/socket.js
===================================================================
--- frontend/node_modules/node-forge/lib/socket.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/socket.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,287 @@
+/**
+ * Socket implementation that uses flash SocketPool class as a backend.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2013 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./util');
+
+// define net namespace
+var net = module.exports = forge.net = forge.net || {};
+
+// map of flash ID to socket pool
+net.socketPools = {};
+
+/**
+ * Creates a flash socket pool.
+ *
+ * @param options:
+ *          flashId: the dom ID for the flash object element.
+ *          policyPort: the default policy port for sockets, 0 to use the
+ *            flash default.
+ *          policyUrl: the default policy file URL for sockets (if provided
+ *            used instead of a policy port).
+ *          msie: true if the browser is msie, false if not.
+ *
+ * @return the created socket pool.
+ */
+net.createSocketPool = function(options) {
+  // set default
+  options.msie = options.msie || false;
+
+  // initialize the flash interface
+  var spId = options.flashId;
+  var api = document.getElementById(spId);
+  api.init({marshallExceptions: !options.msie});
+
+  // create socket pool entry
+  var sp = {
+    // ID of the socket pool
+    id: spId,
+    // flash interface
+    flashApi: api,
+    // map of socket ID to sockets
+    sockets: {},
+    // default policy port
+    policyPort: options.policyPort || 0,
+    // default policy URL
+    policyUrl: options.policyUrl || null
+  };
+  net.socketPools[spId] = sp;
+
+  // create event handler, subscribe to flash events
+  if(options.msie === true) {
+    sp.handler = function(e) {
+      if(e.id in sp.sockets) {
+        // get handler function
+        var f;
+        switch(e.type) {
+        case 'connect':
+          f = 'connected';
+          break;
+        case 'close':
+          f = 'closed';
+          break;
+        case 'socketData':
+          f = 'data';
+          break;
+        default:
+          f = 'error';
+          break;
+        }
+        /* IE calls javascript on the thread of the external object
+          that triggered the event (in this case flash) ... which will
+          either run concurrently with other javascript or pre-empt any
+          running javascript in the middle of its execution (BAD!) ...
+          calling setTimeout() will schedule the javascript to run on
+          the javascript thread and solve this EVIL problem. */
+        setTimeout(function() {sp.sockets[e.id][f](e);}, 0);
+      }
+    };
+  } else {
+    sp.handler = function(e) {
+      if(e.id in sp.sockets) {
+        // get handler function
+        var f;
+        switch(e.type) {
+        case 'connect':
+          f = 'connected';
+          break;
+        case 'close':
+          f = 'closed';
+          break;
+        case 'socketData':
+          f = 'data';
+          break;
+        default:
+          f = 'error';
+          break;
+        }
+        sp.sockets[e.id][f](e);
+      }
+    };
+  }
+  var handler = 'forge.net.socketPools[\'' + spId + '\'].handler';
+  api.subscribe('connect', handler);
+  api.subscribe('close', handler);
+  api.subscribe('socketData', handler);
+  api.subscribe('ioError', handler);
+  api.subscribe('securityError', handler);
+
+  /**
+   * Destroys a socket pool. The socket pool still needs to be cleaned
+   * up via net.cleanup().
+   */
+  sp.destroy = function() {
+    delete net.socketPools[options.flashId];
+    for(var id in sp.sockets) {
+      sp.sockets[id].destroy();
+    }
+    sp.sockets = {};
+    api.cleanup();
+  };
+
+  /**
+   * Creates a new socket.
+   *
+   * @param options:
+   *          connected: function(event) called when the socket connects.
+   *          closed: function(event) called when the socket closes.
+   *          data: function(event) called when socket data has arrived,
+   *            it can be read from the socket using receive().
+   *          error: function(event) called when a socket error occurs.
+   */
+   sp.createSocket = function(options) {
+     // default to empty options
+     options = options || {};
+
+     // create flash socket
+     var id = api.create();
+
+     // create javascript socket wrapper
+     var socket = {
+       id: id,
+       // set handlers
+       connected: options.connected || function(e) {},
+       closed: options.closed || function(e) {},
+       data: options.data || function(e) {},
+       error: options.error || function(e) {}
+     };
+
+     /**
+      * Destroys this socket.
+      */
+     socket.destroy = function() {
+       api.destroy(id);
+       delete sp.sockets[id];
+     };
+
+     /**
+      * Connects this socket.
+      *
+      * @param options:
+      *          host: the host to connect to.
+      *          port: the port to connect to.
+      *          policyPort: the policy port to use (if non-default), 0 to
+      *            use the flash default.
+      *          policyUrl: the policy file URL to use (instead of port).
+      */
+     socket.connect = function(options) {
+       // give precedence to policy URL over policy port
+       // if no policy URL and passed port isn't 0, use default port,
+       // otherwise use 0 for the port
+       var policyUrl = options.policyUrl || null;
+       var policyPort = 0;
+       if(policyUrl === null && options.policyPort !== 0) {
+         policyPort = options.policyPort || sp.policyPort;
+       }
+       api.connect(id, options.host, options.port, policyPort, policyUrl);
+     };
+
+     /**
+      * Closes this socket.
+      */
+     socket.close = function() {
+       api.close(id);
+       socket.closed({
+         id: socket.id,
+         type: 'close',
+         bytesAvailable: 0
+       });
+     };
+
+     /**
+      * Determines if the socket is connected or not.
+      *
+      * @return true if connected, false if not.
+      */
+     socket.isConnected = function() {
+       return api.isConnected(id);
+     };
+
+     /**
+      * Writes bytes to this socket.
+      *
+      * @param bytes the bytes (as a string) to write.
+      *
+      * @return true on success, false on failure.
+      */
+     socket.send = function(bytes) {
+       return api.send(id, forge.util.encode64(bytes));
+     };
+
+     /**
+      * Reads bytes from this socket (non-blocking). Fewer than the number
+      * of bytes requested may be read if enough bytes are not available.
+      *
+      * This method should be called from the data handler if there are
+      * enough bytes available. To see how many bytes are available, check
+      * the 'bytesAvailable' property on the event in the data handler or
+      * call the bytesAvailable() function on the socket. If the browser is
+      * msie, then the bytesAvailable() function should be used to avoid
+      * race conditions. Otherwise, using the property on the data handler's
+      * event may be quicker.
+      *
+      * @param count the maximum number of bytes to read.
+      *
+      * @return the bytes read (as a string) or null on error.
+      */
+     socket.receive = function(count) {
+       var rval = api.receive(id, count).rval;
+       return (rval === null) ? null : forge.util.decode64(rval);
+     };
+
+     /**
+      * Gets the number of bytes available for receiving on the socket.
+      *
+      * @return the number of bytes available for receiving.
+      */
+     socket.bytesAvailable = function() {
+       return api.getBytesAvailable(id);
+     };
+
+     // store and return socket
+     sp.sockets[id] = socket;
+     return socket;
+  };
+
+  return sp;
+};
+
+/**
+ * Destroys a flash socket pool.
+ *
+ * @param options:
+ *          flashId: the dom ID for the flash object element.
+ */
+net.destroySocketPool = function(options) {
+  if(options.flashId in net.socketPools) {
+    var sp = net.socketPools[options.flashId];
+    sp.destroy();
+  }
+};
+
+/**
+ * Creates a new socket.
+ *
+ * @param options:
+ *          flashId: the dom ID for the flash object element.
+ *          connected: function(event) called when the socket connects.
+ *          closed: function(event) called when the socket closes.
+ *          data: function(event) called when socket data has arrived, it
+ *            can be read from the socket using receive().
+ *          error: function(event) called when a socket error occurs.
+ *
+ * @return the created socket.
+ */
+net.createSocket = function(options) {
+  var socket = null;
+  if(options.flashId in net.socketPools) {
+    // get related socket pool
+    var sp = net.socketPools[options.flashId];
+    socket = sp.createSocket(options);
+  }
+  return socket;
+};
Index: frontend/node_modules/node-forge/lib/ssh.js
===================================================================
--- frontend/node_modules/node-forge/lib/ssh.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/ssh.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,236 @@
+/**
+ * Functions to output keys in SSH-friendly formats.
+ *
+ * This is part of the Forge project which may be used under the terms of
+ * either the BSD License or the GNU General Public License (GPL) Version 2.
+ *
+ * See: https://github.com/digitalbazaar/forge/blob/cbebca3780658703d925b61b2caffb1d263a6c1d/LICENSE
+ *
+ * @author https://github.com/shellac
+ */
+var forge = require('./forge');
+require('./aes');
+require('./hmac');
+require('./md5');
+require('./sha1');
+require('./util');
+
+var ssh = module.exports = forge.ssh = forge.ssh || {};
+
+/**
+ * Encodes (and optionally encrypts) a private RSA key as a Putty PPK file.
+ *
+ * @param privateKey the key.
+ * @param passphrase a passphrase to protect the key (falsy for no encryption).
+ * @param comment a comment to include in the key file.
+ *
+ * @return the PPK file as a string.
+ */
+ssh.privateKeyToPutty = function(privateKey, passphrase, comment) {
+  comment = comment || '';
+  passphrase = passphrase || '';
+  var algorithm = 'ssh-rsa';
+  var encryptionAlgorithm = (passphrase === '') ? 'none' : 'aes256-cbc';
+
+  var ppk = 'PuTTY-User-Key-File-2: ' + algorithm + '\r\n';
+  ppk += 'Encryption: ' + encryptionAlgorithm + '\r\n';
+  ppk += 'Comment: ' + comment + '\r\n';
+
+  // public key into buffer for ppk
+  var pubbuffer = forge.util.createBuffer();
+  _addStringToBuffer(pubbuffer, algorithm);
+  _addBigIntegerToBuffer(pubbuffer, privateKey.e);
+  _addBigIntegerToBuffer(pubbuffer, privateKey.n);
+
+  // write public key
+  var pub = forge.util.encode64(pubbuffer.bytes(), 64);
+  var length = Math.floor(pub.length / 66) + 1; // 66 = 64 + \r\n
+  ppk += 'Public-Lines: ' + length + '\r\n';
+  ppk += pub;
+
+  // private key into a buffer
+  var privbuffer = forge.util.createBuffer();
+  _addBigIntegerToBuffer(privbuffer, privateKey.d);
+  _addBigIntegerToBuffer(privbuffer, privateKey.p);
+  _addBigIntegerToBuffer(privbuffer, privateKey.q);
+  _addBigIntegerToBuffer(privbuffer, privateKey.qInv);
+
+  // optionally encrypt the private key
+  var priv;
+  if(!passphrase) {
+    // use the unencrypted buffer
+    priv = forge.util.encode64(privbuffer.bytes(), 64);
+  } else {
+    // encrypt RSA key using passphrase
+    var encLen = privbuffer.length() + 16 - 1;
+    encLen -= encLen % 16;
+
+    // pad private key with sha1-d data -- needs to be a multiple of 16
+    var padding = _sha1(privbuffer.bytes());
+
+    padding.truncate(padding.length() - encLen + privbuffer.length());
+    privbuffer.putBuffer(padding);
+
+    var aeskey = forge.util.createBuffer();
+    aeskey.putBuffer(_sha1('\x00\x00\x00\x00', passphrase));
+    aeskey.putBuffer(_sha1('\x00\x00\x00\x01', passphrase));
+
+    // encrypt some bytes using CBC mode
+    // key is 40 bytes, so truncate *by* 8 bytes
+    var cipher = forge.aes.createEncryptionCipher(aeskey.truncate(8), 'CBC');
+    cipher.start(forge.util.createBuffer().fillWithByte(0, 16));
+    cipher.update(privbuffer.copy());
+    cipher.finish();
+    var encrypted = cipher.output;
+
+    // Note: this appears to differ from Putty -- is forge wrong, or putty?
+    // due to padding we finish as an exact multiple of 16
+    encrypted.truncate(16); // all padding
+
+    priv = forge.util.encode64(encrypted.bytes(), 64);
+  }
+
+  // output private key
+  length = Math.floor(priv.length / 66) + 1; // 64 + \r\n
+  ppk += '\r\nPrivate-Lines: ' + length + '\r\n';
+  ppk += priv;
+
+  // MAC
+  var mackey = _sha1('putty-private-key-file-mac-key', passphrase);
+
+  var macbuffer = forge.util.createBuffer();
+  _addStringToBuffer(macbuffer, algorithm);
+  _addStringToBuffer(macbuffer, encryptionAlgorithm);
+  _addStringToBuffer(macbuffer, comment);
+  macbuffer.putInt32(pubbuffer.length());
+  macbuffer.putBuffer(pubbuffer);
+  macbuffer.putInt32(privbuffer.length());
+  macbuffer.putBuffer(privbuffer);
+
+  var hmac = forge.hmac.create();
+  hmac.start('sha1', mackey);
+  hmac.update(macbuffer.bytes());
+
+  ppk += '\r\nPrivate-MAC: ' + hmac.digest().toHex() + '\r\n';
+
+  return ppk;
+};
+
+/**
+ * Encodes a public RSA key as an OpenSSH file.
+ *
+ * @param key the key.
+ * @param comment a comment.
+ *
+ * @return the public key in OpenSSH format.
+ */
+ssh.publicKeyToOpenSSH = function(key, comment) {
+  var type = 'ssh-rsa';
+  comment = comment || '';
+
+  var buffer = forge.util.createBuffer();
+  _addStringToBuffer(buffer, type);
+  _addBigIntegerToBuffer(buffer, key.e);
+  _addBigIntegerToBuffer(buffer, key.n);
+
+  return type + ' ' + forge.util.encode64(buffer.bytes()) + ' ' + comment;
+};
+
+/**
+ * Encodes a private RSA key as an OpenSSH file.
+ *
+ * @param key the key.
+ * @param passphrase a passphrase to protect the key (falsy for no encryption).
+ *
+ * @return the public key in OpenSSH format.
+ */
+ssh.privateKeyToOpenSSH = function(privateKey, passphrase) {
+  if(!passphrase) {
+    return forge.pki.privateKeyToPem(privateKey);
+  }
+  // OpenSSH private key is just a legacy format, it seems
+  return forge.pki.encryptRsaPrivateKey(privateKey, passphrase,
+    {legacy: true, algorithm: 'aes128'});
+};
+
+/**
+ * Gets the SSH fingerprint for the given public key.
+ *
+ * @param options the options to use.
+ *          [md] the message digest object to use (defaults to forge.md.md5).
+ *          [encoding] an alternative output encoding, such as 'hex'
+ *            (defaults to none, outputs a byte buffer).
+ *          [delimiter] the delimiter to use between bytes for 'hex' encoded
+ *            output, eg: ':' (defaults to none).
+ *
+ * @return the fingerprint as a byte buffer or other encoding based on options.
+ */
+ssh.getPublicKeyFingerprint = function(key, options) {
+  options = options || {};
+  var md = options.md || forge.md.md5.create();
+
+  var type = 'ssh-rsa';
+  var buffer = forge.util.createBuffer();
+  _addStringToBuffer(buffer, type);
+  _addBigIntegerToBuffer(buffer, key.e);
+  _addBigIntegerToBuffer(buffer, key.n);
+
+  // hash public key bytes
+  md.start();
+  md.update(buffer.getBytes());
+  var digest = md.digest();
+  if(options.encoding === 'hex') {
+    var hex = digest.toHex();
+    if(options.delimiter) {
+      return hex.match(/.{2}/g).join(options.delimiter);
+    }
+    return hex;
+  } else if(options.encoding === 'binary') {
+    return digest.getBytes();
+  } else if(options.encoding) {
+    throw new Error('Unknown encoding "' + options.encoding + '".');
+  }
+  return digest;
+};
+
+/**
+ * Adds len(val) then val to a buffer.
+ *
+ * @param buffer the buffer to add to.
+ * @param val a big integer.
+ */
+function _addBigIntegerToBuffer(buffer, val) {
+  var hexVal = val.toString(16);
+  // ensure 2s complement +ve
+  if(hexVal[0] >= '8') {
+    hexVal = '00' + hexVal;
+  }
+  var bytes = forge.util.hexToBytes(hexVal);
+  buffer.putInt32(bytes.length);
+  buffer.putBytes(bytes);
+}
+
+/**
+ * Adds len(val) then val to a buffer.
+ *
+ * @param buffer the buffer to add to.
+ * @param val a string.
+ */
+function _addStringToBuffer(buffer, val) {
+  buffer.putInt32(val.length);
+  buffer.putString(val);
+}
+
+/**
+ * Hashes the arguments into one value using SHA-1.
+ *
+ * @return the sha1 hash of the provided arguments.
+ */
+function _sha1() {
+  var sha = forge.md.sha1.create();
+  var num = arguments.length;
+  for (var i = 0; i < num; ++i) {
+    sha.update(arguments[i]);
+  }
+  return sha.digest();
+}
Index: frontend/node_modules/node-forge/lib/tls.js
===================================================================
--- frontend/node_modules/node-forge/lib/tls.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/tls.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,4282 @@
+/**
+ * A Javascript implementation of Transport Layer Security (TLS).
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2009-2014 Digital Bazaar, Inc.
+ *
+ * The TLS Handshake Protocol involves the following steps:
+ *
+ * - Exchange hello messages to agree on algorithms, exchange random values,
+ * and check for session resumption.
+ *
+ * - Exchange the necessary cryptographic parameters to allow the client and
+ * server to agree on a premaster secret.
+ *
+ * - Exchange certificates and cryptographic information to allow the client
+ * and server to authenticate themselves.
+ *
+ * - Generate a master secret from the premaster secret and exchanged random
+ * values.
+ *
+ * - Provide security parameters to the record layer.
+ *
+ * - Allow the client and server to verify that their peer has calculated the
+ * same security parameters and that the handshake occurred without tampering
+ * by an attacker.
+ *
+ * Up to 4 different messages may be sent during a key exchange. The server
+ * certificate, the server key exchange, the client certificate, and the
+ * client key exchange.
+ *
+ * A typical handshake (from the client's perspective).
+ *
+ * 1. Client sends ClientHello.
+ * 2. Client receives ServerHello.
+ * 3. Client receives optional Certificate.
+ * 4. Client receives optional ServerKeyExchange.
+ * 5. Client receives ServerHelloDone.
+ * 6. Client sends optional Certificate.
+ * 7. Client sends ClientKeyExchange.
+ * 8. Client sends optional CertificateVerify.
+ * 9. Client sends ChangeCipherSpec.
+ * 10. Client sends Finished.
+ * 11. Client receives ChangeCipherSpec.
+ * 12. Client receives Finished.
+ * 13. Client sends/receives application data.
+ *
+ * To reuse an existing session:
+ *
+ * 1. Client sends ClientHello with session ID for reuse.
+ * 2. Client receives ServerHello with same session ID if reusing.
+ * 3. Client receives ChangeCipherSpec message if reusing.
+ * 4. Client receives Finished.
+ * 5. Client sends ChangeCipherSpec.
+ * 6. Client sends Finished.
+ *
+ * Note: Client ignores HelloRequest if in the middle of a handshake.
+ *
+ * Record Layer:
+ *
+ * The record layer fragments information blocks into TLSPlaintext records
+ * carrying data in chunks of 2^14 bytes or less. Client message boundaries are
+ * not preserved in the record layer (i.e., multiple client messages of the
+ * same ContentType MAY be coalesced into a single TLSPlaintext record, or a
+ * single message MAY be fragmented across several records).
+ *
+ * struct {
+ *   uint8 major;
+ *   uint8 minor;
+ * } ProtocolVersion;
+ *
+ * struct {
+ *   ContentType type;
+ *   ProtocolVersion version;
+ *   uint16 length;
+ *   opaque fragment[TLSPlaintext.length];
+ * } TLSPlaintext;
+ *
+ * type:
+ *   The higher-level protocol used to process the enclosed fragment.
+ *
+ * version:
+ *   The version of the protocol being employed. TLS Version 1.2 uses version
+ *   {3, 3}. TLS Version 1.0 uses version {3, 1}. Note that a client that
+ *   supports multiple versions of TLS may not know what version will be
+ *   employed before it receives the ServerHello.
+ *
+ * length:
+ *   The length (in bytes) of the following TLSPlaintext.fragment. The length
+ *   MUST NOT exceed 2^14 = 16384 bytes.
+ *
+ * fragment:
+ *   The application data. This data is transparent and treated as an
+ *   independent block to be dealt with by the higher-level protocol specified
+ *   by the type field.
+ *
+ * Implementations MUST NOT send zero-length fragments of Handshake, Alert, or
+ * ChangeCipherSpec content types. Zero-length fragments of Application data
+ * MAY be sent as they are potentially useful as a traffic analysis
+ * countermeasure.
+ *
+ * Note: Data of different TLS record layer content types MAY be interleaved.
+ * Application data is generally of lower precedence for transmission than
+ * other content types. However, records MUST be delivered to the network in
+ * the same order as they are protected by the record layer. Recipients MUST
+ * receive and process interleaved application layer traffic during handshakes
+ * subsequent to the first one on a connection.
+ *
+ * struct {
+ *   ContentType type;       // same as TLSPlaintext.type
+ *   ProtocolVersion version;// same as TLSPlaintext.version
+ *   uint16 length;
+ *   opaque fragment[TLSCompressed.length];
+ * } TLSCompressed;
+ *
+ * length:
+ *   The length (in bytes) of the following TLSCompressed.fragment.
+ *   The length MUST NOT exceed 2^14 + 1024.
+ *
+ * fragment:
+ *   The compressed form of TLSPlaintext.fragment.
+ *
+ * Note: A CompressionMethod.null operation is an identity operation; no fields
+ * are altered. In this implementation, since no compression is supported,
+ * uncompressed records are always the same as compressed records.
+ *
+ * Encryption Information:
+ *
+ * The encryption and MAC functions translate a TLSCompressed structure into a
+ * TLSCiphertext. The decryption functions reverse the process. The MAC of the
+ * record also includes a sequence number so that missing, extra, or repeated
+ * messages are detectable.
+ *
+ * struct {
+ *   ContentType type;
+ *   ProtocolVersion version;
+ *   uint16 length;
+ *   select (SecurityParameters.cipher_type) {
+ *     case stream: GenericStreamCipher;
+ *     case block:  GenericBlockCipher;
+ *     case aead:   GenericAEADCipher;
+ *   } fragment;
+ * } TLSCiphertext;
+ *
+ * type:
+ *   The type field is identical to TLSCompressed.type.
+ *
+ * version:
+ *   The version field is identical to TLSCompressed.version.
+ *
+ * length:
+ *   The length (in bytes) of the following TLSCiphertext.fragment.
+ *   The length MUST NOT exceed 2^14 + 2048.
+ *
+ * fragment:
+ *   The encrypted form of TLSCompressed.fragment, with the MAC.
+ *
+ * Note: Only CBC Block Ciphers are supported by this implementation.
+ *
+ * The TLSCompressed.fragment structures are converted to/from block
+ * TLSCiphertext.fragment structures.
+ *
+ * struct {
+ *   opaque IV[SecurityParameters.record_iv_length];
+ *   block-ciphered struct {
+ *     opaque content[TLSCompressed.length];
+ *     opaque MAC[SecurityParameters.mac_length];
+ *     uint8 padding[GenericBlockCipher.padding_length];
+ *     uint8 padding_length;
+ *   };
+ * } GenericBlockCipher;
+ *
+ * The MAC is generated as described in Section 6.2.3.1.
+ *
+ * IV:
+ *   The Initialization Vector (IV) SHOULD be chosen at random, and MUST be
+ *   unpredictable. Note that in versions of TLS prior to 1.1, there was no
+ *   IV field, and the last ciphertext block of the previous record (the "CBC
+ *   residue") was used as the IV. This was changed to prevent the attacks
+ *   described in [CBCATT]. For block ciphers, the IV length is of length
+ *   SecurityParameters.record_iv_length, which is equal to the
+ *   SecurityParameters.block_size.
+ *
+ * padding:
+ *   Padding that is added to force the length of the plaintext to be an
+ *   integral multiple of the block cipher's block length. The padding MAY be
+ *   any length up to 255 bytes, as long as it results in the
+ *   TLSCiphertext.length being an integral multiple of the block length.
+ *   Lengths longer than necessary might be desirable to frustrate attacks on
+ *   a protocol that are based on analysis of the lengths of exchanged
+ *   messages. Each uint8 in the padding data vector MUST be filled with the
+ *   padding length value. The receiver MUST check this padding and MUST use
+ *   the bad_record_mac alert to indicate padding errors.
+ *
+ * padding_length:
+ *   The padding length MUST be such that the total size of the
+ *   GenericBlockCipher structure is a multiple of the cipher's block length.
+ *   Legal values range from zero to 255, inclusive. This length specifies the
+ *   length of the padding field exclusive of the padding_length field itself.
+ *
+ * The encrypted data length (TLSCiphertext.length) is one more than the sum of
+ * SecurityParameters.block_length, TLSCompressed.length,
+ * SecurityParameters.mac_length, and padding_length.
+ *
+ * Example: If the block length is 8 bytes, the content length
+ * (TLSCompressed.length) is 61 bytes, and the MAC length is 20 bytes, then the
+ * length before padding is 82 bytes (this does not include the IV. Thus, the
+ * padding length modulo 8 must be equal to 6 in order to make the total length
+ * an even multiple of 8 bytes (the block length). The padding length can be
+ * 6, 14, 22, and so on, through 254. If the padding length were the minimum
+ * necessary, 6, the padding would be 6 bytes, each containing the value 6.
+ * Thus, the last 8 octets of the GenericBlockCipher before block encryption
+ * would be xx 06 06 06 06 06 06 06, where xx is the last octet of the MAC.
+ *
+ * Note: With block ciphers in CBC mode (Cipher Block Chaining), it is critical
+ * that the entire plaintext of the record be known before any ciphertext is
+ * transmitted. Otherwise, it is possible for the attacker to mount the attack
+ * described in [CBCATT].
+ *
+ * Implementation note: Canvel et al. [CBCTIME] have demonstrated a timing
+ * attack on CBC padding based on the time required to compute the MAC. In
+ * order to defend against this attack, implementations MUST ensure that
+ * record processing time is essentially the same whether or not the padding
+ * is correct. In general, the best way to do this is to compute the MAC even
+ * if the padding is incorrect, and only then reject the packet. For instance,
+ * if the pad appears to be incorrect, the implementation might assume a
+ * zero-length pad and then compute the MAC. This leaves a small timing
+ * channel, since MAC performance depends, to some extent, on the size of the
+ * data fragment, but it is not believed to be large enough to be exploitable,
+ * due to the large block size of existing MACs and the small size of the
+ * timing signal.
+ */
+var forge = require('./forge');
+require('./asn1');
+require('./hmac');
+require('./md5');
+require('./pem');
+require('./pki');
+require('./random');
+require('./sha1');
+require('./util');
+
+/**
+ * Generates pseudo random bytes by mixing the result of two hash functions,
+ * MD5 and SHA-1.
+ *
+ * prf_TLS1(secret, label, seed) =
+ *   P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed);
+ *
+ * Each P_hash function functions as follows:
+ *
+ * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+ *                        HMAC_hash(secret, A(2) + seed) +
+ *                        HMAC_hash(secret, A(3) + seed) + ...
+ * A() is defined as:
+ *   A(0) = seed
+ *   A(i) = HMAC_hash(secret, A(i-1))
+ *
+ * The '+' operator denotes concatenation.
+ *
+ * As many iterations A(N) as are needed are performed to generate enough
+ * pseudo random byte output. If an iteration creates more data than is
+ * necessary, then it is truncated.
+ *
+ * Therefore:
+ * A(1) = HMAC_hash(secret, A(0))
+ *      = HMAC_hash(secret, seed)
+ * A(2) = HMAC_hash(secret, A(1))
+ *      = HMAC_hash(secret, HMAC_hash(secret, seed))
+ *
+ * Therefore:
+ * P_hash(secret, seed) =
+ *   HMAC_hash(secret, HMAC_hash(secret, A(0)) + seed) +
+ *   HMAC_hash(secret, HMAC_hash(secret, A(1)) + seed) +
+ *   ...
+ *
+ * Therefore:
+ * P_hash(secret, seed) =
+ *   HMAC_hash(secret, HMAC_hash(secret, seed) + seed) +
+ *   HMAC_hash(secret, HMAC_hash(secret, HMAC_hash(secret, seed)) + seed) +
+ *   ...
+ *
+ * @param secret the secret to use.
+ * @param label the label to use.
+ * @param seed the seed value to use.
+ * @param length the number of bytes to generate.
+ *
+ * @return the pseudo random bytes in a byte buffer.
+ */
+var prf_TLS1 = function(secret, label, seed, length) {
+  var rval = forge.util.createBuffer();
+
+  /* For TLS 1.0, the secret is split in half, into two secrets of equal
+    length. If the secret has an odd length then the last byte of the first
+    half will be the same as the first byte of the second. The length of the
+    two secrets is half of the secret rounded up. */
+  var idx = (secret.length >> 1);
+  var slen = idx + (secret.length & 1);
+  var s1 = secret.substr(0, slen);
+  var s2 = secret.substr(idx, slen);
+  var ai = forge.util.createBuffer();
+  var hmac = forge.hmac.create();
+  seed = label + seed;
+
+  // determine the number of iterations that must be performed to generate
+  // enough output bytes, md5 creates 16 byte hashes, sha1 creates 20
+  var md5itr = Math.ceil(length / 16);
+  var sha1itr = Math.ceil(length / 20);
+
+  // do md5 iterations
+  hmac.start('MD5', s1);
+  var md5bytes = forge.util.createBuffer();
+  ai.putBytes(seed);
+  for(var i = 0; i < md5itr; ++i) {
+    // HMAC_hash(secret, A(i-1))
+    hmac.start(null, null);
+    hmac.update(ai.getBytes());
+    ai.putBuffer(hmac.digest());
+
+    // HMAC_hash(secret, A(i) + seed)
+    hmac.start(null, null);
+    hmac.update(ai.bytes() + seed);
+    md5bytes.putBuffer(hmac.digest());
+  }
+
+  // do sha1 iterations
+  hmac.start('SHA1', s2);
+  var sha1bytes = forge.util.createBuffer();
+  ai.clear();
+  ai.putBytes(seed);
+  for(var i = 0; i < sha1itr; ++i) {
+    // HMAC_hash(secret, A(i-1))
+    hmac.start(null, null);
+    hmac.update(ai.getBytes());
+    ai.putBuffer(hmac.digest());
+
+    // HMAC_hash(secret, A(i) + seed)
+    hmac.start(null, null);
+    hmac.update(ai.bytes() + seed);
+    sha1bytes.putBuffer(hmac.digest());
+  }
+
+  // XOR the md5 bytes with the sha1 bytes
+  rval.putBytes(forge.util.xorBytes(
+    md5bytes.getBytes(), sha1bytes.getBytes(), length));
+
+  return rval;
+};
+
+/**
+ * Generates pseudo random bytes using a SHA256 algorithm. For TLS 1.2.
+ *
+ * @param secret the secret to use.
+ * @param label the label to use.
+ * @param seed the seed value to use.
+ * @param length the number of bytes to generate.
+ *
+ * @return the pseudo random bytes in a byte buffer.
+ */
+var prf_sha256 = function(secret, label, seed, length) {
+   // FIXME: implement me for TLS 1.2
+};
+
+/**
+ * Gets a MAC for a record using the SHA-1 hash algorithm.
+ *
+ * @param key the mac key.
+ * @param state the sequence number (array of two 32-bit integers).
+ * @param record the record.
+ *
+ * @return the sha-1 hash (20 bytes) for the given record.
+ */
+var hmac_sha1 = function(key, seqNum, record) {
+  /* MAC is computed like so:
+  HMAC_hash(
+    key, seqNum +
+      TLSCompressed.type +
+      TLSCompressed.version +
+      TLSCompressed.length +
+      TLSCompressed.fragment)
+  */
+  var hmac = forge.hmac.create();
+  hmac.start('SHA1', key);
+  var b = forge.util.createBuffer();
+  b.putInt32(seqNum[0]);
+  b.putInt32(seqNum[1]);
+  b.putByte(record.type);
+  b.putByte(record.version.major);
+  b.putByte(record.version.minor);
+  b.putInt16(record.length);
+  b.putBytes(record.fragment.bytes());
+  hmac.update(b.getBytes());
+  return hmac.digest().getBytes();
+};
+
+/**
+ * Compresses the TLSPlaintext record into a TLSCompressed record using the
+ * deflate algorithm.
+ *
+ * @param c the TLS connection.
+ * @param record the TLSPlaintext record to compress.
+ * @param s the ConnectionState to use.
+ *
+ * @return true on success, false on failure.
+ */
+var deflate = function(c, record, s) {
+  var rval = false;
+
+  try {
+    var bytes = c.deflate(record.fragment.getBytes());
+    record.fragment = forge.util.createBuffer(bytes);
+    record.length = bytes.length;
+    rval = true;
+  } catch(ex) {
+    // deflate error, fail out
+  }
+
+  return rval;
+};
+
+/**
+ * Decompresses the TLSCompressed record into a TLSPlaintext record using the
+ * deflate algorithm.
+ *
+ * @param c the TLS connection.
+ * @param record the TLSCompressed record to decompress.
+ * @param s the ConnectionState to use.
+ *
+ * @return true on success, false on failure.
+ */
+var inflate = function(c, record, s) {
+  var rval = false;
+
+  try {
+    var bytes = c.inflate(record.fragment.getBytes());
+    record.fragment = forge.util.createBuffer(bytes);
+    record.length = bytes.length;
+    rval = true;
+  } catch(ex) {
+    // inflate error, fail out
+  }
+
+  return rval;
+};
+
+/**
+ * Reads a TLS variable-length vector from a byte buffer.
+ *
+ * Variable-length vectors are defined by specifying a subrange of legal
+ * lengths, inclusively, using the notation <floor..ceiling>. When these are
+ * encoded, the actual length precedes the vector's contents in the byte
+ * stream. The length will be in the form of a number consuming as many bytes
+ * as required to hold the vector's specified maximum (ceiling) length. A
+ * variable-length vector with an actual length field of zero is referred to
+ * as an empty vector.
+ *
+ * @param b the byte buffer.
+ * @param lenBytes the number of bytes required to store the length.
+ *
+ * @return the resulting byte buffer.
+ */
+var readVector = function(b, lenBytes) {
+  var len = 0;
+  switch(lenBytes) {
+  case 1:
+    len = b.getByte();
+    break;
+  case 2:
+    len = b.getInt16();
+    break;
+  case 3:
+    len = b.getInt24();
+    break;
+  case 4:
+    len = b.getInt32();
+    break;
+  }
+
+  // read vector bytes into a new buffer
+  return forge.util.createBuffer(b.getBytes(len));
+};
+
+/**
+ * Writes a TLS variable-length vector to a byte buffer.
+ *
+ * @param b the byte buffer.
+ * @param lenBytes the number of bytes required to store the length.
+ * @param v the byte buffer vector.
+ */
+var writeVector = function(b, lenBytes, v) {
+  // encode length at the start of the vector, where the number of bytes for
+  // the length is the maximum number of bytes it would take to encode the
+  // vector's ceiling
+  b.putInt(v.length(), lenBytes << 3);
+  b.putBuffer(v);
+};
+
+/**
+ * The tls implementation.
+ */
+var tls = {};
+
+/**
+ * Version: TLS 1.2 = 3.3, TLS 1.1 = 3.2, TLS 1.0 = 3.1. Both TLS 1.1 and
+ * TLS 1.2 were still too new (ie: openSSL didn't implement them) at the time
+ * of this implementation so TLS 1.0 was implemented instead.
+ */
+tls.Versions = {
+  TLS_1_0: {major: 3, minor: 1},
+  TLS_1_1: {major: 3, minor: 2},
+  TLS_1_2: {major: 3, minor: 3}
+};
+tls.SupportedVersions = [
+  tls.Versions.TLS_1_1,
+  tls.Versions.TLS_1_0
+];
+tls.Version = tls.SupportedVersions[0];
+
+/**
+ * Maximum fragment size. True maximum is 16384, but we fragment before that
+ * to allow for unusual small increases during compression.
+ */
+tls.MaxFragment = 16384 - 1024;
+
+/**
+ * Whether this entity is considered the "client" or "server".
+ * enum { server, client } ConnectionEnd;
+ */
+tls.ConnectionEnd = {
+  server: 0,
+  client: 1
+};
+
+/**
+ * Pseudo-random function algorithm used to generate keys from the master
+ * secret.
+ * enum { tls_prf_sha256 } PRFAlgorithm;
+ */
+tls.PRFAlgorithm = {
+  tls_prf_sha256: 0
+};
+
+/**
+ * Bulk encryption algorithms.
+ * enum { null, rc4, des3, aes } BulkCipherAlgorithm;
+ */
+tls.BulkCipherAlgorithm = {
+  none: null,
+  rc4: 0,
+  des3: 1,
+  aes: 2
+};
+
+/**
+ * Cipher types.
+ * enum { stream, block, aead } CipherType;
+ */
+tls.CipherType = {
+  stream: 0,
+  block: 1,
+  aead: 2
+};
+
+/**
+ * MAC (Message Authentication Code) algorithms.
+ * enum { null, hmac_md5, hmac_sha1, hmac_sha256,
+ *   hmac_sha384, hmac_sha512} MACAlgorithm;
+ */
+tls.MACAlgorithm = {
+  none: null,
+  hmac_md5: 0,
+  hmac_sha1: 1,
+  hmac_sha256: 2,
+  hmac_sha384: 3,
+  hmac_sha512: 4
+};
+
+/**
+ * Compression algorithms.
+ * enum { null(0), deflate(1), (255) } CompressionMethod;
+ */
+tls.CompressionMethod = {
+  none: 0,
+  deflate: 1
+};
+
+/**
+ * TLS record content types.
+ * enum {
+ *   change_cipher_spec(20), alert(21), handshake(22),
+ *   application_data(23), (255)
+ * } ContentType;
+ */
+tls.ContentType = {
+  change_cipher_spec: 20,
+  alert: 21,
+  handshake: 22,
+  application_data: 23,
+  heartbeat: 24
+};
+
+/**
+ * TLS handshake types.
+ * enum {
+ *   hello_request(0), client_hello(1), server_hello(2),
+ *   certificate(11), server_key_exchange (12),
+ *   certificate_request(13), server_hello_done(14),
+ *   certificate_verify(15), client_key_exchange(16),
+ *   finished(20), (255)
+ * } HandshakeType;
+ */
+tls.HandshakeType = {
+  hello_request: 0,
+  client_hello: 1,
+  server_hello: 2,
+  certificate: 11,
+  server_key_exchange: 12,
+  certificate_request: 13,
+  server_hello_done: 14,
+  certificate_verify: 15,
+  client_key_exchange: 16,
+  finished: 20
+};
+
+/**
+ * TLS Alert Protocol.
+ *
+ * enum { warning(1), fatal(2), (255) } AlertLevel;
+ *
+ * enum {
+ *   close_notify(0),
+ *   unexpected_message(10),
+ *   bad_record_mac(20),
+ *   decryption_failed(21),
+ *   record_overflow(22),
+ *   decompression_failure(30),
+ *   handshake_failure(40),
+ *   bad_certificate(42),
+ *   unsupported_certificate(43),
+ *   certificate_revoked(44),
+ *   certificate_expired(45),
+ *   certificate_unknown(46),
+ *   illegal_parameter(47),
+ *   unknown_ca(48),
+ *   access_denied(49),
+ *   decode_error(50),
+ *   decrypt_error(51),
+ *   export_restriction(60),
+ *   protocol_version(70),
+ *   insufficient_security(71),
+ *   internal_error(80),
+ *   user_canceled(90),
+ *   no_renegotiation(100),
+ *   (255)
+ * } AlertDescription;
+ *
+ * struct {
+ *   AlertLevel level;
+ *   AlertDescription description;
+ * } Alert;
+ */
+tls.Alert = {};
+tls.Alert.Level = {
+  warning: 1,
+  fatal: 2
+};
+tls.Alert.Description = {
+  close_notify: 0,
+  unexpected_message: 10,
+  bad_record_mac: 20,
+  decryption_failed: 21,
+  record_overflow: 22,
+  decompression_failure: 30,
+  handshake_failure: 40,
+  bad_certificate: 42,
+  unsupported_certificate: 43,
+  certificate_revoked: 44,
+  certificate_expired: 45,
+  certificate_unknown: 46,
+  illegal_parameter: 47,
+  unknown_ca: 48,
+  access_denied: 49,
+  decode_error: 50,
+  decrypt_error: 51,
+  export_restriction: 60,
+  protocol_version: 70,
+  insufficient_security: 71,
+  internal_error: 80,
+  user_canceled: 90,
+  no_renegotiation: 100
+};
+
+/**
+ * TLS Heartbeat Message types.
+ * enum {
+ *   heartbeat_request(1),
+ *   heartbeat_response(2),
+ *   (255)
+ * } HeartbeatMessageType;
+ */
+tls.HeartbeatMessageType = {
+  heartbeat_request: 1,
+  heartbeat_response: 2
+};
+
+/**
+ * Supported cipher suites.
+ */
+tls.CipherSuites = {};
+
+/**
+ * Gets a supported cipher suite from its 2 byte ID.
+ *
+ * @param twoBytes two bytes in a string.
+ *
+ * @return the matching supported cipher suite or null.
+ */
+tls.getCipherSuite = function(twoBytes) {
+  var rval = null;
+  for(var key in tls.CipherSuites) {
+    var cs = tls.CipherSuites[key];
+    if(cs.id[0] === twoBytes.charCodeAt(0) &&
+      cs.id[1] === twoBytes.charCodeAt(1)) {
+      rval = cs;
+      break;
+    }
+  }
+  return rval;
+};
+
+/**
+ * Called when an unexpected record is encountered.
+ *
+ * @param c the connection.
+ * @param record the record.
+ */
+tls.handleUnexpected = function(c, record) {
+  // if connection is client and closed, ignore unexpected messages
+  var ignore = (!c.open && c.entity === tls.ConnectionEnd.client);
+  if(!ignore) {
+    c.error(c, {
+      message: 'Unexpected message. Received TLS record out of order.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.unexpected_message
+      }
+    });
+  }
+};
+
+/**
+ * Called when a client receives a HelloRequest record.
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleHelloRequest = function(c, record, length) {
+  // ignore renegotiation requests from the server during a handshake, but
+  // if handshaking, send a warning alert that renegotiation is denied
+  if(!c.handshaking && c.handshakes > 0) {
+    // send alert warning
+    tls.queue(c, tls.createAlert(c, {
+       level: tls.Alert.Level.warning,
+       description: tls.Alert.Description.no_renegotiation
+    }));
+    tls.flush(c);
+  }
+
+  // continue
+  c.process();
+};
+
+/**
+ * Parses a hello message from a ClientHello or ServerHello record.
+ *
+ * @param record the record to parse.
+ *
+ * @return the parsed message.
+ */
+tls.parseHelloMessage = function(c, record, length) {
+  var msg = null;
+
+  var client = (c.entity === tls.ConnectionEnd.client);
+
+  // minimum of 38 bytes in message
+  if(length < 38) {
+    c.error(c, {
+      message: client ?
+        'Invalid ServerHello message. Message too short.' :
+        'Invalid ClientHello message. Message too short.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.illegal_parameter
+      }
+    });
+  } else {
+    // use 'remaining' to calculate # of remaining bytes in the message
+    var b = record.fragment;
+    var remaining = b.length();
+    msg = {
+      version: {
+        major: b.getByte(),
+        minor: b.getByte()
+      },
+      random: forge.util.createBuffer(b.getBytes(32)),
+      session_id: readVector(b, 1),
+      extensions: []
+    };
+    if(client) {
+      msg.cipher_suite = b.getBytes(2);
+      msg.compression_method = b.getByte();
+    } else {
+      msg.cipher_suites = readVector(b, 2);
+      msg.compression_methods = readVector(b, 1);
+    }
+
+    // read extensions if there are any bytes left in the message
+    remaining = length - (remaining - b.length());
+    if(remaining > 0) {
+      // parse extensions
+      var exts = readVector(b, 2);
+      while(exts.length() > 0) {
+        msg.extensions.push({
+          type: [exts.getByte(), exts.getByte()],
+          data: readVector(exts, 2)
+        });
+      }
+
+      // TODO: make extension support modular
+      if(!client) {
+        for(var i = 0; i < msg.extensions.length; ++i) {
+          var ext = msg.extensions[i];
+
+          // support SNI extension
+          if(ext.type[0] === 0x00 && ext.type[1] === 0x00) {
+            // get server name list
+            var snl = readVector(ext.data, 2);
+            while(snl.length() > 0) {
+              // read server name type
+              var snType = snl.getByte();
+
+              // only HostName type (0x00) is known, break out if
+              // another type is detected
+              if(snType !== 0x00) {
+                break;
+              }
+
+              // add host name to server name list
+              c.session.extensions.server_name.serverNameList.push(
+                readVector(snl, 2).getBytes());
+            }
+          }
+        }
+      }
+    }
+
+    // version already set, do not allow version change
+    if(c.session.version) {
+      if(msg.version.major !== c.session.version.major ||
+        msg.version.minor !== c.session.version.minor) {
+        return c.error(c, {
+          message: 'TLS version change is disallowed during renegotiation.',
+          send: true,
+          alert: {
+            level: tls.Alert.Level.fatal,
+            description: tls.Alert.Description.protocol_version
+          }
+        });
+      }
+    }
+
+    // get the chosen (ServerHello) cipher suite
+    if(client) {
+      // FIXME: should be checking configured acceptable cipher suites
+      c.session.cipherSuite = tls.getCipherSuite(msg.cipher_suite);
+    } else {
+      // get a supported preferred (ClientHello) cipher suite
+      // choose the first supported cipher suite
+      var tmp = forge.util.createBuffer(msg.cipher_suites.bytes());
+      while(tmp.length() > 0) {
+        // FIXME: should be checking configured acceptable suites
+        // cipher suites take up 2 bytes
+        c.session.cipherSuite = tls.getCipherSuite(tmp.getBytes(2));
+        if(c.session.cipherSuite !== null) {
+          break;
+        }
+      }
+    }
+
+    // cipher suite not supported
+    if(c.session.cipherSuite === null) {
+      return c.error(c, {
+        message: 'No cipher suites in common.',
+        send: true,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          description: tls.Alert.Description.handshake_failure
+        },
+        cipherSuite: forge.util.bytesToHex(msg.cipher_suite)
+      });
+    }
+
+    // TODO: handle compression methods
+    if(client) {
+      c.session.compressionMethod = msg.compression_method;
+    } else {
+      // no compression
+      c.session.compressionMethod = tls.CompressionMethod.none;
+    }
+  }
+
+  return msg;
+};
+
+/**
+ * Creates security parameters for the given connection based on the given
+ * hello message.
+ *
+ * @param c the TLS connection.
+ * @param msg the hello message.
+ */
+tls.createSecurityParameters = function(c, msg) {
+  /* Note: security params are from TLS 1.2, some values like prf_algorithm
+  are ignored for TLS 1.0/1.1 and the builtin as specified in the spec is
+  used. */
+
+  // TODO: handle other options from server when more supported
+
+  // get client and server randoms
+  var client = (c.entity === tls.ConnectionEnd.client);
+  var msgRandom = msg.random.bytes();
+  var cRandom = client ? c.session.sp.client_random : msgRandom;
+  var sRandom = client ? msgRandom : tls.createRandom().getBytes();
+
+  // create new security parameters
+  c.session.sp = {
+    entity: c.entity,
+    prf_algorithm: tls.PRFAlgorithm.tls_prf_sha256,
+    bulk_cipher_algorithm: null,
+    cipher_type: null,
+    enc_key_length: null,
+    block_length: null,
+    fixed_iv_length: null,
+    record_iv_length: null,
+    mac_algorithm: null,
+    mac_length: null,
+    mac_key_length: null,
+    compression_algorithm: c.session.compressionMethod,
+    pre_master_secret: null,
+    master_secret: null,
+    client_random: cRandom,
+    server_random: sRandom
+  };
+};
+
+/**
+ * Called when a client receives a ServerHello record.
+ *
+ * When a ServerHello message will be sent:
+ *   The server will send this message in response to a client hello message
+ *   when it was able to find an acceptable set of algorithms. If it cannot
+ *   find such a match, it will respond with a handshake failure alert.
+ *
+ * uint24 length;
+ * struct {
+ *   ProtocolVersion server_version;
+ *   Random random;
+ *   SessionID session_id;
+ *   CipherSuite cipher_suite;
+ *   CompressionMethod compression_method;
+ *   select(extensions_present) {
+ *     case false:
+ *       struct {};
+ *     case true:
+ *       Extension extensions<0..2^16-1>;
+ *   };
+ * } ServerHello;
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleServerHello = function(c, record, length) {
+  var msg = tls.parseHelloMessage(c, record, length);
+  if(c.fail) {
+    return;
+  }
+
+  // ensure server version is compatible
+  if(msg.version.minor <= c.version.minor) {
+    c.version.minor = msg.version.minor;
+  } else {
+    return c.error(c, {
+      message: 'Incompatible TLS version.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.protocol_version
+      }
+    });
+  }
+
+  // indicate session version has been set
+  c.session.version = c.version;
+
+  // get the session ID from the message
+  var sessionId = msg.session_id.bytes();
+
+  // if the session ID is not blank and matches the cached one, resume
+  // the session
+  if(sessionId.length > 0 && sessionId === c.session.id) {
+    // resuming session, expect a ChangeCipherSpec next
+    c.expect = SCC;
+    c.session.resuming = true;
+
+    // get new server random
+    c.session.sp.server_random = msg.random.bytes();
+  } else {
+    // not resuming, expect a server Certificate message next
+    c.expect = SCE;
+    c.session.resuming = false;
+
+    // create new security parameters
+    tls.createSecurityParameters(c, msg);
+  }
+
+  // set new session ID
+  c.session.id = sessionId;
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a server receives a ClientHello record.
+ *
+ * When a ClientHello message will be sent:
+ *   When a client first connects to a server it is required to send the
+ *   client hello as its first message. The client can also send a client
+ *   hello in response to a hello request or on its own initiative in order
+ *   to renegotiate the security parameters in an existing connection.
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleClientHello = function(c, record, length) {
+  var msg = tls.parseHelloMessage(c, record, length);
+  if(c.fail) {
+    return;
+  }
+
+  // get the session ID from the message
+  var sessionId = msg.session_id.bytes();
+
+  // see if the given session ID is in the cache
+  var session = null;
+  if(c.sessionCache) {
+    session = c.sessionCache.getSession(sessionId);
+    if(session === null) {
+      // session ID not found
+      sessionId = '';
+    } else if(session.version.major !== msg.version.major ||
+      session.version.minor > msg.version.minor) {
+      // if session version is incompatible with client version, do not resume
+      session = null;
+      sessionId = '';
+    }
+  }
+
+  // no session found to resume, generate a new session ID
+  if(sessionId.length === 0) {
+    sessionId = forge.random.getBytes(32);
+  }
+
+  // update session
+  c.session.id = sessionId;
+  c.session.clientHelloVersion = msg.version;
+  c.session.sp = {};
+  if(session) {
+    // use version and security parameters from resumed session
+    c.version = c.session.version = session.version;
+    c.session.sp = session.sp;
+  } else {
+    // use highest compatible minor version
+    var version;
+    for(var i = 1; i < tls.SupportedVersions.length; ++i) {
+      version = tls.SupportedVersions[i];
+      if(version.minor <= msg.version.minor) {
+        break;
+      }
+    }
+    c.version = {major: version.major, minor: version.minor};
+    c.session.version = c.version;
+  }
+
+  // if a session is set, resume it
+  if(session !== null) {
+    // resuming session, expect a ChangeCipherSpec next
+    c.expect = CCC;
+    c.session.resuming = true;
+
+    // get new client random
+    c.session.sp.client_random = msg.random.bytes();
+  } else {
+    // not resuming, expect a Certificate or ClientKeyExchange
+    c.expect = (c.verifyClient !== false) ? CCE : CKE;
+    c.session.resuming = false;
+
+    // create new security parameters
+    tls.createSecurityParameters(c, msg);
+  }
+
+  // connection now open
+  c.open = true;
+
+  // queue server hello
+  tls.queue(c, tls.createRecord(c, {
+    type: tls.ContentType.handshake,
+    data: tls.createServerHello(c)
+  }));
+
+  if(c.session.resuming) {
+    // queue change cipher spec message
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.change_cipher_spec,
+      data: tls.createChangeCipherSpec()
+    }));
+
+    // create pending state
+    c.state.pending = tls.createConnectionState(c);
+
+    // change current write state to pending write state
+    c.state.current.write = c.state.pending.write;
+
+    // queue finished
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.handshake,
+      data: tls.createFinished(c)
+    }));
+  } else {
+    // queue server certificate
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.handshake,
+      data: tls.createCertificate(c)
+    }));
+
+    if(!c.fail) {
+      // queue server key exchange
+      tls.queue(c, tls.createRecord(c, {
+        type: tls.ContentType.handshake,
+        data: tls.createServerKeyExchange(c)
+      }));
+
+      // request client certificate if set
+      if(c.verifyClient !== false) {
+        // queue certificate request
+        tls.queue(c, tls.createRecord(c, {
+          type: tls.ContentType.handshake,
+          data: tls.createCertificateRequest(c)
+        }));
+      }
+
+      // queue server hello done
+      tls.queue(c, tls.createRecord(c, {
+        type: tls.ContentType.handshake,
+        data: tls.createServerHelloDone(c)
+      }));
+    }
+  }
+
+  // send records
+  tls.flush(c);
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a client receives a Certificate record.
+ *
+ * When this message will be sent:
+ *   The server must send a certificate whenever the agreed-upon key exchange
+ *   method is not an anonymous one. This message will always immediately
+ *   follow the server hello message.
+ *
+ * Meaning of this message:
+ *   The certificate type must be appropriate for the selected cipher suite's
+ *   key exchange algorithm, and is generally an X.509v3 certificate. It must
+ *   contain a key which matches the key exchange method, as follows. Unless
+ *   otherwise specified, the signing algorithm for the certificate must be
+ *   the same as the algorithm for the certificate key. Unless otherwise
+ *   specified, the public key may be of any length.
+ *
+ * opaque ASN.1Cert<1..2^24-1>;
+ * struct {
+ *   ASN.1Cert certificate_list<1..2^24-1>;
+ * } Certificate;
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleCertificate = function(c, record, length) {
+  // minimum of 3 bytes in message
+  if(length < 3) {
+    return c.error(c, {
+      message: 'Invalid Certificate message. Message too short.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.illegal_parameter
+      }
+    });
+  }
+
+  var b = record.fragment;
+  var msg = {
+    certificate_list: readVector(b, 3)
+  };
+
+  /* The sender's certificate will be first in the list (chain), each
+    subsequent one that follows will certify the previous one, but root
+    certificates (self-signed) that specify the certificate authority may
+    be omitted under the assumption that clients must already possess it. */
+  var cert, asn1;
+  var certs = [];
+  try {
+    while(msg.certificate_list.length() > 0) {
+      // each entry in msg.certificate_list is a vector with 3 len bytes
+      cert = readVector(msg.certificate_list, 3);
+      asn1 = forge.asn1.fromDer(cert);
+      cert = forge.pki.certificateFromAsn1(asn1, true);
+      certs.push(cert);
+    }
+  } catch(ex) {
+    return c.error(c, {
+      message: 'Could not parse certificate list.',
+      cause: ex,
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.bad_certificate
+      }
+    });
+  }
+
+  // ensure at least 1 certificate was provided if in client-mode
+  // or if verifyClient was set to true to require a certificate
+  // (as opposed to 'optional')
+  var client = (c.entity === tls.ConnectionEnd.client);
+  if((client || c.verifyClient === true) && certs.length === 0) {
+    // error, no certificate
+    c.error(c, {
+      message: client ?
+        'No server certificate provided.' :
+        'No client certificate provided.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.illegal_parameter
+      }
+    });
+  } else if(certs.length === 0) {
+    // no certs to verify
+    // expect a ServerKeyExchange or ClientKeyExchange message next
+    c.expect = client ? SKE : CKE;
+  } else {
+    // save certificate in session
+    if(client) {
+      c.session.serverCertificate = certs[0];
+    } else {
+      c.session.clientCertificate = certs[0];
+    }
+
+    if(tls.verifyCertificateChain(c, certs)) {
+      // expect a ServerKeyExchange or ClientKeyExchange message next
+      c.expect = client ? SKE : CKE;
+    }
+  }
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a client receives a ServerKeyExchange record.
+ *
+ * When this message will be sent:
+ *   This message will be sent immediately after the server certificate
+ *   message (or the server hello message, if this is an anonymous
+ *   negotiation).
+ *
+ *   The server key exchange message is sent by the server only when the
+ *   server certificate message (if sent) does not contain enough data to
+ *   allow the client to exchange a premaster secret.
+ *
+ * Meaning of this message:
+ *   This message conveys cryptographic information to allow the client to
+ *   communicate the premaster secret: either an RSA public key to encrypt
+ *   the premaster secret with, or a Diffie-Hellman public key with which the
+ *   client can complete a key exchange (with the result being the premaster
+ *   secret.)
+ *
+ * enum {
+ *   dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa
+ * } KeyExchangeAlgorithm;
+ *
+ * struct {
+ *   opaque dh_p<1..2^16-1>;
+ *   opaque dh_g<1..2^16-1>;
+ *   opaque dh_Ys<1..2^16-1>;
+ * } ServerDHParams;
+ *
+ * struct {
+ *   select(KeyExchangeAlgorithm) {
+ *     case dh_anon:
+ *       ServerDHParams params;
+ *     case dhe_dss:
+ *     case dhe_rsa:
+ *       ServerDHParams params;
+ *       digitally-signed struct {
+ *         opaque client_random[32];
+ *         opaque server_random[32];
+ *         ServerDHParams params;
+ *       } signed_params;
+ *     case rsa:
+ *     case dh_dss:
+ *     case dh_rsa:
+ *       struct {};
+ *   };
+ * } ServerKeyExchange;
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleServerKeyExchange = function(c, record, length) {
+  // this implementation only supports RSA, no Diffie-Hellman support
+  // so any length > 0 is invalid
+  if(length > 0) {
+    return c.error(c, {
+      message: 'Invalid key parameters. Only RSA is supported.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.unsupported_certificate
+      }
+    });
+  }
+
+  // expect an optional CertificateRequest message next
+  c.expect = SCR;
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a client receives a ClientKeyExchange record.
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleClientKeyExchange = function(c, record, length) {
+  // this implementation only supports RSA, no Diffie-Hellman support
+  // so any length < 48 is invalid
+  if(length < 48) {
+    return c.error(c, {
+      message: 'Invalid key parameters. Only RSA is supported.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.unsupported_certificate
+      }
+    });
+  }
+
+  var b = record.fragment;
+  var msg = {
+    enc_pre_master_secret: readVector(b, 2).getBytes()
+  };
+
+  // do rsa decryption
+  var privateKey = null;
+  if(c.getPrivateKey) {
+    try {
+      privateKey = c.getPrivateKey(c, c.session.serverCertificate);
+      privateKey = forge.pki.privateKeyFromPem(privateKey);
+    } catch(ex) {
+      c.error(c, {
+        message: 'Could not get private key.',
+        cause: ex,
+        send: true,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          description: tls.Alert.Description.internal_error
+        }
+      });
+    }
+  }
+
+  if(privateKey === null) {
+    return c.error(c, {
+      message: 'No private key set.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.internal_error
+      }
+    });
+  }
+
+  try {
+    // decrypt 48-byte pre-master secret
+    var sp = c.session.sp;
+    sp.pre_master_secret = privateKey.decrypt(msg.enc_pre_master_secret);
+
+    // ensure client hello version matches first 2 bytes
+    var version = c.session.clientHelloVersion;
+    if(version.major !== sp.pre_master_secret.charCodeAt(0) ||
+      version.minor !== sp.pre_master_secret.charCodeAt(1)) {
+      // error, do not send alert (see BLEI attack below)
+      throw new Error('TLS version rollback attack detected.');
+    }
+  } catch(ex) {
+    /* Note: Daniel Bleichenbacher [BLEI] can be used to attack a
+      TLS server which is using PKCS#1 encoded RSA, so instead of
+      failing here, we generate 48 random bytes and use that as
+      the pre-master secret. */
+    sp.pre_master_secret = forge.random.getBytes(48);
+  }
+
+  // expect a CertificateVerify message if a Certificate was received that
+  // does not have fixed Diffie-Hellman params, otherwise expect
+  // ChangeCipherSpec
+  c.expect = CCC;
+  if(c.session.clientCertificate !== null) {
+    // only RSA support, so expect CertificateVerify
+    // TODO: support Diffie-Hellman
+    c.expect = CCV;
+  }
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a client receives a CertificateRequest record.
+ *
+ * When this message will be sent:
+ *   A non-anonymous server can optionally request a certificate from the
+ *   client, if appropriate for the selected cipher suite. This message, if
+ *   sent, will immediately follow the Server Key Exchange message (if it is
+ *   sent; otherwise, the Server Certificate message).
+ *
+ * enum {
+ *   rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+ *   rsa_ephemeral_dh_RESERVED(5), dss_ephemeral_dh_RESERVED(6),
+ *   fortezza_dms_RESERVED(20), (255)
+ * } ClientCertificateType;
+ *
+ * opaque DistinguishedName<1..2^16-1>;
+ *
+ * struct {
+ *   ClientCertificateType certificate_types<1..2^8-1>;
+ *   SignatureAndHashAlgorithm supported_signature_algorithms<2^16-1>;
+ *   DistinguishedName certificate_authorities<0..2^16-1>;
+ * } CertificateRequest;
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleCertificateRequest = function(c, record, length) {
+  // minimum of 3 bytes in message
+  if(length < 3) {
+    return c.error(c, {
+      message: 'Invalid CertificateRequest. Message too short.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.illegal_parameter
+      }
+    });
+  }
+
+  // TODO: TLS 1.2+ has different format including
+  // SignatureAndHashAlgorithm after cert types
+  var b = record.fragment;
+  var msg = {
+    certificate_types: readVector(b, 1),
+    certificate_authorities: readVector(b, 2)
+  };
+
+  // save certificate request in session
+  c.session.certificateRequest = msg;
+
+  // expect a ServerHelloDone message next
+  c.expect = SHD;
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a server receives a CertificateVerify record.
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleCertificateVerify = function(c, record, length) {
+  if(length < 2) {
+    return c.error(c, {
+      message: 'Invalid CertificateVerify. Message too short.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.illegal_parameter
+      }
+    });
+  }
+
+  // rewind to get full bytes for message so it can be manually
+  // digested below (special case for CertificateVerify messages because
+  // they must be digested *after* handling as opposed to all others)
+  var b = record.fragment;
+  b.read -= 4;
+  var msgBytes = b.bytes();
+  b.read += 4;
+
+  var msg = {
+    signature: readVector(b, 2).getBytes()
+  };
+
+  // TODO: add support for DSA
+
+  // generate data to verify
+  var verify = forge.util.createBuffer();
+  verify.putBuffer(c.session.md5.digest());
+  verify.putBuffer(c.session.sha1.digest());
+  verify = verify.getBytes();
+
+  try {
+    var cert = c.session.clientCertificate;
+    /*b = forge.pki.rsa.decrypt(
+      msg.signature, cert.publicKey, true, verify.length);
+    if(b !== verify) {*/
+    if(!cert.publicKey.verify(verify, msg.signature, 'NONE')) {
+      throw new Error('CertificateVerify signature does not match.');
+    }
+
+    // digest message now that it has been handled
+    c.session.md5.update(msgBytes);
+    c.session.sha1.update(msgBytes);
+  } catch(ex) {
+    return c.error(c, {
+      message: 'Bad signature in CertificateVerify.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.handshake_failure
+      }
+    });
+  }
+
+  // expect ChangeCipherSpec
+  c.expect = CCC;
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a client receives a ServerHelloDone record.
+ *
+ * When this message will be sent:
+ *   The server hello done message is sent by the server to indicate the end
+ *   of the server hello and associated messages. After sending this message
+ *   the server will wait for a client response.
+ *
+ * Meaning of this message:
+ *   This message means that the server is done sending messages to support
+ *   the key exchange, and the client can proceed with its phase of the key
+ *   exchange.
+ *
+ *   Upon receipt of the server hello done message the client should verify
+ *   that the server provided a valid certificate if required and check that
+ *   the server hello parameters are acceptable.
+ *
+ * struct {} ServerHelloDone;
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleServerHelloDone = function(c, record, length) {
+  // len must be 0 bytes
+  if(length > 0) {
+    return c.error(c, {
+      message: 'Invalid ServerHelloDone message. Invalid length.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.record_overflow
+      }
+    });
+  }
+
+  if(c.serverCertificate === null) {
+    // no server certificate was provided
+    var error = {
+      message: 'No server certificate provided. Not enough security.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.insufficient_security
+      }
+    };
+
+    // call application callback
+    var depth = 0;
+    var ret = c.verify(c, error.alert.description, depth, []);
+    if(ret !== true) {
+      // check for custom alert info
+      if(ret || ret === 0) {
+        // set custom message and alert description
+        if(typeof ret === 'object' && !forge.util.isArray(ret)) {
+          if(ret.message) {
+            error.message = ret.message;
+          }
+          if(ret.alert) {
+            error.alert.description = ret.alert;
+          }
+        } else if(typeof ret === 'number') {
+          // set custom alert description
+          error.alert.description = ret;
+        }
+      }
+
+      // send error
+      return c.error(c, error);
+    }
+  }
+
+  // create client certificate message if requested
+  if(c.session.certificateRequest !== null) {
+    record = tls.createRecord(c, {
+      type: tls.ContentType.handshake,
+      data: tls.createCertificate(c)
+    });
+    tls.queue(c, record);
+  }
+
+  // create client key exchange message
+  record = tls.createRecord(c, {
+     type: tls.ContentType.handshake,
+     data: tls.createClientKeyExchange(c)
+  });
+  tls.queue(c, record);
+
+  // expect no messages until the following callback has been called
+  c.expect = SER;
+
+  // create callback to handle client signature (for client-certs)
+  var callback = function(c, signature) {
+    if(c.session.certificateRequest !== null &&
+      c.session.clientCertificate !== null) {
+      // create certificate verify message
+      tls.queue(c, tls.createRecord(c, {
+        type: tls.ContentType.handshake,
+        data: tls.createCertificateVerify(c, signature)
+      }));
+    }
+
+    // create change cipher spec message
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.change_cipher_spec,
+      data: tls.createChangeCipherSpec()
+    }));
+
+    // create pending state
+    c.state.pending = tls.createConnectionState(c);
+
+    // change current write state to pending write state
+    c.state.current.write = c.state.pending.write;
+
+    // create finished message
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.handshake,
+      data: tls.createFinished(c)
+    }));
+
+    // expect a server ChangeCipherSpec message next
+    c.expect = SCC;
+
+    // send records
+    tls.flush(c);
+
+    // continue
+    c.process();
+  };
+
+  // if there is no certificate request or no client certificate, do
+  // callback immediately
+  if(c.session.certificateRequest === null ||
+    c.session.clientCertificate === null) {
+    return callback(c, null);
+  }
+
+  // otherwise get the client signature
+  tls.getClientSignature(c, callback);
+};
+
+/**
+ * Called when a ChangeCipherSpec record is received.
+ *
+ * @param c the connection.
+ * @param record the record.
+ */
+tls.handleChangeCipherSpec = function(c, record) {
+  if(record.fragment.getByte() !== 0x01) {
+    return c.error(c, {
+      message: 'Invalid ChangeCipherSpec message received.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.illegal_parameter
+      }
+    });
+  }
+
+  // create pending state if:
+  // 1. Resuming session in client mode OR
+  // 2. NOT resuming session in server mode
+  var client = (c.entity === tls.ConnectionEnd.client);
+  if((c.session.resuming && client) || (!c.session.resuming && !client)) {
+    c.state.pending = tls.createConnectionState(c);
+  }
+
+  // change current read state to pending read state
+  c.state.current.read = c.state.pending.read;
+
+  // clear pending state if:
+  // 1. NOT resuming session in client mode OR
+  // 2. resuming a session in server mode
+  if((!c.session.resuming && client) || (c.session.resuming && !client)) {
+    c.state.pending = null;
+  }
+
+  // expect a Finished record next
+  c.expect = client ? SFI : CFI;
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a Finished record is received.
+ *
+ * When this message will be sent:
+ *   A finished message is always sent immediately after a change
+ *   cipher spec message to verify that the key exchange and
+ *   authentication processes were successful. It is essential that a
+ *   change cipher spec message be received between the other
+ *   handshake messages and the Finished message.
+ *
+ * Meaning of this message:
+ *   The finished message is the first protected with the just-
+ *   negotiated algorithms, keys, and secrets. Recipients of finished
+ *   messages must verify that the contents are correct.  Once a side
+ *   has sent its Finished message and received and validated the
+ *   Finished message from its peer, it may begin to send and receive
+ *   application data over the connection.
+ *
+ * struct {
+ *   opaque verify_data[verify_data_length];
+ * } Finished;
+ *
+ * verify_data
+ *   PRF(master_secret, finished_label, Hash(handshake_messages))
+ *     [0..verify_data_length-1];
+ *
+ * finished_label
+ *   For Finished messages sent by the client, the string
+ *   "client finished". For Finished messages sent by the server, the
+ *   string "server finished".
+ *
+ * verify_data_length depends on the cipher suite. If it is not specified
+ * by the cipher suite, then it is 12. Versions of TLS < 1.2 always used
+ * 12 bytes.
+ *
+ * @param c the connection.
+ * @param record the record.
+ * @param length the length of the handshake message.
+ */
+tls.handleFinished = function(c, record, length) {
+  // rewind to get full bytes for message so it can be manually
+  // digested below (special case for Finished messages because they
+  // must be digested *after* handling as opposed to all others)
+  var b = record.fragment;
+  b.read -= 4;
+  var msgBytes = b.bytes();
+  b.read += 4;
+
+  // message contains only verify_data
+  var vd = record.fragment.getBytes();
+
+  // ensure verify data is correct
+  b = forge.util.createBuffer();
+  b.putBuffer(c.session.md5.digest());
+  b.putBuffer(c.session.sha1.digest());
+
+  // set label based on entity type
+  var client = (c.entity === tls.ConnectionEnd.client);
+  var label = client ? 'server finished' : 'client finished';
+
+  // TODO: determine prf function and verify length for TLS 1.2
+  var sp = c.session.sp;
+  var vdl = 12;
+  var prf = prf_TLS1;
+  b = prf(sp.master_secret, label, b.getBytes(), vdl);
+  if(b.getBytes() !== vd) {
+    return c.error(c, {
+      message: 'Invalid verify_data in Finished message.',
+      send: true,
+      alert: {
+        level: tls.Alert.Level.fatal,
+        description: tls.Alert.Description.decrypt_error
+      }
+    });
+  }
+
+  // digest finished message now that it has been handled
+  c.session.md5.update(msgBytes);
+  c.session.sha1.update(msgBytes);
+
+  // resuming session as client or NOT resuming session as server
+  if((c.session.resuming && client) || (!c.session.resuming && !client)) {
+    // create change cipher spec message
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.change_cipher_spec,
+      data: tls.createChangeCipherSpec()
+    }));
+
+    // change current write state to pending write state, clear pending
+    c.state.current.write = c.state.pending.write;
+    c.state.pending = null;
+
+    // create finished message
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.handshake,
+      data: tls.createFinished(c)
+    }));
+  }
+
+  // expect application data next
+  c.expect = client ? SAD : CAD;
+
+  // handshake complete
+  c.handshaking = false;
+  ++c.handshakes;
+
+  // save access to peer certificate
+  c.peerCertificate = client ?
+    c.session.serverCertificate : c.session.clientCertificate;
+
+  // send records
+  tls.flush(c);
+
+  // now connected
+  c.isConnected = true;
+  c.connected(c);
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when an Alert record is received.
+ *
+ * @param c the connection.
+ * @param record the record.
+ */
+tls.handleAlert = function(c, record) {
+  // read alert
+  var b = record.fragment;
+  var alert = {
+    level: b.getByte(),
+    description: b.getByte()
+  };
+
+  // TODO: consider using a table?
+  // get appropriate message
+  var msg;
+  switch(alert.description) {
+  case tls.Alert.Description.close_notify:
+    msg = 'Connection closed.';
+    break;
+  case tls.Alert.Description.unexpected_message:
+    msg = 'Unexpected message.';
+    break;
+  case tls.Alert.Description.bad_record_mac:
+    msg = 'Bad record MAC.';
+    break;
+  case tls.Alert.Description.decryption_failed:
+    msg = 'Decryption failed.';
+    break;
+  case tls.Alert.Description.record_overflow:
+    msg = 'Record overflow.';
+    break;
+  case tls.Alert.Description.decompression_failure:
+    msg = 'Decompression failed.';
+    break;
+  case tls.Alert.Description.handshake_failure:
+    msg = 'Handshake failure.';
+    break;
+  case tls.Alert.Description.bad_certificate:
+    msg = 'Bad certificate.';
+    break;
+  case tls.Alert.Description.unsupported_certificate:
+    msg = 'Unsupported certificate.';
+    break;
+  case tls.Alert.Description.certificate_revoked:
+    msg = 'Certificate revoked.';
+    break;
+  case tls.Alert.Description.certificate_expired:
+    msg = 'Certificate expired.';
+    break;
+  case tls.Alert.Description.certificate_unknown:
+    msg = 'Certificate unknown.';
+    break;
+  case tls.Alert.Description.illegal_parameter:
+    msg = 'Illegal parameter.';
+    break;
+  case tls.Alert.Description.unknown_ca:
+    msg = 'Unknown certificate authority.';
+    break;
+  case tls.Alert.Description.access_denied:
+    msg = 'Access denied.';
+    break;
+  case tls.Alert.Description.decode_error:
+    msg = 'Decode error.';
+    break;
+  case tls.Alert.Description.decrypt_error:
+    msg = 'Decrypt error.';
+    break;
+  case tls.Alert.Description.export_restriction:
+    msg = 'Export restriction.';
+    break;
+  case tls.Alert.Description.protocol_version:
+    msg = 'Unsupported protocol version.';
+    break;
+  case tls.Alert.Description.insufficient_security:
+    msg = 'Insufficient security.';
+    break;
+  case tls.Alert.Description.internal_error:
+    msg = 'Internal error.';
+    break;
+  case tls.Alert.Description.user_canceled:
+    msg = 'User canceled.';
+    break;
+  case tls.Alert.Description.no_renegotiation:
+    msg = 'Renegotiation not supported.';
+    break;
+  default:
+    msg = 'Unknown error.';
+    break;
+  }
+
+  // close connection on close_notify, not an error
+  if(alert.description === tls.Alert.Description.close_notify) {
+    return c.close();
+  }
+
+  // call error handler
+  c.error(c, {
+    message: msg,
+    send: false,
+    // origin is the opposite end
+    origin: (c.entity === tls.ConnectionEnd.client) ? 'server' : 'client',
+    alert: alert
+  });
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a Handshake record is received.
+ *
+ * @param c the connection.
+ * @param record the record.
+ */
+tls.handleHandshake = function(c, record) {
+  // get the handshake type and message length
+  var b = record.fragment;
+  var type = b.getByte();
+  var length = b.getInt24();
+
+  // see if the record fragment doesn't yet contain the full message
+  if(length > b.length()) {
+    // cache the record, clear its fragment, and reset the buffer read
+    // pointer before the type and length were read
+    c.fragmented = record;
+    record.fragment = forge.util.createBuffer();
+    b.read -= 4;
+
+    // continue
+    return c.process();
+  }
+
+  // full message now available, clear cache, reset read pointer to
+  // before type and length
+  c.fragmented = null;
+  b.read -= 4;
+
+  // save the handshake bytes for digestion after handler is found
+  // (include type and length of handshake msg)
+  var bytes = b.bytes(length + 4);
+
+  // restore read pointer
+  b.read += 4;
+
+  // handle expected message
+  if(type in hsTable[c.entity][c.expect]) {
+    // initialize server session
+    if(c.entity === tls.ConnectionEnd.server && !c.open && !c.fail) {
+      c.handshaking = true;
+      c.session = {
+        version: null,
+        extensions: {
+          server_name: {
+            serverNameList: []
+          }
+        },
+        cipherSuite: null,
+        compressionMethod: null,
+        serverCertificate: null,
+        clientCertificate: null,
+        md5: forge.md.md5.create(),
+        sha1: forge.md.sha1.create()
+      };
+    }
+
+    /* Update handshake messages digest. Finished and CertificateVerify
+      messages are not digested here. They can't be digested as part of
+      the verify_data that they contain. These messages are manually
+      digested in their handlers. HelloRequest messages are simply never
+      included in the handshake message digest according to spec. */
+    if(type !== tls.HandshakeType.hello_request &&
+      type !== tls.HandshakeType.certificate_verify &&
+      type !== tls.HandshakeType.finished) {
+      c.session.md5.update(bytes);
+      c.session.sha1.update(bytes);
+    }
+
+    // handle specific handshake type record
+    hsTable[c.entity][c.expect][type](c, record, length);
+  } else {
+    // unexpected record
+    tls.handleUnexpected(c, record);
+  }
+};
+
+/**
+ * Called when an ApplicationData record is received.
+ *
+ * @param c the connection.
+ * @param record the record.
+ */
+tls.handleApplicationData = function(c, record) {
+  // buffer data, notify that its ready
+  c.data.putBuffer(record.fragment);
+  c.dataReady(c);
+
+  // continue
+  c.process();
+};
+
+/**
+ * Called when a Heartbeat record is received.
+ *
+ * @param c the connection.
+ * @param record the record.
+ */
+tls.handleHeartbeat = function(c, record) {
+  // get the heartbeat type and payload
+  var b = record.fragment;
+  var type = b.getByte();
+  var length = b.getInt16();
+  var payload = b.getBytes(length);
+
+  if(type === tls.HeartbeatMessageType.heartbeat_request) {
+    // discard request during handshake or if length is too large
+    if(c.handshaking || length > payload.length) {
+      // continue
+      return c.process();
+    }
+    // retransmit payload
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.heartbeat,
+      data: tls.createHeartbeat(
+        tls.HeartbeatMessageType.heartbeat_response, payload)
+    }));
+    tls.flush(c);
+  } else if(type === tls.HeartbeatMessageType.heartbeat_response) {
+    // check payload against expected payload, discard heartbeat if no match
+    if(payload !== c.expectedHeartbeatPayload) {
+      // continue
+      return c.process();
+    }
+
+    // notify that a valid heartbeat was received
+    if(c.heartbeatReceived) {
+      c.heartbeatReceived(c, forge.util.createBuffer(payload));
+    }
+  }
+
+  // continue
+  c.process();
+};
+
+/**
+ * The transistional state tables for receiving TLS records. It maps the
+ * current TLS engine state and a received record to a function to handle the
+ * record and update the state.
+ *
+ * For instance, if the current state is SHE, then the TLS engine is expecting
+ * a ServerHello record. Once a record is received, the handler function is
+ * looked up using the state SHE and the record's content type.
+ *
+ * The resulting function will either be an error handler or a record handler.
+ * The function will take whatever action is appropriate and update the state
+ * for the next record.
+ *
+ * The states are all based on possible server record types. Note that the
+ * client will never specifically expect to receive a HelloRequest or an alert
+ * from the server so there is no state that reflects this. These messages may
+ * occur at any time.
+ *
+ * There are two tables for mapping states because there is a second tier of
+ * types for handshake messages. Once a record with a content type of handshake
+ * is received, the handshake record handler will look up the handshake type in
+ * the secondary map to get its appropriate handler.
+ *
+ * Valid message orders are as follows:
+ *
+ * =======================FULL HANDSHAKE======================
+ * Client                                               Server
+ *
+ * ClientHello                  -------->
+ *                                                 ServerHello
+ *                                                Certificate*
+ *                                          ServerKeyExchange*
+ *                                         CertificateRequest*
+ *                              <--------      ServerHelloDone
+ * Certificate*
+ * ClientKeyExchange
+ * CertificateVerify*
+ * [ChangeCipherSpec]
+ * Finished                     -------->
+ *                                          [ChangeCipherSpec]
+ *                              <--------             Finished
+ * Application Data             <------->     Application Data
+ *
+ * =====================SESSION RESUMPTION=====================
+ * Client                                                Server
+ *
+ * ClientHello                   -------->
+ *                                                  ServerHello
+ *                                           [ChangeCipherSpec]
+ *                               <--------             Finished
+ * [ChangeCipherSpec]
+ * Finished                      -------->
+ * Application Data              <------->     Application Data
+ */
+// client expect states (indicate which records are expected to be received)
+var SHE = 0; // rcv server hello
+var SCE = 1; // rcv server certificate
+var SKE = 2; // rcv server key exchange
+var SCR = 3; // rcv certificate request
+var SHD = 4; // rcv server hello done
+var SCC = 5; // rcv change cipher spec
+var SFI = 6; // rcv finished
+var SAD = 7; // rcv application data
+var SER = 8; // not expecting any messages at this point
+
+// server expect states
+var CHE = 0; // rcv client hello
+var CCE = 1; // rcv client certificate
+var CKE = 2; // rcv client key exchange
+var CCV = 3; // rcv certificate verify
+var CCC = 4; // rcv change cipher spec
+var CFI = 5; // rcv finished
+var CAD = 6; // rcv application data
+var CER = 7; // not expecting any messages at this point
+
+// map client current expect state and content type to function
+var __ = tls.handleUnexpected;
+var R0 = tls.handleChangeCipherSpec;
+var R1 = tls.handleAlert;
+var R2 = tls.handleHandshake;
+var R3 = tls.handleApplicationData;
+var R4 = tls.handleHeartbeat;
+var ctTable = [];
+ctTable[tls.ConnectionEnd.client] = [
+//      CC,AL,HS,AD,HB
+/*SHE*/[__,R1,R2,__,R4],
+/*SCE*/[__,R1,R2,__,R4],
+/*SKE*/[__,R1,R2,__,R4],
+/*SCR*/[__,R1,R2,__,R4],
+/*SHD*/[__,R1,R2,__,R4],
+/*SCC*/[R0,R1,__,__,R4],
+/*SFI*/[__,R1,R2,__,R4],
+/*SAD*/[__,R1,R2,R3,R4],
+/*SER*/[__,R1,R2,__,R4]
+];
+
+// map server current expect state and content type to function
+ctTable[tls.ConnectionEnd.server] = [
+//      CC,AL,HS,AD
+/*CHE*/[__,R1,R2,__,R4],
+/*CCE*/[__,R1,R2,__,R4],
+/*CKE*/[__,R1,R2,__,R4],
+/*CCV*/[__,R1,R2,__,R4],
+/*CCC*/[R0,R1,__,__,R4],
+/*CFI*/[__,R1,R2,__,R4],
+/*CAD*/[__,R1,R2,R3,R4],
+/*CER*/[__,R1,R2,__,R4]
+];
+
+// map client current expect state and handshake type to function
+var H0 = tls.handleHelloRequest;
+var H1 = tls.handleServerHello;
+var H2 = tls.handleCertificate;
+var H3 = tls.handleServerKeyExchange;
+var H4 = tls.handleCertificateRequest;
+var H5 = tls.handleServerHelloDone;
+var H6 = tls.handleFinished;
+var hsTable = [];
+hsTable[tls.ConnectionEnd.client] = [
+//      HR,01,SH,03,04,05,06,07,08,09,10,SC,SK,CR,HD,15,CK,17,18,19,FI
+/*SHE*/[__,__,H1,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],
+/*SCE*/[H0,__,__,__,__,__,__,__,__,__,__,H2,H3,H4,H5,__,__,__,__,__,__],
+/*SKE*/[H0,__,__,__,__,__,__,__,__,__,__,__,H3,H4,H5,__,__,__,__,__,__],
+/*SCR*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,H4,H5,__,__,__,__,__,__],
+/*SHD*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,H5,__,__,__,__,__,__],
+/*SCC*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],
+/*SFI*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,H6],
+/*SAD*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],
+/*SER*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__]
+];
+
+// map server current expect state and handshake type to function
+// Note: CAD[CH] does not map to FB because renegotiation is prohibited
+var H7 = tls.handleClientHello;
+var H8 = tls.handleClientKeyExchange;
+var H9 = tls.handleCertificateVerify;
+hsTable[tls.ConnectionEnd.server] = [
+//      01,CH,02,03,04,05,06,07,08,09,10,CC,12,13,14,CV,CK,17,18,19,FI
+/*CHE*/[__,H7,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],
+/*CCE*/[__,__,__,__,__,__,__,__,__,__,__,H2,__,__,__,__,__,__,__,__,__],
+/*CKE*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,H8,__,__,__,__],
+/*CCV*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,H9,__,__,__,__,__],
+/*CCC*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],
+/*CFI*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,H6],
+/*CAD*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],
+/*CER*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__]
+];
+
+/**
+ * Generates the master_secret and keys using the given security parameters.
+ *
+ * The security parameters for a TLS connection state are defined as such:
+ *
+ * struct {
+ *   ConnectionEnd          entity;
+ *   PRFAlgorithm           prf_algorithm;
+ *   BulkCipherAlgorithm    bulk_cipher_algorithm;
+ *   CipherType             cipher_type;
+ *   uint8                  enc_key_length;
+ *   uint8                  block_length;
+ *   uint8                  fixed_iv_length;
+ *   uint8                  record_iv_length;
+ *   MACAlgorithm           mac_algorithm;
+ *   uint8                  mac_length;
+ *   uint8                  mac_key_length;
+ *   CompressionMethod      compression_algorithm;
+ *   opaque                 master_secret[48];
+ *   opaque                 client_random[32];
+ *   opaque                 server_random[32];
+ * } SecurityParameters;
+ *
+ * Note that this definition is from TLS 1.2. In TLS 1.0 some of these
+ * parameters are ignored because, for instance, the PRFAlgorithm is a
+ * builtin-fixed algorithm combining iterations of MD5 and SHA-1 in TLS 1.0.
+ *
+ * The Record Protocol requires an algorithm to generate keys required by the
+ * current connection state.
+ *
+ * The master secret is expanded into a sequence of secure bytes, which is then
+ * split to a client write MAC key, a server write MAC key, a client write
+ * encryption key, and a server write encryption key. In TLS 1.0 a client write
+ * IV and server write IV are also generated. Each of these is generated from
+ * the byte sequence in that order. Unused values are empty. In TLS 1.2, some
+ * AEAD ciphers may additionally require a client write IV and a server write
+ * IV (see Section 6.2.3.3).
+ *
+ * When keys, MAC keys, and IVs are generated, the master secret is used as an
+ * entropy source.
+ *
+ * To generate the key material, compute:
+ *
+ * master_secret = PRF(pre_master_secret, "master secret",
+ *                     ClientHello.random + ServerHello.random)
+ *
+ * key_block = PRF(SecurityParameters.master_secret,
+ *                 "key expansion",
+ *                 SecurityParameters.server_random +
+ *                 SecurityParameters.client_random);
+ *
+ * until enough output has been generated. Then, the key_block is
+ * partitioned as follows:
+ *
+ * client_write_MAC_key[SecurityParameters.mac_key_length]
+ * server_write_MAC_key[SecurityParameters.mac_key_length]
+ * client_write_key[SecurityParameters.enc_key_length]
+ * server_write_key[SecurityParameters.enc_key_length]
+ * client_write_IV[SecurityParameters.fixed_iv_length]
+ * server_write_IV[SecurityParameters.fixed_iv_length]
+ *
+ * In TLS 1.2, the client_write_IV and server_write_IV are only generated for
+ * implicit nonce techniques as described in Section 3.2.1 of [AEAD]. This
+ * implementation uses TLS 1.0 so IVs are generated.
+ *
+ * Implementation note: The currently defined cipher suite which requires the
+ * most material is AES_256_CBC_SHA256. It requires 2 x 32 byte keys and 2 x 32
+ * byte MAC keys, for a total 128 bytes of key material. In TLS 1.0 it also
+ * requires 2 x 16 byte IVs, so it actually takes 160 bytes of key material.
+ *
+ * @param c the connection.
+ * @param sp the security parameters to use.
+ *
+ * @return the security keys.
+ */
+tls.generateKeys = function(c, sp) {
+  // TLS_RSA_WITH_AES_128_CBC_SHA (required to be compliant with TLS 1.2) &
+  // TLS_RSA_WITH_AES_256_CBC_SHA are the only cipher suites implemented
+  // at present
+
+  // TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA is required to be compliant with
+  // TLS 1.0 but we don't care right now because AES is better and we have
+  // an implementation for it
+
+  // TODO: TLS 1.2 implementation
+  /*
+  // determine the PRF
+  var prf;
+  switch(sp.prf_algorithm) {
+  case tls.PRFAlgorithm.tls_prf_sha256:
+    prf = prf_sha256;
+    break;
+  default:
+    // should never happen
+    throw new Error('Invalid PRF');
+  }
+  */
+
+  // TLS 1.0/1.1 implementation
+  var prf = prf_TLS1;
+
+  // concatenate server and client random
+  var random = sp.client_random + sp.server_random;
+
+  // only create master secret if session is new
+  if(!c.session.resuming) {
+    // create master secret, clean up pre-master secret
+    sp.master_secret = prf(
+      sp.pre_master_secret, 'master secret', random, 48).bytes();
+    sp.pre_master_secret = null;
+  }
+
+  // generate the amount of key material needed
+  random = sp.server_random + sp.client_random;
+  var length = 2 * sp.mac_key_length + 2 * sp.enc_key_length;
+
+  // include IV for TLS/1.0
+  var tls10 = (c.version.major === tls.Versions.TLS_1_0.major &&
+    c.version.minor === tls.Versions.TLS_1_0.minor);
+  if(tls10) {
+    length += 2 * sp.fixed_iv_length;
+  }
+  var km = prf(sp.master_secret, 'key expansion', random, length);
+
+  // split the key material into the MAC and encryption keys
+  var rval = {
+    client_write_MAC_key: km.getBytes(sp.mac_key_length),
+    server_write_MAC_key: km.getBytes(sp.mac_key_length),
+    client_write_key: km.getBytes(sp.enc_key_length),
+    server_write_key: km.getBytes(sp.enc_key_length)
+  };
+
+  // include TLS 1.0 IVs
+  if(tls10) {
+    rval.client_write_IV = km.getBytes(sp.fixed_iv_length);
+    rval.server_write_IV = km.getBytes(sp.fixed_iv_length);
+  }
+
+  return rval;
+};
+
+/**
+ * Creates a new initialized TLS connection state. A connection state has
+ * a read mode and a write mode.
+ *
+ * compression state:
+ *   The current state of the compression algorithm.
+ *
+ * cipher state:
+ *   The current state of the encryption algorithm. This will consist of the
+ *   scheduled key for that connection. For stream ciphers, this will also
+ *   contain whatever state information is necessary to allow the stream to
+ *   continue to encrypt or decrypt data.
+ *
+ * MAC key:
+ *   The MAC key for the connection.
+ *
+ * sequence number:
+ *   Each connection state contains a sequence number, which is maintained
+ *   separately for read and write states. The sequence number MUST be set to
+ *   zero whenever a connection state is made the active state. Sequence
+ *   numbers are of type uint64 and may not exceed 2^64-1. Sequence numbers do
+ *   not wrap. If a TLS implementation would need to wrap a sequence number,
+ *   it must renegotiate instead. A sequence number is incremented after each
+ *   record: specifically, the first record transmitted under a particular
+ *   connection state MUST use sequence number 0.
+ *
+ * @param c the connection.
+ *
+ * @return the new initialized TLS connection state.
+ */
+tls.createConnectionState = function(c) {
+  var client = (c.entity === tls.ConnectionEnd.client);
+
+  var createMode = function() {
+    var mode = {
+      // two 32-bit numbers, first is most significant
+      sequenceNumber: [0, 0],
+      macKey: null,
+      macLength: 0,
+      macFunction: null,
+      cipherState: null,
+      cipherFunction: function(record) {return true;},
+      compressionState: null,
+      compressFunction: function(record) {return true;},
+      updateSequenceNumber: function() {
+        if(mode.sequenceNumber[1] === 0xFFFFFFFF) {
+          mode.sequenceNumber[1] = 0;
+          ++mode.sequenceNumber[0];
+        } else {
+          ++mode.sequenceNumber[1];
+        }
+      }
+    };
+    return mode;
+  };
+  var state = {
+    read: createMode(),
+    write: createMode()
+  };
+
+  // update function in read mode will decrypt then decompress a record
+  state.read.update = function(c, record) {
+    if(!state.read.cipherFunction(record, state.read)) {
+      c.error(c, {
+        message: 'Could not decrypt record or bad MAC.',
+        send: true,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          // doesn't matter if decryption failed or MAC was
+          // invalid, return the same error so as not to reveal
+          // which one occurred
+          description: tls.Alert.Description.bad_record_mac
+        }
+      });
+    } else if(!state.read.compressFunction(c, record, state.read)) {
+      c.error(c, {
+        message: 'Could not decompress record.',
+        send: true,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          description: tls.Alert.Description.decompression_failure
+        }
+      });
+    }
+    return !c.fail;
+  };
+
+  // update function in write mode will compress then encrypt a record
+  state.write.update = function(c, record) {
+    if(!state.write.compressFunction(c, record, state.write)) {
+      // error, but do not send alert since it would require
+      // compression as well
+      c.error(c, {
+        message: 'Could not compress record.',
+        send: false,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          description: tls.Alert.Description.internal_error
+        }
+      });
+    } else if(!state.write.cipherFunction(record, state.write)) {
+      // error, but do not send alert since it would require
+      // encryption as well
+      c.error(c, {
+        message: 'Could not encrypt record.',
+        send: false,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          description: tls.Alert.Description.internal_error
+        }
+      });
+    }
+    return !c.fail;
+  };
+
+  // handle security parameters
+  if(c.session) {
+    var sp = c.session.sp;
+    c.session.cipherSuite.initSecurityParameters(sp);
+
+    // generate keys
+    sp.keys = tls.generateKeys(c, sp);
+    state.read.macKey = client ?
+      sp.keys.server_write_MAC_key : sp.keys.client_write_MAC_key;
+    state.write.macKey = client ?
+      sp.keys.client_write_MAC_key : sp.keys.server_write_MAC_key;
+
+    // cipher suite setup
+    c.session.cipherSuite.initConnectionState(state, c, sp);
+
+    // compression setup
+    switch(sp.compression_algorithm) {
+    case tls.CompressionMethod.none:
+      break;
+    case tls.CompressionMethod.deflate:
+      state.read.compressFunction = inflate;
+      state.write.compressFunction = deflate;
+      break;
+    default:
+      throw new Error('Unsupported compression algorithm.');
+    }
+  }
+
+  return state;
+};
+
+/**
+ * Creates a Random structure.
+ *
+ * struct {
+ *   uint32 gmt_unix_time;
+ *   opaque random_bytes[28];
+ * } Random;
+ *
+ * gmt_unix_time:
+ *   The current time and date in standard UNIX 32-bit format (seconds since
+ *   the midnight starting Jan 1, 1970, UTC, ignoring leap seconds) according
+ *   to the sender's internal clock. Clocks are not required to be set
+ *   correctly by the basic TLS protocol; higher-level or application
+ *   protocols may define additional requirements. Note that, for historical
+ *   reasons, the data element is named using GMT, the predecessor of the
+ *   current worldwide time base, UTC.
+ * random_bytes:
+ *   28 bytes generated by a secure random number generator.
+ *
+ * @return the Random structure as a byte array.
+ */
+tls.createRandom = function() {
+  // get UTC milliseconds
+  var d = new Date();
+  var utc = +d + d.getTimezoneOffset() * 60000;
+  var rval = forge.util.createBuffer();
+  rval.putInt32(utc);
+  rval.putBytes(forge.random.getBytes(28));
+  return rval;
+};
+
+/**
+ * Creates a TLS record with the given type and data.
+ *
+ * @param c the connection.
+ * @param options:
+ *   type: the record type.
+ *   data: the plain text data in a byte buffer.
+ *
+ * @return the created record.
+ */
+tls.createRecord = function(c, options) {
+  if(!options.data) {
+    return null;
+  }
+  var record = {
+    type: options.type,
+    version: {
+      major: c.version.major,
+      minor: c.version.minor
+    },
+    length: options.data.length(),
+    fragment: options.data
+  };
+  return record;
+};
+
+/**
+ * Creates a TLS alert record.
+ *
+ * @param c the connection.
+ * @param alert:
+ *   level: the TLS alert level.
+ *   description: the TLS alert description.
+ *
+ * @return the created alert record.
+ */
+tls.createAlert = function(c, alert) {
+  var b = forge.util.createBuffer();
+  b.putByte(alert.level);
+  b.putByte(alert.description);
+  return tls.createRecord(c, {
+    type: tls.ContentType.alert,
+    data: b
+  });
+};
+
+/* The structure of a TLS handshake message.
+ *
+ * struct {
+ *    HandshakeType msg_type;    // handshake type
+ *    uint24 length;             // bytes in message
+ *    select(HandshakeType) {
+ *       case hello_request:       HelloRequest;
+ *       case client_hello:        ClientHello;
+ *       case server_hello:        ServerHello;
+ *       case certificate:         Certificate;
+ *       case server_key_exchange: ServerKeyExchange;
+ *       case certificate_request: CertificateRequest;
+ *       case server_hello_done:   ServerHelloDone;
+ *       case certificate_verify:  CertificateVerify;
+ *       case client_key_exchange: ClientKeyExchange;
+ *       case finished:            Finished;
+ *    } body;
+ * } Handshake;
+ */
+
+/**
+ * Creates a ClientHello message.
+ *
+ * opaque SessionID<0..32>;
+ * enum { null(0), deflate(1), (255) } CompressionMethod;
+ * uint8 CipherSuite[2];
+ *
+ * struct {
+ *   ProtocolVersion client_version;
+ *   Random random;
+ *   SessionID session_id;
+ *   CipherSuite cipher_suites<2..2^16-2>;
+ *   CompressionMethod compression_methods<1..2^8-1>;
+ *   select(extensions_present) {
+ *     case false:
+ *       struct {};
+ *     case true:
+ *       Extension extensions<0..2^16-1>;
+ *   };
+ * } ClientHello;
+ *
+ * The extension format for extended client hellos and server hellos is:
+ *
+ * struct {
+ *   ExtensionType extension_type;
+ *   opaque extension_data<0..2^16-1>;
+ * } Extension;
+ *
+ * Here:
+ *
+ * - "extension_type" identifies the particular extension type.
+ * - "extension_data" contains information specific to the particular
+ * extension type.
+ *
+ * The extension types defined in this document are:
+ *
+ * enum {
+ *   server_name(0), max_fragment_length(1),
+ *   client_certificate_url(2), trusted_ca_keys(3),
+ *   truncated_hmac(4), status_request(5), (65535)
+ * } ExtensionType;
+ *
+ * @param c the connection.
+ *
+ * @return the ClientHello byte buffer.
+ */
+tls.createClientHello = function(c) {
+  // save hello version
+  c.session.clientHelloVersion = {
+    major: c.version.major,
+    minor: c.version.minor
+  };
+
+  // create supported cipher suites
+  var cipherSuites = forge.util.createBuffer();
+  for(var i = 0; i < c.cipherSuites.length; ++i) {
+    var cs = c.cipherSuites[i];
+    cipherSuites.putByte(cs.id[0]);
+    cipherSuites.putByte(cs.id[1]);
+  }
+  var cSuites = cipherSuites.length();
+
+  // create supported compression methods, null always supported, but
+  // also support deflate if connection has inflate and deflate methods
+  var compressionMethods = forge.util.createBuffer();
+  compressionMethods.putByte(tls.CompressionMethod.none);
+  // FIXME: deflate support disabled until issues with raw deflate data
+  // without zlib headers are resolved
+  /*
+  if(c.inflate !== null && c.deflate !== null) {
+    compressionMethods.putByte(tls.CompressionMethod.deflate);
+  }
+  */
+  var cMethods = compressionMethods.length();
+
+  // create TLS SNI (server name indication) extension if virtual host
+  // has been specified, see RFC 3546
+  var extensions = forge.util.createBuffer();
+  if(c.virtualHost) {
+    // create extension struct
+    var ext = forge.util.createBuffer();
+    ext.putByte(0x00); // type server_name (ExtensionType is 2 bytes)
+    ext.putByte(0x00);
+
+    /* In order to provide the server name, clients MAY include an
+     * extension of type "server_name" in the (extended) client hello.
+     * The "extension_data" field of this extension SHALL contain
+     * "ServerNameList" where:
+     *
+     * struct {
+     *   NameType name_type;
+     *   select(name_type) {
+     *     case host_name: HostName;
+     *   } name;
+     * } ServerName;
+     *
+     * enum {
+     *   host_name(0), (255)
+     * } NameType;
+     *
+     * opaque HostName<1..2^16-1>;
+     *
+     * struct {
+     *   ServerName server_name_list<1..2^16-1>
+     * } ServerNameList;
+     */
+    var serverName = forge.util.createBuffer();
+    serverName.putByte(0x00); // type host_name
+    writeVector(serverName, 2, forge.util.createBuffer(c.virtualHost));
+
+    // ServerNameList is in extension_data
+    var snList = forge.util.createBuffer();
+    writeVector(snList, 2, serverName);
+    writeVector(ext, 2, snList);
+    extensions.putBuffer(ext);
+  }
+  var extLength = extensions.length();
+  if(extLength > 0) {
+    // add extension vector length
+    extLength += 2;
+  }
+
+  // determine length of the handshake message
+  // cipher suites and compression methods size will need to be
+  // updated if more get added to the list
+  var sessionId = c.session.id;
+  var length =
+    sessionId.length + 1 + // session ID vector
+    2 +                    // version (major + minor)
+    4 + 28 +               // random time and random bytes
+    2 + cSuites +          // cipher suites vector
+    1 + cMethods +         // compression methods vector
+    extLength;             // extensions vector
+
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(tls.HandshakeType.client_hello);
+  rval.putInt24(length);                     // handshake length
+  rval.putByte(c.version.major);             // major version
+  rval.putByte(c.version.minor);             // minor version
+  rval.putBytes(c.session.sp.client_random); // random time + bytes
+  writeVector(rval, 1, forge.util.createBuffer(sessionId));
+  writeVector(rval, 2, cipherSuites);
+  writeVector(rval, 1, compressionMethods);
+  if(extLength > 0) {
+    writeVector(rval, 2, extensions);
+  }
+  return rval;
+};
+
+/**
+ * Creates a ServerHello message.
+ *
+ * @param c the connection.
+ *
+ * @return the ServerHello byte buffer.
+ */
+tls.createServerHello = function(c) {
+  // determine length of the handshake message
+  var sessionId = c.session.id;
+  var length =
+    sessionId.length + 1 + // session ID vector
+    2 +                    // version (major + minor)
+    4 + 28 +               // random time and random bytes
+    2 +                    // chosen cipher suite
+    1;                     // chosen compression method
+
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(tls.HandshakeType.server_hello);
+  rval.putInt24(length);                     // handshake length
+  rval.putByte(c.version.major);             // major version
+  rval.putByte(c.version.minor);             // minor version
+  rval.putBytes(c.session.sp.server_random); // random time + bytes
+  writeVector(rval, 1, forge.util.createBuffer(sessionId));
+  rval.putByte(c.session.cipherSuite.id[0]);
+  rval.putByte(c.session.cipherSuite.id[1]);
+  rval.putByte(c.session.compressionMethod);
+  return rval;
+};
+
+/**
+ * Creates a Certificate message.
+ *
+ * When this message will be sent:
+ *   This is the first message the client can send after receiving a server
+ *   hello done message and the first message the server can send after
+ *   sending a ServerHello. This client message is only sent if the server
+ *   requests a certificate. If no suitable certificate is available, the
+ *   client should send a certificate message containing no certificates. If
+ *   client authentication is required by the server for the handshake to
+ *   continue, it may respond with a fatal handshake failure alert.
+ *
+ * opaque ASN.1Cert<1..2^24-1>;
+ *
+ * struct {
+ *   ASN.1Cert certificate_list<0..2^24-1>;
+ * } Certificate;
+ *
+ * @param c the connection.
+ *
+ * @return the Certificate byte buffer.
+ */
+tls.createCertificate = function(c) {
+  // TODO: check certificate request to ensure types are supported
+
+  // get a certificate (a certificate as a PEM string)
+  var client = (c.entity === tls.ConnectionEnd.client);
+  var cert = null;
+  if(c.getCertificate) {
+    var hint;
+    if(client) {
+      hint = c.session.certificateRequest;
+    } else {
+      hint = c.session.extensions.server_name.serverNameList;
+    }
+    cert = c.getCertificate(c, hint);
+  }
+
+  // buffer to hold certificate list
+  var certList = forge.util.createBuffer();
+  if(cert !== null) {
+    try {
+      // normalize cert to a chain of certificates
+      if(!forge.util.isArray(cert)) {
+        cert = [cert];
+      }
+      var asn1 = null;
+      for(var i = 0; i < cert.length; ++i) {
+        var msg = forge.pem.decode(cert[i])[0];
+        if(msg.type !== 'CERTIFICATE' &&
+          msg.type !== 'X509 CERTIFICATE' &&
+          msg.type !== 'TRUSTED CERTIFICATE') {
+          var error = new Error('Could not convert certificate from PEM; PEM ' +
+            'header type is not "CERTIFICATE", "X509 CERTIFICATE", or ' +
+            '"TRUSTED CERTIFICATE".');
+          error.headerType = msg.type;
+          throw error;
+        }
+        if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+          throw new Error('Could not convert certificate from PEM; PEM is encrypted.');
+        }
+
+        var der = forge.util.createBuffer(msg.body);
+        if(asn1 === null) {
+          asn1 = forge.asn1.fromDer(der.bytes(), false);
+        }
+
+        // certificate entry is itself a vector with 3 length bytes
+        var certBuffer = forge.util.createBuffer();
+        writeVector(certBuffer, 3, der);
+
+        // add cert vector to cert list vector
+        certList.putBuffer(certBuffer);
+      }
+
+      // save certificate
+      cert = forge.pki.certificateFromAsn1(asn1);
+      if(client) {
+        c.session.clientCertificate = cert;
+      } else {
+        c.session.serverCertificate = cert;
+      }
+    } catch(ex) {
+      return c.error(c, {
+        message: 'Could not send certificate list.',
+        cause: ex,
+        send: true,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          description: tls.Alert.Description.bad_certificate
+        }
+      });
+    }
+  }
+
+  // determine length of the handshake message
+  var length = 3 + certList.length(); // cert list vector
+
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(tls.HandshakeType.certificate);
+  rval.putInt24(length);
+  writeVector(rval, 3, certList);
+  return rval;
+};
+
+/**
+ * Creates a ClientKeyExchange message.
+ *
+ * When this message will be sent:
+ *   This message is always sent by the client. It will immediately follow the
+ *   client certificate message, if it is sent. Otherwise it will be the first
+ *   message sent by the client after it receives the server hello done
+ *   message.
+ *
+ * Meaning of this message:
+ *   With this message, the premaster secret is set, either though direct
+ *   transmission of the RSA-encrypted secret, or by the transmission of
+ *   Diffie-Hellman parameters which will allow each side to agree upon the
+ *   same premaster secret. When the key exchange method is DH_RSA or DH_DSS,
+ *   client certification has been requested, and the client was able to
+ *   respond with a certificate which contained a Diffie-Hellman public key
+ *   whose parameters (group and generator) matched those specified by the
+ *   server in its certificate, this message will not contain any data.
+ *
+ * Meaning of this message:
+ *   If RSA is being used for key agreement and authentication, the client
+ *   generates a 48-byte premaster secret, encrypts it using the public key
+ *   from the server's certificate or the temporary RSA key provided in a
+ *   server key exchange message, and sends the result in an encrypted
+ *   premaster secret message. This structure is a variant of the client
+ *   key exchange message, not a message in itself.
+ *
+ * struct {
+ *   select(KeyExchangeAlgorithm) {
+ *     case rsa: EncryptedPreMasterSecret;
+ *     case diffie_hellman: ClientDiffieHellmanPublic;
+ *   } exchange_keys;
+ * } ClientKeyExchange;
+ *
+ * struct {
+ *   ProtocolVersion client_version;
+ *   opaque random[46];
+ * } PreMasterSecret;
+ *
+ * struct {
+ *   public-key-encrypted PreMasterSecret pre_master_secret;
+ * } EncryptedPreMasterSecret;
+ *
+ * A public-key-encrypted element is encoded as a vector <0..2^16-1>.
+ *
+ * @param c the connection.
+ *
+ * @return the ClientKeyExchange byte buffer.
+ */
+tls.createClientKeyExchange = function(c) {
+  // create buffer to encrypt
+  var b = forge.util.createBuffer();
+
+  // add highest client-supported protocol to help server avoid version
+  // rollback attacks
+  b.putByte(c.session.clientHelloVersion.major);
+  b.putByte(c.session.clientHelloVersion.minor);
+
+  // generate and add 46 random bytes
+  b.putBytes(forge.random.getBytes(46));
+
+  // save pre-master secret
+  var sp = c.session.sp;
+  sp.pre_master_secret = b.getBytes();
+
+  // RSA-encrypt the pre-master secret
+  var key = c.session.serverCertificate.publicKey;
+  b = key.encrypt(sp.pre_master_secret);
+
+  /* Note: The encrypted pre-master secret will be stored in a
+    public-key-encrypted opaque vector that has the length prefixed using
+    2 bytes, so include those 2 bytes in the handshake message length. This
+    is done as a minor optimization instead of calling writeVector(). */
+
+  // determine length of the handshake message
+  var length = b.length + 2;
+
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(tls.HandshakeType.client_key_exchange);
+  rval.putInt24(length);
+  // add vector length bytes
+  rval.putInt16(b.length);
+  rval.putBytes(b);
+  return rval;
+};
+
+/**
+ * Creates a ServerKeyExchange message.
+ *
+ * @param c the connection.
+ *
+ * @return the ServerKeyExchange byte buffer.
+ */
+tls.createServerKeyExchange = function(c) {
+  // this implementation only supports RSA, no Diffie-Hellman support,
+  // so this record is empty
+
+  // determine length of the handshake message
+  var length = 0;
+
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  if(length > 0) {
+    rval.putByte(tls.HandshakeType.server_key_exchange);
+    rval.putInt24(length);
+  }
+  return rval;
+};
+
+/**
+ * Gets the signed data used to verify a client-side certificate. See
+ * tls.createCertificateVerify() for details.
+ *
+ * @param c the connection.
+ * @param callback the callback to call once the signed data is ready.
+ */
+tls.getClientSignature = function(c, callback) {
+  // generate data to RSA encrypt
+  var b = forge.util.createBuffer();
+  b.putBuffer(c.session.md5.digest());
+  b.putBuffer(c.session.sha1.digest());
+  b = b.getBytes();
+
+  // create default signing function as necessary
+  c.getSignature = c.getSignature || function(c, b, callback) {
+    // do rsa encryption, call callback
+    var privateKey = null;
+    if(c.getPrivateKey) {
+      try {
+        privateKey = c.getPrivateKey(c, c.session.clientCertificate);
+        privateKey = forge.pki.privateKeyFromPem(privateKey);
+      } catch(ex) {
+        c.error(c, {
+          message: 'Could not get private key.',
+          cause: ex,
+          send: true,
+          alert: {
+            level: tls.Alert.Level.fatal,
+            description: tls.Alert.Description.internal_error
+          }
+        });
+      }
+    }
+    if(privateKey === null) {
+      c.error(c, {
+        message: 'No private key set.',
+        send: true,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          description: tls.Alert.Description.internal_error
+        }
+      });
+    } else {
+      b = privateKey.sign(b, null);
+    }
+    callback(c, b);
+  };
+
+  // get client signature
+  c.getSignature(c, b, callback);
+};
+
+/**
+ * Creates a CertificateVerify message.
+ *
+ * Meaning of this message:
+ *   This structure conveys the client's Diffie-Hellman public value
+ *   (Yc) if it was not already included in the client's certificate.
+ *   The encoding used for Yc is determined by the enumerated
+ *   PublicValueEncoding. This structure is a variant of the client
+ *   key exchange message, not a message in itself.
+ *
+ * When this message will be sent:
+ *   This message is used to provide explicit verification of a client
+ *   certificate. This message is only sent following a client
+ *   certificate that has signing capability (i.e. all certificates
+ *   except those containing fixed Diffie-Hellman parameters). When
+ *   sent, it will immediately follow the client key exchange message.
+ *
+ * struct {
+ *   Signature signature;
+ * } CertificateVerify;
+ *
+ * CertificateVerify.signature.md5_hash
+ *   MD5(handshake_messages);
+ *
+ * Certificate.signature.sha_hash
+ *   SHA(handshake_messages);
+ *
+ * Here handshake_messages refers to all handshake messages sent or
+ * received starting at client hello up to but not including this
+ * message, including the type and length fields of the handshake
+ * messages.
+ *
+ * select(SignatureAlgorithm) {
+ *   case anonymous: struct { };
+ *   case rsa:
+ *     digitally-signed struct {
+ *       opaque md5_hash[16];
+ *       opaque sha_hash[20];
+ *     };
+ *   case dsa:
+ *     digitally-signed struct {
+ *       opaque sha_hash[20];
+ *     };
+ * } Signature;
+ *
+ * In digital signing, one-way hash functions are used as input for a
+ * signing algorithm. A digitally-signed element is encoded as an opaque
+ * vector <0..2^16-1>, where the length is specified by the signing
+ * algorithm and key.
+ *
+ * In RSA signing, a 36-byte structure of two hashes (one SHA and one
+ * MD5) is signed (encrypted with the private key). It is encoded with
+ * PKCS #1 block type 0 or type 1 as described in [PKCS1].
+ *
+ * In DSS, the 20 bytes of the SHA hash are run directly through the
+ * Digital Signing Algorithm with no additional hashing.
+ *
+ * @param c the connection.
+ * @param signature the signature to include in the message.
+ *
+ * @return the CertificateVerify byte buffer.
+ */
+tls.createCertificateVerify = function(c, signature) {
+  /* Note: The signature will be stored in a "digitally-signed" opaque
+    vector that has the length prefixed using 2 bytes, so include those
+    2 bytes in the handshake message length. This is done as a minor
+    optimization instead of calling writeVector(). */
+
+  // determine length of the handshake message
+  var length = signature.length + 2;
+
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(tls.HandshakeType.certificate_verify);
+  rval.putInt24(length);
+  // add vector length bytes
+  rval.putInt16(signature.length);
+  rval.putBytes(signature);
+  return rval;
+};
+
+/**
+ * Creates a CertificateRequest message.
+ *
+ * @param c the connection.
+ *
+ * @return the CertificateRequest byte buffer.
+ */
+tls.createCertificateRequest = function(c) {
+  // TODO: support other certificate types
+  var certTypes = forge.util.createBuffer();
+
+  // common RSA certificate type
+  certTypes.putByte(0x01);
+
+  // add distinguished names from CA store
+  var cAs = forge.util.createBuffer();
+  for(var key in c.caStore.certs) {
+    var cert = c.caStore.certs[key];
+    var dn = forge.pki.distinguishedNameToAsn1(cert.subject);
+    var byteBuffer = forge.asn1.toDer(dn);
+    cAs.putInt16(byteBuffer.length());
+    cAs.putBuffer(byteBuffer);
+  }
+
+  // TODO: TLS 1.2+ has a different format
+
+  // determine length of the handshake message
+  var length =
+    1 + certTypes.length() +
+    2 + cAs.length();
+
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(tls.HandshakeType.certificate_request);
+  rval.putInt24(length);
+  writeVector(rval, 1, certTypes);
+  writeVector(rval, 2, cAs);
+  return rval;
+};
+
+/**
+ * Creates a ServerHelloDone message.
+ *
+ * @param c the connection.
+ *
+ * @return the ServerHelloDone byte buffer.
+ */
+tls.createServerHelloDone = function(c) {
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(tls.HandshakeType.server_hello_done);
+  rval.putInt24(0);
+  return rval;
+};
+
+/**
+ * Creates a ChangeCipherSpec message.
+ *
+ * The change cipher spec protocol exists to signal transitions in
+ * ciphering strategies. The protocol consists of a single message,
+ * which is encrypted and compressed under the current (not the pending)
+ * connection state. The message consists of a single byte of value 1.
+ *
+ * struct {
+ *   enum { change_cipher_spec(1), (255) } type;
+ * } ChangeCipherSpec;
+ *
+ * @return the ChangeCipherSpec byte buffer.
+ */
+tls.createChangeCipherSpec = function() {
+  var rval = forge.util.createBuffer();
+  rval.putByte(0x01);
+  return rval;
+};
+
+/**
+ * Creates a Finished message.
+ *
+ * struct {
+ *   opaque verify_data[12];
+ * } Finished;
+ *
+ * verify_data
+ *   PRF(master_secret, finished_label, MD5(handshake_messages) +
+ *   SHA-1(handshake_messages)) [0..11];
+ *
+ * finished_label
+ *   For Finished messages sent by the client, the string "client
+ *   finished". For Finished messages sent by the server, the
+ *   string "server finished".
+ *
+ * handshake_messages
+ *   All of the data from all handshake messages up to but not
+ *   including this message. This is only data visible at the
+ *   handshake layer and does not include record layer headers.
+ *   This is the concatenation of all the Handshake structures as
+ *   defined in 7.4 exchanged thus far.
+ *
+ * @param c the connection.
+ *
+ * @return the Finished byte buffer.
+ */
+tls.createFinished = function(c) {
+  // generate verify_data
+  var b = forge.util.createBuffer();
+  b.putBuffer(c.session.md5.digest());
+  b.putBuffer(c.session.sha1.digest());
+
+  // TODO: determine prf function and verify length for TLS 1.2
+  var client = (c.entity === tls.ConnectionEnd.client);
+  var sp = c.session.sp;
+  var vdl = 12;
+  var prf = prf_TLS1;
+  var label = client ? 'client finished' : 'server finished';
+  b = prf(sp.master_secret, label, b.getBytes(), vdl);
+
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(tls.HandshakeType.finished);
+  rval.putInt24(b.length());
+  rval.putBuffer(b);
+  return rval;
+};
+
+/**
+ * Creates a HeartbeatMessage (See RFC 6520).
+ *
+ * struct {
+ *   HeartbeatMessageType type;
+ *   uint16 payload_length;
+ *   opaque payload[HeartbeatMessage.payload_length];
+ *   opaque padding[padding_length];
+ * } HeartbeatMessage;
+ *
+ * The total length of a HeartbeatMessage MUST NOT exceed 2^14 or
+ * max_fragment_length when negotiated as defined in [RFC6066].
+ *
+ * type: The message type, either heartbeat_request or heartbeat_response.
+ *
+ * payload_length: The length of the payload.
+ *
+ * payload: The payload consists of arbitrary content.
+ *
+ * padding: The padding is random content that MUST be ignored by the
+ *   receiver. The length of a HeartbeatMessage is TLSPlaintext.length
+ *   for TLS and DTLSPlaintext.length for DTLS. Furthermore, the
+ *   length of the type field is 1 byte, and the length of the
+ *   payload_length is 2. Therefore, the padding_length is
+ *   TLSPlaintext.length - payload_length - 3 for TLS and
+ *   DTLSPlaintext.length - payload_length - 3 for DTLS. The
+ *   padding_length MUST be at least 16.
+ *
+ * The sender of a HeartbeatMessage MUST use a random padding of at
+ * least 16 bytes. The padding of a received HeartbeatMessage message
+ * MUST be ignored.
+ *
+ * If the payload_length of a received HeartbeatMessage is too large,
+ * the received HeartbeatMessage MUST be discarded silently.
+ *
+ * @param c the connection.
+ * @param type the tls.HeartbeatMessageType.
+ * @param payload the heartbeat data to send as the payload.
+ * @param [payloadLength] the payload length to use, defaults to the
+ *          actual payload length.
+ *
+ * @return the HeartbeatRequest byte buffer.
+ */
+tls.createHeartbeat = function(type, payload, payloadLength) {
+  if(typeof payloadLength === 'undefined') {
+    payloadLength = payload.length;
+  }
+  // build record fragment
+  var rval = forge.util.createBuffer();
+  rval.putByte(type);               // heartbeat message type
+  rval.putInt16(payloadLength);     // payload length
+  rval.putBytes(payload);           // payload
+  // padding
+  var plaintextLength = rval.length();
+  var paddingLength = Math.max(16, plaintextLength - payloadLength - 3);
+  rval.putBytes(forge.random.getBytes(paddingLength));
+  return rval;
+};
+
+/**
+ * Fragments, compresses, encrypts, and queues a record for delivery.
+ *
+ * @param c the connection.
+ * @param record the record to queue.
+ */
+tls.queue = function(c, record) {
+  // error during record creation
+  if(!record) {
+    return;
+  }
+
+  if(record.fragment.length() === 0) {
+    if(record.type === tls.ContentType.handshake ||
+      record.type === tls.ContentType.alert ||
+      record.type === tls.ContentType.change_cipher_spec) {
+      // Empty handshake, alert of change cipher spec messages are not allowed per the TLS specification and should not be sent.
+      return;
+    }
+  }
+
+  // if the record is a handshake record, update handshake hashes
+  if(record.type === tls.ContentType.handshake) {
+    var bytes = record.fragment.bytes();
+    c.session.md5.update(bytes);
+    c.session.sha1.update(bytes);
+    bytes = null;
+  }
+
+  // handle record fragmentation
+  var records;
+  if(record.fragment.length() <= tls.MaxFragment) {
+    records = [record];
+  } else {
+    // fragment data as long as it is too long
+    records = [];
+    var data = record.fragment.bytes();
+    while(data.length > tls.MaxFragment) {
+      records.push(tls.createRecord(c, {
+        type: record.type,
+        data: forge.util.createBuffer(data.slice(0, tls.MaxFragment))
+      }));
+      data = data.slice(tls.MaxFragment);
+    }
+    // add last record
+    if(data.length > 0) {
+      records.push(tls.createRecord(c, {
+        type: record.type,
+        data: forge.util.createBuffer(data)
+      }));
+    }
+  }
+
+  // compress and encrypt all fragmented records
+  for(var i = 0; i < records.length && !c.fail; ++i) {
+    // update the record using current write state
+    var rec = records[i];
+    var s = c.state.current.write;
+    if(s.update(c, rec)) {
+      // store record
+      c.records.push(rec);
+    }
+  }
+};
+
+/**
+ * Flushes all queued records to the output buffer and calls the
+ * tlsDataReady() handler on the given connection.
+ *
+ * @param c the connection.
+ *
+ * @return true on success, false on failure.
+ */
+tls.flush = function(c) {
+  for(var i = 0; i < c.records.length; ++i) {
+    var record = c.records[i];
+
+    // add record header and fragment
+    c.tlsData.putByte(record.type);
+    c.tlsData.putByte(record.version.major);
+    c.tlsData.putByte(record.version.minor);
+    c.tlsData.putInt16(record.fragment.length());
+    c.tlsData.putBuffer(c.records[i].fragment);
+  }
+  c.records = [];
+  return c.tlsDataReady(c);
+};
+
+/**
+ * Maps a pki.certificateError to a tls.Alert.Description.
+ *
+ * @param error the error to map.
+ *
+ * @return the alert description.
+ */
+var _certErrorToAlertDesc = function(error) {
+  switch(error) {
+  case true:
+    return true;
+  case forge.pki.certificateError.bad_certificate:
+    return tls.Alert.Description.bad_certificate;
+  case forge.pki.certificateError.unsupported_certificate:
+    return tls.Alert.Description.unsupported_certificate;
+  case forge.pki.certificateError.certificate_revoked:
+    return tls.Alert.Description.certificate_revoked;
+  case forge.pki.certificateError.certificate_expired:
+    return tls.Alert.Description.certificate_expired;
+  case forge.pki.certificateError.certificate_unknown:
+    return tls.Alert.Description.certificate_unknown;
+  case forge.pki.certificateError.unknown_ca:
+    return tls.Alert.Description.unknown_ca;
+  default:
+    return tls.Alert.Description.bad_certificate;
+  }
+};
+
+/**
+ * Maps a tls.Alert.Description to a pki.certificateError.
+ *
+ * @param desc the alert description.
+ *
+ * @return the certificate error.
+ */
+var _alertDescToCertError = function(desc) {
+  switch(desc) {
+  case true:
+    return true;
+  case tls.Alert.Description.bad_certificate:
+    return forge.pki.certificateError.bad_certificate;
+  case tls.Alert.Description.unsupported_certificate:
+    return forge.pki.certificateError.unsupported_certificate;
+  case tls.Alert.Description.certificate_revoked:
+    return forge.pki.certificateError.certificate_revoked;
+  case tls.Alert.Description.certificate_expired:
+    return forge.pki.certificateError.certificate_expired;
+  case tls.Alert.Description.certificate_unknown:
+    return forge.pki.certificateError.certificate_unknown;
+  case tls.Alert.Description.unknown_ca:
+    return forge.pki.certificateError.unknown_ca;
+  default:
+    return forge.pki.certificateError.bad_certificate;
+  }
+};
+
+/**
+ * Verifies a certificate chain against the given connection's
+ * Certificate Authority store.
+ *
+ * @param c the TLS connection.
+ * @param chain the certificate chain to verify, with the root or highest
+ *          authority at the end.
+ *
+ * @return true if successful, false if not.
+ */
+tls.verifyCertificateChain = function(c, chain) {
+  try {
+    // Make a copy of c.verifyOptions so that we can modify options.verify
+    // without modifying c.verifyOptions.
+    var options = {};
+    for (var key in c.verifyOptions) {
+      options[key] = c.verifyOptions[key];
+    }
+
+    options.verify = function(vfd, depth, chain) {
+      // convert pki.certificateError to tls alert description
+      var desc = _certErrorToAlertDesc(vfd);
+
+      // call application callback
+      var ret = c.verify(c, vfd, depth, chain);
+      if(ret !== true) {
+        if(typeof ret === 'object' && !forge.util.isArray(ret)) {
+          // throw custom error
+          var error = new Error('The application rejected the certificate.');
+          error.send = true;
+          error.alert = {
+            level: tls.Alert.Level.fatal,
+            description: tls.Alert.Description.bad_certificate
+          };
+          if(ret.message) {
+            error.message = ret.message;
+          }
+          if(ret.alert) {
+            error.alert.description = ret.alert;
+          }
+          throw error;
+        }
+
+        // convert tls alert description to pki.certificateError
+        if(ret !== vfd) {
+          ret = _alertDescToCertError(ret);
+        }
+      }
+
+      return ret;
+    };
+
+    // verify chain
+    forge.pki.verifyCertificateChain(c.caStore, chain, options);
+  } catch(ex) {
+    // build tls error if not already customized
+    var err = ex;
+    if(typeof err !== 'object' || forge.util.isArray(err)) {
+      err = {
+        send: true,
+        alert: {
+          level: tls.Alert.Level.fatal,
+          description: _certErrorToAlertDesc(ex)
+        }
+      };
+    }
+    if(!('send' in err)) {
+      err.send = true;
+    }
+    if(!('alert' in err)) {
+      err.alert = {
+        level: tls.Alert.Level.fatal,
+        description: _certErrorToAlertDesc(err.error)
+      };
+    }
+
+    // send error
+    c.error(c, err);
+  }
+
+  return !c.fail;
+};
+
+/**
+ * Creates a new TLS session cache.
+ *
+ * @param cache optional map of session ID to cached session.
+ * @param capacity the maximum size for the cache (default: 100).
+ *
+ * @return the new TLS session cache.
+ */
+tls.createSessionCache = function(cache, capacity) {
+  var rval = null;
+
+  // assume input is already a session cache object
+  if(cache && cache.getSession && cache.setSession && cache.order) {
+    rval = cache;
+  } else {
+    // create cache
+    rval = {};
+    rval.cache = cache || {};
+    rval.capacity = Math.max(capacity || 100, 1);
+    rval.order = [];
+
+    // store order for sessions, delete session overflow
+    for(var key in cache) {
+      if(rval.order.length <= capacity) {
+        rval.order.push(key);
+      } else {
+        delete cache[key];
+      }
+    }
+
+    // get a session from a session ID (or get any session)
+    rval.getSession = function(sessionId) {
+      var session = null;
+      var key = null;
+
+      // if session ID provided, use it
+      if(sessionId) {
+        key = forge.util.bytesToHex(sessionId);
+      } else if(rval.order.length > 0) {
+        // get first session from cache
+        key = rval.order[0];
+      }
+
+      if(key !== null && key in rval.cache) {
+        // get cached session and remove from cache
+        session = rval.cache[key];
+        delete rval.cache[key];
+        for(var i in rval.order) {
+          if(rval.order[i] === key) {
+            rval.order.splice(i, 1);
+            break;
+          }
+        }
+      }
+
+      return session;
+    };
+
+    // set a session in the cache
+    rval.setSession = function(sessionId, session) {
+      // remove session from cache if at capacity
+      if(rval.order.length === rval.capacity) {
+        var key = rval.order.shift();
+        delete rval.cache[key];
+      }
+      // add session to cache
+      var key = forge.util.bytesToHex(sessionId);
+      rval.order.push(key);
+      rval.cache[key] = session;
+    };
+  }
+
+  return rval;
+};
+
+/**
+ * Creates a new TLS connection.
+ *
+ * See public createConnection() docs for more details.
+ *
+ * @param options the options for this connection.
+ *
+ * @return the new TLS connection.
+ */
+tls.createConnection = function(options) {
+  var caStore = null;
+  if(options.caStore) {
+    // if CA store is an array, convert it to a CA store object
+    if(forge.util.isArray(options.caStore)) {
+      caStore = forge.pki.createCaStore(options.caStore);
+    } else {
+      caStore = options.caStore;
+    }
+  } else {
+    // create empty CA store
+    caStore = forge.pki.createCaStore();
+  }
+
+  // setup default cipher suites
+  var cipherSuites = options.cipherSuites || null;
+  if(cipherSuites === null) {
+    cipherSuites = [];
+    for(var key in tls.CipherSuites) {
+      cipherSuites.push(tls.CipherSuites[key]);
+    }
+  }
+
+  // set default entity
+  var entity = (options.server || false) ?
+    tls.ConnectionEnd.server : tls.ConnectionEnd.client;
+
+  // create session cache if requested
+  var sessionCache = options.sessionCache ?
+    tls.createSessionCache(options.sessionCache) : null;
+
+  // create TLS connection
+  var c = {
+    version: {major: tls.Version.major, minor: tls.Version.minor},
+    entity: entity,
+    sessionId: options.sessionId,
+    caStore: caStore,
+    sessionCache: sessionCache,
+    cipherSuites: cipherSuites,
+    connected: options.connected,
+    virtualHost: options.virtualHost || null,
+    verifyClient: options.verifyClient || false,
+    verify: options.verify || function(cn, vfd, dpth, cts) {return vfd;},
+    verifyOptions: options.verifyOptions || {},
+    getCertificate: options.getCertificate || null,
+    getPrivateKey: options.getPrivateKey || null,
+    getSignature: options.getSignature || null,
+    input: forge.util.createBuffer(),
+    tlsData: forge.util.createBuffer(),
+    data: forge.util.createBuffer(),
+    tlsDataReady: options.tlsDataReady,
+    dataReady: options.dataReady,
+    heartbeatReceived: options.heartbeatReceived,
+    closed: options.closed,
+    error: function(c, ex) {
+      // set origin if not set
+      ex.origin = ex.origin ||
+        ((c.entity === tls.ConnectionEnd.client) ? 'client' : 'server');
+
+      // send TLS alert
+      if(ex.send) {
+        tls.queue(c, tls.createAlert(c, ex.alert));
+        tls.flush(c);
+      }
+
+      // error is fatal by default
+      var fatal = (ex.fatal !== false);
+      if(fatal) {
+        // set fail flag
+        c.fail = true;
+      }
+
+      // call error handler first
+      options.error(c, ex);
+
+      if(fatal) {
+        // fatal error, close connection, do not clear fail
+        c.close(false);
+      }
+    },
+    deflate: options.deflate || null,
+    inflate: options.inflate || null
+  };
+
+  /**
+   * Resets a closed TLS connection for reuse. Called in c.close().
+   *
+   * @param clearFail true to clear the fail flag (default: true).
+   */
+  c.reset = function(clearFail) {
+    c.version = {major: tls.Version.major, minor: tls.Version.minor};
+    c.record = null;
+    c.session = null;
+    c.peerCertificate = null;
+    c.state = {
+      pending: null,
+      current: null
+    };
+    c.expect = (c.entity === tls.ConnectionEnd.client) ? SHE : CHE;
+    c.fragmented = null;
+    c.records = [];
+    c.open = false;
+    c.handshakes = 0;
+    c.handshaking = false;
+    c.isConnected = false;
+    c.fail = !(clearFail || typeof(clearFail) === 'undefined');
+    c.input.clear();
+    c.tlsData.clear();
+    c.data.clear();
+    c.state.current = tls.createConnectionState(c);
+  };
+
+  // do initial reset of connection
+  c.reset();
+
+  /**
+   * Updates the current TLS engine state based on the given record.
+   *
+   * @param c the TLS connection.
+   * @param record the TLS record to act on.
+   */
+  var _update = function(c, record) {
+    // get record handler (align type in table by subtracting lowest)
+    var aligned = record.type - tls.ContentType.change_cipher_spec;
+    var handlers = ctTable[c.entity][c.expect];
+    if(aligned in handlers) {
+      handlers[aligned](c, record);
+    } else {
+      // unexpected record
+      tls.handleUnexpected(c, record);
+    }
+  };
+
+  /**
+   * Reads the record header and initializes the next record on the given
+   * connection.
+   *
+   * @param c the TLS connection with the next record.
+   *
+   * @return 0 if the input data could be processed, otherwise the
+   *         number of bytes required for data to be processed.
+   */
+  var _readRecordHeader = function(c) {
+    var rval = 0;
+
+    // get input buffer and its length
+    var b = c.input;
+    var len = b.length();
+
+    // need at least 5 bytes to initialize a record
+    if(len < 5) {
+      rval = 5 - len;
+    } else {
+      // enough bytes for header
+      // initialize record
+      c.record = {
+        type: b.getByte(),
+        version: {
+          major: b.getByte(),
+          minor: b.getByte()
+        },
+        length: b.getInt16(),
+        fragment: forge.util.createBuffer(),
+        ready: false
+      };
+
+      // check record version
+      var compatibleVersion = (c.record.version.major === c.version.major);
+      if(compatibleVersion && c.session && c.session.version) {
+        // session version already set, require same minor version
+        compatibleVersion = (c.record.version.minor === c.version.minor);
+      }
+      if(!compatibleVersion) {
+        c.error(c, {
+          message: 'Incompatible TLS version.',
+          send: true,
+          alert: {
+            level: tls.Alert.Level.fatal,
+            description: tls.Alert.Description.protocol_version
+          }
+        });
+      }
+    }
+
+    return rval;
+  };
+
+  /**
+   * Reads the next record's contents and appends its message to any
+   * previously fragmented message.
+   *
+   * @param c the TLS connection with the next record.
+   *
+   * @return 0 if the input data could be processed, otherwise the
+   *         number of bytes required for data to be processed.
+   */
+  var _readRecord = function(c) {
+    var rval = 0;
+
+    // ensure there is enough input data to get the entire record
+    var b = c.input;
+    var len = b.length();
+    if(len < c.record.length) {
+      // not enough data yet, return how much is required
+      rval = c.record.length - len;
+    } else {
+      // there is enough data to parse the pending record
+      // fill record fragment and compact input buffer
+      c.record.fragment.putBytes(b.getBytes(c.record.length));
+      b.compact();
+
+      // update record using current read state
+      var s = c.state.current.read;
+      if(s.update(c, c.record)) {
+        // see if there is a previously fragmented message that the
+        // new record's message fragment should be appended to
+        if(c.fragmented !== null) {
+          // if the record type matches a previously fragmented
+          // record, append the record fragment to it
+          if(c.fragmented.type === c.record.type) {
+            // concatenate record fragments
+            c.fragmented.fragment.putBuffer(c.record.fragment);
+            c.record = c.fragmented;
+          } else {
+            // error, invalid fragmented record
+            c.error(c, {
+              message: 'Invalid fragmented record.',
+              send: true,
+              alert: {
+                level: tls.Alert.Level.fatal,
+                description:
+                  tls.Alert.Description.unexpected_message
+              }
+            });
+          }
+        }
+
+        // record is now ready
+        c.record.ready = true;
+      }
+    }
+
+    return rval;
+  };
+
+  /**
+   * Performs a handshake using the TLS Handshake Protocol, as a client.
+   *
+   * This method should only be called if the connection is in client mode.
+   *
+   * @param sessionId the session ID to use, null to start a new one.
+   */
+  c.handshake = function(sessionId) {
+    // error to call this in non-client mode
+    if(c.entity !== tls.ConnectionEnd.client) {
+      // not fatal error
+      c.error(c, {
+        message: 'Cannot initiate handshake as a server.',
+        fatal: false
+      });
+    } else if(c.handshaking) {
+      // handshake is already in progress, fail but not fatal error
+      c.error(c, {
+        message: 'Handshake already in progress.',
+        fatal: false
+      });
+    } else {
+      // clear fail flag on reuse
+      if(c.fail && !c.open && c.handshakes === 0) {
+        c.fail = false;
+      }
+
+      // now handshaking
+      c.handshaking = true;
+
+      // default to blank (new session)
+      sessionId = sessionId || '';
+
+      // if a session ID was specified, try to find it in the cache
+      var session = null;
+      if(sessionId.length > 0) {
+        if(c.sessionCache) {
+          session = c.sessionCache.getSession(sessionId);
+        }
+
+        // matching session not found in cache, clear session ID
+        if(session === null) {
+          sessionId = '';
+        }
+      }
+
+      // no session given, grab a session from the cache, if available
+      if(sessionId.length === 0 && c.sessionCache) {
+        session = c.sessionCache.getSession();
+        if(session !== null) {
+          sessionId = session.id;
+        }
+      }
+
+      // set up session
+      c.session = {
+        id: sessionId,
+        version: null,
+        cipherSuite: null,
+        compressionMethod: null,
+        serverCertificate: null,
+        certificateRequest: null,
+        clientCertificate: null,
+        sp: {},
+        md5: forge.md.md5.create(),
+        sha1: forge.md.sha1.create()
+      };
+
+      // use existing session information
+      if(session) {
+        // only update version on connection, session version not yet set
+        c.version = session.version;
+        c.session.sp = session.sp;
+      }
+
+      // generate new client random
+      c.session.sp.client_random = tls.createRandom().getBytes();
+
+      // connection now open
+      c.open = true;
+
+      // send hello
+      tls.queue(c, tls.createRecord(c, {
+        type: tls.ContentType.handshake,
+        data: tls.createClientHello(c)
+      }));
+      tls.flush(c);
+    }
+  };
+
+  /**
+   * Called when TLS protocol data has been received from somewhere and should
+   * be processed by the TLS engine.
+   *
+   * @param data the TLS protocol data, as a string, to process.
+   *
+   * @return 0 if the data could be processed, otherwise the number of bytes
+   *         required for data to be processed.
+   */
+  c.process = function(data) {
+    var rval = 0;
+
+    // buffer input data
+    if(data) {
+      c.input.putBytes(data);
+    }
+
+    // process next record if no failure, process will be called after
+    // each record is handled (since handling can be asynchronous)
+    if(!c.fail) {
+      // reset record if ready and now empty
+      if(c.record !== null &&
+        c.record.ready && c.record.fragment.isEmpty()) {
+        c.record = null;
+      }
+
+      // if there is no pending record, try to read record header
+      if(c.record === null) {
+        rval = _readRecordHeader(c);
+      }
+
+      // read the next record (if record not yet ready)
+      if(!c.fail && c.record !== null && !c.record.ready) {
+        rval = _readRecord(c);
+      }
+
+      // record ready to be handled, update engine state
+      if(!c.fail && c.record !== null && c.record.ready) {
+        _update(c, c.record);
+      }
+    }
+
+    return rval;
+  };
+
+  /**
+   * Requests that application data be packaged into a TLS record. The
+   * tlsDataReady handler will be called when the TLS record(s) have been
+   * prepared.
+   *
+   * @param data the application data, as a raw 'binary' encoded string, to
+   *          be sent; to send utf-16/utf-8 string data, use the return value
+   *          of util.encodeUtf8(str).
+   *
+   * @return true on success, false on failure.
+   */
+  c.prepare = function(data) {
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.application_data,
+      data: forge.util.createBuffer(data)
+    }));
+    return tls.flush(c);
+  };
+
+  /**
+   * Requests that a heartbeat request be packaged into a TLS record for
+   * transmission. The tlsDataReady handler will be called when TLS record(s)
+   * have been prepared.
+   *
+   * When a heartbeat response has been received, the heartbeatReceived
+   * handler will be called with the matching payload. This handler can
+   * be used to clear a retransmission timer, etc.
+   *
+   * @param payload the heartbeat data to send as the payload in the message.
+   * @param [payloadLength] the payload length to use, defaults to the
+   *          actual payload length.
+   *
+   * @return true on success, false on failure.
+   */
+  c.prepareHeartbeatRequest = function(payload, payloadLength) {
+    if(payload instanceof forge.util.ByteBuffer) {
+      payload = payload.bytes();
+    }
+    if(typeof payloadLength === 'undefined') {
+      payloadLength = payload.length;
+    }
+    c.expectedHeartbeatPayload = payload;
+    tls.queue(c, tls.createRecord(c, {
+      type: tls.ContentType.heartbeat,
+      data: tls.createHeartbeat(
+        tls.HeartbeatMessageType.heartbeat_request, payload, payloadLength)
+    }));
+    return tls.flush(c);
+  };
+
+  /**
+   * Closes the connection (sends a close_notify alert).
+   *
+   * @param clearFail true to clear the fail flag (default: true).
+   */
+  c.close = function(clearFail) {
+    // save session if connection didn't fail
+    if(!c.fail && c.sessionCache && c.session) {
+      // only need to preserve session ID, version, and security params
+      var session = {
+        id: c.session.id,
+        version: c.session.version,
+        sp: c.session.sp
+      };
+      session.sp.keys = null;
+      c.sessionCache.setSession(session.id, session);
+    }
+
+    if(c.open) {
+      // connection no longer open, clear input
+      c.open = false;
+      c.input.clear();
+
+      // if connected or handshaking, send an alert
+      if(c.isConnected || c.handshaking) {
+        c.isConnected = c.handshaking = false;
+
+        // send close_notify alert
+        tls.queue(c, tls.createAlert(c, {
+          level: tls.Alert.Level.warning,
+          description: tls.Alert.Description.close_notify
+        }));
+        tls.flush(c);
+      }
+
+      // call handler
+      c.closed(c);
+    }
+
+    // reset TLS connection, do not clear fail flag
+    c.reset(clearFail);
+  };
+
+  return c;
+};
+
+/* TLS API */
+module.exports = forge.tls = forge.tls || {};
+
+// expose non-functions
+for(var key in tls) {
+  if(typeof tls[key] !== 'function') {
+    forge.tls[key] = tls[key];
+  }
+}
+
+// expose prf_tls1 for testing
+forge.tls.prf_tls1 = prf_TLS1;
+
+// expose sha1 hmac method
+forge.tls.hmac_sha1 = hmac_sha1;
+
+// expose session cache creation
+forge.tls.createSessionCache = tls.createSessionCache;
+
+/**
+ * Creates a new TLS connection. This does not make any assumptions about the
+ * transport layer that TLS is working on top of, ie: it does not assume there
+ * is a TCP/IP connection or establish one. A TLS connection is totally
+ * abstracted away from the layer is runs on top of, it merely establishes a
+ * secure channel between a client" and a "server".
+ *
+ * A TLS connection contains 4 connection states: pending read and write, and
+ * current read and write.
+ *
+ * At initialization, the current read and write states will be null. Only once
+ * the security parameters have been set and the keys have been generated can
+ * the pending states be converted into current states. Current states will be
+ * updated for each record processed.
+ *
+ * A custom certificate verify callback may be provided to check information
+ * like the common name on the server's certificate. It will be called for
+ * every certificate in the chain. It has the following signature:
+ *
+ * variable func(c, certs, index, preVerify)
+ * Where:
+ * c         The TLS connection
+ * verified  Set to true if certificate was verified, otherwise the alert
+ *           tls.Alert.Description for why the certificate failed.
+ * depth     The current index in the chain, where 0 is the server's cert.
+ * certs     The certificate chain, *NOTE* if the server was anonymous then
+ *           the chain will be empty.
+ *
+ * The function returns true on success and on failure either the appropriate
+ * tls.Alert.Description or an object with 'alert' set to the appropriate
+ * tls.Alert.Description and 'message' set to a custom error message. If true
+ * is not returned then the connection will abort using, in order of
+ * availability, first the returned alert description, second the preVerify
+ * alert description, and lastly the default 'bad_certificate'.
+ *
+ * There are three callbacks that can be used to make use of client-side
+ * certificates where each takes the TLS connection as the first parameter:
+ *
+ * getCertificate(conn, hint)
+ *   The second parameter is a hint as to which certificate should be
+ *   returned. If the connection entity is a client, then the hint will be
+ *   the CertificateRequest message from the server that is part of the
+ *   TLS protocol. If the connection entity is a server, then it will be
+ *   the servername list provided via an SNI extension the ClientHello, if
+ *   one was provided (empty array if not). The hint can be examined to
+ *   determine which certificate to use (advanced). Most implementations
+ *   will just return a certificate. The return value must be a
+ *   PEM-formatted certificate or an array of PEM-formatted certificates
+ *   that constitute a certificate chain, with the first in the array/chain
+ *   being the client's certificate.
+ * getPrivateKey(conn, certificate)
+ *   The second parameter is an forge.pki X.509 certificate object that
+ *   is associated with the requested private key. The return value must
+ *   be a PEM-formatted private key.
+ * getSignature(conn, bytes, callback)
+ *   This callback can be used instead of getPrivateKey if the private key
+ *   is not directly accessible in javascript or should not be. For
+ *   instance, a secure external web service could provide the signature
+ *   in exchange for appropriate credentials. The second parameter is a
+ *   string of bytes to be signed that are part of the TLS protocol. These
+ *   bytes are used to verify that the private key for the previously
+ *   provided client-side certificate is accessible to the client. The
+ *   callback is a function that takes 2 parameters, the TLS connection
+ *   and the RSA encrypted (signed) bytes as a string. This callback must
+ *   be called once the signature is ready.
+ *
+ * @param options the options for this connection:
+ *   server: true if the connection is server-side, false for client.
+ *   sessionId: a session ID to reuse, null for a new connection.
+ *   caStore: an array of certificates to trust.
+ *   sessionCache: a session cache to use.
+ *   cipherSuites: an optional array of cipher suites to use,
+ *     see tls.CipherSuites.
+ *   connected: function(conn) called when the first handshake completes.
+ *   virtualHost: the virtual server name to use in a TLS SNI extension.
+ *   verifyClient: true to require a client certificate in server mode,
+ *     'optional' to request one, false not to (default: false).
+ *   verify: a handler used to custom verify certificates in the chain.
+ *   verifyOptions: an object with options for the certificate chain validation.
+ *     See documentation of pki.verifyCertificateChain for possible options.
+ *     verifyOptions.verify is ignored. If you wish to specify a verify handler
+ *     use the verify key.
+ *   getCertificate: an optional callback used to get a certificate or
+ *     a chain of certificates (as an array).
+ *   getPrivateKey: an optional callback used to get a private key.
+ *   getSignature: an optional callback used to get a signature.
+ *   tlsDataReady: function(conn) called when TLS protocol data has been
+ *     prepared and is ready to be used (typically sent over a socket
+ *     connection to its destination), read from conn.tlsData buffer.
+ *   dataReady: function(conn) called when application data has
+ *     been parsed from a TLS record and should be consumed by the
+ *     application, read from conn.data buffer.
+ *   closed: function(conn) called when the connection has been closed.
+ *   error: function(conn, error) called when there was an error.
+ *   deflate: function(inBytes) if provided, will deflate TLS records using
+ *     the deflate algorithm if the server supports it.
+ *   inflate: function(inBytes) if provided, will inflate TLS records using
+ *     the deflate algorithm if the server supports it.
+ *
+ * @return the new TLS connection.
+ */
+forge.tls.createConnection = tls.createConnection;
Index: frontend/node_modules/node-forge/lib/tlssocket.js
===================================================================
--- frontend/node_modules/node-forge/lib/tlssocket.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/tlssocket.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,249 @@
+/**
+ * Socket wrapping functions for TLS.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2009-2012 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./tls');
+
+/**
+ * Wraps a forge.net socket with a TLS layer.
+ *
+ * @param options:
+ *   sessionId: a session ID to reuse, null for a new connection if no session
+ *     cache is provided or it is empty.
+ *   caStore: an array of certificates to trust.
+ *   sessionCache: a session cache to use.
+ *   cipherSuites: an optional array of cipher suites to use, see
+ *     tls.CipherSuites.
+ *   socket: the socket to wrap.
+ *   virtualHost: the virtual server name to use in a TLS SNI extension.
+ *   verify: a handler used to custom verify certificates in the chain.
+ *   getCertificate: an optional callback used to get a certificate.
+ *   getPrivateKey: an optional callback used to get a private key.
+ *   getSignature: an optional callback used to get a signature.
+ *   deflate: function(inBytes) if provided, will deflate TLS records using
+ *     the deflate algorithm if the server supports it.
+ *   inflate: function(inBytes) if provided, will inflate TLS records using
+ *     the deflate algorithm if the server supports it.
+ *
+ * @return the TLS-wrapped socket.
+ */
+forge.tls.wrapSocket = function(options) {
+  // get raw socket
+  var socket = options.socket;
+
+  // create TLS socket
+  var tlsSocket = {
+    id: socket.id,
+    // set handlers
+    connected: socket.connected || function(e) {},
+    closed: socket.closed || function(e) {},
+    data: socket.data || function(e) {},
+    error: socket.error || function(e) {}
+  };
+
+  // create TLS connection
+  var c = forge.tls.createConnection({
+    server: false,
+    sessionId: options.sessionId || null,
+    caStore: options.caStore || [],
+    sessionCache: options.sessionCache || null,
+    cipherSuites: options.cipherSuites || null,
+    virtualHost: options.virtualHost,
+    verify: options.verify,
+    getCertificate: options.getCertificate,
+    getPrivateKey: options.getPrivateKey,
+    getSignature: options.getSignature,
+    deflate: options.deflate,
+    inflate: options.inflate,
+    connected: function(c) {
+      // first handshake complete, call handler
+      if(c.handshakes === 1) {
+        tlsSocket.connected({
+          id: socket.id,
+          type: 'connect',
+          bytesAvailable: c.data.length()
+        });
+      }
+    },
+    tlsDataReady: function(c) {
+      // send TLS data over socket
+      return socket.send(c.tlsData.getBytes());
+    },
+    dataReady: function(c) {
+      // indicate application data is ready
+      tlsSocket.data({
+        id: socket.id,
+        type: 'socketData',
+        bytesAvailable: c.data.length()
+      });
+    },
+    closed: function(c) {
+      // close socket
+      socket.close();
+    },
+    error: function(c, e) {
+      // send error, close socket
+      tlsSocket.error({
+        id: socket.id,
+        type: 'tlsError',
+        message: e.message,
+        bytesAvailable: 0,
+        error: e
+      });
+      socket.close();
+    }
+  });
+
+  // handle doing handshake after connecting
+  socket.connected = function(e) {
+    c.handshake(options.sessionId);
+  };
+
+  // handle closing TLS connection
+  socket.closed = function(e) {
+    if(c.open && c.handshaking) {
+      // error
+      tlsSocket.error({
+        id: socket.id,
+        type: 'ioError',
+        message: 'Connection closed during handshake.',
+        bytesAvailable: 0
+      });
+    }
+    c.close();
+
+    // call socket handler
+    tlsSocket.closed({
+      id: socket.id,
+      type: 'close',
+      bytesAvailable: 0
+    });
+  };
+
+  // handle error on socket
+  socket.error = function(e) {
+    // error
+    tlsSocket.error({
+      id: socket.id,
+      type: e.type,
+      message: e.message,
+      bytesAvailable: 0
+    });
+    c.close();
+  };
+
+  // handle receiving raw TLS data from socket
+  var _requiredBytes = 0;
+  socket.data = function(e) {
+    // drop data if connection not open
+    if(!c.open) {
+      socket.receive(e.bytesAvailable);
+    } else {
+      // only receive if there are enough bytes available to
+      // process a record
+      if(e.bytesAvailable >= _requiredBytes) {
+        var count = Math.max(e.bytesAvailable, _requiredBytes);
+        var data = socket.receive(count);
+        if(data !== null) {
+          _requiredBytes = c.process(data);
+        }
+      }
+    }
+  };
+
+  /**
+   * Destroys this socket.
+   */
+  tlsSocket.destroy = function() {
+    socket.destroy();
+  };
+
+  /**
+   * Sets this socket's TLS session cache. This should be called before
+   * the socket is connected or after it is closed.
+   *
+   * The cache is an object mapping session IDs to internal opaque state.
+   * An application might need to change the cache used by a particular
+   * tlsSocket between connections if it accesses multiple TLS hosts.
+   *
+   * @param cache the session cache to use.
+   */
+  tlsSocket.setSessionCache = function(cache) {
+    c.sessionCache = tls.createSessionCache(cache);
+  };
+
+  /**
+   * Connects this socket.
+   *
+   * @param options:
+   *           host: the host to connect to.
+   *           port: the port to connect to.
+   *           policyPort: the policy port to use (if non-default), 0 to
+   *              use the flash default.
+   *           policyUrl: the policy file URL to use (instead of port).
+   */
+  tlsSocket.connect = function(options) {
+    socket.connect(options);
+  };
+
+  /**
+   * Closes this socket.
+   */
+  tlsSocket.close = function() {
+    c.close();
+  };
+
+  /**
+   * Determines if the socket is connected or not.
+   *
+   * @return true if connected, false if not.
+   */
+  tlsSocket.isConnected = function() {
+    return c.isConnected && socket.isConnected();
+  };
+
+  /**
+   * Writes bytes to this socket.
+   *
+   * @param bytes the bytes (as a string) to write.
+   *
+   * @return true on success, false on failure.
+   */
+  tlsSocket.send = function(bytes) {
+    return c.prepare(bytes);
+  };
+
+  /**
+   * Reads bytes from this socket (non-blocking). Fewer than the number of
+   * bytes requested may be read if enough bytes are not available.
+   *
+   * This method should be called from the data handler if there are enough
+   * bytes available. To see how many bytes are available, check the
+   * 'bytesAvailable' property on the event in the data handler or call the
+   * bytesAvailable() function on the socket. If the browser is msie, then the
+   * bytesAvailable() function should be used to avoid race conditions.
+   * Otherwise, using the property on the data handler's event may be quicker.
+   *
+   * @param count the maximum number of bytes to read.
+   *
+   * @return the bytes read (as a string) or null on error.
+   */
+  tlsSocket.receive = function(count) {
+    return c.data.getBytes(count);
+  };
+
+  /**
+   * Gets the number of bytes available for receiving on the socket.
+   *
+   * @return the number of bytes available for receiving.
+   */
+  tlsSocket.bytesAvailable = function() {
+    return c.data.length();
+  };
+
+  return tlsSocket;
+};
Index: frontend/node_modules/node-forge/lib/util.js
===================================================================
--- frontend/node_modules/node-forge/lib/util.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/util.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,2652 @@
+/**
+ * Utility functions for web applications.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2018 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+var baseN = require('./baseN');
+
+/* Utilities API */
+var util = module.exports = forge.util = forge.util || {};
+
+// define setImmediate and nextTick
+(function() {
+  // use native nextTick (unless we're in webpack)
+  // webpack (or better node-libs-browser polyfill) sets process.browser.
+  // this way we can detect webpack properly
+  if(typeof process !== 'undefined' && process.nextTick && !process.browser) {
+    util.nextTick = process.nextTick;
+    if(typeof setImmediate === 'function') {
+      util.setImmediate = setImmediate;
+    } else {
+      // polyfill setImmediate with nextTick, older versions of node
+      // (those w/o setImmediate) won't totally starve IO
+      util.setImmediate = util.nextTick;
+    }
+    return;
+  }
+
+  // polyfill nextTick with native setImmediate
+  if(typeof setImmediate === 'function') {
+    util.setImmediate = function() { return setImmediate.apply(undefined, arguments); };
+    util.nextTick = function(callback) {
+      return setImmediate(callback);
+    };
+    return;
+  }
+
+  /* Note: A polyfill upgrade pattern is used here to allow combining
+  polyfills. For example, MutationObserver is fast, but blocks UI updates,
+  so it needs to allow UI updates periodically, so it falls back on
+  postMessage or setTimeout. */
+
+  // polyfill with setTimeout
+  util.setImmediate = function(callback) {
+    setTimeout(callback, 0);
+  };
+
+  // upgrade polyfill to use postMessage
+  if(typeof window !== 'undefined' &&
+    typeof window.postMessage === 'function') {
+    var msg = 'forge.setImmediate';
+    var callbacks = [];
+    util.setImmediate = function(callback) {
+      callbacks.push(callback);
+      // only send message when one hasn't been sent in
+      // the current turn of the event loop
+      if(callbacks.length === 1) {
+        window.postMessage(msg, '*');
+      }
+    };
+    function handler(event) {
+      if(event.source === window && event.data === msg) {
+        event.stopPropagation();
+        var copy = callbacks.slice();
+        callbacks.length = 0;
+        copy.forEach(function(callback) {
+          callback();
+        });
+      }
+    }
+    window.addEventListener('message', handler, true);
+  }
+
+  // upgrade polyfill to use MutationObserver
+  if(typeof MutationObserver !== 'undefined') {
+    // polyfill with MutationObserver
+    var now = Date.now();
+    var attr = true;
+    var div = document.createElement('div');
+    var callbacks = [];
+    new MutationObserver(function() {
+      var copy = callbacks.slice();
+      callbacks.length = 0;
+      copy.forEach(function(callback) {
+        callback();
+      });
+    }).observe(div, {attributes: true});
+    var oldSetImmediate = util.setImmediate;
+    util.setImmediate = function(callback) {
+      if(Date.now() - now > 15) {
+        now = Date.now();
+        oldSetImmediate(callback);
+      } else {
+        callbacks.push(callback);
+        // only trigger observer when it hasn't been triggered in
+        // the current turn of the event loop
+        if(callbacks.length === 1) {
+          div.setAttribute('a', attr = !attr);
+        }
+      }
+    };
+  }
+
+  util.nextTick = util.setImmediate;
+})();
+
+// check if running under Node.js
+util.isNodejs =
+  typeof process !== 'undefined' && process.versions && process.versions.node;
+
+
+// 'self' will also work in Web Workers (instance of WorkerGlobalScope) while
+// it will point to `window` in the main thread.
+// To remain compatible with older browsers, we fall back to 'window' if 'self'
+// is not available.
+util.globalScope = (function() {
+  if(util.isNodejs) {
+    return global;
+  }
+
+  return typeof self === 'undefined' ? window : self;
+})();
+
+// define isArray
+util.isArray = Array.isArray || function(x) {
+  return Object.prototype.toString.call(x) === '[object Array]';
+};
+
+// define isArrayBuffer
+util.isArrayBuffer = function(x) {
+  return typeof ArrayBuffer !== 'undefined' && x instanceof ArrayBuffer;
+};
+
+// define isArrayBufferView
+util.isArrayBufferView = function(x) {
+  return x && util.isArrayBuffer(x.buffer) && x.byteLength !== undefined;
+};
+
+/**
+ * Ensure a bits param is 8, 16, 24, or 32. Used to validate input for
+ * algorithms where bit manipulation, JavaScript limitations, and/or algorithm
+ * design only allow for byte operations of a limited size.
+ *
+ * @param n number of bits.
+ *
+ * Throw Error if n invalid.
+ */
+function _checkBitsParam(n) {
+  if(!(n === 8 || n === 16 || n === 24 || n === 32)) {
+    throw new Error('Only 8, 16, 24, or 32 bits supported: ' + n);
+  }
+}
+
+// TODO: set ByteBuffer to best available backing
+util.ByteBuffer = ByteStringBuffer;
+
+/** Buffer w/BinaryString backing */
+
+/**
+ * Constructor for a binary string backed byte buffer.
+ *
+ * @param [b] the bytes to wrap (either encoded as string, one byte per
+ *          character, or as an ArrayBuffer or Typed Array).
+ */
+function ByteStringBuffer(b) {
+  // TODO: update to match DataBuffer API
+
+  // the data in this buffer
+  this.data = '';
+  // the pointer for reading from this buffer
+  this.read = 0;
+
+  if(typeof b === 'string') {
+    this.data = b;
+  } else if(util.isArrayBuffer(b) || util.isArrayBufferView(b)) {
+    if(typeof Buffer !== 'undefined' && b instanceof Buffer) {
+      this.data = b.toString('binary');
+    } else {
+      // convert native buffer to forge buffer
+      // FIXME: support native buffers internally instead
+      var arr = new Uint8Array(b);
+      try {
+        this.data = String.fromCharCode.apply(null, arr);
+      } catch(e) {
+        for(var i = 0; i < arr.length; ++i) {
+          this.putByte(arr[i]);
+        }
+      }
+    }
+  } else if(b instanceof ByteStringBuffer ||
+    (typeof b === 'object' && typeof b.data === 'string' &&
+    typeof b.read === 'number')) {
+    // copy existing buffer
+    this.data = b.data;
+    this.read = b.read;
+  }
+
+  // used for v8 optimization
+  this._constructedStringLength = 0;
+}
+util.ByteStringBuffer = ByteStringBuffer;
+
+/* Note: This is an optimization for V8-based browsers. When V8 concatenates
+  a string, the strings are only joined logically using a "cons string" or
+  "constructed/concatenated string". These containers keep references to one
+  another and can result in very large memory usage. For example, if a 2MB
+  string is constructed by concatenating 4 bytes together at a time, the
+  memory usage will be ~44MB; so ~22x increase. The strings are only joined
+  together when an operation requiring their joining takes place, such as
+  substr(). This function is called when adding data to this buffer to ensure
+  these types of strings are periodically joined to reduce the memory
+  footprint. */
+var _MAX_CONSTRUCTED_STRING_LENGTH = 4096;
+util.ByteStringBuffer.prototype._optimizeConstructedString = function(x) {
+  this._constructedStringLength += x;
+  if(this._constructedStringLength > _MAX_CONSTRUCTED_STRING_LENGTH) {
+    // this substr() should cause the constructed string to join
+    this.data.substr(0, 1);
+    this._constructedStringLength = 0;
+  }
+};
+
+/**
+ * Gets the number of bytes in this buffer.
+ *
+ * @return the number of bytes in this buffer.
+ */
+util.ByteStringBuffer.prototype.length = function() {
+  return this.data.length - this.read;
+};
+
+/**
+ * Gets whether or not this buffer is empty.
+ *
+ * @return true if this buffer is empty, false if not.
+ */
+util.ByteStringBuffer.prototype.isEmpty = function() {
+  return this.length() <= 0;
+};
+
+/**
+ * Puts a byte in this buffer.
+ *
+ * @param b the byte to put.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putByte = function(b) {
+  return this.putBytes(String.fromCharCode(b));
+};
+
+/**
+ * Puts a byte in this buffer N times.
+ *
+ * @param b the byte to put.
+ * @param n the number of bytes of value b to put.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.fillWithByte = function(b, n) {
+  b = String.fromCharCode(b);
+  var d = this.data;
+  while(n > 0) {
+    if(n & 1) {
+      d += b;
+    }
+    n >>>= 1;
+    if(n > 0) {
+      b += b;
+    }
+  }
+  this.data = d;
+  this._optimizeConstructedString(n);
+  return this;
+};
+
+/**
+ * Puts bytes in this buffer.
+ *
+ * @param bytes the bytes (as a binary encoded string) to put.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putBytes = function(bytes) {
+  this.data += bytes;
+  this._optimizeConstructedString(bytes.length);
+  return this;
+};
+
+/**
+ * Puts a UTF-16 encoded string into this buffer.
+ *
+ * @param str the string to put.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putString = function(str) {
+  return this.putBytes(util.encodeUtf8(str));
+};
+
+/**
+ * Puts a 16-bit integer in this buffer in big-endian order.
+ *
+ * @param i the 16-bit integer.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putInt16 = function(i) {
+  return this.putBytes(
+    String.fromCharCode(i >> 8 & 0xFF) +
+    String.fromCharCode(i & 0xFF));
+};
+
+/**
+ * Puts a 24-bit integer in this buffer in big-endian order.
+ *
+ * @param i the 24-bit integer.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putInt24 = function(i) {
+  return this.putBytes(
+    String.fromCharCode(i >> 16 & 0xFF) +
+    String.fromCharCode(i >> 8 & 0xFF) +
+    String.fromCharCode(i & 0xFF));
+};
+
+/**
+ * Puts a 32-bit integer in this buffer in big-endian order.
+ *
+ * @param i the 32-bit integer.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putInt32 = function(i) {
+  return this.putBytes(
+    String.fromCharCode(i >> 24 & 0xFF) +
+    String.fromCharCode(i >> 16 & 0xFF) +
+    String.fromCharCode(i >> 8 & 0xFF) +
+    String.fromCharCode(i & 0xFF));
+};
+
+/**
+ * Puts a 16-bit integer in this buffer in little-endian order.
+ *
+ * @param i the 16-bit integer.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putInt16Le = function(i) {
+  return this.putBytes(
+    String.fromCharCode(i & 0xFF) +
+    String.fromCharCode(i >> 8 & 0xFF));
+};
+
+/**
+ * Puts a 24-bit integer in this buffer in little-endian order.
+ *
+ * @param i the 24-bit integer.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putInt24Le = function(i) {
+  return this.putBytes(
+    String.fromCharCode(i & 0xFF) +
+    String.fromCharCode(i >> 8 & 0xFF) +
+    String.fromCharCode(i >> 16 & 0xFF));
+};
+
+/**
+ * Puts a 32-bit integer in this buffer in little-endian order.
+ *
+ * @param i the 32-bit integer.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putInt32Le = function(i) {
+  return this.putBytes(
+    String.fromCharCode(i & 0xFF) +
+    String.fromCharCode(i >> 8 & 0xFF) +
+    String.fromCharCode(i >> 16 & 0xFF) +
+    String.fromCharCode(i >> 24 & 0xFF));
+};
+
+/**
+ * Puts an n-bit integer in this buffer in big-endian order.
+ *
+ * @param i the n-bit integer.
+ * @param n the number of bits in the integer (8, 16, 24, or 32).
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putInt = function(i, n) {
+  _checkBitsParam(n);
+  var bytes = '';
+  do {
+    n -= 8;
+    bytes += String.fromCharCode((i >> n) & 0xFF);
+  } while(n > 0);
+  return this.putBytes(bytes);
+};
+
+/**
+ * Puts a signed n-bit integer in this buffer in big-endian order. Two's
+ * complement representation is used.
+ *
+ * @param i the n-bit integer.
+ * @param n the number of bits in the integer (8, 16, 24, or 32).
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putSignedInt = function(i, n) {
+  // putInt checks n
+  if(i < 0) {
+    i += 2 << (n - 1);
+  }
+  return this.putInt(i, n);
+};
+
+/**
+ * Puts the given buffer into this buffer.
+ *
+ * @param buffer the buffer to put into this one.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.putBuffer = function(buffer) {
+  return this.putBytes(buffer.getBytes());
+};
+
+/**
+ * Gets a byte from this buffer and advances the read pointer by 1.
+ *
+ * @return the byte.
+ */
+util.ByteStringBuffer.prototype.getByte = function() {
+  return this.data.charCodeAt(this.read++);
+};
+
+/**
+ * Gets a uint16 from this buffer in big-endian order and advances the read
+ * pointer by 2.
+ *
+ * @return the uint16.
+ */
+util.ByteStringBuffer.prototype.getInt16 = function() {
+  var rval = (
+    this.data.charCodeAt(this.read) << 8 ^
+    this.data.charCodeAt(this.read + 1));
+  this.read += 2;
+  return rval;
+};
+
+/**
+ * Gets a uint24 from this buffer in big-endian order and advances the read
+ * pointer by 3.
+ *
+ * @return the uint24.
+ */
+util.ByteStringBuffer.prototype.getInt24 = function() {
+  var rval = (
+    this.data.charCodeAt(this.read) << 16 ^
+    this.data.charCodeAt(this.read + 1) << 8 ^
+    this.data.charCodeAt(this.read + 2));
+  this.read += 3;
+  return rval;
+};
+
+/**
+ * Gets a uint32 from this buffer in big-endian order and advances the read
+ * pointer by 4.
+ *
+ * @return the word.
+ */
+util.ByteStringBuffer.prototype.getInt32 = function() {
+  var rval = (
+    this.data.charCodeAt(this.read) << 24 ^
+    this.data.charCodeAt(this.read + 1) << 16 ^
+    this.data.charCodeAt(this.read + 2) << 8 ^
+    this.data.charCodeAt(this.read + 3));
+  this.read += 4;
+  return rval;
+};
+
+/**
+ * Gets a uint16 from this buffer in little-endian order and advances the read
+ * pointer by 2.
+ *
+ * @return the uint16.
+ */
+util.ByteStringBuffer.prototype.getInt16Le = function() {
+  var rval = (
+    this.data.charCodeAt(this.read) ^
+    this.data.charCodeAt(this.read + 1) << 8);
+  this.read += 2;
+  return rval;
+};
+
+/**
+ * Gets a uint24 from this buffer in little-endian order and advances the read
+ * pointer by 3.
+ *
+ * @return the uint24.
+ */
+util.ByteStringBuffer.prototype.getInt24Le = function() {
+  var rval = (
+    this.data.charCodeAt(this.read) ^
+    this.data.charCodeAt(this.read + 1) << 8 ^
+    this.data.charCodeAt(this.read + 2) << 16);
+  this.read += 3;
+  return rval;
+};
+
+/**
+ * Gets a uint32 from this buffer in little-endian order and advances the read
+ * pointer by 4.
+ *
+ * @return the word.
+ */
+util.ByteStringBuffer.prototype.getInt32Le = function() {
+  var rval = (
+    this.data.charCodeAt(this.read) ^
+    this.data.charCodeAt(this.read + 1) << 8 ^
+    this.data.charCodeAt(this.read + 2) << 16 ^
+    this.data.charCodeAt(this.read + 3) << 24);
+  this.read += 4;
+  return rval;
+};
+
+/**
+ * Gets an n-bit integer from this buffer in big-endian order and advances the
+ * read pointer by ceil(n/8).
+ *
+ * @param n the number of bits in the integer (8, 16, 24, or 32).
+ *
+ * @return the integer.
+ */
+util.ByteStringBuffer.prototype.getInt = function(n) {
+  _checkBitsParam(n);
+  var rval = 0;
+  do {
+    // TODO: Use (rval * 0x100) if adding support for 33 to 53 bits.
+    rval = (rval << 8) + this.data.charCodeAt(this.read++);
+    n -= 8;
+  } while(n > 0);
+  return rval;
+};
+
+/**
+ * Gets a signed n-bit integer from this buffer in big-endian order, using
+ * two's complement, and advances the read pointer by n/8.
+ *
+ * @param n the number of bits in the integer (8, 16, 24, or 32).
+ *
+ * @return the integer.
+ */
+util.ByteStringBuffer.prototype.getSignedInt = function(n) {
+  // getInt checks n
+  var x = this.getInt(n);
+  var max = 2 << (n - 2);
+  if(x >= max) {
+    x -= max << 1;
+  }
+  return x;
+};
+
+/**
+ * Reads bytes out as a binary encoded string and clears them from the
+ * buffer. Note that the resulting string is binary encoded (in node.js this
+ * encoding is referred to as `binary`, it is *not* `utf8`).
+ *
+ * @param count the number of bytes to read, undefined or null for all.
+ *
+ * @return a binary encoded string of bytes.
+ */
+util.ByteStringBuffer.prototype.getBytes = function(count) {
+  var rval;
+  if(count) {
+    // read count bytes
+    count = Math.min(this.length(), count);
+    rval = this.data.slice(this.read, this.read + count);
+    this.read += count;
+  } else if(count === 0) {
+    rval = '';
+  } else {
+    // read all bytes, optimize to only copy when needed
+    rval = (this.read === 0) ? this.data : this.data.slice(this.read);
+    this.clear();
+  }
+  return rval;
+};
+
+/**
+ * Gets a binary encoded string of the bytes from this buffer without
+ * modifying the read pointer.
+ *
+ * @param count the number of bytes to get, omit to get all.
+ *
+ * @return a string full of binary encoded characters.
+ */
+util.ByteStringBuffer.prototype.bytes = function(count) {
+  return (typeof(count) === 'undefined' ?
+    this.data.slice(this.read) :
+    this.data.slice(this.read, this.read + count));
+};
+
+/**
+ * Gets a byte at the given index without modifying the read pointer.
+ *
+ * @param i the byte index.
+ *
+ * @return the byte.
+ */
+util.ByteStringBuffer.prototype.at = function(i) {
+  return this.data.charCodeAt(this.read + i);
+};
+
+/**
+ * Puts a byte at the given index without modifying the read pointer.
+ *
+ * @param i the byte index.
+ * @param b the byte to put.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.setAt = function(i, b) {
+  this.data = this.data.substr(0, this.read + i) +
+    String.fromCharCode(b) +
+    this.data.substr(this.read + i + 1);
+  return this;
+};
+
+/**
+ * Gets the last byte without modifying the read pointer.
+ *
+ * @return the last byte.
+ */
+util.ByteStringBuffer.prototype.last = function() {
+  return this.data.charCodeAt(this.data.length - 1);
+};
+
+/**
+ * Creates a copy of this buffer.
+ *
+ * @return the copy.
+ */
+util.ByteStringBuffer.prototype.copy = function() {
+  var c = util.createBuffer(this.data);
+  c.read = this.read;
+  return c;
+};
+
+/**
+ * Compacts this buffer.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.compact = function() {
+  if(this.read > 0) {
+    this.data = this.data.slice(this.read);
+    this.read = 0;
+  }
+  return this;
+};
+
+/**
+ * Clears this buffer.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.clear = function() {
+  this.data = '';
+  this.read = 0;
+  return this;
+};
+
+/**
+ * Shortens this buffer by trimming bytes off of the end of this buffer.
+ *
+ * @param count the number of bytes to trim off.
+ *
+ * @return this buffer.
+ */
+util.ByteStringBuffer.prototype.truncate = function(count) {
+  var len = Math.max(0, this.length() - count);
+  this.data = this.data.substr(this.read, len);
+  this.read = 0;
+  return this;
+};
+
+/**
+ * Converts this buffer to a hexadecimal string.
+ *
+ * @return a hexadecimal string.
+ */
+util.ByteStringBuffer.prototype.toHex = function() {
+  var rval = '';
+  for(var i = this.read; i < this.data.length; ++i) {
+    var b = this.data.charCodeAt(i);
+    if(b < 16) {
+      rval += '0';
+    }
+    rval += b.toString(16);
+  }
+  return rval;
+};
+
+/**
+ * Converts this buffer to a UTF-16 string (standard JavaScript string).
+ *
+ * @return a UTF-16 string.
+ */
+util.ByteStringBuffer.prototype.toString = function() {
+  return util.decodeUtf8(this.bytes());
+};
+
+/** End Buffer w/BinaryString backing */
+
+/** Buffer w/UInt8Array backing */
+
+/**
+ * FIXME: Experimental. Do not use yet.
+ *
+ * Constructor for an ArrayBuffer-backed byte buffer.
+ *
+ * The buffer may be constructed from a string, an ArrayBuffer, DataView, or a
+ * TypedArray.
+ *
+ * If a string is given, its encoding should be provided as an option,
+ * otherwise it will default to 'binary'. A 'binary' string is encoded such
+ * that each character is one byte in length and size.
+ *
+ * If an ArrayBuffer, DataView, or TypedArray is given, it will be used
+ * *directly* without any copying. Note that, if a write to the buffer requires
+ * more space, the buffer will allocate a new backing ArrayBuffer to
+ * accommodate. The starting read and write offsets for the buffer may be
+ * given as options.
+ *
+ * @param [b] the initial bytes for this buffer.
+ * @param options the options to use:
+ *          [readOffset] the starting read offset to use (default: 0).
+ *          [writeOffset] the starting write offset to use (default: the
+ *            length of the first parameter).
+ *          [growSize] the minimum amount, in bytes, to grow the buffer by to
+ *            accommodate writes (default: 1024).
+ *          [encoding] the encoding ('binary', 'utf8', 'utf16', 'hex') for the
+ *            first parameter, if it is a string (default: 'binary').
+ */
+function DataBuffer(b, options) {
+  // default options
+  options = options || {};
+
+  // pointers for read from/write to buffer
+  this.read = options.readOffset || 0;
+  this.growSize = options.growSize || 1024;
+
+  var isArrayBuffer = util.isArrayBuffer(b);
+  var isArrayBufferView = util.isArrayBufferView(b);
+  if(isArrayBuffer || isArrayBufferView) {
+    // use ArrayBuffer directly
+    if(isArrayBuffer) {
+      this.data = new DataView(b);
+    } else {
+      // TODO: adjust read/write offset based on the type of view
+      // or specify that this must be done in the options ... that the
+      // offsets are byte-based
+      this.data = new DataView(b.buffer, b.byteOffset, b.byteLength);
+    }
+    this.write = ('writeOffset' in options ?
+      options.writeOffset : this.data.byteLength);
+    return;
+  }
+
+  // initialize to empty array buffer and add any given bytes using putBytes
+  this.data = new DataView(new ArrayBuffer(0));
+  this.write = 0;
+
+  if(b !== null && b !== undefined) {
+    this.putBytes(b);
+  }
+
+  if('writeOffset' in options) {
+    this.write = options.writeOffset;
+  }
+}
+util.DataBuffer = DataBuffer;
+
+/**
+ * Gets the number of bytes in this buffer.
+ *
+ * @return the number of bytes in this buffer.
+ */
+util.DataBuffer.prototype.length = function() {
+  return this.write - this.read;
+};
+
+/**
+ * Gets whether or not this buffer is empty.
+ *
+ * @return true if this buffer is empty, false if not.
+ */
+util.DataBuffer.prototype.isEmpty = function() {
+  return this.length() <= 0;
+};
+
+/**
+ * Ensures this buffer has enough empty space to accommodate the given number
+ * of bytes. An optional parameter may be given that indicates a minimum
+ * amount to grow the buffer if necessary. If the parameter is not given,
+ * the buffer will be grown by some previously-specified default amount
+ * or heuristic.
+ *
+ * @param amount the number of bytes to accommodate.
+ * @param [growSize] the minimum amount, in bytes, to grow the buffer by if
+ *          necessary.
+ */
+util.DataBuffer.prototype.accommodate = function(amount, growSize) {
+  if(this.length() >= amount) {
+    return this;
+  }
+  growSize = Math.max(growSize || this.growSize, amount);
+
+  // grow buffer
+  var src = new Uint8Array(
+    this.data.buffer, this.data.byteOffset, this.data.byteLength);
+  var dst = new Uint8Array(this.length() + growSize);
+  dst.set(src);
+  this.data = new DataView(dst.buffer);
+
+  return this;
+};
+
+/**
+ * Puts a byte in this buffer.
+ *
+ * @param b the byte to put.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putByte = function(b) {
+  this.accommodate(1);
+  this.data.setUint8(this.write++, b);
+  return this;
+};
+
+/**
+ * Puts a byte in this buffer N times.
+ *
+ * @param b the byte to put.
+ * @param n the number of bytes of value b to put.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.fillWithByte = function(b, n) {
+  this.accommodate(n);
+  for(var i = 0; i < n; ++i) {
+    this.data.setUint8(b);
+  }
+  return this;
+};
+
+/**
+ * Puts bytes in this buffer. The bytes may be given as a string, an
+ * ArrayBuffer, a DataView, or a TypedArray.
+ *
+ * @param bytes the bytes to put.
+ * @param [encoding] the encoding for the first parameter ('binary', 'utf8',
+ *          'utf16', 'hex'), if it is a string (default: 'binary').
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putBytes = function(bytes, encoding) {
+  if(util.isArrayBufferView(bytes)) {
+    var src = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+    var len = src.byteLength - src.byteOffset;
+    this.accommodate(len);
+    var dst = new Uint8Array(this.data.buffer, this.write);
+    dst.set(src);
+    this.write += len;
+    return this;
+  }
+
+  if(util.isArrayBuffer(bytes)) {
+    var src = new Uint8Array(bytes);
+    this.accommodate(src.byteLength);
+    var dst = new Uint8Array(this.data.buffer);
+    dst.set(src, this.write);
+    this.write += src.byteLength;
+    return this;
+  }
+
+  // bytes is a util.DataBuffer or equivalent
+  if(bytes instanceof util.DataBuffer ||
+    (typeof bytes === 'object' &&
+    typeof bytes.read === 'number' && typeof bytes.write === 'number' &&
+    util.isArrayBufferView(bytes.data))) {
+    var src = new Uint8Array(bytes.data.byteLength, bytes.read, bytes.length());
+    this.accommodate(src.byteLength);
+    var dst = new Uint8Array(bytes.data.byteLength, this.write);
+    dst.set(src);
+    this.write += src.byteLength;
+    return this;
+  }
+
+  if(bytes instanceof util.ByteStringBuffer) {
+    // copy binary string and process as the same as a string parameter below
+    bytes = bytes.data;
+    encoding = 'binary';
+  }
+
+  // string conversion
+  encoding = encoding || 'binary';
+  if(typeof bytes === 'string') {
+    var view;
+
+    // decode from string
+    if(encoding === 'hex') {
+      this.accommodate(Math.ceil(bytes.length / 2));
+      view = new Uint8Array(this.data.buffer, this.write);
+      this.write += util.binary.hex.decode(bytes, view, this.write);
+      return this;
+    }
+    if(encoding === 'base64') {
+      this.accommodate(Math.ceil(bytes.length / 4) * 3);
+      view = new Uint8Array(this.data.buffer, this.write);
+      this.write += util.binary.base64.decode(bytes, view, this.write);
+      return this;
+    }
+
+    // encode text as UTF-8 bytes
+    if(encoding === 'utf8') {
+      // encode as UTF-8 then decode string as raw binary
+      bytes = util.encodeUtf8(bytes);
+      encoding = 'binary';
+    }
+
+    // decode string as raw binary
+    if(encoding === 'binary' || encoding === 'raw') {
+      // one byte per character
+      this.accommodate(bytes.length);
+      view = new Uint8Array(this.data.buffer, this.write);
+      this.write += util.binary.raw.decode(view);
+      return this;
+    }
+
+    // encode text as UTF-16 bytes
+    if(encoding === 'utf16') {
+      // two bytes per character
+      this.accommodate(bytes.length * 2);
+      view = new Uint16Array(this.data.buffer, this.write);
+      this.write += util.text.utf16.encode(view);
+      return this;
+    }
+
+    throw new Error('Invalid encoding: ' + encoding);
+  }
+
+  throw Error('Invalid parameter: ' + bytes);
+};
+
+/**
+ * Puts the given buffer into this buffer.
+ *
+ * @param buffer the buffer to put into this one.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putBuffer = function(buffer) {
+  this.putBytes(buffer);
+  buffer.clear();
+  return this;
+};
+
+/**
+ * Puts a string into this buffer.
+ *
+ * @param str the string to put.
+ * @param [encoding] the encoding for the string (default: 'utf16').
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putString = function(str) {
+  return this.putBytes(str, 'utf16');
+};
+
+/**
+ * Puts a 16-bit integer in this buffer in big-endian order.
+ *
+ * @param i the 16-bit integer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putInt16 = function(i) {
+  this.accommodate(2);
+  this.data.setInt16(this.write, i);
+  this.write += 2;
+  return this;
+};
+
+/**
+ * Puts a 24-bit integer in this buffer in big-endian order.
+ *
+ * @param i the 24-bit integer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putInt24 = function(i) {
+  this.accommodate(3);
+  this.data.setInt16(this.write, i >> 8 & 0xFFFF);
+  this.data.setInt8(this.write, i >> 16 & 0xFF);
+  this.write += 3;
+  return this;
+};
+
+/**
+ * Puts a 32-bit integer in this buffer in big-endian order.
+ *
+ * @param i the 32-bit integer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putInt32 = function(i) {
+  this.accommodate(4);
+  this.data.setInt32(this.write, i);
+  this.write += 4;
+  return this;
+};
+
+/**
+ * Puts a 16-bit integer in this buffer in little-endian order.
+ *
+ * @param i the 16-bit integer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putInt16Le = function(i) {
+  this.accommodate(2);
+  this.data.setInt16(this.write, i, true);
+  this.write += 2;
+  return this;
+};
+
+/**
+ * Puts a 24-bit integer in this buffer in little-endian order.
+ *
+ * @param i the 24-bit integer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putInt24Le = function(i) {
+  this.accommodate(3);
+  this.data.setInt8(this.write, i >> 16 & 0xFF);
+  this.data.setInt16(this.write, i >> 8 & 0xFFFF, true);
+  this.write += 3;
+  return this;
+};
+
+/**
+ * Puts a 32-bit integer in this buffer in little-endian order.
+ *
+ * @param i the 32-bit integer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putInt32Le = function(i) {
+  this.accommodate(4);
+  this.data.setInt32(this.write, i, true);
+  this.write += 4;
+  return this;
+};
+
+/**
+ * Puts an n-bit integer in this buffer in big-endian order.
+ *
+ * @param i the n-bit integer.
+ * @param n the number of bits in the integer (8, 16, 24, or 32).
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putInt = function(i, n) {
+  _checkBitsParam(n);
+  this.accommodate(n / 8);
+  do {
+    n -= 8;
+    this.data.setInt8(this.write++, (i >> n) & 0xFF);
+  } while(n > 0);
+  return this;
+};
+
+/**
+ * Puts a signed n-bit integer in this buffer in big-endian order. Two's
+ * complement representation is used.
+ *
+ * @param i the n-bit integer.
+ * @param n the number of bits in the integer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.putSignedInt = function(i, n) {
+  _checkBitsParam(n);
+  this.accommodate(n / 8);
+  if(i < 0) {
+    i += 2 << (n - 1);
+  }
+  return this.putInt(i, n);
+};
+
+/**
+ * Gets a byte from this buffer and advances the read pointer by 1.
+ *
+ * @return the byte.
+ */
+util.DataBuffer.prototype.getByte = function() {
+  return this.data.getInt8(this.read++);
+};
+
+/**
+ * Gets a uint16 from this buffer in big-endian order and advances the read
+ * pointer by 2.
+ *
+ * @return the uint16.
+ */
+util.DataBuffer.prototype.getInt16 = function() {
+  var rval = this.data.getInt16(this.read);
+  this.read += 2;
+  return rval;
+};
+
+/**
+ * Gets a uint24 from this buffer in big-endian order and advances the read
+ * pointer by 3.
+ *
+ * @return the uint24.
+ */
+util.DataBuffer.prototype.getInt24 = function() {
+  var rval = (
+    this.data.getInt16(this.read) << 8 ^
+    this.data.getInt8(this.read + 2));
+  this.read += 3;
+  return rval;
+};
+
+/**
+ * Gets a uint32 from this buffer in big-endian order and advances the read
+ * pointer by 4.
+ *
+ * @return the word.
+ */
+util.DataBuffer.prototype.getInt32 = function() {
+  var rval = this.data.getInt32(this.read);
+  this.read += 4;
+  return rval;
+};
+
+/**
+ * Gets a uint16 from this buffer in little-endian order and advances the read
+ * pointer by 2.
+ *
+ * @return the uint16.
+ */
+util.DataBuffer.prototype.getInt16Le = function() {
+  var rval = this.data.getInt16(this.read, true);
+  this.read += 2;
+  return rval;
+};
+
+/**
+ * Gets a uint24 from this buffer in little-endian order and advances the read
+ * pointer by 3.
+ *
+ * @return the uint24.
+ */
+util.DataBuffer.prototype.getInt24Le = function() {
+  var rval = (
+    this.data.getInt8(this.read) ^
+    this.data.getInt16(this.read + 1, true) << 8);
+  this.read += 3;
+  return rval;
+};
+
+/**
+ * Gets a uint32 from this buffer in little-endian order and advances the read
+ * pointer by 4.
+ *
+ * @return the word.
+ */
+util.DataBuffer.prototype.getInt32Le = function() {
+  var rval = this.data.getInt32(this.read, true);
+  this.read += 4;
+  return rval;
+};
+
+/**
+ * Gets an n-bit integer from this buffer in big-endian order and advances the
+ * read pointer by n/8.
+ *
+ * @param n the number of bits in the integer (8, 16, 24, or 32).
+ *
+ * @return the integer.
+ */
+util.DataBuffer.prototype.getInt = function(n) {
+  _checkBitsParam(n);
+  var rval = 0;
+  do {
+    // TODO: Use (rval * 0x100) if adding support for 33 to 53 bits.
+    rval = (rval << 8) + this.data.getInt8(this.read++);
+    n -= 8;
+  } while(n > 0);
+  return rval;
+};
+
+/**
+ * Gets a signed n-bit integer from this buffer in big-endian order, using
+ * two's complement, and advances the read pointer by n/8.
+ *
+ * @param n the number of bits in the integer (8, 16, 24, or 32).
+ *
+ * @return the integer.
+ */
+util.DataBuffer.prototype.getSignedInt = function(n) {
+  // getInt checks n
+  var x = this.getInt(n);
+  var max = 2 << (n - 2);
+  if(x >= max) {
+    x -= max << 1;
+  }
+  return x;
+};
+
+/**
+ * Reads bytes out as a binary encoded string and clears them from the
+ * buffer.
+ *
+ * @param count the number of bytes to read, undefined or null for all.
+ *
+ * @return a binary encoded string of bytes.
+ */
+util.DataBuffer.prototype.getBytes = function(count) {
+  // TODO: deprecate this method, it is poorly named and
+  // this.toString('binary') replaces it
+  // add a toTypedArray()/toArrayBuffer() function
+  var rval;
+  if(count) {
+    // read count bytes
+    count = Math.min(this.length(), count);
+    rval = this.data.slice(this.read, this.read + count);
+    this.read += count;
+  } else if(count === 0) {
+    rval = '';
+  } else {
+    // read all bytes, optimize to only copy when needed
+    rval = (this.read === 0) ? this.data : this.data.slice(this.read);
+    this.clear();
+  }
+  return rval;
+};
+
+/**
+ * Gets a binary encoded string of the bytes from this buffer without
+ * modifying the read pointer.
+ *
+ * @param count the number of bytes to get, omit to get all.
+ *
+ * @return a string full of binary encoded characters.
+ */
+util.DataBuffer.prototype.bytes = function(count) {
+  // TODO: deprecate this method, it is poorly named, add "getString()"
+  return (typeof(count) === 'undefined' ?
+    this.data.slice(this.read) :
+    this.data.slice(this.read, this.read + count));
+};
+
+/**
+ * Gets a byte at the given index without modifying the read pointer.
+ *
+ * @param i the byte index.
+ *
+ * @return the byte.
+ */
+util.DataBuffer.prototype.at = function(i) {
+  return this.data.getUint8(this.read + i);
+};
+
+/**
+ * Puts a byte at the given index without modifying the read pointer.
+ *
+ * @param i the byte index.
+ * @param b the byte to put.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.setAt = function(i, b) {
+  this.data.setUint8(i, b);
+  return this;
+};
+
+/**
+ * Gets the last byte without modifying the read pointer.
+ *
+ * @return the last byte.
+ */
+util.DataBuffer.prototype.last = function() {
+  return this.data.getUint8(this.write - 1);
+};
+
+/**
+ * Creates a copy of this buffer.
+ *
+ * @return the copy.
+ */
+util.DataBuffer.prototype.copy = function() {
+  return new util.DataBuffer(this);
+};
+
+/**
+ * Compacts this buffer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.compact = function() {
+  if(this.read > 0) {
+    var src = new Uint8Array(this.data.buffer, this.read);
+    var dst = new Uint8Array(src.byteLength);
+    dst.set(src);
+    this.data = new DataView(dst);
+    this.write -= this.read;
+    this.read = 0;
+  }
+  return this;
+};
+
+/**
+ * Clears this buffer.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.clear = function() {
+  this.data = new DataView(new ArrayBuffer(0));
+  this.read = this.write = 0;
+  return this;
+};
+
+/**
+ * Shortens this buffer by trimming bytes off of the end of this buffer.
+ *
+ * @param count the number of bytes to trim off.
+ *
+ * @return this buffer.
+ */
+util.DataBuffer.prototype.truncate = function(count) {
+  this.write = Math.max(0, this.length() - count);
+  this.read = Math.min(this.read, this.write);
+  return this;
+};
+
+/**
+ * Converts this buffer to a hexadecimal string.
+ *
+ * @return a hexadecimal string.
+ */
+util.DataBuffer.prototype.toHex = function() {
+  var rval = '';
+  for(var i = this.read; i < this.data.byteLength; ++i) {
+    var b = this.data.getUint8(i);
+    if(b < 16) {
+      rval += '0';
+    }
+    rval += b.toString(16);
+  }
+  return rval;
+};
+
+/**
+ * Converts this buffer to a string, using the given encoding. If no
+ * encoding is given, 'utf8' (UTF-8) is used.
+ *
+ * @param [encoding] the encoding to use: 'binary', 'utf8', 'utf16', 'hex',
+ *          'base64' (default: 'utf8').
+ *
+ * @return a string representation of the bytes in this buffer.
+ */
+util.DataBuffer.prototype.toString = function(encoding) {
+  var view = new Uint8Array(this.data, this.read, this.length());
+  encoding = encoding || 'utf8';
+
+  // encode to string
+  if(encoding === 'binary' || encoding === 'raw') {
+    return util.binary.raw.encode(view);
+  }
+  if(encoding === 'hex') {
+    return util.binary.hex.encode(view);
+  }
+  if(encoding === 'base64') {
+    return util.binary.base64.encode(view);
+  }
+
+  // decode to text
+  if(encoding === 'utf8') {
+    return util.text.utf8.decode(view);
+  }
+  if(encoding === 'utf16') {
+    return util.text.utf16.decode(view);
+  }
+
+  throw new Error('Invalid encoding: ' + encoding);
+};
+
+/** End Buffer w/UInt8Array backing */
+
+/**
+ * Creates a buffer that stores bytes. A value may be given to populate the
+ * buffer with data. This value can either be string of encoded bytes or a
+ * regular string of characters. When passing a string of binary encoded
+ * bytes, the encoding `raw` should be given. This is also the default. When
+ * passing a string of characters, the encoding `utf8` should be given.
+ *
+ * @param [input] a string with encoded bytes to store in the buffer.
+ * @param [encoding] (default: 'raw', other: 'utf8').
+ */
+util.createBuffer = function(input, encoding) {
+  // TODO: deprecate, use new ByteBuffer() instead
+  encoding = encoding || 'raw';
+  if(input !== undefined && encoding === 'utf8') {
+    input = util.encodeUtf8(input);
+  }
+  return new util.ByteBuffer(input);
+};
+
+/**
+ * Fills a string with a particular value. If you want the string to be a byte
+ * string, pass in String.fromCharCode(theByte).
+ *
+ * @param c the character to fill the string with, use String.fromCharCode
+ *          to fill the string with a byte value.
+ * @param n the number of characters of value c to fill with.
+ *
+ * @return the filled string.
+ */
+util.fillString = function(c, n) {
+  var s = '';
+  while(n > 0) {
+    if(n & 1) {
+      s += c;
+    }
+    n >>>= 1;
+    if(n > 0) {
+      c += c;
+    }
+  }
+  return s;
+};
+
+/**
+ * Performs a per byte XOR between two byte strings and returns the result as a
+ * string of bytes.
+ *
+ * @param s1 first string of bytes.
+ * @param s2 second string of bytes.
+ * @param n the number of bytes to XOR.
+ *
+ * @return the XOR'd result.
+ */
+util.xorBytes = function(s1, s2, n) {
+  var s3 = '';
+  var b = '';
+  var t = '';
+  var i = 0;
+  var c = 0;
+  for(; n > 0; --n, ++i) {
+    b = s1.charCodeAt(i) ^ s2.charCodeAt(i);
+    if(c >= 10) {
+      s3 += t;
+      t = '';
+      c = 0;
+    }
+    t += String.fromCharCode(b);
+    ++c;
+  }
+  s3 += t;
+  return s3;
+};
+
+/**
+ * Converts a hex string into a 'binary' encoded string of bytes.
+ *
+ * @param hex the hexadecimal string to convert.
+ *
+ * @return the binary-encoded string of bytes.
+ */
+util.hexToBytes = function(hex) {
+  // TODO: deprecate: "Deprecated. Use util.binary.hex.decode instead."
+  var rval = '';
+  var i = 0;
+  if(hex.length & 1 == 1) {
+    // odd number of characters, convert first character alone
+    i = 1;
+    rval += String.fromCharCode(parseInt(hex[0], 16));
+  }
+  // convert 2 characters (1 byte) at a time
+  for(; i < hex.length; i += 2) {
+    rval += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+  }
+  return rval;
+};
+
+/**
+ * Converts a 'binary' encoded string of bytes to hex.
+ *
+ * @param bytes the byte string to convert.
+ *
+ * @return the string of hexadecimal characters.
+ */
+util.bytesToHex = function(bytes) {
+  // TODO: deprecate: "Deprecated. Use util.binary.hex.encode instead."
+  return util.createBuffer(bytes).toHex();
+};
+
+/**
+ * Converts an 32-bit integer to 4-big-endian byte string.
+ *
+ * @param i the integer.
+ *
+ * @return the byte string.
+ */
+util.int32ToBytes = function(i) {
+  return (
+    String.fromCharCode(i >> 24 & 0xFF) +
+    String.fromCharCode(i >> 16 & 0xFF) +
+    String.fromCharCode(i >> 8 & 0xFF) +
+    String.fromCharCode(i & 0xFF));
+};
+
+// base64 characters, reverse mapping
+var _base64 =
+  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+var _base64Idx = [
+/*43 -43 = 0*/
+/*'+',  1,  2,  3,'/' */
+   62, -1, -1, -1, 63,
+
+/*'0','1','2','3','4','5','6','7','8','9' */
+   52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+
+/*15, 16, 17,'=', 19, 20, 21 */
+  -1, -1, -1, 64, -1, -1, -1,
+
+/*65 - 43 = 22*/
+/*'A','B','C','D','E','F','G','H','I','J','K','L','M', */
+   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
+
+/*'N','O','P','Q','R','S','T','U','V','W','X','Y','Z' */
+   13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+
+/*91 - 43 = 48 */
+/*48, 49, 50, 51, 52, 53 */
+  -1, -1, -1, -1, -1, -1,
+
+/*97 - 43 = 54*/
+/*'a','b','c','d','e','f','g','h','i','j','k','l','m' */
+   26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+
+/*'n','o','p','q','r','s','t','u','v','w','x','y','z' */
+   39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+];
+
+// base58 characters (Bitcoin alphabet)
+var _base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
+
+/**
+ * Base64 encodes a 'binary' encoded string of bytes.
+ *
+ * @param input the binary encoded string of bytes to base64-encode.
+ * @param maxline the maximum number of encoded characters per line to use,
+ *          defaults to none.
+ *
+ * @return the base64-encoded output.
+ */
+util.encode64 = function(input, maxline) {
+  // TODO: deprecate: "Deprecated. Use util.binary.base64.encode instead."
+  var line = '';
+  var output = '';
+  var chr1, chr2, chr3;
+  var i = 0;
+  while(i < input.length) {
+    chr1 = input.charCodeAt(i++);
+    chr2 = input.charCodeAt(i++);
+    chr3 = input.charCodeAt(i++);
+
+    // encode 4 character group
+    line += _base64.charAt(chr1 >> 2);
+    line += _base64.charAt(((chr1 & 3) << 4) | (chr2 >> 4));
+    if(isNaN(chr2)) {
+      line += '==';
+    } else {
+      line += _base64.charAt(((chr2 & 15) << 2) | (chr3 >> 6));
+      line += isNaN(chr3) ? '=' : _base64.charAt(chr3 & 63);
+    }
+
+    if(maxline && line.length > maxline) {
+      output += line.substr(0, maxline) + '\r\n';
+      line = line.substr(maxline);
+    }
+  }
+  output += line;
+  return output;
+};
+
+/**
+ * Base64 decodes a string into a 'binary' encoded string of bytes.
+ *
+ * @param input the base64-encoded input.
+ *
+ * @return the binary encoded string.
+ */
+util.decode64 = function(input) {
+  // TODO: deprecate: "Deprecated. Use util.binary.base64.decode instead."
+
+  // remove all non-base64 characters
+  input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
+
+  var output = '';
+  var enc1, enc2, enc3, enc4;
+  var i = 0;
+
+  while(i < input.length) {
+    enc1 = _base64Idx[input.charCodeAt(i++) - 43];
+    enc2 = _base64Idx[input.charCodeAt(i++) - 43];
+    enc3 = _base64Idx[input.charCodeAt(i++) - 43];
+    enc4 = _base64Idx[input.charCodeAt(i++) - 43];
+
+    output += String.fromCharCode((enc1 << 2) | (enc2 >> 4));
+    if(enc3 !== 64) {
+      // decoded at least 2 bytes
+      output += String.fromCharCode(((enc2 & 15) << 4) | (enc3 >> 2));
+      if(enc4 !== 64) {
+        // decoded 3 bytes
+        output += String.fromCharCode(((enc3 & 3) << 6) | enc4);
+      }
+    }
+  }
+
+  return output;
+};
+
+/**
+ * Encodes the given string of characters (a standard JavaScript
+ * string) as a binary encoded string where the bytes represent
+ * a UTF-8 encoded string of characters. Non-ASCII characters will be
+ * encoded as multiple bytes according to UTF-8.
+ *
+ * @param str a standard string of characters to encode.
+ *
+ * @return the binary encoded string.
+ */
+util.encodeUtf8 = function(str) {
+  return unescape(encodeURIComponent(str));
+};
+
+/**
+ * Decodes a binary encoded string that contains bytes that
+ * represent a UTF-8 encoded string of characters -- into a
+ * string of characters (a standard JavaScript string).
+ *
+ * @param str the binary encoded string to decode.
+ *
+ * @return the resulting standard string of characters.
+ */
+util.decodeUtf8 = function(str) {
+  return decodeURIComponent(escape(str));
+};
+
+// binary encoding/decoding tools
+// FIXME: Experimental. Do not use yet.
+util.binary = {
+  raw: {},
+  hex: {},
+  base64: {},
+  base58: {},
+  baseN : {
+    encode: baseN.encode,
+    decode: baseN.decode
+  }
+};
+
+/**
+ * Encodes a Uint8Array as a binary-encoded string. This encoding uses
+ * a value between 0 and 255 for each character.
+ *
+ * @param bytes the Uint8Array to encode.
+ *
+ * @return the binary-encoded string.
+ */
+util.binary.raw.encode = function(bytes) {
+  return String.fromCharCode.apply(null, bytes);
+};
+
+/**
+ * Decodes a binary-encoded string to a Uint8Array. This encoding uses
+ * a value between 0 and 255 for each character.
+ *
+ * @param str the binary-encoded string to decode.
+ * @param [output] an optional Uint8Array to write the output to; if it
+ *          is too small, an exception will be thrown.
+ * @param [offset] the start offset for writing to the output (default: 0).
+ *
+ * @return the Uint8Array or the number of bytes written if output was given.
+ */
+util.binary.raw.decode = function(str, output, offset) {
+  var out = output;
+  if(!out) {
+    out = new Uint8Array(str.length);
+  }
+  offset = offset || 0;
+  var j = offset;
+  for(var i = 0; i < str.length; ++i) {
+    out[j++] = str.charCodeAt(i);
+  }
+  return output ? (j - offset) : out;
+};
+
+/**
+ * Encodes a 'binary' string, ArrayBuffer, DataView, TypedArray, or
+ * ByteBuffer as a string of hexadecimal characters.
+ *
+ * @param bytes the bytes to convert.
+ *
+ * @return the string of hexadecimal characters.
+ */
+util.binary.hex.encode = util.bytesToHex;
+
+/**
+ * Decodes a hex-encoded string to a Uint8Array.
+ *
+ * @param hex the hexadecimal string to convert.
+ * @param [output] an optional Uint8Array to write the output to; if it
+ *          is too small, an exception will be thrown.
+ * @param [offset] the start offset for writing to the output (default: 0).
+ *
+ * @return the Uint8Array or the number of bytes written if output was given.
+ */
+util.binary.hex.decode = function(hex, output, offset) {
+  var out = output;
+  if(!out) {
+    out = new Uint8Array(Math.ceil(hex.length / 2));
+  }
+  offset = offset || 0;
+  var i = 0, j = offset;
+  if(hex.length & 1) {
+    // odd number of characters, convert first character alone
+    i = 1;
+    out[j++] = parseInt(hex[0], 16);
+  }
+  // convert 2 characters (1 byte) at a time
+  for(; i < hex.length; i += 2) {
+    out[j++] = parseInt(hex.substr(i, 2), 16);
+  }
+  return output ? (j - offset) : out;
+};
+
+/**
+ * Base64-encodes a Uint8Array.
+ *
+ * @param input the Uint8Array to encode.
+ * @param maxline the maximum number of encoded characters per line to use,
+ *          defaults to none.
+ *
+ * @return the base64-encoded output string.
+ */
+util.binary.base64.encode = function(input, maxline) {
+  var line = '';
+  var output = '';
+  var chr1, chr2, chr3;
+  var i = 0;
+  while(i < input.byteLength) {
+    chr1 = input[i++];
+    chr2 = input[i++];
+    chr3 = input[i++];
+
+    // encode 4 character group
+    line += _base64.charAt(chr1 >> 2);
+    line += _base64.charAt(((chr1 & 3) << 4) | (chr2 >> 4));
+    if(isNaN(chr2)) {
+      line += '==';
+    } else {
+      line += _base64.charAt(((chr2 & 15) << 2) | (chr3 >> 6));
+      line += isNaN(chr3) ? '=' : _base64.charAt(chr3 & 63);
+    }
+
+    if(maxline && line.length > maxline) {
+      output += line.substr(0, maxline) + '\r\n';
+      line = line.substr(maxline);
+    }
+  }
+  output += line;
+  return output;
+};
+
+/**
+ * Decodes a base64-encoded string to a Uint8Array.
+ *
+ * @param input the base64-encoded input string.
+ * @param [output] an optional Uint8Array to write the output to; if it
+ *          is too small, an exception will be thrown.
+ * @param [offset] the start offset for writing to the output (default: 0).
+ *
+ * @return the Uint8Array or the number of bytes written if output was given.
+ */
+util.binary.base64.decode = function(input, output, offset) {
+  var out = output;
+  if(!out) {
+    out = new Uint8Array(Math.ceil(input.length / 4) * 3);
+  }
+
+  // remove all non-base64 characters
+  input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
+
+  offset = offset || 0;
+  var enc1, enc2, enc3, enc4;
+  var i = 0, j = offset;
+
+  while(i < input.length) {
+    enc1 = _base64Idx[input.charCodeAt(i++) - 43];
+    enc2 = _base64Idx[input.charCodeAt(i++) - 43];
+    enc3 = _base64Idx[input.charCodeAt(i++) - 43];
+    enc4 = _base64Idx[input.charCodeAt(i++) - 43];
+
+    out[j++] = (enc1 << 2) | (enc2 >> 4);
+    if(enc3 !== 64) {
+      // decoded at least 2 bytes
+      out[j++] = ((enc2 & 15) << 4) | (enc3 >> 2);
+      if(enc4 !== 64) {
+        // decoded 3 bytes
+        out[j++] = ((enc3 & 3) << 6) | enc4;
+      }
+    }
+  }
+
+  // make sure result is the exact decoded length
+  return output ? (j - offset) : out.subarray(0, j);
+};
+
+// add support for base58 encoding/decoding with Bitcoin alphabet
+util.binary.base58.encode = function(input, maxline) {
+  return util.binary.baseN.encode(input, _base58, maxline);
+};
+util.binary.base58.decode = function(input, maxline) {
+  return util.binary.baseN.decode(input, _base58, maxline);
+};
+
+// text encoding/decoding tools
+// FIXME: Experimental. Do not use yet.
+util.text = {
+  utf8: {},
+  utf16: {}
+};
+
+/**
+ * Encodes the given string as UTF-8 in a Uint8Array.
+ *
+ * @param str the string to encode.
+ * @param [output] an optional Uint8Array to write the output to; if it
+ *          is too small, an exception will be thrown.
+ * @param [offset] the start offset for writing to the output (default: 0).
+ *
+ * @return the Uint8Array or the number of bytes written if output was given.
+ */
+util.text.utf8.encode = function(str, output, offset) {
+  str = util.encodeUtf8(str);
+  var out = output;
+  if(!out) {
+    out = new Uint8Array(str.length);
+  }
+  offset = offset || 0;
+  var j = offset;
+  for(var i = 0; i < str.length; ++i) {
+    out[j++] = str.charCodeAt(i);
+  }
+  return output ? (j - offset) : out;
+};
+
+/**
+ * Decodes the UTF-8 contents from a Uint8Array.
+ *
+ * @param bytes the Uint8Array to decode.
+ *
+ * @return the resulting string.
+ */
+util.text.utf8.decode = function(bytes) {
+  return util.decodeUtf8(String.fromCharCode.apply(null, bytes));
+};
+
+/**
+ * Encodes the given string as UTF-16 in a Uint8Array.
+ *
+ * @param str the string to encode.
+ * @param [output] an optional Uint8Array to write the output to; if it
+ *          is too small, an exception will be thrown.
+ * @param [offset] the start offset for writing to the output (default: 0).
+ *
+ * @return the Uint8Array or the number of bytes written if output was given.
+ */
+util.text.utf16.encode = function(str, output, offset) {
+  var out = output;
+  if(!out) {
+    out = new Uint8Array(str.length * 2);
+  }
+  var view = new Uint16Array(out.buffer);
+  offset = offset || 0;
+  var j = offset;
+  var k = offset;
+  for(var i = 0; i < str.length; ++i) {
+    view[k++] = str.charCodeAt(i);
+    j += 2;
+  }
+  return output ? (j - offset) : out;
+};
+
+/**
+ * Decodes the UTF-16 contents from a Uint8Array.
+ *
+ * @param bytes the Uint8Array to decode.
+ *
+ * @return the resulting string.
+ */
+util.text.utf16.decode = function(bytes) {
+  return String.fromCharCode.apply(null, new Uint16Array(bytes.buffer));
+};
+
+/**
+ * Deflates the given data using a flash interface.
+ *
+ * @param api the flash interface.
+ * @param bytes the data.
+ * @param raw true to return only raw deflate data, false to include zlib
+ *          header and trailer.
+ *
+ * @return the deflated data as a string.
+ */
+util.deflate = function(api, bytes, raw) {
+  bytes = util.decode64(api.deflate(util.encode64(bytes)).rval);
+
+  // strip zlib header and trailer if necessary
+  if(raw) {
+    // zlib header is 2 bytes (CMF,FLG) where FLG indicates that
+    // there is a 4-byte DICT (alder-32) block before the data if
+    // its 5th bit is set
+    var start = 2;
+    var flg = bytes.charCodeAt(1);
+    if(flg & 0x20) {
+      start = 6;
+    }
+    // zlib trailer is 4 bytes of adler-32
+    bytes = bytes.substring(start, bytes.length - 4);
+  }
+
+  return bytes;
+};
+
+/**
+ * Inflates the given data using a flash interface.
+ *
+ * @param api the flash interface.
+ * @param bytes the data.
+ * @param raw true if the incoming data has no zlib header or trailer and is
+ *          raw DEFLATE data.
+ *
+ * @return the inflated data as a string, null on error.
+ */
+util.inflate = function(api, bytes, raw) {
+  // TODO: add zlib header and trailer if necessary/possible
+  var rval = api.inflate(util.encode64(bytes)).rval;
+  return (rval === null) ? null : util.decode64(rval);
+};
+
+/**
+ * Sets a storage object.
+ *
+ * @param api the storage interface.
+ * @param id the storage ID to use.
+ * @param obj the storage object, null to remove.
+ */
+var _setStorageObject = function(api, id, obj) {
+  if(!api) {
+    throw new Error('WebStorage not available.');
+  }
+
+  var rval;
+  if(obj === null) {
+    rval = api.removeItem(id);
+  } else {
+    // json-encode and base64-encode object
+    obj = util.encode64(JSON.stringify(obj));
+    rval = api.setItem(id, obj);
+  }
+
+  // handle potential flash error
+  if(typeof(rval) !== 'undefined' && rval.rval !== true) {
+    var error = new Error(rval.error.message);
+    error.id = rval.error.id;
+    error.name = rval.error.name;
+    throw error;
+  }
+};
+
+/**
+ * Gets a storage object.
+ *
+ * @param api the storage interface.
+ * @param id the storage ID to use.
+ *
+ * @return the storage object entry or null if none exists.
+ */
+var _getStorageObject = function(api, id) {
+  if(!api) {
+    throw new Error('WebStorage not available.');
+  }
+
+  // get the existing entry
+  var rval = api.getItem(id);
+
+  /* Note: We check api.init because we can't do (api == localStorage)
+    on IE because of "Class doesn't support Automation" exception. Only
+    the flash api has an init method so this works too, but we need a
+    better solution in the future. */
+
+  // flash returns item wrapped in an object, handle special case
+  if(api.init) {
+    if(rval.rval === null) {
+      if(rval.error) {
+        var error = new Error(rval.error.message);
+        error.id = rval.error.id;
+        error.name = rval.error.name;
+        throw error;
+      }
+      // no error, but also no item
+      rval = null;
+    } else {
+      rval = rval.rval;
+    }
+  }
+
+  // handle decoding
+  if(rval !== null) {
+    // base64-decode and json-decode data
+    rval = JSON.parse(util.decode64(rval));
+  }
+
+  return rval;
+};
+
+/**
+ * Stores an item in local storage.
+ *
+ * @param api the storage interface.
+ * @param id the storage ID to use.
+ * @param key the key for the item.
+ * @param data the data for the item (any javascript object/primitive).
+ */
+var _setItem = function(api, id, key, data) {
+  // get storage object
+  var obj = _getStorageObject(api, id);
+  if(obj === null) {
+    // create a new storage object
+    obj = {};
+  }
+  // update key
+  obj[key] = data;
+
+  // set storage object
+  _setStorageObject(api, id, obj);
+};
+
+/**
+ * Gets an item from local storage.
+ *
+ * @param api the storage interface.
+ * @param id the storage ID to use.
+ * @param key the key for the item.
+ *
+ * @return the item.
+ */
+var _getItem = function(api, id, key) {
+  // get storage object
+  var rval = _getStorageObject(api, id);
+  if(rval !== null) {
+    // return data at key
+    rval = (key in rval) ? rval[key] : null;
+  }
+
+  return rval;
+};
+
+/**
+ * Removes an item from local storage.
+ *
+ * @param api the storage interface.
+ * @param id the storage ID to use.
+ * @param key the key for the item.
+ */
+var _removeItem = function(api, id, key) {
+  // get storage object
+  var obj = _getStorageObject(api, id);
+  if(obj !== null && key in obj) {
+    // remove key
+    delete obj[key];
+
+    // see if entry has no keys remaining
+    var empty = true;
+    for(var prop in obj) {
+      empty = false;
+      break;
+    }
+    if(empty) {
+      // remove entry entirely if no keys are left
+      obj = null;
+    }
+
+    // set storage object
+    _setStorageObject(api, id, obj);
+  }
+};
+
+/**
+ * Clears the local disk storage identified by the given ID.
+ *
+ * @param api the storage interface.
+ * @param id the storage ID to use.
+ */
+var _clearItems = function(api, id) {
+  _setStorageObject(api, id, null);
+};
+
+/**
+ * Calls a storage function.
+ *
+ * @param func the function to call.
+ * @param args the arguments for the function.
+ * @param location the location argument.
+ *
+ * @return the return value from the function.
+ */
+var _callStorageFunction = function(func, args, location) {
+  var rval = null;
+
+  // default storage types
+  if(typeof(location) === 'undefined') {
+    location = ['web', 'flash'];
+  }
+
+  // apply storage types in order of preference
+  var type;
+  var done = false;
+  var exception = null;
+  for(var idx in location) {
+    type = location[idx];
+    try {
+      if(type === 'flash' || type === 'both') {
+        if(args[0] === null) {
+          throw new Error('Flash local storage not available.');
+        }
+        rval = func.apply(this, args);
+        done = (type === 'flash');
+      }
+      if(type === 'web' || type === 'both') {
+        args[0] = localStorage;
+        rval = func.apply(this, args);
+        done = true;
+      }
+    } catch(ex) {
+      exception = ex;
+    }
+    if(done) {
+      break;
+    }
+  }
+
+  if(!done) {
+    throw exception;
+  }
+
+  return rval;
+};
+
+/**
+ * Stores an item on local disk.
+ *
+ * The available types of local storage include 'flash', 'web', and 'both'.
+ *
+ * The type 'flash' refers to flash local storage (SharedObject). In order
+ * to use flash local storage, the 'api' parameter must be valid. The type
+ * 'web' refers to WebStorage, if supported by the browser. The type 'both'
+ * refers to storing using both 'flash' and 'web', not just one or the
+ * other.
+ *
+ * The location array should list the storage types to use in order of
+ * preference:
+ *
+ * ['flash']: flash only storage
+ * ['web']: web only storage
+ * ['both']: try to store in both
+ * ['flash','web']: store in flash first, but if not available, 'web'
+ * ['web','flash']: store in web first, but if not available, 'flash'
+ *
+ * The location array defaults to: ['web', 'flash']
+ *
+ * @param api the flash interface, null to use only WebStorage.
+ * @param id the storage ID to use.
+ * @param key the key for the item.
+ * @param data the data for the item (any javascript object/primitive).
+ * @param location an array with the preferred types of storage to use.
+ */
+util.setItem = function(api, id, key, data, location) {
+  _callStorageFunction(_setItem, arguments, location);
+};
+
+/**
+ * Gets an item on local disk.
+ *
+ * Set setItem() for details on storage types.
+ *
+ * @param api the flash interface, null to use only WebStorage.
+ * @param id the storage ID to use.
+ * @param key the key for the item.
+ * @param location an array with the preferred types of storage to use.
+ *
+ * @return the item.
+ */
+util.getItem = function(api, id, key, location) {
+  return _callStorageFunction(_getItem, arguments, location);
+};
+
+/**
+ * Removes an item on local disk.
+ *
+ * Set setItem() for details on storage types.
+ *
+ * @param api the flash interface.
+ * @param id the storage ID to use.
+ * @param key the key for the item.
+ * @param location an array with the preferred types of storage to use.
+ */
+util.removeItem = function(api, id, key, location) {
+  _callStorageFunction(_removeItem, arguments, location);
+};
+
+/**
+ * Clears the local disk storage identified by the given ID.
+ *
+ * Set setItem() for details on storage types.
+ *
+ * @param api the flash interface if flash is available.
+ * @param id the storage ID to use.
+ * @param location an array with the preferred types of storage to use.
+ */
+util.clearItems = function(api, id, location) {
+  _callStorageFunction(_clearItems, arguments, location);
+};
+
+/**
+ * Check if an object is empty.
+ *
+ * Taken from:
+ * http://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object-from-json/679937#679937
+ *
+ * @param object the object to check.
+ */
+util.isEmpty = function(obj) {
+  for(var prop in obj) {
+    if(obj.hasOwnProperty(prop)) {
+      return false;
+    }
+  }
+  return true;
+};
+
+/**
+ * Format with simple printf-style interpolation.
+ *
+ * %%: literal '%'
+ * %s,%o: convert next argument into a string.
+ *
+ * @param format the string to format.
+ * @param ... arguments to interpolate into the format string.
+ */
+util.format = function(format) {
+  var re = /%./g;
+  // current match
+  var match;
+  // current part
+  var part;
+  // current arg index
+  var argi = 0;
+  // collected parts to recombine later
+  var parts = [];
+  // last index found
+  var last = 0;
+  // loop while matches remain
+  while((match = re.exec(format))) {
+    part = format.substring(last, re.lastIndex - 2);
+    // don't add empty strings (ie, parts between %s%s)
+    if(part.length > 0) {
+      parts.push(part);
+    }
+    last = re.lastIndex;
+    // switch on % code
+    var code = match[0][1];
+    switch(code) {
+    case 's':
+    case 'o':
+      // check if enough arguments were given
+      if(argi < arguments.length) {
+        parts.push(arguments[argi++ + 1]);
+      } else {
+        parts.push('<?>');
+      }
+      break;
+    // FIXME: do proper formatting for numbers, etc
+    //case 'f':
+    //case 'd':
+    case '%':
+      parts.push('%');
+      break;
+    default:
+      parts.push('<%' + code + '?>');
+    }
+  }
+  // add trailing part of format string
+  parts.push(format.substring(last));
+  return parts.join('');
+};
+
+/**
+ * Formats a number.
+ *
+ * http://snipplr.com/view/5945/javascript-numberformat--ported-from-php/
+ */
+util.formatNumber = function(number, decimals, dec_point, thousands_sep) {
+  // http://kevin.vanzonneveld.net
+  // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +     bugfix by: Michael White (http://crestidg.com)
+  // +     bugfix by: Benjamin Lupton
+  // +     bugfix by: Allan Jensen (http://www.winternet.no)
+  // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
+  // *     example 1: number_format(1234.5678, 2, '.', '');
+  // *     returns 1: 1234.57
+
+  var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
+  var d = dec_point === undefined ? ',' : dec_point;
+  var t = thousands_sep === undefined ?
+   '.' : thousands_sep, s = n < 0 ? '-' : '';
+  var i = parseInt((n = Math.abs(+n || 0).toFixed(c)), 10) + '';
+  var j = (i.length > 3) ? i.length % 3 : 0;
+  return s + (j ? i.substr(0, j) + t : '') +
+    i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + t) +
+    (c ? d + Math.abs(n - i).toFixed(c).slice(2) : '');
+};
+
+/**
+ * Formats a byte size.
+ *
+ * http://snipplr.com/view/5949/format-humanize-file-byte-size-presentation-in-javascript/
+ */
+util.formatSize = function(size) {
+  if(size >= 1073741824) {
+    size = util.formatNumber(size / 1073741824, 2, '.', '') + ' GiB';
+  } else if(size >= 1048576) {
+    size = util.formatNumber(size / 1048576, 2, '.', '') + ' MiB';
+  } else if(size >= 1024) {
+    size = util.formatNumber(size / 1024, 0) + ' KiB';
+  } else {
+    size = util.formatNumber(size, 0) + ' bytes';
+  }
+  return size;
+};
+
+/**
+ * Converts an IPv4 or IPv6 string representation into bytes (in network order).
+ *
+ * @param ip the IPv4 or IPv6 address to convert.
+ *
+ * @return the 4-byte IPv6 or 16-byte IPv6 address or null if the address can't
+ *         be parsed.
+ */
+util.bytesFromIP = function(ip) {
+  if(ip.indexOf('.') !== -1) {
+    return util.bytesFromIPv4(ip);
+  }
+  if(ip.indexOf(':') !== -1) {
+    return util.bytesFromIPv6(ip);
+  }
+  return null;
+};
+
+/**
+ * Converts an IPv4 string representation into bytes (in network order).
+ *
+ * @param ip the IPv4 address to convert.
+ *
+ * @return the 4-byte address or null if the address can't be parsed.
+ */
+util.bytesFromIPv4 = function(ip) {
+  ip = ip.split('.');
+  if(ip.length !== 4) {
+    return null;
+  }
+  var b = util.createBuffer();
+  for(var i = 0; i < ip.length; ++i) {
+    var num = parseInt(ip[i], 10);
+    if(isNaN(num)) {
+      return null;
+    }
+    b.putByte(num);
+  }
+  return b.getBytes();
+};
+
+/**
+ * Converts an IPv6 string representation into bytes (in network order).
+ *
+ * @param ip the IPv6 address to convert.
+ *
+ * @return the 16-byte address or null if the address can't be parsed.
+ */
+util.bytesFromIPv6 = function(ip) {
+  var blanks = 0;
+  ip = ip.split(':').filter(function(e) {
+    if(e.length === 0) ++blanks;
+    return true;
+  });
+  var zeros = (8 - ip.length + blanks) * 2;
+  var b = util.createBuffer();
+  for(var i = 0; i < 8; ++i) {
+    if(!ip[i] || ip[i].length === 0) {
+      b.fillWithByte(0, zeros);
+      zeros = 0;
+      continue;
+    }
+    var bytes = util.hexToBytes(ip[i]);
+    if(bytes.length < 2) {
+      b.putByte(0);
+    }
+    b.putBytes(bytes);
+  }
+  return b.getBytes();
+};
+
+/**
+ * Converts 4-bytes into an IPv4 string representation or 16-bytes into
+ * an IPv6 string representation. The bytes must be in network order.
+ *
+ * @param bytes the bytes to convert.
+ *
+ * @return the IPv4 or IPv6 string representation if 4 or 16 bytes,
+ *         respectively, are given, otherwise null.
+ */
+util.bytesToIP = function(bytes) {
+  if(bytes.length === 4) {
+    return util.bytesToIPv4(bytes);
+  }
+  if(bytes.length === 16) {
+    return util.bytesToIPv6(bytes);
+  }
+  return null;
+};
+
+/**
+ * Converts 4-bytes into an IPv4 string representation. The bytes must be
+ * in network order.
+ *
+ * @param bytes the bytes to convert.
+ *
+ * @return the IPv4 string representation or null for an invalid # of bytes.
+ */
+util.bytesToIPv4 = function(bytes) {
+  if(bytes.length !== 4) {
+    return null;
+  }
+  var ip = [];
+  for(var i = 0; i < bytes.length; ++i) {
+    ip.push(bytes.charCodeAt(i));
+  }
+  return ip.join('.');
+};
+
+/**
+ * Converts 16-bytes into an IPv16 string representation. The bytes must be
+ * in network order.
+ *
+ * @param bytes the bytes to convert.
+ *
+ * @return the IPv16 string representation or null for an invalid # of bytes.
+ */
+util.bytesToIPv6 = function(bytes) {
+  if(bytes.length !== 16) {
+    return null;
+  }
+  var ip = [];
+  var zeroGroups = [];
+  var zeroMaxGroup = 0;
+  for(var i = 0; i < bytes.length; i += 2) {
+    var hex = util.bytesToHex(bytes[i] + bytes[i + 1]);
+    // canonicalize zero representation
+    while(hex[0] === '0' && hex !== '0') {
+      hex = hex.substr(1);
+    }
+    if(hex === '0') {
+      var last = zeroGroups[zeroGroups.length - 1];
+      var idx = ip.length;
+      if(!last || idx !== last.end + 1) {
+        zeroGroups.push({start: idx, end: idx});
+      } else {
+        last.end = idx;
+        if((last.end - last.start) >
+          (zeroGroups[zeroMaxGroup].end - zeroGroups[zeroMaxGroup].start)) {
+          zeroMaxGroup = zeroGroups.length - 1;
+        }
+      }
+    }
+    ip.push(hex);
+  }
+  if(zeroGroups.length > 0) {
+    var group = zeroGroups[zeroMaxGroup];
+    // only shorten group of length > 0
+    if(group.end - group.start > 0) {
+      ip.splice(group.start, group.end - group.start + 1, '');
+      if(group.start === 0) {
+        ip.unshift('');
+      }
+      if(group.end === 7) {
+        ip.push('');
+      }
+    }
+  }
+  return ip.join(':');
+};
+
+/**
+ * Estimates the number of processes that can be run concurrently. If
+ * creating Web Workers, keep in mind that the main JavaScript process needs
+ * its own core.
+ *
+ * @param options the options to use:
+ *          update true to force an update (not use the cached value).
+ * @param callback(err, max) called once the operation completes.
+ */
+util.estimateCores = function(options, callback) {
+  if(typeof options === 'function') {
+    callback = options;
+    options = {};
+  }
+  options = options || {};
+  if('cores' in util && !options.update) {
+    return callback(null, util.cores);
+  }
+  if(typeof navigator !== 'undefined' &&
+    'hardwareConcurrency' in navigator &&
+    navigator.hardwareConcurrency > 0) {
+    util.cores = navigator.hardwareConcurrency;
+    return callback(null, util.cores);
+  }
+  if(typeof Worker === 'undefined') {
+    // workers not available
+    util.cores = 1;
+    return callback(null, util.cores);
+  }
+  if(typeof Blob === 'undefined') {
+    // can't estimate, default to 2
+    util.cores = 2;
+    return callback(null, util.cores);
+  }
+
+  // create worker concurrency estimation code as blob
+  var blobUrl = URL.createObjectURL(new Blob(['(',
+    function() {
+      self.addEventListener('message', function(e) {
+        // run worker for 4 ms
+        var st = Date.now();
+        var et = st + 4;
+        while(Date.now() < et);
+        self.postMessage({st: st, et: et});
+      });
+    }.toString(),
+  ')()'], {type: 'application/javascript'}));
+
+  // take 5 samples using 16 workers
+  sample([], 5, 16);
+
+  function sample(max, samples, numWorkers) {
+    if(samples === 0) {
+      // get overlap average
+      var avg = Math.floor(max.reduce(function(avg, x) {
+        return avg + x;
+      }, 0) / max.length);
+      util.cores = Math.max(1, avg);
+      URL.revokeObjectURL(blobUrl);
+      return callback(null, util.cores);
+    }
+    map(numWorkers, function(err, results) {
+      max.push(reduce(numWorkers, results));
+      sample(max, samples - 1, numWorkers);
+    });
+  }
+
+  function map(numWorkers, callback) {
+    var workers = [];
+    var results = [];
+    for(var i = 0; i < numWorkers; ++i) {
+      var worker = new Worker(blobUrl);
+      worker.addEventListener('message', function(e) {
+        results.push(e.data);
+        if(results.length === numWorkers) {
+          for(var i = 0; i < numWorkers; ++i) {
+            workers[i].terminate();
+          }
+          callback(null, results);
+        }
+      });
+      workers.push(worker);
+    }
+    for(var i = 0; i < numWorkers; ++i) {
+      workers[i].postMessage(i);
+    }
+  }
+
+  function reduce(numWorkers, results) {
+    // find overlapping time windows
+    var overlaps = [];
+    for(var n = 0; n < numWorkers; ++n) {
+      var r1 = results[n];
+      var overlap = overlaps[n] = [];
+      for(var i = 0; i < numWorkers; ++i) {
+        if(n === i) {
+          continue;
+        }
+        var r2 = results[i];
+        if((r1.st > r2.st && r1.st < r2.et) ||
+          (r2.st > r1.st && r2.st < r1.et)) {
+          overlap.push(i);
+        }
+      }
+    }
+    // get maximum overlaps ... don't include overlapping worker itself
+    // as the main JS process was also being scheduled during the work and
+    // would have to be subtracted from the estimate anyway
+    return overlaps.reduce(function(max, overlap) {
+      return Math.max(max, overlap.length);
+    }, 0);
+  }
+};
Index: frontend/node_modules/node-forge/lib/x509.js
===================================================================
--- frontend/node_modules/node-forge/lib/x509.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/x509.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3251 @@
+/**
+ * Javascript implementation of X.509 and related components (such as
+ * Certification Signing Requests) of a Public Key Infrastructure.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2014 Digital Bazaar, Inc.
+ *
+ * The ASN.1 representation of an X.509v3 certificate is as follows
+ * (see RFC 2459):
+ *
+ * Certificate ::= SEQUENCE {
+ *   tbsCertificate       TBSCertificate,
+ *   signatureAlgorithm   AlgorithmIdentifier,
+ *   signatureValue       BIT STRING
+ * }
+ *
+ * TBSCertificate ::= SEQUENCE {
+ *   version         [0]  EXPLICIT Version DEFAULT v1,
+ *   serialNumber         CertificateSerialNumber,
+ *   signature            AlgorithmIdentifier,
+ *   issuer               Name,
+ *   validity             Validity,
+ *   subject              Name,
+ *   subjectPublicKeyInfo SubjectPublicKeyInfo,
+ *   issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+ *                        -- If present, version shall be v2 or v3
+ *   subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+ *                        -- If present, version shall be v2 or v3
+ *   extensions      [3]  EXPLICIT Extensions OPTIONAL
+ *                        -- If present, version shall be v3
+ * }
+ *
+ * Version ::= INTEGER  { v1(0), v2(1), v3(2) }
+ *
+ * CertificateSerialNumber ::= INTEGER
+ *
+ * Name ::= CHOICE {
+ *   // only one possible choice for now
+ *   RDNSequence
+ * }
+ *
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ *   type     AttributeType,
+ *   value    AttributeValue
+ * }
+ * AttributeType ::= OBJECT IDENTIFIER
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ *
+ * Validity ::= SEQUENCE {
+ *   notBefore      Time,
+ *   notAfter       Time
+ * }
+ *
+ * Time ::= CHOICE {
+ *   utcTime        UTCTime,
+ *   generalTime    GeneralizedTime
+ * }
+ *
+ * UniqueIdentifier ::= BIT STRING
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ *   algorithm            AlgorithmIdentifier,
+ *   subjectPublicKey     BIT STRING
+ * }
+ *
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ *
+ * Extension ::= SEQUENCE {
+ *   extnID      OBJECT IDENTIFIER,
+ *   critical    BOOLEAN DEFAULT FALSE,
+ *   extnValue   OCTET STRING
+ * }
+ *
+ * The only key algorithm currently supported for PKI is RSA.
+ *
+ * RSASSA-PSS signatures are described in RFC 3447 and RFC 4055.
+ *
+ * PKCS#10 v1.7 describes certificate signing requests:
+ *
+ * CertificationRequestInfo:
+ *
+ * CertificationRequestInfo ::= SEQUENCE {
+ *   version       INTEGER { v1(0) } (v1,...),
+ *   subject       Name,
+ *   subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+ *   attributes    [0] Attributes{{ CRIAttributes }}
+ * }
+ *
+ * Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
+ *
+ * CRIAttributes  ATTRIBUTE  ::= {
+ *   ... -- add any locally defined attributes here -- }
+ *
+ * Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
+ *   type   ATTRIBUTE.&id({IOSet}),
+ *   values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
+ * }
+ *
+ * CertificationRequest ::= SEQUENCE {
+ *   certificationRequestInfo CertificationRequestInfo,
+ *   signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
+ *   signature          BIT STRING
+ * }
+ */
+var forge = require('./forge');
+require('./aes');
+require('./asn1');
+require('./des');
+require('./md');
+require('./mgf');
+require('./oids');
+require('./pem');
+require('./pss');
+require('./rsa');
+require('./util');
+
+// shortcut for asn.1 API
+var asn1 = forge.asn1;
+
+/* Public Key Infrastructure (PKI) implementation. */
+var pki = module.exports = forge.pki = forge.pki || {};
+var oids = pki.oids;
+
+// short name OID mappings
+var _shortNames = {};
+_shortNames['CN'] = oids['commonName'];
+_shortNames['commonName'] = 'CN';
+_shortNames['C'] = oids['countryName'];
+_shortNames['countryName'] = 'C';
+_shortNames['L'] = oids['localityName'];
+_shortNames['localityName'] = 'L';
+_shortNames['ST'] = oids['stateOrProvinceName'];
+_shortNames['stateOrProvinceName'] = 'ST';
+_shortNames['O'] = oids['organizationName'];
+_shortNames['organizationName'] = 'O';
+_shortNames['OU'] = oids['organizationalUnitName'];
+_shortNames['organizationalUnitName'] = 'OU';
+_shortNames['E'] = oids['emailAddress'];
+_shortNames['emailAddress'] = 'E';
+
+// validator for an SubjectPublicKeyInfo structure
+// Note: Currently only works with an RSA public key
+var publicKeyValidator = forge.pki.rsa.publicKeyValidator;
+
+// validator for an X.509v3 certificate
+var x509CertificateValidator = {
+  name: 'Certificate',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'Certificate.TBSCertificate',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    captureAsn1: 'tbsCertificate',
+    value: [{
+      name: 'Certificate.TBSCertificate.version',
+      tagClass: asn1.Class.CONTEXT_SPECIFIC,
+      type: 0,
+      constructed: true,
+      optional: true,
+      value: [{
+        name: 'Certificate.TBSCertificate.version.integer',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.INTEGER,
+        constructed: false,
+        capture: 'certVersion'
+      }]
+    }, {
+      name: 'Certificate.TBSCertificate.serialNumber',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.INTEGER,
+      constructed: false,
+      capture: 'certSerialNumber'
+    }, {
+      name: 'Certificate.TBSCertificate.signature',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      value: [{
+        name: 'Certificate.TBSCertificate.signature.algorithm',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.OID,
+        constructed: false,
+        capture: 'certinfoSignatureOid'
+      }, {
+        name: 'Certificate.TBSCertificate.signature.parameters',
+        tagClass: asn1.Class.UNIVERSAL,
+        optional: true,
+        captureAsn1: 'certinfoSignatureParams'
+      }]
+    }, {
+      name: 'Certificate.TBSCertificate.issuer',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      captureAsn1: 'certIssuer'
+    }, {
+      name: 'Certificate.TBSCertificate.validity',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      // Note: UTC and generalized times may both appear so the capture
+      // names are based on their detected order, the names used below
+      // are only for the common case, which validity time really means
+      // "notBefore" and which means "notAfter" will be determined by order
+      value: [{
+        // notBefore (Time) (UTC time case)
+        name: 'Certificate.TBSCertificate.validity.notBefore (utc)',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.UTCTIME,
+        constructed: false,
+        optional: true,
+        capture: 'certValidity1UTCTime'
+      }, {
+        // notBefore (Time) (generalized time case)
+        name: 'Certificate.TBSCertificate.validity.notBefore (generalized)',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.GENERALIZEDTIME,
+        constructed: false,
+        optional: true,
+        capture: 'certValidity2GeneralizedTime'
+      }, {
+        // notAfter (Time) (only UTC time is supported)
+        name: 'Certificate.TBSCertificate.validity.notAfter (utc)',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.UTCTIME,
+        constructed: false,
+        optional: true,
+        capture: 'certValidity3UTCTime'
+      }, {
+        // notAfter (Time) (only UTC time is supported)
+        name: 'Certificate.TBSCertificate.validity.notAfter (generalized)',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.GENERALIZEDTIME,
+        constructed: false,
+        optional: true,
+        capture: 'certValidity4GeneralizedTime'
+      }]
+    }, {
+      // Name (subject) (RDNSequence)
+      name: 'Certificate.TBSCertificate.subject',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      captureAsn1: 'certSubject'
+    },
+    // SubjectPublicKeyInfo
+    publicKeyValidator,
+    {
+      // issuerUniqueID (optional)
+      name: 'Certificate.TBSCertificate.issuerUniqueID',
+      tagClass: asn1.Class.CONTEXT_SPECIFIC,
+      type: 1,
+      constructed: true,
+      optional: true,
+      value: [{
+        name: 'Certificate.TBSCertificate.issuerUniqueID.id',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.BITSTRING,
+        constructed: false,
+        // TODO: support arbitrary bit length ids
+        captureBitStringValue: 'certIssuerUniqueId'
+      }]
+    }, {
+      // subjectUniqueID (optional)
+      name: 'Certificate.TBSCertificate.subjectUniqueID',
+      tagClass: asn1.Class.CONTEXT_SPECIFIC,
+      type: 2,
+      constructed: true,
+      optional: true,
+      value: [{
+        name: 'Certificate.TBSCertificate.subjectUniqueID.id',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.BITSTRING,
+        constructed: false,
+        // TODO: support arbitrary bit length ids
+        captureBitStringValue: 'certSubjectUniqueId'
+      }]
+    }, {
+      // Extensions (optional)
+      name: 'Certificate.TBSCertificate.extensions',
+      tagClass: asn1.Class.CONTEXT_SPECIFIC,
+      type: 3,
+      constructed: true,
+      captureAsn1: 'certExtensions',
+      optional: true
+    }]
+  }, {
+    // AlgorithmIdentifier (signature algorithm)
+    name: 'Certificate.signatureAlgorithm',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    value: [{
+      // algorithm
+      name: 'Certificate.signatureAlgorithm.algorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.OID,
+      constructed: false,
+      capture: 'certSignatureOid'
+    }, {
+      name: 'Certificate.TBSCertificate.signature.parameters',
+      tagClass: asn1.Class.UNIVERSAL,
+      optional: true,
+      captureAsn1: 'certSignatureParams'
+    }]
+  }, {
+    // SignatureValue
+    name: 'Certificate.signatureValue',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.BITSTRING,
+    constructed: false,
+    captureBitStringValue: 'certSignature'
+  }]
+};
+
+var rsassaPssParameterValidator = {
+  name: 'rsapss',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  value: [{
+    name: 'rsapss.hashAlgorithm',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 0,
+    constructed: true,
+    value: [{
+      name: 'rsapss.hashAlgorithm.AlgorithmIdentifier',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Class.SEQUENCE,
+      constructed: true,
+      optional: true,
+      value: [{
+        name: 'rsapss.hashAlgorithm.AlgorithmIdentifier.algorithm',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.OID,
+        constructed: false,
+        capture: 'hashOid'
+        /* parameter block omitted, for SHA1 NULL anyhow. */
+      }]
+    }]
+  }, {
+    name: 'rsapss.maskGenAlgorithm',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 1,
+    constructed: true,
+    value: [{
+      name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Class.SEQUENCE,
+      constructed: true,
+      optional: true,
+      value: [{
+        name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.algorithm',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.OID,
+        constructed: false,
+        capture: 'maskGenOid'
+      }, {
+        name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.params',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.SEQUENCE,
+        constructed: true,
+        value: [{
+          name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.params.algorithm',
+          tagClass: asn1.Class.UNIVERSAL,
+          type: asn1.Type.OID,
+          constructed: false,
+          capture: 'maskGenHashOid'
+          /* parameter block omitted, for SHA1 NULL anyhow. */
+        }]
+      }]
+    }]
+  }, {
+    name: 'rsapss.saltLength',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 2,
+    optional: true,
+    value: [{
+      name: 'rsapss.saltLength.saltLength',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Class.INTEGER,
+      constructed: false,
+      capture: 'saltLength'
+    }]
+  }, {
+    name: 'rsapss.trailerField',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 3,
+    optional: true,
+    value: [{
+      name: 'rsapss.trailer.trailer',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Class.INTEGER,
+      constructed: false,
+      capture: 'trailer'
+    }]
+  }]
+};
+
+// validator for a CertificationRequestInfo structure
+var certificationRequestInfoValidator = {
+  name: 'CertificationRequestInfo',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  captureAsn1: 'certificationRequestInfo',
+  value: [{
+    name: 'CertificationRequestInfo.integer',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.INTEGER,
+    constructed: false,
+    capture: 'certificationRequestInfoVersion'
+  }, {
+    // Name (subject) (RDNSequence)
+    name: 'CertificationRequestInfo.subject',
+    tagClass: asn1.Class.UNIVERSAL,
+    type: asn1.Type.SEQUENCE,
+    constructed: true,
+    captureAsn1: 'certificationRequestInfoSubject'
+  },
+  // SubjectPublicKeyInfo
+  publicKeyValidator,
+  {
+    name: 'CertificationRequestInfo.attributes',
+    tagClass: asn1.Class.CONTEXT_SPECIFIC,
+    type: 0,
+    constructed: true,
+    optional: true,
+    capture: 'certificationRequestInfoAttributes',
+    value: [{
+      name: 'CertificationRequestInfo.attributes',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      value: [{
+        name: 'CertificationRequestInfo.attributes.type',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.OID,
+        constructed: false
+      }, {
+        name: 'CertificationRequestInfo.attributes.value',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.SET,
+        constructed: true
+      }]
+    }]
+  }]
+};
+
+// validator for a CertificationRequest structure
+var certificationRequestValidator = {
+  name: 'CertificationRequest',
+  tagClass: asn1.Class.UNIVERSAL,
+  type: asn1.Type.SEQUENCE,
+  constructed: true,
+  captureAsn1: 'csr',
+  value: [
+    certificationRequestInfoValidator, {
+      // AlgorithmIdentifier (signature algorithm)
+      name: 'CertificationRequest.signatureAlgorithm',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.SEQUENCE,
+      constructed: true,
+      value: [{
+        // algorithm
+        name: 'CertificationRequest.signatureAlgorithm.algorithm',
+        tagClass: asn1.Class.UNIVERSAL,
+        type: asn1.Type.OID,
+        constructed: false,
+        capture: 'csrSignatureOid'
+      }, {
+        name: 'CertificationRequest.signatureAlgorithm.parameters',
+        tagClass: asn1.Class.UNIVERSAL,
+        optional: true,
+        captureAsn1: 'csrSignatureParams'
+      }]
+    }, {
+      // signature
+      name: 'CertificationRequest.signature',
+      tagClass: asn1.Class.UNIVERSAL,
+      type: asn1.Type.BITSTRING,
+      constructed: false,
+      captureBitStringValue: 'csrSignature'
+    }
+  ]
+};
+
+/**
+ * Converts an RDNSequence of ASN.1 DER-encoded RelativeDistinguishedName
+ * sets into an array with objects that have type and value properties.
+ *
+ * @param rdn the RDNSequence to convert.
+ * @param md a message digest to append type and value to if provided.
+ */
+pki.RDNAttributesAsArray = function(rdn, md) {
+  var rval = [];
+
+  // each value in 'rdn' in is a SET of RelativeDistinguishedName
+  var set, attr, obj;
+  for(var si = 0; si < rdn.value.length; ++si) {
+    // get the RelativeDistinguishedName set
+    set = rdn.value[si];
+
+    // each value in the SET is an AttributeTypeAndValue sequence
+    // containing first a type (an OID) and second a value (defined by
+    // the OID)
+    for(var i = 0; i < set.value.length; ++i) {
+      obj = {};
+      attr = set.value[i];
+      obj.type = asn1.derToOid(attr.value[0].value);
+      obj.value = attr.value[1].value;
+      obj.valueTagClass = attr.value[1].type;
+      // if the OID is known, get its name and short name
+      if(obj.type in oids) {
+        obj.name = oids[obj.type];
+        if(obj.name in _shortNames) {
+          obj.shortName = _shortNames[obj.name];
+        }
+      }
+      if(md) {
+        md.update(obj.type);
+        md.update(obj.value);
+      }
+      rval.push(obj);
+    }
+  }
+
+  return rval;
+};
+
+/**
+ * Converts ASN.1 CRIAttributes into an array with objects that have type and
+ * value properties.
+ *
+ * @param attributes the CRIAttributes to convert.
+ */
+pki.CRIAttributesAsArray = function(attributes) {
+  var rval = [];
+
+  // each value in 'attributes' in is a SEQUENCE with an OID and a SET
+  for(var si = 0; si < attributes.length; ++si) {
+    // get the attribute sequence
+    var seq = attributes[si];
+
+    // each value in the SEQUENCE containing first a type (an OID) and
+    // second a set of values (defined by the OID)
+    var type = asn1.derToOid(seq.value[0].value);
+    var values = seq.value[1].value;
+    for(var vi = 0; vi < values.length; ++vi) {
+      var obj = {};
+      obj.type = type;
+      obj.value = values[vi].value;
+      obj.valueTagClass = values[vi].type;
+      // if the OID is known, get its name and short name
+      if(obj.type in oids) {
+        obj.name = oids[obj.type];
+        if(obj.name in _shortNames) {
+          obj.shortName = _shortNames[obj.name];
+        }
+      }
+      // parse extensions
+      if(obj.type === oids.extensionRequest) {
+        obj.extensions = [];
+        for(var ei = 0; ei < obj.value.length; ++ei) {
+          obj.extensions.push(pki.certificateExtensionFromAsn1(obj.value[ei]));
+        }
+      }
+      rval.push(obj);
+    }
+  }
+
+  return rval;
+};
+
+/**
+ * Gets an issuer or subject attribute from its name, type, or short name.
+ *
+ * @param obj the issuer or subject object.
+ * @param options a short name string or an object with:
+ *          shortName the short name for the attribute.
+ *          name the name for the attribute.
+ *          type the type for the attribute.
+ *
+ * @return the attribute.
+ */
+function _getAttribute(obj, options) {
+  if(typeof options === 'string') {
+    options = {shortName: options};
+  }
+
+  var rval = null;
+  var attr;
+  for(var i = 0; rval === null && i < obj.attributes.length; ++i) {
+    attr = obj.attributes[i];
+    if(options.type && options.type === attr.type) {
+      rval = attr;
+    } else if(options.name && options.name === attr.name) {
+      rval = attr;
+    } else if(options.shortName && options.shortName === attr.shortName) {
+      rval = attr;
+    }
+  }
+  return rval;
+}
+
+/**
+ * Converts signature parameters from ASN.1 structure.
+ *
+ * Currently only RSASSA-PSS supported.  The PKCS#1 v1.5 signature scheme had
+ * no parameters.
+ *
+ * RSASSA-PSS-params  ::=  SEQUENCE  {
+ *   hashAlgorithm      [0] HashAlgorithm DEFAULT
+ *                             sha1Identifier,
+ *   maskGenAlgorithm   [1] MaskGenAlgorithm DEFAULT
+ *                             mgf1SHA1Identifier,
+ *   saltLength         [2] INTEGER DEFAULT 20,
+ *   trailerField       [3] INTEGER DEFAULT 1
+ * }
+ *
+ * HashAlgorithm  ::=  AlgorithmIdentifier
+ *
+ * MaskGenAlgorithm  ::=  AlgorithmIdentifier
+ *
+ * AlgorithmIdentifer ::= SEQUENCE {
+ *   algorithm OBJECT IDENTIFIER,
+ *   parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ *
+ * @param oid The OID specifying the signature algorithm
+ * @param obj The ASN.1 structure holding the parameters
+ * @param fillDefaults Whether to use return default values where omitted
+ * @return signature parameter object
+ */
+var _readSignatureParameters = function(oid, obj, fillDefaults) {
+  var params = {};
+
+  if(oid !== oids['RSASSA-PSS']) {
+    return params;
+  }
+
+  if(fillDefaults) {
+    params = {
+      hash: {
+        algorithmOid: oids['sha1']
+      },
+      mgf: {
+        algorithmOid: oids['mgf1'],
+        hash: {
+          algorithmOid: oids['sha1']
+        }
+      },
+      saltLength: 20
+    };
+  }
+
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, rsassaPssParameterValidator, capture, errors)) {
+    var error = new Error('Cannot read RSASSA-PSS parameter block.');
+    error.errors = errors;
+    throw error;
+  }
+
+  if(capture.hashOid !== undefined) {
+    params.hash = params.hash || {};
+    params.hash.algorithmOid = asn1.derToOid(capture.hashOid);
+  }
+
+  if(capture.maskGenOid !== undefined) {
+    params.mgf = params.mgf || {};
+    params.mgf.algorithmOid = asn1.derToOid(capture.maskGenOid);
+    params.mgf.hash = params.mgf.hash || {};
+    params.mgf.hash.algorithmOid = asn1.derToOid(capture.maskGenHashOid);
+  }
+
+  if(capture.saltLength !== undefined) {
+    params.saltLength = capture.saltLength.charCodeAt(0);
+  }
+
+  return params;
+};
+
+/**
+ * Create signature digest for OID.
+ *
+ * @param options
+ *   signatureOid: the OID specifying the signature algorithm.
+ *   type: a human readable type for error messages
+ * @return a created md instance. throws if unknown oid.
+ */
+var _createSignatureDigest = function(options) {
+  switch(oids[options.signatureOid]) {
+    case 'sha1WithRSAEncryption':
+    // deprecated alias
+    case 'sha1WithRSASignature':
+      return forge.md.sha1.create();
+    case 'md5WithRSAEncryption':
+      return forge.md.md5.create();
+    case 'sha256WithRSAEncryption':
+      return forge.md.sha256.create();
+    case 'sha384WithRSAEncryption':
+      return forge.md.sha384.create();
+    case 'sha512WithRSAEncryption':
+      return forge.md.sha512.create();
+    case 'RSASSA-PSS':
+      return forge.md.sha256.create();
+    default:
+      var error = new Error(
+        'Could not compute ' + options.type + ' digest. ' +
+        'Unknown signature OID.');
+      error.signatureOid = options.signatureOid;
+      throw error;
+  }
+};
+
+/**
+ * Verify signature on certificate or CSR.
+ *
+ * @param options:
+ *   certificate the certificate or CSR to verify.
+ *   md the signature digest.
+ *   signature the signature
+ * @return a created md instance. throws if unknown oid.
+ */
+var _verifySignature = function(options) {
+  var cert = options.certificate;
+  var scheme;
+
+  switch(cert.signatureOid) {
+    case oids.sha1WithRSAEncryption:
+    // deprecated alias
+    case oids.sha1WithRSASignature:
+      /* use PKCS#1 v1.5 padding scheme */
+      break;
+    case oids['RSASSA-PSS']:
+      var hash, mgf;
+
+      /* initialize mgf */
+      hash = oids[cert.signatureParameters.mgf.hash.algorithmOid];
+      if(hash === undefined || forge.md[hash] === undefined) {
+        var error = new Error('Unsupported MGF hash function.');
+        error.oid = cert.signatureParameters.mgf.hash.algorithmOid;
+        error.name = hash;
+        throw error;
+      }
+
+      mgf = oids[cert.signatureParameters.mgf.algorithmOid];
+      if(mgf === undefined || forge.mgf[mgf] === undefined) {
+        var error = new Error('Unsupported MGF function.');
+        error.oid = cert.signatureParameters.mgf.algorithmOid;
+        error.name = mgf;
+        throw error;
+      }
+
+      mgf = forge.mgf[mgf].create(forge.md[hash].create());
+
+      /* initialize hash function */
+      hash = oids[cert.signatureParameters.hash.algorithmOid];
+      if(hash === undefined || forge.md[hash] === undefined) {
+        var error = new Error('Unsupported RSASSA-PSS hash function.');
+        error.oid = cert.signatureParameters.hash.algorithmOid;
+        error.name = hash;
+        throw error;
+      }
+
+      scheme = forge.pss.create(
+        forge.md[hash].create(), mgf, cert.signatureParameters.saltLength
+      );
+      break;
+  }
+
+  // verify signature on cert using public key
+  return cert.publicKey.verify(
+    options.md.digest().getBytes(), options.signature, scheme
+  );
+};
+
+/**
+ * Converts an X.509 certificate from PEM format.
+ *
+ * Note: If the certificate is to be verified then compute hash should
+ * be set to true. This will scan the TBSCertificate part of the ASN.1
+ * object while it is converted so it doesn't need to be converted back
+ * to ASN.1-DER-encoding later.
+ *
+ * @param pem the PEM-formatted certificate.
+ * @param computeHash true to compute the hash for verification.
+ * @param strict true to be strict when checking ASN.1 value lengths, false to
+ *          allow truncated values (default: true).
+ *
+ * @return the certificate.
+ */
+pki.certificateFromPem = function(pem, computeHash, strict) {
+  var msg = forge.pem.decode(pem)[0];
+
+  if(msg.type !== 'CERTIFICATE' &&
+    msg.type !== 'X509 CERTIFICATE' &&
+    msg.type !== 'TRUSTED CERTIFICATE') {
+    var error = new Error(
+      'Could not convert certificate from PEM; PEM header type ' +
+      'is not "CERTIFICATE", "X509 CERTIFICATE", or "TRUSTED CERTIFICATE".');
+    error.headerType = msg.type;
+    throw error;
+  }
+  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+    throw new Error(
+      'Could not convert certificate from PEM; PEM is encrypted.');
+  }
+
+  // convert DER to ASN.1 object
+  var obj = asn1.fromDer(msg.body, strict);
+
+  return pki.certificateFromAsn1(obj, computeHash);
+};
+
+/**
+ * Converts an X.509 certificate to PEM format.
+ *
+ * @param cert the certificate.
+ * @param maxline the maximum characters per line, defaults to 64.
+ *
+ * @return the PEM-formatted certificate.
+ */
+pki.certificateToPem = function(cert, maxline) {
+  // convert to ASN.1, then DER, then PEM-encode
+  var msg = {
+    type: 'CERTIFICATE',
+    body: asn1.toDer(pki.certificateToAsn1(cert)).getBytes()
+  };
+  return forge.pem.encode(msg, {maxline: maxline});
+};
+
+/**
+ * Converts an RSA public key from PEM format.
+ *
+ * @param pem the PEM-formatted public key.
+ *
+ * @return the public key.
+ */
+pki.publicKeyFromPem = function(pem) {
+  var msg = forge.pem.decode(pem)[0];
+
+  if(msg.type !== 'PUBLIC KEY' && msg.type !== 'RSA PUBLIC KEY') {
+    var error = new Error('Could not convert public key from PEM; PEM header ' +
+      'type is not "PUBLIC KEY" or "RSA PUBLIC KEY".');
+    error.headerType = msg.type;
+    throw error;
+  }
+  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+    throw new Error('Could not convert public key from PEM; PEM is encrypted.');
+  }
+
+  // convert DER to ASN.1 object
+  var obj = asn1.fromDer(msg.body);
+
+  return pki.publicKeyFromAsn1(obj);
+};
+
+/**
+ * Converts an RSA public key to PEM format (using a SubjectPublicKeyInfo).
+ *
+ * @param key the public key.
+ * @param maxline the maximum characters per line, defaults to 64.
+ *
+ * @return the PEM-formatted public key.
+ */
+pki.publicKeyToPem = function(key, maxline) {
+  // convert to ASN.1, then DER, then PEM-encode
+  var msg = {
+    type: 'PUBLIC KEY',
+    body: asn1.toDer(pki.publicKeyToAsn1(key)).getBytes()
+  };
+  return forge.pem.encode(msg, {maxline: maxline});
+};
+
+/**
+ * Converts an RSA public key to PEM format (using an RSAPublicKey).
+ *
+ * @param key the public key.
+ * @param maxline the maximum characters per line, defaults to 64.
+ *
+ * @return the PEM-formatted public key.
+ */
+pki.publicKeyToRSAPublicKeyPem = function(key, maxline) {
+  // convert to ASN.1, then DER, then PEM-encode
+  var msg = {
+    type: 'RSA PUBLIC KEY',
+    body: asn1.toDer(pki.publicKeyToRSAPublicKey(key)).getBytes()
+  };
+  return forge.pem.encode(msg, {maxline: maxline});
+};
+
+/**
+ * Gets a fingerprint for the given public key.
+ *
+ * @param options the options to use.
+ *          [md] the message digest object to use (defaults to forge.md.sha1).
+ *          [type] the type of fingerprint, such as 'RSAPublicKey',
+ *            'SubjectPublicKeyInfo' (defaults to 'RSAPublicKey').
+ *          [encoding] an alternative output encoding, such as 'hex'
+ *            (defaults to none, outputs a byte buffer).
+ *          [delimiter] the delimiter to use between bytes for 'hex' encoded
+ *            output, eg: ':' (defaults to none).
+ *
+ * @return the fingerprint as a byte buffer or other encoding based on options.
+ */
+pki.getPublicKeyFingerprint = function(key, options) {
+  options = options || {};
+  var md = options.md || forge.md.sha1.create();
+  var type = options.type || 'RSAPublicKey';
+
+  var bytes;
+  switch(type) {
+    case 'RSAPublicKey':
+      bytes = asn1.toDer(pki.publicKeyToRSAPublicKey(key)).getBytes();
+      break;
+    case 'SubjectPublicKeyInfo':
+      bytes = asn1.toDer(pki.publicKeyToAsn1(key)).getBytes();
+      break;
+    default:
+      throw new Error('Unknown fingerprint type "' + options.type + '".');
+  }
+
+  // hash public key bytes
+  md.start();
+  md.update(bytes);
+  var digest = md.digest();
+  if(options.encoding === 'hex') {
+    var hex = digest.toHex();
+    if(options.delimiter) {
+      return hex.match(/.{2}/g).join(options.delimiter);
+    }
+    return hex;
+  } else if(options.encoding === 'binary') {
+    return digest.getBytes();
+  } else if(options.encoding) {
+    throw new Error('Unknown encoding "' + options.encoding + '".');
+  }
+  return digest;
+};
+
+/**
+ * Converts a PKCS#10 certification request (CSR) from PEM format.
+ *
+ * Note: If the certification request is to be verified then compute hash
+ * should be set to true. This will scan the CertificationRequestInfo part of
+ * the ASN.1 object while it is converted so it doesn't need to be converted
+ * back to ASN.1-DER-encoding later.
+ *
+ * @param pem the PEM-formatted certificate.
+ * @param computeHash true to compute the hash for verification.
+ * @param strict true to be strict when checking ASN.1 value lengths, false to
+ *          allow truncated values (default: true).
+ *
+ * @return the certification request (CSR).
+ */
+pki.certificationRequestFromPem = function(pem, computeHash, strict) {
+  var msg = forge.pem.decode(pem)[0];
+
+  if(msg.type !== 'CERTIFICATE REQUEST') {
+    var error = new Error('Could not convert certification request from PEM; ' +
+      'PEM header type is not "CERTIFICATE REQUEST".');
+    error.headerType = msg.type;
+    throw error;
+  }
+  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
+    throw new Error('Could not convert certification request from PEM; ' +
+      'PEM is encrypted.');
+  }
+
+  // convert DER to ASN.1 object
+  var obj = asn1.fromDer(msg.body, strict);
+
+  return pki.certificationRequestFromAsn1(obj, computeHash);
+};
+
+/**
+ * Converts a PKCS#10 certification request (CSR) to PEM format.
+ *
+ * @param csr the certification request.
+ * @param maxline the maximum characters per line, defaults to 64.
+ *
+ * @return the PEM-formatted certification request.
+ */
+pki.certificationRequestToPem = function(csr, maxline) {
+  // convert to ASN.1, then DER, then PEM-encode
+  var msg = {
+    type: 'CERTIFICATE REQUEST',
+    body: asn1.toDer(pki.certificationRequestToAsn1(csr)).getBytes()
+  };
+  return forge.pem.encode(msg, {maxline: maxline});
+};
+
+/**
+ * Creates an empty X.509v3 RSA certificate.
+ *
+ * @return the certificate.
+ */
+pki.createCertificate = function() {
+  var cert = {};
+  cert.version = 0x02;
+  cert.serialNumber = '00';
+  cert.signatureOid = null;
+  cert.signature = null;
+  cert.siginfo = {};
+  cert.siginfo.algorithmOid = null;
+  cert.validity = {};
+  cert.validity.notBefore = new Date();
+  cert.validity.notAfter = new Date();
+
+  cert.issuer = {};
+  cert.issuer.getField = function(sn) {
+    return _getAttribute(cert.issuer, sn);
+  };
+  cert.issuer.addField = function(attr) {
+    _fillMissingFields([attr]);
+    cert.issuer.attributes.push(attr);
+  };
+  cert.issuer.attributes = [];
+  cert.issuer.hash = null;
+
+  cert.subject = {};
+  cert.subject.getField = function(sn) {
+    return _getAttribute(cert.subject, sn);
+  };
+  cert.subject.addField = function(attr) {
+    _fillMissingFields([attr]);
+    cert.subject.attributes.push(attr);
+  };
+  cert.subject.attributes = [];
+  cert.subject.hash = null;
+
+  cert.extensions = [];
+  cert.publicKey = null;
+  cert.md = null;
+
+  /**
+   * Sets the subject of this certificate.
+   *
+   * @param attrs the array of subject attributes to use.
+   * @param uniqueId an optional a unique ID to use.
+   */
+  cert.setSubject = function(attrs, uniqueId) {
+    // set new attributes, clear hash
+    _fillMissingFields(attrs);
+    cert.subject.attributes = attrs;
+    delete cert.subject.uniqueId;
+    if(uniqueId) {
+      // TODO: support arbitrary bit length ids
+      cert.subject.uniqueId = uniqueId;
+    }
+    cert.subject.hash = null;
+  };
+
+  /**
+   * Sets the issuer of this certificate.
+   *
+   * @param attrs the array of issuer attributes to use.
+   * @param uniqueId an optional a unique ID to use.
+   */
+  cert.setIssuer = function(attrs, uniqueId) {
+    // set new attributes, clear hash
+    _fillMissingFields(attrs);
+    cert.issuer.attributes = attrs;
+    delete cert.issuer.uniqueId;
+    if(uniqueId) {
+      // TODO: support arbitrary bit length ids
+      cert.issuer.uniqueId = uniqueId;
+    }
+    cert.issuer.hash = null;
+  };
+
+  /**
+   * Sets the extensions of this certificate.
+   *
+   * @param exts the array of extensions to use.
+   */
+  cert.setExtensions = function(exts) {
+    for(var i = 0; i < exts.length; ++i) {
+      _fillMissingExtensionFields(exts[i], {cert: cert});
+    }
+    // set new extensions
+    cert.extensions = exts;
+  };
+
+  /**
+   * Gets an extension by its name or id.
+   *
+   * @param options the name to use or an object with:
+   *          name the name to use.
+   *          id the id to use.
+   *
+   * @return the extension or null if not found.
+   */
+  cert.getExtension = function(options) {
+    if(typeof options === 'string') {
+      options = {name: options};
+    }
+
+    var rval = null;
+    var ext;
+    for(var i = 0; rval === null && i < cert.extensions.length; ++i) {
+      ext = cert.extensions[i];
+      if(options.id && ext.id === options.id) {
+        rval = ext;
+      } else if(options.name && ext.name === options.name) {
+        rval = ext;
+      }
+    }
+    return rval;
+  };
+
+  /**
+   * Signs this certificate using the given private key.
+   *
+   * @param key the private key to sign with.
+   * @param md the message digest object to use (defaults to forge.md.sha1).
+   */
+  cert.sign = function(key, md) {
+    // TODO: get signature OID from private key
+    cert.md = md || forge.md.sha1.create();
+    var algorithmOid = oids[cert.md.algorithm + 'WithRSAEncryption'];
+    if(!algorithmOid) {
+      var error = new Error('Could not compute certificate digest. ' +
+        'Unknown message digest algorithm OID.');
+      error.algorithm = cert.md.algorithm;
+      throw error;
+    }
+    cert.signatureOid = cert.siginfo.algorithmOid = algorithmOid;
+
+    // get TBSCertificate, convert to DER
+    cert.tbsCertificate = pki.getTBSCertificate(cert);
+    var bytes = asn1.toDer(cert.tbsCertificate);
+
+    // digest and sign
+    cert.md.update(bytes.getBytes());
+    cert.signature = key.sign(cert.md);
+  };
+
+  /**
+   * Attempts verify the signature on the passed certificate using this
+   * certificate's public key.
+   *
+   * @param child the certificate to verify.
+   *
+   * @return true if verified, false if not.
+   */
+  cert.verify = function(child) {
+    var rval = false;
+
+    if(!cert.issued(child)) {
+      var issuer = child.issuer;
+      var subject = cert.subject;
+      var error = new Error(
+        'The parent certificate did not issue the given child ' +
+        'certificate; the child certificate\'s issuer does not match the ' +
+        'parent\'s subject.');
+      error.expectedIssuer = subject.attributes;
+      error.actualIssuer = issuer.attributes;
+      throw error;
+    }
+
+    var md = child.md;
+    if(md === null) {
+      // create digest for OID signature types
+      md = _createSignatureDigest({
+        signatureOid: child.signatureOid,
+        type: 'certificate'
+      });
+
+      // produce DER formatted TBSCertificate and digest it
+      var tbsCertificate = child.tbsCertificate || pki.getTBSCertificate(child);
+      var bytes = asn1.toDer(tbsCertificate);
+      md.update(bytes.getBytes());
+    }
+
+    if(md !== null) {
+      rval = _verifySignature({
+        certificate: cert, md: md, signature: child.signature
+      });
+    }
+
+    return rval;
+  };
+
+  /**
+   * Returns true if this certificate's issuer matches the passed
+   * certificate's subject. Note that no signature check is performed.
+   *
+   * @param parent the certificate to check.
+   *
+   * @return true if this certificate's issuer matches the passed certificate's
+   *         subject.
+   */
+  cert.isIssuer = function(parent) {
+    var rval = false;
+
+    var i = cert.issuer;
+    var s = parent.subject;
+
+    // compare hashes if present
+    if(i.hash && s.hash) {
+      rval = (i.hash === s.hash);
+    } else if(i.attributes.length === s.attributes.length) {
+      // all attributes are the same so issuer matches subject
+      rval = true;
+      var iattr, sattr;
+      for(var n = 0; rval && n < i.attributes.length; ++n) {
+        iattr = i.attributes[n];
+        sattr = s.attributes[n];
+        if(iattr.type !== sattr.type || iattr.value !== sattr.value) {
+          // attribute mismatch
+          rval = false;
+        }
+      }
+    }
+
+    return rval;
+  };
+
+  /**
+   * Returns true if this certificate's subject matches the issuer of the
+   * given certificate). Note that not signature check is performed.
+   *
+   * @param child the certificate to check.
+   *
+   * @return true if this certificate's subject matches the passed
+   *         certificate's issuer.
+   */
+  cert.issued = function(child) {
+    return child.isIssuer(cert);
+  };
+
+  /**
+   * Generates the subjectKeyIdentifier for this certificate as byte buffer.
+   *
+   * @return the subjectKeyIdentifier for this certificate as byte buffer.
+   */
+  cert.generateSubjectKeyIdentifier = function() {
+    /* See: 4.2.1.2 section of the the RFC3280, keyIdentifier is either:
+
+      (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
+        value of the BIT STRING subjectPublicKey (excluding the tag,
+        length, and number of unused bits).
+
+      (2) The keyIdentifier is composed of a four bit type field with
+        the value 0100 followed by the least significant 60 bits of the
+        SHA-1 hash of the value of the BIT STRING subjectPublicKey
+        (excluding the tag, length, and number of unused bit string bits).
+    */
+
+    // skipping the tag, length, and number of unused bits is the same
+    // as just using the RSAPublicKey (for RSA keys, which are the
+    // only ones supported)
+    return pki.getPublicKeyFingerprint(cert.publicKey, {type: 'RSAPublicKey'});
+  };
+
+  /**
+   * Verifies the subjectKeyIdentifier extension value for this certificate
+   * against its public key. If no extension is found, false will be
+   * returned.
+   *
+   * @return true if verified, false if not.
+   */
+  cert.verifySubjectKeyIdentifier = function() {
+    var oid = oids['subjectKeyIdentifier'];
+    for(var i = 0; i < cert.extensions.length; ++i) {
+      var ext = cert.extensions[i];
+      if(ext.id === oid) {
+        var ski = cert.generateSubjectKeyIdentifier().getBytes();
+        return (forge.util.hexToBytes(ext.subjectKeyIdentifier) === ski);
+      }
+    }
+    return false;
+  };
+
+  return cert;
+};
+
+/**
+ * Converts an X.509v3 RSA certificate from an ASN.1 object.
+ *
+ * Note: If the certificate is to be verified then compute hash should
+ * be set to true. There is currently no implementation for converting
+ * a certificate back to ASN.1 so the TBSCertificate part of the ASN.1
+ * object needs to be scanned before the cert object is created.
+ *
+ * @param obj the asn1 representation of an X.509v3 RSA certificate.
+ * @param computeHash true to compute the hash for verification.
+ *
+ * @return the certificate.
+ */
+pki.certificateFromAsn1 = function(obj, computeHash) {
+  // validate certificate and capture data
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, x509CertificateValidator, capture, errors)) {
+    var error = new Error('Cannot read X.509 certificate. ' +
+      'ASN.1 object is not an X509v3 Certificate.');
+    error.errors = errors;
+    throw error;
+  }
+
+  // get oid
+  var oid = asn1.derToOid(capture.publicKeyOid);
+  if(oid !== pki.oids.rsaEncryption) {
+    throw new Error('Cannot read public key. OID is not RSA.');
+  }
+
+  // create certificate
+  var cert = pki.createCertificate();
+  cert.version = capture.certVersion ?
+    capture.certVersion.charCodeAt(0) : 0;
+  var serial = forge.util.createBuffer(capture.certSerialNumber);
+  cert.serialNumber = serial.toHex();
+  cert.signatureOid = forge.asn1.derToOid(capture.certSignatureOid);
+  cert.signatureParameters = _readSignatureParameters(
+    cert.signatureOid, capture.certSignatureParams, true);
+  cert.siginfo.algorithmOid = forge.asn1.derToOid(capture.certinfoSignatureOid);
+  cert.siginfo.parameters = _readSignatureParameters(cert.siginfo.algorithmOid,
+    capture.certinfoSignatureParams, false);
+  cert.signature = capture.certSignature;
+
+  var validity = [];
+  if(capture.certValidity1UTCTime !== undefined) {
+    validity.push(asn1.utcTimeToDate(capture.certValidity1UTCTime));
+  }
+  if(capture.certValidity2GeneralizedTime !== undefined) {
+    validity.push(asn1.generalizedTimeToDate(
+      capture.certValidity2GeneralizedTime));
+  }
+  if(capture.certValidity3UTCTime !== undefined) {
+    validity.push(asn1.utcTimeToDate(capture.certValidity3UTCTime));
+  }
+  if(capture.certValidity4GeneralizedTime !== undefined) {
+    validity.push(asn1.generalizedTimeToDate(
+      capture.certValidity4GeneralizedTime));
+  }
+  if(validity.length > 2) {
+    throw new Error('Cannot read notBefore/notAfter validity times; more ' +
+      'than two times were provided in the certificate.');
+  }
+  if(validity.length < 2) {
+    throw new Error('Cannot read notBefore/notAfter validity times; they ' +
+      'were not provided as either UTCTime or GeneralizedTime.');
+  }
+  cert.validity.notBefore = validity[0];
+  cert.validity.notAfter = validity[1];
+
+  // keep TBSCertificate to preserve signature when exporting
+  cert.tbsCertificate = capture.tbsCertificate;
+
+  if(computeHash) {
+    // create digest for OID signature type
+    cert.md = _createSignatureDigest({
+      signatureOid: cert.signatureOid,
+      type: 'certificate'
+    });
+
+    // produce DER formatted TBSCertificate and digest it
+    var bytes = asn1.toDer(cert.tbsCertificate);
+    cert.md.update(bytes.getBytes());
+  }
+
+  // handle issuer, build issuer message digest
+  var imd = forge.md.sha1.create();
+  var ibytes = asn1.toDer(capture.certIssuer);
+  imd.update(ibytes.getBytes());
+  cert.issuer.getField = function(sn) {
+    return _getAttribute(cert.issuer, sn);
+  };
+  cert.issuer.addField = function(attr) {
+    _fillMissingFields([attr]);
+    cert.issuer.attributes.push(attr);
+  };
+  cert.issuer.attributes = pki.RDNAttributesAsArray(capture.certIssuer);
+  if(capture.certIssuerUniqueId) {
+    cert.issuer.uniqueId = capture.certIssuerUniqueId;
+  }
+  cert.issuer.hash = imd.digest().toHex();
+
+  // handle subject, build subject message digest
+  var smd = forge.md.sha1.create();
+  var sbytes = asn1.toDer(capture.certSubject);
+  smd.update(sbytes.getBytes());
+  cert.subject.getField = function(sn) {
+    return _getAttribute(cert.subject, sn);
+  };
+  cert.subject.addField = function(attr) {
+    _fillMissingFields([attr]);
+    cert.subject.attributes.push(attr);
+  };
+  cert.subject.attributes = pki.RDNAttributesAsArray(capture.certSubject);
+  if(capture.certSubjectUniqueId) {
+    cert.subject.uniqueId = capture.certSubjectUniqueId;
+  }
+  cert.subject.hash = smd.digest().toHex();
+
+  // handle extensions
+  if(capture.certExtensions) {
+    cert.extensions = pki.certificateExtensionsFromAsn1(capture.certExtensions);
+  } else {
+    cert.extensions = [];
+  }
+
+  // convert RSA public key from ASN.1
+  cert.publicKey = pki.publicKeyFromAsn1(capture.subjectPublicKeyInfo);
+
+  return cert;
+};
+
+/**
+ * Converts an ASN.1 extensions object (with extension sequences as its
+ * values) into an array of extension objects with types and values.
+ *
+ * Supported extensions:
+ *
+ * id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
+ * KeyUsage ::= BIT STRING {
+ *   digitalSignature        (0),
+ *   nonRepudiation          (1),
+ *   keyEncipherment         (2),
+ *   dataEncipherment        (3),
+ *   keyAgreement            (4),
+ *   keyCertSign             (5),
+ *   cRLSign                 (6),
+ *   encipherOnly            (7),
+ *   decipherOnly            (8)
+ * }
+ *
+ * id-ce-basicConstraints OBJECT IDENTIFIER ::=  { id-ce 19 }
+ * BasicConstraints ::= SEQUENCE {
+ *   cA                      BOOLEAN DEFAULT FALSE,
+ *   pathLenConstraint       INTEGER (0..MAX) OPTIONAL
+ * }
+ *
+ * subjectAltName EXTENSION ::= {
+ *   SYNTAX GeneralNames
+ *   IDENTIFIED BY id-ce-subjectAltName
+ * }
+ *
+ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ *
+ * GeneralName ::= CHOICE {
+ *   otherName      [0] INSTANCE OF OTHER-NAME,
+ *   rfc822Name     [1] IA5String,
+ *   dNSName        [2] IA5String,
+ *   x400Address    [3] ORAddress,
+ *   directoryName  [4] Name,
+ *   ediPartyName   [5] EDIPartyName,
+ *   uniformResourceIdentifier [6] IA5String,
+ *   IPAddress      [7] OCTET STRING,
+ *   registeredID   [8] OBJECT IDENTIFIER
+ * }
+ *
+ * OTHER-NAME ::= TYPE-IDENTIFIER
+ *
+ * EDIPartyName ::= SEQUENCE {
+ *   nameAssigner [0] DirectoryString {ub-name} OPTIONAL,
+ *   partyName    [1] DirectoryString {ub-name}
+ * }
+ *
+ * @param exts the extensions ASN.1 with extension sequences to parse.
+ *
+ * @return the array.
+ */
+pki.certificateExtensionsFromAsn1 = function(exts) {
+  var rval = [];
+  for(var i = 0; i < exts.value.length; ++i) {
+    // get extension sequence
+    var extseq = exts.value[i];
+    for(var ei = 0; ei < extseq.value.length; ++ei) {
+      rval.push(pki.certificateExtensionFromAsn1(extseq.value[ei]));
+    }
+  }
+
+  return rval;
+};
+
+/**
+ * Parses a single certificate extension from ASN.1.
+ *
+ * @param ext the extension in ASN.1 format.
+ *
+ * @return the parsed extension as an object.
+ */
+pki.certificateExtensionFromAsn1 = function(ext) {
+  // an extension has:
+  // [0] extnID      OBJECT IDENTIFIER
+  // [1] critical    BOOLEAN DEFAULT FALSE
+  // [2] extnValue   OCTET STRING
+  var e = {};
+  e.id = asn1.derToOid(ext.value[0].value);
+  e.critical = false;
+  if(ext.value[1].type === asn1.Type.BOOLEAN) {
+    e.critical = (ext.value[1].value.charCodeAt(0) !== 0x00);
+    e.value = ext.value[2].value;
+  } else {
+    e.value = ext.value[1].value;
+  }
+  // if the oid is known, get its name
+  if(e.id in oids) {
+    e.name = oids[e.id];
+
+    // handle key usage
+    if(e.name === 'keyUsage') {
+      // get value as BIT STRING
+      var ev = asn1.fromDer(e.value);
+      var b2 = 0x00;
+      var b3 = 0x00;
+      if(ev.value.length > 1) {
+        // skip first byte, just indicates unused bits which
+        // will be padded with 0s anyway
+        // get bytes with flag bits
+        b2 = ev.value.charCodeAt(1);
+        b3 = ev.value.length > 2 ? ev.value.charCodeAt(2) : 0;
+      }
+      // set flags
+      e.digitalSignature = (b2 & 0x80) === 0x80;
+      e.nonRepudiation = (b2 & 0x40) === 0x40;
+      e.keyEncipherment = (b2 & 0x20) === 0x20;
+      e.dataEncipherment = (b2 & 0x10) === 0x10;
+      e.keyAgreement = (b2 & 0x08) === 0x08;
+      e.keyCertSign = (b2 & 0x04) === 0x04;
+      e.cRLSign = (b2 & 0x02) === 0x02;
+      e.encipherOnly = (b2 & 0x01) === 0x01;
+      e.decipherOnly = (b3 & 0x80) === 0x80;
+    } else if(e.name === 'basicConstraints') {
+      // handle basic constraints
+      // get value as SEQUENCE
+      var ev = asn1.fromDer(e.value);
+      // get cA BOOLEAN flag (defaults to false)
+      if(ev.value.length > 0 && ev.value[0].type === asn1.Type.BOOLEAN) {
+        e.cA = (ev.value[0].value.charCodeAt(0) !== 0x00);
+      } else {
+        e.cA = false;
+      }
+      // get path length constraint
+      var value = null;
+      if(ev.value.length > 0 && ev.value[0].type === asn1.Type.INTEGER) {
+        value = ev.value[0].value;
+      } else if(ev.value.length > 1) {
+        value = ev.value[1].value;
+      }
+      if(value !== null) {
+        e.pathLenConstraint = asn1.derToInteger(value);
+      }
+    } else if(e.name === 'extKeyUsage') {
+      // handle extKeyUsage
+      // value is a SEQUENCE of OIDs
+      var ev = asn1.fromDer(e.value);
+      for(var vi = 0; vi < ev.value.length; ++vi) {
+        var oid = asn1.derToOid(ev.value[vi].value);
+        if(oid in oids) {
+          e[oids[oid]] = true;
+        } else {
+          e[oid] = true;
+        }
+      }
+    } else if(e.name === 'nsCertType') {
+      // handle nsCertType
+      // get value as BIT STRING
+      var ev = asn1.fromDer(e.value);
+      var b2 = 0x00;
+      if(ev.value.length > 1) {
+        // skip first byte, just indicates unused bits which
+        // will be padded with 0s anyway
+        // get bytes with flag bits
+        b2 = ev.value.charCodeAt(1);
+      }
+      // set flags
+      e.client = (b2 & 0x80) === 0x80;
+      e.server = (b2 & 0x40) === 0x40;
+      e.email = (b2 & 0x20) === 0x20;
+      e.objsign = (b2 & 0x10) === 0x10;
+      e.reserved = (b2 & 0x08) === 0x08;
+      e.sslCA = (b2 & 0x04) === 0x04;
+      e.emailCA = (b2 & 0x02) === 0x02;
+      e.objCA = (b2 & 0x01) === 0x01;
+    } else if(
+      e.name === 'subjectAltName' ||
+      e.name === 'issuerAltName') {
+      // handle subjectAltName/issuerAltName
+      e.altNames = [];
+
+      // ev is a SYNTAX SEQUENCE
+      var gn;
+      var ev = asn1.fromDer(e.value);
+      for(var n = 0; n < ev.value.length; ++n) {
+        // get GeneralName
+        gn = ev.value[n];
+
+        var altName = {
+          type: gn.type,
+          value: gn.value
+        };
+        e.altNames.push(altName);
+
+        // Note: Support for types 1,2,6,7,8
+        switch(gn.type) {
+          // rfc822Name
+          case 1:
+          // dNSName
+          case 2:
+          // uniformResourceIdentifier (URI)
+          case 6:
+            break;
+          // IPAddress
+          case 7:
+            // convert to IPv4/IPv6 string representation
+            altName.ip = forge.util.bytesToIP(gn.value);
+            break;
+          // registeredID
+          case 8:
+            altName.oid = asn1.derToOid(gn.value);
+            break;
+          default:
+            // unsupported
+        }
+      }
+    } else if(e.name === 'subjectKeyIdentifier') {
+      // value is an OCTETSTRING w/the hash of the key-type specific
+      // public key structure (eg: RSAPublicKey)
+      var ev = asn1.fromDer(e.value);
+      e.subjectKeyIdentifier = forge.util.bytesToHex(ev.value);
+    }
+  }
+  return e;
+};
+
+/**
+ * Converts a PKCS#10 certification request (CSR) from an ASN.1 object.
+ *
+ * Note: If the certification request is to be verified then compute hash
+ * should be set to true. There is currently no implementation for converting
+ * a certificate back to ASN.1 so the CertificationRequestInfo part of the
+ * ASN.1 object needs to be scanned before the csr object is created.
+ *
+ * @param obj the asn1 representation of a PKCS#10 certification request (CSR).
+ * @param computeHash true to compute the hash for verification.
+ *
+ * @return the certification request (CSR).
+ */
+pki.certificationRequestFromAsn1 = function(obj, computeHash) {
+  // validate certification request and capture data
+  var capture = {};
+  var errors = [];
+  if(!asn1.validate(obj, certificationRequestValidator, capture, errors)) {
+    var error = new Error('Cannot read PKCS#10 certificate request. ' +
+      'ASN.1 object is not a PKCS#10 CertificationRequest.');
+    error.errors = errors;
+    throw error;
+  }
+
+  // get oid
+  var oid = asn1.derToOid(capture.publicKeyOid);
+  if(oid !== pki.oids.rsaEncryption) {
+    throw new Error('Cannot read public key. OID is not RSA.');
+  }
+
+  // create certification request
+  var csr = pki.createCertificationRequest();
+  csr.version = capture.csrVersion ? capture.csrVersion.charCodeAt(0) : 0;
+  csr.signatureOid = forge.asn1.derToOid(capture.csrSignatureOid);
+  csr.signatureParameters = _readSignatureParameters(
+    csr.signatureOid, capture.csrSignatureParams, true);
+  csr.siginfo.algorithmOid = forge.asn1.derToOid(capture.csrSignatureOid);
+  csr.siginfo.parameters = _readSignatureParameters(
+    csr.siginfo.algorithmOid, capture.csrSignatureParams, false);
+  csr.signature = capture.csrSignature;
+
+  // keep CertificationRequestInfo to preserve signature when exporting
+  csr.certificationRequestInfo = capture.certificationRequestInfo;
+
+  if(computeHash) {
+    // create digest for OID signature type
+    csr.md = _createSignatureDigest({
+      signatureOid: csr.signatureOid,
+      type: 'certification request'
+    });
+
+    // produce DER formatted CertificationRequestInfo and digest it
+    var bytes = asn1.toDer(csr.certificationRequestInfo);
+    csr.md.update(bytes.getBytes());
+  }
+
+  // handle subject, build subject message digest
+  var smd = forge.md.sha1.create();
+  csr.subject.getField = function(sn) {
+    return _getAttribute(csr.subject, sn);
+  };
+  csr.subject.addField = function(attr) {
+    _fillMissingFields([attr]);
+    csr.subject.attributes.push(attr);
+  };
+  csr.subject.attributes = pki.RDNAttributesAsArray(
+    capture.certificationRequestInfoSubject, smd);
+  csr.subject.hash = smd.digest().toHex();
+
+  // convert RSA public key from ASN.1
+  csr.publicKey = pki.publicKeyFromAsn1(capture.subjectPublicKeyInfo);
+
+  // convert attributes from ASN.1
+  csr.getAttribute = function(sn) {
+    return _getAttribute(csr, sn);
+  };
+  csr.addAttribute = function(attr) {
+    _fillMissingFields([attr]);
+    csr.attributes.push(attr);
+  };
+  csr.attributes = pki.CRIAttributesAsArray(
+    capture.certificationRequestInfoAttributes || []);
+
+  return csr;
+};
+
+/**
+ * Creates an empty certification request (a CSR or certificate signing
+ * request). Once created, its public key and attributes can be set and then
+ * it can be signed.
+ *
+ * @return the empty certification request.
+ */
+pki.createCertificationRequest = function() {
+  var csr = {};
+  csr.version = 0x00;
+  csr.signatureOid = null;
+  csr.signature = null;
+  csr.siginfo = {};
+  csr.siginfo.algorithmOid = null;
+
+  csr.subject = {};
+  csr.subject.getField = function(sn) {
+    return _getAttribute(csr.subject, sn);
+  };
+  csr.subject.addField = function(attr) {
+    _fillMissingFields([attr]);
+    csr.subject.attributes.push(attr);
+  };
+  csr.subject.attributes = [];
+  csr.subject.hash = null;
+
+  csr.publicKey = null;
+  csr.attributes = [];
+  csr.getAttribute = function(sn) {
+    return _getAttribute(csr, sn);
+  };
+  csr.addAttribute = function(attr) {
+    _fillMissingFields([attr]);
+    csr.attributes.push(attr);
+  };
+  csr.md = null;
+
+  /**
+   * Sets the subject of this certification request.
+   *
+   * @param attrs the array of subject attributes to use.
+   */
+  csr.setSubject = function(attrs) {
+    // set new attributes
+    _fillMissingFields(attrs);
+    csr.subject.attributes = attrs;
+    csr.subject.hash = null;
+  };
+
+  /**
+   * Sets the attributes of this certification request.
+   *
+   * @param attrs the array of attributes to use.
+   */
+  csr.setAttributes = function(attrs) {
+    // set new attributes
+    _fillMissingFields(attrs);
+    csr.attributes = attrs;
+  };
+
+  /**
+   * Signs this certification request using the given private key.
+   *
+   * @param key the private key to sign with.
+   * @param md the message digest object to use (defaults to forge.md.sha1).
+   */
+  csr.sign = function(key, md) {
+    // TODO: get signature OID from private key
+    csr.md = md || forge.md.sha1.create();
+    var algorithmOid = oids[csr.md.algorithm + 'WithRSAEncryption'];
+    if(!algorithmOid) {
+      var error = new Error('Could not compute certification request digest. ' +
+        'Unknown message digest algorithm OID.');
+      error.algorithm = csr.md.algorithm;
+      throw error;
+    }
+    csr.signatureOid = csr.siginfo.algorithmOid = algorithmOid;
+
+    // get CertificationRequestInfo, convert to DER
+    csr.certificationRequestInfo = pki.getCertificationRequestInfo(csr);
+    var bytes = asn1.toDer(csr.certificationRequestInfo);
+
+    // digest and sign
+    csr.md.update(bytes.getBytes());
+    csr.signature = key.sign(csr.md);
+  };
+
+  /**
+   * Attempts verify the signature on the passed certification request using
+   * its public key.
+   *
+   * A CSR that has been exported to a file in PEM format can be verified using
+   * OpenSSL using this command:
+   *
+   * openssl req -in <the-csr-pem-file> -verify -noout -text
+   *
+   * @return true if verified, false if not.
+   */
+  csr.verify = function() {
+    var rval = false;
+
+    var md = csr.md;
+    if(md === null) {
+      md = _createSignatureDigest({
+        signatureOid: csr.signatureOid,
+        type: 'certification request'
+      });
+
+      // produce DER formatted CertificationRequestInfo and digest it
+      var cri = csr.certificationRequestInfo ||
+        pki.getCertificationRequestInfo(csr);
+      var bytes = asn1.toDer(cri);
+      md.update(bytes.getBytes());
+    }
+
+    if(md !== null) {
+      rval = _verifySignature({
+        certificate: csr, md: md, signature: csr.signature
+      });
+    }
+
+    return rval;
+  };
+
+  return csr;
+};
+
+/**
+ * Converts an X.509 subject or issuer to an ASN.1 RDNSequence.
+ *
+ * @param obj the subject or issuer (distinguished name).
+ *
+ * @return the ASN.1 RDNSequence.
+ */
+function _dnToAsn1(obj) {
+  // create an empty RDNSequence
+  var rval = asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+
+  // iterate over attributes
+  var attr, set;
+  var attrs = obj.attributes;
+  for(var i = 0; i < attrs.length; ++i) {
+    attr = attrs[i];
+    var value = attr.value;
+
+    // reuse tag class for attribute value if available
+    var valueTagClass = asn1.Type.PRINTABLESTRING;
+    if('valueTagClass' in attr) {
+      valueTagClass = attr.valueTagClass;
+
+      if(valueTagClass === asn1.Type.UTF8) {
+        value = forge.util.encodeUtf8(value);
+      }
+      // FIXME: handle more encodings
+    }
+
+    // create a RelativeDistinguishedName set
+    // each value in the set is an AttributeTypeAndValue first
+    // containing the type (an OID) and second the value
+    set = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // AttributeType
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+          asn1.oidToDer(attr.type).getBytes()),
+        // AttributeValue
+        asn1.create(asn1.Class.UNIVERSAL, valueTagClass, false, value)
+      ])
+    ]);
+    rval.value.push(set);
+  }
+
+  return rval;
+}
+
+/**
+ * Gets all printable attributes (typically of an issuer or subject) in a
+ * simplified JSON format for display.
+ *
+ * @param attrs the attributes.
+ *
+ * @return the JSON for display.
+ */
+function _getAttributesAsJson(attrs) {
+  var rval = {};
+  for(var i = 0; i < attrs.length; ++i) {
+    var attr = attrs[i];
+    if(attr.shortName && (
+      attr.valueTagClass === asn1.Type.UTF8 ||
+      attr.valueTagClass === asn1.Type.PRINTABLESTRING ||
+      attr.valueTagClass === asn1.Type.IA5STRING)) {
+      var value = attr.value;
+      if(attr.valueTagClass === asn1.Type.UTF8) {
+        value = forge.util.encodeUtf8(attr.value);
+      }
+      if(!(attr.shortName in rval)) {
+        rval[attr.shortName] = value;
+      } else if(forge.util.isArray(rval[attr.shortName])) {
+        rval[attr.shortName].push(value);
+      } else {
+        rval[attr.shortName] = [rval[attr.shortName], value];
+      }
+    }
+  }
+  return rval;
+}
+
+/**
+ * Fills in missing fields in attributes.
+ *
+ * @param attrs the attributes to fill missing fields in.
+ */
+function _fillMissingFields(attrs) {
+  var attr;
+  for(var i = 0; i < attrs.length; ++i) {
+    attr = attrs[i];
+
+    // populate missing name
+    if(typeof attr.name === 'undefined') {
+      if(attr.type && attr.type in pki.oids) {
+        attr.name = pki.oids[attr.type];
+      } else if(attr.shortName && attr.shortName in _shortNames) {
+        attr.name = pki.oids[_shortNames[attr.shortName]];
+      }
+    }
+
+    // populate missing type (OID)
+    if(typeof attr.type === 'undefined') {
+      if(attr.name && attr.name in pki.oids) {
+        attr.type = pki.oids[attr.name];
+      } else {
+        var error = new Error('Attribute type not specified.');
+        error.attribute = attr;
+        throw error;
+      }
+    }
+
+    // populate missing shortname
+    if(typeof attr.shortName === 'undefined') {
+      if(attr.name && attr.name in _shortNames) {
+        attr.shortName = _shortNames[attr.name];
+      }
+    }
+
+    // convert extensions to value
+    if(attr.type === oids.extensionRequest) {
+      attr.valueConstructed = true;
+      attr.valueTagClass = asn1.Type.SEQUENCE;
+      if(!attr.value && attr.extensions) {
+        attr.value = [];
+        for(var ei = 0; ei < attr.extensions.length; ++ei) {
+          attr.value.push(pki.certificateExtensionToAsn1(
+            _fillMissingExtensionFields(attr.extensions[ei])));
+        }
+      }
+    }
+
+    if(typeof attr.value === 'undefined') {
+      var error = new Error('Attribute value not specified.');
+      error.attribute = attr;
+      throw error;
+    }
+  }
+}
+
+/**
+ * Fills in missing fields in certificate extensions.
+ *
+ * @param e the extension.
+ * @param [options] the options to use.
+ *          [cert] the certificate the extensions are for.
+ *
+ * @return the extension.
+ */
+function _fillMissingExtensionFields(e, options) {
+  options = options || {};
+
+  // populate missing name
+  if(typeof e.name === 'undefined') {
+    if(e.id && e.id in pki.oids) {
+      e.name = pki.oids[e.id];
+    }
+  }
+
+  // populate missing id
+  if(typeof e.id === 'undefined') {
+    if(e.name && e.name in pki.oids) {
+      e.id = pki.oids[e.name];
+    } else {
+      var error = new Error('Extension ID not specified.');
+      error.extension = e;
+      throw error;
+    }
+  }
+
+  if(typeof e.value !== 'undefined') {
+    return e;
+  }
+
+  // handle missing value:
+
+  // value is a BIT STRING
+  if(e.name === 'keyUsage') {
+    // build flags
+    var unused = 0;
+    var b2 = 0x00;
+    var b3 = 0x00;
+    if(e.digitalSignature) {
+      b2 |= 0x80;
+      unused = 7;
+    }
+    if(e.nonRepudiation) {
+      b2 |= 0x40;
+      unused = 6;
+    }
+    if(e.keyEncipherment) {
+      b2 |= 0x20;
+      unused = 5;
+    }
+    if(e.dataEncipherment) {
+      b2 |= 0x10;
+      unused = 4;
+    }
+    if(e.keyAgreement) {
+      b2 |= 0x08;
+      unused = 3;
+    }
+    if(e.keyCertSign) {
+      b2 |= 0x04;
+      unused = 2;
+    }
+    if(e.cRLSign) {
+      b2 |= 0x02;
+      unused = 1;
+    }
+    if(e.encipherOnly) {
+      b2 |= 0x01;
+      unused = 0;
+    }
+    if(e.decipherOnly) {
+      b3 |= 0x80;
+      unused = 7;
+    }
+
+    // create bit string
+    var value = String.fromCharCode(unused);
+    if(b3 !== 0) {
+      value += String.fromCharCode(b2) + String.fromCharCode(b3);
+    } else if(b2 !== 0) {
+      value += String.fromCharCode(b2);
+    }
+    e.value = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, value);
+  } else if(e.name === 'basicConstraints') {
+    // basicConstraints is a SEQUENCE
+    e.value = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+    // cA BOOLEAN flag defaults to false
+    if(e.cA) {
+      e.value.value.push(asn1.create(
+        asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false,
+        String.fromCharCode(0xFF)));
+    }
+    if('pathLenConstraint' in e) {
+      e.value.value.push(asn1.create(
+        asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+        asn1.integerToDer(e.pathLenConstraint).getBytes()));
+    }
+  } else if(e.name === 'extKeyUsage') {
+    // extKeyUsage is a SEQUENCE of OIDs
+    e.value = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+    var seq = e.value.value;
+    for(var key in e) {
+      if(e[key] !== true) {
+        continue;
+      }
+      // key is name in OID map
+      if(key in oids) {
+        seq.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID,
+          false, asn1.oidToDer(oids[key]).getBytes()));
+      } else if(key.indexOf('.') !== -1) {
+        // assume key is an OID
+        seq.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID,
+          false, asn1.oidToDer(key).getBytes()));
+      }
+    }
+  } else if(e.name === 'nsCertType') {
+    // nsCertType is a BIT STRING
+    // build flags
+    var unused = 0;
+    var b2 = 0x00;
+
+    if(e.client) {
+      b2 |= 0x80;
+      unused = 7;
+    }
+    if(e.server) {
+      b2 |= 0x40;
+      unused = 6;
+    }
+    if(e.email) {
+      b2 |= 0x20;
+      unused = 5;
+    }
+    if(e.objsign) {
+      b2 |= 0x10;
+      unused = 4;
+    }
+    if(e.reserved) {
+      b2 |= 0x08;
+      unused = 3;
+    }
+    if(e.sslCA) {
+      b2 |= 0x04;
+      unused = 2;
+    }
+    if(e.emailCA) {
+      b2 |= 0x02;
+      unused = 1;
+    }
+    if(e.objCA) {
+      b2 |= 0x01;
+      unused = 0;
+    }
+
+    // create bit string
+    var value = String.fromCharCode(unused);
+    if(b2 !== 0) {
+      value += String.fromCharCode(b2);
+    }
+    e.value = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, value);
+  } else if(e.name === 'subjectAltName' || e.name === 'issuerAltName') {
+    // SYNTAX SEQUENCE
+    e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+
+    var altName;
+    for(var n = 0; n < e.altNames.length; ++n) {
+      altName = e.altNames[n];
+      var value = altName.value;
+      // handle IP
+      if(altName.type === 7 && altName.ip) {
+        value = forge.util.bytesFromIP(altName.ip);
+        if(value === null) {
+          var error = new Error(
+            'Extension "ip" value is not a valid IPv4 or IPv6 address.');
+          error.extension = e;
+          throw error;
+        }
+      } else if(altName.type === 8) {
+        // handle OID
+        if(altName.oid) {
+          value = asn1.oidToDer(asn1.oidToDer(altName.oid));
+        } else {
+          // deprecated ... convert value to OID
+          value = asn1.oidToDer(value);
+        }
+      }
+      e.value.value.push(asn1.create(
+        asn1.Class.CONTEXT_SPECIFIC, altName.type, false,
+        value));
+    }
+  } else if(e.name === 'nsComment' && options.cert) {
+    // sanity check value is ASCII (req'd) and not too big
+    if(!(/^[\x00-\x7F]*$/.test(e.comment)) ||
+      (e.comment.length < 1) || (e.comment.length > 128)) {
+      throw new Error('Invalid "nsComment" content.');
+    }
+    // IA5STRING opaque comment
+    e.value = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.IA5STRING, false, e.comment);
+  } else if(e.name === 'subjectKeyIdentifier' && options.cert) {
+    var ski = options.cert.generateSubjectKeyIdentifier();
+    e.subjectKeyIdentifier = ski.toHex();
+    // OCTETSTRING w/digest
+    e.value = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, ski.getBytes());
+  } else if(e.name === 'authorityKeyIdentifier' && options.cert) {
+    // SYNTAX SEQUENCE
+    e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+    var seq = e.value.value;
+
+    if(e.keyIdentifier) {
+      var keyIdentifier = (e.keyIdentifier === true ?
+        options.cert.generateSubjectKeyIdentifier().getBytes() :
+        e.keyIdentifier);
+      seq.push(
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, false, keyIdentifier));
+    }
+
+    if(e.authorityCertIssuer) {
+      var authorityCertIssuer = [
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 4, true, [
+          _dnToAsn1(e.authorityCertIssuer === true ?
+            options.cert.issuer : e.authorityCertIssuer)
+        ])
+      ];
+      seq.push(
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, authorityCertIssuer));
+    }
+
+    if(e.serialNumber) {
+      var serialNumber = forge.util.hexToBytes(e.serialNumber === true ?
+        options.cert.serialNumber : e.serialNumber);
+      seq.push(
+        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, false, serialNumber));
+    }
+  } else if(e.name === 'cRLDistributionPoints') {
+    e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+    var seq = e.value.value;
+
+    // Create sub SEQUENCE of DistributionPointName
+    var subSeq = asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+
+    // Create fullName CHOICE
+    var fullNameGeneralNames = asn1.create(
+      asn1.Class.CONTEXT_SPECIFIC, 0, true, []);
+    var altName;
+    for(var n = 0; n < e.altNames.length; ++n) {
+      altName = e.altNames[n];
+      var value = altName.value;
+      // handle IP
+      if(altName.type === 7 && altName.ip) {
+        value = forge.util.bytesFromIP(altName.ip);
+        if(value === null) {
+          var error = new Error(
+            'Extension "ip" value is not a valid IPv4 or IPv6 address.');
+          error.extension = e;
+          throw error;
+        }
+      } else if(altName.type === 8) {
+        // handle OID
+        if(altName.oid) {
+          value = asn1.oidToDer(asn1.oidToDer(altName.oid));
+        } else {
+          // deprecated ... convert value to OID
+          value = asn1.oidToDer(value);
+        }
+      }
+      fullNameGeneralNames.value.push(asn1.create(
+        asn1.Class.CONTEXT_SPECIFIC, altName.type, false,
+        value));
+    }
+
+    // Add to the parent SEQUENCE
+    subSeq.value.push(asn1.create(
+      asn1.Class.CONTEXT_SPECIFIC, 0, true, [fullNameGeneralNames]));
+    seq.push(subSeq);
+  }
+
+  // ensure value has been defined by now
+  if(typeof e.value === 'undefined') {
+    var error = new Error('Extension value not specified.');
+    error.extension = e;
+    throw error;
+  }
+
+  return e;
+}
+
+/**
+ * Convert signature parameters object to ASN.1
+ *
+ * @param {String} oid Signature algorithm OID
+ * @param params The signature parameters object
+ * @return ASN.1 object representing signature parameters
+ */
+function _signatureParametersToAsn1(oid, params) {
+  switch(oid) {
+    case oids['RSASSA-PSS']:
+      var parts = [];
+
+      if(params.hash.algorithmOid !== undefined) {
+        parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+              asn1.oidToDer(params.hash.algorithmOid).getBytes()),
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+          ])
+        ]));
+      }
+
+      if(params.mgf.algorithmOid !== undefined) {
+        parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+              asn1.oidToDer(params.mgf.algorithmOid).getBytes()),
+            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+              asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+                asn1.oidToDer(params.mgf.hash.algorithmOid).getBytes()),
+              asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+            ])
+          ])
+        ]));
+      }
+
+      if(params.saltLength !== undefined) {
+        parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
+          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+            asn1.integerToDer(params.saltLength).getBytes())
+        ]));
+      }
+
+      return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, parts);
+
+    default:
+      return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '');
+  }
+}
+
+/**
+ * Converts a certification request's attributes to an ASN.1 set of
+ * CRIAttributes.
+ *
+ * @param csr certification request.
+ *
+ * @return the ASN.1 set of CRIAttributes.
+ */
+function _CRIAttributesToAsn1(csr) {
+  // create an empty context-specific container
+  var rval = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, []);
+
+  // no attributes, return empty container
+  if(csr.attributes.length === 0) {
+    return rval;
+  }
+
+  // each attribute has a sequence with a type and a set of values
+  var attrs = csr.attributes;
+  for(var i = 0; i < attrs.length; ++i) {
+    var attr = attrs[i];
+    var value = attr.value;
+
+    // reuse tag class for attribute value if available
+    var valueTagClass = asn1.Type.UTF8;
+    if('valueTagClass' in attr) {
+      valueTagClass = attr.valueTagClass;
+    }
+    if(valueTagClass === asn1.Type.UTF8) {
+      value = forge.util.encodeUtf8(value);
+    }
+    var valueConstructed = false;
+    if('valueConstructed' in attr) {
+      valueConstructed = attr.valueConstructed;
+    }
+    // FIXME: handle more encodings
+
+    // create a RelativeDistinguishedName set
+    // each value in the set is an AttributeTypeAndValue first
+    // containing the type (an OID) and second the value
+    var seq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // AttributeType
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(attr.type).getBytes()),
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
+        // AttributeValue
+        asn1.create(
+          asn1.Class.UNIVERSAL, valueTagClass, valueConstructed, value)
+      ])
+    ]);
+    rval.value.push(seq);
+  }
+
+  return rval;
+}
+
+var jan_1_1950 = new Date('1950-01-01T00:00:00Z');
+var jan_1_2050 = new Date('2050-01-01T00:00:00Z');
+
+/**
+ * Converts a Date object to ASN.1
+ * Handles the different format before and after 1st January 2050
+ *
+ * @param date date object.
+ *
+ * @return the ASN.1 object representing the date.
+ */
+function _dateToAsn1(date) {
+  if(date >= jan_1_1950 && date < jan_1_2050) {
+    return asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,
+      asn1.dateToUtcTime(date));
+  } else {
+    return asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false,
+      asn1.dateToGeneralizedTime(date));
+  }
+}
+
+/**
+ * Gets the ASN.1 TBSCertificate part of an X.509v3 certificate.
+ *
+ * @param cert the certificate.
+ *
+ * @return the asn1 TBSCertificate.
+ */
+pki.getTBSCertificate = function(cert) {
+  // TBSCertificate
+  var notBefore = _dateToAsn1(cert.validity.notBefore);
+  var notAfter = _dateToAsn1(cert.validity.notAfter);
+  var tbs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // version
+    asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
+      // integer
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+        asn1.integerToDer(cert.version).getBytes())
+    ]),
+    // serialNumber
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      forge.util.hexToBytes(cert.serialNumber)),
+    // signature
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // algorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(cert.siginfo.algorithmOid).getBytes()),
+      // parameters
+      _signatureParametersToAsn1(
+        cert.siginfo.algorithmOid, cert.siginfo.parameters)
+    ]),
+    // issuer
+    _dnToAsn1(cert.issuer),
+    // validity
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      notBefore,
+      notAfter
+    ]),
+    // subject
+    _dnToAsn1(cert.subject),
+    // SubjectPublicKeyInfo
+    pki.publicKeyToAsn1(cert.publicKey)
+  ]);
+
+  if(cert.issuer.uniqueId) {
+    // issuerUniqueID (optional)
+    tbs.value.push(
+      asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,
+          // TODO: support arbitrary bit length ids
+          String.fromCharCode(0x00) +
+          cert.issuer.uniqueId
+        )
+      ])
+    );
+  }
+  if(cert.subject.uniqueId) {
+    // subjectUniqueID (optional)
+    tbs.value.push(
+      asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,
+          // TODO: support arbitrary bit length ids
+          String.fromCharCode(0x00) +
+          cert.subject.uniqueId
+        )
+      ])
+    );
+  }
+
+  if(cert.extensions.length > 0) {
+    // extensions (optional)
+    tbs.value.push(pki.certificateExtensionsToAsn1(cert.extensions));
+  }
+
+  return tbs;
+};
+
+/**
+ * Gets the ASN.1 CertificationRequestInfo part of a
+ * PKCS#10 CertificationRequest.
+ *
+ * @param csr the certification request.
+ *
+ * @return the asn1 CertificationRequestInfo.
+ */
+pki.getCertificationRequestInfo = function(csr) {
+  // CertificationRequestInfo
+  var cri = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // version
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
+      asn1.integerToDer(csr.version).getBytes()),
+    // subject
+    _dnToAsn1(csr.subject),
+    // SubjectPublicKeyInfo
+    pki.publicKeyToAsn1(csr.publicKey),
+    // attributes
+    _CRIAttributesToAsn1(csr)
+  ]);
+
+  return cri;
+};
+
+/**
+ * Converts a DistinguishedName (subject or issuer) to an ASN.1 object.
+ *
+ * @param dn the DistinguishedName.
+ *
+ * @return the asn1 representation of a DistinguishedName.
+ */
+pki.distinguishedNameToAsn1 = function(dn) {
+  return _dnToAsn1(dn);
+};
+
+/**
+ * Converts an X.509v3 RSA certificate to an ASN.1 object.
+ *
+ * @param cert the certificate.
+ *
+ * @return the asn1 representation of an X.509v3 RSA certificate.
+ */
+pki.certificateToAsn1 = function(cert) {
+  // prefer cached TBSCertificate over generating one
+  var tbsCertificate = cert.tbsCertificate || pki.getTBSCertificate(cert);
+
+  // Certificate
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // TBSCertificate
+    tbsCertificate,
+    // AlgorithmIdentifier (signature algorithm)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // algorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(cert.signatureOid).getBytes()),
+      // parameters
+      _signatureParametersToAsn1(cert.signatureOid, cert.signatureParameters)
+    ]),
+    // SignatureValue
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,
+      String.fromCharCode(0x00) + cert.signature)
+  ]);
+};
+
+/**
+ * Converts X.509v3 certificate extensions to ASN.1.
+ *
+ * @param exts the extensions to convert.
+ *
+ * @return the extensions in ASN.1 format.
+ */
+pki.certificateExtensionsToAsn1 = function(exts) {
+  // create top-level extension container
+  var rval = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 3, true, []);
+
+  // create extension sequence (stores a sequence for each extension)
+  var seq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+  rval.value.push(seq);
+
+  for(var i = 0; i < exts.length; ++i) {
+    seq.value.push(pki.certificateExtensionToAsn1(exts[i]));
+  }
+
+  return rval;
+};
+
+/**
+ * Converts a single certificate extension to ASN.1.
+ *
+ * @param ext the extension to convert.
+ *
+ * @return the extension in ASN.1 format.
+ */
+pki.certificateExtensionToAsn1 = function(ext) {
+  // create a sequence for each extension
+  var extseq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
+
+  // extnID (OID)
+  extseq.value.push(asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+    asn1.oidToDer(ext.id).getBytes()));
+
+  // critical defaults to false
+  if(ext.critical) {
+    // critical BOOLEAN DEFAULT FALSE
+    extseq.value.push(asn1.create(
+      asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false,
+      String.fromCharCode(0xFF)));
+  }
+
+  var value = ext.value;
+  if(typeof ext.value !== 'string') {
+    // value is asn.1
+    value = asn1.toDer(value).getBytes();
+  }
+
+  // extnValue (OCTET STRING)
+  extseq.value.push(asn1.create(
+    asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, value));
+
+  return extseq;
+};
+
+/**
+ * Converts a PKCS#10 certification request to an ASN.1 object.
+ *
+ * @param csr the certification request.
+ *
+ * @return the asn1 representation of a certification request.
+ */
+pki.certificationRequestToAsn1 = function(csr) {
+  // prefer cached CertificationRequestInfo over generating one
+  var cri = csr.certificationRequestInfo ||
+    pki.getCertificationRequestInfo(csr);
+
+  // Certificate
+  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // CertificationRequestInfo
+    cri,
+    // AlgorithmIdentifier (signature algorithm)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // algorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
+        asn1.oidToDer(csr.signatureOid).getBytes()),
+      // parameters
+      _signatureParametersToAsn1(csr.signatureOid, csr.signatureParameters)
+    ]),
+    // signature
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,
+      String.fromCharCode(0x00) + csr.signature)
+  ]);
+};
+
+/**
+ * Creates a CA store.
+ *
+ * @param certs an optional array of certificate objects or PEM-formatted
+ *          certificate strings to add to the CA store.
+ *
+ * @return the CA store.
+ */
+pki.createCaStore = function(certs) {
+  // create CA store
+  var caStore = {
+    // stored certificates
+    certs: {}
+  };
+
+  /**
+   * Gets the certificate that issued the passed certificate or its
+   * 'parent'.
+   *
+   * @param cert the certificate to get the parent for.
+   *
+   * @return the parent certificate or null if none was found.
+   */
+  caStore.getIssuer = function(cert) {
+    var rval = getBySubject(cert.issuer);
+
+    // see if there are multiple matches
+    /*if(forge.util.isArray(rval)) {
+      // TODO: resolve multiple matches by checking
+      // authorityKey/subjectKey/issuerUniqueID/other identifiers, etc.
+      // FIXME: or alternatively do authority key mapping
+      // if possible (X.509v1 certs can't work?)
+      throw new Error('Resolving multiple issuer matches not implemented yet.');
+    }*/
+
+    return rval;
+  };
+
+  /**
+   * Adds a trusted certificate to the store.
+   *
+   * @param cert the certificate to add as a trusted certificate (either a
+   *          pki.certificate object or a PEM-formatted certificate).
+   */
+  caStore.addCertificate = function(cert) {
+    // convert from pem if necessary
+    if(typeof cert === 'string') {
+      cert = forge.pki.certificateFromPem(cert);
+    }
+
+    ensureSubjectHasHash(cert.subject);
+
+    if(!caStore.hasCertificate(cert)) { // avoid duplicate certificates in store
+      if(cert.subject.hash in caStore.certs) {
+        // subject hash already exists, append to array
+        var tmp = caStore.certs[cert.subject.hash];
+        if(!forge.util.isArray(tmp)) {
+          tmp = [tmp];
+        }
+        tmp.push(cert);
+        caStore.certs[cert.subject.hash] = tmp;
+      } else {
+        caStore.certs[cert.subject.hash] = cert;
+      }
+    }
+  };
+
+  /**
+   * Checks to see if the given certificate is in the store.
+   *
+   * @param cert the certificate to check (either a pki.certificate or a
+   *          PEM-formatted certificate).
+   *
+   * @return true if the certificate is in the store, false if not.
+   */
+  caStore.hasCertificate = function(cert) {
+    // convert from pem if necessary
+    if(typeof cert === 'string') {
+      cert = forge.pki.certificateFromPem(cert);
+    }
+
+    var match = getBySubject(cert.subject);
+    if(!match) {
+      return false;
+    }
+    if(!forge.util.isArray(match)) {
+      match = [match];
+    }
+    // compare DER-encoding of certificates
+    var der1 = asn1.toDer(pki.certificateToAsn1(cert)).getBytes();
+    for(var i = 0; i < match.length; ++i) {
+      var der2 = asn1.toDer(pki.certificateToAsn1(match[i])).getBytes();
+      if(der1 === der2) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  /**
+   * Lists all of the certificates kept in the store.
+   *
+   * @return an array of all of the pki.certificate objects in the store.
+   */
+  caStore.listAllCertificates = function() {
+    var certList = [];
+
+    for(var hash in caStore.certs) {
+      if(caStore.certs.hasOwnProperty(hash)) {
+        var value = caStore.certs[hash];
+        if(!forge.util.isArray(value)) {
+          certList.push(value);
+        } else {
+          for(var i = 0; i < value.length; ++i) {
+            certList.push(value[i]);
+          }
+        }
+      }
+    }
+
+    return certList;
+  };
+
+  /**
+   * Removes a certificate from the store.
+   *
+   * @param cert the certificate to remove (either a pki.certificate or a
+   *          PEM-formatted certificate).
+   *
+   * @return the certificate that was removed or null if the certificate
+   *           wasn't in store.
+   */
+  caStore.removeCertificate = function(cert) {
+    var result;
+
+    // convert from pem if necessary
+    if(typeof cert === 'string') {
+      cert = forge.pki.certificateFromPem(cert);
+    }
+    ensureSubjectHasHash(cert.subject);
+    if(!caStore.hasCertificate(cert)) {
+      return null;
+    }
+
+    var match = getBySubject(cert.subject);
+
+    if(!forge.util.isArray(match)) {
+      result = caStore.certs[cert.subject.hash];
+      delete caStore.certs[cert.subject.hash];
+      return result;
+    }
+
+    // compare DER-encoding of certificates
+    var der1 = asn1.toDer(pki.certificateToAsn1(cert)).getBytes();
+    for(var i = 0; i < match.length; ++i) {
+      var der2 = asn1.toDer(pki.certificateToAsn1(match[i])).getBytes();
+      if(der1 === der2) {
+        result = match[i];
+        match.splice(i, 1);
+      }
+    }
+    if(match.length === 0) {
+      delete caStore.certs[cert.subject.hash];
+    }
+
+    return result;
+  };
+
+  function getBySubject(subject) {
+    ensureSubjectHasHash(subject);
+    return caStore.certs[subject.hash] || null;
+  }
+
+  function ensureSubjectHasHash(subject) {
+    // produce subject hash if it doesn't exist
+    if(!subject.hash) {
+      var md = forge.md.sha1.create();
+      subject.attributes = pki.RDNAttributesAsArray(_dnToAsn1(subject), md);
+      subject.hash = md.digest().toHex();
+    }
+  }
+
+  // auto-add passed in certs
+  if(certs) {
+    // parse PEM-formatted certificates as necessary
+    for(var i = 0; i < certs.length; ++i) {
+      var cert = certs[i];
+      caStore.addCertificate(cert);
+    }
+  }
+
+  return caStore;
+};
+
+/**
+ * Certificate verification errors, based on TLS.
+ */
+pki.certificateError = {
+  bad_certificate: 'forge.pki.BadCertificate',
+  unsupported_certificate: 'forge.pki.UnsupportedCertificate',
+  certificate_revoked: 'forge.pki.CertificateRevoked',
+  certificate_expired: 'forge.pki.CertificateExpired',
+  certificate_unknown: 'forge.pki.CertificateUnknown',
+  unknown_ca: 'forge.pki.UnknownCertificateAuthority'
+};
+
+/**
+ * Verifies a certificate chain against the given Certificate Authority store
+ * with an optional custom verify callback.
+ *
+ * @param caStore a certificate store to verify against.
+ * @param chain the certificate chain to verify, with the root or highest
+ *          authority at the end (an array of certificates).
+ * @param options a callback to be called for every certificate in the chain or
+ *                  an object with:
+ *                  verify a callback to be called for every certificate in the
+ *                    chain
+ *                  validityCheckDate the date against which the certificate
+ *                    validity period should be checked. Pass null to not check
+ *                    the validity period. By default, the current date is used.
+ *
+ * The verify callback has the following signature:
+ *
+ * verified - Set to true if certificate was verified, otherwise the
+ *   pki.certificateError for why the certificate failed.
+ * depth - The current index in the chain, where 0 is the end point's cert.
+ * certs - The certificate chain, *NOTE* an empty chain indicates an anonymous
+ *   end point.
+ *
+ * The function returns true on success and on failure either the appropriate
+ * pki.certificateError or an object with 'error' set to the appropriate
+ * pki.certificateError and 'message' set to a custom error message.
+ *
+ * @return true if successful, error thrown if not.
+ */
+pki.verifyCertificateChain = function(caStore, chain, options) {
+  /* From: RFC3280 - Internet X.509 Public Key Infrastructure Certificate
+    Section 6: Certification Path Validation
+    See inline parentheticals related to this particular implementation.
+
+    The primary goal of path validation is to verify the binding between
+    a subject distinguished name or a subject alternative name and subject
+    public key, as represented in the end entity certificate, based on the
+    public key of the trust anchor. This requires obtaining a sequence of
+    certificates that support that binding. That sequence should be provided
+    in the passed 'chain'. The trust anchor should be in the given CA
+    store. The 'end entity' certificate is the certificate provided by the
+    end point (typically a server) and is the first in the chain.
+
+    To meet this goal, the path validation process verifies, among other
+    things, that a prospective certification path (a sequence of n
+    certificates or a 'chain') satisfies the following conditions:
+
+    (a) for all x in {1, ..., n-1}, the subject of certificate x is
+          the issuer of certificate x+1;
+
+    (b) certificate 1 is issued by the trust anchor;
+
+    (c) certificate n is the certificate to be validated; and
+
+    (d) for all x in {1, ..., n}, the certificate was valid at the
+          time in question.
+
+    Note that here 'n' is index 0 in the chain and 1 is the last certificate
+    in the chain and it must be signed by a certificate in the connection's
+    CA store.
+
+    The path validation process also determines the set of certificate
+    policies that are valid for this path, based on the certificate policies
+    extension, policy mapping extension, policy constraints extension, and
+    inhibit any-policy extension.
+
+    Note: Policy mapping extension not supported (Not Required).
+
+    Note: If the certificate has an unsupported critical extension, then it
+    must be rejected.
+
+    Note: A certificate is self-issued if the DNs that appear in the subject
+    and issuer fields are identical and are not empty.
+
+    The path validation algorithm assumes the following seven inputs are
+    provided to the path processing logic. What this specific implementation
+    will use is provided parenthetically:
+
+    (a) a prospective certification path of length n (the 'chain')
+    (b) the current date/time: ('now').
+    (c) user-initial-policy-set: A set of certificate policy identifiers
+          naming the policies that are acceptable to the certificate user.
+          The user-initial-policy-set contains the special value any-policy
+          if the user is not concerned about certificate policy
+          (Not implemented. Any policy is accepted).
+    (d) trust anchor information, describing a CA that serves as a trust
+          anchor for the certification path. The trust anchor information
+          includes:
+
+      (1)  the trusted issuer name,
+      (2)  the trusted public key algorithm,
+      (3)  the trusted public key, and
+      (4)  optionally, the trusted public key parameters associated
+             with the public key.
+
+      (Trust anchors are provided via certificates in the CA store).
+
+      The trust anchor information may be provided to the path processing
+      procedure in the form of a self-signed certificate. The trusted anchor
+      information is trusted because it was delivered to the path processing
+      procedure by some trustworthy out-of-band procedure. If the trusted
+      public key algorithm requires parameters, then the parameters are
+      provided along with the trusted public key (No parameters used in this
+      implementation).
+
+    (e) initial-policy-mapping-inhibit, which indicates if policy mapping is
+          allowed in the certification path.
+          (Not implemented, no policy checking)
+
+    (f) initial-explicit-policy, which indicates if the path must be valid
+          for at least one of the certificate policies in the user-initial-
+          policy-set.
+          (Not implemented, no policy checking)
+
+    (g) initial-any-policy-inhibit, which indicates whether the
+          anyPolicy OID should be processed if it is included in a
+          certificate.
+          (Not implemented, so any policy is valid provided that it is
+          not marked as critical) */
+
+  /* Basic Path Processing:
+
+    For each certificate in the 'chain', the following is checked:
+
+    1. The certificate validity period includes the current time.
+    2. The certificate was signed by its parent (where the parent is either
+       the next in the chain or from the CA store). Allow processing to
+       continue to the next step if no parent is found but the certificate is
+       in the CA store.
+    3. TODO: The certificate has not been revoked.
+    4. The certificate issuer name matches the parent's subject name.
+    5. TODO: If the certificate is self-issued and not the final certificate
+       in the chain, skip this step, otherwise verify that the subject name
+       is within one of the permitted subtrees of X.500 distinguished names
+       and that each of the alternative names in the subjectAltName extension
+       (critical or non-critical) is within one of the permitted subtrees for
+       that name type.
+    6. TODO: If the certificate is self-issued and not the final certificate
+       in the chain, skip this step, otherwise verify that the subject name
+       is not within one of the excluded subtrees for X.500 distinguished
+       names and none of the subjectAltName extension names are excluded for
+       that name type.
+    7. The other steps in the algorithm for basic path processing involve
+       handling the policy extension which is not presently supported in this
+       implementation. Instead, if a critical policy extension is found, the
+       certificate is rejected as not supported.
+    8. If the certificate is not the first or if its the only certificate in
+       the chain (having no parent from the CA store or is self-signed) and it
+       has a critical key usage extension, verify that the keyCertSign bit is
+       set. If the key usage extension exists, verify that the basic
+       constraints extension exists. If the basic constraints extension exists,
+       verify that the cA flag is set. If pathLenConstraint is set, ensure that
+       the number of certificates that precede in the chain (come earlier
+       in the chain as implemented below), excluding the very first in the
+       chain (typically the end-entity one), isn't greater than the
+       pathLenConstraint. This constraint limits the number of intermediate
+       CAs that may appear below a CA before only end-entity certificates
+       may be issued. */
+
+  // if a verify callback is passed as the third parameter, package it within
+  // the options object. This is to support a legacy function signature that
+  // expected the verify callback as the third parameter.
+  if(typeof options === 'function') {
+    options = {verify: options};
+  }
+  options = options || {};
+
+  // copy cert chain references to another array to protect against changes
+  // in verify callback
+  chain = chain.slice(0);
+  var certs = chain.slice(0);
+
+  var validityCheckDate = options.validityCheckDate;
+  // if no validityCheckDate is specified, default to the current date. Make
+  // sure to maintain the value null because it indicates that the validity
+  // period should not be checked.
+  if(typeof validityCheckDate === 'undefined') {
+    validityCheckDate = new Date();
+  }
+
+  // verify each cert in the chain using its parent, where the parent
+  // is either the next in the chain or from the CA store
+  var first = true;
+  var error = null;
+  var depth = 0;
+  do {
+    var cert = chain.shift();
+    var parent = null;
+    var selfSigned = false;
+
+    if(validityCheckDate) {
+      // 1. check valid time
+      if(validityCheckDate < cert.validity.notBefore ||
+         validityCheckDate > cert.validity.notAfter) {
+        error = {
+          message: 'Certificate is not valid yet or has expired.',
+          error: pki.certificateError.certificate_expired,
+          notBefore: cert.validity.notBefore,
+          notAfter: cert.validity.notAfter,
+          // TODO: we might want to reconsider renaming 'now' to
+          // 'validityCheckDate' should this API be changed in the future.
+          now: validityCheckDate
+        };
+      }
+    }
+
+    // 2. verify with parent from chain or CA store
+    if(error === null) {
+      parent = chain[0] || caStore.getIssuer(cert);
+      if(parent === null) {
+        // check for self-signed cert
+        if(cert.isIssuer(cert)) {
+          selfSigned = true;
+          parent = cert;
+        }
+      }
+
+      if(parent) {
+        // FIXME: current CA store implementation might have multiple
+        // certificates where the issuer can't be determined from the
+        // certificate (happens rarely with, eg: old certificates) so normalize
+        // by always putting parents into an array
+        // TODO: there's may be an extreme degenerate case currently uncovered
+        // where an old intermediate certificate seems to have a matching parent
+        // but none of the parents actually verify ... but the intermediate
+        // is in the CA and it should pass this check; needs investigation
+        var parents = parent;
+        if(!forge.util.isArray(parents)) {
+          parents = [parents];
+        }
+
+        // try to verify with each possible parent (typically only one)
+        var verified = false;
+        while(!verified && parents.length > 0) {
+          parent = parents.shift();
+          try {
+            verified = parent.verify(cert);
+          } catch(ex) {
+            // failure to verify, don't care why, try next one
+          }
+        }
+
+        if(!verified) {
+          error = {
+            message: 'Certificate signature is invalid.',
+            error: pki.certificateError.bad_certificate
+          };
+        }
+      }
+
+      if(error === null && (!parent || selfSigned) &&
+        !caStore.hasCertificate(cert)) {
+        // no parent issuer and certificate itself is not trusted
+        error = {
+          message: 'Certificate is not trusted.',
+          error: pki.certificateError.unknown_ca
+        };
+      }
+    }
+
+    // TODO: 3. check revoked
+
+    // 4. check for matching issuer/subject
+    if(error === null && parent && !cert.isIssuer(parent)) {
+      // parent is not issuer
+      error = {
+        message: 'Certificate issuer is invalid.',
+        error: pki.certificateError.bad_certificate
+      };
+    }
+
+    // 5. TODO: check names with permitted names tree
+
+    // 6. TODO: check names against excluded names tree
+
+    // 7. check for unsupported critical extensions
+    if(error === null) {
+      // supported extensions
+      var se = {
+        keyUsage: true,
+        basicConstraints: true
+      };
+      for(var i = 0; error === null && i < cert.extensions.length; ++i) {
+        var ext = cert.extensions[i];
+        if(ext.critical && !(ext.name in se)) {
+          error = {
+            message:
+              'Certificate has an unsupported critical extension.',
+            error: pki.certificateError.unsupported_certificate
+          };
+        }
+      }
+    }
+
+    // 8. check for CA if cert is not first or is the only certificate
+    // remaining in chain with no parent or is self-signed
+    if(error === null &&
+      (!first || (chain.length === 0 && (!parent || selfSigned)))) {
+      // first check keyUsage extension and then basic constraints
+      var bcExt = cert.getExtension('basicConstraints');
+      var keyUsageExt = cert.getExtension('keyUsage');
+      if(keyUsageExt !== null) {
+        // keyCertSign must be true and there must be a basic
+        // constraints extension
+        if(!keyUsageExt.keyCertSign || bcExt === null) {
+          // bad certificate
+          error = {
+            message:
+              'Certificate keyUsage or basicConstraints conflict ' +
+              'or indicate that the certificate is not a CA. ' +
+              'If the certificate is the only one in the chain or ' +
+              'isn\'t the first then the certificate must be a ' +
+              'valid CA.',
+            error: pki.certificateError.bad_certificate
+          };
+        }
+      }
+      // check for absent basicConstraints on non-leaf certificates
+      if(error === null && bcExt === null) {
+        error = {
+          message:
+            'Certificate is missing basicConstraints extension and cannot ' +
+            'be used as a CA.',
+          error: pki.certificateError.bad_certificate
+        };
+      }
+      // basic constraints cA flag must be set
+      if(error === null && bcExt !== null && !bcExt.cA) {
+        // bad certificate
+        error = {
+          message:
+            'Certificate basicConstraints indicates the certificate ' +
+            'is not a CA.',
+          error: pki.certificateError.bad_certificate
+        };
+      }
+      // if error is not null and keyUsage is available, then we know it
+      // has keyCertSign and there is a basic constraints extension too,
+      // which means we can check pathLenConstraint (if it exists)
+      if(error === null && keyUsageExt !== null &&
+        'pathLenConstraint' in bcExt) {
+        // pathLen is the maximum # of intermediate CA certs that can be
+        // found between the current certificate and the end-entity (depth 0)
+        // certificate; this number does not include the end-entity (depth 0,
+        // last in the chain) even if it happens to be a CA certificate itself
+        var pathLen = depth - 1;
+        if(pathLen > bcExt.pathLenConstraint) {
+          // pathLenConstraint violated, bad certificate
+          error = {
+            message:
+              'Certificate basicConstraints pathLenConstraint violated.',
+            error: pki.certificateError.bad_certificate
+          };
+        }
+      }
+    }
+
+    // call application callback
+    var vfd = (error === null) ? true : error.error;
+    var ret = options.verify ? options.verify(vfd, depth, certs) : vfd;
+    if(ret === true) {
+      // clear any set error
+      error = null;
+    } else {
+      // if passed basic tests, set default message and alert
+      if(vfd === true) {
+        error = {
+          message: 'The application rejected the certificate.',
+          error: pki.certificateError.bad_certificate
+        };
+      }
+
+      // check for custom error info
+      if(ret || ret === 0) {
+        // set custom message and error
+        if(typeof ret === 'object' && !forge.util.isArray(ret)) {
+          if(ret.message) {
+            error.message = ret.message;
+          }
+          if(ret.error) {
+            error.error = ret.error;
+          }
+        } else if(typeof ret === 'string') {
+          // set custom error
+          error.error = ret;
+        }
+      }
+
+      // throw error
+      throw error;
+    }
+
+    // no longer first cert in chain
+    first = false;
+    ++depth;
+  } while(chain.length > 0);
+
+  return true;
+};
Index: frontend/node_modules/node-forge/lib/xhr.js
===================================================================
--- frontend/node_modules/node-forge/lib/xhr.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/lib/xhr.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,738 @@
+/**
+ * XmlHttpRequest implementation that uses TLS and flash SocketPool.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2013 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./socket');
+require('./http');
+
+/* XHR API */
+var xhrApi = module.exports = forge.xhr = forge.xhr || {};
+
+(function($) {
+
+// logging category
+var cat = 'forge.xhr';
+
+/*
+XMLHttpRequest interface definition from:
+http://www.w3.org/TR/XMLHttpRequest
+
+interface XMLHttpRequest {
+  // event handler
+  attribute EventListener onreadystatechange;
+
+  // state
+  const unsigned short UNSENT = 0;
+  const unsigned short OPENED = 1;
+  const unsigned short HEADERS_RECEIVED = 2;
+  const unsigned short LOADING = 3;
+  const unsigned short DONE = 4;
+  readonly attribute unsigned short readyState;
+
+  // request
+  void open(in DOMString method, in DOMString url);
+  void open(in DOMString method, in DOMString url, in boolean async);
+  void open(in DOMString method, in DOMString url,
+            in boolean async, in DOMString user);
+  void open(in DOMString method, in DOMString url,
+            in boolean async, in DOMString user, in DOMString password);
+  void setRequestHeader(in DOMString header, in DOMString value);
+  void send();
+  void send(in DOMString data);
+  void send(in Document data);
+  void abort();
+
+  // response
+  DOMString getAllResponseHeaders();
+  DOMString getResponseHeader(in DOMString header);
+  readonly attribute DOMString responseText;
+  readonly attribute Document responseXML;
+  readonly attribute unsigned short status;
+  readonly attribute DOMString statusText;
+};
+*/
+
+// readyStates
+var UNSENT = 0;
+var OPENED = 1;
+var HEADERS_RECEIVED = 2;
+var LOADING = 3;
+var DONE = 4;
+
+// exceptions
+var INVALID_STATE_ERR = 11;
+var SYNTAX_ERR = 12;
+var SECURITY_ERR = 18;
+var NETWORK_ERR = 19;
+var ABORT_ERR = 20;
+
+// private flash socket pool vars
+var _sp = null;
+var _policyPort = 0;
+var _policyUrl = null;
+
+// default client (used if no special URL provided when creating an XHR)
+var _client = null;
+
+// all clients including the default, key'd by full base url
+// (multiple cross-domain http clients are permitted so there may be more
+// than one client in this map)
+// TODO: provide optional clean up API for non-default clients
+var _clients = {};
+
+// the default maximum number of concurrents connections per client
+var _maxConnections = 10;
+
+var net = forge.net;
+var http = forge.http;
+
+/**
+ * Initializes flash XHR support.
+ *
+ * @param options:
+ *   url: the default base URL to connect to if xhr URLs are relative,
+ *     ie: https://myserver.com.
+ *   flashId: the dom ID of the flash SocketPool.
+ *   policyPort: the port that provides the server's flash policy, 0 to use
+ *     the flash default.
+ *   policyUrl: the policy file URL to use instead of a policy port.
+ *   msie: true if browser is internet explorer, false if not.
+ *   connections: the maximum number of concurrent connections.
+ *   caCerts: a list of PEM-formatted certificates to trust.
+ *   cipherSuites: an optional array of cipher suites to use,
+ *     see forge.tls.CipherSuites.
+ *   verify: optional TLS certificate verify callback to use (see forge.tls
+ *     for details).
+ *   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.
+ */
+xhrApi.init = function(options) {
+  forge.log.debug(cat, 'initializing', options);
+
+  // update default policy port and max connections
+  _policyPort = options.policyPort || _policyPort;
+  _policyUrl = options.policyUrl || _policyUrl;
+  _maxConnections = options.connections || _maxConnections;
+
+  // create the flash socket pool
+  _sp = net.createSocketPool({
+    flashId: options.flashId,
+    policyPort: _policyPort,
+    policyUrl: _policyUrl,
+    msie: options.msie || false
+  });
+
+  // create default http client
+  _client = http.createClient({
+    url: options.url || (
+      window.location.protocol + '//' + window.location.host),
+    socketPool: _sp,
+    policyPort: _policyPort,
+    policyUrl: _policyUrl,
+    connections: options.connections || _maxConnections,
+    caCerts: options.caCerts,
+    cipherSuites: options.cipherSuites,
+    persistCookies: options.persistCookies || true,
+    primeTlsSockets: options.primeTlsSockets || false,
+    verify: options.verify,
+    getCertificate: options.getCertificate,
+    getPrivateKey: options.getPrivateKey,
+    getSignature: options.getSignature
+  });
+  _clients[_client.url.origin] = _client;
+
+  forge.log.debug(cat, 'ready');
+};
+
+/**
+ * Called to clean up the clients and socket pool.
+ */
+xhrApi.cleanup = function() {
+  // destroy all clients
+  for(var key in _clients) {
+    _clients[key].destroy();
+  }
+  _clients = {};
+  _client = null;
+
+  // destroy socket pool
+  _sp.destroy();
+  _sp = null;
+};
+
+/**
+ * Sets a cookie.
+ *
+ * @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.
+ */
+xhrApi.setCookie = function(cookie) {
+  // default cookie expiration to never
+  cookie.maxAge = cookie.maxAge || -1;
+
+  // if the cookie's domain is set, use the appropriate client
+  if(cookie.domain) {
+    // add the cookies to the applicable domains
+    for(var key in _clients) {
+      var client = _clients[key];
+      if(http.withinCookieDomain(client.url, cookie) &&
+        client.secure === cookie.secure) {
+        client.setCookie(cookie);
+      }
+    }
+  } else {
+    // use the default domain
+    // FIXME: should a null domain cookie be added to all clients? should
+    // this be an option?
+    _client.setCookie(cookie);
+  }
+};
+
+/**
+ * Gets a cookie.
+ *
+ * @param name the name of the cookie.
+ * @param path an optional path for the cookie (if there are multiple cookies
+ *          with the same name but different paths).
+ * @param domain an optional domain for the cookie (if not using the default
+ *          domain).
+ *
+ * @return the cookie, cookies (if multiple matches), or null if not found.
+ */
+xhrApi.getCookie = function(name, path, domain) {
+  var rval = null;
+
+  if(domain) {
+    // get the cookies from the applicable domains
+    for(var key in _clients) {
+      var client = _clients[key];
+      if(http.withinCookieDomain(client.url, domain)) {
+        var cookie = client.getCookie(name, path);
+        if(cookie !== null) {
+          if(rval === null) {
+            rval = cookie;
+          } else if(!forge.util.isArray(rval)) {
+            rval = [rval, cookie];
+          } else {
+            rval.push(cookie);
+          }
+        }
+      }
+    }
+  } else {
+    // get cookie from default domain
+    rval = _client.getCookie(name, path);
+  }
+
+  return rval;
+};
+
+/**
+ * Removes a cookie.
+ *
+ * @param name the name of the cookie.
+ * @param path an optional path for the cookie (if there are multiple cookies
+ *          with the same name but different paths).
+ * @param domain an optional domain for the cookie (if not using the default
+ *          domain).
+ *
+ * @return true if a cookie was removed, false if not.
+ */
+xhrApi.removeCookie = function(name, path, domain) {
+  var rval = false;
+
+  if(domain) {
+    // remove the cookies from the applicable domains
+    for(var key in _clients) {
+      var client = _clients[key];
+      if(http.withinCookieDomain(client.url, domain)) {
+        if(client.removeCookie(name, path)) {
+           rval = true;
+        }
+      }
+    }
+  } else {
+    // remove cookie from default domain
+    rval = _client.removeCookie(name, path);
+  }
+
+  return rval;
+};
+
+/**
+ * Creates a new XmlHttpRequest. By default the base URL, flash policy port,
+ * etc, will be used. However, an XHR can be created to point at another
+ * cross-domain URL.
+ *
+ * @param options:
+ *   logWarningOnError: If true and an HTTP error status code is received then
+ *     log a warning, otherwise log a verbose message.
+ *   verbose: If true be very verbose in the output including the response
+ *     event and response body, otherwise only include status, timing, and
+ *     data size.
+ *   logError: a multi-var log function for warnings that takes the log
+ *     category as the first var.
+ *   logWarning: a multi-var log function for warnings that takes the log
+ *     category as the first var.
+ *   logDebug: a multi-var log function for warnings that takes the log
+ *     category as the first var.
+ *   logVerbose: a multi-var log function for warnings that takes the log
+ *     category as the first var.
+ *   url: the default base URL to connect to if xhr URLs are relative,
+ *     eg: https://myserver.com, and note that the following options will be
+ *     ignored if the URL is absent or the same as the default base URL.
+ *   policyPort: the port that provides the server's flash policy, 0 to use
+ *     the flash default.
+ *   policyUrl: the policy file URL to use instead of a policy port.
+ *   connections: the maximum number of concurrent connections.
+ *   caCerts: a list of PEM-formatted certificates to trust.
+ *   cipherSuites: an optional array of cipher suites to use, see
+ *     forge.tls.CipherSuites.
+ *   verify: optional TLS certificate verify callback to use (see forge.tls
+ *     for details).
+ *   getCertificate: an optional callback used to get a client-side
+ *     certificate.
+ *   getPrivateKey: an optional callback used to get a client-side private key.
+ *   getSignature: an optional callback used to get a client-side signature.
+ *   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 XmlHttpRequest.
+ */
+xhrApi.create = function(options) {
+  // set option defaults
+  options = $.extend({
+    logWarningOnError: true,
+    verbose: false,
+    logError: function() {},
+    logWarning: function() {},
+    logDebug: function() {},
+    logVerbose: function() {},
+    url: null
+  }, options || {});
+
+  // private xhr state
+  var _state = {
+    // the http client to use
+    client: null,
+    // request storage
+    request: null,
+    // response storage
+    response: null,
+    // asynchronous, true if doing asynchronous communication
+    asynchronous: true,
+    // sendFlag, true if send has been called
+    sendFlag: false,
+    // errorFlag, true if a network error occurred
+    errorFlag: false
+  };
+
+  // private log functions
+  var _log = {
+    error: options.logError || forge.log.error,
+    warning: options.logWarning || forge.log.warning,
+    debug: options.logDebug || forge.log.debug,
+    verbose: options.logVerbose || forge.log.verbose
+  };
+
+  // create public xhr interface
+  var xhr = {
+    // an EventListener
+    onreadystatechange: null,
+    // readonly, the current readyState
+    readyState: UNSENT,
+    // a string with the response entity-body
+    responseText: '',
+    // a Document for response entity-bodies that are XML
+    responseXML: null,
+    // readonly, returns the HTTP status code (i.e. 404)
+    status: 0,
+    // readonly, returns the HTTP status message (i.e. 'Not Found')
+    statusText: ''
+  };
+
+  // determine which http client to use
+  if(options.url === null) {
+    // use default
+    _state.client = _client;
+  } else {
+    var url;
+    try {
+      url = new URL(options.url);
+    } catch(e) {
+      var error = new Error('Invalid url.');
+      error.details = {
+        url: options.url
+      };
+    }
+
+    // find client
+    if(url.origin in _clients) {
+      // client found
+      _state.client = _clients[url.origin];
+    } else {
+      // create client
+      _state.client = http.createClient({
+        url: options.url,
+        socketPool: _sp,
+        policyPort: options.policyPort || _policyPort,
+        policyUrl: options.policyUrl || _policyUrl,
+        connections: options.connections || _maxConnections,
+        caCerts: options.caCerts,
+        cipherSuites: options.cipherSuites,
+        persistCookies: options.persistCookies || true,
+        primeTlsSockets: options.primeTlsSockets || false,
+        verify: options.verify,
+        getCertificate: options.getCertificate,
+        getPrivateKey: options.getPrivateKey,
+        getSignature: options.getSignature
+      });
+      _clients[url.origin] = _state.client;
+    }
+  }
+
+  /**
+   * Opens the request. This method will create the HTTP request to send.
+   *
+   * @param method the HTTP method (i.e. 'GET').
+   * @param url the relative url (the HTTP request path).
+   * @param async always true, ignored.
+   * @param user always null, ignored.
+   * @param password always null, ignored.
+   */
+  xhr.open = function(method, url, async, user, password) {
+    // 1. validate Document if one is associated
+    // TODO: not implemented (not used yet)
+
+    // 2. validate method token
+    // 3. change method to uppercase if it matches a known
+    // method (here we just require it to be uppercase, and
+    // we do not allow the standard methods)
+    // 4. disallow CONNECT, TRACE, or TRACK with a security error
+    switch(method) {
+    case 'DELETE':
+    case 'GET':
+    case 'HEAD':
+    case 'OPTIONS':
+    case 'PATCH':
+    case 'POST':
+    case 'PUT':
+      // valid method
+      break;
+    case 'CONNECT':
+    case 'TRACE':
+    case 'TRACK':
+      throw new Error('CONNECT, TRACE and TRACK methods are disallowed');
+    default:
+      throw new Error('Invalid method: ' + method);
+    }
+
+    // TODO: other validation steps in algorithm are not implemented
+
+    // 19. set send flag to false
+    // set response body to null
+    // empty list of request headers
+    // set request method to given method
+    // set request URL
+    // set username, password
+    // set asynchronous flag
+    _state.sendFlag = false;
+    xhr.responseText = '';
+    xhr.responseXML = null;
+
+    // custom: reset status and statusText
+    xhr.status = 0;
+    xhr.statusText = '';
+
+    // create the HTTP request
+    _state.request = http.createRequest({
+      method: method,
+      path: url
+    });
+
+    // 20. set state to OPENED
+    xhr.readyState = OPENED;
+
+    // 21. dispatch onreadystatechange
+    if(xhr.onreadystatechange) {
+       xhr.onreadystatechange();
+    }
+  };
+
+  /**
+   * Adds an HTTP header field to the request.
+   *
+   * @param header the name of the header field.
+   * @param value the value of the header field.
+   */
+  xhr.setRequestHeader = function(header, value) {
+    // 1. if state is not OPENED or send flag is true, raise exception
+    if(xhr.readyState != OPENED || _state.sendFlag) {
+      throw new Error('XHR not open or sending');
+    }
+
+    // TODO: other validation steps in spec aren't implemented
+
+    // set header
+    _state.request.setField(header, value);
+  };
+
+  /**
+   * Sends the request and any associated data.
+   *
+   * @param data a string or Document object to send, null to send no data.
+   */
+  xhr.send = function(data) {
+    // 1. if state is not OPENED or 2. send flag is true, raise
+    // an invalid state exception
+    if(xhr.readyState != OPENED || _state.sendFlag) {
+      throw new Error('XHR not open or sending');
+    }
+
+    // 3. ignore data if method is GET or HEAD
+    if(data &&
+      _state.request.method !== 'GET' &&
+      _state.request.method !== 'HEAD') {
+      // handle non-IE case
+      if(typeof(XMLSerializer) !== 'undefined') {
+        if(data instanceof Document) {
+          var xs = new XMLSerializer();
+          _state.request.body = xs.serializeToString(data);
+        } else {
+          _state.request.body = data;
+        }
+      } else {
+        // poorly implemented IE case
+        if(typeof(data.xml) !== 'undefined') {
+          _state.request.body = data.xml;
+        } else {
+          _state.request.body = data;
+        }
+      }
+    }
+
+    // 4. release storage mutex (not used)
+
+    // 5. set error flag to false
+    _state.errorFlag = false;
+
+    // 6. if asynchronous is true (must be in this implementation)
+
+    // 6.1 set send flag to true
+    _state.sendFlag = true;
+
+    // 6.2 dispatch onreadystatechange
+    if(xhr.onreadystatechange) {
+      xhr.onreadystatechange();
+    }
+
+    // create send options
+    var options = {};
+    options.request = _state.request;
+    options.headerReady = function(e) {
+      // make cookies available for ease of use/iteration
+      xhr.cookies = _state.client.cookies;
+
+      // TODO: update document.cookie with any cookies where the
+      // script's domain matches
+
+      // headers received
+      xhr.readyState = HEADERS_RECEIVED;
+      xhr.status = e.response.code;
+      xhr.statusText = e.response.message;
+      _state.response = e.response;
+      if(xhr.onreadystatechange) {
+        xhr.onreadystatechange();
+      }
+      if(!_state.response.aborted) {
+        // now loading body
+        xhr.readyState = LOADING;
+        if(xhr.onreadystatechange) {
+           xhr.onreadystatechange();
+        }
+      }
+    };
+    options.bodyReady = function(e) {
+      xhr.readyState = DONE;
+      var ct = e.response.getField('Content-Type');
+      // Note: this null/undefined check is done outside because IE
+      // dies otherwise on a "'null' is null" error
+      if(ct) {
+        if(ct.indexOf('text/xml') === 0 ||
+          ct.indexOf('application/xml') === 0 ||
+          ct.indexOf('+xml') !== -1) {
+          try {
+            var doc = new ActiveXObject('MicrosoftXMLDOM');
+            doc.async = false;
+            doc.loadXML(e.response.body);
+            xhr.responseXML = doc;
+          } catch(ex) {
+            var parser = new DOMParser();
+            xhr.responseXML = parser.parseFromString(ex.body, 'text/xml');
+          }
+        }
+      }
+
+      var length = 0;
+      if(e.response.body !== null) {
+        xhr.responseText = e.response.body;
+        length = e.response.body.length;
+      }
+      // build logging output
+      var req = _state.request;
+      var output =
+        req.method + ' ' + req.path + ' ' +
+        xhr.status + ' ' + xhr.statusText + ' ' +
+        length + 'B ' +
+        (e.request.connectTime + e.request.time + e.response.time) +
+        'ms';
+      var lFunc;
+      if(options.verbose) {
+        lFunc = (xhr.status >= 400 && options.logWarningOnError) ?
+          _log.warning : _log.verbose;
+        lFunc(cat, output,
+          e, e.response.body ? '\n' + e.response.body : '\nNo content');
+      } else {
+        lFunc = (xhr.status >= 400 && options.logWarningOnError) ?
+          _log.warning : _log.debug;
+        lFunc(cat, output);
+      }
+      if(xhr.onreadystatechange) {
+        xhr.onreadystatechange();
+      }
+    };
+    options.error = function(e) {
+      var req = _state.request;
+      _log.error(cat, req.method + ' ' + req.path, e);
+
+      // 1. set response body to null
+      xhr.responseText = '';
+      xhr.responseXML = null;
+
+      // 2. set error flag to true (and reset status)
+      _state.errorFlag = true;
+      xhr.status = 0;
+      xhr.statusText = '';
+
+      // 3. set state to done
+      xhr.readyState = DONE;
+
+      // 4. asyc flag is always true, so dispatch onreadystatechange
+      if(xhr.onreadystatechange) {
+        xhr.onreadystatechange();
+      }
+    };
+
+    // 7. send request
+    _state.client.send(options);
+  };
+
+  /**
+   * Aborts the request.
+   */
+  xhr.abort = function() {
+    // 1. abort send
+    // 2. stop network activity
+    _state.request.abort();
+
+    // 3. set response to null
+    xhr.responseText = '';
+    xhr.responseXML = null;
+
+    // 4. set error flag to true (and reset status)
+    _state.errorFlag = true;
+    xhr.status = 0;
+    xhr.statusText = '';
+
+    // 5. clear user headers
+    _state.request = null;
+    _state.response = null;
+
+    // 6. if state is DONE or UNSENT, or if OPENED and send flag is false
+    if(xhr.readyState === DONE || xhr.readyState === UNSENT ||
+     (xhr.readyState === OPENED && !_state.sendFlag)) {
+      // 7. set ready state to unsent
+      xhr.readyState = UNSENT;
+    } else {
+      // 6.1 set state to DONE
+      xhr.readyState = DONE;
+
+      // 6.2 set send flag to false
+      _state.sendFlag = false;
+
+      // 6.3 dispatch onreadystatechange
+      if(xhr.onreadystatechange) {
+        xhr.onreadystatechange();
+      }
+
+      // 7. set state to UNSENT
+      xhr.readyState = UNSENT;
+    }
+  };
+
+  /**
+   * Gets all response headers as a string.
+   *
+   * @return the HTTP-encoded response header fields.
+   */
+  xhr.getAllResponseHeaders = function() {
+    var rval = '';
+    if(_state.response !== null) {
+      var fields = _state.response.fields;
+      $.each(fields, function(name, array) {
+        $.each(array, function(i, value) {
+          rval += name + ': ' + value + '\r\n';
+        });
+      });
+    }
+    return rval;
+  };
+
+  /**
+   * Gets a single header field value or, if there are multiple
+   * fields with the same name, a comma-separated list of header
+   * values.
+   *
+   * @return the header field value(s) or null.
+   */
+  xhr.getResponseHeader = function(header) {
+    var rval = null;
+    if(_state.response !== null) {
+      if(header in _state.response.fields) {
+        rval = _state.response.fields[header];
+        if(forge.util.isArray(rval)) {
+          rval = rval.join();
+        }
+      }
+    }
+    return rval;
+  };
+
+  return xhr;
+};
+
+})(jQuery);
Index: frontend/node_modules/node-forge/package.json
===================================================================
--- frontend/node_modules/node-forge/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/node-forge/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,123 @@
+{
+  "name": "node-forge",
+  "version": "1.4.0",
+  "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.",
+  "homepage": "https://github.com/digitalbazaar/forge",
+  "author": {
+    "name": "Digital Bazaar, Inc.",
+    "email": "support@digitalbazaar.com",
+    "url": "http://digitalbazaar.com/"
+  },
+  "contributors": [
+    "Dave Longley <dlongley@digitalbazaar.com>",
+    "David I. Lehn <dlehn@digitalbazaar.com>",
+    "Stefan Siegl <stesie@brokenpipe.de>",
+    "Christoph Dorn <christoph@christophdorn.com>"
+  ],
+  "devDependencies": {
+    "browserify": "^16.5.2",
+    "commander": "^2.20.0",
+    "cross-env": "^5.2.1",
+    "eslint": "^7.27.0",
+    "eslint-config-digitalbazaar": "^2.8.0",
+    "express": "^4.16.2",
+    "karma": "^4.4.1",
+    "karma-browserify": "^7.0.0",
+    "karma-chrome-launcher": "^3.1.0",
+    "karma-edge-launcher": "^0.4.2",
+    "karma-firefox-launcher": "^1.3.0",
+    "karma-ie-launcher": "^1.0.0",
+    "karma-mocha": "^1.3.0",
+    "karma-mocha-reporter": "^2.2.5",
+    "karma-safari-launcher": "^1.0.0",
+    "karma-sauce-launcher": "^2.0.2",
+    "karma-sourcemap-loader": "^0.3.8",
+    "karma-tap-reporter": "0.0.6",
+    "karma-webpack": "^4.0.2",
+    "mocha": "^5.2.0",
+    "mocha-lcov-reporter": "^1.2.0",
+    "nodejs-websocket": "^1.7.1",
+    "nyc": "^15.1.0",
+    "opts": "^1.2.7",
+    "webpack": "^4.44.1",
+    "webpack-cli": "^3.3.12",
+    "worker-loader": "^2.0.0"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/digitalbazaar/forge"
+  },
+  "bugs": {
+    "url": "https://github.com/digitalbazaar/forge/issues",
+    "email": "support@digitalbazaar.com"
+  },
+  "license": "(BSD-3-Clause OR GPL-2.0)",
+  "main": "lib/index.js",
+  "files": [
+    "lib/*.js",
+    "flash/swf/*.swf",
+    "dist/*.min.js",
+    "dist/*.min.js.map"
+  ],
+  "engines": {
+    "node": ">= 6.13.0"
+  },
+  "keywords": [
+    "aes",
+    "asn",
+    "asn.1",
+    "cbc",
+    "crypto",
+    "cryptography",
+    "csr",
+    "des",
+    "gcm",
+    "hmac",
+    "http",
+    "https",
+    "md5",
+    "network",
+    "pkcs",
+    "pki",
+    "prng",
+    "rc2",
+    "rsa",
+    "sha1",
+    "sha256",
+    "sha384",
+    "sha512",
+    "ssh",
+    "tls",
+    "x.509",
+    "x509"
+  ],
+  "scripts": {
+    "prepublish": "npm run build",
+    "build": "webpack",
+    "test-build": "webpack --config webpack-tests.config.js",
+    "test": "npm run test-node",
+    "test-node": "cross-env NODE_ENV=test mocha -t 30000 -R ${REPORTER:-spec} tests/unit/index.js",
+    "test-karma": "karma start",
+    "test-karma-sauce": "karma start karma-sauce.conf",
+    "test-server": "node tests/server.js",
+    "test-server-ws": "node tests/websockets/server-ws.js",
+    "test-server-webid": "node tests/websockets/server-webid.js",
+    "coverage": "rm -rf coverage && nyc --reporter=lcov --reporter=text-summary npm test",
+    "coverage-ci": "rm -rf coverage && nyc --reporter=lcovonly npm test",
+    "coverage-report": "nyc report",
+    "lint": "eslint *.js lib/*.js tests/*.js tests/**/*.js examples/*.js flash/*.js"
+  },
+  "nyc": {
+    "exclude": [
+      "tests"
+    ]
+  },
+  "jspm": {
+    "format": "amd"
+  },
+  "browser": {
+    "buffer": false,
+    "crypto": false,
+    "process": false
+  }
+}
