aboutsummaryrefslogtreecommitdiffstats
path: root/apps/encryption/tests/Crypto
diff options
context:
space:
mode:
Diffstat (limited to 'apps/encryption/tests/Crypto')
-rw-r--r--apps/encryption/tests/Crypto/CryptTest.php429
-rw-r--r--apps/encryption/tests/Crypto/DecryptAllTest.php109
-rw-r--r--apps/encryption/tests/Crypto/EncryptAllTest.php385
-rw-r--r--apps/encryption/tests/Crypto/EncryptionTest.php406
4 files changed, 1329 insertions, 0 deletions
diff --git a/apps/encryption/tests/Crypto/CryptTest.php b/apps/encryption/tests/Crypto/CryptTest.php
new file mode 100644
index 00000000000..1355e2c855d
--- /dev/null
+++ b/apps/encryption/tests/Crypto/CryptTest.php
@@ -0,0 +1,429 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\Encryption\Tests\Crypto;
+
+use OCA\Encryption\Crypto\Crypt;
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class CryptTest extends TestCase {
+ protected LoggerInterface&MockObject $logger;
+ protected IUserSession&MockObject $userSession;
+ protected IConfig&MockObject $config;
+ protected IL10N&MockObject $l;
+
+ protected Crypt $crypt;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->logger = $this->getMockBuilder(LoggerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->logger->expects($this->any())
+ ->method('warning');
+ $this->userSession = $this->getMockBuilder(IUserSession::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->config = $this->getMockBuilder(IConfig::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->l = $this->createMock(IL10N::class);
+
+ $this->crypt = new Crypt($this->logger, $this->userSession, $this->config, $this->l);
+ }
+
+ /**
+ * test getOpenSSLConfig without any additional parameters
+ */
+ public function testGetOpenSSLConfigBasic(): void {
+ $this->config->expects($this->once())
+ ->method('getSystemValue')
+ ->with($this->equalTo('openssl'), $this->equalTo([]))
+ ->willReturn([]);
+
+ $result = self::invokePrivate($this->crypt, 'getOpenSSLConfig');
+ $this->assertSame(1, count($result));
+ $this->assertArrayHasKey('private_key_bits', $result);
+ $this->assertSame(4096, $result['private_key_bits']);
+ }
+
+ /**
+ * test getOpenSSLConfig with additional parameters defined in config.php
+ */
+ public function testGetOpenSSLConfig(): void {
+ $this->config->expects($this->once())
+ ->method('getSystemValue')
+ ->with($this->equalTo('openssl'), $this->equalTo([]))
+ ->willReturn(['foo' => 'bar', 'private_key_bits' => 1028]);
+
+ $result = self::invokePrivate($this->crypt, 'getOpenSSLConfig');
+ $this->assertSame(2, count($result));
+ $this->assertArrayHasKey('private_key_bits', $result);
+ $this->assertArrayHasKey('foo', $result);
+ $this->assertSame(1028, $result['private_key_bits']);
+ $this->assertSame('bar', $result['foo']);
+ }
+
+
+ /**
+ * test generateHeader with valid key formats
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGenerateHeader')]
+ public function testGenerateHeader($keyFormat, $expected): void {
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with($this->equalTo('cipher'), $this->equalTo('AES-256-CTR'))
+ ->willReturn('AES-128-CFB');
+
+ if ($keyFormat) {
+ $result = $this->crypt->generateHeader($keyFormat);
+ } else {
+ $result = $this->crypt->generateHeader();
+ }
+
+ $this->assertSame($expected, $result);
+ }
+
+ /**
+ * test generateHeader with invalid key format
+ *
+ */
+ public function testGenerateHeaderInvalid(): void {
+ $this->expectException(\InvalidArgumentException::class);
+
+ $this->crypt->generateHeader('unknown');
+ }
+
+ public static function dataTestGenerateHeader(): array {
+ return [
+ [null, 'HBEGIN:cipher:AES-128-CFB:keyFormat:hash2:encoding:binary:HEND'],
+ ['password', 'HBEGIN:cipher:AES-128-CFB:keyFormat:password:encoding:binary:HEND'],
+ ['hash', 'HBEGIN:cipher:AES-128-CFB:keyFormat:hash:encoding:binary:HEND']
+ ];
+ }
+
+ public function testGetCipherWithInvalidCipher(): void {
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with($this->equalTo('cipher'), $this->equalTo('AES-256-CTR'))
+ ->willReturn('Not-Existing-Cipher');
+ $this->logger
+ ->expects($this->once())
+ ->method('warning')
+ ->with('Unsupported cipher (Not-Existing-Cipher) defined in config.php supported. Falling back to AES-256-CTR');
+
+ $this->assertSame('AES-256-CTR', $this->crypt->getCipher());
+ }
+
+ /**
+ * @param string $configValue
+ * @param string $expected
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderGetCipher')]
+ public function testGetCipher($configValue, $expected): void {
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with($this->equalTo('cipher'), $this->equalTo('AES-256-CTR'))
+ ->willReturn($configValue);
+
+ $this->assertSame($expected,
+ $this->crypt->getCipher()
+ );
+ }
+
+ /**
+ * data provider for testGetCipher
+ */
+ public static function dataProviderGetCipher(): array {
+ return [
+ ['AES-128-CFB', 'AES-128-CFB'],
+ ['AES-256-CFB', 'AES-256-CFB'],
+ ['AES-128-CTR', 'AES-128-CTR'],
+ ['AES-256-CTR', 'AES-256-CTR'],
+
+ ['unknown', 'AES-256-CTR']
+ ];
+ }
+
+ /**
+ * test concatIV()
+ */
+ public function testConcatIV(): void {
+ $result = self::invokePrivate(
+ $this->crypt,
+ 'concatIV',
+ ['content', 'my_iv']);
+
+ $this->assertSame('content00iv00my_iv',
+ $result
+ );
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSplitMetaData')]
+ public function testSplitMetaData($data, $expected): void {
+ $this->config->method('getSystemValueBool')
+ ->with('encryption_skip_signature_check', false)
+ ->willReturn(true);
+ $result = self::invokePrivate($this->crypt, 'splitMetaData', [$data, 'AES-256-CFB']);
+ $this->assertTrue(is_array($result));
+ $this->assertSame(3, count($result));
+ $this->assertArrayHasKey('encrypted', $result);
+ $this->assertArrayHasKey('iv', $result);
+ $this->assertArrayHasKey('signature', $result);
+ $this->assertSame($expected['encrypted'], $result['encrypted']);
+ $this->assertSame($expected['iv'], $result['iv']);
+ $this->assertSame($expected['signature'], $result['signature']);
+ }
+
+ public static function dataTestSplitMetaData(): array {
+ return [
+ ['encryptedContent00iv001234567890123456xx',
+ ['encrypted' => 'encryptedContent', 'iv' => '1234567890123456', 'signature' => false]],
+ ['encryptedContent00iv00123456789012345600sig00e1992521e437f6915f9173b190a512cfc38a00ac24502db44e0ba10c2bb0cc86xxx',
+ ['encrypted' => 'encryptedContent', 'iv' => '1234567890123456', 'signature' => 'e1992521e437f6915f9173b190a512cfc38a00ac24502db44e0ba10c2bb0cc86']],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestHasSignature')]
+ public function testHasSignature($data, $expected): void {
+ $this->config->method('getSystemValueBool')
+ ->with('encryption_skip_signature_check', false)
+ ->willReturn(true);
+ $this->assertSame($expected,
+ $this->invokePrivate($this->crypt, 'hasSignature', [$data, 'AES-256-CFB'])
+ );
+ }
+
+ public static function dataTestHasSignature(): array {
+ return [
+ ['encryptedContent00iv001234567890123456xx', false],
+ ['encryptedContent00iv00123456789012345600sig00e1992521e437f6915f9173b190a512cfc38a00ac24502db44e0ba10c2bb0cc86xxx', true]
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestHasSignatureFail')]
+ public function testHasSignatureFail($cipher): void {
+ $this->expectException(GenericEncryptionException::class);
+
+ $data = 'encryptedContent00iv001234567890123456xx';
+ $this->invokePrivate($this->crypt, 'hasSignature', [$data, $cipher]);
+ }
+
+ public static function dataTestHasSignatureFail(): array {
+ return [
+ ['AES-256-CTR'],
+ ['aes-256-ctr'],
+ ['AES-128-CTR'],
+ ['ctr-256-ctr']
+ ];
+ }
+
+ /**
+ * test addPadding()
+ */
+ public function testAddPadding(): void {
+ $result = self::invokePrivate($this->crypt, 'addPadding', ['data']);
+ $this->assertSame('dataxxx', $result);
+ }
+
+ /**
+ * test removePadding()
+ *
+ * @param $data
+ * @param $expected
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderRemovePadding')]
+ public function testRemovePadding($data, $expected): void {
+ $result = self::invokePrivate($this->crypt, 'removePadding', [$data]);
+ $this->assertSame($expected, $result);
+ }
+
+ /**
+ * data provider for testRemovePadding
+ */
+ public static function dataProviderRemovePadding(): array {
+ return [
+ ['dataxx', 'data'],
+ ['data', false]
+ ];
+ }
+
+ /**
+ * test parseHeader()
+ */
+ public function testParseHeader(): void {
+ $header = 'HBEGIN:foo:bar:cipher:AES-256-CFB:encoding:binary:HEND';
+ $result = self::invokePrivate($this->crypt, 'parseHeader', [$header]);
+
+ $this->assertTrue(is_array($result));
+ $this->assertSame(3, count($result));
+ $this->assertArrayHasKey('foo', $result);
+ $this->assertArrayHasKey('cipher', $result);
+ $this->assertArrayHasKey('encoding', $result);
+ $this->assertSame('bar', $result['foo']);
+ $this->assertSame('AES-256-CFB', $result['cipher']);
+ $this->assertSame('binary', $result['encoding']);
+ }
+
+ /**
+ * test encrypt()
+ *
+ * @return string
+ */
+ public function testEncrypt() {
+ $decrypted = 'content';
+ $password = 'password';
+ $cipher = 'AES-256-CTR';
+ $iv = self::invokePrivate($this->crypt, 'generateIv');
+
+ $this->assertTrue(is_string($iv));
+ $this->assertSame(16, strlen($iv));
+
+ $result = self::invokePrivate($this->crypt, 'encrypt', [$decrypted, $iv, $password, $cipher]);
+
+ $this->assertTrue(is_string($result));
+
+ return [
+ 'password' => $password,
+ 'iv' => $iv,
+ 'cipher' => $cipher,
+ 'encrypted' => $result,
+ 'decrypted' => $decrypted];
+ }
+
+ /**
+ * test decrypt()
+ *
+ * @depends testEncrypt
+ */
+ public function testDecrypt($data): void {
+ $result = self::invokePrivate(
+ $this->crypt,
+ 'decrypt',
+ [$data['encrypted'], $data['iv'], $data['password'], $data['cipher'], true]);
+
+ $this->assertSame($data['decrypted'], $result);
+ }
+
+ /**
+ * test return values of valid ciphers
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetKeySize')]
+ public function testGetKeySize($cipher, $expected): void {
+ $result = $this->invokePrivate($this->crypt, 'getKeySize', [$cipher]);
+ $this->assertSame($expected, $result);
+ }
+
+ /**
+ * test exception if cipher is unknown
+ *
+ */
+ public function testGetKeySizeFailure(): void {
+ $this->expectException(\InvalidArgumentException::class);
+
+ $this->invokePrivate($this->crypt, 'getKeySize', ['foo']);
+ }
+
+ public static function dataTestGetKeySize(): array {
+ return [
+ ['AES-256-CFB', 32],
+ ['AES-128-CFB', 16],
+ ['AES-256-CTR', 32],
+ ['AES-128-CTR', 16],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestDecryptPrivateKey')]
+ public function testDecryptPrivateKey($header, $privateKey, $expectedCipher, $isValidKey, $expected): void {
+ $this->config->method('getSystemValueBool')
+ ->willReturnMap([
+ ['encryption.legacy_format_support', false, true],
+ ['encryption.use_legacy_base64_encoding', false, false],
+ ]);
+
+ /** @var Crypt|\PHPUnit\Framework\MockObject\MockObject $crypt */
+ $crypt = $this->getMockBuilder(Crypt::class)
+ ->setConstructorArgs([
+ $this->logger,
+ $this->userSession,
+ $this->config,
+ $this->l
+ ])
+ ->onlyMethods([
+ 'parseHeader',
+ 'generatePasswordHash',
+ 'symmetricDecryptFileContent',
+ 'isValidPrivateKey'
+ ])
+ ->getMock();
+
+ $crypt->expects($this->once())->method('parseHeader')->willReturn($header);
+ if (isset($header['keyFormat']) && $header['keyFormat'] === 'hash') {
+ $crypt->expects($this->once())->method('generatePasswordHash')->willReturn('hash');
+ $password = 'hash';
+ } else {
+ $crypt->expects($this->never())->method('generatePasswordHash');
+ $password = 'password';
+ }
+
+ $crypt->expects($this->once())->method('symmetricDecryptFileContent')
+ ->with('privateKey', $password, $expectedCipher)->willReturn('key');
+ $crypt->expects($this->once())->method('isValidPrivateKey')->willReturn($isValidKey);
+
+ $result = $crypt->decryptPrivateKey($privateKey, 'password');
+
+ $this->assertSame($expected, $result);
+ }
+
+ public static function dataTestDecryptPrivateKey(): array {
+ return [
+ [['cipher' => 'AES-128-CFB', 'keyFormat' => 'password'], 'HBEGIN:HENDprivateKey', 'AES-128-CFB', true, 'key'],
+ [['cipher' => 'AES-256-CFB', 'keyFormat' => 'password'], 'HBEGIN:HENDprivateKey', 'AES-256-CFB', true, 'key'],
+ [['cipher' => 'AES-256-CFB', 'keyFormat' => 'password'], 'HBEGIN:HENDprivateKey', 'AES-256-CFB', false, false],
+ [['cipher' => 'AES-256-CFB', 'keyFormat' => 'hash'], 'HBEGIN:HENDprivateKey', 'AES-256-CFB', true, 'key'],
+ [['cipher' => 'AES-256-CFB'], 'HBEGIN:HENDprivateKey', 'AES-256-CFB', true, 'key'],
+ [[], 'privateKey', 'AES-128-CFB', true, 'key'],
+ ];
+ }
+
+ public function testIsValidPrivateKey(): void {
+ $res = openssl_pkey_new();
+ openssl_pkey_export($res, $privateKey);
+
+ // valid private key
+ $this->assertTrue(
+ $this->invokePrivate($this->crypt, 'isValidPrivateKey', [$privateKey])
+ );
+
+ // invalid private key
+ $this->assertFalse(
+ $this->invokePrivate($this->crypt, 'isValidPrivateKey', ['foo'])
+ );
+ }
+
+ public function testMultiKeyEncrypt(): void {
+ $res = openssl_pkey_new();
+ openssl_pkey_export($res, $privateKey);
+ $publicKeyPem = openssl_pkey_get_details($res)['key'];
+ $publicKey = openssl_pkey_get_public($publicKeyPem);
+
+ $shareKeys = $this->crypt->multiKeyEncrypt('content', ['user1' => $publicKey]);
+ $this->assertEquals(
+ 'content',
+ $this->crypt->multiKeyDecrypt($shareKeys['user1'], $privateKey)
+ );
+ }
+}
diff --git a/apps/encryption/tests/Crypto/DecryptAllTest.php b/apps/encryption/tests/Crypto/DecryptAllTest.php
new file mode 100644
index 00000000000..82e6100bce5
--- /dev/null
+++ b/apps/encryption/tests/Crypto/DecryptAllTest.php
@@ -0,0 +1,109 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\Encryption\Tests\Crypto;
+
+use OCA\Encryption\Crypto\Crypt;
+use OCA\Encryption\Crypto\DecryptAll;
+use OCA\Encryption\KeyManager;
+use OCA\Encryption\Session;
+use OCA\Encryption\Util;
+use PHPUnit\Framework\MockObject\MockObject;
+use Symfony\Component\Console\Helper\QuestionHelper;
+use Test\TestCase;
+
+class DecryptAllTest extends TestCase {
+
+ protected DecryptAll $instance;
+
+ protected Util&MockObject $util;
+ protected KeyManager&MockObject $keyManager;
+ protected Crypt&MockObject $crypt;
+ protected Session&MockObject $session;
+ protected QuestionHelper&MockObject $questionHelper;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->util = $this->getMockBuilder(Util::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->keyManager = $this->getMockBuilder(KeyManager::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->crypt = $this->getMockBuilder(Crypt::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->session = $this->getMockBuilder(Session::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->questionHelper = $this->getMockBuilder(QuestionHelper::class)
+ ->disableOriginalConstructor()->getMock();
+
+ $this->instance = new DecryptAll(
+ $this->util,
+ $this->keyManager,
+ $this->crypt,
+ $this->session,
+ $this->questionHelper
+ );
+ }
+
+ public function testUpdateSession(): void {
+ $this->session->expects($this->once())->method('prepareDecryptAll')
+ ->with('user1', 'key1');
+
+ $this->invokePrivate($this->instance, 'updateSession', ['user1', 'key1']);
+ }
+
+ /**
+ *
+ * @param string $user
+ * @param string $recoveryKeyId
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetPrivateKey')]
+ public function testGetPrivateKey($user, $recoveryKeyId, $masterKeyId): void {
+ $password = 'passwd';
+ $recoveryKey = 'recoveryKey';
+ $userKey = 'userKey';
+ $masterKey = 'userKey';
+ $unencryptedKey = 'unencryptedKey';
+
+ $this->keyManager->expects($this->any())->method('getRecoveryKeyId')
+ ->willReturn($recoveryKeyId);
+
+ if ($user === $recoveryKeyId) {
+ $this->keyManager->expects($this->once())->method('getSystemPrivateKey')
+ ->with($recoveryKeyId)->willReturn($recoveryKey);
+ $this->keyManager->expects($this->never())->method('getPrivateKey');
+ $this->crypt->expects($this->once())->method('decryptPrivateKey')
+ ->with($recoveryKey, $password)->willReturn($unencryptedKey);
+ } elseif ($user === $masterKeyId) {
+ $this->keyManager->expects($this->once())->method('getSystemPrivateKey')
+ ->with($masterKeyId)->willReturn($masterKey);
+ $this->keyManager->expects($this->never())->method('getPrivateKey');
+ $this->crypt->expects($this->once())->method('decryptPrivateKey')
+ ->with($masterKey, $password, $masterKeyId)->willReturn($unencryptedKey);
+ } else {
+ $this->keyManager->expects($this->never())->method('getSystemPrivateKey');
+ $this->keyManager->expects($this->once())->method('getPrivateKey')
+ ->with($user)->willReturn($userKey);
+ $this->crypt->expects($this->once())->method('decryptPrivateKey')
+ ->with($userKey, $password, $user)->willReturn($unencryptedKey);
+ }
+
+ $this->assertSame($unencryptedKey,
+ $this->invokePrivate($this->instance, 'getPrivateKey', [$user, $password])
+ );
+ }
+
+ public static function dataTestGetPrivateKey() {
+ return [
+ ['user1', 'recoveryKey', 'masterKeyId'],
+ ['recoveryKeyId', 'recoveryKeyId', 'masterKeyId'],
+ ['masterKeyId', 'masterKeyId', 'masterKeyId']
+ ];
+ }
+}
diff --git a/apps/encryption/tests/Crypto/EncryptAllTest.php b/apps/encryption/tests/Crypto/EncryptAllTest.php
new file mode 100644
index 00000000000..c56e3375a73
--- /dev/null
+++ b/apps/encryption/tests/Crypto/EncryptAllTest.php
@@ -0,0 +1,385 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\Encryption\Tests\Crypto;
+
+use OC\Files\View;
+use OCA\Encryption\Crypto\EncryptAll;
+use OCA\Encryption\KeyManager;
+use OCA\Encryption\Users\Setup;
+use OCA\Encryption\Util;
+use OCP\Files\FileInfo;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IUserManager;
+use OCP\L10N\IFactory;
+use OCP\Mail\IMailer;
+use OCP\Security\ISecureRandom;
+use OCP\UserInterface;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Console\Formatter\OutputFormatterInterface;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Helper\QuestionHelper;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Test\TestCase;
+
+class EncryptAllTest extends TestCase {
+
+ protected KeyManager&MockObject $keyManager;
+ protected Util&MockObject $util;
+ protected IUserManager&MockObject $userManager;
+ protected Setup&MockObject $setupUser;
+ protected View&MockObject $view;
+ protected IConfig&MockObject $config;
+ protected IMailer&MockObject $mailer;
+ protected IL10N&MockObject $l;
+ protected IFactory&MockObject $l10nFactory;
+ protected \Symfony\Component\Console\Helper\QuestionHelper&MockObject $questionHelper;
+ protected \Symfony\Component\Console\Input\InputInterface&MockObject $inputInterface;
+ protected \Symfony\Component\Console\Output\OutputInterface&MockObject $outputInterface;
+ protected UserInterface&MockObject $userInterface;
+ protected ISecureRandom&MockObject $secureRandom;
+ protected LoggerInterface&MockObject $logger;
+
+ protected EncryptAll $encryptAll;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->setupUser = $this->getMockBuilder(Setup::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->keyManager = $this->getMockBuilder(KeyManager::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->util = $this->getMockBuilder(Util::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->userManager = $this->getMockBuilder(IUserManager::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->view = $this->getMockBuilder(View::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->config = $this->getMockBuilder(IConfig::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->mailer = $this->getMockBuilder(IMailer::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->l10nFactory = $this->createMock(IFactory::class);
+ $this->l = $this->getMockBuilder(IL10N::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->questionHelper = $this->getMockBuilder(QuestionHelper::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->inputInterface = $this->getMockBuilder(InputInterface::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->outputInterface = $this->getMockBuilder(OutputInterface::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->userInterface = $this->getMockBuilder(UserInterface::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->logger = $this->createMock(LoggerInterface::class);
+
+ /**
+ * We need format method to return a string
+ * @var OutputFormatterInterface&MockObject
+ */
+ $outputFormatter = $this->createMock(OutputFormatterInterface::class);
+ $outputFormatter->method('isDecorated')->willReturn(false);
+ $outputFormatter->method('format')->willReturnArgument(0);
+
+ $this->outputInterface->expects($this->any())->method('getFormatter')
+ ->willReturn($outputFormatter);
+
+ $this->userManager->expects($this->any())->method('getBackends')->willReturn([$this->userInterface]);
+ $this->userInterface->expects($this->any())->method('getUsers')->willReturn(['user1', 'user2']);
+
+ $this->secureRandom = $this->getMockBuilder(ISecureRandom::class)->disableOriginalConstructor()->getMock();
+ $this->secureRandom->expects($this->any())->method('generate')->willReturn('12345678');
+
+
+ $this->encryptAll = new EncryptAll(
+ $this->setupUser,
+ $this->userManager,
+ $this->view,
+ $this->keyManager,
+ $this->util,
+ $this->config,
+ $this->mailer,
+ $this->l,
+ $this->l10nFactory,
+ $this->questionHelper,
+ $this->secureRandom,
+ $this->logger,
+ );
+ }
+
+ protected function createFileInfoMock($type, string $name): FileInfo&MockObject {
+ $fileInfo = $this->createMock(FileInfo::class);
+ $fileInfo->method('getType')->willReturn($type);
+ $fileInfo->method('getName')->willReturn($name);
+ return $fileInfo;
+ }
+
+ public function testEncryptAll(): void {
+ /** @var EncryptAll&MockObject $encryptAll */
+ $encryptAll = $this->getMockBuilder(EncryptAll::class)
+ ->setConstructorArgs(
+ [
+ $this->setupUser,
+ $this->userManager,
+ $this->view,
+ $this->keyManager,
+ $this->util,
+ $this->config,
+ $this->mailer,
+ $this->l,
+ $this->l10nFactory,
+ $this->questionHelper,
+ $this->secureRandom,
+ $this->logger,
+ ]
+ )
+ ->onlyMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords'])
+ ->getMock();
+
+ $this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false);
+ $encryptAll->expects($this->once())->method('createKeyPairs')->with();
+ $encryptAll->expects($this->once())->method('outputPasswords')->with();
+ $encryptAll->expects($this->once())->method('encryptAllUsersFiles')->with();
+
+ $encryptAll->encryptAll($this->inputInterface, $this->outputInterface);
+ }
+
+ public function testEncryptAllWithMasterKey(): void {
+ /** @var EncryptAll&MockObject $encryptAll */
+ $encryptAll = $this->getMockBuilder(EncryptAll::class)
+ ->setConstructorArgs(
+ [
+ $this->setupUser,
+ $this->userManager,
+ $this->view,
+ $this->keyManager,
+ $this->util,
+ $this->config,
+ $this->mailer,
+ $this->l,
+ $this->l10nFactory,
+ $this->questionHelper,
+ $this->secureRandom,
+ $this->logger,
+ ]
+ )
+ ->onlyMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords'])
+ ->getMock();
+
+ $this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(true);
+ $encryptAll->expects($this->never())->method('createKeyPairs');
+ $this->keyManager->expects($this->once())->method('validateMasterKey');
+ $encryptAll->expects($this->once())->method('encryptAllUsersFiles')->with();
+ $encryptAll->expects($this->never())->method('outputPasswords');
+
+ $encryptAll->encryptAll($this->inputInterface, $this->outputInterface);
+ }
+
+ public function testCreateKeyPairs(): void {
+ /** @var EncryptAll&MockObject $encryptAll */
+ $encryptAll = $this->getMockBuilder(EncryptAll::class)
+ ->setConstructorArgs(
+ [
+ $this->setupUser,
+ $this->userManager,
+ $this->view,
+ $this->keyManager,
+ $this->util,
+ $this->config,
+ $this->mailer,
+ $this->l,
+ $this->l10nFactory,
+ $this->questionHelper,
+ $this->secureRandom,
+ $this->logger,
+ ]
+ )
+ ->onlyMethods(['setupUserFS', 'generateOneTimePassword'])
+ ->getMock();
+
+
+ // set protected property $output
+ $this->invokePrivate($encryptAll, 'output', [$this->outputInterface]);
+
+ $this->keyManager->expects($this->exactly(2))->method('userHasKeys')
+ ->willReturnCallback(
+ function ($user) {
+ if ($user === 'user1') {
+ return false;
+ }
+ return true;
+ }
+ );
+
+ $encryptAll->expects($this->once())->method('setupUserFS')->with('user1');
+ $encryptAll->expects($this->once())->method('generateOneTimePassword')->with('user1')->willReturn('password');
+ $this->setupUser->expects($this->once())->method('setupUser')->with('user1', 'password');
+
+ $this->invokePrivate($encryptAll, 'createKeyPairs');
+
+ $userPasswords = $this->invokePrivate($encryptAll, 'userPasswords');
+
+ // we only expect the skipped user, because generateOneTimePassword which
+ // would set the user with the new password was mocked.
+ // This method will be tested separately
+ $this->assertSame(1, count($userPasswords));
+ $this->assertSame('', $userPasswords['user2']);
+ }
+
+ public function testEncryptAllUsersFiles(): void {
+ /** @var EncryptAll&MockObject $encryptAll */
+ $encryptAll = $this->getMockBuilder(EncryptAll::class)
+ ->setConstructorArgs(
+ [
+ $this->setupUser,
+ $this->userManager,
+ $this->view,
+ $this->keyManager,
+ $this->util,
+ $this->config,
+ $this->mailer,
+ $this->l,
+ $this->l10nFactory,
+ $this->questionHelper,
+ $this->secureRandom,
+ $this->logger,
+ ]
+ )
+ ->onlyMethods(['encryptUsersFiles'])
+ ->getMock();
+
+ $this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false);
+
+ // set protected property $output
+ $this->invokePrivate($encryptAll, 'output', [$this->outputInterface]);
+ $this->invokePrivate($encryptAll, 'userPasswords', [['user1' => 'pwd1', 'user2' => 'pwd2']]);
+
+ $encryptAllCalls = [];
+ $encryptAll->expects($this->exactly(2))
+ ->method('encryptUsersFiles')
+ ->willReturnCallback(function ($uid) use (&$encryptAllCalls): void {
+ $encryptAllCalls[] = $uid;
+ });
+
+ $this->invokePrivate($encryptAll, 'encryptAllUsersFiles');
+ self::assertEquals([
+ 'user1',
+ 'user2',
+ ], $encryptAllCalls);
+ }
+
+ public function testEncryptUsersFiles(): void {
+ /** @var EncryptAll&MockObject $encryptAll */
+ $encryptAll = $this->getMockBuilder(EncryptAll::class)
+ ->setConstructorArgs(
+ [
+ $this->setupUser,
+ $this->userManager,
+ $this->view,
+ $this->keyManager,
+ $this->util,
+ $this->config,
+ $this->mailer,
+ $this->l,
+ $this->l10nFactory,
+ $this->questionHelper,
+ $this->secureRandom,
+ $this->logger,
+ ]
+ )
+ ->onlyMethods(['encryptFile', 'setupUserFS'])
+ ->getMock();
+
+ $this->util->expects($this->any())->method('isMasterKeyEnabled')->willReturn(false);
+
+ $this->view->expects($this->exactly(2))->method('getDirectoryContent')
+ ->willReturnMap([
+ [
+ '/user1/files',
+ '',
+ null,
+ [
+ $this->createFileInfoMock(FileInfo::TYPE_FOLDER, 'foo'),
+ $this->createFileInfoMock(FileInfo::TYPE_FILE, 'bar'),
+ ],
+ ],
+ [
+ '/user1/files/foo',
+ '',
+ null,
+ [
+ $this->createFileInfoMock(FileInfo::TYPE_FILE, 'subfile'),
+ ],
+ ],
+ ]);
+
+ $encryptAllCalls = [];
+ $encryptAll->expects($this->exactly(2))
+ ->method('encryptFile')
+ ->willReturnCallback(function (FileInfo $file, string $path) use (&$encryptAllCalls): bool {
+ $encryptAllCalls[] = $path;
+ return true;
+ });
+
+ $outputFormatter = $this->createMock(OutputFormatterInterface::class);
+ $outputFormatter->method('isDecorated')->willReturn(false);
+ $this->outputInterface->expects($this->any())
+ ->method('getFormatter')
+ ->willReturn($outputFormatter);
+ $progressBar = new ProgressBar($this->outputInterface);
+
+ $this->invokePrivate($encryptAll, 'encryptUsersFiles', ['user1', $progressBar, '']);
+ self::assertEquals([
+ '/user1/files/bar',
+ '/user1/files/foo/subfile',
+ ], $encryptAllCalls);
+ }
+
+ public function testGenerateOneTimePassword(): void {
+ $password = $this->invokePrivate($this->encryptAll, 'generateOneTimePassword', ['user1']);
+ $this->assertTrue(is_string($password));
+ $this->assertSame(8, strlen($password));
+
+ $userPasswords = $this->invokePrivate($this->encryptAll, 'userPasswords');
+ $this->assertSame(1, count($userPasswords));
+ $this->assertSame($password, $userPasswords['user1']);
+ }
+
+ /**
+ * @param $isEncrypted
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestEncryptFile')]
+ public function testEncryptFile($isEncrypted): void {
+ $fileInfo = $this->createMock(FileInfo::class);
+ $fileInfo->expects($this->any())->method('isEncrypted')
+ ->willReturn($isEncrypted);
+ $this->view->expects($this->never())->method('getFileInfo');
+
+
+ if ($isEncrypted) {
+ $this->view->expects($this->never())->method('copy');
+ $this->view->expects($this->never())->method('rename');
+ } else {
+ $this->view->expects($this->once())->method('copy');
+ $this->view->expects($this->once())->method('rename');
+ }
+
+ $this->assertTrue(
+ $this->invokePrivate($this->encryptAll, 'encryptFile', [$fileInfo, 'foo.txt'])
+ );
+ }
+
+ public static function dataTestEncryptFile(): array {
+ return [
+ [true],
+ [false],
+ ];
+ }
+}
diff --git a/apps/encryption/tests/Crypto/EncryptionTest.php b/apps/encryption/tests/Crypto/EncryptionTest.php
new file mode 100644
index 00000000000..37e484550ef
--- /dev/null
+++ b/apps/encryption/tests/Crypto/EncryptionTest.php
@@ -0,0 +1,406 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\Encryption\Tests\Crypto;
+
+use OC\Encryption\Exceptions\DecryptionFailedException;
+use OC\Files\View;
+use OCA\Encryption\Crypto\Crypt;
+use OCA\Encryption\Crypto\DecryptAll;
+use OCA\Encryption\Crypto\EncryptAll;
+use OCA\Encryption\Crypto\Encryption;
+use OCA\Encryption\Exceptions\PublicKeyMissingException;
+use OCA\Encryption\KeyManager;
+use OCA\Encryption\Session;
+use OCA\Encryption\Util;
+use OCP\Files\Storage\IStorage;
+use OCP\IL10N;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Test\TestCase;
+
+class EncryptionTest extends TestCase {
+
+ protected Encryption $instance;
+
+ protected KeyManager&MockObject $keyManagerMock;
+ protected EncryptAll&MockObject $encryptAllMock;
+ protected DecryptAll&MockObject $decryptAllMock;
+ protected Session&MockObject $sessionMock;
+ protected Crypt&MockObject $cryptMock;
+ protected Util&MockObject $utilMock;
+ protected LoggerInterface&MockObject $loggerMock;
+ protected IL10N&MockObject $l10nMock;
+ protected IStorage&MockObject $storageMock;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->storageMock = $this->getMockBuilder(IStorage::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->cryptMock = $this->getMockBuilder(Crypt::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->utilMock = $this->getMockBuilder(Util::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->keyManagerMock = $this->getMockBuilder(KeyManager::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->sessionMock = $this->getMockBuilder(Session::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->encryptAllMock = $this->getMockBuilder(EncryptAll::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->decryptAllMock = $this->getMockBuilder(DecryptAll::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->loggerMock = $this->getMockBuilder(LoggerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->l10nMock = $this->getMockBuilder(IL10N::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->l10nMock->expects($this->any())
+ ->method('t')
+ ->with($this->anything())
+ ->willReturnArgument(0);
+
+ $this->instance = new Encryption(
+ $this->cryptMock,
+ $this->keyManagerMock,
+ $this->utilMock,
+ $this->sessionMock,
+ $this->encryptAllMock,
+ $this->decryptAllMock,
+ $this->loggerMock,
+ $this->l10nMock
+ );
+ }
+
+ /**
+ * test if public key from one of the recipients is missing
+ */
+ public function testEndUser1(): void {
+ $this->sessionMock->expects($this->once())
+ ->method('decryptAllModeActivated')
+ ->willReturn(false);
+
+ $this->instance->begin('/foo/bar', 'user1', 'r', [], ['users' => ['user1', 'user2', 'user3']]);
+ $this->endTest();
+ }
+
+ /**
+ * test if public key from owner is missing
+ *
+ */
+ public function testEndUser2(): void {
+ $this->sessionMock->expects($this->once())
+ ->method('decryptAllModeActivated')
+ ->willReturn(false);
+
+ $this->expectException(PublicKeyMissingException::class);
+
+ $this->instance->begin('/foo/bar', 'user2', 'r', [], ['users' => ['user1', 'user2', 'user3']]);
+ $this->endTest();
+ }
+
+ /**
+ * common part of testEndUser1 and testEndUser2
+ *
+ * @throws PublicKeyMissingException
+ */
+ public function endTest() {
+ // prepare internal variables
+ self::invokePrivate($this->instance, 'isWriteOperation', [true]);
+ self::invokePrivate($this->instance, 'writeCache', ['']);
+
+ $this->keyManagerMock->expects($this->any())
+ ->method('getPublicKey')
+ ->willReturnCallback([$this, 'getPublicKeyCallback']);
+ $this->keyManagerMock->expects($this->any())
+ ->method('addSystemKeys')
+ ->willReturnCallback([$this, 'addSystemKeysCallback']);
+ $this->cryptMock->expects($this->any())
+ ->method('multiKeyEncrypt')
+ ->willReturn([]);
+
+ $this->instance->end('/foo/bar');
+ }
+
+
+ public function getPublicKeyCallback($uid) {
+ if ($uid === 'user2') {
+ throw new PublicKeyMissingException($uid);
+ }
+ return $uid;
+ }
+
+ public function addSystemKeysCallback($accessList, $publicKeys) {
+ $this->assertSame(2, count($publicKeys));
+ $this->assertArrayHasKey('user1', $publicKeys);
+ $this->assertArrayHasKey('user3', $publicKeys);
+ return $publicKeys;
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderForTestGetPathToRealFile')]
+ public function testGetPathToRealFile($path, $expected): void {
+ $this->assertSame($expected,
+ self::invokePrivate($this->instance, 'getPathToRealFile', [$path])
+ );
+ }
+
+ public static function dataProviderForTestGetPathToRealFile(): array {
+ return [
+ ['/user/files/foo/bar.txt', '/user/files/foo/bar.txt'],
+ ['/user/files/foo.txt', '/user/files/foo.txt'],
+ ['/user/files_versions/foo.txt.v543534', '/user/files/foo.txt'],
+ ['/user/files_versions/foo/bar.txt.v5454', '/user/files/foo/bar.txt'],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestBegin')]
+ public function testBegin($mode, $header, $legacyCipher, $defaultCipher, $fileKey, $expected): void {
+ $this->sessionMock->expects($this->once())
+ ->method('decryptAllModeActivated')
+ ->willReturn(false);
+
+ $this->sessionMock->expects($this->never())->method('getDecryptAllUid');
+ $this->sessionMock->expects($this->never())->method('getDecryptAllKey');
+ $this->keyManagerMock->expects($this->never())->method('getEncryptedFileKey');
+ $this->keyManagerMock->expects($this->never())->method('getShareKey');
+ $this->cryptMock->expects($this->never())->method('multiKeyDecrypt');
+
+ $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(self::invokePrivate($this->instance, 'isWriteOperation'));
+ } else {
+ $this->assertFalse(self::invokePrivate($this->instance, 'isWriteOperation'));
+ }
+ }
+
+ public static function dataTestBegin(): array {
+ return [
+ ['w', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'defaultCipher'],
+ ['r', ['cipher' => 'myCipher'], 'legacyCipher', 'defaultCipher', 'fileKey', 'myCipher'],
+ ['w', [], 'legacyCipher', 'defaultCipher', '', 'defaultCipher'],
+ ['r', [], 'legacyCipher', 'defaultCipher', 'file_key', 'legacyCipher'],
+ ];
+ }
+
+
+ /**
+ * test begin() if decryptAll mode was activated
+ */
+ public function testBeginDecryptAll(): void {
+ $path = '/user/files/foo.txt';
+ $fileKey = 'fileKey';
+
+ $this->sessionMock->expects($this->once())
+ ->method('decryptAllModeActivated')
+ ->willReturn(true);
+ $this->keyManagerMock->expects($this->once())
+ ->method('getFileKey')
+ ->with($path, 'user', null, true)
+ ->willReturn($fileKey);
+
+ $this->instance->begin($path, 'user', 'r', [], []);
+
+ $this->assertSame($fileKey,
+ $this->invokePrivate($this->instance, 'fileKey')
+ );
+ }
+
+ /**
+ * test begin() if encryption is not initialized but the master key is enabled
+ * in this case we can initialize the encryption without a username/password
+ * and continue
+ */
+ public function testBeginInitMasterKey(): void {
+ $this->sessionMock->expects($this->once())
+ ->method('decryptAllModeActivated')
+ ->willReturn(false);
+
+ $this->sessionMock->expects($this->once())->method('isReady')->willReturn(false);
+ $this->utilMock->expects($this->once())->method('isMasterKeyEnabled')
+ ->willReturn(true);
+ $this->keyManagerMock->expects($this->once())->method('init')->with('', '');
+
+ $this->instance->begin('/user/files/welcome.txt', 'user', 'r', [], []);
+ }
+
+ /**
+ *
+ * @param string $fileKey
+ * @param boolean $expected
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestUpdate')]
+ public function testUpdate($fileKey, $expected): void {
+ $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->keyManagerMock->expects($this->never())->method('getVersion');
+ $this->keyManagerMock->expects($this->never())->method('setVersion');
+
+ $this->assertSame($expected,
+ $this->instance->update('path', 'user1', ['users' => ['user1']])
+ );
+ }
+
+ public static function dataTestUpdate(): array {
+ return [
+ ['', false],
+ ['fileKey', true]
+ ];
+ }
+
+ public function testUpdateNoUsers(): void {
+ $this->invokePrivate($this->instance, 'rememberVersion', [['path' => 2]]);
+
+ $this->keyManagerMock->expects($this->never())->method('getFileKey');
+ $this->keyManagerMock->expects($this->never())->method('getPublicKey');
+ $this->keyManagerMock->expects($this->never())->method('addSystemKeys');
+ $this->keyManagerMock->expects($this->once())->method('setVersion')
+ ->willReturnCallback(function ($path, $version, $view): void {
+ $this->assertSame('path', $path);
+ $this->assertSame(2, $version);
+ $this->assertTrue($view instanceof View);
+ });
+ $this->instance->update('path', 'user1', []);
+ }
+
+ /**
+ * Test case if the public key is missing. Nextcloud should still encrypt
+ * the file for the remaining users
+ */
+ public function testUpdateMissingPublicKey(): void {
+ $this->keyManagerMock->expects($this->once())
+ ->method('getFileKey')->willReturn('fileKey');
+
+ $this->keyManagerMock->expects($this->any())
+ ->method('getPublicKey')->willReturnCallback(
+ function ($user): void {
+ throw new PublicKeyMissingException($user);
+ }
+ );
+
+ $this->keyManagerMock->expects($this->any())
+ ->method('addSystemKeys')
+ ->willReturnCallback(function ($accessList, $publicKeys) {
+ return $publicKeys;
+ });
+
+ $this->cryptMock->expects($this->once())->method('multiKeyEncrypt')
+ ->willReturnCallback(
+ function ($fileKey, $publicKeys) {
+ $this->assertEmpty($publicKeys);
+ $this->assertSame('fileKey', $fileKey);
+ return [];
+ }
+ );
+
+ $this->keyManagerMock->expects($this->never())->method('getVersion');
+ $this->keyManagerMock->expects($this->never())->method('setVersion');
+
+ $this->assertTrue(
+ $this->instance->update('path', 'user1', ['users' => ['user1']])
+ );
+ }
+
+ /**
+ * by default the encryption module should encrypt regular files, files in
+ * files_versions and files in files_trashbin
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestShouldEncrypt')]
+ public function testShouldEncrypt($path, $shouldEncryptHomeStorage, $isHomeStorage, $expected): void {
+ $this->utilMock->expects($this->once())->method('shouldEncryptHomeStorage')
+ ->willReturn($shouldEncryptHomeStorage);
+
+ if ($shouldEncryptHomeStorage === false) {
+ $this->storageMock->expects($this->once())->method('instanceOfStorage')
+ ->with('\OCP\Files\IHomeStorage')->willReturn($isHomeStorage);
+ $this->utilMock->expects($this->once())->method('getStorage')->with($path)
+ ->willReturn($this->storageMock);
+ }
+
+ $this->assertSame($expected,
+ $this->instance->shouldEncrypt($path)
+ );
+ }
+
+ public static function dataTestShouldEncrypt(): array {
+ return [
+ ['/user1/files/foo.txt', true, true, true],
+ ['/user1/files_versions/foo.txt', true, true, true],
+ ['/user1/files_trashbin/foo.txt', true, true, true],
+ ['/user1/some_folder/foo.txt', true, true, false],
+ ['/user1/foo.txt', true, true, false],
+ ['/user1/files', true, true, false],
+ ['/user1/files_trashbin', true, true, false],
+ ['/user1/files_versions', true, true, false],
+ // test if shouldEncryptHomeStorage is set to false
+ ['/user1/files/foo.txt', false, true, false],
+ ['/user1/files_versions/foo.txt', false, false, true],
+ ];
+ }
+
+
+ public function testDecrypt(): void {
+ $this->expectException(DecryptionFailedException::class);
+ $this->expectExceptionMessage('Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.');
+
+ $this->instance->decrypt('abc');
+ }
+
+ public function testPrepareDecryptAll(): void {
+ /** @var \Symfony\Component\Console\Input\InputInterface $input */
+ $input = $this->createMock(InputInterface::class);
+ /** @var \Symfony\Component\Console\Output\OutputInterface $output */
+ $output = $this->createMock(OutputInterface::class);
+
+ $this->decryptAllMock->expects($this->once())->method('prepare')
+ ->with($input, $output, 'user');
+
+ $this->instance->prepareDecryptAll($input, $output, 'user');
+ }
+}