commit code to git
authorMatthew Vernon <mcv21@cam.ac.uk>
Tue, 22 Jul 2014 13:36:52 +0000 (14:36 +0100)
committerMatthew Vernon <mcv21@cam.ac.uk>
Tue, 22 Jul 2014 13:36:52 +0000 (14:36 +0100)
28 files changed:
Default.asp [new file with mode: 0644]
LICENSE.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
Test.asp [new file with mode: 0644]
Ucam_Webauth.vbs [new file with mode: 0644]
certificates/901.crt [new file with mode: 0644]
certificates/raventest.crt [new file with mode: 0644]
docs/I - Overview of Raven Authentication Process.pdf [new file with mode: 0644]
docs/II - Ucam_Webauth - Flowchart for Valid Authentication.pdf [new file with mode: 0644]
docs/III - Ucam_Webauth - Flowchart for Cancelled Authentication.pdf [new file with mode: 0644]
js/asn1hex-1.1.js [new file with mode: 0644]
js/base64.js [new file with mode: 0644]
js/core.js [new file with mode: 0644]
js/crypto-1.1.js [new file with mode: 0644]
js/hex_sha1_js.js [new file with mode: 0644]
js/jsbn.js [new file with mode: 0644]
js/jsbn2.js [new file with mode: 0644]
js/md5.js [new file with mode: 0644]
js/ripemd160.js [new file with mode: 0644]
js/rsa.js [new file with mode: 0644]
js/rsa2.js [new file with mode: 0644]
js/rsapem-1.1.js [new file with mode: 0644]
js/rsasign-1.2.js [new file with mode: 0644]
js/sha1.js [new file with mode: 0644]
js/sha256.js [new file with mode: 0644]
js/sha512.js [new file with mode: 0644]
js/x509-1.1.js [new file with mode: 0644]
js/x64-core.js [new file with mode: 0644]

