aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Wurst <christoph@winzerhof-wurst.at>2024-11-27 19:19:36 +0100
committerChristoph Wurst <christoph@winzerhof-wurst.at>2024-11-28 09:44:56 +0100
commite6738874a7343c5f55a53098487e207f64096210 (patch)
tree834f8acb860e2e63a4356269a4abb66ddc152360
parenta0ae6404cbd64b6ee1a22db31a1d53a500b42973 (diff)
downloadnextcloud-server-e6738874a7343c5f55a53098487e207f64096210.tar.gz
nextcloud-server-e6738874a7343c5f55a53098487e207f64096210.zip
fix(migration): Decrypt ownCloud secrets v2
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
-rw-r--r--lib/private/Security/Crypto.php32
-rw-r--r--tests/lib/Security/CryptoTest.php13
2 files changed, 42 insertions, 3 deletions
diff --git a/lib/private/Security/Crypto.php b/lib/private/Security/Crypto.php
index 033456f3f2e..2c42f2506af 100644
--- a/lib/private/Security/Crypto.php
+++ b/lib/private/Security/Crypto.php
@@ -135,6 +135,25 @@ class Crypto implements ICrypto {
throw new Exception('Authenticated ciphertext could not be decoded.');
}
+ /*
+ * Rearrange arguments for legacy ownCloud migrations
+ *
+ * The original scheme consistent of three parts. Nextcloud added a
+ * fourth at the end as "2" or later "3", ownCloud added "v2" at the
+ * beginning.
+ */
+ $originalParts = $parts;
+ $isOwnCloudV2Migration = $partCount === 4 && $originalParts[0] === 'v2';
+ if ($isOwnCloudV2Migration) {
+ $parts = [
+ $parts[1],
+ $parts[2],
+ $parts[3],
+ '2'
+ ];
+ }
+
+ // Convert hex-encoded values to binary
$ciphertext = $this->hex2bin($parts[0]);
$iv = $parts[1];
$hmac = $this->hex2bin($parts[2]);
@@ -145,7 +164,7 @@ class Crypto implements ICrypto {
$iv = $this->hex2bin($iv);
}
- if ($version === '3') {
+ if ($version === '3' || $isOwnCloudV2Migration) {
$keyMaterial = hash_hkdf('sha512', $password);
$encryptionKey = substr($keyMaterial, 0, 32);
$hmacKey = substr($keyMaterial, 32);
@@ -154,8 +173,15 @@ class Crypto implements ICrypto {
$this->cipher->setPassword($encryptionKey);
$this->cipher->setIV($iv);
- if (!hash_equals($this->calculateHMAC($parts[0] . $parts[1], $hmacKey), $hmac)) {
- throw new Exception('HMAC does not match.');
+ if ($isOwnCloudV2Migration) {
+ // ownCloud uses the binary IV for HMAC calculation
+ if (!hash_equals($this->calculateHMAC($parts[0] . $iv, $hmacKey), $hmac)) {
+ throw new Exception('HMAC does not match.');
+ }
+ } else {
+ if (!hash_equals($this->calculateHMAC($parts[0] . $parts[1], $hmacKey), $hmac)) {
+ throw new Exception('HMAC does not match.');
+ }
}
$result = $this->cipher->decrypt($ciphertext);
diff --git a/tests/lib/Security/CryptoTest.php b/tests/lib/Security/CryptoTest.php
index bdbad8b2610..80a2bf85d8a 100644
--- a/tests/lib/Security/CryptoTest.php
+++ b/tests/lib/Security/CryptoTest.php
@@ -89,6 +89,19 @@ class CryptoTest extends \Test\TestCase {
);
}
+ /**
+ * Test data taken from https://github.com/owncloud/core/blob/9deb8196b20354c8de0cd720ad4d18d52ccc96d8/tests/lib/Security/CryptoTest.php#L56-L60
+ */
+ public function testOcVersion2CiphertextDecryptsToCorrectPlaintext() {
+ $this->assertSame(
+ 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.',
+ $this->crypto->decrypt(
+ 'v2|d57dbe4d1317cdf19d4ddc2df807f6b5d63ab1e119c46590ce54bae56a9cd3969168c4ec1600ac9758dd7e7afb9c4c962dd23072c1463add1d9c77c467723b37bb768ef00e3c50898e59247cbb59ce56b74ce5990648ffe9e40d0e95076c27a785bdcf32c219ea4ad5c316b1f12f48c1|6bd21db258a5e406a2c288a444de195f|a19111a4cf1a11ee95fc1734699c20964eaa05bb007e1cecc4cc6872f827a4b7deedc977c13b138d728d68116aa3d82f9673e20c7e447a9788aa3be994b67cd6',
+ 'ThisIsAVeryS3cur3P4ssw0rd'
+ )
+ );
+ }
+
public function testVersion3CiphertextDecryptsToCorrectPlaintext() {
$this->assertSame(
'Another plaintext value that will be encrypted with version 3. It addresses the related key issue. Old ciphertexts should be decrypted properly, but only use the better version for encryption.',