Added feature #11016: Implement RSA authentication for BE and FE -- part 1 (add rsaau...
authorDmitry Dulepov <dmitry.dulepov@gmail.com>
Mon, 4 May 2009 14:25:58 +0000 (14:25 +0000)
committerDmitry Dulepov <dmitry.dulepov@gmail.com>
Mon, 4 May 2009 14:25:58 +0000 (14:25 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@5386 709f56b5-9817-0410-a4d7-c38de5d9e867

33 files changed:
ChangeLog
typo3/sysext/rsaauth/ChangeLog [new file with mode: 0644]
typo3/sysext/rsaauth/doc/manual.sxw [new file with mode: 0644]
typo3/sysext/rsaauth/ext_conf_template.txt [new file with mode: 0644]
typo3/sysext/rsaauth/ext_emconf.php [new file with mode: 0644]
typo3/sysext/rsaauth/ext_icon.gif [new file with mode: 0644]
typo3/sysext/rsaauth/ext_localconf.php [new file with mode: 0644]
typo3/sysext/rsaauth/ext_tables.php [new file with mode: 0644]
typo3/sysext/rsaauth/ext_tables.sql [new file with mode: 0644]
typo3/sysext/rsaauth/hooks/class.tx_rsaauth_backendwarnings.php [new file with mode: 0644]
typo3/sysext/rsaauth/hooks/class.tx_rsaauth_feloginhook.php [new file with mode: 0644]
typo3/sysext/rsaauth/hooks/class.tx_rsaauth_loginformhook.php [new file with mode: 0644]
typo3/sysext/rsaauth/hooks/locallang.xml [new file with mode: 0644]
typo3/sysext/rsaauth/resources/jsbn/LICENSE [new file with mode: 0644]
typo3/sysext/rsaauth/resources/jsbn/base64.js [new file with mode: 0644]
typo3/sysext/rsaauth/resources/jsbn/jsbn.js [new file with mode: 0644]
typo3/sysext/rsaauth/resources/jsbn/jsbn2.js [new file with mode: 0644]
typo3/sysext/rsaauth/resources/jsbn/prng4.js [new file with mode: 0644]
typo3/sysext/rsaauth/resources/jsbn/rng.js [new file with mode: 0644]
typo3/sysext/rsaauth/resources/jsbn/rsa.js [new file with mode: 0644]
typo3/sysext/rsaauth/resources/jsbn/rsa2.js [new file with mode: 0644]
typo3/sysext/rsaauth/resources/rsaauth.js [new file with mode: 0644]
typo3/sysext/rsaauth/resources/rsaauth_min.js [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_abstract_backend.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_backendfactory.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_cmdline_backend.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_keypair.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_php_backend.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/class.tx_rsaauth_sv1.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_abstract_storage.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_session_storage.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_split_storage.php [new file with mode: 0644]
typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_storagefactory.php [new file with mode: 0644]

index 7c37cee..9ac1b35 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-04-05  Dmitry Dulepov  <dmitry.dulepov@gmail.com>
+
+       * Added feature #11016: Implement RSA authentication for BE and FE -- part 1 (add rsaauth system extension)
+
 2009-05-03  Oliver Hader  <oliver@typo3.org>
 
        * Fixed bug #11024: Typo issue in t3lib_cache_backend_DbBackend (thanks to Xavier Perseguers)
diff --git a/typo3/sysext/rsaauth/ChangeLog b/typo3/sysext/rsaauth/ChangeLog
new file mode 100644 (file)
index 0000000..56121e8
--- /dev/null
@@ -0,0 +1,37 @@
+2009-04-30  Dmitry Dulepov  <dmitry@typo3.org>
+
+       * Increased service priority to make it run before t3sec_saltedpw
+
+2009-04-28  Dmitry Dulepov  <dmitry@typo3.org>
+
+       * Added a method to set an alternative storage to the storage factory
+       * Do not use expired keys in the split storage
+       * Rename publicKey to publicKeyModulus in the keypair class
+       * Fixed: wrong unserializaion of the extension configuration
+       * Added a configuration check and warnings in the backend if the check fails
+       * Added a warning if command line backend is used
+       * Move login JavaScript addition to a separate hook
+       * Updated core patch, fixed some JavaScript errors on the login page
+
+2009-04-13  Dmitry Dulepov  <dmitry@typo3.org>
+
+       * Added a "split" storage for keys
+       * Fix XCLASS definitions
+       * Added FE authentication
+       * Added manual
+
+2009-04-03  Dmitry Dulepov  <dmitry@typo3.org>
+
+       * Added a factory for backends
+       * Added a factory for storages
+
+2009-04-03  Dmitry Dulepov  <dmitry@typo3.org>
+
+       * The service is working for BE users
+       * Change priority for the service from 100 to 55
+       * Added core patch to extras/ directory
+       * Fix command line backend for openssl versions with different base64 syntax
+
+2009-03-16  Dmitry Dulepov  <dmitry@typo3.org>
+
+       * Extension is generated and initial work is performed
diff --git a/typo3/sysext/rsaauth/doc/manual.sxw b/typo3/sysext/rsaauth/doc/manual.sxw
new file mode 100644 (file)
index 0000000..7048485
Binary files /dev/null and b/typo3/sysext/rsaauth/doc/manual.sxw differ
diff --git a/typo3/sysext/rsaauth/ext_conf_template.txt b/typo3/sysext/rsaauth/ext_conf_template.txt
new file mode 100644 (file)
index 0000000..e66a206
--- /dev/null
@@ -0,0 +1,2 @@
+# cat=basic/enable; type=string; label=Path to the temporary directory:This directory will contain temporary files for encryption/decrytion. It should be located outside of the web site root and it should not be /tmp. Directory permissions should be 0700 and the owner should be the web server user. If left empty, typo3conf/ will be used and it is INSECURE! Make sure that the directory is included into open_basedir if you use this setting.
+temporaryDirectory =
diff --git a/typo3/sysext/rsaauth/ext_emconf.php b/typo3/sysext/rsaauth/ext_emconf.php
new file mode 100644 (file)
index 0000000..31a802d
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+########################################################################
+# Extension Manager/Repository config file for ext: "rsaauth"
+#
+# Auto generated 16-03-2009 11:20
+#
+# Manual updates:
+# Only the data in the array - anything else is removed by next write.
+# "version" and "dependencies" must not be touched!
+########################################################################
+
+$EM_CONF[$_EXTKEY] = array(
+       'title' => 'RSA authentication for TYPO3',
+       'description' => 'Contains a service to authenticate TYPO3 BE and FE users using private/public key encryption of passwords',
+       'category' => 'services',
+       'author' => 'Dmitry Dulepov',
+       'author_email' => 'dmitry@typo3.org',
+       'shy' => '',
+       'dependencies' => '',
+       'conflicts' => '',
+       'priority' => '',
+       'module' => '',
+       'state' => 'beta',
+       'internal' => '',
+       'uploadfolder' => 0,
+       'createDirs' => '',
+       'modify_tables' => '',
+       'clearCacheOnLoad' => 0,
+       'lockType' => '',
+       'author_company' => '',
+       'version' => '0.0.0',
+       'constraints' => array(
+               'depends' => array(
+               ),
+               'conflicts' => array(
+               ),
+               'suggests' => array(
+               ),
+       ),
+       '_md5_values_when_last_written' => 'a:6:{s:9:"ChangeLog";s:4:"5c50";s:12:"ext_icon.gif";s:4:"1bdc";s:17:"ext_localconf.php";s:4:"0665";s:19:"doc/wizard_form.dat";s:4:"81c1";s:20:"doc/wizard_form.html";s:4:"9542";s:28:"sv1/class.tx_rsaauth_sv1.php";s:4:"9972";}',
+);
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/ext_icon.gif b/typo3/sysext/rsaauth/ext_icon.gif
new file mode 100644 (file)
index 0000000..9161205
Binary files /dev/null and b/typo3/sysext/rsaauth/ext_icon.gif differ
diff --git a/typo3/sysext/rsaauth/ext_localconf.php b/typo3/sysext/rsaauth/ext_localconf.php
new file mode 100644 (file)
index 0000000..a8e6909
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+if (!defined ('TYPO3_MODE')) {
+       die ('Access denied.');
+}
+
+// Add the service
+t3lib_extMgm::addService($_EXTKEY,  'auth' /* sv type */,  'tx_rsaauth_sv1' /* sv key */,
+       array(
+               'title' => 'RSA authentication',
+               'description' => 'Authenticates users by using encrypted passwords',
+
+               'subtype' => 'getUserBE,authUserBE,getUserFE,authUserFE',
+
+               'available' => TRUE,
+               'priority' => 60,       // tx_svauth_sv1 has 50, t3sec_saltedpw has 55. This service must have higher priority!
+               'quality' => 60,        // tx_svauth_sv1 has 50. This service must have higher quality!
+
+               'os' => '',
+               'exec' => '',           // Do not put a dependency on openssh here or service loading will fail!
+
+               'classFile' => t3lib_extMgm::extPath($_EXTKEY) . 'sv1/class.tx_rsaauth_sv1.php',
+               'className' => 'tx_rsaauth_sv1',
+       )
+);
+
+// Add a hook to the BE login form
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginFormHook'][$_EXTKEY] = 'EXT:' . $_EXTKEY . '/hooks/class.tx_rsaauth_loginformhook.php:tx_rsaauth_loginformhook->getLoginFormTag';
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginScriptHook'][$_EXTKEY] = 'EXT:' . $_EXTKEY . '/hooks/class.tx_rsaauth_loginformhook.php:tx_rsaauth_loginformhook->getLoginScripts';
+
+// Add a hook to the FE login form (felogin system extension)
+$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['felogin']['loginFormOnSubmitFuncs'][$_EXTKEY] = 'EXT:' . $_EXTKEY . '/hooks/class.tx_rsaauth_feloginhook.php:tx_rsaauth_feloginhook->loginFormHook';
+
+// Add a hook to show Backend warnings
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['displayWarningMessages'][$_EXTKEY] = 'EXT:' . $_EXTKEY . '/hooks/class.tx_rsaauth_backendwarnings.php:tx_rsaauth_backendwarnings';
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/ext_tables.php b/typo3/sysext/rsaauth/ext_tables.php
new file mode 100644 (file)
index 0000000..5438816
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+if (!defined ('TYPO3_MODE')) {
+       die('Access denied.');
+}
+
+// Define the table for keys. Make sure that it cannot be edited or seen by
+// any user in any way.
+$TCA['tx_rsaauth_keys'] = array (
+       'ctrl' => array (
+               'adminOnly' => true,
+               'hideTable' => true,
+               'is_static' => true,
+               'label' => 'uid',
+               'readOnly' => true,
+               'rootLevel' => 1,
+               'title' => 'Oops! You should not see this!'
+       ),
+       'columns' => array(
+       ),
+       'types' => array(
+               '0' => array(
+                       'showitem' => ''
+               )
+       )
+);
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/ext_tables.sql b/typo3/sysext/rsaauth/ext_tables.sql
new file mode 100644 (file)
index 0000000..f61474f
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Table structure for table 'tx_rsauth_keys'
+#
+CREATE TABLE tx_rsaauth_keys (
+       uid int(11) NOT NULL auto_increment,
+       pid int(11) DEFAULT '0' NOT NULL,
+       crdate int(11) DEFAULT '0' NOT NULL,
+       key_value text,
+
+       PRIMARY KEY (uid),
+       KEY crdate (crdate)
+);
diff --git a/typo3/sysext/rsaauth/hooks/class.tx_rsaauth_backendwarnings.php b/typo3/sysext/rsaauth/hooks/class.tx_rsaauth_backendwarnings.php
new file mode 100644 (file)
index 0000000..84e5fbc
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_backendwarnings.php 19635 2009-04-28 13:54:40Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/backends/class.tx_rsaauth_backendfactory.php'));
+
+/**
+ * This class contains a hook to the backend warnings collection. It checks
+ * RSA configuration and create a warning if the configuration is wrong.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+
+class tx_rsaauth_backendwarnings {
+
+       /**
+        * Checks RSA configuration and creates warnings if necessary.
+        *
+        * @param       array   $warnings       Warnings
+        * @return      void
+        * @see t3lib_BEfunc::displayWarningMessages()
+        */
+       public function displayWarningMessages_postProcess(array &$warnings) {
+               $backend = tx_rsaauth_backendfactory::getBackend();
+               if ($backend instanceof tx_rsaauth_cmdline_backend) {
+
+                       // Not using the PHP extension!
+                       $warnings['rsaauth_cmdline'] = $GLOBALS['LANG']->sL('LLL:EXT:rsaauth/hooks/locallang.xml:hook_using_cmdline');
+
+                       // Check the path
+                       $extconf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['rsaauth']);
+                       $path = trim($extconf['temporaryDirectory']);
+                       if ($path == '') {
+                               // Path is empty
+                               $warnings['rsaauth'] = $GLOBALS['LANG']->sL('LLL:EXT:rsaauth/hooks/locallang.xml:hook_empty_directory');
+                       }
+                       elseif (!t3lib_div::isAbsPath($path)) {
+                               // Path is not absolute
+                               $warnings['rsaauth'] = $GLOBALS['LANG']->sL('LLL:EXT:rsaauth/hooks/locallang.xml:hook_directory_not_absolute');
+                       }
+                       elseif (!@is_dir($path)) {
+                               // Path does not represent a directory
+                               $warnings['rsaauth'] = $GLOBALS['LANG']->sL('LLL:EXT:rsaauth/hooks/locallang.xml:hook_directory_not_exist');
+                       }
+                       elseif (!@is_writable($path)) {
+                               // Directory is not writable
+                               $warnings['rsaauth'] = $GLOBALS['LANG']->sL('LLL:EXT:rsaauth/hooks/locallang.xml:hook_directory_not_writable');
+                       }
+                       elseif (substr($path, 0, strlen(PATH_site)) == PATH_site) {
+                               // Directory is inside the site root
+                               $warnings['rsaauth'] = $GLOBALS['LANG']->sL('LLL:EXT:rsaauth/hooks/locallang.xml:hook_directory_inside_siteroot');
+                       }
+               }
+       }
+
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/hooks/class.tx_rsaauth_backendwarnings.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/hooks/class.tx_rsaauth_backendwarnings.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/hooks/class.tx_rsaauth_feloginhook.php b/typo3/sysext/rsaauth/hooks/class.tx_rsaauth_feloginhook.php
new file mode 100644 (file)
index 0000000..b3efaf0
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/backends/class.tx_rsaauth_backendfactory.php');
+require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/storage/class.tx_rsaauth_storagefactory.php');
+
+/**
+ * This class contains a hook to implement RSA authentication for the TYPO3
+ * Frontend. Warning: felogin must be USER_INT for this to work!
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_feloginhook {
+
+       /**
+        * Hooks to the felogin extension to provide additional code for FE login
+        *
+        * @return      array   0 => onSubmit function, 1 => extra fields and required files
+        */
+       public function loginFormHook() {
+               $result = array(0 => '', 1 => '');
+
+               if ($GLOBALS['TYPO3_CONF_VARS']['FE']['loginSecurityLevel'] == 'rsa') {
+                       $backend = tx_rsaauth_backendfactory::getBackend();
+                       if ($backend) {
+                               $result[0] = 'tx_rsaauth_feencrypt(this);';
+
+                               $javascriptPath = t3lib_extMgm::siteRelPath('rsaauth') . 'resources/';
+                               $files = array(
+                                       'jsbn/jsbn.js',
+                                       'jsbn/prng4.js',
+                                       'jsbn/rng.js',
+                                       'jsbn/rsa.js',
+                                       'jsbn/base64.js',
+                                       'rsaauth_min.js'
+                               );
+
+                               foreach ($files as $file) {
+                                       $result[1] .= '<script type="text/javascript" src="' .
+                                               t3lib_div::getIndpEnv('TYPO3_SITE_URL') .
+                                               $javascriptPath . $file . '"></script>';
+                               }
+
+                               // Generate a new key pair
+                               $keyPair = $backend->createNewKeyPair();
+
+                               // Save private key
+                               $storage = tx_rsaauth_storagefactory::getStorage();
+                               /* @var $storage tx_rsaauth_abstract_storage */
+                               $storage->put($keyPair->getPrivateKey());
+
+                               // Add RSA hidden fields
+                               $result[1] .= '<input type="hidden" id="rsa_n" name="n" value="' . htmlspecialchars($keyPair->getPublicKeyModulus()) . '" />';
+                               $result[1] .= '<input type="hidden" id="rsa_e" name="e" value="' . sprintf('%x', $keyPair->getExponent()) . '" />';
+                       }
+               }
+               return $result;
+       }
+
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/hooks/class.tx_rsaauth_feloginhook.php'])     {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/hooks/class.tx_rsaauth_feloginhook.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/hooks/class.tx_rsaauth_loginformhook.php b/typo3/sysext/rsaauth/hooks/class.tx_rsaauth_loginformhook.php
new file mode 100644 (file)
index 0000000..824a15d
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_loginformhook.php 19655 2009-04-28 16:01:58Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/backends/class.tx_rsaauth_backendfactory.php');
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/storage/class.tx_rsaauth_storagefactory.php'));
+
+/**
+ * This class provides a hook to the login form to add extra javascript code
+ * and supply a proper form tag.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_loginformhook {
+
+       /**
+        * Adds RSA-specific JavaScript and returns a form tag
+        *
+        * @return      string  Form tag
+        */
+       public function getLoginFormTag(array $params, SC_index& $pObj) {
+               $form = null;
+               if ($pObj->loginSecurityLevel == 'rsa') {
+
+                       // If we can get the backend, we can proceed
+                       $backend = tx_rsaauth_backendfactory::getBackend();
+                       if (!is_null($backend)) {
+
+                               // Add form tag
+                               $form = '<form action="index.php" method="post" name="loginform" onsubmit="tx_rsaauth_encrypt();">';
+
+                               // Generate a new key pair
+                               $keyPair = $backend->createNewKeyPair();
+
+                               // Save private key
+                               $storage = tx_rsaauth_storagefactory::getStorage();
+                               /* @var $storage tx_rsaauth_abstract_storage */
+                               $storage->put($keyPair->getPrivateKey());
+
+                               // Add RSA hidden fields
+                               $form .= '<input type="hidden" id="rsa_n" name="n" value="' . htmlspecialchars($keyPair->getPublicKeyModulus()) . '" />';
+                               $form .= '<input type="hidden" id="rsa_e" name="e" value="' . sprintf('%x', $keyPair->getExponent()) . '" />';
+                       }
+               }
+               return $form;
+       }
+
+
+       /**
+        * Provides form code for the superchallenged authentication.
+        *
+        * @param       array   $params Parameters to the script
+        * @param       SC_index        $pObj   Calling object
+        * @return      string  The code for the login form
+        */
+       public function getLoginScripts(array $params, SC_index &$pObj) {
+               $content = '';
+
+               if ($pObj->loginSecurityLevel == 'rsa') {
+                       $javascriptPath = t3lib_extMgm::siteRelPath('rsaauth') . 'resources/';
+                       $files = array(
+                               'jsbn/jsbn.js',
+                               'jsbn/prng4.js',
+                               'jsbn/rng.js',
+                               'jsbn/rsa.js',
+                               'jsbn/base64.js',
+                               'rsaauth_min.js'
+                       );
+
+                       $content = '';
+                       foreach ($files as $file) {
+                               $content .= '<script type="text/javascript" src="' .
+                                       t3lib_div::getIndpEnv('TYPO3_SITE_URL') .
+                                       $javascriptPath . $file . '"></script>';
+                       }
+               }
+
+               return $content;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/hooks/class.tx_rsaauth_loginformhook.php'])   {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/hooks/class.tx_rsaauth_loginformhook.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/hooks/locallang.xml b/typo3/sysext/rsaauth/hooks/locallang.xml
new file mode 100644 (file)
index 0000000..9be2c01
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+       <meta type="array">
+               <type>module</type>
+               <description>Language labels for the backend warning hook of the rsaauth extension</description>
+       </meta>
+       <data type="array">
+               <languageKey index="default" type="array">
+                       <label index="hook_empty_directory">RSA temporary directory is empty. This is a security risk. Please, go to the Extension Manager, select the "RSA authentication" extension and set the directory according to instructions there.</label>
+                       <label index="hook_directory_not_absolute">RSA temporary directory path is not absolute. This may cause RSA authentication errors and it is a security risk. Please, go to the Extension Manager, select the "RSA authentication" extension and set the directory according to instructions there.</label>
+                       <label index="hook_directory_not_exist">RSA temporary directory does not exist. This is a security risk because an insecure directory will be used for RSA operations. Please, go to the Extension Manager, select the "RSA authentication" extension and set the directory according to instructions there.</label>
+                       <label index="hook_directory_not_writable">RSA temporary directory is not writable. This may cause RSA authentication errors and it is a security risk. Please, go to the Extension Manager, select the "RSA authentication" extension and set the directory according to instructions there.</label>
+                       <label index="hook_directory_inside_siteroot">RSA temporary directory is inside the web site root directory. This is a security risk. Please, go to the Extension Manager, select the "RSA authentication" extension and set the directory according to instructions there.</label>
+                       <label index="hook_using_cmdline">RSA authentication could not detect the openssl PHP extension. Currently RSA authentication uses a command line openssl utility. This is slower and less secure. Please, consider installing openssl PHP extension.</label>
+               </languageKey>
+       </data>
+</T3locallang>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/resources/jsbn/LICENSE b/typo3/sysext/rsaauth/resources/jsbn/LICENSE
new file mode 100644 (file)
index 0000000..24502a9
--- /dev/null
@@ -0,0 +1,40 @@
+Licensing
+---------
+
+This software is covered under the following copyright:
+
+/*
+ * Copyright (c) 2003-2005  Tom Wu
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ *
+ * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
+ * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * In addition, the following condition applies:
+ *
+ * All redistributions must retain an intact copy of this copyright notice
+ * and disclaimer.
+ */
+
+Address all questions regarding this license to:
+
+  Tom Wu
+  tjw@cs.Stanford.EDU
diff --git a/typo3/sysext/rsaauth/resources/jsbn/base64.js b/typo3/sysext/rsaauth/resources/jsbn/base64.js
new file mode 100644 (file)
index 0000000..f5b168c
--- /dev/null
@@ -0,0 +1,71 @@
+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);
+  }
+  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;
+  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/typo3/sysext/rsaauth/resources/jsbn/jsbn.js b/typo3/sysext/rsaauth/resources/jsbn/jsbn.js
new file mode 100644 (file)
index 0000000..928cc4f
--- /dev/null
@@ -0,0 +1,559 @@
+// 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 && (navigator.appName == "Microsoft Internet Explorer")) {
+  BigInteger.prototype.am = am2;
+  dbits = 30;
+}
+else if(j_lm && (navigator.appName != "Netscape")) {
+  BigInteger.prototype.am = am1;
+  dbits = 26;
+}
+else { // Mozilla/Netscape seems to prefer am3
+  BigInteger.prototype.am = am3;
+  dbits = 28;
+}
+
+BigInteger.prototype.DB = dbits;
+BigInteger.prototype.DM = ((1<<dbits)-1);
+BigInteger.prototype.DV = (1<<dbits);
+
+var BI_FP = 52;
+BigInteger.prototype.FV = Math.pow(2,BI_FP);
+BigInteger.prototype.F1 = BI_FP-dbits;
+BigInteger.prototype.F2 = 2*dbits-BI_FP;
+
+// Digit conversions
+var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
+var BI_RC = new Array();
+var rr,vv;
+rr = "0".charCodeAt(0);
+for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
+rr = "a".charCodeAt(0);
+for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
+rr = "A".charCodeAt(0);
+for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
+
+function int2char(n) { return BI_RM.charAt(n); }
+function intAt(s,i) {
+  var c = BI_RC[s.charCodeAt(i)];
+  return (c==null)?-1:c;
+}
+
+// (protected) copy this to r
+function bnpCopyTo(r) {
+  for(var i = this.t-1; i >= 0; --i) r[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+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 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/typo3/sysext/rsaauth/resources/jsbn/jsbn2.js b/typo3/sysext/rsaauth/resources/jsbn/jsbn2.js
new file mode 100644 (file)
index 0000000..cad0d7b
--- /dev/null
@@ -0,0 +1,645 @@
+// Copyright (c) 2005  Tom Wu
+// All Rights Reserved.
+// See "LICENSE" for details.
+
+// Extended JavaScript BN functions, required for RSA private ops.
+
+// (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 / 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) {
+  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];
+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) {
+    a.fromInt(lowprimes[i]);
+    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;
+
+// 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/typo3/sysext/rsaauth/resources/jsbn/prng4.js b/typo3/sysext/rsaauth/resources/jsbn/prng4.js
new file mode 100644 (file)
index 0000000..3034f3f
--- /dev/null
@@ -0,0 +1,45 @@
+// prng4.js - uses Arcfour as a PRNG
+
+function Arcfour() {
+  this.i = 0;
+  this.j = 0;
+  this.S = new Array();
+}
+
+// Initialize arcfour context from key, an array of ints, each from [0..255]
+function ARC4init(key) {
+  var i, j, t;
+  for(i = 0; i < 256; ++i)
+    this.S[i] = i;
+  j = 0;
+  for(i = 0; i < 256; ++i) {
+    j = (j + this.S[i] + key[i % key.length]) & 255;
+    t = this.S[i];
+    this.S[i] = this.S[j];
+    this.S[j] = t;
+  }
+  this.i = 0;
+  this.j = 0;
+}
+
+function ARC4next() {
+  var t;
+  this.i = (this.i + 1) & 255;
+  this.j = (this.j + this.S[this.i]) & 255;
+  t = this.S[this.i];
+  this.S[this.i] = this.S[this.j];
+  this.S[this.j] = t;
+  return this.S[(t + this.S[this.i]) & 255];
+}
+
+Arcfour.prototype.init = ARC4init;
+Arcfour.prototype.next = ARC4next;
+
+// Plug in your RNG constructor here
+function prng_newstate() {
+  return new Arcfour();
+}
+
+// Pool size must be a multiple of 4 and greater than 32.
+// An array of bytes the size of the pool will be passed to init()
+var rng_psize = 256;
diff --git a/typo3/sysext/rsaauth/resources/jsbn/rng.js b/typo3/sysext/rsaauth/resources/jsbn/rng.js
new file mode 100644 (file)
index 0000000..6347e5b
--- /dev/null
@@ -0,0 +1,68 @@
+// Random number generator - requires a PRNG backend, e.g. prng4.js
+
+// For best results, put code like
+// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
+// in your main HTML document.
+
+var rng_state;
+var rng_pool;
+var rng_pptr;
+
+// Mix in a 32-bit integer into the pool
+function rng_seed_int(x) {
+  rng_pool[rng_pptr++] ^= x & 255;
+  rng_pool[rng_pptr++] ^= (x >> 8) & 255;
+  rng_pool[rng_pptr++] ^= (x >> 16) & 255;
+  rng_pool[rng_pptr++] ^= (x >> 24) & 255;
+  if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
+}
+
+// Mix in the current time (w/milliseconds) into the pool
+function rng_seed_time() {
+  rng_seed_int(new Date().getTime());
+}
+
+// Initialize the pool with junk if needed.
+if(rng_pool == null) {
+  rng_pool = new Array();
+  rng_pptr = 0;
+  var t;
+  if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) {
+    // Extract entropy (256 bits) from NS4 RNG if available
+    var z = window.crypto.random(32);
+    for(t = 0; t < z.length; ++t)
+      rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
+  }
+  while(rng_pptr < rng_psize) {  // extract some randomness from Math.random()
+    t = Math.floor(65536 * Math.random());
+    rng_pool[rng_pptr++] = t >>> 8;
+    rng_pool[rng_pptr++] = t & 255;
+  }
+  rng_pptr = 0;
+  rng_seed_time();
+  //rng_seed_int(window.screenX);
+  //rng_seed_int(window.screenY);
+}
+
+function rng_get_byte() {
+  if(rng_state == null) {
+    rng_seed_time();
+    rng_state = prng_newstate();
+    rng_state.init(rng_pool);
+    for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
+      rng_pool[rng_pptr] = 0;
+    rng_pptr = 0;
+    //rng_pool = null;
+  }
+  // TODO: allow reseeding after first request
+  return rng_state.next();
+}
+
+function rng_get_bytes(ba) {
+  var i;
+  for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
+}
+
+function SecureRandom() {}
+
+SecureRandom.prototype.nextBytes = rng_get_bytes;
diff --git a/typo3/sysext/rsaauth/resources/jsbn/rsa.js b/typo3/sysext/rsaauth/resources/jsbn/rsa.js
new file mode 100644 (file)
index 0000000..4f22883
--- /dev/null
@@ -0,0 +1,96 @@
+// Depends on jsbn.js and rng.js
+
+// 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) {
+    alert("Message too long for RSA");
+    return null;
+  }
+  var ba = new Array();
+  var i = s.length - 1;
+  while(i >= 0 && n > 0) ba[--n] = s.charCodeAt(i--);
+  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);
+}
+
+// "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) {
+  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 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.encrypt_b64 = RSAEncryptB64;
diff --git a/typo3/sysext/rsaauth/resources/jsbn/rsa2.js b/typo3/sysext/rsaauth/resources/jsbn/rsa2.js
new file mode 100644 (file)
index 0000000..fa04b86
--- /dev/null
@@ -0,0 +1,118 @@
+// Depends on rsa.js and jsbn2.js
+
+// 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)
+    ret += String.fromCharCode(b[i]);
+  return ret;
+}
+
+// Set the private key fields N, e, and d from hex strings
+function RSASetPrivate(N,E,D) {
+  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) {
+  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");
+}
+
+// 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);
+    var q1 = this.q.subtract(BigInteger.ONE);
+    var phi = p1.multiply(q1);
+    if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
+      this.n = this.p.multiply(this.q);
+      this.d = ee.modInverse(phi);
+      this.dmp1 = this.d.mod(p1);
+      this.dmq1 = this.d.mod(q1);
+      this.coeff = this.q.modInverse(this.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);
+  var xq = x.mod(this.q).modPow(this.dmq1, this.q);
+
+  while(xp.compareTo(xq) < 0)
+    xp = xp.add(this.p);
+  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 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.b64_decrypt = RSAB64Decrypt;
diff --git a/typo3/sysext/rsaauth/resources/rsaauth.js b/typo3/sysext/rsaauth/resources/rsaauth.js
new file mode 100644 (file)
index 0000000..5601688
--- /dev/null
@@ -0,0 +1,37 @@
+function tx_rsaauth_encrypt() {
+       var rsa = new RSAKey();
+       rsa.setPublic(document.loginform.n.value, document.loginform.e.value);
+
+       var username = document.loginform.username.value;
+       var password = document.loginform.p_field.value;
+
+       var res = rsa.encrypt(password);
+
+       // Remove all plaintext-data
+       document.loginform.p_field.value = "";
+       document.loginform.e.value = "";
+       document.loginform.n.value = "";
+
+       if (res) {
+               document.loginform.userident.value = 'rsa:' + hex2b64(res);
+       }
+}
+
+function tx_rsaauth_feencrypt(form) {
+       var rsa = new RSAKey();
+       rsa.setPublic(form.n.value, form.e.value);
+
+       var username = form.user.value;
+       var password = form.pass.value;
+
+       var res = rsa.encrypt(password);
+
+       // Remove all plaintext-data. This will also prevent plain text authentication.
+       form.pass.value = "";
+       form.e.value = "";
+       form.n.value = "";
+
+       if (res) {
+               form.pass.value = 'rsa:' + hex2b64(res);
+       }
+}
diff --git a/typo3/sysext/rsaauth/resources/rsaauth_min.js b/typo3/sysext/rsaauth/resources/rsaauth_min.js
new file mode 100644 (file)
index 0000000..8f20fb5
--- /dev/null
@@ -0,0 +1,2 @@
+function tx_rsaauth_encrypt(){var rsa=new RSAKey();rsa.setPublic(document.loginform.n.value,document.loginform.e.value);var username=document.loginform.username.value;var password=document.loginform.p_field.value;var res=rsa.encrypt(password);document.loginform.p_field.value="";document.loginform.e.value="";document.loginform.n.value="";if(res){document.loginform.userident.value='rsa:'+hex2b64(res);}}
+function tx_rsaauth_feencrypt(form){var rsa=new RSAKey();rsa.setPublic(form.n.value,form.e.value);var username=form.user.value;var password=form.pass.value;var res=rsa.encrypt(password);form.pass.value="";form.e.value="";form.n.value="";if(res){form.pass.value='rsa:'+hex2b64(res);}}
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_abstract_backend.php b/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_abstract_backend.php
new file mode 100644 (file)
index 0000000..0a7bcb3
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_abstract_backend.php 18249 2009-03-24 11:00:01Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/backends/class.tx_rsaauth_keypair.php'));
+
+/**
+ * This class contains an abstract SSL backend for the TYPO3 RSA authentication
+ * service.
+ *
+ * There are two steps:
+ *     - prepare data for encoding
+ *     - decode incoming data
+ *
+ * To prepare data for encoding, the createNewKeyPair() method should be called.
+ * This method returns an instance of tx_rsaauth_keypair class, which contains
+ * the private and public keys. Public key is sent to the client to encode data.
+ * Private key should be stored somewhere (preferrably in user's session).
+ *
+ * To decode data, the decrypt() method should be called with the private key
+ * created at the previous step and the data to decode. If the data is decoded
+ * successfully, the result is a string. Otherwise it is null.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+abstract class tx_rsaauth_abstract_backend {
+
+       /**
+        * Error message for the last operation. Derieved classes should always set
+        * or clear this variable inside the createNewKeyPair() or decypt().
+        *
+        * @var string
+        */
+       protected       $error = '';
+
+       /**
+        * Creates a new key pair for the encryption.
+        *
+        * @return      tx_rsaauth_keypair      A new key pair or null in case of error
+        */
+       abstract public function createNewKeyPair();
+
+       /**
+        * Decripts the data using the private key.
+        *
+        * @param       string  $privateKey     The private key (obtained from a call to createNewKeyPair())
+        * @param       string  $data   Data to decrypt (base64-encoded)
+        * @return      string  Decrypted data or null in case of a error
+        */
+       abstract public function decrypt($privateKey, $data);
+
+       /**
+        * Checks if this backend is available for calling.
+        *
+        * @return      void
+        */
+       abstract public function isAvailable();
+
+       /**
+        * Retrieves a error message.
+        *
+        * @return      string  A error message or empty string if there were no error
+        */
+       public function getLastError() {
+               return $this->error;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backend/class.tx_rsaauth_abstract_backend.php'])  {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backend/class.tx_rsaauth_abstract_backend.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_backendfactory.php b/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_backendfactory.php
new file mode 100644 (file)
index 0000000..42e5c6d
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_backendfactory.php 18942 2009-04-13 09:42:01Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/backends/class.tx_rsaauth_abstract_backend.php'));
+
+/**
+ * This class contains a factory for the RSA backends.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_backendfactory {
+
+       /**
+        * A list of all available backends. Currently this list cannot be extended.
+        * This is for security reasons to avoid inserting some dummy backend to
+        * the list.
+        *
+        * @var array
+        */
+       static protected $availableBackends = array(
+               'EXT:rsaauth/sv1/backends/class.tx_rsaauth_php_backend.php:tx_rsaauth_php_backend',
+               'EXT:rsaauth/sv1/backends/class.tx_rsaauth_cmdline_backend.php:tx_rsaauth_cmdline_backend'
+       );
+
+       /**
+        * A flag that tells if the factory is initialized. This is to prevent
+        * continious creation of backends in case if none of them is available.
+        *
+        * @var boolean
+        */
+       static protected $initialized = false;
+
+       /**
+        * A selected backend. This member is set in the getBackend() function. It
+        * will not be an abstract backend as shown below but a real class, which is
+        * derieved from the tx_rsaauth_abstract_backend.
+        *
+        * <!-- Please, keep the variable type! It helps IDEs to provide autocomple! -->
+        *
+        * @var tx_rsaauth_abstract_backend
+        */
+       static protected $selectedBackend = null;
+
+       /**
+        * Obtains a backend. This function will return a non-abstract class, which
+        * is derieved from the tx_rsaauth_abstract_backend. Applications should
+        * not use anoy methods that are not declared in the tx_rsaauth_abstract_backend.
+        *
+        * @return      tx_rsaauth_abstract_backend     A backend
+        */
+       static public function getBackend() {
+               if (!self::$initialized) {
+                       // Backend does not exist yet. Create it.
+                       foreach (self::$availableBackends as $backend) {
+                               $backendObject = t3lib_div::getUserObj($backend);
+                               // Check that it is derieved from the proper base class
+                               if ($backendObject instanceof tx_rsaauth_abstract_backend) {
+                                       /* @var $backendObject tx_rsaauth_abstract_backend */
+                                       if ($backendObject->isAvailable()) {
+                                               // The backend is available, save it and stop the loop
+                                               self::$selectedBackend = $backendObject;
+                                               self::$initialized = true;
+                                               break;
+                                       }
+                                       // Attempt to force destruction of the object
+                                       unset($backend);
+                               }
+                       }
+               }
+               return self::$selectedBackend;
+       }
+
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backends/class.tx_rsaauth_backendfactory.php'])   {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backends/class.tx_rsaauth_backendfactory.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_cmdline_backend.php b/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_cmdline_backend.php
new file mode 100644 (file)
index 0000000..7e69389
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_cmdline_backend.php 19612 2009-04-28 12:56:11Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/backends/class.tx_rsaauth_abstract_backend.php'));
+require_once(PATH_t3lib . 'class.t3lib_exec.php');
+
+/**
+ * This class contains a OpenSSL backend for the TYPO3 RSA authentication
+ * service. It uses shell version of OpenSSL to perform tasks. See class
+ * tx_rsaauth_abstract_backend for the information on using backends.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_cmdline_backend extends tx_rsaauth_abstract_backend {
+
+       /**
+        * A path to the openssl binary or false if the binary does not exist
+        *
+        * @var mixed
+        */
+       protected       $opensslPath;
+
+       /**
+        * Temporary directory. It is best of it is outside of the web site root and
+        * not publically readable.
+        * For now we use typo3temp/.
+        *
+        * @var string
+        */
+       protected       $temporaryDirectory;
+
+       /**
+        * Creates an instance of this class. It obtains a path to the OpenSSL
+        * binary.
+        *
+        * @return      void
+        */
+       public function __construct() {
+               $this->opensslPath = t3lib_exec::getCommand('openssl');
+               $this->temporaryDirectory = PATH_site . 'typo3temp';
+
+               // Get temporary directory from the configuration
+               $extconf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['rsaauth']);
+               if ($extconf['temporaryDirectory'] != '' &&
+                               $extconf['temporaryDirectory']{0} == '/' &&
+                               @is_dir($extconf['temporaryDirectory']) &&
+                               is_writable($extconf['temporaryDirectory'])) {
+                       $this->temporaryDirectory = $extconf['temporaryDirectory'];
+               }
+       }
+
+       /**
+        *
+        * @return tx_rsaauth_keypair   A new key pair or null in case of error
+        * @see tx_rsaauth_abstract_backend::createNewKeyPair()
+        */
+       public function createNewKeyPair() {
+               $result = null;
+
+               // Create a temporary file. Security: tempnam() sets permissions to 0600
+               $privateKeyFile = tempnam($this->temporaryDirectory, uniqid());
+
+               // Generate the private key.
+               //
+               // PHP generates 1024 bit key files. We force command line version
+               // to do the same and use the F4 (0x10001) exponent. This is the most
+               // secure.
+               $command = $this->opensslPath . ' genrsa -out ' .
+                       escapeshellarg($privateKeyFile) . ' 1024';
+               exec($command);
+
+               // Test that we got a private key
+               $privateKey = file_get_contents($privateKeyFile);
+               if (false !== strpos($privateKey, 'BEGIN RSA PRIVATE KEY')) {
+                       // Ok, we got the private key. Get the modulus.
+                       $command = $this->opensslPath . ' rsa -noout -modulus -in ' .
+                               escapeshellarg($privateKeyFile);
+                       $value = exec($command);
+                       if (substr($value, 0, 8) === 'Modulus=') {
+                               $publicKey = substr($value, 8);
+
+                               // Create a result object
+                               $result = t3lib_div::makeInstance('tx_rsaauth_keypair');
+                               /* @var $result tx_rsa_keypair */
+                               $result->setExponent(0x10001);
+                               $result->setPrivateKey($privateKey);
+                               $result->setPublicKey($publicKey);
+                       }
+               }
+
+               @unlink($privateKeyFile);
+
+               return $result;
+       }
+
+       /**
+        *
+        * @param string        $privateKey     The private key (obtained from a call to createNewKeyPair())
+        * @param string        $data   Data to decrypt (base64-encoded)
+        * @return string       Decrypted data or null in case of a error
+        * @see tx_rsaauth_abstract_backend::decrypt()
+        */
+       public function decrypt($privateKey, $data) {
+               // Key must be put to the file
+               $privateKeyFile = tempnam($this->temporaryDirectory, uniqid());
+               file_put_contents($privateKeyFile, $privateKey);
+
+               $dataFile = tempnam($this->temporaryDirectory, uniqid());
+               file_put_contents($dataFile, base64_decode($data));
+
+               // Prepare the command
+               $command = $this->opensslPath . ' rsautl -inkey ' .
+                       escapeshellarg($privateKeyFile) . ' -in ' .
+                       escapeshellarg($dataFile) .
+                       ' -decrypt';
+
+               // Execute the command and capture the result
+               $output = array();
+               exec($command, $output);
+
+               // Remove the file
+               @unlink($privateKeyFile);
+               @unlink($dataFile);
+
+               return implode(chr(10), $output);
+       }
+
+       /**
+        * Checks if command line version of the OpenSSL is available and can be
+        * executed successfully.
+        *
+        * @return void
+        * @see tx_rsaauth_abstract_backend::isAvailable()
+        */
+       public function isAvailable() {
+               $result = false;
+               if ($this->opensslPath) {
+                       // If path exists, test that command runs and can produce output
+                       $test = exec($this->opensslPath . ' version');
+                       $result = (substr($test, 0, 8) == 'OpenSSL ');
+               }
+               return $result;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backends/class.tx_rsaauth_cmdline_backend.php'])  {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backends/class.tx_rsaauth_cmdline_backend.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_keypair.php b/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_keypair.php
new file mode 100644 (file)
index 0000000..ece43b1
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_keypair.php 19610 2009-04-28 12:53:15Z dmitry $
+ */
+
+
+/**
+ * This class contain an RSA keypair class. Its purpose is to keep to keys
+ * and trasnfer these keys between other PHP classes.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+final class tx_rsaauth_keypair {
+
+       /**
+        * RSA public exponent (3 or 0x10001)
+        *
+        * @var int
+        */
+       protected       $exponent = 0x10001;
+
+       /**
+        * The private key
+        *
+        * @var string
+        */
+       protected       $privateKey = '';
+
+       /**
+        * The public key modulus
+        *
+        * @var string
+        */
+       protected       $publicKeyModulus = '';
+
+       /**
+        * Retrieves the exponent.
+        *
+        * @return      string  The exponent
+        */
+       public function getExponent() {
+               return $this->exponent;
+       }
+
+       /**
+        * Sets the private key
+        *
+        * @param       string  $privateKey     The new private key
+        * @return      void
+        */
+       public function setExponent($exponent) {
+               $this->exponent = $exponent;
+       }
+
+       /**
+        * Retrieves the private key.
+        *
+        * @return      string  The private key
+        */
+       public function getPrivateKey() {
+               return $this->privateKey;
+       }
+
+       /**
+        * Sets the private key
+        *
+        * @param       string  $privateKey     The new private key
+        * @return      void
+        */
+       public function setPrivateKey($privateKey) {
+               $this->privateKey = $privateKey;
+       }
+
+       /**
+        * Retrieves the public key modulus
+        *
+        * @return      string  The public key modulus
+        */
+       public function getPublicKeyModulus() {
+               return $this->publicKeyModulus;
+       }
+
+       /**
+        * Sets the public key modulus
+        *
+        * @param       string  $publicKeyModulus       The new public key modulus
+        * @return      void
+        */
+       public function setPublicKey($publicKeyModulus) {
+               $this->publicKeyModulus = $publicKeyModulus;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backends/class.tx_rsaauth_keypair.php'])  {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backends/class.tx_rsaauth_keypair.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_php_backend.php b/typo3/sysext/rsaauth/sv1/backends/class.tx_rsaauth_php_backend.php
new file mode 100644 (file)
index 0000000..7bffe5f
--- /dev/null
@@ -0,0 +1,158 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_php_backend.php 18661 2009-04-03 14:44:24Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/backends/class.tx_rsaauth_abstract_backend.php'));
+
+/**
+ * This class contains a PHP OpenSSL backend for the TYPO3 RSA authentication
+ * service. See class tx_rsaauth_abstract_backend for the information on using
+ * backends.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_php_backend extends tx_rsaauth_abstract_backend {
+
+       /**
+        * Creates a new public/private key pair using PHP OpenSSL extension.
+        *
+        * @return tx_rsaauth_keypair   A new key pair or null in case of error
+        * @see tx_rsaauth_abstract_backend::createNewKeyPair()
+        */
+       public function createNewKeyPair() {
+               $result = null;
+               $privateKey = @openssl_pkey_new();
+               if ($privateKey) {
+                       // Create private key as string
+                       $privateKeyStr = '';
+                       openssl_pkey_export($privateKey, $privateKeyStr);
+
+                       // Prepare public key information
+                       $exportedData = '';
+                       $csr = openssl_csr_new(array(), $privateKey);
+                       openssl_csr_export($csr, $exportedData, false);
+
+                       // Get public key (in fact modulus) and exponent
+                       $publicKey = $this->extractPublicKeyModulus($exportedData);
+                       $exponent = $this->extractExponent($exportedData);
+
+                       // Create result object
+                       $result = t3lib_div::makeInstance('tx_rsaauth_keypair');
+                       /* @var $result tx_rsaauth_keypair */
+                       $result->setExponent($exponent);
+                       $result->setPrivateKey($privateKeyStr);
+                       $result->setPublicKey($publicKey);
+
+                       // Clean up all resources
+                       openssl_free_key($privateKey);
+               }
+               return $result;
+       }
+
+       /**
+        * Decrypts data using the private key. This implementation uses PHP OpenSSL
+        * extension.
+        *
+        * @param string        $privateKey     The private key (obtained from a call to createNewKeyPair())
+        * @param string        $data   Data to decrypt (base64-encoded)
+        * @return string       Decrypted data or null in case of a error
+        * @see tx_rsaauth_abstract_backend::decrypt()
+        */
+       public function decrypt($privateKey, $data) {
+               $result = '';
+               if (!@openssl_private_decrypt(base64_decode($data), $result, $privateKey)) {
+                       $result = null;
+               }
+               return $result;
+       }
+
+       /**
+        * Checks if this backend is available for calling. In particular checks if
+        * PHP OpenSSl extension is installed and functional.
+        *
+        * @return void
+        * @see tx_rsaauth_abstract_backend::isAvailable()
+        */
+       public function isAvailable() {
+               $result = false;
+               if (is_callable('openssl_pkey_new')) {
+                       if (TYPO3_OS !== 'WIN') {
+                               // If the server does not run Windows, we can be sure than
+                               // OpenSSL will work
+                               $result = true;
+                       }
+                       else {
+                               // On Windows PHP extension has to be configured properly. It
+                               // can be installed and available but will not work unless
+                               // configured. So we check if it works.
+                               $testKey = @openssl_pkey_new();
+                               if ($testKey) {
+                                       openssl_free_key($testKey);
+                                       $result = true;
+                               }
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Extracts the exponent from the OpenSSL CSR
+        *
+        * @param       string  $data   The result of openssl_csr_export()
+        * @return      int     The exponent as a number
+        */
+       protected function extractExponent($data) {
+               $index = strpos($data, 'Exponent: ');
+               // We do not check for '$index === false' because the exponent is
+               // always there!
+               return intval(substr($data, $index + 10));
+       }
+
+       /**
+        * Extracts public key modulus from the OpenSSL CSR.
+        *
+        * @param       string  $data   The result of openssl_csr_export()
+        * @return      string  Modulus as uppercase hex string
+        */
+       protected function extractPublicKeyModulus($data) {
+               $fragment = preg_replace('/.*Modulus.*?\n(.*)Exponent:.*/ms', '\1', $data);
+               $fragment = preg_replace('/[\s\n\r:]/', '', $fragment);
+               $result = trim(strtoupper(substr($fragment, 2)));
+
+               return $result;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backends/class.tx_rsaauth_php_backend.php'])      {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/backends/class.tx_rsaauth_php_backend.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/class.tx_rsaauth_sv1.php b/typo3/sysext/rsaauth/sv1/class.tx_rsaauth_sv1.php
new file mode 100644 (file)
index 0000000..27e7525
--- /dev/null
@@ -0,0 +1,208 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_sv1.php 19610 2009-04-28 12:53:15Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('sv') . 'class.tx_sv_auth.php');
+require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/backends/class.tx_rsaauth_backendfactory.php');
+require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/storage/class.tx_rsaauth_storagefactory.php');
+
+// Include backends
+
+/**
+ * Service "RSA authentication" for the "rsaauth" extension. This service will
+ * authenticate a user using hos password encoded with one time public key. It
+ * uses the standard TYPO3 service to do all dirty work. Firsts, it will decode
+ * the password and then pass it to the parent service ('sv'). This ensures that it
+ * always works, even if other TYPO3 internals change.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_sv1 extends tx_sv_auth  {
+
+       /**
+        * An RSA backend.
+        *
+        * @var tx_rsaauth_abstract_backend
+        */
+       protected       $backend = null;
+
+       /**
+        * Standard extension key for the service
+        *
+        * @var string
+        */
+       public  $extKey = 'rsaauth';    // The extension key.
+
+       /**
+        * Standard prefix id for the service
+        *
+        * @var string
+        */
+       public  $prefixId = 'tx_rsaauth_sv1';           // Same as class name
+
+       /**
+        * Standard relative path for the service
+        *
+        * @var string
+        */
+       public  $scriptRelPath = 'sv1/class.tx_rsaauth_sv1.php';        // Path to this script relative to the extension dir.
+
+       /**
+        * Authenticates a user. The function decrypts the password, runs evaluations
+        * on it and passes to the parent authentication service.
+        *
+        * @param       array   $userRecord     User record
+        * @return      int             Code that shows if user is really authenticated.
+        * @see t3lib_userAuth::checkAuthentication()
+        */
+       public function authUser(array $userRecord) {
+               $result = 100;
+
+               if ($this->pObj->security_level == 'rsa') {
+
+                       $storage = tx_rsaauth_storagefactory::getStorage();
+                       /* @var $storage tx_rsaauth_abstract_storage */
+
+                       // Set failure status by default
+                       $result = -1;
+
+                       // Preprocess the password
+                       $password = $this->login['uident'];
+                       $key = $storage->get();
+                       if ($key != null && substr($password, 0, 4) == 'rsa:') {
+                               // Decode password and pass to parent
+                               $decryptedPassword = $this->backend->decrypt($key, substr($password, 4));
+                               if ($decryptedPassword != null) {
+                                       // Run the password through the eval function
+                                       $decryptedPassword = $this->runPasswordEvaluations($decryptedPassword);
+                                       if ($decryptedPassword != null) {
+                                               $this->login['uident'] = $decryptedPassword;
+                                               if (parent::authUser($userRecord)) {
+                                                       $result = 200;
+                                               }
+                                       }
+                               }
+                               // Reset the password to its original value
+                               $this->login['uident'] = $password;
+                               // Remove the key
+                               $storage->put(null);
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Initializes the service.
+        *
+        * @return      boolean
+        */
+       public function init()  {
+               $available = parent::init();
+               if ($available) {
+                       // Get the backend
+                       $this->backend = tx_rsaauth_backendfactory::getBackend();
+                       if (is_null($this->backend)) {
+                               $available = false;
+                       }
+               }
+
+               return $available;
+       }
+
+       /**
+        * Runs password evaluations. This is necessary because other extensions can
+        * modify the way the password is stored in the database. We check for all
+        * evaluations for the password column and run those.
+        *
+        * Notes:
+        * - we call t3lib_TCEmain::checkValue_input_Eval() but it is risky: if a hook
+        *   relies on BE_USER, it will fail. No hook should do this, so we risk it.
+        * - we cannot use t3lib_TCEmain::checkValue_input_Eval() for running all
+        *   evaluations because it does not create md5 hashes.
+        *
+        * @param       string  $password       Evaluated password
+        * @return      void
+        * @see t3lib_TCEmain::checkValue_input_Eval()
+        */
+       protected function runPasswordEvaluations($password) {
+               $table = $this->pObj->user_table;
+               t3lib_div::loadTCA($table);
+               $conf = &$GLOBALS['TCA'][$table]['columns'][$this->pObj->userident_column]['config'];
+               $evaluations = $conf['eval'];
+               if ($evaluations) {
+                       $tce = null;
+                       foreach (t3lib_div::trimExplode(',', $evaluations, true) as $evaluation) {
+                               switch ($evaluation) {
+                                       case 'md5':
+                                               $password = md5($password);
+                                               break;
+                                       case 'upper':
+                                               // We do not pass this to TCEmain because TCEmain will use objects unavailable in FE
+                                               $csConvObj = (TYPO3_MODE == 'BE' ? $GLOBALS['LANG']->csConvObj : $GLOBALS['TSFE']->csConvObj);
+                                               $charset = (TYPO3_MODE == 'BE' ? $GLOBALS['LANG']->charSet : $GLOBALS['TSFE']->metaCharset);
+                                               $password = $csConvObj->conv_case($charset, $password, 'toUpper');
+                                               break;
+                                       case 'lower':
+                                               // We do not pass this to TCEmain because TCEmain will use objects unavailable in FE
+                                               $csConvObj = (TYPO3_MODE == 'BE' ? $GLOBALS['LANG']->csConvObj : $GLOBALS['TSFE']->csConvObj);
+                                               $charset = (TYPO3_MODE == 'BE' ? $GLOBALS['LANG']->charSet : $GLOBALS['TSFE']->metaCharset);
+                                               $password = $csConvObj->conv_case($charset, $password, 'toLower');
+                                               break;
+                                       case 'password':
+                                       case 'required':
+                                               // Do nothing!
+                                               break;
+                                       default:
+                                               // We must run these evaluations through TCEmain to avoid
+                                               // code duplication and ensure that any custom evaluations
+                                               // are called in a proper context
+                                               if ($tce == null) {
+                                                       t3lib_div::requireOnce(PATH_t3lib . 'class.t3lib_tcemain.php');
+                                                       $tce = t3lib_div::makeInstance('t3lib_TCEmain');
+                                                       /* @var $tce t3lib_TCEmain */
+                                               }
+                                               $result = $tce->checkValue_input_Eval($password, array($evaluation), $conf['is_in']);
+                                               if (!isset($result['value'])) {
+                                                       // Failure!!!
+                                                       return null;
+                                               }
+                                               $password = $result['value'];
+                               }
+                       }
+               }
+               return $password;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/class.tx_rsaauth_sv1.php'])       {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/class.tx_rsaauth_sv1.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_abstract_storage.php b/typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_abstract_storage.php
new file mode 100644 (file)
index 0000000..3042980
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_abstract_storage.php 17901 2009-03-16 16:58:22Z dmitry $
+ */
+
+
+/**
+ * This class contains the abstract storage for the RSA private keys
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+abstract class tx_rsaauth_abstract_storage {
+
+       /**
+        * Retrieves the key from the storage
+        *
+        * @return      string  The key or null
+        */
+       abstract public function get();
+
+       /**
+        * Stores the key in the storage
+        *
+        * @param       string  $key    The key
+        */
+       abstract public function put($key);
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/storage/class.tx_rsaauth_abstract_storage.php'])      {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/storage/class.tx_rsaauth_abstract_storage.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_session_storage.php b/typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_session_storage.php
new file mode 100644 (file)
index 0000000..9a3ea90
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_session_storage.php 18942 2009-04-13 09:42:01Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/storage/class.tx_rsaauth_abstract_storage.php'));
+
+/**
+ * This class contains a session-based storage for private keys. This storage
+ * is not secure enough because its implementation stores keys completely in the
+ * PHP sessions. PHP sessions usually store data in the file system and it is
+ * easy to extract. This storage is useful only as an example. It is better to
+ * use "split" storage for keys.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_session_storage extends tx_rsaauth_abstract_storage {
+
+       /**
+        * Creates an instance of this class. It checks and initializes PHP
+        * sessions if necessary.
+        *
+        * @return      void
+        */
+       public function __construct() {
+               if (!isset($_SESSION) || !is_array($_SESSION)) {
+                       session_start();
+               }
+       }
+
+       /**
+        * Obtains key from the session
+        *
+        * @return string       The key or null
+        * @see tx_rsaauth_abstract_storage::get()
+        */
+       public function get() {
+               return (isset($_SESSION['tx_rsaauth_key']) ? $_SESSION['tx_rsaauth_key'] : null);
+       }
+
+       /**
+        * Puts key to the session
+        *
+        * @param string        $key    The key
+        * @see tx_rsaauth_abstract_storage::put()
+        */
+       public function put($key) {
+               $_SESSION['tx_rsaauth_key'] = $key;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/storage/class.tx_rsaauth_session_storage.php'])   {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/storage/class.tx_rsaauth_session_storage.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_split_storage.php b/typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_split_storage.php
new file mode 100644 (file)
index 0000000..bef2a29
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_split_storage.php 19606 2009-04-28 12:16:30Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/storage/class.tx_rsaauth_abstract_storage.php'));
+
+/**
+ * This class contains a "split" storage for the data. It keeps part of the data
+ * in the database, part in the database.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_split_storage extends tx_rsaauth_abstract_storage {
+
+       /**
+        * Creates an instance of this class. It checks and initializes PHP
+        * sessions if necessary.
+        *
+        * @return      void
+        */
+       public function __construct() {
+               if (!isset($_SESSION) || !is_array($_SESSION)) {
+                       session_start();
+               }
+       }
+
+       /**
+        * Obtains a key from the database
+        *
+        * @return string       The key or null
+        * @see tx_rsaauth_abstract_storage::get()
+        */
+       public function get() {
+               $result = null;
+
+               list($keyId, $keyPart1) = $_SESSION['tx_rsaauth_key'];
+               if (t3lib_div::testInt($keyId)) {
+
+                       // Remove expired keys (more than 30 minutes old)
+                       $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_rsaauth_keys',
+                                               'crdate<' . (time() - 30*60));
+
+                       // Get our value
+                       list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('key_value',
+                               'tx_rsaauth_keys', 'uid=' . $keyId);
+                       if (is_array($row)) {
+                               $result = $keyPart1 . $row['key_value'];
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Adds a key to the storage or removes existing key
+        *
+        * @param       string  $key    The key
+        * @return      void
+        * @see tx_rsaauth_abstract_storage::put()
+        */
+       public function put($key) {
+               if ($key == null) {
+                       // Remove existing key
+                       list($keyId) = $_SESSION['tx_rsaauth_key'];
+
+                       if (t3lib_div::testInt($keyId)) {
+                               $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_rsaauth_keys',
+                                       'uid=' . $keyId);
+                               unset($_SESSION['tx_rsaauth_key']);
+                       }
+               }
+               else {
+                       // Add key
+
+                       // Get split point. First part is always smaller than the second
+                       // because it goes to the file system
+                       $keyLength = strlen($key);
+                       $splitPoint = rand(intval($keyLength/10), intval($keyLength/2));
+
+                       // Get key parts
+                       $keyPart1 = substr($key, 0, $splitPoint);
+                       $keyPart2 = substr($key, $splitPoint);
+
+                       // Store part of the key in the database
+                       //
+                       // Notice: we may not use TCEmain below to insert key part into the
+                       // table because TCEmain requires a valid BE user!
+                       $time = time();
+                       $GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_rsaauth_keys', array(
+                               'pid' => 0,
+                               'crdate' => $time,
+                               'key_value' => $keyPart2
+                       ));
+                       $keyId = $GLOBALS['TYPO3_DB']->sql_insert_id();
+
+                       // Store another part in session
+                       $_SESSION['tx_rsaauth_key'] = array($keyId, $keyPart1);
+               }
+
+               // Remove expired keys (more than 30 minutes old)
+               $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_rsaauth_keys',
+                                       'crdate<' . (time() - 30*60));
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/storage/class.tx_rsaauth_split_storage.php'])     {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/storage/class.tx_rsaauth_split_storage.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_storagefactory.php b/typo3/sysext/rsaauth/sv1/storage/class.tx_rsaauth_storagefactory.php
new file mode 100644 (file)
index 0000000..f0075c8
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Dmitry Dulepov <dmitry@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ * $Id: class.tx_rsaauth_storagefactory.php 19605 2009-04-28 12:12:32Z dmitry $
+ */
+
+require_once(t3lib_extMgm::extPath('rsaauth', 'sv1/storage/class.tx_rsaauth_abstract_storage.php'));
+
+/**
+ * This class contains a factory for the RSA backends.
+ *
+ * @author     Dmitry Dulepov <dmitry@typo3.org>
+ * @package    TYPO3
+ * @subpackage tx_rsaauth
+ */
+class tx_rsaauth_storagefactory {
+
+       /**
+        * A list of all available storages. Currently this list cannot be extended.
+        * This is for security reasons to avoid inserting some dummy storage to
+        * the list.
+        *
+        * @var string
+        */
+       static protected $preferredStorage = 'EXT:rsaauth/sv1/storage/class.tx_rsaauth_split_storage.php:tx_rsaauth_split_storage';
+
+       /**
+        * An instance of the storage. This member is set in the getStorage() function.
+        * It will not be an abstract storage as shown below but a real class, which is
+        * derieved from the tx_rsaauth_abstract_storage.
+        *
+        * <!-- Please, keep the variable type! It helps IDEs to provide autocomple! -->
+        *
+        * @var tx_rsaauth_abstract_storage
+        */
+       static protected $storageInstance = null;
+
+       /**
+        * Obtains a storage. This function will return a non-abstract class, which
+        * is derieved from the tx_rsaauth_abstract_storage. Applications should
+        * not use anoy methods that are not declared in the tx_rsaauth_abstract_storage.
+        *
+        * @return      tx_rsaauth_abstract_storage     A storage
+        */
+       static public function getStorage() {
+               if (is_null(self::$storageInstance)) {
+                       self::$storageInstance = t3lib_div::getUserObj(self::$preferredStorage);
+               }
+               return self::$storageInstance;
+       }
+
+       /**
+        * Sets the preffered storage to the factory. This method can be called from
+        * another extension or ext_localconf.php
+        *
+        * @param       string  $preferredStorage       Preffered storage
+        * @return      void
+        */
+       static public function setPreferredStorage($preferredStorage) {
+               self::$preferredStorage = $preferredStorage;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/storage/class.tx_rsaauth_storagefactory.php'])    {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rsaauth/sv1/storage/class.tx_rsaauth_storagefactory.php']);
+}
+
+?>
\ No newline at end of file