Skip to content

Commit e112900

Browse files
authored
add E2E improvements and Nextcloud31 test (#62)
1 parent 1f5b183 commit e112900

File tree

6 files changed

+91
-34
lines changed

6 files changed

+91
-34
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## v31.0.0 (2025-03-16)
4+
5+
* [add E2E improvements and Nextcloud31 test (#62)](https://github.com/nextcloud/encryption-recovery-tools/pull/62)
6+
37
## v30.0.0 (2025-03-16)
48

59
* [add support for metadata format version 2.0 (#51)](https://github.com/nextcloud/encryption-recovery-tools/pull/51)

end-to-end-encryption/recover.php

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -754,44 +754,64 @@ function decryptPrivateKey($file, $mnemonic) {
754754
$salt = base64_decode($parts[2]);
755755
$tag = substr(base64_decode($parts[0]), -TAGSIZE);
756756

757-
// derive actual secret
758-
$mnemonic = hash_pbkdf2("sha1",
759-
$mnemonic,
760-
$salt,
761-
1024,
762-
32,
763-
true);
764-
765-
// migrate GCM nonce to CTR counter,
766-
// we don't use GCM so that broken
767-
// integrity data do not break the
768-
// decryption
769-
$nonce = convertGCMtoCTR($nonce, $mnemonic, "aes-256-ecb");
770-
771-
// decrypt private key
772-
$privatekey = openssl_decrypt($ciphertext,
773-
"aes-256-ctr",
774-
$mnemonic,
775-
OPENSSL_RAW_DATA,
776-
$nonce);
777-
if (false !== $privatekey) {
778-
// base64-decode again just for good measure
779-
$privatekey = base64_decode($privatekey);
757+
// try to decrypt private key with different methods
758+
$methods = [["algorithm" => "sha256", "iterations" => 600000],
759+
["algorithm" => "sha1", "iterations" => 600000],
760+
["algorithm" => "sha1", "iterations" => 1024]];
761+
foreach ($methods as $method) {
762+
// take method parameters
763+
$algorithm = $method["algorithm"];
764+
$iterations = $method["iterations"];
765+
766+
// derive actual secret
767+
$secret = hash_pbkdf2($algorithm,
768+
$mnemonic,
769+
$salt,
770+
$iterations,
771+
32,
772+
true);
773+
774+
// migrate GCM nonce to CTR counter,
775+
// we don't use GCM so that broken
776+
// integrity data do not break the
777+
// decryption
778+
$counter = convertGCMtoCTR($nonce, $secret, "aes-256-ecb");
779+
780+
// decrypt private key
781+
$privatekey = openssl_decrypt($ciphertext,
782+
"aes-256-ctr",
783+
$secret,
784+
OPENSSL_RAW_DATA,
785+
$counter);
780786
if (false !== $privatekey) {
781-
$res = openssl_pkey_get_private($privatekey);
782-
if (is_resource($res) || ($res instanceof OpenSSLAsymmetricKey)) {
783-
$sslInfo = openssl_pkey_get_details($res);
784-
if (array_key_exists("key", $sslInfo)) {
785-
$result = $privatekey;
787+
// base64-decode again just for good measure
788+
$privatekey = base64_decode($privatekey);
789+
if (false !== $privatekey) {
790+
$res = openssl_pkey_get_private($privatekey);
791+
if (is_resource($res) || ($res instanceof OpenSSLAsymmetricKey)) {
792+
$sslInfo = openssl_pkey_get_details($res);
793+
if (array_key_exists("key", $sslInfo)) {
794+
$result = $privatekey;
795+
}
796+
} else {
797+
debug("decrypted content is not a privatekey");
786798
}
787799
} else {
788-
debug("decrypted content is not a privatekey");
800+
debug("decrypted content is not base64-encoded");
789801
}
790802
} else {
791-
debug("decrypted content is not base64-encoded");
803+
debug("privatekey could not be decrypted: ".openssl_error_string());
792804
}
793-
} else {
794-
debug("privatekey could not be decrypted: ".openssl_error_string());
805+
806+
// take a shortcut
807+
if (false !== $result) {
808+
break;
809+
}
810+
}
811+
812+
// if we do not have a result then print a debug message
813+
if (false === $result) {
814+
debug("privatekey is encrypted with an unsupported method");
795815
}
796816
} else {
797817
debug("privatekey file has wrong structure");
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
// use prepared test setup
3+
include_once(__DIR__."/main.php");
4+
5+
final class version20_sha256 extends main {
6+
const EXTERNAL_STORAGES = [];
7+
const SOURCEPATHS = [];
8+
const USER_MNEMONICS = ["admin" => "busy equip guard poverty music cabin promote label payment uncle arena toy"];
9+
const VERSION = "version20_sha256";
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
// use prepared test setup
3+
include_once(__DIR__."/main.php");
4+
5+
final class version20_strong_sha1 extends main {
6+
const EXTERNAL_STORAGES = [];
7+
const SOURCEPATHS = [];
8+
const USER_MNEMONICS = ["admin" => "tiny game must seminar legend give cloud figure pink bread diamond almost"];
9+
const VERSION = "version20_strong_sha1";
10+
}

tests/src/end-to-end-encryption/version20.php renamed to tests/src/end-to-end-encryption/version20_weak_sha1.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// use prepared test setup
33
include_once(__DIR__."/main.php");
44

5-
final class version20 extends main {
5+
final class version20_weak_sha1 extends main {
66
const EXTERNAL_STORAGES = [];
77
const SOURCEPATHS = [];
88
const USER_MNEMONICS = ["admin" => "drift company glass demise table grass skill master oval wait century kite"];
9-
const VERSION = "version20";
9+
const VERSION = "version20_weak_sha1";
1010
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
// use prepared test setup
3+
include_once(__DIR__."/main.php");
4+
5+
final class nextcloud31 extends main {
6+
const EXTERNAL_STORAGES = [];
7+
const INSTANCEID = "ocmphv6n7mfc";
8+
const RECOVERY_PASSWORD = "recovery";
9+
const SECRET = "xTnQZceVgHneA8OQ7nUcoCLioKkQiElsmzFZOBTv6tNW1WDp";
10+
const SOURCEPATHS = [];
11+
const USER_PASSWORDS = ["admin" => "admin"];
12+
const VERSION = "31.0.0";
13+
}

0 commit comments

Comments
 (0)