diff --git a/Default.asp b/Default.asp
new file mode 100644 (file)
index 0000000..6185f9a
--- /dev/null
@@ -0,0 +1,145 @@
+<!DOCTYPE html>\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\r
+<!--#include file="Ucam_Webauth.vbs"-->\r
+</head>\r
+<%\r
+\r
+Sub Main\r
+\r
+       ' Create 'Scripting.Dictionary' object \r
+       ' to hold the arguments we will supply \r
+       ' to the 'Ucam_Webauth' object.\r
+       \r
+       Set args = CreateObject("Scripting.Dictionary")\r
+       \r
+       ' Add the different arguments to the 'args' \r
+       ' associative array as name/value pairs.\r
+       ' Both name and value must be strings \r
+       ' so integers must be added as "x", eg. "60".\r
+\r
+       ' 'auth_service' is the WLS authentication server.\r
+       ' The following line gives the the demo Raven testing server:                   \r
+       \r
+       ' args.Add "auth_service", "https://demo.raven.cam.ac.uk/auth/authenticate.html"\r
+\r
+       ' We're testing with our Dummy Raven WLS server so use that:\r
+       ' args.Add "auth_service", "http://www2.careers.cam.ac.uk:11812"        \r
+\r
+       ' 'hostname' must be a domain name and perhaps a \r
+       ' port but nothing more.\r
+       \r
+       args.Add "hostname", "localhost:81"\r
+\r
+       ' 'log_file' is the location of the logfile \r
+       ' which must be read/writable by the webserver.\r
+       \r
+       args.Add "log_file", "C:/wamp/www/raven_asp/logs/vbscriptlog.txt"\r
+\r
+       ' 'key_dir' is the directory holding the \r
+       ' public key certificate.\r
+       \r
+       args.Add "key_dir", "C:/wamp/www/raven_asp/certificates"\r
+\r
+       ' 'cookie_key' is the key used to generate \r
+       ' hash values of the authentication cookie. \r
+       ' Ideally it should be changed on a regular basis \r
+       ' but not during sessions.\r
+       \r
+       args.Add "cookie_key", "Random string"\r
+\r
+       ' Create new instance of 'Ucam_Webauth' \r
+       ' and supply arguments.\r
+       ' We do not need to include 'Request' and 'Response' \r
+       ' variables (as in C# version), in order to get/set \r
+       ' cookies and server variables and perform redirects \r
+       ' as these variables are globally accessible to ASP class.\r
+                              \r
+       Set oUcam_Webauth = New Ucam_Webauth\r
+       Call oUcam_Webauth(args)       \r
+\r
+       \r
+       ' For the purposes of testing, we provide \r
+       ' a 'Logout' link that removes the local \r
+       ' authentication cookie and then displays \r
+       ' a link to easily logout the Raven WLS.\r
+       ' So we check to see if this 'Action=Logout' \r
+       ' link has been called and logout/display \r
+       ' link accordingly.\r
+                       \r
+       If (Request.ServerVariables("QUERY_STRING") = "Action=Logout") Then\r
+               oUcam_Webauth.ResetState()\r
+               Response.Write("Logged out of Raven (local)<br/>")\r
+               Response.Write("<a href='https://raven.cam.ac.uk/auth/logout.html'>Logout Raven (remote)</a><br/>" & _\r
+                             "<a href='Default.asp'>Access Raven authenticated page</a>")            \r
+               Exit Sub\r
+       End If          \r
+\r
+       ' When you first access this page \r
+       ' the 'Authenticate' function will be called. \r
+       ' This will typically be called three times \r
+       ' in total to successfully authenticate the \r
+       ' user. In the first two iterations of \r
+       ' 'Authenticate', it will return \r
+       ' 'AUTHENTICATE_INCOMPLETE' while it \r
+       ' redirects the user's browser first to \r
+       ' the Raven WLS and then back to this page.\r
+       ' On the third iteration of 'Authenticate', it \r
+       ' will return 'AUTHENTICATE_COMPLETE_AUTHENTICATED' \r
+       ' or 'AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED' \r
+       ' if the authentication process has fully \r
+       ' completed without error.\r
+               \r
+       Select Case oUcam_Webauth.Authenticate()\r
+               \r
+               Case oUcam_Webauth.AUTHENTICATE_INCOMPLETE\r
+\r
+                       ' 'Authenticate' still redirecting pages \r
+                       ' so don't do anything else.\r
+                       \r
+                       Exit Sub\r
+\r
+               Case oUcam_Webauth.AUTHENTICATE_COMPLETE_AUTHENTICATED          \r
+\r
+                       ' Success so display the 'principal', ie. the user id.\r
+                       \r
+                       Response.Write("SUCCESS. You are " & oUcam_Webauth.principal() & "<br/>")\r
+                       \r
+                       ' Also display the 'ptags' parameter indicating \r
+                       ' whether the user is 'current' or not.\r
+                       \r
+                       Response.Write("Ptags = " & oUcam_Webauth.ptags() & "<br/>")\r
+                       \r
+                       ' Display any 'GET variables' to check they \r
+                       ' have carried through from the original \r
+                       ' page request.\r
+                       \r
+                       For Each item In Request.QueryString() \r
+                       Response.Write item & "=" & Request.QueryString()(item) & "<br/>"  \r
+                       Next                    \r
+                       \r
+                       ' Display a 'Logout' link to make it easy to \r
+                       ' test authentication repeatedly.\r
+                       \r
+                       Response.Write("<a href='Default.asp?Action=Logout'>Logout Raven (local)</a>")                \r
+               \r
+               Case Else\r
+                       \r
+                       ' Either there was an error or a failed \r
+                       ' authentication so print out the result either way.\r
+\r
+                       Response.Write("FAIL - " & oUcam_Webauth.status() & ": " & oUcam_Webauth.msg())\r
+\r
+                       ' Also log the error for debugging purposes.\r
+                                                               \r
+                       oUcam_Webauth.write_log("FAIL - " & oUcam_Webauth.status() & ": " & oUcam_Webauth.msg())\r
+                                       \r
+       End Select\r
+               \r
+End Sub\r
+\r
+Call Main\r
+\r
+%>\r
+</html>\r
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..20c2c4f
--- /dev/null
@@ -0,0 +1,24 @@
+' This VBScript class implements a Raven v3 agent for the University of Cambridge\r
+' Web Authentication System.\r
+'\r
+' See http://raven.cam.ac.uk/project/ for more details\r
+'\r
+' Loosely based on the PHP module for Raven\r
+' https://wiki.cam.ac.uk/raven/PHP_library\r
+'\r
+' Copyright (c) 2004, 2005, 2008, 2014 University of Cambridge\r
+'\r
+' This module is free software; you can redistribute it and/or modify\r
+' it under the terms of the GNU Lesser General Public License as\r
+' published by the Free Software Foundation; either version 2.1 of the\r
+' License, or (at your option) any later version.\r
+'\r
+' The module is distributed in the hope that it will be useful, but\r
+' WITHOUT ANY WARRANTY; without even the implied warranty of\r
+' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+' Lesser General Public License for more details.\r
+'\r
+' You should have received a copy of the GNU Lesser General Public\r
+' License along with this toolkit; if not, write to the Free Software\r
+' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\r
+' USA
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..f4b1ba3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,395 @@
+´╗┐University of Cambridge Classic ASP/VBScript Raven Authentication Module - v.1.0 (29/04/2014)\r
+==========================================================================\r
+\r
+Description\r
+-----------\r
+The software comprises a VBScript class 'Ucam_Webauth' and sample files that \r
+provide a Classic ASP/VBScript implementation of a 'Raven' authentication \r
+module; Raven is the web authentication protocol used by the University of \r
+Cambridge, UK. The logic and code of the 'Ucam_Webauth' class are loosely \r
+based on the PHP Raven authentication class provided at \r
+http://raven.cam.ac.uk/project/. \r
+- For a full description of the latest Raven specification and an explanation \r
+of how Raven works, go to http://raven.cam.ac.uk/project/.\r
+- This software was originally created for the Careers Service, University \r
+of Cambridge by sh801@cam.ac.uk\r
+\r
+Files and folders\r
+-----------------\r
+- [certificates]: Temporary location for Raven public key certificates.\r
+- [js]: Folder containing Javascript cryptography libraries.\r
+- [docs]: Supporting documentation.\r
+- [logs]: Possible location for log files created by the module.\r
+- Default.asp: A sample file showing how the 'Ucam_Webauth' class is used \r
+to provide Raven authentication.\r
+- Test.asp: A test file for unit testing the 'Ucam_Webauth' module using the \r
+'Ucam_RavenWLS' dummy Raven server (separate project, not included).\r
+- Ucam_Webauth.vbs: The main 'Ucam_Webauth' VBScript class.\r
+\r
+Platform requirements\r
+---------------------\r
+This module has been tested on IIS7 with .NET Framework set to \r
+'No managed code', ie. classic ASP.\r
+\r
+Installation\r
+------------\r
+### Cryptographic functions\r
+Cryptographic functions are provided by the Javascript libraries within the \r
+'js' folder. These libraries are versions of the client-side Javascript \r
+libraries provided at http://kjur.github.io/jsrsasign/index.html \r
+but modified to run server-side. There is no need to install any \r
+additional cryptography libraries.\r
+\r
+### Install Raven certificates\r
+The authentication module uses the Raven public key certificate at \r
+https://raven.cam.ac.uk/project/keys/ to verify authentication responses. \r
+Download the certificate from https://raven.cam.ac.uk/project/keys/ and copy \r
+to the 'certificates' folder provided with this authentication module \r
+download - the 'certificates' folder is a temporary location for the \r
+certificate while you get the module up and running. You will need to supply \r
+the full path to the 'certificates' folder as either a 'key_dir' argument \r
+to the 'Ucam_Webauth' constructor or by modifying the 'Ucam_Webauth' \r
+variable 'DEFAULT_KEY_DIR' directly. \r
+\r
+Once you have everything working, move the 'certificates' folder \r
+to a new location on your webserver that is not web- or publicly-accessible \r
+and modify the 'key_dir' string accordingly.\r
\r
+- NOTE: you may have to change the name of the key file from 'pubkey2.crt' \r
+to '2.crt'. \r
+\r
+If you're using the Raven test server \r
+(http://raven.cam.ac.uk/project/test-demo/) for testing purposes, make sure \r
+you install the test server keys instead, but ensure you remove these keys \r
+before using the authentication module in a production environment, as \r
+recommended by the demo Raven server:\r
+>> It is vital to keep these demo keys seperate from keys \r
+>> used with a production service - failure to do so could \r
+>> allow an attacker to successfully replay a response \r
+>> from the demonstration server, which anyone can easily \r
+>> obtain, against a production service. \r
+\r
+\r
+Getting started\r
+---------------\r
+\r
+The 'Ucam_Webauth' VBScript class must be used within an ASP server-side page \r
+as it interacts directly with a user's browser session. To use the \r
+'Ucam_Webauth' VBScript class:\r
+\r
+- 1. Ensure the 'Ucam_Webauth.vbs' class file and the folder 'js' are in the \r
+same directory as your ASP script. The folders 'certificates' and 'logs' may \r
+also be located here temporarily. \r
+- 2. Include the 'Ucam_Webauth.vbs' class file in the 'head' of your ASP file:\r
+\r
+```\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\r
+<!--#include file="Ucam_Webauth.vbs"-->\r
+</head>\r
+\r
+```\r
+\r
+- 3. Set up the initial arguments for the 'Ucam_Webauth' class:\r
+\r
+```\r
+Set args = CreateObject("Scripting.Dictionary")\r
+args.Add "hostname", "localhost"\r
+args.Add "auth_service", "https://demo.raven.cam.ac.uk/auth/authenticate.html"\r
+args.Add "key_dir", "C:/Ucam_Webauth/certificates"\r
+```\r
+\r
+'args' is an associative array of *text* strings so parameter values must \r
+be converted into strings, ie. numbers and booleans must be supplied within \r
+quotes as in "23", "TRUE", "FALSE".\r
+A full list of allowed parameters is provided at the end of this README. \r
+\r
+- 4. Create an instance of the 'Ucam_Webauth' class from within your ASP \r
+server page and initialize with setup variables:\r
+\r
+```\r
+Set oUcam_Webauth = New Ucam_Webauth\r
+Call oUcam_Webauth(args)       \r
+```\r
+   \r
+- 5. Call 'Authenticate()' on the Ucam_Webauth object and act according to \r
+the value returned:\r
+\r
+```\r
+       Select Case oUcam_Webauth.Authenticate()\r
+               \r
+               Case oUcam_Webauth.AUTHENTICATE_INCOMPLETE\r
+\r
+                       ...                     \r
+                       Exit Sub\r
+\r
+               Case oUcam_Webauth.AUTHENTICATE_COMPLETE_AUTHENTICATED          \r
+\r
+                       ...\r
+                                       \r
+               Case oUcam_Webauth.AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED              \r
+\r
+                       ...\r
+\r
+               Case oUcam_Webauth.AUTHENTICATE_COMPLETE_ERROR          \r
+\r
+                       ...\r
+                                       \r
+       End Select\r
+```\r
+\r
+The four possible return values of 'Authenticate()' are:\r
+\r
+- AUTHENTICATE_INCOMPLETE : The authentication process has yet to complete. \r
+The user may have been redirected to the Raven server and has yet to enter \r
+their login details.\r
+- AUTHENTICATE_COMPLETE_AUTHENTICATED : The authentication process completed \r
+and the user has been successfully authenticated.\r
+- AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED : The authentication process \r
+completed and the user was not successfully authenticated. \r
+The user may have clicked 'Cancel' at the Raven server.\r
+- AUTHENTICATE_COMPLETE_ERROR : There was an error during the authentication \r
+process forcing the authentication cycle to terminate early.\r
+\r
+As the 'Authenticate()' function may need to send HTTP headers, it must be \r
+called before any output (e.g. HTML, HTTP headers) is sent to the browser.\r
+\r
+The 'Default.asp' file provided is a sample of a simple server page using \r
+the 'Ucam_Webauth' VBScript class.\r
+\r
+Overview of Raven process\r
+-------------------------\r
+A diagram of the Raven authentication process is located within the 'docs' \r
+folder as [I - Overview of Raven Authentication Process.pdf]. \r
+\r
+The authentication cycle consists of the following key stages:\r
+\r
+#### User first tries to access authenticated page\r
+User tries to load an authenticated page on a particular website. \r
+The 'Ucam_Webauth' class is loaded and the 'Authenticate()' function is called. \r
+If no authentication cookie is found to indicate the user is authenticated, \r
+the user's browser is redirected to a separate Raven server using a special \r
+'Authentication Request'. The authentication request consists of a series of \r
+authentication parameters encoded into the URL redirect as name/value pairs.\r
+\r
+#### User is prompted for login information\r
+The Raven server interprets the authentication request sent by the main \r
+website and prompts the user for their username and password. The user may \r
+then be successfully authenticated or may decide to click 'Cancel'. They are \r
+redirected back to the main website with a series of 'Authentication Response' \r
+parameters encoded into a 'WLS-Response' GET variable.\r
+\r
+#### User redirected back to main webserver\r
+The user's original page is loaded again and 'Authenticate()' is called a \r
+second time. 'Ucam_Webauth' processes the new 'WLS-Response' GET value and, \r
+if it's valid, sets an authentication cookie on the user's browser. The \r
+user's original page is then loaded again. \r
+\r
+#### User redirected back to main webserver again \r
+With an authentication cookie now set, 'Authenticate()' checks the status \r
+code contained in the value of the authentication cookie and returns either \r
+'AUTHENTICATE_COMPLETE_AUTHENTICATED' or \r
+'AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED'. If \r
+'AUTHENTICATE_COMPLETE_AUTHENTICATED' is returned , the original page can \r
+go on to display authenticated content to the user.\r
+\r
+Specifics of module\r
+-------------------\r
+The 'Authenticate()' function is the overarching authentication function of \r
+'Ucam_Webauth'. It performs some basic sanity checks using 'CheckSetup()' \r
+then uses 'GetCurrentState()' to establish the current state of the \r
+authentication process before branching accordingly. The possible return \r
+values of 'GetCurrentState()' are:\r
+\r
+#### STATE_NEW_AUTHENTICATION\r
+A completely fresh authentication. 'SendAuthenticationRequest()' [*1*] is \r
+then triggered which performs some basic data checks, sets the authentication \r
+cookie to 'AUTHENTICATIONCOOKIE_REDIRECT_WLS' (to record where we are in the \r
+authentication process) and redirects the user's browser to the Raven \r
+authentication server with a series of authentication parameters \r
+encoded as name/value pairs.\r
+\r
+#### STATE_WLS_RESPONSE_RECEIVED\r
+The Raven authentication server has processed the user and has returned the \r
+user's browser back to the original website with a series of authentication \r
+response parameters encoded into the 'WLS-Response' GET variable. \r
+'ProcessAuthenticationResponse()' [*2*] is then called which checks the \r
+validity of the 'WLS-Response' value, sets an authentication cookie and \r
+redirects the user back to the original page.\r
+\r
+#### STATE_WAA_AUTHENTICATIONCOOKIE_SET\r
+A valid authentication cookie has been set \r
+(<> AUTHENTICATIONCOOKIE_REDIRECT_WLS). \r
+'ProcessAuthenticationCookie()' [*3*] is then called which checks the \r
+validity of the cookie. If the cookie has expired, \r
+'SendAuthenticationRequest()' is called again in case the user needs to \r
+reauthenticate themselves. If the cookie is still valid, an \r
+'AUTHENTICATE_COMPLETE_XXX' value is returned, indicating that the \r
+authentication cycle has completed successfully. \r
+NOTE: this may be true if the user has cancelled the authentication process, \r
+in which case 'AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED' will be returned.\r
+\r
+#### STATE_ERROR\r
+An error occurred, breaking the authentication cycle and returning \r
+AUTHENTICATE_COMPLETE_ERROR to 'Authenticate()'. This return value will \r
+also be generated by the other functions above if they generate an error.\r
+\r
+## Accompanying diagrams\r
+Detailed diagrams of the Raven process flow for (i) a successful \r
+authentication (ii) a cancelled authentication, are located in the 'docs' \r
+folder as [II - Ucam_Webauth - Flowchart for Valid Authentication.pdf] and \r
+[III - Ucam_Webauth - Flowchart for Cancelled Authentication.pdf], respectively. \r
+\r
+The numbers on these diagrams correspond to the three key secondary functions \r
+described above:\r
+- *1*. SendAuthenticationRequest()\r
+- *2*. ProcessAuthenticationResponse()\r
+- *3*. ProcessAuthenticationCookie()\r
+\r
+Other important functions include:\r
+\r
+### ResetState()\r
+Attempts to reset state as if a new user has just loaded a fresh browser \r
+window. This is typically used when a user has experienced an error and we \r
+want to give them a fresh opportunity to try again. \r
+\r
+### check_signature(...)\r
+Checks the signature provided by the Raven server, when it signed the \r
+'WLS-Response' variable, is a valid signature for the data. This ensures the \r
+data has not been tampered with.\r
+\r
+### hmac_sha1(...)\r
+Creates a hash value for signing the local authentication cookie.\r
+\r
+### wls_encode/decode(...)\r
+Encoding/decoding functions to allow base64 signatures to be sent within URLs.\r
+\r
+Possible arguments to 'Ucam_Webauth'\r
+------------------------------------\r
+(Based on documentation for PHP Raven authentication module)\r
+\r
+- log_file : \r
+The location for a log file that will record progress and track possible \r
+errors. The folder containing the file must be read/writable by the webserver.\r
+Default: log.txt\r
+\r
+- response_timeout : \r
+Responses from the central authentication server are time-stamped. \r
+This parameter sets the period of time in seconds during which these \r
+responses are considered valid. \r
+Default: 30 seconds.\r
+\r
+- key_dir : \r
+The name of the directory containing the public key certificate(s) required \r
+to validate the authentication responses sent by the server. \r
+Default: '/etc/httpd/conf/webauth_keys'.\r
+\r
+- max_session_life : \r
+The maximum period of time in seconds for which an established session will \r
+be valid. This may be overriden if the authentication reply contains a \r
+shorter 'life' parameter. Note that this does NOT define an expiry time for \r
+the session cookie. Session cookies are always set without an expiry time, \r
+causing them to expire when the browser session finishes. \r
+Default: 7200 (2 hours).\r
+\r
+- timeout_message : \r
+A re-authentication by the authentication service will be triggered when an \r
+established session expires. This option sets a text string which is sent to \r
+the authentication server to explain to the user why they are being asked to \r
+authenticate again. HTML markup is suppressed as for the description \r
+parameter described below. \r
+Default: 'your login to the site has expired'.\r
+\r
+- hostname (required) :\r
+The fully-qualified TCP/IP hostname that should be used in request URLs \r
+referencing the Ucam_Webauth-enabled application. This *must* be set, as it \r
+is needed for multiple reasons - primarily security but also to avoid varying \r
+hostnames in URLs leading to failed or inconsistent authentication. \r
+No default.\r
+\r
+- cookie_key (required): \r
+A random key used to protect session cookies from tampering. Any reasonably \r
+unpredictable string (for example the MD5 checksum of a rapidly changing \r
+logfile) will be satisfactory. This key must be the same for all uses of the \r
+web authentication system that will receive the same session cookies (see the \r
+cookie_name, cookie_path and cookie_domain parameters below). \r
+No default.\r
+\r
+- cookie_name :\r
+The name used for the session cookie. \r
+When used for access to resources over HTTPS the string '-S' is appended to \r
+this name. \r
+Default: 'Ucam-Webauth-Session'.\r
+\r
+- cookie_path :\r
+The 'Path' attribute for the session cookie. The default is the directory \r
+component of the path to the script currently being executed. This should \r
+result in the cookie being returned for future requests for this script and \r
+for the other resources in the same 'directory'; see the important \r
+information about the cookie_key parameter above. \r
+Default: '/'.\r
+\r
+- cookie_domain :\r
+The 'Domain' attribute for the session cookie. By default the 'Domain' \r
+attribute is omitted when setting the cookie. This should result in the \r
+cookie being returned only to the server running the script. Be aware that \r
+some people may treat with suspicion cookies with domain attributes that are \r
+wider than the host setting the cookie. \r
+No default.\r
+\r
+- auth_service : The full URL for the web login service to be used. \r
+Default: 'https://raven.cam.ac.uk/auth/authenticate.html' \r
+\r
+#### Authentication request properties\r
+The following setup parameters prefixed with 'authrequest_' relate to \r
+properties that will be sent to the authentication server as part of an \r
+authentication request:\r
+\r
+- authrequest_desc : A text description of the resource that is requesting \r
+authentication. This may be displayed to the user by the authentication \r
+service. It is restricted to printable ASCII characters (0x20 - 0x7e) though \r
+it may contain HTML entities representing other characters. The characters \r
+'<' and '>' will be converted into HTML entities before being sent to the \r
+browser and so this text cannot usefully contain HTML markup.\r
+No default.\r
+\r
+- authrequest_params : Data that will be returned unaltered to the WAA in \r
+any 'authentication response message' issued as a result of this request. \r
+This could be used to carry the identity of the resource originally requested \r
+or other WAA state, or to associate authentication requests with their \r
+eventual replies. When returned, this data will be protected by the digital \r
+signature applied to the authentication response message but nothing else is \r
+done to ensure the integrity or confidentiality of this data - the WAA MUST \r
+take responsibility for this if necessary.\r
+No default.\r
+\r
+- authrequest_skew : Interpretation of response_timeout is difficult if the \r
+clocks on the server running the PHP agent and on the authentication server \r
+are out of step. Both servers should use NTP to synchronize their clocks, \r
+but if they don't then this parameter should be set to an estimate of the \r
+maximum expected difference between them (in seconds). \r
+Default: 0.\r
+\r
+- authrequest_fail :\r
+If TRUE, sets the fail parameter in any authentication request sent to the \r
+authentication server to 'yes'. This has the effect of requiring the \r
+authentication server itself to report any errors that it encounters, rather \r
+than returning an error indication. Note however that even with this parameter \r
+set errors may be detected by this module that will result in authentication \r
+failing here. \r
+Default: FALSE.\r
+\r
+- authrequest_iact :\r
+If TRUE, then the 'iact' parameter provided to the authentication server is \r
+set to 'yes'. If FALSE, then the 'iact' parameter is set to 'no'. If no value \r
+is provided for 'authrequest_iact', the 'iact' parameter is left blank. \r
+The value 'yes' for 'iact' requires that a re-authentication exchange takes \r
+place with the user. This could be used prior to a sensitive transaction in \r
+an attempt to ensure that a previously authenticated user is still present \r
+at the browser. The value 'no' requires that the authentication request will \r
+only succeed if the user's identity can be returned without interacting with \r
+the user. This could be used as an optimisation to take advantage of any \r
+existing authentication but without actively soliciting one. If omitted or \r
+empty, then a previously established identity may be returned if the WLS \r
+supports doing so, and if not then the user will be prompted as necessary.\r
+Default: omitted.
\ No newline at end of file
diff --git a/Test.asp b/Test.asp
new file mode 100644 (file)
index 0000000..cfb68e9
--- /dev/null
+++ b/Test.asp
@@ -0,0 +1,219 @@
+<!DOCTYPE html>\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\r
+<!--#include file="Ucam_Webauth.vbs"-->\r
+</head>\r
+<%\r
+\r
+Sub Main\r
+\r
+       ' Examine the 'UcamWebAuthTestingClient' cookie.\r
+       ' This cookie is used to store the index number of \r
+       ' the LAST test and the outcome of that test. This \r
+       ' cookie is only updated when an authentication \r
+       ' cycle is complete (which could be due to an \r
+       ' error) so we use the cookie to determine the \r
+       ' index of the CURRENT test. \r
+       ' \r
+       ' NOTE: This cookie is completely separate \r
+       ' from the 'Authentication cookie' and is \r
+       ' purely used for testing.\r
+       '\r
+       ' The actual value of the cookie consists of \r
+       ' three elements delimited by '!': \r
+       '\r
+       ' 'index!status_code!status_message'\r
+                 \r
+       testing_client_cookie_name = "UcamWebAuthTestingClient"\r
+       testing_client_cookie_index = 1\r
+       \r
+       If (Request.Cookies(testing_client_cookie_name) <> "") Then\r
+               testing_client_cookie_value = Request.Cookies(testing_client_cookie_name)\r
+               testing_client_cookie_array = Split(testing_client_cookie_value, "!")\r
+               testing_client_cookie_index = CInt(testing_client_cookie_array(0))\r
+               testing_client_cookie_index = testing_client_cookie_index + 1\r
+       End If\r
+               \r
+       ' Create 'Scripting.Dictionary' object \r
+       ' to hold the arguments we will supply \r
+       ' to the 'Ucam_Webauth' object.\r
+       \r
+       Set args = CreateObject("Scripting.Dictionary")\r
+       \r
+       ' Add the different arguments to the 'args' \r
+       ' associative array as name/value pairs.\r
+       ' Both name and value must be strings \r
+       ' so integers must be added as "x", eg. "60".\r
+\r
+       ' 'auth_service' is the WLS authentication server.\r
+       ' The following line gives the the demo Raven testing server:                   \r
+\r
+       ' args.Add "auth_service", "https://demo.raven.cam.ac.uk/auth/authenticate.html"\r
+\r
+       ' We're testing with our Dummy Raven WLS server so use that:\r
+       args.Add "auth_service", "http://www2.careers.cam.ac.uk:11812"        \r
+\r
+       ' 'hostname' must be a domain name and perhaps a \r
+       ' port but nothing more.\r
+       \r
+       args.Add "hostname", "localhost:81"\r
+\r
+       ' 'log_file' is the location of the logfile \r
+       ' which must be read/writable by the webserver.\r
+       \r
+       args.Add "log_file", "C:/wamp/www/raven/vbscriptlog.txt"\r
+\r
+       ' 'key_dir' is the directory holding the \r
+       ' public key certificate.\r
+       \r
+       args.Add "key_dir", "C:/wamp/www/raven"\r
+\r
+       ' 'cookie_key' is the key used to generate \r
+       ' hash values of the authentication cookie. \r
+       ' Ideally it should be changed on a regular basis \r
+       ' but not during sessions.\r
+       \r
+       args.Add "cookie_key", "Random string"\r
+\r
+       ' We add the current iteration of testing in \r
+       ' 'testing_client_cookie_index' as a parameter \r
+       ' to Ucam_Webauth which should then be \r
+       ' included as a parameter in the authentication \r
+       ' request to the WLS and the subsequent \r
+       ' authentication response back from the WLS.\r
+       \r
+       args.Add "authrequest_params", CStr(testing_client_cookie_index)\r
+\r
+\r
+       ' Create new instance of 'Ucam_Webauth' \r
+       ' and supply arguments.\r
+       ' We do not need to include 'Request' and 'Response' \r
+       ' variables (as in C# version), in order to get/set \r
+       ' cookies and server variables and perform redirects \r
+       ' as these variables are globally accessible to ASP class.\r
+                              \r
+       Set oUcam_Webauth = New Ucam_Webauth\r
+       Call oUcam_Webauth(args)       \r
+       \r
+       ' For the purposes of testing, we provide \r
+       ' a 'Logout' link that removes the local \r
+       ' authentication cookie and then displays \r
+       ' a link to easily logout the Raven WLS.\r
+       ' So we check to see if this 'Action=Logout' \r
+       ' link has been called and logout/display \r
+       ' link accordingly.\r
+                       \r
+       If (Request.ServerVariables("QUERY_STRING") = "Action=Logout") Then\r
+               oUcam_Webauth.ResetState()\r
+               Response.Write("Logged out of Raven (local)<br/>")\r
+               Response.Write("<a href='https://raven.cam.ac.uk/auth/logout.html'>Logout Raven (remote)</a><br/>" & _\r
+                             "<a href='Default.asp'>Access Raven authenticated page</a>")            \r
+               Exit Sub\r
+       End If          \r
+\r
+       ' When you first access this page \r
+       ' the 'Authenticate' function will be called. \r
+       ' This will typically be called three times \r
+       ' in total to successfully authenticate the \r
+       ' user. In the first two iterations of \r
+       ' 'Authenticate', it will return \r
+       ' 'AUTHENTICATE_INCOMPLETE' while it \r
+       ' redirects the user's browser first to \r
+       ' the Raven WLS and then back to this page.\r
+       ' On the third iteration of 'Authenticate', it \r
+       ' will return 'AUTHENTICATE_COMPLETE_AUTHENTICATED' \r
+       ' or 'AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED' \r
+       ' if the authentication process has fully \r
+       ' completed without error.\r
+               \r
+       Select Case oUcam_Webauth.Authenticate()\r
+               \r
+               Case oUcam_Webauth.AUTHENTICATE_INCOMPLETE\r
+\r
+                       ' 'Authenticate' still redirecting pages \r
+                       ' so don't do anything else.\r
+                       \r
+                       Exit Sub\r
+\r
+               Case oUcam_Webauth.AUTHENTICATE_COMPLETE_AUTHENTICATED          \r
+\r
+                       ' Success so display the 'principal', ie. the user id.\r
+                       \r
+                       Response.Write("SUCCESS. You are " & oUcam_Webauth.principal() & "<br/>")\r
+                       \r
+                       ' Also display the 'ptags' parameter indicating \r
+                       ' whether the user is 'current' or not.\r
+                       \r
+                       Response.Write("Ptags = " & oUcam_Webauth.ptags() & "<br/>")\r
+                       \r
+                       ' Display any 'GET variables' to check they \r
+                       ' have carried through from the original \r
+                       ' page request.\r
+                       \r
+                       For Each item In Request.QueryString() \r
+                       Response.Write item & "=" & Request.QueryString()(item) & "<br/>"  \r
+                       Next                    \r
+                       \r
+                       ' Display a 'Logout' link to make it easy to \r
+                       ' test authentication repeatedly.\r
+                       \r
+                       Response.Write("<a href='Default.asp?Action=Logout'>Logout Raven (local)</a>")                \r
+               \r
+               Case Else\r
+                       \r
+                       ' Either there was an error or a failed \r
+                       ' authentication so print out the result either way.\r
+\r
+                       Response.Write("FAIL - " & oUcam_Webauth.status() & ": " & oUcam_Webauth.msg())\r
+\r
+                       ' Also log the error for debugging purposes.\r
+                                                               \r
+                       oUcam_Webauth.write_log("FAIL - " & oUcam_Webauth.status() & ": " & oUcam_Webauth.msg())\r
+                                       \r
+       End Select\r
+\r
+       ' We use a 'UcamWebAuthTestingClient' cookie \r
+       ' to store the return 'status' of the most recent  \r
+       ' authentication attempt. The Dummy WLS server \r
+       ' looks at the value of this cookie, compares \r
+       ' it with its most recent attempt to generate a \r
+       ' particular status and logs the results. \r
+       ' Ideally the status/error the Dummy WLS server \r
+       ' tried to generate should match the status/error \r
+       ' recorded here.\r
+       '\r
+       ' NOTE: The Dummy WLS server only performs the \r
+       ' comparison of 'actual' and 'expected' when \r
+       ' it receives a subsequent authentication request.\r
+       ' ie. when testing is terminated, the final \r
+       ' authentication attempt comparison may be lost.\r
+               \r
+       ' Store number of testing iteration, return status and status msg.\r
+       ' To make it a session cookie, we don't specify 'Expires'.\r
+               \r
+       Response.Cookies("UcamWebAuthTestingClient") = CStr(testing_client_cookie_index) & "!" & oUcam_Webauth.status() & "!" & oUcam_Webauth.msg()     \r
+               \r
+       ' We intend to perform another authentication attempt\r
+       ' so reset the state of Ucam_Webauth, ie. remove \r
+       ' the authentication cookie.\r
+       \r
+       oUcam_Webauth.ResetState()                    \r
+\r
+               \r
+End Sub\r
+\r
+Call Main\r
+\r
+%>\r
+\r
+<script language="javascript">\r
+\r
+    // Set a brief timeout before reloading this page again \r
+    // and triggering off another authentication attempt cycle.\r
+\r
+    window.setTimeout(function () { window.location.href = "http://localhost:81/Test.asp?Test1=Value1&Test2=Value2&Test3=Value3+Value4"; }, 10);\r
+\r
+</script>\r
+\r
+</html>\r
diff --git a/Ucam_Webauth.vbs b/Ucam_Webauth.vbs
new file mode 100644 (file)
index 0000000..d80fbe8
--- /dev/null
@@ -0,0 +1,1930 @@
+<%\r
+\r
+' This VBScript class implements a Raven v3 agent for the University of Cambridge\r
+' Web Authentication System.\r
+'\r
+' See http://raven.cam.ac.uk/project/ for more details\r
+'\r
+' Loosely based on the PHP module for Raven\r
+' https://wiki.cam.ac.uk/raven/PHP_library\r
+'\r
+' Copyright (c) 2004, 2005, 2008, 2014 University of Cambridge\r
+'\r
+' This module is free software; you can redistribute it and/or modify\r
+' it under the terms of the GNU Lesser General Public License as\r
+' published by the Free Software Foundation; either version 2.1 of the\r
+' License, or (at your option) any later version.\r
+'\r
+' The module is distributed in the hope that it will be useful, but\r
+' WITHOUT ANY WARRANTY; without even the implied warranty of\r
+' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+' Lesser General Public License for more details.\r
+'\r
+' You should have received a copy of the GNU Lesser General Public\r
+' License along with this toolkit; if not, write to the Free Software\r
+' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\r
+' USA\r
+'\r
+' $Id: ucam_webauth.vbs, v1.0 2014/04/05 08:13:00 sh801 \r
+'\r
+' Version 1.0\r
+\r
+  \r
+Class Ucam_Webauth\r
+\r
+       ' ****************************************\r
+       ' \r
+       ' ----------------------------------------\r
+       ' ---------- class Ucam_Webauth ----------\r
+       ' ----------------------------------------\r
+       ' \r
+       ' ****************************************\r
+       '\r
+       ' Methods are listed in order of importance\r
+       ' with MAIN functions at top and lower- \r
+       ' level functions towards the end.\r
+       '\r
+       ' ****************************************\r
+\r
+\r
+    ' 'status_codes' is associative array of status codes of the form {"CODE" => "Description", ..}\r
+\r
+    Private status_codes \r
+\r
+    ' ****************************************\r
+    ' Default values for key parameters\r
+    ' ****************************************\r
+\r
+    Private PROTOCOL_VERSION\r
+    Private AUTHENTICATION_RESPONSE_VERSION\r
+    Private DEFAULT_AUTH_SERVICE\r
+    Private DEFAULT_KEY_DIR\r
+    Private DEFAULT_COOKIE_NAME\r
+    Private DEFAULT_TIMEOUT_MESSAGE\r
+    Private DEFAULT_HOSTNAME\r
+    Private WLS_LOGOUT\r
+    Private DEFAULT_LOG_FILE\r
+       Private AUTHENTICATIONCOOKIE_REDIRECT_WLS\r
+\r
+    ' ****************************************\r
+    ' Constants for tracking state\r
+    ' ****************************************\r
+    '\r
+    ' The 'Authenticate' function is called multiple \r
+    ' times for a successful authentication so we \r
+    ' need to track where we are in the authentication \r
+    ' process with some constants. \r
+\r
+    Private STATE_ERROR\r
+    Private STATE_NEW_AUTHENTICATION\r
+    Private STATE_WLS_RESPONSE_RECEIVED\r
+    private STATE_WAA_AUTHENTICATIONCOOKIE_SET\r
+\r
+    ' ****************************************\r
+    ' Constants for returning state\r
+    ' ****************************************\r
+    '\r
+    ' The 'Authenticate' function needs to \r
+    ' return relevant information as to where \r
+    ' it currently is in the authentication process.\r
+\r
+    Public AUTHENTICATE_INCOMPLETE\r
+    Public AUTHENTICATE_COMPLETE_ERROR\r
+    Public AUTHENTICATE_COMPLETE_AUTHENTICATED\r
+    Public AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED\r
+\r
+    ' ****************************************\r
+    ' Index numbers for the 'Authentication response' fields\r
+    ' ****************************************\r
+    '\r
+    ' See Raven specification documentation for descriptions of each parameter\r
+\r
+    Private AUTHENTICATION_RESPONSE_VER\r
+    Private AUTHENTICATION_RESPONSE_STATUS\r
+    Private AUTHENTICATION_RESPONSE_MSG\r
+    Private AUTHENTICATION_RESPONSE_ISSUE\r
+    Private AUTHENTICATION_RESPONSE_EXPIRE\r
+    Private AUTHENTICATION_RESPONSE_ID\r
+    Private AUTHENTICATION_RESPONSE_PRINCIPAL\r
+    Private AUTHENTICATION_RESPONSE_PTAGS\r
+    Private AUTHENTICATION_RESPONSE_AUTH\r
+    Private AUTHENTICATION_RESPONSE_SSO\r
+    Private AUTHENTICATION_RESPONSE_PARAMS\r
+    Private AUTHENTICATION_RESPONSE_SIG\r
+    Private AUTHENTICATION_RESPONSE_SIZE ' Size of required array\r
+\r
+    Private WLS_RESPONSE_VER \r
+    Private WLS_RESPONSE_STATUS \r
+    Private WLS_RESPONSE_MSG \r
+    Private WLS_RESPONSE_ISSUE \r
+    Private WLS_RESPONSE_ID \r
+    Private WLS_RESPONSE_URL \r
+    Private WLS_RESPONSE_PRINCIPAL \r
+    Private WLS_RESPONSE_PTAGS\r
+    Private WLS_RESPONSE_AUTH\r
+    Private WLS_RESPONSE_SSO\r
+    Private WLS_RESPONSE_LIFE\r
+    Private WLS_RESPONSE_PARAMS\r
+    Private WLS_RESPONSE_KID\r
+    Private WLS_RESPONSE_SIG\r
+    Private WLS_RESPONSE_SIZE ' Size of required array\r
+\r
+    ' ****************************************\r
+    ' General private member variables\r
+    ' ****************************************\r
+\r
+    Private m_response_timeout\r
+    Private m_authrequest_skew\r
+    Private m_max_session_life\r
+    Private m_redirected\r
+    Private m_use_authrequest_iact\r
+    Private m_authrequest_iact\r
+    Private m_authrequest_fail\r
+    Private m_authrequest_desc\r
+    Private m_authrequest_aauth\r
+    Private m_authrequest_params\r
+    Private m_authentication_response_string\r
+    Private m_authentication_cookie\r
+    Private m_auth_service\r
+    Private m_hostname\r
+    Private m_key_dir\r
+    Private m_timeout_message\r
+    Private m_cookie_key\r
+    Private m_cookie_path\r
+    Private m_cookie_name\r
+    Private m_cookie_domain\r
+    Private m_log_file\r
+    Private m_authentication_response\r
+\r
+       ' ****************************************\r
+       ' \r
+       ' ----------------------------------------\r
+       ' ----------- MAIN FUNCTIONS ------------- \r
+       ' ----------------------------------------\r
+       '\r
+       ' ****************************************        \r
+\r
+    Private Sub Class_Initialize\r
+    \r
+               ' Initialize all constants as it's not possible \r
+               ' to declare initial values in class definition, above.\r
+               \r
+               PROTOCOL_VERSION = "3"\r
+               AUTHENTICATION_RESPONSE_VERSION = "3"\r
+               DEFAULT_AUTH_SERVICE = "https://raven.cam.ac.uk/auth/authenticate.html"\r
+               DEFAULT_KEY_DIR = "/etc/httpd/conf/webauth_keys"\r
+               DEFAULT_COOKIE_NAME = "Ucam-WebAuth-Session"\r
+               DEFAULT_TIMEOUT_MESSAGE = "your logon to the site has expired"\r
+               DEFAULT_HOSTNAME = "" ' must be supplied explicitly\r
+               WLS_LOGOUT = "Not-authenticated"\r
+               DEFAULT_LOG_FILE = "log.txt"\r
+               AUTHENTICATIONCOOKIE_REDIRECT_WLS = "REDIRECT_WLS"\r
+\r
+               STATE_ERROR = -1\r
+               STATE_NEW_AUTHENTICATION = 0\r
+               STATE_WLS_RESPONSE_RECEIVED = 1\r
+               STATE_WAA_AUTHENTICATIONCOOKIE_SET = 2\r
+\r
+       AUTHENTICATE_INCOMPLETE = 0\r
+               AUTHENTICATE_COMPLETE_ERROR = -1\r
+               AUTHENTICATE_COMPLETE_AUTHENTICATED = 1\r
+               AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED = 2\r
+\r
+               AUTHENTICATION_RESPONSE_VER = 0\r
+               AUTHENTICATION_RESPONSE_STATUS = 1\r
+               AUTHENTICATION_RESPONSE_MSG = 2\r
+               AUTHENTICATION_RESPONSE_ISSUE = 3\r
+               AUTHENTICATION_RESPONSE_EXPIRE = 4\r
+               AUTHENTICATION_RESPONSE_ID = 5\r
+               AUTHENTICATION_RESPONSE_PRINCIPAL = 6\r
+               AUTHENTICATION_RESPONSE_PTAGS = 7\r
+               AUTHENTICATION_RESPONSE_AUTH = 8\r
+               AUTHENTICATION_RESPONSE_SSO = 9\r
+               AUTHENTICATION_RESPONSE_PARAMS = 10\r
+               AUTHENTICATION_RESPONSE_SIG = 11\r
+               AUTHENTICATION_RESPONSE_SIZE = 12 ' Size of required array\r
+\r
+               WLS_RESPONSE_VER = 0\r
+               WLS_RESPONSE_STATUS = 1\r
+               WLS_RESPONSE_MSG = 2\r
+               WLS_RESPONSE_ISSUE = 3\r
+               WLS_RESPONSE_ID = 4\r
+               WLS_RESPONSE_URL = 5\r
+               WLS_RESPONSE_PRINCIPAL = 6\r
+               WLS_RESPONSE_PTAGS = 7\r
+               WLS_RESPONSE_AUTH = 8\r
+               WLS_RESPONSE_SSO = 9\r
+               WLS_RESPONSE_LIFE = 10\r
+               WLS_RESPONSE_PARAMS = 11\r
+               WLS_RESPONSE_KID = 12\r
+               WLS_RESPONSE_SIG = 13\r
+       WLS_RESPONSE_SIZE = 14 ' Size of required array\r
+               \r
+        ' Set up 'status_codes' associative array\r
+        ' See Raven specification documentation for descriptions of each parameter\r
+\r
+               Set status_codes = CreateObject("Scripting.Dictionary")\r
+\r
+        status_codes.Add "200", "Successful authentication"\r
+        status_codes.Add "410", "The user cancelled the authentication request"\r
+        status_codes.Add "510", "No mutually acceptable authentication types available"\r
+        status_codes.Add "520", "Unsupported protocol version"\r
+        status_codes.Add "530", "General request parameter error"\r
+        status_codes.Add "540", "Interaction would be required"\r
+        status_codes.Add "560", "WAA not authorised" \r
+        status_codes.Add "570", "Authentication declined" \r
+\r
+               ' Set up array for holding authentication response parameters\r
+\r
+        ReDim m_authentication_response(AUTHENTICATION_RESPONSE_SIZE)\r
+\r
+    End Sub\r
+    \r
+    \r
+    Public Default Function Construct(ByVal args)\r
+\r
+        ' ****************************************\r
+        ' Constructor for Ucam_Webauth\r
+        ' \r
+        ' We split the constructor that takes \r
+        ' parameters from the initializer as \r
+        ' VBScript doesn't allow initializers \r
+        ' to take parameters.\r
+        ' \r
+        ' Arguments\r
+        ' ================\r
+        ' args: An associative array of arguments as name,value pairs, eg. {"auth_service" => "http://..", "hostname" => "www...", "log_file" => }.\r
+        '\r
+        ' To supply arguments:\r
+               ' Set args = CreateObject("Scripting.Dictionary")            \r
+        ' args.Add auth_service", "https://..."\r
+        ' args.Add hostname", "www..."\r
+        ' args.Add log_file", "C:/logfile.txt"\r
+        ' args.Add key_dir", "C:/raven"\r
+        ' args.Add cookie_key", "Random string"\r
+        '\r
+        ' ****************************************\r
+\r
+\r
+        ' Set up default values\r
+\r
+        log_file = DEFAULT_LOG_FILE\r
+        response_timeout = 30\r
+        key_dir = DEFAULT_KEY_DIR\r
+        max_session_life = 2 * 60 * 60\r
+        timeout_message = DEFAULT_TIMEOUT_MESSAGE\r
+        hostname = DEFAULT_HOSTNAME\r
+        cookie_name = DEFAULT_COOKIE_NAME\r
+        cookie_path = "" ' *** SHOULD BE PATH RELATIVE PATH TO SCRIPT BY DEFAULT ***\r
+        cookie_domain = ""\r
+        auth_service = DEFAULT_AUTH_SERVICE\r
+        authrequest_skew = 5\r
+        authrequest_fail = False\r
+               authrequest_iact = False\r
+        use_authrequest_iact = False\r
+\r
+        ' If specific arguments are provided, then override default values\r
+\r
+        If (args.Exists("log_file")) Then log_file = args("log_file")\r
+        If (args.Exists("response_timeout")) Then response_timeout = Int(args("response_timeout"))\r
+        If (args.Exists("key_dir")) Then key_dir = args("key_dir")\r
+        If (args.Exists("max_session_life")) Then max_session_life = Int(args("max_session_life"))\r
+        If (args.Exists("timeout_message")) Then timeout_message = args("timeout_message")\r
+        If (args.Exists("hostname")) Then hostname = args("hostname")\r
+        If (args.Exists("cookie_key")) Then cookie_key = args("cookie_key")\r
+        If (args.Exists("cookie_name")) Then cookie_name = args("cookie_name")\r
+        If (args.Exists("cookie_path")) Then cookie_path = args("cookie_path")\r
+        If (args.Exists("cookie_domain")) Then cookie_domain = args("cookie_domain")\r
+        If (args.Exists("auth_service")) Then auth_service = args("auth_service")\r
+        If (args.Exists("authrequest_desc")) Then authrequest_desc = args("authrequest_desc")\r
+        If (args.Exists("authrequest_params")) Then authrequest_params = args("authrequest_params")\r
+        If (args.Exists("authrequest_skew")) Then authrequest_skew = Int(args("authrequest_skew"))\r
+        If (args.Exists("authrequest_fail")) Then authrequest_fail = CBool(args("authrequest_fail"))\r
+        If (args.Exists("authrequest_iact")) Then \r
+               authrequest_iact = CBool(args("authrequest_iact"))\r
+                       use_authrequest_iact = True\r
+               End If\r
+               \r
+    End Function\r
+\r
+       ' ****************************************\r
+       ' \r
+       ' ----------------------------------------\r
+       ' -- PRIMARY FUNCTION - Authenticate() ---\r
+       ' ----------------------------------------\r
+       '\r
+       ' ****************************************        \r
+\r
+       Public Function Authenticate()\r
+\r
+               ' ****************************************\r
+               ' Authenticate()\r
+               '\r
+               ' This function is called three times in \r
+               ' order to complete the authentication process.\r
+               '\r
+               ' STEP 1: A fresh user requests an Authenticated \r
+               ' resource with the URL 'X'. 'Authenticate' \r
+               ' redirects the user's browser to a Web Login \r
+               ' Service (WLS) via 'SendAuthenticationRequest()'.\r
+               '\r
+               ' STEP 2: The WLS processes the user's user id\r
+               ' and password and then redirects the user's \r
+               ' browser back to the client's Web Application \r
+               ' Agent (WAA) with a specific set of parameters \r
+               ' in a 'WLS-Response' GET variable. \r
+               ' 'Authenticate' is called again and validates \r
+               ' these values using 'ProcessAuthenticationRequest()'. \r
+               ' If they all check out, it creates a \r
+               ' local authentication cookie on the user's \r
+               ' browser and redirects the user's browser \r
+               ' back to the original URL 'X'.\r
+               '\r
+               ' STEP 3: We return to our original page, except \r
+               ' this time we have a local authentication cookie \r
+               ' set. 'Authenticate' calls \r
+               ' 'ProcessAuthenticationCookie' which checks \r
+               ' this cookie is valid and if the status code \r
+               ' of the cookie is '200', the user was successfully \r
+               ' verified at the WLS - \r
+               ' 'AUTHENTICATE_COMPLETE_AUTHENTICATED' is then \r
+               ' returned to the user.\r
+               '\r
+               ' ****************************************\r
+               \r
+               \r
+               ' First off, perform a sanity check on basic variables\r
+\r
+               If (CheckSetup() = False) Then\r
+                       ResetState()\r
+                       Authenticate = AUTHENTICATE_COMPLETE_ERROR\r
+                       Exit Function\r
+               End If\r
+               \r
+               ' Now work out where we are in the authentication process:\r
+               ' \r
+               ' STATE_NEW_AUTHENTICATION\r
+               ' A completely fresh authentication attempt.\r
+               '\r
+               ' STATE_WLS_RESPONSE_RECEIVED\r
+               ' The user has already been redirected to the WLS, which has \r
+               ' then redirected them back here. They may have successfully \r
+               ' authenticated or simply pressed cancel. As long as it's a \r
+               ' valid, signed WLS-Response - regardless of whether it's a \r
+               ' successful authentication or not - we set a new \r
+               ' authentication cookie which indicates to the code \r
+               ' that the user has been processed by the WLS.\r
+               '\r
+               ' STATE_WAA_AUTHENTICATIONCOOKIE_SET\r
+               ' We have a valid authentication cookie and just need to \r
+               ' check what the status of that is. The user may have \r
+               ' successfully authenticated, the authentication token  \r
+               ' may have expired, or the user may have skipped out \r
+               ' the authentication process. In the event of expiration, \r
+               ' 'ProcessAuthenticationCookie' redirects the user back \r
+               ' to the WLS.\r
+               '\r
+               ' STATE_ERROR\r
+               ' There was an error somewhere along the way.\r
+               ' If this happens, it's not a simple case of \r
+               ' an invalid authentication but something more serious\r
+               ' that falls outside the expected process flow.\r
+\r
+               state = GetCurrentState()\r
+\r
+               Select Case state\r
+               \r
+                       Case STATE_NEW_AUTHENTICATION\r
+                       \r
+                               Authenticate = SendAuthenticationRequest("")\r
+                               Exit Function\r
+                       \r
+                       Case STATE_WLS_RESPONSE_RECEIVED\r
+                       \r
+                               Authenticate = ProcessAuthenticationResponse()\r
+                               Exit Function\r
+                       \r
+                       Case STATE_WAA_AUTHENTICATIONCOOKIE_SET\r
+                       \r
+                               Authenticate = ProcessAuthenticationCookie()\r
+                               Exit Function\r
+                                                                       \r
+                       Case Else ' This includes the case STATE_ERROR\r
+                       \r
+                               write_log("STATE_ERROR received: " & status() & ": " & msg())                           \r
+                               ResetState()                            \r
+                               Authenticate = AUTHENTICATE_COMPLETE_ERROR\r
+                               Exit Function\r
+                       \r
+               End Select\r
+               \r
+       End Function\r
+       \r
+\r
+       ' ****************************************\r
+       ' \r
+       ' ----------------------------------------\r
+       ' ---- SECONDARY FUNCTIONS TRIGGERED -----\r
+       ' ------- FROM 'Authenticate()' ----------\r
+       ' ----------------------------------------\r
+       '\r
+       ' 1. SendAuthenticationRequest()\r
+       ' 2. ProcessAuthenticationResponse()\r
+       ' 3. ProcessAuthenticationCookie()\r
+       '\r
+       ' ****************************************        \r
+\r
+       Public Function SendAuthenticationRequest(msg)\r
+\r
+               ' ****************************************\r
+               ' 1. SendAuthenticationRequest()\r
+               '\r
+               ' Send an authentication request from the WAA to the WLS. \r
+               '\r
+               ' ****************************************\r
+               \r
+               \r
+               ' Write a long line of astericks to make log easier to read.\r
+               \r
+               write_log("****************************************************************")           \r
+               write_log("SendAuthenticationRequest: Starting...")\r
+               \r
+               ' If the hostname from the request (Host: header) does not match the\r
+               ' server's preferred name for itself (which should be what's configured\r
+               ' as hostname), cookies are likely to break "randomly" (or more\r
+               ' accurately, the cookie may not be sent by the browser since it"s for\r
+               ' a different hostname) as a result of following links that use the\r
+               ' preferred name, or server-level redirects e.g. to fix "directory"\r
+               ' URLs lacking the trailing "/". Attempt to avoid that by redirecting\r
+               ' to an equivalent URL using the configured hostname.\r
+\r
+               http_host = Request.ServerVariables("HTTP_HOST")\r
+               \r
+               If ((http_host <> "") And (LCase(hostname) <> LCase(http_host))) Then           \r
+                       write_log("SendAuthenticationRequest: Redirect to tidy up hostname mismatch")                \r
+                       Redirect(url())                \r
+                       SendAuthenticationRequest = AUTHENTICATE_INCOMPLETE                \r
+                       Exit Function\r
+               End If\r
+               \r
+               ' We set the authentication cookie to 'AUTHENTICATIONCOOKIE_REDIRECT_WLS'\r
+               ' to keep a track of state.\r
+               \r
+               write_log("SendAuthenticationRequest: Setting pre-session cookie")\r
+               \r
+               authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS\r
+               \r
+               ' Build the full 'Authentication Request' URL that will \r
+               ' redirect the user to the WLS. \r
+               ' \r
+               ' Full information about each of these parameters can be \r
+               ' found in Raven v3 documentation.\r
+               \r
+               wls_redirect = auth_service\r
+               wls_redirect = wls_redirect & "?ver=" & PROTOCOL_VERSION\r
+               wls_redirect = wls_redirect & "&url=" & Server.URLEncode(url())\r
+\r
+               If (authrequest_desc <> "") Then wls_redirect = wls_redirect & "&desc=" & Server.URLEncode(authrequest_desc)\r
+               If (authrequest_aauth <> "") Then wls_redirect = wls_redirect & "&aauth=" & Server.URLEncode(authrequest_aauth)\r
+               \r
+               If use_authrequest_iact Then\r
+                       If authrequest_iact Then\r
+                               wls_redirect = wls_redirect & "&iact=yes"\r
+                       Else\r
+                               wls_redirect = wls_redirect & "&iact=no"\r
+                       End If\r
+               End If\r
+               \r
+               If (msg <> "") Then wls_redirect = wls_redirect & "&msg=" & Server.URLEncode(msg)\r
+               if (authrequest_params <> "") Then wls_redirect = wls_redirect & "&params=" & Server.URLEncode(authrequest_params)\r
+\r
+        wls_redirect = wls_redirect & "&date=" & Server.URLEncode(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow())))\r
+\r
+        ' If (clock_skew <> 0) THen wls_redirect = wls_redirect & "&skew=" & Server.URLEncode(Convert.ToString(clock_skew)) ' 'skew' parameter deprecated in v3\r
+\r
+               If (authrequest_fail = True) Then wls_redirect = wls_redirect & "&fail=yes"\r
+\r
+               write_log("SendAuthenticationRequest: Redirecting to WLS with URL=" & wls_redirect)\r
+               \r
+               Redirect(wls_redirect)\r
+               \r
+               SendAuthenticationRequest = AUTHENTICATE_INCOMPLETE\r
+               \r
+       End Function\r
+       \r
+       \r
+       Public Function ProcessAuthenticationResponse()\r
+\r
+               ' ****************************************\r
+               ' 2. ProcessAuthenticationResponse()\r
+               '\r
+               ' Process the authentication response received from the WLS. \r
+               '\r
+               ' ****************************************\r
+\r
+               write_log("ProcessAuthenticationResponse: Starting...")\r
+               write_log("ProcessAuthenticationResponse: WLS response=" & authentication_response_string)\r
+\r
+               wls_response = Split(authentication_response_string, "!")\r
+               \r
+               set_status "200", ""\r
+               \r
+               \r
+               If (UBound(wls_response) < 1) Then\r
+               \r
+                       ' Response is too short to have been signed or have a status.\r
+               \r
+                       set_status "620", "WLS response has too few parameters"\r
+                       ResetState()\r
+                       ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR\r
+                       Exit Function\r
+\r
+               End If\r
+               \r
+               ' We only check signature if status = "200".\r
+\r
+               If (wls_response(WLS_RESPONSE_STATUS) = "200") Then\r
+\r
+                       ' Get signature and key_id off the end of the wls_response array\r
+                       ' and collapse the remaining array into a string. \r
+                       ' It's this string that will have been signed by the WLS \r
+                       ' and whose signature we then verify using the WLS\r
+                       ' public certificate on our server.\r
+       \r
+                       signature = wls_response(UBound(wls_response))\r
+                       key_id = wls_response(UBound(wls_response) - 1)\r
+                       Redim Preserve wls_response (UBound(wls_response) - 2)\r
+                       wls_response_signedstring = Join(wls_response, "!")\r
+                       \r
+                       ' write_log("Signature=" & signature)\r
+                       ' write_log("Signed string=" & wls_response_signedstring)\r
+                       \r
+                       If (check_signature(wls_response_signedstring, signature, key_id) = False) Then\r
+                       \r
+                               ' Signature is not correct for the wls_response\r
+                               \r
+                               set_status "606", "Invalid WLS wls_response signature"\r
+                               ResetState()\r
+                               ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR\r
+                               Exit Function\r
+                       \r
+                       End If\r
+                       \r
+                       write_log("ProcessAuthenticationResponse: Signature is correct.")\r
+                       \r
+               End If\r
+                               \r
+\r
+               ' Expand 'wls_response' array to maximum size just in case it's too small \r
+               ' and subsequent calls to wls_response(WLS_RESPONSE_XXX) raise 'IndexOutOfRangeException'\r
+               \r
+               Redim Preserve wls_response (WLS_RESPONSE_SIZE)\r
+               \r
+               \r
+               ' Remove the query part of our current url \r
+               ' and the url provided by the WLS and check \r
+               ' they match.\r
+               \r
+               Set rgx = New RegExp\r
+               rgx.Global = True\r
+               rgx.IgnoreCase = False\r
+               rgx.Pattern = "\?.*$"\r
+               this_url = rgx.Replace(url(), "")\r
+               \r
+               If (wls_response(WLS_RESPONSE_URL) = "") Then\r
+                       set_status "607", "Null URL in response ticket doesn't match this URL: " & this_url\r
+                       ResetState()                \r
+                       ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR\r
+                       Exit Function            \r
+               End If\r
+               \r
+               response_url = wls_response(WLS_RESPONSE_URL)                   \r
+               response_url = rgx.Replace(response_url, "")\r
+               \r
+               If (this_url <> response_url) Then\r
+                       set_status "607", "URL in response ticket doesn't match this URL: " & response_url & " != " & this_url\r
+                       ResetState()\r
+                       ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR\r
+                       Exit Function\r
+               End If\r
+           \r
+           \r
+               ' Go through the possible different response codes \r
+               ' from the WLS and set our status accordingly.\r
+                       \r
+               If (wls_response(WLS_RESPONSE_STATUS) = "410") Then\r
+               \r
+                       set_status "410", status_codes("410")\r
+               \r
+               ElseIf ((wls_response(WLS_RESPONSE_VER) <> PROTOCOL_VERSION) And (wls_response(WLS_RESPONSE_STATUS) <> "520")) Then\r
+               \r
+                       set_status "608", "Wrong protocol version in authentication service reply"\r
+               \r
+               ElseIf (wls_response(WLS_RESPONSE_STATUS) <> "200") Then\r
+               \r
+                       ' If status code != "200" then we have an error of some description.\r
+                       ' First off, check that it's an error for which we have an error code / msg.\r
+                       \r
+                       If (status_codes.Exists(wls_response(WLS_RESPONSE_STATUS)) = False) Then\r
+                               write_log("No status code for error " & wls_response(WLS_RESPONSE_STATUS))                    \r
+                               ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR\r
+                               Exit Function                \r
+                       End If\r
+                       \r
+                       ' If we have an error code / msg for the error, then set status to that code / msg.\r
+                       \r
+                       set_status wls_response(WLS_RESPONSE_STATUS), status_codes(wls_response(WLS_RESPONSE_STATUS))\r
+                       \r
+                       ' If the authentication response provided a response message then set status msg to that.\r
+                       \r
+                       If (wls_response(WLS_RESPONSE_MSG) <> "") Then m_authentication_response(AUTHENTICATION_RESPONSE_MSG) = wls_response(WLS_RESPONSE_MSG)\r
+\r
+               Else\r
+\r
+                       ' Status code must be '200' to get here. \r
+                       ' But we need to check the issue time of the \r
+                       ' authentication response is close to our current time.\r
+\r
+                       timestamp_now = DateDiff("s", "01/01/1970 00:00:00", UTCNow())                \r
+                       timestamp_issue = iso2time(wls_response(WLS_RESPONSE_ISSUE))\r
+                                               \r
+                       If (timestamp_issue = 0) Then\r
+                       \r
+                               set_status "609", "Unable to read issue time in authentication service reply"\r
+\r
+                       ElseIf (timestamp_issue > (timestamp_now + authrequest_skew + 1)) Then\r
+\r
+                               set_status "610", "Authentication service reply apparently issued in the future: " & wls_response(WLS_RESPONSE_ISSUE)\r
+\r
+                       ElseIf ((timestamp_now - authrequest_skew - 1) > (timestamp_issue + response_timeout)) Then\r
+                       \r
+                               set_status "611", "Stale authentication service reply issue at " & wls_response(WLS_RESPONSE_ISSUE)\r
+\r
+                       End If\r
+                                       \r
+               End If\r
+\r
\r
+               ' Calculate session expiry time\r
+               \r
+               expiry = max_session_life\r
+               \r
+               If (wls_response(WLS_RESPONSE_LIFE) <> "") Then\r
+               \r
+                       wls_token_life = Int(wls_response(WLS_RESPONSE_LIFE))\r
+                       \r
+                       If ((wls_token_life > 0) And (wls_token_life < expiry)) Then expiry = wls_token_life\r
+               \r
+               End If\r
+               \r
+               \r
+               ' Populate authentication response with information collected so far\r
+               \r
+               m_authentication_response(AUTHENTICATION_RESPONSE_ISSUE) = CStr(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow())))\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_EXPIRE) = CStr(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow()) + expiry))\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_ID) = wls_response(WLS_RESPONSE_ID)\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_PRINCIPAL) = wls_response(WLS_RESPONSE_PRINCIPAL)\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_AUTH) = wls_response(WLS_RESPONSE_AUTH)\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_SSO) = wls_response(WLS_RESPONSE_SSO)\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS) = wls_response(WLS_RESPONSE_PARAMS)\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_PTAGS) = wls_response(WLS_RESPONSE_PTAGS)\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_VER) = AUTHENTICATION_RESPONSE_VERSION\r
+\r
+\r
+               ' Collapse parameters of authentication_response into a string \r
+               ' then create HMAC hash code of this string and append this code \r
+               ' onto the string to form the value of our authentication cookie.\r
+               \r
+               authentication_cookie_value = ""\r
+               \r
+               For i = 0 To (AUTHENTICATION_RESPONSE_PARAMS - 1)\r
+                       If (m_authentication_response(i) <> "") Then authentication_cookie_value = authentication_cookie_value & m_authentication_response(i)\r
+                       authentication_cookie_value = authentication_cookie_value & "!"\r
+               Next\r
+\r
+               If (m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS) <> "") Then\r
+                       authentication_cookie_value = authentication_cookie_value & m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS)\r
+               End If\r
+\r
+               hmac_signature = hmac_sha1(cookie_key, authentication_cookie_value)\r
+               authentication_cookie_value = authentication_cookie_value & "!" & hmac_signature\r
+               \r
+               write_log("ProcessAuthenticationResponse: Setting cookie=" & authentication_cookie_value)\r
+               \r
+               authentication_cookie = authentication_cookie_value\r
+\r
+               ' Now the authentication cookie's in place, we \r
+               ' can redirect user back to the page they started from.\r
+               ' As long as our authentication cookie doesn't expire, \r
+               ' we then only need to call 'ProcessAuthenticationCookie' \r
+               ' to check the cookie is valid - ie. we don't need to \r
+               ' keep returning to the WLS for every single \r
+               ' authentication request.\r
+               \r
+               write_log("ProcessAuthenticationResponse: Session cookie established, redirecting...")\r
+               \r
+               Redirect(wls_response(WLS_RESPONSE_URL))\r
+               \r
+               ProcessAuthenticationResponse = AUTHENTICATE_INCOMPLETE\r
+       \r
+       End Function\r
+       \r
+\r
+       Public Function ProcessAuthenticationCookie()\r
+\r
+               ' ****************************************\r
+               ' 3. ProcessAuthenticationCookie()\r
+               '\r
+               ' Check the signature of the cookie is valid and if so,\r
+               ' process/interpret the values of the authentication cookie. \r
+               '\r
+               ' ****************************************\r
+               \r
+               write_log("ProcessAuthenticationCookie: Starting...")\r
+\r
+               ' Do some quick checks to ensure the authentication \r
+               ' cookie looks like a valid response from WLS.\r
+\r
+               If (authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS) Then\r
+                       write_log("ProcessAuthenticationCookie: Processing authentication cookie when it's still set to REDIRECT_WLS.")\r
+                       set_status "612", "Processing authentication cookie when its still set to REDIRECT_WLS"\r
+                       ResetState()\r
+                       ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR\r
+                       Exit Function\r
+               End If\r
+\r
+               If (CheckValidAuthenticationCookie(authentication_cookie) = False) Then\r
+                       write_log("ProcessAuthenticationCookie: Authentication cookie looks invalid.")\r
+                       set_status "613", "Authentication cookie invalid"\r
+                       ResetState()\r
+                       ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR\r
+                       Exit Function               \r
+               End If\r
+\r
+               write_log("ProcessAuthenticationCookie: Interpreting authentication cookie=" & authentication_cookie)\r
+\r
+               ' Split up authentication cookie into _authentication_response array \r
+               ' of response parameters using '!' as the delimiter.\r
+\r
+               m_authentication_response = Split(url_decode(authentication_cookie), "!")\r
+\r
+               ' ****************************************\r
+               ' Check authentication cookie has been correctly signed\r
+               ' ****************************************\r
+\r
+               ' Get signature from last element of authentication response array.\r
+\r
+               signature = m_authentication_response(UBound(m_authentication_response))\r
+\r
+               ' Copy m_authentication_response into a separate array \r
+               ' then remove the last element of that array \r
+               ' and bundle together as a string. \r
+\r
+               Dim values_for_verify()\r
+               ReDim values_for_verify(UBound(m_authentication_response) - 1)\r
+               \r
+               For i = 0 To (UBound(values_for_verify))\r
+                       values_for_verify(i) = m_authentication_response(i)\r
+               Next\r
+\r
+               values_for_verify_string = Join(values_for_verify, "!")\r
+\r
+               ' Check whether the hash of this shorter string matches the signature \r
+               ' we generated in the previous stage of the authentication process. \r
+       \r
+               If (False = hmac_sha1_verify(cookie_key, values_for_verify_string, signature)) Then\r
+                       write_log("ProcessAuthenticationCookie: AUTHENTICATION FAILED, session cookie signature invalid")\r
+                       set_status "614", "Session cookie signature invalid"\r
+                       ResetState()\r
+                       ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR\r
+                       Exit Function                                \r
+               End If\r
+\r
+               write_log("ProcessAuthenticationCookie: Existing authentication cookie verified")\r
+\r
+               ' ****************************************\r
+               ' Check authentication cookie hasn't expired\r
+               ' ****************************************\r
+               \r
+               timestamp_issue = iso2time(m_authentication_response(AUTHENTICATION_RESPONSE_ISSUE))\r
+               timestamp_expire = iso2time(m_authentication_response(AUTHENTICATION_RESPONSE_EXPIRE))\r
+               timestamp_now = DateDiff("s", "01/01/1970 00:00:00", UTCNow()) ' Get current time as Unix timestamp\r
+\r
+               If ((timestamp_issue > timestamp_now) Or (timestamp_now >= timestamp_expire)) Then\r
+\r
+                       ' Session has expired so send new authentication request to WLS.\r
+\r
+                       write_log("ProcessAuthenticationCookie: Local session cookie expired. Issue/now/expire: " & Cstr(timestamp_issue) & "/" & CStr(timestamp_now) & "/" & CStr(timestamp_expire))\r
+\r
+                       ' this.ResetState(); This won't work as we need to set the cookie during 'SendAuthenticationRequest'\r
+                       \r
+                       ProcessAuthenticationCookie = SendAuthenticationRequest(timeout_message)\r
+                       Exit Function\r
+                       \r
+               End If\r
+\r
+               ' ****************************************\r
+               ' Authentication process is COMPLETE \r
+               ' though the user may not have been \r
+               ' successfully authenticated. \r
+               ' ****************************************\r
+\r
+               If (m_authentication_response(AUTHENTICATION_RESPONSE_STATUS) <> "200") Then\r
+                       write_log("ProcessAuthenticationCookie: AUTHENTICATION COMPLETE but not successfully authenticated.")\r
+                       write_log("****************************************************************")\r
+                       ResetState()\r
+                       ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED\r
+                       Exit Function\r
+               End If\r
+               \r
+               write_log("ProcessAuthenticationCookie: AUTHENTICATION COMPLETE and user authenticated.")\r
+               write_log("****************************************************************")           \r
+               ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_AUTHENTICATED\r
+                       \r
+       End Function\r
+       \r
+\r
+       ' ****************************************\r
+       ' \r
+       ' ----------------------------------------\r
+       ' --- AUTHENTICATE()-SPECIFIC FUNCTIONS --\r
+       ' ----------------------------------------\r
+       '\r
+       ' ****************************************        \r
+\r
+       Public Function CheckSetup()\r
+\r
+               ' ****************************************\r
+               ' CheckSetup()\r
+               '\r
+               ' Check the essential variables are in place.\r
+               '\r
+               ' ****************************************\r
+\r
+               request_method = Request.ServerVariables("REQUEST_METHOD")\r
+               \r
+               ' Check 'cookie_key' is defined, the key we will be using to create hash of cookie value.\r
+               \r
+               If (cookie_key = "") Then\r
+                       set_status "601", "No key defined for session cookie"\r
+                       CheckSetup = False\r
+                       Exit Function           \r
+               End If\r
+\r
+               ' Log a warning if being used to authenticate POST requests.\r
+               \r
+               If (request_method = "POST") Then\r
+                       write_log("Ucam_Webauth agent invoked for POST request, which it doesn't really support")\r
+               End If\r
+               \r
+               ' Check that the hostname is set explicitly (since we cannot trust\r
+               ' the Host: header); if it returns false (i.e. not set).\r
+\r
+               If (hostname = "") Then\r
+                       write_log("hostname not set in Ucam_Webauth object, but is mandatory")\r
+                       set_status "602", "Ucam_Webauth configuration error - mandatory hostname not defined"\r
+                       CheckSetup = False\r
+                       Exit Function\r
+               End If\r
+               \r
+               ' If we have got this far without problems, then everything is good.\r
+\r
+               CheckSetup = True\r
+                                               \r
+       End Function\r
+       \r
+\r
+       Public Sub ResetState()\r
+\r
+               ' Reset state as if a new user has just loaded a \r
+               ' fresh browser window. Unfortunately we can't reset \r
+               ' any cookies that the remote WLS may have set.\r
+               ' This will not completely log you out of Raven as \r
+               ' the Raven WLS also stores a session cookie that \r
+               ' you cannot delete remotely. So accessing Raven \r
+               ' after logout may elicit a "You are already logged \r
+               ' in" from the Raven WLS.  \r
+               '\r
+               ' As both local and WLS cookies are session cookies, the safest \r
+               ' thing to do is to quit the browser to remove both session cookies. \r
+               ' Note that some browsers with 'Restore session' functionality, \r
+               ' eg. Firefox, may not remove session cookies properly.\r
+               ' <returns>'true' whatever happens.</returns>\r
+               \r
+               ' By setting the expiry to '1', we ensure the \r
+               ' authentication cookie is removed straightaway.\r
+               \r
+               setcookie full_cookie_name(), "", 1, cookie_path, cookie_domain, using_https(), False\r
+\r
+       End Sub \r
+\r
+\r
+       Public Function GetCurrentState()\r
+\r
+               ' If no authentication cookie and no WLS-response, then we're \r
+               ' starting fresh so return 'STAGE_AUTHENTICATION_NEW'\r
+               \r
+               If ((authentication_cookie = "") And (authentication_response_string = "")) Then\r
+                       GetCurrentState = STATE_NEW_AUTHENTICATION\r
+                       Exit Function\r
+               End If\r
+                               \r
+               ' If no cookie and a non-empty WLS-Response, then either (i) we have \r
+               ' returned from the WLS without the previous cookie that we set staying intact.\r
+               ' So there must have been a problem setting that earlier cookie.\r
+               ' Or (ii) a 'WLS-Response' GET variable was supplied to our initial\r
+               ' page to begin with, which is going to cause confusion. \r
+               ' This may be due to reloading the post-WLS page after an error.\r
+               \r
+               If ((authentication_cookie = "") And (authentication_response_string <> "")) Then\r
+                       set_status "610", "Either browser is not accepting session cookie or you supplied WLS-Response variable to initial page."\r
+                       \r
+                       ' Don't do any redirecting back to the original \r
+                       ' page as it might catch us in an infinite loop. \r
+                       ' Instead show a page to the user with the \r
+                       ' option to click on a particular link or \r
+                       ' put something in code to go to a specific \r
+                       ' safe page.\r
+                       \r
+                       GetCurrentState = STATE_ERROR\r
+                       Exit Function\r
+               End If\r
+                               \r
+               ' If authentication cookie = 'AUTHENTICATIONCOOKIE_REDIRECT_WLS'\r
+               ' and WLS-Response is non-empty, then we've been redirected here \r
+               ' by the WLS (though you could simulate this redirection manually by \r
+               ' interrupting the WLS process and manually pasting in a URL with a \r
+               ' WLS-Response GET variable.)\r
+               \r
+               If ((authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS) And (authentication_response_string <> "")) Then \r
+                       GetCurrentState = STATE_WLS_RESPONSE_RECEIVED\r
+                       Exit Function\r
+               End If          \r
+\r
+               \r
+               ' If authentication cookie = 'AUTHENTICATIONCOOKIE_REDIRECT_WLS'\r
+               ' and WLS-Response is empty, then the WLS is not working correctly.\r
+               ' So we trigger another authentication request.\r
+               ' Ideally we'd 'ResetState()' but this seems to create \r
+               ' confusion when we subsequently set the authentication \r
+               ' cookie to 'AUTHENTICATIONCOOKIE_REDIRECT_WLS' again - \r
+               ' so we leave it as is, with authentication cookie still \r
+               ' with this value.\r
+                                               \r
+               If ((authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS) And (authentication_response_string = "")) Then\r
+                       GetCurrentState = STATE_NEW_AUTHENTICATION\r
+                       Exit Function\r
+               End If\r
+               \r
+               \r
+               ' If authentication cookie != "" and != 'AUTHENTICATIONCOOKIE_REDIRECT_WLS' \r
+               ' then there's a possibility we have a legitimate authentication cookie.\r
+               ' Run 'CheckValidAuthenticationCookie' to check it looks in the \r
+               ' right format.\r
+               \r
+               If ((authentication_cookie <> "") And (authentication_cookie <> AUTHENTICATIONCOOKIE_REDIRECT_WLS)) Then\r
+\r
+                       If (CheckValidAuthenticationCookie(authentication_cookie)) Then\r
+                               GetCurrentState = STATE_WAA_AUTHENTICATIONCOOKIE_SET\r
+                               Exit Function                           \r
+                       End If                  \r
+               \r
+                       set_status "604", "Authentication cookie invalid"\r
+                       GetCurrentState = STATE_ERROR\r
+                       Exit Function\r
+               End If\r
+                               \r
+               ' If none of the conditions have been satisfied so far, \r
+               ' there must have been an error along the way. \r
+               \r
+               If (status() <> "200") Then set_status "605", "Miscellaneous error while getting current state."\r
+               \r
+               GetCurrentState = STATE_ERROR\r
+       \r
+       End Function\r
+       \r
+\r
+       Public Function CheckValidAuthenticationCookie(ByVal vAuthenticationCookie)\r
+\r
+               ' A quick check to ensure the cookie value looks the \r
+               ' right format to be an authentication cookie. \r
+               ' ie. it has the correct number of values, delimited by '!'.\r
+\r
+               If (vAuthenticationCookie = "") Then\r
+                       CheckValidAuthenticationCookie = False\r
+                       Exit Function\r
+               End If\r
+               \r
+               ' Split up authentication cookie into Array\r
+               ' using '!' as the delimiter.\r
+       \r
+               authentication_response = Split(url_decode(vAuthenticationCookie), "!")\r
+\r
+               If (UBound(authentication_response) <> (AUTHENTICATION_RESPONSE_SIZE - 1)) Then\r
+                       CheckValidAuthenticationCookie = False\r
+                       Exit Function\r
+               End If                          \r
+               \r
+               CheckValidAuthenticationCookie = True\r
+       \r
+       End Function\r
+       \r
\r
+       Public Sub Redirect(url)\r
+       \r
+           ' Redirect user's browser making \r
+           ' sure to set redirected flag accordingly\r
+       \r
+           Response.Redirect(url)\r
+           redirected = True\r
+       \r
+       End Sub\r
+\r
+\r
+       ' ****************************************\r
+       ' \r
+       ' ----------------------------------------\r
+       ' ------- MISCELLANEOUS FUNCTIONS -------- \r
+       ' ----------------------------------------\r
+       '\r
+       ' ****************************************\r
\r
+    Public Sub write_log(ByVal message )\r
+\r
+               ' Write a message to the log file with a timestamp prefix on every line\r
+               ' \r
+               ' Note relative paths, eg. 'logfile.txt', will be relative \r
+               ' to the IIS server directories where permissions to create\r
+               ' new files may not be available. So provide an absolute\r
+               ' path in the initial arguments to Ucam_Webauth ("log_file" => Path)      \r
+    \r
+               Set objFSO = CreateObject("Scripting.FileSystemObject")\r
+\r
+               If (objFSO.FileExists(log_file) = False) Then\r
+                       Set objFile = objFSO.CreateTextFile(log_file)\r
+               Else\r
+                       Set objFile = objFSO.OpenTextFile(log_file, 8)\r
+               End If\r
+\r
+        date_prefix = "[" & lpad(Year(Date()), 4) & "-" & lpad(Month(Date()),2) & "-" & lpad(Day(Date()),2) & " " & _\r
+                                       lpad(Hour(Time()), 2) & ":" & lpad(Minute(Time()),2) & ":" & lpad(Second(Time()),2) & "] "    \r
+\r
+               objFile.WriteLine(date_prefix & message)\r
+               objFile.Close\r
+\r
+    End Sub\r
+\r
\r
+    Public Sub set_status(response_status, response_msg) \r
+\r
+               ' Quick function to set 'status' and 'msg' in a single line. \r
+               ' Makes the code look a little neater.\r
+    \r
+               m_authentication_response(AUTHENTICATION_RESPONSE_STATUS) = response_status\r
+               m_authentication_response(AUTHENTICATION_RESPONSE_MSG) = response_msg\r
+               \r
+    End Sub\r
\r
+\r
+    Public Function using_https() \r
+\r
+               ' Determines whether we're using https or Not\r
+\r
+        using_https = False\r
+\r
+        If (Request.ServerVariables("HTTPS") <> "") Then\r
+            servertype = Request.ServerVariables("HTTPS")\r
+            If (servertype = "on") Then using_https = True\r
+        End If\r
+        \r
+    End Function\r
\r
+\r
+    Public Function url() \r
+\r
+               ' Get the url to redirect the browser to after a successful authentication attempt.\r
+               ' \r
+               ' This is typically identical to the url that first calls this script. \r
+               ' But due to the possibility of faking 'HOST' variables in a server response, \r
+               ' the 'hostname' must be supplied as a setup / default parameter in the code. \r
+               '\r
+               ' In versions 2 and 3, we must include the query component (GET parameters) of the \r
+               ' original calling url if they exist.\r
+               \r
+                   \r
+        ' Strip out port number from hostname\r
+\r
+               Set rgx = New RegExp\r
+               rgx.Global = True\r
+               rgx.IgnoreCase = False\r
+               rgx.Pattern = ":[0-9]+$"\r
+\r
+        basichostname = rgx.Replace(hostname, "")\r
+        port = Request.ServerVariables("SERVER_PORT")\r
+        protocol = "http://"\r
+\r
+        If (using_https()) Then\r
+            protocol = "https://"\r
+            If (port = "443") Then port = ""\r
+        Else\r
+            If (port = "80") Then port = ""\r
+        End If\r
+\r
+        finalurl = protocol & basichostname\r
+        If (port <> "") Then finalurl = finalurl & ":" & port\r
+               \r
+               ' Remove any possible WLS-Response variables \r
+               ' from query part of URL to avoid \r
+               ' confusion and just in case we've \r
+               ' called 'SendAuthenticationRequest' \r
+               ' after WLS returned incorrect WLS-Response \r
+               ' 'GET' - which could lead to multiple \r
+               ' 'WLS-Response=&WLS-Response=...'\r
+               ' coming back from WLS server.\r
+               \r
+               url_query = Request.ServerVariables("REQUEST_URI")\r
+               rgx.Pattern = "&WLS-Response=[^&]*"\r
+               url_query = rgx.Replace(url_query, "")\r
+               rgx.Pattern = "\?WLS-Response=[^&]*"\r
+               url_query = rgx.Replace(url_query, "?")\r
+               rgx.Pattern = "\?&"\r
+               url_query = rgx.Replace(url_query, "?") \r
+               rgx.Pattern = "\?$"\r
+               url_query = rgx.Replace(url_query, "")  \r
+                               \r
+        url = finalurl & url_query\r
+        \r
+    End Function\r
+\r
+       \r
+    Public Function full_cookie_name() \r
+\r
+               ' Get name of cookie, typically 'Ucam-WebAuth-Session'\r
+               ' If 'https', then distinguish the name of the cookie by suffixing '-S'\r
+\r
+        full_cookie_name = cookie_name\r
+\r
+        If (using_https()) Then full_cookie_name = cookie_name & "-S"\r
+        \r
+    End Function\r
+        \r
+\r
+    Public Sub setcookie(ByVal name , ByVal value , ByVal expire , ByVal path , ByVal domain , ByVal secure , ByVal httponly )\r
+               \r
+               ' Sets cookie.\r
+               ' \r
+               ' name: Name of cookie.\r
+               ' value: Value of cookie.\r
+               ' expire: Expiry of cookie. Don't set it to make it a session cookie.\r
+               ' path: Path of cookie.\r
+               ' domain: Domain.\r
+               ' secure: Only allow on secure domains.\r
+               ' httponly: Only allow on http non-secure domains.\r
+               \r
+        If (expire <> 0) Then\r
+               If (expire = 1) Then\r
+                               expire_datetime = DateAdd("s", 1, UTCNow())\r
+               Else\r
+                               timestamp_now = DateDiff("s", "01/01/1970 00:00:00", UTCNow())\r
+               \r
+                       If (expire < timestamp_now) Then\r
+                               write_log("setcookie: Setting expire to past so don't bother setting cookie.")\r
+                               Exit Sub\r
+                       End If\r
+               \r
+                               expire_datetime = DateAdd("s", expire, "01/01/1970 00:00:00")\r
+               End If          \r
+        End If\r
+\r
+        Response.Cookies(name) = value\r
+\r
+               ' Whats correct value for Expires field when expire = 0?\r
+               \r
+               If (expire_datetime <> 0) Then\r
+               Response.Cookies(name).Expires = expire_datetime\r
+               End If\r
+\r
+               ' Problems setting 'httponly' in VBScript as \r
+               ' VBScript encodes everything to do with cookies. \r
+               ' So if 'httponly' is required, a different \r
+               ' way of setting cookies by directly setting \r
+               ' the header with 'Set-Cookie' will be necessary. \r
+        \r
+        ' If (httponly) Then \r
+        '      Response.Cookies(name).Path = path & "; HttpOnly"\r
+               ' Else \r
+               '       Response.Cookies(name).Path = path               \r
+        ' End If\r
+\r
+               Response.Cookies(name).Path = path                              \r
\r
+               If (domain <> "") Then Response.Cookies(name).Domain = domain\r
\r
+        Response.Cookies(name).Secure = secure\r
+\r
+        ' Response.Cookies.Add(cookie)\r
+    End Sub\r
+\r
+\r
+    Public Function time2iso(t) \r
+\r
+               ' Convert a Unix timestamp into the format required by Raven. \r
+               ' Format based on RFC 3339, but see Raven documentation for \r
+               ' a full description.\r
+\r
+           unUDate = DateAdd("s", t, "01/01/1970 00:00:00")\r
+\r
+           time2iso = (lpad(Year(unUDate), 4) & lpad(Month(unUDate),2) & lpad(Day(unUDate),2) & "T" &_\r
+                                       lpad(Hour(unUDate), 2) & lpad(Minute(unUDate),2) & lpad(Second(unUDate),2) & "Z") \r
+                                               \r
+    End Function\r
+\r
+\r
+    Public Function iso2time(t) \r
+\r
+               ' Convert a time in Raven format into a Unix timestamp \r
+               ' ie. the number of seconds since epoch.\r
+\r
+               iso2time = 0\r
+               \r
+               if (Len(t) <> 16) Then Exit Function                        \r
+               \r
+               On Error Resume Next\r
+               \r
+               sYear = Mid(t,1,4)\r
+               sMonth = Mid(t, 5, 2)\r
+               sDay = Mid(t, 7, 2)\r
+               sHour = Mid(t, 10, 2)\r
+               sMinute = Mid(t, 12, 2)\r
+               sSecond = Mid(t, 14, 2)\r
+               \r
+        var_origin = CDate("01/01/1970 00:00:00")\r
+        var_date = CDate(sDay & "/" & sMonth & "/" & sYear & " " & sHour & ":" & sMinute & ":" & sSecond)\r
+               \r
+               iso2time = DateDiff("s", var_origin, var_date)         \r
+               \r
+               On Error GoTo 0\r
+               \r
+    End Function\r
+\r
+\r
+    Public Function load_key(ByVal key_id ) \r
+\r
+               ' Returns content of a certificate key as a string\r
+\r
+        key_filename = key_dir & "/" & key_id & ".crt"\r
+\r
+               Set fso = CreateObject("Scripting.FileSystemObject")\r
+               Set file = fso.getFile(key_filename)\r
+               If isNull(file) Then\r
+                   Exit Function\r
+               End If\r
+       \r
+               Set ts = file.OpenAsTextStream()\r
+               s = Space(file.size)\r
+               a = Split(s," ")\r
+\r
+               i = 0\r
+               ' Do not replace the following block by readBinary = by ts.readAll(), \r
+               ' it would result in broken output, because that method is not intended for binary data \r
+               While Not ts.atEndOfStream\r
+                   a(i) = ts.read(1)\r
+               i = i + 1\r
+               Wend\r
+               ts.close\r
+\r
+               load_key = Join(a,"")\r
+               \r
+    End Function\r
+\r
+\r
+    Public Function check_signature(ByVal data , ByVal sig , ByVal key_id ) \r
+\r
+               ' Checks the 'signature' provided by the WLS when it signed 'data' \r
+               ' is a valid signature for the data. This ensures the data has not been \r
+               ' tampered with.\r
+\r
+        key_str = load_key(key_id)\r
+\r
+               ' We make a call to a some Javascript libraries to do the verification\r
+               \r
+               check_signature = JavascriptCertVerify(data, wls_decode(sig), key_str)\r
+               \r
+    End Function\r
+\r
+\r
+    Public Function wls_encode(plainTextBytes) \r
+\r
+               ' Encode a byte array of data in a way that can be \r
+               ' easily sent to the WLS\r
+\r
+               Set rgx = New RegExp\r
+               rgx.Global = True\r
+               rgx.IgnoreCase = False\r
+               rgx.Pattern = "\+": plainTextBytes = rgx.Replace(plainTextBytes, "_")\r
+               rgx.Pattern = "/": plainTextBytes = rgx.Replace(plainTextBytes, ".")\r
+               rgx.Pattern = "=": plainTextBytes = rgx.Replace(plainTextBytes, "_")\r
+\r
+        wls_encode = plainTextBytes\r
+        \r
+    End Function\r
+\r
+\r
+    Public Function wls_decode(ByVal sig )\r
+\r
+               ' Decode a string of data received from the WLS \r
+               ' into a string with data as hex doubledigits (as used by javascript security library)\r
+    \r
+               Set rgx = New RegExp\r
+               rgx.Global = True\r
+               rgx.IgnoreCase = False\r
+               rgx.Pattern = "-": sig = rgx.Replace(sig, "+")\r
+               rgx.Pattern = "\.": sig = rgx.Replace(sig, "/")\r
+               rgx.Pattern = "_": sig = rgx.Replace(sig, "=")\r
+               \r
+               sig_base64 = base64_decode(sig)\r
+               \r
+               sig_hex = ""\r
+               For i = 1 To LenB(sig_base64)\r
+                        sig_hex = sig_hex & Right(String(2, "0") & LCase(Hex(AscB(MidB(sig_base64, i, 1)))), 2)\r
+               Next\r
+\r
+        wls_decode = sig_hex\r
+        \r
+    End Function\r
+\r
+\r
+    Public Function hmac_sha1(ByVal key , ByVal data ) \r
+\r
+               ' Create HMACSHA1 hash value for 'data' using public 'key'.\r
+               '\r
+               ' key: raw content of a certificate file as string.\r
+               ' data: data to create hash value from as string.\r
+    \r
+           hmac_sha1 = wls_encode(b64_hmac_sha1(key, data))\r
+           \r
+    End Function\r
+\r
+\r
+    Public Function hmac_sha1_verify(ByVal key , ByVal data , ByVal sig ) \r
+\r
+               ' Verify the 'data' has been signed by public 'key'\r
+               ' \r
+               ' Compute HMACSHA1 hash value for 'data' using public 'key'\r
+               ' then compare the value to 'sig'(nature).\r
+               ' \r
+               ' key: Full text for public key.\r
+               ' data: Data to be verified.\r
+               ' signature: Signature of signed data (ie. hash value generated by us earlier).\r
+    \r
+        hmac_sha1_verify = (sig = hmac_sha1(key, data))\r
+        \r
+    End Function\r
+\r
+\r
+       ' ****************************************\r
+       ' \r
+       ' ----------------------------------------\r
+       ' ------- MISCELLANEOUS FUNCTIONS -------- \r
+       ' ------- SPECIFIC TO ASP VERSION --------\r
+       ' ----------------------------------------\r
+       '\r
+       ' ****************************************\r
+\r
+       Function lpad(strInput, length)\r
+\r
+       ' Left pad string with zeros.\r
+       \r
+         lpad = Right(String(length, "0") & strInput, length)\r
+         \r
+       End Function\r
+\r
+\r
+       Public Function url_decode(ByVal url_encoded)\r
+\r
+               ' Decodes URL-encoded string \r
+\r
+               url_decode = ""\r
+       \r
+               If (url_encoded = "") Then Exit Function\r
+\r
+               Dim aSplit\r
+               Dim sOutput\r
+               Dim I\r
+               If IsNull(url_encoded) Then\r
+                       Exit Function\r
+               End If\r
+               \r
+               ' convert all pluses to spaces\r
+               sOutput = REPLACE(url_encoded, "+", " ")\r
+               \r
+               ' next convert %hexdigits to the character\r
+               aSplit = Split(sOutput, "%")\r
+               \r
+               If IsArray(aSplit) Then\r
+                       sOutput = aSplit(0)\r
+                       For I = 0 to UBound(aSplit) - 1\r
+                               sOutput = sOutput & _\r
+                               Chr("&H" & Left(aSplit(i + 1), 2)) &_\r
+                               Right(aSplit(i + 1), Len(aSplit(i + 1)) - 2)\r
+                       Next\r
+               End If\r
+               \r
+               url_decode = sOutput\r
+          \r
+       End Function\r
+\r
+       \r
+       Public Function UTCNow()\r
+\r
+           ' Gets the UTC current time\r
+           ' The UTC time is the standard time the Raven server is operating with.\r
+       \r
+               Set dateTime = CreateObject("WbemScripting.SWbemDateTime")    \r
+               dateTime.SetVarDate (now())\r
+               UTCNow = dateTime.GetVarDate (false)\r
+               \r
+       End Function\r
+\r
+\r
+       Public Function base64_decode(strB64)\r
+\r
+               ' Base64 decodes a string\r
+       \r
+               strXML = "<B64DECODE xmlns:dt=" & Chr(34) & _\r
+                                       "urn:schemas-microsoft-com:datatypes" & Chr(34) & " " & _\r
+                                       "dt:dt=" & Chr(34) & "bin.base64" & Chr(34) & ">" & _\r
+                                       strB64 & "</B64DECODE>"\r
+               Set oXMLDoc = CreateObject("MSXML2.DOMDocument.3.0")\r
+               oXMLDoc.LoadXML(strXML)\r
+               \r
+               base64_decode = oXMLDoc.selectsinglenode("B64DECODE").nodeTypedValue\r
+               \r
+               set oXMLDoc = Nothing\r
+               \r
+       End Function\r
+       \r
+\r
+       ' ****************************************\r
+       ' \r
+       ' Special get/set methods to enable \r
+       ' seamless access to cookies or query \r
+       ' variables as if they were ordinary \r
+       ' member variables.\r
+       '\r
+       ' ****************************************\r
+\r
+       ' Get/set authentication_cookie\r
+       ' 'authentication_cookie' is used to get and set \r
+       ' the value of the authentication cookie.\r
+\r
+    Public Property Get authentication_cookie\r
+\r
+               ' If the current value of the private variable \r
+               ' m_authentication_cookie is null then we check \r
+               ' to see what the value of the actual cookie is \r
+               ' and use that to set the variable accordingly.\r
+\r
+               If (m_authentication_cookie = "") Then\r
+                       On Error Resume Next\r
+                       m_authentication_cookie = Request.Cookies(full_cookie_name())\r
+                       On Error GoTo 0\r
+               End If\r
+\r
+               authentication_cookie = m_authentication_cookie\r
+\r
+    End Property    \r
+    \r
+       Public Property Let authentication_cookie(v_authentication_cookie)\r
+\r
+               ' Reset the private variable _authentication_cookie \r
+               ' so the system is forced to query the cookie directly\r
+               ' next time it wants to check the value through \r
+               ' authentication_cookie.\r
+\r
+               m_authentication_cookie = ""\r
+               \r
+               setcookie full_cookie_name(), v_authentication_cookie, 0, cookie_path, cookie_domain, using_https(), False\r
+                               \r
+    End Property\r
+\r
+\r
+       Public Property Get authentication_response_string()\r
+\r
+               ' Get authentication_response_string\r
+               ' \r
+               ' Checks to see if there is a 'WLS-Response' \r
+               ' variable set and uses this as the value \r
+               ' for authentication_response_string\r
+\r
+               ' If the value is already set, we just return it straightaway\r
+       \r
+               If (m_authentication_response_string <> "") Then\r
+                       authentication_response_string = m_authentication_response_string\r
+                       Exit Property\r
+               End If\r
+               \r
+               ' If the value is not set, we look for the value of 'WLS-Response' in 'QUERY_STRING'\r
+               \r
+        query_string = Request.ServerVariables("QUERY_STRING")\r
+                       \r
+               If (query_string = "") Then\r
+                       authentication_response_string = m_authentication_response_string\r
+                       Exit Property\r
+               End If\r
+               \r
+\r
+               ' We're processing urls with possible name-value pairs\r
+               ' in addition to WLS-Response=... so we need to extract all \r
+               ' name-value pairs and check specifically for WLS-Response. \r
+               \r
+               namevaluepairs = Split(query_string, "&")\r
+               \r
+               For Each namevalue In namevaluepairs\r
+               \r
+                       pair = Split(namevalue, "=")\r
+                       \r
+                       If (UBound(pair) = 1) Then                      \r
+                               If (pair(0) = "WLS-Response") Then                              \r
+                                       authentication_response_string = url_decode(pair(1))\r
+                                       Exit Property                           \r
+                               End If\r
+                       End If\r
+               Next\r
+\r
+               authentication_response_string = ""             \r
+               \r
+       End Property\r
+       \r
+\r
+       ' ****************************************\r
+       ' \r
+       ' Get/set methods to enable public access \r
+       ' to private variables.\r
+       '\r
+       ' ****************************************\r
+\r
+       ' Get/set auth_service\r
+\r
+    Public Property Get auth_service\r
+            auth_service = m_auth_service\r
+    End Property    \r
+       Public Property Let auth_service(v_auth_service)\r
+            m_auth_service = v_auth_service\r
+    End Property\r
+\r
+       ' Get/set authrequest_desc\r
+\r
+    Public Property Get authrequest_desc\r
+            authrequest_desc = m_description\r
+    End Property    \r
+       Public Property Let authrequest_desc(v_authrequest_desc)\r
+            m_authrequest_desc = v_authrequest_desc\r
+    End Property\r
+\r
+       ' Get/set authrequest_skew\r
+\r
+    Public Property Get authrequest_skew\r
+            authrequest_skew = m_authrequest_skew\r
+    End Property    \r
+       Public Property Let authrequest_skew(v_authrequest_skew)\r
+            m_authrequest_skew = v_authrequest_skew\r
+    End Property\r
+\r
+       ' Get/set authrequest_fail\r
+\r
+    Public Property Get authrequest_fail\r
+            authrequest_fail = m_authrequest_fail\r
+    End Property    \r
+       Public Property Let authrequest_fail(v_authrequest_fail)\r
+            m_authrequest_fail = v_authrequest_fail\r
+    End Property\r
+\r
+       ' Get/set authrequest_iact\r
+\r
+    Public Property Get authrequest_iact\r
+            authrequest_iact = m_authrequest_iact\r
+    End Property    \r
+       Public Property Let authrequest_iact(v_authrequest_iact)\r
+            m_authrequest_iact = v_authrequest_iact\r
+    End Property\r
+\r
+       ' Get/set use_authrequest_iact\r
+\r
+    Public Property Get use_authrequest_iact\r
+            use_authrequest_iact = m_use_authrequest_iact\r
+    End Property    \r
+       Public Property Let use_authrequest_iact(v_use_authrequest_iact)\r
+            m_use_authrequest_iact = v_use_authrequest_iact\r
+    End Property\r
+\r
+       ' Get/set authrequest_aauth\r
+\r
+    Public Property Get authrequest_aauth\r
+            authrequest_aauth = m_authrequest_aauth\r
+    End Property    \r
+       Public Property Let authrequest_aauth(v_authrequest_aauth)\r
+            m_authrequest_aauth = v_authrequest_aauth\r
+    End Property\r
+\r
+       ' Get/set authrequest_params\r
+\r
+    Public Property Get authrequest_params\r
+            authrequest_params = m_authrequest_params\r
+    End Property    \r
+       Public Property Let authrequest_params(v_authrequest_params)\r
+            m_authrequest_params = v_authrequest_params\r
+    End Property\r
+       ' Get/set cookie_key\r
+\r
+    Public Property Get cookie_key\r
+            cookie_key = m_cookie_key\r
+    End Property    \r
+       Public Property Let cookie_key(v_cookie_key)\r
+            m_cookie_key = v_cookie_key\r
+    End Property\r
+\r
+       ' Get/set redirected\r
+       ' \r
+       ' 'redirected' is a flag that says \r
+       ' whether a redirect has been sent \r
+       ' which is useful to check before \r
+       ' attempting to output any information \r
+       ' to the browser.\r
+\r
+    Public Property Get redirected\r
+            redirected = m_redirected\r
+    End Property    \r
+       Public Property Let redirected(v_redirected)\r
+            m_redirected = v_redirected\r
+    End Property\r
+\r
+       ' Get/set cookie_path\r
+\r
+    Public Property Get cookie_path\r
+            cookie_path = m_cookie_path\r
+    End Property    \r
+       Public Property Let cookie_path(v_cookie_path)\r
+            m_cookie_path = v_cookie_path\r
+    End Property\r
+    \r
+       ' Get/set response_timeout\r
+\r
+    Public Property Get response_timeout\r
+            response_timeout = m_response_timeout\r
+    End Property    \r
+       Public Property Let response_timeout(v_response_timeout)\r
+            m_response_timeout = v_response_timeout\r
+    End Property\r
+\r
+       ' Get/set hostname\r
+\r
+    Public Property Get hostname\r
+            hostname = m_hostname\r
+    End Property    \r
+       Public Property Let hostname(v_hostname)\r
+            m_hostname = v_hostname\r
+    End Property\r
+\r
+       ' Get/set key_dir\r
+\r
+    Public Property Get key_dir\r
+            key_dir = m_key_dir\r
+    End Property\r
+       Public Property Let key_dir(v_key_dir)\r
+            m_key_dir = v_key_dir\r
+    End Property\r
+\r
+       ' Get/set max_session_life\r
+\r
+    Public Property Get max_session_life\r
+            max_session_life = m_max_session_life\r
+    End Property    \r
+       Public Property Let max_session_life(v_max_session_life)\r
+            m_max_session_life = v_max_session_life\r
+    End Property\r
+\r
+       ' Get/set timeout_message\r
+\r
+    Public Property Get timeout_message\r
+            timeout_message = m_timeout_message\r
+    End Property    \r
+       Public Property Let timeout_message(v_timeout_message)\r
+            m_timeout_message = v_timeout_message\r
+    End Property\r
+\r
+       ' Get/set cookie_name\r
+\r
+    Public Property Get cookie_name\r
+            cookie_name = m_cookie_name\r
+    End Property    \r
+       Public Property Let cookie_name(v_cookie_name)\r
+            m_cookie_name = v_cookie_name\r
+    End Property\r
+\r
+       ' Get/set cookie_domain\r
+\r
+    Public Property Get cookie_domain\r
+            cookie_domain = m_cookie_domain\r
+    End Property    \r
+       Public Property Let cookie_domain(v_cookie_domain)\r
+            m_cookie_domain = v_cookie_domain\r
+    End Property\r
+\r
+       ' Get/set log_file\r
+\r
+    Public Property Get log_file\r
+            log_file = m_log_file\r
+    End Property    \r
+       Public Property Let log_file(v_log_file)\r
+            m_log_file = v_log_file\r
+    End Property\r
+    \r
+    ' ****************************************\r
+    ' \r
+    ' Read-only methods to retrieve \r
+    ' authentication response values.\r
+    '\r
+    ' See Raven documentation for a full \r
+    ' explanation of each parameter.\r
+    '\r
+    ' ****************************************\r
+       \r
+    Public Function status() \r
+\r
+               ' Read-only status\r
+\r
+        status = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_STATUS) Then\r
+                       status = m_authentication_response(AUTHENTICATION_RESPONSE_STATUS)\r
+        End If\r
+\r
+       End Function\r
+\r
+    Public Function success() \r
+\r
+               ' Read-only success\r
+\r
+        success = False\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_STATUS) Then\r
+                       success = (m_authentication_response(AUTHENTICATION_RESPONSE_STATUS) = "200")\r
+        End If\r
+        \r
+       End Function\r
+\r
+    Public Function msg() \r
+\r
+               ' Read-only msg\r
+\r
+        msg = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_MSG) Then\r
+                       msg = m_authentication_response(AUTHENTICATION_RESPONSE_MSG)\r
+        End If\r
+        \r
+       End Function\r
+   \r
+    Public Function issue() \r
+\r
+               ' Read-only issue\r
+\r
+        issue = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_ISSUE) Then\r
+                       issue = m_authentication_response(AUTHENTICATION_RESPONSE_ISSUE)\r
+        End If\r
+\r
+       End Function\r
+\r
+    Public Function expire() \r
+\r
+               ' Read-only expire\r
+\r
+        expire = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_EXPIRE) Then\r
+                       expire = m_authentication_response(AUTHENTICATION_RESPONSE_EXPIRE)\r
+        End If\r
+        \r
+       End Function\r
+\r
+    Public Function id() \r
+\r
+               ' Read-only id\r
+\r
+        id = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_ID) Then\r
+                       id = m_authentication_response(AUTHENTICATION_RESPONSE_ID)\r
+        End If\r
+        \r
+       End Function\r
+\r
+    Public Function principal() \r
+\r
+               ' Read-only principal\r
+               ' ie. the user id of the authenticated user\r
+\r
+        principal = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_PRINCIPAL) Then\r
+                       principal = m_authentication_response(AUTHENTICATION_RESPONSE_PRINCIPAL)\r
+        End If\r
+\r
+       End Function\r
+\r
+    Public Function auth() \r
+       \r
+               ' Read-only auth\r
+\r
+        auth = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_AUTH) Then\r
+                       auth = m_authentication_response(AUTHENTICATION_RESPONSE_AUTH)\r
+        End If\r
+\r
+       End Function\r
+   \r
+    Public Function sso() \r
+\r
+               ' Read-only sso\r
+\r
+        sso = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_SSO) Then\r
+                       sso = m_authentication_response(AUTHENTICATION_RESPONSE_SSO)\r
+        End If\r
+\r
+       End Function\r
+   \r
+    Public Function webauth_params() \r
+\r
+               ' Read-only params\r
+\r
+        webauth_params = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_PARAMS) Then\r
+                       webauth_params = m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS)\r
+        End If\r
+\r
+       End Function\r
+   \r
+       Public Function ptags() \r
+\r
+        ' Read-only ptags\r
+        ' ie. whether user is 'current' staff/student \r
+        ' or another type of user.\r
+\r
+        ptags = ""\r
+\r
+        If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_PTAGS) Then\r
+                       ptags = m_authentication_response(AUTHENTICATION_RESPONSE_PTAGS)\r
+        End If\r
+\r
+       End Function            \r
+        \r
+End Class\r
+\r
+%>\r
+\r
+<script runat="server" language="JavaScript" src="js/core.js"></script>\r
+<script runat="server" language="JavaScript" src="js/md5.js"></script>\r
+<script runat="server" language="JavaScript" src="js/sha1.js"></script>\r
+<script runat="server" language="JavaScript" src="js/sha256.js"></script>\r
+<script runat="server" language="JavaScript" src="js/ripemd160.js"></script>\r
+<script runat="server" language="JavaScript" src="js/x64-core.js"></script>\r
+<script runat="server" language="JavaScript" src="js/sha512.js"></script>\r
+<script runat="server" language="JavaScript" src="js/jsbn.js"></script>\r
+<script runat="server" language="JavaScript" src="js/jsbn2.js"></script>\r
+<script runat="server" language="JavaScript" src="js/rsa.js"></script>\r
+<script runat="server" language="JavaScript" src="js/rsa2.js"></script>\r
+<script runat="server" language="JavaScript" src="js/crypto-1.1.js"></script>\r
+<script runat="server" language="JavaScript" src="js/x509-1.1.js"></script>\r
+<script runat="server" language="JavaScript" src="js/base64.js"></script>\r
+<script runat="server" language="JavaScript" src="js/asn1hex-1.1.js"></script>\r
+<script runat="server" language="JavaScript" src="js/rsapem-1.1.js"></script>\r
+<script runat="server" language="JavaScript" src="js/rsasign-1.2.js"></script>\r
+<script runat="server" language="JavaScript" src="js/hex_sha1_js.js"></script>\r
+<script runat="server" language="JavaScript" >\r
+\r
+// Javascript cryptographic libraries above adapted for ASP use from \r
+// client libraries at http://kjur.github.io/jsrsasign/index.html \r
+// These libraries are used in 'JavascriptCertVerify' (Javascript) \r
+// and 'hmac_sha1' functions. \r
\r
+function JavascriptCertVerify(sMsg, sSignature, sCertificateValue) \r
+{\r
+       // Javascript function to verify message has \r
+       // been signed with particular certificate.\r
+       \r
+       var bytes = [];\r
+       var x509 = new X509();\r
+\r
+       x509.readCertPEM(sCertificateValue);\r
+\r
+       return x509.subjectPublicKeyRSA.verifyString(sMsg, sSignature);\r
+}\r
+\r
+</script>\r
diff --git a/certificates/901.crt b/certificates/901.crt
new file mode 100644 (file)
index 0000000..cb36708
--- /dev/null
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDzTCCAzagAwIBAgIBADANBgkqhkiG9w0BAQQFADCBpjELMAkGA1UEBhMCR0Ix
+EDAOBgNVBAgTB0VuZ2xhbmQxEjAQBgNVBAcTCUNhbWJyaWRnZTEgMB4GA1UEChMX
+VW5pdmVyc2l0eSBvZiBDYW1icmlkZ2UxLTArBgNVBAsTJENvbXB1dGluZyBTZXJ2
+aWNlIERFTU8gUmF2ZW4gU2VydmljZTEgMB4GA1UEAxMXUmF2ZW4gREVNTyBwdWJs
+aWMga2V5IDEwHhcNMDUwNzI2MTMyMTIwWhcNMDUwODI1MTMyMTIwWjCBpjELMAkG
+A1UEBhMCR0IxEDAOBgNVBAgTB0VuZ2xhbmQxEjAQBgNVBAcTCUNhbWJyaWRnZTEg
+MB4GA1UEChMXVW5pdmVyc2l0eSBvZiBDYW1icmlkZ2UxLTArBgNVBAsTJENvbXB1
+dGluZyBTZXJ2aWNlIERFTU8gUmF2ZW4gU2VydmljZTEgMB4GA1UEAxMXUmF2ZW4g
+REVNTyBwdWJsaWMga2V5IDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALhF
+i9tIZvjYQQRfOzP3cy5ujR91ZntQnQehldByHlchHRmXwA1ot/e1WlHPgIjYkFRW
+lSNcSDM5r7BkFu69zM66IHcF80NIopBp+3FYqi5uglEDlpzFrd+vYllzw7lBzUnp
+CrwTxyO5JBaWnFMZrQkSdspXv89VQUO4V4QjXV7/AgMBAAGjggEHMIIBAzAdBgNV
+HQ4EFgQUgjC6WtA4jFf54kxlidhFi8w+0HkwgdMGA1UdIwSByzCByIAUgjC6WtA4
+jFf54kxlidhFi8w+0HmhgaykgakwgaYxCzAJBgNVBAYTAkdCMRAwDgYDVQQIEwdF
+bmdsYW5kMRIwEAYDVQQHEwlDYW1icmlkZ2UxIDAeBgNVBAoTF1VuaXZlcnNpdHkg
+b2YgQ2FtYnJpZGdlMS0wKwYDVQQLEyRDb21wdXRpbmcgU2VydmljZSBERU1PIFJh
+dmVuIFNlcnZpY2UxIDAeBgNVBAMTF1JhdmVuIERFTU8gcHVibGljIGtleSAxggEA
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAsdyB+9szctHHIHE+S2Kg
+LSxbGuFG9yfPFIqaSntlYMxKKB5ba/tIAMzyAOHxdEM5hi1DXRsOok3ElWjOw9oN
+6Psvk/hLUN+YfC1saaUs3oh+OTfD7I4gRTbXPgsd6JgJQ0TQtuGygJdaht9cRBHW
+wOq24EIbX5LquL9w+uvnfXw=
+-----END CERTIFICATE-----
diff --git a/certificates/raventest.crt b/certificates/raventest.crt
new file mode 100644 (file)
index 0000000..108e5ed
--- /dev/null
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAjegAwIBAgIJALfog7E7XoTvMA0GCSqGSIb3DQEBBQUAME8xCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMQ0wCwYDVQQKEwRUZXN0MQ0wCwYD
+VQQLEwRUZXN0MQ0wCwYDVQQDEwRUZXN0MB4XDTE0MDQwNDE1MDUzNloXDTE1MDQw
+NDE1MDUzNlowTzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxDTAL
+BgNVBAoTBFRlc3QxDTALBgNVBAsTBFRlc3QxDTALBgNVBAMTBFRlc3QwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBAOG1Ire2quEcidKWI0WQ3XjHQ1gI/QXifX2O
+FzuvETOpvKlRImI2IpRPKL3+ebsDnw3X96Dzer0tW/aNMtSrjV8QsiS/kWWIQzmb
+1wRjxjBTSf2SY4/xW0aBGVYzH/YrzmLMYUl2Kmuq0Zpk85ZQ2mlN0TL46W8iTFrr
+br9JH6RjAgMBAAGjgbEwga4wHQYDVR0OBBYEFD8vDiIdpN5qwc12dVKkQso/59GX
+MH8GA1UdIwR4MHaAFD8vDiIdpN5qwc12dVKkQso/59GXoVOkUTBPMQswCQYDVQQG
+EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTENMAsGA1UEChMEVGVzdDENMAsGA1UE
+CxMEVGVzdDENMAsGA1UEAxMEVGVzdIIJALfog7E7XoTvMAwGA1UdEwQFMAMBAf8w
+DQYJKoZIhvcNAQEFBQADgYEAJEoknXd8iXVHkUpwKQmf1xWbNXxqhuLkweDD6JEN
+/fQpfHVnWEj+Et2VBtvtF2zO3b9gZij75pNOk+yw8wLs1xhSL7MJYXSx5rXEXE3i
+Y4Y6bRdH1/BAoWJvkGfun9wLhA22z3cS2iMx7xsdaWvZBrT36JKIZ4kNOLqQOjFH
+ukE=
+-----END CERTIFICATE-----
diff --git a/docs/I - Overview of Raven Authentication Process.pdf b/docs/I - Overview of Raven Authentication Process.pdf
new file mode 100644 (file)
index 0000000..b634236
Binary files /dev/null and b/docs/I - Overview of Raven Authentication Process.pdf differ
diff --git a/docs/II - Ucam_Webauth - Flowchart for Valid Authentication.pdf b/docs/II - Ucam_Webauth - Flowchart for Valid Authentication.pdf
new file mode 100644 (file)
index 0000000..f788d7f
Binary files /dev/null and b/docs/II - Ucam_Webauth - Flowchart for Valid Authentication.pdf differ
diff --git a/docs/III - Ucam_Webauth - Flowchart for Cancelled Authentication.pdf b/docs/III - Ucam_Webauth - Flowchart for Cancelled Authentication.pdf
new file mode 100644 (file)
index 0000000..1ac7199
Binary files /dev/null and b/docs/III - Ucam_Webauth - Flowchart for Cancelled Authentication.pdf differ
diff --git a/js/asn1hex-1.1.js b/js/asn1hex-1.1.js
new file mode 100644 (file)
index 0000000..73a8117
--- /dev/null
@@ -0,0 +1,292 @@
+/*! asn1hex-1.1.4.js (c) 2012-2013 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+/*
+ * asn1hex.js - Hexadecimal represented ASN.1 string library
+ *
+ * Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
+ *
+ * This software is licensed under the terms of the MIT License.
+ * http://kjur.github.com/jsrsasign/license/
+ *
+ * The above copyright and license notice shall be 
+ * included in all copies or substantial portions of the Software.
+ */
+
+/**
+ * @fileOverview
+ * @name asn1hex-1.1.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version asn1hex 1.1.4 (2013-Oct-02)
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ */
+
+/*
+ * MEMO:
+ *   f('3082025b02...', 2) ... 82025b ... 3bytes
+ *   f('020100', 2) ... 01 ... 1byte
+ *   f('0203001...', 2) ... 03 ... 1byte
+ *   f('02818003...', 2) ... 8180 ... 2bytes
+ *   f('3080....0000', 2) ... 80 ... -1
+ *
+ *   Requirements:
+ *   - ASN.1 type octet length MUST be 1. 
+ *     (i.e. ASN.1 primitives like SET, SEQUENCE, INTEGER, OCTETSTRING ...)
+ */
+
+/**
+ * ASN.1 DER encoded hexadecimal string utility class
+ * @name ASN1HEX
+ * @class ASN.1 DER encoded hexadecimal string utility class
+ * @since jsrsasign 1.1
+ */
+var ASN1HEX = new function() {
+    /**
+     * get byte length for ASN.1 L(length) bytes
+     * @name getByteLengthOfL_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} s hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} pos string index
+     * @return byte length for ASN.1 L(length) bytes
+     */
+    this.getByteLengthOfL_AtObj = function(s, pos) {
+       if (s.substring(pos + 2, pos + 3) != '8') return 1;
+       var i = parseInt(s.substring(pos + 3, pos + 4));
+       if (i == 0) return -1;          // length octet '80' indefinite length
+       if (0 < i && i < 10) return i + 1;      // including '8?' octet;
+       return -2;                              // malformed format
+    };
+
+    /**
+     * get hexadecimal string for ASN.1 L(length) bytes
+     * @name getHexOfL_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} s hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} pos string index
+     * @return {String} hexadecimal string for ASN.1 L(length) bytes
+     */
+    this.getHexOfL_AtObj = function(s, pos) {
+       var len = this.getByteLengthOfL_AtObj(s, pos);
+       if (len < 1) return '';
+       return s.substring(pos + 2, pos + 2 + len * 2);
+    };
+
+    //   getting ASN.1 length value at the position 'idx' of
+    //   hexa decimal string 's'.
+    //
+    //   f('3082025b02...', 0) ... 82025b ... ???
+    //   f('020100', 0) ... 01 ... 1
+    //   f('0203001...', 0) ... 03 ... 3
+    //   f('02818003...', 0) ... 8180 ... 128
+    /**
+     * get integer value of ASN.1 length for ASN.1 data
+     * @name getIntOfL_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} s hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} pos string index
+     * @return ASN.1 L(length) integer value
+     */
+    this.getIntOfL_AtObj = function(s, pos) {
+       var hLength = this.getHexOfL_AtObj(s, pos);
+       if (hLength == '') return -1;
+       var bi;
+       if (parseInt(hLength.substring(0, 1)) < 8) {
+           bi = new BigInteger(hLength, 16);
+       } else {
+           bi = new BigInteger(hLength.substring(2), 16);
+       }
+       return bi.intValue();
+    };
+
+    /**
+     * get ASN.1 value starting string position for ASN.1 object refered by index 'idx'.
+     * @name getStartPosOfV_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} s hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} pos string index
+     */
+    this.getStartPosOfV_AtObj = function(s, pos) {
+       var l_len = this.getByteLengthOfL_AtObj(s, pos);
+       if (l_len < 0) return l_len;
+       return pos + (l_len + 1) * 2;
+    };
+
+    /**
+     * get hexadecimal string of ASN.1 V(value)
+     * @name getHexOfV_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} s hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} pos string index
+     * @return {String} hexadecimal string of ASN.1 value.
+     */
+    this.getHexOfV_AtObj = function(s, pos) {
+       var pos1 = this.getStartPosOfV_AtObj(s, pos);
+       var len = this.getIntOfL_AtObj(s, pos);
+       return s.substring(pos1, pos1 + len * 2);
+    };
+
+    /**
+     * get hexadecimal string of ASN.1 TLV at
+     * @name getHexOfTLV_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} s hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} pos string index
+     * @return {String} hexadecimal string of ASN.1 TLV.
+     * @since 1.1
+     */
+    this.getHexOfTLV_AtObj = function(s, pos) {
+       var hT = s.substr(pos, 2);
+       var hL = this.getHexOfL_AtObj(s, pos);
+       var hV = this.getHexOfV_AtObj(s, pos);
+       return hT + hL + hV;
+    };
+
+    /**
+     * get next sibling starting index for ASN.1 object string
+     * @name getPosOfNextSibling_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} s hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} pos string index
+     * @return next sibling starting index for ASN.1 object string
+     */
+    this.getPosOfNextSibling_AtObj = function(s, pos) {
+       var pos1 = this.getStartPosOfV_AtObj(s, pos);
+       var len = this.getIntOfL_AtObj(s, pos);
+       return pos1 + len * 2;
+    };
+
+    /**
+     * get array of indexes of child ASN.1 objects
+     * @name getPosArrayOfChildren_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} s hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} start string index of ASN.1 object
+     * @return {Array of Number} array of indexes for childen of ASN.1 objects
+     */
+    this.getPosArrayOfChildren_AtObj = function(h, pos) {
+       var a = new Array();
+       var p0 = this.getStartPosOfV_AtObj(h, pos);
+       a.push(p0);
+
+       var len = this.getIntOfL_AtObj(h, pos);
+       var p = p0;
+       var k = 0;
+       while (1) {
+           var pNext = this.getPosOfNextSibling_AtObj(h, p);
+           if (pNext == null || (pNext - p0  >= (len * 2))) break;
+           if (k >= 200) break;
+           
+           a.push(pNext);
+           p = pNext;
+           
+           k++;
+       }
+       
+       return a;
+    };
+
+    /**
+     * get string index of nth child object of ASN.1 object refered by h, idx
+     * @name getNthChildIndex_AtObj
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} h hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} idx start string index of ASN.1 object
+     * @param {Number} nth for child
+     * @return {Number} string index of nth child.
+     * @since 1.1
+     */
+    this.getNthChildIndex_AtObj = function(h, idx, nth) {
+       var a = this.getPosArrayOfChildren_AtObj(h, idx);
+       return a[nth];
+    };
+
+    // ========== decendant methods ==============================
+    /**
+     * get string index of nth child object of ASN.1 object refered by h, idx
+     * @name getDecendantIndexByNthList
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} h hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} currentIndex start string index of ASN.1 object
+     * @param {Array of Number} nthList array list of nth
+     * @return {Number} string index refered by nthList
+     * @since 1.1
+     * @example
+     * The "nthList" is a index list of structured ASN.1 object
+     * reference. Here is a sample structure and "nthList"s which
+     * refers each objects.
+     *
+     * SQUENCE               - [0]
+     *   SEQUENCE            - [0, 0]
+     *     IA5STRING 000     - [0, 0, 0]
+     *     UTF8STRING 001    - [0, 0, 1]
+     *   SET                 - [0, 1]
+     *     IA5STRING 010     - [0, 1, 0]
+     *     UTF8STRING 011    - [0, 1, 1]
+     */
+    this.getDecendantIndexByNthList = function(h, currentIndex, nthList) {
+       if (nthList.length == 0) {
+           return currentIndex;
+       }
+       var firstNth = nthList.shift();
+       var a = this.getPosArrayOfChildren_AtObj(h, currentIndex);
+       return this.getDecendantIndexByNthList(h, a[firstNth], nthList);
+    };
+
+    /**
+     * get hexadecimal string of ASN.1 TLV refered by current index and nth index list.
+     * @name getDecendantHexTLVByNthList
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} h hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} currentIndex start string index of ASN.1 object
+     * @param {Array of Number} nthList array list of nth
+     * @return {Number} hexadecimal string of ASN.1 TLV refered by nthList
+     * @since 1.1
+     */
+    this.getDecendantHexTLVByNthList = function(h, currentIndex, nthList) {
+       var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList);
+       return this.getHexOfTLV_AtObj(h, idx);
+    };
+
+    /**
+     * get hexadecimal string of ASN.1 V refered by current index and nth index list.
+     * @name getDecendantHexVByNthList
+     * @memberOf ASN1HEX
+     * @function
+     * @param {String} h hexadecimal string of ASN.1 DER encoded data
+     * @param {Number} currentIndex start string index of ASN.1 object
+     * @param {Array of Number} nthList array list of nth
+     * @return {Number} hexadecimal string of ASN.1 V refered by nthList
+     * @since 1.1
+     */
+    this.getDecendantHexVByNthList = function(h, currentIndex, nthList) {
+       var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList);
+       return this.getHexOfV_AtObj(h, idx);
+    };
+};
+
+/*
+ * @since asn1hex 1.1.4
+ */
+ASN1HEX.getVbyList = function(h, currentIndex, nthList, checkingTag) {
+    var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList);
+    if (idx === undefined) {
+       throw "can't find nthList object";
+    }
+    if (checkingTag !== undefined) {
+       if (h.substr(idx, 2) != checkingTag) {
+           throw "checking tag doesn't match: " + h.substr(idx,2) + "!=" + checkingTag;
+       }
+    }
+    return this.getHexOfV_AtObj(h, idx);
+};
+
diff --git a/js/base64.js b/js/base64.js
new file mode 100644 (file)
index 0000000..e8757ac
--- /dev/null
@@ -0,0 +1,74 @@
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
+var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+var b64pad="=";
+
+function hex2b64(h) {
+  var i;
+  var c;
+  var ret = "";
+  for(i = 0; i+3 <= h.length; i+=3) {
+    c = parseInt(h.substring(i,i+3),16);
+    ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
+  }
+  if(i+1 == h.length) {
+    c = parseInt(h.substring(i,i+1),16);
+    ret += b64map.charAt(c << 2);
+  }
+  else if(i+2 == h.length) {
+    c = parseInt(h.substring(i,i+2),16);
+    ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
+  }
+  if (b64pad) while((ret.length & 3) > 0) ret += b64pad;
+  return ret;
+}
+
+// convert a base64 string to hex
+function b64tohex(s) {
+  var ret = ""
+  var i;
+  var k = 0; // b64 state, 0-3
+  var slop;
+  var v;
+  for(i = 0; i < s.length; ++i) {
+    if(s.charAt(i) == b64pad) break;
+    v = b64map.indexOf(s.charAt(i));
+    if(v < 0) continue;
+    if(k == 0) {
+      ret += int2char(v >> 2);
+      slop = v & 3;
+      k = 1;
+    }
+    else if(k == 1) {
+      ret += int2char((slop << 2) | (v >> 4));
+      slop = v & 0xf;
+      k = 2;
+    }
+    else if(k == 2) {
+      ret += int2char(slop);
+      ret += int2char(v >> 2);
+      slop = v & 3;
+      k = 3;
+    }
+    else {
+      ret += int2char((slop << 2) | (v >> 4));
+      ret += int2char(v & 0xf);
+      k = 0;
+    }
+  }
+  if(k == 1)
+    ret += int2char(slop << 2);
+  return ret;
+}
+
+// convert a base64 string to a byte/number array
+function b64toBA(s) {
+  //piggyback on b64tohex for now, optimize later
+  var h = b64tohex(s);
+  var i;
+  var a = new Array();
+  for(i = 0; 2*i < h.length; ++i) {
+    a[i] = parseInt(h.substring(2*i,2*i+2),16);
+  }
+  return a;
+}
diff --git a/js/core.js b/js/core.js
new file mode 100644 (file)
index 0000000..996aa07
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+/**\r
+ * CryptoJS core components.\r
+ */\r
+var CryptoJS = CryptoJS || (function (Math, undefined) {\r
+    /**\r
+     * CryptoJS namespace.\r
+     */\r
+    var C = {};\r
+\r
+    /**\r
+     * Library namespace.\r
+     */\r
+    var C_lib = C.lib = {};\r
+\r
+    /**\r
+     * Base object for prototypal inheritance.\r
+     */\r
+    var Base = C_lib.Base = (function () {\r
+        function F() {}\r
+\r
+        return {\r
+            /**\r
+             * Creates a new object that inherits from this object.\r
+             *\r
+             * @param {Object} overrides Properties to copy into the new object.\r
+             *\r
+             * @return {Object} The new object.\r
+             *\r
+             * @static\r
+             *\r
+             * @example\r
+             *\r
+             *     var MyType = CryptoJS.lib.Base.extend({\r
+             *         field: 'value',\r
+             *\r
+             *         method: function () {\r
+             *         }\r
+             *     });\r
+             */\r
+            extend: function (overrides) {\r
+                // Spawn\r
+                F.prototype = this;\r
+                var subtype = new F();\r
+\r
+                // Augment\r
+                if (overrides) {\r
+                    subtype.mixIn(overrides);\r
+                }\r
+\r
+                // Create default initializer\r
+                if (!subtype.hasOwnProperty('init')) {\r
+                    subtype.init = function () {\r
+                        subtype.$super.init.apply(this, arguments);\r
+                    };\r
+                }\r
+\r
+                // Initializer's prototype is the subtype object\r
+                subtype.init.prototype = subtype;\r
+\r
+                // Reference supertype\r
+                subtype.$super = this;\r
+\r
+                return subtype;\r
+            },\r
+\r
+            /**\r
+             * Extends this object and runs the init method.\r
+             * Arguments to create() will be passed to init().\r
+             *\r
+             * @return {Object} The new object.\r
+             *\r
+             * @static\r
+             *\r
+             * @example\r
+             *\r
+             *     var instance = MyType.create();\r
+             */\r
+            create: function () {\r
+                var instance = this.extend();\r
+                instance.init.apply(instance, arguments);\r
+\r
+                return instance;\r
+            },\r
+\r
+            /**\r
+             * Initializes a newly created object.\r
+             * Override this method to add some logic when your objects are created.\r
+             *\r
+             * @example\r
+             *\r
+             *     var MyType = CryptoJS.lib.Base.extend({\r
+             *         init: function () {\r
+             *             // ...\r
+             *         }\r
+             *     });\r
+             */\r
+            init: function () {\r
+            },\r
+\r
+            /**\r
+             * Copies properties into this object.\r
+             *\r
+             * @param {Object} properties The properties to mix in.\r
+             *\r
+             * @example\r
+             *\r
+             *     MyType.mixIn({\r
+             *         field: 'value'\r
+             *     });\r
+             */\r
+            mixIn: function (properties) {\r
+                for (var propertyName in properties) {\r
+                    if (properties.hasOwnProperty(propertyName)) {\r
+                        this[propertyName] = properties[propertyName];\r
+                    }\r
+                }\r
+\r
+                // IE won't copy toString using the loop above\r
+                if (properties.hasOwnProperty('toString')) {\r
+                    this.toString = properties.toString;\r
+                }\r
+            },\r
+\r
+            /**\r
+             * Creates a copy of this object.\r
+             *\r
+             * @return {Object} The clone.\r
+             *\r
+             * @example\r
+             *\r
+             *     var clone = instance.clone();\r
+             */\r
+            clone: function () {\r
+                return this.init.prototype.extend(this);\r
+            }\r
+        };\r
+    }());\r
+\r
+    /**\r
+     * An array of 32-bit words.\r
+     *\r
+     * @property {Array} words The array of 32-bit words.\r
+     * @property {number} sigBytes The number of significant bytes in this word array.\r
+     */\r
+    var WordArray = C_lib.WordArray = Base.extend({\r
+        /**\r
+         * Initializes a newly created word array.\r
+         *\r
+         * @param {Array} words (Optional) An array of 32-bit words.\r
+         * @param {number} sigBytes (Optional) The number of significant bytes in the words.\r
+         *\r
+         * @example\r
+         *\r
+         *     var wordArray = CryptoJS.lib.WordArray.create();\r
+         *     var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);\r
+         *     var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);\r
+         */\r
+        init: function (words, sigBytes) {\r
+            words = this.words = words || [];\r
+\r
+            if (sigBytes != undefined) {\r
+                this.sigBytes = sigBytes;\r
+            } else {\r
+                this.sigBytes = words.length * 4;\r
+            }\r
+        },\r
+\r
+        /**\r
+         * Converts this word array to a string.\r
+         *\r
+         * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex\r
+         *\r
+         * @return {string} The stringified word array.\r
+         *\r
+         * @example\r
+         *\r
+         *     var string = wordArray + '';\r
+         *     var string = wordArray.toString();\r
+         *     var string = wordArray.toString(CryptoJS.enc.Utf8);\r
+         */\r
+        toString: function (encoder) {\r
+            return (encoder || Hex).stringify(this);\r
+        },\r
+\r
+        /**\r
+         * Concatenates a word array to this word array.\r
+         *\r
+         * @param {WordArray} wordArray The word array to append.\r
+         *\r
+         * @return {WordArray} This word array.\r
+         *\r
+         * @example\r
+         *\r
+         *     wordArray1.concat(wordArray2);\r
+         */\r
+        concat: function (wordArray) {\r
+            // Shortcuts\r
+            var thisWords = this.words;\r
+            var thatWords = wordArray.words;\r
+            var thisSigBytes = this.sigBytes;\r
+            var thatSigBytes = wordArray.sigBytes;\r
+\r
+            // Clamp excess bits\r
+            this.clamp();\r
+\r
+            // Concat\r
+            if (thisSigBytes % 4) {\r
+                // Copy one byte at a time\r
+                for (var i = 0; i < thatSigBytes; i++) {\r
+                    var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;\r
+                    thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8);\r
+                }\r
+            } else if (thatWords.length > 0xffff) {\r
+                // Copy one word at a time\r
+                for (var i = 0; i < thatSigBytes; i += 4) {\r
+                    thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2];\r
+                }\r
+            } else {\r
+                // Copy all words at once\r
+                thisWords.push.apply(thisWords, thatWords);\r
+            }\r
+            this.sigBytes += thatSigBytes;\r
+\r
+            // Chainable\r
+            return this;\r
+        },\r
+\r
+        /**\r
+         * Removes insignificant bits.\r
+         *\r
+         * @example\r
+         *\r
+         *     wordArray.clamp();\r
+         */\r
+        clamp: function () {\r
+            // Shortcuts\r
+            var words = this.words;\r
+            var sigBytes = this.sigBytes;\r
+\r
+            // Clamp\r
+            words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8);\r
+            words.length = Math.ceil(sigBytes / 4);\r
+        },\r
+\r
+        /**\r
+         * Creates a copy of this word array.\r
+         *\r
+         * @return {WordArray} The clone.\r
+         *\r
+         * @example\r
+         *\r
+         *     var clone = wordArray.clone();\r
+         */\r
+        clone: function () {\r
+            var clone = Base.clone.call(this);\r
+            clone.words = this.words.slice(0);\r
+\r
+            return clone;\r
+        },\r
+\r
+        /**\r
+         * Creates a word array filled with random bytes.\r
+         *\r
+         * @param {number} nBytes The number of random bytes to generate.\r
+         *\r
+         * @return {WordArray} The random word array.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var wordArray = CryptoJS.lib.WordArray.random(16);\r
+         */\r
+        random: function (nBytes) {\r
+            var words = [];\r
+            for (var i = 0; i < nBytes; i += 4) {\r
+                words.push((Math.random() * 0x100000000) | 0);\r
+            }\r
+\r
+            return new WordArray.init(words, nBytes);\r
+        }\r
+    });\r
+\r
+    /**\r
+     * Encoder namespace.\r
+     */\r
+    var C_enc = C.enc = {};\r
+\r
+    /**\r
+     * Hex encoding strategy.\r
+     */\r
+    var Hex = C_enc.Hex = {\r
+        /**\r
+         * Converts a word array to a hex string.\r
+         *\r
+         * @param {WordArray} wordArray The word array.\r
+         *\r
+         * @return {string} The hex string.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var hexString = CryptoJS.enc.Hex.stringify(wordArray);\r
+         */\r
+        stringify: function (wordArray) {\r
+            // Shortcuts\r
+            var words = wordArray.words;\r
+            var sigBytes = wordArray.sigBytes;\r
+\r
+            // Convert\r
+            var hexChars = [];\r
+            for (var i = 0; i < sigBytes; i++) {\r
+                var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;\r
+                hexChars.push((bite >>> 4).toString(16));\r
+                hexChars.push((bite & 0x0f).toString(16));\r
+            }\r
+\r
+            return hexChars.join('');\r
+        },\r
+\r
+        /**\r
+         * Converts a hex string to a word array.\r
+         *\r
+         * @param {string} hexStr The hex string.\r
+         *\r
+         * @return {WordArray} The word array.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var wordArray = CryptoJS.enc.Hex.parse(hexString);\r
+         */\r
+        parse: function (hexStr) {\r
+            // Shortcut\r
+            var hexStrLength = hexStr.length;\r
+\r
+            // Convert\r
+            var words = [];\r
+            for (var i = 0; i < hexStrLength; i += 2) {\r
+                words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);\r
+            }\r
+\r
+            return new WordArray.init(words, hexStrLength / 2);\r
+        }\r
+    };\r
+\r
+    /**\r
+     * Latin1 encoding strategy.\r
+     */\r
+    var Latin1 = C_enc.Latin1 = {\r
+        /**\r
+         * Converts a word array to a Latin1 string.\r
+         *\r
+         * @param {WordArray} wordArray The word array.\r
+         *\r
+         * @return {string} The Latin1 string.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var latin1String = CryptoJS.enc.Latin1.stringify(wordArray);\r
+         */\r
+        stringify: function (wordArray) {\r
+            // Shortcuts\r
+            var words = wordArray.words;\r
+            var sigBytes = wordArray.sigBytes;\r
+\r
+            // Convert\r
+            var latin1Chars = [];\r
+            for (var i = 0; i < sigBytes; i++) {\r
+                var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;\r
+                latin1Chars.push(String.fromCharCode(bite));\r
+            }\r
+\r
+            return latin1Chars.join('');\r
+        },\r
+\r
+        /**\r
+         * Converts a Latin1 string to a word array.\r
+         *\r
+         * @param {string} latin1Str The Latin1 string.\r
+         *\r
+         * @return {WordArray} The word array.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var wordArray = CryptoJS.enc.Latin1.parse(latin1String);\r
+         */\r
+        parse: function (latin1Str) {\r
+            // Shortcut\r
+            var latin1StrLength = latin1Str.length;\r
+\r
+            // Convert\r
+            var words = [];\r
+            for (var i = 0; i < latin1StrLength; i++) {\r
+                words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8);\r
+            }\r
+\r
+            return new WordArray.init(words, latin1StrLength);\r
+        }\r
+    };\r
+\r
+    /**\r
+     * UTF-8 encoding strategy.\r
+     */\r
+    var Utf8 = C_enc.Utf8 = {\r
+        /**\r
+         * Converts a word array to a UTF-8 string.\r
+         *\r
+         * @param {WordArray} wordArray The word array.\r
+         *\r
+         * @return {string} The UTF-8 string.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);\r
+         */\r
+        stringify: function (wordArray) {\r
+            try {\r
+                return decodeURIComponent(escape(Latin1.stringify(wordArray)));\r
+            } catch (e) {\r
+                throw new Error('Malformed UTF-8 data');\r
+            }\r
+        },\r
+\r
+        /**\r
+         * Converts a UTF-8 string to a word array.\r
+         *\r
+         * @param {string} utf8Str The UTF-8 string.\r
+         *\r
+         * @return {WordArray} The word array.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var wordArray = CryptoJS.enc.Utf8.parse(utf8String);\r
+         */\r
+        parse: function (utf8Str) {\r
+            return Latin1.parse(unescape(encodeURIComponent(utf8Str)));\r
+        }\r
+    };\r
+\r
+    /**\r
+     * Abstract buffered block algorithm template.\r
+     *\r
+     * The property blockSize must be implemented in a concrete subtype.\r
+     *\r
+     * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0\r
+     */\r
+    var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({\r
+        /**\r
+         * Resets this block algorithm's data buffer to its initial state.\r
+         *\r
+         * @example\r
+         *\r
+         *     bufferedBlockAlgorithm.reset();\r
+         */\r
+        reset: function () {\r
+            // Initial values\r
+            this._data = new WordArray.init();\r
+            this._nDataBytes = 0;\r
+        },\r
+\r
+        /**\r
+         * Adds new data to this block algorithm's buffer.\r
+         *\r
+         * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8.\r
+         *\r
+         * @example\r
+         *\r
+         *     bufferedBlockAlgorithm._append('data');\r
+         *     bufferedBlockAlgorithm._append(wordArray);\r
+         */\r
+        _append: function (data) {\r
+            // Convert string to WordArray, else assume WordArray already\r
+            if (typeof data == 'string') {\r
+                data = Utf8.parse(data);\r
+            }\r
+\r
+            // Append\r
+            this._data.concat(data);\r
+            this._nDataBytes += data.sigBytes;\r
+        },\r
+\r
+        /**\r
+         * Processes available data blocks.\r
+         *\r
+         * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.\r
+         *\r
+         * @param {boolean} doFlush Whether all blocks and partial blocks should be processed.\r
+         *\r
+         * @return {WordArray} The processed data.\r
+         *\r
+         * @example\r
+         *\r
+         *     var processedData = bufferedBlockAlgorithm._process();\r
+         *     var processedData = bufferedBlockAlgorithm._process(!!'flush');\r
+         */\r
+        _process: function (doFlush) {\r
+            // Shortcuts\r
+            var data = this._data;\r
+            var dataWords = data.words;\r
+            var dataSigBytes = data.sigBytes;\r
+            var blockSize = this.blockSize;\r
+            var blockSizeBytes = blockSize * 4;\r
+\r
+            // Count blocks ready\r
+            var nBlocksReady = dataSigBytes / blockSizeBytes;\r
+            if (doFlush) {\r
+                // Round up to include partial blocks\r
+                nBlocksReady = Math.ceil(nBlocksReady);\r
+            } else {\r
+                // Round down to include only full blocks,\r
+                // less the number of blocks that must remain in the buffer\r
+                nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);\r
+            }\r
+\r
+            // Count words ready\r
+            var nWordsReady = nBlocksReady * blockSize;\r
+\r
+            // Count bytes ready\r
+            var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);\r
+\r
+            // Process blocks\r
+            if (nWordsReady) {\r
+                for (var offset = 0; offset < nWordsReady; offset += blockSize) {\r
+                    // Perform concrete-algorithm logic\r
+                    this._doProcessBlock(dataWords, offset);\r
+                }\r
+\r
+                // Remove processed words\r
+                var processedWords = dataWords.splice(0, nWordsReady);\r
+                data.sigBytes -= nBytesReady;\r
+            }\r
+\r
+            // Return processed words\r
+            return new WordArray.init(processedWords, nBytesReady);\r
+        },\r
+\r
+        /**\r
+         * Creates a copy of this object.\r
+         *\r
+         * @return {Object} The clone.\r
+         *\r
+         * @example\r
+         *\r
+         *     var clone = bufferedBlockAlgorithm.clone();\r
+         */\r
+        clone: function () {\r
+            var clone = Base.clone.call(this);\r
+            clone._data = this._data.clone();\r
+\r
+            return clone;\r
+        },\r
+\r
+        _minBufferSize: 0\r
+    });\r
+\r
+    /**\r
+     * Abstract hasher template.\r
+     *\r
+     * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits)\r
+     */\r
+    var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({\r
+        /**\r
+         * Configuration options.\r
+         */\r
+        cfg: Base.extend(),\r
+\r
+        /**\r
+         * Initializes a newly created hasher.\r
+         *\r
+         * @param {Object} cfg (Optional) The configuration options to use for this hash computation.\r
+         *\r
+         * @example\r
+         *\r
+         *     var hasher = CryptoJS.algo.SHA256.create();\r
+         */\r
+        init: function (cfg) {\r
+            // Apply config defaults\r
+            this.cfg = this.cfg.extend(cfg);\r
+\r
+            // Set initial values\r
+            this.reset();\r
+        },\r
+\r
+        /**\r
+         * Resets this hasher to its initial state.\r
+         *\r
+         * @example\r
+         *\r
+         *     hasher.reset();\r
+         */\r
+        reset: function () {\r
+            // Reset data buffer\r
+            BufferedBlockAlgorithm.reset.call(this);\r
+\r
+            // Perform concrete-hasher logic\r
+            this._doReset();\r
+        },\r
+\r
+        /**\r
+         * Updates this hasher with a message.\r
+         *\r
+         * @param {WordArray|string} messageUpdate The message to append.\r
+         *\r
+         * @return {Hasher} This hasher.\r
+         *\r
+         * @example\r
+         *\r
+         *     hasher.update('message');\r
+         *     hasher.update(wordArray);\r
+         */\r
+        update: function (messageUpdate) {\r
+            // Append\r
+            this._append(messageUpdate);\r
+\r
+            // Update the hash\r
+            this._process();\r
+\r
+            // Chainable\r
+            return this;\r
+        },\r
+\r
+        /**\r
+         * Finalizes the hash computation.\r
+         * Note that the finalize operation is effectively a destructive, read-once operation.\r
+         *\r
+         * @param {WordArray|string} messageUpdate (Optional) A final message update.\r
+         *\r
+         * @return {WordArray} The hash.\r
+         *\r
+         * @example\r
+         *\r
+         *     var hash = hasher.finalize();\r
+         *     var hash = hasher.finalize('message');\r
+         *     var hash = hasher.finalize(wordArray);\r
+         */\r
+        finalize: function (messageUpdate) {\r
+            // Final message update\r
+            if (messageUpdate) {\r
+                this._append(messageUpdate);\r
+            }\r
+\r
+            // Perform concrete-hasher logic\r
+            var hash = this._doFinalize();\r
+\r
+            return hash;\r
+        },\r
+\r
+        blockSize: 512/32,\r
+\r
+        /**\r
+         * Creates a shortcut function to a hasher's object interface.\r
+         *\r
+         * @param {Hasher} hasher The hasher to create a helper for.\r
+         *\r
+         * @return {Function} The shortcut function.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);\r
+         */\r
+        _createHelper: function (hasher) {\r
+            return function (message, cfg) {\r
+                return new hasher.init(cfg).finalize(message);\r
+            };\r
+        },\r
+\r
+        /**\r
+         * Creates a shortcut function to the HMAC's object interface.\r
+         *\r
+         * @param {Hasher} hasher The hasher to use in this HMAC helper.\r
+         *\r
+         * @return {Function} The shortcut function.\r
+         *\r
+         * @static\r
+         *\r
+         * @example\r
+         *\r
+         *     var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);\r
+         */\r
+        _createHmacHelper: function (hasher) {\r
+            return function (message, key) {\r
+                return new C_algo.HMAC.init(hasher, key).finalize(message);\r
+            };\r
+        }\r
+    });\r
+\r
+    /**\r
+     * Algorithm namespace.\r
+     */\r
+    var C_algo = C.algo = {};\r
+\r
+    return C;\r
+}(Math));\r
diff --git a/js/crypto-1.1.js b/js/crypto-1.1.js
new file mode 100644 (file)
index 0000000..2282b97
--- /dev/null
@@ -0,0 +1,1136 @@
+/*! crypto-1.1.5.js (c) 2013 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+/*
+ * crypto.js - Cryptographic Algorithm Provider class
+ *
+ * Copyright (c) 2013 Kenji Urushima (kenji.urushima@gmail.com)
+ *
+ * This software is licensed under the terms of the MIT License.
+ * http://kjur.github.com/jsrsasign/license
+ *
+ * The above copyright and license notice shall be 
+ * included in all copies or substantial portions of the Software.
+ */
+
+/**
+ * @fileOverview
+ * @name crypto-1.1.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version 1.1.5 (2013-Oct-06)
+ * @since jsrsasign 2.2
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ */
+
+/** 
+ * kjur's class library name space
+ * @name KJUR
+ * @namespace kjur's class library name space
+ */
+if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
+/**
+ * kjur's cryptographic algorithm provider library name space
+ * <p>
+ * This namespace privides following crytpgrahic classes.
+ * <ul>
+ * <li>{@link KJUR.crypto.MessageDigest} - Java JCE(cryptograhic extension) style MessageDigest class</li>
+ * <li>{@link KJUR.crypto.Signature} - Java JCE(cryptograhic extension) style Signature class</li>
+ * <li>{@link KJUR.crypto.Util} - cryptographic utility functions and properties</li>
+ * </ul>
+ * NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2.
+ * </p>
+ * @name KJUR.crypto
+ * @namespace
+ */
+if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) KJUR.crypto = {};
+
+/**
+ * static object for cryptographic function utilities
+ * @name KJUR.crypto.Util
+ * @class static object for cryptographic function utilities
+ * @property {Array} DIGESTINFOHEAD PKCS#1 DigestInfo heading hexadecimal bytes for each hash algorithms
+ * @property {Array} DEFAULTPROVIDER associative array of default provider name for each hash and signature algorithms
+ * @description
+ */
+KJUR.crypto.Util = new function() {
+    this.DIGESTINFOHEAD = {
+       'sha1':      "3021300906052b0e03021a05000414",
+        'sha224':    "302d300d06096086480165030402040500041c",
+       'sha256':    "3031300d060960864801650304020105000420",
+       'sha384':    "3041300d060960864801650304020205000430",
+       'sha512':    "3051300d060960864801650304020305000440",
+       'md2':       "3020300c06082a864886f70d020205000410",
+       'md5':       "3020300c06082a864886f70d020505000410",
+       'ripemd160': "3021300906052b2403020105000414"
+    };
+
+    /*
+     * @since crypto 1.1.1
+     */
+    this.DEFAULTPROVIDER = {
+       'md5':                  'cryptojs',
+       'sha1':                 'cryptojs',
+       'sha224':               'cryptojs',
+       'sha256':               'cryptojs',
+       'sha384':               'cryptojs',
+       'sha512':               'cryptojs',
+       'ripemd160':            'cryptojs',
+       'hmacmd5':              'cryptojs',
+       'hmacsha1':             'cryptojs',
+       'hmacsha224':           'cryptojs',
+       'hmacsha256':           'cryptojs',
+       'hmacsha384':           'cryptojs',
+       'hmacsha512':           'cryptojs',
+       'hmacripemd160':        'cryptojs',
+
+       'MD5withRSA':           'cryptojs/jsrsa',
+       'SHA1withRSA':          'cryptojs/jsrsa',
+       'SHA224withRSA':        'cryptojs/jsrsa',
+       'SHA256withRSA':        'cryptojs/jsrsa',
+       'SHA384withRSA':        'cryptojs/jsrsa',
+       'SHA512withRSA':        'cryptojs/jsrsa',
+       'RIPEMD160withRSA':     'cryptojs/jsrsa',
+
+       'MD5withECDSA':         'cryptojs/jsrsa',
+       'SHA1withECDSA':        'cryptojs/jsrsa',
+       'SHA224withECDSA':      'cryptojs/jsrsa',
+       'SHA256withECDSA':      'cryptojs/jsrsa',
+       'SHA384withECDSA':      'cryptojs/jsrsa',
+       'SHA512withECDSA':      'cryptojs/jsrsa',
+       'RIPEMD160withECDSA':   'cryptojs/jsrsa',
+
+       'SHA1withDSA':          'cryptojs/jsrsa',
+       'SHA224withDSA':        'cryptojs/jsrsa',
+       'SHA256withDSA':        'cryptojs/jsrsa',
+
+       'MD5withRSAandMGF1':            'cryptojs/jsrsa',
+       'SHA1withRSAandMGF1':           'cryptojs/jsrsa',
+       'SHA224withRSAandMGF1':         'cryptojs/jsrsa',
+       'SHA256withRSAandMGF1':         'cryptojs/jsrsa',
+       'SHA384withRSAandMGF1':         'cryptojs/jsrsa',
+       'SHA512withRSAandMGF1':         'cryptojs/jsrsa',
+       'RIPEMD160withRSAandMGF1':      'cryptojs/jsrsa'
+    };
+
+    /*
+     * @since crypto 1.1.2
+     */
+    this.CRYPTOJSMESSAGEDIGESTNAME = {
+       'md5':          'CryptoJS.algo.MD5',
+       'sha1':         'CryptoJS.algo.SHA1',
+       'sha224':       'CryptoJS.algo.SHA224',
+       'sha256':       'CryptoJS.algo.SHA256',
+       'sha384':       'CryptoJS.algo.SHA384',
+       'sha512':       'CryptoJS.algo.SHA512',
+       'ripemd160':    'CryptoJS.algo.RIPEMD160'
+    };
+
+    /**
+     * get hexadecimal DigestInfo
+     * @name getDigestInfoHex
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} hHash hexadecimal hash value
+     * @param {String} alg hash algorithm name (ex. 'sha1')
+     * @return {String} hexadecimal string DigestInfo ASN.1 structure
+     */
+    this.getDigestInfoHex = function(hHash, alg) {
+       if (typeof this.DIGESTINFOHEAD[alg] == "undefined")
+           throw "alg not supported in Util.DIGESTINFOHEAD: " + alg;
+       return this.DIGESTINFOHEAD[alg] + hHash;
+    };
+
+    /**
+     * get PKCS#1 padded hexadecimal DigestInfo
+     * @name getPaddedDigestInfoHex
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} hHash hexadecimal hash value of message to be signed
+     * @param {String} alg hash algorithm name (ex. 'sha1')
+     * @param {Integer} keySize key bit length (ex. 1024)
+     * @return {String} hexadecimal string of PKCS#1 padded DigestInfo
+     */
+    this.getPaddedDigestInfoHex = function(hHash, alg, keySize) {
+       var hDigestInfo = this.getDigestInfoHex(hHash, alg);
+       var pmStrLen = keySize / 4; // minimum PM length
+
+       if (hDigestInfo.length + 22 > pmStrLen) // len(0001+ff(*8)+00+hDigestInfo)=22
+           throw "key is too short for SigAlg: keylen=" + keySize + "," + alg;
+
+       var hHead = "0001";
+       var hTail = "00" + hDigestInfo;
+       var hMid = "";
+       var fLen = pmStrLen - hHead.length - hTail.length;
+       for (var i = 0; i < fLen; i += 2) {
+           hMid += "ff";
+       }
+       var hPaddedMessage = hHead + hMid + hTail;
+       return hPaddedMessage;
+    };
+
+    /**
+     * get hexadecimal hash of string with specified algorithm
+     * @name hashString
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} s input string to be hashed
+     * @param {String} alg hash algorithm name
+     * @return {String} hexadecimal string of hash value
+     * @since 1.1.1
+     */
+    this.hashString = function(s, alg) {
+        var md = new KJUR.crypto.MessageDigest({'alg': alg});
+        return md.digestString(s);
+    };
+
+    /**
+     * get hexadecimal hash of hexadecimal string with specified algorithm
+     * @name hashHex
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} sHex input hexadecimal string to be hashed
+     * @param {String} alg hash algorithm name
+     * @return {String} hexadecimal string of hash value
+     * @since 1.1.1
+     */
+    this.hashHex = function(sHex, alg) {
+        var md = new KJUR.crypto.MessageDigest({'alg': alg});
+        return md.digestHex(sHex);
+    };
+
+    /**
+     * get hexadecimal SHA1 hash of string
+     * @name sha1
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} s input string to be hashed
+     * @return {String} hexadecimal string of hash value
+     * @since 1.0.3
+     */
+    this.sha1 = function(s) {
+        var md = new KJUR.crypto.MessageDigest({'alg':'sha1', 'prov':'cryptojs'});
+        return md.digestString(s);
+    };
+
+    /**
+     * get hexadecimal SHA256 hash of string
+     * @name sha256
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} s input string to be hashed
+     * @return {String} hexadecimal string of hash value
+     * @since 1.0.3
+     */
+    this.sha256 = function(s) {
+        var md = new KJUR.crypto.MessageDigest({'alg':'sha256', 'prov':'cryptojs'});
+        return md.digestString(s);
+    };
+
+    this.sha256Hex = function(s) {
+        var md = new KJUR.crypto.MessageDigest({'alg':'sha256', 'prov':'cryptojs'});
+        return md.digestHex(s);
+    };
+
+    /**
+     * get hexadecimal SHA512 hash of string
+     * @name sha512
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} s input string to be hashed
+     * @return {String} hexadecimal string of hash value
+     * @since 1.0.3
+     */
+    this.sha512 = function(s) {
+        var md = new KJUR.crypto.MessageDigest({'alg':'sha512', 'prov':'cryptojs'});
+        return md.digestString(s);
+    };
+
+    this.sha512Hex = function(s) {
+        var md = new KJUR.crypto.MessageDigest({'alg':'sha512', 'prov':'cryptojs'});
+        return md.digestHex(s);
+    };
+
+    /**
+     * get hexadecimal MD5 hash of string
+     * @name md5
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} s input string to be hashed
+     * @return {String} hexadecimal string of hash value
+     * @since 1.0.3
+     */
+    this.md5 = function(s) {
+        var md = new KJUR.crypto.MessageDigest({'alg':'md5', 'prov':'cryptojs'});
+        return md.digestString(s);
+    };
+
+    /**
+     * get hexadecimal RIPEMD160 hash of string
+     * @name ripemd160
+     * @memberOf KJUR.crypto.Util
+     * @function
+     * @param {String} s input string to be hashed
+     * @return {String} hexadecimal string of hash value
+     * @since 1.0.3
+     */
+    this.ripemd160 = function(s) {
+        var md = new KJUR.crypto.MessageDigest({'alg':'ripemd160', 'prov':'cryptojs'});
+        return md.digestString(s);
+    };
+
+    /*
+     * @since 1.1.2
+     */
+    this.getCryptoJSMDByName = function(s) {
+       
+    };
+};
+
+/**
+ * MessageDigest class which is very similar to java.security.MessageDigest class
+ * @name KJUR.crypto.MessageDigest
+ * @class MessageDigest class which is very similar to java.security.MessageDigest class
+ * @param {Array} params parameters for constructor
+ * @description
+ * <br/>
+ * Currently this supports following algorithm and providers combination:
+ * <ul>
+ * <li>md5 - cryptojs</li>
+ * <li>sha1 - cryptojs</li>
+ * <li>sha224 - cryptojs</li>
+ * <li>sha256 - cryptojs</li>
+ * <li>sha384 - cryptojs</li>
+ * <li>sha512 - cryptojs</li>
+ * <li>ripemd160 - cryptojs</li>
+ * <li>sha256 - sjcl (NEW from crypto.js 1.0.4)</li>
+ * </ul>
+ * @example
+ * // CryptoJS provider sample
+ * &lt;script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/core.js"&gt;&lt;/script&gt;
+ * &lt;script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/sha1.js"&gt;&lt;/script&gt;
+ * &lt;script src="crypto-1.0.js"&gt;&lt;/script&gt;
+ * var md = new KJUR.crypto.MessageDigest({alg: "sha1", prov: "cryptojs"});
+ * md.updateString('aaa')
+ * var mdHex = md.digest()
+ *
+ * // SJCL(Stanford JavaScript Crypto Library) provider sample
+ * &lt;script src="http://bitwiseshiftleft.github.io/sjcl/sjcl.js"&gt;&lt;/script&gt;
+ * &lt;script src="crypto-1.0.js"&gt;&lt;/script&gt;
+ * var md = new KJUR.crypto.MessageDigest({alg: "sha256", prov: "sjcl"}); // sjcl supports sha256 only
+ * md.updateString('aaa')
+ * var mdHex = md.digest()
+ */
+KJUR.crypto.MessageDigest = function(params) {
+    var md = null;
+    var algName = null;
+    var provName = null;
+
+    /**
+     * set hash algorithm and provider
+     * @name setAlgAndProvider
+     * @memberOf KJUR.crypto.MessageDigest
+     * @function
+     * @param {String} alg hash algorithm name
+     * @param {String} prov provider name
+     * @description
+     * @example
+     * // for SHA1
+     * md.setAlgAndProvider('sha1', 'cryptojs');
+     * // for RIPEMD160
+     * md.setAlgAndProvider('ripemd160', 'cryptojs');
+     */
+    this.setAlgAndProvider = function(alg, prov) {
+       if (alg != null && prov === undefined) prov = KJUR.crypto.Util.DEFAULTPROVIDER[alg];
+
+       // for cryptojs
+       if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(alg) != -1 &&
+           prov == 'cryptojs') {
+           try {
+               this.md = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[alg]).create();
+           } catch (ex) {
+               throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex;
+           }
+           this.updateString = function(str) {
+               this.md.update(str);
+           };
+           this.updateHex = function(hex) {
+               var wHex = CryptoJS.enc.Hex.parse(hex);
+               this.md.update(wHex);
+           };
+           this.digest = function() {
+               var hash = this.md.finalize();
+               return hash.toString(CryptoJS.enc.Hex);
+           };
+           this.digestString = function(str) {
+               this.updateString(str);
+               return this.digest();
+           };
+           this.digestHex = function(hex) {
+               this.updateHex(hex);
+               return this.digest();
+           };
+       }
+       if (':sha256:'.indexOf(alg) != -1 &&
+           prov == 'sjcl') {
+           try {
+               this.md = new sjcl.hash.sha256();
+           } catch (ex) {
+               throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex;
+           }
+           this.updateString = function(str) {
+               this.md.update(str);
+           };
+           this.updateHex = function(hex) {
+               var baHex = sjcl.codec.hex.toBits(hex);
+               this.md.update(baHex);
+           };
+           this.digest = function() {
+               var hash = this.md.finalize();
+               return sjcl.codec.hex.fromBits(hash);
+           };
+           this.digestString = function(str) {
+               this.updateString(str);
+               return this.digest();
+           };
+           this.digestHex = function(hex) {
+               this.updateHex(hex);
+               return this.digest();
+           };
+       }
+    };
+
+    /**
+     * update digest by specified string
+     * @name updateString
+     * @memberOf KJUR.crypto.MessageDigest
+     * @function
+     * @param {String} str string to update
+     * @description
+     * @example
+     * md.updateString('New York');
+     */
+    this.updateString = function(str) {
+       throw "updateString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName;
+    };
+
+    /**
+     * update digest by specified hexadecimal string
+     * @name updateHex
+     * @memberOf KJUR.crypto.MessageDigest
+     * @function
+     * @param {String} hex hexadecimal string to update
+     * @description
+     * @example
+     * md.updateHex('0afe36');
+     */
+    this.updateHex = function(hex) {
+       throw "updateHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName;
+    };
+
+    /**
+     * completes hash calculation and returns hash result
+     * @name digest
+     * @memberOf KJUR.crypto.MessageDigest
+     * @function
+     * @description
+     * @example
+     * md.digest()
+     */
+    this.digest = function() {
+       throw "digest() not supported for this alg/prov: " + this.algName + "/" + this.provName;
+    };
+
+    /**
+     * performs final update on the digest using string, then completes the digest computation
+     * @name digestString
+     * @memberOf KJUR.crypto.MessageDigest
+     * @function
+     * @param {String} str string to final update
+     * @description
+     * @example
+     * md.digestString('aaa')
+     */
+    this.digestString = function(str) {
+       throw "digestString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName;
+    };
+
+    /**
+     * performs final update on the digest using hexadecimal string, then completes the digest computation
+     * @name digestHex
+     * @memberOf KJUR.crypto.MessageDigest
+     * @function
+     * @param {String} hex hexadecimal string to final update
+     * @description
+     * @example
+     * md.digestHex('0f2abd')
+     */
+    this.digestHex = function(hex) {
+       throw "digestHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName;
+    };
+
+    if (params !== undefined) {
+       if (params['alg'] !== undefined) {
+           this.algName = params['alg'];
+           if (params['prov'] === undefined)
+               this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
+           this.setAlgAndProvider(this.algName, this.provName);
+       }
+    }
+};
+
+/**
+ * Mac(Message Authentication Code) class which is very similar to java.security.Mac class 
+ * @name KJUR.crypto.Mac
+ * @class Mac class which is very similar to java.security.Mac class
+ * @param {Array} params parameters for constructor
+ * @description
+ * <br/>
+ * Currently this supports following algorithm and providers combination:
+ * <ul>
+ * <li>hmacmd5 - cryptojs</li>
+ * <li>hmacsha1 - cryptojs</li>
+ * <li>hmacsha224 - cryptojs</li>
+ * <li>hmacsha256 - cryptojs</li>
+ * <li>hmacsha384 - cryptojs</li>
+ * <li>hmacsha512 - cryptojs</li>
+ * </ul>
+ * NOTE: HmacSHA224 and HmacSHA384 issue was fixed since jsrsasign 4.1.4.
+ * Please use 'ext/cryptojs-312-core-fix*.js' instead of 'core.js' of original CryptoJS
+ * to avoid those issue.
+ * @example
+ * var mac = new KJUR.crypto.Mac({alg: "HmacSHA1", prov: "cryptojs", "pass": "pass"});
+ * mac.updateString('aaa')
+ * var macHex = md.doFinal()
+ */
+KJUR.crypto.Mac = function(params) {
+    var mac = null;
+    var pass = null;
+    var algName = null;
+    var provName = null;
+    var algProv = null;
+
+    this.setAlgAndProvider = function(alg, prov) {
+       if (alg == null) alg = "hmacsha1";
+
+       alg = alg.toLowerCase();
+        if (alg.substr(0, 4) != "hmac") {
+           throw "setAlgAndProvider unsupported HMAC alg: " + alg;
+       }
+
+       if (prov === undefined) prov = KJUR.crypto.Util.DEFAULTPROVIDER[alg];
+       this.algProv = alg + "/" + prov;
+
+       var hashAlg = alg.substr(4);
+
+       // for cryptojs
+       if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(hashAlg) != -1 &&
+           prov == 'cryptojs') {
+           try {
+               var mdObj = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[hashAlg]);
+               this.mac = CryptoJS.algo.HMAC.create(mdObj, this.pass);
+           } catch (ex) {
+               throw "setAlgAndProvider hash alg set fail hashAlg=" + hashAlg + "/" + ex;
+           }
+           this.updateString = function(str) {
+               this.mac.update(str);
+           };
+           this.updateHex = function(hex) {
+               var wHex = CryptoJS.enc.Hex.parse(hex);
+               this.mac.update(wHex);
+           };
+           this.doFinal = function() {
+               var hash = this.mac.finalize();
+               return hash.toString(CryptoJS.enc.Hex);
+           };
+           this.doFinalString = function(str) {
+               this.updateString(str);
+               return this.doFinal();
+           };
+           this.doFinalHex = function(hex) {
+               this.updateHex(hex);
+               return this.doFinal();
+           };
+       }
+    };
+
+    /**
+     * update digest by specified string
+     * @name updateString
+     * @memberOf KJUR.crypto.Mac
+     * @function
+     * @param {String} str string to update
+     * @description
+     * @example
+     * md.updateString('New York');
+     */
+    this.updateString = function(str) {
+       throw "updateString(str) not supported for this alg/prov: " + this.algProv;
+    };
+
+    /**
+     * update digest by specified hexadecimal string
+     * @name updateHex
+     * @memberOf KJUR.crypto.Mac
+     * @function
+     * @param {String} hex hexadecimal string to update
+     * @description
+     * @example
+     * md.updateHex('0afe36');
+     */
+    this.updateHex = function(hex) {
+       throw "updateHex(hex) not supported for this alg/prov: " + this.algProv;
+    };
+
+    /**
+     * completes hash calculation and returns hash result
+     * @name doFinal
+     * @memberOf KJUR.crypto.Mac
+     * @function
+     * @description
+     * @example
+     * md.digest()
+     */
+    this.doFinal = function() {
+       throw "digest() not supported for this alg/prov: " + this.algProv;
+    };
+
+    /**
+     * performs final update on the digest using string, then completes the digest computation
+     * @name doFinalString
+     * @memberOf KJUR.crypto.Mac
+     * @function
+     * @param {String} str string to final update
+     * @description
+     * @example
+     * md.digestString('aaa')
+     */
+    this.doFinalString = function(str) {
+       throw "digestString(str) not supported for this alg/prov: " + this.algProv;
+    };
+
+    /**
+     * performs final update on the digest using hexadecimal string, 
+     * then completes the digest computation
+     * @name doFinalHex
+     * @memberOf KJUR.crypto.Mac
+     * @function
+     * @param {String} hex hexadecimal string to final update
+     * @description
+     * @example
+     * md.digestHex('0f2abd')
+     */
+    this.doFinalHex = function(hex) {
+       throw "digestHex(hex) not supported for this alg/prov: " + this.algProv;
+    };
+
+    if (params !== undefined) {
+       if (params['pass'] !== undefined) {
+           this.pass = params['pass'];
+       }
+       if (params['alg'] !== undefined) {
+           this.algName = params['alg'];
+           if (params['prov'] === undefined)
+               this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
+           this.setAlgAndProvider(this.algName, this.provName);
+       }
+    }
+};
+
+/**
+ * Signature class which is very similar to java.security.Signature class
+ * @name KJUR.crypto.Signature
+ * @class Signature class which is very similar to java.security.Signature class
+ * @param {Array} params parameters for constructor
+ * @property {String} state Current state of this signature object whether 'SIGN', 'VERIFY' or null
+ * @description
+ * <br/>
+ * As for params of constructor's argument, it can be specify following attributes:
+ * <ul>
+ * <li>alg - signature algorithm name (ex. {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160}with{RSA,ECDSA,DSA})</li>
+ * <li>provider - currently 'cryptojs/jsrsa' only</li>
+ * </ul>
+ * <h4>SUPPORTED ALGORITHMS AND PROVIDERS</h4>
+ * This Signature class supports following signature algorithm and provider names:
+ * <ul>
+ * <li>MD5withRSA - cryptojs/jsrsa</li>
+ * <li>SHA1withRSA - cryptojs/jsrsa</li>
+ * <li>SHA224withRSA - cryptojs/jsrsa</li>
+ * <li>SHA256withRSA - cryptojs/jsrsa</li>
+ * <li>SHA384withRSA - cryptojs/jsrsa</li>
+ * <li>SHA512withRSA - cryptojs/jsrsa</li>
+ * <li>RIPEMD160withRSA - cryptojs/jsrsa</li>
+ * <li>MD5withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA1withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA224withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA256withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA384withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA512withECDSA - cryptojs/jsrsa</li>
+ * <li>RIPEMD160withECDSA - cryptojs/jsrsa</li>
+ * <li>MD5withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA1withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA224withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA256withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA384withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA512withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>RIPEMD160withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA1withDSA - cryptojs/jsrsa</li>
+ * <li>SHA224withDSA - cryptojs/jsrsa</li>
+ * <li>SHA256withDSA - cryptojs/jsrsa</li>
+ * </ul>
+ * Here are supported elliptic cryptographic curve names and their aliases for ECDSA:
+ * <ul>
+ * <li>secp256k1</li>
+ * <li>secp256r1, NIST P-256, P-256, prime256v1</li>
+ * <li>secp384r1, NIST P-384, P-384</li>
+ * </ul>
+ * NOTE1: DSA signing algorithm is also supported since crypto 1.1.5.
+ * <h4>EXAMPLES</h4>
+ * @example
+ * // RSA signature generation
+ * var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA"});
+ * sig.init(prvKeyPEM);
+ * sig.updateString('aaa');
+ * var hSigVal = sig.sign();
+ *
+ * // DSA signature validation
+ * var sig2 = new KJUR.crypto.Signature({"alg": "SHA1withDSA"});
+ * sig2.init(certPEM);
+ * sig.updateString('aaa');
+ * var isValid = sig2.verify(hSigVal);
+ * 
+ * // ECDSA signing
+ * var sig = new KJUR.crypto.Signature({'alg':'SHA1withECDSA'});
+ * sig.init(prvKeyPEM);
+ * sig.updateString('aaa');
+ * var sigValueHex = sig.sign();
+ *
+ * // ECDSA verifying
+ * var sig2 = new KJUR.crypto.Signature({'alg':'SHA1withECDSA'});
+ * sig.init(certPEM);
+ * sig.updateString('aaa');
+ * var isValid = sig.verify(sigValueHex);
+ */
+KJUR.crypto.Signature = function(params) {
+    var prvKey = null; // RSAKey/KJUR.crypto.{ECDSA,DSA} object for signing
+    var pubKey = null; // RSAKey/KJUR.crypto.{ECDSA,DSA} object for verifying
+
+    var md = null; // KJUR.crypto.MessageDigest object
+    var sig = null;
+    var algName = null;
+    var provName = null;
+    var algProvName = null;
+    var mdAlgName = null;
+    var pubkeyAlgName = null;  // rsa,ecdsa,rsaandmgf1(=rsapss)
+    var state = null;
+    var pssSaltLen = -1;
+    var initParams = null;
+
+    var sHashHex = null; // hex hash value for hex
+    var hDigestInfo = null;
+    var hPaddedDigestInfo = null;
+    var hSign = null;
+
+    this._setAlgNames = function() {
+       if (this.algName.match(/^(.+)with(.+)$/)) {
+           this.mdAlgName = RegExp.$1.toLowerCase();
+           this.pubkeyAlgName = RegExp.$2.toLowerCase();
+       }
+    };
+
+    this._zeroPaddingOfSignature = function(hex, bitLength) {
+       var s = "";
+       var nZero = bitLength / 4 - hex.length;
+       for (var i = 0; i < nZero; i++) {
+           s = s + "0";
+       }
+       return s + hex;
+    };
+
+    /**
+     * set signature algorithm and provider
+     * @name setAlgAndProvider
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {String} alg signature algorithm name
+     * @param {String} prov provider name
+     * @description
+     * @example
+     * md.setAlgAndProvider('SHA1withRSA', 'cryptojs/jsrsa');
+     */
+    this.setAlgAndProvider = function(alg, prov) {
+       this._setAlgNames();
+       if (prov != 'cryptojs/jsrsa')
+           throw "provider not supported: " + prov;
+
+       if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(this.mdAlgName) != -1) {
+           try {
+               this.md = new KJUR.crypto.MessageDigest({'alg':this.mdAlgName});
+           } catch (ex) {
+               throw "setAlgAndProvider hash alg set fail alg=" +
+                      this.mdAlgName + "/" + ex;
+           }
+
+           this.init = function(keyparam, pass) {
+               var keyObj = null;
+               try {
+                   if (pass === undefined) {
+                       keyObj = KEYUTIL.getKey(keyparam);
+                   } else {
+                       keyObj = KEYUTIL.getKey(keyparam, pass);
+                   }
+               } catch (ex) {
+                   throw "init failed:" + ex;
+               }
+
+               if (keyObj.isPrivate === true) {
+                   this.prvKey = keyObj;
+                   this.state = "SIGN";
+               } else if (keyObj.isPublic === true) {
+                   this.pubKey = keyObj;
+                   this.state = "VERIFY";
+               } else {
+                   throw "init failed.:" + keyObj;
+               }
+           };
+
+           this.initSign = function(params) {
+               if (typeof params['ecprvhex'] == 'string' &&
+                    typeof params['eccurvename'] == 'string') {
+                   this.ecprvhex = params['ecprvhex'];
+                   this.eccurvename = params['eccurvename'];
+               } else {
+                   this.prvKey = params;
+               }
+               this.state = "SIGN";
+           };
+
+           this.initVerifyByPublicKey = function(params) {
+               if (typeof params['ecpubhex'] == 'string' &&
+                   typeof params['eccurvename'] == 'string') {
+                   this.ecpubhex = params['ecpubhex'];
+                   this.eccurvename = params['eccurvename'];
+               } else if (params instanceof KJUR.crypto.ECDSA) {
+                   this.pubKey = params;
+               } else if (params instanceof RSAKey) {
+                   this.pubKey = params;
+               }
+               this.state = "VERIFY";
+           };
+
+           this.initVerifyByCertificatePEM = function(certPEM) {
+               var x509 = new X509();
+               x509.readCertPEM(certPEM);
+               this.pubKey = x509.subjectPublicKeyRSA;
+               this.state = "VERIFY";
+           };
+
+           this.updateString = function(str) {
+               this.md.updateString(str);
+           };
+           this.updateHex = function(hex) {
+               this.md.updateHex(hex);
+           };
+
+           this.sign = function() {
+               this.sHashHex = this.md.digest();
+               if (typeof this.ecprvhex != "undefined" &&
+                   typeof this.eccurvename != "undefined") {
+                   var ec = new KJUR.crypto.ECDSA({'curve': this.eccurvename});
+                   this.hSign = ec.signHex(this.sHashHex, this.ecprvhex);
+               } else if (this.pubkeyAlgName == "rsaandmgf1") {
+                   this.hSign = this.prvKey.signWithMessageHashPSS(this.sHashHex,
+                                                                   this.mdAlgName,
+                                                                   this.pssSaltLen);
+               } else if (this.pubkeyAlgName == "rsa") {
+                   this.hSign = this.prvKey.signWithMessageHash(this.sHashHex,
+                                                                this.mdAlgName);
+               } else if (this.prvKey instanceof KJUR.crypto.ECDSA) {
+                   this.hSign = this.prvKey.signWithMessageHash(this.sHashHex);
+               } else if (this.prvKey instanceof KJUR.crypto.DSA) {
+                   this.hSign = this.prvKey.signWithMessageHash(this.sHashHex);
+               } else {
+                   throw "Signature: unsupported public key alg: " + this.pubkeyAlgName;
+               }
+               return this.hSign;
+           };
+           this.signString = function(str) {
+               this.updateString(str);
+               this.sign();
+           };
+           this.signHex = function(hex) {
+               this.updateHex(hex);
+               this.sign();
+           };
+           this.verify = function(hSigVal) {
+               this.sHashHex = this.md.digest();
+               if (typeof this.ecpubhex != "undefined" &&
+                   typeof this.eccurvename != "undefined") {
+                   var ec = new KJUR.crypto.ECDSA({curve: this.eccurvename});
+                   return ec.verifyHex(this.sHashHex, hSigVal, this.ecpubhex);
+               } else if (this.pubkeyAlgName == "rsaandmgf1") {
+                   return this.pubKey.verifyWithMessageHashPSS(this.sHashHex, hSigVal, 
+                                                               this.mdAlgName,
+                                                               this.pssSaltLen);
+               } else if (this.pubkeyAlgName == "rsa") {
+                   return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal);
+               } else if (this.pubKey instanceof KJUR.crypto.ECDSA) {
+                   return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal);
+               } else if (this.pubKey instanceof KJUR.crypto.DSA) {
+                   return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal);
+               } else {
+                   throw "Signature: unsupported public key alg: " + this.pubkeyAlgName;
+               }
+           };
+       }
+    };
+
+    /**
+     * Initialize this object for signing or verifying depends on key
+     * @name init
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {Object} key specifying public or private key as plain/encrypted PKCS#5/8 PEM file, certificate PEM or {@link RSAKey}, {@link KJUR.crypto.DSA} or {@link KJUR.crypto.ECDSA} object
+     * @param {String} pass (OPTION) passcode for encrypted private key
+     * @since crypto 1.1.3
+     * @description
+     * This method is very useful initialize method for Signature class since
+     * you just specify key then this method will automatically initialize it
+     * using {@link KEYUTIL.getKey} method.
+     * As for 'key',  following argument type are supported:
+     * <h5>signing</h5>
+     * <ul>
+     * <li>PEM formatted PKCS#8 encrypted RSA/ECDSA private key concluding "BEGIN ENCRYPTED PRIVATE KEY"</li>
+     * <li>PEM formatted PKCS#5 encrypted RSA/DSA private key concluding "BEGIN RSA/DSA PRIVATE KEY" and ",ENCRYPTED"</li>
+     * <li>PEM formatted PKCS#8 plain RSA/ECDSA private key concluding "BEGIN PRIVATE KEY"</li>
+     * <li>PEM formatted PKCS#5 plain RSA/DSA private key concluding "BEGIN RSA/DSA PRIVATE KEY" without ",ENCRYPTED"</li>
+     * <li>RSAKey object of private key</li>
+     * <li>KJUR.crypto.ECDSA object of private key</li>
+     * <li>KJUR.crypto.DSA object of private key</li>
+     * </ul>
+     * <h5>verification</h5>
+     * <ul>
+     * <li>PEM formatted PKCS#8 RSA/EC/DSA public key concluding "BEGIN PUBLIC KEY"</li>
+     * <li>PEM formatted X.509 certificate with RSA/EC/DSA public key concluding
+     *     "BEGIN CERTIFICATE", "BEGIN X509 CERTIFICATE" or "BEGIN TRUSTED CERTIFICATE".</li>
+     * <li>RSAKey object of public key</li>
+     * <li>KJUR.crypto.ECDSA object of public key</li>
+     * <li>KJUR.crypto.DSA object of public key</li>
+     * </ul>
+     * @example
+     * sig.init(sCertPEM)
+     */
+    this.init = function(key, pass) {
+       throw "init(key, pass) not supported for this alg:prov=" +
+             this.algProvName;
+    };
+
+    /**
+     * Initialize this object for verifying with a public key
+     * @name initVerifyByPublicKey
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {Object} param RSAKey object of public key or associative array for ECDSA
+     * @since 1.0.2
+     * @deprecated from crypto 1.1.5. please use init() method instead.
+     * @description
+     * Public key information will be provided as 'param' parameter and the value will be
+     * following:
+     * <ul>
+     * <li>{@link RSAKey} object for RSA verification</li>
+     * <li>associative array for ECDSA verification
+     *     (ex. <code>{'ecpubhex': '041f..', 'eccurvename': 'secp256r1'}</code>)
+     * </li>
+     * </ul>
+     * @example
+     * sig.initVerifyByPublicKey(rsaPrvKey)
+     */
+    this.initVerifyByPublicKey = function(rsaPubKey) {
+       throw "initVerifyByPublicKey(rsaPubKeyy) not supported for this alg:prov=" +
+             this.algProvName;
+    };
+
+    /**
+     * Initialize this object for verifying with a certficate
+     * @name initVerifyByCertificatePEM
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {String} certPEM PEM formatted string of certificate
+     * @since 1.0.2
+     * @deprecated from crypto 1.1.5. please use init() method instead.
+     * @description
+     * @example
+     * sig.initVerifyByCertificatePEM(certPEM)
+     */
+    this.initVerifyByCertificatePEM = function(certPEM) {
+       throw "initVerifyByCertificatePEM(certPEM) not supported for this alg:prov=" +
+           this.algProvName;
+    };
+
+    /**
+     * Initialize this object for signing
+     * @name initSign
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {Object} param RSAKey object of public key or associative array for ECDSA
+     * @deprecated from crypto 1.1.5. please use init() method instead.
+     * @description
+     * Private key information will be provided as 'param' parameter and the value will be
+     * following:
+     * <ul>
+     * <li>{@link RSAKey} object for RSA signing</li>
+     * <li>associative array for ECDSA signing
+     *     (ex. <code>{'ecprvhex': '1d3f..', 'eccurvename': 'secp256r1'}</code>)</li>
+     * </ul>
+     * @example
+     * sig.initSign(prvKey)
+     */
+    this.initSign = function(prvKey) {
+       throw "initSign(prvKey) not supported for this alg:prov=" + this.algProvName;
+    };
+
+    /**
+     * Updates the data to be signed or verified by a string
+     * @name updateString
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {String} str string to use for the update
+     * @description
+     * @example
+     * sig.updateString('aaa')
+     */
+    this.updateString = function(str) {
+       throw "updateString(str) not supported for this alg:prov=" + this.algProvName;
+    };
+
+    /**
+     * Updates the data to be signed or verified by a hexadecimal string
+     * @name updateHex
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {String} hex hexadecimal string to use for the update
+     * @description
+     * @example
+     * sig.updateHex('1f2f3f')
+     */
+    this.updateHex = function(hex) {
+       throw "updateHex(hex) not supported for this alg:prov=" + this.algProvName;
+    };
+
+    /**
+     * Returns the signature bytes of all data updates as a hexadecimal string
+     * @name sign
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @return the signature bytes as a hexadecimal string
+     * @description
+     * @example
+     * var hSigValue = sig.sign()
+     */
+    this.sign = function() {
+       throw "sign() not supported for this alg:prov=" + this.algProvName;
+    };
+
+    /**
+     * performs final update on the sign using string, then returns the signature bytes of all data updates as a hexadecimal string
+     * @name signString
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {String} str string to final update
+     * @return the signature bytes of a hexadecimal string
+     * @description
+     * @example
+     * var hSigValue = sig.signString('aaa')
+     */
+    this.signString = function(str) {
+       throw "digestString(str) not supported for this alg:prov=" + this.algProvName;
+    };
+
+    /**
+     * performs final update on the sign using hexadecimal string, then returns the signature bytes of all data updates as a hexadecimal string
+     * @name signHex
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {String} hex hexadecimal string to final update
+     * @return the signature bytes of a hexadecimal string
+     * @description
+     * @example
+     * var hSigValue = sig.signHex('1fdc33')
+     */
+    this.signHex = function(hex) {
+       throw "digestHex(hex) not supported for this alg:prov=" + this.algProvName;
+    };
+
+    /**
+     * verifies the passed-in signature.
+     * @name verify
+     * @memberOf KJUR.crypto.Signature
+     * @function
+     * @param {String} str string to final update
+     * @return {Boolean} true if the signature was verified, otherwise false
+     * @description
+     * @example
+     * var isValid = sig.verify('1fbcefdca4823a7(snip)')
+     */
+    this.verify = function(hSigVal) {
+       throw "verify(hSigVal) not supported for this alg:prov=" + this.algProvName;
+    };
+
+    this.initParams = params;
+
+    if (params !== undefined) {
+       if (params['alg'] !== undefined) {
+           this.algName = params['alg'];
+           if (params['prov'] === undefined) {
+               this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
+           } else {
+               this.provName = params['prov'];
+           }
+           this.algProvName = this.algName + ":" + this.provName;
+           this.setAlgAndProvider(this.algName, this.provName);
+           this._setAlgNames();
+       }
+
+       if (params['psssaltlen'] !== undefined) this.pssSaltLen = params['psssaltlen'];
+
+       if (params['prvkeypem'] !== undefined) {
+           if (params['prvkeypas'] !== undefined) {
+               throw "both prvkeypem and prvkeypas parameters not supported";
+           } else {
+               try {
+                   var prvKey = new RSAKey();
+                   prvKey.readPrivateKeyFromPEMString(params['prvkeypem']);
+                   this.initSign(prvKey);
+               } catch (ex) {
+                   throw "fatal error to load pem private key: " + ex;
+               }
+           }
+       }
+    }
+};
+
+/**
+ * static object for cryptographic function utilities
+ * @name KJUR.crypto.OID
+ * @class static object for cryptography related OIDs
+ * @property {Array} oidhex2name key value of hexadecimal OID and its name
+ *           (ex. '2a8648ce3d030107' and 'secp256r1')
+ * @since crypto 1.1.3
+ * @description
+ */
+
+
+KJUR.crypto.OID = new function() {
+    this.oidhex2name = {
+       '2a864886f70d010101': 'rsaEncryption',
+       '2a8648ce3d0201': 'ecPublicKey',
+       '2a8648ce380401': 'dsa',
+       '2a8648ce3d030107': 'secp256r1',
+       '2b8104001f': 'secp192k1',
+       '2b81040021': 'secp224r1',
+       '2b8104000a': 'secp256k1',
+       '2b81040023': 'secp521r1',
+       '2b81040022': 'secp384r1',
+       '2a8648ce380403': 'SHA1withDSA', // 1.2.840.10040.4.3
+       '608648016503040301': 'SHA224withDSA', // 2.16.840.1.101.3.4.3.1
+       '608648016503040302': 'SHA256withDSA' // 2.16.840.1.101.3.4.3.2
+    };
+};
diff --git a/js/hex_sha1_js.js b/js/hex_sha1_js.js
new file mode 100644 (file)
index 0000000..ee73a63
--- /dev/null
@@ -0,0 +1,202 @@
+/*\r
+ * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined\r
+ * in FIPS PUB 180-1\r
+ * Version 2.1a Copyright Paul Johnston 2000 - 2002.\r
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\r
+ * Distributed under the BSD License\r
+ * See http://pajhome.org.uk/crypt/md5 for details.\r
+ */\r
+\r
+/*\r
+ * Configurable variables. You may need to tweak these to be compatible with\r
+ * the server-side, but the defaults work in most cases.\r
+ */\r
+var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */\r
+var b64pad  = "="; /* base-64 pad character. "=" for strict RFC compliance   */\r
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */\r
+\r
+/*\r
+ * These are the functions you'll usually want to call\r
+ * They take string arguments and return either hex or base-64 encoded strings\r
+ */\r
+function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}\r
+function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}\r
+function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}\r
+function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}\r
+function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}\r
+function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}\r
+\r
+/*\r
+ * Perform a simple self-test to see if the VM is working\r
+ */\r
+function sha1_vm_test()\r
+{\r
+  return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";\r
+}\r
+\r
+/*\r
+ * Calculate the SHA-1 of an array of big-endian words, and a bit length\r
+ */\r
+function core_sha1(x, len)\r
+{\r
+  /* append padding */\r
+  x[len >> 5] |= 0x80 << (24 - len % 32);\r
+  x[((len + 64 >> 9) << 4) + 15] = len;\r
+\r
+  var w = Array(80);\r
+  var a =  1732584193;\r
+  var b = -271733879;\r
+  var c = -1732584194;\r
+  var d =  271733878;\r
+  var e = -1009589776;\r
+\r
+  for(var i = 0; i < x.length; i += 16)\r
+  {\r
+    var olda = a;\r
+    var oldb = b;\r
+    var oldc = c;\r
+    var oldd = d;\r
+    var olde = e;\r
+\r
+    for(var j = 0; j < 80; j++)\r
+    {\r
+      if(j < 16) w[j] = x[i + j];\r
+      else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);\r
+      var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),\r
+                       safe_add(safe_add(e, w[j]), sha1_kt(j)));\r
+      e = d;\r
+      d = c;\r
+      c = rol(b, 30);\r
+      b = a;\r
+      a = t;\r
+    }\r
+\r
+    a = safe_add(a, olda);\r
+    b = safe_add(b, oldb);\r
+    c = safe_add(c, oldc);\r
+    d = safe_add(d, oldd);\r
+    e = safe_add(e, olde);\r
+  }\r
+  return Array(a, b, c, d, e);\r
+\r
+}\r
+\r
+/*\r
+ * Perform the appropriate triplet combination function for the current\r
+ * iteration\r
+ */\r
+function sha1_ft(t, b, c, d)\r
+{\r
+  if(t < 20) return (b & c) | ((~b) & d);\r
+  if(t < 40) return b ^ c ^ d;\r
+  if(t < 60) return (b & c) | (b & d) | (c & d);\r
+  return b ^ c ^ d;\r
+}\r
+\r
+/*\r
+ * Determine the appropriate additive constant for the current iteration\r
+ */\r
+function sha1_kt(t)\r
+{\r
+  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :\r
+         (t < 60) ? -1894007588 : -899497514;\r
+}\r
+\r
+/*\r
+ * Calculate the HMAC-SHA1 of a key and some data\r
+ */\r
+function core_hmac_sha1(key, data)\r
+{\r
+  var bkey = str2binb(key);\r
+  if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);\r
+\r
+  var ipad = Array(16), opad = Array(16);\r
+  for(var i = 0; i < 16; i++)\r
+  {\r
+    ipad[i] = bkey[i] ^ 0x36363636;\r
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;\r
+  }\r
+\r
+  var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);\r
+  return core_sha1(opad.concat(hash), 512 + 160);\r
+}\r
+\r
+/*\r
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally\r
+ * to work around bugs in some JS interpreters.\r
+ */\r
+function safe_add(x, y)\r
+{\r
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);\r
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\r
+  return (msw << 16) | (lsw & 0xFFFF);\r
+}\r
+\r
+/*\r
+ * Bitwise rotate a 32-bit number to the left.\r
+ */\r
+function rol(num, cnt)\r
+{\r
+  return (num << cnt) | (num >>> (32 - cnt));\r
+}\r
+\r
+/*\r
+ * Convert an 8-bit or 16-bit string to an array of big-endian words\r
+ * In 8-bit function, characters >255 have their hi-byte silently ignored.\r
+ */\r
+function str2binb(str)\r
+{\r
+  var bin = Array();\r
+  var mask = (1 << chrsz) - 1;\r
+  for(var i = 0; i < str.length * chrsz; i += chrsz)\r
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);\r
+  return bin;\r
+}\r
+\r
+/*\r
+ * Convert an array of big-endian words to a string\r
+ */\r
+function binb2str(bin)\r
+{\r
+  var str = "";\r
+  var mask = (1 << chrsz) - 1;\r
+  for(var i = 0; i < bin.length * 32; i += chrsz)\r
+    str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);\r
+  return str;\r
+}\r
+\r
+/*\r
+ * Convert an array of big-endian words to a hex string.\r
+ */\r
+function binb2hex(binarray)\r
+{\r
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";\r
+  var str = "";\r
+  for(var i = 0; i < binarray.length * 4; i++)\r
+  {\r
+    str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +\r
+           hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);\r
+  }\r
+  return str;\r
+}\r
+\r
+/*\r
+ * Convert an array of big-endian words to a base-64 string\r
+ */\r
+function binb2b64(binarray)\r
+{\r
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
+  var str = "";\r
+  for(var i = 0; i < binarray.length * 4; i += 3)\r
+  {\r
+    var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16)\r
+                | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )\r
+                |  ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);\r
+    for(var j = 0; j < 4; j++)\r
+    {\r
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;\r
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);\r
+    }\r
+  }\r
+  return str;\r
+}\r
diff --git a/js/jsbn.js b/js/jsbn.js
new file mode 100644 (file)
index 0000000..4e81b02
--- /dev/null
@@ -0,0 +1,561 @@
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
+// Copyright (c) 2005  Tom Wu
+// All Rights Reserved.
+// See "LICENSE" for details.
+
+// Basic JavaScript BN library - subset useful for RSA encryption.
+
+// Bits per digit
+var dbits;
+
+// JavaScript engine analysis
+var canary = 0xdeadbeefcafe;
+var j_lm = ((canary&0xffffff)==0xefcafe);
+
+// (public) Constructor
+function BigInteger(a,b,c) {
+  if(a != null)
+    if("number" == typeof a) this.fromNumber(a,b,c);
+    else if(b == null && "string" != typeof a) this.fromString(a,256);
+    else this.fromString(a,b);
+}
+
+// return new, unset BigInteger
+function nbi() { return new BigInteger(null); }
+
+// 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[i++]+w[j]+c;
+    c = Math.floor(v/0x4000000);
+    w[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[i]&0x7fff;
+    var h = this[i++]>>15;
+    var m = xh*l+h*xl;
+    l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
+    c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
+    w[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[i]&0x3fff;
+    var h = this[i++]>>14;
+    var m = xh*l+h*xl;
+    l = xl*l+((m&0x3fff)<<14)+w[j]+c;
+    c = (l>>28)+(m>>14)+xh*h;
+    w[j++] = l&0xfffffff;
+  }
+  return c;
+}
+if(j_lm ) {
+  BigInteger.prototype.am = am2;
+  dbits = 30;
+}
+
+
+
+
+
+
+
+
+
+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[i] = this[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[0] = x;
+  else if(x < -1) this[0] = x+this.DV;
+  else this.t = 0;
+}
+
+// return bigint initialized to value
+function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
+
+// (protected) set from string and radix
+function bnpFromString(s,b) {
+  var k;
+  if(b == 16) k = 4;
+  else if(b == 8) k = 3;
+  else if(b == 256) k = 8; // byte array
+  else if(b == 2) k = 1;
+  else if(b == 32) k = 5;
+  else if(b == 4) k = 2;
+  else { this.fromRadix(s,b); return; }
+  this.t = 0;
+  this.s = 0;
+  var i = s.length, mi = false, sh = 0;
+  while(--i >= 0) {
+    var x = (k==8)?s[i]&0xff:intAt(s,i);
+    if(x < 0) {
+      if(s.charAt(i) == "-") mi = true;
+      continue;
+    }
+    mi = false;
+    if(sh == 0)
+      this[this.t++] = x;
+    else if(sh+k > this.DB) {
+      this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
+      this[this.t++] = (x>>(this.DB-sh));
+    }
+    else
+      this[this.t-1] |= x<<sh;
+    sh += k;
+    if(sh >= this.DB) sh -= this.DB;
+  }
+  if(k == 8 && (s[0]&0x80) != 0) {
+    this.s = -1;
+    if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
+  }
+  this.clamp();
+  if(mi) BigInteger.ZERO.subTo(this,this);
+}
+
+// (protected) clamp off excess high words
+function bnpClamp() {
+  var c = this.s&this.DM;
+  while(this.t > 0 && this[this.t-1] == c) --this.t;
+}
+
+// (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[i]>>p) > 0) { m = true; r = int2char(d); }
+    while(i >= 0) {
+      if(p < k) {
+        d = (this[i]&((1<<p)-1))<<(k-p);
+        d |= this[--i]>>(p+=this.DB-k);
+      }
+      else {
+        d = (this[i]>>(p-=k))&km;
+        if(p <= 0) { p += this.DB; --i; }
+      }
+      if(d > 0) m = true;
+      if(m) r += int2char(d);
+    }
+  }
+  return m?r:"0";
+}
+
+// (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[i]-a[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[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[i+n] = this[i];
+  for(i = n-1; i >= 0; --i) r[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[i-n] = this[i];
+  r.t = Math.max(this.t-n,0);
+  r.s = this.s;
+}
+
+// (protected) r = this << n
+function bnpLShiftTo(n,r) {
+  var bs = n%this.DB;
+  var cbs = this.DB-bs;
+  var bm = (1<<cbs)-1;
+  var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
+  for(i = this.t-1; i >= 0; --i) {
+    r[i+ds+1] = (this[i]>>cbs)|c;
+    c = (this[i]&bm)<<bs;
+  }
+  for(i = ds-1; i >= 0; --i) r[i] = 0;
+  r[ds] = c;
+  r.t = this.t+ds+1;
+  r.s = this.s;
+  r.clamp();
+}
+
+// (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[0] = this[ds]>>bs;
+  for(var i = ds+1; i < this.t; ++i) {
+    r[i-ds-1] |= (this[i]&bm)<<cbs;
+    r[i-ds] = this[i]>>bs;
+  }
+  if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
+  r.t = this.t-ds;
+  r.clamp();
+}
+
+// (protected) r = this - a
+function bnpSubTo(a,r) {
+  var i = 0, c = 0, m = Math.min(a.t,this.t);
+  while(i < m) {
+    c += this[i]-a[i];
+    r[i++] = c&this.DM;
+    c >>= this.DB;
+  }
+  if(a.t < this.t) {
+    c -= a.s;
+    while(i < this.t) {
+      c += this[i];
+      r[i++] = c&this.DM;
+      c >>= this.DB;
+    }
+    c += this.s;
+  }
+  else {
+    c += this.s;
+    while(i < a.t) {
+      c -= a[i];
+      r[i++] = c&this.DM;
+      c >>= this.DB;
+    }
+    c -= a.s;
+  }
+  r.s = (c<0)?-1:0;
+  if(c < -1) r[i++] = this.DV+c;
+  else if(c > 0) r[i++] = c;
+  r.t = i;
+  r.clamp();
+}
+
+// (protected) r = this * a, r != this,a (HAC 14.12)
+// "this" should be the larger one if appropriate.
+function bnpMultiplyTo(a,r) {
+  var x = this.abs(), y = a.abs();
+  var i = x.t;
+  r.t = i+y.t;
+  while(--i >= 0) r[i] = 0;
+  for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
+  r.s = 0;
+  r.clamp();
+  if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
+}
+
+// (protected) r = this^2, r != this (HAC 14.16)
+function bnpSquareTo(r) {
+  var x = this.abs();
+  var i = r.t = 2*x.t;
+  while(--i >= 0) r[i] = 0;
+  for(i = 0; i < x.t-1; ++i) {
+    var c = x.am(i,x[i],r,2*i,0,1);
+    if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
+      r[i+x.t] -= x.DV;
+      r[i+x.t+1] = 1;
+    }
+  }
+  if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
+  r.s = 0;
+  r.clamp();
+}
+
+// (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[pm.t-1]); // normalize modulus
+  if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
+  else { pm.copyTo(y); pt.copyTo(r); }
+  var ys = y.t;
+  var y0 = y[ys-1];
+  if(y0 == 0) return;
+  var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
+  var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
+  var i = r.t, j = i-ys, t = (q==null)?nbi():q;
+  y.dlShiftTo(j,t);
+  if(r.compareTo(t) >= 0) {
+    r[r.t++] = 1;
+    r.subTo(t,r);
+  }
+  BigInteger.ONE.dlShiftTo(ys,t);
+  t.subTo(y,y);        // "negative" y so we can replace sub with am later
+  while(y.t < ys) y[y.t++] = 0;
+  while(--j >= 0) {
+    // Estimate quotient digit
+    var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
+    if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) {     // Try it out
+      y.dlShiftTo(j,t);
+      r.subTo(t,r);
+      while(r[i] < --qd) r.subTo(t,r);
+    }
+  }
+  if(q != null) {
+    r.drShiftTo(ys,q);
+    if(ts != ms) BigInteger.ZERO.subTo(q,q);
+  }
+  r.t = ys;
+  r.clamp();
+  if(nsh > 0) r.rShiftTo(nsh,r);       // Denormalize remainder
+  if(ts < 0) BigInteger.ZERO.subTo(r,r);
+}
+
+// (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[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[x.t++] = 0;
+  for(var i = 0; i < this.m.t; ++i) {
+    // faster way of calculating u0 = x[i]*mp mod DV
+    var j = x[i]&0x7fff;
+    var u0 = (j*this.mpl+(((j*this.mph+(x[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[j] += this.m.am(0,u0,x,i,0,this.m.t);
+    // propagate carry
+    while(x[j] >= x.DV) { x[j] -= x.DV; x[++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[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);
diff --git a/js/jsbn2.js b/js/jsbn2.js
new file mode 100644 (file)
index 0000000..732ffae
--- /dev/null
@@ -0,0 +1,658 @@
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
+// Copyright (c) 2005-2009  Tom Wu
+// All Rights Reserved.
+// See "LICENSE" for details.
+
+// 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[0]-this.DV;
+    else if(this.t == 0) return -1;
+  }
+  else if(this.t == 1) return this[0];
+  else if(this.t == 0) return 0;
+  // assumes 16 < DB < 32
+  return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0];
+}
+
+// (public) return value as byte
+function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; }
+
+// (public) return value as short (assumes DB>=16)
+function bnShortValue() { return (this.t==0)?this.s:(this[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[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[i]>>p) != (this.s&this.DM)>>p)
+      r[k++] = d|(this.s<<(this.DB-p));
+    while(i >= 0) {
+      if(p < 8) {
+        d = (this[i]&((1<<p)-1))<<(8-p);
+        d |= this[--i]>>(p+=this.DB-8);
+      }
+      else {
+        d = (this[i]>>(p-=8))&0xff;
+        if(p <= 0) { p += this.DB; --i; }
+      }
+      if((d&0x80) != 0) d |= -256;
+      if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
+      if(k > 0 || d != this.s) r[k++] = d;
+    }
+  }
+  return r;
+}
+
+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[i] = op(this[i],a[i]);
+  if(a.t < this.t) {
+    f = a.s&this.DM;
+    for(i = m; i < this.t; ++i) r[i] = op(this[i],f);
+    r.t = this.t;
+  }
+  else {
+    f = this.s&this.DM;
+    for(i = m; i < a.t; ++i) r[i] = op(f,a[i]);
+    r.t = a.t;
+  }
+  r.s = op(this.s,a.s);
+  r.clamp();
+}
+
+// (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[i] = this.DM&~this[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[i] != 0) return i*this.DB+lbit(this[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[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[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[i]+a[i];
+    r[i++] = c&this.DM;
+    c >>= this.DB;
+  }
+  if(a.t < this.t) {
+    c += a.s;
+    while(i < this.t) {
+      c += this[i];
+      r[i++] = c&this.DM;
+      c >>= this.DB;
+    }
+    c += this.s;
+  }
+  else {
+    c += this.s;
+    while(i < a.t) {
+      c += a[i];
+      r[i++] = c&this.DM;
+      c >>= this.DB;
+    }
+    c += a.s;
+  }
+  r.s = (c<0)?-1:0;
+  if(c > 0) r[i++] = c;
+  else if(c < -1) r[i++] = this.DV+c;
+  r.t = i;
+  r.clamp();
+}
+
+// (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[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[this.t++] = 0;
+  this[w] += n;
+  while(this[w] >= this.DV) {
+    this[w] -= this.DV;
+    if(++w >= this.t) this[this.t++] = 0;
+    ++this[w];
+  }
+}
+
+// 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[--i] = 0;
+  var j;
+  for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t);
+  for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i);
+  r.clamp();
+}
+
+// (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[i] = 0;
+  for(i = Math.max(n-this.t,0); i < a.t; ++i)
+    r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n);
+  r.clamp();
+  r.drShiftTo(1,r);
+}
+
+// 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[j])-1;
+  while(j >= 0) {
+    if(i >= k1) w = (e[j]>>(i-k1))&km;
+    else {
+      w = (e[j]&((1<<(i+1))-1))<<(k1-i);
+      if(j > 0) w |= e[j-1]>>(this.DB+i-k1);
+    }
+
+    n = k;
+    while((w&1) == 0) { w >>= 1; --n; }
+    if((i -= n) < 0) { i += this.DB; --j; }
+    if(is1) {  // ret == 1, don't bother squaring or multiplying it
+      g[w].copyTo(r);
+      is1 = false;
+    }
+    else {
+      while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
+      if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
+      z.mulTo(r2,g[w],r);
+    }
+
+    while(j >= 0 && (e[j]&(1<<i)) == 0) {
+      z.sqrTo(r,r2); t = r; r = r2; r2 = t;
+      if(--i < 0) { i = this.DB-1; --j; }
+    }
+  }
+  return z.revert(r);
+}
+
+// (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[0]%n;
+    else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n;
+  return r;
+}
+
+// (public) 1/this % m (HAC 14.61)
+function bnModInverse(m) {
+  var ac = m.isEven();
+  if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;
+  var u = m.clone(), v = this.clone();
+  var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
+  while(u.signum() != 0) {
+    while(u.isEven()) {
+      u.rShiftTo(1,u);
+      if(ac) {
+        if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
+        a.rShiftTo(1,a);
+      }
+      else if(!b.isEven()) b.subTo(m,b);
+      b.rShiftTo(1,b);
+    }
+    while(v.isEven()) {
+      v.rShiftTo(1,v);
+      if(ac) {
+        if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
+        c.rShiftTo(1,c);
+      }
+      else if(!d.isEven()) d.subTo(m,d);
+      d.rShiftTo(1,d);
+    }
+    if(u.compareTo(v) >= 0) {
+      u.subTo(v,u);
+      if(ac) a.subTo(c,a);
+      b.subTo(d,b);
+    }
+    else {
+      v.subTo(u,v);
+      if(ac) c.subTo(a,c);
+      d.subTo(b,d);
+    }
+  }
+  if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
+  if(d.compareTo(m) >= 0) return d.subtract(m);
+  if(d.signum() < 0) d.addTo(m,d); else return d;
+  if(d.signum() < 0) return d.add(m); else return d;
+}
+
+var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,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[0] <= lowprimes[lowprimes.length-1]) {
+    for(i = 0; i < lowprimes.length; ++i)
+      if(x[0] == lowprimes[i]) return true;
+    return false;
+  }
+  if(x.isEven()) return false;
+  i = 1;
+  while(i < lowprimes.length) {
+    var m = lowprimes[i], j = i+1;
+    while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
+    m = x.modInt(m);
+    while(i < j) if(m%lowprimes[i++] == 0) return false;
+  }
+  return x.millerRabin(t);
+}
+
+// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
+function bnpMillerRabin(t) {
+  var n1 = this.subtract(BigInteger.ONE);
+  var k = n1.getLowestSetBit();
+  if(k <= 0) return false;
+  var r = n1.shiftRight(k);
+  t = (t+1)>>1;
+  if(t > lowprimes.length) t = lowprimes.length;
+  var a = nbi();
+  for(var i = 0; i < t; ++i) {
+    //Pick bases at random, instead of starting at 2
+    a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
+    var y = a.modPow(r,this);
+    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;
+}
+
+// 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)
diff --git a/js/md5.js b/js/md5.js
new file mode 100644 (file)
index 0000000..f4c46b9
--- /dev/null
+++ b/js/md5.js
@@ -0,0 +1,254 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+(function (Math) {\r
+    // Shortcuts\r
+    var C = CryptoJS;\r
+    var C_lib = C.lib;\r
+    var WordArray = C_lib.WordArray;\r
+    var Hasher = C_lib.Hasher;\r
+    var C_algo = C.algo;\r
+\r
+    // Constants table\r
+    var T = [];\r
+\r
+    // Compute constants\r
+    (function () {\r
+        for (var i = 0; i < 64; i++) {\r
+            T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0;\r
+        }\r
+    }());\r
+\r
+    /**\r
+     * MD5 hash algorithm.\r
+     */\r
+    var MD5 = C_algo.MD5 = Hasher.extend({\r
+        _doReset: function () {\r
+            this._hash = new WordArray.init([\r
+                0x67452301, 0xefcdab89,\r
+                0x98badcfe, 0x10325476\r
+            ]);\r
+        },\r
+\r
+        _doProcessBlock: function (M, offset) {\r
+            // Swap endian\r
+            for (var i = 0; i < 16; i++) {\r
+                // Shortcuts\r
+                var offset_i = offset + i;\r
+                var M_offset_i = M[offset_i];\r
+\r
+                M[offset_i] = (\r
+                    (((M_offset_i << 8)  | (M_offset_i >>> 24)) & 0x00ff00ff) |\r
+                    (((M_offset_i << 24) | (M_offset_i >>> 8))  & 0xff00ff00)\r
+                );\r
+            }\r
+\r
+            // Shortcuts\r
+            var H = this._hash.words;\r
+\r
+            var M_offset_0  = M[offset + 0];\r
+            var M_offset_1  = M[offset + 1];\r
+            var M_offset_2  = M[offset + 2];\r
+            var M_offset_3  = M[offset + 3];\r
+            var M_offset_4  = M[offset + 4];\r
+            var M_offset_5  = M[offset + 5];\r
+            var M_offset_6  = M[offset + 6];\r
+            var M_offset_7  = M[offset + 7];\r
+            var M_offset_8  = M[offset + 8];\r
+            var M_offset_9  = M[offset + 9];\r
+            var M_offset_10 = M[offset + 10];\r
+            var M_offset_11 = M[offset + 11];\r
+            var M_offset_12 = M[offset + 12];\r
+            var M_offset_13 = M[offset + 13];\r
+            var M_offset_14 = M[offset + 14];\r
+            var M_offset_15 = M[offset + 15];\r
+\r
+            // Working varialbes\r
+            var a = H[0];\r
+            var b = H[1];\r
+            var c = H[2];\r
+            var d = H[3];\r
+\r
+            // Computation\r
+            a = FF(a, b, c, d, M_offset_0,  7,  T[0]);\r
+            d = FF(d, a, b, c, M_offset_1,  12, T[1]);\r
+            c = FF(c, d, a, b, M_offset_2,  17, T[2]);\r
+            b = FF(b, c, d, a, M_offset_3,  22, T[3]);\r
+            a = FF(a, b, c, d, M_offset_4,  7,  T[4]);\r
+            d = FF(d, a, b, c, M_offset_5,  12, T[5]);\r
+            c = FF(c, d, a, b, M_offset_6,  17, T[6]);\r
+            b = FF(b, c, d, a, M_offset_7,  22, T[7]);\r
+            a = FF(a, b, c, d, M_offset_8,  7,  T[8]);\r
+            d = FF(d, a, b, c, M_offset_9,  12, T[9]);\r
+            c = FF(c, d, a, b, M_offset_10, 17, T[10]);\r
+            b = FF(b, c, d, a, M_offset_11, 22, T[11]);\r
+            a = FF(a, b, c, d, M_offset_12, 7,  T[12]);\r
+            d = FF(d, a, b, c, M_offset_13, 12, T[13]);\r
+            c = FF(c, d, a, b, M_offset_14, 17, T[14]);\r
+            b = FF(b, c, d, a, M_offset_15, 22, T[15]);\r
+\r
+            a = GG(a, b, c, d, M_offset_1,  5,  T[16]);\r
+            d = GG(d, a, b, c, M_offset_6,  9,  T[17]);\r
+            c = GG(c, d, a, b, M_offset_11, 14, T[18]);\r
+            b = GG(b, c, d, a, M_offset_0,  20, T[19]);\r
+            a = GG(a, b, c, d, M_offset_5,  5,  T[20]);\r
+            d = GG(d, a, b, c, M_offset_10, 9,  T[21]);\r
+            c = GG(c, d, a, b, M_offset_15, 14, T[22]);\r
+            b = GG(b, c, d, a, M_offset_4,  20, T[23]);\r
+            a = GG(a, b, c, d, M_offset_9,  5,  T[24]);\r
+            d = GG(d, a, b, c, M_offset_14, 9,  T[25]);\r
+            c = GG(c, d, a, b, M_offset_3,  14, T[26]);\r
+            b = GG(b, c, d, a, M_offset_8,  20, T[27]);\r
+            a = GG(a, b, c, d, M_offset_13, 5,  T[28]);\r
+            d = GG(d, a, b, c, M_offset_2,  9,  T[29]);\r
+            c = GG(c, d, a, b, M_offset_7,  14, T[30]);\r
+            b = GG(b, c, d, a, M_offset_12, 20, T[31]);\r
+\r
+            a = HH(a, b, c, d, M_offset_5,  4,  T[32]);\r
+            d = HH(d, a, b, c, M_offset_8,  11, T[33]);\r
+            c = HH(c, d, a, b, M_offset_11, 16, T[34]);\r
+            b = HH(b, c, d, a, M_offset_14, 23, T[35]);\r
+            a = HH(a, b, c, d, M_offset_1,  4,  T[36]);\r
+            d = HH(d, a, b, c, M_offset_4,  11, T[37]);\r
+            c = HH(c, d, a, b, M_offset_7,  16, T[38]);\r
+            b = HH(b, c, d, a, M_offset_10, 23, T[39]);\r
+            a = HH(a, b, c, d, M_offset_13, 4,  T[40]);\r
+            d = HH(d, a, b, c, M_offset_0,  11, T[41]);\r
+            c = HH(c, d, a, b, M_offset_3,  16, T[42]);\r
+            b = HH(b, c, d, a, M_offset_6,  23, T[43]);\r
+            a = HH(a, b, c, d, M_offset_9,  4,  T[44]);\r
+            d = HH(d, a, b, c, M_offset_12, 11, T[45]);\r
+            c = HH(c, d, a, b, M_offset_15, 16, T[46]);\r
+            b = HH(b, c, d, a, M_offset_2,  23, T[47]);\r
+\r
+            a = II(a, b, c, d, M_offset_0,  6,  T[48]);\r
+            d = II(d, a, b, c, M_offset_7,  10, T[49]);\r
+            c = II(c, d, a, b, M_offset_14, 15, T[50]);\r
+            b = II(b, c, d, a, M_offset_5,  21, T[51]);\r
+            a = II(a, b, c, d, M_offset_12, 6,  T[52]);\r
+            d = II(d, a, b, c, M_offset_3,  10, T[53]);\r
+            c = II(c, d, a, b, M_offset_10, 15, T[54]);\r
+            b = II(b, c, d, a, M_offset_1,  21, T[55]);\r
+            a = II(a, b, c, d, M_offset_8,  6,  T[56]);\r
+            d = II(d, a, b, c, M_offset_15, 10, T[57]);\r
+            c = II(c, d, a, b, M_offset_6,  15, T[58]);\r
+            b = II(b, c, d, a, M_offset_13, 21, T[59]);\r
+            a = II(a, b, c, d, M_offset_4,  6,  T[60]);\r
+            d = II(d, a, b, c, M_offset_11, 10, T[61]);\r
+            c = II(c, d, a, b, M_offset_2,  15, T[62]);\r
+            b = II(b, c, d, a, M_offset_9,  21, T[63]);\r
+\r
+            // Intermediate hash value\r
+            H[0] = (H[0] + a) | 0;\r
+            H[1] = (H[1] + b) | 0;\r
+            H[2] = (H[2] + c) | 0;\r
+            H[3] = (H[3] + d) | 0;\r
+        },\r
+\r
+        _doFinalize: function () {\r
+            // Shortcuts\r
+            var data = this._data;\r
+            var dataWords = data.words;\r
+\r
+            var nBitsTotal = this._nDataBytes * 8;\r
+            var nBitsLeft = data.sigBytes * 8;\r
+\r
+            // Add padding\r
+            dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);\r
+\r
+            var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);\r
+            var nBitsTotalL = nBitsTotal;\r
+            dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = (\r
+                (((nBitsTotalH << 8)  | (nBitsTotalH >>> 24)) & 0x00ff00ff) |\r
+                (((nBitsTotalH << 24) | (nBitsTotalH >>> 8))  & 0xff00ff00)\r
+            );\r
+            dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (\r
+                (((nBitsTotalL << 8)  | (nBitsTotalL >>> 24)) & 0x00ff00ff) |\r
+                (((nBitsTotalL << 24) | (nBitsTotalL >>> 8))  & 0xff00ff00)\r
+            );\r
+\r
+            data.sigBytes = (dataWords.length + 1) * 4;\r
+\r
+            // Hash final blocks\r
+            this._process();\r
+\r
+            // Shortcuts\r
+            var hash = this._hash;\r
+            var H = hash.words;\r
+\r
+            // Swap endian\r
+            for (var i = 0; i < 4; i++) {\r
+                // Shortcut\r
+                var H_i = H[i];\r
+\r
+                H[i] = (((H_i << 8)  | (H_i >>> 24)) & 0x00ff00ff) |\r
+                       (((H_i << 24) | (H_i >>> 8))  & 0xff00ff00);\r
+            }\r
+\r
+            // Return final computed hash\r
+            return hash;\r
+        },\r
+\r
+        clone: function () {\r
+            var clone = Hasher.clone.call(this);\r
+            clone._hash = this._hash.clone();\r
+\r
+            return clone;\r
+        }\r
+    });\r
+\r
+    function FF(a, b, c, d, x, s, t) {\r
+        var n = a + ((b & c) | (~b & d)) + x + t;\r
+        return ((n << s) | (n >>> (32 - s))) + b;\r
+    }\r
+\r
+    function GG(a, b, c, d, x, s, t) {\r
+        var n = a + ((b & d) | (c & ~d)) + x + t;\r
+        return ((n << s) | (n >>> (32 - s))) + b;\r
+    }\r
+\r
+    function HH(a, b, c, d, x, s, t) {\r
+        var n = a + (b ^ c ^ d) + x + t;\r
+        return ((n << s) | (n >>> (32 - s))) + b;\r
+    }\r
+\r
+    function II(a, b, c, d, x, s, t) {\r
+        var n = a + (c ^ (b | ~d)) + x + t;\r
+        return ((n << s) | (n >>> (32 - s))) + b;\r
+    }\r
+\r
+    /**\r
+     * Shortcut function to the hasher's object interface.\r
+     *\r
+     * @param {WordArray|string} message The message to hash.\r
+     *\r
+     * @return {WordArray} The hash.\r
+     *\r
+     * @static\r
+     *\r
+     * @example\r
+     *\r
+     *     var hash = CryptoJS.MD5('message');\r
+     *     var hash = CryptoJS.MD5(wordArray);\r
+     */\r
+    C.MD5 = Hasher._createHelper(MD5);\r
+\r
+    /**\r
+     * Shortcut function to the HMAC's object interface.\r
+     *\r
+     * @param {WordArray|string} message The message to hash.\r
+     * @param {WordArray|string} key The secret key.\r
+     *\r
+     * @return {WordArray} The HMAC.\r
+     *\r
+     * @static\r
+     *\r
+     * @example\r
+     *\r
+     *     var hmac = CryptoJS.HmacMD5(message, key);\r
+     */\r
+    C.HmacMD5 = Hasher._createHmacHelper(MD5);\r
+}(Math));\r
diff --git a/js/ripemd160.js b/js/ripemd160.js
new file mode 100644 (file)
index 0000000..4acab8b
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+/** @preserve
+(c) 2012 by C├ędric Mesnil. 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.
+
+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 THE COPYRIGHT HOLDER 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.
+*/
+
+(function (Math) {
+    // Shortcuts
+    var C = CryptoJS;
+    var C_lib = C.lib;
+    var WordArray = C_lib.WordArray;
+    var Hasher = C_lib.Hasher;
+    var C_algo = C.algo;
+
+    // Constants table
+    var _zl = WordArray.create([
+        0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+        7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
+        3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
+        1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
+        4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13]);
+    var _zr = WordArray.create([
+        5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
+        6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
+        15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
+        8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
+        12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11]);
+    var _sl = WordArray.create([
+         11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
+        7, 6,   8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
+        11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
+          11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
+        9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6 ]);
+    var _sr = WordArray.create([
+        8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
+        9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
+        9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
+        15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
+        8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11 ]);
+
+    var _hl =  WordArray.create([ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]);
+    var _hr =  WordArray.create([ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]);
+
+    /**
+     * RIPEMD160 hash algorithm.
+     */
+    var RIPEMD160 = C_algo.RIPEMD160 = Hasher.extend({
+        _doReset: function () {
+            this._hash  = WordArray.create([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]);
+        },
+
+        _doProcessBlock: function (M, offset) {
+
+            // Swap endian
+            for (var i = 0; i < 16; i++) {
+                // Shortcuts
+                var offset_i = offset + i;
+                var M_offset_i = M[offset_i];
+
+                // Swap
+                M[offset_i] = (
+                    (((M_offset_i << 8)  | (M_offset_i >>> 24)) & 0x00ff00ff) |
+                    (((M_offset_i << 24) | (M_offset_i >>> 8))  & 0xff00ff00)
+                );
+            }
+            // Shortcut
+            var H  = this._hash.words;
+            var hl = _hl.words;
+            var hr = _hr.words;
+            var zl = _zl.words;
+            var zr = _zr.words;
+            var sl = _sl.words;
+            var sr = _sr.words;
+
+            // Working variables
+            var al, bl, cl, dl, el;
+            var ar, br, cr, dr, er;
+
+            ar = al = H[0];
+            br = bl = H[1];
+            cr = cl = H[2];
+            dr = dl = H[3];
+            er = el = H[4];
+            // Computation
+            var t;
+            for (var i = 0; i < 80; i += 1) {
+                t = (al +  M[offset+zl[i]])|0;
+                if (i<16){
+                   t +=  f1(bl,cl,dl) + hl[0];
+                } else if (i<32) {
+                   t +=  f2(bl,cl,dl) + hl[1];
+                } else if (i<48) {
+                   t +=  f3(bl,cl,dl) + hl[2];
+                } else if (i<64) {
+                   t +=  f4(bl,cl,dl) + hl[3];
+                } else {// if (i<80) {
+                   t +=  f5(bl,cl,dl) + hl[4];
+                }
+                t = t|0;
+                t =  rotl(t,sl[i]);
+                t = (t+el)|0;
+                al = el;
+                el = dl;
+                dl = rotl(cl, 10);
+                cl = bl;
+                bl = t;
+
+                t = (ar + M[offset+zr[i]])|0;
+                if (i<16){
+                   t +=  f5(br,cr,dr) + hr[0];
+                } else if (i<32) {
+                   t +=  f4(br,cr,dr) + hr[1];
+                } else if (i<48) {
+                   t +=  f3(br,cr,dr) + hr[2];
+                } else if (i<64) {
+                   t +=  f2(br,cr,dr) + hr[3];
+                } else {// if (i<80) {
+                   t +=  f1(br,cr,dr) + hr[4];
+                }
+                t = t|0;
+                t =  rotl(t,sr[i]) ;
+                t = (t+er)|0;
+                ar = er;
+                er = dr;
+                dr = rotl(cr, 10);
+                cr = br;
+                br = t;
+            }
+            // Intermediate hash value
+            t    = (H[1] + cl + dr)|0;
+            H[1] = (H[2] + dl + er)|0;
+            H[2] = (H[3] + el + ar)|0;
+            H[3] = (H[4] + al + br)|0;
+            H[4] = (H[0] + bl + cr)|0;
+            H[0] =  t;
+        },
+
+        _doFinalize: function () {
+            // Shortcuts
+            var data = this._data;
+            var dataWords = data.words;
+
+            var nBitsTotal = this._nDataBytes * 8;
+            var nBitsLeft = data.sigBytes * 8;
+
+            // Add padding
+            dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
+            dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
+                (((nBitsTotal << 8)  | (nBitsTotal >>> 24)) & 0x00ff00ff) |
+                (((nBitsTotal << 24) | (nBitsTotal >>> 8))  & 0xff00ff00)
+            );
+            data.sigBytes = (dataWords.length + 1) * 4;
+
+            // Hash final blocks
+            this._process();
+
+            // Shortcuts
+            var hash = this._hash;
+            var H = hash.words;
+
+            // Swap endian
+            for (var i = 0; i < 5; i++) {
+                // Shortcut
+                var H_i = H[i];
+
+                // Swap
+                H[i] = (((H_i << 8)  | (H_i >>> 24)) & 0x00ff00ff) |
+                       (((H_i << 24) | (H_i >>> 8))  & 0xff00ff00);
+            }
+
+            // Return final computed hash
+            return hash;
+        },
+
+        clone: function () {
+            var clone = Hasher.clone.call(this);
+            clone._hash = this._hash.clone();
+
+            return clone;
+        }
+    });
+
+
+    function f1(x, y, z) {
+        return ((x) ^ (y) ^ (z));
+
+    }
+
+    function f2(x, y, z) {
+        return (((x)&(y)) | ((~x)&(z)));
+    }
+
+    function f3(x, y, z) {
+        return (((x) | (~(y))) ^ (z));
+    }
+
+    function f4(x, y, z) {
+        return (((x) & (z)) | ((y)&(~(z))));
+    }
+
+    function f5(x, y, z) {
+        return ((x) ^ ((y) |(~(z))));
+
+    }
+
+    function rotl(x,n) {
+        return (x<<n) | (x>>>(32-n));
+    }
+
+
+    /**
+     * Shortcut function to the hasher's object interface.
+     *
+     * @param {WordArray|string} message The message to hash.
+     *
+     * @return {WordArray} The hash.
+     *
+     * @static
+     *
+     * @example
+     *
+     *     var hash = CryptoJS.RIPEMD160('message');
+     *     var hash = CryptoJS.RIPEMD160(wordArray);
+     */
+    C.RIPEMD160 = Hasher._createHelper(RIPEMD160);
+
+    /**
+     * Shortcut function to the HMAC's object interface.
+     *
+     * @param {WordArray|string} message The message to hash.
+     * @param {WordArray|string} key The secret key.
+     *
+     * @return {WordArray} The HMAC.
+     *
+     * @static
+     *
+     * @example
+     *
+     *     var hmac = CryptoJS.HmacRIPEMD160(message, key);
+     */
+    C.HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160);
+}(Math));
diff --git a/js/rsa.js b/js/rsa.js
new file mode 100644 (file)
index 0000000..2007296
--- /dev/null
+++ b/js/rsa.js
@@ -0,0 +1,191 @@
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
+// Depends on jsbn.js and rng.js
+
+// Version 1.1: support utf-8 encoding in pkcs1pad2
+
+// convert a (hex) string to a bignum object
+function parseBigInt(str,r) {
+  return new BigInteger(str,r);
+}
+
+function linebrk(s,n) {
+  var ret = "";
+  var i = 0;
+  while(i + n < s.length) {
+    ret += s.substring(i,i+n) + "\n";
+    i += n;
+  }
+  return ret + s.substring(i,s.length);
+}
+
+function byte2Hex(b) {
+  if(b < 0x10)
+    return "0" + b.toString(16);
+  else
+    return b.toString(16);
+}
+
+// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
+function pkcs1pad2(s,n) {
+  if(n < s.length + 11) { // TODO: fix for utf-8
+    alert("Message too long for RSA");
+    return null;
+  }
+  var ba = new Array();
+  var i = s.length - 1;
+  while(i >= 0 && n > 0) {
+    var c = s.charCodeAt(i--);
+    if(c < 128) { // encode using utf-8
+      ba[--n] = c;
+    }
+    else if((c > 127) && (c < 2048)) {
+      ba[--n] = (c & 63) | 128;
+      ba[--n] = (c >> 6) | 192;
+    }
+    else {
+      ba[--n] = (c & 63) | 128;
+      ba[--n] = ((c >> 6) & 63) | 128;
+      ba[--n] = (c >> 12) | 224;
+    }
+  }
+  ba[--n] = 0;
+  var rng = new SecureRandom();
+  var x = new Array();
+  while(n > 2) { // random non-zero pad
+    x[0] = 0;
+    while(x[0] == 0) rng.nextBytes(x);
+    ba[--n] = x[0];
+  }
+  ba[--n] = 2;
+  ba[--n] = 0;
+  return new BigInteger(ba);
+}
+
+// PKCS#1 (OAEP) mask generation function
+function oaep_mgf1_arr(seed, len, hash)
+{
+    var mask = '', i = 0;
+
+    while (mask.length < len)
+    {
+        mask += hash(String.fromCharCode.apply(String, seed.concat([
+                (i & 0xff000000) >> 24,
+                (i & 0x00ff0000) >> 16,
+                (i & 0x0000ff00) >> 8,
+                i & 0x000000ff])));
+        i += 1;
+    }
+
+    return mask;
+}
+
+var SHA1_SIZE = 20;
+
+// PKCS#1 (OAEP) pad input string s to n bytes, and return a bigint
+function oaep_pad(s, n, hash)
+{
+    if (s.length + 2 * SHA1_SIZE + 2 > n)
+    {
+        throw "Message too long for RSA";
+    }
+
+    var PS = '', i;
+
+    for (i = 0; i < n - s.length - 2 * SHA1_SIZE - 2; i += 1)
+    {
+        PS += '\x00';
+    }
+
+    var DB = rstr_sha1('') + PS + '\x01' + s;
+    var seed = new Array(SHA1_SIZE);
+    new SecureRandom().nextBytes(seed);
+    
+    var dbMask = oaep_mgf1_arr(seed, DB.length, hash || rstr_sha1);
+    var maskedDB = [];
+
+    for (i = 0; i < DB.length; i += 1)
+    {
+        maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+    }
+
+    var seedMask = oaep_mgf1_arr(maskedDB, seed.length, rstr_sha1);
+    var maskedSeed = [0];
+
+    for (i = 0; i < seed.length; i += 1)
+    {
+        maskedSeed[i + 1] = seed[i] ^ seedMask.charCodeAt(i);
+    }
+
+    return new BigInteger(maskedSeed.concat(maskedDB));
+}
+
+// "empty" RSA key constructor
+function RSAKey() {
+  this.n = null;
+  this.e = 0;
+  this.d = null;
+  this.p = null;
+  this.q = null;
+  this.dmp1 = null;
+  this.dmq1 = null;
+  this.coeff = null;
+}
+
+// Set the public key fields N and e from hex strings
+function RSASetPublic(N,E) {
+  this.isPublic = true;
+  if (typeof N !== "string") 
+  {
+    this.n = N;
+    this.e = E;
+  }
+  else if(N != null && E != null && N.length > 0 && E.length > 0) {
+    this.n = parseBigInt(N,16);
+    this.e = parseInt(E,16);
+  }
+  else
+    alert("Invalid RSA public key");
+}
+
+// Perform raw public operation on "x": return x^e (mod n)
+function RSADoPublic(x) {
+  return x.modPowInt(this.e, this.n);
+}
+
+// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
+function RSAEncrypt(text) {
+  var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
+  if(m == null) return null;
+  var c = this.doPublic(m);
+  if(c == null) return null;
+  var h = c.toString(16);
+  if((h.length & 1) == 0) return h; else return "0" + h;
+}
+
+// Return the PKCS#1 OAEP RSA encryption of "text" as an even-length hex string
+function RSAEncryptOAEP(text, hash) {
+  var m = oaep_pad(text, (this.n.bitLength()+7)>>3, hash);
+  if(m == null) return null;
+  var c = this.doPublic(m);
+  if(c == null) return null;
+  var h = c.toString(16);
+  if((h.length & 1) == 0) return h; else return "0" + h;
+}
+
+// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
+//function RSAEncryptB64(text) {
+//  var h = this.encrypt(text);
+//  if(h) return hex2b64(h); else return null;
+//}
+
+// protected
+RSAKey.prototype.doPublic = RSADoPublic;
+
+// public
+RSAKey.prototype.setPublic = RSASetPublic;
+RSAKey.prototype.encrypt = RSAEncrypt;
+RSAKey.prototype.encryptOAEP = RSAEncryptOAEP;
+//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
+
+RSAKey.prototype.type = "RSA";
diff --git a/js/rsa2.js b/js/rsa2.js
new file mode 100644 (file)
index 0000000..c470230
--- /dev/null
@@ -0,0 +1,246 @@
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
+// Depends on rsa.js and jsbn2.js
+
+// Version 1.1: support utf-8 decoding in pkcs1unpad2
+
+// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
+function pkcs1unpad2(d,n) {
+  var b = d.toByteArray();
+  var i = 0;
+  while(i < b.length && b[i] == 0) ++i;
+  if(b.length-i != n-1 || b[i] != 2)
+    return null;
+  ++i;
+  while(b[i] != 0)
+    if(++i >= b.length) return null;
+  var ret = "";
+  while(++i < b.length) {
+    var c = b[i] & 255;
+    if(c < 128) { // utf-8 decode
+      ret += String.fromCharCode(c);
+    }
+    else if((c > 191) && (c < 224)) {
+      ret += String.fromCharCode(((c & 31) << 6) | (b[i+1] & 63));
+      ++i;
+    }
+    else {
+      ret += String.fromCharCode(((c & 15) << 12) | ((b[i+1] & 63) << 6) | (b[i+2] & 63));
+      i += 2;
+    }
+  }
+  return ret;
+}
+
+// PKCS#1 (OAEP) mask generation function
+function oaep_mgf1_str(seed, len, hash)
+{
+    var mask = '', i = 0;
+
+    while (mask.length < len)
+    {
+        mask += hash(seed + String.fromCharCode.apply(String, [
+                (i & 0xff000000) >> 24,
+                (i & 0x00ff0000) >> 16,
+                (i & 0x0000ff00) >> 8,
+                i & 0x000000ff]));
+        i += 1;
+    }
+
+    return mask;
+}
+
+var SHA1_SIZE = 20;
+
+// Undo PKCS#1 (OAEP) padding and, if valid, return the plaintext
+function oaep_unpad(d, n, hash)
+{
+    d = d.toByteArray();
+
+    var i;
+
+    for (i = 0; i < d.length; i += 1)
+    {
+        d[i] &= 0xff;
+    }
+
+    while (d.length < n)
+    {
+        d.unshift(0);
+    }
+
+    d = String.fromCharCode.apply(String, d);
+
+    if (d.length < 2 * SHA1_SIZE + 2)
+    {
+        throw "Cipher too short";
+    }
+
+    var maskedSeed = d.substr(1, SHA1_SIZE)
+    var maskedDB = d.substr(SHA1_SIZE + 1);
+
+    var seedMask = oaep_mgf1_str(maskedDB, SHA1_SIZE, hash || rstr_sha1);
+    var seed = [], i;
+
+    for (i = 0; i < maskedSeed.length; i += 1)
+    {
+        seed[i] = maskedSeed.charCodeAt(i) ^ seedMask.charCodeAt(i);
+    }
+
+    var dbMask = oaep_mgf1_str(String.fromCharCode.apply(String, seed),
+                           d.length - SHA1_SIZE, rstr_sha1);
+
+    var DB = [];
+
+    for (i = 0; i < maskedDB.length; i += 1)
+    {
+        DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+    }
+
+    DB = String.fromCharCode.apply(String, DB);
+
+    if (DB.substr(0, SHA1_SIZE) !== rstr_sha1(''))
+    {
+        throw "Hash mismatch";
+    }
+
+    DB = DB.substr(SHA1_SIZE);
+
+    var first_one = DB.indexOf('\x01');
+    var last_zero = (first_one != -1) ? DB.substr(0, first_one).lastIndexOf('\x00') : -1;
+
+    if (last_zero + 1 != first_one)
+    {
+        throw "Malformed data";
+    }
+
+    return DB.substr(first_one + 1);
+}
+
+// Set the private key fields N, e, and d from hex strings
+function RSASetPrivate(N,E,D) {
+  this.isPrivate = true;
+  if (typeof N !== "string")
+  {
+    this.n = N;
+    this.e = E;
+    this.d = D;
+  }
+  else if(N != null && E != null && N.length > 0 && E.length > 0) {
+    this.n = parseBigInt(N,16);
+    this.e = parseInt(E,16);
+    this.d = parseBigInt(D,16);
+  }
+  else
+    alert("Invalid RSA private key");
+}
+
+// Set the private key fields N, e, d and CRT params from hex strings
+function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) {
+  this.isPrivate = true;
+  if (N == null) throw "RSASetPrivateEx N == null";
+  if (E == null) throw "RSASetPrivateEx E == null";
+  if (N.length == 0) throw "RSASetPrivateEx N.length == 0";
+  if (E.length == 0) throw "RSASetPrivateEx E.length == 0";
+
+  if (N != null && E != null && N.length > 0 && E.length > 0) {
+    this.n = parseBigInt(N,16);
+    this.e = parseInt(E,16);
+    this.d = parseBigInt(D,16);
+    this.p = parseBigInt(P,16);
+    this.q = parseBigInt(Q,16);
+    this.dmp1 = parseBigInt(DP,16);
+    this.dmq1 = parseBigInt(DQ,16);
+    this.coeff = parseBigInt(C,16);
+  } else {
+    alert("Invalid RSA private key in RSASetPrivateEx");
+  }
+}
+
+// Generate a new random private key B bits long, using public expt E
+function RSAGenerate(B,E) {
+  var rng = new SecureRandom();
+  var qs = B>>1;
+  this.e = parseInt(E,16);
+  var ee = new BigInteger(E,16);
+  for(;;) {
+    for(;;) {
+      this.p = new BigInteger(B-qs,1,rng);
+      if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break;
+    }
+    for(;;) {
+      this.q = new BigInteger(qs,1,rng);
+      if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break;
+    }
+    if(this.p.compareTo(this.q) <= 0) {
+      var t = this.p;
+      this.p = this.q;
+      this.q = t;
+    }
+    var p1 = this.p.subtract(BigInteger.ONE);  // p1 = p - 1
+    var q1 = this.q.subtract(BigInteger.ONE);  // q1 = q - 1
+    var phi = p1.multiply(q1);
+    if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
+      this.n = this.p.multiply(this.q);        // this.n = p * q
+      this.d = ee.modInverse(phi);     // this.d = 
+      this.dmp1 = this.d.mod(p1);      // this.dmp1 = d mod (p - 1)
+      this.dmq1 = this.d.mod(q1);      // this.dmq1 = d mod (q - 1)
+      this.coeff = this.q.modInverse(this.p);  // this.coeff = (q ^ -1) mod p
+      break;
+    }
+  }
+}
+
+// Perform raw private operation on "x": return x^d (mod n)
+function RSADoPrivate(x) {
+  if(this.p == null || this.q == null)
+    return x.modPow(this.d, this.n);
+
+  // TODO: re-calculate any missing CRT params
+  var xp = x.mod(this.p).modPow(this.dmp1, this.p); // xp=cp?
+  var xq = x.mod(this.q).modPow(this.dmq1, this.q); // xq=cq?
+
+  while(xp.compareTo(xq) < 0)
+    xp = xp.add(this.p);
+  // NOTE:
+  // xp.subtract(xq) => cp -cq
+  // xp.subtract(xq).multiply(this.coeff).mod(this.p) => (cp - cq) * u mod p = h
+  // xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq) => cq + (h * q) = M
+  return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
+}
+
+// Return the PKCS#1 RSA decryption of "ctext".
+// "ctext" is an even-length hex string and the output is a plain string.
+function RSADecrypt(ctext) {
+  var c = parseBigInt(ctext, 16);
+  var m = this.doPrivate(c);
+  if(m == null) return null;
+  return pkcs1unpad2(m, (this.n.bitLength()+7)>>3);
+}
+
+// Return the PKCS#1 OAEP RSA decryption of "ctext".
+// "ctext" is an even-length hex string and the output is a plain string.
+function RSADecryptOAEP(ctext, hash) {
+  var c = parseBigInt(ctext, 16);
+  var m = this.doPrivate(c);
+  if(m == null) return null;
+  return oaep_unpad(m, (this.n.bitLength()+7)>>3, hash);
+}
+
+// Return the PKCS#1 RSA decryption of "ctext".
+// "ctext" is a Base64-encoded string and the output is a plain string.
+//function RSAB64Decrypt(ctext) {
+//  var h = b64tohex(ctext);
+//  if(h) return this.decrypt(h); else return null;
+//}
+
+// protected
+RSAKey.prototype.doPrivate = RSADoPrivate;
+
+// public
+RSAKey.prototype.setPrivate = RSASetPrivate;
+RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
+RSAKey.prototype.generate = RSAGenerate;
+RSAKey.prototype.decrypt = RSADecrypt;
+RSAKey.prototype.decryptOAEP = RSADecryptOAEP;
+//RSAKey.prototype.b64_decrypt = RSAB64Decrypt;
diff --git a/js/rsapem-1.1.js b/js/rsapem-1.1.js
new file mode 100644 (file)
index 0000000..f7371ce
--- /dev/null
@@ -0,0 +1,102 @@
+/*! rsapem-1.1.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+//
+// rsa-pem.js - adding function for reading/writing PKCS#1 PEM private key
+//              to RSAKey class.
+//
+// version: 1.1.1 (2013-Apr-12)
+//
+// Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
+//
+// This software is licensed under the terms of the MIT License.
+// http://kjur.github.com/jsrsasign/license/
+//
+// The above copyright and license notice shall be 
+// included in all copies or substantial portions of the Software.
+// 
+//
+// Depends on:
+//
+//
+//
+// _RSApem_pemToBase64(sPEM)
+//
+//   removing PEM header, PEM footer and space characters including
+//   new lines from PEM formatted RSA private key string.
+//
+
+/**
+ * @fileOverview
+ * @name rsapem-1.1.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version 1.1
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ */
+function _rsapem_pemToBase64(sPEMPrivateKey) {
+  var s = sPEMPrivateKey;
+  s = s.replace("-----BEGIN RSA PRIVATE KEY-----", "");
+  s = s.replace("-----END RSA PRIVATE KEY-----", "");
+  s = s.replace(/[ \n]+/g, "");
+  return s;
+}
+
+function _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey) {
+  var a = new Array();
+  var v1 = ASN1HEX.getStartPosOfV_AtObj(hPrivateKey, 0);
+  var n1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, v1);
+  var e1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, n1);
+  var d1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, e1);
+  var p1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, d1);
+  var q1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, p1);
+  var dp1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, q1);
+  var dq1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, dp1);
+  var co1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, dq1);
+  a.push(v1, n1, e1, d1, p1, q1, dp1, dq1, co1);
+  return a;
+}
+
+function _rsapem_getHexValueArrayOfChildrenFromHex(hPrivateKey) {
+  var posArray = _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey);
+  var v =  ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[0]);
+  var n =  ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[1]);
+  var e =  ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[2]);
+  var d =  ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[3]);
+  var p =  ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[4]);
+  var q =  ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[5]);
+  var dp = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[6]);
+  var dq = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[7]);
+  var co = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[8]);
+  var a = new Array();
+  a.push(v, n, e, d, p, q, dp, dq, co);
+  return a;
+}
+
+/**
+ * read RSA private key from a ASN.1 hexadecimal string
+ * @name readPrivateKeyFromASN1HexString
+ * @memberOf RSAKey#
+ * @function
+ * @param {String} keyHex ASN.1 hexadecimal string of PKCS#1 private key.
+ * @since 1.1.1
+ */
+function _rsapem_readPrivateKeyFromASN1HexString(keyHex) {
+  var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex);
+  this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]);
+}
+
+/**
+ * read PKCS#1 private key from a string
+ * @name readPrivateKeyFromPEMString
+ * @memberOf RSAKey#
+ * @function
+ * @param {String} keyPEM string of PKCS#1 private key.
+ */
+function _rsapem_readPrivateKeyFromPEMString(keyPEM) {
+  var keyB64 = _rsapem_pemToBase64(keyPEM);
+  var keyHex = b64tohex(keyB64) // depends base64.js
+  var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex);
+  this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]);
+}
+
+RSAKey.prototype.readPrivateKeyFromPEMString = _rsapem_readPrivateKeyFromPEMString;
+RSAKey.prototype.readPrivateKeyFromASN1HexString = _rsapem_readPrivateKeyFromASN1HexString;
diff --git a/js/rsasign-1.2.js b/js/rsasign-1.2.js
new file mode 100644 (file)
index 0000000..1a3c3cb
--- /dev/null
@@ -0,0 +1,456 @@
+/*! rsasign-1.2.7.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+/*
+ * rsa-sign.js - adding signing functions to RSAKey class.
+ *
+ * version: 1.2.7 (2013 Aug 25)
+ *
+ * Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
+ *
+ * This software is licensed under the terms of the MIT License.
+ * http://kjur.github.com/jsrsasign/license/
+ *
+ * The above copyright and license notice shall be 
+ * included in all copies or substantial portions of the Software.
+ */
+
+/**
+ * @fileOverview
+ * @name rsasign-1.2.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version rsasign 1.2.7
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ */
+
+var _RE_HEXDECONLY = new RegExp("");
+_RE_HEXDECONLY.compile("[^0-9a-f]", "gi");
+
+// ========================================================================
+// Signature Generation
+// ========================================================================
+
+function _rsasign_getHexPaddedDigestInfoForString(s, keySize, hashAlg) {
+    var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); };
+    var sHashHex = hashFunc(s);
+
+    return KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, keySize);
+}
+
+function _zeroPaddingOfSignature(hex, bitLength) {
+    var s = "";
+    var nZero = bitLength / 4 - hex.length;
+    for (var i = 0; i < nZero; i++) {
+       s = s + "0";
+    }
+    return s + hex;
+}
+
+/**
+ * sign for a message string with RSA private key.<br/>
+ * @name signString
+ * @memberOf RSAKey
+ * @function
+ * @param {String} s message string to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.<br/>
+ * @return returns hexadecimal string of signature value.
+ */
+function _rsasign_signString(s, hashAlg) {
+    var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); };
+    var sHashHex = hashFunc(s);
+
+    return this.signWithMessageHash(sHashHex, hashAlg);
+}
+
+/**
+ * sign hash value of message to be signed with RSA private key.<br/>
+ * @name signWithMessageHash
+ * @memberOf RSAKey
+ * @function
+ * @param {String} sHashHex hexadecimal string of hash value of message to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.<br/>
+ * @return returns hexadecimal string of signature value.
+ * @since rsasign 1.2.6
+ */
+function _rsasign_signWithMessageHash(sHashHex, hashAlg) {
+    var hPM = KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, this.n.bitLength());
+    var biPaddedMessage = parseBigInt(hPM, 16);
+    var biSign = this.doPrivate(biPaddedMessage);
+    var hexSign = biSign.toString(16);
+    return _zeroPaddingOfSignature(hexSign, this.n.bitLength());
+}
+
+function _rsasign_signStringWithSHA1(s) {
+    return _rsasign_signString.call(this, s, 'sha1');
+}
+
+function _rsasign_signStringWithSHA256(s) {
+    return _rsasign_signString.call(this, s, 'sha256');
+}
+
+// PKCS#1 (PSS) mask generation function
+function pss_mgf1_str(seed, len, hash) {
+    var mask = '', i = 0;
+
+    while (mask.length < len) {
+        mask += hextorstr(hash(rstrtohex(seed + String.fromCharCode.apply(String, [
+                (i & 0xff000000) >> 24,
+                (i & 0x00ff0000) >> 16,
+                (i & 0x0000ff00) >> 8,
+                i & 0x000000ff]))));
+        i += 1;
+    }
+
+    return mask;
+}
+
+/**
+ * sign for a message string with RSA private key by PKCS#1 PSS signing.<br/>
+ * @name signStringPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} s message string to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ *        There are two special values:
+ *        <ul>
+ *        <li>-1: sets the salt length to the digest length</li>
+ *        <li>-2: sets the salt length to maximum permissible value
+ *           (i.e. keybytelen - hashbytelen - 2)</li>
+ *        </ul>
+ *        DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns hexadecimal string of signature value.
+ */
+function _rsasign_signStringPSS(s, hashAlg, sLen) {
+    var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); } 
+    var hHash = hashFunc(rstrtohex(s));
+
+    if (sLen === undefined) sLen = -1;
+    return this.signWithMessageHashPSS(hHash, hashAlg, sLen);
+}
+
+/**
+ * sign hash value of message with RSA private key by PKCS#1 PSS signing.<br/>
+ * @name signWithMessageHashPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} hHash hexadecimal hash value of message to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ *        There are two special values:
+ *        <ul>
+ *        <li>-1: sets the salt length to the digest length</li>
+ *        <li>-2: sets the salt length to maximum permissible value
+ *           (i.e. keybytelen - hashbytelen - 2)</li>
+ *        </ul>
+ *        DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns hexadecimal string of signature value.
+ * @since rsasign 1.2.6
+ */
+function _rsasign_signWithMessageHashPSS(hHash, hashAlg, sLen) {
+    var mHash = hextorstr(hHash);
+    var hLen = mHash.length;
+    var emBits = this.n.bitLength() - 1;
+    var emLen = Math.ceil(emBits / 8);
+    var i;
+    var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); } 
+
+    if (sLen === -1 || sLen === undefined) {
+        sLen = hLen; // same as hash length
+    } else if (sLen === -2) {
+        sLen = emLen - hLen - 2; // maximum
+    } else if (sLen < -2) {
+        throw "invalid salt length";
+    }
+
+    if (emLen < (hLen + sLen + 2)) {
+        throw "data too long";
+    }
+
+    var salt = '';
+
+    if (sLen > 0) {
+        salt = new Array(sLen);
+        new SecureRandom().nextBytes(salt);
+        salt = String.fromCharCode.apply(String, salt);
+    }
+
+    var H = hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash + salt)));
+    var PS = [];
+
+    for (i = 0; i < emLen - sLen - hLen - 2; i += 1) {
+        PS[i] = 0x00;
+    }
+
+    var DB = String.fromCharCode.apply(String, PS) + '\x01' + salt;
+    var dbMask = pss_mgf1_str(H, DB.length, hashFunc);
+    var maskedDB = [];
+
+    for (i = 0; i < DB.length; i += 1) {
+        maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+    }
+
+    var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff;
+    maskedDB[0] &= ~mask;
+
+    for (i = 0; i < hLen; i++) {
+        maskedDB.push(H.charCodeAt(i));
+    }
+
+    maskedDB.push(0xbc);
+
+    return _zeroPaddingOfSignature(this.doPrivate(new BigInteger(maskedDB)).toString(16),
+                                  this.n.bitLength());
+}
+
+// ========================================================================
+// Signature Verification
+// ========================================================================
+
+function _rsasign_getDecryptSignatureBI(biSig, hN, hE) {
+    var rsa = new RSAKey();
+    rsa.setPublic(hN, hE);
+    var biDecryptedSig = rsa.doPublic(biSig);
+    return biDecryptedSig;
+}
+
+function _rsasign_getHexDigestInfoFromSig(biSig, hN, hE) {
+    var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE);
+    var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+    return hDigestInfo;
+}
+
+function _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo) {
+    for (var algName in KJUR.crypto.Util.DIGESTINFOHEAD) {
+       var head = KJUR.crypto.Util.DIGESTINFOHEAD[algName];
+       var len = head.length;
+       if (hDigestInfo.substring(0, len) == head) {
+           var a = [algName, hDigestInfo.substring(len)];
+           return a;
+       }
+    }
+    return [];
+}
+
+function _rsasign_verifySignatureWithArgs(sMsg, biSig, hN, hE) {
+    var hDigestInfo = _rsasign_getHexDigestInfoFromSig(biSig, hN, hE);
+    var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+    if (digestInfoAry.length == 0) return false;
+    var algName = digestInfoAry[0];
+    var diHashValue = digestInfoAry[1];
+    var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); };
+    var msgHashValue = ff(sMsg);
+    return (diHashValue == msgHashValue);
+}
+
+function _rsasign_verifyHexSignatureForMessage(hSig, sMsg) {
+    var biSig = parseBigInt(hSig, 16);
+    var result = _rsasign_verifySignatureWithArgs(sMsg, biSig,
+                                                 this.n.toString(16),
+                                                 this.e.toString(16));
+    return result;
+}
+
+/**
+ * verifies a sigature for a message string with RSA public key.<br/>
+ * @name verifyString
+ * @memberOf RSAKey#
+ * @function
+ * @param {String} sMsg message string to be verified.
+ * @param {String} hSig hexadecimal string of siganture.<br/>
+ *                 non-hexadecimal charactors including new lines will be ignored.
+ * @return returns 1 if valid, otherwise 0
+ */
+function _rsasign_verifyString(sMsg, hSig) {
+    hSig = hSig.replace(_RE_HEXDECONLY, '');
+    hSig = hSig.replace(/[ \n]+/g, "");
+    var biSig = parseBigInt(hSig, 16);
+    if (biSig.bitLength() > this.n.bitLength()) return 0;
+    var biDecryptedSig = this.doPublic(biSig);
+    var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+    var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+  
+    if (digestInfoAry.length == 0) return false;
+    var algName = digestInfoAry[0];
+    var diHashValue = digestInfoAry[1];
+    var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); };
+    var msgHashValue = ff(sMsg);
+    return (diHashValue == msgHashValue);
+}
+
+/**
+ * verifies a sigature for a message string with RSA public key.<br/>
+ * @name verifyWithMessageHash
+ * @memberOf RSAKey
+ * @function
+ * @param {String} sHashHex hexadecimal hash value of message to be verified.
+ * @param {String} hSig hexadecimal string of siganture.<br/>
+ *                 non-hexadecimal charactors including new lines will be ignored.
+ * @return returns 1 if valid, otherwise 0
+ * @since rsasign 1.2.6
+ */
+function _rsasign_verifyWithMessageHash(sHashHex, hSig) {
+    hSig = hSig.replace(_RE_HEXDECONLY, '');
+    hSig = hSig.replace(/[ \n]+/g, "");
+    var biSig = parseBigInt(hSig, 16);
+    if (biSig.bitLength() > this.n.bitLength()) return 0;
+    var biDecryptedSig = this.doPublic(biSig);
+    var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+    var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+  
+    if (digestInfoAry.length == 0) return false;
+    var algName = digestInfoAry[0];
+    var diHashValue = digestInfoAry[1];
+    return (diHashValue == sHashHex);
+}
+
+/**
+ * verifies a sigature for a message string with RSA public key by PKCS#1 PSS sign.<br/>
+ * @name verifyStringPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} sMsg message string to be verified.
+ * @param {String} hSig hexadecimal string of signature value
+ * @param {String} hashAlg hash algorithm name
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ *        There are two special values:
+ *        <ul>
+ *        <li>-1: sets the salt length to the digest length</li>
+ *        <li>-2: sets the salt length to maximum permissible value
+ *           (i.e. keybytelen - hashbytelen - 2)</li>
+ *        </ul>
+ *        DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns true if valid, otherwise false
+ */
+function _rsasign_verifyStringPSS(sMsg, hSig, hashAlg, sLen) {
+    var hashFunc = function(sHex) { ret