diff options
Diffstat (limited to 'apps/encryption')
-rw-r--r-- | apps/encryption/appinfo/application.php | 6 | ||||
-rw-r--r-- | apps/encryption/controller/recoverycontroller.php | 100 | ||||
-rw-r--r-- | apps/encryption/hooks/userhooks.php | 4 | ||||
-rw-r--r-- | apps/encryption/js/settings-admin.js | 65 | ||||
-rw-r--r-- | apps/encryption/js/settings-personal.js | 48 | ||||
-rw-r--r-- | apps/encryption/l10n/es.js | 8 | ||||
-rw-r--r-- | apps/encryption/l10n/es.json | 8 | ||||
-rw-r--r-- | apps/encryption/l10n/sl.js | 2 | ||||
-rw-r--r-- | apps/encryption/l10n/sl.json | 2 | ||||
-rw-r--r-- | apps/encryption/lib/crypto/crypt.php | 11 | ||||
-rw-r--r-- | apps/encryption/lib/crypto/encryption.php | 96 | ||||
-rw-r--r-- | apps/encryption/lib/keymanager.php | 8 | ||||
-rw-r--r-- | apps/encryption/lib/users/setup.php | 1 | ||||
-rw-r--r-- | apps/encryption/tests/controller/RecoveryControllerTest.php | 179 | ||||
-rw-r--r-- | apps/encryption/tests/hooks/UserHooksTest.php | 13 | ||||
-rw-r--r-- | apps/encryption/tests/lib/crypto/cryptTest.php | 2 | ||||
-rw-r--r-- | apps/encryption/tests/lib/crypto/encryptionTest.php | 153 |
17 files changed, 567 insertions, 139 deletions
diff --git a/apps/encryption/appinfo/application.php b/apps/encryption/appinfo/application.php index fa620992c81..0d6f57f46e9 100644 --- a/apps/encryption/appinfo/application.php +++ b/apps/encryption/appinfo/application.php @@ -94,16 +94,20 @@ class Application extends \OCP\AppFramework\App { public function registerEncryptionModule() { $container = $this->getContainer(); + $this->encryptionManager->registerEncryptionModule( Encryption::ID, Encryption::DISPLAY_NAME, function() use ($container) { + return new Encryption( $container->query('Crypt'), $container->query('KeyManager'), - $container->query('Util') + $container->query('Util'), + $container->getServer()->getLogger() ); }); + } public function registerServices() { diff --git a/apps/encryption/controller/recoverycontroller.php b/apps/encryption/controller/recoverycontroller.php index 9c07bda62e4..f1a2651443e 100644 --- a/apps/encryption/controller/recoverycontroller.php +++ b/apps/encryption/controller/recoverycontroller.php @@ -26,6 +26,7 @@ namespace OCA\Encryption\Controller; use OCA\Encryption\Recovery; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; @@ -72,31 +73,36 @@ class RecoveryController extends Controller { public function adminRecovery($recoveryPassword, $confirmPassword, $adminEnableRecovery) { // Check if both passwords are the same if (empty($recoveryPassword)) { - $errorMessage = (string) $this->l->t('Missing recovery key password'); - return new DataResponse(['data' => ['message' => $errorMessage]], 500); + $errorMessage = (string)$this->l->t('Missing recovery key password'); + return new DataResponse(['data' => ['message' => $errorMessage]], + Http::STATUS_BAD_REQUEST); } if (empty($confirmPassword)) { - $errorMessage = (string) $this->l->t('Please repeat the recovery key password'); - return new DataResponse(['data' => ['message' => $errorMessage]], 500); + $errorMessage = (string)$this->l->t('Please repeat the recovery key password'); + return new DataResponse(['data' => ['message' => $errorMessage]], + Http::STATUS_BAD_REQUEST); } if ($recoveryPassword !== $confirmPassword) { - $errorMessage = (string) $this->l->t('Repeated recovery key password does not match the provided recovery key password'); - return new DataResponse(['data' => ['message' => $errorMessage]], 500); + $errorMessage = (string)$this->l->t('Repeated recovery key password does not match the provided recovery key password'); + return new DataResponse(['data' => ['message' => $errorMessage]], + Http::STATUS_BAD_REQUEST); } if (isset($adminEnableRecovery) && $adminEnableRecovery === '1') { if ($this->recovery->enableAdminRecovery($recoveryPassword)) { - return new DataResponse(['status' =>'success', 'data' => array('message' => (string) $this->l->t('Recovery key successfully enabled'))]); + return new DataResponse(['data' => ['message' => (string)$this->l->t('Recovery key successfully enabled')]]); } - return new DataResponse(['data' => array('message' => (string) $this->l->t('Could not enable recovery key. Please check your recovery key password!'))]); + return new DataResponse(['data' => ['message' => (string)$this->l->t('Could not enable recovery key. Please check your recovery key password!')]], Http::STATUS_BAD_REQUEST); } elseif (isset($adminEnableRecovery) && $adminEnableRecovery === '0') { if ($this->recovery->disableAdminRecovery($recoveryPassword)) { - return new DataResponse(['data' => array('message' => (string) $this->l->t('Recovery key successfully disabled'))]); + return new DataResponse(['data' => ['message' => (string)$this->l->t('Recovery key successfully disabled')]]); } - return new DataResponse(['data' => array('message' => (string) $this->l->t('Could not disable recovery key. Please check your recovery key password!'))]); + return new DataResponse(['data' => ['message' => (string)$this->l->t('Could not disable recovery key. Please check your recovery key password!')]], Http::STATUS_BAD_REQUEST); } + // this response should never be sent but just in case. + return new DataResponse(['data' => ['message' => (string)$this->l->t('Missing parameters')]], Http::STATUS_BAD_REQUEST); } /** @@ -108,43 +114,42 @@ class RecoveryController extends Controller { public function changeRecoveryPassword($newPassword, $oldPassword, $confirmPassword) { //check if both passwords are the same if (empty($oldPassword)) { - $errorMessage = (string) $this->l->t('Please provide the old recovery password'); - return new DataResponse(array('data' => array('message' => $errorMessage))); + $errorMessage = (string)$this->l->t('Please provide the old recovery password'); + return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); } if (empty($newPassword)) { - $errorMessage = (string) $this->l->t('Please provide a new recovery password'); - return new DataResponse (array('data' => array('message' => $errorMessage))); + $errorMessage = (string)$this->l->t('Please provide a new recovery password'); + return new DataResponse (['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); } if (empty($confirmPassword)) { - $errorMessage = (string) $this->l->t('Please repeat the new recovery password'); - return new DataResponse(array('data' => array('message' => $errorMessage))); + $errorMessage = (string)$this->l->t('Please repeat the new recovery password'); + return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); } if ($newPassword !== $confirmPassword) { - $errorMessage = (string) $this->l->t('Repeated recovery key password does not match the provided recovery key password'); - return new DataResponse(array('data' => array('message' => $errorMessage))); + $errorMessage = (string)$this->l->t('Repeated recovery key password does not match the provided recovery key password'); + return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); } - $result = $this->recovery->changeRecoveryKeyPassword($newPassword, $oldPassword); + $result = $this->recovery->changeRecoveryKeyPassword($newPassword, + $oldPassword); if ($result) { return new DataResponse( - array( - 'status' => 'success' , - 'data' => array( - 'message' => (string) $this->l->t('Password successfully changed.')) - ) - ); - } else { - return new DataResponse( - array( - 'data' => array - ('message' => (string) $this->l->t('Could not change the password. Maybe the old password was not correct.')) - ) - ); + [ + 'data' => [ + 'message' => (string)$this->l->t('Password successfully changed.')] + ] + ); } + return new DataResponse( + [ + 'data' => [ + 'message' => (string)$this->l->t('Could not change the password. Maybe the old password was not correct.') + ] + ], Http::STATUS_BAD_REQUEST); } /** @@ -159,22 +164,29 @@ class RecoveryController extends Controller { $result = $this->recovery->setRecoveryForUser($userEnableRecovery); if ($result) { + if ($userEnableRecovery === '0') { + return new DataResponse( + [ + 'data' => [ + 'message' => (string)$this->l->t('Recovery Key disabled')] + ] + ); + } return new DataResponse( - array( - 'status' => 'success', - 'data' => array( - 'message' => (string) $this->l->t('Recovery Key enabled')) - ) - ); - } else { - return new DataResponse( - array( - 'data' => array - ('message' => (string) $this->l->t('Could not enable the recovery key, please try again or contact your administrator')) - ) + [ + 'data' => [ + 'message' => (string)$this->l->t('Recovery Key enabled')] + ] ); } + } + return new DataResponse( + [ + 'data' => [ + 'message' => (string)$this->l->t('Could not enable the recovery key, please try again or contact your administrator') + ] + ], Http::STATUS_BAD_REQUEST); } } diff --git a/apps/encryption/hooks/userhooks.php b/apps/encryption/hooks/userhooks.php index b09b8e7049a..a86b8662781 100644 --- a/apps/encryption/hooks/userhooks.php +++ b/apps/encryption/hooks/userhooks.php @@ -226,7 +226,7 @@ class UserHooks implements IHook { // Save private key if ($encryptedPrivateKey) { $this->keyManager->setPrivateKey($this->user->getUser()->getUID(), - $encryptedPrivateKey); + $this->crypt->generateHeader() . $encryptedPrivateKey); } else { $this->logger->error('Encryption could not update users encryption password'); } @@ -263,7 +263,7 @@ class UserHooks implements IHook { $newUserPassword); if ($encryptedKey) { - $this->keyManager->setPrivateKey($user, $encryptedKey); + $this->keyManager->setPrivateKey($user, $this->crypt->generateHeader() . $encryptedKey); if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files $this->recovery->recoverUsersFiles($recoveryPassword, $user); diff --git a/apps/encryption/js/settings-admin.js b/apps/encryption/js/settings-admin.js index bb539f6a4e2..fdc53c52152 100644 --- a/apps/encryption/js/settings-admin.js +++ b/apps/encryption/js/settings-admin.js @@ -7,52 +7,59 @@ * See the COPYING-README file. */ -$(document).ready(function(){ +$(document).ready(function () { - $( 'input:radio[name="adminEnableRecovery"]' ).change( - function() { - var recoveryStatus = $( this ).val(); - var oldStatus = (1+parseInt(recoveryStatus, 10)) % 2; - var recoveryPassword = $( '#encryptionRecoveryPassword' ).val(); - var confirmPassword = $( '#repeatEncryptionRecoveryPassword' ).val(); + $('input:radio[name="adminEnableRecovery"]').change( + function () { + var recoveryStatus = $(this).val(); + var oldStatus = (1 + parseInt(recoveryStatus)) % 2; + var recoveryPassword = $('#encryptionRecoveryPassword').val(); + var confirmPassword = $('#repeatEncryptionRecoveryPassword').val(); OC.msg.startSaving('#encryptionSetRecoveryKey .msg'); $.post( OC.generateUrl('/apps/encryption/ajax/adminRecovery'), - { adminEnableRecovery: recoveryStatus, + { + adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword, - confirmPassword: confirmPassword }, - function( result ) { - OC.msg.finishedSaving('#encryptionSetRecoveryKey .msg', result); - if (result.status === "error") { - $('input:radio[name="adminEnableRecovery"][value="'+oldStatus.toString()+'"]') - .attr("checked", "true"); + confirmPassword: confirmPassword + } + ).done(function (data) { + OC.msg.finishedSuccess('#encryptionSetRecoveryKey .msg', data.data.message); + + if (recoveryStatus === "0") { + $('p[name="changeRecoveryPasswordBlock"]').addClass("hidden"); } else { - if (recoveryStatus === "0") { - $('p[name="changeRecoveryPasswordBlock"]').addClass("hidden"); - } else { - $('input:password[name="changeRecoveryPassword"]').val(""); - $('p[name="changeRecoveryPasswordBlock"]').removeClass("hidden"); - } + $('input:password[name="changeRecoveryPassword"]').val(""); + $('p[name="changeRecoveryPasswordBlock"]').removeClass("hidden"); } - } - ); + }) + .fail(function (jqXHR) { + $('input:radio[name="adminEnableRecovery"][value="' + oldStatus.toString() + '"]').attr("checked", "true"); + OC.msg.finishedError('#encryptionSetRecoveryKey .msg', JSON.parse(jqXHR.responseText).data.message); + }); } ); // change recovery password - $('button:button[name="submitChangeRecoveryKey"]').click(function() { + $('button:button[name="submitChangeRecoveryKey"]').click(function () { var oldRecoveryPassword = $('#oldEncryptionRecoveryPassword').val(); var newRecoveryPassword = $('#newEncryptionRecoveryPassword').val(); var confirmNewPassword = $('#repeatedNewEncryptionRecoveryPassword').val(); OC.msg.startSaving('#encryptionChangeRecoveryKey .msg'); $.post( - OC.generateUrl('/apps/encryption/ajax/changeRecoveryPassword'), - { oldPassword: oldRecoveryPassword, newPassword: newRecoveryPassword, confirmPassword: confirmNewPassword }, - function( data ) { - OC.msg.finishedSaving('#encryptionChangeRecoveryKey .msg', data); - } - ); + OC.generateUrl('/apps/encryption/ajax/changeRecoveryPassword'), + { + oldPassword: oldRecoveryPassword, + newPassword: newRecoveryPassword, + confirmPassword: confirmNewPassword + } + ).done(function (data) { + OC.msg.finishedSuccess('#encryptionChangeRecoveryKey .msg', data.data.message); + }) + .fail(function (jqXHR) { + OC.msg.finishedError('#encryptionChangeRecoveryKey .msg', JSON.parse(jqXHR.responseText).data.message); + }); }); }); diff --git a/apps/encryption/js/settings-personal.js b/apps/encryption/js/settings-personal.js index e36f10a244e..658ba2a86ec 100644 --- a/apps/encryption/js/settings-personal.js +++ b/apps/encryption/js/settings-personal.js @@ -9,35 +9,43 @@ if (!OC.Encryption) { } OC.Encryption = { - updatePrivateKeyPassword: function() { + updatePrivateKeyPassword: function () { var oldPrivateKeyPassword = $('input:password[id="oldPrivateKeyPassword"]').val(); var newPrivateKeyPassword = $('input:password[id="newPrivateKeyPassword"]').val(); OC.msg.startSaving('#encryption .msg'); $.post( OC.generateUrl('/apps/encryption/ajax/updatePrivateKeyPassword'), - {oldPassword: oldPrivateKeyPassword, newPassword: newPrivateKeyPassword} - ).success(function (response) { - OC.msg.finishedSuccess('#encryption .msg', response.message); - }).fail(function (response) { - OC.msg.finishedError('#encryption .msg', response.responseJSON.message); - }); + { + oldPassword: oldPrivateKeyPassword, + newPassword: newPrivateKeyPassword + } + ).done(function (data) { + OC.msg.finishedSuccess('#encryption .msg', data.message); + }) + .fail(function (jqXHR) { + OC.msg.finishedError('#encryption .msg', JSON.parse(jqXHR.responseText).message); + }); } }; -$(document).ready(function(){ +$(document).ready(function () { // Trigger ajax on recoveryAdmin status change - $( 'input:radio[name="userEnableRecovery"]' ).change( - function() { - var recoveryStatus = $( this ).val(); + $('input:radio[name="userEnableRecovery"]').change( + function () { + var recoveryStatus = $(this).val(); OC.msg.startAction('#userEnableRecovery .msg', 'Updating recovery keys. This can take some time...'); $.post( - OC.generateUrl('/apps/encryption/ajax/userSetRecovery'), - { userEnableRecovery: recoveryStatus }, - function( data ) { - OC.msg.finishedAction('#userEnableRecovery .msg', data); + OC.generateUrl('/apps/encryption/ajax/userSetRecovery'), + { + userEnableRecovery: recoveryStatus } - ); + ).done(function (data) { + OC.msg.finishedSuccess('#userEnableRecovery .msg', data.data.message); + }) + .fail(function (jqXHR) { + OC.msg.finishedError('#userEnableRecovery .msg', JSON.parse(jqXHR.responseText).data.message); + }); // Ensure page is not reloaded on form submit return false; } @@ -45,12 +53,12 @@ $(document).ready(function(){ // update private key password - $('input:password[name="changePrivateKeyPassword"]').keyup(function(event) { + $('input:password[name="changePrivateKeyPassword"]').keyup(function (event) { var oldPrivateKeyPassword = $('input:password[id="oldPrivateKeyPassword"]').val(); var newPrivateKeyPassword = $('input:password[id="newPrivateKeyPassword"]').val(); - if (newPrivateKeyPassword !== '' && oldPrivateKeyPassword !== '' ) { + if (newPrivateKeyPassword !== '' && oldPrivateKeyPassword !== '') { $('button:button[name="submitChangePrivateKeyPassword"]').removeAttr("disabled"); - if(event.which === 13) { + if (event.which === 13) { OC.Encryption.updatePrivateKeyPassword(); } } else { @@ -58,7 +66,7 @@ $(document).ready(function(){ } }); - $('button:button[name="submitChangePrivateKeyPassword"]').click(function() { + $('button:button[name="submitChangePrivateKeyPassword"]').click(function () { OC.Encryption.updatePrivateKeyPassword(); }); diff --git a/apps/encryption/l10n/es.js b/apps/encryption/l10n/es.js index 6a682d1a209..a1c86efad2f 100644 --- a/apps/encryption/l10n/es.js +++ b/apps/encryption/l10n/es.js @@ -5,19 +5,19 @@ OC.L10N.register( "Please repeat the recovery key password" : "Por favor, repita la contraseña de recuperación", "Repeated recovery key password does not match the provided recovery key password" : "La contraseña de recuperación reintroducida no coincide con la contraseña de recuperación proporcionada", "Recovery key successfully enabled" : "Se ha habilitado la recuperación de archivos", - "Could not enable recovery key. Please check your recovery key password!" : "No se pudo habilitar la contraseña de recuperación. Por favor compruebe su contraseña de recuperación!", + "Could not enable recovery key. Please check your recovery key password!" : "No se pudo habilitar la contraseña de recuperación. Por favor, ¡compruebe su contraseña de recuperación!", "Recovery key successfully disabled" : "Clave de recuperación deshabilitada", "Could not disable recovery key. Please check your recovery key password!" : "No se pudo deshabilitar la clave de recuperación. Por favor, ¡compruebe su contraseña!", "Please provide the old recovery password" : "Por favor, ingrese su antigua contraseña de recuperación", - "Please provide a new recovery password" : "Por favor, ingrese una nueva contraseña de recuperación", + "Please provide a new recovery password" : "Por favor, provea una nueva contraseña de recuperación", "Please repeat the new recovery password" : "Por favor, repita su nueva contraseña de recuperación", "Password successfully changed." : "Su contraseña ha sido cambiada", "Could not change the password. Maybe the old password was not correct." : "No se pudo cambiar la contraseña. Compruebe que la contraseña actual sea correcta.", "Recovery Key enabled" : "Recuperación de clave habilitada", "Could not enable the recovery key, please try again or contact your administrator" : "No se pudo habilitar la clave de recuperación, por favor vuelva a intentarlo o póngase en contacto con el administrador", "Could not update the private key password." : "No se pudo actualizar la contraseña de la clave privada.", - "The old password was not correct, please try again." : "La antigua contraseña no es correcta, por favor intente de nuevo.", - "The current log-in password was not correct, please try again." : "La contraseña de inicio de sesión actual no es correcto, por favor intente de nuevo.", + "The old password was not correct, please try again." : "La antigua contraseña no es correcta, por favor inténtelo de nuevo.", + "The current log-in password was not correct, please try again." : "La contraseña de inicio de sesión actual no es correcta, por favor inténtelo de nuevo.", "Private key password successfully updated." : "Contraseña de clave privada actualizada con éxito.", "Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." : "La clave privada no es válida para la app de cifrado. Por favor, actualiza la contraseña de tu clave privada en tus ajustes personales para recuperar el acceso a tus archivos cifrados.", "Encryption App is enabled but your keys are not initialized, please log-out and log-in again" : "La app de cifrado está habilitada pero sus claves no se han inicializado, por favor, cierre la sesión y vuelva a iniciarla de nuevo.", diff --git a/apps/encryption/l10n/es.json b/apps/encryption/l10n/es.json index 9aaedac1f10..172205a7a1f 100644 --- a/apps/encryption/l10n/es.json +++ b/apps/encryption/l10n/es.json @@ -3,19 +3,19 @@ "Please repeat the recovery key password" : "Por favor, repita la contraseña de recuperación", "Repeated recovery key password does not match the provided recovery key password" : "La contraseña de recuperación reintroducida no coincide con la contraseña de recuperación proporcionada", "Recovery key successfully enabled" : "Se ha habilitado la recuperación de archivos", - "Could not enable recovery key. Please check your recovery key password!" : "No se pudo habilitar la contraseña de recuperación. Por favor compruebe su contraseña de recuperación!", + "Could not enable recovery key. Please check your recovery key password!" : "No se pudo habilitar la contraseña de recuperación. Por favor, ¡compruebe su contraseña de recuperación!", "Recovery key successfully disabled" : "Clave de recuperación deshabilitada", "Could not disable recovery key. Please check your recovery key password!" : "No se pudo deshabilitar la clave de recuperación. Por favor, ¡compruebe su contraseña!", "Please provide the old recovery password" : "Por favor, ingrese su antigua contraseña de recuperación", - "Please provide a new recovery password" : "Por favor, ingrese una nueva contraseña de recuperación", + "Please provide a new recovery password" : "Por favor, provea una nueva contraseña de recuperación", "Please repeat the new recovery password" : "Por favor, repita su nueva contraseña de recuperación", "Password successfully changed." : "Su contraseña ha sido cambiada", "Could not change the password. Maybe the old password was not correct." : "No se pudo cambiar la contraseña. Compruebe que la contraseña actual sea correcta.", "Recovery Key enabled" : "Recuperación de clave habilitada", "Could not enable the recovery key, please try again or contact your administrator" : "No se pudo habilitar la clave de recuperación, por favor vuelva a intentarlo o póngase en contacto con el administrador", "Could not update the private key password." : "No se pudo actualizar la contraseña de la clave privada.", - "The old password was not correct, please try again." : "La antigua contraseña no es correcta, por favor intente de nuevo.", - "The current log-in password was not correct, please try again." : "La contraseña de inicio de sesión actual no es correcto, por favor intente de nuevo.", + "The old password was not correct, please try again." : "La antigua contraseña no es correcta, por favor inténtelo de nuevo.", + "The current log-in password was not correct, please try again." : "La contraseña de inicio de sesión actual no es correcta, por favor inténtelo de nuevo.", "Private key password successfully updated." : "Contraseña de clave privada actualizada con éxito.", "Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." : "La clave privada no es válida para la app de cifrado. Por favor, actualiza la contraseña de tu clave privada en tus ajustes personales para recuperar el acceso a tus archivos cifrados.", "Encryption App is enabled but your keys are not initialized, please log-out and log-in again" : "La app de cifrado está habilitada pero sus claves no se han inicializado, por favor, cierre la sesión y vuelva a iniciarla de nuevo.", diff --git a/apps/encryption/l10n/sl.js b/apps/encryption/l10n/sl.js index e10a9b302da..a5e1818a0cb 100644 --- a/apps/encryption/l10n/sl.js +++ b/apps/encryption/l10n/sl.js @@ -14,7 +14,7 @@ OC.L10N.register( "Password successfully changed." : "Geslo je uspešno spremenjeno.", "Could not change the password. Maybe the old password was not correct." : "Gesla ni mogoče spremeniti. Morda vnos starega gesla ni pravilen.", "Could not update the private key password." : "Ni mogoče posodobiti gesla zasebnega ključa.", - "The old password was not correct, please try again." : "Staro geslo ni vpisana pravilno. Poskusite znova.", + "The old password was not correct, please try again." : "Staro geslo ni vpisano pravilno. Poskusite znova.", "The current log-in password was not correct, please try again." : "Trenutno geslo za prijavo ni vpisano pravilno. Poskusite znova.", "Private key password successfully updated." : "Zasebni ključ za geslo je uspešno posodobljen.", "Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." : "Ni ustreznega osebnega ključa za program za šifriranje. Posodobite osebni ključ za dostop do šifriranih datotek med nastavitvami.", diff --git a/apps/encryption/l10n/sl.json b/apps/encryption/l10n/sl.json index 55a40653bd7..184e932da9d 100644 --- a/apps/encryption/l10n/sl.json +++ b/apps/encryption/l10n/sl.json @@ -12,7 +12,7 @@ "Password successfully changed." : "Geslo je uspešno spremenjeno.", "Could not change the password. Maybe the old password was not correct." : "Gesla ni mogoče spremeniti. Morda vnos starega gesla ni pravilen.", "Could not update the private key password." : "Ni mogoče posodobiti gesla zasebnega ključa.", - "The old password was not correct, please try again." : "Staro geslo ni vpisana pravilno. Poskusite znova.", + "The old password was not correct, please try again." : "Staro geslo ni vpisano pravilno. Poskusite znova.", "The current log-in password was not correct, please try again." : "Trenutno geslo za prijavo ni vpisano pravilno. Poskusite znova.", "Private key password successfully updated." : "Zasebni ključ za geslo je uspešno posodobljen.", "Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." : "Ni ustreznega osebnega ključa za program za šifriranje. Posodobite osebni ključ za dostop do šifriranih datotek med nastavitvami.", diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php index 9ada9200551..65af3a93d0a 100644 --- a/apps/encryption/lib/crypto/crypt.php +++ b/apps/encryption/lib/crypto/crypt.php @@ -210,6 +210,15 @@ class Crypt { } /** + * get legacy cipher + * + * @return string + */ + public function getLegacyCipher() { + return self::LEGACY_CIPHER; + } + + /** * @param string $encryptedContent * @param string $iv * @return string @@ -401,7 +410,7 @@ class Crypt { * @return string * @throws \Exception */ - public static function generateFileKey() { + public function generateFileKey() { // Generate key $key = base64_encode(openssl_random_pseudo_bytes(32, $strong)); if (!$key || !$strong) { diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php index 8498b4223e1..29fda09e87f 100644 --- a/apps/encryption/lib/crypto/encryption.php +++ b/apps/encryption/lib/crypto/encryption.php @@ -25,9 +25,11 @@ namespace OCA\Encryption\Crypto; +use OCA\Encryption\Exceptions\PublicKeyMissingException; use OCA\Encryption\Util; use OCP\Encryption\IEncryptionModule; use OCA\Encryption\KeyManager; +use OCP\ILogger; class Encryption implements IEncryptionModule { @@ -66,16 +68,25 @@ class Encryption implements IEncryptionModule { /** @var Util */ private $util; + + /** @var ILogger */ + private $logger; + /** * - * @param \OCA\Encryption\Crypto\Crypt $crypt + * @param Crypt $crypt * @param KeyManager $keyManager * @param Util $util + * @param ILogger $logger */ - public function __construct(Crypt $crypt, KeyManager $keyManager, Util $util) { + public function __construct(Crypt $crypt, + KeyManager $keyManager, + Util $util, + ILogger $logger) { $this->crypt = $crypt; $this->keyManager = $keyManager; $this->util = $util; + $this->logger = $logger; } /** @@ -101,6 +112,7 @@ class Encryption implements IEncryptionModule { * * @param string $path to the file * @param string $user who read/write the file + * @param string $mode php stream open mode * @param array $header contains the header data read from the file * @param array $accessList who has access to the file contains the key 'users' and 'public' * @@ -108,22 +120,38 @@ class Encryption implements IEncryptionModule { * written to the header, in case of a write operation * or if no additional data is needed return a empty array */ - public function begin($path, $user, array $header, array $accessList) { - - if (isset($header['cipher'])) { - $this->cipher = $header['cipher']; - } else { - $this->cipher = $this->crypt->getCipher(); - } + public function begin($path, $user, $mode, array $header, array $accessList) { $this->path = $this->getPathToRealFile($path); $this->accessList = $accessList; $this->user = $user; - $this->writeCache = ''; $this->isWriteOperation = false; + $this->writeCache = ''; $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user); + if ( + $mode === 'w' + || $mode === 'w+' + || $mode === 'wb' + || $mode === 'wb+' + ) { + $this->isWriteOperation = true; + if (empty($this->fileKey)) { + $this->fileKey = $this->crypt->generateFileKey(); + } + } + + if (isset($header['cipher'])) { + $this->cipher = $header['cipher']; + } elseif ($this->isWriteOperation) { + $this->cipher = $this->crypt->getCipher(); + } else { + // if we read a file without a header we fall-back to the legacy cipher + // which was used in <=oC6 + $this->cipher = $this->crypt->getLegacyCipher(); + } + return array('cipher' => $this->cipher); } @@ -135,6 +163,9 @@ class Encryption implements IEncryptionModule { * @param string $path to the file * @return string remained data which should be written to the file in case * of a write operation + * @throws PublicKeyMissingException + * @throws \Exception + * @throws \OCA\Encryption\Exceptions\MultiKeyEncryptException */ public function end($path) { $result = ''; @@ -145,7 +176,18 @@ class Encryption implements IEncryptionModule { } $publicKeys = array(); foreach ($this->accessList['users'] as $uid) { - $publicKeys[$uid] = $this->keyManager->getPublicKey($uid); + try { + $publicKeys[$uid] = $this->keyManager->getPublicKey($uid); + } catch (PublicKeyMissingException $e) { + $this->logger->warning( + 'no public key found for user "{uid}", user will not be able to read the file', + ['app' => 'encryption', 'uid' => $uid] + ); + // if the public key of the owner is missing we should fail + if ($uid === $this->user) { + throw $e; + } + } } $publicKeys = $this->keyManager->addSystemKeys($this->accessList, $publicKeys); @@ -163,10 +205,6 @@ class Encryption implements IEncryptionModule { * @return mixed encrypted data */ public function encrypt($data) { - $this->isWriteOperation = true; - if (empty($this->fileKey)) { - $this->fileKey = $this->crypt->generateFileKey(); - } // If extra data is left over from the last round, make sure it // is integrated into the next 6126 / 8192 block @@ -234,7 +272,7 @@ class Encryption implements IEncryptionModule { public function decrypt($data) { $result = ''; if (!empty($data)) { - $result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey); + $result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher); } return $result; } @@ -249,18 +287,28 @@ class Encryption implements IEncryptionModule { */ public function update($path, $uid, array $accessList) { $fileKey = $this->keyManager->getFileKey($path, $uid); - $publicKeys = array(); - foreach ($accessList['users'] as $user) { - $publicKeys[$user] = $this->keyManager->getPublicKey($user); - } - $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys); + if (!empty($fileKey)) { - $encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); + $publicKeys = array(); + foreach ($accessList['users'] as $user) { + $publicKeys[$user] = $this->keyManager->getPublicKey($user); + } - $this->keyManager->deleteAllFileKeys($path); + $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys); - $this->keyManager->setAllFileKeys($path, $encryptedFileKey); + $encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys); + + $this->keyManager->deleteAllFileKeys($path); + + $this->keyManager->setAllFileKeys($path, $encryptedFileKey); + + } else { + $this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted', + array('file' => $path, 'app' => 'encryption')); + + return false; + } return true; } diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php index 1e6f3d29be8..bde8212e9dc 100644 --- a/apps/encryption/lib/keymanager.php +++ b/apps/encryption/lib/keymanager.php @@ -131,6 +131,11 @@ class KeyManager { $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId); } + $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false; + $this->log = $log; + } + + public function validateShareKey() { $shareKey = $this->getPublicShareKey(); if (empty($shareKey)) { $keyPair = $this->crypt->createKeyPair(); @@ -145,9 +150,6 @@ class KeyManager { $header = $this->crypt->generateHeader(); $this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey); } - - $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false; - $this->log = $log; } /** diff --git a/apps/encryption/lib/users/setup.php b/apps/encryption/lib/users/setup.php index 2ec49b5c7fb..fd8261cc637 100644 --- a/apps/encryption/lib/users/setup.php +++ b/apps/encryption/lib/users/setup.php @@ -79,6 +79,7 @@ class Setup { * @return bool */ public function setupServerSide($uid, $password) { + $this->keyManager->validateShareKey(); // Check if user already has keys if (!$this->keyManager->userHasKeys($uid)) { return $this->keyManager->storeKeyPair($uid, $password, diff --git a/apps/encryption/tests/controller/RecoveryControllerTest.php b/apps/encryption/tests/controller/RecoveryControllerTest.php new file mode 100644 index 00000000000..89b541e7bd6 --- /dev/null +++ b/apps/encryption/tests/controller/RecoveryControllerTest.php @@ -0,0 +1,179 @@ +<?php +/** + * @author Clark Tomlinson <clark@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + */ + + +namespace OCA\Encryption\Tests\Controller; + + +use OCA\Encryption\Controller\RecoveryController; +use OCP\AppFramework\Http; +use Test\TestCase; + +class RecoveryControllerTest extends TestCase { + /** + * @var RecoveryController + */ + private $controller; + private $appName; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $l10nMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $recoveryMock; + + public function adminRecoveryProvider() { + return [ + ['test', 'test', '1', 'Recovery key successfully enabled', HTTP::STATUS_OK], + ['', 'test', '1', 'Missing recovery key password', HTTP::STATUS_BAD_REQUEST], + ['test', '', '1', 'Please repeat the recovery key password', HTTP::STATUS_BAD_REQUEST], + ['test', 'soimething that doesn\'t match', '1', 'Repeated recovery key password does not match the provided recovery key password', HTTP::STATUS_BAD_REQUEST], + ['test', 'test', '0', 'Recovery key successfully disabled', HTTP::STATUS_OK], + ]; + } + + /** + * @dataProvider adminRecoveryProvider + * @param $recoveryPassword + * @param $passconfirm + * @param $enableRecovery + * @param $expectedMessage + * @param $expectedStatus + */ + public function testAdminRecovery($recoveryPassword, $passconfirm, $enableRecovery, $expectedMessage, $expectedStatus) { + + + $this->recoveryMock->expects($this->any()) + ->method('enableAdminRecovery') + ->willReturn(true); + + $this->recoveryMock->expects($this->any()) + ->method('disableAdminRecovery') + ->willReturn(true); + + $response = $this->controller->adminRecovery($recoveryPassword, + $passconfirm, + $enableRecovery); + + + $this->assertEquals($expectedMessage, $response->getData()['data']['message']); + $this->assertEquals($expectedStatus, $response->getStatus()); + + + } + + public function changeRecoveryPasswordProvider() { + return [ + ['test', 'test', 'oldtestFail', 'Could not change the password. Maybe the old password was not correct.', HTTP::STATUS_BAD_REQUEST], + ['test', 'test', 'oldtest', 'Password successfully changed.', HTTP::STATUS_OK], + ['test', 'notmatch', 'oldtest', 'Repeated recovery key password does not match the provided recovery key password', HTTP::STATUS_BAD_REQUEST], + ['', 'test', 'oldtest', 'Please provide a new recovery password', HTTP::STATUS_BAD_REQUEST], + ['test', 'test', '', 'Please provide the old recovery password', HTTP::STATUS_BAD_REQUEST] + ]; + } + + /** + * @dataProvider changeRecoveryPasswordProvider + * @param $password + * @param $confirmPassword + * @param $oldPassword + * @param $expectedMessage + * @param $expectedStatus + */ + public function testChangeRecoveryPassword($password, $confirmPassword, $oldPassword, $expectedMessage, $expectedStatus) { + $this->recoveryMock->expects($this->any()) + ->method('changeRecoveryKeyPassword') + ->with($password, $oldPassword) + ->will($this->returnValueMap([ + ['test', 'oldTestFail', false], + ['test', 'oldtest', true] + ])); + + $response = $this->controller->changeRecoveryPassword($password, + $oldPassword, + $confirmPassword); + + $this->assertEquals($expectedMessage, $response->getData()['data']['message']); + $this->assertEquals($expectedStatus, $response->getStatus()); + + + } + + public function userSetRecoveryProvider() { + return [ + ['1', 'Recovery Key enabled', Http::STATUS_OK], + ['0', 'Could not enable the recovery key, please try again or contact your administrator', Http::STATUS_BAD_REQUEST] + ]; + } + + /** + * @dataProvider userSetRecoveryProvider + * @param $enableRecovery + * @param $expectedMessage + * @param $expectedStatus + */ + public function testUserSetRecovery($enableRecovery, $expectedMessage, $expectedStatus) { + $this->recoveryMock->expects($this->any()) + ->method('setRecoveryForUser') + ->with($enableRecovery) + ->will($this->returnValueMap([ + ['1', true], + ['0', false] + ])); + + + $response = $this->controller->userSetRecovery($enableRecovery); + + $this->assertEquals($expectedMessage, $response->getData()['data']['message']); + $this->assertEquals($expectedStatus, $response->getStatus()); + + } + + protected function setUp() { + parent::setUp(); + + $this->appName = 'encryption'; + $this->requestMock = $this->getMockBuilder('\OCP\IRequest') + ->disableOriginalConstructor() + ->getMock(); + + $this->configMock = $this->getMockBuilder('OCP\IConfig') + ->disableOriginalConstructor() + ->getMock(); + + $this->l10nMock = $this->getMockBuilder('OCP\IL10N') + ->disableOriginalConstructor() + ->getMock(); + + // Make l10n work in our tests + $this->l10nMock->expects($this->any()) + ->method('t') + ->willReturnArgument(0); + + $this->recoveryMock = $this->getMockBuilder('OCA\Encryption\Recovery') + ->disableOriginalConstructor() + ->getMock(); + + $this->controller = new RecoveryController($this->appName, + $this->requestMock, + $this->configMock, + $this->l10nMock, + $this->recoveryMock); + } + +} diff --git a/apps/encryption/tests/hooks/UserHooksTest.php b/apps/encryption/tests/hooks/UserHooksTest.php index bcfb33e86cb..b0cc9cc924a 100644 --- a/apps/encryption/tests/hooks/UserHooksTest.php +++ b/apps/encryption/tests/hooks/UserHooksTest.php @@ -11,6 +11,7 @@ namespace OCA\Encryption\Tests\Hooks; +use OCA\Encryption\Crypto\Crypt; use OCA\Encryption\Hooks\UserHooks; use Test\TestCase; @@ -101,8 +102,18 @@ class UserHooksTest extends TestCase { ->method('symmetricEncryptFileContent') ->willReturn(true); + $this->cryptMock->expects($this->any()) + ->method('generateHeader') + ->willReturn(Crypt::HEADER_START . ':Cipher:test:' . Crypt::HEADER_END); + $this->keyManagerMock->expects($this->exactly(4)) - ->method('setPrivateKey'); + ->method('setPrivateKey') + ->willReturnCallback(function ($user, $key) { + $header = substr($key, 0, strlen(Crypt::HEADER_START)); + $this->assertSame( + Crypt::HEADER_START, + $header, 'every encrypted file should start with a header'); + }); $this->assertNull($this->instance->setPassphrase($this->params)); $this->params['recoveryPassword'] = 'password'; diff --git a/apps/encryption/tests/lib/crypto/cryptTest.php b/apps/encryption/tests/lib/crypto/cryptTest.php index 3ea57668348..4114adb115a 100644 --- a/apps/encryption/tests/lib/crypto/cryptTest.php +++ b/apps/encryption/tests/lib/crypto/cryptTest.php @@ -20,7 +20,7 @@ */ -namespace OCA\Encryption\Tests\Crypt; +namespace OCA\Encryption\Tests\lib\Crypto; use OCA\Encryption\Crypto\Crypt; diff --git a/apps/encryption/tests/lib/crypto/encryptionTest.php b/apps/encryption/tests/lib/crypto/encryptionTest.php index 9e14a70ebb0..aa28a8b44a4 100644 --- a/apps/encryption/tests/lib/crypto/encryptionTest.php +++ b/apps/encryption/tests/lib/crypto/encryptionTest.php @@ -19,8 +19,9 @@ * */ -namespace OCA\Encryption\Tests\Crypto; +namespace OCA\Encryption\Tests\lib\Crypto; +use OCA\Encryption\Exceptions\PublicKeyMissingException; use Test\TestCase; use OCA\Encryption\Crypto\Encryption; @@ -38,6 +39,9 @@ class EncryptionTest extends TestCase { /** @var \PHPUnit_Framework_MockObject_MockObject */ private $utilMock; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $loggerMock; + public function setUp() { parent::setUp(); @@ -50,8 +54,76 @@ class EncryptionTest extends TestCase { $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager') ->disableOriginalConstructor() ->getMock(); + $this->loggerMock = $this->getMockBuilder('OCP\ILogger') + ->disableOriginalConstructor() + ->getMock(); + + $this->instance = new Encryption( + $this->cryptMock, + $this->keyManagerMock, + $this->utilMock, + $this->loggerMock + ); + + } + + /** + * test if public key from one of the recipients is missing + */ + public function testEndUser1() { + $this->instance->begin('/foo/bar', 'user1', 'r', array(), array('users' => array('user1', 'user2', 'user3'))); + $this->endTest(); + } + + /** + * test if public key from owner is missing + * + * @expectedException \OCA\Encryption\Exceptions\PublicKeyMissingException + */ + public function testEndUser2() { + $this->instance->begin('/foo/bar', 'user2', 'r', array(), array('users' => array('user1', 'user2', 'user3'))); + $this->endTest(); + } + + /** + * common part of testEndUser1 and testEndUser2 + * + * @throws PublicKeyMissingException + */ + public function endTest() { + // prepare internal variables + \Test_Helper::invokePrivate($this->instance, 'isWriteOperation', [true]); + \Test_Helper::invokePrivate($this->instance, 'writeCache', ['']); + + $this->keyManagerMock->expects($this->any()) + ->method('getPublicKey') + ->will($this->returnCallback([$this, 'getPublicKeyCallback'])); + $this->keyManagerMock->expects($this->any()) + ->method('addSystemKeys') + ->will($this->returnCallback([$this, 'addSystemKeysCallback'])); + $this->cryptMock->expects($this->any()) + ->method('multiKeyEncrypt') + ->willReturn(true); + $this->cryptMock->expects($this->any()) + ->method('setAllFileKeys') + ->willReturn(true); + + $this->instance->end('/foo/bar'); + } + + + public function getPublicKeyCallback($uid) { + if ($uid === 'user2') { + throw new PublicKeyMissingException($uid); + } + return $uid; + } - $this->instance = new Encryption($this->cryptMock, $this->keyManagerMock, $this->utilMock); + public function addSystemKeysCallback($accessList, $publicKeys) { + $this->assertSame(2, count($publicKeys)); + $this->assertArrayHasKey('user1', $publicKeys); + $this->assertArrayHasKey('user3', $publicKeys); + return $publicKeys; } /** @@ -72,5 +144,80 @@ class EncryptionTest extends TestCase { ); } + /** + * @dataProvider dataTestBegin + */ + public function testBegin($mode, $header, $legacyCipher, $defaultCipher, $fileKey, $expected) { + + $this->cryptMock->expects($this->any()) + ->method('getCipher') + ->willReturn($defaultCipher); + $this->cryptMock->expects($this->any()) + ->method('getLegacyCipher') + ->willReturn($legacyCipher); + if (empty($fileKey)) { + $this->cryptMock->expects($this->once()) + ->method('generateFileKey') + ->willReturn('fileKey'); + } else { + $this->cryptMock->expects($this->never()) + ->method('generateFileKey'); + } + + $this->keyManagerMock->expects($this->once()) + ->method('getFileKey') + ->willReturn($fileKey); + + $result = $this->instance->begin('/user/files/foo.txt', 'user', $mode, $header, []); + + $this->assertArrayHasKey('cipher', $result); + $this->assertSame($expected, $result['cipher']); + if ($mode === 'w') { + $this->assertTrue(\Test_Helper::invokePrivate($this->instance, 'isWriteOperation')); + } else { + $this->assertFalse(\Test_Helper::invokePrivate($this->instance, 'isWriteOperation')); + } + } + + public function dataTestBegin() { + return array( + array('w', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'myCipher'), + array('r', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'myCipher'), + array('w', [], 'legacyCipher', 'defaultCipher', '', 'defaultCipher'), + array('r', [], 'legacyCipher', 'defaultCipher', 'file_key', 'legacyCipher'), + ); + } + + /** + * @dataProvider dataTestUpdate + * + * @param string $fileKey + * @param boolean $expected + */ + public function testUpdate($fileKey, $expected) { + $this->keyManagerMock->expects($this->once()) + ->method('getFileKey')->willReturn($fileKey); + + $this->keyManagerMock->expects($this->any()) + ->method('getPublicKey')->willReturn('publicKey'); + + $this->keyManagerMock->expects($this->any()) + ->method('addSystemKeys') + ->willReturnCallback(function($accessList, $publicKeys) { + return $publicKeys; + }); + + $this->assertSame($expected, + $this->instance->update('path', 'user1', ['users' => ['user1']]) + ); + + } + + public function dataTestUpdate() { + return array( + array('', false), + array('fileKey', true) + ); + } -}
\ No newline at end of file +} |