diff --git a/.gitignore b/.gitignore index 8c8b61d701..25cb1b227f 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ nbproject # Tests - auto-generated files /data-autotest /tests/coverage* +/tests/karma-coverage /tests/autoconfig* /tests/autotest* /tests/data/lorem-copy.txt diff --git a/.htaccess b/.htaccess index fa6263c7ff..4ba5095e14 100755 --- a/.htaccess +++ b/.htaccess @@ -26,7 +26,7 @@ RewriteRule ^.well-known/carddav /remote.php/carddav/ [R] RewriteRule ^.well-known/caldav /remote.php/caldav/ [R] RewriteRule ^apps/calendar/caldav.php remote.php/caldav/ [QSA,L] RewriteRule ^apps/contacts/carddav.php remote.php/carddav/ [QSA,L] -RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L] +RewriteRule ^apps/([^/]*)/(.*\.(php))$ index.php?app=$1&getfile=$2 [QSA,L] RewriteRule ^remote/(.*) remote.php [QSA,L] diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000000..f40dd22b5f --- /dev/null +++ b/.jshintrc @@ -0,0 +1,28 @@ +{ + "camelCase": true, + "eqeqeq": true, + "immed": true, + "latedef": false, + "noarg": true, + "nonbsp": true, + "undef": true, + "unused": true, + "trailing": true, + "maxparams": 5, + "curly": true, + "jquery": true, + "maxlen": 80, + "indent": 4, + "browser": true, + "globals": { + "console": true, + "it": true, + "itx": true, + "expect": true, + "describe": true, + "beforeEach": true, + "afterEach": true, + "sinon": true, + "fakeServer": true + } +} diff --git a/README.md b/README.md index ca7b04a925..3f76c1a477 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ A personal cloud which runs on your own server. ### Build Status on [Jenkins CI](https://ci.owncloud.org/) -Git master: [![Build Status](https://ci.owncloud.org/buildStatus/icon?job=ownCloud-Server%28master%29)](https://ci.owncloud.org/job/ownCloud-Server%28master%29/) +Git master: [![Build Status](https://ci.owncloud.org/job/server-master-linux/badge/icon)](https://ci.owncloud.org/job/server-master-linux/) + +Quality: [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/owncloud/core/badges/quality-score.png?s=ce2f5ded03d4ac628e9ee5c767243fa7412e644f)](https://scrutinizer-ci.com/g/owncloud/core/) ### Installation instructions http://doc.owncloud.org/server/5.0/developer_manual/app/gettingstarted.html diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index ec5b716fb2..1853098c50 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -64,6 +64,15 @@ if(strpos($filename, '/') !== false) { exit(); } +if (!\OC\Files\Filesystem::file_exists($dir . '/')) { + $result['data'] = array('message' => (string)$l10n->t( + 'The target folder has been moved or deleted.'), + 'code' => 'targetnotfound' + ); + OCP\JSON::error($result); + exit(); +} + //TODO why is stripslashes used on foldername in newfolder.php but not here? $target = $dir.'/'.$filename; diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php index 2cbc8cfeba..4cfcae3090 100644 --- a/apps/files/ajax/newfolder.php +++ b/apps/files/ajax/newfolder.php @@ -29,6 +29,15 @@ if(strpos($foldername, '/') !== false) { exit(); } +if (!\OC\Files\Filesystem::file_exists($dir . '/')) { + $result['data'] = array('message' => (string)$l10n->t( + 'The target folder has been moved or deleted.'), + 'code' => 'targetnotfound' + ); + OCP\JSON::error($result); + exit(); +} + //TODO why is stripslashes used on foldername here but not in newfile.php? $target = $dir . '/' . stripslashes($foldername); diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index bdaf6a77d1..754c34ef08 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -8,6 +8,7 @@ OCP\JSON::setContentTypeHeader('text/plain'); // If no token is sent along, rely on login only $allowedPermissions = OCP\PERMISSION_ALL; +$errorCode = null; $l = OC_L10N::get('files'); if (empty($_POST['dirToken'])) { @@ -125,7 +126,8 @@ if (strpos($dir, '..') === false) { $meta = \OC\Files\Filesystem::getFileInfo($target); if ($meta === false) { - $error = $l->t('Upload failed. Could not get file info.'); + $error = $l->t('The target folder has been moved or deleted.'); + $errorCode = 'targetnotfound'; } else { $result[] = array('status' => 'success', 'mime' => $meta['mimetype'], @@ -137,7 +139,8 @@ if (strpos($dir, '..') === false) { 'originalname' => $files['tmp_name'][$i], 'uploadMaxFilesize' => $maxUploadFileSize, 'maxHumanFilesize' => $maxHumanFileSize, - 'permissions' => $meta['permissions'] & $allowedPermissions + 'permissions' => $meta['permissions'] & $allowedPermissions, + 'directory' => \OC\Files\Filesystem::normalizePath(stripslashes($dir)), ); } @@ -164,7 +167,8 @@ if (strpos($dir, '..') === false) { 'originalname' => $files['tmp_name'][$i], 'uploadMaxFilesize' => $maxUploadFileSize, 'maxHumanFilesize' => $maxHumanFileSize, - 'permissions' => $meta['permissions'] & $allowedPermissions + 'permissions' => $meta['permissions'] & $allowedPermissions, + 'directory' => \OC\Files\Filesystem::normalizePath(stripslashes($dir)), ); } } @@ -177,5 +181,5 @@ if ($error === false) { OCP\JSON::encodedPrint($result); exit(); } else { - OCP\JSON::error(array(array('data' => array_merge(array('message' => $error), $storageStats)))); + OCP\JSON::error(array(array('data' => array_merge(array('message' => $error, 'code' => $errorCode), $storageStats)))); } diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index 9f29079620..ef22fe9218 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -52,6 +52,7 @@ $server->addPlugin(new OC_Connector_Sabre_FilesPlugin()); $server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin()); $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin()); $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); +$server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav')); // And off we go! $server->exec(); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 2fc86ca537..5526abaf6e 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -3,7 +3,7 @@ See the COPYING-README file. */ /* FILE MENU */ -.actions { padding:.3em; height:2em; width: 100%; } +.actions { padding:5px; height:32px; width: 100%; } .actions input, .actions button, .actions .button { margin:0; float:left; } .actions .button a { color: #555; } .actions .button a:hover, .actions .button a:active { color: #333; } @@ -33,9 +33,9 @@ #new>ul { display: none; position: fixed; - min-width: 7em; + min-width: 112px; z-index: 10; - padding: .5em; + padding: 8px; padding-bottom: 0; margin-top: 14px; margin-left: -1px; @@ -46,7 +46,7 @@ border-top-left-radius: 0; box-shadow:0 2px 7px rgba(170,170,170,.4); } -#new>ul>li { height:36px; margin:.3em; padding-left:3em; padding-bottom:0.1em; +#new>ul>li { height:36px; margin:5px; padding-left:48px; padding-bottom:2px; background-repeat:no-repeat; cursor:pointer; } #new>ul>li>p { cursor:pointer; padding-top: 7px; padding-bottom: 7px;} @@ -65,10 +65,15 @@ top: 44px; width: 100%; } -#filestable, #controls { - min-width: 680px; +/* make sure there's enough room for the file actions */ +#body-user #filestable { + min-width: 750px; } -#filestable tbody tr { background-color:#fff; height:2.5em; } +#body-user #controls { + min-width: 600px; +} + +#filestable tbody tr { background-color:#fff; height:40px; } #filestable tbody tr:hover, tbody tr:active { background-color: rgb(240,240,240); } @@ -83,7 +88,7 @@ span.extension, span.uploading, td.date { color:#999; } span.extension { text-transform:lowercase; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } tr:hover span.extension { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; color:#777; } table tr.mouseOver td { background-color:#eee; } -table th { height:2em; padding:0 .5em; color:#999; } +table th { height:24px; padding:0 8px; color:#999; } table th .name { position: absolute; left: 55px; @@ -98,7 +103,7 @@ table td { } table th#headerName { position: relative; - width: 100em; /* not really sure why this works better than 100% … table styling */ + width: 9999px; /* not really sure why this works better than 100% … table styling */ padding: 0; } #headerName-container { @@ -106,15 +111,17 @@ table th#headerName { height: 50px; } table th#headerSize, table td.filesize { - min-width: 3em; - padding: 0 1em; + min-width: 48px; + padding: 0 16px; text-align: right; } table th#headerDate, table td.date { -moz-box-sizing: border-box; box-sizing: border-box; position: relative; - min-width: 11em; + /* this can not be just width, both need to be set … table styling */ + min-width: 176px; + max-width: 176px; } /* Multiselect bar */ @@ -140,9 +147,9 @@ table.multiselect thead th { } table.multiselect #headerName { position: relative; - width: 100%; + width: 9999px; /* when we use 100%, the styling breaks on mobile … table styling */ } -table td.selection, table th.selection, table td.fileaction { width:2em; text-align:center; } +table td.selection, table th.selection, table td.fileaction { width:32px; text-align:center; } table td.filename a.name { position:relative; /* Firefox needs to explicitly have this default set … */ -moz-box-sizing: border-box; @@ -160,8 +167,8 @@ table td.filename input.filename { margin-left: 2px; cursor: text; } -table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:.2em .5em .5em .3em; } -table td.filename .nametext, .uploadtext, .modified { float:left; padding:.3em 0; } +table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:3px 8px 8px 3px; } +table td.filename .nametext, .uploadtext, .modified { float:left; padding:14px 0; } #modified { position: absolute; @@ -169,6 +176,15 @@ table td.filename .nametext, .uploadtext, .modified { float:left; padding:.3em 0 } .modified { position: relative; + padding-left: 8px; + overflow: hidden; + text-overflow: ellipsis; + width: 90%; +} +/* ellipsize long modified dates to make room for showing delete button */ +#fileList tr:hover .modified, +#fileList tr:focus .modified { + width: 75%; } /* TODO fix usability bug (accidental file/folder selection) */ @@ -181,8 +197,8 @@ table td.filename .nametext { text-overflow: ellipsis; max-width: 800px; } -table td.filename .uploadtext { font-weight:normal; margin-left:.5em; } -table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } +table td.filename .uploadtext { font-weight:normal; margin-left:8px; } +table td.filename form { font-size:14px; margin-left:48px; margin-right:48px; } .ie8 input[type="checkbox"]{ padding: 0; @@ -217,6 +233,11 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } width: 50px; z-index: 5; } +#fileList tr td.filename>input[type="checkbox"]{ + /* sometimes checkbox height is bigger (KDE/Qt), so setting to absolute + * to prevent it to increase the height */ + position: absolute; +} #fileList tr td.filename>input[type="checkbox"] + label { left: 0; } @@ -237,7 +258,7 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } #fileList tr td.filename a.name label { position: absolute; - width: 100%; + width: 80%; height: 50px; } @@ -248,14 +269,16 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } position: absolute; top: 14px; right: 0; + font-size: 11px; } -#fileList img.move2trash { display:inline; margin:-.5em 0; padding:1em .5em 1em .5em !important; float:right; } +#fileList img.move2trash { display:inline; margin:-8px 0; padding:16px 8px 16px 8px !important; float:right; } #fileList a.action.delete { position: absolute; right: 0; - padding: 9px 14px 19px !important; + padding: 28px 14px 19px !important; } + a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } /* Actions for selected files */ @@ -272,19 +295,23 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } } .selectedActions a img { position:relative; - top:.3em; + top:5px; } #fileList a.action { display: inline; - margin: -.5em 0; + margin: -8px 0; padding: 18px 8px !important; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); opacity: 0; display:none; } + +#fileList a.action[data-action="Rename"] { + padding:18px 14px !important; +} #fileList tr:hover a.action, #fileList a.action.permanent { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50); diff --git a/apps/files/css/upload.css b/apps/files/css/upload.css index ef04356909..06b4d4655f 100644 --- a/apps/files/css/upload.css +++ b/apps/files/css/upload.css @@ -5,7 +5,7 @@ height: 36px; width: 39px; padding: 0 !important; /* override default control bar button padding */ - margin-left: .2em; + margin-left: 3px; overflow: hidden; vertical-align: top; } @@ -18,9 +18,6 @@ margin: -5px -3px; cursor: pointer; z-index: 10; - background-image: url('%webroot%/core/img/actions/upload.svg'); - background-repeat: no-repeat; - background-position: center; opacity: .65; } .file_upload_target { display:none; } @@ -33,7 +30,7 @@ height: 44px; margin: -5px -3px; padding: 0; - font-size: 1em; + font-size: 16px; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index: 20; cursor: pointer; @@ -119,11 +116,6 @@ .oc-dialog .fileexists .conflict input[type='checkbox'] { float: left; } -.oc-dialog .fileexists .toggle { - background-image: url('%webroot%/core/img/actions/triangle-e.png'); - width: 16px; - height: 16px; -} .oc-dialog .fileexists #allfileslabel { float:right; } diff --git a/apps/files/index.php b/apps/files/index.php index 8f6838aa0d..dd63f29bc2 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -63,7 +63,6 @@ $files = array(); $user = OC_User::getUser(); if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we need to upgrade the cache $needUpgrade = true; - $freeSpace = 0; } else { if ($isIE8){ // after the redirect above, the URL will have a format @@ -77,7 +76,6 @@ if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we ne else{ $files = \OCA\Files\Helper::getFiles($dir); } - $freeSpace = \OC\Files\Filesystem::free_space($dir); $needUpgrade = false; } @@ -103,6 +101,8 @@ if ($needUpgrade) { } else { // information about storage capacities $storageInfo=OC_Helper::getStorageInfo($dir); + $freeSpace=$storageInfo['free']; + $uploadLimit=OCP\Util::uploadLimit(); $maxUploadFilesize=OCP\Util::maxUploadFilesize($dir); $publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'); // if the encryption app is disabled, than everything is fine (INIT_SUCCESSFUL status code) @@ -134,8 +134,10 @@ if ($needUpgrade) { $tmpl->assign('files', $files); $tmpl->assign('trash', $trashEnabled); $tmpl->assign('trashEmpty', $trashEmpty); - $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); + $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); // minimium of freeSpace and uploadLimit $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); + $tmpl->assign('freeSpace', $freeSpace); + $tmpl->assign('uploadLimit', $uploadLimit); // PHP upload limit $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); $tmpl->assign('usedSpacePercent', (int)$storageInfo['relative']); $tmpl->assign('isPublic', false); diff --git a/apps/files/js/admin.js b/apps/files/js/admin.js index bfa9667063..f735079fcb 100644 --- a/apps/files/js/admin.js +++ b/apps/files/js/admin.js @@ -1,3 +1,13 @@ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + function switchPublicFolder() { var publicEnable = $('#publicEnable').is(':checked'); @@ -10,7 +20,7 @@ function switchPublicFolder() $(document).ready(function(){ switchPublicFolder(); // Execute the function after loading DOM tree $('#publicEnable').click(function(){ - switchPublicFolder(); // To get rid of onClick() + switchPublicFolder(); // To get rid of onClick() }); $('#allowZipDownload').bind('change', function() { diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 225c331910..f962a7044a 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,3 +1,13 @@ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + /** * The file upload code uses several hooks to interact with blueimps jQuery file upload library: * 1. the core upload handling hooks are added when initializing the plugin, @@ -8,6 +18,8 @@ * - TODO music upload button */ +/* global OC, t, n */ + /** * Function that will allow us to know if Ajax uploads are supported * @link https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html @@ -241,10 +253,22 @@ $(document).ready(function() { // add size selection.totalBytes += file.size; - //check max upload size - if (selection.totalBytes > $('#max_upload').val()) { + // check PHP upload limit + if (selection.totalBytes > $('#upload_limit').val()) { + data.textStatus = 'sizeexceedlimit'; + data.errorThrown = t('files', 'Total file size {size1} exceeds upload limit {size2}', { + 'size1': humanFileSize(selection.totalBytes), + 'size2': humanFileSize($('#upload_limit').val()) + }); + } + + // check free space + if (selection.totalBytes > $('#free_space').val()) { data.textStatus = 'notenoughspace'; - data.errorThrown = t('files', 'Not enough space available'); + data.errorThrown = t('files', 'Not enough free space, you are uploading {size1} but only {size2} is left', { + 'size1': humanFileSize(selection.totalBytes), + 'size2': humanFileSize($('#free_space').val()) + }); } // end upload for whole selection on error @@ -315,6 +339,13 @@ $(document).ready(function() { } else { // HTTP connection problem OC.Notification.show(data.errorThrown); + if (data.result) { + var result = JSON.parse(data.result); + if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') { + // abort upload of next files if any + OC.Upload.cancelUploads(); + } + } } //hide notification after 10 sec setTimeout(function() { diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 74bb711ef3..9a69d7b368 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -1,3 +1,15 @@ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +/* global OC, FileList */ +/* global trashBinApp */ var FileActions = { actions: {}, defaults: {}, @@ -45,8 +57,9 @@ var FileActions = { return filteredActions; }, getDefault: function (mime, type, permissions) { + var mimePart; if (mime) { - var mimePart = mime.substr(0, mime.indexOf('/')); + mimePart = mime.substr(0, mime.indexOf('/')); } var name = false; if (mime && FileActions.defaults[mime]) { @@ -71,13 +84,15 @@ var FileActions = { FileActions.currentFile = parent; var actions = FileActions.get(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions()); var file = FileActions.getCurrentFile(); + var nameLinks; if (FileList.findFileEl(file).data('renaming')) { return; } // recreate fileactions - parent.children('a.name').find('.fileactions').remove(); - parent.children('a.name').append(''); + nameLinks = parent.children('a.name'); + nameLinks.find('.fileactions, .nametext .action').remove(); + nameLinks.append(''); var defaultAction = FileActions.getDefault(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions()); var actionHandler = function (event) { @@ -97,21 +112,30 @@ var FileActions = { } if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') { - var img = FileActions.icons[name]; + var img = FileActions.icons[name], + actionText = t('files', name), + actionContainer = 'a.name>span.fileactions'; + + if (name === 'Rename') { + // rename has only an icon which appears behind + // the file name + actionText = ''; + actionContainer = 'a.name span.nametext'; + } if (img.call) { img = img(file); } var html = ''; if (img) { - html += ' '; + html += ''; } - html += t('files', name) + ''; + html += ' ' + actionText + ''; var element = $(html); element.data('action', name); //alert(element); element.on('click', {a: null, elem: parent, actionFunc: actions[name]}, actionHandler); - parent.find('a.name>span.fileactions').append(element); + parent.find(actionContainer).append(element); } }; @@ -130,13 +154,14 @@ var FileActions = { parent.parent().children().last().find('.action.delete').remove(); if (actions['Delete']) { var img = FileActions.icons['Delete']; + var html; if (img.call) { img = img(file); } if (typeof trashBinApp !== 'undefined' && trashBinApp) { - var html = ''; + html = ''; } else { - var html = ''; + html = ''; } var element = $(html); element.data('action', actions['Delete']); @@ -163,17 +188,21 @@ var FileActions = { }; $(document).ready(function () { + var downloadScope; if ($('#allowZipDownload').val() == 1) { - var downloadScope = 'all'; + downloadScope = 'all'; } else { - var downloadScope = 'file'; + downloadScope = 'file'; } if (typeof disableDownloadActions == 'undefined' || !disableDownloadActions) { FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { return OC.imagePath('core', 'actions/download'); }, function (filename) { - window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); + var url = FileList.getDownloadUrl(filename); + if (url) { + OC.redirect(url); + } }); } $('#fileList tr').each(function () { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 66968ab54c..a855d6cbe5 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1,4 +1,16 @@ -var FileList={ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +/* global OC, t, n, FileList, FileActions, Files */ +/* global procesSelection, dragOptions, SVGSupport, replaceSVG */ +window.FileList={ useUndo:true, postProcessList: function() { $('#fileList tr').each(function() { @@ -28,7 +40,8 @@ var FileList={ } FileList.updateFileSummary(); procesSelection(); - + + $(window).scrollTop(0); $fileList.trigger(jQuery.Event("updated")); }, createRow:function(type, name, iconurl, linktarget, size, lastModified, permissions) { @@ -191,6 +204,7 @@ var FileList={ return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/'); }, setCurrentDir: function(targetDir, changeUrl) { + var url; $('#dir').val(targetDir); if (changeUrl !== false) { if (window.history.pushState && changeUrl !== false) { @@ -394,7 +408,7 @@ var FileList={ } return true; }; - + form.submit(function(event) { event.stopPropagation(); event.preventDefault(); @@ -421,10 +435,9 @@ var FileList={ tr.attr('data-file', newname); var path = td.children('a.name').attr('href'); td.children('a.name').attr('href', path.replace(encodeURIComponent(oldname), encodeURIComponent(newname))); + var basename = newname; if (newname.indexOf('.') > 0 && tr.data('type') !== 'dir') { - var basename=newname.substr(0,newname.lastIndexOf('.')); - } else { - var basename=newname; + basename = newname.substr(0,newname.lastIndexOf('.')); } td.find('a.name span.nametext').text(basename); if (newname.indexOf('.') > 0 && tr.data('type') !== 'dir') { @@ -468,7 +481,7 @@ var FileList={ var basename = newname; if (newname.indexOf('.') > 0 && tr.data('type') !== 'dir') { basename = newname.substr(0, newname.lastIndexOf('.')); - } + } td.find('a.name span.nametext').text(basename); if (newname.indexOf('.') > 0 && tr.data('type') !== 'dir') { if ( ! td.find('a.name span.extension').exists() ) { @@ -477,6 +490,7 @@ var FileList={ td.find('a.name span.extension').text(newname.substr(newname.lastIndexOf('.'))); } form.remove(); + FileActions.display( tr.find('td.filename'), true); td.children('a.name').show(); } catch (error) { input.attr('title', error); @@ -529,10 +543,9 @@ var FileList={ td.children('a.name .span').text(newName); var path = td.children('a.name').attr('href'); td.children('a.name').attr('href', path.replace(encodeURIComponent(oldName), encodeURIComponent(newName))); + var basename = newName; if (newName.indexOf('.') > 0) { - var basename = newName.substr(0, newName.lastIndexOf('.')); - } else { - var basename = newName; + basename = newName.substr(0, newName.lastIndexOf('.')); } td.children('a.name').empty(); var span = $(''); @@ -780,6 +793,20 @@ var FileList={ $('#fileList tr.searchresult').each(function(i,e) { $(e).removeClass("searchresult"); }); + }, + + /** + * Returns the download URL of the given file + * @param filename file name of the file + * @param dir optional directory in which the file name is, defaults to the current directory + */ + getDownloadUrl: function(filename, dir) { + var params = { + files: filename, + dir: dir || FileList.getCurrentDirectory(), + download: null + }; + return OC.filePath('files', 'ajax', 'download.php') + '?' + OC.buildQueryString(params); } }; @@ -819,7 +846,7 @@ $(document).ready(function() { {name: 'requesttoken', value: oc_requesttoken} ]; }; - } + } }); file_upload_start.on('fileuploadadd', function(e, data) { @@ -858,7 +885,7 @@ $(document).ready(function() { */ file_upload_start.on('fileuploaddone', function(e, data) { OC.Upload.log('filelist handle fileuploaddone', e, data); - + var response; if (typeof data.result === 'string') { response = data.result; @@ -895,8 +922,8 @@ $(document).ready(function() { data.context.find('td.filesize').text(humanFileSize(size)); } else { - // only append new file if dragged onto current dir's crumb (last) - if (data.context && data.context.hasClass('crumb') && !data.context.hasClass('last')) { + // only append new file if uploaded into the current folder + if (file.directory !== FileList.getCurrentDirectory()) { return; } diff --git a/apps/files/js/files.js b/apps/files/js/files.js index dc7e893340..1ec4c4ec7a 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,4 +1,16 @@ -Files={ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +/* global OC, t, n, FileList, FileActions */ +/* global getURLParameter, isPublic */ +var Files = { // file space size sync _updateStorageStatistics: function() { Files._updateStorageStatisticsTimeout = null; @@ -41,6 +53,7 @@ Files={ } if (response.data !== undefined && response.data.uploadMaxFilesize !== undefined) { $('#max_upload').val(response.data.uploadMaxFilesize); + $('#free_space').val(response.data.freeSpace); $('#upload.button').attr('original-title', response.data.maxHumanFilesize); $('#usedSpacePercent').val(response.data.usedSpacePercent); Files.displayStorageWarnings(); @@ -67,17 +80,25 @@ Files={ return fileName; }, - isFileNameValid:function (name) { - if (name === '.') { - throw t('files', '\'.\' is an invalid file name.'); - } else if (name.length === 0) { + /** + * Checks whether the given file name is valid. + * @param name file name to check + * @return true if the file name is valid. + * Throws a string exception with an error message if + * the file name is not valid + */ + isFileNameValid: function (name) { + var trimmedName = name.trim(); + if (trimmedName === '.' || trimmedName === '..') { + throw t('files', '"{name}" is an invalid file name.', {name: name}); + } else if (trimmedName.length === 0) { throw t('files', 'File name cannot be empty.'); } - // check for invalid characters - var invalid_characters = ['\\', '/', '<', '>', ':', '"', '|', '?', '*']; + var invalid_characters = + ['\\', '/', '<', '>', ':', '"', '|', '?', '*', '\n']; for (var i = 0; i < invalid_characters.length; i++) { - if (name.indexOf(invalid_characters[i]) !== -1) { + if (trimmedName.indexOf(invalid_characters[i]) !== -1) { throw t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed."); } } @@ -384,7 +405,7 @@ $(document).ready(function() { Files.resizeBreadcrumbs(width, true); // display storage warnings - setTimeout ( "Files.displayStorageWarnings()", 100 ); + setTimeout(Files.displayStorageWarnings, 100); OC.Notification.setDefault(Files.displayStorageWarnings); // only possible at the moment if user is logged in @@ -493,7 +514,7 @@ var createDragShadow = function(event) { var dir=$('#dir').val(); $(selectedFiles).each(function(i,elem) { - var newtr = $('').attr('data-dir', dir).attr('data-filename', elem.name); + var newtr = $('').attr('data-dir', dir).attr('data-filename', elem.name).attr('data-origin', elem.origin); newtr.append($('').addClass('filename').text(elem.name)); newtr.append($('').addClass('size').text(humanFileSize(elem.size))); tbody.append(newtr); @@ -511,13 +532,30 @@ var createDragShadow = function(event) { }; //options for file drag/drop +//start&stop handlers needs some cleaning up var dragOptions={ revert: 'invalid', revertDuration: 300, opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: 24, top: 18 }, helper: createDragShadow, cursor: 'move', - stop: function(event, ui) { - $('#fileList tr td.filename').addClass('ui-draggable'); - } + start: function(event, ui){ + var $selectedFiles = $('td.filename input:checkbox:checked'); + if($selectedFiles.length > 1){ + $selectedFiles.parents('tr').fadeTo(250, 0.2); + } + else{ + $(this).fadeTo(250, 0.2); + } + }, + stop: function(event, ui) { + var $selectedFiles = $('td.filename input:checkbox:checked'); + if($selectedFiles.length > 1){ + $selectedFiles.parents('tr').fadeTo(250, 1); + } + else{ + $(this).fadeTo(250, 1); + } + $('#fileList tr td.filename').addClass('ui-draggable'); + } }; // sane browsers support using the distance option if ( $('html.ie').length === 0) { @@ -525,6 +563,7 @@ if ( $('html.ie').length === 0) { } var folderDropOptions={ + hoverClass: "canDrop", drop: function( event, ui ) { //don't allow moving a file into a selected folder if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) { @@ -537,6 +576,11 @@ var folderDropOptions={ $(files).each(function(i,row) { var dir = $(row).data('dir'); var file = $(row).data('filename'); + //slapdash selector, tracking down our original element that the clone budded off of. + var origin = $('tr[data-id=' + $(row).data('origin') + ']'); + var td = origin.children('td.filename'); + var oldBackgroundImage = td.css('background-image'); + td.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: dir+'/'+target }, function(result) { if (result) { if (result.status === 'success') { @@ -559,6 +603,7 @@ var folderDropOptions={ } else { OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } + td.css('background-image', oldBackgroundImage); }); }); }, @@ -583,6 +628,11 @@ var crumbDropOptions={ $(files).each(function(i,row) { var dir = $(row).data('dir'); var file = $(row).data('filename'); + //slapdash selector, tracking down our original element that the clone budded off of. + var origin = $('tr[data-id=' + $(row).data('origin') + ']'); + var td = origin.children('td.filename'); + var oldBackgroundImage = td.css('background-image'); + td.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: target }, function(result) { if (result) { if (result.status === 'success') { @@ -597,6 +647,7 @@ var crumbDropOptions={ } else { OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } + td.css('background-image', oldBackgroundImage); }); }); }, @@ -624,10 +675,10 @@ function procesSelection() { var totalSize = 0; for(var i=0; i 0) { @@ -663,7 +714,8 @@ function getSelectedFilesTrash(property) { mime:$(element).data('mime'), type:$(element).data('type'), size:$(element).data('size'), - etag:$(element).data('etag') + etag:$(element).data('etag'), + origin: $(element).data('id') }; if (property) { files.push(file[property]); @@ -720,7 +772,7 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { console.warn('Files.lazyLoadPreview(): missing etag argument'); } - if ( $('#publicUploadButtonMock').length ) { + if ( $('#isPublic').length ) { urlSpec.t = $('#dirToken').val(); previewURL = OC.Router.generate('core_ajax_public_preview', urlSpec); } else { @@ -738,10 +790,11 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { } img.src = previewURL; }); -} +}; function getUniqueName(name) { if (FileList.findFileEl(name).exists()) { + var numMatch; var parts=name.split('.'); var extension = ""; if (parts.length > 1) { @@ -775,7 +828,7 @@ function checkTrashStatus() { function onClickBreadcrumb(e) { var $el = $(e.target).closest('.crumb'), - $targetDir = $el.data('dir'); + $targetDir = $el.data('dir'), isPublic = !!$('#isPublic').val(); if ($targetDir !== undefined && !isPublic) { @@ -783,3 +836,4 @@ function onClickBreadcrumb(e) { FileList.changeDirectory(decodeURIComponent($targetDir)); } } + diff --git a/apps/files/js/upgrade.js b/apps/files/js/upgrade.js index 02d57fc9e6..714adf824a 100644 --- a/apps/files/js/upgrade.js +++ b/apps/files/js/upgrade.js @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +/* global OC */ $(document).ready(function () { var eventSource, total, bar = $('#progressbar'); console.log('start'); diff --git a/apps/files/js/upload.js b/apps/files/js/upload.js index 9d9f61f600..617cf4b1c1 100644 --- a/apps/files/js/upload.js +++ b/apps/files/js/upload.js @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +/* global OC */ function Upload(fileSelector) { if ($.support.xhrFileUpload) { return new XHRUpload(fileSelector.target.files); diff --git a/apps/files/lib/app.php b/apps/files/lib/app.php index e04ac173d5..fea88faa92 100644 --- a/apps/files/lib/app.php +++ b/apps/files/lib/app.php @@ -59,6 +59,13 @@ class App { $result['data'] = array( 'message' => $this->l10n->t("Invalid folder name. Usage of 'Shared' is reserved.") ); + // rename to non-existing folder is denied + } else if (!$this->view->file_exists($dir)) { + $result['data'] = array('message' => (string)$this->l10n->t( + 'The target folder has been moved or deleted.', + array($dir)), + 'code' => 'targetnotfound' + ); // rename to existing file is denied } else if ($this->view->file_exists($dir . '/' . $newname)) { @@ -83,14 +90,17 @@ class App { else { $meta['type'] = 'file'; } + // these need to be set for determineIcon() + $meta['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($meta['mimetype']); + $meta['directory'] = $dir; $fileinfo = array( 'id' => $meta['fileid'], 'mime' => $meta['mimetype'], 'size' => $meta['size'], 'etag' => $meta['etag'], - 'directory' => $dir, + 'directory' => $meta['directory'], 'name' => $newname, - 'isPreviewAvailable' => \OC::$server->getPreviewManager()->isMimeSupported($meta['mimetype']), + 'isPreviewAvailable' => $meta['isPreviewAvailable'], 'icon' => \OCA\Files\Helper::determineIcon($meta) ); $result['success'] = true; diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index eaff28178e..21d1f50e58 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -15,6 +15,7 @@ class Helper return array('uploadMaxFilesize' => $maxUploadFilesize, 'maxHumanFilesize' => $maxHumanFilesize, + 'freeSpace' => $storageInfo['free'], 'usedSpacePercent' => (int)$storageInfo['relative']); } diff --git a/apps/files/templates/admin.php b/apps/files/templates/admin.php index 697fc52526..a5afd55fbc 100644 --- a/apps/files/templates/admin.php +++ b/apps/files/templates/admin.php @@ -5,7 +5,7 @@

t('File handling')); ?>

- '/> + '/> (t('max. possible: ')); p($_['maxPossibleUploadSize']) ?>) diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 00ec109621..939043b2c9 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -1,6 +1,7 @@
+
t('New'));?>
    @@ -12,21 +13,27 @@ data-type='web'>

    t('From link'));?>

+
= 0):?> - + + + + + + + - +
- > + />
@@ -44,7 +51,7 @@
class="hidden">t('Nothing in here. Upload something!'))?>
- + diff --git a/apps/files/tests/ajax_rename.php b/apps/files/tests/ajax_rename.php index 350ff5d368..a1a5c8983b 100644 --- a/apps/files/tests/ajax_rename.php +++ b/apps/files/tests/ajax_rename.php @@ -38,7 +38,7 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $l10nMock->expects($this->any()) ->method('t') ->will($this->returnArgument(0)); - $viewMock = $this->getMock('\OC\Files\View', array('rename', 'normalizePath', 'getFileInfo'), array(), '', false); + $viewMock = $this->getMock('\OC\Files\View', array('rename', 'normalizePath', 'getFileInfo', 'file_exists'), array(), '', false); $viewMock->expects($this->any()) ->method('normalizePath') ->will($this->returnArgument(0)); @@ -63,6 +63,11 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $oldname = 'Shared'; $newname = 'new_name'; + $this->viewMock->expects($this->at(0)) + ->method('file_exists') + ->with('/') + ->will($this->returnValue(true)); + $result = $this->files->rename($dir, $oldname, $newname); $expected = array( 'success' => false, @@ -80,6 +85,11 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $oldname = 'Shared'; $newname = 'new_name'; + $this->viewMock->expects($this->at(0)) + ->method('file_exists') + ->with('/test') + ->will($this->returnValue(true)); + $this->viewMock->expects($this->any()) ->method('getFileInfo') ->will($this->returnValue(array( @@ -129,6 +139,11 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $oldname = 'oldname'; $newname = 'newname'; + $this->viewMock->expects($this->at(0)) + ->method('file_exists') + ->with('/') + ->will($this->returnValue(true)); + $this->viewMock->expects($this->any()) ->method('getFileInfo') ->will($this->returnValue(array( @@ -141,7 +156,6 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { 'name' => 'new_name', ))); - $result = $this->files->rename($dir, $oldname, $newname); $this->assertTrue($result['success']); @@ -154,4 +168,35 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $this->assertEquals(\OC_Helper::mimetypeIcon('dir'), $result['data']['icon']); $this->assertFalse($result['data']['isPreviewAvailable']); } + + /** + * Test rename inside a folder that doesn't exist any more + */ + function testRenameInNonExistingFolder() { + $dir = '/unexist'; + $oldname = 'oldname'; + $newname = 'newname'; + + $this->viewMock->expects($this->at(0)) + ->method('file_exists') + ->with('/unexist') + ->will($this->returnValue(false)); + + $this->viewMock->expects($this->any()) + ->method('getFileInfo') + ->will($this->returnValue(array( + 'fileid' => 123, + 'type' => 'dir', + 'mimetype' => 'httpd/unix-directory', + 'size' => 18, + 'etag' => 'abcdef', + 'directory' => '/unexist', + 'name' => 'new_name', + ))); + + $result = $this->files->rename($dir, $oldname, $newname); + + $this->assertFalse($result['success']); + $this->assertEquals('targetnotfound', $result['data']['code']); + } } diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js new file mode 100644 index 0000000000..8bbc1d3d14 --- /dev/null +++ b/apps/files/tests/js/fileactionsSpec.js @@ -0,0 +1,75 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ + +/* global OC, FileActions, FileList */ +describe('FileActions tests', function() { + var $filesTable; + beforeEach(function() { + // init horrible parameters + var $body = $('body'); + $body.append(''); + $body.append(''); + // dummy files table + $filesTable = $body.append('
'); + }); + afterEach(function() { + $('#dir, #permissions, #filestable').remove(); + }); + it('calling display() sets file actions', function() { + // note: download_url is actually the link target, not the actual download URL... + var $tr = FileList.addFile('testName.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'}); + + // no actions before call + expect($tr.find('.action[data-action=Download]').length).toEqual(0); + expect($tr.find('.action[data-action=Rename]').length).toEqual(0); + expect($tr.find('.action.delete').length).toEqual(0); + + FileActions.display($tr.find('td.filename'), true); + + // actions defined after cal + expect($tr.find('.action[data-action=Download]').length).toEqual(1); + expect($tr.find('.nametext .action[data-action=Rename]').length).toEqual(1); + expect($tr.find('.action.delete').length).toEqual(1); + }); + it('calling display() twice correctly replaces file actions', function() { + var $tr = FileList.addFile('testName.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'}); + + FileActions.display($tr.find('td.filename'), true); + FileActions.display($tr.find('td.filename'), true); + + // actions defined after cal + expect($tr.find('.action[data-action=Download]').length).toEqual(1); + expect($tr.find('.nametext .action[data-action=Rename]').length).toEqual(1); + expect($tr.find('.action.delete').length).toEqual(1); + }); + it('redirects to download URL when clicking download', function() { + var redirectStub = sinon.stub(OC, 'redirect'); + // note: download_url is actually the link target, not the actual download URL... + var $tr = FileList.addFile('test download File.txt', 1234, new Date(), false, false, {download_url: 'test/download/url'}); + FileActions.display($tr.find('td.filename'), true); + + $tr.find('.action[data-action=Download]').click(); + + expect(redirectStub.calledOnce).toEqual(true); + expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=test%20download%20File.txt&dir=%2Fsubdir&download'); + redirectStub.restore(); + }); +}); diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js new file mode 100644 index 0000000000..c26e65fc4d --- /dev/null +++ b/apps/files/tests/js/filelistSpec.js @@ -0,0 +1,65 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ + +/* global OC, FileList */ +describe('FileList tests', function() { + beforeEach(function() { + // init horrible parameters + var $body = $('body'); + $body.append(''); + $body.append(''); + // dummy files table + $body.append('
'); + }); + afterEach(function() { + $('#dir, #permissions, #filestable').remove(); + }); + it('generates file element with correct attributes when calling addFile', function() { + var lastMod = new Date(10000); + // note: download_url is actually the link target, not the actual download URL... + var $tr = FileList.addFile('testName.txt', 1234, lastMod, false, false, {download_url: 'test/download/url'}); + + expect($tr).toBeDefined(); + expect($tr[0].tagName.toLowerCase()).toEqual('tr'); + expect($tr.find('a:first').attr('href')).toEqual('test/download/url'); + expect($tr.attr('data-type')).toEqual('file'); + expect($tr.attr('data-file')).toEqual('testName.txt'); + expect($tr.attr('data-size')).toEqual('1234'); + expect($tr.attr('data-permissions')).toEqual('31'); + //expect($tr.attr('data-mime')).toEqual('plain/text'); + }); + it('generates dir element with correct attributes when calling addDir', function() { + var lastMod = new Date(10000); + var $tr = FileList.addDir('testFolder', 1234, lastMod, false); + + expect($tr).toBeDefined(); + expect($tr[0].tagName.toLowerCase()).toEqual('tr'); + expect($tr.attr('data-type')).toEqual('dir'); + expect($tr.attr('data-file')).toEqual('testFolder'); + expect($tr.attr('data-size')).toEqual('1234'); + expect($tr.attr('data-permissions')).toEqual('31'); + //expect($tr.attr('data-mime')).toEqual('httpd/unix-directory'); + }); + it('returns correct download URL', function() { + expect(FileList.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=some%20file.txt&dir=%2Fsubdir&download'); + expect(FileList.getDownloadUrl('some file.txt', '/anotherpath/abc')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?files=some%20file.txt&dir=%2Fanotherpath%2Fabc&download'); + }); +}); diff --git a/apps/files/tests/js/filesSpec.js b/apps/files/tests/js/filesSpec.js new file mode 100644 index 0000000000..018c8ef0f3 --- /dev/null +++ b/apps/files/tests/js/filesSpec.js @@ -0,0 +1,85 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ + +/* global Files */ +describe('Files tests', function() { + describe('File name validation', function() { + it('Validates correct file names', function() { + var fileNames = [ + 'boringname', + 'something.with.extension', + 'now with spaces', + '.a', + '..a', + '.dotfile', + 'single\'quote', + ' spaces before', + 'spaces after ', + 'allowed chars including the crazy ones $%&_-^@!,()[]{}=;#', + '汉字也能用', + 'und Ãœmläüte sind auch willkommen' + ]; + for ( var i = 0; i < fileNames.length; i++ ) { + var error = false; + try { + expect(Files.isFileNameValid(fileNames[i])).toEqual(true); + } + catch (e) { + error = e; + } + expect(error).toEqual(false); + } + }); + it('Detects invalid file names', function() { + var fileNames = [ + '', + ' ', + '.', + '..', + 'back\\slash', + 'sl/ash', + 'ltgt', + 'col:on', + 'double"quote', + 'pi|pe', + 'dont?ask?questions?', + 'super*star', + 'new\nline', + ' ..', + '.. ', + '. ', + ' .' + ]; + for ( var i = 0; i < fileNames.length; i++ ) { + var threwException = false; + try { + Files.isFileNameValid(fileNames[i]); + console.error('Invalid file name not detected:', fileNames[i]); + } + catch (e) { + threwException = true; + } + expect(threwException).toEqual(true); + } + }); + }); +}); diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 09d5687e22..83abf3ba9d 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -32,6 +32,8 @@ class Hooks { // file for which we want to rename the keys after the rename operation was successful private static $renamedFiles = array(); + // file for which we want to delete the keys after the delete operation was successful + private static $deleteFiles = array(); /** * @brief Startup encryption backend upon user login @@ -78,8 +80,15 @@ class Hooks { // Check if first-run file migration has already been performed $ready = false; - if ($util->getMigrationStatus() === Util::MIGRATION_OPEN) { + $migrationStatus = $util->getMigrationStatus(); + if ($migrationStatus === Util::MIGRATION_OPEN) { $ready = $util->beginMigration(); + } elseif ($migrationStatus === Util::MIGRATION_IN_PROGRESS) { + // refuse login as long as the initial encryption is running + while ($migrationStatus === Util::MIGRATION_IN_PROGRESS) { + sleep(60); + $migrationStatus = $util->getMigrationStatus(); + } } // If migration not yet done @@ -630,4 +639,66 @@ class Hooks { } } + /** + * @brief if the file was really deleted we remove the encryption keys + * @param array $params + * @return boolean + */ + public static function postDelete($params) { + + if (!isset(self::$deleteFiles[$params[\OC\Files\Filesystem::signal_param_path]])) { + return true; + } + + $deletedFile = self::$deleteFiles[$params[\OC\Files\Filesystem::signal_param_path]]; + $path = $deletedFile['path']; + $user = $deletedFile['uid']; + + // we don't need to remember the file any longer + unset(self::$deleteFiles[$params[\OC\Files\Filesystem::signal_param_path]]); + + $view = new \OC\Files\View('/'); + + // return if the file still exists and wasn't deleted correctly + if ($view->file_exists('/' . $user . '/files/' . $path)) { + return true; + } + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // Delete keyfile & shareKey so it isn't orphaned + if (!Keymanager::deleteFileKey($view, $path, $user)) { + \OCP\Util::writeLog('Encryption library', + 'Keyfile or shareKey could not be deleted for file "' . $user.'/files/'.$path . '"', \OCP\Util::ERROR); + } + + Keymanager::delAllShareKeys($view, $user, $path); + + \OC_FileProxy::$enabled = $proxyStatus; + } + + /** + * @brief remember the file which should be deleted and it's owner + * @param array $params + * @return boolean + */ + public static function preDelete($params) { + $path = $params[\OC\Files\Filesystem::signal_param_path]; + + // skip this method if the trash bin is enabled or if we delete a file + // outside of /data/user/files + if (\OCP\App::isEnabled('files_trashbin')) { + return true; + } + + $util = new Util(new \OC_FilesystemView('/'), \OCP\USER::getUser()); + list($owner, $ownerPath) = $util->getUidAndFilename($path); + + self::$deleteFiles[$params[\OC\Files\Filesystem::signal_param_path]] = array( + 'uid' => $owner, + 'path' => $ownerPath); + } + } diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 5dcb05fa19..bb06a57c71 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -63,6 +63,8 @@ class Helper { \OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Encryption\Hooks', 'preRename'); \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename'); + \OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Encryption\Hooks', 'postDelete'); + \OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Encryption\Hooks', 'preDelete'); } /** diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index b2c756894b..7abc565f60 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -214,15 +214,24 @@ class Keymanager { * * @param \OC_FilesystemView $view * @param string $path path of the file the key belongs to + * @param string $userId the user to whom the file belongs * @return bool Outcome of unlink operation * @note $path must be relative to data/user/files. e.g. mydoc.txt NOT * /data/admin/files/mydoc.txt */ - public static function deleteFileKey(\OC_FilesystemView $view, $path) { + public static function deleteFileKey($view, $path, $userId=null) { $trimmed = ltrim($path, '/'); - $userId = Helper::getUser($path); + if ($trimmed === '') { + \OCP\Util::writeLog('Encryption library', + 'Can\'t delete file-key empty path given!', \OCP\Util::ERROR); + return false; + } + + if ($userId === null) { + $userId = Helper::getUser($path); + } $util = new Util($view, $userId); if($util->isSystemWideMountPoint($path)) { @@ -402,7 +411,15 @@ class Keymanager { * @param string $userId owner of the file * @param string $filePath path to the file, relative to the owners file dir */ - public static function delAllShareKeys(\OC_FilesystemView $view, $userId, $filePath) { + public static function delAllShareKeys($view, $userId, $filePath) { + + $filePath = ltrim($filePath, '/'); + + if ($filePath === '') { + \OCP\Util::writeLog('Encryption library', + 'Can\'t delete share-keys empty path given!', \OCP\Util::ERROR); + return false; + } $util = new util($view, $userId); @@ -413,17 +430,15 @@ class Keymanager { } - if ($view->is_dir($userId . '/files/' . $filePath)) { + if ($view->is_dir($baseDir . $filePath)) { $view->unlink($baseDir . $filePath); } else { - $localKeyPath = $view->getLocalFile($baseDir . $filePath); - $escapedPath = Helper::escapeGlobPattern($localKeyPath); - $matches = glob($escapedPath . '*.shareKey'); - foreach ($matches as $ma) { - $result = unlink($ma); - if (!$result) { - \OCP\Util::writeLog('Encryption library', - 'Keyfile or shareKey could not be deleted for file "' . $filePath . '"', \OCP\Util::ERROR); + $parentDir = dirname($baseDir . $filePath); + $filename = pathinfo($filePath, PATHINFO_BASENAME); + foreach($view->getDirectoryContent($parentDir) as $content) { + $path = $content['path']; + if (self::getFilenameFromShareKey($content['name']) === $filename) { + $view->unlink('/' . $userId . '/' . $path); } } } @@ -523,4 +538,20 @@ class Keymanager { return $targetPath; } + + /** + * @brief extract filename from share key name + * @param string $shareKey (filename.userid.sharekey) + * @return mixed filename or false + */ + protected static function getFilenameFromShareKey($shareKey) { + $parts = explode('.', $shareKey); + + $filename = false; + if(count($parts) > 2) { + $filename = implode('.', array_slice($parts, 0, count($parts)-2)); + } + + return $filename; + } } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 4fe76b9771..1104800596 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -37,6 +37,7 @@ namespace OCA\Encryption; class Proxy extends \OC_FileProxy { private static $blackList = null; //mimetypes blacklisted from encryption + private static $unencryptedSizes = array(); // remember unencrypted size /** * Check if a file requires encryption @@ -114,14 +115,12 @@ class Proxy extends \OC_FileProxy { // get encrypted content $data = $view->file_get_contents($tmpPath); - // update file cache for target file + // store new unenecrypted size so that it can be updated + // in the post proxy $tmpFileInfo = $view->getFileInfo($tmpPath); - $fileInfo = $view->getFileInfo($path); - if (is_array($fileInfo) && is_array($tmpFileInfo)) { - $fileInfo['encrypted'] = true; - $fileInfo['unencrypted_size'] = $tmpFileInfo['size']; - $view->putFileInfo($path, $fileInfo); - } + if ( isset($tmpFileInfo['size']) ) { + self::$unencryptedSizes[\OC_Filesystem::normalizePath($path)] = $tmpFileInfo['size']; + } // remove our temp file $view->deleteAll('/' . \OCP\User::getUser() . '/cache/' . $cacheFolder); @@ -136,6 +135,24 @@ class Proxy extends \OC_FileProxy { } + /** + * @brief update file cache with the new unencrypted size after file was written + * @param string $path + * @param mixed $result + * @return mixed + */ + public function postFile_put_contents($path, $result) { + $normalizedPath = \OC_Filesystem::normalizePath($path); + if ( isset(self::$unencryptedSizes[$normalizedPath]) ) { + $view = new \OC_FilesystemView('/'); + $view->putFileInfo($normalizedPath, + array('encrypted' => true, 'encrypted_size' => self::$unencryptedSizes[$normalizedPath])); + unset(self::$unencryptedSizes[$normalizedPath]); + } + + return $result; + } + /** * @param string $path Path of file from which has been read * @param string $data Data that has been read from file @@ -186,47 +203,6 @@ class Proxy extends \OC_FileProxy { } - /** - * @brief When a file is deleted, remove its keyfile also - */ - public function preUnlink($path) { - - $relPath = Helper::stripUserFilesPath($path); - - // skip this method if the trash bin is enabled or if we delete a file - // outside of /data/user/files - if (\OCP\App::isEnabled('files_trashbin') || $relPath === false) { - return true; - } - - // Disable encryption proxy to prevent recursive calls - $proxyStatus = \OC_FileProxy::$enabled; - \OC_FileProxy::$enabled = false; - - $view = new \OC_FilesystemView('/'); - - $userId = \OCP\USER::getUser(); - - $util = new Util($view, $userId); - - list($owner, $ownerPath) = $util->getUidAndFilename($relPath); - - // Delete keyfile & shareKey so it isn't orphaned - if (!Keymanager::deleteFileKey($view, $ownerPath)) { - \OCP\Util::writeLog('Encryption library', - 'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OCP\Util::ERROR); - } - - Keymanager::delAllShareKeys($view, $owner, $ownerPath); - - \OC_FileProxy::$enabled = $proxyStatus; - - // If we don't return true then file delete will fail; better - // to leave orphaned keyfiles than to disallow file deletion - return true; - - } - /** * @param $path * @return bool diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 8a5dfabeec..ae3e2a2e15 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -2,9 +2,10 @@ /** * ownCloud * - * @author Sam Tuke, Frank Karlitschek + * @author Sam Tuke, Frank Karlitschek, Bjoern Schiessle * @copyright 2012 Sam Tuke , - * Frank Karlitschek + * Frank Karlitschek , + * Bjoern Schiessle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -56,7 +57,7 @@ class Util { * @param $userId * @param bool $client */ - public function __construct(\OC_FilesystemView $view, $userId, $client = false) { + public function __construct($view, $userId, $client = false) { $this->view = $view; $this->client = $client; @@ -1360,59 +1361,32 @@ class Util { } } - /** * @brief go recursively through a dir and collect all files and sub files. * @param string $dir relative to the users files folder * @return array with list of files relative to the users files folder */ public function getAllFiles($dir) { - $result = array(); + $dirList = array($dir); - $content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath( - $this->userFilesDir . '/' . $dir)); - - // handling for re shared folders - $pathSplit = explode('/', $dir); - - foreach ($content as $c) { - - $sharedPart = $pathSplit[sizeof($pathSplit) - 1]; - $targetPathSplit = array_reverse(explode('/', $c['path'])); - - $path = ''; - - // rebuild path - foreach ($targetPathSplit as $pathPart) { - - if ($pathPart !== $sharedPart) { - - $path = '/' . $pathPart . $path; + while ($dirList) { + $dir = array_pop($dirList); + $content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath( + $this->userFilesDir . '/' . $dir)); + foreach ($content as $c) { + $usersPath = isset($c['usersPath']) ? $c['usersPath'] : $c['path']; + if ($c['type'] === 'dir') { + $dirList[] = substr($usersPath, strlen("files")); } else { - - break; - + $result[] = substr($usersPath, strlen("files")); } - } - $path = $dir . $path; - - if ($c['type'] === 'dir') { - - $result = array_merge($result, $this->getAllFiles($path)); - - } else { - - $result[] = $path; - - } } return $result; - } /** diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php new file mode 100644 index 0000000000..4452579174 --- /dev/null +++ b/apps/files_encryption/tests/hooks.php @@ -0,0 +1,271 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ + +require_once __DIR__ . '/../../../lib/base.php'; +require_once __DIR__ . '/../lib/crypt.php'; +require_once __DIR__ . '/../lib/keymanager.php'; +require_once __DIR__ . '/../lib/stream.php'; +require_once __DIR__ . '/../lib/util.php'; +require_once __DIR__ . '/../appinfo/app.php'; +require_once __DIR__ . '/util.php'; + +use OCA\Encryption; + +/** + * Class Test_Encryption_Hooks + * @brief this class provide basic hook app tests + */ +class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { + + const TEST_ENCRYPTION_HOOKS_USER1 = "test-encryption-hooks-user1"; + const TEST_ENCRYPTION_HOOKS_USER2 = "test-encryption-hooks-user2"; + + /** + * @var \OC_FilesystemView + */ + public $user1View; // view on /data/user1/files + public $user2View; // view on /data/user2/files + public $rootView; // view on /data/user + public $data; + public $filename; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + \OC_Hook::clear('OC_Filesystem'); + \OC_Hook::clear('OC_User'); + + // clear share hooks + \OC_Hook::clear('OCP\\Share'); + \OC::registerShareHooks(); + \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // Sharing related hooks + \OCA\Encryption\Helper::registerShareHooks(); + + // clear and register proxies + \OC_FileProxy::clearProxies(); + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // create test user + \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1, true); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2, true); + } + + function setUp() { + // set user id + \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); + \OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); + + // init filesystem view + $this->user1View = new \OC_FilesystemView('/'. \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '/files'); + $this->user2View = new \OC_FilesystemView('/'. \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '/files'); + $this->rootView = new \OC_FilesystemView('/'); + + // init short data + $this->data = 'hats'; + $this->filename = 'enc_hooks_tests-' . uniqid() . '.txt'; + + } + + public static function tearDownAfterClass() { + // cleanup test user + \OC_User::deleteUser(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); + \OC_User::deleteUser(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2); + } + + function testDeleteHooks() { + + // remember files_trashbin state + $stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we want to tests with app files_trashbin disabled + \OC_App::disable('files_trashbin'); + + // make sure that the trash bin is disabled + $this->assertFalse(\OC_APP::isEnabled('files_trashbin')); + + $this->user1View->file_put_contents($this->filename, $this->data); + + // check if all keys are generated + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + + \Test_Encryption_Util::logoutHelper(); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2); + \OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2); + + + $this->user2View->file_put_contents($this->filename, $this->data); + + // check if all keys are generated + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + + // create a dummy file that we can delete something outside of data/user/files + // in this case no share or file keys should be deleted + $this->rootView->file_put_contents(self::TEST_ENCRYPTION_HOOKS_USER2 . "/" . $this->filename, $this->data); + + // delete dummy file outside of data/user/files + $this->rootView->unlink(self::TEST_ENCRYPTION_HOOKS_USER2 . "/" . $this->filename); + + // all keys should still exist + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + + // delete the file in data/user/files + // now the correspondig share and file keys from user2 should be deleted + $this->user2View->unlink($this->filename); + + // check if keys from user2 are really deleted + $this->assertFalse($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); + $this->assertFalse($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + // but user1 keys should still exist + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + if ($stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + + function testDeleteHooksForSharedFiles() { + + \Test_Encryption_Util::logoutHelper(); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); + \OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); + + // remember files_trashbin state + $stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we want to tests with app files_trashbin disabled + \OC_App::disable('files_trashbin'); + + // make sure that the trash bin is disabled + $this->assertFalse(\OC_APP::isEnabled('files_trashbin')); + + $this->user1View->file_put_contents($this->filename, $this->data); + + // check if all keys are generated + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + // get the file info from previous created file + $fileInfo = $this->user1View->getFileInfo($this->filename); + + // check if we have a valid file info + $this->assertTrue(is_array($fileInfo)); + + // share the file with user2 + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_ENCRYPTION_HOOKS_USER2, OCP\PERMISSION_ALL); + + // check if new share key exists + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); + + \Test_Encryption_Util::logoutHelper(); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2); + \OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2); + + // user2 has a local file with the same name + $this->user2View->file_put_contents($this->filename, $this->data); + + // check if all keys are generated + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + // delete the Shared file from user1 in data/user2/files/Shared + $this->user2View->unlink('/Shared/' . $this->filename); + + // now keys from user1s home should be gone + $this->assertFalse($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + $this->assertFalse($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); + $this->assertFalse($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + // but user2 keys should still exist + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key')); + + // cleanup + + $this->user2View->unlink($this->filename); + + \Test_Encryption_Util::logoutHelper(); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); + \OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); + + // unshare the file + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_ENCRYPTION_HOOKS_USER2); + + $this->user1View->unlink($this->filename); + + if ($stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + +} diff --git a/apps/files_encryption/tests/keymanager.php b/apps/files_encryption/tests/keymanager.php index 58a57ee5af..6f32c50743 100644 --- a/apps/files_encryption/tests/keymanager.php +++ b/apps/files_encryption/tests/keymanager.php @@ -136,6 +136,17 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase { $this->assertArrayHasKey('key', $sslInfo); } + /** + * @small + */ + function testGetFilenameFromShareKey() { + $this->assertEquals("file", + \TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.user.shareKey")); + $this->assertEquals("file.name.with.dots", + \TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.name.with.dots.user.shareKey")); + $this->assertFalse(\TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.txt")); + } + /** * @medium */ @@ -234,3 +245,12 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase { \OC_FileProxy::$enabled = $proxyStatus; } } + +/** + * dummy class to access protected methods of \OCA\Encryption\Keymanager for testing + */ +class TestProtectedKeymanagerMethods extends \OCA\Encryption\Keymanager { + public static function testGetFilenameFromShareKey($sharekey) { + return self::getFilenameFromShareKey($sharekey); + } +} \ No newline at end of file diff --git a/apps/files_encryption/tests/proxy.php b/apps/files_encryption/tests/proxy.php index c3006274d6..51cc0b795e 100644 --- a/apps/files_encryption/tests/proxy.php +++ b/apps/files_encryption/tests/proxy.php @@ -112,54 +112,4 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase { } - function testPreUnlinkWithoutTrash() { - - // remember files_trashbin state - $stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); - - // we want to tests with app files_trashbin enabled - \OC_App::disable('files_trashbin'); - - $this->view->file_put_contents($this->filename, $this->data); - - // create a dummy file that we can delete something outside of data/user/files - $this->rootView->file_put_contents("dummy.txt", $this->data); - - // check if all keys are generated - $this->assertTrue($this->rootView->file_exists( - '/files_encryption/share-keys/' - . $this->filename . '.' . \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '.shareKey')); - $this->assertTrue($this->rootView->file_exists( - '/files_encryption/keyfiles/' . $this->filename . '.key')); - - - // delete dummy file outside of data/user/files - $this->rootView->unlink("dummy.txt"); - - // all keys should still exist - $this->assertTrue($this->rootView->file_exists( - '/files_encryption/share-keys/' - . $this->filename . '.' . \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '.shareKey')); - $this->assertTrue($this->rootView->file_exists( - '/files_encryption/keyfiles/' . $this->filename . '.key')); - - - // delete the file in data/user/files - $this->view->unlink($this->filename); - - // now also the keys should be gone - $this->assertFalse($this->rootView->file_exists( - '/files_encryption/share-keys/' - . $this->filename . '.' . \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '.shareKey')); - $this->assertFalse($this->rootView->file_exists( - '/files_encryption/keyfiles/' . $this->filename . '.key')); - - if ($stateFilesTrashbin) { - OC_App::enable('files_trashbin'); - } - else { - OC_App::disable('files_trashbin'); - } - } - } diff --git a/apps/files_encryption/tests/share.php b/apps/files_encryption/tests/share.php index e55427620a..acf408a07f 100755 --- a/apps/files_encryption/tests/share.php +++ b/apps/files_encryption/tests/share.php @@ -194,8 +194,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); // cleanup - $this->view->unlink( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/'); + $this->view->unlink($this->filename); + $this->view->chroot('/'); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -265,8 +266,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); // cleanup - $this->view->unlink( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/'); + $this->view->unlink($this->filename); + $this->view->chroot('/'); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -352,7 +354,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); // cleanup - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files'); + $this->view->unlink($this->folder1); + $this->view->chroot('/'); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -482,9 +486,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); // cleanup - $this->view->unlink( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1 . $this->subfolder - . $this->subsubfolder . '/' . $this->filename); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files'); + $this->view->unlink($this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename); + $this->view->chroot('/'); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -559,7 +563,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->filename . '.' . $publicShareKeyId . '.shareKey')); // cleanup - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/'); + $this->view->unlink($this->filename); + $this->view->chroot('/'); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -636,7 +642,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey')); // cleanup - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/'); + $this->view->unlink($this->filename); + $this->view->chroot('/'); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -731,8 +739,10 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->filename . '.' . $recoveryKeyId . '.shareKey')); // cleanup - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->folder1); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/'); + $this->view->unlink($this->filename); + $this->view->unlink($this->folder1); + $this->view->chroot('/'); // check if share key for recovery not exists $this->assertFalse($this->view->file_exists( @@ -828,8 +838,10 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { $this->assertEquals($this->dataShort, $retrievedCryptedFile2); // cleanup - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->folder1); - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/'); + $this->view->unlink($this->folder1); + $this->view->unlink($this->filename); + $this->view->chroot('/'); // check if share key for user and recovery exists $this->assertFalse($this->view->file_exists( @@ -930,7 +942,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); // cleanup - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + $this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/'); + $this->view->unlink($this->filename); + $this->view->chroot('/'); } } diff --git a/apps/files_external/3rdparty/phpseclib/AUTHORS b/apps/files_external/3rdparty/phpseclib/AUTHORS index 7bae8ab94e..e175f9f22c 100644 --- a/apps/files_external/3rdparty/phpseclib/AUTHORS +++ b/apps/files_external/3rdparty/phpseclib/AUTHORS @@ -1,3 +1,5 @@ phpseclib Lead Developer: TerraFrost (Jim Wigginton) -phpseclib Developers: monnerat (Patrick Monnerat) \ No newline at end of file +phpseclib Developers: monnerat (Patrick Monnerat) + bantu (Andreas Fischer) + petrich (Hans-Jürgen Petrich) diff --git a/apps/files_external/3rdparty/phpseclib/README.md b/apps/files_external/3rdparty/phpseclib/README.md index fbd58bd82b..e90b12f4a6 100644 --- a/apps/files_external/3rdparty/phpseclib/README.md +++ b/apps/files_external/3rdparty/phpseclib/README.md @@ -4,9 +4,9 @@ MIT-licensed pure-PHP implementations of an arbitrary-precision integer arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael, -AES, SSH-1, SSH-2, SFTP, and X.509 +AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 -* [Download (0.3.1)](http://sourceforge.net/projects/phpseclib/files/phpseclib0.3.1.zip/download) +* [Download (0.3.5)](http://sourceforge.net/projects/phpseclib/files/phpseclib0.3.5.zip/download) * [Browse Git](https://github.com/phpseclib/phpseclib) * [Documentation](http://phpseclib.sourceforge.net/) * [Support](http://www.frostjedi.com/phpbb/viewforum.php?f=46) diff --git a/apps/files_external/3rdparty/phpseclib/composer.json b/apps/files_external/3rdparty/phpseclib/composer.json index 11008cd81d..79c92b52e4 100644 --- a/apps/files_external/3rdparty/phpseclib/composer.json +++ b/apps/files_external/3rdparty/phpseclib/composer.json @@ -43,6 +43,14 @@ "File": "phpseclib/", "Math": "phpseclib/", "Net": "phpseclib/" + }, + "files": [ + "phpseclib/Crypt/Random.php" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" } } } diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php index bc05adf67a..81fa2feab6 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/AES.php @@ -4,13 +4,13 @@ /** * Pure-PHP implementation of AES. * - * Uses mcrypt, if available, and an internal implementation, otherwise. + * Uses mcrypt, if available/possible, and an internal implementation, otherwise. * * PHP versions 4 and 5 * * If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from * {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits - * it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()} + * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link Crypt_AES::setKey() setKey()} * is called, again, at which point, it'll be recalculated. * * Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't @@ -42,10 +42,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -59,7 +59,6 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -67,7 +66,7 @@ * Include Crypt_Rijndael */ if (!class_exists('Crypt_Rijndael')) { - require_once 'Rijndael.php'; + require_once('Rijndael.php'); } /**#@+ @@ -82,31 +81,31 @@ if (!class_exists('Crypt_Rijndael')) { * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 */ -define('CRYPT_AES_MODE_CTR', -1); +define('CRYPT_AES_MODE_CTR', CRYPT_MODE_CTR); /** * Encrypt / decrypt using the Electronic Code Book mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 */ -define('CRYPT_AES_MODE_ECB', 1); +define('CRYPT_AES_MODE_ECB', CRYPT_MODE_ECB); /** * Encrypt / decrypt using the Code Book Chaining mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 */ -define('CRYPT_AES_MODE_CBC', 2); +define('CRYPT_AES_MODE_CBC', CRYPT_MODE_CBC); /** * Encrypt / decrypt using the Cipher Feedback mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 */ -define('CRYPT_AES_MODE_CFB', 3); +define('CRYPT_AES_MODE_CFB', CRYPT_MODE_CFB); /** * Encrypt / decrypt using the Cipher Feedback mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 */ -define('CRYPT_AES_MODE_OFB', 4); +define('CRYPT_AES_MODE_OFB', CRYPT_MODE_OFB); /**#@-*/ /**#@+ @@ -116,11 +115,11 @@ define('CRYPT_AES_MODE_OFB', 4); /** * Toggles the internal implementation */ -define('CRYPT_AES_MODE_INTERNAL', 1); +define('CRYPT_AES_MODE_INTERNAL', CRYPT_MODE_INTERNAL); /** * Toggles the mcrypt implementation */ -define('CRYPT_AES_MODE_MCRYPT', 2); +define('CRYPT_AES_MODE_MCRYPT', CRYPT_MODE_MCRYPT); /**#@-*/ /** @@ -133,195 +132,41 @@ define('CRYPT_AES_MODE_MCRYPT', 2); */ class Crypt_AES extends Crypt_Rijndael { /** - * mcrypt resource for encryption + * The namespace used by the cipher for its constants. * - * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. - * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. - * - * @see Crypt_AES::encrypt() + * @see Crypt_Base::const_namespace * @var String * @access private */ - var $enmcrypt; - - /** - * mcrypt resource for decryption - * - * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. - * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. - * - * @see Crypt_AES::decrypt() - * @var String - * @access private - */ - var $demcrypt; - - /** - * mcrypt resource for CFB mode - * - * @see Crypt_AES::encrypt() - * @see Crypt_AES::decrypt() - * @var String - * @access private - */ - var $ecb; - - /** - * The SubByte S-Box - * - * @see Crypt_AES::_encryptBlock() - * @var Array - * @access intern - */ - var $sbox = array( - 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 - ); - - /** - * The inverse SubByte S-Box - * - * @see Crypt_AES::_decryptBlock() - * @var Array - * @access intern - */ - var $isbox = array( - 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, - 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, - 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, - 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, - 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, - 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, - 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, - 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, - 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, - 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, - 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, - 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, - 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, - 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, - 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D - ); + var $const_namespace = 'AES'; /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be - * CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used. + * Determines whether or not the mcrypt extension should be used. * + * $mode could be: + * + * - CRYPT_AES_MODE_ECB + * + * - CRYPT_AES_MODE_CBC + * + * - CRYPT_AES_MODE_CTR + * + * - CRYPT_AES_MODE_CFB + * + * - CRYPT_AES_MODE_OFB + * + * If not explictly set, CRYPT_AES_MODE_CBC will be used. + * + * @see Crypt_Rijndael::Crypt_Rijndael() + * @see Crypt_Base::Crypt_Base() * @param optional Integer $mode - * @return Crypt_AES * @access public */ function Crypt_AES($mode = CRYPT_AES_MODE_CBC) { - if ( !defined('CRYPT_AES_MODE') ) { - switch (true) { - case extension_loaded('mcrypt') && in_array('rijndael-128', mcrypt_list_algorithms()): - define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT); - break; - default: - define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL); - } - } - - switch ( CRYPT_AES_MODE ) { - case CRYPT_AES_MODE_MCRYPT: - switch ($mode) { - case CRYPT_AES_MODE_ECB: - $this->paddable = true; - $this->mode = MCRYPT_MODE_ECB; - break; - case CRYPT_AES_MODE_CTR: - // ctr doesn't have a constant associated with it even though it appears to be fairly widely - // supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to - // include a compatibility layer. the layer has been implemented but, for now, is commented out. - $this->mode = 'ctr'; - //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR; - break; - case CRYPT_AES_MODE_CFB: - $this->mode = 'ncfb'; - break; - case CRYPT_AES_MODE_OFB: - $this->mode = MCRYPT_MODE_NOFB; - break; - case CRYPT_AES_MODE_CBC: - default: - $this->paddable = true; - $this->mode = MCRYPT_MODE_CBC; - } - - break; - default: - switch ($mode) { - case CRYPT_AES_MODE_ECB: - $this->paddable = true; - $this->mode = CRYPT_RIJNDAEL_MODE_ECB; - break; - case CRYPT_AES_MODE_CTR: - $this->mode = CRYPT_RIJNDAEL_MODE_CTR; - break; - case CRYPT_AES_MODE_CFB: - $this->mode = CRYPT_RIJNDAEL_MODE_CFB; - break; - case CRYPT_AES_MODE_OFB: - $this->mode = CRYPT_RIJNDAEL_MODE_OFB; - break; - case CRYPT_AES_MODE_CBC: - default: - $this->paddable = true; - $this->mode = CRYPT_RIJNDAEL_MODE_CBC; - } - } - - if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) { - parent::Crypt_Rijndael($this->mode); - } - } - - /** - * Extended Crypt_Rijndael::_setup() - * - * Optimizing the key schedule arrays ($w, $dw) for _encryptBlock() and _decryptBlock() after Crypt_Rijndael::_setup() - * - * @see Crypt_Rijndael::_setup() - * @access private - */ - function _setup() - { - if (!$this->changed) { - return; - } - - $this->w = $this->dw = array(); - parent::_setup(); - - $this->dw = array_reverse($this->dw); - $w = array_pop($this->w); - $dw = array_pop($this->dw); - foreach ($this->w as $r => $wr) { - foreach ($wr as $c => $wc) { - $w[] = $wc; - $dw[] = $this->dw[$r][$c]; - } - } - $this->w = $w; - $this->dw = $dw; + parent::Crypt_Rijndael($mode); } /** @@ -329,6 +174,7 @@ class Crypt_AES extends Crypt_Rijndael { * * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything. * + * @see Crypt_Rijndael::setBlockLength() * @access public * @param Integer $length */ @@ -336,610 +182,6 @@ class Crypt_AES extends Crypt_Rijndael { { return; } - - /** - * Sets the initialization vector. (optional) - * - * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed - * to be all zero's. - * - * @access public - * @param String $iv - */ - function setIV($iv) - { - parent::setIV($iv); - if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { - $this->changed = true; - } - } - - /** - * Encrypts a message. - * - * $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the - * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following - * URL: - * - * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} - * - * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. - * strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that - * length. - * - * @see Crypt_AES::decrypt() - * @access public - * @param String $plaintext - */ - function encrypt($plaintext) - { - if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { - $this->_mcryptSetup(); - - // re: http://phpseclib.sourceforge.net/cfb-demo.phps - // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's - // rewritten CFB implementation the above outputs the same thing twice. - if ($this->mode == 'ncfb' && $this->continuousBuffer) { - $iv = &$this->encryptIV; - $pos = &$this->enbuffer['pos']; - $len = strlen($plaintext); - $ciphertext = ''; - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = 16 - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - $ciphertext = substr($iv, $orig_pos) ^ $plaintext; - $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); - $this->enbuffer['enmcrypt_init'] = true; - } - if ($len >= 16) { - if ($this->enbuffer['enmcrypt_init'] === false || $len > 280) { - if ($this->enbuffer['enmcrypt_init'] === true) { - mcrypt_generic_init($this->enmcrypt, $this->key, $iv); - $this->enbuffer['enmcrypt_init'] = false; - } - $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 16)); - $iv = substr($ciphertext, -16); - $len%= 16; - } else { - while ($len >= 16) { - $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 16); - $ciphertext.= $iv; - $len-= 16; - $i+= 16; - } - } - } - - if ($len) { - $iv = mcrypt_generic($this->ecb, $iv); - $block = $iv ^ substr($plaintext, -$len); - $iv = substr_replace($iv, $block, 0, $len); - $ciphertext.= $block; - $pos = $len; - } - - return $ciphertext; - } - - if ($this->paddable) { - $plaintext = $this->_pad($plaintext); - } - - $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); - - if (!$this->continuousBuffer) { - mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); - } - - return $ciphertext; - } - - return parent::encrypt($plaintext); - } - - /** - * Decrypts a message. - * - * If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is. - * - * @see Crypt_AES::encrypt() - * @access public - * @param String $ciphertext - */ - function decrypt($ciphertext) - { - if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { - $this->_mcryptSetup(); - - if ($this->mode == 'ncfb' && $this->continuousBuffer) { - $iv = &$this->decryptIV; - $pos = &$this->debuffer['pos']; - $len = strlen($ciphertext); - $plaintext = ''; - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = 16 - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize - $plaintext = substr($iv, $orig_pos) ^ $ciphertext; - $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); - } - if ($len >= 16) { - $cb = substr($ciphertext, $i, $len - $len % 16); - $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; - $iv = substr($cb, -16); - $len%= 16; - } - if ($len) { - $iv = mcrypt_generic($this->ecb, $iv); - $plaintext.= $iv ^ substr($ciphertext, -$len); - $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); - $pos = $len; - } - - return $plaintext; - } - - if ($this->paddable) { - // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0)); - } - - $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); - - if (!$this->continuousBuffer) { - mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); - } - - return $this->paddable ? $this->_unpad($plaintext) : $plaintext; - } - - return parent::decrypt($ciphertext); - } - - /** - * Setup mcrypt - * - * Validates all the variables. - * - * @access private - */ - function _mcryptSetup() - { - if (!$this->changed) { - return; - } - - if (!$this->explicit_key_length) { - // this just copied from Crypt_Rijndael::_setup() - $length = strlen($this->key) >> 2; - if ($length > 8) { - $length = 8; - } else if ($length < 4) { - $length = 4; - } - $this->Nk = $length; - $this->key_size = $length << 2; - } - - switch ($this->Nk) { - case 4: // 128 - $this->key_size = 16; - break; - case 5: // 160 - case 6: // 192 - $this->key_size = 24; - break; - case 7: // 224 - case 8: // 256 - $this->key_size = 32; - } - - $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0)); - $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0)); - - if (!isset($this->enmcrypt)) { - $mode = $this->mode; - //$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode; - - $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, ''); - $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, ''); - - if ($mode == 'ncfb') { - $this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, ''); - } - - } // else should mcrypt_generic_deinit be called? - - mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); - mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); - - if ($this->mode == 'ncfb') { - mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); - } - - $this->changed = false; - } - - /** - * Encrypts a block - * - * Optimized over Crypt_Rijndael's implementation by means of loop unrolling. - * - * @see Crypt_Rijndael::_encryptBlock() - * @access private - * @param String $in - * @return String - */ - function _encryptBlock($in) - { - $state = unpack('N*', $in); - - $sbox = $this->sbox; - $w = $this->w; - $t0 = $this->t0; - $t1 = $this->t1; - $t2 = $this->t2; - $t3 = $this->t3; - - // addRoundKey - $s0 = $state[1] ^ $w[4]; - $s1 = $state[2] ^ $w[5]; - $s2 = $state[3] ^ $w[6]; - $s3 = $state[4] ^ $w[7]; - - // shiftRows + subWord + mixColumns + addRoundKey - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[8]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[9]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[10]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[11]; - - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[12]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[13]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[14]; - $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[15]; - - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[16]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[17]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[18]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[19]; - - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[20]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[21]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[22]; - $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[23]; - - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[24]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[25]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[26]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[27]; - - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[28]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[29]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[30]; - $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[31]; - - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[32]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[33]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[34]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[35]; - - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[36]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[37]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[38]; - $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[39]; - - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[40]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[41]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[42]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[43]; - - switch ($this->Nr) { - case 10: - break; - - case 14: - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46]; - $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47]; - - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51]; - - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[52]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[53]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[54]; - $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[55]; - - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[56]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[57]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[58]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[59]; - break; - - case 12: - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46]; - $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47]; - - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51]; - break; - - case 13: - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46]; - $s3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47]; - - $e0 = $t0[($s0 >> 24) & 0xff] ^ $t1[($s1 >> 16) & 0xff] ^ $t2[($s2 >> 8) & 0xff] ^ $t3[$s3 & 0xff] ^ $w[48]; - $e1 = $t0[($s1 >> 24) & 0xff] ^ $t1[($s2 >> 16) & 0xff] ^ $t2[($s3 >> 8) & 0xff] ^ $t3[$s0 & 0xff] ^ $w[49]; - $e2 = $t0[($s2 >> 24) & 0xff] ^ $t1[($s3 >> 16) & 0xff] ^ $t2[($s0 >> 8) & 0xff] ^ $t3[$s1 & 0xff] ^ $w[50]; - $e3 = $t0[($s3 >> 24) & 0xff] ^ $t1[($s0 >> 16) & 0xff] ^ $t2[($s1 >> 8) & 0xff] ^ $t3[$s2 & 0xff] ^ $w[51]; - - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[52]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[53]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[54]; - $e3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[55]; - // Note: Here we skip $s3 but using $e3 - - $e0 = $s0; - $e1 = $s1; - $e2 = $s2; - // $e3 = $s3; - break; - - default: // 11 - $s0 = $t0[($e0 >> 24) & 0xff] ^ $t1[($e1 >> 16) & 0xff] ^ $t2[($e2 >> 8) & 0xff] ^ $t3[$e3 & 0xff] ^ $w[44]; - $s1 = $t0[($e1 >> 24) & 0xff] ^ $t1[($e2 >> 16) & 0xff] ^ $t2[($e3 >> 8) & 0xff] ^ $t3[$e0 & 0xff] ^ $w[45]; - $s2 = $t0[($e2 >> 24) & 0xff] ^ $t1[($e3 >> 16) & 0xff] ^ $t2[($e0 >> 8) & 0xff] ^ $t3[$e1 & 0xff] ^ $w[46]; - $e3 = $t0[($e3 >> 24) & 0xff] ^ $t1[($e0 >> 16) & 0xff] ^ $t2[($e1 >> 8) & 0xff] ^ $t3[$e2 & 0xff] ^ $w[47]; - // Note: Here we skip $s3 but using $e3 - - $e0 = $s0; - $e1 = $s1; - $e2 = $s2; - // $e3 = $s3; - } - - // subWord - $e0 = $sbox[$e0 & 0xff] | ($sbox[($e0 >> 8) & 0xff] << 8) | ($sbox[($e0 >> 16) & 0xff] << 16) | ($sbox[($e0 >> 24) & 0xff] << 24); - $e1 = $sbox[$e1 & 0xff] | ($sbox[($e1 >> 8) & 0xff] << 8) | ($sbox[($e1 >> 16) & 0xff] << 16) | ($sbox[($e1 >> 24) & 0xff] << 24); - $e2 = $sbox[$e2 & 0xff] | ($sbox[($e2 >> 8) & 0xff] << 8) | ($sbox[($e2 >> 16) & 0xff] << 16) | ($sbox[($e2 >> 24) & 0xff] << 24); - $e3 = $sbox[$e3 & 0xff] | ($sbox[($e3 >> 8) & 0xff] << 8) | ($sbox[($e3 >> 16) & 0xff] << 16) | ($sbox[($e3 >> 24) & 0xff] << 24); - - // shiftRows + addRoundKey - return pack('N*', - ($e0 & 0xFF000000) ^ ($e1 & 0x00FF0000) ^ ($e2 & 0x0000FF00) ^ ($e3 & 0x000000FF) ^ $w[0], - ($e1 & 0xFF000000) ^ ($e2 & 0x00FF0000) ^ ($e3 & 0x0000FF00) ^ ($e0 & 0x000000FF) ^ $w[1], - ($e2 & 0xFF000000) ^ ($e3 & 0x00FF0000) ^ ($e0 & 0x0000FF00) ^ ($e1 & 0x000000FF) ^ $w[2], - ($e3 & 0xFF000000) ^ ($e0 & 0x00FF0000) ^ ($e1 & 0x0000FF00) ^ ($e2 & 0x000000FF) ^ $w[3] - ); - } - - /** - * Decrypts a block - * - * Optimized over Crypt_Rijndael's implementation by means of loop unrolling. - * - * @see Crypt_Rijndael::_decryptBlock() - * @access private - * @param String $in - * @return String - */ - function _decryptBlock($in) - { - $state = unpack('N*', $in); - - $sbox = $this->isbox; - $dw = $this->dw; - $dt0 = $this->dt0; - $dt1 = $this->dt1; - $dt2 = $this->dt2; - $dt3 = $this->dt3; - - // addRoundKey - $s0 = $state[1] ^ $dw[4]; - $s1 = $state[2] ^ $dw[5]; - $s2 = $state[3] ^ $dw[6]; - $s3 = $state[4] ^ $dw[7]; - - // invShiftRows + invSubBytes + invMixColumns + addRoundKey - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[8]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[9]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[10]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[11]; - - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[12]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[13]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[14]; - $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[15]; - - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[16]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[17]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[18]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[19]; - - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[20]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[21]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[22]; - $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[23]; - - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[24]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[25]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[26]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[27]; - - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[28]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[29]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[30]; - $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[31]; - - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[32]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[33]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[34]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[35]; - - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[36]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[37]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[38]; - $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[39]; - - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[40]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[41]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[42]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[43]; - - switch ($this->Nr) { - case 10: - break; - - case 14: - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46]; - $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47]; - - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51]; - - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[52]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[53]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[54]; - $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[55]; - - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[56]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[57]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[58]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[59]; - break; - - case 12: - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46]; - $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47]; - - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51]; - break; - - case 13: - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46]; - $s3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47]; - - $e0 = $dt0[($s0 >> 24) & 0xff] ^ $dt1[($s3 >> 16) & 0xff] ^ $dt2[($s2 >> 8) & 0xff] ^ $dt3[$s1 & 0xff] ^ $dw[48]; - $e1 = $dt0[($s1 >> 24) & 0xff] ^ $dt1[($s0 >> 16) & 0xff] ^ $dt2[($s3 >> 8) & 0xff] ^ $dt3[$s2 & 0xff] ^ $dw[49]; - $e2 = $dt0[($s2 >> 24) & 0xff] ^ $dt1[($s1 >> 16) & 0xff] ^ $dt2[($s0 >> 8) & 0xff] ^ $dt3[$s3 & 0xff] ^ $dw[50]; - $e3 = $dt0[($s3 >> 24) & 0xff] ^ $dt1[($s2 >> 16) & 0xff] ^ $dt2[($s1 >> 8) & 0xff] ^ $dt3[$s0 & 0xff] ^ $dw[51]; - - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[52]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[53]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[54]; - $e3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[55]; - // Note: Here we skip $s3 but using $e3 - - $e0 = $s0; - $e1 = $s1; - $e2 = $s2; - // $e3 = $s3; - break; - - default: // 11 - $s0 = $dt0[($e0 >> 24) & 0xff] ^ $dt1[($e3 >> 16) & 0xff] ^ $dt2[($e2 >> 8) & 0xff] ^ $dt3[$e1 & 0xff] ^ $dw[44]; - $s1 = $dt0[($e1 >> 24) & 0xff] ^ $dt1[($e0 >> 16) & 0xff] ^ $dt2[($e3 >> 8) & 0xff] ^ $dt3[$e2 & 0xff] ^ $dw[45]; - $s2 = $dt0[($e2 >> 24) & 0xff] ^ $dt1[($e1 >> 16) & 0xff] ^ $dt2[($e0 >> 8) & 0xff] ^ $dt3[$e3 & 0xff] ^ $dw[46]; - $e3 = $dt0[($e3 >> 24) & 0xff] ^ $dt1[($e2 >> 16) & 0xff] ^ $dt2[($e1 >> 8) & 0xff] ^ $dt3[$e0 & 0xff] ^ $dw[47]; - // Note: Here we skip $s3 but using $e3 - - $e0 = $s0; - $e1 = $s1; - $e2 = $s2; - // $e3 = $s3; - } - - // invSubWord - $e0 = $sbox[$e0 & 0xff] | ($sbox[($e0 >> 8) & 0xff] << 8) | ($sbox[($e0 >> 16) & 0xff] << 16) | ($sbox[($e0 >> 24) & 0xff] << 24); - $e1 = $sbox[$e1 & 0xff] | ($sbox[($e1 >> 8) & 0xff] << 8) | ($sbox[($e1 >> 16) & 0xff] << 16) | ($sbox[($e1 >> 24) & 0xff] << 24); - $e2 = $sbox[$e2 & 0xff] | ($sbox[($e2 >> 8) & 0xff] << 8) | ($sbox[($e2 >> 16) & 0xff] << 16) | ($sbox[($e2 >> 24) & 0xff] << 24); - $e3 = $sbox[$e3 & 0xff] | ($sbox[($e3 >> 8) & 0xff] << 8) | ($sbox[($e3 >> 16) & 0xff] << 16) | ($sbox[($e3 >> 24) & 0xff] << 24); - - // invShiftRows + addRoundKey - return pack('N*', - ($e0 & 0xFF000000) ^ ($e3 & 0x00FF0000) ^ ($e2 & 0x0000FF00) ^ ($e1 & 0x000000FF) ^ $dw[0], - ($e1 & 0xFF000000) ^ ($e0 & 0x00FF0000) ^ ($e3 & 0x0000FF00) ^ ($e2 & 0x000000FF) ^ $dw[1], - ($e2 & 0xFF000000) ^ ($e1 & 0x00FF0000) ^ ($e0 & 0x0000FF00) ^ ($e3 & 0x000000FF) ^ $dw[2], - ($e3 & 0xFF000000) ^ ($e2 & 0x00FF0000) ^ ($e1 & 0x0000FF00) ^ ($e0 & 0x000000FF) ^ $dw[3] - ); - } - - /** - * Treat consecutive "packets" as if they are a continuous buffer. - * - * The default behavior. - * - * @see Crypt_Rijndael::disableContinuousBuffer() - * @access public - */ - function enableContinuousBuffer() - { - parent::enableContinuousBuffer(); - - if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) { - $this->enbuffer['enmcrypt_init'] = true; - $this->debuffer['demcrypt_init'] = true; - } - } - - /** - * Treat consecutive packets as if they are a discontinuous buffer. - * - * The default behavior. - * - * @see Crypt_Rijndael::enableContinuousBuffer() - * @access public - */ - function disableContinuousBuffer() - { - parent::disableContinuousBuffer(); - - if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) { - mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); - mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); - } - } } // vim: ts=4:sw=4:et: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Base.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Base.php new file mode 100644 index 0000000000..7c650ca729 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Base.php @@ -0,0 +1,1989 @@ + + * @author Hans-Juergen Petrich + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 1.0.1 + * @link http://phpseclib.sourceforge.net + */ + +/**#@+ + * @access public + * @see Crypt_Base::encrypt() + * @see Crypt_Base::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_MODE_CTR', -1); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +define('CRYPT_MODE_ECB', 1); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +define('CRYPT_MODE_CBC', 2); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +define('CRYPT_MODE_CFB', 3); +/** + * Encrypt / decrypt using the Output Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +define('CRYPT_MODE_OFB', 4); +/** + * Encrypt / decrypt using streaming mode. + * + */ +define('CRYPT_MODE_STREAM', 5); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_Base::Crypt_Base() + */ +/** + * Base value for the internal implementation $engine switch + */ +define('CRYPT_MODE_INTERNAL', 1); +/** + * Base value for the mcrypt implementation $engine switch + */ +define('CRYPT_MODE_MCRYPT', 2); +/**#@-*/ + +/** + * Base Class for all Crypt_* cipher classes + * + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @version 1.0.0 + * @access public + * @package Crypt_Base + */ +class Crypt_Base { + /** + * The Encryption Mode + * + * @see Crypt_Base::Crypt_Base() + * @var Integer + * @access private + */ + var $mode; + + /** + * The Block Length of the block cipher + * + * @var Integer + * @access private + */ + var $block_size = 16; + + /** + * The Key + * + * @see Crypt_Base::setKey() + * @var String + * @access private + */ + var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + + /** + * The Initialization Vector + * + * @see Crypt_Base::setIV() + * @var String + * @access private + */ + var $iv; + + /** + * A "sliding" Initialization Vector + * + * @see Crypt_Base::enableContinuousBuffer() + * @see Crypt_Base::_clearBuffers() + * @var String + * @access private + */ + var $encryptIV; + + /** + * A "sliding" Initialization Vector + * + * @see Crypt_Base::enableContinuousBuffer() + * @see Crypt_Base::_clearBuffers() + * @var String + * @access private + */ + var $decryptIV; + + /** + * Continuous Buffer status + * + * @see Crypt_Base::enableContinuousBuffer() + * @var Boolean + * @access private + */ + var $continuousBuffer = false; + + /** + * Encryption buffer for CTR, OFB and CFB modes + * + * @see Crypt_Base::encrypt() + * @see Crypt_Base::_clearBuffers() + * @var Array + * @access private + */ + var $enbuffer; + + /** + * Decryption buffer for CTR, OFB and CFB modes + * + * @see Crypt_Base::decrypt() + * @see Crypt_Base::_clearBuffers() + * @var Array + * @access private + */ + var $debuffer; + + /** + * mcrypt resource for encryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_Base::encrypt() + * @var Resource + * @access private + */ + var $enmcrypt; + + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_Base::decrypt() + * @var Resource + * @access private + */ + var $demcrypt; + + /** + * Does the enmcrypt resource need to be (re)initialized? + * + * @see Crypt_Twofish::setKey() + * @see Crypt_Twofish::setIV() + * @var Boolean + * @access private + */ + var $enchanged = true; + + /** + * Does the demcrypt resource need to be (re)initialized? + * + * @see Crypt_Twofish::setKey() + * @see Crypt_Twofish::setIV() + * @var Boolean + * @access private + */ + var $dechanged = true; + + /** + * mcrypt resource for CFB mode + * + * mcrypt's CFB mode, in (and only in) buffered context, + * is broken, so phpseclib implements the CFB mode by it self, + * even when the mcrypt php extension is available. + * + * In order to do the CFB-mode work (fast) phpseclib + * use a separate ECB-mode mcrypt resource. + * + * @link http://phpseclib.sourceforge.net/cfb-demo.phps + * @see Crypt_Base::encrypt() + * @see Crypt_Base::decrypt() + * @see Crypt_Base::_setupMcrypt() + * @var Resource + * @access private + */ + var $ecb; + + /** + * Optimizing value while CFB-encrypting + * + * Only relevant if $continuousBuffer enabled + * and $engine == CRYPT_MODE_MCRYPT + * + * It's faster to re-init $enmcrypt if + * $buffer bytes > $cfb_init_len than + * using the $ecb resource furthermore. + * + * This value depends of the choosen cipher + * and the time it would be needed for it's + * initialization [by mcrypt_generic_init()] + * which, typically, depends on the complexity + * on its internaly Key-expanding algorithm. + * + * @see Crypt_Base::encrypt() + * @var Integer + * @access private + */ + var $cfb_init_len = 600; + + /** + * Does internal cipher state need to be (re)initialized? + * + * @see setKey() + * @see setIV() + * @see disableContinuousBuffer() + * @var Boolean + * @access private + */ + var $changed = true; + + /** + * Padding status + * + * @see Crypt_Base::enablePadding() + * @var Boolean + * @access private + */ + var $padding = true; + + /** + * Is the mode one that is paddable? + * + * @see Crypt_Base::Crypt_Base() + * @var Boolean + * @access private + */ + var $paddable = false; + + /** + * Holds which crypt engine internaly should be use, + * which will be determined automatically on __construct() + * + * Currently available $engines are: + * - CRYPT_MODE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required) + * - CRYPT_MODE_INTERNAL (slower, pure php-engine, no php-extension required) + * + * In the pipeline... maybe. But currently not available: + * - CRYPT_MODE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required) + * + * If possible, CRYPT_MODE_MCRYPT will be used for each cipher. + * Otherwise CRYPT_MODE_INTERNAL + * + * @see Crypt_Base::encrypt() + * @see Crypt_Base::decrypt() + * @var Integer + * @access private + */ + var $engine; + + /** + * The mcrypt specific name of the cipher + * + * Only used if $engine == CRYPT_MODE_MCRYPT + * + * @link http://www.php.net/mcrypt_module_open + * @link http://www.php.net/mcrypt_list_algorithms + * @see Crypt_Base::_setupMcrypt() + * @var String + * @access private + */ + var $cipher_name_mcrypt; + + /** + * The default password key_size used by setPassword() + * + * @see Crypt_Base::setPassword() + * @var Integer + * @access private + */ + var $password_key_size = 32; + + /** + * The default salt used by setPassword() + * + * @see Crypt_Base::setPassword() + * @var String + * @access private + */ + var $password_default_salt = 'phpseclib/salt'; + + /** + * The namespace used by the cipher for its constants. + * + * ie: AES.php is using CRYPT_AES_MODE_* for its constants + * so $const_namespace is AES + * + * DES.php is using CRYPT_DES_MODE_* for its constants + * so $const_namespace is DES... and so on + * + * All CRYPT_<$const_namespace>_MODE_* are aliases of + * the generic CRYPT_MODE_* constants, so both could be used + * for each cipher. + * + * Example: + * $aes = new Crypt_AES(CRYPT_AES_MODE_CFB); // $aes will operate in cfb mode + * $aes = new Crypt_AES(CRYPT_MODE_CFB); // identical + * + * @see Crypt_Base::Crypt_Base() + * @var String + * @access private + */ + var $const_namespace; + + /** + * The name of the performance-optimized callback function + * + * Used by encrypt() / decrypt() + * only if $engine == CRYPT_MODE_INTERNAL + * + * @see Crypt_Base::encrypt() + * @see Crypt_Base::decrypt() + * @see Crypt_Base::_setupInlineCrypt() + * @see Crypt_Base::$use_inline_crypt + * @var Callback + * @access private + */ + var $inline_crypt; + + /** + * Holds whether performance-optimized $inline_crypt() can/should be used. + * + * @see Crypt_Base::encrypt() + * @see Crypt_Base::decrypt() + * @see Crypt_Base::inline_crypt + * @var mixed + * @access private + */ + var $use_inline_crypt; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_MODE_ECB + * + * - CRYPT_MODE_CBC + * + * - CRYPT_MODE_CTR + * + * - CRYPT_MODE_CFB + * + * - CRYPT_MODE_OFB + * + * (or the alias constants of the choosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...) + * + * If not explictly set, CRYPT_MODE_CBC will be used. + * + * @param optional Integer $mode + * @access public + */ + function Crypt_Base($mode = CRYPT_MODE_CBC) + { + $const_crypt_mode = 'CRYPT_' . $this->const_namespace . '_MODE'; + + // Determining the availibility of mcrypt support for the cipher + if (!defined($const_crypt_mode)) { + switch (true) { + case extension_loaded('mcrypt') && in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()): + define($const_crypt_mode, CRYPT_MODE_MCRYPT); + break; + default: + define($const_crypt_mode, CRYPT_MODE_INTERNAL); + } + } + + // Determining which internal $engine should be used. + // The fastes possible first. + switch (true) { + case empty($this->cipher_name_mcrypt): // The cipher module has no mcrypt-engine support at all so we force CRYPT_MODE_INTERNAL + $this->engine = CRYPT_MODE_INTERNAL; + break; + case constant($const_crypt_mode) == CRYPT_MODE_MCRYPT: + $this->engine = CRYPT_MODE_MCRYPT; + break; + default: + $this->engine = CRYPT_MODE_INTERNAL; + } + + // $mode dependent settings + switch ($mode) { + case CRYPT_MODE_ECB: + $this->paddable = true; + $this->mode = $mode; + break; + case CRYPT_MODE_CTR: + case CRYPT_MODE_CFB: + case CRYPT_MODE_OFB: + case CRYPT_MODE_STREAM: + $this->mode = $mode; + break; + case CRYPT_MODE_CBC: + default: + $this->paddable = true; + $this->mode = CRYPT_MODE_CBC; + } + + // Determining whether inline crypting can be used by the cipher + if ($this->use_inline_crypt !== false && function_exists('create_function')) { + $this->use_inline_crypt = true; + } + } + + /** + * Sets the initialization vector. (optional) + * + * SetIV is not required when CRYPT_MODE_ECB (or ie for AES: CRYPT_AES_MODE_ECB) is being used. If not explictly set, it'll be assumed + * to be all zero's. + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @access public + * @param String $iv + */ + function setIV($iv) + { + if ($this->mode == CRYPT_MODE_ECB) { + return; + } + + $this->iv = $iv; + $this->changed = true; + } + + /** + * Sets the key. + * + * The min/max length(s) of the key depends on the cipher which is used. + * If the key not fits the length(s) of the cipher it will paded with null bytes + * up to the closest valid key length. If the key is more than max length, + * we trim the excess bits. + * + * If the key is not explicitly set, it'll be assumed to be all null bytes. + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @access public + * @param String $key + */ + function setKey($key) + { + $this->key = $key; + $this->changed = true; + } + + /** + * Sets the password. + * + * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: + * $hash, $salt, $count, $dkLen + * + * Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @see Crypt/Hash.php + * @param String $password + * @param optional String $method + * @access public + */ + function setPassword($password, $method = 'pbkdf2') + { + $key = ''; + + switch ($method) { + default: // 'pbkdf2' + $func_args = func_get_args(); + + // Hash function + $hash = isset($func_args[2]) ? $func_args[2] : 'sha1'; + + // WPA and WPA2 use the SSID as the salt + $salt = isset($func_args[3]) ? $func_args[3] : $this->password_default_salt; + + // RFC2898#section-4.2 uses 1,000 iterations by default + // WPA and WPA2 use 4,096. + $count = isset($func_args[4]) ? $func_args[4] : 1000; + + // Keylength + $dkLen = isset($func_args[5]) ? $func_args[5] : $this->password_key_size; + + // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable + switch (true) { + case !function_exists('hash_pbkdf2'): + case !function_exists('hash_algos'): + case !in_array($hash, hash_algos()): + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + $i = 1; + while (strlen($key) < $dkLen) { + $hmac = new Crypt_Hash(); + $hmac->setHash($hash); + $hmac->setKey($password); + $f = $u = $hmac->hash($salt . pack('N', $i++)); + for ($j = 2; $j <= $count; ++$j) { + $u = $hmac->hash($u); + $f^= $u; + } + $key.= $f; + } + $key = substr($key, 0, $dkLen); + break; + default: + $key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true); + } + } + + $this->setKey($key); + } + + /** + * Encrypts a message. + * + * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher + * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's + * necessary are discussed in the following + * URL: + * + * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} + * + * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. + * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that + * length. + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @see Crypt_Base::decrypt() + * @access public + * @param String $plaintext + * @return String $cipertext + */ + function encrypt($plaintext) + { + if ($this->engine == CRYPT_MODE_MCRYPT) { + if ($this->changed) { + $this->_setupMcrypt(); + $this->changed = false; + } + if ($this->enchanged) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + $this->enchanged = false; + } + + // re: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} + // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's + // rewritten CFB implementation the above outputs the same thing twice. + if ($this->mode == CRYPT_MODE_CFB && $this->continuousBuffer) { + $block_size = $this->block_size; + $iv = &$this->encryptIV; + $pos = &$this->enbuffer['pos']; + $len = strlen($plaintext); + $ciphertext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + $this->enbuffer['enmcrypt_init'] = true; + } + if ($len >= $block_size) { + if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { + if ($this->enbuffer['enmcrypt_init'] === true) { + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + $this->enbuffer['enmcrypt_init'] = false; + } + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); + $iv = substr($ciphertext, -$block_size); + $len%= $block_size; + } else { + while ($len >= $block_size) { + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); + $ciphertext.= $iv; + $len-= $block_size; + $i+= $block_size; + } + } + } + + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $block = $iv ^ substr($plaintext, -$len); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + + return $ciphertext; + } + + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + } + + return $ciphertext; + } + + if ($this->changed) { + $this->_setup(); + $this->changed = false; + } + if ($this->use_inline_crypt) { + $inline = $this->inline_crypt; + return $inline('encrypt', $this, $plaintext); + } + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + + $buffer = &$this->enbuffer; + $block_size = $this->block_size; + $ciphertext = ''; + switch ($this->mode) { + case CRYPT_MODE_ECB: + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size)); + } + break; + case CRYPT_MODE_CBC: + $xor = $this->encryptIV; + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $block = $this->_encryptBlock($block ^ $xor); + $xor = $block; + $ciphertext.= $block; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } + break; + case CRYPT_MODE_CTR: + $xor = $this->encryptIV; + if (strlen($buffer['encrypted'])) { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + if (strlen($block) > strlen($buffer['encrypted'])) { + $buffer['encrypted'].= $this->_encryptBlock($this->_generateXor($xor, $block_size)); + } + $key = $this->_stringShift($buffer['encrypted'], $block_size); + $ciphertext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $key = $this->_encryptBlock($this->_generateXor($xor, $block_size)); + $ciphertext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) % $block_size) { + $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; + } + } + break; + case CRYPT_MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + if ($this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); + $ciphertext.= $iv; + $len-= $block_size; + $i+= $block_size; + } + if ($len) { + $iv = $this->_encryptBlock($iv); + $block = $iv ^ substr($plaintext, $i); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + break; + case CRYPT_MODE_OFB: + $xor = $this->encryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + if (strlen($block) > strlen($buffer['xor'])) { + $xor = $this->_encryptBlock($xor); + $buffer['xor'].= $xor; + } + $key = $this->_stringShift($buffer['xor'], $block_size); + $ciphertext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $xor = $this->_encryptBlock($xor); + $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) % $block_size) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + break; + case CRYPT_MODE_STREAM: + $ciphertext = $this->_encryptBlock($plaintext); + break; + } + + return $ciphertext; + } + + /** + * Decrypts a message. + * + * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until + * it is. + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @see Crypt_Base::encrypt() + * @access public + * @param String $ciphertext + * @return String $plaintext + */ + function decrypt($ciphertext) + { + if ($this->engine == CRYPT_MODE_MCRYPT) { + $block_size = $this->block_size; + if ($this->changed) { + $this->_setupMcrypt(); + $this->changed = false; + } + if ($this->dechanged) { + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + $this->dechanged = false; + } + + if ($this->mode == CRYPT_MODE_CFB && $this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$this->debuffer['pos']; + $len = strlen($ciphertext); + $plaintext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + if ($len >= $block_size) { + $cb = substr($ciphertext, $i, $len - $len % $block_size); + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $iv = substr($cb, -$block_size); + $len%= $block_size; + } + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $plaintext.= $iv ^ substr($ciphertext, -$len); + $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); + $pos = $len; + } + + return $plaintext; + } + + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from {@link http://www.php.net/function.mcrypt-generic}: + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($block_size - strlen($ciphertext) % $block_size) % $block_size, chr(0)); + } + + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + if ($this->changed) { + $this->_setup(); + $this->changed = false; + } + if ($this->use_inline_crypt) { + $inline = $this->inline_crypt; + return $inline('decrypt', $this, $ciphertext); + } + + $block_size = $this->block_size; + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does [...] + $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($block_size - strlen($ciphertext) % $block_size) % $block_size, chr(0)); + } + + $buffer = &$this->debuffer; + $plaintext = ''; + switch ($this->mode) { + case CRYPT_MODE_ECB: + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size)); + } + break; + case CRYPT_MODE_CBC: + $xor = $this->decryptIV; + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + $plaintext.= $this->_decryptBlock($block) ^ $xor; + $xor = $block; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } + break; + case CRYPT_MODE_CTR: + $xor = $this->decryptIV; + if (strlen($buffer['ciphertext'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + if (strlen($block) > strlen($buffer['ciphertext'])) { + $buffer['ciphertext'].= $this->_encryptBlock($this->_generateXor($xor, $block_size)); + } + $key = $this->_stringShift($buffer['ciphertext'], $block_size); + $plaintext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + $key = $this->_encryptBlock($this->_generateXor($xor, $block_size)); + $plaintext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) % $block_size) { + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + } + } + break; + case CRYPT_MODE_CFB: + if ($this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; + } + $len = strlen($ciphertext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->_encryptBlock($iv); + $cb = substr($ciphertext, $i, $block_size); + $plaintext.= $iv ^ $cb; + $iv = $cb; + $len-= $block_size; + $i+= $block_size; + } + if ($len) { + $iv = $this->_encryptBlock($iv); + $plaintext.= $iv ^ substr($ciphertext, $i); + $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len); + $pos = $len; + } + break; + case CRYPT_MODE_OFB: + $xor = $this->decryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + if (strlen($block) > strlen($buffer['xor'])) { + $xor = $this->_encryptBlock($xor); + $buffer['xor'].= $xor; + } + $key = $this->_stringShift($buffer['xor'], $block_size); + $plaintext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $xor = $this->_encryptBlock($xor); + $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) % $block_size) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + break; + case CRYPT_MODE_STREAM: + $plaintext = $this->_decryptBlock($ciphertext); + break; + } + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + /** + * Pad "packets". + * + * Block ciphers working by encrypting between their specified [$this->]block_size at a time + * If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to + * pad the input so that it is of the proper length. + * + * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH, + * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping + * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is + * transmitted separately) + * + * @see Crypt_Base::disablePadding() + * @access public + */ + function enablePadding() + { + $this->padding = true; + } + + /** + * Do not pad packets. + * + * @see Crypt_Base::enablePadding() + * @access public + */ + function disablePadding() + { + $this->padding = false; + } + + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $rijndael->encrypt(substr($plaintext, 0, 16)); + * echo $rijndael->encrypt(substr($plaintext, 16, 16)); + * + * + * echo $rijndael->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $rijndael->encrypt(substr($plaintext, 0, 16)); + * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16))); + * + * + * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the Crypt_*() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @see Crypt_Base::disableContinuousBuffer() + * @access public + */ + function enableContinuousBuffer() + { + if ($this->mode == CRYPT_MODE_ECB) { + return; + } + + $this->continuousBuffer = true; + } + + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @see Crypt_Base::enableContinuousBuffer() + * @access public + */ + function disableContinuousBuffer() + { + if ($this->mode == CRYPT_MODE_ECB) { + return; + } + if (!$this->continuousBuffer) { + return; + } + + $this->continuousBuffer = false; + $this->changed = true; + } + + /** + * Encrypts a block + * + * Note: Must extend by the child Crypt_* class + * + * @access private + * @param String $in + * @return String + */ + function _encryptBlock($in) + { + user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); + } + + /** + * Decrypts a block + * + * Note: Must extend by the child Crypt_* class + * + * @access private + * @param String $in + * @return String + */ + function _decryptBlock($in) + { + user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); + } + + /** + * Setup the key (expansion) + * + * Only used if $engine == CRYPT_MODE_INTERNAL + * + * Note: Must extend by the child Crypt_* class + * + * @see Crypt_Base::_setup() + * @access private + */ + function _setupKey() + { + user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); + } + + /** + * Setup the CRYPT_MODE_INTERNAL $engine + * + * (re)init, if necessary, the internal cipher $engine and flush all $buffers + * Used (only) if $engine == CRYPT_MODE_INTERNAL + * + * _setup() will be called each time if $changed === true + * typically this happens when using one or more of following public methods: + * + * - setKey() + * + * - setIV() + * + * - disableContinuousBuffer() + * + * - First run of encrypt() / decrypt() with no init-settings + * + * Internally: _setup() is called always before(!) en/decryption. + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @see setKey() + * @see setIV() + * @see disableContinuousBuffer() + * @access private + */ + function _setup() + { + $this->_clearBuffers(); + $this->_setupKey(); + + if ($this->use_inline_crypt) { + $this->_setupInlineCrypt(); + } + } + + /** + * Setup the CRYPT_MODE_MCRYPT $engine + * + * (re)init, if necessary, the (ext)mcrypt resources and flush all $buffers + * Used (only) if $engine = CRYPT_MODE_MCRYPT + * + * _setupMcrypt() will be called each time if $changed === true + * typically this happens when using one or more of following public methods: + * + * - setKey() + * + * - setIV() + * + * - disableContinuousBuffer() + * + * - First run of encrypt() / decrypt() + * + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @see setKey() + * @see setIV() + * @see disableContinuousBuffer() + * @access private + */ + function _setupMcrypt() + { + $this->_clearBuffers(); + $this->enchanged = $this->dechanged = true; + + if (!isset($this->enmcrypt)) { + static $mcrypt_modes = array( + CRYPT_MODE_CTR => 'ctr', + CRYPT_MODE_ECB => MCRYPT_MODE_ECB, + CRYPT_MODE_CBC => MCRYPT_MODE_CBC, + CRYPT_MODE_CFB => 'ncfb', + CRYPT_MODE_OFB => MCRYPT_MODE_NOFB, + CRYPT_MODE_STREAM => MCRYPT_MODE_STREAM, + ); + + $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + + // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() + // to workaround mcrypt's broken ncfb implementation in buffered mode + // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} + if ($this->mode == CRYPT_MODE_CFB) { + $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); + } + + } // else should mcrypt_generic_deinit be called? + + if ($this->mode == CRYPT_MODE_CFB) { + mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); + } + } + + /** + * Pads a string + * + * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize. + * $this->block_size - (strlen($text) % $this->block_size) bytes are added, each of which is equal to + * chr($this->block_size - (strlen($text) % $this->block_size) + * + * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless + * and padding will, hence forth, be enabled. + * + * @see Crypt_Base::_unpad() + * @param String $text + * @access private + * @return String + */ + function _pad($text) + { + $length = strlen($text); + + if (!$this->padding) { + if ($length % $this->block_size == 0) { + return $text; + } else { + user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); + $this->padding = true; + } + } + + $pad = $this->block_size - ($length % $this->block_size); + + return str_pad($text, $length + $pad, chr($pad)); + } + + /** + * Unpads a string. + * + * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong + * and false will be returned. + * + * @see Crypt_Base::_pad() + * @param String $text + * @access private + * @return String + */ + function _unpad($text) + { + if (!$this->padding) { + return $text; + } + + $length = ord($text[strlen($text) - 1]); + + if (!$length || $length > $this->block_size) { + return false; + } + + return substr($text, 0, -$length); + } + + /** + * Clears internal buffers + * + * Clearing/resetting the internal buffers is done everytime + * after disableContinuousBuffer() or on cipher $engine (re)init + * ie after setKey() or setIV() + * + * Note: Could, but not must, extend by the child Crypt_* class + * + * @access public + */ + function _clearBuffers() + { + $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); + + // mcrypt's handling of invalid's $iv: + // $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size); + $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0"); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @access private + * @return String + */ + function _stringShift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Generate CTR XOR encryption key + * + * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the + * plaintext / ciphertext in CTR mode. + * + * @see Crypt_Base::decrypt() + * @see Crypt_Base::encrypt() + * @param String $iv + * @param Integer $length + * @access private + * @return String $xor + */ + function _generateXor(&$iv, $length) + { + $xor = ''; + $block_size = $this->block_size; + $num_blocks = floor(($length + ($block_size - 1)) / $block_size); + for ($i = 0; $i < $num_blocks; $i++) { + $xor.= $iv; + for ($j = 4; $j <= $block_size; $j+= 4) { + $temp = substr($iv, -$j, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); + break; + case "\x7F\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); + break 2; + default: + extract(unpack('Ncount', $temp)); + $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); + break 2; + } + } + } + + return $xor; + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * Stores the created (or existing) callback function-name + * in $this->inline_crypt + * + * Internally for phpseclib developers: + * + * _setupInlineCrypt() would be called only if: + * + * - $engine == CRYPT_MODE_INTERNAL and + * + * - $use_inline_crypt === true + * + * - each time on _setup(), after(!) _setupKey() + * + * + * This ensures that _setupInlineCrypt() has allways a + * full ready2go initializated internal cipher $engine state + * where, for example, the keys allready expanded, + * keys/block_size calculated and such. + * + * It is, each time if called, the responsibility of _setupInlineCrypt(): + * + * - to set $this->inline_crypt to a valid and fully working callback function + * as a (faster) replacement for encrypt() / decrypt() + * + * - NOT to create unlimited callback functions (for memory reasons!) + * no matter how often _setupInlineCrypt() would be called. At some + * point of amount they must be generic re-useable. + * + * - the code of _setupInlineCrypt() it self, + * and the generated callback code, + * must be, in following order: + * - 100% safe + * - 100% compatible to encrypt()/decrypt() + * - using only php5+ features/lang-constructs/php-extensions if + * compatibility (down to php4) or fallback is provided + * - readable/maintainable/understandable/commented and... not-cryptic-styled-code :-) + * - >= 10% faster than encrypt()/decrypt() [which is, by the way, + * the reason for the existence of _setupInlineCrypt() :-)] + * - memory-nice + * - short (as good as possible) + * + * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code. + * - In case of using inline crypting, _setupInlineCrypt() must extend by the child Crypt_* class. + * - The following variable names are reserved: + * - $_* (all variable names prefixed with an underscore) + * - $self (object reference to it self. Do not use $this, but $self instead) + * - $in (the content of $in has to en/decrypt by the generated code) + * - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only + * + * + * @see Crypt_Base::_setup() + * @see Crypt_Base::_createInlineCryptFunction() + * @see Crypt_Base::encrypt() + * @see Crypt_Base::decrypt() + * @access private + */ + function _setupInlineCrypt() + { + // If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt() + + // If, for any reason, an extending Crypt_Base() Crypt_* class + // not using inline crypting then it must be ensured that: $this->use_inline_crypt = false + // ie in the class var declaration of $use_inline_crypt in general for the Crypt_* class, + // in the constructor at object instance-time + // or, if it's runtime-specific, at runtime + + $this->use_inline_crypt = false; + } + + /** + * Creates the performance-optimized function for en/decrypt() + * + * Internally for phpseclib developers: + * + * _createInlineCryptFunction(): + * + * - merge the $cipher_code [setup'ed by _setupInlineCrypt()] + * with the current [$this->]mode of operation code + * + * - create the $inline function, which called by encrypt() / decrypt() + * as its replacement to speed up the en/decryption operations. + * + * - return the name of the created $inline callback function + * + * - used to speed up en/decryption + * + * + * + * The main reason why can speed up things [up to 50%] this way are: + * + * - using variables more effective then regular. + * (ie no use of expensive arrays but integers $k_0, $k_1 ... + * or even, for example, the pure $key[] values hardcoded) + * + * - avoiding 1000's of function calls of ie _encryptBlock() + * but inlining the crypt operations. + * in the mode of operation for() loop. + * + * - full loop unroll the (sometimes key-dependent) rounds + * avoiding this way ++$i counters and runtime-if's etc... + * + * The basic code architectur of the generated $inline en/decrypt() + * lambda function, in pseudo php, is: + * + * + * +----------------------------------------------------------------------------------------------+ + * | callback $inline = create_function: | + * | lambda_function_0001_crypt_ECB($action, $text) | + * | { | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_crypt']; // general init code. | + * | // ie: $sbox'es declarations used for | + * | // encrypt and decrypt'ing. | + * | | + * | switch ($action) { | + * | case 'encrypt': | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_encrypt']; // encrypt sepcific init code. | + * | ie: specified $key or $box | + * | declarations for encrypt'ing. | + * | | + * | foreach ($ciphertext) { | + * | $in = $block_size of $ciphertext; | + * | | + * | INSERT PHP CODE OF: | + * | $cipher_code['encrypt_block']; // encrypt's (string) $in, which is always: | + * | // strlen($in) == $this->block_size | + * | // here comes the cipher algorithm in action | + * | // for encryption. | + * | // $cipher_code['encrypt_block'] has to | + * | // encrypt the content of the $in variable | + * | | + * | $plaintext .= $in; | + * | } | + * | return $plaintext; | + * | | + * | case 'decrypt': | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_decrypt']; // decrypt sepcific init code | + * | ie: specified $key or $box | + * | declarations for decrypt'ing. | + * | foreach ($plaintext) { | + * | $in = $block_size of $plaintext; | + * | | + * | INSERT PHP CODE OF: | + * | $cipher_code['decrypt_block']; // decrypt's (string) $in, which is always | + * | // strlen($in) == $this->block_size | + * | // here comes the cipher algorithm in action | + * | // for decryption. | + * | // $cipher_code['decrypt_block'] has to | + * | // decrypt the content of the $in variable | + * | $ciphertext .= $in; | + * | } | + * | return $ciphertext; | + * | } | + * | } | + * +----------------------------------------------------------------------------------------------+ + * + * + * See also the Crypt_*::_setupInlineCrypt()'s for + * productive inline $cipher_code's how they works. + * + * Structure of: + * + * $cipher_code = array( + * 'init_crypt' => (string) '', // optional + * 'init_encrypt' => (string) '', // optional + * 'init_decrypt' => (string) '', // optional + * 'encrypt_block' => (string) '', // required + * 'decrypt_block' => (string) '' // required + * ); + * + * + * @see Crypt_Base::_setupInlineCrypt() + * @see Crypt_Base::encrypt() + * @see Crypt_Base::decrypt() + * @param Array $cipher_code + * @access private + * @return String (the name of the created callback function) + */ + function _createInlineCryptFunction($cipher_code) + { + $block_size = $this->block_size; + + // optional + $init_crypt = isset($cipher_code['init_crypt']) ? $cipher_code['init_crypt'] : ''; + $init_encrypt = isset($cipher_code['init_encrypt']) ? $cipher_code['init_encrypt'] : ''; + $init_decrypt = isset($cipher_code['init_decrypt']) ? $cipher_code['init_decrypt'] : ''; + // required + $encrypt_block = $cipher_code['encrypt_block']; + $decrypt_block = $cipher_code['decrypt_block']; + + // Generating mode of operation inline code, + // merged with the $cipher_code algorithm + // for encrypt- and decryption. + switch ($this->mode) { + case CRYPT_MODE_ECB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_text = $self->_pad($_text); + $_plaintext_len = strlen($_text); + + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $in = substr($_text, $_i, '.$block_size.'); + '.$encrypt_block.' + $_ciphertext.= $in; + } + + return $_ciphertext; + '; + + $decrypt = $init_decrypt . ' + $_plaintext = ""; + $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0)); + $_ciphertext_len = strlen($_text); + + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $in = substr($_text, $_i, '.$block_size.'); + '.$decrypt_block.' + $_plaintext.= $in; + } + + return $self->_unpad($_plaintext); + '; + break; + case CRYPT_MODE_CTR: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + $_xor = $self->encryptIV; + $_buffer = &$self->enbuffer; + + if (strlen($_buffer["encrypted"])) { + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["encrypted"])) { + $in = $self->_generateXor($_xor, '.$block_size.'); + '.$encrypt_block.' + $_buffer["encrypted"].= $in; + } + $_key = $self->_stringShift($_buffer["encrypted"], '.$block_size.'); + $_ciphertext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + $in = $self->_generateXor($_xor, '.$block_size.'); + '.$encrypt_block.' + $_key = $in; + $_ciphertext.= $_block ^ $_key; + } + } + if ($self->continuousBuffer) { + $self->encryptIV = $_xor; + if ($_start = $_plaintext_len % '.$block_size.') { + $_buffer["encrypted"] = substr($_key, $_start) . $_buffer["encrypted"]; + } + } + + return $_ciphertext; + '; + + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_ciphertext_len = strlen($_text); + $_xor = $self->decryptIV; + $_buffer = &$self->debuffer; + + if (strlen($_buffer["ciphertext"])) { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["ciphertext"])) { + $in = $self->_generateXor($_xor, '.$block_size.'); + '.$encrypt_block.' + $_buffer["ciphertext"].= $in; + } + $_key = $self->_stringShift($_buffer["ciphertext"], '.$block_size.'); + $_plaintext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + $in = $self->_generateXor($_xor, '.$block_size.'); + '.$encrypt_block.' + $_key = $in; + $_plaintext.= $_block ^ $_key; + } + } + if ($self->continuousBuffer) { + $self->decryptIV = $_xor; + if ($_start = $_ciphertext_len % '.$block_size.') { + $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; + } + } + + return $_plaintext; + '; + break; + case CRYPT_MODE_CFB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_buffer = &$self->enbuffer; + + if ($self->continuousBuffer) { + $_iv = &$self->encryptIV; + $_pos = &$_buffer["pos"]; + } else { + $_iv = $self->encryptIV; + $_pos = 0; + } + $_len = strlen($_text); + $_i = 0; + if ($_pos) { + $_orig_pos = $_pos; + $_max = '.$block_size.' - $_pos; + if ($_len >= $_max) { + $_i = $_max; + $_len-= $_max; + $_pos = 0; + } else { + $_i = $_len; + $_pos+= $_len; + $_len = 0; + } + $_ciphertext = substr($_iv, $_orig_pos) ^ $_text; + $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i); + } + while ($_len >= '.$block_size.') { + $in = $_iv; + '.$encrypt_block.'; + $_iv = $in ^ substr($_text, $_i, '.$block_size.'); + $_ciphertext.= $_iv; + $_len-= '.$block_size.'; + $_i+= '.$block_size.'; + } + if ($_len) { + $in = $_iv; + '.$encrypt_block.' + $_iv = $in; + $_block = $_iv ^ substr($_text, $_i); + $_iv = substr_replace($_iv, $_block, 0, $_len); + $_ciphertext.= $_block; + $_pos = $_len; + } + return $_ciphertext; + '; + + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_buffer = &$self->debuffer; + + if ($self->continuousBuffer) { + $_iv = &$self->decryptIV; + $_pos = &$_buffer["pos"]; + } else { + $_iv = $self->decryptIV; + $_pos = 0; + } + $_len = strlen($_text); + $_i = 0; + if ($_pos) { + $_orig_pos = $_pos; + $_max = '.$block_size.' - $_pos; + if ($_len >= $_max) { + $_i = $_max; + $_len-= $_max; + $_pos = 0; + } else { + $_i = $_len; + $_pos+= $_len; + $_len = 0; + } + $_plaintext = substr($_iv, $_orig_pos) ^ $_text; + $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i); + } + while ($_len >= '.$block_size.') { + $in = $_iv; + '.$encrypt_block.' + $_iv = $in; + $cb = substr($_text, $_i, '.$block_size.'); + $_plaintext.= $_iv ^ $cb; + $_iv = $cb; + $_len-= '.$block_size.'; + $_i+= '.$block_size.'; + } + if ($_len) { + $in = $_iv; + '.$encrypt_block.' + $_iv = $in; + $_plaintext.= $_iv ^ substr($_text, $_i); + $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len); + $_pos = $_len; + } + + return $_plaintext; + '; + break; + case CRYPT_MODE_OFB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + $_xor = $self->encryptIV; + $_buffer = &$self->enbuffer; + + if (strlen($_buffer["xor"])) { + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["xor"])) { + $in = $_xor; + '.$encrypt_block.' + $_xor = $in; + $_buffer["xor"].= $_xor; + } + $_key = $self->_stringShift($_buffer["xor"], '.$block_size.'); + $_ciphertext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $in = $_xor; + '.$encrypt_block.' + $_xor = $in; + $_ciphertext.= substr($_text, $_i, '.$block_size.') ^ $_xor; + } + $_key = $_xor; + } + if ($self->continuousBuffer) { + $self->encryptIV = $_xor; + if ($_start = $_plaintext_len % '.$block_size.') { + $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; + } + } + return $_ciphertext; + '; + + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_ciphertext_len = strlen($_text); + $_xor = $self->decryptIV; + $_buffer = &$self->debuffer; + + if (strlen($_buffer["xor"])) { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["xor"])) { + $in = $_xor; + '.$encrypt_block.' + $_xor = $in; + $_buffer["xor"].= $_xor; + } + $_key = $self->_stringShift($_buffer["xor"], '.$block_size.'); + $_plaintext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $in = $_xor; + '.$encrypt_block.' + $_xor = $in; + $_plaintext.= substr($_text, $_i, '.$block_size.') ^ $_xor; + } + $_key = $_xor; + } + if ($self->continuousBuffer) { + $self->decryptIV = $_xor; + if ($_start = $_ciphertext_len % '.$block_size.') { + $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; + } + } + return $_plaintext; + '; + break; + case CRYPT_MODE_STREAM: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + '.$encrypt_block.' + return $_ciphertext; + '; + $decrypt = $init_decrypt . ' + $_plaintext = ""; + '.$decrypt_block.' + return $_plaintext; + '; + break; + // case CRYPT_MODE_CBC: + default: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_text = $self->_pad($_text); + $_plaintext_len = strlen($_text); + + $in = $self->encryptIV; + + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $in = substr($_text, $_i, '.$block_size.') ^ $in; + '.$encrypt_block.' + $_ciphertext.= $in; + } + + if ($self->continuousBuffer) { + $self->encryptIV = $in; + } + + return $_ciphertext; + '; + + $decrypt = $init_decrypt . ' + $_plaintext = ""; + $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0)); + $_ciphertext_len = strlen($_text); + + $_iv = $self->decryptIV; + + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $in = $_block = substr($_text, $_i, '.$block_size.'); + '.$decrypt_block.' + $_plaintext.= $in ^ $_iv; + $_iv = $_block; + } + + if ($self->continuousBuffer) { + $self->decryptIV = $_iv; + } + + return $self->_unpad($_plaintext); + '; + break; + } + + // Create the $inline function and return its name as string. Ready to run! + return create_function('$_action, &$self, $_text', $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }'); + } + + /** + * Holds the lambda_functions table (classwide) + * + * Each name of the lambda function, created from + * _setupInlineCrypt() && _createInlineCryptFunction() + * is stored, classwide (!), here for reusing. + * + * The string-based index of $function is a classwide + * uniqe value representing, at least, the $mode of + * operation (or more... depends of the optimizing level) + * for which $mode the lambda function was created. + * + * @access private + * @return &Array + */ + function &_getLambdaFunctions() + { + static $functions = array(); + return $functions; + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Blowfish.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Blowfish.php new file mode 100644 index 0000000000..c8ef67e8f7 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Blowfish.php @@ -0,0 +1,678 @@ + + * setKey('12345678901234567890123456789012'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $blowfish->decrypt($blowfish->encrypt($plaintext)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_Blowfish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 1.0 + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Crypt_Base + * + * Base cipher class + */ +if (!class_exists('Crypt_Base')) { + require_once('Base.php'); +} + +/**#@+ + * @access public + * @see Crypt_Blowfish::encrypt() + * @see Crypt_Blowfish::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_BLOWFISH_MODE_CTR', CRYPT_MODE_CTR); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +define('CRYPT_BLOWFISH_MODE_ECB', CRYPT_MODE_ECB); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +define('CRYPT_BLOWFISH_MODE_CBC', CRYPT_MODE_CBC); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +define('CRYPT_BLOWFISH_MODE_CFB', CRYPT_MODE_CFB); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +define('CRYPT_BLOWFISH_MODE_OFB', CRYPT_MODE_OFB); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_Blowfish::Crypt_Blowfish() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_BLOWFISH_MODE_INTERNAL', CRYPT_MODE_INTERNAL); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_BLOWFISH_MODE_MCRYPT', CRYPT_MODE_MCRYPT); +/**#@-*/ + +/** + * Pure-PHP implementation of Blowfish. + * + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @version 1.0 + * @access public + * @package Crypt_Blowfish + */ +class Crypt_Blowfish extends Crypt_Base { + /** + * Block Length of the cipher + * + * @see Crypt_Base::block_size + * @var Integer + * @access private + */ + var $block_size = 8; + + /** + * The default password key_size used by setPassword() + * + * @see Crypt_Base::password_key_size + * @see Crypt_Base::setPassword() + * @var Integer + * @access private + */ + var $password_key_size = 56; + + /** + * The namespace used by the cipher for its constants. + * + * @see Crypt_Base::const_namespace + * @var String + * @access private + */ + var $const_namespace = 'BLOWFISH'; + + /** + * The mcrypt specific name of the cipher + * + * @see Crypt_Base::cipher_name_mcrypt + * @var String + * @access private + */ + var $cipher_name_mcrypt = 'blowfish'; + + /** + * Optimizing value while CFB-encrypting + * + * @see Crypt_Base::cfb_init_len + * @var Integer + * @access private + */ + var $cfb_init_len = 500; + + /** + * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each + * + * S-Box 1 + * + * @access private + * @var array + */ + var $sbox0 = array ( + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + ); + + /** + * S-Box 1 + * + * @access private + * @var array + */ + var $sbox1 = array( + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + ); + + /** + * S-Box 2 + * + * @access private + * @var array + */ + var $sbox2 = array( + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + ); + + /** + * S-Box 3 + * + * @access private + * @var array + */ + var $sbox3 = array( + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + ); + + /** + * P-Array consists of 18 32-bit subkeys + * + * @var array $parray + * @access private + */ + var $parray = array( + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b + ); + + /** + * The BCTX-working Array + * + * Holds the expanded key [p] and the key-depended s-boxes [sb] + * + * @var array $bctx + * @access private + */ + var $bctx; + + /** + * Holds the last used key + * + * @var Array + * @access private + */ + var $kl; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_BLOWFISH_MODE_ECB + * + * - CRYPT_BLOWFISH_MODE_CBC + * + * - CRYPT_BLOWFISH_MODE_CTR + * + * - CRYPT_BLOWFISH_MODE_CFB + * + * - CRYPT_BLOWFISH_MODE_OFB + * + * If not explictly set, CRYPT_BLOWFISH_MODE_CBC will be used. + * + * @see Crypt_Base::Crypt_Base() + * @param optional Integer $mode + * @access public + */ + function Crypt_Blowfish($mode = CRYPT_BLOWFISH_MODE_CBC) + { + parent::Crypt_Base($mode); + } + + /** + * Sets the key. + * + * Keys can be of any length. Blowfish, itself, requires the use of a key between 32 and max. 448-bits long. + * If the key is less than 32-bits we NOT fill the key to 32bit but let the key as it is to be compatible + * with mcrypt because mcrypt act this way with blowfish key's < 32 bits. + * + * If the key is more than 448-bits, we trim the excess bits. + * + * If the key is not explicitly set, or empty, it'll be assumed a 128 bits key to be all null bytes. + * + * @access public + * @see Crypt_Base::setKey() + * @param String $key + */ + function setKey($key) + { + $keylength = strlen($key); + + if (!$keylength) { + $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + } + elseif ($keylength > 56) { + $key = substr($key, 0, 56); + } + + parent::setKey($key); + } + + /** + * Setup the key (expansion) + * + * @see Crypt_Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key); + + /* key-expanding p[] and S-Box building sb[] */ + $this->bctx = array( + 'p' => array(), + 'sb' => array( + $this->sbox0, + $this->sbox1, + $this->sbox2, + $this->sbox3 + ) + ); + + // unpack binary string in unsigned chars + $key = array_values(unpack('C*', $this->key)); + $keyl = count($key); + for ($j = 0, $i = 0; $i < 18; ++$i) { + // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ... + for ($data = 0, $k = 0; $k < 4; ++$k) { + $data = ($data << 8) | $key[$j]; + if (++$j >= $keyl) { + $j = 0; + } + } + $this->bctx['p'][] = $this->parray[$i] ^ $data; + } + + // encrypt the zero-string, replace P1 and P2 with the encrypted data, + // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys + $data = "\0\0\0\0\0\0\0\0"; + for ($i = 0; $i < 18; $i += 2) { + list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); + $this->bctx['p'][$i ] = $l; + $this->bctx['p'][$i + 1] = $r; + } + for ($i = 0; $i < 4; ++$i) { + for ($j = 0; $j < 256; $j += 2) { + list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); + $this->bctx['sb'][$i][$j ] = $l; + $this->bctx['sb'][$i][$j + 1] = $r; + } + } + } + + /** + * Encrypts a block + * + * @access private + * @param String $in + * @return String + */ + function _encryptBlock($in) + { + $p = $this->bctx["p"]; + // extract($this->bctx["sb"], EXTR_PREFIX_ALL, "sb"); // slower + $sb_0 = $this->bctx["sb"][0]; + $sb_1 = $this->bctx["sb"][1]; + $sb_2 = $this->bctx["sb"][2]; + $sb_3 = $this->bctx["sb"][3]; + + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + + for ($i = 0; $i < 16; $i+= 2) { + $l^= $p[$i]; + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]; + + $r^= $p[$i + 1]; + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]; + } + return pack("N*", $r ^ $p[17], $l ^ $p[16]); + } + + /** + * Decrypts a block + * + * @access private + * @param String $in + * @return String + */ + function _decryptBlock($in) + { + $p = $this->bctx["p"]; + $sb_0 = $this->bctx["sb"][0]; + $sb_1 = $this->bctx["sb"][1]; + $sb_2 = $this->bctx["sb"][2]; + $sb_3 = $this->bctx["sb"][3]; + + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + + for ($i = 17; $i > 2; $i-= 2) { + $l^= $p[$i]; + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]; + + $r^= $p[$i - 1]; + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]; + } + + return pack("N*", $r ^ $p[0], $l ^ $p[1]); + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see Crypt_Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& Crypt_Blowfish::_getLambdaFunctions(); + + // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. + // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one. + $gen_hi_opt_code = (bool)( count($lambda_functions) < 10); + + switch (true) { + case $gen_hi_opt_code: + $code_hash = md5(str_pad("Crypt_Blowfish, {$this->mode}, ", 32, "\0") . $this->key); + break; + default: + $code_hash = "Crypt_Blowfish, {$this->mode}"; + } + + if (!isset($lambda_functions[$code_hash])) { + switch (true) { + case $gen_hi_opt_code: + $p = $this->bctx['p']; + $init_crypt = ' + static $sb_0, $sb_1, $sb_2, $sb_3; + if (!$sb_0) { + $sb_0 = $self->bctx["sb"][0]; + $sb_1 = $self->bctx["sb"][1]; + $sb_2 = $self->bctx["sb"][2]; + $sb_3 = $self->bctx["sb"][3]; + } + '; + break; + default: + $p = array(); + for ($i = 0; $i < 18; ++$i) { + $p[] = '$p_' . $i; + } + $init_crypt = ' + list($sb_0, $sb_1, $sb_2, $sb_3) = $self->bctx["sb"]; + list(' . implode(',', $p) . ') = $self->bctx["p"]; + + '; + } + + // Generating encrypt code: + $encrypt_block = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + '; + for ($i = 0; $i < 16; $i+= 2) { + $encrypt_block.= ' + $l^= ' . $p[$i] . '; + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]; + + $r^= ' . $p[$i + 1] . '; + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]; + '; + } + $encrypt_block.= ' + $in = pack("N*", + $r ^ ' . $p[17] . ', + $l ^ ' . $p[16] . ' + ); + '; + + // Generating decrypt code: + $decrypt_block = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + '; + + for ($i = 17; $i > 2; $i-= 2) { + $decrypt_block.= ' + $l^= ' . $p[$i] . '; + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]; + + $r^= ' . $p[$i - 1] . '; + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]; + '; + } + + $decrypt_block.= ' + $in = pack("N*", + $r ^ ' . $p[0] . ', + $l ^ ' . $p[1] . ' + ); + '; + + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'init_encrypt' => '', + 'init_decrypt' => '', + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php index 1197a50ab7..8b04210659 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/DES.php @@ -39,10 +39,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -56,21 +56,29 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ +/** + * Include Crypt_Base + * + * Base cipher class + */ +if (!class_exists('Crypt_Base')) { + require_once('Base.php'); +} + /**#@+ * @access private - * @see Crypt_DES::_prepareKey() + * @see Crypt_DES::_setupKey() * @see Crypt_DES::_processBlock() */ /** - * Contains array_reverse($keys[CRYPT_DES_DECRYPT]) + * Contains $keys[CRYPT_DES_ENCRYPT] */ define('CRYPT_DES_ENCRYPT', 0); /** - * Contains array_reverse($keys[CRYPT_DES_ENCRYPT]) + * Contains $keys[CRYPT_DES_DECRYPT] */ define('CRYPT_DES_DECRYPT', 1); /**#@-*/ @@ -87,31 +95,31 @@ define('CRYPT_DES_DECRYPT', 1); * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 */ -define('CRYPT_DES_MODE_CTR', -1); +define('CRYPT_DES_MODE_CTR', CRYPT_MODE_CTR); /** * Encrypt / decrypt using the Electronic Code Book mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 */ -define('CRYPT_DES_MODE_ECB', 1); +define('CRYPT_DES_MODE_ECB', CRYPT_MODE_ECB); /** * Encrypt / decrypt using the Code Book Chaining mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 */ -define('CRYPT_DES_MODE_CBC', 2); +define('CRYPT_DES_MODE_CBC', CRYPT_MODE_CBC); /** * Encrypt / decrypt using the Cipher Feedback mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 */ -define('CRYPT_DES_MODE_CFB', 3); +define('CRYPT_DES_MODE_CFB', CRYPT_MODE_CFB); /** * Encrypt / decrypt using the Cipher Feedback mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 */ -define('CRYPT_DES_MODE_OFB', 4); +define('CRYPT_DES_MODE_OFB', CRYPT_MODE_OFB); /**#@-*/ /**#@+ @@ -121,11 +129,11 @@ define('CRYPT_DES_MODE_OFB', 4); /** * Toggles the internal implementation */ -define('CRYPT_DES_MODE_INTERNAL', 1); +define('CRYPT_DES_MODE_INTERNAL', CRYPT_MODE_INTERNAL); /** * Toggles the mcrypt implementation */ -define('CRYPT_DES_MODE_MCRYPT', 2); +define('CRYPT_DES_MODE_MCRYPT', CRYPT_MODE_MCRYPT); /**#@-*/ /** @@ -136,217 +144,550 @@ define('CRYPT_DES_MODE_MCRYPT', 2); * @access public * @package Crypt_DES */ -class Crypt_DES { +class Crypt_DES extends Crypt_Base { /** - * The Key Schedule + * Block Length of the cipher * - * @see Crypt_DES::setKey() - * @var Array - * @access private - */ - var $keys = "\0\0\0\0\0\0\0\0"; - - /** - * The Encryption Mode - * - * @see Crypt_DES::Crypt_DES() + * @see Crypt_Base::block_size * @var Integer * @access private */ - var $mode; + var $block_size = 8; /** - * Continuous Buffer status + * The Key * - * @see Crypt_DES::enableContinuousBuffer() - * @var Boolean - * @access private - */ - var $continuousBuffer = false; - - /** - * Padding status - * - * @see Crypt_DES::enablePadding() - * @var Boolean - * @access private - */ - var $padding = true; - - /** - * The Initialization Vector - * - * @see Crypt_DES::setIV() + * @see Crypt_Base::key + * @see setKey() * @var String * @access private */ - var $iv = "\0\0\0\0\0\0\0\0"; + var $key = "\0\0\0\0\0\0\0\0"; /** - * A "sliding" Initialization Vector + * The default password key_size used by setPassword() * - * @see Crypt_DES::enableContinuousBuffer() + * @see Crypt_Base::password_key_size + * @see Crypt_Base::setPassword() + * @var Integer + * @access private + */ + var $password_key_size = 8; + + /** + * The namespace used by the cipher for its constants. + * + * @see Crypt_Base::const_namespace * @var String * @access private */ - var $encryptIV = "\0\0\0\0\0\0\0\0"; + var $const_namespace = 'DES'; /** - * A "sliding" Initialization Vector + * The mcrypt specific name of the cipher * - * @see Crypt_DES::enableContinuousBuffer() + * @see Crypt_Base::cipher_name_mcrypt * @var String * @access private */ - var $decryptIV = "\0\0\0\0\0\0\0\0"; + var $cipher_name_mcrypt = 'des'; /** - * mcrypt resource for encryption + * Optimizing value while CFB-encrypting * - * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. - * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. - * - * @see Crypt_DES::encrypt() - * @var String + * @see Crypt_Base::cfb_init_len + * @var Integer * @access private */ - var $enmcrypt; + var $cfb_init_len = 500; /** - * mcrypt resource for decryption + * Switch for DES/3DES encryption * - * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. - * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * Used only if $engine == CRYPT_DES_MODE_INTERNAL * - * @see Crypt_DES::decrypt() - * @var String + * @see Crypt_DES::_setupKey() + * @see Crypt_DES::_processBlock() + * @var Integer * @access private */ - var $demcrypt; + var $des_rounds = 1; /** - * Does the enmcrypt resource need to be (re)initialized? + * max possible size of $key * * @see Crypt_DES::setKey() - * @see Crypt_DES::setIV() - * @var Boolean + * @var String * @access private */ - var $enchanged = true; + var $key_size_max = 8; /** - * Does the demcrypt resource need to be (re)initialized? + * The Key Schedule * - * @see Crypt_DES::setKey() - * @see Crypt_DES::setIV() - * @var Boolean - * @access private - */ - var $dechanged = true; - - /** - * Is the mode one that is paddable? - * - * @see Crypt_DES::Crypt_DES() - * @var Boolean - * @access private - */ - var $paddable = false; - - /** - * Encryption buffer for CTR, OFB and CFB modes - * - * @see Crypt_DES::encrypt() + * @see Crypt_DES::_setupKey() * @var Array * @access private */ - var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + var $keys; /** - * Decryption buffer for CTR, OFB and CFB modes + * Shuffle table. * - * @see Crypt_DES::decrypt() + * For each byte value index, the entry holds an 8-byte string + * with each byte containing all bits in the same state as the + * corresponding bit in the index value. + * + * @see Crypt_DES::_processBlock() + * @see Crypt_DES::_setupKey() * @var Array * @access private */ - var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); + var $shuffle = array( + "\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xFF", + "\x00\x00\x00\x00\x00\x00\xFF\x00", "\x00\x00\x00\x00\x00\x00\xFF\xFF", + "\x00\x00\x00\x00\x00\xFF\x00\x00", "\x00\x00\x00\x00\x00\xFF\x00\xFF", + "\x00\x00\x00\x00\x00\xFF\xFF\x00", "\x00\x00\x00\x00\x00\xFF\xFF\xFF", + "\x00\x00\x00\x00\xFF\x00\x00\x00", "\x00\x00\x00\x00\xFF\x00\x00\xFF", + "\x00\x00\x00\x00\xFF\x00\xFF\x00", "\x00\x00\x00\x00\xFF\x00\xFF\xFF", + "\x00\x00\x00\x00\xFF\xFF\x00\x00", "\x00\x00\x00\x00\xFF\xFF\x00\xFF", + "\x00\x00\x00\x00\xFF\xFF\xFF\x00", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF", + "\x00\x00\x00\xFF\x00\x00\x00\x00", "\x00\x00\x00\xFF\x00\x00\x00\xFF", + "\x00\x00\x00\xFF\x00\x00\xFF\x00", "\x00\x00\x00\xFF\x00\x00\xFF\xFF", + "\x00\x00\x00\xFF\x00\xFF\x00\x00", "\x00\x00\x00\xFF\x00\xFF\x00\xFF", + "\x00\x00\x00\xFF\x00\xFF\xFF\x00", "\x00\x00\x00\xFF\x00\xFF\xFF\xFF", + "\x00\x00\x00\xFF\xFF\x00\x00\x00", "\x00\x00\x00\xFF\xFF\x00\x00\xFF", + "\x00\x00\x00\xFF\xFF\x00\xFF\x00", "\x00\x00\x00\xFF\xFF\x00\xFF\xFF", + "\x00\x00\x00\xFF\xFF\xFF\x00\x00", "\x00\x00\x00\xFF\xFF\xFF\x00\xFF", + "\x00\x00\x00\xFF\xFF\xFF\xFF\x00", "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF", + "\x00\x00\xFF\x00\x00\x00\x00\x00", "\x00\x00\xFF\x00\x00\x00\x00\xFF", + "\x00\x00\xFF\x00\x00\x00\xFF\x00", "\x00\x00\xFF\x00\x00\x00\xFF\xFF", + "\x00\x00\xFF\x00\x00\xFF\x00\x00", "\x00\x00\xFF\x00\x00\xFF\x00\xFF", + "\x00\x00\xFF\x00\x00\xFF\xFF\x00", "\x00\x00\xFF\x00\x00\xFF\xFF\xFF", + "\x00\x00\xFF\x00\xFF\x00\x00\x00", "\x00\x00\xFF\x00\xFF\x00\x00\xFF", + "\x00\x00\xFF\x00\xFF\x00\xFF\x00", "\x00\x00\xFF\x00\xFF\x00\xFF\xFF", + "\x00\x00\xFF\x00\xFF\xFF\x00\x00", "\x00\x00\xFF\x00\xFF\xFF\x00\xFF", + "\x00\x00\xFF\x00\xFF\xFF\xFF\x00", "\x00\x00\xFF\x00\xFF\xFF\xFF\xFF", + "\x00\x00\xFF\xFF\x00\x00\x00\x00", "\x00\x00\xFF\xFF\x00\x00\x00\xFF", + "\x00\x00\xFF\xFF\x00\x00\xFF\x00", "\x00\x00\xFF\xFF\x00\x00\xFF\xFF", + "\x00\x00\xFF\xFF\x00\xFF\x00\x00", "\x00\x00\xFF\xFF\x00\xFF\x00\xFF", + "\x00\x00\xFF\xFF\x00\xFF\xFF\x00", "\x00\x00\xFF\xFF\x00\xFF\xFF\xFF", + "\x00\x00\xFF\xFF\xFF\x00\x00\x00", "\x00\x00\xFF\xFF\xFF\x00\x00\xFF", + "\x00\x00\xFF\xFF\xFF\x00\xFF\x00", "\x00\x00\xFF\xFF\xFF\x00\xFF\xFF", + "\x00\x00\xFF\xFF\xFF\xFF\x00\x00", "\x00\x00\xFF\xFF\xFF\xFF\x00\xFF", + "\x00\x00\xFF\xFF\xFF\xFF\xFF\x00", "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF", + "\x00\xFF\x00\x00\x00\x00\x00\x00", "\x00\xFF\x00\x00\x00\x00\x00\xFF", + "\x00\xFF\x00\x00\x00\x00\xFF\x00", "\x00\xFF\x00\x00\x00\x00\xFF\xFF", + "\x00\xFF\x00\x00\x00\xFF\x00\x00", "\x00\xFF\x00\x00\x00\xFF\x00\xFF", + "\x00\xFF\x00\x00\x00\xFF\xFF\x00", "\x00\xFF\x00\x00\x00\xFF\xFF\xFF", + "\x00\xFF\x00\x00\xFF\x00\x00\x00", "\x00\xFF\x00\x00\xFF\x00\x00\xFF", + "\x00\xFF\x00\x00\xFF\x00\xFF\x00", "\x00\xFF\x00\x00\xFF\x00\xFF\xFF", + "\x00\xFF\x00\x00\xFF\xFF\x00\x00", "\x00\xFF\x00\x00\xFF\xFF\x00\xFF", + "\x00\xFF\x00\x00\xFF\xFF\xFF\x00", "\x00\xFF\x00\x00\xFF\xFF\xFF\xFF", + "\x00\xFF\x00\xFF\x00\x00\x00\x00", "\x00\xFF\x00\xFF\x00\x00\x00\xFF", + "\x00\xFF\x00\xFF\x00\x00\xFF\x00", "\x00\xFF\x00\xFF\x00\x00\xFF\xFF", + "\x00\xFF\x00\xFF\x00\xFF\x00\x00", "\x00\xFF\x00\xFF\x00\xFF\x00\xFF", + "\x00\xFF\x00\xFF\x00\xFF\xFF\x00", "\x00\xFF\x00\xFF\x00\xFF\xFF\xFF", + "\x00\xFF\x00\xFF\xFF\x00\x00\x00", "\x00\xFF\x00\xFF\xFF\x00\x00\xFF", + "\x00\xFF\x00\xFF\xFF\x00\xFF\x00", "\x00\xFF\x00\xFF\xFF\x00\xFF\xFF", + "\x00\xFF\x00\xFF\xFF\xFF\x00\x00", "\x00\xFF\x00\xFF\xFF\xFF\x00\xFF", + "\x00\xFF\x00\xFF\xFF\xFF\xFF\x00", "\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF", + "\x00\xFF\xFF\x00\x00\x00\x00\x00", "\x00\xFF\xFF\x00\x00\x00\x00\xFF", + "\x00\xFF\xFF\x00\x00\x00\xFF\x00", "\x00\xFF\xFF\x00\x00\x00\xFF\xFF", + "\x00\xFF\xFF\x00\x00\xFF\x00\x00", "\x00\xFF\xFF\x00\x00\xFF\x00\xFF", + "\x00\xFF\xFF\x00\x00\xFF\xFF\x00", "\x00\xFF\xFF\x00\x00\xFF\xFF\xFF", + "\x00\xFF\xFF\x00\xFF\x00\x00\x00", "\x00\xFF\xFF\x00\xFF\x00\x00\xFF", + "\x00\xFF\xFF\x00\xFF\x00\xFF\x00", "\x00\xFF\xFF\x00\xFF\x00\xFF\xFF", + "\x00\xFF\xFF\x00\xFF\xFF\x00\x00", "\x00\xFF\xFF\x00\xFF\xFF\x00\xFF", + "\x00\xFF\xFF\x00\xFF\xFF\xFF\x00", "\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF", + "\x00\xFF\xFF\xFF\x00\x00\x00\x00", "\x00\xFF\xFF\xFF\x00\x00\x00\xFF", + "\x00\xFF\xFF\xFF\x00\x00\xFF\x00", "\x00\xFF\xFF\xFF\x00\x00\xFF\xFF", + "\x00\xFF\xFF\xFF\x00\xFF\x00\x00", "\x00\xFF\xFF\xFF\x00\xFF\x00\xFF", + "\x00\xFF\xFF\xFF\x00\xFF\xFF\x00", "\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF", + "\x00\xFF\xFF\xFF\xFF\x00\x00\x00", "\x00\xFF\xFF\xFF\xFF\x00\x00\xFF", + "\x00\xFF\xFF\xFF\xFF\x00\xFF\x00", "\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF", + "\x00\xFF\xFF\xFF\xFF\xFF\x00\x00", "\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF", + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF", + "\xFF\x00\x00\x00\x00\x00\x00\x00", "\xFF\x00\x00\x00\x00\x00\x00\xFF", + "\xFF\x00\x00\x00\x00\x00\xFF\x00", "\xFF\x00\x00\x00\x00\x00\xFF\xFF", + "\xFF\x00\x00\x00\x00\xFF\x00\x00", "\xFF\x00\x00\x00\x00\xFF\x00\xFF", + "\xFF\x00\x00\x00\x00\xFF\xFF\x00", "\xFF\x00\x00\x00\x00\xFF\xFF\xFF", + "\xFF\x00\x00\x00\xFF\x00\x00\x00", "\xFF\x00\x00\x00\xFF\x00\x00\xFF", + "\xFF\x00\x00\x00\xFF\x00\xFF\x00", "\xFF\x00\x00\x00\xFF\x00\xFF\xFF", + "\xFF\x00\x00\x00\xFF\xFF\x00\x00", "\xFF\x00\x00\x00\xFF\xFF\x00\xFF", + "\xFF\x00\x00\x00\xFF\xFF\xFF\x00", "\xFF\x00\x00\x00\xFF\xFF\xFF\xFF", + "\xFF\x00\x00\xFF\x00\x00\x00\x00", "\xFF\x00\x00\xFF\x00\x00\x00\xFF", + "\xFF\x00\x00\xFF\x00\x00\xFF\x00", "\xFF\x00\x00\xFF\x00\x00\xFF\xFF", + "\xFF\x00\x00\xFF\x00\xFF\x00\x00", "\xFF\x00\x00\xFF\x00\xFF\x00\xFF", + "\xFF\x00\x00\xFF\x00\xFF\xFF\x00", "\xFF\x00\x00\xFF\x00\xFF\xFF\xFF", + "\xFF\x00\x00\xFF\xFF\x00\x00\x00", "\xFF\x00\x00\xFF\xFF\x00\x00\xFF", + "\xFF\x00\x00\xFF\xFF\x00\xFF\x00", "\xFF\x00\x00\xFF\xFF\x00\xFF\xFF", + "\xFF\x00\x00\xFF\xFF\xFF\x00\x00", "\xFF\x00\x00\xFF\xFF\xFF\x00\xFF", + "\xFF\x00\x00\xFF\xFF\xFF\xFF\x00", "\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF", + "\xFF\x00\xFF\x00\x00\x00\x00\x00", "\xFF\x00\xFF\x00\x00\x00\x00\xFF", + "\xFF\x00\xFF\x00\x00\x00\xFF\x00", "\xFF\x00\xFF\x00\x00\x00\xFF\xFF", + "\xFF\x00\xFF\x00\x00\xFF\x00\x00", "\xFF\x00\xFF\x00\x00\xFF\x00\xFF", + "\xFF\x00\xFF\x00\x00\xFF\xFF\x00", "\xFF\x00\xFF\x00\x00\xFF\xFF\xFF", + "\xFF\x00\xFF\x00\xFF\x00\x00\x00", "\xFF\x00\xFF\x00\xFF\x00\x00\xFF", + "\xFF\x00\xFF\x00\xFF\x00\xFF\x00", "\xFF\x00\xFF\x00\xFF\x00\xFF\xFF", + "\xFF\x00\xFF\x00\xFF\xFF\x00\x00", "\xFF\x00\xFF\x00\xFF\xFF\x00\xFF", + "\xFF\x00\xFF\x00\xFF\xFF\xFF\x00", "\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF", + "\xFF\x00\xFF\xFF\x00\x00\x00\x00", "\xFF\x00\xFF\xFF\x00\x00\x00\xFF", + "\xFF\x00\xFF\xFF\x00\x00\xFF\x00", "\xFF\x00\xFF\xFF\x00\x00\xFF\xFF", + "\xFF\x00\xFF\xFF\x00\xFF\x00\x00", "\xFF\x00\xFF\xFF\x00\xFF\x00\xFF", + "\xFF\x00\xFF\xFF\x00\xFF\xFF\x00", "\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF", + "\xFF\x00\xFF\xFF\xFF\x00\x00\x00", "\xFF\x00\xFF\xFF\xFF\x00\x00\xFF", + "\xFF\x00\xFF\xFF\xFF\x00\xFF\x00", "\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF", + "\xFF\x00\xFF\xFF\xFF\xFF\x00\x00", "\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF", + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF", + "\xFF\xFF\x00\x00\x00\x00\x00\x00", "\xFF\xFF\x00\x00\x00\x00\x00\xFF", + "\xFF\xFF\x00\x00\x00\x00\xFF\x00", "\xFF\xFF\x00\x00\x00\x00\xFF\xFF", + "\xFF\xFF\x00\x00\x00\xFF\x00\x00", "\xFF\xFF\x00\x00\x00\xFF\x00\xFF", + "\xFF\xFF\x00\x00\x00\xFF\xFF\x00", "\xFF\xFF\x00\x00\x00\xFF\xFF\xFF", + "\xFF\xFF\x00\x00\xFF\x00\x00\x00", "\xFF\xFF\x00\x00\xFF\x00\x00\xFF", + "\xFF\xFF\x00\x00\xFF\x00\xFF\x00", "\xFF\xFF\x00\x00\xFF\x00\xFF\xFF", + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00", "\xFF\xFF\x00\x00\xFF\xFF\x00\xFF", + "\xFF\xFF\x00\x00\xFF\xFF\xFF\x00", "\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF", + "\xFF\xFF\x00\xFF\x00\x00\x00\x00", "\xFF\xFF\x00\xFF\x00\x00\x00\xFF", + "\xFF\xFF\x00\xFF\x00\x00\xFF\x00", "\xFF\xFF\x00\xFF\x00\x00\xFF\xFF", + "\xFF\xFF\x00\xFF\x00\xFF\x00\x00", "\xFF\xFF\x00\xFF\x00\xFF\x00\xFF", + "\xFF\xFF\x00\xFF\x00\xFF\xFF\x00", "\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF", + "\xFF\xFF\x00\xFF\xFF\x00\x00\x00", "\xFF\xFF\x00\xFF\xFF\x00\x00\xFF", + "\xFF\xFF\x00\xFF\xFF\x00\xFF\x00", "\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF", + "\xFF\xFF\x00\xFF\xFF\xFF\x00\x00", "\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF", + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF", + "\xFF\xFF\xFF\x00\x00\x00\x00\x00", "\xFF\xFF\xFF\x00\x00\x00\x00\xFF", + "\xFF\xFF\xFF\x00\x00\x00\xFF\x00", "\xFF\xFF\xFF\x00\x00\x00\xFF\xFF", + "\xFF\xFF\xFF\x00\x00\xFF\x00\x00", "\xFF\xFF\xFF\x00\x00\xFF\x00\xFF", + "\xFF\xFF\xFF\x00\x00\xFF\xFF\x00", "\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF", + "\xFF\xFF\xFF\x00\xFF\x00\x00\x00", "\xFF\xFF\xFF\x00\xFF\x00\x00\xFF", + "\xFF\xFF\xFF\x00\xFF\x00\xFF\x00", "\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF", + "\xFF\xFF\xFF\x00\xFF\xFF\x00\x00", "\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF", + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF", + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00", "\xFF\xFF\xFF\xFF\x00\x00\x00\xFF", + "\xFF\xFF\xFF\xFF\x00\x00\xFF\x00", "\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF", + "\xFF\xFF\xFF\xFF\x00\xFF\x00\x00", "\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF", + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF", + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", "\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF", + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF", + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF", + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + ); /** - * mcrypt resource for CFB mode + * IP mapping helper table. * - * @see Crypt_DES::encrypt() - * @see Crypt_DES::decrypt() - * @var String + * Indexing this table with each source byte performs the initial bit permutation. + * + * @var Array * @access private */ - var $ecb; + var $ipmap = array( + 0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31, + 0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33, + 0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71, + 0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73, + 0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35, + 0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37, + 0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75, + 0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77, + 0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1, + 0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3, + 0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1, + 0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3, + 0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5, + 0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7, + 0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5, + 0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7, + 0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39, + 0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B, + 0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79, + 0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B, + 0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D, + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9, + 0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB, + 0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9, + 0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB, + 0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD, + 0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF, + 0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD, + 0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF + ); + + /** + * Inverse IP mapping helper table. + * Indexing this table with a byte value reverses the bit order. + * + * @var Array + * @access private + */ + var $invipmap = array( + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF + ); + + /** + * Pre-permuted S-box1 + * + * Each box ($sbox1-$sbox8) has been vectorized, then each value pre-permuted using the + * P table: concatenation can then be replaced by exclusive ORs. + * + * @var Array + * @access private + */ + var $sbox1 = array( + 0x00808200, 0x00000000, 0x00008000, 0x00808202, + 0x00808002, 0x00008202, 0x00000002, 0x00008000, + 0x00000200, 0x00808200, 0x00808202, 0x00000200, + 0x00800202, 0x00808002, 0x00800000, 0x00000002, + 0x00000202, 0x00800200, 0x00800200, 0x00008200, + 0x00008200, 0x00808000, 0x00808000, 0x00800202, + 0x00008002, 0x00800002, 0x00800002, 0x00008002, + 0x00000000, 0x00000202, 0x00008202, 0x00800000, + 0x00008000, 0x00808202, 0x00000002, 0x00808000, + 0x00808200, 0x00800000, 0x00800000, 0x00000200, + 0x00808002, 0x00008000, 0x00008200, 0x00800002, + 0x00000200, 0x00000002, 0x00800202, 0x00008202, + 0x00808202, 0x00008002, 0x00808000, 0x00800202, + 0x00800002, 0x00000202, 0x00008202, 0x00808200, + 0x00000202, 0x00800200, 0x00800200, 0x00000000, + 0x00008002, 0x00008200, 0x00000000, 0x00808002 + ); + + /** + * Pre-permuted S-box2 + * + * @var Array + * @access private + */ + var $sbox2 = array( + 0x40084010, 0x40004000, 0x00004000, 0x00084010, + 0x00080000, 0x00000010, 0x40080010, 0x40004010, + 0x40000010, 0x40084010, 0x40084000, 0x40000000, + 0x40004000, 0x00080000, 0x00000010, 0x40080010, + 0x00084000, 0x00080010, 0x40004010, 0x00000000, + 0x40000000, 0x00004000, 0x00084010, 0x40080000, + 0x00080010, 0x40000010, 0x00000000, 0x00084000, + 0x00004010, 0x40084000, 0x40080000, 0x00004010, + 0x00000000, 0x00084010, 0x40080010, 0x00080000, + 0x40004010, 0x40080000, 0x40084000, 0x00004000, + 0x40080000, 0x40004000, 0x00000010, 0x40084010, + 0x00084010, 0x00000010, 0x00004000, 0x40000000, + 0x00004010, 0x40084000, 0x00080000, 0x40000010, + 0x00080010, 0x40004010, 0x40000010, 0x00080010, + 0x00084000, 0x00000000, 0x40004000, 0x00004010, + 0x40000000, 0x40080010, 0x40084010, 0x00084000 + ); + + /** + * Pre-permuted S-box3 + * + * @var Array + * @access private + */ + var $sbox3 = array( + 0x00000104, 0x04010100, 0x00000000, 0x04010004, + 0x04000100, 0x00000000, 0x00010104, 0x04000100, + 0x00010004, 0x04000004, 0x04000004, 0x00010000, + 0x04010104, 0x00010004, 0x04010000, 0x00000104, + 0x04000000, 0x00000004, 0x04010100, 0x00000100, + 0x00010100, 0x04010000, 0x04010004, 0x00010104, + 0x04000104, 0x00010100, 0x00010000, 0x04000104, + 0x00000004, 0x04010104, 0x00000100, 0x04000000, + 0x04010100, 0x04000000, 0x00010004, 0x00000104, + 0x00010000, 0x04010100, 0x04000100, 0x00000000, + 0x00000100, 0x00010004, 0x04010104, 0x04000100, + 0x04000004, 0x00000100, 0x00000000, 0x04010004, + 0x04000104, 0x00010000, 0x04000000, 0x04010104, + 0x00000004, 0x00010104, 0x00010100, 0x04000004, + 0x04010000, 0x04000104, 0x00000104, 0x04010000, + 0x00010104, 0x00000004, 0x04010004, 0x00010100 + ); + + /** + * Pre-permuted S-box4 + * + * @var Array + * @access private + */ + var $sbox4 = array( + 0x80401000, 0x80001040, 0x80001040, 0x00000040, + 0x00401040, 0x80400040, 0x80400000, 0x80001000, + 0x00000000, 0x00401000, 0x00401000, 0x80401040, + 0x80000040, 0x00000000, 0x00400040, 0x80400000, + 0x80000000, 0x00001000, 0x00400000, 0x80401000, + 0x00000040, 0x00400000, 0x80001000, 0x00001040, + 0x80400040, 0x80000000, 0x00001040, 0x00400040, + 0x00001000, 0x00401040, 0x80401040, 0x80000040, + 0x00400040, 0x80400000, 0x00401000, 0x80401040, + 0x80000040, 0x00000000, 0x00000000, 0x00401000, + 0x00001040, 0x00400040, 0x80400040, 0x80000000, + 0x80401000, 0x80001040, 0x80001040, 0x00000040, + 0x80401040, 0x80000040, 0x80000000, 0x00001000, + 0x80400000, 0x80001000, 0x00401040, 0x80400040, + 0x80001000, 0x00001040, 0x00400000, 0x80401000, + 0x00000040, 0x00400000, 0x00001000, 0x00401040 + ); + + /** + * Pre-permuted S-box5 + * + * @var Array + * @access private + */ + var $sbox5 = array( + 0x00000080, 0x01040080, 0x01040000, 0x21000080, + 0x00040000, 0x00000080, 0x20000000, 0x01040000, + 0x20040080, 0x00040000, 0x01000080, 0x20040080, + 0x21000080, 0x21040000, 0x00040080, 0x20000000, + 0x01000000, 0x20040000, 0x20040000, 0x00000000, + 0x20000080, 0x21040080, 0x21040080, 0x01000080, + 0x21040000, 0x20000080, 0x00000000, 0x21000000, + 0x01040080, 0x01000000, 0x21000000, 0x00040080, + 0x00040000, 0x21000080, 0x00000080, 0x01000000, + 0x20000000, 0x01040000, 0x21000080, 0x20040080, + 0x01000080, 0x20000000, 0x21040000, 0x01040080, + 0x20040080, 0x00000080, 0x01000000, 0x21040000, + 0x21040080, 0x00040080, 0x21000000, 0x21040080, + 0x01040000, 0x00000000, 0x20040000, 0x21000000, + 0x00040080, 0x01000080, 0x20000080, 0x00040000, + 0x00000000, 0x20040000, 0x01040080, 0x20000080 + ); + + /** + * Pre-permuted S-box6 + * + * @var Array + * @access private + */ + var $sbox6 = array( + 0x10000008, 0x10200000, 0x00002000, 0x10202008, + 0x10200000, 0x00000008, 0x10202008, 0x00200000, + 0x10002000, 0x00202008, 0x00200000, 0x10000008, + 0x00200008, 0x10002000, 0x10000000, 0x00002008, + 0x00000000, 0x00200008, 0x10002008, 0x00002000, + 0x00202000, 0x10002008, 0x00000008, 0x10200008, + 0x10200008, 0x00000000, 0x00202008, 0x10202000, + 0x00002008, 0x00202000, 0x10202000, 0x10000000, + 0x10002000, 0x00000008, 0x10200008, 0x00202000, + 0x10202008, 0x00200000, 0x00002008, 0x10000008, + 0x00200000, 0x10002000, 0x10000000, 0x00002008, + 0x10000008, 0x10202008, 0x00202000, 0x10200000, + 0x00202008, 0x10202000, 0x00000000, 0x10200008, + 0x00000008, 0x00002000, 0x10200000, 0x00202008, + 0x00002000, 0x00200008, 0x10002008, 0x00000000, + 0x10202000, 0x10000000, 0x00200008, 0x10002008 + ); + + /** + * Pre-permuted S-box7 + * + * @var Array + * @access private + */ + var $sbox7 = array( + 0x00100000, 0x02100001, 0x02000401, 0x00000000, + 0x00000400, 0x02000401, 0x00100401, 0x02100400, + 0x02100401, 0x00100000, 0x00000000, 0x02000001, + 0x00000001, 0x02000000, 0x02100001, 0x00000401, + 0x02000400, 0x00100401, 0x00100001, 0x02000400, + 0x02000001, 0x02100000, 0x02100400, 0x00100001, + 0x02100000, 0x00000400, 0x00000401, 0x02100401, + 0x00100400, 0x00000001, 0x02000000, 0x00100400, + 0x02000000, 0x00100400, 0x00100000, 0x02000401, + 0x02000401, 0x02100001, 0x02100001, 0x00000001, + 0x00100001, 0x02000000, 0x02000400, 0x00100000, + 0x02100400, 0x00000401, 0x00100401, 0x02100400, + 0x00000401, 0x02000001, 0x02100401, 0x02100000, + 0x00100400, 0x00000000, 0x00000001, 0x02100401, + 0x00000000, 0x00100401, 0x02100000, 0x00000400, + 0x02000001, 0x02000400, 0x00000400, 0x00100001 + ); + + /** + * Pre-permuted S-box8 + * + * @var Array + * @access private + */ + var $sbox8 = array( + 0x08000820, 0x00000800, 0x00020000, 0x08020820, + 0x08000000, 0x08000820, 0x00000020, 0x08000000, + 0x00020020, 0x08020000, 0x08020820, 0x00020800, + 0x08020800, 0x00020820, 0x00000800, 0x00000020, + 0x08020000, 0x08000020, 0x08000800, 0x00000820, + 0x00020800, 0x00020020, 0x08020020, 0x08020800, + 0x00000820, 0x00000000, 0x00000000, 0x08020020, + 0x08000020, 0x08000800, 0x00020820, 0x00020000, + 0x00020820, 0x00020000, 0x08020800, 0x00000800, + 0x00000020, 0x08020020, 0x00000800, 0x00020820, + 0x08000800, 0x00000020, 0x08000020, 0x08020000, + 0x08020020, 0x08000000, 0x00020000, 0x08000820, + 0x00000000, 0x08020820, 0x00020020, 0x08000020, + 0x08020000, 0x08000800, 0x08000820, 0x00000000, + 0x08020820, 0x00020800, 0x00020800, 0x00000820, + 0x00000820, 0x00020020, 0x08000000, 0x08020800 + ); /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be - * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used. + * Determines whether or not the mcrypt extension should be used. * + * $mode could be: + * + * - CRYPT_DES_MODE_ECB + * + * - CRYPT_DES_MODE_CBC + * + * - CRYPT_DES_MODE_CTR + * + * - CRYPT_DES_MODE_CFB + * + * - CRYPT_DES_MODE_OFB + * + * If not explictly set, CRYPT_DES_MODE_CBC will be used. + * + * @see Crypt_Base::Crypt_Base() * @param optional Integer $mode - * @return Crypt_DES * @access public */ function Crypt_DES($mode = CRYPT_DES_MODE_CBC) { - if ( !defined('CRYPT_DES_MODE') ) { - switch (true) { - case extension_loaded('mcrypt') && in_array('des', mcrypt_list_algorithms()): - define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT); - break; - default: - define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL); - } - } - - switch ( CRYPT_DES_MODE ) { - case CRYPT_DES_MODE_MCRYPT: - switch ($mode) { - case CRYPT_DES_MODE_ECB: - $this->paddable = true; - $this->mode = MCRYPT_MODE_ECB; - break; - case CRYPT_DES_MODE_CTR: - $this->mode = 'ctr'; - //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR; - break; - case CRYPT_DES_MODE_CFB: - $this->mode = 'ncfb'; - $this->ecb = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, ''); - break; - case CRYPT_DES_MODE_OFB: - $this->mode = MCRYPT_MODE_NOFB; - break; - case CRYPT_DES_MODE_CBC: - default: - $this->paddable = true; - $this->mode = MCRYPT_MODE_CBC; - } - $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); - $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); - - break; - default: - switch ($mode) { - case CRYPT_DES_MODE_ECB: - case CRYPT_DES_MODE_CBC: - $this->paddable = true; - $this->mode = $mode; - break; - case CRYPT_DES_MODE_CTR: - case CRYPT_DES_MODE_CFB: - case CRYPT_DES_MODE_OFB: - $this->mode = $mode; - break; - default: - $this->paddable = true; - $this->mode = CRYPT_DES_MODE_CBC; - } - } + parent::Crypt_Base($mode); } /** @@ -360,653 +701,50 @@ class Crypt_DES { * * If the key is not explicitly set, it'll be assumed to be all zero's. * + * @see Crypt_Base::setKey() * @access public * @param String $key */ function setKey($key) { - $this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? str_pad(substr($key, 0, 8), 8, chr(0)) : $this->_prepareKey($key); - $this->enchanged = true; - $this->dechanged = true; - } - - /** - * Sets the password. - * - * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: - * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: - * $hash, $salt, $count - * - * @param String $password - * @param optional String $method - * @access public - */ - function setPassword($password, $method = 'pbkdf2') - { - $key = ''; - - switch ($method) { - default: // 'pbkdf2' - list(, , $hash, $salt, $count) = func_get_args(); - if (!isset($hash)) { - $hash = 'sha1'; - } - // WPA and WPA2 use the SSID as the salt - if (!isset($salt)) { - $salt = 'phpseclib/salt'; - } - // RFC2898#section-4.2 uses 1,000 iterations by default - // WPA and WPA2 use 4,096. - if (!isset($count)) { - $count = 1000; - } - - if (!class_exists('Crypt_Hash')) { - require_once('Crypt/Hash.php'); - } - - $i = 1; - while (strlen($key) < 8) { // $dkLen == 8 - //$dk.= $this->_pbkdf($password, $salt, $count, $i++); - $hmac = new Crypt_Hash(); - $hmac->setHash($hash); - $hmac->setKey($password); - $f = $u = $hmac->hash($salt . pack('N', $i++)); - for ($j = 2; $j <= $count; $j++) { - $u = $hmac->hash($u); - $f^= $u; - } - $key.= $f; - } + // We check/cut here only up to max length of the key. + // Key padding to the proper length will be done in _setupKey() + if (strlen($key) > $this->key_size_max) { + $key = substr($key, 0, $this->key_size_max); } - $this->setKey($key); + // Sets the key + parent::setKey($key); } /** - * Sets the initialization vector. (optional) + * Encrypts a block * - * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed - * to be all zero's. - * - * @access public - * @param String $iv - */ - function setIV($iv) - { - $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0)); - $this->enchanged = true; - $this->dechanged = true; - } - - /** - * Generate CTR XOR encryption key - * - * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the - * plaintext / ciphertext in CTR mode. - * - * @see Crypt_DES::decrypt() + * @see Crypt_Base::_encryptBlock() + * @see Crypt_Base::encrypt() * @see Crypt_DES::encrypt() - * @access public - * @param String $iv + * @access private + * @param String $in + * @return String */ - function _generate_xor(&$iv) + function _encryptBlock($in) { - $xor = $iv; - for ($j = 4; $j <= 8; $j+=4) { - $temp = substr($iv, -$j, 4); - switch ($temp) { - case "\xFF\xFF\xFF\xFF": - $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); - break; - case "\x7F\xFF\xFF\xFF": - $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); - break 2; - default: - extract(unpack('Ncount', $temp)); - $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); - break 2; - } - } - - return $xor; + return $this->_processBlock($in, CRYPT_DES_ENCRYPT); } /** - * Encrypts a message. - * - * $plaintext will be padded with up to 8 additional bytes. Other DES implementations may or may not pad in the - * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following - * URL: - * - * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} - * - * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. - * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that - * length. + * Decrypts a block * + * @see Crypt_Base::_decryptBlock() + * @see Crypt_Base::decrypt() * @see Crypt_DES::decrypt() - * @access public - * @param String $plaintext - */ - function encrypt($plaintext) - { - if ($this->paddable) { - $plaintext = $this->_pad($plaintext); - } - - if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { - if ($this->enchanged) { - mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); - if ($this->mode == 'ncfb') { - mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0"); - } - $this->enchanged = false; - } - - if ($this->mode != 'ncfb' || !$this->continuousBuffer) { - $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); - } else { - $iv = &$this->encryptIV; - $pos = &$this->enbuffer['pos']; - $len = strlen($plaintext); - $ciphertext = ''; - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = 8 - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - $ciphertext = substr($iv, $orig_pos) ^ $plaintext; - $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); - $this->enbuffer['enmcrypt_init'] = true; - } - if ($len >= 8) { - if ($this->enbuffer['enmcrypt_init'] === false || $len > 600) { - if ($this->enbuffer['enmcrypt_init'] === true) { - mcrypt_generic_init($this->enmcrypt, $this->keys, $iv); - $this->enbuffer['enmcrypt_init'] = false; - } - $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8)); - $iv = substr($ciphertext, -8); - $len%= 8; - } else { - while ($len >= 8) { - $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8); - $ciphertext.= $iv; - $len-= 8; - $i+= 8; - } - } - } - if ($len) { - $iv = mcrypt_generic($this->ecb, $iv); - $block = $iv ^ substr($plaintext, -$len); - $iv = substr_replace($iv, $block, 0, $len); - $ciphertext.= $block; - $pos = $len; - } - return $ciphertext; - } - - if (!$this->continuousBuffer) { - mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); - } - - return $ciphertext; - } - - if (!is_array($this->keys)) { - $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0"); - } - - $buffer = &$this->enbuffer; - $continuousBuffer = $this->continuousBuffer; - $ciphertext = ''; - switch ($this->mode) { - case CRYPT_DES_MODE_ECB: - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $ciphertext.= $this->_processBlock(substr($plaintext, $i, 8), CRYPT_DES_ENCRYPT); - } - break; - case CRYPT_DES_MODE_CBC: - $xor = $this->encryptIV; - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8); - $block = $this->_processBlock($block ^ $xor, CRYPT_DES_ENCRYPT); - $xor = $block; - $ciphertext.= $block; - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - } - break; - case CRYPT_DES_MODE_CTR: - $xor = $this->encryptIV; - if (strlen($buffer['encrypted'])) { - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8); - $buffer['encrypted'].= $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT); - $key = $this->_string_shift($buffer['encrypted'], 8); - $ciphertext.= $block ^ $key; - } - } else { - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8); - $key = $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT); - $ciphertext.= $block ^ $key; - } - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - if ($start = strlen($plaintext) & 7) { - $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; - } - } - break; - case CRYPT_DES_MODE_CFB: - if ($this->continuousBuffer) { - $iv = &$this->encryptIV; - $pos = &$buffer['pos']; - } else { - $iv = $this->encryptIV; - $pos = 0; - } - $len = strlen($plaintext); - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = 8 - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - $ciphertext = substr($iv, $orig_pos) ^ $plaintext; - $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); - } - while ($len >= 8) { - $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT) ^ substr($plaintext, $i, 8); - $ciphertext.= $iv; - $len-= 8; - $i+= 8; - } - if ($len) { - $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); - $block = $iv ^ substr($plaintext, $i); - $iv = substr_replace($iv, $block, 0, $len); - $ciphertext.= $block; - $pos = $len; - } - return $ciphertext; - case CRYPT_DES_MODE_OFB: - $xor = $this->encryptIV; - if (strlen($buffer['xor'])) { - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT); - $buffer['xor'].= $xor; - $key = $this->_string_shift($buffer['xor'], 8); - $ciphertext.= substr($plaintext, $i, 8) ^ $key; - } - } else { - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT); - $ciphertext.= substr($plaintext, $i, 8) ^ $xor; - } - $key = $xor; - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - if ($start = strlen($plaintext) & 7) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; - } - } - } - - return $ciphertext; - } - - /** - * Decrypts a message. - * - * If strlen($ciphertext) is not a multiple of 8, null bytes will be added to the end of the string until it is. - * - * @see Crypt_DES::encrypt() - * @access public - * @param String $ciphertext - */ - function decrypt($ciphertext) - { - if ($this->paddable) { - // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0)); - } - - if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { - if ($this->dechanged) { - mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); - if ($this->mode == 'ncfb') { - mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0"); - } - $this->dechanged = false; - } - - if ($this->mode != 'ncfb' || !$this->continuousBuffer) { - $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); - } else { - $iv = &$this->decryptIV; - $pos = &$this->debuffer['pos']; - $len = strlen($ciphertext); - $plaintext = ''; - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = 8 - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - $plaintext = substr($iv, $orig_pos) ^ $ciphertext; - $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); - } - if ($len >= 8) { - $cb = substr($ciphertext, $i, $len - $len % 8); - $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; - $iv = substr($cb, -8); - $len%= 8; - } - if ($len) { - $iv = mcrypt_generic($this->ecb, $iv); - $plaintext.= $iv ^ substr($ciphertext, -$len); - $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); - $pos = $len; - } - return $plaintext; - } - - if (!$this->continuousBuffer) { - mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); - } - - return $this->paddable ? $this->_unpad($plaintext) : $plaintext; - } - - if (!is_array($this->keys)) { - $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0"); - } - - $buffer = &$this->debuffer; - $continuousBuffer = $this->continuousBuffer; - $plaintext = ''; - switch ($this->mode) { - case CRYPT_DES_MODE_ECB: - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $plaintext.= $this->_processBlock(substr($ciphertext, $i, 8), CRYPT_DES_DECRYPT); - } - break; - case CRYPT_DES_MODE_CBC: - $xor = $this->decryptIV; - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $block = substr($ciphertext, $i, 8); - $plaintext.= $this->_processBlock($block, CRYPT_DES_DECRYPT) ^ $xor; - $xor = $block; - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - } - break; - case CRYPT_DES_MODE_CTR: - $xor = $this->decryptIV; - if (strlen($buffer['ciphertext'])) { - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $block = substr($ciphertext, $i, 8); - $buffer['ciphertext'].= $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT); - $key = $this->_string_shift($buffer['ciphertext'], 8); - $plaintext.= $block ^ $key; - } - } else { - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $block = substr($ciphertext, $i, 8); - $key = $this->_processBlock($this->_generate_xor($xor), CRYPT_DES_ENCRYPT); - $plaintext.= $block ^ $key; - } - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - if ($start = strlen($ciphertext) % 8) { - $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; - } - } - break; - case CRYPT_DES_MODE_CFB: - if ($this->continuousBuffer) { - $iv = &$this->decryptIV; - $pos = &$buffer['pos']; - } else { - $iv = $this->decryptIV; - $pos = 0; - } - $len = strlen($ciphertext); - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = 8 - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - $plaintext = substr($iv, $orig_pos) ^ $ciphertext; - $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); - } - while ($len >= 8) { - $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); - $cb = substr($ciphertext, $i, 8); - $plaintext.= $iv ^ $cb; - $iv = $cb; - $len-= 8; - $i+= 8; - } - if ($len) { - $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); - $plaintext.= $iv ^ substr($ciphertext, $i); - $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len); - $pos = $len; - } - return $plaintext; - case CRYPT_DES_MODE_OFB: - $xor = $this->decryptIV; - if (strlen($buffer['xor'])) { - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT); - $buffer['xor'].= $xor; - $key = $this->_string_shift($buffer['xor'], 8); - $plaintext.= substr($ciphertext, $i, 8) ^ $key; - } - } else { - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT); - $plaintext.= substr($ciphertext, $i, 8) ^ $xor; - } - $key = $xor; - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - if ($start = strlen($ciphertext) % 8) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; - } - } - } - - return $this->paddable ? $this->_unpad($plaintext) : $plaintext; - } - - /** - * Treat consecutive "packets" as if they are a continuous buffer. - * - * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets - * will yield different outputs: - * - * - * echo $des->encrypt(substr($plaintext, 0, 8)); - * echo $des->encrypt(substr($plaintext, 8, 8)); - * - * - * echo $des->encrypt($plaintext); - * - * - * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates - * another, as demonstrated with the following: - * - * - * $des->encrypt(substr($plaintext, 0, 8)); - * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); - * - * - * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); - * - * - * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different - * outputs. The reason is due to the fact that the initialization vector's change after every encryption / - * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. - * - * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each - * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that - * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), - * however, they are also less intuitive and more likely to cause you problems. - * - * @see Crypt_DES::disableContinuousBuffer() - * @access public - */ - function enableContinuousBuffer() - { - $this->continuousBuffer = true; - } - - /** - * Treat consecutive packets as if they are a discontinuous buffer. - * - * The default behavior. - * - * @see Crypt_DES::enableContinuousBuffer() - * @access public - */ - function disableContinuousBuffer() - { - $this->continuousBuffer = false; - $this->encryptIV = $this->iv; - $this->decryptIV = $this->iv; - $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); - $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); - - if (CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT) { - mcrypt_generic_init($this->enmcrypt, $this->keys, $this->iv); - mcrypt_generic_init($this->demcrypt, $this->keys, $this->iv); - } - } - - /** - * Pad "packets". - * - * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not - * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight. - * - * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1, - * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping - * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is - * transmitted separately) - * - * @see Crypt_DES::disablePadding() - * @access public - */ - function enablePadding() - { - $this->padding = true; - } - - /** - * Do not pad packets. - * - * @see Crypt_DES::enablePadding() - * @access public - */ - function disablePadding() - { - $this->padding = false; - } - - /** - * Pads a string - * - * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8). - * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7) - * - * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless - * and padding will, hence forth, be enabled. - * - * @see Crypt_DES::_unpad() * @access private + * @param String $in + * @return String */ - function _pad($text) + function _decryptBlock($in) { - $length = strlen($text); - - if (!$this->padding) { - if (($length & 7) == 0) { - return $text; - } else { - user_error("The plaintext's length ($length) is not a multiple of the block size (8)"); - $this->padding = true; - } - } - - $pad = 8 - ($length & 7); - return str_pad($text, $length + $pad, chr($pad)); - } - - /** - * Unpads a string - * - * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong - * and false will be returned. - * - * @see Crypt_DES::_pad() - * @access private - */ - function _unpad($text) - { - if (!$this->padding) { - return $text; - } - - $length = ord($text[strlen($text) - 1]); - - if (!$length || $length > 8) { - return false; - } - - return substr($text, 0, -$length); + return $this->_processBlock($in, CRYPT_DES_DECRYPT); } /** @@ -1016,6 +754,8 @@ class Crypt_DES { * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general * idea of what this function does. * + * @see Crypt_DES::_encryptBlock() + * @see Crypt_DES::_decryptBlock() * @access private * @param String $block * @param Integer $mode @@ -1023,310 +763,772 @@ class Crypt_DES { */ function _processBlock($block, $mode) { - // s-boxes. in the official DES docs, they're described as being matrices that - // one accesses by using the first and last bits to determine the row and the - // middle four bits to determine the column. in this implementation, they've - // been converted to vectors - static $sbox = array( - array( - 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, - 3, 10 ,10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, - 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, - 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13 - ), - array( - 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, - 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, - 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, - 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9 - ), - array( - 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, - 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, - 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, - 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12 - ), - array( - 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, - 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, - 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, - 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14 - ), - array( - 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, - 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, - 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, - 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3 - ), - array( - 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, - 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, - 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, - 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13 - ), - array( - 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, - 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, - 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, - 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12 - ), - array( - 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, - 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, - 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, - 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 - ) - ); - - $keys = $this->keys; - - $temp = unpack('Na/Nb', $block); - $block = array($temp['a'], $temp['b']); - - // because php does arithmetic right shifts, if the most significant bits are set, right - // shifting those into the correct position will add 1's - not 0's. this will intefere - // with the | operation unless a second & is done. so we isolate these bits and left shift - // them into place. we then & each block with 0x7FFFFFFF to prevennt 1's from being added - // for any other shifts. - $msb = array( - ($block[0] >> 31) & 1, - ($block[1] >> 31) & 1 - ); - $block[0] &= 0x7FFFFFFF; - $block[1] &= 0x7FFFFFFF; - - // we isolate the appropriate bit in the appropriate integer and shift as appropriate. in - // some cases, there are going to be multiple bits in the same integer that need to be shifted - // in the same way. we combine those into one shift operation. - $block = array( - (($block[1] & 0x00000040) << 25) | (($block[1] & 0x00004000) << 16) | - (($block[1] & 0x00400001) << 7) | (($block[1] & 0x40000100) >> 2) | - (($block[0] & 0x00000040) << 21) | (($block[0] & 0x00004000) << 12) | - (($block[0] & 0x00400001) << 3) | (($block[0] & 0x40000100) >> 6) | - (($block[1] & 0x00000010) << 19) | (($block[1] & 0x00001000) << 10) | - (($block[1] & 0x00100000) << 1) | (($block[1] & 0x10000000) >> 8) | - (($block[0] & 0x00000010) << 15) | (($block[0] & 0x00001000) << 6) | - (($block[0] & 0x00100000) >> 3) | (($block[0] & 0x10000000) >> 12) | - (($block[1] & 0x00000004) << 13) | (($block[1] & 0x00000400) << 4) | - (($block[1] & 0x00040000) >> 5) | (($block[1] & 0x04000000) >> 14) | - (($block[0] & 0x00000004) << 9) | ( $block[0] & 0x00000400 ) | - (($block[0] & 0x00040000) >> 9) | (($block[0] & 0x04000000) >> 18) | - (($block[1] & 0x00010000) >> 11) | (($block[1] & 0x01000000) >> 20) | - (($block[0] & 0x00010000) >> 15) | (($block[0] & 0x01000000) >> 24) - , - (($block[1] & 0x00000080) << 24) | (($block[1] & 0x00008000) << 15) | - (($block[1] & 0x00800002) << 6) | (($block[0] & 0x00000080) << 20) | - (($block[0] & 0x00008000) << 11) | (($block[0] & 0x00800002) << 2) | - (($block[1] & 0x00000020) << 18) | (($block[1] & 0x00002000) << 9) | - ( $block[1] & 0x00200000 ) | (($block[1] & 0x20000000) >> 9) | - (($block[0] & 0x00000020) << 14) | (($block[0] & 0x00002000) << 5) | - (($block[0] & 0x00200000) >> 4) | (($block[0] & 0x20000000) >> 13) | - (($block[1] & 0x00000008) << 12) | (($block[1] & 0x00000800) << 3) | - (($block[1] & 0x00080000) >> 6) | (($block[1] & 0x08000000) >> 15) | - (($block[0] & 0x00000008) << 8) | (($block[0] & 0x00000800) >> 1) | - (($block[0] & 0x00080000) >> 10) | (($block[0] & 0x08000000) >> 19) | - (($block[1] & 0x00000200) >> 3) | (($block[0] & 0x00000200) >> 7) | - (($block[1] & 0x00020000) >> 12) | (($block[1] & 0x02000000) >> 21) | - (($block[0] & 0x00020000) >> 16) | (($block[0] & 0x02000000) >> 25) | - ($msb[1] << 28) | ($msb[0] << 24) - ); - - for ($i = 0; $i < 16; $i++) { - // start of "the Feistel (F) function" - see the following URL: - // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png - $temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $keys[$mode][$i][0]]) << 28) - | (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $keys[$mode][$i][1]]) << 24) - | (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $keys[$mode][$i][2]]) << 20) - | (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $keys[$mode][$i][3]]) << 16) - | (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $keys[$mode][$i][4]]) << 12) - | (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $keys[$mode][$i][5]]) << 8) - | (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $keys[$mode][$i][6]]) << 4) - | ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $keys[$mode][$i][7]]); - - $msb = ($temp >> 31) & 1; - $temp &= 0x7FFFFFFF; - $newBlock = (($temp & 0x00010000) << 15) | (($temp & 0x02020120) << 5) - | (($temp & 0x00001800) << 17) | (($temp & 0x01000000) >> 10) - | (($temp & 0x00000008) << 24) | (($temp & 0x00100000) << 6) - | (($temp & 0x00000010) << 21) | (($temp & 0x00008000) << 9) - | (($temp & 0x00000200) << 12) | (($temp & 0x10000000) >> 27) - | (($temp & 0x00000040) << 14) | (($temp & 0x08000000) >> 8) - | (($temp & 0x00004000) << 4) | (($temp & 0x00000002) << 16) - | (($temp & 0x00442000) >> 6) | (($temp & 0x40800000) >> 15) - | (($temp & 0x00000001) << 11) | (($temp & 0x20000000) >> 20) - | (($temp & 0x00080000) >> 13) | (($temp & 0x00000004) << 3) - | (($temp & 0x04000000) >> 22) | (($temp & 0x00000480) >> 7) - | (($temp & 0x00200000) >> 19) | ($msb << 23); - // end of "the Feistel (F) function" - $newBlock is F's output - - $temp = $block[1]; - $block[1] = $block[0] ^ $newBlock; - $block[0] = $temp; + static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; + if (!$sbox1) { + $sbox1 = array_map("intval", $this->sbox1); + $sbox2 = array_map("intval", $this->sbox2); + $sbox3 = array_map("intval", $this->sbox3); + $sbox4 = array_map("intval", $this->sbox4); + $sbox5 = array_map("intval", $this->sbox5); + $sbox6 = array_map("intval", $this->sbox6); + $sbox7 = array_map("intval", $this->sbox7); + $sbox8 = array_map("intval", $this->sbox8); + /* Merge $shuffle with $[inv]ipmap */ + for ($i = 0; $i < 256; ++$i) { + $shuffleip[] = $this->shuffle[$this->ipmap[$i]]; + $shuffleinvip[] = $this->shuffle[$this->invipmap[$i]]; + } } - $msb = array( - ($block[0] >> 31) & 1, - ($block[1] >> 31) & 1 - ); - $block[0] &= 0x7FFFFFFF; - $block[1] &= 0x7FFFFFFF; + $keys = $this->keys[$mode]; + $ki = -1; - $block = array( - (($block[0] & 0x01000004) << 7) | (($block[1] & 0x01000004) << 6) | - (($block[0] & 0x00010000) << 13) | (($block[1] & 0x00010000) << 12) | - (($block[0] & 0x00000100) << 19) | (($block[1] & 0x00000100) << 18) | - (($block[0] & 0x00000001) << 25) | (($block[1] & 0x00000001) << 24) | - (($block[0] & 0x02000008) >> 2) | (($block[1] & 0x02000008) >> 3) | - (($block[0] & 0x00020000) << 4) | (($block[1] & 0x00020000) << 3) | - (($block[0] & 0x00000200) << 10) | (($block[1] & 0x00000200) << 9) | - (($block[0] & 0x00000002) << 16) | (($block[1] & 0x00000002) << 15) | - (($block[0] & 0x04000000) >> 11) | (($block[1] & 0x04000000) >> 12) | - (($block[0] & 0x00040000) >> 5) | (($block[1] & 0x00040000) >> 6) | - (($block[0] & 0x00000400) << 1) | ( $block[1] & 0x00000400 ) | - (($block[0] & 0x08000000) >> 20) | (($block[1] & 0x08000000) >> 21) | - (($block[0] & 0x00080000) >> 14) | (($block[1] & 0x00080000) >> 15) | - (($block[0] & 0x00000800) >> 8) | (($block[1] & 0x00000800) >> 9) - , - (($block[0] & 0x10000040) << 3) | (($block[1] & 0x10000040) << 2) | - (($block[0] & 0x00100000) << 9) | (($block[1] & 0x00100000) << 8) | - (($block[0] & 0x00001000) << 15) | (($block[1] & 0x00001000) << 14) | - (($block[0] & 0x00000010) << 21) | (($block[1] & 0x00000010) << 20) | - (($block[0] & 0x20000080) >> 6) | (($block[1] & 0x20000080) >> 7) | - ( $block[0] & 0x00200000 ) | (($block[1] & 0x00200000) >> 1) | - (($block[0] & 0x00002000) << 6) | (($block[1] & 0x00002000) << 5) | - (($block[0] & 0x00000020) << 12) | (($block[1] & 0x00000020) << 11) | - (($block[0] & 0x40000000) >> 15) | (($block[1] & 0x40000000) >> 16) | - (($block[0] & 0x00400000) >> 9) | (($block[1] & 0x00400000) >> 10) | - (($block[0] & 0x00004000) >> 3) | (($block[1] & 0x00004000) >> 4) | - (($block[0] & 0x00800000) >> 18) | (($block[1] & 0x00800000) >> 19) | - (($block[0] & 0x00008000) >> 12) | (($block[1] & 0x00008000) >> 13) | - ($msb[0] << 7) | ($msb[1] << 6) - ); + // Do the initial IP permutation. + $t = unpack('Nl/Nr', $block); + list($l, $r) = array($t['l'], $t['r']); + $block = ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | + ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | + ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | + ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | + ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | + ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | + ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | + ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); - return pack('NN', $block[0], $block[1]); + // Extract L0 and R0. + $t = unpack('Nl/Nr', $block); + list($l, $r) = array($t['l'], $t['r']); + + for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { + // Perform the 16 steps. + for ($i = 0; $i < 16; $i++) { + // start of "the Feistel (F) function" - see the following URL: + // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png + // Merge key schedule. + $b1 = (($r >> 3) & 0x1FFFFFFF) ^ ($r << 29) ^ $keys[++$ki]; + $b2 = (($r >> 31) & 0x00000001) ^ ($r << 1) ^ $keys[++$ki]; + + // S-box indexing. + $t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ + $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ + $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ + $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ $l; + // end of "the Feistel (F) function" + + $l = $r; + $r = $t; + } + + // Last step should not permute L & R. + $t = $l; + $l = $r; + $r = $t; + } + + // Perform the inverse IP permutation. + return ($shuffleinvip[($r >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | + ($shuffleinvip[($l >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | + ($shuffleinvip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | + ($shuffleinvip[($l >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | + ($shuffleinvip[($r >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | + ($shuffleinvip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | + ($shuffleinvip[ $r & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | + ($shuffleinvip[ $l & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); } /** - * Creates the key schedule. + * Creates the key schedule * + * @see Crypt_Base::_setupKey() * @access private - * @param String $key - * @return Array */ - function _prepareKey($key) + function _setupKey() { + if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key, 'des_rounds' => $this->des_rounds); + static $shifts = array( // number of key bits shifted per round 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 ); - // pad the key and remove extra characters as appropriate. - $key = str_pad(substr($key, 0, 8), 8, chr(0)); - - $temp = unpack('Na/Nb', $key); - $key = array($temp['a'], $temp['b']); - $msb = array( - ($key[0] >> 31) & 1, - ($key[1] >> 31) & 1 + static $pc1map = array( + 0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C, + 0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E, + 0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C, + 0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E, + 0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C, + 0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E, + 0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C, + 0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E, + 0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C, + 0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E, + 0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C, + 0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E, + 0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C, + 0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E, + 0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C, + 0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E, + 0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C, + 0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E, + 0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C, + 0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E, + 0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC, + 0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE, + 0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC, + 0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE, + 0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC, + 0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE, + 0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC, + 0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE, + 0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC, + 0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE, + 0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC, + 0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE ); - $key[0] &= 0x7FFFFFFF; - $key[1] &= 0x7FFFFFFF; - $key = array( - (($key[1] & 0x00000002) << 26) | (($key[1] & 0x00000204) << 17) | - (($key[1] & 0x00020408) << 8) | (($key[1] & 0x02040800) >> 1) | - (($key[0] & 0x00000002) << 22) | (($key[0] & 0x00000204) << 13) | - (($key[0] & 0x00020408) << 4) | (($key[0] & 0x02040800) >> 5) | - (($key[1] & 0x04080000) >> 10) | (($key[0] & 0x04080000) >> 14) | - (($key[1] & 0x08000000) >> 19) | (($key[0] & 0x08000000) >> 23) | - (($key[0] & 0x00000010) >> 1) | (($key[0] & 0x00001000) >> 10) | - (($key[0] & 0x00100000) >> 19) | (($key[0] & 0x10000000) >> 28) - , - (($key[1] & 0x00000080) << 20) | (($key[1] & 0x00008000) << 11) | - (($key[1] & 0x00800000) << 2) | (($key[0] & 0x00000080) << 16) | - (($key[0] & 0x00008000) << 7) | (($key[0] & 0x00800000) >> 2) | - (($key[1] & 0x00000040) << 13) | (($key[1] & 0x00004000) << 4) | - (($key[1] & 0x00400000) >> 5) | (($key[1] & 0x40000000) >> 14) | - (($key[0] & 0x00000040) << 9) | ( $key[0] & 0x00004000 ) | - (($key[0] & 0x00400000) >> 9) | (($key[0] & 0x40000000) >> 18) | - (($key[1] & 0x00000020) << 6) | (($key[1] & 0x00002000) >> 3) | - (($key[1] & 0x00200000) >> 12) | (($key[1] & 0x20000000) >> 21) | - (($key[0] & 0x00000020) << 2) | (($key[0] & 0x00002000) >> 7) | - (($key[0] & 0x00200000) >> 16) | (($key[0] & 0x20000000) >> 25) | - (($key[1] & 0x00000010) >> 1) | (($key[1] & 0x00001000) >> 10) | - (($key[1] & 0x00100000) >> 19) | (($key[1] & 0x10000000) >> 28) | - ($msb[1] << 24) | ($msb[0] << 20) - ); + // Mapping tables for the PC-2 transformation. + static $pc2mapc1 = array( + 0x00000000, 0x00000400, 0x00200000, 0x00200400, + 0x00000001, 0x00000401, 0x00200001, 0x00200401, + 0x02000000, 0x02000400, 0x02200000, 0x02200400, + 0x02000001, 0x02000401, 0x02200001, 0x02200401 + ); + static $pc2mapc2 = array( + 0x00000000, 0x00000800, 0x08000000, 0x08000800, + 0x00010000, 0x00010800, 0x08010000, 0x08010800, + 0x00000000, 0x00000800, 0x08000000, 0x08000800, + 0x00010000, 0x00010800, 0x08010000, 0x08010800, + 0x00000100, 0x00000900, 0x08000100, 0x08000900, + 0x00010100, 0x00010900, 0x08010100, 0x08010900, + 0x00000100, 0x00000900, 0x08000100, 0x08000900, + 0x00010100, 0x00010900, 0x08010100, 0x08010900, + 0x00000010, 0x00000810, 0x08000010, 0x08000810, + 0x00010010, 0x00010810, 0x08010010, 0x08010810, + 0x00000010, 0x00000810, 0x08000010, 0x08000810, + 0x00010010, 0x00010810, 0x08010010, 0x08010810, + 0x00000110, 0x00000910, 0x08000110, 0x08000910, + 0x00010110, 0x00010910, 0x08010110, 0x08010910, + 0x00000110, 0x00000910, 0x08000110, 0x08000910, + 0x00010110, 0x00010910, 0x08010110, 0x08010910, + 0x00040000, 0x00040800, 0x08040000, 0x08040800, + 0x00050000, 0x00050800, 0x08050000, 0x08050800, + 0x00040000, 0x00040800, 0x08040000, 0x08040800, + 0x00050000, 0x00050800, 0x08050000, 0x08050800, + 0x00040100, 0x00040900, 0x08040100, 0x08040900, + 0x00050100, 0x00050900, 0x08050100, 0x08050900, + 0x00040100, 0x00040900, 0x08040100, 0x08040900, + 0x00050100, 0x00050900, 0x08050100, 0x08050900, + 0x00040010, 0x00040810, 0x08040010, 0x08040810, + 0x00050010, 0x00050810, 0x08050010, 0x08050810, + 0x00040010, 0x00040810, 0x08040010, 0x08040810, + 0x00050010, 0x00050810, 0x08050010, 0x08050810, + 0x00040110, 0x00040910, 0x08040110, 0x08040910, + 0x00050110, 0x00050910, 0x08050110, 0x08050910, + 0x00040110, 0x00040910, 0x08040110, 0x08040910, + 0x00050110, 0x00050910, 0x08050110, 0x08050910, + 0x01000000, 0x01000800, 0x09000000, 0x09000800, + 0x01010000, 0x01010800, 0x09010000, 0x09010800, + 0x01000000, 0x01000800, 0x09000000, 0x09000800, + 0x01010000, 0x01010800, 0x09010000, 0x09010800, + 0x01000100, 0x01000900, 0x09000100, 0x09000900, + 0x01010100, 0x01010900, 0x09010100, 0x09010900, + 0x01000100, 0x01000900, 0x09000100, 0x09000900, + 0x01010100, 0x01010900, 0x09010100, 0x09010900, + 0x01000010, 0x01000810, 0x09000010, 0x09000810, + 0x01010010, 0x01010810, 0x09010010, 0x09010810, + 0x01000010, 0x01000810, 0x09000010, 0x09000810, + 0x01010010, 0x01010810, 0x09010010, 0x09010810, + 0x01000110, 0x01000910, 0x09000110, 0x09000910, + 0x01010110, 0x01010910, 0x09010110, 0x09010910, + 0x01000110, 0x01000910, 0x09000110, 0x09000910, + 0x01010110, 0x01010910, 0x09010110, 0x09010910, + 0x01040000, 0x01040800, 0x09040000, 0x09040800, + 0x01050000, 0x01050800, 0x09050000, 0x09050800, + 0x01040000, 0x01040800, 0x09040000, 0x09040800, + 0x01050000, 0x01050800, 0x09050000, 0x09050800, + 0x01040100, 0x01040900, 0x09040100, 0x09040900, + 0x01050100, 0x01050900, 0x09050100, 0x09050900, + 0x01040100, 0x01040900, 0x09040100, 0x09040900, + 0x01050100, 0x01050900, 0x09050100, 0x09050900, + 0x01040010, 0x01040810, 0x09040010, 0x09040810, + 0x01050010, 0x01050810, 0x09050010, 0x09050810, + 0x01040010, 0x01040810, 0x09040010, 0x09040810, + 0x01050010, 0x01050810, 0x09050010, 0x09050810, + 0x01040110, 0x01040910, 0x09040110, 0x09040910, + 0x01050110, 0x01050910, 0x09050110, 0x09050910, + 0x01040110, 0x01040910, 0x09040110, 0x09040910, + 0x01050110, 0x01050910, 0x09050110, 0x09050910 + ); + static $pc2mapc3 = array( + 0x00000000, 0x00000004, 0x00001000, 0x00001004, + 0x00000000, 0x00000004, 0x00001000, 0x00001004, + 0x10000000, 0x10000004, 0x10001000, 0x10001004, + 0x10000000, 0x10000004, 0x10001000, 0x10001004, + 0x00000020, 0x00000024, 0x00001020, 0x00001024, + 0x00000020, 0x00000024, 0x00001020, 0x00001024, + 0x10000020, 0x10000024, 0x10001020, 0x10001024, + 0x10000020, 0x10000024, 0x10001020, 0x10001024, + 0x00080000, 0x00080004, 0x00081000, 0x00081004, + 0x00080000, 0x00080004, 0x00081000, 0x00081004, + 0x10080000, 0x10080004, 0x10081000, 0x10081004, + 0x10080000, 0x10080004, 0x10081000, 0x10081004, + 0x00080020, 0x00080024, 0x00081020, 0x00081024, + 0x00080020, 0x00080024, 0x00081020, 0x00081024, + 0x10080020, 0x10080024, 0x10081020, 0x10081024, + 0x10080020, 0x10080024, 0x10081020, 0x10081024, + 0x20000000, 0x20000004, 0x20001000, 0x20001004, + 0x20000000, 0x20000004, 0x20001000, 0x20001004, + 0x30000000, 0x30000004, 0x30001000, 0x30001004, + 0x30000000, 0x30000004, 0x30001000, 0x30001004, + 0x20000020, 0x20000024, 0x20001020, 0x20001024, + 0x20000020, 0x20000024, 0x20001020, 0x20001024, + 0x30000020, 0x30000024, 0x30001020, 0x30001024, + 0x30000020, 0x30000024, 0x30001020, 0x30001024, + 0x20080000, 0x20080004, 0x20081000, 0x20081004, + 0x20080000, 0x20080004, 0x20081000, 0x20081004, + 0x30080000, 0x30080004, 0x30081000, 0x30081004, + 0x30080000, 0x30080004, 0x30081000, 0x30081004, + 0x20080020, 0x20080024, 0x20081020, 0x20081024, + 0x20080020, 0x20080024, 0x20081020, 0x20081024, + 0x30080020, 0x30080024, 0x30081020, 0x30081024, + 0x30080020, 0x30080024, 0x30081020, 0x30081024, + 0x00000002, 0x00000006, 0x00001002, 0x00001006, + 0x00000002, 0x00000006, 0x00001002, 0x00001006, + 0x10000002, 0x10000006, 0x10001002, 0x10001006, + 0x10000002, 0x10000006, 0x10001002, 0x10001006, + 0x00000022, 0x00000026, 0x00001022, 0x00001026, + 0x00000022, 0x00000026, 0x00001022, 0x00001026, + 0x10000022, 0x10000026, 0x10001022, 0x10001026, + 0x10000022, 0x10000026, 0x10001022, 0x10001026, + 0x00080002, 0x00080006, 0x00081002, 0x00081006, + 0x00080002, 0x00080006, 0x00081002, 0x00081006, + 0x10080002, 0x10080006, 0x10081002, 0x10081006, + 0x10080002, 0x10080006, 0x10081002, 0x10081006, + 0x00080022, 0x00080026, 0x00081022, 0x00081026, + 0x00080022, 0x00080026, 0x00081022, 0x00081026, + 0x10080022, 0x10080026, 0x10081022, 0x10081026, + 0x10080022, 0x10080026, 0x10081022, 0x10081026, + 0x20000002, 0x20000006, 0x20001002, 0x20001006, + 0x20000002, 0x20000006, 0x20001002, 0x20001006, + 0x30000002, 0x30000006, 0x30001002, 0x30001006, + 0x30000002, 0x30000006, 0x30001002, 0x30001006, + 0x20000022, 0x20000026, 0x20001022, 0x20001026, + 0x20000022, 0x20000026, 0x20001022, 0x20001026, + 0x30000022, 0x30000026, 0x30001022, 0x30001026, + 0x30000022, 0x30000026, 0x30001022, 0x30001026, + 0x20080002, 0x20080006, 0x20081002, 0x20081006, + 0x20080002, 0x20080006, 0x20081002, 0x20081006, + 0x30080002, 0x30080006, 0x30081002, 0x30081006, + 0x30080002, 0x30080006, 0x30081002, 0x30081006, + 0x20080022, 0x20080026, 0x20081022, 0x20081026, + 0x20080022, 0x20080026, 0x20081022, 0x20081026, + 0x30080022, 0x30080026, 0x30081022, 0x30081026, + 0x30080022, 0x30080026, 0x30081022, 0x30081026 + ); + static $pc2mapc4 = array( + 0x00000000, 0x00100000, 0x00000008, 0x00100008, + 0x00000200, 0x00100200, 0x00000208, 0x00100208, + 0x00000000, 0x00100000, 0x00000008, 0x00100008, + 0x00000200, 0x00100200, 0x00000208, 0x00100208, + 0x04000000, 0x04100000, 0x04000008, 0x04100008, + 0x04000200, 0x04100200, 0x04000208, 0x04100208, + 0x04000000, 0x04100000, 0x04000008, 0x04100008, + 0x04000200, 0x04100200, 0x04000208, 0x04100208, + 0x00002000, 0x00102000, 0x00002008, 0x00102008, + 0x00002200, 0x00102200, 0x00002208, 0x00102208, + 0x00002000, 0x00102000, 0x00002008, 0x00102008, + 0x00002200, 0x00102200, 0x00002208, 0x00102208, + 0x04002000, 0x04102000, 0x04002008, 0x04102008, + 0x04002200, 0x04102200, 0x04002208, 0x04102208, + 0x04002000, 0x04102000, 0x04002008, 0x04102008, + 0x04002200, 0x04102200, 0x04002208, 0x04102208, + 0x00000000, 0x00100000, 0x00000008, 0x00100008, + 0x00000200, 0x00100200, 0x00000208, 0x00100208, + 0x00000000, 0x00100000, 0x00000008, 0x00100008, + 0x00000200, 0x00100200, 0x00000208, 0x00100208, + 0x04000000, 0x04100000, 0x04000008, 0x04100008, + 0x04000200, 0x04100200, 0x04000208, 0x04100208, + 0x04000000, 0x04100000, 0x04000008, 0x04100008, + 0x04000200, 0x04100200, 0x04000208, 0x04100208, + 0x00002000, 0x00102000, 0x00002008, 0x00102008, + 0x00002200, 0x00102200, 0x00002208, 0x00102208, + 0x00002000, 0x00102000, 0x00002008, 0x00102008, + 0x00002200, 0x00102200, 0x00002208, 0x00102208, + 0x04002000, 0x04102000, 0x04002008, 0x04102008, + 0x04002200, 0x04102200, 0x04002208, 0x04102208, + 0x04002000, 0x04102000, 0x04002008, 0x04102008, + 0x04002200, 0x04102200, 0x04002208, 0x04102208, + 0x00020000, 0x00120000, 0x00020008, 0x00120008, + 0x00020200, 0x00120200, 0x00020208, 0x00120208, + 0x00020000, 0x00120000, 0x00020008, 0x00120008, + 0x00020200, 0x00120200, 0x00020208, 0x00120208, + 0x04020000, 0x04120000, 0x04020008, 0x04120008, + 0x04020200, 0x04120200, 0x04020208, 0x04120208, + 0x04020000, 0x04120000, 0x04020008, 0x04120008, + 0x04020200, 0x04120200, 0x04020208, 0x04120208, + 0x00022000, 0x00122000, 0x00022008, 0x00122008, + 0x00022200, 0x00122200, 0x00022208, 0x00122208, + 0x00022000, 0x00122000, 0x00022008, 0x00122008, + 0x00022200, 0x00122200, 0x00022208, 0x00122208, + 0x04022000, 0x04122000, 0x04022008, 0x04122008, + 0x04022200, 0x04122200, 0x04022208, 0x04122208, + 0x04022000, 0x04122000, 0x04022008, 0x04122008, + 0x04022200, 0x04122200, 0x04022208, 0x04122208, + 0x00020000, 0x00120000, 0x00020008, 0x00120008, + 0x00020200, 0x00120200, 0x00020208, 0x00120208, + 0x00020000, 0x00120000, 0x00020008, 0x00120008, + 0x00020200, 0x00120200, 0x00020208, 0x00120208, + 0x04020000, 0x04120000, 0x04020008, 0x04120008, + 0x04020200, 0x04120200, 0x04020208, 0x04120208, + 0x04020000, 0x04120000, 0x04020008, 0x04120008, + 0x04020200, 0x04120200, 0x04020208, 0x04120208, + 0x00022000, 0x00122000, 0x00022008, 0x00122008, + 0x00022200, 0x00122200, 0x00022208, 0x00122208, + 0x00022000, 0x00122000, 0x00022008, 0x00122008, + 0x00022200, 0x00122200, 0x00022208, 0x00122208, + 0x04022000, 0x04122000, 0x04022008, 0x04122008, + 0x04022200, 0x04122200, 0x04022208, 0x04122208, + 0x04022000, 0x04122000, 0x04022008, 0x04122008, + 0x04022200, 0x04122200, 0x04022208, 0x04122208 + ); + static $pc2mapd1 = array( + 0x00000000, 0x00000001, 0x08000000, 0x08000001, + 0x00200000, 0x00200001, 0x08200000, 0x08200001, + 0x00000002, 0x00000003, 0x08000002, 0x08000003, + 0x00200002, 0x00200003, 0x08200002, 0x08200003 + ); + static $pc2mapd2 = array( + 0x00000000, 0x00100000, 0x00000800, 0x00100800, + 0x00000000, 0x00100000, 0x00000800, 0x00100800, + 0x04000000, 0x04100000, 0x04000800, 0x04100800, + 0x04000000, 0x04100000, 0x04000800, 0x04100800, + 0x00000004, 0x00100004, 0x00000804, 0x00100804, + 0x00000004, 0x00100004, 0x00000804, 0x00100804, + 0x04000004, 0x04100004, 0x04000804, 0x04100804, + 0x04000004, 0x04100004, 0x04000804, 0x04100804, + 0x00000000, 0x00100000, 0x00000800, 0x00100800, + 0x00000000, 0x00100000, 0x00000800, 0x00100800, + 0x04000000, 0x04100000, 0x04000800, 0x04100800, + 0x04000000, 0x04100000, 0x04000800, 0x04100800, + 0x00000004, 0x00100004, 0x00000804, 0x00100804, + 0x00000004, 0x00100004, 0x00000804, 0x00100804, + 0x04000004, 0x04100004, 0x04000804, 0x04100804, + 0x04000004, 0x04100004, 0x04000804, 0x04100804, + 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, + 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, + 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, + 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, + 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, + 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, + 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, + 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, + 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, + 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, + 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, + 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, + 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, + 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, + 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, + 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, + 0x00020000, 0x00120000, 0x00020800, 0x00120800, + 0x00020000, 0x00120000, 0x00020800, 0x00120800, + 0x04020000, 0x04120000, 0x04020800, 0x04120800, + 0x04020000, 0x04120000, 0x04020800, 0x04120800, + 0x00020004, 0x00120004, 0x00020804, 0x00120804, + 0x00020004, 0x00120004, 0x00020804, 0x00120804, + 0x04020004, 0x04120004, 0x04020804, 0x04120804, + 0x04020004, 0x04120004, 0x04020804, 0x04120804, + 0x00020000, 0x00120000, 0x00020800, 0x00120800, + 0x00020000, 0x00120000, 0x00020800, 0x00120800, + 0x04020000, 0x04120000, 0x04020800, 0x04120800, + 0x04020000, 0x04120000, 0x04020800, 0x04120800, + 0x00020004, 0x00120004, 0x00020804, 0x00120804, + 0x00020004, 0x00120004, 0x00020804, 0x00120804, + 0x04020004, 0x04120004, 0x04020804, 0x04120804, + 0x04020004, 0x04120004, 0x04020804, 0x04120804, + 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, + 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, + 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, + 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, + 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, + 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, + 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, + 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, + 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, + 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, + 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, + 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, + 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, + 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, + 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, + 0x04020204, 0x04120204, 0x04020A04, 0x04120A04 + ); + static $pc2mapd3 = array( + 0x00000000, 0x00010000, 0x02000000, 0x02010000, + 0x00000020, 0x00010020, 0x02000020, 0x02010020, + 0x00040000, 0x00050000, 0x02040000, 0x02050000, + 0x00040020, 0x00050020, 0x02040020, 0x02050020, + 0x00002000, 0x00012000, 0x02002000, 0x02012000, + 0x00002020, 0x00012020, 0x02002020, 0x02012020, + 0x00042000, 0x00052000, 0x02042000, 0x02052000, + 0x00042020, 0x00052020, 0x02042020, 0x02052020, + 0x00000000, 0x00010000, 0x02000000, 0x02010000, + 0x00000020, 0x00010020, 0x02000020, 0x02010020, + 0x00040000, 0x00050000, 0x02040000, 0x02050000, + 0x00040020, 0x00050020, 0x02040020, 0x02050020, + 0x00002000, 0x00012000, 0x02002000, 0x02012000, + 0x00002020, 0x00012020, 0x02002020, 0x02012020, + 0x00042000, 0x00052000, 0x02042000, 0x02052000, + 0x00042020, 0x00052020, 0x02042020, 0x02052020, + 0x00000010, 0x00010010, 0x02000010, 0x02010010, + 0x00000030, 0x00010030, 0x02000030, 0x02010030, + 0x00040010, 0x00050010, 0x02040010, 0x02050010, + 0x00040030, 0x00050030, 0x02040030, 0x02050030, + 0x00002010, 0x00012010, 0x02002010, 0x02012010, + 0x00002030, 0x00012030, 0x02002030, 0x02012030, + 0x00042010, 0x00052010, 0x02042010, 0x02052010, + 0x00042030, 0x00052030, 0x02042030, 0x02052030, + 0x00000010, 0x00010010, 0x02000010, 0x02010010, + 0x00000030, 0x00010030, 0x02000030, 0x02010030, + 0x00040010, 0x00050010, 0x02040010, 0x02050010, + 0x00040030, 0x00050030, 0x02040030, 0x02050030, + 0x00002010, 0x00012010, 0x02002010, 0x02012010, + 0x00002030, 0x00012030, 0x02002030, 0x02012030, + 0x00042010, 0x00052010, 0x02042010, 0x02052010, + 0x00042030, 0x00052030, 0x02042030, 0x02052030, + 0x20000000, 0x20010000, 0x22000000, 0x22010000, + 0x20000020, 0x20010020, 0x22000020, 0x22010020, + 0x20040000, 0x20050000, 0x22040000, 0x22050000, + 0x20040020, 0x20050020, 0x22040020, 0x22050020, + 0x20002000, 0x20012000, 0x22002000, 0x22012000, + 0x20002020, 0x20012020, 0x22002020, 0x22012020, + 0x20042000, 0x20052000, 0x22042000, 0x22052000, + 0x20042020, 0x20052020, 0x22042020, 0x22052020, + 0x20000000, 0x20010000, 0x22000000, 0x22010000, + 0x20000020, 0x20010020, 0x22000020, 0x22010020, + 0x20040000, 0x20050000, 0x22040000, 0x22050000, + 0x20040020, 0x20050020, 0x22040020, 0x22050020, + 0x20002000, 0x20012000, 0x22002000, 0x22012000, + 0x20002020, 0x20012020, 0x22002020, 0x22012020, + 0x20042000, 0x20052000, 0x22042000, 0x22052000, + 0x20042020, 0x20052020, 0x22042020, 0x22052020, + 0x20000010, 0x20010010, 0x22000010, 0x22010010, + 0x20000030, 0x20010030, 0x22000030, 0x22010030, + 0x20040010, 0x20050010, 0x22040010, 0x22050010, + 0x20040030, 0x20050030, 0x22040030, 0x22050030, + 0x20002010, 0x20012010, 0x22002010, 0x22012010, + 0x20002030, 0x20012030, 0x22002030, 0x22012030, + 0x20042010, 0x20052010, 0x22042010, 0x22052010, + 0x20042030, 0x20052030, 0x22042030, 0x22052030, + 0x20000010, 0x20010010, 0x22000010, 0x22010010, + 0x20000030, 0x20010030, 0x22000030, 0x22010030, + 0x20040010, 0x20050010, 0x22040010, 0x22050010, + 0x20040030, 0x20050030, 0x22040030, 0x22050030, + 0x20002010, 0x20012010, 0x22002010, 0x22012010, + 0x20002030, 0x20012030, 0x22002030, 0x22012030, + 0x20042010, 0x20052010, 0x22042010, 0x22052010, + 0x20042030, 0x20052030, 0x22042030, 0x22052030 + ); + static $pc2mapd4 = array( + 0x00000000, 0x00000400, 0x01000000, 0x01000400, + 0x00000000, 0x00000400, 0x01000000, 0x01000400, + 0x00000100, 0x00000500, 0x01000100, 0x01000500, + 0x00000100, 0x00000500, 0x01000100, 0x01000500, + 0x10000000, 0x10000400, 0x11000000, 0x11000400, + 0x10000000, 0x10000400, 0x11000000, 0x11000400, + 0x10000100, 0x10000500, 0x11000100, 0x11000500, + 0x10000100, 0x10000500, 0x11000100, 0x11000500, + 0x00080000, 0x00080400, 0x01080000, 0x01080400, + 0x00080000, 0x00080400, 0x01080000, 0x01080400, + 0x00080100, 0x00080500, 0x01080100, 0x01080500, + 0x00080100, 0x00080500, 0x01080100, 0x01080500, + 0x10080000, 0x10080400, 0x11080000, 0x11080400, + 0x10080000, 0x10080400, 0x11080000, 0x11080400, + 0x10080100, 0x10080500, 0x11080100, 0x11080500, + 0x10080100, 0x10080500, 0x11080100, 0x11080500, + 0x00000008, 0x00000408, 0x01000008, 0x01000408, + 0x00000008, 0x00000408, 0x01000008, 0x01000408, + 0x00000108, 0x00000508, 0x01000108, 0x01000508, + 0x00000108, 0x00000508, 0x01000108, 0x01000508, + 0x10000008, 0x10000408, 0x11000008, 0x11000408, + 0x10000008, 0x10000408, 0x11000008, 0x11000408, + 0x10000108, 0x10000508, 0x11000108, 0x11000508, + 0x10000108, 0x10000508, 0x11000108, 0x11000508, + 0x00080008, 0x00080408, 0x01080008, 0x01080408, + 0x00080008, 0x00080408, 0x01080008, 0x01080408, + 0x00080108, 0x00080508, 0x01080108, 0x01080508, + 0x00080108, 0x00080508, 0x01080108, 0x01080508, + 0x10080008, 0x10080408, 0x11080008, 0x11080408, + 0x10080008, 0x10080408, 0x11080008, 0x11080408, + 0x10080108, 0x10080508, 0x11080108, 0x11080508, + 0x10080108, 0x10080508, 0x11080108, 0x11080508, + 0x00001000, 0x00001400, 0x01001000, 0x01001400, + 0x00001000, 0x00001400, 0x01001000, 0x01001400, + 0x00001100, 0x00001500, 0x01001100, 0x01001500, + 0x00001100, 0x00001500, 0x01001100, 0x01001500, + 0x10001000, 0x10001400, 0x11001000, 0x11001400, + 0x10001000, 0x10001400, 0x11001000, 0x11001400, + 0x10001100, 0x10001500, 0x11001100, 0x11001500, + 0x10001100, 0x10001500, 0x11001100, 0x11001500, + 0x00081000, 0x00081400, 0x01081000, 0x01081400, + 0x00081000, 0x00081400, 0x01081000, 0x01081400, + 0x00081100, 0x00081500, 0x01081100, 0x01081500, + 0x00081100, 0x00081500, 0x01081100, 0x01081500, + 0x10081000, 0x10081400, 0x11081000, 0x11081400, + 0x10081000, 0x10081400, 0x11081000, 0x11081400, + 0x10081100, 0x10081500, 0x11081100, 0x11081500, + 0x10081100, 0x10081500, 0x11081100, 0x11081500, + 0x00001008, 0x00001408, 0x01001008, 0x01001408, + 0x00001008, 0x00001408, 0x01001008, 0x01001408, + 0x00001108, 0x00001508, 0x01001108, 0x01001508, + 0x00001108, 0x00001508, 0x01001108, 0x01001508, + 0x10001008, 0x10001408, 0x11001008, 0x11001408, + 0x10001008, 0x10001408, 0x11001008, 0x11001408, + 0x10001108, 0x10001508, 0x11001108, 0x11001508, + 0x10001108, 0x10001508, 0x11001108, 0x11001508, + 0x00081008, 0x00081408, 0x01081008, 0x01081408, + 0x00081008, 0x00081408, 0x01081008, 0x01081408, + 0x00081108, 0x00081508, 0x01081108, 0x01081508, + 0x00081108, 0x00081508, 0x01081108, 0x01081508, + 0x10081008, 0x10081408, 0x11081008, 0x11081408, + 0x10081008, 0x10081408, 0x11081008, 0x11081408, + 0x10081108, 0x10081508, 0x11081108, 0x11081508, + 0x10081108, 0x10081508, 0x11081108, 0x11081508 + ); $keys = array(); - for ($i = 0; $i < 16; $i++) { - $key[0] <<= $shifts[$i]; - $temp = ($key[0] & 0xF0000000) >> 28; - $key[0] = ($key[0] | $temp) & 0x0FFFFFFF; + for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { + // pad the key and remove extra characters as appropriate. + $key = str_pad(substr($this->key, $des_round * 8, 8), 8, "\0"); - $key[1] <<= $shifts[$i]; - $temp = ($key[1] & 0xF0000000) >> 28; - $key[1] = ($key[1] | $temp) & 0x0FFFFFFF; + // Perform the PC/1 transformation and compute C and D. + $t = unpack('Nl/Nr', $key); + list($l, $r) = array($t['l'], $t['r']); + $key = ($this->shuffle[$pc1map[ $r & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") | + ($this->shuffle[$pc1map[($r >> 8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") | + ($this->shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") | + ($this->shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") | + ($this->shuffle[$pc1map[ $l & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") | + ($this->shuffle[$pc1map[($l >> 8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") | + ($this->shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") | + ($this->shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00"); + $key = unpack('Nc/Nd', $key); + $c = ( $key['c'] >> 4) & 0x0FFFFFFF; + $d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F); - $temp = array( - (($key[1] & 0x00004000) >> 9) | (($key[1] & 0x00000800) >> 7) | - (($key[1] & 0x00020000) >> 14) | (($key[1] & 0x00000010) >> 2) | - (($key[1] & 0x08000000) >> 26) | (($key[1] & 0x00800000) >> 23) - , - (($key[1] & 0x02400000) >> 20) | (($key[1] & 0x00000001) << 4) | - (($key[1] & 0x00002000) >> 10) | (($key[1] & 0x00040000) >> 18) | - (($key[1] & 0x00000080) >> 6) - , - ( $key[1] & 0x00000020 ) | (($key[1] & 0x00000200) >> 5) | - (($key[1] & 0x00010000) >> 13) | (($key[1] & 0x01000000) >> 22) | - (($key[1] & 0x00000004) >> 1) | (($key[1] & 0x00100000) >> 20) - , - (($key[1] & 0x00001000) >> 7) | (($key[1] & 0x00200000) >> 17) | - (($key[1] & 0x00000002) << 2) | (($key[1] & 0x00000100) >> 6) | - (($key[1] & 0x00008000) >> 14) | (($key[1] & 0x04000000) >> 26) - , - (($key[0] & 0x00008000) >> 10) | ( $key[0] & 0x00000010 ) | - (($key[0] & 0x02000000) >> 22) | (($key[0] & 0x00080000) >> 17) | - (($key[0] & 0x00000200) >> 8) | (($key[0] & 0x00000002) >> 1) - , - (($key[0] & 0x04000000) >> 21) | (($key[0] & 0x00010000) >> 12) | - (($key[0] & 0x00000020) >> 2) | (($key[0] & 0x00000800) >> 9) | - (($key[0] & 0x00800000) >> 22) | (($key[0] & 0x00000100) >> 8) - , - (($key[0] & 0x00001000) >> 7) | (($key[0] & 0x00000088) >> 3) | - (($key[0] & 0x00020000) >> 14) | (($key[0] & 0x00000001) << 2) | - (($key[0] & 0x00400000) >> 21) - , - (($key[0] & 0x00000400) >> 5) | (($key[0] & 0x00004000) >> 10) | - (($key[0] & 0x00000040) >> 3) | (($key[0] & 0x00100000) >> 18) | - (($key[0] & 0x08000000) >> 26) | (($key[0] & 0x01000000) >> 24) + $keys[$des_round] = array( + CRYPT_DES_ENCRYPT => array(), + CRYPT_DES_DECRYPT => array_fill(0, 32, 0) ); + for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) { + $c <<= $shifts[$i]; + $c = ($c | ($c >> 28)) & 0x0FFFFFFF; + $d <<= $shifts[$i]; + $d = ($d | ($d >> 28)) & 0x0FFFFFFF; - $keys[] = $temp; + // Perform the PC-2 transformation. + $cp = $pc2mapc1[ $c >> 24 ] | $pc2mapc2[($c >> 16) & 0xFF] | + $pc2mapc3[($c >> 8) & 0xFF] | $pc2mapc4[ $c & 0xFF]; + $dp = $pc2mapd1[ $d >> 24 ] | $pc2mapd2[($d >> 16) & 0xFF] | + $pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[ $d & 0xFF]; + + // Reorder: odd bytes/even bytes. Push the result in key schedule. + $keys[$des_round][CRYPT_DES_ENCRYPT][ ] = + $keys[$des_round][CRYPT_DES_DECRYPT][$ki - 1] = ( $cp & 0xFF000000) | (($cp << 8) & 0x00FF0000) | + (($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF); + $keys[$des_round][CRYPT_DES_ENCRYPT][ ] = + $keys[$des_round][CRYPT_DES_DECRYPT][$ki ] = (($cp << 8) & 0xFF000000) | (($cp << 16) & 0x00FF0000) | + (($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF); + } } - $temp = array( - CRYPT_DES_ENCRYPT => $keys, - CRYPT_DES_DECRYPT => array_reverse($keys) - ); - - return $temp; + switch ($this->des_rounds) { + case 3: // 3DES keys + $this->keys = array( + CRYPT_DES_ENCRYPT => array_merge( + $keys[0][CRYPT_DES_ENCRYPT], + $keys[1][CRYPT_DES_DECRYPT], + $keys[2][CRYPT_DES_ENCRYPT] + ), + CRYPT_DES_DECRYPT => array_merge( + $keys[2][CRYPT_DES_DECRYPT], + $keys[1][CRYPT_DES_ENCRYPT], + $keys[0][CRYPT_DES_DECRYPT] + ) + ); + break; + // case 1: // DES keys + default: + $this->keys = array( + CRYPT_DES_ENCRYPT => $keys[0][CRYPT_DES_ENCRYPT], + CRYPT_DES_DECRYPT => $keys[0][CRYPT_DES_DECRYPT] + ); + } } /** - * String Shift + * Setup the performance-optimized function for de/encrypt() * - * Inspired by array_shift - * - * @param String $string - * @param optional Integer $index - * @return String + * @see Crypt_Base::_setupInlineCrypt() * @access private */ - function _string_shift(&$string, $index = 1) + function _setupInlineCrypt() { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; + $lambda_functions =& Crypt_DES::_getLambdaFunctions(); + + // Engine configuration for: + // - DES ($des_rounds == 1) or + // - 3DES ($des_rounds == 3) + $des_rounds = $this->des_rounds; + + // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. + // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one + $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 ); + + // Generation of a uniqe hash for our generated code + switch (true) { + case $gen_hi_opt_code: + // For hi-optimized code, we create for each combination of + // $mode, $des_rounds and $this->key its own encrypt/decrypt function. + $code_hash = md5(str_pad("Crypt_DES, $des_rounds, {$this->mode}, ", 32, "\0") . $this->key); + break; + default: + // After max 10 hi-optimized functions, we create generic + // (still very fast.. but not ultra) functions for each $mode/$des_rounds + // Currently 2 * 5 generic functions will be then max. possible. + $code_hash = "Crypt_DES, $des_rounds, {$this->mode}"; + } + + // Is there a re-usable $lambda_functions in there? If not, we have to create it. + if (!isset($lambda_functions[$code_hash])) { + // Init code for both, encrypt and decrypt. + $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; + if (!$sbox1) { + $sbox1 = array_map("intval", $self->sbox1); + $sbox2 = array_map("intval", $self->sbox2); + $sbox3 = array_map("intval", $self->sbox3); + $sbox4 = array_map("intval", $self->sbox4); + $sbox5 = array_map("intval", $self->sbox5); + $sbox6 = array_map("intval", $self->sbox6); + $sbox7 = array_map("intval", $self->sbox7); + $sbox8 = array_map("intval", $self->sbox8);' + /* Merge $shuffle with $[inv]ipmap */ . ' + for ($i = 0; $i < 256; ++$i) { + $shuffleip[] = $self->shuffle[$self->ipmap[$i]]; + $shuffleinvip[] = $self->shuffle[$self->invipmap[$i]]; + } + } + '; + + switch (true) { + case $gen_hi_opt_code: + // In Hi-optimized code mode, we use our [3]DES key schedule as hardcoded integers. + // No futher initialisation of the $keys schedule is necessary. + // That is the extra performance boost. + $k = array( + CRYPT_DES_ENCRYPT => $this->keys[CRYPT_DES_ENCRYPT], + CRYPT_DES_DECRYPT => $this->keys[CRYPT_DES_DECRYPT] + ); + $init_encrypt = ''; + $init_decrypt = ''; + break; + default: + // In generic optimized code mode, we have to use, as the best compromise [currently], + // our key schedule as $ke/$kd arrays. (with hardcoded indexes...) + $k = array( + CRYPT_DES_ENCRYPT => array(), + CRYPT_DES_DECRYPT => array() + ); + for ($i = 0, $c = count($this->keys[CRYPT_DES_ENCRYPT]); $i < $c; ++$i) { + $k[CRYPT_DES_ENCRYPT][$i] = '$ke[' . $i . ']'; + $k[CRYPT_DES_DECRYPT][$i] = '$kd[' . $i . ']'; + } + $init_encrypt = '$ke = $self->keys[CRYPT_DES_ENCRYPT];'; + $init_decrypt = '$kd = $self->keys[CRYPT_DES_DECRYPT];'; + break; + } + + // Creating code for en- and decryption. + $crypt_block = array(); + foreach (array(CRYPT_DES_ENCRYPT, CRYPT_DES_DECRYPT) as $c) { + + /* Do the initial IP permutation. */ + $crypt_block[$c] = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + $in = unpack("N*", + ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | + ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | + ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | + ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | + ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | + ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | + ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | + ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01") + ); + ' . /* Extract L0 and R0 */ ' + $l = $in[1]; + $r = $in[2]; + '; + + $l = '$l'; + $r = '$r'; + + // Perform DES or 3DES. + for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) { + // Perform the 16 steps. + for ($i = 0; $i < 16; ++$i) { + // start of "the Feistel (F) function" - see the following URL: + // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png + // Merge key schedule. + $crypt_block[$c].= ' + $b1 = ((' . $r . ' >> 3) & 0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . '; + $b2 = ((' . $r . ' >> 31) & 0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki] . ';' . + /* S-box indexing. */ + $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ + $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ + $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ + $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ ' . $l . '; + '; + // end of "the Feistel (F) function" + + // swap L & R + list($l, $r) = array($r, $l); + } + list($l, $r) = array($r, $l); + } + + // Perform the inverse IP permutation. + $crypt_block[$c].= '$in = + ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | + ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | + ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | + ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | + ($shuffleinvip[($l >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | + ($shuffleinvip[($r >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | + ($shuffleinvip[ $l & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | + ($shuffleinvip[ $r & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); + '; + } + + // Creates the inline-crypt function + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'init_encrypt' => $init_encrypt, + 'init_decrypt' => $init_decrypt, + 'encrypt_block' => $crypt_block[CRYPT_DES_ENCRYPT], + 'decrypt_block' => $crypt_block[CRYPT_DES_DECRYPT] + ) + ); + } + + // Set the inline-crypt function as callback in: $this->inline_crypt + $this->inline_crypt = $lambda_functions[$code_hash]; } } diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php index c5d314f009..2189724198 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Hash.php @@ -52,7 +52,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: Hash.php,v 1.6 2009/11/23 23:37:07 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -83,6 +82,15 @@ define('CRYPT_HASH_MODE_HASH', 3); * @package Crypt_Hash */ class Crypt_Hash { + /** + * Hash Parameter + * + * @see Crypt_Hash::setHash() + * @var Integer + * @access private + */ + var $hashParam; + /** * Byte-length of compression blocks / key (Internal HMAC) * @@ -168,13 +176,26 @@ class Crypt_Hash { * Keys can be of any length. * * @access public - * @param String $key + * @param optional String $key */ function setKey($key = false) { $this->key = $key; } + /** + * Gets the hash function. + * + * As set by the constructor or by the setHash() method. + * + * @access public + * @return String + */ + function getHash() + { + return $this->hashParam; + } + /** * Sets the hash function. * @@ -183,7 +204,7 @@ class Crypt_Hash { */ function setHash($hash) { - $hash = strtolower($hash); + $this->hashParam = $hash = strtolower($hash); switch ($hash) { case 'md5-96': case 'sha1-96': @@ -350,7 +371,7 @@ class Crypt_Hash { * Wrapper for MD5 * * @access private - * @param String $text + * @param String $m */ function _md5($m) { @@ -361,7 +382,7 @@ class Crypt_Hash { * Wrapper for SHA1 * * @access private - * @param String $text + * @param String $m */ function _sha1($m) { @@ -374,7 +395,7 @@ class Crypt_Hash { * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}. * * @access private - * @param String $text + * @param String $m */ function _md2($m) { @@ -450,7 +471,7 @@ class Crypt_Hash { * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}. * * @access private - * @param String $text + * @param String $m */ function _sha256($m) { @@ -555,7 +576,7 @@ class Crypt_Hash { * Pure-PHP implementation of SHA384 and SHA512 * * @access private - * @param String $text + * @param String $m */ function _sha512($m) { @@ -784,9 +805,8 @@ class Crypt_Hash { * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster. * - * @param String $string - * @param optional Integer $index - * @return String + * @param Integer $... + * @return Integer * @see _sha256() * @access private */ diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC2.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC2.php new file mode 100755 index 0000000000..5e0ca88c8f --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC2.php @@ -0,0 +1,656 @@ + + * setKey('abcdefgh'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $rc2->decrypt($rc2->encrypt($plaintext)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_RC2 + * @author Patrick Monnerat + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Crypt_Base + * + * Base cipher class + */ +if (!class_exists('Crypt_Base')) { + require_once('Base.php'); +} + +/**#@+ + * @access public + * @see Crypt_RC2::encrypt() + * @see Crypt_RC2::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_RC2_MODE_CTR', CRYPT_MODE_CTR); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +define('CRYPT_RC2_MODE_ECB', CRYPT_MODE_ECB); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +define('CRYPT_RC2_MODE_CBC', CRYPT_MODE_CBC); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +define('CRYPT_RC2_MODE_CFB', CRYPT_MODE_CFB); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +define('CRYPT_RC2_MODE_OFB', CRYPT_MODE_OFB); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_RC2::Crypt_RC2() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_RC2_MODE_INTERNAL', CRYPT_MODE_INTERNAL); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_RC2_MODE_MCRYPT', CRYPT_MODE_MCRYPT); +/**#@-*/ + +/** + * Pure-PHP implementation of RC2. + * + * @version 0.1.1 + * @access public + * @package Crypt_RC2 + */ +class Crypt_RC2 extends Crypt_Base { + /** + * Block Length of the cipher + * + * @see Crypt_Base::block_size + * @var Integer + * @access private + */ + var $block_size = 8; + + /** + * The Key + * + * @see Crypt_Base::key + * @see setKey() + * @var String + * @access private + */ + var $key = "\0"; + + /** + * The default password key_size used by setPassword() + * + * @see Crypt_Base::password_key_size + * @see Crypt_Base::setPassword() + * @var Integer + * @access private + */ + var $password_key_size = 16; // = 128 bits + + /** + * The namespace used by the cipher for its constants. + * + * @see Crypt_Base::const_namespace + * @var String + * @access private + */ + var $const_namespace = 'RC2'; + + /** + * The mcrypt specific name of the cipher + * + * @see Crypt_Base::cipher_name_mcrypt + * @var String + * @access private + */ + var $cipher_name_mcrypt = 'rc2'; + + /** + * Optimizing value while CFB-encrypting + * + * @see Crypt_Base::cfb_init_len + * @var Integer + * @access private + */ + var $cfb_init_len = 500; + +/** + * The key length in bits. + * + * @see Crypt_RC2::setKeyLength() + * @see Crypt_RC2::setKey() + * @var Integer + * @access private + * @internal Should be in range [1..1024]. + * @internal Changing this value after setting the key has no effect. + */ + var $default_key_length = 1024; + + /** + * The Key Schedule + * + * @see Crypt_RC2::_setupKey() + * @var Array + * @access private + */ + var $keys; + + /** + * Key expansion randomization table. + * Twice the same 256-value sequence to save a modulus in key expansion. + * + * @see Crypt_RC2::setKey() + * @var Array + * @access private + */ + var $pitable = array( + 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, + 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, + 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, + 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, + 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, + 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, + 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, + 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, + 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, + 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, + 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, + 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, + 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, + 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, + 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, + 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, + 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, + 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, + 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, + 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, + 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, + 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, + 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, + 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, + 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, + 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, + 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, + 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, + 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, + 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, + 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD, + 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, + 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, + 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, + 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, + 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, + 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, + 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, + 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, + 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, + 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, + 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, + 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, + 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, + 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, + 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, + 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, + 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, + 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, + 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, + 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, + 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, + 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, + 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, + 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, + 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, + 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, + 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, + 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, + 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, + 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, + 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD + ); + + /** + * Inverse key expansion randomization table. + * + * @see Crypt_RC2::setKey() + * @var Array + * @access private + */ + var $invpitable = array( + 0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66, + 0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4, + 0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20, + 0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53, + 0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68, + 0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B, + 0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12, + 0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB, + 0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3, + 0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26, + 0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67, + 0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB, + 0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC, + 0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60, + 0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7, + 0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD, + 0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24, + 0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31, + 0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE, + 0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99, + 0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C, + 0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA, + 0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35, + 0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61, + 0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72, + 0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3, + 0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F, + 0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9, + 0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77, + 0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75, + 0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87, + 0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6 + ); + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_RC2_MODE_ECB + * + * - CRYPT_RC2_MODE_CBC + * + * - CRYPT_RC2_MODE_CTR + * + * - CRYPT_RC2_MODE_CFB + * + * - CRYPT_RC2_MODE_OFB + * + * If not explictly set, CRYPT_RC2_MODE_CBC will be used. + * + * @see Crypt_Base::Crypt_Base() + * @param optional Integer $mode + * @access public + */ + function Crypt_RC2($mode = CRYPT_RC2_MODE_CBC) + { + parent::Crypt_Base($mode); + $this->setKey(''); + } + + /** + * Sets the key length + * + * Valid key lengths are 1 to 1024. + * Calling this function after setting the key has no effect until the next + * Crypt_RC2::setKey() call. + * + * @access public + * @param Integer $length in bits + */ + function setKeyLength($length) + { + if ($length >= 1 && $length <= 1024) { + $this->default_key_length = $length; + } + } + + /** + * Sets the key. + * + * Keys can be of any length. RC2, itself, uses 1 to 1024 bit keys (eg. + * strlen($key) <= 128), however, we only use the first 128 bytes if $key + * has more then 128 bytes in it, and set $key to a single null byte if + * it is empty. + * + * If the key is not explicitly set, it'll be assumed to be a single + * null byte. + * + * @see Crypt_Base::setKey() + * @access public + * @param String $key + * @param Integer $t1 optional Effective key length in bits. + */ + function setKey($key, $t1 = 0) + { + if ($t1 <= 0) { + $t1 = $this->default_key_length; + } else if ($t1 > 1024) { + $t1 = 1024; + } + // Key byte count should be 1..128. + $key = strlen($key) ? substr($key, 0, 128): "\x00"; + $t = strlen($key); + + // The mcrypt RC2 implementation only supports effective key length + // of 1024 bits. It is however possible to handle effective key + // lengths in range 1..1024 by expanding the key and applying + // inverse pitable mapping to the first byte before submitting it + // to mcrypt. + + // Key expansion. + $l = array_values(unpack('C*', $key)); + $t8 = ($t1 + 7) >> 3; + $tm = 0xFF >> (8 * $t8 - $t1); + + // Expand key. + $pitable = $this->pitable; + for ($i = $t; $i < 128; $i++) { + $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]]; + } + $i = 128 - $t8; + $l[$i] = $pitable[$l[$i] & $tm]; + while ($i--) { + $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]]; + } + + // Prepare the key for mcrypt. + $l[0] = $this->invpitable[$l[0]]; + array_unshift($l, 'C*'); + parent::setKey(call_user_func_array('pack', $l)); + } + + /** + * Encrypts a block + * + * @see Crypt_Base::_encryptBlock() + * @see Crypt_Base::encrypt() + * @access private + * @param String $in + * @return String + */ + function _encryptBlock($in) + { + list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); + $keys = $this->keys; + $limit = 20; + $actions = array($limit => 44, 44 => 64); + $j = 0; + + for (;;) { + // Mixing round. + $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; + $r0 |= $r0 >> 16; + $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; + $r1 |= $r1 >> 16; + $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; + $r2 |= $r2 >> 16; + $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; + $r3 |= $r3 >> 16; + + if ($j == $limit) { + if ($limit == 64) { + break; + } + + // Mashing round. + $r0 += $keys[$r3 & 0x3F]; + $r1 += $keys[$r0 & 0x3F]; + $r2 += $keys[$r1 & 0x3F]; + $r3 += $keys[$r2 & 0x3F]; + $limit = $actions[$limit]; + } + } + + return pack('vvvv', $r0, $r1, $r2, $r3); + } + + /** + * Decrypts a block + * + * @see Crypt_Base::_decryptBlock() + * @see Crypt_Base::decrypt() + * @access private + * @param String $in + * @return String + */ + function _decryptBlock($in) + { + list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); + $keys = $this->keys; + $limit = 44; + $actions = array($limit => 20, 20 => 0); + $j = 64; + + for (;;) { + // R-mixing round. + $r3 = ($r3 | ($r3 << 16)) >> 5; + $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; + $r2 = ($r2 | ($r2 << 16)) >> 3; + $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; + $r1 = ($r1 | ($r1 << 16)) >> 2; + $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; + $r0 = ($r0 | ($r0 << 16)) >> 1; + $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF; + + if ($j == $limit) { + if (!$limit) { + break; + } + + // R-mashing round. + $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; + $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; + $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; + $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF; + $limit = $actions[$limit]; + } + } + + return pack('vvvv', $r0, $r1, $r2, $r3); + } + + /** + * Creates the key schedule + * + * @see Crypt_Base::_setupKey() + * @access private + */ + function _setupKey() + { + // Key has already been expanded in Crypt_RC2::setKey(): + // Only the first value must be altered. + $l = unpack('Ca/Cb/v*', $this->key); + array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8)); + unset($l['a']); + unset($l['b']); + $this->keys = $l; + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see Crypt_Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions = &Crypt_RC2::_getLambdaFunctions(); + + // The first 10 generated $lambda_functions will use the $keys hardcoded as integers + // for the mixing rounds, for better inline crypt performance [~20% faster]. + // But for memory reason we have to limit those ultra-optimized $lambda_functions to an amount of 10. + $keys = $this->keys; + if (count($lambda_functions) >= 10) { + foreach ($this->keys as $k => $v) { + $keys[$k] = '$keys[' . $k . ']'; + } + } + + $code_hash = md5(str_pad("Crypt_RC2, {$this->mode}, ", 32, "\0") . implode(',', $keys)); + + // Is there a re-usable $lambda_functions in there? + // If not, we have to create it. + if (!isset($lambda_functions[$code_hash])) { + // Init code for both, encrypt and decrypt. + $init_crypt = '$keys = $self->keys;'; + + // $in is the current 8 bytes block which has to be en/decrypt + $encrypt_block = $decrypt_block = ' + $in = unpack("v4", $in); + $r0 = $in[1]; + $r1 = $in[2]; + $r2 = $in[3]; + $r3 = $in[4]; + '; + + // Create code for encryption. + $limit = 20; + $actions = array($limit => 44, 44 => 64); + $j = 0; + + for (;;) { + // Mixing round. + $encrypt_block .= ' + $r0 = (($r0 + ' . $keys[$j++] . ' + + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; + $r0 |= $r0 >> 16; + $r1 = (($r1 + ' . $keys[$j++] . ' + + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; + $r1 |= $r1 >> 16; + $r2 = (($r2 + ' . $keys[$j++] . ' + + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; + $r2 |= $r2 >> 16; + $r3 = (($r3 + ' . $keys[$j++] . ' + + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; + $r3 |= $r3 >> 16;'; + + if ($j == $limit) { + if ($limit == 64) { + break; + } + + // Mashing round. + $encrypt_block .= ' + $r0 += $keys[$r3 & 0x3F]; + $r1 += $keys[$r0 & 0x3F]; + $r2 += $keys[$r1 & 0x3F]; + $r3 += $keys[$r2 & 0x3F];'; + $limit = $actions[$limit]; + } + } + + $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; + + // Create code for decryption. + $limit = 44; + $actions = array($limit => 20, 20 => 0); + $j = 64; + + for (;;) { + // R-mixing round. + $decrypt_block .= ' + $r3 = ($r3 | ($r3 << 16)) >> 5; + $r3 = ($r3 - ' . $keys[--$j] . ' - + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; + $r2 = ($r2 | ($r2 << 16)) >> 3; + $r2 = ($r2 - ' . $keys[--$j] . ' - + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; + $r1 = ($r1 | ($r1 << 16)) >> 2; + $r1 = ($r1 - ' . $keys[--$j] . ' - + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; + $r0 = ($r0 | ($r0 << 16)) >> 1; + $r0 = ($r0 - ' . $keys[--$j] . ' - + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;'; + + if ($j == $limit) { + if (!$limit) { + break; + } + + // R-mashing round. + $decrypt_block .= ' + $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; + $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; + $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; + $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;'; + $limit = $actions[$limit]; + } + } + + $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; + + // Creates the inline-crypt function + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + + // Set the inline-crypt function as callback in: $this->inline_crypt + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php index 390108e048..f6a9eae2fb 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RC4.php @@ -14,7 +14,7 @@ * - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4} * * RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not - * ARCFOUR or ARC4 because RC4 is how it is refered to in the SSH1 specification. + * ARCFOUR or ARC4 because RC4 is how it is referred to in the SSH1 specification. * * Here's a short example of how to use this library: * @@ -41,10 +41,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -58,10 +58,18 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ +/** + * Include Crypt_Base + * + * Base cipher class + */ +if (!class_exists('Crypt_Base')) { + require_once('Base.php'); +} + /**#@+ * @access private * @see Crypt_RC4::Crypt_RC4() @@ -69,11 +77,11 @@ /** * Toggles the internal implementation */ -define('CRYPT_RC4_MODE_INTERNAL', 1); +define('CRYPT_RC4_MODE_INTERNAL', CRYPT_MODE_INTERNAL); /** * Toggles the mcrypt implementation */ -define('CRYPT_RC4_MODE_MCRYPT', 2); +define('CRYPT_RC4_MODE_MCRYPT', CRYPT_MODE_MCRYPT); /**#@-*/ /**#@+ @@ -92,7 +100,57 @@ define('CRYPT_RC4_DECRYPT', 1); * @access public * @package Crypt_RC4 */ -class Crypt_RC4 { +class Crypt_RC4 extends Crypt_Base { + /** + * Block Length of the cipher + * + * RC4 is a stream cipher + * so we the block_size to 0 + * + * @see Crypt_Base::block_size + * @var Integer + * @access private + */ + var $block_size = 0; + + /** + * The default password key_size used by setPassword() + * + * @see Crypt_Base::password_key_size + * @see Crypt_Base::setPassword() + * @var Integer + * @access private + */ + var $password_key_size = 128; // = 1024 bits + + /** + * The namespace used by the cipher for its constants. + * + * @see Crypt_Base::const_namespace + * @var String + * @access private + */ + var $const_namespace = 'RC4'; + + + /** + * The mcrypt specific name of the cipher + * + * @see Crypt_Base::cipher_name_mcrypt + * @var String + * @access private + */ + var $cipher_name_mcrypt = 'arcfour'; + + /** + * Holds whether performance-optimized $inline_crypt() can/should be used. + * + * @see Crypt_Base::inline_crypt + * @var mixed + * @access private + */ + var $use_inline_crypt = false; // currently not available + /** * The Key * @@ -103,190 +161,26 @@ class Crypt_RC4 { var $key = "\0"; /** - * The Key Stream for encryption - * - * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object + * The Key Stream for decryption and encryption * * @see Crypt_RC4::setKey() * @var Array * @access private */ - var $encryptStream = false; - - /** - * The Key Stream for decryption - * - * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object - * - * @see Crypt_RC4::setKey() - * @var Array - * @access private - */ - var $decryptStream = false; - - /** - * The $i and $j indexes for encryption - * - * @see Crypt_RC4::_crypt() - * @var Integer - * @access private - */ - var $encryptIndex = 0; - - /** - * The $i and $j indexes for decryption - * - * @see Crypt_RC4::_crypt() - * @var Integer - * @access private - */ - var $decryptIndex = 0; - - /** - * The Encryption Algorithm - * - * Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR. - * - * @see Crypt_RC4::Crypt_RC4() - * @var Integer - * @access private - */ - var $mode; - - /** - * Continuous Buffer status - * - * @see Crypt_RC4::enableContinuousBuffer() - * @var Boolean - * @access private - */ - var $continuousBuffer = false; + var $stream; /** * Default Constructor. * * Determines whether or not the mcrypt extension should be used. * + * @see Crypt_Base::Crypt_Base() * @return Crypt_RC4 * @access public */ function Crypt_RC4() { - if ( !defined('CRYPT_RC4_MODE') ) { - switch (true) { - case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')) && in_array('arcfour', mcrypt_list_algorithms()): - define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT); - break; - default: - define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL); - } - } - - switch ( CRYPT_RC4_MODE ) { - case CRYPT_RC4_MODE_MCRYPT: - switch (true) { - case defined('MCRYPT_ARCFOUR'): - $this->mode = MCRYPT_ARCFOUR; - break; - case defined('MCRYPT_RC4'); - $this->mode = MCRYPT_RC4; - } - $this->encryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, ''); - $this->decryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, ''); - - } - } - - /** - * Sets the key. - * - * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will - * be used. If no key is explicitly set, it'll be assumed to be a single null byte. - * - * @access public - * @param String $key - */ - function setKey($key) - { - $this->key = $key; - - if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { - mcrypt_generic_init($this->encryptStream, $this->key, ''); - mcrypt_generic_init($this->decryptStream, $this->key, ''); - return; - } - - $keyLength = strlen($key); - $keyStream = array(); - for ($i = 0; $i < 256; $i++) { - $keyStream[$i] = $i; - } - $j = 0; - for ($i = 0; $i < 256; $i++) { - $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; - $temp = $keyStream[$i]; - $keyStream[$i] = $keyStream[$j]; - $keyStream[$j] = $temp; - } - - $this->encryptIndex = $this->decryptIndex = array(0, 0); - $this->encryptStream = $this->decryptStream = $keyStream; - } - - /** - * Sets the password. - * - * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: - * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: - * $hash, $salt, $count, $dkLen - * - * @param String $password - * @param optional String $method - * @access public - */ - function setPassword($password, $method = 'pbkdf2') - { - $key = ''; - - switch ($method) { - default: // 'pbkdf2' - list(, , $hash, $salt, $count) = func_get_args(); - if (!isset($hash)) { - $hash = 'sha1'; - } - // WPA and WPA2 use the SSID as the salt - if (!isset($salt)) { - $salt = 'phpseclib/salt'; - } - // RFC2898#section-4.2 uses 1,000 iterations by default - // WPA and WPA2 use 4,096. - if (!isset($count)) { - $count = 1000; - } - if (!isset($dkLen)) { - $dkLen = 128; - } - - if (!class_exists('Crypt_Hash')) { - require_once('Crypt/Hash.php'); - } - - $i = 1; - while (strlen($key) < $dkLen) { - //$dk.= $this->_pbkdf($password, $salt, $count, $i++); - $hmac = new Crypt_Hash(); - $hmac->setHash($hash); - $hmac->setKey($password); - $f = $u = $hmac->hash($salt . pack('N', $i++)); - for ($j = 2; $j <= $count; $j++) { - $u = $hmac->hash($u); - $f^= $u; - } - $key.= $f; - } - } - - $this->setKey(substr($key, 0, $dkLen)); + parent::Crypt_Base(CRYPT_MODE_STREAM); } /** @@ -312,15 +206,35 @@ class Crypt_RC4 { { } + /** + * Sets the key. + * + * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will + * be used. If no key is explicitly set, it'll be assumed to be a single null byte. + * + * @access public + * @see Crypt_Base::setKey() + * @param String $key + */ + function setKey($key) + { + parent::setKey(substr($key, 0, 256)); + } + /** * Encrypts a message. * + * @see Crypt_Base::decrypt() * @see Crypt_RC4::_crypt() * @access public * @param String $plaintext + * @return String $ciphertext */ function encrypt($plaintext) { + if ($this->engine == CRYPT_MODE_MCRYPT) { + return parent::encrypt($plaintext); + } return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT); } @@ -330,15 +244,51 @@ class Crypt_RC4 { * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). * Atleast if the continuous buffer is disabled. * + * @see Crypt_Base::encrypt() * @see Crypt_RC4::_crypt() * @access public * @param String $ciphertext + * @return String $plaintext */ function decrypt($ciphertext) { + if ($this->engine == CRYPT_MODE_MCRYPT) { + return parent::decrypt($ciphertext); + } return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT); } + + /** + * Setup the key (expansion) + * + * @see Crypt_Base::_setupKey() + * @access private + */ + function _setupKey() + { + $key = $this->key; + $keyLength = strlen($key); + $keyStream = array(); + for ($i = 0; $i < 256; $i++) { + $keyStream[$i] = $i; + } + $j = 0; + for ($i = 0; $i < 256; $i++) { + $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; + $temp = $keyStream[$i]; + $keyStream[$i] = $keyStream[$j]; + $keyStream[$j] = $temp; + } + + $this->stream = array(); + $this->stream[CRYPT_RC4_DECRYPT] = $this->stream[CRYPT_RC4_ENCRYPT] = array( + 0, // index $i + 0, // index $j + $keyStream + ); + } + /** * Encrypts or decrypts a message. * @@ -347,173 +297,41 @@ class Crypt_RC4 { * @access private * @param String $text * @param Integer $mode + * @return String $text */ function _crypt($text, $mode) { - if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { - $keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream'; - - if (!$this->continuousBuffer) { - mcrypt_generic_init($this->$keyStream, $this->key, ''); - } - - return mcrypt_generic($this->$keyStream, $text); - } - - if ($this->encryptStream === false) { - $this->setKey($this->key); - } - - switch ($mode) { - case CRYPT_RC4_ENCRYPT: - $keyStream = $this->encryptStream; - list($i, $j) = $this->encryptIndex; - break; - case CRYPT_RC4_DECRYPT: - $keyStream = $this->decryptStream; - list($i, $j) = $this->decryptIndex; - } - - $newText = ''; - for ($k = 0; $k < strlen($text); $k++) { - $i = ($i + 1) & 255; - $j = ($j + $keyStream[$i]) & 255; - $temp = $keyStream[$i]; - $keyStream[$i] = $keyStream[$j]; - $keyStream[$j] = $temp; - $temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255]; - $newText.= chr(ord($text[$k]) ^ $temp); + if ($this->changed) { + $this->_setup(); + $this->changed = false; } + $stream = &$this->stream[$mode]; if ($this->continuousBuffer) { - switch ($mode) { - case CRYPT_RC4_ENCRYPT: - $this->encryptStream = $keyStream; - $this->encryptIndex = array($i, $j); - break; - case CRYPT_RC4_DECRYPT: - $this->decryptStream = $keyStream; - $this->decryptIndex = array($i, $j); - } + $i = &$stream[0]; + $j = &$stream[1]; + $keyStream = &$stream[2]; + } else { + $i = $stream[0]; + $j = $stream[1]; + $keyStream = $stream[2]; } - return $newText; - } + $len = strlen($text); + for ($k = 0; $k < $len; ++$k) { + $i = ($i + 1) & 255; + $ksi = $keyStream[$i]; + $j = ($j + $ksi) & 255; + $ksj = $keyStream[$j]; - /** - * Treat consecutive "packets" as if they are a continuous buffer. - * - * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets - * will yield different outputs: - * - * - * echo $rc4->encrypt(substr($plaintext, 0, 8)); - * echo $rc4->encrypt(substr($plaintext, 8, 8)); - * - * - * echo $rc4->encrypt($plaintext); - * - * - * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates - * another, as demonstrated with the following: - * - * - * $rc4->encrypt(substr($plaintext, 0, 8)); - * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8))); - * - * - * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8))); - * - * - * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different - * outputs. The reason is due to the fact that the initialization vector's change after every encryption / - * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. - * - * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each - * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that - * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), - * however, they are also less intuitive and more likely to cause you problems. - * - * @see Crypt_RC4::disableContinuousBuffer() - * @access public - */ - function enableContinuousBuffer() - { - if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { - mcrypt_generic_init($this->encryptStream, $this->key, ''); - mcrypt_generic_init($this->decryptStream, $this->key, ''); + $keyStream[$i] = $ksj; + $keyStream[$j] = $ksi; + $text[$k] = chr(ord($text[$k]) ^ $keyStream[($ksj + $ksi) & 255]); } - $this->continuousBuffer = true; - } - - /** - * Treat consecutive packets as if they are a discontinuous buffer. - * - * The default behavior. - * - * @see Crypt_RC4::enableContinuousBuffer() - * @access public - */ - function disableContinuousBuffer() - { - if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) { - $this->encryptIndex = $this->decryptIndex = array(0, 0); - $this->encryptStream = $this->decryptStream = false; - } - - $this->continuousBuffer = false; - } - - /** - * Dummy function. - * - * Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is - * included is so that you can switch between a block cipher and a stream cipher transparently. - * - * @see Crypt_RC4::disablePadding() - * @access public - */ - function enablePadding() - { - } - - /** - * Dummy function. - * - * @see Crypt_RC4::enablePadding() - * @access public - */ - function disablePadding() - { - } - - /** - * Class destructor. - * - * Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really - * needs to be called if mcrypt is being used. - * - * @access public - */ - function __destruct() - { - if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { - $this->_closeMCrypt(); - } - } - - /** - * Properly close the MCrypt objects. - * - * @access prviate - */ - function _closeMCrypt() - { - mcrypt_module_close($this->encryptStream); - mcrypt_module_close($this->decryptStream); + return $text; } } // vim: ts=4:sw=4:et: -// vim6: fdl=1: \ No newline at end of file +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php index db1ba1581b..92ebfdf723 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/RSA.php @@ -65,17 +65,9 @@ * @author Jim Wigginton * @copyright MMIX Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: RSA.php,v 1.19 2010/09/12 21:58:54 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ -/** - * Include Math_BigInteger - */ -if (!class_exists('Math_BigInteger')) { - require_once('Math/BigInteger.php'); -} - /** * Include Crypt_Random */ @@ -83,15 +75,15 @@ if (!class_exists('Math_BigInteger')) { // will trigger a call to __autoload() if you're wanting to auto-load classes // call function_exists() a second time to stop the require_once from being called outside // of the auto loader -if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) { - require_once('Crypt/Random.php'); +if (!function_exists('crypt_random_string')) { + require_once('Random.php'); } /** * Include Crypt_Hash */ if (!class_exists('Crypt_Hash')) { - require_once('Crypt/Hash.php'); + require_once('Hash.php'); } /**#@+ @@ -181,7 +173,6 @@ define('CRYPT_RSA_MODE_OPENSSL', 2); */ define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf'); - /**#@+ * @access public * @see Crypt_RSA::createKey() @@ -449,6 +440,14 @@ class Crypt_RSA { */ var $configFile; + /** + * Public key comment field. + * + * @var String + * @access private + */ + var $comment = 'phpseclib-generated-key'; + /** * The constructor * @@ -461,22 +460,54 @@ class Crypt_RSA { */ function Crypt_RSA() { + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + $this->configFile = CRYPT_RSA_OPENSSL_CONFIG; if ( !defined('CRYPT_RSA_MODE') ) { - switch (true) { - case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='): - define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL); + // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, + // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger + // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. + if ( defined('MATH_BIGINTEGER_OPENSSL_DISABLE') ) { + define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); + } + + switch ( !defined('CRYPT_RSA_MODE') ) { // ie. only run this if the above didn't set CRYPT_RSA_MODE already + case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile): + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + ob_start(); + phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + $versions = array(); + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL); + break; + default: + define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); + define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } break; - default: + case true: define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); } } - if (!defined('CRYPT_RSA_COMMENT')) { - define('CRYPT_RSA_COMMENT', 'phpseclib-generated-key'); - } - $this->zero = new Math_BigInteger(); $this->one = new Math_BigInteger(1); @@ -642,12 +673,12 @@ class Crypt_RSA { $exponents[$i] = $e->modInverse($temp); } - list($lcm) = $lcm['top']->divide($lcm['bottom']); - $gcd = $lcm->gcd($e); + list($temp) = $lcm['top']->divide($lcm['bottom']); + $gcd = $temp->gcd($e); $i0 = 1; } while (!$gcd->equals($this->one)); - $d = $e->modInverse($lcm); + $d = $e->modInverse($temp); $coefficients[2] = $primes[2]->modInverse($primes[1]); @@ -720,16 +751,16 @@ class Crypt_RSA { $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; $key.= $encryption; - $key.= "\r\nComment: " . CRYPT_RSA_COMMENT . "\r\n"; + $key.= "\r\nComment: " . $this->comment . "\r\n"; $public = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus'] ); $source = pack('Na*Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption, - strlen(CRYPT_RSA_COMMENT), CRYPT_RSA_COMMENT, strlen($public), $public + strlen($this->comment), $this->comment, strlen($public), $public ); $public = base64_encode($public); - $key.= "Public-Lines: " . ((strlen($public) + 32) >> 6) . "\r\n"; + $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; $key.= chunk_split($public, 64); $private = pack('Na*Na*Na*Na*', strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'], @@ -760,7 +791,7 @@ class Crypt_RSA { } $private = base64_encode($private); - $key.= 'Private-Lines: ' . ((strlen($private) + 32) >> 6) . "\r\n"; + $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n"; $key.= chunk_split($private, 64); if (!class_exists('Crypt_Hash')) { require_once('Crypt/Hash.php'); @@ -853,7 +884,7 @@ class Crypt_RSA { // mpint e // mpint n $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); - $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . CRYPT_RSA_COMMENT; + $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment; return $RSAPublicKey; default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1 @@ -960,13 +991,19 @@ class Crypt_RSA { if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { $iv = pack('H*', trim($matches[2])); $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key - $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); + $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); $ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-| #s', '', $key); $ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false; if ($ciphertext === false) { $ciphertext = $key; } switch ($matches[1]) { + case 'AES-256-CBC': + if (!class_exists('Crypt_AES')) { + require_once('Crypt/AES.php'); + } + $crypto = new Crypt_AES(); + break; case 'AES-128-CBC': if (!class_exists('Crypt_AES')) { require_once('Crypt/AES.php'); @@ -984,6 +1021,7 @@ class Crypt_RSA { if (!class_exists('Crypt_TripleDES')) { require_once('Crypt/TripleDES.php'); } + $symkey = substr($symkey, 0, 24); $crypto = new Crypt_TripleDES(); break; case 'DES-CBC': @@ -1121,11 +1159,15 @@ class Crypt_RSA { return $components; case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH: - $key = base64_decode(preg_replace('#^ssh-rsa | .+$#', '', $key)); + $parts = explode(' ', $key, 3); + + $key = isset($parts[1]) ? base64_decode($parts[1]) : false; if ($key === false) { return false; } + $comment = isset($parts[2]) ? $parts[2] : false; + $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; if (strlen($key) <= 4) { @@ -1147,12 +1189,14 @@ class Crypt_RSA { $realModulus = new Math_BigInteger($this->_string_shift($key, $length), -256); return strlen($key) ? false : array( 'modulus' => $realModulus, - 'publicExponent' => $modulus + 'publicExponent' => $modulus, + 'comment' => $comment ); } else { return strlen($key) ? false : array( 'modulus' => $modulus, - 'publicExponent' => $publicExponent + 'publicExponent' => $publicExponent, + 'comment' => $comment ); } // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue @@ -1180,6 +1224,7 @@ class Crypt_RSA { return false; } $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); + $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); @@ -1297,9 +1342,6 @@ class Crypt_RSA { break; case 'D': $this->current = &$this->components['privateExponent']; - break; - default: - unset($this->current); } $this->current = ''; } @@ -1315,11 +1357,10 @@ class Crypt_RSA { */ function _stop_element_handler($parser, $name) { - //$name = strtoupper($name); - if ($name == 'RSAKEYVALUE') { - return; + if (isset($this->current)) { + $this->current = new Math_BigInteger(base64_decode($this->current), 256); + unset($this->current); } - $this->current = new Math_BigInteger(base64_decode($this->current), 256); } /** @@ -1350,6 +1391,53 @@ class Crypt_RSA { */ function loadKey($key, $type = false) { + if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') { + $this->privateKeyFormat = $key->privateKeyFormat; + $this->publicKeyFormat = $key->publicKeyFormat; + $this->k = $key->k; + $this->hLen = $key->hLen; + $this->sLen = $key->sLen; + $this->mgfHLen = $key->mgfHLen; + $this->encryptionMode = $key->encryptionMode; + $this->signatureMode = $key->signatureMode; + $this->password = $key->password; + $this->configFile = $key->configFile; + $this->comment = $key->comment; + + if (is_object($key->hash)) { + $this->hash = new Crypt_Hash($key->hash->getHash()); + } + if (is_object($key->mgfHash)) { + $this->mgfHash = new Crypt_Hash($key->mgfHash->getHash()); + } + + if (is_object($key->modulus)) { + $this->modulus = $key->modulus->copy(); + } + if (is_object($key->exponent)) { + $this->exponent = $key->exponent->copy(); + } + if (is_object($key->publicExponent)) { + $this->publicExponent = $key->publicExponent->copy(); + } + + $this->primes = array(); + $this->exponents = array(); + $this->coefficients = array(); + + foreach ($this->primes as $prime) { + $this->primes[] = $prime->copy(); + } + foreach ($this->exponents as $exponent) { + $this->exponents[] = $exponent->copy(); + } + foreach ($this->coefficients as $coefficient) { + $this->coefficients[] = $coefficient->copy(); + } + + return true; + } + if ($type === false) { $types = array( CRYPT_RSA_PUBLIC_FORMAT_RAW, @@ -1373,6 +1461,9 @@ class Crypt_RSA { return false; } + if (isset($components['comment']) && $components['comment'] !== false) { + $this->comment = $components['comment']; + } $this->modulus = $components['modulus']; $this->k = strlen($this->modulus->toBytes()); $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; @@ -1428,6 +1519,11 @@ class Crypt_RSA { */ function setPublicKey($key = false, $type = false) { + // if a public key has already been loaded return false + if (!empty($this->publicExponent)) { + return false; + } + if ($key === false && !empty($this->modulus)) { $this->publicExponent = $this->exponent; return true; @@ -1552,6 +1648,18 @@ class Crypt_RSA { return $key !== false ? $key : ''; } + /** + * __clone() magic method + * + * @access public + */ + function __clone() + { + $key = new Crypt_RSA(); + $key->loadKey($this); + return $key; + } + /** * Generates the smallest and largest numbers requiring $bits bits * @@ -1582,7 +1690,7 @@ class Crypt_RSA { * DER-decode the length * * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See - * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 § 8.1.3} for more information. + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private * @param String $string @@ -1603,7 +1711,7 @@ class Crypt_RSA { * DER-encode the length * * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See - * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 § 8.1.3} for more information. + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private * @param Integer $length @@ -1872,7 +1980,7 @@ class Crypt_RSA { * * Protects against a particular type of timing attack described. * - * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don’t use MessageDigest.isEquals)} + * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)} * * Thanks for the heads up singpolyma! * @@ -2135,6 +2243,7 @@ class Crypt_RSA { } // EME-PKCS1-v1_5 encoding + $psLen = $this->k - $mLen - 3; $ps = ''; while (strlen($ps) != $psLen) { @@ -2142,7 +2251,14 @@ class Crypt_RSA { $temp = str_replace("\x00", '', $temp); $ps.= $temp; } - $em = chr(0) . chr(2) . $ps . chr(0) . $m; + $type = 2; + // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done + if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { + $type = 1; + // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF" + $ps = str_repeat("\xFF", $psLen); + } + $em = chr(0) . chr($type) . $ps . chr(0) . $m; // RSA encryption $m = $this->_os2ip($em); @@ -2515,6 +2631,28 @@ class Crypt_RSA { $this->signatureMode = $mode; } + /** + * Set public key comment. + * + * @access public + * @param String $comment + */ + function setComment($comment) + { + $this->comment = $comment; + } + + /** + * Get public key comment. + * + * @access public + * @return String + */ + function getComment() + { + return $this->comment; + } + /** * Encryption * diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php index a60857df95..8532aab5a5 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Random.php @@ -21,10 +21,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -38,10 +38,16 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ +/** + * "Is Windows" test + * + * @access private + */ +define('CRYPT_RANDOM_IS_WINDOWS', strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); + /** * Generate a random string. * @@ -53,9 +59,9 @@ * @return String * @access public */ -function crypt_random_string($length) { - // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster - if ((PHP_OS & "\xDF\xDF\xDF") === 'WIN') { +function crypt_random_string($length) +{ + if (CRYPT_RANDOM_IS_WINDOWS) { // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call. // ie. class_alias is a function that was introduced in PHP 5.3 if (function_exists('mcrypt_create_iv') && function_exists('class_alias')) { @@ -143,7 +149,7 @@ function crypt_random_string($length) { serialize($_POST) . serialize($_GET) . serialize($_COOKIE) . - serialize($_GLOBAL) . + serialize($GLOBALS) . serialize($_SESSION) . serialize($_OLD_SESSION) )); diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php index 335d5233c4..c63e0ff7e3 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Rijndael.php @@ -4,25 +4,26 @@ /** * Pure-PHP implementation of Rijndael. * - * Does not use mcrypt, even when available, for reasons that are explained below. + * Uses mcrypt, if available/possible, and an internal implementation, otherwise. * * PHP versions 4 and 5 * - * If {@link Crypt_Rijndael::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If - * {@link Crypt_Rijndael::setKeyLength() setKeyLength()} isn't called, it'll be calculated from - * {@link Crypt_Rijndael::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's - * 136-bits it'll be null-padded to 160-bits and 160 bits will be the key length until + * If {@link Crypt_Rijndael::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If + * {@link Crypt_Rijndael::setKeyLength() setKeyLength()} isn't called, it'll be calculated from + * {@link Crypt_Rijndael::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's + * 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until * {@link Crypt_Rijndael::setKey() setKey()} is called, again, at which point, it'll be recalculated. * * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example, * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256. * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224 - * are first defined as valid key / block lengths in - * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}: + * are first defined as valid key / block lengths in + * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}: * Extensions: Other block and Cipher Key lengths. + * Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224). * - * {@internal The variable names are the same as those in + * {@internal The variable names are the same as those in * {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}} * * Here's a short example of how to use this library: @@ -50,10 +51,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -67,10 +68,18 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: Rijndael.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ +/** + * Include Crypt_Base + * + * Base cipher class + */ +if (!class_exists('Crypt_Base')) { + require_once('Base.php'); +} + /**#@+ * @access public * @see Crypt_Rijndael::encrypt() @@ -83,31 +92,31 @@ * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 */ -define('CRYPT_RIJNDAEL_MODE_CTR', -1); +define('CRYPT_RIJNDAEL_MODE_CTR', CRYPT_MODE_CTR); /** * Encrypt / decrypt using the Electronic Code Book mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 */ -define('CRYPT_RIJNDAEL_MODE_ECB', 1); +define('CRYPT_RIJNDAEL_MODE_ECB', CRYPT_MODE_ECB); /** * Encrypt / decrypt using the Code Book Chaining mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 */ -define('CRYPT_RIJNDAEL_MODE_CBC', 2); +define('CRYPT_RIJNDAEL_MODE_CBC', CRYPT_MODE_CBC); /** * Encrypt / decrypt using the Cipher Feedback mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 */ -define('CRYPT_RIJNDAEL_MODE_CFB', 3); +define('CRYPT_RIJNDAEL_MODE_CFB', CRYPT_MODE_CFB); /** * Encrypt / decrypt using the Cipher Feedback mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 */ -define('CRYPT_RIJNDAEL_MODE_OFB', 4); +define('CRYPT_RIJNDAEL_MODE_OFB', CRYPT_MODE_OFB); /**#@-*/ /**#@+ @@ -117,11 +126,11 @@ define('CRYPT_RIJNDAEL_MODE_OFB', 4); /** * Toggles the internal implementation */ -define('CRYPT_RIJNDAEL_MODE_INTERNAL', 1); +define('CRYPT_RIJNDAEL_MODE_INTERNAL', CRYPT_MODE_INTERNAL); /** * Toggles the mcrypt implementation */ -define('CRYPT_RIJNDAEL_MODE_MCRYPT', 2); +define('CRYPT_RIJNDAEL_MODE_MCRYPT', CRYPT_MODE_MCRYPT); /**#@-*/ /** @@ -132,80 +141,51 @@ define('CRYPT_RIJNDAEL_MODE_MCRYPT', 2); * @access public * @package Crypt_Rijndael */ -class Crypt_Rijndael { +class Crypt_Rijndael extends Crypt_Base { /** - * The Encryption Mode + * The default password key_size used by setPassword() * - * @see Crypt_Rijndael::Crypt_Rijndael() + * @see Crypt_Base::password_key_size + * @see Crypt_Base::setPassword() * @var Integer * @access private */ - var $mode; + var $password_key_size = 16; /** - * The Key + * The namespace used by the cipher for its constants. * - * @see Crypt_Rijndael::setKey() + * @see Crypt_Base::const_namespace * @var String * @access private */ - var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + var $const_namespace = 'RIJNDAEL'; /** - * The Initialization Vector + * The mcrypt specific name of the cipher * - * @see Crypt_Rijndael::setIV() + * Mcrypt is useable for 128/192/256-bit $block_size/$key_size. For 160/224 not. + * Crypt_Rijndael determines automatically whether mcrypt is useable + * or not for the current $block_size/$key_size. + * In case of, $cipher_name_mcrypt will be set dynamicaly at run time accordingly. + * + * @see Crypt_Base::cipher_name_mcrypt + * @see Crypt_Base::engine + * @see _setupEngine() * @var String * @access private */ - var $iv = ''; + var $cipher_name_mcrypt = 'rijndael-128'; /** - * A "sliding" Initialization Vector + * The default salt used by setPassword() * - * @see Crypt_Rijndael::enableContinuousBuffer() + * @see Crypt_Base::password_default_salt + * @see Crypt_Base::setPassword() * @var String * @access private */ - var $encryptIV = ''; - - /** - * A "sliding" Initialization Vector - * - * @see Crypt_Rijndael::enableContinuousBuffer() - * @var String - * @access private - */ - var $decryptIV = ''; - - /** - * Continuous Buffer status - * - * @see Crypt_Rijndael::enableContinuousBuffer() - * @var Boolean - * @access private - */ - var $continuousBuffer = false; - - /** - * Padding status - * - * @see Crypt_Rijndael::enablePadding() - * @var Boolean - * @access private - */ - var $padding = true; - - /** - * Does the key schedule need to be (re)calculated? - * - * @see setKey() - * @see setBlockLength() - * @see setKeyLength() - * @var Boolean - * @access private - */ - var $changed = true; + var $password_default_salt = 'phpseclib'; /** * Has the key length explicitly been set or should it be derived from the key, itself? @@ -234,25 +214,14 @@ class Crypt_Rijndael { */ var $dw; - /** - * The Block Length - * - * @see setBlockLength() - * @var Integer - * @access private - * @internal The max value is 32, the min value is 16. All valid values are multiples of 4. Exists in conjunction with - * $Nb because we need this value and not $Nb to pad strings appropriately. - */ - var $block_size = 16; - /** * The Block Length divided by 32 * * @see setBlockLength() * @var Integer * @access private - * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size - * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could + * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size + * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once. * @@ -265,8 +234,8 @@ class Crypt_Rijndael { * @see setKeyLength() * @var Integer * @access private - * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $key_size - * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could + * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk + * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could * derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once. */ @@ -300,224 +269,439 @@ class Crypt_Rijndael { var $c; /** - * Precomputed mixColumns table + * Holds the last used key- and block_size information * - * @see Crypt_Rijndael() * @var Array * @access private */ - var $t0; + var $kl; /** * Precomputed mixColumns table * - * @see Crypt_Rijndael() + * According to (section 5.2.1), + * precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so + * those are the names we'll use. + * + * @see Crypt_Rijndael:_encryptBlock() + * @see Crypt_Rijndael:_decryptBlock() * @var Array * @access private */ - var $t1; + var $t0 = array( + 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, + 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, + 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, + 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, + 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, + 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, + 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, + 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, + 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, + 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, + 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, + 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, + 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, + 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, + 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, + 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, + 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, + 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, + 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, + 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, + 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, + 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, + 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, + 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, + 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, + 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, + 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, + 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, + 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, + 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, + 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, + 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A + ); /** * Precomputed mixColumns table * - * @see Crypt_Rijndael() + * @see Crypt_Rijndael:_encryptBlock() + * @see Crypt_Rijndael:_decryptBlock() * @var Array * @access private */ - var $t2; + var $t1 = array( + 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, + 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, + 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, + 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, + 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, + 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, + 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, + 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, + 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, + 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, + 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, + 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, + 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, + 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, + 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, + 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, + 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, + 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, + 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, + 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, + 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, + 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, + 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, + 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, + 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, + 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, + 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, + 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, + 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, + 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, + 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, + 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616 + ); /** * Precomputed mixColumns table * - * @see Crypt_Rijndael() + * @see Crypt_Rijndael:_encryptBlock() + * @see Crypt_Rijndael:_decryptBlock() * @var Array * @access private */ - var $t3; + var $t2 = array( + 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, + 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, + 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, + 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, + 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, + 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, + 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, + 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, + 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, + 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, + 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, + 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, + 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, + 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, + 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, + 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, + 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, + 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, + 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, + 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, + 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, + 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, + 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, + 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, + 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, + 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, + 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, + 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, + 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, + 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, + 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, + 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16 + ); + + /** + * Precomputed mixColumns table + * + * @see Crypt_Rijndael:_encryptBlock() + * @see Crypt_Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $t3 = array( + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, + 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, + 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, + 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, + 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, + 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, + 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, + 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, + 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, + 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, + 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, + 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, + 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, + 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, + 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, + 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, + 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, + 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C + ); /** * Precomputed invMixColumns table * - * @see Crypt_Rijndael() + * @see Crypt_Rijndael:_encryptBlock() + * @see Crypt_Rijndael:_decryptBlock() * @var Array * @access private */ - var $dt0; + var $dt0 = array( + 0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, 0x1F9D45F1, 0xACFA58AB, 0x4BE30393, + 0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25, 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F, + 0xDEB15A49, 0x25BA1B67, 0x45EA0E98, 0x5DFEC0E1, 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6, + 0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3, 0x49E06929, 0x8EC9C844, + 0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD, 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4, + 0x63DF4A18, 0xE51A3182, 0x97513360, 0x62537F45, 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94, + 0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2, 0xE31F8F57, 0x6655AB2A, + 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C, + 0x8ACF1C2B, 0xA779B492, 0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A, + 0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA, 0x5E719F06, 0xBD6E1051, + 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, 0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF, + 0x1998FB24, 0xD6BDE997, 0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB, + 0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, 0x09808683, 0x322BED48, 0x1E1170AC, 0x6C5A724E, + 0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927, 0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A, + 0x0C0A67B1, 0x9357E70F, 0xB4EE96D2, 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16, + 0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, 0xF28BC7AD, 0x2DB6A8B9, 0x141EA9C8, + 0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD, 0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34, + 0x8B432976, 0xCB23C6DC, 0xB6EDFC68, 0xB8E4F163, 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120, + 0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3, 0x0D8652EC, 0x77C1E3D0, + 0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422, 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF, + 0x87494EC7, 0xD938D1C1, 0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4, + 0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8, 0x2E39F75E, 0x82C3AFF5, + 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B, + 0xCD267809, 0x6E5918F4, 0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6, + 0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331, 0xC6A59430, 0x35A266C0, + 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, 0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F, + 0x764DD68D, 0x43EFB04D, 0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F, + 0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, 0xB3671D5A, 0x92DBD252, 0xE9105633, 0x6DD64713, + 0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89, 0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C, + 0x9CD2DF59, 0x55F2733F, 0x1814CE79, 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86, + 0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, 0xBCE2250C, 0x283C498B, 0xFF0D9541, + 0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190, 0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742 + ); /** * Precomputed invMixColumns table * - * @see Crypt_Rijndael() + * @see Crypt_Rijndael:_encryptBlock() + * @see Crypt_Rijndael:_decryptBlock() * @var Array * @access private */ - var $dt1; + var $dt1 = array( + 0x5051F4A7, 0x537E4165, 0xC31A17A4, 0x963A275E, 0xCB3BAB6B, 0xF11F9D45, 0xABACFA58, 0x934BE303, + 0x552030FA, 0xF6AD766D, 0x9188CC76, 0x25F5024C, 0xFC4FE5D7, 0xD7C52ACB, 0x80263544, 0x8FB562A3, + 0x49DEB15A, 0x6725BA1B, 0x9845EA0E, 0xE15DFEC0, 0x02C32F75, 0x12814CF0, 0xA38D4697, 0xC66BD3F9, + 0xE7038F5F, 0x9515929C, 0xEBBF6D7A, 0xDA955259, 0x2DD4BE83, 0xD3587421, 0x2949E069, 0x448EC9C8, + 0x6A75C289, 0x78F48E79, 0x6B99583E, 0xDD27B971, 0xB6BEE14F, 0x17F088AD, 0x66C920AC, 0xB47DCE3A, + 0x1863DF4A, 0x82E51A31, 0x60975133, 0x4562537F, 0xE0B16477, 0x84BB6BAE, 0x1CFE81A0, 0x94F9082B, + 0x58704868, 0x198F45FD, 0x8794DE6C, 0xB7527BF8, 0x23AB73D3, 0xE2724B02, 0x57E31F8F, 0x2A6655AB, + 0x07B2EB28, 0x032FB5C2, 0x9A86C57B, 0xA5D33708, 0xF2302887, 0xB223BFA5, 0xBA02036A, 0x5CED1682, + 0x2B8ACF1C, 0x92A779B4, 0xF0F307F2, 0xA14E69E2, 0xCD65DAF4, 0xD50605BE, 0x1FD13462, 0x8AC4A6FE, + 0x9D342E53, 0xA0A2F355, 0x32058AE1, 0x75A4F6EB, 0x390B83EC, 0xAA4060EF, 0x065E719F, 0x51BD6E10, + 0xF93E218A, 0x3D96DD06, 0xAEDD3E05, 0x464DE6BD, 0xB591548D, 0x0571C45D, 0x6F0406D4, 0xFF605015, + 0x241998FB, 0x97D6BDE9, 0xCC894043, 0x7767D99E, 0xBDB0E842, 0x8807898B, 0x38E7195B, 0xDB79C8EE, + 0x47A17C0A, 0xE97C420F, 0xC9F8841E, 0x00000000, 0x83098086, 0x48322BED, 0xAC1E1170, 0x4E6C5A72, + 0xFBFD0EFF, 0x560F8538, 0x1E3DAED5, 0x27362D39, 0x640A0FD9, 0x21685CA6, 0xD19B5B54, 0x3A24362E, + 0xB10C0A67, 0x0F9357E7, 0xD2B4EE96, 0x9E1B9B91, 0x4F80C0C5, 0xA261DC20, 0x695A774B, 0x161C121A, + 0x0AE293BA, 0xE5C0A02A, 0x433C22E0, 0x1D121B17, 0x0B0E090D, 0xADF28BC7, 0xB92DB6A8, 0xC8141EA9, + 0x8557F119, 0x4CAF7507, 0xBBEE99DD, 0xFDA37F60, 0x9FF70126, 0xBC5C72F5, 0xC544663B, 0x345BFB7E, + 0x768B4329, 0xDCCB23C6, 0x68B6EDFC, 0x63B8E4F1, 0xCAD731DC, 0x10426385, 0x40139722, 0x2084C611, + 0x7D854A24, 0xF8D2BB3D, 0x11AEF932, 0x6DC729A1, 0x4B1D9E2F, 0xF3DCB230, 0xEC0D8652, 0xD077C1E3, + 0x6C2BB316, 0x99A970B9, 0xFA119448, 0x2247E964, 0xC4A8FC8C, 0x1AA0F03F, 0xD8567D2C, 0xEF223390, + 0xC787494E, 0xC1D938D1, 0xFE8CCAA2, 0x3698D40B, 0xCFA6F581, 0x28A57ADE, 0x26DAB78E, 0xA43FADBF, + 0xE42C3A9D, 0x0D507892, 0x9B6A5FCC, 0x62547E46, 0xC2F68D13, 0xE890D8B8, 0x5E2E39F7, 0xF582C3AF, + 0xBE9F5D80, 0x7C69D093, 0xA96FD52D, 0xB3CF2512, 0x3BC8AC99, 0xA710187D, 0x6EE89C63, 0x7BDB3BBB, + 0x09CD2678, 0xF46E5918, 0x01EC9AB7, 0xA8834F9A, 0x65E6956E, 0x7EAAFFE6, 0x0821BCCF, 0xE6EF15E8, + 0xD9BAE79B, 0xCE4A6F36, 0xD4EA9F09, 0xD629B07C, 0xAF31A4B2, 0x312A3F23, 0x30C6A594, 0xC035A266, + 0x37744EBC, 0xA6FC82CA, 0xB0E090D0, 0x1533A7D8, 0x4AF10498, 0xF741ECDA, 0x0E7FCD50, 0x2F1791F6, + 0x8D764DD6, 0x4D43EFB0, 0x54CCAA4D, 0xDFE49604, 0xE39ED1B5, 0x1B4C6A88, 0xB8C12C1F, 0x7F466551, + 0x049D5EEA, 0x5D018C35, 0x73FA8774, 0x2EFB0B41, 0x5AB3671D, 0x5292DBD2, 0x33E91056, 0x136DD647, + 0x8C9AD761, 0x7A37A10C, 0x8E59F814, 0x89EB133C, 0xEECEA927, 0x35B761C9, 0xEDE11CE5, 0x3C7A47B1, + 0x599CD2DF, 0x3F55F273, 0x791814CE, 0xBF73C737, 0xEA53F7CD, 0x5B5FFDAA, 0x14DF3D6F, 0x867844DB, + 0x81CAAFF3, 0x3EB968C4, 0x2C382434, 0x5FC2A340, 0x72161DC3, 0x0CBCE225, 0x8B283C49, 0x41FF0D95, + 0x7139A801, 0xDE080CB3, 0x9CD8B4E4, 0x906456C1, 0x617BCB84, 0x70D532B6, 0x74486C5C, 0x42D0B857 + ); /** * Precomputed invMixColumns table * - * @see Crypt_Rijndael() + * @see Crypt_Rijndael:_encryptBlock() + * @see Crypt_Rijndael:_decryptBlock() * @var Array * @access private */ - var $dt2; + var $dt2 = array( + 0xA75051F4, 0x65537E41, 0xA4C31A17, 0x5E963A27, 0x6BCB3BAB, 0x45F11F9D, 0x58ABACFA, 0x03934BE3, + 0xFA552030, 0x6DF6AD76, 0x769188CC, 0x4C25F502, 0xD7FC4FE5, 0xCBD7C52A, 0x44802635, 0xA38FB562, + 0x5A49DEB1, 0x1B6725BA, 0x0E9845EA, 0xC0E15DFE, 0x7502C32F, 0xF012814C, 0x97A38D46, 0xF9C66BD3, + 0x5FE7038F, 0x9C951592, 0x7AEBBF6D, 0x59DA9552, 0x832DD4BE, 0x21D35874, 0x692949E0, 0xC8448EC9, + 0x896A75C2, 0x7978F48E, 0x3E6B9958, 0x71DD27B9, 0x4FB6BEE1, 0xAD17F088, 0xAC66C920, 0x3AB47DCE, + 0x4A1863DF, 0x3182E51A, 0x33609751, 0x7F456253, 0x77E0B164, 0xAE84BB6B, 0xA01CFE81, 0x2B94F908, + 0x68587048, 0xFD198F45, 0x6C8794DE, 0xF8B7527B, 0xD323AB73, 0x02E2724B, 0x8F57E31F, 0xAB2A6655, + 0x2807B2EB, 0xC2032FB5, 0x7B9A86C5, 0x08A5D337, 0x87F23028, 0xA5B223BF, 0x6ABA0203, 0x825CED16, + 0x1C2B8ACF, 0xB492A779, 0xF2F0F307, 0xE2A14E69, 0xF4CD65DA, 0xBED50605, 0x621FD134, 0xFE8AC4A6, + 0x539D342E, 0x55A0A2F3, 0xE132058A, 0xEB75A4F6, 0xEC390B83, 0xEFAA4060, 0x9F065E71, 0x1051BD6E, + 0x8AF93E21, 0x063D96DD, 0x05AEDD3E, 0xBD464DE6, 0x8DB59154, 0x5D0571C4, 0xD46F0406, 0x15FF6050, + 0xFB241998, 0xE997D6BD, 0x43CC8940, 0x9E7767D9, 0x42BDB0E8, 0x8B880789, 0x5B38E719, 0xEEDB79C8, + 0x0A47A17C, 0x0FE97C42, 0x1EC9F884, 0x00000000, 0x86830980, 0xED48322B, 0x70AC1E11, 0x724E6C5A, + 0xFFFBFD0E, 0x38560F85, 0xD51E3DAE, 0x3927362D, 0xD9640A0F, 0xA621685C, 0x54D19B5B, 0x2E3A2436, + 0x67B10C0A, 0xE70F9357, 0x96D2B4EE, 0x919E1B9B, 0xC54F80C0, 0x20A261DC, 0x4B695A77, 0x1A161C12, + 0xBA0AE293, 0x2AE5C0A0, 0xE0433C22, 0x171D121B, 0x0D0B0E09, 0xC7ADF28B, 0xA8B92DB6, 0xA9C8141E, + 0x198557F1, 0x074CAF75, 0xDDBBEE99, 0x60FDA37F, 0x269FF701, 0xF5BC5C72, 0x3BC54466, 0x7E345BFB, + 0x29768B43, 0xC6DCCB23, 0xFC68B6ED, 0xF163B8E4, 0xDCCAD731, 0x85104263, 0x22401397, 0x112084C6, + 0x247D854A, 0x3DF8D2BB, 0x3211AEF9, 0xA16DC729, 0x2F4B1D9E, 0x30F3DCB2, 0x52EC0D86, 0xE3D077C1, + 0x166C2BB3, 0xB999A970, 0x48FA1194, 0x642247E9, 0x8CC4A8FC, 0x3F1AA0F0, 0x2CD8567D, 0x90EF2233, + 0x4EC78749, 0xD1C1D938, 0xA2FE8CCA, 0x0B3698D4, 0x81CFA6F5, 0xDE28A57A, 0x8E26DAB7, 0xBFA43FAD, + 0x9DE42C3A, 0x920D5078, 0xCC9B6A5F, 0x4662547E, 0x13C2F68D, 0xB8E890D8, 0xF75E2E39, 0xAFF582C3, + 0x80BE9F5D, 0x937C69D0, 0x2DA96FD5, 0x12B3CF25, 0x993BC8AC, 0x7DA71018, 0x636EE89C, 0xBB7BDB3B, + 0x7809CD26, 0x18F46E59, 0xB701EC9A, 0x9AA8834F, 0x6E65E695, 0xE67EAAFF, 0xCF0821BC, 0xE8E6EF15, + 0x9BD9BAE7, 0x36CE4A6F, 0x09D4EA9F, 0x7CD629B0, 0xB2AF31A4, 0x23312A3F, 0x9430C6A5, 0x66C035A2, + 0xBC37744E, 0xCAA6FC82, 0xD0B0E090, 0xD81533A7, 0x984AF104, 0xDAF741EC, 0x500E7FCD, 0xF62F1791, + 0xD68D764D, 0xB04D43EF, 0x4D54CCAA, 0x04DFE496, 0xB5E39ED1, 0x881B4C6A, 0x1FB8C12C, 0x517F4665, + 0xEA049D5E, 0x355D018C, 0x7473FA87, 0x412EFB0B, 0x1D5AB367, 0xD25292DB, 0x5633E910, 0x47136DD6, + 0x618C9AD7, 0x0C7A37A1, 0x148E59F8, 0x3C89EB13, 0x27EECEA9, 0xC935B761, 0xE5EDE11C, 0xB13C7A47, + 0xDF599CD2, 0x733F55F2, 0xCE791814, 0x37BF73C7, 0xCDEA53F7, 0xAA5B5FFD, 0x6F14DF3D, 0xDB867844, + 0xF381CAAF, 0xC43EB968, 0x342C3824, 0x405FC2A3, 0xC372161D, 0x250CBCE2, 0x498B283C, 0x9541FF0D, + 0x017139A8, 0xB3DE080C, 0xE49CD8B4, 0xC1906456, 0x84617BCB, 0xB670D532, 0x5C74486C, 0x5742D0B8 + ); /** * Precomputed invMixColumns table * - * @see Crypt_Rijndael() + * @see Crypt_Rijndael:_encryptBlock() + * @see Crypt_Rijndael:_decryptBlock() * @var Array * @access private */ - var $dt3; + var $dt3 = array( + 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, + 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, + 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, + 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, + 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, + 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, + 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, + 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, + 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, + 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, + 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, + 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, + 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, + 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, + 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, + 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, + 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, + 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, + 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, + 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, + 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, + 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, + 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, + 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, + 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, + 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, + 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, + 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, + 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, + 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, + 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, + 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 + ); /** - * Is the mode one that is paddable? + * The SubByte S-Box * - * @see Crypt_Rijndael::Crypt_Rijndael() - * @var Boolean + * @see Crypt_Rijndael::_encryptBlock() + * @var Array * @access private */ - var $paddable = false; + var $sbox = array( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 + ); /** - * Encryption buffer for CTR, OFB and CFB modes + * The inverse SubByte S-Box * - * @see Crypt_Rijndael::encrypt() - * @var String + * @see Crypt_Rijndael::_decryptBlock() + * @var Array * @access private */ - var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0); - - /** - * Decryption buffer for CTR, OFB and CFB modes - * - * @see Crypt_Rijndael::decrypt() - * @var String - * @access private - */ - var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0); + var $isbox = array( + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D + ); /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be - * CRYPT_RIJNDAEL_MODE_ECB or CRYPT_RIJNDAEL_MODE_CBC. If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used. + * Determines whether or not the mcrypt extension should be used. * + * $mode could be: + * + * - CRYPT_RIJNDAEL_MODE_ECB + * + * - CRYPT_RIJNDAEL_MODE_CBC + * + * - CRYPT_RIJNDAEL_MODE_CTR + * + * - CRYPT_RIJNDAEL_MODE_CFB + * + * - CRYPT_RIJNDAEL_MODE_OFB + * + * If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used. + * + * @see Crypt_Base::Crypt_Base() * @param optional Integer $mode - * @return Crypt_Rijndael * @access public */ function Crypt_Rijndael($mode = CRYPT_RIJNDAEL_MODE_CBC) { - switch ($mode) { - case CRYPT_RIJNDAEL_MODE_ECB: - case CRYPT_RIJNDAEL_MODE_CBC: - $this->paddable = true; - $this->mode = $mode; - break; - case CRYPT_RIJNDAEL_MODE_CTR: - case CRYPT_RIJNDAEL_MODE_CFB: - case CRYPT_RIJNDAEL_MODE_OFB: - $this->mode = $mode; - break; - default: - $this->paddable = true; - $this->mode = CRYPT_RIJNDAEL_MODE_CBC; - } - - $t3 = &$this->t3; - $t2 = &$this->t2; - $t1 = &$this->t1; - $t0 = &$this->t0; - - $dt3 = &$this->dt3; - $dt2 = &$this->dt2; - $dt1 = &$this->dt1; - $dt0 = &$this->dt0; - - // according to (section 5.2.1), - // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so - // those are the names we'll use. - $t3 = array( - 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, - 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, - 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, - 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, - 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, - 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, - 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, - 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, - 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, - 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, - 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, - 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, - 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, - 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, - 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, - 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, - 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, - 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, - 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, - 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, - 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, - 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, - 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, - 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, - 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, - 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, - 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, - 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, - 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, - 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, - 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, - 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C - ); - - $dt3 = array( - 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, - 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, - 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, - 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, - 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, - 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, - 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, - 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, - 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, - 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, - 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, - 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, - 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, - 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, - 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, - 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, - 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, - 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, - 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, - 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, - 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, - 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, - 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, - 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, - 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, - 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, - 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, - 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, - 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, - 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, - 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, - 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 - ); - - for ($i = 0; $i < 256; $i++) { - $t2[] = (($t3[$i] << 8) & 0xFFFFFF00) | (($t3[$i] >> 24) & 0x000000FF); - $t1[] = (($t3[$i] << 16) & 0xFFFF0000) | (($t3[$i] >> 16) & 0x0000FFFF); - $t0[] = (($t3[$i] << 24) & 0xFF000000) | (($t3[$i] >> 8) & 0x00FFFFFF); - - $dt2[] = (($dt3[$i] << 8) & 0xFFFFFF00) | (($dt3[$i] >> 24) & 0x000000FF); - $dt1[] = (($dt3[$i] << 16) & 0xFFFF0000) | (($dt3[$i] >> 16) & 0x0000FFFF); - $dt0[] = (($dt3[$i] << 24) & 0xFF000000) | (($dt3[$i] >> 8) & 0x00FFFFFF); - } + parent::Crypt_Base($mode); } /** @@ -530,112 +714,82 @@ class Crypt_Rijndael { * * If the key is not explicitly set, it'll be assumed to be all null bytes. * + * Note: 160/224-bit keys must explicitly set by setKeyLength(), otherwise they will be round/pad up to 192/256 bits. + * + * @see Crypt_Base:setKey() + * @see setKeyLength() * @access public * @param String $key */ function setKey($key) { - $this->key = $key; - $this->changed = true; - } + parent::setKey($key); - /** - * Sets the initialization vector. (optional) - * - * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed - * to be all zero's. - * - * @access public - * @param String $iv - */ - function setIV($iv) - { - $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, $this->block_size), $this->block_size, chr(0)); + if (!$this->explicit_key_length) { + $length = strlen($key); + switch (true) { + case $length <= 16: + $this->key_size = 16; + break; + case $length <= 24: + $this->key_size = 24; + break; + default: + $this->key_size = 32; + } + $this->_setupEngine(); + } } /** * Sets the key length * * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to - * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount. + * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * + * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined + * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to + * 192/256 bits as, for example, mcrypt will do. + * + * That said, if you want be compatible with other Rijndael and AES implementations, + * you should not setKeyLength(160) or setKeyLength(224). + * + * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use + * the mcrypt php extention, even if available. + * This results then in slower encryption. * * @access public * @param Integer $length */ function setKeyLength($length) { - $length >>= 5; - if ($length > 8) { - $length = 8; - } else if ($length < 4) { - $length = 4; + switch (true) { + case $length == 160: + $this->key_size = 20; + break; + case $length == 224: + $this->key_size = 28; + break; + case $length <= 128: + $this->key_size = 16; + break; + case $length <= 192: + $this->key_size = 24; + break; + default: + $this->key_size = 32; } - $this->Nk = $length; - $this->key_size = $length << 2; $this->explicit_key_length = true; $this->changed = true; - } - - /** - * Sets the password. - * - * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: - * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: - * $hash, $salt, $method - * Set $dkLen by calling setKeyLength() - * - * @param String $password - * @param optional String $method - * @access public - */ - function setPassword($password, $method = 'pbkdf2') - { - $key = ''; - - switch ($method) { - default: // 'pbkdf2' - list(, , $hash, $salt, $count) = func_get_args(); - if (!isset($hash)) { - $hash = 'sha1'; - } - // WPA and WPA2 use the SSID as the salt - if (!isset($salt)) { - $salt = 'phpseclib'; - } - // RFC2898#section-4.2 uses 1,000 iterations by default - // WPA and WPA2 use 4,096. - if (!isset($count)) { - $count = 1000; - } - - if (!class_exists('Crypt_Hash')) { - require_once('Crypt/Hash.php'); - } - - $i = 1; - while (strlen($key) < $this->key_size) { // $dkLen == $this->key_size - //$dk.= $this->_pbkdf($password, $salt, $count, $i++); - $hmac = new Crypt_Hash(); - $hmac->setHash($hash); - $hmac->setKey($password); - $f = $u = $hmac->hash($salt . pack('N', $i++)); - for ($j = 2; $j <= $count; $j++) { - $u = $hmac->hash($u); - $f^= $u; - } - $key.= $f; - } - } - - $this->setKey(substr($key, 0, $this->key_size)); + $this->_setupEngine(); } /** * Sets the block length * * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to - * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount. + * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. * * @access public * @param Integer $length @@ -651,314 +805,76 @@ class Crypt_Rijndael { $this->Nb = $length; $this->block_size = $length << 2; $this->changed = true; + $this->_setupEngine(); } /** - * Generate CTR XOR encryption key + * Setup the fastest possible $engine * - * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the - * plaintext / ciphertext in CTR mode. + * Determines if the mcrypt (MODE_MCRYPT) $engine available + * and usable for the current $block_size and $key_size. * - * @see Crypt_Rijndael::decrypt() - * @see Crypt_Rijndael::encrypt() - * @access public - * @param Integer $length - * @param String $iv + * If not, the slower MODE_INTERNAL $engine will be set. + * + * @see setKey() + * @see setKeyLength() + * @see setBlockLength() + * @access private */ - function _generate_xor($length, &$iv) + function _setupEngine() { - $xor = ''; - $block_size = $this->block_size; - $num_blocks = floor(($length + ($block_size - 1)) / $block_size); - for ($i = 0; $i < $num_blocks; $i++) { - $xor.= $iv; - for ($j = 4; $j <= $block_size; $j+=4) { - $temp = substr($iv, -$j, 4); - switch ($temp) { - case "\xFF\xFF\xFF\xFF": - $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); - break; - case "\x7F\xFF\xFF\xFF": - $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); - break 2; - default: - extract(unpack('Ncount', $temp)); - $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); - break 2; - } + if (constant('CRYPT_' . $this->const_namespace . '_MODE') == CRYPT_MODE_INTERNAL) { + // No mcrypt support at all for rijndael + return; + } + + // The required mcrypt module name for the current $block_size of rijndael + $cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); + + // Determining the availibility/usability of $cipher_name_mcrypt + switch (true) { + case $this->key_size % 8: // mcrypt is not usable for 160/224-bit keys, only for 128/192/256-bit keys + case !in_array($cipher_name_mcrypt, mcrypt_list_algorithms()): // $cipher_name_mcrypt is not available for the current $block_size + $engine = CRYPT_MODE_INTERNAL; + break; + default: + $engine = CRYPT_MODE_MCRYPT; + } + + if ($this->engine == $engine && $this->cipher_name_mcrypt == $cipher_name_mcrypt) { + // allready set, so we not unnecessary close $this->enmcrypt/demcrypt/ecb + return; + } + + // Set the $engine + $this->engine = $engine; + $this->cipher_name_mcrypt = $cipher_name_mcrypt; + + if ($this->enmcrypt) { + // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, + // (re)open them with the module named in $this->cipher_name_mcrypt + mcrypt_module_close($this->enmcrypt); + mcrypt_module_close($this->demcrypt); + $this->enmcrypt = null; + $this->demcrypt = null; + + if ($this->ecb) { + mcrypt_module_close($this->ecb); + $this->ecb = null; } } - - return $xor; } /** - * Encrypts a message. + * Setup the CRYPT_MODE_MCRYPT $engine * - * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other Rjindael - * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's - * necessary are discussed in the following - * URL: - * - * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} - * - * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. - * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that - * length. - * - * @see Crypt_Rijndael::decrypt() - * @access public - * @param String $plaintext + * @see Crypt_Base::_setupMcrypt() + * @access private */ - function encrypt($plaintext) + function _setupMcrypt() { - $this->_setup(); - if ($this->paddable) { - $plaintext = $this->_pad($plaintext); - } - - $block_size = $this->block_size; - $buffer = &$this->enbuffer; - $ciphertext = ''; - switch ($this->mode) { - case CRYPT_RIJNDAEL_MODE_ECB: - for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { - $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size)); - } - break; - case CRYPT_RIJNDAEL_MODE_CBC: - $xor = $this->encryptIV; - for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { - $block = substr($plaintext, $i, $block_size); - $block = $this->_encryptBlock($block ^ $xor); - $xor = $block; - $ciphertext.= $block; - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - } - break; - case CRYPT_RIJNDAEL_MODE_CTR: - $xor = $this->encryptIV; - if (strlen($buffer['encrypted'])) { - for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { - $block = substr($plaintext, $i, $block_size); - $buffer['encrypted'].= $this->_encryptBlock($this->_generate_xor($block_size, $xor)); - $key = $this->_string_shift($buffer['encrypted'], $block_size); - $ciphertext.= $block ^ $key; - } - } else { - for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { - $block = substr($plaintext, $i, $block_size); - $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor)); - $ciphertext.= $block ^ $key; - } - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - if ($start = strlen($plaintext) % $block_size) { - $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; - } - } - break; - case CRYPT_RIJNDAEL_MODE_CFB: - // cfb loosely routines inspired by openssl's: - // http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1 - if ($this->continuousBuffer) { - $iv = &$this->encryptIV; - $pos = &$buffer['pos']; - } else { - $iv = $this->encryptIV; - $pos = 0; - } - $len = strlen($plaintext); - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = $block_size - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize - $ciphertext = substr($iv, $orig_pos) ^ $plaintext; - $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); - } - while ($len >= $block_size) { - $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); - $ciphertext.= $iv; - $len-= $block_size; - $i+= $block_size; - } - if ($len) { - $iv = $this->_encryptBlock($iv); - $block = $iv ^ substr($plaintext, $i); - $iv = substr_replace($iv, $block, 0, $len); - $ciphertext.= $block; - $pos = $len; - } - break; - case CRYPT_RIJNDAEL_MODE_OFB: - $xor = $this->encryptIV; - if (strlen($buffer['xor'])) { - for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { - $xor = $this->_encryptBlock($xor); - $buffer['xor'].= $xor; - $key = $this->_string_shift($buffer['xor'], $block_size); - $ciphertext.= substr($plaintext, $i, $block_size) ^ $key; - } - } else { - for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { - $xor = $this->_encryptBlock($xor); - $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor; - } - $key = $xor; - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - if ($start = strlen($plaintext) % $block_size) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; - } - } - } - - return $ciphertext; - } - - /** - * Decrypts a message. - * - * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until - * it is. - * - * @see Crypt_Rijndael::encrypt() - * @access public - * @param String $ciphertext - */ - function decrypt($ciphertext) - { - $this->_setup(); - - if ($this->paddable) { - // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0)); - } - - $block_size = $this->block_size; - $buffer = &$this->debuffer; - $plaintext = ''; - switch ($this->mode) { - case CRYPT_RIJNDAEL_MODE_ECB: - for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { - $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size)); - } - break; - case CRYPT_RIJNDAEL_MODE_CBC: - $xor = $this->decryptIV; - for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { - $block = substr($ciphertext, $i, $block_size); - $plaintext.= $this->_decryptBlock($block) ^ $xor; - $xor = $block; - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - } - break; - case CRYPT_RIJNDAEL_MODE_CTR: - $xor = $this->decryptIV; - if (strlen($buffer['ciphertext'])) { - for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { - $block = substr($ciphertext, $i, $block_size); - $buffer['ciphertext'].= $this->_encryptBlock($this->_generate_xor($block_size, $xor)); - $key = $this->_string_shift($buffer['ciphertext'], $block_size); - $plaintext.= $block ^ $key; - } - } else { - for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { - $block = substr($ciphertext, $i, $block_size); - $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor)); - $plaintext.= $block ^ $key; - } - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - if ($start = strlen($ciphertext) % $block_size) { - $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; - } - } - break; - case CRYPT_RIJNDAEL_MODE_CFB: - if ($this->continuousBuffer) { - $iv = &$this->decryptIV; - $pos = &$buffer['pos']; - } else { - $iv = $this->decryptIV; - $pos = 0; - } - $len = strlen($ciphertext); - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = $block_size - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize - $plaintext = substr($iv, $orig_pos) ^ $ciphertext; - $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); - } - while ($len >= $block_size) { - $iv = $this->_encryptBlock($iv); - $cb = substr($ciphertext, $i, $block_size); - $plaintext.= $iv ^ $cb; - $iv = $cb; - $len-= $block_size; - $i+= $block_size; - } - if ($len) { - $iv = $this->_encryptBlock($iv); - $plaintext.= $iv ^ substr($ciphertext, $i); - $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len); - $pos = $len; - } - break; - case CRYPT_RIJNDAEL_MODE_OFB: - $xor = $this->decryptIV; - if (strlen($buffer['xor'])) { - for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { - $xor = $this->_encryptBlock($xor); - $buffer['xor'].= $xor; - $key = $this->_string_shift($buffer['xor'], $block_size); - $plaintext.= substr($ciphertext, $i, $block_size) ^ $key; - } - } else { - for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { - $xor = $this->_encryptBlock($xor); - $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor; - } - $key = $xor; - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - if ($start = strlen($ciphertext) % $block_size) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; - } - } - } - - return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, "\0"); + parent::_setupMcrypt(); } /** @@ -970,17 +886,24 @@ class Crypt_Rijndael { */ function _encryptBlock($in) { - $state = array(); - $words = unpack('N*word', $in); + static $t0, $t1, $t2, $t3, $sbox; + if (!$t0) { + for ($i = 0; $i < 256; ++$i) { + $t0[] = (int)$this->t0[$i]; + $t1[] = (int)$this->t1[$i]; + $t2[] = (int)$this->t2[$i]; + $t3[] = (int)$this->t3[$i]; + $sbox[] = (int)$this->sbox[$i]; + } + } + $state = array(); + $words = unpack('N*', $in); + + $c = $this->c; $w = $this->w; - $t0 = $this->t0; - $t1 = $this->t1; - $t2 = $this->t2; - $t3 = $this->t3; $Nb = $this->Nb; $Nr = $this->Nr; - $c = $this->c; // addRoundKey $i = -1; @@ -988,11 +911,11 @@ class Crypt_Rijndael { $state[] = $word ^ $w[0][++$i]; } - // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - - // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding + // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - + // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf. // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization. - // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1], + // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1], // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well. // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf @@ -1019,7 +942,10 @@ class Crypt_Rijndael { // subWord for ($i = 0; $i < $Nb; ++$i) { - $state[$i] = $this->_subWord($state[$i]); + $state[$i] = $sbox[$state[$i] & 0x000000FF] | + ($sbox[$state[$i] >> 8 & 0x000000FF] << 8) | + ($sbox[$state[$i] >> 16 & 0x000000FF] << 16) | + ($sbox[$state[$i] >> 24 & 0x000000FF] << 24); } // shiftRows + addRoundKey @@ -1039,7 +965,6 @@ class Crypt_Rijndael { $l = ($l + 1) % $Nb; } - // 100% ugly switch/case code... but ~5% faster (meaning: ~half second faster de/encrypting 1MB text, tested with php5.4.9 on linux/32bit with an AMD Athlon II P360 CPU) then the commented smart code below. Don't know it's worth or not switch ($Nb) { case 8: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]); @@ -1052,13 +977,6 @@ class Crypt_Rijndael { default: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]); } - /* - $state = $temp; - - array_unshift($state, 'N*'); - - return call_user_func_array('pack', $state); - */ } /** @@ -1070,17 +988,24 @@ class Crypt_Rijndael { */ function _decryptBlock($in) { - $state = array(); - $words = unpack('N*word', $in); + static $dt0, $dt1, $dt2, $dt3, $isbox; + if (!$dt0) { + for ($i = 0; $i < 256; ++$i) { + $dt0[] = (int)$this->dt0[$i]; + $dt1[] = (int)$this->dt1[$i]; + $dt2[] = (int)$this->dt2[$i]; + $dt3[] = (int)$this->dt3[$i]; + $isbox[] = (int)$this->isbox[$i]; + } + } + $state = array(); + $words = unpack('N*', $in); + + $c = $this->c; $dw = $this->dw; - $dt0 = $this->dt0; - $dt1 = $this->dt1; - $dt2 = $this->dt2; - $dt3 = $this->dt3; $Nb = $this->Nb; $Nr = $this->Nr; - $c = $this->c; // addRoundKey $i = -1; @@ -1116,11 +1041,15 @@ class Crypt_Rijndael { $l = $Nb - $c[3]; while ($i < $Nb) { - $temp[$i] = $dw[0][$i] ^ - $this->_invSubWord(($state[$i] & 0xFF000000) | - ($state[$j] & 0x00FF0000) | - ($state[$k] & 0x0000FF00) | - ($state[$l] & 0x000000FF)); + $word = ($state[$i] & 0xFF000000) | + ($state[$j] & 0x00FF0000) | + ($state[$k] & 0x0000FF00) | + ($state[$l] & 0x000000FF); + + $temp[$i] = $dw[0][$i] ^ ($isbox[$word & 0x000000FF] | + ($isbox[$word >> 8 & 0x000000FF] << 8) | + ($isbox[$word >> 16 & 0x000000FF] << 16) | + ($isbox[$word >> 24 & 0x000000FF] << 24)); ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -1139,24 +1068,15 @@ class Crypt_Rijndael { default: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]); } - /* - $state = $temp; - - array_unshift($state, 'N*'); - - return call_user_func_array('pack', $state); - */ } /** - * Setup Rijndael - * - * Validates all the variables and calculates $Nr - the number of rounds that need to be performed - and $w - the key - * key schedule. + * Setup the key (expansion) * + * @see Crypt_Base::_setupKey() * @access private */ - function _setup() + function _setupKey() { // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field. // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse @@ -1169,25 +1089,15 @@ class Crypt_Rijndael { 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000 ); - if (!$this->changed) { + $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, "\0"); + + if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_size === $this->kl['key_size'] && $this->block_size === $this->kl['block_size']) { + // already expanded return; } + $this->kl = array('key' => $this->key, 'key_size' => $this->key_size, 'block_size' => $this->block_size); - if (!$this->explicit_key_length) { - // we do >> 2, here, and not >> 5, as we do above, since strlen($this->key) tells us the number of bytes - not bits - $length = strlen($this->key) >> 2; - if ($length > 8) { - $length = 8; - } else if ($length < 4) { - $length = 4; - } - $this->Nk = $length; - $this->key_size = $length << 2; - } - - $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0)); - $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, chr(0)); - + $this->Nk = $this->key_size >> 2; // see Rijndael-ammended.pdf#page=44 $this->Nr = max($this->Nk, $this->Nb) + 6; @@ -1208,9 +1118,7 @@ class Crypt_Rijndael { $this->c = array(0, 1, 3, 4); } - $key = $this->key; - - $w = array_values(unpack('N*words', $key)); + $w = array_values(unpack('N*words', $this->key)); $length = $this->Nb * ($this->Nr + 1); for ($i = $this->Nk; $i < $length; $i++) { @@ -1230,12 +1138,12 @@ class Crypt_Rijndael { // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns // and generate the inverse key schedule. more specifically, - // according to (section 5.3.3), + // according to (section 5.3.3), // "The key expansion for the Inverse Cipher is defined as follows: // 1. Apply the Key Expansion. // 2. Apply InvMixColumn to all Round Keys except the first and the last one." // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher" - $temp = array(); + $temp = $this->w = $this->dw = array(); for ($i = $row = $col = 0; $i < $length; $i++, $col++) { if ($col == $this->Nb) { if ($row == 0) { @@ -1245,9 +1153,9 @@ class Crypt_Rijndael { $j = 0; while ($j < $this->Nb) { $dw = $this->_subWord($this->w[$row][$j]); - $temp[$j] = $this->dt0[$dw >> 24 & 0x000000FF] ^ - $this->dt1[$dw >> 16 & 0x000000FF] ^ - $this->dt2[$dw >> 8 & 0x000000FF] ^ + $temp[$j] = $this->dt0[$dw >> 24 & 0x000000FF] ^ + $this->dt1[$dw >> 16 & 0x000000FF] ^ + $this->dt2[$dw >> 8 & 0x000000FF] ^ $this->dt3[$dw & 0x000000FF]; $j++; } @@ -1262,264 +1170,205 @@ class Crypt_Rijndael { $this->dw[$row] = $this->w[$row]; - $this->changed = false; + // In case of $this->use_inline_crypt === true we have to use 1-dim key arrays (both ascending) + if ($this->use_inline_crypt) { + $this->dw = array_reverse($this->dw); + $w = array_pop($this->w); + $dw = array_pop($this->dw); + foreach ($this->w as $r => $wr) { + foreach ($wr as $c => $wc) { + $w[] = $wc; + $dw[] = $this->dw[$r][$c]; + } + } + $this->w = $w; + $this->dw = $dw; + } } /** * Performs S-Box substitutions * * @access private + * @param Integer $word */ function _subWord($word) { - static $sbox0, $sbox1, $sbox2, $sbox3; + $sbox = $this->sbox; - if (empty($sbox0)) { - $sbox0 = array( - 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 + return $sbox[$word & 0x000000FF] | + ($sbox[$word >> 8 & 0x000000FF] << 8) | + ($sbox[$word >> 16 & 0x000000FF] << 16) | + ($sbox[$word >> 24 & 0x000000FF] << 24); + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see Crypt_Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + // Note: _setupInlineCrypt() will be called only if $this->changed === true + // So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt(). + // However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible. + + $lambda_functions =& Crypt_Rijndael::_getLambdaFunctions(); + + // The first 10 generated $lambda_functions will use the key-words hardcoded for better performance. + // For memory reason we limit those ultra-optimized functions. + // After that, we use pure (extracted) integer vars for the key-words which is faster than accessing them via array. + if (count($lambda_functions) < 10) { + $w = $this->w; + $dw = $this->dw; + $init_encrypt = ''; + $init_decrypt = ''; + } else { + for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) { + $w[] = '$w[' . $i . ']'; + $dw[] = '$dw[' . $i . ']'; + } + $init_encrypt = '$w = $self->w;'; + $init_decrypt = '$dw = $self->dw;'; + } + + $code_hash = md5(str_pad("Crypt_Rijndael, {$this->mode}, {$this->block_size}, ", 32, "\0") . implode(',', $w)); + + if (!isset($lambda_functions[$code_hash])) { + $Nr = $this->Nr; + $Nb = $this->Nb; + $c = $this->c; + + // Generating encrypt code: + $init_encrypt.= ' + static $t0, $t1, $t2, $t3, $sbox; + if (!$t0) { + for ($i = 0; $i < 256; ++$i) { + $t0[$i] = (int)$self->t0[$i]; + $t1[$i] = (int)$self->t1[$i]; + $t2[$i] = (int)$self->t2[$i]; + $t3[$i] = (int)$self->t3[$i]; + $sbox[$i] = (int)$self->sbox[$i]; + } + } + '; + + $s = 'e'; + $e = 's'; + $wc = $Nb - 1; + + // Preround: addRoundKey + $encrypt_block = '$in = unpack("N*", $in);'."\n"; + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n"; + } + + // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey + for ($round = 1; $round < $Nr; ++$round) { + list($s, $e) = array($e, $s); + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block.= + '$'.$e.$i.' = + $t0[($'.$s.$i .' >> 24) & 0xff] ^ + $t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^ + $t2[($'.$s.(($i + $c[2]) % $Nb).' >> 8) & 0xff] ^ + $t3[ $'.$s.(($i + $c[3]) % $Nb).' & 0xff] ^ + '.$w[++$wc].";\n"; + } + } + + // Finalround: subWord + shiftRows + addRoundKey + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block.= + '$'.$e.$i.' = + $sbox[ $'.$e.$i.' & 0xff] | + ($sbox[($'.$e.$i.' >> 8) & 0xff] << 8) | + ($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) | + ($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n"; + } + $encrypt_block .= '$in = pack("N*"'."\n"; + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block.= ', + ($'.$e.$i .' & 0xFF000000) ^ + ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000) ^ + ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00) ^ + ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF) ^ + '.$w[$i]."\n"; + } + $encrypt_block .= ');'; + + // Generating decrypt code: + $init_decrypt.= ' + static $dt0, $dt1, $dt2, $dt3, $isbox; + if (!$dt0) { + for ($i = 0; $i < 256; ++$i) { + $dt0[$i] = (int)$self->dt0[$i]; + $dt1[$i] = (int)$self->dt1[$i]; + $dt2[$i] = (int)$self->dt2[$i]; + $dt3[$i] = (int)$self->dt3[$i]; + $isbox[$i] = (int)$self->isbox[$i]; + } + } + '; + + $s = 'e'; + $e = 's'; + $wc = $Nb - 1; + + // Preround: addRoundKey + $decrypt_block = '$in = unpack("N*", $in);'."\n"; + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n"; + } + + // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey + for ($round = 1; $round < $Nr; ++$round) { + list($s, $e) = array($e, $s); + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block.= + '$'.$e.$i.' = + $dt0[($'.$s.$i .' >> 24) & 0xff] ^ + $dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^ + $dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >> 8) & 0xff] ^ + $dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).' & 0xff] ^ + '.$dw[++$wc].";\n"; + } + } + + // Finalround: subWord + shiftRows + addRoundKey + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block.= + '$'.$e.$i.' = + $isbox[ $'.$e.$i.' & 0xff] | + ($isbox[($'.$e.$i.' >> 8) & 0xff] << 8) | + ($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) | + ($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n"; + } + $decrypt_block .= '$in = pack("N*"'."\n"; + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block.= ', + ($'.$e.$i. ' & 0xFF000000) ^ + ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000) ^ + ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00) ^ + ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF) ^ + '.$dw[$i]."\n"; + } + $decrypt_block .= ');'; + + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => '', + 'init_encrypt' => $init_encrypt, + 'init_decrypt' => $init_decrypt, + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) ); - - $sbox1 = array(); - $sbox2 = array(); - $sbox3 = array(); - - for ($i = 0; $i < 256; $i++) { - $sbox1[] = $sbox0[$i] << 8; - $sbox2[] = $sbox0[$i] << 16; - $sbox3[] = $sbox0[$i] << 24; - } } - - return $sbox0[$word & 0x000000FF] | - $sbox1[$word >> 8 & 0x000000FF] | - $sbox2[$word >> 16 & 0x000000FF] | - $sbox3[$word >> 24 & 0x000000FF]; - } - - - /** - * Performs inverse S-Box substitutions - * - * @access private - */ - function _invSubWord($word) - { - static $sbox0, $sbox1, $sbox2, $sbox3; - - if (empty($sbox0)) { - $sbox0 = array( - 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, - 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, - 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, - 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, - 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, - 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, - 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, - 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, - 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, - 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, - 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, - 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, - 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, - 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, - 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D - ); - - $sbox1 = array(); - $sbox2 = array(); - $sbox3 = array(); - - for ($i = 0; $i < 256; $i++) { - $sbox1[] = $sbox0[$i] << 8; - $sbox2[] = $sbox0[$i] << 16; - $sbox3[] = $sbox0[$i] << 24; - } - } - - return $sbox0[$word & 0x000000FF] | - $sbox1[$word >> 8 & 0x000000FF] | - $sbox2[$word >> 16 & 0x000000FF] | - $sbox3[$word >> 24 & 0x000000FF]; - } - - /** - * Pad "packets". - * - * Rijndael works by encrypting between sixteen and thirty-two bytes at a time, provided that number is also a multiple - * of four. If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to - * pad the input so that it is of the proper length. - * - * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH, - * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping - * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is - * transmitted separately) - * - * @see Crypt_Rijndael::disablePadding() - * @access public - */ - function enablePadding() - { - $this->padding = true; - } - - /** - * Do not pad packets. - * - * @see Crypt_Rijndael::enablePadding() - * @access public - */ - function disablePadding() - { - $this->padding = false; - } - - /** - * Pads a string - * - * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize. - * $block_size - (strlen($text) % $block_size) bytes are added, each of which is equal to - * chr($block_size - (strlen($text) % $block_size) - * - * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless - * and padding will, hence forth, be enabled. - * - * @see Crypt_Rijndael::_unpad() - * @access private - */ - function _pad($text) - { - $length = strlen($text); - - if (!$this->padding) { - if ($length % $this->block_size == 0) { - return $text; - } else { - user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); - $this->padding = true; - } - } - - $pad = $this->block_size - ($length % $this->block_size); - - return str_pad($text, $length + $pad, chr($pad)); - } - - /** - * Unpads a string. - * - * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong - * and false will be returned. - * - * @see Crypt_Rijndael::_pad() - * @access private - */ - function _unpad($text) - { - if (!$this->padding) { - return $text; - } - - $length = ord($text[strlen($text) - 1]); - - if (!$length || $length > $this->block_size) { - return false; - } - - return substr($text, 0, -$length); - } - - /** - * Treat consecutive "packets" as if they are a continuous buffer. - * - * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets - * will yield different outputs: - * - * - * echo $rijndael->encrypt(substr($plaintext, 0, 16)); - * echo $rijndael->encrypt(substr($plaintext, 16, 16)); - * - * - * echo $rijndael->encrypt($plaintext); - * - * - * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates - * another, as demonstrated with the following: - * - * - * $rijndael->encrypt(substr($plaintext, 0, 16)); - * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16))); - * - * - * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16))); - * - * - * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different - * outputs. The reason is due to the fact that the initialization vector's change after every encryption / - * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. - * - * Put another way, when the continuous buffer is enabled, the state of the Crypt_Rijndael() object changes after each - * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that - * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), - * however, they are also less intuitive and more likely to cause you problems. - * - * @see Crypt_Rijndael::disableContinuousBuffer() - * @access public - */ - function enableContinuousBuffer() - { - $this->continuousBuffer = true; - } - - /** - * Treat consecutive packets as if they are a discontinuous buffer. - * - * The default behavior. - * - * @see Crypt_Rijndael::enableContinuousBuffer() - * @access public - */ - function disableContinuousBuffer() - { - $this->continuousBuffer = false; - $this->encryptIV = $this->iv; - $this->decryptIV = $this->iv; - $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0); - $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0); - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param String $string - * @param optional Integer $index - * @return String - * @access private - */ - function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; + $this->inline_crypt = $lambda_functions[$code_hash]; } } // vim: ts=4:sw=4:et: -// vim6: fdl=1: \ No newline at end of file +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php index 3b4c8c3622..4030c6c9fb 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/TripleDES.php @@ -33,10 +33,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -50,7 +50,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -81,253 +80,157 @@ define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC); * @author Jim Wigginton * @version 0.1.0 * @access public - * @package Crypt_TerraDES + * @package Crypt_TripleDES */ -class Crypt_TripleDES { +class Crypt_TripleDES extends Crypt_DES { /** - * The Three Keys + * The default password key_size used by setPassword() * - * @see Crypt_TripleDES::setKey() - * @var String - * @access private - */ - var $key = "\0\0\0\0\0\0\0\0"; - - /** - * The Encryption Mode - * - * @see Crypt_TripleDES::Crypt_TripleDES() + * @see Crypt_DES::password_key_size + * @see Crypt_Base::password_key_size + * @see Crypt_Base::setPassword() * @var Integer * @access private */ - var $mode = CRYPT_DES_MODE_CBC; + var $password_key_size = 24; /** - * Continuous Buffer status + * The default salt used by setPassword() + * + * @see Crypt_Base::password_default_salt + * @see Crypt_Base::setPassword() + * @var String + * @access private + */ + var $password_default_salt = 'phpseclib'; + + /** + * The namespace used by the cipher for its constants. + * + * @see Crypt_DES::const_namespace + * @see Crypt_Base::const_namespace + * @var String + * @access private + */ + var $const_namespace = 'DES'; + + /** + * The mcrypt specific name of the cipher + * + * @see Crypt_DES::cipher_name_mcrypt + * @see Crypt_Base::cipher_name_mcrypt + * @var String + * @access private + */ + var $cipher_name_mcrypt = 'tripledes'; + + /** + * Optimizing value while CFB-encrypting + * + * @see Crypt_Base::cfb_init_len + * @var Integer + * @access private + */ + var $cfb_init_len = 750; + + /** + * max possible size of $key + * + * @see Crypt_TripleDES::setKey() + * @see Crypt_DES::setKey() + * @var String + * @access private + */ + var $key_size_max = 24; + + /** + * Internal flag whether using CRYPT_DES_MODE_3CBC or not * - * @see Crypt_TripleDES::enableContinuousBuffer() * @var Boolean * @access private */ - var $continuousBuffer = false; - - /** - * Padding status - * - * @see Crypt_TripleDES::enablePadding() - * @var Boolean - * @access private - */ - var $padding = true; - - /** - * The Initialization Vector - * - * @see Crypt_TripleDES::setIV() - * @var String - * @access private - */ - var $iv = "\0\0\0\0\0\0\0\0"; - - /** - * A "sliding" Initialization Vector - * - * @see Crypt_TripleDES::enableContinuousBuffer() - * @var String - * @access private - */ - var $encryptIV = "\0\0\0\0\0\0\0\0"; - - /** - * A "sliding" Initialization Vector - * - * @see Crypt_TripleDES::enableContinuousBuffer() - * @var String - * @access private - */ - var $decryptIV = "\0\0\0\0\0\0\0\0"; + var $mode_3cbc; /** * The Crypt_DES objects * + * Used only if $mode_3cbc === true + * * @var Array * @access private */ var $des; - /** - * mcrypt resource for encryption - * - * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. - * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. - * - * @see Crypt_TripleDES::encrypt() - * @var String - * @access private - */ - var $enmcrypt; - - /** - * mcrypt resource for decryption - * - * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. - * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. - * - * @see Crypt_TripleDES::decrypt() - * @var String - * @access private - */ - var $demcrypt; - - /** - * Does the enmcrypt resource need to be (re)initialized? - * - * @see Crypt_TripleDES::setKey() - * @see Crypt_TripleDES::setIV() - * @var Boolean - * @access private - */ - var $enchanged = true; - - /** - * Does the demcrypt resource need to be (re)initialized? - * - * @see Crypt_TripleDES::setKey() - * @see Crypt_TripleDES::setIV() - * @var Boolean - * @access private - */ - var $dechanged = true; - - /** - * Is the mode one that is paddable? - * - * @see Crypt_TripleDES::Crypt_TripleDES() - * @var Boolean - * @access private - */ - var $paddable = false; - - /** - * Encryption buffer for CTR, OFB and CFB modes - * - * @see Crypt_TripleDES::encrypt() - * @var Array - * @access private - */ - var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); - - /** - * Decryption buffer for CTR, OFB and CFB modes - * - * @see Crypt_TripleDES::decrypt() - * @var Array - * @access private - */ - var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); - - /** - * mcrypt resource for CFB mode - * - * @see Crypt_TripleDES::encrypt() - * @see Crypt_TripleDES::decrypt() - * @var String - * @access private - */ - var $ecb; - /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be - * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used. + * Determines whether or not the mcrypt extension should be used. * + * $mode could be: + * + * - CRYPT_DES_MODE_ECB + * + * - CRYPT_DES_MODE_CBC + * + * - CRYPT_DES_MODE_CTR + * + * - CRYPT_DES_MODE_CFB + * + * - CRYPT_DES_MODE_OFB + * + * - CRYPT_DES_MODE_3CBC + * + * If not explictly set, CRYPT_DES_MODE_CBC will be used. + * + * @see Crypt_DES::Crypt_DES() + * @see Crypt_Base::Crypt_Base() * @param optional Integer $mode - * @return Crypt_TripleDES * @access public */ function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC) { - if ( !defined('CRYPT_DES_MODE') ) { - switch (true) { - case extension_loaded('mcrypt') && in_array('tripledes', mcrypt_list_algorithms()): - define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT); - break; - default: - define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL); - } - } + switch ($mode) { + // In case of CRYPT_DES_MODE_3CBC, we init as CRYPT_DES_MODE_CBC + // and additional flag us internally as 3CBC + case CRYPT_DES_MODE_3CBC: + parent::Crypt_DES(CRYPT_DES_MODE_CBC); + $this->mode_3cbc = true; - if ( $mode == CRYPT_DES_MODE_3CBC ) { - $this->mode = CRYPT_DES_MODE_3CBC; - $this->des = array( - new Crypt_DES(CRYPT_DES_MODE_CBC), - new Crypt_DES(CRYPT_DES_MODE_CBC), - new Crypt_DES(CRYPT_DES_MODE_CBC) - ); - $this->paddable = true; - - // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects - $this->des[0]->disablePadding(); - $this->des[1]->disablePadding(); - $this->des[2]->disablePadding(); - - return; - } - - switch ( CRYPT_DES_MODE ) { - case CRYPT_DES_MODE_MCRYPT: - switch ($mode) { - case CRYPT_DES_MODE_ECB: - $this->paddable = true; - $this->mode = MCRYPT_MODE_ECB; - break; - case CRYPT_DES_MODE_CTR: - $this->mode = 'ctr'; - break; - case CRYPT_DES_MODE_CFB: - $this->mode = 'ncfb'; - $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, ''); - break; - case CRYPT_DES_MODE_OFB: - $this->mode = MCRYPT_MODE_NOFB; - break; - case CRYPT_DES_MODE_CBC: - default: - $this->paddable = true; - $this->mode = MCRYPT_MODE_CBC; - } - $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); - $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); - - break; - default: + // This three $des'es will do the 3CBC work (if $key > 64bits) $this->des = array( - new Crypt_DES(CRYPT_DES_MODE_ECB), - new Crypt_DES(CRYPT_DES_MODE_ECB), - new Crypt_DES(CRYPT_DES_MODE_ECB) + new Crypt_DES(CRYPT_DES_MODE_CBC), + new Crypt_DES(CRYPT_DES_MODE_CBC), + new Crypt_DES(CRYPT_DES_MODE_CBC), ); - + // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects $this->des[0]->disablePadding(); $this->des[1]->disablePadding(); $this->des[2]->disablePadding(); + break; + // If not 3CBC, we init as usual + default: + parent::Crypt_DES($mode); + } + } - switch ($mode) { - case CRYPT_DES_MODE_ECB: - case CRYPT_DES_MODE_CBC: - $this->paddable = true; - $this->mode = $mode; - break; - case CRYPT_DES_MODE_CTR: - case CRYPT_DES_MODE_CFB: - case CRYPT_DES_MODE_OFB: - $this->mode = $mode; - break; - default: - $this->paddable = true; - $this->mode = CRYPT_DES_MODE_CBC; - } + /** + * Sets the initialization vector. (optional) + * + * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed + * to be all zero's. + * + * @see Crypt_Base::setIV() + * @access public + * @param String $iv + */ + function setIV($iv) + { + parent::setIV($iv); + if ($this->mode_3cbc) { + $this->des[0]->setIV($iv); + $this->des[1]->setIV($iv); + $this->des[2]->setIV($iv); } } @@ -339,568 +242,77 @@ class Crypt_TripleDES { * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * - * If the key is not explicitly set, it'll be assumed to be all zero's. + * If the key is not explicitly set, it'll be assumed to be all null bytes. * * @access public + * @see Crypt_DES::setKey() + * @see Crypt_Base::setKey() * @param String $key */ function setKey($key) { $length = strlen($key); if ($length > 8) { - $key = str_pad($key, 24, chr(0)); + $key = str_pad(substr($key, 0, 24), 24, chr(0)); // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this: // http://php.net/function.mcrypt-encrypt#47973 //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); } else { $key = str_pad($key, 8, chr(0)); } - $this->key = $key; - switch (true) { - case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL: - case $this->mode == CRYPT_DES_MODE_3CBC: - $this->des[0]->setKey(substr($key, 0, 8)); - $this->des[1]->setKey(substr($key, 8, 8)); - $this->des[2]->setKey(substr($key, 16, 8)); + parent::setKey($key); + + // And in case of CRYPT_DES_MODE_3CBC: + // if key <= 64bits we not need the 3 $des to work, + // because we will then act as regular DES-CBC with just a <= 64bit key. + // So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des. + if ($this->mode_3cbc && $length > 8) { + $this->des[0]->setKey(substr($key, 0, 8)); + $this->des[1]->setKey(substr($key, 8, 8)); + $this->des[2]->setKey(substr($key, 16, 8)); } - $this->enchanged = $this->dechanged = true; - } - - /** - * Sets the password. - * - * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: - * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: - * $hash, $salt, $method - * - * @param String $password - * @param optional String $method - * @access public - */ - function setPassword($password, $method = 'pbkdf2') - { - $key = ''; - - switch ($method) { - default: // 'pbkdf2' - list(, , $hash, $salt, $count) = func_get_args(); - if (!isset($hash)) { - $hash = 'sha1'; - } - // WPA and WPA2 use the SSID as the salt - if (!isset($salt)) { - $salt = 'phpseclib'; - } - // RFC2898#section-4.2 uses 1,000 iterations by default - // WPA and WPA2 use 4,096. - if (!isset($count)) { - $count = 1000; - } - - if (!class_exists('Crypt_Hash')) { - require_once('Crypt/Hash.php'); - } - - $i = 1; - while (strlen($key) < 24) { // $dkLen == 24 - $hmac = new Crypt_Hash(); - $hmac->setHash($hash); - $hmac->setKey($password); - $f = $u = $hmac->hash($salt . pack('N', $i++)); - for ($j = 2; $j <= $count; $j++) { - $u = $hmac->hash($u); - $f^= $u; - } - $key.= $f; - } - } - - $this->setKey($key); - } - - /** - * Sets the initialization vector. (optional) - * - * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed - * to be all zero's. - * - * @access public - * @param String $iv - */ - function setIV($iv) - { - $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0)); - if ($this->mode == CRYPT_DES_MODE_3CBC) { - $this->des[0]->setIV($iv); - $this->des[1]->setIV($iv); - $this->des[2]->setIV($iv); - } - $this->enchanged = $this->dechanged = true; - } - - /** - * Generate CTR XOR encryption key - * - * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the - * plaintext / ciphertext in CTR mode. - * - * @see Crypt_TripleDES::decrypt() - * @see Crypt_TripleDES::encrypt() - * @access private - * @param String $iv - */ - function _generate_xor(&$iv) - { - $xor = $iv; - for ($j = 4; $j <= 8; $j+=4) { - $temp = substr($iv, -$j, 4); - switch ($temp) { - case "\xFF\xFF\xFF\xFF": - $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); - break; - case "\x7F\xFF\xFF\xFF": - $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); - break 2; - default: - extract(unpack('Ncount', $temp)); - $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); - break 2; - } - } - - return $xor; } /** * Encrypts a message. * + * @see Crypt_Base::encrypt() * @access public * @param String $plaintext + * @return String $cipertext */ function encrypt($plaintext) { - if ($this->paddable) { - $plaintext = $this->_pad($plaintext); - } + // parent::en/decrypt() is able to do all the work for all modes and keylengths, + // except for: CRYPT_DES_MODE_3CBC (inner chaining CBC) with a key > 64bits // if the key is smaller then 8, do what we'd normally do - if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) { - $ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext))); - - return $ciphertext; + if ($this->mode_3cbc && strlen($this->key) > 8) { + return $this->des[2]->encrypt( + $this->des[1]->decrypt( + $this->des[0]->encrypt($this->_pad($plaintext)))); } - if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { - if ($this->enchanged) { - mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); - if ($this->mode == 'ncfb') { - mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0"); - } - $this->enchanged = false; - } - - if ($this->mode != 'ncfb' || !$this->continuousBuffer) { - $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); - } else { - $iv = &$this->encryptIV; - $pos = &$this->enbuffer['pos']; - $len = strlen($plaintext); - $ciphertext = ''; - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = 8 - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - $ciphertext = substr($iv, $orig_pos) ^ $plaintext; - $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); - $this->enbuffer['enmcrypt_init'] = true; - } - if ($len >= 8) { - if ($this->enbuffer['enmcrypt_init'] === false || $len > 950) { - if ($this->enbuffer['enmcrypt_init'] === true) { - mcrypt_generic_init($this->enmcrypt, $this->key, $iv); - $this->enbuffer['enmcrypt_init'] = false; - } - $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8)); - $iv = substr($ciphertext, -8); - $i = strlen($ciphertext); - $len%= 8; - } else { - while ($len >= 8) { - $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8); - $ciphertext.= $iv; - $len-= 8; - $i+= 8; - } - } - } - if ($len) { - $iv = mcrypt_generic($this->ecb, $iv); - $block = $iv ^ substr($plaintext, $i); - $iv = substr_replace($iv, $block, 0, $len); - $ciphertext.= $block; - $pos = $len; - } - return $ciphertext; - } - - if (!$this->continuousBuffer) { - mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); - } - - return $ciphertext; - } - - if (strlen($this->key) <= 8) { - $this->des[0]->mode = $this->mode; - - return $this->des[0]->encrypt($plaintext); - } - - $des = $this->des; - - $buffer = &$this->enbuffer; - $continuousBuffer = $this->continuousBuffer; - $ciphertext = ''; - switch ($this->mode) { - case CRYPT_DES_MODE_ECB: - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8); - // all of these _processBlock calls could, in theory, be put in a function - say Crypt_TripleDES::_ede_encrypt() or something. - // only problem with that: it would slow encryption and decryption down. $this->des would have to be called every time that - // function is called, instead of once for the whole string of text that's being encrypted, which would, in turn, make - // encryption and decryption take more time, per this: - // - // http://blog.libssh2.org/index.php?/archives/21-Compiled-Variables.html - $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); - $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT); - $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT); - $ciphertext.= $block; - } - break; - case CRYPT_DES_MODE_CBC: - $xor = $this->encryptIV; - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8) ^ $xor; - $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); - $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT); - $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT); - $xor = $block; - $ciphertext.= $block; - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - } - break; - case CRYPT_DES_MODE_CTR: - $xor = $this->encryptIV; - if (strlen($buffer['encrypted'])) { - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8); - $key = $this->_generate_xor($xor); - $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); - $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); - $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); - $buffer['encrypted'].= $key; - $key = $this->_string_shift($buffer['encrypted'], 8); - $ciphertext.= $block ^ $key; - } - } else { - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8); - $key = $this->_generate_xor($xor); - $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); - $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); - $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); - $ciphertext.= $block ^ $key; - } - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - if ($start = strlen($plaintext) & 7) { - $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; - } - } - break; - case CRYPT_DES_MODE_CFB: - if (strlen($buffer['xor'])) { - $ciphertext = $plaintext ^ $buffer['xor']; - $iv = $buffer['encrypted'] . $ciphertext; - $start = strlen($ciphertext); - $buffer['encrypted'].= $ciphertext; - $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext)); - } else { - $ciphertext = ''; - $iv = $this->encryptIV; - $start = 0; - } - - for ($i = $start; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8); - $iv = $des[0]->_processBlock($iv, CRYPT_DES_ENCRYPT); - $iv = $des[1]->_processBlock($iv, CRYPT_DES_DECRYPT); - $xor= $des[2]->_processBlock($iv, CRYPT_DES_ENCRYPT); - - $iv = $block ^ $xor; - if ($continuousBuffer && strlen($iv) != 8) { - $buffer = array( - 'encrypted' => $iv, - 'xor' => substr($xor, strlen($iv)) - ); - } - $ciphertext.= $iv; - } - - if ($this->continuousBuffer) { - $this->encryptIV = $iv; - } - break; - case CRYPT_DES_MODE_OFB: - $xor = $this->encryptIV; - if (strlen($buffer['xor'])) { - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); - $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $buffer['xor'].= $xor; - $key = $this->_string_shift($buffer['xor'], 8); - $ciphertext.= substr($plaintext, $i, 8) ^ $key; - } - } else { - for ($i = 0; $i < strlen($plaintext); $i+=8) { - $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); - $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $ciphertext.= substr($plaintext, $i, 8) ^ $xor; - } - $key = $xor; - } - if ($this->continuousBuffer) { - $this->encryptIV = $xor; - if ($start = strlen($plaintext) & 7) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; - } - } - } - - return $ciphertext; + return parent::encrypt($plaintext); } /** * Decrypts a message. * + * @see Crypt_Base::decrypt() * @access public * @param String $ciphertext + * @return String $plaintext */ function decrypt($ciphertext) { - if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) { - $plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext))); - - return $this->_unpad($plaintext); + if ($this->mode_3cbc && strlen($this->key) > 8) { + return $this->_unpad($this->des[0]->decrypt( + $this->des[1]->encrypt( + $this->des[2]->decrypt(str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0"))))); } - if ($this->paddable) { - // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0)); - } - - if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { - if ($this->dechanged) { - mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); - if ($this->mode == 'ncfb') { - mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0"); - } - $this->dechanged = false; - } - - if ($this->mode != 'ncfb' || !$this->continuousBuffer) { - $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); - } else { - $iv = &$this->decryptIV; - $pos = &$this->debuffer['pos']; - $len = strlen($ciphertext); - $plaintext = ''; - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = 8 - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - $plaintext = substr($iv, $orig_pos) ^ $ciphertext; - $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); - } - if ($len >= 8) { - $cb = substr($ciphertext, $i, $len - $len % 8); - $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; - $iv = substr($cb, -8); - $len%= 8; - } - if ($len) { - $iv = mcrypt_generic($this->ecb, $iv); - $cb = substr($ciphertext, -$len); - $plaintext.= $iv ^ $cb; - $iv = substr_replace($iv, $cb, 0, $len); - $pos = $len; - } - return $plaintext; - } - - if (!$this->continuousBuffer) { - mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); - } - - return $this->paddable ? $this->_unpad($plaintext) : $plaintext; - } - - if (strlen($this->key) <= 8) { - $this->des[0]->mode = $this->mode; - $plaintext = $this->des[0]->decrypt($ciphertext); - return $this->paddable ? $this->_unpad($plaintext) : $plaintext; - } - - $des = $this->des; - - $buffer = &$this->debuffer; - $continuousBuffer = $this->continuousBuffer; - $plaintext = ''; - switch ($this->mode) { - case CRYPT_DES_MODE_ECB: - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $block = substr($ciphertext, $i, 8); - $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT); - $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT); - $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT); - $plaintext.= $block; - } - break; - case CRYPT_DES_MODE_CBC: - $xor = $this->decryptIV; - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $orig = $block = substr($ciphertext, $i, 8); - $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT); - $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT); - $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT); - $plaintext.= $block ^ $xor; - $xor = $orig; - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - } - break; - case CRYPT_DES_MODE_CTR: - $xor = $this->decryptIV; - if (strlen($buffer['ciphertext'])) { - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $block = substr($ciphertext, $i, 8); - $key = $this->_generate_xor($xor); - $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); - $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); - $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); - $buffer['ciphertext'].= $key; - $key = $this->_string_shift($buffer['ciphertext'], 8); - $plaintext.= $block ^ $key; - } - } else { - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $block = substr($ciphertext, $i, 8); - $key = $this->_generate_xor($xor); - $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); - $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); - $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); - $plaintext.= $block ^ $key; - } - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - if ($start = strlen($plaintext) & 7) { - $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; - } - } - break; - case CRYPT_DES_MODE_CFB: - if (strlen($buffer['ciphertext'])) { - $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext'])); - $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext)); - if (strlen($buffer['ciphertext']) != 8) { - $block = $this->decryptIV; - } else { - $block = $buffer['ciphertext']; - $xor = $des[0]->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT); - $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); - $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $buffer['ciphertext'] = ''; - } - $start = strlen($plaintext); - } else { - $plaintext = ''; - $xor = $des[0]->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT); - $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); - $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $start = 0; - } - - for ($i = $start; $i < strlen($ciphertext); $i+=8) { - $block = substr($ciphertext, $i, 8); - $plaintext.= $block ^ $xor; - if ($continuousBuffer && strlen($block) != 8) { - $buffer['ciphertext'].= $block; - $block = $xor; - } else if (strlen($block) == 8) { - $xor = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); - $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); - $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - } - } - if ($this->continuousBuffer) { - $this->decryptIV = $block; - } - break; - case CRYPT_DES_MODE_OFB: - $xor = $this->decryptIV; - if (strlen($buffer['xor'])) { - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); - $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $buffer['xor'].= $xor; - $key = $this->_string_shift($buffer['xor'], 8); - $plaintext.= substr($ciphertext, $i, 8) ^ $key; - } - } else { - for ($i = 0; $i < strlen($ciphertext); $i+=8) { - $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); - $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $plaintext.= substr($ciphertext, $i, 8) ^ $xor; - } - $key = $xor; - } - if ($this->continuousBuffer) { - $this->decryptIV = $xor; - if ($start = strlen($ciphertext) & 7) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; - } - } - } - - return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + return parent::decrypt($ciphertext); } /** @@ -937,13 +349,14 @@ class Crypt_TripleDES { * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), * however, they are also less intuitive and more likely to cause you problems. * + * @see Crypt_Base::enableContinuousBuffer() * @see Crypt_TripleDES::disableContinuousBuffer() * @access public */ function enableContinuousBuffer() { - $this->continuousBuffer = true; - if ($this->mode == CRYPT_DES_MODE_3CBC) { + parent::enableContinuousBuffer(); + if ($this->mode_3cbc) { $this->des[0]->enableContinuousBuffer(); $this->des[1]->enableContinuousBuffer(); $this->des[2]->enableContinuousBuffer(); @@ -955,20 +368,14 @@ class Crypt_TripleDES { * * The default behavior. * + * @see Crypt_Base::disableContinuousBuffer() * @see Crypt_TripleDES::enableContinuousBuffer() * @access public */ function disableContinuousBuffer() { - $this->continuousBuffer = false; - $this->encryptIV = $this->iv; - $this->decryptIV = $this->iv; - $this->enchanged = true; - $this->dechanged = true; - $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); - $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); - - if ($this->mode == CRYPT_DES_MODE_3CBC) { + parent::disableContinuousBuffer(); + if ($this->mode_3cbc) { $this->des[0]->disableContinuousBuffer(); $this->des[1]->disableContinuousBuffer(); $this->des[2]->disableContinuousBuffer(); @@ -976,103 +383,38 @@ class Crypt_TripleDES { } /** - * Pad "packets". + * Creates the key schedule * - * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not - * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight. - * - * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1, - * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping - * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is - * transmitted separately) - * - * @see Crypt_TripleDES::disablePadding() - * @access public - */ - function enablePadding() - { - $this->padding = true; - } - - /** - * Do not pad packets. - * - * @see Crypt_TripleDES::enablePadding() - * @access public - */ - function disablePadding() - { - $this->padding = false; - } - - /** - * Pads a string - * - * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8). - * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7) - * - * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless - * and padding will, hence forth, be enabled. - * - * @see Crypt_TripleDES::_unpad() + * @see Crypt_DES::_setupKey() + * @see Crypt_Base::_setupKey() * @access private */ - function _pad($text) + function _setupKey() { - $length = strlen($text); + switch (true) { + // if $key <= 64bits we configure our internal pure-php cipher engine + // to act as regular [1]DES, not as 3DES. mcrypt.so::tripledes does the same. + case strlen($this->key) <= 8: + $this->des_rounds = 1; + break; - if (!$this->padding) { - if (($length & 7) == 0) { - return $text; - } else { - user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE); - $this->padding = true; - } + // otherwise, if $key > 64bits, we configure our engine to work as 3DES. + default: + $this->des_rounds = 3; + + // (only) if 3CBC is used we have, of course, to setup the $des[0-2] keys also separately. + if ($this->mode_3cbc) { + $this->des[0]->_setupKey(); + $this->des[1]->_setupKey(); + $this->des[2]->_setupKey(); + + // because $des[0-2] will, now, do all the work we can return here + // not need unnecessary stress parent::_setupKey() with our, now unused, $key. + return; + } } - - $pad = 8 - ($length & 7); - return str_pad($text, $length + $pad, chr($pad)); - } - - /** - * Unpads a string - * - * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong - * and false will be returned. - * - * @see Crypt_TripleDES::_pad() - * @access private - */ - function _unpad($text) - { - if (!$this->padding) { - return $text; - } - - $length = ord($text[strlen($text) - 1]); - - if (!$length || $length > 8) { - return false; - } - - return substr($text, 0, -$length); - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param String $string - * @param optional Integer $index - * @return String - * @access private - */ - function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; + // setup our key + parent::_setupKey(); } } diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Twofish.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Twofish.php new file mode 100644 index 0000000000..6342298d1a --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Crypt/Twofish.php @@ -0,0 +1,924 @@ + + * setKey('12345678901234567890123456789012'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $twofish->decrypt($twofish->encrypt($plaintext)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Crypt + * @package Crypt_Twofish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @copyright MMVII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 1.0 + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Crypt_Base + * + * Base cipher class + */ +if (!class_exists('Crypt_Base')) { + require_once('Base.php'); +} + +/**#@+ + * @access public + * @see Crypt_Twofish::encrypt() + * @see Crypt_Twofish::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_TWOFISH_MODE_CTR', CRYPT_MODE_CTR); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +define('CRYPT_TWOFISH_MODE_ECB', CRYPT_MODE_ECB); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +define('CRYPT_TWOFISH_MODE_CBC', CRYPT_MODE_CBC); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +define('CRYPT_TWOFISH_MODE_CFB', CRYPT_MODE_CFB); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +define('CRYPT_TWOFISH_MODE_OFB', CRYPT_MODE_OFB); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_Twofish::Crypt_Twofish() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_TWOFISH_MODE_INTERNAL', CRYPT_MODE_INTERNAL); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_TWOFISH_MODE_MCRYPT', CRYPT_MODE_MCRYPT); +/**#@-*/ + +/** + * Pure-PHP implementation of Twofish. + * + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @version 1.0 + * @access public + * @package Crypt_Twofish + */ +class Crypt_Twofish extends Crypt_Base { + /** + * The namespace used by the cipher for its constants. + * + * @see Crypt_Base::const_namespace + * @var String + * @access private + */ + var $const_namespace = 'TWOFISH'; + + /** + * The mcrypt specific name of the cipher + * + * @see Crypt_Base::cipher_name_mcrypt + * @var String + * @access private + */ + var $cipher_name_mcrypt = 'twofish'; + + /** + * Optimizing value while CFB-encrypting + * + * @see Crypt_Base::cfb_init_len + * @var Integer + * @access private + */ + var $cfb_init_len = 800; + + /** + * Q-Table + * + * @var Array + * @access private + */ + var $q0 = array ( + 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, + 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, + 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, + 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, + 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, + 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, + 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, + 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, + 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, + 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, + 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, + 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, + 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, + 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, + 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, + 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, + 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, + 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, + 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, + 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, + 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, + 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, + 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, + 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, + 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, + 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, + 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, + 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, + 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, + 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, + 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, + 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0 + ); + + /** + * Q-Table + * + * @var Array + * @access private + */ + var $q1 = array ( + 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, + 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, + 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, + 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, + 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, + 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, + 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, + 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, + 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, + 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, + 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, + 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, + 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, + 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, + 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, + 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, + 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, + 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, + 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, + 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, + 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, + 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, + 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, + 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, + 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, + 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, + 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, + 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, + 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, + 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, + 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, + 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 + ); + + /** + * M-Table + * + * @var Array + * @access private + */ + var $m0 = array ( + 0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8, + 0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, + 0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1, + 0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F, + 0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D, + 0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5, + 0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, 0x3131272C, 0x808065A3, + 0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51, + 0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796, + 0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C, + 0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70, + 0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8, + 0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC, + 0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2, + 0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9, + 0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, 0xEDEDD07A, 0x4343FC17, + 0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3, + 0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E, + 0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149, + 0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9, + 0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01, + 0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48, + 0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519, + 0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64, + 0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, 0x2D2D333C, 0x3030D6A5, + 0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969, + 0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E, + 0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC, + 0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB, + 0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9, + 0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2, + 0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91 + ); + + /** + * M-Table + * + * @var Array + * @access private + */ + var $m1 = array ( + 0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4, + 0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, + 0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141, + 0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E, + 0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060, + 0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757, + 0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, 0x9B53AAAA, 0x7C635D5D, + 0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7, + 0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656, + 0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B, + 0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8, + 0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3, + 0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D, + 0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB, + 0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282, + 0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, 0x86135050, 0xE730F7F7, + 0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B, + 0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC, + 0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E, + 0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9, + 0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272, + 0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F, + 0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED, + 0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5, + 0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, 0x4C5F7979, 0x02B6B7B7, + 0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2, + 0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3, + 0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323, + 0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA, + 0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF, + 0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000, + 0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8 + ); + + /** + * M-Table + * + * @var Array + * @access private + */ + var $m2 = array ( + 0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA, + 0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, + 0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783, + 0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE, + 0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0, + 0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA, + 0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, 0x312C3127, 0x80A38065, + 0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F, + 0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07, + 0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF, + 0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C, + 0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96, + 0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF, + 0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E, + 0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD, + 0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, 0xED7AEDD0, 0x431743FC, + 0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71, + 0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85, + 0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101, + 0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5, + 0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B, + 0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A, + 0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45, + 0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D, + 0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, 0x2D3C2D33, 0x30A530D6, + 0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929, + 0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D, + 0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB, + 0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F, + 0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9, + 0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746, + 0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF + ); + + /** + * M-Table + * + * @var Array + * @access private + */ + var $m3 = array ( + 0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF, + 0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, + 0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77, + 0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A, + 0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5, + 0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216, + 0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, 0x53AA9B53, 0x635D7C63, + 0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123, + 0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7, + 0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197, + 0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB, + 0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C, + 0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20, + 0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137, + 0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE, + 0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, 0x13508613, 0x30F7E730, + 0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252, + 0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4, + 0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F, + 0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A, + 0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB, + 0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D, + 0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0, + 0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8, + 0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, 0x5F794C5F, 0xB6B702B6, + 0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38, + 0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA, + 0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439, + 0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6, + 0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D, + 0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000, + 0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8 + ); + + /** + * The Key Schedule Array + * + * @var Array + * @access private + */ + var $K = array(); + + /** + * The Key depended S-Table 0 + * + * @var Array + * @access private + */ + var $S0 = array(); + + /** + * The Key depended S-Table 1 + * + * @var Array + * @access private + */ + var $S1 = array(); + + /** + * The Key depended S-Table 2 + * + * @var Array + * @access private + */ + var $S2 = array(); + + /** + * The Key depended S-Table 3 + * + * @var Array + * @access private + */ + var $S3 = array(); + + /** + * Holds the last used key + * + * @var Array + * @access private + */ + var $kl; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_TWOFISH_MODE_ECB + * + * - CRYPT_TWOFISH_MODE_CBC + * + * - CRYPT_TWOFISH_MODE_CTR + * + * - CRYPT_TWOFISH_MODE_CFB + * + * - CRYPT_TWOFISH_MODE_OFB + * + * If not explictly set, CRYPT_TWOFISH_MODE_CBC will be used. + * + * @see Crypt_Base::Crypt_Base() + * @param optional Integer $mode + * @access public + */ + function Crypt_Twofish($mode = CRYPT_TWOFISH_MODE_CBC) + { + parent::Crypt_Base($mode); + } + + /** + * Sets the key. + * + * Keys can be of any length. Twofish, itself, requires the use of a key that's 128, 192 or 256-bits long. + * If the key is less than 256-bits we round the length up to the closest valid key length, + * padding $key with null bytes. If the key is more than 256-bits, we trim the excess bits. + * + * If the key is not explicitly set, it'll be assumed a 128 bits key to be all null bytes. + * + * @access public + * @see Crypt_Base::setKey() + * @param String $key + */ + function setKey($key) + { + $keylength = strlen($key); + switch (true) { + case $keylength <= 16: + $key = str_pad($key, 16, "\0"); + break; + case $keylength <= 24: + $key = str_pad($key, 24, "\0"); + break; + case $keylength < 32: + $key = str_pad($key, 32, "\0"); + break; + case $keylength > 32: + $key = substr($key, 0, 32); + } + parent::setKey($key); + } + + /** + * Setup the key (expansion) + * + * @see Crypt_Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key); + + /* Key expanding and generating the key-depended s-boxes */ + $le_longs = unpack('V*', $this->key); + $key = unpack('C*', $this->key); + $m0 = $this->m0; + $m1 = $this->m1; + $m2 = $this->m2; + $m3 = $this->m3; + $q0 = $this->q0; + $q1 = $this->q1; + + $K = $S0 = $S1 = $S2 = $S3 = array(); + + switch (strlen($this->key)) { + case 16: + list ($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list ($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3], $le_longs[4]); + for ($i = 0, $j = 1; $i < 40; $i+= 2,$j+= 2) { + $A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3]; + } + break; + case 24: + list ($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list ($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3], $le_longs[4]); + list ($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5], $le_longs[6]); + for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { + $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3]; + } + break; + default: // 32 + list ($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list ($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3], $le_longs[4]); + list ($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5], $le_longs[6]); + list ($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7], $le_longs[8]); + for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { + $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3]; + } + } + + $this->K = $K; + $this->S0 = $S0; + $this->S1 = $S1; + $this->S2 = $S2; + $this->S3 = $S3; + } + + /** + * _mdsrem function using by the twofish cipher algorithm + * + * @access private + * @param String $A + * @param String $B + * @return Array + */ + function _mdsrem($A, $B) + { + // No gain by unrolling this loop. + for ($i = 0; $i < 8; ++$i) { + // Get most significant coefficient. + $t = 0xff & ($B >> 24); + + // Shift the others up. + $B = ($B << 8) | (0xff & ($A >> 24)); + $A<<= 8; + + $u = $t << 1; + + // Subtract the modular polynomial on overflow. + if ($t & 0x80) { + $u^= 0x14d; + } + + // Remove t * (a * x^2 + 1). + $B ^= $t ^ ($u << 16); + + // Form u = a*t + t/a = t*(a + 1/a). + $u^= 0x7fffffff & ($t >> 1); + + // Add the modular polynomial on underflow. + if ($t & 0x01) $u^= 0xa6 ; + + // Remove t * (a + 1/a) * (x^3 + x). + $B^= ($u << 24) | ($u << 8); + } + + return array( + 0xff & $B >> 24, + 0xff & $B >> 16, + 0xff & $B >> 8, + 0xff & $B); + } + + /** + * Encrypts a block + * + * @access private + * @param String $in + * @return String + */ + function _encryptBlock($in) + { + $S0 = $this->S0; + $S1 = $this->S1; + $S2 = $this->S2; + $S3 = $this->S3; + $K = $this->K; + + $in = unpack("V4", $in); + $R0 = $K[0] ^ $in[1]; + $R1 = $K[1] ^ $in[2]; + $R2 = $K[2] ^ $in[3]; + $R3 = $K[3] ^ $in[4]; + + $ki = 7; + while ($ki < 39) { + $t0 = $S0[ $R0 & 0xff] ^ + $S1[($R0 >> 8) & 0xff] ^ + $S2[($R0 >> 16) & 0xff] ^ + $S3[($R0 >> 24) & 0xff]; + $t1 = $S0[($R1 >> 24) & 0xff] ^ + $S1[ $R1 & 0xff] ^ + $S2[($R1 >> 8) & 0xff] ^ + $S3[($R1 >> 16) & 0xff]; + $R2^= $t0 + $t1 + $K[++$ki]; + $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); + + $t0 = $S0[ $R2 & 0xff] ^ + $S1[($R2 >> 8) & 0xff] ^ + $S2[($R2 >> 16) & 0xff] ^ + $S3[($R2 >> 24) & 0xff]; + $t1 = $S0[($R3 >> 24) & 0xff] ^ + $S1[ $R3 & 0xff] ^ + $S2[($R3 >> 8) & 0xff] ^ + $S3[($R3 >> 16) & 0xff]; + $R0^= ($t0 + $t1 + $K[++$ki]); + $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); + } + + return pack("V4", $K[4] ^ $R2, + $K[5] ^ $R3, + $K[6] ^ $R0, + $K[7] ^ $R1); + } + + /** + * Decrypts a block + * + * @access private + * @param String $in + * @return String + */ + function _decryptBlock($in) + { + $S0 = $this->S0; + $S1 = $this->S1; + $S2 = $this->S2; + $S3 = $this->S3; + $K = $this->K; + + $in = unpack("V4", $in); + $R0 = $K[4] ^ $in[1]; + $R1 = $K[5] ^ $in[2]; + $R2 = $K[6] ^ $in[3]; + $R3 = $K[7] ^ $in[4]; + + $ki = 40; + while ($ki > 8) { + $t0 = $S0[$R0 & 0xff] ^ + $S1[$R0 >> 8 & 0xff] ^ + $S2[$R0 >> 16 & 0xff] ^ + $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ + $S1[$R1 & 0xff] ^ + $S2[$R1 >> 8 & 0xff] ^ + $S3[$R1 >> 16 & 0xff]; + $R3^= $t0 + ($t1 << 1) + $K[--$ki]; + $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + $K[--$ki]); + + $t0 = $S0[$R2 & 0xff] ^ + $S1[$R2 >> 8 & 0xff] ^ + $S2[$R2 >> 16 & 0xff] ^ + $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ + $S1[$R3 & 0xff] ^ + $S2[$R3 >> 8 & 0xff] ^ + $S3[$R3 >> 16 & 0xff]; + $R1^= $t0 + ($t1 << 1) + $K[--$ki]; + $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + $K[--$ki]); + } + + return pack("V4", $K[0] ^ $R2, + $K[1] ^ $R3, + $K[2] ^ $R0, + $K[3] ^ $R1); + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see Crypt_Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& Crypt_Twofish::_getLambdaFunctions(); + + // Max. 10 Ultra-Hi-optimized inline-crypt functions. After that, we'll (still) create very fast code, but not the ultimate fast one. + $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 ); + + switch (true) { + case $gen_hi_opt_code: + $code_hash = md5(str_pad("Crypt_Twofish, {$this->mode}, ", 32, "\0") . $this->key); + break; + default: + $code_hash = "Crypt_Twofish, {$this->mode}"; + } + + if (!isset($lambda_functions[$code_hash])) { + switch (true) { + case $gen_hi_opt_code: + $K = $this->K; + + $init_crypt = ' + static $S0, $S1, $S2, $S3; + if (!$S0) { + for ($i = 0; $i < 256; ++$i) { + $S0[] = (int)$self->S0[$i]; + $S1[] = (int)$self->S1[$i]; + $S2[] = (int)$self->S2[$i]; + $S3[] = (int)$self->S3[$i]; + } + } + '; + break; + default: + $K = array(); + for ($i = 0; $i < 40; ++$i) { + $K[] = '$K_' . $i; + } + + $init_crypt = ' + $S0 = $self->S0; + $S1 = $self->S1; + $S2 = $self->S2; + $S3 = $self->S3; + list(' . implode(',', $K) . ') = $self->K; + '; + } + + // Generating encrypt code: + $encrypt_block = ' + $in = unpack("V4", $in); + $R0 = '.$K[0].' ^ $in[1]; + $R1 = '.$K[1].' ^ $in[2]; + $R2 = '.$K[2].' ^ $in[3]; + $R3 = '.$K[3].' ^ $in[4]; + '; + for ($ki = 7, $i = 0; $i < 8; ++$i) { + $encrypt_block.= ' + $t0 = $S0[ $R0 & 0xff] ^ + $S1[($R0 >> 8) & 0xff] ^ + $S2[($R0 >> 16) & 0xff] ^ + $S3[($R0 >> 24) & 0xff]; + $t1 = $S0[($R1 >> 24) & 0xff] ^ + $S1[ $R1 & 0xff] ^ + $S2[($R1 >> 8) & 0xff] ^ + $S3[($R1 >> 16) & 0xff]; + $R2^= ($t0 + $t1 + '.$K[++$ki].'); + $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); + + $t0 = $S0[ $R2 & 0xff] ^ + $S1[($R2 >> 8) & 0xff] ^ + $S2[($R2 >> 16) & 0xff] ^ + $S3[($R2 >> 24) & 0xff]; + $t1 = $S0[($R3 >> 24) & 0xff] ^ + $S1[ $R3 & 0xff] ^ + $S2[($R3 >> 8) & 0xff] ^ + $S3[($R3 >> 16) & 0xff]; + $R0^= ($t0 + $t1 + '.$K[++$ki].'); + $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); + '; + } + $encrypt_block.= ' + $in = pack("V4", '.$K[4].' ^ $R2, + '.$K[5].' ^ $R3, + '.$K[6].' ^ $R0, + '.$K[7].' ^ $R1); + '; + + // Generating decrypt code: + $decrypt_block = ' + $in = unpack("V4", $in); + $R0 = '.$K[4].' ^ $in[1]; + $R1 = '.$K[5].' ^ $in[2]; + $R2 = '.$K[6].' ^ $in[3]; + $R3 = '.$K[7].' ^ $in[4]; + '; + for ($ki = 40, $i = 0; $i < 8; ++$i) { + $decrypt_block.= ' + $t0 = $S0[$R0 & 0xff] ^ + $S1[$R0 >> 8 & 0xff] ^ + $S2[$R0 >> 16 & 0xff] ^ + $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ + $S1[$R1 & 0xff] ^ + $S2[$R1 >> 8 & 0xff] ^ + $S3[$R1 >> 16 & 0xff]; + $R3^= $t0 + ($t1 << 1) + '.$K[--$ki].'; + $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); + + $t0 = $S0[$R2 & 0xff] ^ + $S1[$R2 >> 8 & 0xff] ^ + $S2[$R2 >> 16 & 0xff] ^ + $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ + $S1[$R3 & 0xff] ^ + $S2[$R3 >> 8 & 0xff] ^ + $S3[$R3 >> 16 & 0xff]; + $R1^= $t0 + ($t1 << 1) + '.$K[--$ki].'; + $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); + '; + } + $decrypt_block.= ' + $in = pack("V4", '.$K[0].' ^ $R2, + '.$K[1].' ^ $R3, + '.$K[2].' ^ $R0, + '.$K[3].' ^ $R1); + '; + + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'init_encrypt' => '', + 'init_decrypt' => '', + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php index 29ad949e10..f7ac85536f 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ANSI.php @@ -34,8 +34,7 @@ * @author Jim Wigginton * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id$ - * @link htp://phpseclib.sourceforge.net + * @link http://phpseclib.sourceforge.net */ /** @@ -298,12 +297,12 @@ class File_ANSI { } // http://ascii-table.com/ansi-escape-sequences-vt-100.php switch ($this->ansi) { - case "\x1B[H": + case "\x1B[H": // Move cursor to upper left corner $this->old_x = $this->x; $this->old_y = $this->y; $this->x = $this->y = 0; break; - case "\x1B[J": + case "\x1B[J": // Clear screen from cursor down $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y)); $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, '')); @@ -314,36 +313,45 @@ class File_ANSI { array_shift($this->history); array_shift($this->history_attrs); } - case "\x1B[K": + case "\x1B[K": // Clear screen from cursor right $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); array_splice($this->attrs[$this->y], $this->x + 1); break; + case "\x1B[2K": // Clear entire line + $this->screen[$this->y] = str_repeat(' ', $this->x); + $this->attrs[$this->y] = $this->attr_row; + break; case "\x1B[?1h": // set cursor key to application + case "\x1B[?25h": // show the cursor + break; + case "\x1BE": // Move to next line + $this->_newLine(); + $this->x = 0; break; default: switch (true) { - case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): + case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h $this->old_x = $this->x; $this->old_y = $this->y; $this->x = $match[2] - 1; $this->y = $match[1] - 1; break; - case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): + case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines $this->old_x = $this->x; $x = $match[1] - 1; break; case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window break; - case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): + case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes $mods = explode(';', $match[1]); foreach ($mods as $mod) { switch ($mod) { - case 0: + case 0: // Turn off character attributes $this->attrs[$this->y][$this->x] = ''; if ($this->bold) $this->attrs[$this->y][$this->x].= ''; - if ($this->underline) $this->attrs[$this->y][$this->x].= ''; + if ($this->underline) $this->attrs[$this->y][$this->x].= ''; if ($this->blink) $this->attrs[$this->y][$this->x].= ''; if ($this->color) $this->attrs[$this->y][$this->x].= ''; @@ -355,25 +363,25 @@ class File_ANSI { $this->bold = $this->underline = $this->blink = $this->color = $this->reverse = false; break; - case 1: + case 1: // Turn bold mode on if (!$this->bold) { $this->attrs[$this->y][$this->x] = ''; $this->bold = true; } break; - case 4: + case 4: // Turn underline mode on if (!$this->underline) { $this->attrs[$this->y][$this->x] = ''; $this->underline = true; } break; - case 5: + case 5: // Turn blinking mode on if (!$this->blink) { $this->attrs[$this->y][$this->x] = ''; $this->blink = true; } break; - case 7: + case 7: // Turn reverse video on $this->reverse = !$this->reverse; $temp = $this->background; $this->background = $this->foreground; @@ -384,7 +392,7 @@ class File_ANSI { } $this->color = true; break; - default: + default: // set colors //$front = $this->reverse ? &$this->background : &$this->foreground; $front = &$this->{ $this->reverse ? 'background' : 'foreground' }; //$back = $this->reverse ? &$this->foreground : &$this->background; @@ -424,7 +432,7 @@ class File_ANSI { } break; default: - echo "{$this->ansi} unsupported\r\n"; + user_error("{$this->ansi} unsupported\r\n"); } } $this->ansi = ''; @@ -436,25 +444,7 @@ class File_ANSI { $this->x = 0; break; case "\n": - //if ($this->y < $this->max_y) { - // $this->y++; - //} - - while ($this->y >= $this->max_y) { - $this->history = array_merge($this->history, array(array_shift($this->screen))); - $this->screen[] = ''; - - $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs))); - $this->attrs[] = $this->attr_row; - - if (count($this->history) >= $this->max_history) { - array_shift($this->history); - array_shift($this->history_attrs); - } - - $this->y--; - } - $this->y++; + $this->_newLine(); break; case "\x0F": // shift break; @@ -479,6 +469,36 @@ class File_ANSI { } } + /** + * Add a new line + * + * Also update the $this->screen and $this->history buffers + * + * @access private + */ + function _newLine() + { + //if ($this->y < $this->max_y) { + // $this->y++; + //} + + while ($this->y >= $this->max_y) { + $this->history = array_merge($this->history, array(array_shift($this->screen))); + $this->screen[] = ''; + + $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs))); + $this->attrs[] = $this->attr_row; + + if (count($this->history) >= $this->max_history) { + array_shift($this->history); + array_shift($this->history_attrs); + } + + $this->y--; + } + $this->y++; + } + /** * Returns the current screen without preformating * @@ -537,4 +557,4 @@ class File_ANSI { return '
' . $scrollback . '
'; } -} +} \ No newline at end of file diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php index 766c6e7ebf..9f481fc3cc 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/ASN1.php @@ -37,17 +37,9 @@ * @author Jim Wigginton * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id$ * @link http://phpseclib.sourceforge.net */ -/** - * Include Math_BigInteger - */ -if (!class_exists('Math_BigInteger')) { - require_once('Math/BigInteger.php'); -} - /**#@+ * Tag Classes * @@ -249,6 +241,22 @@ class File_ASN1 { FILE_ASN1_TYPE_VISIBLE_STRING => 1, ); + /** + * Default Constructor. + * + * @access public + */ + function File_ASN1() + { + static $static_init = null; + if (!$static_init) { + $static_init = true; + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + } + } + /** * Parse BER-encoding * @@ -304,12 +312,12 @@ class File_ASN1 { } while ( $loop ); } - // Length, as discussed in § 8.1.3 of X.690-0207.pdf#page=13 + // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 $length = ord($this->_string_shift($encoded)); $start++; if ( $length == 0x80 ) { // indefinite length // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all - // immediately available." -- § 8.1.3.2.c + // immediately available." -- paragraph 8.1.3.2.c //if ( !$constructed ) { // return false; //} @@ -319,11 +327,15 @@ class File_ASN1 { // support it up to four. $length&= 0x7F; $temp = $this->_string_shift($encoded, $length); + // tags of indefinite length don't really have a header length; this length includes the tag + $current+= array('headerlength' => $length + 2); $start+= $length; extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); + } else { + $current+= array('headerlength' => 2); } - // End-of-content, see §§ 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 + // End-of-content, see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 if (!$type && !$length) { return $decoded; } @@ -349,6 +361,7 @@ class File_ASN1 { 'content' => $constructed ? $this->_decode_ber($content, $start) : $content, 'length' => $length + $start - $current['start'] ) + $current; + $start+= $length; continue 2; } @@ -357,7 +370,7 @@ class File_ASN1 { // decode UNIVERSAL tags switch ($tag) { case FILE_ASN1_TYPE_BOOLEAN: - // "The contents octets shall consist of a single octet." -- § 8.2.1 + // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 //if (strlen($content) != 1) { // return false; //} @@ -410,7 +423,7 @@ class File_ASN1 { } break; case FILE_ASN1_TYPE_NULL: - // "The contents octets shall not contain any octets." -- § 8.8.2 + // "The contents octets shall not contain any octets." -- paragraph 8.8.2 //if (strlen($content)) { // return false; //} @@ -441,7 +454,7 @@ class File_ASN1 { /* Each character string type shall be encoded as if it had been declared: [UNIVERSAL x] IMPLICIT OCTET STRING - -- X.690-0207.pdf#page=23 (§ 8.21.3) + -- X.690-0207.pdf#page=23 (paragraph 8.21.3) Per that, we're not going to do any validation. If there are any illegal characters in the string, we don't really care */ @@ -487,12 +500,15 @@ class File_ASN1 { * * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. * + * "Special" mappings may be applied on a per tag-name basis via $special. + * * @param Array $decoded * @param Array $mapping + * @param Array $special * @return Array * @access public */ - function asn1map($decoded, $mapping) + function asn1map($decoded, $mapping, $special = array()) { if (isset($mapping['explicit'])) { $decoded = $decoded['content'][0]; @@ -506,7 +522,7 @@ class File_ASN1 { } $inmap = $this->ANYmap[$intype]; if (is_string($inmap)) { - return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping)); + return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special)); } break; case $mapping['type'] == FILE_ASN1_TYPE_CHOICE: @@ -514,15 +530,18 @@ class File_ASN1 { switch (true) { case isset($option['constant']) && $option['constant'] == $decoded['constant']: case !isset($option['constant']) && $option['type'] == $decoded['type']: - $value = $this->asn1map($decoded, $option); + $value = $this->asn1map($decoded, $option, $special); break; case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE: - $v = $this->asn1map($decoded, $option); + $v = $this->asn1map($decoded, $option, $special); if (isset($v)) { $value = $v; } } if (isset($value)) { + if (isset($special[$key])) { + $value = call_user_func($special[$key], $value); + } return array($key => $value); } } @@ -547,7 +566,7 @@ class File_ASN1 { if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($decoded['content'] as $content) { - if (($map[] = $this->asn1map($content, $child)) === NULL) { + if (($map[] = $this->asn1map($content, $child, $special)) === NULL) { return NULL; } } @@ -591,12 +610,15 @@ class File_ASN1 { if ($maymatch) { // Attempt submapping. - $candidate = $this->asn1map($temp, $child); + $candidate = $this->asn1map($temp, $child, $special); $maymatch = $candidate !== NULL; } if ($maymatch) { // Got the match: use it. + if (isset($special[$key])) { + $candidate = call_user_func($special[$key], $candidate); + } $map[$key] = $candidate; $i++; } elseif (isset($child['default'])) { @@ -617,7 +639,7 @@ class File_ASN1 { if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($decoded['content'] as $content) { - if (($map[] = $this->asn1map($content, $child)) === NULL) { + if (($map[] = $this->asn1map($content, $child, $special)) === NULL) { return NULL; } } @@ -660,7 +682,7 @@ class File_ASN1 { if ($maymatch) { // Attempt submapping. - $candidate = $this->asn1map($temp, $child); + $candidate = $this->asn1map($temp, $child, $special); $maymatch = $candidate !== NULL; } @@ -669,6 +691,9 @@ class File_ASN1 { } // Got the match: use it. + if (isset($special[$key])) { + $candidate = call_user_func($special[$key], $candidate); + } $map[$key] = $candidate; break; } @@ -761,18 +786,30 @@ class File_ASN1 { * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function * an ASN.1 compiler. * + * "Special" mappings can be applied via $special. + * * @param String $source * @param String $mapping * @param Integer $idx * @return String * @access public */ - function encodeDER($source, $mapping) + function encodeDER($source, $mapping, $special = array()) { $this->location = array(); - return $this->_encode_der($source, $mapping); + return $this->_encode_der($source, $mapping, NULL, $special); } + /** + * ASN.1 Encode (Helper function) + * + * @param String $source + * @param Array $mapping + * @param Integer $idx + * @param Array $special + * @return String + * @access private + */ /** * ASN.1 Encode (Helper function) * @@ -782,7 +819,7 @@ class File_ASN1 { * @return String * @access private */ - function _encode_der($source, $mapping, $idx = NULL) + function _encode_der($source, $mapping, $idx = NULL, $special = array()) { if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') { return $source->element; @@ -794,6 +831,9 @@ class File_ASN1 { } if (isset($idx)) { + if (isset($special[$idx])) { + $source = call_user_func($special[$idx], $source); + } $this->location[] = $idx; } @@ -810,7 +850,7 @@ class File_ASN1 { $child = $mapping['children']; foreach ($source as $content) { - $temp = $this->_encode_der($content, $child); + $temp = $this->_encode_der($content, $child, NULL, $special); if ($temp === false) { return false; } @@ -827,7 +867,7 @@ class File_ASN1 { continue; } - $temp = $this->_encode_der($source[$key], $child, $key); + $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } @@ -868,7 +908,7 @@ class File_ASN1 { continue; } - $temp = $this->_encode_der($source[$key], $child, $key); + $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } @@ -914,6 +954,9 @@ class File_ASN1 { $value = new Math_BigInteger($value); $value = $value->toBytes(true); } + if (!strlen($value)) { + $value = chr(0); + } break; case FILE_ASN1_TYPE_UTC_TIME: case FILE_ASN1_TYPE_GENERALIZED_TIME: @@ -987,19 +1030,19 @@ class File_ASN1 { switch (true) { case !isset($source): - return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping); + return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping, NULL, $special); case is_int($source): case is_object($source) && strtolower(get_class($source)) == 'math_biginteger': - return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping); + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping, NULL, $special); case is_float($source): - return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping); + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping, NULL, $special); case is_bool($source): - return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping); + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping, NULL, $special); case is_array($source) && count($source) == 1: $typename = implode('', array_keys($source)); $outtype = array_search($typename, $this->ANYmap, true); if ($outtype !== false) { - return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping); + return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, NULL, $special); } } @@ -1015,7 +1058,7 @@ class File_ASN1 { user_error('No filters defined for ' . implode('/', $loc)); return false; } - return $this->_encode_der($source, $filters + $mapping); + return $this->_encode_der($source, $filters + $mapping, NULL, $special); case FILE_ASN1_TYPE_NULL: $value = ''; break; @@ -1055,7 +1098,7 @@ class File_ASN1 { * DER-encode the length * * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See - * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 § 8.1.3} for more information. + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private * @param Integer $length diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php b/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php index 278da62e26..0b4e291361 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/File/X509.php @@ -40,22 +40,22 @@ * @author Jim Wigginton * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id$ - * @link htp://phpseclib.sourceforge.net + * @link http://phpseclib.sourceforge.net */ /** * Include File_ASN1 */ if (!class_exists('File_ASN1')) { - require_once('File/ASN1.php'); + require_once('ASN1.php'); } /** * Flag to only accept signatures signed by certificate authorities * + * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs + * * @access public - * @see File_X509::validateSignature() */ define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1); @@ -306,6 +306,10 @@ class File_X509 { */ function File_X509() { + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + // Explicitly Tagged Module, 1988 Syntax // http://tools.ietf.org/html/rfc5280#appendix-A.1 @@ -1436,18 +1440,7 @@ class File_X509 { $asn1 = new File_ASN1(); - /* - X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them above and beyond the ceritificate. ie. - some may have the following preceeding the -----BEGIN CERTIFICATE----- line: - - subject=/O=organization/OU=org unit/CN=common name - issuer=/O=organization/CN=common name - */ - $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $cert); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; - if ($temp != false) { - $cert = $temp; - } + $cert = $this->_extractBER($cert); if ($cert === false) { $this->currentCert = false; @@ -1569,7 +1562,7 @@ class File_X509 { corresponding to the extension type identified by extnID */ $map = $this->_getMapping($id); if (!is_bool($map)) { - $mapped = $asn1->asn1map($decoded[0], $map); + $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => array($this, '_decodeIP'))); $value = $mapped === false ? $decoded[0] : $mapped; if ($id == 'id-ce-certificatePolicies') { @@ -1651,7 +1644,7 @@ class File_X509 { unset($extensions[$i]); } } else { - $temp = $asn1->encodeDER($value, $map); + $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP'))); $value = base64_encode($temp); } } @@ -2001,16 +1994,19 @@ class File_X509 { * Works on X.509 certs, CSR's and CRL's. * Returns true if the signature is verified, false if it is not correct or NULL on error * + * By default returns false for self-signed certs. Call validateSignature(false) to make this support + * self-signed. + * * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. * - * @param Integer $options optional + * @param Boolean $caonly optional * @access public * @return Mixed */ - function validateSignature($options = 0) + function validateSignature($caonly = true) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { - return 0; + return NULL; } /* TODO: @@ -2048,10 +2044,10 @@ class File_X509 { } } } - if (count($this->CAs) == $i && ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) { + if (count($this->CAs) == $i && $caonly) { return false; } - } elseif (!isset($signingCert) || ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) { + } elseif (!isset($signingCert) || $caonly) { return false; } return $this->_validateSignature( @@ -2182,6 +2178,36 @@ class File_X509 { } } + /** + * Decodes an IP address + * + * Takes in a base64 encoded "blob" and returns a human readable IP address + * + * @param String $ip + * @access private + * @return String + */ + function _decodeIP($ip) + { + $ip = base64_decode($ip); + list(, $ip) = unpack('N', $ip); + return long2ip($ip); + } + + /** + * Encodes an IP address + * + * Takes a human readable IP address into a base64-encoded "blob" + * + * @param String $ip + * @access private + * @return String + */ + function _encodeIP($ip) + { + return base64_encode(pack('N', ip2long($ip))); + } + /** * "Normalizes" a Distinguished Name property * @@ -2207,7 +2233,7 @@ class File_X509 { case 'commonname': case 'cn': return 'id-at-commonName'; - case 'id-at-stateorprovinceName': + case 'id-at-stateorprovincename': case 'stateorprovincename': case 'state': case 'province': @@ -2630,9 +2656,9 @@ class File_X509 { case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): - return $this->getDNProp($propname, $this->currentCert['tbsCertificate']['issuer'], $withType); + return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['issuer'], $withType); case isset($this->currentCert['tbsCertList']): - return $this->getDNProp($propname, $this->currentCert['tbsCertList']['issuer'], $withType); + return $this->getDNProp($propName, $this->currentCert['tbsCertList']['issuer'], $withType); } return false; @@ -2656,7 +2682,7 @@ class File_X509 { case isset($this->currentCert['tbsCertificate']): return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType); case isset($this->currentCert['certificationRequestInfo']): - return $this->getDNProp($propname, $this->currentCert['certificationRequestInfo']['subject'], $withType); + return $this->getDNProp($propName, $this->currentCert['certificationRequestInfo']['subject'], $withType); } return false; @@ -2718,6 +2744,7 @@ class File_X509 { */ function setPublicKey($key) { + $key->setPublicKey(); $this->publicKey = $key; } @@ -2804,11 +2831,7 @@ class File_X509 { $asn1 = new File_ASN1(); - $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $csr); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; - if ($temp != false) { - $csr = $temp; - } + $csr = $this->_extractBER($csr); $orig = $csr; if ($csr === false) { @@ -2917,51 +2940,51 @@ class File_X509 { * @access public * @return Mixed */ - function loadSPKAC($csr) + function loadSPKAC($spkac) { - if (is_array($csr) && isset($csr['publicKeyAndChallenge'])) { + if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) { unset($this->currentCert); unset($this->currentKeyIdentifier); unset($this->signatureSubject); - $this->currentCert = $csr; - return $csr; + $this->currentCert = $spkac; + return $spkac; } // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge $asn1 = new File_ASN1(); - $temp = preg_replace('#(?:^[^=]+=)|[\r\n\\\]#', '', $csr); + $temp = preg_replace('#(?:^[^=]+=)|[\r\n\\\]#', '', $spkac); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; if ($temp != false) { - $csr = $temp; + $spkac = $temp; } - $orig = $csr; + $orig = $spkac; - if ($csr === false) { + if ($spkac === false) { $this->currentCert = false; return false; } $asn1->loadOIDs($this->oids); - $decoded = $asn1->decodeBER($csr); + $decoded = $asn1->decodeBER($spkac); if (empty($decoded)) { $this->currentCert = false; return false; } - $csr = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge); + $spkac = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge); - if (!isset($csr) || $csr === false) { + if (!isset($spkac) || $spkac === false) { $this->currentCert = false; return false; } $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); - $algorithm = &$csr['publicKeyAndChallenge']['spki']['algorithm']['algorithm']; - $key = &$csr['publicKeyAndChallenge']['spki']['subjectPublicKey']; + $algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm']; + $key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']; $key = $this->_reformatKey($algorithm, $key); switch ($algorithm) { @@ -2978,9 +3001,9 @@ class File_X509 { } $this->currentKeyIdentifier = NULL; - $this->currentCert = $csr; + $this->currentCert = $spkac; - return $csr; + return $spkac; } /** @@ -3000,11 +3023,7 @@ class File_X509 { $asn1 = new File_ASN1(); - $temp = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]| #', '', $crl); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; - if ($temp != false) { - $crl = $temp; - } + $crl = $this->_extractBER($crl); $orig = $crl; if ($crl === false) { @@ -3209,9 +3228,29 @@ class File_X509 { $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier); } + $altName = array(); + if (isset($subject->domains) && count($subject->domains) > 1) { - $this->setExtension('id-ce-subjectAltName', - array_map(array('File_X509', '_dnsName'), $subject->domains)); + $altName = array_map(array('File_X509', '_dnsName'), $subject->domains); + } + + if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { + // should an IP address appear as the CN if no domain name is specified? idk + //$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1); + $ipAddresses = array(); + foreach ($subject->ipAddresses as $ipAddress) { + $encoded = $subject->_ipAddress($ipAddress); + if ($encoded !== false) { + $ipAddresses[] = $encoded; + } + } + if (count($ipAddresses)) { + $altName = array_merge($altName, $ipAddresses); + } + } + + if (!empty($altName)) { + $this->setExtension('id-ce-subjectAltName', $altName); } if ($this->caFlag) { @@ -4012,12 +4051,29 @@ class File_X509 { case !is_object($key): return false; case strtolower(get_class($key)) == 'file_asn1_element': + // Assume the element is a bitstring-packed key. $asn1 = new File_ASN1(); - $decoded = $asn1->decodeBER($cert); + $decoded = $asn1->decodeBER($key->element); if (empty($decoded)) { return false; } - $key = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING)); + $raw = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING)); + if (empty($raw)) { + return false; + } + $raw = base64_decode($raw); + // If the key is private, compute identifier from its corresponding public key. + if (!class_exists('Crypt_RSA')) { + require_once('Crypt/RSA.php'); + } + $key = new Crypt_RSA(); + if (!$key->loadKey($raw)) { + return false; // Not an unencrypted RSA key. + } + if ($key->getPrivateKey() !== false) { // If private. + return $this->computeKeyIdentifier($key, $method); + } + $key = $raw; // Is a public key. break; case strtolower(get_class($key)) == 'file_x509': if (isset($key->publicKey)) { @@ -4036,9 +4092,7 @@ class File_X509 { } // If in PEM format, convert to binary. - if (preg_match('#^-----BEGIN #', $key)) { - $key = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $key)); - } + $key = $this->_extractBER($key); // Now we have the key string: compute its sha-1 sum. if (!class_exists('Crypt_Hash')) { @@ -4094,6 +4148,23 @@ class File_X509 { $this->setDNProp('id-at-commonName', $this->domains[0]); } + /** + * Set the IP Addresses's which the cert is to be valid for + * + * @access public + * @param String $ipAddress optional + */ + function setIPAddress() + { + $this->ipAddresses = func_get_args(); + /* + if (!isset($this->domains)) { + $this->removeDNProp('id-at-commonName'); + $this->setDNProp('id-at-commonName', $this->ipAddresses[0]); + } + */ + } + /** * Helper function to build domain array * @@ -4106,6 +4177,20 @@ class File_X509 { return array('dNSName' => $domain); } + /** + * Helper function to build IP Address array + * + * (IPv6 is not currently supported) + * + * @access private + * @param String $address + * @return Array + */ + function _iPAddress($address) + { + return array('iPAddress' => $address); + } + /** * Get the index of a revoked certificate. * @@ -4320,4 +4405,31 @@ class File_X509 { return false; } + + /** + * Extract raw BER from Base64 encoding + * + * @access private + * @param String $str + * @return String + */ + function _extractBER($str) + { + /* + X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them above and beyond the ceritificate. ie. + some may have the following preceding the -----BEGIN CERTIFICATE----- line: + + Bag Attributes + localKeyID: 01 00 00 00 + subject=/O=organization/OU=org unit/CN=common name + issuer=/O=organization/CN=common name + */ + $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#-+[^-]+-+#', '', $temp); + // remove new lines + $temp = str_replace(array("\r", "\n", ' '), '', $temp); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + return $temp != false ? $temp : $str; + } } diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php index d048cb032c..e40433de5b 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Math/BigInteger.php @@ -70,7 +70,6 @@ * @author Jim Wigginton * @copyright MMVI Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: BigInteger.php,v 1.33 2010/03/22 22:32:03 terrafrost Exp $ * @link http://pear.php.net/package/Math_BigInteger */ @@ -162,16 +161,6 @@ define('MATH_BIGINTEGER_MODE_BCMATH', 2); define('MATH_BIGINTEGER_MODE_GMP', 3); /**#@-*/ -/** - * The largest digit that may be used in addition / subtraction - * - * (we do pow(2, 52) instead of using 4503599627370496, directly, because some PHP installations - * will truncate 4503599627370496) - * - * @access private - */ -define('MATH_BIGINTEGER_MAX_DIGIT52', pow(2, 52)); - /** * Karatsuba Cutoff * @@ -232,7 +221,7 @@ class Math_BigInteger { var $bitmask = false; /** - * Mode independant value used for serialization. + * Mode independent value used for serialization. * * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value, @@ -246,20 +235,20 @@ class Math_BigInteger { var $hex; /** - * Converts base-2, base-10, base-16, and binary strings (eg. base-256) to BigIntegers. + * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers. * * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using * two's compliment. The sole exception to this is -10, which is treated the same as 10 is. * * Here's an example: * - * toString(); // outputs 50 - * ?> + * ?> * * * @param optional $x base-10 number or base-$base number if $base set. @@ -283,7 +272,64 @@ class Math_BigInteger { } if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { - define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + ob_start(); + phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + $versions = array(); + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + break; + default: + define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } + } + + if (!defined('PHP_INT_SIZE')) { + define('PHP_INT_SIZE', 4); + } + + if (!defined('MATH_BIGINTEGER_BASE') && MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_INTERNAL) { + switch (PHP_INT_SIZE) { + case 8: // use 64-bit integers if int size is 8 bytes + define('MATH_BIGINTEGER_BASE', 31); + define('MATH_BIGINTEGER_BASE_FULL', 0x80000000); + define('MATH_BIGINTEGER_MAX_DIGIT', 0x7FFFFFFF); + define('MATH_BIGINTEGER_MSB', 0x40000000); + // 10**9 is the closest we can get to 2**31 without passing it + define('MATH_BIGINTEGER_MAX10', 1000000000); + define('MATH_BIGINTEGER_MAX10_LEN', 9); + // the largest digit that may be used in addition / subtraction + define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 62)); + break; + //case 4: // use 64-bit floats if int size is 4 bytes + default: + define('MATH_BIGINTEGER_BASE', 26); + define('MATH_BIGINTEGER_BASE_FULL', 0x4000000); + define('MATH_BIGINTEGER_MAX_DIGIT', 0x3FFFFFF); + define('MATH_BIGINTEGER_MSB', 0x2000000); + // 10**7 is the closest to 2**26 without passing it + define('MATH_BIGINTEGER_MAX10', 10000000); + define('MATH_BIGINTEGER_MAX10_LEN', 7); + // the largest digit that may be used in addition / subtraction + // we do pow(2, 52) instead of using 4503599627370496 directly because some + // PHP installations will truncate 4503599627370496. + define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 52)); + } } switch ( MATH_BIGINTEGER_MODE ) { @@ -338,7 +384,7 @@ class Math_BigInteger { // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb) default: while (strlen($x)) { - $this->value[] = $this->_bytes2int($this->_base256_rshift($x, 26)); + $this->value[] = $this->_bytes2int($this->_base256_rshift($x, MATH_BIGINTEGER_BASE)); } } @@ -390,7 +436,10 @@ class Math_BigInteger { break; case 10: case -10: - $x = preg_replace('#^(-?[0-9]*).*#', '$1', $x); + // (?value = (string) $x; + $this->value = $x === '-' ? '0' : (string) $x; break; default: $temp = new Math_BigInteger(); - // array(10000000) is 10**7 in base-2**26. 10**7 is the closest to 2**26 we can get without passing it. $multiplier = new Math_BigInteger(); - $multiplier->value = array(10000000); + $multiplier->value = array(MATH_BIGINTEGER_MAX10); if ($x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } - $x = str_pad($x, strlen($x) + (6 * strlen($x)) % 7, 0, STR_PAD_LEFT); - + $x = str_pad($x, strlen($x) + ((MATH_BIGINTEGER_MAX10_LEN - 1) * strlen($x)) % MATH_BIGINTEGER_MAX10_LEN, 0, STR_PAD_LEFT); while (strlen($x)) { $temp = $temp->multiply($multiplier); - $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, 7)), 256)); - $x = substr($x, 7); + $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, MATH_BIGINTEGER_MAX10_LEN)), 256)); + $x = substr($x, MATH_BIGINTEGER_MAX10_LEN); } $this->value = $temp->value; @@ -543,7 +590,7 @@ class Math_BigInteger { $temp = $this->copy(); for ($i = count($temp->value) - 2; $i >= 0; --$i) { - $temp->_base256_lshift($result, 26); + $temp->_base256_lshift($result, MATH_BIGINTEGER_BASE); $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); } @@ -659,11 +706,11 @@ class Math_BigInteger { $temp->is_negative = false; $divisor = new Math_BigInteger(); - $divisor->value = array(10000000); // eg. 10**7 + $divisor->value = array(MATH_BIGINTEGER_MAX10); $result = ''; while (count($temp->value)) { list($temp, $mod) = $temp->divide($divisor); - $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', 7, '0', STR_PAD_LEFT) . $result; + $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', MATH_BIGINTEGER_MAX10_LEN, '0', STR_PAD_LEFT) . $result; } $result = ltrim($result, '0'); if (empty($result)) { @@ -874,25 +921,25 @@ class Math_BigInteger { $carry = 0; for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) { - $sum = $x_value[$j] * 0x4000000 + $x_value[$i] + $y_value[$j] * 0x4000000 + $y_value[$i] + $carry; - $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT52; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 - $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT52 : $sum; + $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] + $y_value[$j] * MATH_BIGINTEGER_BASE_FULL + $y_value[$i] + $carry; + $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT2 : $sum; - $temp = (int) ($sum / 0x4000000); + $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); - $value[$i] = (int) ($sum - 0x4000000 * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) + $value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) $value[$j] = $temp; } if ($j == $size) { // ie. if $y_size is odd $sum = $x_value[$i] + $y_value[$i] + $carry; - $carry = $sum >= 0x4000000; - $value[$i] = $carry ? $sum - 0x4000000 : $sum; + $carry = $sum >= MATH_BIGINTEGER_BASE_FULL; + $value[$i] = $carry ? $sum - MATH_BIGINTEGER_BASE_FULL : $sum; ++$i; // ie. let $i = $j since we've just done $value[$i] } if ($carry) { - for (; $value[$i] == 0x3FFFFFF; ++$i) { + for (; $value[$i] == MATH_BIGINTEGER_MAX_DIGIT; ++$i) { $value[$i] = 0; } ++$value[$i]; @@ -1010,26 +1057,26 @@ class Math_BigInteger { $carry = 0; for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) { - $sum = $x_value[$j] * 0x4000000 + $x_value[$i] - $y_value[$j] * 0x4000000 - $y_value[$i] - $carry; + $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] - $y_value[$j] * MATH_BIGINTEGER_BASE_FULL - $y_value[$i] - $carry; $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 - $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT52 : $sum; + $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT2 : $sum; - $temp = (int) ($sum / 0x4000000); + $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); - $x_value[$i] = (int) ($sum - 0x4000000 * $temp); + $x_value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); $x_value[$j] = $temp; } if ($j == $y_size) { // ie. if $y_size is odd $sum = $x_value[$i] - $y_value[$i] - $carry; $carry = $sum < 0; - $x_value[$i] = $carry ? $sum + 0x4000000 : $sum; + $x_value[$i] = $carry ? $sum + MATH_BIGINTEGER_BASE_FULL : $sum; ++$i; } if ($carry) { for (; !$x_value[$i]; ++$i) { - $x_value[$i] = 0x3FFFFFF; + $x_value[$i] = MATH_BIGINTEGER_MAX_DIGIT; } --$x_value[$i]; } @@ -1162,8 +1209,8 @@ class Math_BigInteger { for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 - $carry = (int) ($temp / 0x4000000); - $product_value[$j] = (int) ($temp - 0x4000000 * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } $product_value[$j] = $carry; @@ -1175,8 +1222,8 @@ class Math_BigInteger { for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; - $carry = (int) ($temp / 0x4000000); - $product_value[$k] = (int) ($temp - 0x4000000 * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } $product_value[$k] = $carry; @@ -1263,14 +1310,14 @@ class Math_BigInteger { $i2 = $i << 1; $temp = $square_value[$i2] + $value[$i] * $value[$i]; - $carry = (int) ($temp / 0x4000000); - $square_value[$i2] = (int) ($temp - 0x4000000 * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $square_value[$i2] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); // note how we start from $i+1 instead of 0 as we do in multiplication. for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; - $carry = (int) ($temp / 0x4000000); - $square_value[$k] = (int) ($temp - 0x4000000 * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $square_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } // the following line can yield values larger 2**15. at this point, PHP should switch @@ -1418,7 +1465,7 @@ class Math_BigInteger { // normalize $x and $y as described in HAC 14.23 / 14.24 $msb = $y->value[count($y->value) - 1]; - for ($shift = 0; !($msb & 0x2000000); ++$shift) { + for ($shift = 0; !($msb & MATH_BIGINTEGER_MSB); ++$shift) { $msb <<= 1; } $x->_lshift($shift); @@ -1465,10 +1512,10 @@ class Math_BigInteger { $q_index = $i - $y_max - 1; if ($x_window[0] == $y_window[0]) { - $quotient_value[$q_index] = 0x3FFFFFF; + $quotient_value[$q_index] = MATH_BIGINTEGER_MAX_DIGIT; } else { $quotient_value[$q_index] = (int) ( - ($x_window[0] * 0x4000000 + $x_window[1]) + ($x_window[0] * MATH_BIGINTEGER_BASE_FULL + $x_window[1]) / $y_window[0] ); @@ -1536,7 +1583,7 @@ class Math_BigInteger { $result = array(); for ($i = count($dividend) - 1; $i >= 0; --$i) { - $temp = 0x4000000 * $carry + $dividend[$i]; + $temp = MATH_BIGINTEGER_BASE_FULL * $carry + $dividend[$i]; $result[$i] = (int) ($temp / $divisor); $carry = (int) ($temp - $divisor * $result[$i]); } @@ -1754,7 +1801,7 @@ class Math_BigInteger { $e_length = count($e_value) - 1; $e_bits = decbin($e_value[$e_length]); for ($i = $e_length - 1; $i >= 0; --$i) { - $e_bits.= str_pad(decbin($e_value[$i]), 26, '0', STR_PAD_LEFT); + $e_bits.= str_pad(decbin($e_value[$i]), MATH_BIGINTEGER_BASE, '0', STR_PAD_LEFT); } $e_length = strlen($e_bits); @@ -2089,7 +2136,7 @@ class Math_BigInteger { if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) { $corrector_value = $this->_array_repeat(0, $n_length + 1); $corrector_value[] = 1; - $result = $this->_add($result, false, $corrector, false); + $result = $this->_add($result, false, $corrector_value, false); $result = $result[MATH_BIGINTEGER_VALUE]; } @@ -2112,6 +2159,7 @@ class Math_BigInteger { * @param Boolean $x_negative * @param Array $y_value * @param Boolean $y_negative + * @param Integer $stop * @return Array * @access private */ @@ -2148,8 +2196,8 @@ class Math_BigInteger { for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 - $carry = (int) ($temp / 0x4000000); - $product_value[$j] = (int) ($temp - 0x4000000 * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } if ($j < $stop) { @@ -2164,8 +2212,8 @@ class Math_BigInteger { for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; - $carry = (int) ($temp / 0x4000000); - $product_value[$k] = (int) ($temp - 0x4000000 * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } if ($k < $stop) { @@ -2213,7 +2261,7 @@ class Math_BigInteger { for ($i = 0; $i < $k; ++$i) { $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key]; - $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000))); + $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $temp = $this->_regularMultiply(array($temp), $n); $temp = array_merge($this->_array_repeat(0, $i), $temp); $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false); @@ -2265,9 +2313,9 @@ class Math_BigInteger { $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1)); for ($i = 0; $i < $n; ++$i) { $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0]; - $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000))); + $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key]; - $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000))); + $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false); $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1); @@ -2325,15 +2373,15 @@ class Math_BigInteger { * @param Array $x * @return Integer */ - function _modInverse67108864($x) // 2**26 == 67108864 + function _modInverse67108864($x) // 2**26 == 67,108,864 { $x = -$x[0]; $result = $x & 0x3; // x**-1 mod 2**2 $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4 $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8 $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16 - $result = fmod($result * (2 - fmod($x * $result, 0x4000000)), 0x4000000); // x**-1 mod 2**26 - return $result & 0x3FFFFFF; + $result = fmod($result * (2 - fmod($x * $result, MATH_BIGINTEGER_BASE_FULL)), MATH_BIGINTEGER_BASE_FULL); // x**-1 mod 2**26 + return $result & MATH_BIGINTEGER_MAX_DIGIT; } /** @@ -2402,12 +2450,12 @@ class Math_BigInteger { } /** - * Calculates the greatest common divisor and Bézout's identity. + * Calculates the greatest common divisor and Bezout's identity. * - * Say you have 693 and 609. The GCD is 21. Bézout's identity states that there exist integers x and y such that + * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which * combination is returned is dependant upon which mode is in use. See - * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bézout's identity - Wikipedia} for more information. + * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. * * Here's an example: * @@ -2604,8 +2652,8 @@ class Math_BigInteger { * * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * - * @param Math_BigInteger $x - * @return Integer < 0 if $this is less than $x; > 0 if $this is greater than $x, and 0 if they are equal. + * @param Math_BigInteger $y + * @return Integer < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @access public * @see equals() * @internal Could return $this->subtract($x), but that's not as fast as what we do do. @@ -2684,9 +2732,8 @@ class Math_BigInteger { * Some bitwise operations give different results depending on the precision being used. Examples include left * shift, not, and rotates. * - * @param Math_BigInteger $x + * @param Integer $bits * @access public - * @return Math_BigInteger */ function setPrecision($bits) { @@ -2736,7 +2783,7 @@ class Math_BigInteger { $result->value = array_slice($result->value, 0, $length); for ($i = 0; $i < $length; ++$i) { - $result->value[$i] = $result->value[$i] & $x->value[$i]; + $result->value[$i]&= $x->value[$i]; } return $this->_normalize($result); @@ -2772,11 +2819,11 @@ class Math_BigInteger { $length = max(count($this->value), count($x->value)); $result = $this->copy(); - $result->value = array_pad($result->value, 0, $length); - $x->value = array_pad($x->value, 0, $length); + $result->value = array_pad($result->value, $length, 0); + $x->value = array_pad($x->value, $length, 0); for ($i = 0; $i < $length; ++$i) { - $result->value[$i] = $this->value[$i] | $x->value[$i]; + $result->value[$i]|= $x->value[$i]; } return $this->_normalize($result); @@ -2812,11 +2859,11 @@ class Math_BigInteger { $length = max(count($this->value), count($x->value)); $result = $this->copy(); - $result->value = array_pad($result->value, 0, $length); - $x->value = array_pad($x->value, 0, $length); + $result->value = array_pad($result->value, $length, 0); + $x->value = array_pad($x->value, $length, 0); for ($i = 0; $i < $length; ++$i) { - $result->value[$i] = $this->value[$i] ^ $x->value[$i]; + $result->value[$i]^= $x->value[$i]; } return $this->_normalize($result); @@ -3004,6 +3051,37 @@ class Math_BigInteger { { } + /** + * Generates a random BigInteger + * + * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not. + * + * @param Integer $length + * @return Math_BigInteger + * @access private + */ + function _random_number_helper($size) + { + $crypt_random = function_exists('crypt_random_string') || (!class_exists('Crypt_Random') && function_exists('crypt_random_string')); + if ($crypt_random) { + $random = crypt_random_string($size); + } else { + $random = ''; + + if ($size & 1) { + $random.= chr(mt_rand(0, 255)); + } + + $blocks = $size >> 1; + for ($i = 0; $i < $blocks; ++$i) { + // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems + $random.= pack('n', mt_rand(0, 0xFFFF)); + } + } + + return new Math_BigInteger($random, 256); + } + /** * Generate a random number * @@ -3033,48 +3111,45 @@ class Math_BigInteger { $min = $temp; } - $generator = $this->generator; - - $max = $max->subtract($min); - $max = ltrim($max->toBytes(), chr(0)); - $size = strlen($max) - 1; - - $crypt_random = function_exists('crypt_random_string') || (!class_exists('Crypt_Random') && function_exists('crypt_random_string')); - if ($crypt_random) { - $random = crypt_random_string($size); - } else { - $random = ''; - - if ($size & 1) { - $random.= chr(mt_rand(0, 255)); - } - - $blocks = $size >> 1; - for ($i = 0; $i < $blocks; ++$i) { - // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems - $random.= pack('n', mt_rand(0, 0xFFFF)); - } + static $one; + if (!isset($one)) { + $one = new Math_BigInteger(1); } - $fragment = new Math_BigInteger($random, 256); - $leading = $fragment->compare(new Math_BigInteger(substr($max, 1), 256)) > 0 ? - ord($max[0]) - 1 : ord($max[0]); + $max = $max->subtract($min->subtract($one)); + $size = strlen(ltrim($max->toBytes(), chr(0))); - if (!$crypt_random) { - $msb = chr(mt_rand(0, $leading)); - } else { - $cutoff = floor(0xFF / $leading) * $leading; - while (true) { - $msb = ord(crypt_random_string(1)); - if ($msb <= $cutoff) { - $msb%= $leading; - break; - } - } - $msb = chr($msb); + /* + doing $random % $max doesn't work because some numbers will be more likely to occur than others. + eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145 + would produce 5 whereas the only value of random that could produce 139 would be 139. ie. + not all numbers would be equally likely. some would be more likely than others. + + creating a whole new random number until you find one that is within the range doesn't work + because, for sufficiently small ranges, the likelihood that you'd get a number within that range + would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability + would be pretty high that $random would be greater than $max. + + phpseclib works around this using the technique described here: + + http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string + */ + $random_max = new Math_BigInteger(chr(1) . str_repeat("\0", $size), 256); + $random = $this->_random_number_helper($size); + + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); + + while ($random->compare($max_multiple) >= 0) { + $random = $random->subtract($max_multiple); + $random_max = $random_max->subtract($max_multiple); + $random = $random->bitwise_leftShift(8); + $random = $random->add($this->_random_number_helper(1)); + $random_max = $random_max->bitwise_leftShift(8); + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); } - - $random = new Math_BigInteger($msb . $random, 256); + list(, $random) = $random->divide($max); return $this->_normalize($random->add($min)); } @@ -3094,10 +3169,18 @@ class Math_BigInteger { */ function randomPrime($min = false, $max = false, $timeout = false) { + if ($min === false) { + $min = new Math_BigInteger(0); + } + + if ($max === false) { + $max = new Math_BigInteger(0x7FFFFFFF); + } + $compare = $max->compare($min); if (!$compare) { - return $min; + return $min->isPrime() ? $min : false; } else if ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; @@ -3105,36 +3188,6 @@ class Math_BigInteger { $min = $temp; } - // gmp_nextprime() requires PHP 5 >= 5.2.0 per . - if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { - // we don't rely on Math_BigInteger::random()'s min / max when gmp_nextprime() is being used since this function - // does its own checks on $max / $min when gmp_nextprime() is used. When gmp_nextprime() is not used, however, - // the same $max / $min checks are not performed. - if ($min === false) { - $min = new Math_BigInteger(0); - } - - if ($max === false) { - $max = new Math_BigInteger(0x7FFFFFFF); - } - - $x = $this->random($min, $max); - - $x->value = gmp_nextprime($x->value); - - if ($x->compare($max) <= 0) { - return $x; - } - - $x->value = gmp_nextprime($min->value); - - if ($x->compare($max) <= 0) { - return $x; - } - - return false; - } - static $one, $two; if (!isset($one)) { $one = new Math_BigInteger(1); @@ -3144,6 +3197,23 @@ class Math_BigInteger { $start = time(); $x = $this->random($min, $max); + + // gmp_nextprime() requires PHP 5 >= 5.2.0 per . + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { + $p = new Math_BigInteger(); + $p->value = gmp_nextprime($x->value); + + if ($p->compare($max) <= 0) { + return $p; + } + + if (!$min->equals($x)) { + $x = $x->subtract($one); + } + + return $x->randomPrime($min, $x); + } + if ($x->equals($two)) { return $x; } @@ -3375,16 +3445,16 @@ class Math_BigInteger { return; } - $num_digits = (int) ($shift / 26); - $shift %= 26; + $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE); + $shift %= MATH_BIGINTEGER_BASE; $shift = 1 << $shift; $carry = 0; for ($i = 0; $i < count($this->value); ++$i) { $temp = $this->value[$i] * $shift + $carry; - $carry = (int) ($temp / 0x4000000); - $this->value[$i] = (int) ($temp - $carry * 0x4000000); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $this->value[$i] = (int) ($temp - $carry * MATH_BIGINTEGER_BASE_FULL); } if ( $carry ) { @@ -3410,9 +3480,9 @@ class Math_BigInteger { return; } - $num_digits = (int) ($shift / 26); - $shift %= 26; - $carry_shift = 26 - $shift; + $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE); + $shift %= MATH_BIGINTEGER_BASE; + $carry_shift = MATH_BIGINTEGER_BASE - $shift; $carry_mask = (1 << $shift) - 1; if ( $num_digits ) { @@ -3485,6 +3555,7 @@ class Math_BigInteger { * * Removes leading zeros * + * @param Array $value * @return Math_BigInteger * @access private */ @@ -3630,4 +3701,4 @@ class Math_BigInteger { $temp = ltrim(pack('N', $length), chr(0)); return pack('Ca*', 0x80 | strlen($temp), $temp); } -} \ No newline at end of file +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SCP.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SCP.php new file mode 100644 index 0000000000..88180cac67 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SCP.php @@ -0,0 +1,362 @@ + + * login('username', 'password')) { + * exit('bad login'); + * } + + * $scp = new Net_SCP($ssh); + * $scp->put('abcd', str_repeat('x', 1024*1024)); + * ?> + * + * + * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @category Net + * @package Net_SCP + * @author Jim Wigginton + * @copyright MMX Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +/**#@+ + * @access public + * @see Net_SCP::put() + */ +/** + * Reads data from a local file. + */ +define('NET_SCP_LOCAL_FILE', 1); +/** + * Reads data from a string. + */ +define('NET_SCP_STRING', 2); +/**#@-*/ + +/**#@+ + * @access private + * @see Net_SCP::_send() + * @see Net_SCP::_receive() + */ +/** + * SSH1 is being used. + */ +define('NET_SCP_SSH1', 1); +/** + * SSH2 is being used. + */ +define('NET_SCP_SSH2', 2); +/**#@-*/ + +/** + * Pure-PHP implementations of SCP. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Net_SCP + */ +class Net_SCP { + /** + * SSH Object + * + * @var Object + * @access private + */ + var $ssh; + + /** + * Packet Size + * + * @var Integer + * @access private + */ + var $packet_size; + + /** + * Mode + * + * @var Integer + * @access private + */ + var $mode; + + /** + * Default Constructor. + * + * Connects to an SSH server + * + * @param String $host + * @param optional Integer $port + * @param optional Integer $timeout + * @return Net_SCP + * @access public + */ + function Net_SCP($ssh) + { + if (!is_object($ssh)) { + return; + } + + switch (strtolower(get_class($ssh))) { + case'net_ssh2': + $this->mode = NET_SCP_SSH2; + break; + case 'net_ssh1': + $this->packet_size = 50000; + $this->mode = NET_SCP_SSH1; + break; + default: + return; + } + + $this->ssh = $ssh; + } + + /** + * Uploads a file to the SCP server. + * + * By default, Net_SCP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. + * So, for example, if you set $data to 'filename.ext' and then do Net_SCP::get(), you will get a file, twelve bytes + * long, containing 'filename.ext' as its contents. + * + * Setting $mode to NET_SCP_LOCAL_FILE will change the above behavior. With NET_SCP_LOCAL_FILE, $remote_file will + * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how + * large $remote_file will be, as well. + * + * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take + * care of that, yourself. + * + * @param String $remote_file + * @param String $data + * @param optional Integer $mode + * @param optional Callable $callback + * @return Boolean + * @access public + */ + function put($remote_file, $data, $mode = NET_SCP_STRING, $callback = null) + { + if (!isset($this->ssh)) { + return false; + } + + if (!$this->ssh->exec('scp -t ' . $remote_file, false)) { // -t = to + return false; + } + + $temp = $this->_receive(); + if ($temp !== chr(0)) { + return false; + } + + if ($this->mode == NET_SCP_SSH2) { + $this->packet_size = $this->ssh->packet_size_client_to_server[NET_SSH2_CHANNEL_EXEC]; + } + + $remote_file = basename($remote_file); + + if ($mode == NET_SCP_STRING) { + $size = strlen($data); + } else { + if (!is_file($data)) { + user_error("$data is not a valid file", E_USER_NOTICE); + return false; + } + + $fp = @fopen($data, 'rb'); + if (!$fp) { + fclose($fp); + return false; + } + $size = filesize($data); + } + + $this->_send('C0644 ' . $size . ' ' . $remote_file . "\n"); + + $temp = $this->_receive(); + if ($temp !== chr(0)) { + return false; + } + + $sent = 0; + while ($sent < $size) { + $temp = $mode & NET_SCP_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size); + $this->_send($temp); + $sent+= strlen($temp); + + if (is_callable($callback)) { + $callback($sent); + } + } + $this->_close(); + + if ($mode != NET_SCP_STRING) { + fclose($fp); + } + + return true; + } + + /** + * Downloads a file from the SCP server. + * + * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if + * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the + * operation + * + * @param String $remote_file + * @param optional String $local_file + * @return Mixed + * @access public + */ + function get($remote_file, $local_file = false) + { + if (!isset($this->ssh)) { + return false; + } + + if (!$this->ssh->exec('scp -f ' . $remote_file, false)) { // -f = from + return false; + } + + $this->_send("\0"); + + if (!preg_match('#(?[^ ]+) (?\d+) (?.+)#', rtrim($this->_receive()), $info)) { + return false; + } + + $this->_send("\0"); + + $size = 0; + + if ($local_file !== false) { + $fp = @fopen($local_file, 'wb'); + if (!$fp) { + return false; + } + } + + $content = ''; + while ($size < $info['size']) { + $data = $this->_receive(); + // SCP usually seems to split stuff out into 16k chunks + $size+= strlen($data); + + if ($local_file === false) { + $content.= $data; + } else { + fputs($fp, $data); + } + } + + $this->_close(); + + if ($local_file !== false) { + fclose($fp); + return true; + } + + return $content; + } + + /** + * Sends a packet to an SSH server + * + * @param String $data + * @access private + */ + function _send($data) + { + switch ($this->mode) { + case NET_SCP_SSH2: + $this->ssh->_send_channel_packet(NET_SSH2_CHANNEL_EXEC, $data); + break; + case NET_SCP_SSH1: + $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data); + $this->ssh->_send_binary_packet($data); + } + } + + /** + * Receives a packet from an SSH server + * + * @return String + * @access private + */ + function _receive() + { + switch ($this->mode) { + case NET_SCP_SSH2: + return $this->ssh->_get_channel_packet(NET_SSH2_CHANNEL_EXEC, true); + case NET_SCP_SSH1: + if (!$this->ssh->bitmap) { + return false; + } + while (true) { + $response = $this->ssh->_get_binary_packet(); + switch ($response[NET_SSH1_RESPONSE_TYPE]) { + case NET_SSH1_SMSG_STDOUT_DATA: + extract(unpack('Nlength', $response[NET_SSH1_RESPONSE_DATA])); + return $this->ssh->_string_shift($response[NET_SSH1_RESPONSE_DATA], $length); + case NET_SSH1_SMSG_STDERR_DATA: + break; + case NET_SSH1_SMSG_EXITSTATUS: + $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION)); + fclose($this->ssh->fsock); + $this->ssh->bitmap = 0; + return false; + default: + user_error('Unknown packet received', E_USER_NOTICE); + return false; + } + } + } + } + + /** + * Closes the connection to an SSH server + * + * @access private + */ + function _close() + { + switch ($this->mode) { + case NET_SCP_SSH2: + $this->ssh->_close_channel(NET_SSH2_CHANNEL_EXEC); + break; + case NET_SCP_SSH1: + $this->ssh->disconnect(); + } + } +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php index 8db087d3d9..5356ffbf77 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP.php @@ -58,7 +58,7 @@ * Include Net_SSH2 */ if (!class_exists('Net_SSH2')) { - require_once('Net/SSH2.php'); + require_once('SSH2.php'); } /**#@+ @@ -88,7 +88,7 @@ define('NET_SFTP_LOG_REALTIME', 3); * @see Net_SSH2::_get_channel_packet() * @access private */ -define('NET_SFTP_CHANNEL', 2); +define('NET_SFTP_CHANNEL', 0x100); /**#@+ * @access public @@ -97,16 +97,20 @@ define('NET_SFTP_CHANNEL', 2); /** * Reads data from a local file. */ -define('NET_SFTP_LOCAL_FILE', 1); +define('NET_SFTP_LOCAL_FILE', 1); /** * Reads data from a string. */ // this value isn't really used anymore but i'm keeping it reserved for historical reasons -define('NET_SFTP_STRING', 2); +define('NET_SFTP_STRING', 2); /** * Resumes an upload */ -define('NET_SFTP_RESUME', 4); +define('NET_SFTP_RESUME', 4); +/** + * Append a local file to an already existing remote file + */ +define('NET_SFTP_RESUME_START', 8); /**#@-*/ /** @@ -225,15 +229,6 @@ class Net_SFTP extends Net_SSH2 { */ var $sftp_errors = array(); - /** - * File Type - * - * @see Net_SFTP::_parseLongname() - * @var Integer - * @access private - */ - var $fileType = 0; - /** * Directory Cache * @@ -248,6 +243,16 @@ class Net_SFTP extends Net_SSH2 { */ var $dirs = array(); + /** + * Max SFTP Packet Size + * + * @see Net_SFTP::Net_SFTP() + * @see Net_SFTP::get() + * @var Array + * @access private + */ + var $max_sftp_packet; + /** * Default Constructor. * @@ -262,6 +267,9 @@ class Net_SFTP extends Net_SSH2 { function Net_SFTP($host, $port = 22, $timeout = 10) { parent::Net_SSH2($host, $port, $timeout); + + $this->max_sftp_packet = 1 << 15; + $this->packet_types = array( 1 => 'NET_SFTP_INIT', 2 => 'NET_SFTP_VERSION', @@ -306,7 +314,30 @@ class Net_SFTP extends Net_SSH2 { 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', - 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED' + 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', + 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', + 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', + 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', + 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', + 13 => 'NET_SFTP_STATUS_NO_MEDIA', + 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', + 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', + 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', + 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', + 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', + 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', + 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', + 21 => 'NET_SFTP_STATUS_LINK_LOOP', + 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', + 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', + 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', + 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', + 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', + 27 => 'NET_SFTP_STATUS_DELETE_PENDING', + 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', + 29 => 'NET_SFTP_STATUS_OWNER_INVALID', + 30 => 'NET_SFTP_STATUS_GROUP_INVALID', + 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' ); // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why @@ -329,7 +360,8 @@ class Net_SFTP extends Net_SSH2 { 0x00000002 => 'NET_SFTP_OPEN_WRITE', 0x00000004 => 'NET_SFTP_OPEN_APPEND', 0x00000008 => 'NET_SFTP_OPEN_CREATE', - 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE' + 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', + 0x00000020 => 'NET_SFTP_OPEN_EXCL' ); // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 // see Net_SFTP::_parseLongname() for an explanation @@ -337,7 +369,14 @@ class Net_SFTP extends Net_SSH2 { 1 => 'NET_SFTP_TYPE_REGULAR', 2 => 'NET_SFTP_TYPE_DIRECTORY', 3 => 'NET_SFTP_TYPE_SYMLINK', - 4 => 'NET_SFTP_TYPE_SPECIAL' + 4 => 'NET_SFTP_TYPE_SPECIAL', + 5 => 'NET_SFTP_TYPE_UNKNOWN', + // the followin types were first defined for use in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + 6 => 'NET_SFTP_TYPE_SOCKET', + 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', + 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', + 9 => 'NET_SFTP_TYPE_FIFO' ); $this->_define_array( $this->packet_types, @@ -346,6 +385,10 @@ class Net_SFTP extends Net_SSH2 { $this->open_flags, $this->file_types ); + + if (!defined('NET_SFTP_QUEUE_SIZE')) { + define('NET_SFTP_QUEUE_SIZE', 50); + } } /** @@ -356,13 +399,14 @@ class Net_SFTP extends Net_SSH2 { * @return Boolean * @access public */ - function login($username, $password = '') + function login($username) { - if (!parent::login($username, $password)) { + $args = func_get_args(); + if (!call_user_func_array(array('Net_SSH2', 'login'), $args)) { return false; } - $this->window_size_client_to_server[NET_SFTP_CHANNEL] = $this->window_size; + $this->window_size_server_to_client[NET_SFTP_CHANNEL] = $this->window_size; $packet = pack('CNa*N3', NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SFTP_CHANNEL, $this->window_size, 0x4000); @@ -388,7 +432,24 @@ class Net_SFTP extends Net_SSH2 { $response = $this->_get_channel_packet(NET_SFTP_CHANNEL); if ($response === false) { - return false; + // from PuTTY's psftp.exe + $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . + "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . + "exec sftp-server"; + // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does + // is redundant + $packet = pack('CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('exec'), 'exec', 1, strlen($command), $command); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(NET_SFTP_CHANNEL); + if ($response === false) { + return false; + } } $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; @@ -460,7 +521,7 @@ class Net_SFTP extends Net_SSH2 { return false; } - $this->pwd = $this->_realpath('.', false); + $this->pwd = $this->_realpath('.'); $this->_save_dir($this->pwd); @@ -485,7 +546,8 @@ class Net_SFTP extends Net_SSH2 { * @param optional Integer $status * @access public */ - function _logError($response, $status = -1) { + function _logError($response, $status = -1) + { if ($status == -1) { extract(unpack('Nstatus', $this->_string_shift($response, 4))); } @@ -507,98 +569,57 @@ class Net_SFTP extends Net_SSH2 { * the absolute (canonicalized) path. * * @see Net_SFTP::chdir() - * @param String $dir + * @param String $path * @return Mixed * @access private */ - function _realpath($dir, $check_dir = true) + function _realpath($path) { - if ($check_dir && $this->_is_dir($dir)) { - return true; - } - - /* - "This protocol represents file names as strings. File names are - assumed to use the slash ('/') character as a directory separator. - - File names starting with a slash are "absolute", and are relative to - the root of the file system. Names starting with any other character - are relative to the user's default directory (home directory). Note - that identifying the user is assumed to take place outside of this - protocol." - - -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-6 - */ - $file = ''; - if ($this->pwd !== false) { - // if the SFTP server returned the canonicalized path even for non-existant files this wouldn't be necessary - // on OpenSSH it isn't necessary but on other SFTP servers it is. that and since the specs say nothing on - // the subject, we'll go ahead and work around it with the following. - if (empty($dir) || $dir[strlen($dir) - 1] != '/') { - $file = basename($dir); - $dir = dirname($dir); - } - - $dir = $dir[0] == '/' ? '/' . rtrim(substr($dir, 1), '/') : rtrim($dir, '/'); - - if ($dir == '.' || $dir == $this->pwd) { - $temp = $this->pwd; - if (!empty($file)) { - $temp.= '/' . $file; - } - return $temp; - } - - if ($dir[0] != '/') { - $dir = $this->pwd . '/' . $dir; - } - // on the surface it seems like maybe resolving a path beginning with / is unnecessary, but such paths - // can contain .'s and ..'s just like any other. we could parse those out as appropriate or we can let - // the server do it. we'll do the latter. - } - - /* - that SSH_FXP_REALPATH returns SSH_FXP_NAME does not necessarily mean that anything actually exists at the - specified path. generally speaking, no attributes are returned with this particular SSH_FXP_NAME packet - regardless of whether or not a file actually exists. and in SFTPv3, the longname field and the filename - field match for this particular SSH_FXP_NAME packet. for other SSH_FXP_NAME packets, this will likely - not be the case, but for this one, it is. - */ - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 - if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($dir), $dir))) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_NAME: - // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following - // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks - // at is the first part and that part is defined the same in SFTP versions 3 through 6. - $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $realpath = $this->_string_shift($response, $length); - // the following is SFTPv3 only code. see Net_SFTP::_parseLongname() for more information. - // per the above comment, this is a shot in the dark that, on most servers, won't help us in determining - // the file type for Net_SFTP::stat() and Net_SFTP::lstat() but it's worth a shot. - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->fileType = $this->_parseLongname($this->_string_shift($response, $length)); - break; - case NET_SFTP_STATUS: - $this->_logError($response); - return false; - default: - user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + if ($this->pwd === false) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 + if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) { return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following + // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks + // at is the first part and that part is defined the same in SFTP versions 3 through 6. + $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway + extract(unpack('Nlength', $this->_string_shift($response, 4))); + return $this->_string_shift($response, $length); + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + return false; + } } - // if $this->pwd isn't set than the only thing $realpath could be is for '.', which is pretty much guaranteed to - // be a bonafide directory - if (!empty($file)) { - $realpath.= '/' . $file; + if ($path[0] != '/') { + $path = $this->pwd . '/' . $path; } - return $realpath; + $path = explode('/', $path); + $new = array(); + foreach ($path as $dir) { + if (!strlen($dir)) { + continue; + } + switch ($dir) { + case '..': + array_pop($new); + case '.': + break; + default: + $new[] = $dir; + } + } + + return '/' . implode('/', $new); } /** @@ -618,18 +639,18 @@ class Net_SFTP extends Net_SSH2 { $dir.= '/'; } + $dir = $this->_realpath($dir); + // confirm that $dir is, in fact, a valid directory if ($this->_is_dir($dir)) { $this->pwd = $dir; return true; } - $dir = $this->_realpath($dir, false); - - if ($this->_is_dir($dir)) { - $this->pwd = $dir; - return true; - } + // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us + // the currently logged in user has the appropriate permissions or not. maybe you could see if + // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy + // way to get those with SFTP if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { return false; @@ -649,19 +670,7 @@ class Net_SFTP extends Net_SSH2 { return false; } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); + if (!$this->_close_handle($handle)) { return false; } @@ -760,18 +769,21 @@ class Net_SFTP extends Net_SSH2 { $shortname = $this->_string_shift($response, $length); extract(unpack('Nlength', $this->_string_shift($response, 4))); $longname = $this->_string_shift($response, $length); - $attributes = $this->_parseAttributes($response); // we also don't care about the attributes + $attributes = $this->_parseAttributes($response); + if (!isset($attributes['type'])) { + $fileType = $this->_parseLongname($longname); + if ($fileType) { + $attributes['type'] = $fileType; + } + } if (!$raw) { $contents[] = $shortname; } else { $contents[$shortname] = $attributes; - $fileType = $this->_parseLongname($longname); - if ($fileType) { - if ($fileType == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { - $this->_save_dir($dir . '/' . $shortname); - } - $contents[$shortname]['type'] = $fileType; - } + } + + if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { + $this->_save_dir($dir . '/' . $shortname); } // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the // final SSH_FXP_STATUS packet should tell us that, already. @@ -790,21 +802,7 @@ class Net_SFTP extends Net_SSH2 { } } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - // "The client MUST release all resources associated with the handle regardless of the status." - // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); + if (!$this->_close_handle($handle)) { return false; } @@ -880,6 +878,9 @@ class Net_SFTP extends Net_SSH2 { /** * Checks cache for directory * + * Mainly used by chdir, which is, in turn, also used for determining whether or not an individual + * file is a directory or not by stat() and lstat() + * * @param String $dir * @access private */ @@ -894,6 +895,7 @@ class Net_SFTP extends Net_SSH2 { } $temp = &$temp[$dir]; } + return true; } /** @@ -920,6 +922,9 @@ class Net_SFTP extends Net_SSH2 { if ($stat === false) { return false; } + if (isset($stat['type'])) { + return $stat; + } $pwd = $this->pwd; $stat['type'] = $this->chdir($filename) ? @@ -951,10 +956,14 @@ class Net_SFTP extends Net_SSH2 { } $lstat = $this->_stat($filename, NET_SFTP_LSTAT); - $stat = $this->_stat($filename, NET_SFTP_STAT); - if ($stat === false) { + if ($lstat === false) { return false; } + if (isset($lstat['type'])) { + return $lstat; + } + + $stat = $this->_stat($filename, NET_SFTP_STAT); if ($lstat != $stat) { return array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK)); @@ -991,11 +1000,7 @@ class Net_SFTP extends Net_SSH2 { $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_ATTRS: - $attributes = $this->_parseAttributes($response); - if ($this->fileType) { - $attributes['type'] = $this->fileType; - } - return $attributes; + return $this->_parseAttributes($response); case NET_SFTP_STATUS: $this->_logError($response); return false; @@ -1005,33 +1010,6 @@ class Net_SFTP extends Net_SSH2 { return false; } - /** - * Attempt to identify the file type - * - * @param String $path - * @param Array $stat - * @param Array $lstat - * @return Integer - * @access private - */ - function _identify_type($path, $stat1, $stat2) - { - $stat1 = $this->_stat($path, $stat1); - $stat2 = $this->_stat($path, $stat2); - - if ($stat1 != $stat2) { - return array_merge($stat1, array('type' => NET_SFTP_TYPE_SYMLINK)); - } - - $pwd = $this->pwd; - $stat1['type'] = $this->chdir($path) ? - NET_SFTP_TYPE_DIRECTORY : - NET_SFTP_TYPE_REGULAR; - $this->pwd = $pwd; - - return $stat1; - } - /** * Returns the file size, in bytes, or false, on failure * @@ -1043,7 +1021,7 @@ class Net_SFTP extends Net_SSH2 { */ function _size($filename) { - $result = $this->_stat($filename, NET_SFTP_LSTAT); + $result = $this->_stat($filename, NET_SFTP_STAT); if ($result === false) { return false; } @@ -1051,16 +1029,32 @@ class Net_SFTP extends Net_SSH2 { } /** - * Set permissions on a file. + * Truncates a file to a given length * - * Returns the new file permissions on success or FALSE on error. - * - * @param Integer $mode * @param String $filename - * @return Mixed + * @param Integer $new_size + * @return Boolean * @access public */ - function chmod($mode, $filename, $recursive = false) + function truncate($filename, $new_size) + { + $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 0x100000000, $new_size); + + return $this->_setstat($filename, $attr, false); + } + + /** + * Sets access and modification time of file. + * + * If the file does not exist, it will be created. + * + * @param String $filename + * @param optional Integer $time + * @param optional Integer $atime + * @return Boolean + * @access public + */ + function touch($filename, $time = NULL, $atime = NULL) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; @@ -1071,36 +1065,99 @@ class Net_SFTP extends Net_SSH2 { return false; } - if ($recursive) { - $i = 0; - $result = $this->_chmod_recursive($mode, $filename, $i); - $this->_read_put_responses($i); - return $result; + if (!isset($time)) { + $time = time(); + } + if (!isset($atime)) { + $atime = $time; } - // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to - // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT. - $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); - if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) { + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL; + $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime); + $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } - /* - "Because some systems must use separate system calls to set various attributes, it is possible that a failure - response will be returned, but yet some of the attributes may be have been successfully modified. If possible, - servers SHOULD avoid this situation; however, clients MUST be aware that this is possible." - - -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6 - */ $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return $this->_close_handle(substr($response, 4)); + case NET_SFTP_STATUS: + $this->_logError($response); + break; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; } - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); + return $this->_setstat($filename, $attr, false); + } + + /** + * Changes file or directory owner + * + * Returns TRUE on success or FALSE on error. + * + * @param String $filename + * @param Integer $uid + * @param optional Boolean $recursive + * @return Boolean + * @access public + */ + function chown($filename, $uid, $recursive = false) + { + // quoting from , + // "if the owner or group is specified as -1, then that ID is not changed" + $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1); + + return $this->_setstat($filename, $attr, $recursive); + } + + /** + * Changes file or directory group + * + * Returns TRUE on success or FALSE on error. + * + * @param String $filename + * @param Integer $gid + * @param optional Boolean $recursive + * @return Boolean + * @access public + */ + function chgrp($filename, $gid, $recursive = false) + { + $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid); + + return $this->_setstat($filename, $attr, $recursive); + } + + /** + * Set permissions on a file. + * + * Returns the new file permissions on success or FALSE on error. + * If $recursive is true than this just returns TRUE or FALSE. + * + * @param Integer $mode + * @param String $filename + * @param optional Boolean $recursive + * @return Mixed + * @access public + */ + function chmod($mode, $filename, $recursive = false) + { + if (is_string($mode) && is_int($filename)) { + $temp = $mode; + $mode = $filename; + $filename = $temp; + } + + $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + if (!$this->_setstat($filename, $attr, $recursive)) { + return false; + } + if ($recursive) { + return true; } // rather than return what the permissions *should* be, we'll return what they actually are. this will also @@ -1126,16 +1183,72 @@ class Net_SFTP extends Net_SSH2 { } /** - * Recursively chmods directories on the SFTP server + * Sets information about a file * - * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. - * - * @param Integer $mode * @param String $filename + * @param String $attr + * @param Boolean $recursive * @return Boolean * @access private */ - function _chmod_recursive($mode, $path, &$i) + function _setstat($filename, $attr, $recursive) + { + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if ($recursive) { + $i = 0; + $result = $this->_setstat_recursive($filename, $attr, $i); + $this->_read_put_responses($i); + return $result; + } + + // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to + // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT. + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) { + return false; + } + + /* + "Because some systems must use separate system calls to set various attributes, it is possible that a failure + response will be returned, but yet some of the attributes may be have been successfully modified. If possible, + servers SHOULD avoid this situation; however, clients MUST be aware that this is possible." + + -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6 + */ + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Recursively sets information on directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param String $path + * @param String $attr + * @param Integer $i + * @return Boolean + * @access private + */ + function _setstat_recursive($path, $attr, &$i) { if (!$this->_read_put_responses($i)) { return false; @@ -1144,7 +1257,7 @@ class Net_SFTP extends Net_SSH2 { $entries = $this->_list($path, true, false); if ($entries === false) { - return $this->chmod($mode, $path); + return $this->_setstat($path, $attr, false); } // normally $entries would have at least . and .. but it might not if the directories @@ -1164,18 +1277,17 @@ class Net_SFTP extends Net_SSH2 { $temp = $path . '/' . $filename; if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { - if (!$this->_chmod_recursive($mode, $temp, $i)) { + if (!$this->_setstat_recursive($temp, $attr, $i)) { return false; } } else { - $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) { return false; } $i++; - if ($i >= 50) { + if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } @@ -1184,14 +1296,13 @@ class Net_SFTP extends Net_SSH2 { } } - $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) { return false; } $i++; - if ($i >= 50) { + if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } @@ -1208,33 +1319,32 @@ class Net_SFTP extends Net_SSH2 { * @return Boolean * @access public */ - function mkdir($dir) + function mkdir($dir, $mode = -1, $recursive = false) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; } - if ($dir[0] != '/') { - $dir = $this->_realpath(rtrim($dir, '/')); - if ($dir === false) { - return false; + $dir = $this->_realpath($dir); + // by not providing any permissions, hopefully the server will use the logged in users umask - their + // default permissions. + $attr = $mode == -1 ? "\0\0\0\0" : pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + + if ($recursive) { + $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); + if (empty($dirs[0])) { + array_shift($dirs); + $dirs[0] = '/' . $dirs[0]; } - if (!$this->_mkdir_helper($dir)) { - return false; - } - } else { - $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir)); - $temp = ''; - foreach ($dirs as $dir) { - $temp.= '/' . $dir; - $result = $this->_mkdir_helper($temp); - } - if (!$result) { - return false; + for ($i = 0; $i < count($dirs); $i++) { + $temp = array_slice($dirs, 0, $i + 1); + $temp = implode('/', $temp); + $result = $this->_mkdir_helper($temp, $attr); } + return $result; } - return true; + return $this->_mkdir_helper($dir, $attr); } /** @@ -1244,11 +1354,9 @@ class Net_SFTP extends Net_SSH2 { * @return Boolean * @access private */ - function _mkdir_helper($dir) + function _mkdir_helper($dir, $attr) { - // by not providing any permissions, hopefully the server will use the logged in users umask - their - // default permissions. - if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*N', strlen($dir), $dir, 0))) { + if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, $attr))) { return false; } @@ -1323,14 +1431,33 @@ class Net_SFTP extends Net_SSH2 { * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take * care of that, yourself. * + * $mode can take an additional two parameters - NET_SFTP_RESUME and NET_SFTP_RESUME_START. These are bitwise AND'd with + * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: + * + * NET_SFTP_LOCAL_FILE | NET_SFTP_RESUME + * + * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace + * NET_SFTP_RESUME with NET_SFTP_RESUME_START. + * + * If $mode & (NET_SFTP_RESUME | NET_SFTP_RESUME_START) then NET_SFTP_RESUME_START will be assumed. + * + * $start and $local_start give you more fine grained control over this process and take precident over NET_SFTP_RESUME + * when they're non-negative. ie. $start could let you write at the end of a file (like NET_SFTP_RESUME) or in the middle + * of one. $local_start could let you start your reading from the end of a file (like NET_SFTP_RESUME_START) or in the + * middle of one. + * + * Setting $local_start to > 0 or $mode | NET_SFTP_RESUME_START doesn't do anything unless $mode | NET_SFTP_LOCAL_FILE. + * * @param String $remote_file * @param String $data * @param optional Integer $mode + * @param optional Integer $start + * @param optional Integer $local_start * @return Boolean * @access public * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode(). */ - function put($remote_file, $data, $mode = NET_SFTP_STRING) + function put($remote_file, $data, $mode = NET_SFTP_STRING, $start = -1, $local_start = -1) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; @@ -1346,15 +1473,16 @@ class Net_SFTP extends Net_SSH2 { // in practice, it doesn't seem to do that. //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; - // if NET_SFTP_OPEN_APPEND worked as it should the following (up until the -----------) wouldn't be necessary - $offset = 0; - if ($mode & NET_SFTP_RESUME) { + if ($start >= 0) { + $offset = $start; + } elseif ($mode & NET_SFTP_RESUME) { + // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called $size = $this->_size($remote_file); $offset = $size !== false ? $size : 0; } else { + $offset = 0; $flags|= NET_SFTP_OPEN_TRUNCATE; } - // -------------- $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { @@ -1387,6 +1515,14 @@ class Net_SFTP extends Net_SSH2 { return false; } $size = filesize($data); + + if ($local_start >= 0) { + fseek($fp, $local_start); + } elseif ($mode & NET_SFTP_RESUME_START) { + // do nothing + } else { + fseek($fp, $offset); + } } else { $size = strlen($data); } @@ -1395,10 +1531,13 @@ class Net_SFTP extends Net_SSH2 { $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; $sftp_packet_size = 4096; // PuTTY uses 4096 + // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header" + $sftp_packet_size-= strlen($handle) + 25; $i = 0; while ($sent < $size) { - $temp = $mode & NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size); - $packet = pack('Na*N3a*', strlen($handle), $handle, 0, $offset + $sent, strlen($temp), $temp); + $temp = $mode & NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); + $subtemp = $offset + $sent; + $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 0x100000000, $subtemp, strlen($temp), $temp); if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { fclose($fp); return false; @@ -1407,7 +1546,7 @@ class Net_SFTP extends Net_SSH2 { $i++; - if ($i == 50) { + if ($i == NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { $i = 0; break; @@ -1417,6 +1556,10 @@ class Net_SFTP extends Net_SSH2 { } if (!$this->_read_put_responses($i)) { + if ($mode & NET_SFTP_LOCAL_FILE) { + fclose($fp); + } + $this->_close_handle($handle); return false; } @@ -1424,23 +1567,7 @@ class Net_SFTP extends Net_SSH2 { fclose($fp); } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - return true; + return $this->_close_handle($handle); } /** @@ -1472,15 +1599,49 @@ class Net_SFTP extends Net_SSH2 { return $i < 0; } + /** + * Close handle + * + * @param String $handle + * @return Boolean + * @access private + */ + function _close_handle($handle) + { + if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + return false; + } + + // "The client MUST release all resources associated with the handle regardless of the status." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + /** * Downloads a file from the SFTP server. * * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the - * operation + * operation. + * + * $offset and $length can be used to download files in chunks. * * @param String $remote_file * @param optional String $local_file + * @param optional Integer $offset + * @param optional Integer $length * @return Mixed * @access public */ @@ -1522,10 +1683,9 @@ class Net_SFTP extends Net_SSH2 { $content = ''; } - $size = (1 << 20) < $length || $length < 0 ? 1 << 20 : $length; - $start = $offset; + $size = $this->max_sftp_packet < $length || $length < 0 ? $this->max_sftp_packet : $length; while (true) { - $packet = pack('Na*N3', strlen($handle), $handle, 0, $offset, $size); + $packet = pack('Na*N3', strlen($handle), $handle, $offset / 0x100000000, $offset, $size); if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { if ($local_file !== false) { fclose($fp); @@ -1545,6 +1705,7 @@ class Net_SFTP extends Net_SSH2 { } break; case NET_SFTP_STATUS: + // could, in theory, return false if !strlen($content) but we'll hold off for the time being $this->_logError($response); break 2; default: @@ -1556,40 +1717,28 @@ class Net_SFTP extends Net_SSH2 { } if ($length > 0 && $length <= $offset - $size) { - if ($local_file === false) { - $content = substr($content, 0, $length); - } else { - ftruncate($fp, $length); - } break; } } + if ($length > 0 && $length <= $offset - $size) { + if ($local_file === false) { + $content = substr($content, 0, $length); + } else { + ftruncate($fp, $length); + } + } + if ($local_file !== false) { fclose($fp); } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + if (!$this->_close_handle($handle)) { return false; } - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - if (isset($content)) { - return $content; - } - - return true; + // if $content isn't set that means a file was written to + return isset($content) ? $content : true; } /** @@ -1683,7 +1832,7 @@ class Net_SFTP extends Net_SSH2 { $i++; - if ($i >= 50) { + if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } @@ -1699,7 +1848,7 @@ class Net_SFTP extends Net_SSH2 { $i++; - if ($i >= 50) { + if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } @@ -1774,17 +1923,21 @@ class Net_SFTP extends Net_SSH2 { // (0xFFFFFFFF bytes), anyway. as such, we'll just represent all file sizes that are bigger than // 4GB as being 4GB. extract(unpack('Nupper/Nsize', $this->_string_shift($response, 8))); - if ($upper) { - $attr['size'] = 0xFFFFFFFF; - } else { - $attr['size'] = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; - } + $attr['size'] = $upper ? 0x100000000 * $upper : 0; + $attr['size']+= $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; break; case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); break; case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); + // mode == permissions; permissions was the original array key and is retained for bc purposes. + // mode was added because that's the more industry standard terminology + $attr+= array('mode' => $attr['permissions']); + $fileType = $this->_parseMode($attr['permissions']); + if ($fileType !== false) { + $attr+= array('type' => $fileType); + } break; case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); @@ -1802,6 +1955,47 @@ class Net_SFTP extends Net_SSH2 { return $attr; } + /** + * Attempt to identify the file type + * + * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway + * + * @param Integer $mode + * @return Integer + * @access private + */ + function _parseMode($mode) + { + // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12 + // see, also, http://linux.die.net/man/2/stat + switch ($mode & 0170000) {// ie. 1111 0000 0000 0000 + case 0000000: // no file type specified - figure out the file type using alternative means + return false; + case 0040000: + return NET_SFTP_TYPE_DIRECTORY; + case 0100000: + return NET_SFTP_TYPE_REGULAR; + case 0120000: + return NET_SFTP_TYPE_SYMLINK; + // new types introduced in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + case 0010000: // named pipe (fifo) + return NET_SFTP_TYPE_FIFO; + case 0020000: // character special + return NET_SFTP_TYPE_CHAR_DEVICE; + case 0060000: // block special + return NET_SFTP_BLOCK_DEVICE; + case 0140000: // socket + return NET_SFTP_TYPE_SOCKET; + case 0160000: // whiteout + // "SPECIAL should be used for files that are of + // a known type which cannot be expressed in the protocol" + return NET_SFTP_TYPE_SPECIAL; + default: + return NET_SFTP_TYPE_UNKNOWN; + } + } + /** * Parse Longname * @@ -2026,4 +2220,4 @@ class Net_SFTP extends Net_SSH2 { $this->pwd = false; parent::_disconnect($reason); } -} \ No newline at end of file +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP/Stream.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP/Stream.php new file mode 100644 index 0000000000..0572c5c402 --- /dev/null +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SFTP/Stream.php @@ -0,0 +1,771 @@ + + * @copyright MMXIII Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +/** + * SFTP Stream Wrapper + * + * @author Jim Wigginton + * @version 0.3.2 + * @access public + * @package Net_SFTP_Stream + */ +class Net_SFTP_Stream { + /** + * SFTP instances + * + * Rather than re-create the connection we re-use instances if possible + * + * @var Array + * @access static + */ + static $instances; + + /** + * SFTP instance + * + * @var Object + * @access private + */ + var $sftp; + + /** + * Path + * + * @var String + * @access private + */ + var $path; + + /** + * Mode + * + * @var String + * @access private + */ + var $mode; + + /** + * Position + * + * @var Integer + * @access private + */ + var $pos; + + /** + * Size + * + * @var Integer + * @access private + */ + var $size; + + /** + * Directory entries + * + * @var Array + * @access private + */ + var $entries; + + /** + * EOF flag + * + * @var Boolean + * @access private + */ + var $eof; + + /** + * Context resource + * + * Technically this needs to be publically accessible so PHP can set it directly + * + * @var Resource + * @access public + */ + var $context; + + /** + * Notification callback function + * + * @var Callable + * @access public + */ + var $notification; + + /** + * The Constructor + * + * @access public + */ + function Net_SFTP_Stream() + { + if (!class_exists('Net_SFTP')) { + require_once('Net/SFTP.php'); + } + } + + /** + * Path Parser + * + * Extract a path from a URI and actually connect to an SSH server if appropriate + * + * If "notification" is set as a context parameter the message code for successful login is + * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE. + * + * @param String $path + * @return String + * @access private + */ + function _parse_path($path) + { + extract(parse_url($path) + array('port' => 22)); + + if (!isset($host)) { + return false; + } + + if (isset($this->context)) { + $context = stream_context_get_params($this->context); + if (isset($context['notification'])) { + $this->notification = $context['notification']; + } + } + + if ($host[0] == '$') { + $host = substr($host, 1); + global $$host; + if (!is_object($$host) || get_class($$host) != 'Net_SFTP') { + return false; + } + $this->sftp = $$host; + } else { + if (isset($this->context)) { + $context = stream_context_get_options($this->context); + } + if (isset($context['sftp']['session'])) { + $sftp = $context['sftp']['session']; + } + if (isset($context['sftp']['sftp'])) { + $sftp = $context['sftp']['sftp']; + } + if (isset($sftp) && is_object($sftp) && get_class($sftp) == 'Net_SFTP') { + $this->sftp = $sftp; + return $path; + } + if (isset($context['sftp']['username'])) { + $user = $context['sftp']['username']; + } + if (isset($context['sftp']['password'])) { + $pass = $context['sftp']['password']; + } + if (isset($context['sftp']['privkey']) && is_object($context['sftp']['privkey']) && get_Class($context['sftp']['privkey']) == 'Crypt_RSA') { + $pass = $context['sftp']['privkey']; + } + + if (!isset($user) || !isset($pass)) { + return false; + } + + // casting $pass to a string is necessary in the event that it's a Crypt_RSA object + if (isset(self::$instances[$host][$port][$user][(string) $pass])) { + $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; + } else { + $this->sftp = new Net_SFTP($host, $port); + if (isset($this->notification) && is_callable($this->notification)) { + /* if !is_callable($this->notification) we could do this: + + user_error('fopen(): failed to call user notifier', E_USER_WARNING); + + the ftp wrapper gives errors like that when the notifier isn't callable. + i've opted not to do that, however, since the ftp wrapper gives the line + on which the fopen occurred as the line number - not the line that the + user_error is on. + */ + call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + if (!$this->sftp->login($user, $pass)) { + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); + return false; + } + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); + } else { + if (!$this->sftp->login($user, $pass)) { + return false; + } + } + self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; + } + } + + return $path; + } + + /** + * Opens file or URL + * + * @param String $path + * @param String $mode + * @param Integer $options + * @param String $opened_path + * @return Boolean + * @access public + */ + function _stream_open($path, $mode, $options, &$opened_path) + { + $path = $this->_parse_path($path); + + if ($path === false) { + return false; + } + $this->path = $path; + + $this->size = $this->sftp->size($path); + $this->mode = preg_replace('#[bt]$#', '', $mode); + $this->eof = false; + + if ($this->size === false) { + if ($this->mode[0] == 'r') { + return false; + } + } else { + switch ($this->mode[0]) { + case 'x': + return false; + case 'w': + case 'c': + $this->sftp->truncate($path, 0); + } + } + + $this->pos = $this->mode[0] != 'a' ? 0 : $this->size; + + return true; + } + + /** + * Read from stream + * + * @param Integer $count + * @return Mixed + * @access public + */ + function _stream_read($count) + { + switch ($this->mode) { + case 'w': + case 'a': + case 'x': + case 'c': + return false; + } + + // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite + //if ($this->pos >= $this->size) { + // $this->eof = true; + // return false; + //} + + $result = $this->sftp->get($this->path, false, $this->pos, $count); + if (isset($this->notification) && is_callable($this->notification)) { + if ($result === false) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP calls stream_read in 8k chunks + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size); + } + + if (empty($result)) { // ie. false or empty string + $this->eof = true; + return false; + } + $this->pos+= strlen($result); + + return $result; + } + + /** + * Write to stream + * + * @param String $data + * @return Mixed + * @access public + */ + function _stream_write($data) + { + switch ($this->mode) { + case 'r': + return false; + } + + $result = $this->sftp->put($this->path, $data, NET_SFTP_STRING, $this->pos); + if (isset($this->notification) && is_callable($this->notification)) { + if (!$result) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP splits up strings into 8k blocks before calling stream_write + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data)); + } + + if ($result === false) { + return false; + } + $this->pos+= strlen($data); + if ($this->pos > $this->size) { + $this->size = $this->pos; + } + $this->eof = false; + return strlen($data); + } + + /** + * Retrieve the current position of a stream + * + * @return Integer + * @access public + */ + function _stream_tell() + { + return $this->pos; + } + + /** + * Tests for end-of-file on a file pointer + * + * In my testing there are four classes functions that normally effect the pointer: + * fseek, fputs / fwrite, fgets / fread and ftruncate. + * + * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. + * + * @return Boolean + * @access public + */ + function _stream_eof() + { + return $this->eof; + } + + /** + * Seeks to specific location in a stream + * + * @param Integer $offset + * @param Integer $whence + * @return Boolean + * @access public + */ + function _stream_seek($offset, $whence) + { + switch ($whence) { + case SEEK_SET: + if ($offset >= $this->size || $offset < 0) { + return false; + } + break; + case SEEK_CUR: + $offset+= $this->pos; + break; + case SEEK_END: + $offset+= $this->size; + } + + $this->pos = $offset; + $this->eof = false; + return true; + } + + /** + * Change stream options + * + * @param String $path + * @param Integer $option + * @param Mixed $var + * @return Boolean + * @access public + */ + function _stream_metadata($path, $option, $var) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined + // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246 + // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 + switch ($option) { + case 1: // PHP_STREAM_META_TOUCH + return $this->sftp->touch($path, $var[0], $var[1]); + case 2: // PHP_STREAM_OWNER_NAME + case 3: // PHP_STREAM_GROUP_NAME + return false; + case 4: // PHP_STREAM_META_OWNER + return $this->sftp->chown($path, $var); + case 5: // PHP_STREAM_META_GROUP + return $this->sftp->chgrp($path, $var); + case 6: // PHP_STREAM_META_ACCESS + return $this->sftp->chmod($path, $var) !== false; + } + } + + /** + * Retrieve the underlaying resource + * + * @param Integer $cast_as + * @return Resource + * @access public + */ + function _stream_cast($cast_as) + { + return $this->sftp->fsock; + } + + /** + * Advisory file locking + * + * @param Integer $operation + * @return Boolean + * @access public + */ + function _stream_lock($operation) + { + return false; + } + + /** + * Renames a file or directory + * + * Attempts to rename oldname to newname, moving it between directories if necessary. + * If newname exists, it will be overwritten. This is a departure from what Net_SFTP + * does. + * + * @param String $path_from + * @param String $path_to + * @return Boolean + * @access public + */ + function _rename($path_from, $path_to) + { + $path1 = parse_url($path_from); + $path2 = parse_url($path_to); + unset($path1['path'], $path2['path']); + if ($path1 != $path2) { + return false; + } + + $path_from = $this->_parse_path($path_from); + $path_to = parse_url($path_to); + if ($path_from == false) { + return false; + } + + $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2 + // "It is an error if there already exists a file with the name specified by newpath." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5 + if (!$this->sftp->rename($path_from, $path_to)) { + if ($this->sftp->stat($path_to)) { + return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to); + } + return false; + } + + return true; + } + + /** + * Open directory handle + * + * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and + * removed in 5.4 I'm just going to ignore it + * + * @param String $path + * @param Integer $options + * @return Boolean + * @access public + */ + function _dir_opendir($path, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + $this->pos = 0; + $this->entries = $this->sftp->nlist($path); + return $this->entries !== false; + } + + /** + * Read entry from directory handle + * + * @return Mixed + * @access public + */ + function _dir_readdir() + { + if (isset($this->entries[$this->pos])) { + return $this->entries[$this->pos++]; + } + return false; + } + + /** + * Rewind directory handle + * + * @return Boolean + * @access public + */ + function _dir_rewinddir() + { + $this->pos = 0; + return true; + } + + /** + * Close directory handle + * + * @return Boolean + * @access public + */ + function _dir_closedir() + { + return true; + } + + /** + * Create a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE + * + * @param String $path + * @param Integer $mode + * @param Integer $options + * @return Boolean + * @access public + */ + function _mkdir($path, $mode, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE); + } + + /** + * Removes a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE per , however, + * does not have a $recursive parameter as mkdir() does so I don't know how + * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as + * $options. What does 8 correspond to? + * + * @param String $path + * @param Integer $mode + * @param Integer $options + * @return Boolean + * @access public + */ + function _rmdir($path, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->rmdir($path); + } + + /** + * Flushes the output + * + * See . Always returns true because Net_SFTP doesn't cache stuff before writing + * + * @return Boolean + * @access public + */ + function _stream_flush() + { + return true; + } + + /** + * Retrieve information about a file resource + * + * @return Mixed + * @access public + */ + function _stream_stat() + { + $results = $this->sftp->stat($this->path); + if ($results === false) { + return false; + } + return $results; + } + + /** + * Delete a file + * + * @param String $path + * @return Boolean + * @access public + */ + function _unlink($path) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->delete($path, false); + } + + /** + * Retrieve information about a file + * + * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of Net_SFTP_Stream is quiet by default + * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll + * cross that bridge when and if it's reached + * + * @param String $path + * @param Integer $flags + * @return Mixed + * @access public + */ + function _url_stat($path, $flags) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path); + if ($results === false) { + return false; + } + + return $results; + } + + /** + * Truncate stream + * + * @param Integer $new_size + * @return Boolean + * @access public + */ + function _stream_truncate($new_size) + { + if (!$this->sftp->truncate($this->path, $new_size)) { + return false; + } + + $this->eof = false; + $this->size = $new_size; + + return true; + } + + /** + * Change stream options + * + * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't. + * The other two aren't supported because of limitations in Net_SFTP. + * + * @param Integer $option + * @param Integer $arg1 + * @param Integer $arg2 + * @return Boolean + * @access public + */ + function _stream_set_option($option, $arg1, $arg2) + { + return false; + } + + /** + * Close an resource + * + * @access public + */ + function _stream_close() + { + } + + /** + * __call Magic Method + * + * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you. + * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function + * lets you figure that out. + * + * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not + * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. + * + * @param String + * @param Array + * @return Mixed + * @access public + */ + function __call($name, $arguments) + { + if (defined('NET_SFTP_STREAM_LOGGING')) { + echo $name . '('; + $last = count($arguments) - 1; + foreach ($arguments as $i => $argument) { + var_export($argument); + if ($i != $last) { + echo ','; + } + } + echo ")\r\n"; + } + $name = '_' . $name; + if (!method_exists($this, $name)) { + return false; + } + return call_user_func_array(array($this, $name), $arguments); + } +} + +if (function_exists('stream_wrapper_register')) { + stream_wrapper_register('sftp', 'Net_SFTP_Stream'); +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php index 8f5c79938e..83d5980d00 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH1.php @@ -62,56 +62,9 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: SSH1.php,v 1.15 2010/03/22 22:01:38 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ -/** - * Include Math_BigInteger - * - * Used to do RSA encryption. - */ -if (!class_exists('Math_BigInteger')) { - require_once('Math/BigInteger.php'); -} - -/** - * Include Crypt_Null - */ -//require_once('Crypt/Null.php'); - -/** - * Include Crypt_DES - */ -if (!class_exists('Crypt_DES')) { - require_once('Crypt/DES.php'); -} - -/** - * Include Crypt_TripleDES - */ -if (!class_exists('Crypt_TripleDES')) { - require_once('Crypt/TripleDES.php'); -} - -/** - * Include Crypt_RC4 - */ -if (!class_exists('Crypt_RC4')) { - require_once('Crypt/RC4.php'); -} - -/** - * Include Crypt_Random - */ -// the class_exists() will only be called if the crypt_random_string function hasn't been defined and -// will trigger a call to __autoload() if you're wanting to auto-load classes -// call function_exists() a second time to stop the require_once from being called outside -// of the auto loader -if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) { - require_once('Crypt/Random.php'); -} - /**#@+ * Encryption Methods * @@ -495,6 +448,19 @@ class Net_SSH1 { */ function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES) { + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + + // Include Crypt_Random + // the class_exists() will only be called if the crypt_random_string function hasn't been defined and + // will trigger a call to __autoload() if you're wanting to auto-load classes + // call function_exists() a second time to stop the require_once from being called outside + // of the auto loader + if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) { + require_once('Crypt/Random.php'); + } + $this->protocol_flags = array( 1 => 'NET_SSH1_MSG_DISCONNECT', 2 => 'NET_SSH1_SMSG_PUBLIC_KEY', @@ -636,18 +602,27 @@ class Net_SSH1 { // $this->crypto = new Crypt_Null(); // break; case NET_SSH1_CIPHER_DES: + if (!class_exists('Crypt_DES')) { + require_once('Crypt/DES.php'); + } $this->crypto = new Crypt_DES(); $this->crypto->disablePadding(); $this->crypto->enableContinuousBuffer(); $this->crypto->setKey(substr($session_key, 0, 8)); break; case NET_SSH1_CIPHER_3DES: + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $this->crypto = new Crypt_TripleDES(CRYPT_DES_MODE_3CBC); $this->crypto->disablePadding(); $this->crypto->enableContinuousBuffer(); $this->crypto->setKey(substr($session_key, 0, 24)); break; //case NET_SSH1_CIPHER_RC4: + // if (!class_exists('Crypt_RC4')) { + // require_once('Crypt/RC4.php'); + // } // $this->crypto = new Crypt_RC4(); // $this->crypto->enableContinuousBuffer(); // $this->crypto->setKey(substr($session_key, 0, 16)); @@ -935,7 +910,7 @@ class Net_SSH1 { * Returns the output of an interactive shell when no more output is available. * * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like - * "", you're seeing ANSI escape codes. According to + * "^[[00m", you're seeing ANSI escape codes. According to * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user, * there's not going to be much recourse. @@ -1574,4 +1549,4 @@ class Net_SSH1 { fputs($this->realtime_log_file, $entry); } } -} \ No newline at end of file +} diff --git a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php index 43bfbca2db..dad0369723 100644 --- a/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php +++ b/apps/files_external/3rdparty/phpseclib/phpseclib/Net/SSH2.php @@ -64,67 +64,20 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: SSH2.php,v 1.53 2010-10-24 01:24:30 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ -/** - * Include Math_BigInteger - * - * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. - */ -if (!class_exists('Math_BigInteger')) { - require_once('Math/BigInteger.php'); -} - -/** - * Include Crypt_Random - */ -// the class_exists() will only be called if the crypt_random_string function hasn't been defined and -// will trigger a call to __autoload() if you're wanting to auto-load classes -// call function_exists() a second time to stop the require_once from being called outside -// of the auto loader -if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) { - require_once('Crypt/Random.php'); -} - -/** - * Include Crypt_Hash - */ -if (!class_exists('Crypt_Hash')) { - require_once('Crypt/Hash.php'); -} - -/** - * Include Crypt_TripleDES - */ -if (!class_exists('Crypt_TripleDES')) { - require_once('Crypt/TripleDES.php'); -} - -/** - * Include Crypt_RC4 - */ -if (!class_exists('Crypt_RC4')) { - require_once('Crypt/RC4.php'); -} - -/** - * Include Crypt_AES - */ -if (!class_exists('Crypt_AES')) { - require_once('Crypt/AES.php'); -} - /**#@+ * Execution Bitmap Masks * * @see Net_SSH2::bitmap * @access private */ -define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001); -define('NET_SSH2_MASK_LOGIN', 0x00000002); -define('NET_SSH2_MASK_SHELL', 0x00000004); +define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001); +define('NET_SSH2_MASK_LOGIN_REQ', 0x00000002); +define('NET_SSH2_MASK_LOGIN', 0x00000004); +define('NET_SSH2_MASK_SHELL', 0x00000008); +define('NET_SSH2_MASK_WINDOW_ADJUST', 0X00000010); /**#@-*/ /**#@+ @@ -143,8 +96,9 @@ define('NET_SSH2_MASK_SHELL', 0x00000004); * @see Net_SSH2::_get_channel_packet() * @access private */ -define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100 -define('NET_SSH2_CHANNEL_SHELL',1); +define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100 +define('NET_SSH2_CHANNEL_SHELL', 1); +define('NET_SSH2_CHANNEL_SUBSYSTEM', 2); /**#@-*/ /**#@+ @@ -580,7 +534,7 @@ class Net_SSH2 { /** * The Window Size * - * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB) + * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) * * @var Integer * @see Net_SSH2::_send_channel_packet() @@ -590,7 +544,7 @@ class Net_SSH2 { var $window_size = 0x7FFFFFFF; /** - * Window size + * Window size, server to client * * Window size indexed by channel * @@ -598,6 +552,17 @@ class Net_SSH2 { * @var Array * @access private */ + var $window_size_server_to_client = array(); + + /** + * Window size, client to server + * + * Window size indexed by channel + * + * @see Net_SSH2::_get_channel_packet() + * @var Array + * @access private + */ var $window_size_client_to_server = array(); /** @@ -717,6 +682,71 @@ class Net_SSH2 { */ var $exit_status; + /** + * Flag to request a PTY when using exec() + * + * @see Net_SSH2::enablePTY() + * @access private + */ + var $request_pty = false; + + /** + * Flag set while exec() is running when using enablePTY() + * + * @access private + */ + var $in_request_pty_exec = false; + + /** + * Flag set after startSubsystem() is called + * + * @access private + */ + var $in_subsystem; + + /** + * Contents of stdError + * + * @access private + */ + var $stdErrorLog; + + /** + * The Last Interactive Response + * + * @see Net_SSH2::_keyboard_interactive_process() + * @access private + */ + var $last_interactive_response = ''; + + /** + * Keyboard Interactive Request / Responses + * + * @see Net_SSH2::_keyboard_interactive_process() + * @access private + */ + var $keyboard_requests_responses = array(); + + /** + * Banner Message + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @see Net_SSH2::_filter() + * @see Net_SSH2::getBannerMessage() + * @access private + */ + var $banner_message = ''; + + /** + * Did read() timeout or return normally? + * + * @see Net_SSH2::isTimeout + * @access private + */ + var $is_timeout = false; + /** * Default Constructor. * @@ -730,6 +760,20 @@ class Net_SSH2 { */ function Net_SSH2($host, $port = 22, $timeout = 10) { + // Include Math_BigInteger + // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + + if (!function_exists('crypt_random_string')) { + require_once('Crypt/Random.php'); + } + + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + $this->last_packet = strtok(microtime(), ' ') + strtok(''); // == microtime(true) in PHP5 $this->message_numbers = array( 1 => 'NET_SSH2_MSG_DISCONNECT', @@ -918,26 +962,76 @@ class Net_SSH2 { 'ssh-dss' // REQUIRED sign Raw DSS Key ); - static $encryption_algorithms = array( - // from : - 'arcfour256', - 'arcfour128', + static $encryption_algorithms = false; + if ($encryption_algorithms === false) { + $encryption_algorithms = array( + // from : + 'arcfour256', + 'arcfour128', - 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key - 'aes128-cbc', // RECOMMENDED AES with a 128-bit key - 'aes192-cbc', // OPTIONAL AES with a 192-bit key - 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key + // CTR modes from : + 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key + 'aes192-ctr', // RECOMMENDED AES with 192-bit key + 'aes256-ctr', // RECOMMENDED AES with 256-bit key - // from : - 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key - 'aes192-ctr', // RECOMMENDED AES with 192-bit key - 'aes256-ctr', // RECOMMENDED AES with 256-bit key - '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode + 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key + 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key + 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key - '3des-cbc', // REQUIRED three-key 3DES in CBC mode - 'none' // OPTIONAL no encryption; NOT RECOMMENDED - ); + 'aes128-cbc', // RECOMMENDED AES with a 128-bit key + 'aes192-cbc', // OPTIONAL AES with a 192-bit key + 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key + + 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key + 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key + 'twofish256-cbc', + 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc" + // (this is being retained for historical reasons) + + 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode + + 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode + + '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode + + '3des-cbc', // REQUIRED three-key 3DES in CBC mode + 'none' // OPTIONAL no encryption; NOT RECOMMENDED + ); + + if (!$this->_is_includable('Crypt/RC4.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('arcfour256', 'arcfour128', 'arcfour') + ); + } + if (!$this->_is_includable('Crypt/Rijndael.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc') + ); + } + if (!$this->_is_includable('Crypt/Twofish.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc') + ); + } + if (!$this->_is_includable('Crypt/Blowfish.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('blowfish-ctr', 'blowfish-cbc') + ); + } + if (!$this->_is_includable('Crypt/TripleDES.php')) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('3des-ctr', '3des-cbc') + ); + } + $encryption_algorithms = array_values($encryption_algorithms); + } static $mac_algorithms = array( 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) @@ -1045,14 +1139,23 @@ class Net_SSH2 { break; case 'aes256-cbc': case 'aes256-ctr': + case 'twofish-cbc': + case 'twofish256-cbc': + case 'twofish256-ctr': $decryptKeyLength = 32; // eg. 256 / 8 break; case 'aes192-cbc': case 'aes192-ctr': + case 'twofish192-cbc': + case 'twofish192-ctr': $decryptKeyLength = 24; // eg. 192 / 8 break; case 'aes128-cbc': case 'aes128-ctr': + case 'twofish128-cbc': + case 'twofish128-ctr': + case 'blowfish-cbc': + case 'blowfish-ctr': $decryptKeyLength = 16; // eg. 128 / 8 break; case 'arcfour': @@ -1080,14 +1183,23 @@ class Net_SSH2 { break; case 'aes256-cbc': case 'aes256-ctr': + case 'twofish-cbc': + case 'twofish256-cbc': + case 'twofish256-ctr': $encryptKeyLength = 32; break; case 'aes192-cbc': case 'aes192-ctr': + case 'twofish192-cbc': + case 'twofish192-ctr': $encryptKeyLength = 24; break; case 'aes128-cbc': case 'aes128-ctr': + case 'twofish128-cbc': + case 'twofish128-ctr': + case 'blowfish-cbc': + case 'blowfish-ctr': $encryptKeyLength = 16; break; case 'arcfour': @@ -1114,28 +1226,29 @@ class Net_SSH2 { // see http://tools.ietf.org/html/rfc2409#section-6.2 and // http://tools.ietf.org/html/rfc2412, appendex E case 'diffie-hellman-group1-sha1': - $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . - '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . - '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . - 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'); - $keyLength = $keyLength < 160 ? $keyLength : 160; - $hash = 'sha1'; + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; break; // see http://tools.ietf.org/html/rfc3526#section-3 case 'diffie-hellman-group14-sha1': - $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . - '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . - '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . - 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . - '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . - '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . - 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . - '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'); - $keyLength = $keyLength < 160 ? $keyLength : 160; - $hash = 'sha1'; + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . + '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . + '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . + '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; + break; } - $p = new Math_BigInteger($p, 256); + // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1 + // the generator field element is 2 (decimal) and the hash function is sha1. + $g = new Math_BigInteger(2); + $prime = new Math_BigInteger($prime, 16); + $kexHash = new Crypt_Hash('sha1'); //$q = $p->bitwise_rightShift(1); /* To increase the speed of the key exchange, both client and server may @@ -1145,14 +1258,12 @@ class Net_SSH2 { [VAN-OORSCHOT]. -- http://tools.ietf.org/html/rfc4419#section-6.2 */ - $q = new Math_BigInteger(1); - $q = $q->bitwise_leftShift(2 * $keyLength); - $q = $q->subtract(new Math_BigInteger(1)); + $one = new Math_BigInteger(1); + $keyLength = min($keyLength, $kexHash->getLength()); + $max = $one->bitwise_leftShift(16 * $keyLength)->subtract($one); // 2 * 8 * $keyLength - $g = new Math_BigInteger(2); - $x = new Math_BigInteger(); - $x = $x->random(new Math_BigInteger(1), $q); - $e = $g->modPow($x, $p); + $x = $one->random($one, $max); + $e = $g->modPow($x, $prime); $eBytes = $e->toBytes(true); $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes); @@ -1190,7 +1301,7 @@ class Net_SSH2 { $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); $this->signature_format = $this->_string_shift($this->signature, $temp['length']); - $key = $f->modPow($x, $p); + $key = $f->modPow($x, $prime); $keyBytes = $key->toBytes(true); $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*', @@ -1200,7 +1311,7 @@ class Net_SSH2 { $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes ); - $this->exchange_hash = pack('H*', $hash($this->exchange_hash)); + $this->exchange_hash = $kexHash->hash($this->exchange_hash); if ($this->session_id === false) { $this->session_id = $this->exchange_hash; @@ -1213,7 +1324,7 @@ class Net_SSH2 { } if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) { - user_error('Sever Host Key Algorithm Mismatch'); + user_error('Server Host Key Algorithm Mismatch'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } @@ -1241,28 +1352,76 @@ class Net_SSH2 { switch ($encrypt) { case '3des-cbc': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $this->encrypt = new Crypt_TripleDES(); // $this->encrypt_block_size = 64 / 8 == the default break; case '3des-ctr': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); // $this->encrypt_block_size = 64 / 8 == the default break; case 'aes256-cbc': case 'aes192-cbc': case 'aes128-cbc': - $this->encrypt = new Crypt_AES(); + if (!class_exists('Crypt_Rijndael')) { + require_once('Crypt/Rijndael.php'); + } + $this->encrypt = new Crypt_Rijndael(); $this->encrypt_block_size = 16; // eg. 128 / 8 break; case 'aes256-ctr': case 'aes192-ctr': case 'aes128-ctr': - $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR); + if (!class_exists('Crypt_Rijndael')) { + require_once('Crypt/Rijndael.php'); + } + $this->encrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR); $this->encrypt_block_size = 16; // eg. 128 / 8 break; + case 'blowfish-cbc': + if (!class_exists('Crypt_Blowfish')) { + require_once('Crypt/Blowfish.php'); + } + $this->encrypt = new Crypt_Blowfish(); + $this->encrypt_block_size = 8; + break; + case 'blowfish-ctr': + if (!class_exists('Crypt_Blowfish')) { + require_once('Crypt/Blowfish.php'); + } + $this->encrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR); + $this->encrypt_block_size = 8; + break; + case 'twofish128-cbc': + case 'twofish192-cbc': + case 'twofish256-cbc': + case 'twofish-cbc': + if (!class_exists('Crypt_Twofish')) { + require_once('Crypt/Twofish.php'); + } + $this->encrypt = new Crypt_Twofish(); + $this->encrypt_block_size = 16; + break; + case 'twofish128-ctr': + case 'twofish192-ctr': + case 'twofish256-ctr': + if (!class_exists('Crypt_Twofish')) { + require_once('Crypt/Twofish.php'); + } + $this->encrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR); + $this->encrypt_block_size = 16; + break; case 'arcfour': case 'arcfour128': case 'arcfour256': + if (!class_exists('Crypt_RC4')) { + require_once('Crypt/RC4.php'); + } $this->encrypt = new Crypt_RC4(); break; case 'none'; @@ -1271,26 +1430,74 @@ class Net_SSH2 { switch ($decrypt) { case '3des-cbc': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $this->decrypt = new Crypt_TripleDES(); break; case '3des-ctr': + if (!class_exists('Crypt_TripleDES')) { + require_once('Crypt/TripleDES.php'); + } $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); break; case 'aes256-cbc': case 'aes192-cbc': case 'aes128-cbc': - $this->decrypt = new Crypt_AES(); + if (!class_exists('Crypt_Rijndael')) { + require_once('Crypt/Rijndael.php'); + } + $this->decrypt = new Crypt_Rijndael(); $this->decrypt_block_size = 16; break; case 'aes256-ctr': case 'aes192-ctr': case 'aes128-ctr': - $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR); + if (!class_exists('Crypt_Rijndael')) { + require_once('Crypt/Rijndael.php'); + } + $this->decrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR); + $this->decrypt_block_size = 16; + break; + case 'blowfish-cbc': + if (!class_exists('Crypt_Blowfish')) { + require_once('Crypt/Blowfish.php'); + } + $this->decrypt = new Crypt_Blowfish(); + $this->decrypt_block_size = 8; + break; + case 'blowfish-ctr': + if (!class_exists('Crypt_Blowfish')) { + require_once('Crypt/Blowfish.php'); + } + $this->decrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR); + $this->decrypt_block_size = 8; + break; + case 'twofish128-cbc': + case 'twofish192-cbc': + case 'twofish256-cbc': + case 'twofish-cbc': + if (!class_exists('Crypt_Twofish')) { + require_once('Crypt/Twofish.php'); + } + $this->decrypt = new Crypt_Twofish(); + $this->decrypt_block_size = 16; + break; + case 'twofish128-ctr': + case 'twofish192-ctr': + case 'twofish256-ctr': + if (!class_exists('Crypt_Twofish')) { + require_once('Crypt/Twofish.php'); + } + $this->decrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR); $this->decrypt_block_size = 16; break; case 'arcfour': case 'arcfour128': case 'arcfour256': + if (!class_exists('Crypt_RC4')) { + require_once('Crypt/RC4.php'); + } $this->decrypt = new Crypt_RC4(); break; case 'none'; @@ -1303,15 +1510,15 @@ class Net_SSH2 { $this->encrypt->enableContinuousBuffer(); $this->encrypt->disablePadding(); - $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id)); + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); while ($this->encrypt_block_size > strlen($iv)) { - $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv)); + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); } $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); while ($encryptKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); } @@ -1320,15 +1527,15 @@ class Net_SSH2 { $this->decrypt->enableContinuousBuffer(); $this->decrypt->disablePadding(); - $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id)); + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); while ($this->decrypt_block_size > strlen($iv)) { - $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv)); + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); } $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); while ($decryptKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); } @@ -1402,15 +1609,15 @@ class Net_SSH2 { $this->hmac_size = 12; } - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); while ($createKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_create->setKey(substr($key, 0, $createKeyLength)); - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); while ($checkKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); @@ -1434,40 +1641,72 @@ class Net_SSH2 { /** * Login * - * The $password parameter can be a plaintext password or a Crypt_RSA object. + * The $password parameter can be a plaintext password, a Crypt_RSA object or an array + * + * @param String $username + * @param Mixed $password + * @param Mixed $... + * @return Boolean + * @see _login_helper + * @access public + */ + function login($username) + { + $args = array_slice(func_get_args(), 1); + if (empty($args)) { + return $this->_login_helper($username); + } + + foreach ($args as $arg) { + if ($this->_login_helper($username, $arg)) { + return true; + } + } + return false; + } + + /** + * Login Helper * * @param String $username * @param optional String $password * @return Boolean - * @access public + * @access private * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * by sending dummy SSH_MSG_IGNORE messages. */ - function login($username, $password = null) + function _login_helper($username, $password = null) { if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) { return false; } - $packet = pack('CNa*', - NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth' - ); + if (!($this->bitmap & NET_SSH2_MASK_LOGIN_REQ)) { + $packet = pack('CNa*', + NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth' + ); - if (!$this->_send_binary_packet($packet)) { - return false; + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { + user_error('Expected SSH_MSG_SERVICE_ACCEPT'); + return false; + } + $this->bitmap |= NET_SSH2_MASK_LOGIN_REQ; } - $response = $this->_get_binary_packet(); - if ($response === false) { - user_error('Connection closed by server'); - return false; - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { - user_error('Expected SSH_MSG_SERVICE_ACCEPT'); - return false; + if (strlen($this->last_interactive_response)) { + return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password); } // although PHP5's get_class() preserves the case, PHP4's does not @@ -1475,6 +1714,14 @@ class Net_SSH2 { return $this->_privatekey_login($username, $password); } + if (is_array($password)) { + if ($this->_keyboard_interactive_login($username, $password)) { + $this->bitmap |= NET_SSH2_MASK_LOGIN; + return true; + } + return false; + } + if (!isset($password)) { $packet = pack('CNa*Na*Na*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', @@ -1508,17 +1755,18 @@ class Net_SSH2 { strlen('password'), 'password', 0, strlen($password), $password ); - if (!$this->_send_binary_packet($packet)) { - return false; - } - - // remove the username and password from the last logged packet - if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) { - $packet = pack('CNa*Na*Na*CNa*', + // remove the username and password from the logged packet + if (!defined('NET_SSH2_LOGGING')) { + $logged = NULL; + } else { + $logged = pack('CNa*Na*Na*CNa*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection', strlen('password'), 'password', 0, strlen('password'), 'password' ); - $this->message_log[count($this->message_log) - 1] = $packet; + } + + if (!$this->_send_binary_packet($packet, $logged)) { + return false; } $response = $this->_get_binary_packet(); @@ -1542,7 +1790,10 @@ class Net_SSH2 { // multi-factor authentication extract(unpack('Nlength', $this->_string_shift($response, 4))); $auth_methods = explode(',', $this->_string_shift($response, $length)); - if (in_array('keyboard-interactive', $auth_methods)) { + extract(unpack('Cpartial_success', $this->_string_shift($response, 1))); + $partial_success = $partial_success != 0; + + if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { if ($this->_keyboard_interactive_login($username, $password)) { $this->bitmap |= NET_SSH2_MASK_LOGIN; return true; @@ -1593,25 +1844,20 @@ class Net_SSH2 { { $responses = func_get_args(); - $response = $this->_get_binary_packet(); - if ($response === false) { - user_error('Connection closed by server'); - return false; + if (strlen($this->last_interactive_response)) { + $response = $this->last_interactive_response; + } else { + $orig = $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: - // see http://tools.ietf.org/html/rfc4256#section-3.2 - if (defined('NET_SSH2_LOGGING')) { - $this->message_number_log[count($this->message_number_log) - 1] = str_replace( - 'UNKNOWN', - 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', - $this->message_number_log[count($this->message_number_log) - 1] - ); - } - extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // name; may be empty extract(unpack('Nlength', $this->_string_shift($response, 4))); @@ -1619,14 +1865,48 @@ class Net_SSH2 { extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // language tag; may be empty extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); - /* - for ($i = 0; $i < $num_prompts; $i++) { - extract(unpack('Nlength', $this->_string_shift($response, 4))); - // prompt - ie. "Password: "; must not be empty - $this->_string_shift($response, $length); - $echo = $this->_string_shift($response) != chr(0); + + for ($i = 0; $i < count($responses); $i++) { + if (is_array($responses[$i])) { + foreach ($responses[$i] as $key => $value) { + $this->keyboard_requests_responses[$key] = $value; + } + unset($responses[$i]); + } + } + $responses = array_values($responses); + + if (isset($this->keyboard_requests_responses)) { + for ($i = 0; $i < $num_prompts; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + // prompt - ie. "Password: "; must not be empty + $prompt = $this->_string_shift($response, $length); + //$echo = $this->_string_shift($response) != chr(0); + foreach ($this->keyboard_requests_responses as $key => $value) { + if (substr($prompt, 0, strlen($key)) == $key) { + $responses[] = $value; + break; + } + } + } + } + + // see http://tools.ietf.org/html/rfc4256#section-3.2 + if (strlen($this->last_interactive_response)) { + $this->last_interactive_response = ''; + } else if (defined('NET_SSH2_LOGGING')) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + 'UNKNOWN', + 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', + $this->message_number_log[count($this->message_number_log) - 1] + ); + } + + if (!count($responses) && $num_prompts) { + $this->last_interactive_response = $orig; + $this->bitmap |= NET_SSH_MASK_LOGIN_INTERACTIVE; + return false; } - */ /* After obtaining the requested information from the user, the client @@ -1639,17 +1919,16 @@ class Net_SSH2 { $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer'); } - if (!$this->_send_binary_packet($packet)) { + if (!$this->_send_binary_packet($packet, $logged)) { return false; } - if (defined('NET_SSH2_LOGGING')) { + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) { $this->message_number_log[count($this->message_number_log) - 1] = str_replace( 'UNKNOWN', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE', $this->message_number_log[count($this->message_number_log) - 1] ); - $this->message_log[count($this->message_log) - 1] = $logged; } /* @@ -1718,11 +1997,11 @@ class Net_SSH2 { case NET_SSH2_MSG_USERAUTH_FAILURE: extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); - return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); + return false; case NET_SSH2_MSG_USERAUTH_PK_OK: // we'll just take it on faith that the public key blob and the public key algorithm name are as // they should be - if (defined('NET_SSH2_LOGGING')) { + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) { $this->message_number_log[count($this->message_number_log) - 1] = str_replace( 'UNKNOWN', 'NET_SSH2_MSG_USERAUTH_PK_OK', @@ -1768,12 +2047,23 @@ class Net_SSH2 { * Setting $timeout to false or 0 will mean there is no timeout. * * @param Mixed $timeout + * @access public */ function setTimeout($timeout) { $this->timeout = $this->curTimeout = $timeout; } + /** + * Get the output from stdError + * + * @access public + */ + function getStdError() + { + return $this->stdErrorLog; + } + /** * Execute Command * @@ -1785,25 +2075,27 @@ class Net_SSH2 { * @return String * @access public */ - function exec($command, $block = true) + function exec($command, $callback = NULL) { $this->curTimeout = $this->timeout; + $this->is_timeout = false; + $this->stdErrorLog = ''; if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; } // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to - // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but, - // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway. + // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, + // honestly, if you're transfering more than 2GB, you probably shouldn't be using phpseclib, anyway. // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info - $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF; + $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF; // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy // uses 0x4000, that's what will be used here, as well. $packet_size = 0x4000; $packet = pack('CNa*N3', - NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC], $packet_size); + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC], $packet_size); if (!$this->_send_binary_packet($packet)) { return false; @@ -1816,6 +2108,34 @@ class Net_SSH2 { return false; } + if ($this->request_pty === true) { + $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); + $packet = pack('CNNa*CNa*N5a*', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100', + 80, 24, 0, 0, strlen($terminal_modes), $terminal_modes); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + + list(, $type) = unpack('C', $this->_string_shift($response, 1)); + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + break; + case NET_SSH2_MSG_CHANNEL_FAILURE: + default: + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + $this->in_request_pty_exec = true; + } + // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &'). // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then @@ -1840,7 +2160,7 @@ class Net_SSH2 { $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; - if (!$block) { + if ($callback === false || $this->in_request_pty_exec) { return true; } @@ -1849,11 +2169,15 @@ class Net_SSH2 { $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC); switch (true) { case $temp === true: - return $output; + return is_callable($callback) ? true : $output; case $temp === false: return false; default: - $output.= $temp; + if (is_callable($callback)) { + $callback($temp); + } else { + $output.= $temp; + } } } } @@ -1868,11 +2192,15 @@ class Net_SSH2 { */ function _initShell() { - $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF; + if ($this->in_request_pty_exec === true) { + return true; + } + + $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF; $packet_size = 0x4000; $packet = pack('CNa*N3', - NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL], $packet_size); + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL], $packet_size); if (!$this->_send_binary_packet($packet)) { return false; @@ -1904,8 +2232,9 @@ class Net_SSH2 { switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: - break; + // if a pty can't be opened maybe commands can still be executed case NET_SSH2_MSG_CHANNEL_FAILURE: + break; default: user_error('Unable to request pseudo-terminal'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); @@ -1931,13 +2260,33 @@ class Net_SSH2 { return true; } + /** + * Return the channel to be used with read() / write() + * + * @see Net_SSH2::read() + * @see Net_SSH2::write() + * @return Integer + * @access public + */ + function _get_interactive_channel() + { + switch (true) { + case $this->in_subsystem: + return NET_SSH2_CHANNEL_SUBSYSTEM; + case $this->in_request_pty_exec: + return NET_SSH2_CHANNEL_EXEC; + default: + return NET_SSH2_CHANNEL_SHELL; + } + } + /** * Returns the output of an interactive shell * * Returns when there's a match for $expect, which can take the form of a string literal or, * if $mode == NET_SSH2_READ_REGEX, a regular expression. * - * @see Net_SSH2::read() + * @see Net_SSH2::write() * @param String $expect * @param Integer $mode * @return String @@ -1946,6 +2295,7 @@ class Net_SSH2 { function read($expect = '', $mode = NET_SSH2_READ_SIMPLE) { $this->curTimeout = $this->timeout; + $this->is_timeout = false; if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { user_error('Operation disallowed prior to login()'); @@ -1957,6 +2307,8 @@ class Net_SSH2 { return false; } + $channel = $this->_get_interactive_channel(); + $match = $expect; while (true) { if ($mode == NET_SSH2_READ_REGEX) { @@ -1967,8 +2319,9 @@ class Net_SSH2 { if ($pos !== false) { return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); } - $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL); + $response = $this->_get_channel_packet($channel); if (is_bool($response)) { + $this->in_request_pty_exec = false; return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false; } @@ -1979,7 +2332,7 @@ class Net_SSH2 { /** * Inputs a command into an interactive shell. * - * @see Net_SSH1::interactiveWrite() + * @see Net_SSH2::read() * @param String $cmd * @return Boolean * @access public @@ -1996,7 +2349,101 @@ class Net_SSH2 { return false; } - return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd); + $channel = $this->in_request_pty_exec ? NET_SSH2_CHANNEL_EXEC : NET_SSH2_CHANNEL_SHELL; + return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); + } + + /** + * Start a subsystem. + * + * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept + * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened. + * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and + * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented + * if there's sufficient demand for such a feature. + * + * @see Net_SSH2::stopSubsystem() + * @param String $subsystem + * @return Boolean + * @access public + */ + function startSubsystem($subsystem) + { + $this->window_size_server_to_client[NET_SSH2_CHANNEL_SUBSYSTEM] = $this->window_size; + + $packet = pack('CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SUBSYSTEM, $this->window_size, 0x4000); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM); + if ($response === false) { + return false; + } + + $packet = pack('CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SUBSYSTEM], strlen('subsystem'), 'subsystem', 1, strlen($subsystem), $subsystem); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM); + + if ($response === false) { + return false; + } + + $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; + + $this->bitmap |= NET_SSH2_MASK_SHELL; + $this->in_subsystem = true; + + return true; + } + + /** + * Stops a subsystem. + * + * @see Net_SSH2::startSubsystem() + * @return Boolean + * @access public + */ + function stopSubsystem() + { + $this->in_subsystem = false; + $this->_close_channel(NET_SSH2_CHANNEL_SUBSYSTEM); + return true; + } + + /** + * Closes a channel + * + * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call + * + * @access public + */ + function reset() + { + $channel = $this->in_request_pty_exec ? NET_SSH2_CHANNEL_EXEC : NET_SSH2_CHANNEL_SHELL; + $this->_close_channel($channel); + } + + /** + * Is timeout? + * + * Did exec() or read() return because they timed out or because they encountered the end? + * + * @access public + */ + function isTimeout() + { + return $this->is_timeout; } /** @@ -2025,6 +2472,16 @@ class Net_SSH2 { $this->disconnect(); } + /** + * Is the connection still active? + * + * @access public + */ + function isConnected() + { + return $this->bitmap & NET_SSH2_MASK_LOGIN; + } + /** * Gets Binary Packets * @@ -2038,7 +2495,7 @@ class Net_SSH2 { { if (!is_resource($this->fsock) || feof($this->fsock)) { user_error('Connection closed prematurely'); - $this->bitmask = 0; + $this->bitmap = 0; return false; } @@ -2125,7 +2582,7 @@ class Net_SSH2 { $this->_string_shift($payload, 1); extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length)); - $this->bitmask = 0; + $this->bitmap = 0; return false; case NET_SSH2_MSG_IGNORE: $payload = $this->_get_binary_packet(); @@ -2141,7 +2598,7 @@ class Net_SSH2 { case NET_SSH2_MSG_KEXINIT: if ($this->session_id !== false) { if (!$this->_key_exchange($payload)) { - $this->bitmask = 0; + $this->bitmap = 0; return false; } $payload = $this->_get_binary_packet(); @@ -2152,7 +2609,7 @@ class Net_SSH2 { if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { $this->_string_shift($payload, 1); extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length)); + $this->banner_message = utf8_decode($this->_string_shift($payload, $length)); $payload = $this->_get_binary_packet(); } @@ -2172,7 +2629,7 @@ class Net_SSH2 { break; case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 $this->_string_shift($payload, 1); - extract(unpack('N', $this->_string_shift($payload, 4))); + extract(unpack('Nlength', $this->_string_shift($payload, 4))); $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length)); $this->_string_shift($payload, 4); // skip over client channel @@ -2188,7 +2645,12 @@ class Net_SSH2 { $payload = $this->_get_binary_packet(); break; case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: - $payload = $this->_get_binary_packet(); + $this->_string_shift($payload, 1); + extract(unpack('Nchannel', $this->_string_shift($payload, 4))); + extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); + $this->window_size_client_to_server[$channel]+= $window_size; + + $payload = ($this->bitmap & NET_SSH2_MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet(); } } @@ -2219,6 +2681,26 @@ class Net_SSH2 { $this->quiet_mode = false; } + /** + * Enable request-pty when using exec() + * + * @access public + */ + function enablePTY() + { + $this->request_pty = true; + } + + /** + * Disable request-pty when using exec() + * + * @access public + */ + function disablePTY() + { + $this->request_pty = false; + } + /** * Gets channel data * @@ -2236,6 +2718,11 @@ class Net_SSH2 { while (true) { if ($this->curTimeout) { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + $read = array($this->fsock); $write = $except = NULL; @@ -2244,7 +2731,7 @@ class Net_SSH2 { $usec = 1000000 * ($this->curTimeout - $sec); // on windows this returns a "Warning: Invalid CRT parameters detected" error if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { - $this->_close_channel($client_channel); + $this->is_timeout = true; return true; } $elapsed = strtok(microtime(), ' ') + strtok('') - $start; @@ -2256,20 +2743,34 @@ class Net_SSH2 { user_error('Connection closed by server'); return false; } - + if ($client_channel == -1 && $response === true) { + return true; + } if (!strlen($response)) { return ''; } extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5))); + $this->window_size_server_to_client[$channel]-= strlen($response) + 4; + + // resize the window, if appropriate + if ($this->window_size_server_to_client[$channel] < 0) { + $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size); + if (!$this->_send_binary_packet($packet)) { + return false; + } + $this->window_size_server_to_client[$channel]+= $this->window_size; + } + switch ($this->channel_status[$channel]) { case NET_SSH2_MSG_CHANNEL_OPEN: switch ($type) { case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); $this->server_channels[$channel] = $server_channel; - $this->_string_shift($response, 4); // skip over (server) window size + extract(unpack('Nwindow_size', $this->_string_shift($response, 4))); + $this->window_size_client_to_server[$channel] = $window_size; $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; return $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); @@ -2283,24 +2784,27 @@ class Net_SSH2 { switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: return true; - //case NET_SSH2_MSG_CHANNEL_FAILURE: + case NET_SSH2_MSG_CHANNEL_FAILURE: + return false; default: - user_error('Unable to request pseudo-terminal'); + user_error('Unable to fulfill channel request'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } case NET_SSH2_MSG_CHANNEL_CLOSE: return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); } + // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA + switch ($type) { case NET_SSH2_MSG_CHANNEL_DATA: /* - if ($client_channel == NET_SSH2_CHANNEL_EXEC) { + if ($channel == NET_SSH2_CHANNEL_EXEC) { // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server // this actually seems to make things twice as fast. more to the point, the message right after // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. // in OpenSSH it slows things down but only by a couple thousandths of a second. - $this->_send_channel_packet($client_channel, chr(0)); + $this->_send_channel_packet($channel, chr(0)); } */ extract(unpack('Nlength', $this->_string_shift($response, 4))); @@ -2308,15 +2812,12 @@ class Net_SSH2 { if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$client_channel])) { - $this->channel_buffers[$client_channel] = array(); + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); } - $this->channel_buffers[$client_channel][] = $data; + $this->channel_buffers[$channel][] = $data; break; case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: - if ($skip_extended || $this->quiet_mode) { - break; - } /* if ($client_channel == NET_SSH2_CHANNEL_EXEC) { $this->_send_channel_packet($client_channel, chr(0)); @@ -2325,13 +2826,17 @@ class Net_SSH2 { // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); $data = $this->_string_shift($response, $length); + $this->stdErrorLog .= $data; + if ($skip_extended || $this->quiet_mode) { + break; + } if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$client_channel])) { - $this->channel_buffers[$client_channel] = array(); + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); } - $this->channel_buffers[$client_channel][] = $data; + $this->channel_buffers[$channel][] = $data; break; case NET_SSH2_MSG_CHANNEL_REQUEST: extract(unpack('Nlength', $this->_string_shift($response, 4))); @@ -2346,6 +2851,13 @@ class Net_SSH2 { if ($length) { $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); } + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + break; case 'exit-status': extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); $this->exit_status = $exit_status; @@ -2355,6 +2867,8 @@ class Net_SSH2 { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + break; default: // "Some systems may not implement signals, in which case they SHOULD ignore this message." // -- http://tools.ietf.org/html/rfc4254#section-6.9 @@ -2388,15 +2902,16 @@ class Net_SSH2 { * See '6. Binary Packet Protocol' of rfc4253 for more info. * * @param String $data + * @param optional String $logged * @see Net_SSH2::_get_binary_packet() * @return Boolean * @access private */ - function _send_binary_packet($data) + function _send_binary_packet($data, $logged = NULL) { if (!is_resource($this->fsock) || feof($this->fsock)) { user_error('Connection closed prematurely'); - $this->bitmask = 0; + $this->bitmap = 0; return false; } @@ -2435,7 +2950,7 @@ class Net_SSH2 { $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')'; $message_number = '-> ' . $message_number . ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; - $this->_append_log($message_number, $data); + $this->_append_log($message_number, isset($logged) ? $logged : $data); $this->last_packet = $current; } @@ -2452,6 +2967,11 @@ class Net_SSH2 { */ function _append_log($message_number, $message) { + // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) + if (strlen($message_number) > 2) { + $this->_string_shift($message); + } + switch (NET_SSH2_LOGGING) { // useful for benchmarks case NET_SSH2_LOG_SIMPLE: @@ -2460,7 +2980,6 @@ class Net_SSH2 { // the most useful log for SSH2 case NET_SSH2_LOG_COMPLEX: $this->message_number_log[] = $message_number; - $this->_string_shift($message); $this->log_size+= strlen($message); $this->message_log[] = $message; while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) { @@ -2472,7 +2991,15 @@ class Net_SSH2 { // passwords won't be filtered out and select other packets may not be correctly // identified case NET_SSH2_LOG_REALTIME: - echo "
\r\n" . $this->_format_log(array($message), array($message_number)) . "\r\n
\r\n"; + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
';
+                            $stop = '
'; + } + echo $start . $this->_format_log(array($message), array($message_number)) . $stop; @flush(); @ob_flush(); break; @@ -2483,7 +3010,7 @@ class Net_SSH2 { case NET_SSH2_LOG_REALTIME_FILE: if (!isset($this->realtime_log_file)) { // PHP doesn't seem to like using constants in fopen() - $filename = NET_SSH2_LOG_REALTIME_FILE; + $filename = NET_SSH2_LOG_REALTIME_FILENAME; $fp = fopen($filename, 'w'); $this->realtime_log_file = $fp; } @@ -2518,39 +3045,49 @@ class Net_SSH2 { */ function _send_channel_packet($client_channel, $data) { - while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) { - // resize the window, if appropriate - $this->window_size_client_to_server[$client_channel]-= $this->packet_size_client_to_server[$client_channel]; - if ($this->window_size_client_to_server[$client_channel] < 0) { - $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size); - if (!$this->_send_binary_packet($packet)) { - return false; - } - $this->window_size_client_to_server[$client_channel]+= $this->window_size; + /* The maximum amount of data allowed is determined by the maximum + packet size for the channel, and the current window size, whichever + is smaller. + + -- http://tools.ietf.org/html/rfc4254#section-5.2 */ + $max_size = min( + $this->packet_size_client_to_server[$client_channel], + $this->window_size_client_to_server[$client_channel] + ) - 4; + while (strlen($data) > $max_size) { + if (!$this->window_size_client_to_server[$client_channel]) { + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + // using an invalid channel will let the buffers be built up for the valid channels + $output = $this->_get_channel_packet(-1); + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + $max_size = min( + $this->packet_size_client_to_server[$client_channel], + $this->window_size_client_to_server[$client_channel] + ) - 4; } $packet = pack('CN2a*', NET_SSH2_MSG_CHANNEL_DATA, $this->server_channels[$client_channel], - $this->packet_size_client_to_server[$client_channel], - $this->_string_shift($data, $this->packet_size_client_to_server[$client_channel]) + $max_size, + $this->_string_shift($data, $max_size) ); + $this->window_size_client_to_server[$client_channel]-= $max_size + 4; + if (!$this->_send_binary_packet($packet)) { return false; } } - // resize the window, if appropriate - $this->window_size_client_to_server[$client_channel]-= strlen($data); - if ($this->window_size_client_to_server[$client_channel] < 0) { - $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size); - if (!$this->_send_binary_packet($packet)) { - return false; - } - $this->window_size_client_to_server[$client_channel]+= $this->window_size; + if (strlen($data) >= $this->window_size_client_to_server[$client_channel] - 4) { + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + $this->_get_channel_packet(-1); + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; } + $this->window_size_client_to_server[$client_channel]-= strlen($data) + 4; + return $this->_send_binary_packet(pack('CN2a*', NET_SSH2_MSG_CHANNEL_DATA, $this->server_channels[$client_channel], @@ -2858,6 +3395,20 @@ class Net_SSH2 { return $this->languages_client_to_server; } + /** + * Returns the banner message. + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @return String + * @access public + */ + function getBannerMessage() + { + return $this->banner_message; + } + /** * Returns the server public host key. * @@ -2885,6 +3436,8 @@ class Net_SSH2 { switch ($this->signature_format) { case 'ssh-dss': + $zero = new Math_BigInteger(); + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); @@ -2909,9 +3462,13 @@ class Net_SSH2 { $r = new Math_BigInteger($this->_string_shift($signature, 20), 256); $s = new Math_BigInteger($this->_string_shift($signature, 20), 256); - if ($r->compare($q) >= 0 || $s->compare($q) >= 0) { - user_error('Invalid signature'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + switch (true) { + case $r->equals($zero): + case $r->compare($q) >= 0: + case $s->equals($zero): + case $s->compare($q) >= 0: + user_error('Invalid signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $w = $s->modInverse($q); @@ -3006,4 +3563,24 @@ class Net_SSH2 { } return $this->exit_status; } + + /** + * Is a path includable? + * + * @return Boolean + * @access private + */ + function _is_includable($suffix) + { + foreach (explode(PATH_SEPARATOR, get_include_path()) as $prefix) { + $ds = substr($prefix, -1) == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR; + $file = $prefix . $ds . $suffix; + + if (file_exists($file)) { + return true; + } + } + + return false; + } } diff --git a/apps/files_external/css/settings.css b/apps/files_external/css/settings.css index 9858c10ea3..11aeb10184 100644 --- a/apps/files_external/css/settings.css +++ b/apps/files_external/css/settings.css @@ -12,8 +12,8 @@ span.error { background: #ce3702; } -td.mountPoint, td.backend { width:10em; } -td.remove>img { visibility:hidden; padding-top:0.8em; } +td.mountPoint, td.backend { width:160px; } +td.remove>img { visibility:hidden; padding-top:13px; } tr:hover>td.remove>img { visibility:visible; cursor:pointer; } #addMountPoint>td { border:none; } #addMountPoint>td.applicable { visibility:hidden; } diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php index 95e0cefa39..cd8a0e7886 100644 --- a/apps/files_external/lib/sftp.php +++ b/apps/files_external/lib/sftp.php @@ -10,6 +10,7 @@ namespace OC\Files\Storage; set_include_path(get_include_path() . PATH_SEPARATOR . \OC_App::getAppPath('files_external') . '/3rdparty/phpseclib/phpseclib'); require 'Net/SFTP.php'; +require 'Net/SFTP/Stream.php'; class SFTP extends \OC\Files\Storage\Common { private $host; @@ -205,16 +206,6 @@ class SFTP extends \OC\Files\Storage\Common { if ( !$this->file_exists($path)) { return false; } - - if (strrpos($path, '.')!==false) { - $ext=substr($path, strrpos($path, '.')); - } else { - $ext=''; - } - $tmp = \OC_Helper::tmpFile($ext); - $this->getFile($absPath, $tmp); - return fopen($tmp, $mode); - case 'w': case 'wb': case 'a': @@ -227,38 +218,14 @@ class SFTP extends \OC\Files\Storage\Common { case 'x+': case 'c': case 'c+': - if (strrpos($path, '.')!==false) { - $ext=substr($path, strrpos($path, '.')); - } else { - $ext=''; - } - - $tmpFile=\OC_Helper::tmpFile($ext); - \OC\Files\Stream\Close::registerCallback( - $tmpFile, - array($this, 'writeBack') - ); - - if ($this->file_exists($path)) { - $this->getFile($absPath, $tmpFile); - } - - self::$tempFiles[$tmpFile]=$absPath; - return fopen('close://'.$tmpFile, $mode); + // FIXME: make client login lazy to prevent it when using fopen() + return fopen($this->constructUrl($path), $mode); } } catch (\Exception $e) { } return false; } - public function writeBack($tmpFile) { - if (array_key_exists($tmpFile, self::$tempFiles)) { - $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]); - unlink($tmpFile); - unset(self::$tempFiles[$tmpFile]); - } - } - public function touch($path, $mtime=null) { try { if (!is_null($mtime)) { @@ -309,4 +276,9 @@ class SFTP extends \OC\Files\Storage\Common { return false; } } + + public function constructUrl($path) { + $url = 'sftp://'.$this->user.':'.$this->password.'@'.$this->host.$this->root.$path; + return $url; + } } diff --git a/apps/files_sharing/css/authenticate.css b/apps/files_sharing/css/authenticate.css index cebe906dd5..ef963ba7c6 100644 --- a/apps/files_sharing/css/authenticate.css +++ b/apps/files_sharing/css/authenticate.css @@ -11,16 +11,13 @@ margin: 6px; } -input[type="submit"]{ +input[type='submit'] { width: 45px; height: 45px; margin: 6px; - background-image: url('%webroot%/core/img/actions/confirm.svg'); - background-repeat: no-repeat; - background-position: center; } -#body-login input[type="submit"] { +#body-login input[type='submit'] { position: absolute; top: 0px; } diff --git a/apps/files_sharing/css/mobile.css b/apps/files_sharing/css/mobile.css new file mode 100644 index 0000000000..7d2116d190 --- /dev/null +++ b/apps/files_sharing/css/mobile.css @@ -0,0 +1,49 @@ +@media only screen and (max-width: 600px) { + +/* make header scroll up for single shares, more view of content on small screens */ +#header.share-file { + position: absolute !important; +} + +/* hide size and date columns */ +table th#headerSize, +table td.filesize, +table th#headerDate, +table td.date { + display: none; +} + +/* restrict length of displayed filename to prevent overflow */ +table td.filename .nametext { + max-width: 75% !important; +} + +/* on mobile, show single shared image at full width without margin */ +#imgframe { + width: 100%; + padding: 0; + margin-bottom: 35px; +} +/* some margin for the file type icon */ +#imgframe .publicpreview { + margin-top: 32px; +} + +/* always show actions on mobile */ +#fileList a.action { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)" !important; + filter: alpha(opacity=20) !important; + opacity: .2 !important; + display: inline !important; +} +/* some padding for better clickability */ +#fileList a.action img { + padding: 0 6px 0 12px; +} +/* hide text of the actions on mobile */ +#fileList a.action span { + display: none; +} + + +} diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css index 060d4dfedc..21f0c82b82 100644 --- a/apps/files_sharing/css/public.css +++ b/apps/files_sharing/css/public.css @@ -3,7 +3,7 @@ body { } #header { - background: #1d2d44 url('%webroot%/core/img/noise.png') repeat; + background-color: #1d2d44; height:32px; left:0; line-height:32px; @@ -14,39 +14,17 @@ body { padding:7px; } -#details { - color:#fff; - float: left; -} - -#public_upload, -#download { - font-weight:700; - margin: 0 0 0 .4em; - padding: 0 5px; - height: 32px; - float: left; - -} - -.header-right #details { - margin-right: 28px; -} - .header-right { padding: 0; height: 32px; } -#public_upload { - margin-left: 0.3em; -} - -#public_upload img, -#download img { - padding-left:.1em; - padding-right:.3em; - vertical-align:text-bottom; +#details { + color:#fff; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + opacity: .5; + padding-right: 5px; } #controls { @@ -61,7 +39,7 @@ body { #noPreview { display:none; - padding-top:5em; + padding-top:80px; } footer { @@ -71,9 +49,8 @@ footer { p.info { color: #777; text-align: center; - width: 22em; margin: 0 auto; - padding: 20px; + padding: 20px 0; } p.info a { @@ -83,8 +60,8 @@ p.info a { #imgframe { height:75%; - padding-bottom:2em; - padding-top:2em; + padding-bottom:32px; + padding-top:32px; width:80%; margin:0 auto; } @@ -94,9 +71,13 @@ p.info a { max-width:100%; } -thead{ - background-color: white; - padding-left:0 !important; /* fixes multiselect bar offset on shared page */ +/* some margin for the file type icon */ +#imgframe .publicpreview { + margin-top: 10%; +} + +thead { + padding-left: 0 !important; /* fixes multiselect bar offset on shared page */ } #data-upload-form { @@ -110,41 +91,20 @@ thead{ margin: 0; } -#file_upload_start { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - filter: alpha(opacity=0); - opacity: 0; - z-index: 20; - position: absolute !important; - top: 0; - left: 0; - width: 100% !important; -} - -#publicUploadButtonMock { - position:relative; - display:block; - width:100%; - height:32px; - cursor:pointer; - z-index:10; - background-image:url('%webroot%/core/img/actions/upload.svg'); - background-repeat:no-repeat; - background-position:7px 8px; -} - -#publicUploadButtonMock span { - margin: 0 5px 0 28px; - color: #555; -} - +.directDownload, .directLink { margin-bottom: 20px; } - .directLink label { - font-weight: normal; - } - .directLink input { - margin-left: 10px; - width: 300px; - } +.directDownload .button img { + vertical-align: text-bottom; +} +.directLink label { + font-weight: normal; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + opacity: .5; +} +.directLink input { + margin-left: 5px; + width: 300px; +} diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 2e34e6f9bc..c1b7eee3fb 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -9,22 +9,13 @@ function fileDownloadPath(dir, file) { $(document).ready(function() { - $('#data-upload-form').tipsy({gravity:'ne', fade:true}); - if (typeof FileActions !== 'undefined') { var mimetype = $('#mimetype').val(); // Show file preview if previewer is available, images are already handled by the template if (mimetype.substr(0, mimetype.indexOf('/')) != 'image' && $('.publicpreview').length === 0) { // Trigger default action if not download TODO var action = FileActions.getDefault(mimetype, 'file', OC.PERMISSION_READ); - if (typeof action === 'undefined') { - $('#noPreview').show(); - if (mimetype != 'httpd/unix-directory') { - // NOTE: Remove when a better file previewer solution exists - $('#content').remove(); - $('table').remove(); - } - } else { + if (typeof action !== 'undefined') { action($('#filename').val()); } } @@ -34,18 +25,16 @@ $(document).ready(function() { window.location = $(tr).find('a.name').attr('href'); } }); - FileActions.register('file', 'Download', OC.PERMISSION_READ, '', function(filename) { + + // override since the format is different + FileList.getDownloadUrl = function(filename, dir) { + // we use this because we need the service and token attributes var tr = FileList.findFileEl(filename); if (tr.length > 0) { - window.location = $(tr).find('a.name').attr('href'); + return $(tr).find('a.name').attr('href') + '&download'; } - }); - FileActions.register('dir', 'Download', OC.PERMISSION_READ, '', function(filename) { - var tr = FileList.findFileEl(filename); - if (tr.length > 0) { - window.location = $(tr).find('a.name').attr('href')+'&download'; - } - }); + return null; + }; } var file_upload_start = $('#file_upload_start'); @@ -58,15 +47,9 @@ $(document).ready(function() { }; }); - // Add Uploadprogress Wrapper to controls bar - $('#controls').append($('#additional_controls div#uploadprogresswrapper')); - - // Cancel upload trigger - $('#cancel_upload_button').click(function() { - OC.Upload.cancelUploads(); - procesSelection(); + $(document).on('click', '#directLink', function() { + $(this).focus(); + $(this).select(); }); - $('#directLink').focus(); - }); diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 84e90c7168..061e60ad8e 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -162,7 +162,7 @@ class Api { $view = new \OC\Files\View('/'.\OCP\User::getUser().'/files'); if(!$view->is_dir($path)) { - return new \OC_OCS_Result(null, 404, "not a directory"); + return new \OC_OCS_Result(null, 400, "not a directory"); } $content = $view->getDirectoryContent($path); @@ -178,8 +178,7 @@ class Api { $share['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']); } if ($share) { - $share['filename'] = $file['name']; - $result[] = $share; + $result = array_merge($result, $share); } } @@ -220,10 +219,8 @@ class Api { $shareWith = isset($_POST['password']) ? $_POST['password'] : null; //check public link share $publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'); - $encryptionEnabled = \OC_App::isEnabled('files_encryption'); - if(isset($_POST['publicUpload']) && - ($encryptionEnabled || $publicUploadEnabled !== 'yes')) { - return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator"); + if(isset($_POST['publicUpload']) && $publicUploadEnabled !== 'yes') { + return new \OC_OCS_Result(null, 403, "public upload disabled by the administrator"); } $publicUpload = isset($_POST['publicUpload']) ? $_POST['publicUpload'] : 'false'; // read, create, update (7) if public upload is enabled or @@ -231,7 +228,7 @@ class Api { $permissions = $publicUpload === 'true' ? 7 : 1; break; default: - return new \OC_OCS_Result(null, 404, "unknown share type"); + return new \OC_OCS_Result(null, 400, "unknown share type"); } try { @@ -243,7 +240,7 @@ class Api { $permissions ); } catch (\Exception $e) { - return new \OC_OCS_Result(null, 404, $e->getMessage()); + return new \OC_OCS_Result(null, 403, $e->getMessage()); } if ($token) { @@ -321,11 +318,8 @@ class Api { $permissions = isset($params['_put']['permissions']) ? (int)$params['_put']['permissions'] : null; $publicUploadStatus = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'); - $encryptionEnabled = \OC_App::isEnabled('files_encryption'); - $publicUploadEnabled = false; - if(!$encryptionEnabled && $publicUploadStatus === 'yes') { - $publicUploadEnabled = true; - } + $publicUploadEnabled = ($publicUploadStatus === 'yes') ? true : false; + // only change permissions for public shares if public upload is enabled // and we want to set permissions to 1 (read only) or 7 (allow upload) @@ -363,9 +357,8 @@ class Api { private static function updatePublicUpload($share, $params) { $publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'); - $encryptionEnabled = \OC_App::isEnabled('files_encryption'); - if($encryptionEnabled || $publicUploadEnabled !== 'yes') { - return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator"); + if($publicUploadEnabled !== 'yes') { + return new \OC_OCS_Result(null, 403, "public upload disabled by the administrator"); } if ($share['item_type'] !== 'folder' || diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 90440d08f4..1b102f9e5f 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -92,12 +92,11 @@ class Shared_Cache extends Cache { } else { $query = \OC_DB::prepare( 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`,' - .' `size`, `mtime`, `encrypted`' + .' `size`, `mtime`, `encrypted`, `unencrypted_size`' .' FROM `*PREFIX*filecache` WHERE `fileid` = ?'); $result = $query->execute(array($file)); $data = $result->fetchRow(); $data['fileid'] = (int)$data['fileid']; - $data['size'] = (int)$data['size']; $data['mtime'] = (int)$data['mtime']; $data['storage_mtime'] = (int)$data['storage_mtime']; $data['encrypted'] = (bool)$data['encrypted']; @@ -106,6 +105,12 @@ class Shared_Cache extends Cache { if ($data['storage_mtime'] === 0) { $data['storage_mtime'] = $data['mtime']; } + if ($data['encrypted'] or ($data['unencrypted_size'] > 0 and $data['mimetype'] === 'httpd/unix-directory')) { + $data['encrypted_size'] = (int)$data['size']; + $data['size'] = (int)$data['unencrypted_size']; + } else { + $data['size'] = (int)$data['size']; + } return $data; } return false; @@ -127,7 +132,18 @@ class Shared_Cache extends Cache { return $files; } else { if ($cache = $this->getSourceCache($folder)) { - return $cache->getFolderContents($this->files[$folder]); + $sourceFolderContent = $cache->getFolderContents($this->files[$folder]); + foreach ($sourceFolderContent as $key => $c) { + $ownerPathParts = explode('/', \OC_Filesystem::normalizePath($c['path'])); + $userPathParts = explode('/', \OC_Filesystem::normalizePath($folder)); + $usersPath = 'files/Shared/'.$userPathParts[1]; + foreach (array_slice($ownerPathParts, 3) as $part) { + $usersPath .= '/'.$part; + } + $sourceFolderContent[$key]['usersPath'] = $usersPath; + } + + return $sourceFolderContent; } } return false; @@ -248,19 +264,40 @@ class Shared_Cache extends Cache { * @return array */ public function searchByMime($mimetype) { - - if (strpos($mimetype, '/')) { - $where = '`mimetype` = ? AND '; - } else { - $where = '`mimepart` = ? AND '; + $mimepart = null; + if (strpos($mimetype, '/') === false) { + $mimepart = $mimetype; + $mimetype = null; } - $value = $this->getMimetypeId($mimetype); - - return $this->searchWithWhere($where, $value); + // note: searchWithWhere is currently broken as it doesn't + // recurse into subdirs nor returns the correct + // file paths, so using getFolderContents() for now + $result = array(); + $exploreDirs = array(''); + while (count($exploreDirs) > 0) { + $dir = array_pop($exploreDirs); + $files = $this->getFolderContents($dir); + // no results? + if (!$files) { + continue; + } + foreach ($files as $file) { + if ($file['mimetype'] === 'httpd/unix-directory') { + $exploreDirs[] = ltrim($dir . '/' . $file['name'], '/'); + } + else if (($mimepart && $file['mimepart'] === $mimepart) || ($mimetype && $file['mimetype'] === $mimetype)) { + // usersPath not reliable + //$file['path'] = $file['usersPath']; + $file['path'] = ltrim($dir . '/' . $file['name'], '/'); + $result[] = $file; + } + } + } + return $result; } - + /** * The maximum number of placeholders that can be used in an SQL query. * Value MUST be <= 1000 for oracle: @@ -268,7 +305,7 @@ class Shared_Cache extends Cache { * FIXME we should get this from doctrine as other DBs allow a lot more placeholders */ const MAX_SQL_CHUNK_SIZE = 1000; - + /** * search for files with a custom where clause and value * the $wherevalue will be array_merge()d with the file id chunks @@ -282,16 +319,16 @@ class Shared_Cache extends Cache { $ids = $this->getAll(); $files = array(); - + // divide into chunks $chunks = array_chunk($ids, $chunksize); - + foreach ($chunks as $chunk) { $placeholders = join(',', array_fill(0, count($chunk), '?')); $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag` FROM `*PREFIX*filecache` WHERE ' . $sqlwhere . ' `fileid` IN (' . $placeholders . ')'; - + $stmt = \OC_DB::prepare($sql); $result = $stmt->execute(array_merge(array($wherevalue), $chunk)); @@ -302,6 +339,12 @@ class Shared_Cache extends Cache { } $row['mimetype'] = $this->getMimetype($row['mimetype']); $row['mimepart'] = $this->getMimetype($row['mimepart']); + if ($row['encrypted'] or ($row['unencrypted_size'] > 0 and $row['mimetype'] === 'httpd/unix-directory')) { + $row['encrypted_size'] = $row['size']; + $row['size'] = $row['unencrypted_size']; + } else { + $row['size'] = $row['size']; + } $files[] = $row; } } diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index c956c55a1d..ec0f368386 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -91,10 +91,17 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $file['name'] = basename($item['file_target']); $file['mimetype'] = $item['mimetype']; $file['mimepart'] = $item['mimepart']; - $file['size'] = $item['size']; $file['mtime'] = $item['mtime']; $file['encrypted'] = $item['encrypted']; $file['etag'] = $item['etag']; + $storage = \OC\Files\Filesystem::getStorage('/'); + $cache = $storage->getCache(); + if ($item['encrypted'] or ($item['unencrypted_size'] > 0 and $cache->getMimetype($item['mimetype']) === 'httpd/unix-directory')) { + $file['size'] = $item['unencrypted_size']; + $file['encrypted_size'] = $item['size']; + } else { + $file['size'] = $item['size']; + } $files[] = $file; } return $files; diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 4a81e48297..f03ac7205a 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -137,21 +137,19 @@ if (isset($path)) { } else { OCP\Util::addScript('files', 'file-upload'); OCP\Util::addStyle('files_sharing', 'public'); + OCP\Util::addStyle('files_sharing', 'mobile'); OCP\Util::addScript('files_sharing', 'public'); OCP\Util::addScript('files', 'fileactions'); OCP\Util::addScript('files', 'jquery.iframe-transport'); OCP\Util::addScript('files', 'jquery.fileupload'); $maxUploadFilesize=OCP\Util::maxUploadFilesize($path); $tmpl = new OCP\Template('files_sharing', 'public', 'base'); - $tmpl->assign('uidOwner', $shareOwner); $tmpl->assign('displayName', \OCP\User::getDisplayName($shareOwner)); $tmpl->assign('filename', $file); $tmpl->assign('directory_path', $linkItem['file_target']); $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); - $tmpl->assign('fileTarget', basename($linkItem['file_target'])); $tmpl->assign('dirToken', $linkItem['token']); $tmpl->assign('sharingToken', $token); - $tmpl->assign('disableSharing', true); $allowPublicUploadEnabled = (bool) ($linkItem['permissions'] & OCP\PERMISSION_CREATE); if (OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes') === 'no') { $allowPublicUploadEnabled = false; @@ -159,9 +157,6 @@ if (isset($path)) { if ($linkItem['item_type'] !== 'folder') { $allowPublicUploadEnabled = false; } - $tmpl->assign('allowPublicUploadEnabled', $allowPublicUploadEnabled); - $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); - $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); $urlLinkIdentifiers= (isset($token)?'&t='.$token:'') .(isset($_GET['dir'])?'&dir='.$_GET['dir']:'') @@ -222,17 +217,23 @@ if (isset($path)) { $maxUploadFilesize=OCP\Util::maxUploadFilesize($path); $fileHeader = (!isset($files) or count($files) > 0); $emptyContent = ($allowPublicUploadEnabled and !$fileHeader); + + $freeSpace=OCP\Util::freeSpace($path); + $uploadLimit=OCP\Util::uploadLimit(); $folder = new OCP\Template('files', 'index', ''); $folder->assign('fileList', $list->fetchPage()); $folder->assign('breadcrumb', $breadcrumbNav->fetchPage()); $folder->assign('dir', $getPath); - $folder->assign('isCreatable', false); + $folder->assign('isCreatable', $allowPublicUploadEnabled); + $folder->assign('dirToken', $linkItem['token']); $folder->assign('permissions', OCP\PERMISSION_READ); $folder->assign('isPublic',true); $folder->assign('publicUploadEnabled', 'no'); $folder->assign('files', $files); $folder->assign('uploadMaxFilesize', $maxUploadFilesize); $folder->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); + $folder->assign('freeSpace', $freeSpace); + $folder->assign('uploadLimit', $uploadLimit); // PHP upload limit $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); $folder->assign('usedSpacePercent', 0); $folder->assign('fileHeader', $fileHeader); diff --git a/apps/files_sharing/templates/authenticate.php b/apps/files_sharing/templates/authenticate.php index 6b98e6c9f3..928be93fc9 100644 --- a/apps/files_sharing/templates/authenticate.php +++ b/apps/files_sharing/templates/authenticate.php @@ -9,7 +9,7 @@

- +

diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 1d527dca8e..3ddaf4446d 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -9,64 +9,14 @@ -
@@ -82,25 +32,28 @@
- -
- -
- +
+ + +
- + +
-
-

- getLongFooter()); ?> -

-
+ +
+
+

+ getLongFooter()); ?> +

+
diff --git a/apps/files_sharing/tests/cache.php b/apps/files_sharing/tests/cache.php new file mode 100644 index 0000000000..56a51c83f6 --- /dev/null +++ b/apps/files_sharing/tests/cache.php @@ -0,0 +1,134 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ +require_once __DIR__ . '/base.php'; + +class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { + + function setUp() { + parent::setUp(); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + // prepare user1's dir structure + $textData = "dummy file data\n"; + $this->view->mkdir('container'); + $this->view->mkdir('container/shareddir'); + $this->view->mkdir('container/shareddir/subdir'); + $this->view->mkdir('container/shareddir/emptydir'); + + $textData = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $this->view->file_put_contents('container/not shared.txt', $textData); + $this->view->file_put_contents('container/shared single file.txt', $textData); + $this->view->file_put_contents('container/shareddir/bar.txt', $textData); + $this->view->file_put_contents('container/shareddir/subdir/another.txt', $textData); + $this->view->file_put_contents('container/shareddir/subdir/another too.txt', $textData); + $this->view->file_put_contents('container/shareddir/subdir/not a text file.xml', ''); + + list($this->ownerStorage, $internalPath) = $this->view->resolvePath(''); + $this->ownerCache = $this->ownerStorage->getCache(); + $this->ownerStorage->getScanner()->scan(''); + + // share "shareddir" with user2 + $fileinfo = $this->view->getFileInfo('container/shareddir'); + \OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2, 31); + + $fileinfo = $this->view->getFileInfo('container/shared single file.txt'); + \OCP\Share::shareItem('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2, 31); + + // login as user2 + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + // retrieve the shared storage + $secondView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2); + list($this->sharedStorage, $internalPath) = $secondView->resolvePath('files/Shared/shareddir'); + $this->sharedCache = $this->sharedStorage->getCache(); + } + + function tearDown() { + $this->sharedCache->clear(); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + $fileinfo = $this->view->getFileInfo('container/shareddir'); + \OCP\Share::unshare('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2); + + $fileinfo = $this->view->getFileInfo('container/shared single file.txt'); + \OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2); + + $this->view->deleteAll('container'); + + $this->ownerCache->clear(); + + parent::tearDown(); + } + + /** + * Test searching by mime type + */ + function testSearchByMime() { + $results = $this->sharedStorage->getCache()->searchByMime('text'); + $check = array( + array( + 'name' => 'shared single file.txt', + 'path' => 'shared single file.txt' + ), + array( + 'name' => 'bar.txt', + 'path' => 'shareddir/bar.txt' + ), + array( + 'name' => 'another too.txt', + 'path' => 'shareddir/subdir/another too.txt' + ), + array( + 'name' => 'another.txt', + 'path' => 'shareddir/subdir/another.txt' + ), + ); + $this->verifyFiles($check, $results); + + $results2 = $this->sharedStorage->getCache()->searchByMime('text/plain'); + + $this->verifyFiles($check, $results); + } + + /** + * Checks that all provided attributes exist in the files list, + * only the values provided in $examples will be used to check against + * the file list. The files order also needs to be the same. + * + * @param array $examples array of example files + * @param array $files array of files + */ + private function verifyFiles($examples, $files) { + $this->assertEquals(count($examples), count($files)); + foreach ($files as $i => $file) { + foreach ($examples[$i] as $key => $value) { + $this->assertEquals($value, $file[$key]); + } + } + } +} diff --git a/apps/files_trashbin/ajax/preview.php b/apps/files_trashbin/ajax/preview.php index ce432f4d14..44738734b1 100644 --- a/apps/files_trashbin/ajax/preview.php +++ b/apps/files_trashbin/ajax/preview.php @@ -34,7 +34,17 @@ try{ if ($view->is_dir($file)) { $mimetype = 'httpd/unix-directory'; } else { - $mimetype = \OC_Helper::getFileNameMimeType(pathinfo($file, PATHINFO_FILENAME)); + $pathInfo = pathinfo($file); + $fileName = $pathInfo['basename']; + // if in root dir + if ($pathInfo['dirname'] === '.') { + // cut off the .d* suffix + $i = strrpos($fileName, '.'); + if ($i !== false) { + $fileName = substr($fileName, 0, $i); + } + } + $mimetype = \OC_Helper::getFileNameMimeType($fileName); } $preview->setMimetype($mimetype); $preview->setMaxX($maxX); @@ -45,4 +55,4 @@ try{ }catch(\Exception $e) { \OC_Response::setStatus(500); \OC_Log::write('core', $e->getmessage(), \OC_Log::DEBUG); -} \ No newline at end of file +} diff --git a/apps/files_trashbin/appinfo/database.xml b/apps/files_trashbin/appinfo/database.xml index aae334b148..d08c3469b0 100644 --- a/apps/files_trashbin/appinfo/database.xml +++ b/apps/files_trashbin/appinfo/database.xml @@ -44,7 +44,7 @@ true 512 - + type text @@ -52,15 +52,15 @@ true 4 - + mime text true - 30 + 255 - + id_index @@ -68,7 +68,7 @@ ascending - + timestamp_index @@ -76,7 +76,7 @@ ascending - + user_index @@ -84,7 +84,7 @@ ascending - + @@ -110,7 +110,7 @@ true 50 - + diff --git a/apps/files_trashbin/appinfo/version b/apps/files_trashbin/appinfo/version index bd73f47072..2eb3c4fe4e 100644 --- a/apps/files_trashbin/appinfo/version +++ b/apps/files_trashbin/appinfo/version @@ -1 +1 @@ -0.4 +0.5 diff --git a/apps/files_versions/ajax/getVersions.php b/apps/files_versions/ajax/getVersions.php index df29f40162..4cc1c42889 100644 --- a/apps/files_versions/ajax/getVersions.php +++ b/apps/files_versions/ajax/getVersions.php @@ -5,7 +5,8 @@ $source = $_GET['source']; $start = $_GET['start']; list ($uid, $filename) = OCA\Files_Versions\Storage::getUidAndFilename($source); $count = 5; //show the newest revisions -if( ($versions = OCA\Files_Versions\Storage::getVersions($uid, $filename, $source)) ) { +$versions = OCA\Files_Versions\Storage::getVersions($uid, $filename, $source); +if( $versions ) { $endReached = false; if (count($versions) <= $start+$count) { diff --git a/apps/files_versions/css/versions.css b/apps/files_versions/css/versions.css index c53935711c..16755f35a7 100644 --- a/apps/files_versions/css/versions.css +++ b/apps/files_versions/css/versions.css @@ -1,5 +1,5 @@ #dropdown.drop-versions { - width:24em; + width:320px; } #found_versions li { diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index 8463bd9482..328ed4305f 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -264,7 +264,7 @@ class Storage { * @param string $uid user id from the owner of the file * @param string $filename file to find versions of, relative to the user files dir * @param string $userFullPath - * @returns array + * @returns array versions newest version first */ public static function getVersions($uid, $filename, $userFullPath = '') { $versions = array(); @@ -397,12 +397,13 @@ class Storage { } } - ksort($versions); + // newest version first + krsort($versions); $result = array(); foreach ($versions as $key => $value) { - $size = $view->filesize($value['path']); + $size = $view->filesize(self::VERSIONS_ROOT.'/'.$value['path'].'.v'.$value['timestamp']); $filename = $value['path']; $result['all'][$key]['version'] = $value['timestamp']; @@ -428,8 +429,6 @@ class Storage { $size = 0; $toDelete = array(); // versions we want to delete - $versions = array_reverse($versions); // newest version first - $interval = 1; $step = Storage::$max_versions_per_interval[$interval]['step']; if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) { diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php index 713f7796c9..25490aa1a0 100644 --- a/apps/files_versions/tests/versions.php +++ b/apps/files_versions/tests/versions.php @@ -40,7 +40,7 @@ class Test_Files_Versioning extends \PHPUnit_Framework_TestCase { $startTime = 5000000; $testClass = new VersionStorageToTest(); - list($deleted, $size) = $testClass->callProtectedGetExpireList($startTime, array_reverse($versions)); + list($deleted, $size) = $testClass->callProtectedGetExpireList($startTime, $versions); // we should have deleted 16 files each of the size 1 $this->assertEquals($sizeOfAllDeletedFiles, $size); diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml index 148a72cecb..9cc908e852 100644 --- a/apps/user_ldap/appinfo/info.xml +++ b/apps/user_ldap/appinfo/info.xml @@ -14,4 +14,7 @@ + + http://doc.owncloud.org/server/6.0/go.php?to=admin-ldap + diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css index 490c3e9592..e09c377a90 100644 --- a/apps/user_ldap/css/settings.css +++ b/apps/user_ldap/css/settings.css @@ -33,9 +33,9 @@ width: 100% !important; height: 50px; background-color: lightyellow; - border-radius: 0.5em; - padding: 0.6em 0.5em 0.4em !important; - margin-bottom: 0.3em; + border-radius: 8px; + padding: 10px 8px 6px !important; + margin-bottom: 5px; } #ldapWizard1 .hostPortCombinator { @@ -68,17 +68,17 @@ } .ldapwarning { - margin-left: 1.4em; + margin-left: 22px; color: #FF3B3B; } .wizSpinner { height: 15px; - margin: 0.3em; + margin: 5px; } .ldapSettingControls { - margin-top: 3ex; + margin-top: 3px; } #ldap fieldset p label { @@ -126,4 +126,4 @@ select[multiple=multiple] + button { .ldap_grey { color: #777; -} \ No newline at end of file +} diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index acf88ef58a..792638f2b5 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -240,6 +240,7 @@ var LdapWizard = { LdapWizard.hideSpinner('#ldap_base'); LdapWizard.showInfoBox('Please specify a Base DN'); LdapWizard.showInfoBox('Could not determine Base DN'); + $('#ldap_base').prop('disabled', false); } ); } diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 72f9c74092..b619f62f29 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -729,7 +729,7 @@ class Access extends LDAPUtility { } } else { if(!is_null($limit)) { - \OCP\Util::writeLog('user_ldap', 'Paged search failed :(', \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO); } } } diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index c4e4efd048..7fbabda710 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -52,7 +52,7 @@ class Connection extends LDAPUtility { $this->configID = $configID; $this->configuration = new Configuration($configPrefix, !is_null($configID)); - $memcache = new \OC\Memcache\Factory(); + $memcache = \OC::$server->getMemCacheFactory(); if($memcache->isAvailable()) { $this->cache = $memcache->create(); } else { diff --git a/apps/user_ldap/lib/ildapwrapper.php b/apps/user_ldap/lib/ildapwrapper.php index 20587cba7d..e60cf5ec63 100644 --- a/apps/user_ldap/lib/ildapwrapper.php +++ b/apps/user_ldap/lib/ildapwrapper.php @@ -145,9 +145,11 @@ interface ILDAPWrapper { * @param $baseDN The DN of the entry to read from * @param $filter An LDAP filter * @param $attr array of the attributes to read + * @param $attrsonly optional, 1 if only attribute types shall be returned + * @param $limit optional, limits the result entries * @return an LDAP search result resource, false on error */ - public function search($link, $baseDN, $filter, $attr); + public function search($link, $baseDN, $filter, $attr, $attrsonly = 0, $limit = 0); /** * @brief Sets the value of the specified option to be $value diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php index dda8533c41..a99c648012 100644 --- a/apps/user_ldap/lib/ldap.php +++ b/apps/user_ldap/lib/ldap.php @@ -85,9 +85,9 @@ class LDAP implements ILDAPWrapper { return $this->invokeLDAPMethod('read', $link, $baseDN, $filter, $attr); } - public function search($link, $baseDN, $filter, $attr) { - return $this->invokeLDAPMethod('search', $link, $baseDN, - $filter, $attr); + public function search($link, $baseDN, $filter, $attr, $attrsonly = 0, $limit = 0) { + return $this->invokeLDAPMethod('search', $link, $baseDN, $filter, + $attr, $attrsonly, $limit); } public function setOption($link, $option, $value) { diff --git a/apps/user_ldap/lib/wizard.php b/apps/user_ldap/lib/wizard.php index b70ede8599..00623b74fb 100644 --- a/apps/user_ldap/lib/wizard.php +++ b/apps/user_ldap/lib/wizard.php @@ -567,6 +567,10 @@ class Wizard extends LDAPUtility { //get a result set > 0 on a proper base $rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1); if(!$this->ldap->isResource($rr)) { + $errorNo = $this->ldap->errno($cr); + $errorMsg = $this->ldap->error($cr); + \OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base. + ' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO); return false; } $entries = $this->ldap->countEntries($cr, $rr); @@ -1010,6 +1014,7 @@ class Wizard extends LDAPUtility { $this->configuration->ldapPort); $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3); + $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0); $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); if($this->configuration->ldapTLS === 1) { $this->ldap->startTls($cr); diff --git a/autotest-js.sh b/autotest-js.sh new file mode 100755 index 0000000000..8b9a106b02 --- /dev/null +++ b/autotest-js.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# ownCloud +# +# Run JS tests +# +# @author Vincent Petry +# @copyright 2014 Vincent Petry +# +NPM="$(which npm 2>/dev/null)" +PREFIX="build" + +if test -z "$NPM" +then + echo 'Node JS >= 0.8 is required to run the JavaScript tests' >&2 + exit 1 +fi + +# update/install test packages +mkdir -p "$PREFIX" && $NPM install --link --prefix "$PREFIX" || exit 3 + +KARMA="$(which karma 2>/dev/null)" + +# If not installed globally, try local version +if test -z "$KARMA" +then + KARMA="$PREFIX/node_modules/karma/bin/karma" +fi + +if test -z "$KARMA" +then + echo 'Karma module executable not found' >&2 + exit 2 +fi + +NODE_PATH='build/node_modules' KARMA_TESTSUITE="$1" $KARMA start tests/karma.config.js --single-run + diff --git a/autotest.sh b/autotest.sh index 94fc692a94..b88e9cf68b 100755 --- a/autotest.sh +++ b/autotest.sh @@ -185,19 +185,23 @@ EOF cp $BASEDIR/tests/autoconfig-$1.php $BASEDIR/config/autoconfig.php # trigger installation - php -f index.php + echo "INDEX" + php -f index.php | grep -i -C9999 error && echo "Error during setup" && exit 101 + echo "END INDEX" #test execution echo "Testing with $1 ..." cd tests rm -rf coverage-html-$1 mkdir coverage-html-$1 - php -f enable_all.php + php -f enable_all.php | grep -i -C9999 error && echo "Error during setup" && exit 101 if [ -z "$NOCOVERAGE" ]; then $PHPUNIT --configuration phpunit-autotest.xml --log-junit autotest-results-$1.xml --coverage-clover autotest-clover-$1.xml --coverage-html coverage-html-$1 $2 $3 + RESULT=$? else echo "No coverage" $PHPUNIT --configuration phpunit-autotest.xml --log-junit autotest-results-$1.xml $2 $3 + RESULT=$? fi } diff --git a/build/package.json b/build/package.json new file mode 100644 index 0000000000..c9ed7b96c6 --- /dev/null +++ b/build/package.json @@ -0,0 +1,20 @@ +{ + "name": "owncloud-js-tests", + "description": "ownCloud tests", + "version": "0.0.1", + "author": { + "name": "Vincent Petry", + "email": "pvince81@owncloud.com" + }, + "private": true, + "homepage": "https://github.com/owncloud/", + "contributors": [], + "dependencies": {}, + "devDependencies": { + "karma": "*", + "karma-jasmine": "*", + "karma-junit-reporter": "*", + "karma-coverage": "*" + }, + "engine": "node >= 0.8" +} diff --git a/config/config.sample.php b/config/config.sample.php index 1070ef72ed..0cd321d095 100755 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -80,6 +80,12 @@ $CONFIG = array( /* Domain name used by ownCloud for the sender mail address, e.g. no-reply@example.com */ "mail_domain" => "example.com", +/* FROM address used by ownCloud for the sender mail address, e.g. owncloud@example.com + This setting overwrites the built in 'sharing-noreply' and 'lostpassword-noreply' + FROM addresses, that ownCloud uses +*/ +"mail_from_address" => "owncloud", + /* Enable SMTP class debugging */ "mail_smtpdebug" => false, @@ -114,8 +120,14 @@ $CONFIG = array( /* Password to use for sendmail mail, depends on mail_smtpauth if this is used */ "mail_smtppassword" => "", -/* memcached hostname and port (Only used when xCache, APC and APCu are absent.) */ -"memcached_server" => array('localhost', 11211), +/* memcached servers (Only used when xCache, APC and APCu are absent.) */ +"memcached_servers" => array( + // hostname, port and optional weight. Also see: + // http://www.php.net/manual/en/memcached.addservers.php + // http://www.php.net/manual/en/memcached.addserver.php + array('localhost', 11211), + //array('other.host.local', 11211), +), /* How long should ownCloud keep deleted files in the trash bin, default value: 30 days */ 'trashbin_retention_obligation' => 30, @@ -178,6 +190,13 @@ $CONFIG = array( /* Life time of a session after inactivity */ "session_lifetime" => 60 * 60 * 24, +/* + * Enable/disable session keep alive when a user is logged in in the Web UI. + * This is achieved by sending a "heartbeat" to the server to prevent + * the session timing out. + */ +"session_keepalive" => true, + /* Custom CSP policy, changing this will overwrite the standard policy */ "custom_csp_policy" => "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src *; font-src 'self' data:; media-src *", diff --git a/core/ajax/share.php b/core/ajax/share.php index be02c05635..8b48effb45 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -119,8 +119,12 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $subject = (string)$l->t('%s shared »%s« with you', array($ownerDisplayName, $filename)); $expiration = null; if (isset($items[0]['expiration'])) { - $date = new DateTime($items[0]['expiration']); - $expiration = $date->format('Y-m-d'); + try { + $date = new DateTime($items[0]['expiration']); + $expiration = $l->l('date', $date->getTimestamp()); + } catch (Exception $e) { + \OCP\Util::writeLog('sharing', "Couldn't read date: " . $e->getMessage(), \OCP\Util::ERROR); + } } if ($itemType === 'folder') { @@ -183,6 +187,8 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo break; case 'email': + // enable l10n support + $l = OC_L10N::get('core'); // read post variables $user = OCP\USER::getUser(); $displayName = OCP\User::getDisplayName(); @@ -191,8 +197,16 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $file = $_POST['file']; $to_address = $_POST['toaddress']; - // enable l10n support - $l = OC_L10N::get('core'); + $expiration = null; + if (isset($_POST['expiration']) && $_POST['expiration'] !== '') { + try { + $date = new DateTime($_POST['expiration']); + $expiration = $l->l('date', $date->getTimestamp()); + } catch (Exception $e) { + \OCP\Util::writeLog('sharing', "Couldn't read date: " . $e->getMessage(), \OCP\Util::ERROR); + } + + } // setup the email $subject = (string)$l->t('%s shared »%s« with you', array($displayName, $file)); @@ -202,6 +216,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $content->assign ('type', $type); $content->assign ('user_displayname', $displayName); $content->assign ('filename', $file); + $content->assign('expiration', $expiration); $text = $content->fetchPage(); $content = new OC_Template("core", "altmail", ""); @@ -209,6 +224,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $content->assign ('type', $type); $content->assign ('user_displayname', $displayName); $content->assign ('filename', $file); + $content->assign('expiration', $expiration); $alttext = $content->fetchPage(); $default_from = OCP\Util::getDefaultEmailAddress('sharing-noreply'); diff --git a/core/css/auth.css b/core/css/auth.css index 0adc10c77d..70df9f0ae0 100644 --- a/core/css/auth.css +++ b/core/css/auth.css @@ -1,7 +1,7 @@ h2 { - font-size:2em; + font-size:32px; font-weight:700; - margin-bottom:1em; + margin-bottom:16px; white-space:nowrap; } @@ -18,8 +18,8 @@ h2 img { } #oauth { - width:20em; - margin:4em auto 2em; + width:320px; + margin:64px auto 32px; } #allow-auth { @@ -33,7 +33,7 @@ h2 img { background:none; border:0; box-shadow:0 0 0 #fff, 0 0 0 #fff inset; - font-size:1.2em; - margin:.7em; + font-size:19px; + margin:11px; padding:0; } diff --git a/core/css/icons.css b/core/css/icons.css new file mode 100644 index 0000000000..2dc3508412 --- /dev/null +++ b/core/css/icons.css @@ -0,0 +1,244 @@ +.icon { + background-repeat: no-repeat; + background-position: center; +} + + + + +/* general assets */ + +.icon-breadcrumb { + background-image: url('../img/breadcrumb.svg'); +} + +.icon-loading { + background-image: url('../img/loading.gif'); +} +.icon-loading-dark { + background-image: url('../img/loading-dark.gif'); +} +.icon-loading-small { + background-image: url('../img/loading-small.gif'); +} + +.icon-noise { + background-image: url('../img/noise.png'); + background-repeat: no-repeat; +} + + + + +/* action icons */ + +.icon-add { + background-image: url('../img/actions/add.svg'); +} + +.icon-caret { + background-image: url('../img/actions/caret.svg'); +} +.icon-caret-dark { + background-image: url('../img/actions/caret-dark.svg'); +} + +.icon-checkmark { + background-image: url('../img/actions/checkmark.svg'); +} + +.icon-checkmark-white { + background-image: url('../img/actions/checkmark-white.svg'); +} + +.icon-clock { + background-image: url('../img/actions/clock.svg'); +} + +.icon-close { + background-image: url('../img/actions/close.svg'); +} + +.icon-confirm { + background-image: url('../img/actions/confirm.svg'); +} + +.icon-delete { + background-image: url('../img/actions/delete.svg'); +} +.icon-delete-hover { + background-image: url('../img/actions/delete-hover.svg'); +} + +.icon-download { + background-image: url('../img/actions/download.svg'); +} + +.icon-history { + background-image: url('../img/actions/history.svg'); +} + +.icon-info { + background-image: url('../img/actions/info.svg'); +} + +.icon-lock { + background-image: url('../img/actions/lock.svg'); +} + +.icon-logout { + background-image: url('../img/actions/logout.svg'); +} + +.icon-mail { + background-image: url('../img/actions/mail.svg'); +} + +.icon-more { + background-image: url('../img/actions/more.svg'); +} + +.icon-password { + background-image: url('../img/actions/password.svg'); +} + +.icon-pause { + background-image: url('../img/actions/pause.svg'); +} +.icon-pause-big { + background-image: url('../img/actions/pause-big.svg'); +} + +.icon-play { + background-image: url('../img/actions/play.svg'); +} +.icon-play-add { + background-image: url('../img/actions/play-add.svg'); +} +.icon-play-big { + background-image: url('../img/actions/play-big.svg'); +} +.icon-play-next { + background-image: url('../img/actions/play-next.svg'); +} +.icon-play-previous { + background-image: url('../img/actions/play-previous.svg'); +} + +.icon-public { + background-image: url('../img/actions/public.svg'); +} + +.icon-rename { + background-image: url('../img/actions/rename.svg'); +} + +.icon-search { + background-image: url('../img/actions/search.svg'); +} + +.icon-settings { + background-image: url('../img/actions/settings.svg'); +} + +.icon-share { + background-image: url('../img/actions/share.svg'); +} +.icon-shared { + background-image: url('../img/actions/shared.svg'); +} + +.icon-sound { + background-image: url('../img/actions/sound.svg'); +} +.icon-sound-off { + background-image: url('../img/actions/sound-off.svg'); +} + +.icon-star { + background-image: url('../img/actions/star.svg'); +} + +.icon-starred { + background-image: url('../img/actions/starred.svg'); +} + +.icon-toggle { + background-image: url('../img/actions/toggle.svg'); +} + +.icon-triangle-e { + background-image: url('../img/actions/triangle-e.svg'); +} +.icon-triangle-n { + background-image: url('../img/actions/triangle-n.svg'); +} +.icon-triangle-s { + background-image: url('../img/actions/triangle-s.svg'); +} + +.icon-upload { + background-image: url('../img/actions/upload.svg'); +} +.icon-upload-white { + background-image: url('../img/actions/upload-white.svg'); +} + +.icon-user { + background-image: url('../img/actions/user.svg'); +} + +.icon-view-close { + background-image: url('../img/actions/view-close.svg'); +} +.icon-view-next { + background-image: url('../img/actions/view-next.svg'); +} +.icon-view-pause { + background-image: url('../img/actions/view-pause.svg'); +} +.icon-view-play { + background-image: url('../img/actions/view-play.svg'); +} +.icon-view-previous { + background-image: url('../img/actions/view-previous.svg'); +} + + + + +/* places icons */ + +.icon-calendar-dark { + background-image: url('../img/places/calendar-dark.svg'); +} + +.icon-contacts-dark { + background-image: url('../img/places/contacts-dark.svg'); +} + +.icon-file { + background-image: url('../img/places/file.svg'); +} +.icon-files { + background-image: url('../img/places/files.svg'); +} +.icon-folder { + background-image: url('../img/places/folder.svg'); +} + +.icon-home { + background-image: url('../img/places/home.svg'); +} + +.icon-link { + background-image: url('../img/places/link.svg'); +} + +.icon-music { + background-image: url('../img/places/music.svg'); +} + +.icon-picture { + background-image: url('../img/places/picture.svg'); +} diff --git a/core/css/jquery.multiselect.css b/core/css/jquery.multiselect.css index 898786a615..9b81c3bdcf 100644 --- a/core/css/jquery.multiselect.css +++ b/core/css/jquery.multiselect.css @@ -1,23 +1,23 @@ -.ui-multiselect { padding:2px 0 2px 4px; text-align:left } -.ui-multiselect span.ui-icon { float:right } +.ui-multiselect { padding:2px 0 2px 4px; text-align:left; } +.ui-multiselect span.ui-icon { float:right; } .ui-multiselect-single .ui-multiselect-checkboxes input { position:absolute !important; top: auto !important; left:-9999px; } -.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important } +.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important; } -.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px } -.ui-multiselect-header ul { font-size:0.9em } -.ui-multiselect-header ul li { float:left; padding:0 10px 0 0 } -.ui-multiselect-header a { text-decoration:none } -.ui-multiselect-header a:hover { text-decoration:underline } -.ui-multiselect-header span.ui-icon { float:left } -.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0 } +.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px; } +.ui-multiselect-header ul { font-size:14px; } +.ui-multiselect-header ul li { float:left; padding:0 10px 0 0; } +.ui-multiselect-header a { text-decoration:none; } +.ui-multiselect-header a:hover { text-decoration:underline; } +.ui-multiselect-header span.ui-icon { float:left;} +.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0; } -.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left } -.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll } -.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px } -.ui-multiselect-checkboxes label input { position:relative; top:1px } -.ui-multiselect-checkboxes li { clear:both; font-size:0.9em; padding-right:3px } -.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid } -.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none } +.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left; } +.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll; } +.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px; } +.ui-multiselect-checkboxes label input { position:relative; top:1px; } +.ui-multiselect-checkboxes li { clear:both; font-size:14px; padding-right:3px; } +.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid; } +.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none; } /* remove label borders in IE6 because IE6 does not support transparency */ -* html .ui-multiselect-checkboxes label { border:none } +* html .ui-multiselect-checkboxes label { border:none; } diff --git a/core/css/multiselect.css b/core/css/multiselect.css index e2a35ac363..60f2f47e69 100644 --- a/core/css/multiselect.css +++ b/core/css/multiselect.css @@ -7,7 +7,7 @@ ul.multiselectoptions { border: 1px solid #ddd; border-top: none; box-shadow: 0 1px 1px #ddd; - padding-top: .5em; + padding-top: 8px; position: absolute; max-height: 20em; overflow-y: auto; @@ -15,8 +15,8 @@ ul.multiselectoptions { } ul.multiselectoptions.down { - border-bottom-left-radius: .5em; - border-bottom-right-radius: .5em; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; width: 100%; /* do not cut off group names */ -webkit-box-shadow: 0px 0px 20px rgba(29,45,68,.4); -moz-box-shadow: 0px 0px 20px rgba(29,45,68,.4); @@ -24,8 +24,8 @@ ul.multiselectoptions.down { } ul.multiselectoptions.up { - border-top-left-radius: .5em; - border-top-right-radius: .5em; + border-top-left-radius: 8px; + border-top-right-radius: 8px; } ul.multiselectoptions>li { @@ -52,8 +52,8 @@ div.multiselect { display: inline-block; max-width: 400px; min-width: 150px; + padding-right: 10px; min-height: 20px; - padding-right: .6em; position: relative; vertical-align: bottom; } @@ -78,7 +78,7 @@ div.multiselect.down { div.multiselect>span:first-child { float: left; - margin-right: 2em; + margin-right: 32px; overflow: hidden; text-overflow: ellipsis; width: 90%; @@ -86,12 +86,12 @@ div.multiselect>span:first-child { div.multiselect>span:last-child { position: absolute; - right: .8em; + right: 13px; } ul.multiselectoptions input.new { - padding-bottom: .2em; - padding-top: .2em; + padding-bottom: 3px; + padding-top: 3px; margin: 0; } diff --git a/core/css/share.css b/core/css/share.css index d8140242e0..4ae3b77757 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -8,54 +8,53 @@ border-bottom-right-radius: 5px; box-shadow:0 1px 1px #777; display:block; - margin-right:7em; + margin-right:112px; position:absolute; right:0; - width:25em; + width:320px; z-index:500; - padding:1em; + padding:16px; } #shareWithList { list-style-type:none; - padding:.5em; + padding:8px; } - #shareWithList li { - padding-top: 10px; - padding-bottom: 10px; - font-weight: bold; - line-height: 21px; - white-space: normal; - } +#shareWithList li { + padding-top: 10px; + padding-bottom: 10px; + font-weight: bold; + line-height: 21px; + white-space: normal; +} - #shareWithList .unshare img, #shareWithList .showCruds img { - vertical-align:text-bottom; /* properly align icons */ - } +#shareWithList .unshare img, #shareWithList .showCruds img { + vertical-align:text-bottom; /* properly align icons */ +} - #shareWithList label input[type=checkbox]{ - margin-left: 0; - } - #shareWithList .username{ - padding-right: .5em; - white-space: nowrap; - text-overflow: ellipsis; - max-width: 254px; - display: inline-block; - overflow: hidden; - vertical-align: middle; - } - #shareWithList li label{ - margin-right: .5em; - } +#shareWithList label input[type=checkbox]{ + margin-left: 0; +} +#shareWithList .username{ + padding-right: 8px; + white-space: nowrap; + text-overflow: ellipsis; + max-width: 254px; + display: inline-block; + overflow: hidden; + vertical-align: middle; +} +#shareWithList li label{ + margin-right: 8px; +} #dropdown label { font-weight:400; white-space: nowrap; } #dropdown input[type="checkbox"] { - margin:0 .2em 0 .5em; - vertical-align: middle; + margin:0 3px 0 8px; } a.showCruds { @@ -67,13 +66,13 @@ a.unshare { display:inline; float:right; opacity:.5; - padding:.3em 0 0 .3em !important; + padding:5px 0 0 5px !important; margin-top:-5px; } #link { border-top:1px solid #ddd; - padding-top:.5em; + padding-top:8px; } #dropdown input[type="text"],#dropdown input[type="password"] { @@ -91,12 +90,12 @@ a.unshare { } #link #showPassword img { - padding-left:.3em; + padding-left:5px; width:12px; } .reshare,#link label,#expiration label { - padding-left:.5em; + padding-left:8px; } a.showCruds:hover,a.unshare:hover { diff --git a/core/css/styles.css b/core/css/styles.css index 29fcdd4573..bee44785f1 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -264,7 +264,7 @@ input[type="submit"].enabled { top: 45px; } #content-wrapper { - position:absolute; height:100%; width:100%; padding-top:3.5em; padding-left:80px; + position:absolute; height:100%; width:100%; padding-left:80px; padding-top: 45px; -moz-box-sizing:border-box; box-sizing:border-box; } #leftcontent, .leftcontent { @@ -686,7 +686,7 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } /* Apps management as sticky footer, less obtrusive in the list */ #navigation .wrapper { min-height: 100%; - margin: 0 auto -72px; + margin: 0 auto -82px 0; } #apps-management, #navigation .push { height: 72px; diff --git a/core/img/actions/checkmark-white.png b/core/img/actions/checkmark-white.png new file mode 100644 index 0000000000..08b8783649 Binary files /dev/null and b/core/img/actions/checkmark-white.png differ diff --git a/core/img/actions/checkmark-white.svg b/core/img/actions/checkmark-white.svg new file mode 100644 index 0000000000..5e8fe8abcc --- /dev/null +++ b/core/img/actions/checkmark-white.svg @@ -0,0 +1,4 @@ +image/svg+xml + + + diff --git a/core/img/actions/toggle-filelist.png b/core/img/actions/toggle-filelist.png new file mode 100644 index 0000000000..45d363f193 Binary files /dev/null and b/core/img/actions/toggle-filelist.png differ diff --git a/core/img/actions/toggle-filelist.svg b/core/img/actions/toggle-filelist.svg new file mode 100644 index 0000000000..940f6a49e6 --- /dev/null +++ b/core/img/actions/toggle-filelist.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/core/img/actions/toggle-pictures.png b/core/img/actions/toggle-pictures.png new file mode 100644 index 0000000000..8068d17e30 Binary files /dev/null and b/core/img/actions/toggle-pictures.png differ diff --git a/core/img/actions/toggle-pictures.svg b/core/img/actions/toggle-pictures.svg new file mode 100644 index 0000000000..5205c0226d --- /dev/null +++ b/core/img/actions/toggle-pictures.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/core/img/places/picture.png b/core/img/places/picture.png index 7b3af8c7f8..2b96ea518c 100644 Binary files a/core/img/places/picture.png and b/core/img/places/picture.png differ diff --git a/core/img/places/picture.svg b/core/img/places/picture.svg index 791cbb5909..82d457f5c7 100644 --- a/core/img/places/picture.svg +++ b/core/img/places/picture.svg @@ -1,7 +1,4 @@ - - - - + diff --git a/core/js/config.php b/core/js/config.php index dd46f7889d..517ea1615a 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -55,6 +55,12 @@ $array = array( ) ), "firstDay" => json_encode($l->l('firstday', 'firstday')) , + "oc_config" => json_encode( + array( + 'session_lifetime' => \OCP\Config::getSystemValue('session_lifetime', 60 * 60 * 24), + 'session_keepalive' => \OCP\Config::getSystemValue('session_keepalive', true) + ) + ) ); // Echo it diff --git a/core/js/core.json b/core/js/core.json new file mode 100644 index 0000000000..4beab7cf79 --- /dev/null +++ b/core/js/core.json @@ -0,0 +1,23 @@ +{ + "libraries": [ + "jquery-1.10.0.min.js", + "jquery-migrate-1.2.1.min.js", + "jquery-ui-1.10.0.custom.js", + "jquery-showpassword.js", + "jquery.infieldlabel.js", + "jquery.placeholder.js", + "jquery-tipsy.js" + ], + "modules": [ + "compatibility.js", + "jquery.ocdialog.js", + "oc-dialogs.js", + "js.js", + "octemplate.js", + "eventsource.js", + "config.js", + "multiselect.js", + "router.js", + "oc-requesttoken.js" + ] +} diff --git a/core/js/js.js b/core/js/js.js index e84f482d67..cb177712a3 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -11,6 +11,8 @@ var oc_webroot; var oc_current_user = document.getElementsByTagName('head')[0].getAttribute('data-user'); var oc_requesttoken = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken'); +window.oc_config = window.oc_config || {}; + if (typeof oc_webroot === "undefined") { oc_webroot = location.pathname; var pos = oc_webroot.indexOf('/index.php/'); @@ -252,6 +254,12 @@ var OC={ } return link; }, + /** + * Redirect to the target URL, can also be used for downloads. + */ + redirect: function(targetUrl) { + window.location = targetUrl; + }, /** * get the absolute path to an image file * @param app the app id to which the image belongs @@ -364,6 +372,34 @@ var OC={ } return result; }, + + /** + * Builds a URL query from a JS map. + * @param params parameter map + * @return string containing a URL query (without question) mark + */ + buildQueryString: function(params) { + var s = ''; + var first = true; + if (!params) { + return s; + } + for (var key in params) { + var value = params[key]; + if (first) { + first = false; + } + else { + s += '&'; + } + s += encodeURIComponent(key); + if (value !== null && typeof(value) !== 'undefined') { + s += '=' + encodeURIComponent(value); + } + } + return s; + }, + /** * Opens a popup with the setting for an app. * @param appid String. The ID of the app e.g. 'calendar', 'contacts' or 'files'. @@ -708,8 +744,39 @@ function fillWindow(selector) { console.warn("This function is deprecated! Use CSS instead"); } -$(document).ready(function(){ - sessionHeartBeat(); +/** + * Initializes core + */ +function initCore() { + + /** + * Calls the server periodically to ensure that session doesn't + * time out + */ + function initSessionHeartBeat(){ + // interval in seconds + var interval = 900; + if (oc_config.session_lifetime) { + interval = Math.floor(oc_config.session_lifetime / 2); + } + // minimum one minute + if (interval < 60) { + interval = 60; + } + OC.Router.registerLoadedCallback(function(){ + var url = OC.Router.generate('heartbeat'); + setInterval(function(){ + $.post(url); + }, interval * 1000); + }); + } + + // session heartbeat (defaults to enabled) + if (typeof(oc_config.session_keepalive) === 'undefined' || + !!oc_config.session_keepalive) { + + initSessionHeartBeat(); + } if(!SVGSupport()){ //replace all svg images with png images for browser that dont support svg replaceSVG(); @@ -822,7 +889,9 @@ $(document).ready(function(){ $('input[type=text]').focus(function(){ this.select(); }); -}); +} + +$(document).ready(initCore); /** * Filter Jquery selector by attribute value @@ -952,15 +1021,3 @@ jQuery.fn.exists = function(){ return this.length > 0; }; -/** - * Calls the server periodically every 15 mins to ensure that session doesnt - * time out - */ -function sessionHeartBeat(){ - OC.Router.registerLoadedCallback(function(){ - var url = OC.Router.generate('heartbeat'); - setInterval(function(){ - $.post(url); - }, 900000); - }); -} diff --git a/core/js/router.js b/core/js/router.js index 44e7c30602..e6ef54a186 100644 --- a/core/js/router.js +++ b/core/js/router.js @@ -3,9 +3,12 @@ OC.Router = { // register your ajax requests to load after the loading of the routes // has finished. otherwise you face problems with race conditions registerLoadedCallback: function(callback){ + if (!this.routes_request){ + return; + } this.routes_request.done(callback); }, - routes_request: $.ajax(OC.router_base_url + '/core/routes.json', { + routes_request: !window.TESTING && $.ajax(OC.router_base_url + '/core/routes.json', { dataType: 'json', success: function(jsondata) { if (jsondata.status === 'success') { @@ -75,4 +78,4 @@ OC.Router = { return OC.router_base_url + url; } -}; +} diff --git a/core/js/share.js b/core/js/share.js index 3637d2e7e7..0939259b7d 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -733,12 +733,16 @@ $(document).ready(function() { var itemSource = $('#dropdown').data('item-source'); var file = $('tr').filterAttr('data-id', String(itemSource)).data('file'); var email = $('#email').val(); + var expirationDate = ''; + if ( $('#expirationCheckbox').is(':checked') === true ) { + expirationDate = $( "#expirationDate" ).val(); + } if (email != '') { $('#email').prop('disabled', true); $('#email').val(t('core', 'Sending ...')); $('#emailButton').prop('disabled', true); - $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'email', toaddress: email, link: link, itemType: itemType, itemSource: itemSource, file: file}, + $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'email', toaddress: email, link: link, itemType: itemType, itemSource: itemSource, file: file, expiration: expirationDate}, function(result) { $('#email').prop('disabled', false); $('#emailButton').prop('disabled', false); diff --git a/core/js/tests/lib/sinon-1.7.3.js b/core/js/tests/lib/sinon-1.7.3.js new file mode 100644 index 0000000000..26c4bd9c46 --- /dev/null +++ b/core/js/tests/lib/sinon-1.7.3.js @@ -0,0 +1,4290 @@ +/** + * Sinon.JS 1.7.3, 2013/06/20 + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + * + * (The BSD License) + * + * Copyright (c) 2010-2013, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +this.sinon = (function () { +var buster = (function (setTimeout, B) { + var isNode = typeof require == "function" && typeof module == "object"; + var div = typeof document != "undefined" && document.createElement("div"); + var F = function () {}; + + var buster = { + bind: function bind(obj, methOrProp) { + var method = typeof methOrProp == "string" ? obj[methOrProp] : methOrProp; + var args = Array.prototype.slice.call(arguments, 2); + return function () { + var allArgs = args.concat(Array.prototype.slice.call(arguments)); + return method.apply(obj, allArgs); + }; + }, + + partial: function partial(fn) { + var args = [].slice.call(arguments, 1); + return function () { + return fn.apply(this, args.concat([].slice.call(arguments))); + }; + }, + + create: function create(object) { + F.prototype = object; + return new F(); + }, + + extend: function extend(target) { + if (!target) { return; } + for (var i = 1, l = arguments.length, prop; i < l; ++i) { + for (prop in arguments[i]) { + target[prop] = arguments[i][prop]; + } + } + return target; + }, + + nextTick: function nextTick(callback) { + if (typeof process != "undefined" && process.nextTick) { + return process.nextTick(callback); + } + setTimeout(callback, 0); + }, + + functionName: function functionName(func) { + if (!func) return ""; + if (func.displayName) return func.displayName; + if (func.name) return func.name; + var matches = func.toString().match(/function\s+([^\(]+)/m); + return matches && matches[1] || ""; + }, + + isNode: function isNode(obj) { + if (!div) return false; + try { + obj.appendChild(div); + obj.removeChild(div); + } catch (e) { + return false; + } + return true; + }, + + isElement: function isElement(obj) { + return obj && obj.nodeType === 1 && buster.isNode(obj); + }, + + isArray: function isArray(arr) { + return Object.prototype.toString.call(arr) == "[object Array]"; + }, + + flatten: function flatten(arr) { + var result = [], arr = arr || []; + for (var i = 0, l = arr.length; i < l; ++i) { + result = result.concat(buster.isArray(arr[i]) ? flatten(arr[i]) : arr[i]); + } + return result; + }, + + each: function each(arr, callback) { + for (var i = 0, l = arr.length; i < l; ++i) { + callback(arr[i]); + } + }, + + map: function map(arr, callback) { + var results = []; + for (var i = 0, l = arr.length; i < l; ++i) { + results.push(callback(arr[i])); + } + return results; + }, + + parallel: function parallel(fns, callback) { + function cb(err, res) { + if (typeof callback == "function") { + callback(err, res); + callback = null; + } + } + if (fns.length == 0) { return cb(null, []); } + var remaining = fns.length, results = []; + function makeDone(num) { + return function done(err, result) { + if (err) { return cb(err); } + results[num] = result; + if (--remaining == 0) { cb(null, results); } + }; + } + for (var i = 0, l = fns.length; i < l; ++i) { + fns[i](makeDone(i)); + } + }, + + series: function series(fns, callback) { + function cb(err, res) { + if (typeof callback == "function") { + callback(err, res); + } + } + var remaining = fns.slice(); + var results = []; + function callNext() { + if (remaining.length == 0) return cb(null, results); + var promise = remaining.shift()(next); + if (promise && typeof promise.then == "function") { + promise.then(buster.partial(next, null), next); + } + } + function next(err, result) { + if (err) return cb(err); + results.push(result); + callNext(); + } + callNext(); + }, + + countdown: function countdown(num, done) { + return function () { + if (--num == 0) done(); + }; + } + }; + + if (typeof process === "object" && + typeof require === "function" && typeof module === "object") { + var crypto = require("crypto"); + var path = require("path"); + + buster.tmpFile = function (fileName) { + var hashed = crypto.createHash("sha1"); + hashed.update(fileName); + var tmpfileName = hashed.digest("hex"); + + if (process.platform == "win32") { + return path.join(process.env["TEMP"], tmpfileName); + } else { + return path.join("/tmp", tmpfileName); + } + }; + } + + if (Array.prototype.some) { + buster.some = function (arr, fn, thisp) { + return arr.some(fn, thisp); + }; + } else { + // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some + buster.some = function (arr, fun, thisp) { + if (arr == null) { throw new TypeError(); } + arr = Object(arr); + var len = arr.length >>> 0; + if (typeof fun !== "function") { throw new TypeError(); } + + for (var i = 0; i < len; i++) { + if (arr.hasOwnProperty(i) && fun.call(thisp, arr[i], i, arr)) { + return true; + } + } + + return false; + }; + } + + if (Array.prototype.filter) { + buster.filter = function (arr, fn, thisp) { + return arr.filter(fn, thisp); + }; + } else { + // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter + buster.filter = function (fn, thisp) { + if (this == null) { throw new TypeError(); } + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fn != "function") { throw new TypeError(); } + + var res = []; + for (var i = 0; i < len; i++) { + if (i in t) { + var val = t[i]; // in case fun mutates this + if (fn.call(thisp, val, i, t)) { res.push(val); } + } + } + + return res; + }; + } + + if (isNode) { + module.exports = buster; + buster.eventEmitter = require("./buster-event-emitter"); + Object.defineProperty(buster, "defineVersionGetter", { + get: function () { + return require("./define-version-getter"); + } + }); + } + + return buster.extend(B || {}, buster); +}(setTimeout, buster)); +if (typeof buster === "undefined") { + var buster = {}; +} + +if (typeof module === "object" && typeof require === "function") { + buster = require("buster-core"); +} + +buster.format = buster.format || {}; +buster.format.excludeConstructors = ["Object", /^.$/]; +buster.format.quoteStrings = true; + +buster.format.ascii = (function () { + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global != "undefined") { + specialObjects.push({ obj: global, value: "[object global]" }); + } + if (typeof document != "undefined") { + specialObjects.push({ obj: document, value: "[object HTMLDocument]" }); + } + if (typeof window != "undefined") { + specialObjects.push({ obj: window, value: "[object Window]" }); + } + + function keys(object) { + var k = Object.keys && Object.keys(object) || []; + + if (k.length == 0) { + for (var prop in object) { + if (hasOwn.call(object, prop)) { + k.push(prop); + } + } + } + + return k.sort(); + } + + function isCircular(object, objects) { + if (typeof object != "object") { + return false; + } + + for (var i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { + return true; + } + } + + return false; + } + + function ascii(object, processed, indent) { + if (typeof object == "string") { + var quote = typeof this.quoteStrings != "boolean" || this.quoteStrings; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object == "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { + return "[Circular]"; + } + + if (Object.prototype.toString.call(object) == "[object Array]") { + return ascii.array.call(this, object, processed); + } + + if (!object) { + return "" + object; + } + + if (buster.isElement(object)) { + return ascii.element(object); + } + + if (typeof object.toString == "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + for (var i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].obj) { + return specialObjects[i].value; + } + } + + return ascii.object.call(this, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + buster.functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + + for (var i = 0, l = array.length; i < l; ++i) { + pieces.push(ascii.call(this, array[i], processed)); + } + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = keys(object), prop, str, obj; + var is = ""; + var length = 3; + + for (var i = 0, l = indent; i < l; ++i) { + is += " "; + } + + for (i = 0, l = properties.length; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii.call(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = ascii.constructorName.call(this, object); + var prefix = cons ? "[" + cons + "] " : "" + + return (length + indent) > 80 ? + prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + is + "}" : + prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attribute, pairs = [], attrName; + + for (var i = 0, l = attrs.length; i < l; ++i) { + attribute = attrs.item(i); + attrName = attribute.nodeName.toLowerCase().replace("html:", ""); + + if (attrName == "contenteditable" && attribute.nodeValue == "inherit") { + continue; + } + + if (!!attribute.nodeValue) { + pairs.push(attrName + "=\"" + attribute.nodeValue + "\""); + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + ascii.constructorName = function (object) { + var name = buster.functionName(object && object.constructor); + var excludes = this.excludeConstructors || buster.format.excludeConstructors || []; + + for (var i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] == "string" && excludes[i] == name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + }; + + return ascii; +}()); + +if (typeof module != "undefined") { + module.exports = buster.format; +} +/*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/ +/*global module, require, __dirname, document*/ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +var sinon = (function (buster) { + var div = typeof document != "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode == obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable (obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + var sinon = { + wrapMethod: function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method != "function") { + throw new TypeError("Method wrapper should be function"); + } + + var wrappedMethod = object[property]; + + if (!isFunction(wrappedMethod)) { + throw new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } + + if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + throw new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + + if (wrappedMethod.calledBefore) { + var verb = !!wrappedMethod.returns ? "stubbed" : "spied on"; + throw new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + // IE 8 does not support hasOwnProperty on the window object. + var owned = hasOwn.call(object, property); + object[property] = method; + method.displayName = property; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + delete object[property]; + } + if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + mirrorProperties(method, wrappedMethod); + + return method; + }, + + extend: function extend(target) { + for (var i = 1, l = arguments.length; i < l; i += 1) { + for (var prop in arguments[i]) { + if (arguments[i].hasOwnProperty(prop)) { + target[prop] = arguments[i][prop]; + } + + // DONT ENUM bug, only care about toString + if (arguments[i].hasOwnProperty("toString") && + arguments[i].toString != target.toString) { + target.toString = arguments[i].toString; + } + } + } + + return target; + }, + + create: function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }, + + deepEqual: function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + if (typeof a != "object" || typeof b != "object") { + return a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + var aString = Object.prototype.toString.call(a); + if (aString != Object.prototype.toString.call(b)) { + return false; + } + + if (aString == "[object Array]") { + if (a.length !== b.length) { + return false; + } + + for (var i = 0, l = a.length; i < l; i += 1) { + if (!deepEqual(a[i], b[i])) { + return false; + } + } + + return true; + } + + if (aString == "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop, aLength = 0, bLength = 0; + + for (prop in a) { + aLength += 1; + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + + for (prop in b) { + bLength += 1; + } + + return aLength == bLength; + }, + + functionName: function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }, + + functionToString: function toString() { + if (this.getCall && this.callCount) { + var thisValue, prop, i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }, + + getConfig: function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }, + + format: function (val) { + return "" + val; + }, + + defaultConfig: { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }, + + timesInWords: function timesInWords(count) { + return count == 1 && "once" || + count == 2 && "twice" || + count == 3 && "thrice" || + (count || 0) + " times"; + }, + + calledInOrder: function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }, + + orderByFirstCall: function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }, + + log: function () {}, + + logError: function (label, err) { + var msg = label + " threw exception: " + sinon.log(msg + "[" + err.name + "] " + err.message); + if (err.stack) { sinon.log(err.stack); } + + setTimeout(function () { + err.message = msg + err.message; + throw err; + }, 0); + }, + + typeOf: function (value) { + if (value === null) { + return "null"; + } + else if (value === undefined) { + return "undefined"; + } + var string = Object.prototype.toString.call(value); + return string.substring(8, string.length - 1).toLowerCase(); + }, + + createStubInstance: function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }, + + restore: function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } + else if (isRestorable(object)) { + object.restore(); + } + } + }; + + var isNode = typeof module == "object" && typeof require == "function"; + + if (isNode) { + try { + buster = { format: require("buster-format") }; + } catch (e) {} + module.exports = sinon; + module.exports.spy = require("./sinon/spy"); + module.exports.stub = require("./sinon/stub"); + module.exports.mock = require("./sinon/mock"); + module.exports.collection = require("./sinon/collection"); + module.exports.assert = require("./sinon/assert"); + module.exports.sandbox = require("./sinon/sandbox"); + module.exports.test = require("./sinon/test"); + module.exports.testCase = require("./sinon/test_case"); + module.exports.assert = require("./sinon/assert"); + module.exports.match = require("./sinon/match"); + } + + if (buster) { + var formatter = sinon.create(buster.format); + formatter.quoteStrings = false; + sinon.format = function () { + return formatter.ascii.apply(formatter, arguments); + }; + } else if (isNode) { + try { + var util = require("util"); + sinon.format = function (value) { + return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value; + }; + } catch (e) { + /* Node, but no util module - would be very old, but better safe than + sorry */ + } + } + + return sinon; +}(typeof buster == "object" && buster)); + +/* @depend ../sinon.js */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Match functions + * + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2012 Maximilian Antoni + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function assertType(value, type, name) { + var actual = sinon.typeOf(value); + if (actual !== type) { + throw new TypeError("Expected type of " + name + " to be " + + type + ", but was " + actual); + } + } + + var matcher = { + toString: function () { + return this.message; + } + }; + + function isMatcher(object) { + return matcher.isPrototypeOf(object); + } + + function matchObject(expectation, actual) { + if (actual === null || actual === undefined) { + return false; + } + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + var exp = expectation[key]; + var act = actual[key]; + if (match.isMatcher(exp)) { + if (!exp.test(act)) { + return false; + } + } else if (sinon.typeOf(exp) === "object") { + if (!matchObject(exp, act)) { + return false; + } + } else if (!sinon.deepEqual(exp, act)) { + return false; + } + } + } + return true; + } + + matcher.or = function (m2) { + if (!isMatcher(m2)) { + throw new TypeError("Matcher expected"); + } + var m1 = this; + var or = sinon.create(matcher); + or.test = function (actual) { + return m1.test(actual) || m2.test(actual); + }; + or.message = m1.message + ".or(" + m2.message + ")"; + return or; + }; + + matcher.and = function (m2) { + if (!isMatcher(m2)) { + throw new TypeError("Matcher expected"); + } + var m1 = this; + var and = sinon.create(matcher); + and.test = function (actual) { + return m1.test(actual) && m2.test(actual); + }; + and.message = m1.message + ".and(" + m2.message + ")"; + return and; + }; + + var match = function (expectation, message) { + var m = sinon.create(matcher); + var type = sinon.typeOf(expectation); + switch (type) { + case "object": + if (typeof expectation.test === "function") { + m.test = function (actual) { + return expectation.test(actual) === true; + }; + m.message = "match(" + sinon.functionName(expectation.test) + ")"; + return m; + } + var str = []; + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + str.push(key + ": " + expectation[key]); + } + } + m.test = function (actual) { + return matchObject(expectation, actual); + }; + m.message = "match(" + str.join(", ") + ")"; + break; + case "number": + m.test = function (actual) { + return expectation == actual; + }; + break; + case "string": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return actual.indexOf(expectation) !== -1; + }; + m.message = "match(\"" + expectation + "\")"; + break; + case "regexp": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return expectation.test(actual); + }; + break; + case "function": + m.test = expectation; + if (message) { + m.message = message; + } else { + m.message = "match(" + sinon.functionName(expectation) + ")"; + } + break; + default: + m.test = function (actual) { + return sinon.deepEqual(expectation, actual); + }; + } + if (!m.message) { + m.message = "match(" + expectation + ")"; + } + return m; + }; + + match.isMatcher = isMatcher; + + match.any = match(function () { + return true; + }, "any"); + + match.defined = match(function (actual) { + return actual !== null && actual !== undefined; + }, "defined"); + + match.truthy = match(function (actual) { + return !!actual; + }, "truthy"); + + match.falsy = match(function (actual) { + return !actual; + }, "falsy"); + + match.same = function (expectation) { + return match(function (actual) { + return expectation === actual; + }, "same(" + expectation + ")"); + }; + + match.typeOf = function (type) { + assertType(type, "string", "type"); + return match(function (actual) { + return sinon.typeOf(actual) === type; + }, "typeOf(\"" + type + "\")"); + }; + + match.instanceOf = function (type) { + assertType(type, "function", "type"); + return match(function (actual) { + return actual instanceof type; + }, "instanceOf(" + sinon.functionName(type) + ")"); + }; + + function createPropertyMatcher(propertyTest, messagePrefix) { + return function (property, value) { + assertType(property, "string", "property"); + var onlyProperty = arguments.length === 1; + var message = messagePrefix + "(\"" + property + "\""; + if (!onlyProperty) { + message += ", " + value; + } + message += ")"; + return match(function (actual) { + if (actual === undefined || actual === null || + !propertyTest(actual, property)) { + return false; + } + return onlyProperty || sinon.deepEqual(value, actual[property]); + }, message); + }; + } + + match.has = createPropertyMatcher(function (actual, property) { + if (typeof actual === "object") { + return property in actual; + } + return actual[property] !== undefined; + }, "has"); + + match.hasOwn = createPropertyMatcher(function (actual, property) { + return actual.hasOwnProperty(property); + }, "hasOwn"); + + match.bool = match.typeOf("boolean"); + match.number = match.typeOf("number"); + match.string = match.typeOf("string"); + match.object = match.typeOf("object"); + match.func = match.typeOf("function"); + match.array = match.typeOf("array"); + match.regexp = match.typeOf("regexp"); + match.date = match.typeOf("date"); + + if (commonJSModule) { + module.exports = match; + } else { + sinon.match = match; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend match.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Spy calls + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + * Copyright (c) 2013 Maximilian Antoni + */ + +var commonJSModule = typeof module == "object" && typeof require == "function"; + +if (!this.sinon && commonJSModule) { + var sinon = require("../sinon"); +} + +(function (sinon) { + function throwYieldError(proxy, text, args) { + var msg = sinon.functionName(proxy) + text; + if (args.length) { + msg += " Received [" + slice.call(args).join(", ") + "]"; + } + throw new Error(msg); + } + + var slice = Array.prototype.slice; + + var callProto = { + calledOn: function calledOn(thisValue) { + if (sinon.match && sinon.match.isMatcher(thisValue)) { + return thisValue.test(this.thisValue); + } + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + for (var i = 0, l = arguments.length; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithMatch: function calledWithMatch() { + for (var i = 0, l = arguments.length; i < l; i += 1) { + var actual = this.args[i]; + var expectation = arguments[i]; + if (!sinon.match || !sinon.match(expectation).test(actual)) { + return false; + } + } + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length == this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + return !this.calledWith.apply(this, arguments); + }, + + notCalledWithMatch: function notCalledWithMatch() { + return !this.calledWithMatch.apply(this, arguments); + }, + + returned: function returned(value) { + return sinon.deepEqual(value, this.returnValue); + }, + + threw: function threw(error) { + if (typeof error === "undefined" || !this.exception) { + return !!this.exception; + } + + return this.exception === error || this.exception.name === error; + }, + + calledWithNew: function calledWithNew(thisValue) { + return this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + callArg: function (pos) { + this.args[pos](); + }, + + callArgOn: function (pos, thisValue) { + this.args[pos].apply(thisValue); + }, + + callArgWith: function (pos) { + this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + }, + + callArgOnWith: function (pos, thisValue) { + var args = slice.call(arguments, 2); + this.args[pos].apply(thisValue, args); + }, + + "yield": function () { + this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + }, + + yieldOn: function (thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (typeof args[i] === "function") { + args[i].apply(thisValue, slice.call(arguments, 1)); + return; + } + } + throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); + }, + + yieldTo: function (prop) { + this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + }, + + yieldToOn: function (prop, thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (args[i] && typeof args[i][prop] === "function") { + args[i][prop].apply(thisValue, slice.call(arguments, 2)); + return; + } + } + throwYieldError(this.proxy, " cannot yield to '" + prop + + "' since no callback was passed.", args); + }, + + toString: function () { + var callStr = this.proxy.toString() + "("; + var args = []; + + for (var i = 0, l = this.args.length; i < l; ++i) { + args.push(sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue != "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + + return callStr; + } + }; + + callProto.invokeCallback = callProto.yield; + + function createSpyCall(spy, thisValue, args, returnValue, exception, id) { + if (typeof id !== "number") { + throw new TypeError("Call id is not a number"); + } + var proxyCall = sinon.create(callProto); + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = id; + + return proxyCall; + }; + createSpyCall.toString = callProto.toString; // used by mocks + + sinon.spyCall = createSpyCall; +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + var push = Array.prototype.push; + var slice = Array.prototype.slice; + var callId = 0; + + function spy(object, property) { + if (!property && typeof object == "function") { + return spy.create(object); + } + + if (!object && !property) { + return spy.create(function () { }); + } + + var method = object[property]; + return sinon.wrapMethod(object, property, spy.create(method)); + } + + function matchingFake(fakes, args, strict) { + if (!fakes) { + return; + } + + var alen = args.length; + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } + } + + function incrementCallCount() { + this.called = true; + this.callCount += 1; + this.notCalled = false; + this.calledOnce = this.callCount == 1; + this.calledTwice = this.callCount == 2; + this.calledThrice = this.callCount == 3; + } + + function createCallProperties() { + this.firstCall = this.getCall(0); + this.secondCall = this.getCall(1); + this.thirdCall = this.getCall(2); + this.lastCall = this.getCall(this.callCount - 1); + } + + var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; + function createProxy(func) { + // Retain the function length: + var p; + if (func.length) { + eval("p = (function proxy(" + vars.substring(0, func.length * 2 - 1) + + ") { return p.invoke(func, this, slice.call(arguments)); });"); + } + else { + p = function proxy() { + return p.invoke(func, this, slice.call(arguments)); + }; + } + return p; + } + + var uuid = 0; + + // Public API + var spyApi = { + reset: function () { + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + if (this.fakes) { + for (var i = 0; i < this.fakes.length; i++) { + this.fakes[i].reset(); + } + } + }, + + create: function create(func) { + var name; + + if (typeof func != "function") { + func = function () { }; + } else { + name = sinon.functionName(func); + } + + var proxy = createProxy(func); + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy._create = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + + incrementCallCount.call(this); + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + try { + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + } catch (e) { + push.call(this.returnValues, undefined); + exception = e; + throw e; + } finally { + push.call(this.exceptions, exception); + } + + push.call(this.returnValues, returnValue); + + createCallProperties.call(this); + + return returnValue; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return sinon.spyCall(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i]); + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this._create(); + fake.matchingAguments = args; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + for (var i = 0; i < this.args.length; i++) { + if (fake.matches(this.args[i])) { + incrementCallCount.call(fake); + push.call(fake.thisValues, this.thisValues[i]); + push.call(fake.args, this.args[i]); + push.call(fake.returnValues, this.returnValues[i]); + push.call(fake.exceptions, this.exceptions[i]); + push.call(fake.callIds, this.callIds[i]); + } + } + createCallProperties.call(fake); + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length == args.length; + } + }, + + printf: function (format) { + var spy = this; + var args = slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter == "function") { + return formatter.call(null, spy, args); + } else if (!isNaN(parseInt(specifyer), 10)) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } + }; + + function delegateToCalls(method, matchAny, actual, notCalled) { + spyApi[method] = function () { + if (!this.called) { + if (notCalled) { + return notCalled.apply(this, arguments); + } + return false; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; + } + + delegateToCalls("calledOn", true); + delegateToCalls("alwaysCalledOn", false, "calledOn"); + delegateToCalls("calledWith", true); + delegateToCalls("calledWithMatch", true); + delegateToCalls("alwaysCalledWith", false, "calledWith"); + delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); + delegateToCalls("calledWithExactly", true); + delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); + delegateToCalls("neverCalledWith", false, "notCalledWith", + function () { return true; }); + delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", + function () { return true; }); + delegateToCalls("threw", true); + delegateToCalls("alwaysThrew", false, "threw"); + delegateToCalls("returned", true); + delegateToCalls("alwaysReturned", false, "returned"); + delegateToCalls("calledWithNew", true); + delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); + delegateToCalls("callArg", false, "callArgWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgWith = spyApi.callArg; + delegateToCalls("callArgOn", false, "callArgOnWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgOnWith = spyApi.callArgOn; + delegateToCalls("yield", false, "yield", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. + spyApi.invokeCallback = spyApi.yield; + delegateToCalls("yieldOn", false, "yieldOn", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + delegateToCalls("yieldTo", false, "yieldTo", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + + spyApi.formatters = { + "c": function (spy) { + return sinon.timesInWords(spy.callCount); + }, + + "n": function (spy) { + return spy.toString(); + }, + + "C": function (spy) { + var calls = []; + + for (var i = 0, l = spy.callCount; i < l; ++i) { + var stringifiedCall = " " + spy.getCall(i).toString(); + if (/\n/.test(calls[i - 1])) { + stringifiedCall = "\n" + stringifiedCall; + } + push.call(calls, stringifiedCall); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + "t": function (spy) { + var objects = []; + + for (var i = 0, l = spy.callCount; i < l; ++i) { + push.call(objects, sinon.format(spy.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spy, args) { + var formatted = []; + + for (var i = 0, l = args.length; i < l; ++i) { + push.call(formatted, sinon.format(args[i])); + } + + return formatted.join(", "); + } + }; + + sinon.extend(spy, spyApi); + + spy.spyCall = sinon.spyCall; + + if (commonJSModule) { + module.exports = spy; + } else { + sinon.spy = spy; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend spy.js + */ +/*jslint eqeqeq: false, onevar: false*/ +/*global module, require, sinon*/ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function stub(object, property, func) { + if (!!func && typeof func != "function") { + throw new TypeError("Custom stub should be function"); + } + + var wrapper; + + if (func) { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = stub.create(); + } + + if (!object && !property) { + return sinon.stub.create(); + } + + if (!property && !!object && typeof object == "object") { + for (var prop in object) { + if (typeof object[prop] === "function") { + stub(object, prop); + } + } + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); + } + + function getChangingValue(stub, property) { + var index = stub.callCount - 1; + var values = stub[property]; + var prop = index in values ? values[index] : values[values.length - 1]; + stub[property + "Last"] = prop; + + return prop; + } + + function getCallback(stub, args) { + var callArgAt = getChangingValue(stub, "callArgAts"); + + if (callArgAt < 0) { + var callArgProp = getChangingValue(stub, "callArgProps"); + + for (var i = 0, l = args.length; i < l; ++i) { + if (!callArgProp && typeof args[i] == "function") { + return args[i]; + } + + if (callArgProp && args[i] && + typeof args[i][callArgProp] == "function") { + return args[i][callArgProp]; + } + } + + return null; + } + + return args[callArgAt]; + } + + var join = Array.prototype.join; + + function getCallbackError(stub, func, args) { + if (stub.callArgAtsLast < 0) { + var msg; + + if (stub.callArgPropsLast) { + msg = sinon.functionName(stub) + + " expected to yield to '" + stub.callArgPropsLast + + "', but no object with such a property was passed." + } else { + msg = sinon.functionName(stub) + + " expected to yield, but no callback was passed." + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + stub.callArgAtsLast + " is not a function: " + func; + } + + var nextTick = (function () { + if (typeof process === "object" && typeof process.nextTick === "function") { + return process.nextTick; + } else if (typeof setImmediate === "function") { + return setImmediate; + } else { + return function (callback) { + setTimeout(callback, 0); + }; + } + })(); + + function callCallback(stub, args) { + if (stub.callArgAts.length > 0) { + var func = getCallback(stub, args); + + if (typeof func != "function") { + throw new TypeError(getCallbackError(stub, func, args)); + } + + var callbackArguments = getChangingValue(stub, "callbackArguments"); + var callbackContext = getChangingValue(stub, "callbackContexts"); + + if (stub.callbackAsync) { + nextTick(function() { + func.apply(callbackContext, callbackArguments); + }); + } else { + func.apply(callbackContext, callbackArguments); + } + } + } + + var uuid = 0; + + sinon.extend(stub, (function () { + var slice = Array.prototype.slice, proto; + + function throwsException(error, message) { + if (typeof error == "string") { + this.exception = new Error(message || ""); + this.exception.name = error; + } else if (!error) { + this.exception = new Error("Error"); + } else { + this.exception = error; + } + + return this; + } + + proto = { + create: function create() { + var functionStub = function () { + + callCallback(functionStub, arguments); + + if (functionStub.exception) { + throw functionStub.exception; + } else if (typeof functionStub.returnArgAt == 'number') { + return arguments[functionStub.returnArgAt]; + } else if (functionStub.returnThis) { + return this; + } + return functionStub.returnValue; + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub); + functionStub.func = orig; + + functionStub.callArgAts = []; + functionStub.callbackArguments = []; + functionStub.callbackContexts = []; + functionStub.callArgProps = []; + + sinon.extend(functionStub, stub); + functionStub._create = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + return functionStub; + }, + + resetBehavior: function () { + var i; + + this.callArgAts = []; + this.callbackArguments = []; + this.callbackContexts = []; + this.callArgProps = []; + + delete this.returnValue; + delete this.returnArgAt; + this.returnThis = false; + + if (this.fakes) { + for (i = 0; i < this.fakes.length; i++) { + this.fakes[i].resetBehavior(); + } + } + }, + + returns: function returns(value) { + this.returnValue = value; + + return this; + }, + + returnsArg: function returnsArg(pos) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + + this.returnArgAt = pos; + + return this; + }, + + returnsThis: function returnsThis() { + this.returnThis = true; + + return this; + }, + + "throws": throwsException, + throwsException: throwsException, + + callsArg: function callsArg(pos) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAts.push(pos); + this.callbackArguments.push([]); + this.callbackContexts.push(undefined); + this.callArgProps.push(undefined); + + return this; + }, + + callsArgOn: function callsArgOn(pos, context) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context != "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAts.push(pos); + this.callbackArguments.push([]); + this.callbackContexts.push(context); + this.callArgProps.push(undefined); + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAts.push(pos); + this.callbackArguments.push(slice.call(arguments, 1)); + this.callbackContexts.push(undefined); + this.callArgProps.push(undefined); + + return this; + }, + + callsArgOnWith: function callsArgWith(pos, context) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context != "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAts.push(pos); + this.callbackArguments.push(slice.call(arguments, 2)); + this.callbackContexts.push(context); + this.callArgProps.push(undefined); + + return this; + }, + + yields: function () { + this.callArgAts.push(-1); + this.callbackArguments.push(slice.call(arguments, 0)); + this.callbackContexts.push(undefined); + this.callArgProps.push(undefined); + + return this; + }, + + yieldsOn: function (context) { + if (typeof context != "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAts.push(-1); + this.callbackArguments.push(slice.call(arguments, 1)); + this.callbackContexts.push(context); + this.callArgProps.push(undefined); + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAts.push(-1); + this.callbackArguments.push(slice.call(arguments, 1)); + this.callbackContexts.push(undefined); + this.callArgProps.push(prop); + + return this; + }, + + yieldsToOn: function (prop, context) { + if (typeof context != "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAts.push(-1); + this.callbackArguments.push(slice.call(arguments, 2)); + this.callbackContexts.push(context); + this.callArgProps.push(prop); + + return this; + } + }; + + // create asynchronous versions of callsArg* and yields* methods + for (var method in proto) { + // need to avoid creating anotherasync versions of the newly added async methods + if (proto.hasOwnProperty(method) && + method.match(/^(callsArg|yields|thenYields$)/) && + !method.match(/Async/)) { + proto[method + 'Async'] = (function (syncFnName) { + return function () { + this.callbackAsync = true; + return this[syncFnName].apply(this, arguments); + }; + })(method); + } + } + + return proto; + + }())); + + if (commonJSModule) { + module.exports = stub; + } else { + sinon.stub = stub; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend stub.js + */ +/*jslint eqeqeq: false, onevar: false, nomen: false*/ +/*global module, require, sinon*/ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + var push = [].push; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function mock(object) { + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); + } + + sinon.mock = mock; + + sinon.extend(mock, (function () { + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + + return { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore == "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = [], met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } else { + sinon.expectation.pass(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method]; + var length = expectations && expectations.length || 0, i; + + for (i = 0; i < length; i += 1) { + if (!expectations[i].met() && + expectations[i].allowsCall(thisValue, args)) { + return expectations[i].apply(thisValue, args); + } + } + + var messages = [], available, exhausted = 0; + + for (i = 0; i < length; i += 1) { + if (expectations[i].allowsCall(thisValue, args)) { + available = available || expectations[i]; + } else { + exhausted += 1; + } + push.call(messages, " " + expectations[i].toString()); + } + + if (exhausted === 0) { + return available.apply(thisValue, args); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } + }; + }())); + + var times = sinon.timesInWords; + + sinon.expectation = (function () { + var slice = Array.prototype.slice; + var _invoke = sinon.spy.invoke; + + function callCountInWords(callCount) { + if (callCount == 0) { + return "never called"; + } else { + return "called " + times(callCount); + } + } + + function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min == "number" && typeof max == "number") { + var str = times(min); + + if (min != max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min == "number") { + return "at least " + times(min); + } + + return "at most " + times(max); + } + + function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls == "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; + } + + function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls != "number") { + return false; + } + + return expectation.callCount == expectation.maxCalls; + } + + return { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return _invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + sinon.format(this.expectedArguments)); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + if (this.expectsExactArgCount && + args.length != this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", expected " + sinon.format(this.expectedArguments)); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met() && receivedMaxCalls(this)) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length != this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method || "anonymous mock expectation", + args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } else { + sinon.expectation.pass(this.toString()); + } + + return true; + }, + + pass: function(message) { + sinon.assert.pass(message); + }, + fail: function (message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } + }; + }()); + + if (commonJSModule) { + module.exports = mock; + } else { + sinon.mock = mock; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend stub.js + * @depend mock.js + */ +/*jslint eqeqeq: false, onevar: false, forin: true*/ +/*global module, require, sinon*/ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + var push = [].push; + var hasOwnProperty = Object.prototype.hasOwnProperty; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; + } + + function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] == "function") { + fakes[i][method](); + } + } + } + + function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } + } + + var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + var original = object[property]; + + if (typeof original != "function") { + if (!hasOwnProperty.call(object, property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + if (!property && !!object && typeof object == "object") { + var stubbedObj = sinon.stub.apply(sinon, arguments); + + for (var prop in stubbedObj) { + if (typeof stubbedObj[prop] === "function") { + this.add(stubbedObj[prop]); + } + } + + return stubbedObj; + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } + }; + + if (commonJSModule) { + module.exports = collection; + } else { + sinon.collection = collection; + } +}(typeof sinon == "object" && sinon || null)); + +/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/ +/*global module, require, window*/ +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +if (typeof sinon == "undefined") { + var sinon = {}; +} + +(function (global) { + var id = 1; + + function addTimer(args, recurring) { + if (args.length === 0) { + throw new Error("Function requires at least 1 parameter"); + } + + var toId = id++; + var delay = args[1] || 0; + + if (!this.timeouts) { + this.timeouts = {}; + } + + this.timeouts[toId] = { + id: toId, + func: args[0], + callAt: this.now + delay, + invokeArgs: Array.prototype.slice.call(args, 2) + }; + + if (recurring === true) { + this.timeouts[toId].interval = delay; + } + + return toId; + } + + function parseTime(str) { + if (!str) { + return 0; + } + + var strings = str.split(":"); + var l = strings.length, i = l; + var ms = 0, parsed; + + if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + function createObject(object) { + var newObject; + + if (Object.create) { + newObject = Object.create(object); + } else { + var F = function () {}; + F.prototype = object; + newObject = new F(); + } + + newObject.Date.clock = newObject; + return newObject; + } + + sinon.clock = { + now: 0, + + create: function create(now) { + var clock = createObject(this); + + if (typeof now == "number") { + clock.now = now; + } + + if (!!now && typeof now == "object") { + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + return clock; + }, + + setTimeout: function setTimeout(callback, timeout) { + return addTimer.call(this, arguments, false); + }, + + clearTimeout: function clearTimeout(timerId) { + if (!this.timeouts) { + this.timeouts = []; + } + + if (timerId in this.timeouts) { + delete this.timeouts[timerId]; + } + }, + + setInterval: function setInterval(callback, timeout) { + return addTimer.call(this, arguments, true); + }, + + clearInterval: function clearInterval(timerId) { + this.clearTimeout(timerId); + }, + + tick: function tick(ms) { + ms = typeof ms == "number" ? ms : parseTime(ms); + var tickFrom = this.now, tickTo = this.now + ms, previous = this.now; + var timer = this.firstTimerInRange(tickFrom, tickTo); + + var firstException; + while (timer && tickFrom <= tickTo) { + if (this.timeouts[timer.id]) { + tickFrom = this.now = timer.callAt; + try { + this.callTimer(timer); + } catch (e) { + firstException = firstException || e; + } + } + + timer = this.firstTimerInRange(previous, tickTo); + previous = tickFrom; + } + + this.now = tickTo; + + if (firstException) { + throw firstException; + } + + return this.now; + }, + + firstTimerInRange: function (from, to) { + var timer, smallest, originalTimer; + + for (var id in this.timeouts) { + if (this.timeouts.hasOwnProperty(id)) { + if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) { + continue; + } + + if (!smallest || this.timeouts[id].callAt < smallest) { + originalTimer = this.timeouts[id]; + smallest = this.timeouts[id].callAt; + + timer = { + func: this.timeouts[id].func, + callAt: this.timeouts[id].callAt, + interval: this.timeouts[id].interval, + id: this.timeouts[id].id, + invokeArgs: this.timeouts[id].invokeArgs + }; + } + } + } + + return timer || null; + }, + + callTimer: function (timer) { + if (typeof timer.interval == "number") { + this.timeouts[timer.id].callAt += timer.interval; + } else { + delete this.timeouts[timer.id]; + } + + try { + if (typeof timer.func == "function") { + timer.func.apply(null, timer.invokeArgs); + } else { + eval(timer.func); + } + } catch (e) { + var exception = e; + } + + if (!this.timeouts[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } + }, + + reset: function reset() { + this.timeouts = {}; + }, + + Date: (function () { + var NativeDate = Date; + + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); + }()) + }; + + function mirrorDateProperties(target, source) { + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + return target; + } + + var methods = ["Date", "setTimeout", "setInterval", + "clearTimeout", "clearInterval"]; + + function restore() { + var method; + + for (var i = 0, l = this.methods.length; i < l; i++) { + method = this.methods[i]; + if (global[method].hadOwnProperty) { + global[method] = this["_" + method]; + } else { + delete global[method]; + } + } + + // Prevent multiple executions which will completely remove these props + this.methods = []; + } + + function stubGlobal(method, clock) { + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method); + clock["_" + method] = global[method]; + + if (method == "Date") { + var date = mirrorDateProperties(clock[method], global[method]); + global[method] = date; + } else { + global[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (var prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + global[method][prop] = clock[method][prop]; + } + } + } + + global[method].clock = clock; + } + + sinon.useFakeTimers = function useFakeTimers(now) { + var clock = sinon.clock.create(now); + clock.restore = restore; + clock.methods = Array.prototype.slice.call(arguments, + typeof now == "number" ? 1 : 0); + + if (clock.methods.length === 0) { + clock.methods = methods; + } + + for (var i = 0, l = clock.methods.length; i < l; i++) { + stubGlobal(clock.methods[i], clock); + } + + return clock; + }; +}(typeof global != "undefined" && typeof global !== "function" ? global : this)); + +sinon.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date +}; + +if (typeof module == "object" && typeof require == "function") { + module.exports = sinon; +} + +/*jslint eqeqeq: false, onevar: false*/ +/*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/ +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ + +if (typeof sinon == "undefined") { + this.sinon = {}; +} + +(function () { + var push = [].push; + + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function(type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener, useCapture) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener, useCapture) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] == listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] == "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; +}()); + +/** + * @depend ../../sinon.js + * @depend event.js + */ +/*jslint eqeqeq: false, onevar: false*/ +/*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +if (typeof sinon == "undefined") { + this.sinon = {}; +} +sinon.xhr = { XMLHttpRequest: this.XMLHttpRequest }; + +// wrapper for global +(function(global) { + var xhr = sinon.xhr; + xhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + xhr.GlobalActiveXObject = global.ActiveXObject; + xhr.supportsActiveX = typeof xhr.GlobalActiveXObject != "undefined"; + xhr.supportsXHR = typeof xhr.GlobalXMLHttpRequest != "undefined"; + xhr.workingXHR = xhr.supportsXHR ? xhr.GlobalXMLHttpRequest : xhr.supportsActiveX + ? function() { return new xhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0") } : false; + + /*jsl:ignore*/ + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + "Connection": true, + "Content-Length": true, + "Cookie": true, + "Cookie2": true, + "Content-Transfer-Encoding": true, + "Date": true, + "Expect": true, + "Host": true, + "Keep-Alive": true, + "Referer": true, + "TE": true, + "Trailer": true, + "Transfer-Encoding": true, + "Upgrade": true, + "User-Agent": true, + "Via": true + }; + /*jsl:end*/ + + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + + var xhr = this; + var events = ["loadstart", "load", "abort", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener == "function") { + listener(event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate == "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) return; + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if(callback(collection[index]) === true) return true; + }; + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function(obj,method,args) { + switch(args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0],args[1]); + case 3: return obj[method](args[0],args[1],args[2]); + case 4: return obj[method](args[0],args[1],args[2],args[3]); + case 5: return obj[method](args[0],args[1],args[2],args[3],args[4]); + }; + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function(fn) { + this.filters.push(fn) + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function(fakeXhr,xhrArgs) { + var xhr = new sinon.xhr.workingXHR(); + each(["open","setRequestHeader","send","abort","getResponseHeader", + "getAllResponseHeaders","addEventListener","overrideMimeType","removeEventListener"], + function(method) { + fakeXhr[method] = function() { + return apply(xhr,method,arguments); + }; + }); + + var copyAttrs = function(args) { + each(args, function(attr) { + try { + fakeXhr[attr] = xhr[attr] + } catch(e) { + if(!IE6Re.test(navigator.userAgent)) throw e; + } + }); + }; + + var stateChange = function() { + fakeXhr.readyState = xhr.readyState; + if(xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status","statusText"]); + } + if(xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText"]); + } + if(xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if(fakeXhr.onreadystatechange) fakeXhr.onreadystatechange.call(fakeXhr); + }; + if(xhr.addEventListener) { + for(var event in fakeXhr.eventListeners) { + if(fakeXhr.eventListeners.hasOwnProperty(event)) { + each(fakeXhr.eventListeners[event],function(handler) { + xhr.addEventListener(event, handler); + }); + } + } + xhr.addEventListener("readystatechange",stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr,"open",xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestSent(xhr) { + if (xhr.readyState == FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body != "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async == "boolean" ? async : true; + this.username = username; + this.password = password; + this.responseText = null; + this.responseXML = null; + this.requestHeaders = {}; + this.sendFlag = false; + if(sinon.FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters,function(filter) { + return filter.apply(this,xhrArgs) + }); + if (defake) { + return sinon.FakeXMLHttpRequest.defake(this,arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + if (typeof this.onreadystatechange == "function") { + try { + this.onreadystatechange(); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + this.dispatchEvent(new sinon.Event("readystatechange")); + + switch (this.readyState) { + case FakeXMLHttpRequest.DONE: + this.dispatchEvent(new sinon.Event("load", false, false, this)); + this.dispatchEvent(new sinon.Event("loadend", false, false, this)); + break; + } + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + if (this.requestHeaders["Content-Type"]) { + var value = this.requestHeaders["Content-Type"].split(";"); + this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8"; + } else { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend == "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + this.requestHeaders = {}; + + if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = sinon.FakeXMLHttpRequest.UNSENT; + + this.dispatchEvent(new sinon.Event("abort", false, false, this)); + if (typeof this.onerror === "function") { + this.onerror(); + } + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = header.toLowerCase(); + + for (var h in this.responseHeaders) { + if (h.toLowerCase() == header) { + return this.responseHeaders[h]; + } + } + + return null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + } + + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + var type = this.getResponseHeader("Content-Type"); + + if (this.responseText && + (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) { + try { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } catch (e) { + // Unable to parse XML - no biggie + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + } else { + this.readyState = FakeXMLHttpRequest.DONE; + } + }, + + respond: function respond(status, headers, body) { + this.setResponseHeaders(headers || {}); + this.status = typeof status == "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseBody(body || ""); + if (typeof this.onload === "function"){ + this.onload(); + } + + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + // Borrowed from JSpec + FakeXMLHttpRequest.parseXML = function parseXML(text) { + var xmlDoc; + + if (typeof DOMParser != "undefined") { + var parser = new DOMParser(); + xmlDoc = parser.parseFromString(text, "text/xml"); + } else { + xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + } + + return xmlDoc; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + sinon.useFakeXMLHttpRequest = function () { + sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (xhr.supportsXHR) { + global.XMLHttpRequest = xhr.GlobalXMLHttpRequest; + } + + if (xhr.supportsActiveX) { + global.ActiveXObject = xhr.GlobalActiveXObject; + } + + delete sinon.FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXMLHttpRequest.onCreate; + } + }; + if (xhr.supportsXHR) { + global.XMLHttpRequest = sinon.FakeXMLHttpRequest; + } + + if (xhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new sinon.FakeXMLHttpRequest(); + } + + return new xhr.GlobalActiveXObject(objId); + }; + } + + return sinon.FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; +})(this); + +if (typeof module == "object" && typeof require == "function") { + module.exports = sinon; +} + +/** + * @depend fake_xml_http_request.js + */ +/*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/ +/*global module, require, window*/ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +if (typeof sinon == "undefined") { + var sinon = {}; +} + +sinon.fakeServer = (function () { + var push = [].push; + function F() {} + + function create(proto) { + F.prototype = proto; + return new F(); + } + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) != "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] != "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestMethod = this.getHTTPMethod(request); + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response == "function") { + var ru = response.url; + var args = [request].concat(!ru ? [] : requestUrl.match(ru).slice(1)); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + } + + return { + create: function () { + var server = create(this); + this.xhr = sinon.useFakeXMLHttpRequest(); + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + }; + + if (this.autoRespond && !this.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, this.autoRespondAfter || 10); + + this.responding = true; + } + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return !!matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length == 1 && typeof method != "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { this.responses = []; } + + if (arguments.length == 1) { + body = method; + url = method = null; + } + + if (arguments.length == 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body == "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) this.respondWith.apply(this, arguments); + var queue = this.queue || []; + var request; + + while(request = queue.shift()) { + this.processRequest(request); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var i = 0, l = this.responses.length; i < l; i++) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState != 4) { + log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; +}()); + +if (typeof module == "object" && typeof require == "function") { + module.exports = sinon; +} + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/*jslint browser: true, eqeqeq: false, onevar: false*/ +/*global sinon*/ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function () { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock == "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; +}()); + +/** + * @depend ../sinon.js + * @depend collection.js + * @depend util/fake_timers.js + * @depend util/fake_server_with_clock.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global require, module*/ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +if (typeof module == "object" && typeof require == "function") { + var sinon = require("../sinon"); + sinon.extend(sinon, require("./util/fake_timers")); +} + +(function () { + var push = [].push; + + function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto) { + config.injectInto[key] = value; + } else { + push.call(sandbox.args, value); + } + } + + function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer == "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers == "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; + } + + sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + return obj; + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + var prop, value, exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop == "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + } + }); + + sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + + if (typeof module == "object" && typeof require == "function") { + module.exports = sinon.sandbox; + } +}()); + +/** + * @depend ../sinon.js + * @depend stub.js + * @depend mock.js + * @depend sandbox.js + */ +/*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function test(callback) { + var type = typeof callback; + + if (type != "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + return function () { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var exception, result; + var args = Array.prototype.slice.call(arguments).concat(sandbox.args); + + try { + result = callback.apply(this, args); + } catch (e) { + exception = e; + } + + if (typeof exception !== "undefined") { + sandbox.restore(); + throw exception; + } + else { + sandbox.verifyAndRestore(); + } + + return result; + }; + } + + test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + if (commonJSModule) { + module.exports = test; + } else { + sinon.test = test; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend test.js + */ +/*jslint eqeqeq: false, onevar: false, eqeqeq: false*/ +/*global module, require, sinon*/ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon || !Object.prototype.hasOwnProperty) { + return; + } + + function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; + } + + function testCase(tests, prefix) { + /*jsl:ignore*/ + if (!tests || typeof tests != "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + /*jsl:end*/ + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}, testName, property, method; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + + for (testName in tests) { + if (tests.hasOwnProperty(testName)) { + property = tests[testName]; + + if (/^(setUp|tearDown)$/.test(testName)) { + continue; + } + + if (typeof property == "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; + } + + if (commonJSModule) { + module.exports = testCase; + } else { + sinon.testCase = testCase; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend stub.js + */ +/*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Assertions matching the test spy retrieval interface. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ + +(function (sinon, global) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + var slice = Array.prototype.slice; + var assert; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function verifyIsStub() { + var method; + + for (var i = 0, l = arguments.length; i < l; ++i) { + method = arguments[i]; + + if (!method) { + assert.fail("fake is not a spy"); + } + + if (typeof method != "function") { + assert.fail(method + " is not a function"); + } + + if (typeof method.getCall != "function") { + assert.fail(method + " is not stubbed"); + } + } + } + + function failAssertion(object, msg) { + object = object || global; + var failMethod = object.fail || assert.fail; + failMethod.call(object, msg); + } + + function mirrorPropAsAssertion(name, method, message) { + if (arguments.length == 2) { + message = method; + method = name; + } + + assert[name] = function (fake) { + verifyIsStub(fake); + + var args = slice.call(arguments, 1); + var failed = false; + + if (typeof method == "function") { + failed = !method(fake); + } else { + failed = typeof fake[method] == "function" ? + !fake[method].apply(fake, args) : !fake[method]; + } + + if (failed) { + failAssertion(this, fake.printf.apply(fake, [message].concat(args))); + } else { + assert.pass(name); + } + }; + } + + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) ? prop : + prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); + }; + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass(assertion) {}, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = "", actual = ""; + + if (!sinon.calledInOrder(arguments)) { + try { + expected = [].join.call(arguments, ", "); + var calls = slice.call(arguments); + var i = calls.length; + while (i) { + if (!calls[--i].called) { + calls.splice(i, 1); + } + } + actual = sinon.orderByFirstCall(calls).join(", "); + } catch (e) { + // If this fails, we'll just fall back to the blank string + } + + failAssertion(this, "expected " + expected + " to be " + + "called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, + + callCount: function assertCallCount(method, count) { + verifyIsStub(method); + + if (method.callCount != count) { + var msg = "expected %n to be called " + sinon.timesInWords(count) + + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, + + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } + + var o = options || {}; + var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix; + var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail; + + for (var method in this) { + if (method != "export" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = this[method]; + } + } + + return target; + } + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; }, + "expected %n to not have been called but was called %c%C"); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t"); + mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); + mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); + mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + if (commonJSModule) { + module.exports = assert; + } else { + sinon.assert = assert; + } +}(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global)); + +return sinon;}.call(typeof window != 'undefined' && window || {})); diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js new file mode 100644 index 0000000000..1848d08354 --- /dev/null +++ b/core/js/tests/specHelper.js @@ -0,0 +1,105 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ + +/* global OC */ + +/** + * Simulate the variables that are normally set by PHP code + */ + +// from core/js/config.php +window.TESTING = true; +window.oc_debug = true; +window.datepickerFormatDate = 'MM d, yy'; +window.dayNames = [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday' +]; +window.monthNames = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' +]; +window.firstDay = 0; + +// setup dummy webroots +window.oc_webroot = location.href + '/'; +window.oc_appswebroots = { + "files": window.oc_webroot + '/apps/files/' +}; +window.oc_config = { + session_lifetime: 600 * 1000, + session_keepalive: false +}; + +// global setup for all tests +(function setupTests() { + var fakeServer = null, + routesRequestStub; + + beforeEach(function() { + // enforce fake XHR, tests should not depend on the server and + // must use fake responses for expected calls + fakeServer = sinon.fakeServer.create(); + + // return fake translations as they might be requested for many test runs + fakeServer.respondWith(/\/index.php\/core\/ajax\/translations.php$/, [ + 200, { + "Content-Type": "application/json" + }, + '{"data": [], "plural_form": "nplurals=2; plural=(n != 1);"}' + ]); + + // make it globally available, so that other tests can define + // custom responses + window.fakeServer = fakeServer; + + OC.Router.routes = []; + OC.Router.routes_request = { + state: sinon.stub().returns('resolved'), + done: sinon.stub() + }; + }); + + afterEach(function() { + OC.Router.routes_request.state.reset(); + OC.Router.routes_request.done.reset(); + + // uncomment this to log requests + // console.log(window.fakeServer.requests); + fakeServer.restore(); + }); +})(); + diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js new file mode 100644 index 0000000000..478505e928 --- /dev/null +++ b/core/js/tests/specs/coreSpec.js @@ -0,0 +1,280 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ + +/* global OC */ +describe('Core base tests', function() { + describe('Base values', function() { + it('Sets webroots', function() { + expect(OC.webroot).toBeDefined(); + expect(OC.appswebroots).toBeDefined(); + }); + }); + describe('basename', function() { + it('Returns the nothing if no file name given', function() { + expect(OC.basename('')).toEqual(''); + }); + it('Returns the nothing if dir is root', function() { + expect(OC.basename('/')).toEqual(''); + }); + it('Returns the same name if no path given', function() { + expect(OC.basename('some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if root path given', function() { + expect(OC.basename('/some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if double root path given', function() { + expect(OC.basename('//some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if subdir given without root', function() { + expect(OC.basename('subdir/some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if subdir given with root', function() { + expect(OC.basename('/subdir/some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if subdir given with double root', function() { + expect(OC.basename('//subdir/some name.txt')).toEqual('some name.txt'); + }); + it('Returns the base name if subdir has dot', function() { + expect(OC.basename('/subdir.dat/some name.txt')).toEqual('some name.txt'); + }); + it('Returns dot if file name is dot', function() { + expect(OC.basename('/subdir/.')).toEqual('.'); + }); + // TODO: fix the source to make it work like PHP's basename + it('Returns the dir itself if no file name given', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.basename('subdir/')).toEqual('subdir'); + expect(OC.basename('subdir/')).toEqual(''); + }); + it('Returns the dir itself if no file name given with root', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.basename('/subdir/')).toEqual('subdir'); + expect(OC.basename('/subdir/')).toEqual(''); + }); + }); + describe('dirname', function() { + it('Returns the nothing if no file name given', function() { + expect(OC.dirname('')).toEqual(''); + }); + it('Returns the root if dir is root', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('/')).toEqual('/'); + expect(OC.dirname('/')).toEqual(''); + }); + it('Returns the root if dir is double root', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('//')).toEqual('/'); + expect(OC.dirname('//')).toEqual('/'); // oh no... + }); + it('Returns dot if dir is dot', function() { + expect(OC.dirname('.')).toEqual('.'); + }); + it('Returns dot if no root given', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('some dir')).toEqual('.'); + expect(OC.dirname('some dir')).toEqual('some dir'); // oh no... + }); + it('Returns the dir name if file name and root path given', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('/some name.txt')).toEqual('/'); + expect(OC.dirname('/some name.txt')).toEqual(''); + }); + it('Returns the dir name if double root path given', function() { + expect(OC.dirname('//some name.txt')).toEqual('/'); // how lucky... + }); + it('Returns the dir name if subdir given without root', function() { + expect(OC.dirname('subdir/some name.txt')).toEqual('subdir'); + }); + it('Returns the dir name if subdir given with root', function() { + expect(OC.dirname('/subdir/some name.txt')).toEqual('/subdir'); + }); + it('Returns the dir name if subdir given with double root', function() { + // TODO: fix the source to make it work like PHP's dirname + // expect(OC.dirname('//subdir/some name.txt')).toEqual('/subdir'); + expect(OC.dirname('//subdir/some name.txt')).toEqual('//subdir'); // oh... + }); + it('Returns the dir name if subdir has dot', function() { + expect(OC.dirname('/subdir.dat/some name.txt')).toEqual('/subdir.dat'); + }); + it('Returns the dir name if file name is dot', function() { + expect(OC.dirname('/subdir/.')).toEqual('/subdir'); + }); + it('Returns the dir name if no file name given', function() { + expect(OC.dirname('subdir/')).toEqual('subdir'); + }); + it('Returns the dir name if no file name given with root', function() { + expect(OC.dirname('/subdir/')).toEqual('/subdir'); + }); + }); + describe('Link functions', function() { + var TESTAPP = 'testapp'; + var TESTAPP_ROOT = OC.webroot + '/appsx/testapp'; + + beforeEach(function() { + OC.appswebroots[TESTAPP] = TESTAPP_ROOT; + }); + afterEach(function() { + // restore original array + delete OC.appswebroots[TESTAPP]; + }); + it('Generates correct links for core apps', function() { + expect(OC.linkTo('core', 'somefile.php')).toEqual(OC.webroot + '/core/somefile.php'); + expect(OC.linkTo('admin', 'somefile.php')).toEqual(OC.webroot + '/admin/somefile.php'); + }); + it('Generates correct links for regular apps', function() { + expect(OC.linkTo(TESTAPP, 'somefile.php')).toEqual(OC.webroot + '/index.php/apps/' + TESTAPP + '/somefile.php'); + }); + it('Generates correct remote links', function() { + expect(OC.linkToRemote('webdav')).toEqual(window.location.protocol + '//' + window.location.host + OC.webroot + '/remote.php/webdav'); + }); + describe('Images', function() { + it('Generates image path with given extension', function() { + var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return true; }); + expect(OC.imagePath('core', 'somefile.jpg')).toEqual(OC.webroot + '/core/img/somefile.jpg'); + expect(OC.imagePath(TESTAPP, 'somefile.jpg')).toEqual(TESTAPP_ROOT + '/img/somefile.jpg'); + svgSupportStub.restore(); + }); + it('Generates image path with svg extension when svg support exists', function() { + var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return true; }); + expect(OC.imagePath('core', 'somefile')).toEqual(OC.webroot + '/core/img/somefile.svg'); + expect(OC.imagePath(TESTAPP, 'somefile')).toEqual(TESTAPP_ROOT + '/img/somefile.svg'); + svgSupportStub.restore(); + }); + it('Generates image path with png ext when svg support is not available', function() { + var svgSupportStub = sinon.stub(window, 'SVGSupport', function() { return false; }); + expect(OC.imagePath('core', 'somefile')).toEqual(OC.webroot + '/core/img/somefile.png'); + expect(OC.imagePath(TESTAPP, 'somefile')).toEqual(TESTAPP_ROOT + '/img/somefile.png'); + svgSupportStub.restore(); + }); + }); + }); + describe('Query string building', function() { + it('Returns empty string when empty params', function() { + expect(OC.buildQueryString()).toEqual(''); + expect(OC.buildQueryString({})).toEqual(''); + }); + it('Encodes regular query strings', function() { + expect(OC.buildQueryString({ + a: 'abc', + b: 'def' + })).toEqual('a=abc&b=def'); + }); + it('Encodes special characters', function() { + expect(OC.buildQueryString({ + unicode: '汉字', + })).toEqual('unicode=%E6%B1%89%E5%AD%97'); + expect(OC.buildQueryString({ + b: 'spaace value', + 'space key': 'normalvalue', + 'slash/this': 'amp&ersand' + })).toEqual('b=spaace%20value&space%20key=normalvalue&slash%2Fthis=amp%26ersand'); + }); + it('Encodes data types and empty values', function() { + expect(OC.buildQueryString({ + 'keywithemptystring': '', + 'keywithnull': null, + 'keywithundefined': null, + something: 'else' + })).toEqual('keywithemptystring=&keywithnull&keywithundefined&something=else'); + expect(OC.buildQueryString({ + 'booleanfalse': false, + 'booleantrue': true + })).toEqual('booleanfalse=false&booleantrue=true'); + expect(OC.buildQueryString({ + 'number': 123, + })).toEqual('number=123'); + }); + }); + describe('Session heartbeat', function() { + var clock, + oldConfig, + loadedStub, + routeStub, + counter; + + beforeEach(function() { + clock = sinon.useFakeTimers(); + oldConfig = window.oc_config; + loadedStub = sinon.stub(OC.Router, 'registerLoadedCallback'); + routeStub = sinon.stub(OC.Router, 'generate').returns('/heartbeat'); + counter = 0; + + fakeServer.autoRespond = true; + fakeServer.autoRespondAfter = 0; + fakeServer.respondWith(/\/heartbeat/, function(xhr) { + counter++; + xhr.respond(200, {'Content-Type': 'application/json'}, '{}'); + }); + }); + afterEach(function() { + clock.restore(); + window.oc_config = oldConfig; + loadedStub.restore(); + routeStub.restore(); + }); + it('sends heartbeat half the session lifetime when heartbeat enabled', function() { + window.oc_config = { + session_keepalive: true, + session_lifetime: 300 + }; + window.initCore(); + expect(loadedStub.calledOnce).toEqual(true); + loadedStub.yield(); + expect(routeStub.calledWith('heartbeat')).toEqual(true); + + expect(counter).toEqual(0); + + // less than half, still nothing + clock.tick(100 * 1000); + expect(counter).toEqual(0); + + // reach past half (160), one call + clock.tick(55 * 1000); + expect(counter).toEqual(1); + + // almost there to the next, still one + clock.tick(140 * 1000); + expect(counter).toEqual(1); + + // past it, second call + clock.tick(20 * 1000); + expect(counter).toEqual(2); + }); + it('does no send heartbeat when heartbeat disabled', function() { + window.oc_config = { + session_keepalive: false, + session_lifetime: 300 + }; + window.initCore(); + expect(loadedStub.notCalled).toEqual(true); + expect(routeStub.notCalled).toEqual(true); + + expect(counter).toEqual(0); + + clock.tick(1000000); + + // still nothing + expect(counter).toEqual(0); + }); + + }); +}); + diff --git a/core/setup.php b/core/setup.php deleted file mode 100644 index 958376b2cc..0000000000 --- a/core/setup.php +++ /dev/null @@ -1,73 +0,0 @@ - $hasSQLite, - 'hasMySQL' => $hasMySQL, - 'hasPostgreSQL' => $hasPostgreSQL, - 'hasOracle' => $hasOracle, - 'hasMSSQL' => $hasMSSQL, - 'directory' => $datadir, - 'secureRNG' => OC_Util::secureRNGAvailable(), - 'htaccessWorking' => OC_Util::isHtAccessWorking(), - 'vulnerableToNullByte' => $vulnerableToNullByte, - 'errors' => array(), - 'dbIsSet' => $dbIsSet, - 'directoryIsSet' => $directoryIsSet, -); - -if(isset($_POST['install']) AND $_POST['install']=='true') { - // We have to launch the installation process : - $e = OC_Setup::install($_POST); - $errors = array('errors' => $e); - - if(count($e) > 0) { - //OC_Template::printGuestPage("", "error", array("errors" => $errors)); - $options = array_merge($_POST, $opts, $errors); - OC_Template::printGuestPage("", "installation", $options); - } - else { - header( 'Location: '.OC_Helper::linkToRoute( 'post_setup_check' )); - exit(); - } -} -else { - OC_Template::printGuestPage("", "installation", $opts); -} diff --git a/core/setup/controller.php b/core/setup/controller.php new file mode 100644 index 0000000000..697408cfb5 --- /dev/null +++ b/core/setup/controller.php @@ -0,0 +1,139 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Core\Setup; + +class Controller { + public function run($post) { + // Check for autosetup: + $post = $this->loadAutoConfig($post); + $opts = $this->getSystemInfo(); + + if(isset($post['install']) AND $post['install']=='true') { + // We have to launch the installation process : + $e = \OC_Setup::install($post); + $errors = array('errors' => $e); + + if(count($e) > 0) { + $options = array_merge($post, $opts, $errors); + $this->display($options); + } + else { + $this->finishSetup(); + } + } + else { + $this->display($opts); + } + } + + public function display($post) { + $defaults = array( + 'adminlogin' => '', + 'adminpass' => '', + 'dbuser' => '', + 'dbpass' => '', + 'dbname' => '', + 'dbtablespace' => '', + 'dbhost' => '', + ); + $parameters = array_merge($defaults, $post); + + \OC_Util::addScript( '3rdparty', 'strengthify/jquery.strengthify' ); + \OC_Util::addStyle( '3rdparty', 'strengthify/strengthify' ); + \OC_Util::addScript('setup'); + \OC_Template::printGuestPage('', 'installation', $parameters); + } + + public function finishSetup() { + header( 'Location: '.\OC_Helper::linkToRoute( 'post_setup_check' )); + exit(); + } + + public function loadAutoConfig($post) { + $autosetup_file = \OC::$SERVERROOT.'/config/autoconfig.php'; + if( file_exists( $autosetup_file )) { + \OC_Log::write('core', 'Autoconfig file found, setting up owncloud...', \OC_Log::INFO); + $AUTOCONFIG = array(); + include $autosetup_file; + $post = array_merge ($post, $AUTOCONFIG); + } + + $dbIsSet = isset($post['dbtype']); + $directoryIsSet = isset($post['directory']); + $adminAccountIsSet = isset($post['adminlogin']); + + if ($dbIsSet AND $directoryIsSet AND $adminAccountIsSet) { + $post['install'] = 'true'; + if( file_exists( $autosetup_file )) { + unlink($autosetup_file); + } + } + $post['dbIsSet'] = $dbIsSet; + $post['directoryIsSet'] = $directoryIsSet; + + return $post; + } + + public function getSystemInfo() { + $hasSQLite = class_exists('SQLite3'); + $hasMySQL = is_callable('mysql_connect'); + $hasPostgreSQL = is_callable('pg_connect'); + $hasOracle = is_callable('oci_connect'); + $hasMSSQL = is_callable('sqlsrv_connect'); + $databases = array(); + if ($hasSQLite) { + $databases['sqlite'] = 'SQLite'; + } + if ($hasMySQL) { + $databases['mysql'] = 'MySQL/MariaDB'; + } + if ($hasPostgreSQL) { + $databases['pgsql'] = 'PostgreSQL'; + } + if ($hasOracle) { + $databases['oci'] = 'Oracle'; + } + if ($hasMSSQL) { + $databases['mssql'] = 'MS SQL'; + } + $datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT.'/data'); + $vulnerableToNullByte = false; + if(@file_exists(__FILE__."\0Nullbyte")) { // Check if the used PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243) + $vulnerableToNullByte = true; + } + + $errors = array(); + + // Protect data directory here, so we can test if the protection is working + \OC_Setup::protectDataDirectory(); + try { + $htaccessWorking = \OC_Util::isHtAccessWorking(); + } catch (\OC\HintException $e) { + $errors[] = array( + 'error' => $e->getMessage(), + 'hint' => $e->getHint() + ); + $htaccessWorking = false; + } + + return array( + 'hasSQLite' => $hasSQLite, + 'hasMySQL' => $hasMySQL, + 'hasPostgreSQL' => $hasPostgreSQL, + 'hasOracle' => $hasOracle, + 'hasMSSQL' => $hasMSSQL, + 'databases' => $databases, + 'directory' => $datadir, + 'secureRNG' => \OC_Util::secureRNGAvailable(), + 'htaccessWorking' => $htaccessWorking, + 'vulnerableToNullByte' => $vulnerableToNullByte, + 'errors' => $errors, + ); + } +} diff --git a/core/templates/installation.php b/core/templates/installation.php index 182fc83a4d..9670a5e9ee 100644 --- a/core/templates/installation.php +++ b/core/templates/installation.php @@ -48,13 +48,13 @@ t( 'Create an admin account' )); ?>

+ value="" autocomplete="off" autofocus required />

+ value="" required /> @@ -75,7 +75,7 @@ " - value="" /> + value="" />

@@ -86,62 +86,16 @@ $hasOtherDB = true; else $hasOtherDB =false; //other than SQLite ?> t( 'Configure the database' )); ?>
- - - -

SQLite t( 'will be used' )); ?>.

- + $label): ?> + +

t( 'will be used' )); ?>.

+ - /> - - - - - - - -

MySQL t( 'will be used' )); ?>.

- - - /> - - - - - - -

PostgreSQL t( 'will be used' )); ?>.

- - - - /> - - - - - -

Oracle t( 'will be used' )); ?>.

- - - - /> - - - - - - -

MS SQL t( 'will be used' )); ?>.

- - - - /> - + /> + +
@@ -149,11 +103,11 @@

+ value="" autocomplete="off" />

+ value="" /> @@ -161,7 +115,7 @@

@@ -169,14 +123,14 @@

+ value="" autocomplete="off" />

+ value="" />

diff --git a/core/templates/layout.base.php b/core/templates/layout.base.php index 8cd237deea..bae52a7323 100644 --- a/core/templates/layout.base.php +++ b/core/templates/layout.base.php @@ -11,7 +11,7 @@ getTitle()); ?> - + diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php index 47ca5903da..6a96b17b10 100644 --- a/core/templates/layout.guest.php +++ b/core/templates/layout.guest.php @@ -11,7 +11,7 @@ getTitle()); ?> - + diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 89987625d6..bc1c700402 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -48,15 +48,16 @@ <?php p($theme->getName()); ?> - +