33
33
34
34
use ArtificialOwl \MySmallPhpTools \Traits \TArrayTools ;
35
35
use Exception ;
36
- use OCA \Backup \Exceptions \EncryptException ;
37
36
use OCA \Backup \Exceptions \EncryptionKeyException ;
38
37
use OCA \Backup \Exceptions \PackDecryptException ;
39
38
use OCA \Backup \Exceptions \PackEncryptException ;
@@ -60,8 +59,11 @@ class EncryptService {
60
59
61
60
public const CHACHA = 'chacha ' ;
62
61
62
+ public const STRING = 'string ' ;
63
+ public const STRING_NONCE = 'string-nonce ' ;
63
64
64
- public static $ LIST = [
65
+
66
+ public static $ EXPORT = [
65
67
self ::AES_GCM ,
66
68
self ::AES_GCM_NONCE ,
67
69
self ::AES_CBC ,
@@ -83,22 +85,29 @@ public function __construct(ConfigService $configService) {
83
85
84
86
85
87
/**
86
- * @param string $data
88
+ * @param string $plain
87
89
* @param string $key
88
90
*
89
91
* @return string
90
- * @throws SodiumException
92
+ * @throws EncryptionKeyException
93
+ * @throws PackEncryptException
91
94
*/
92
- public function encryptString (string $ data , string $ key ): string {
93
- try {
94
- $ nonce = random_bytes (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
95
- } catch (Exception $ e ) {
96
- throw new SodiumException ('random_bytes - ' . $ e ->getMessage ());
95
+ public function encryptString (string $ plain , string &$ key = '' ): string {
96
+ $ key = $ this ->getEncryptionKey (self ::STRING , false );
97
+ $ nonce = $ this ->getEncryptionKey (self ::STRING_NONCE , false );
98
+ $ encrypted = openssl_encrypt (
99
+ $ plain ,
100
+ self ::AES_CBC ,
101
+ $ key ,
102
+ OPENSSL_RAW_DATA ,
103
+ $ nonce
104
+ );
105
+
106
+ if (!$ encrypted ) {
107
+ throw new PackEncryptException ('data were not encrypted ' );
97
108
}
98
109
99
- $ encrypted = $ nonce . sodium_crypto_secretbox ($ data , $ nonce , $ key );
100
- sodium_memzero ($ data );
101
- sodium_memzero ($ key );
110
+ $ key = base64_encode ($ key ) . '. ' . base64_encode ($ nonce );
102
111
103
112
return base64_encode ($ encrypted );
104
113
}
@@ -109,37 +118,25 @@ public function encryptString(string $data, string $key): string {
109
118
* @param string $key
110
119
*
111
120
* @return string
112
- * @throws EncryptException
113
- * @throws SodiumException
121
+ * @throws PackDecryptException
114
122
*/
115
123
public function decryptString (string $ encrypted , string $ key ): string {
116
- $ key = base64_decode ($ key );
117
-
118
- if ($ encrypted === false ) {
119
- throw new EncryptException ('invalid data ' );
120
- }
121
-
122
- if (mb_strlen ($ encrypted , '8bit ' ) < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES
123
- + SODIUM_CRYPTO_SECRETBOX_MACBYTES )) {
124
- throw new EncryptException ('invalid data ' );
125
- }
126
-
127
- $ nonce = mb_substr ($ encrypted , 0 , SODIUM_CRYPTO_SECRETBOX_NONCEBYTES , '8bit ' );
128
- $ ciphertext = mb_substr ($ encrypted , SODIUM_CRYPTO_SECRETBOX_NONCEBYTES , null , '8bit ' );
124
+ [$ k , $ n ] = explode ('. ' , $ key , 2 );
125
+ $ key = base64_decode ($ k );
126
+ $ nonce = base64_decode ($ n );
129
127
130
- $ plain = sodium_crypto_secretbox_open (
131
- $ ciphertext ,
132
- $ nonce ,
133
- $ key
128
+ $ plain = openssl_decrypt (
129
+ $ encrypted ,
130
+ self ::AES_CBC ,
131
+ $ key ,
132
+ OPENSSL_RAW_DATA ,
133
+ $ nonce
134
134
);
135
135
136
136
if ($ plain === false ) {
137
- throw new EncryptException ( ' invalid data ' );
137
+ throw new PackDecryptException ( ' cannot decrypt data ' );
138
138
}
139
139
140
- sodium_memzero ($ ciphertext );
141
- sodium_memzero ($ key );
142
-
143
140
return $ plain ;
144
141
}
145
142
@@ -277,7 +274,7 @@ public function decryptFile(string $input, string $output, string $name, string
277
274
$ this ->decryptFileGCM ($ input , $ output , $ name );
278
275
break ;
279
276
case self ::AES_CBC :
280
- $ this ->decryptFileOpenSSL ($ input , $ output );
277
+ $ this ->decryptFileCBC ($ input , $ output );
281
278
break ;
282
279
}
283
280
}
@@ -323,7 +320,7 @@ public function decryptFileGCM(string $input, string $output, string $name): voi
323
320
* @throws PackDecryptException
324
321
* @throws SodiumException
325
322
*/
326
- public function decryptFileOpenSSL (string $ input , string $ output ): void {
323
+ public function decryptFileCBC (string $ input , string $ output ): void {
327
324
$ key = base64_decode ($ this ->getEncryptionKey (self ::AES_CBC ));
328
325
$ nonce = base64_decode ($ this ->getEncryptionKey (self ::AES_CBC_IV ));
329
326
$ write = fopen ($ output , 'wb ' );
@@ -382,7 +379,7 @@ public function decryptFileChacha(string $input, string $output): void {
382
379
*/
383
380
public function getEncryptionKeys (bool $ generate = false ): array {
384
381
if ($ generate ) {
385
- foreach (self ::$ LIST as $ item ) {
382
+ foreach (self ::$ EXPORT as $ item ) {
386
383
try {
387
384
$ this ->getEncryptionKey ($ item );
388
385
} catch (EncryptionKeyException $ e ) {
@@ -402,7 +399,7 @@ public function getEncryptionKeys(bool $generate = false): array {
402
399
/**
403
400
* @throws EncryptionKeyException
404
401
*/
405
- public function getEncryptionKey (string $ type ): string {
402
+ public function getEncryptionKey (string $ type, bool $ storeIt = true ): string {
406
403
$ keys = $ this ->getEncryptionKeys ();
407
404
408
405
$ key = $ this ->get ($ type , $ keys );
@@ -413,8 +410,10 @@ public function getEncryptionKey(string $type): string {
413
410
throw new EncryptionKeyException ($ e ->getMessage ());
414
411
}
415
412
416
- $ keys [$ type ] = $ key ;
417
- $ this ->configService ->setAppValue (ConfigService::ENCRYPTION_KEYS , json_encode ($ keys ));
413
+ if ($ storeIt ) {
414
+ $ keys [$ type ] = $ key ;
415
+ $ this ->configService ->setAppValue (ConfigService::ENCRYPTION_KEYS , json_encode ($ keys ));
416
+ }
418
417
}
419
418
420
419
return $ key ;
@@ -428,6 +427,10 @@ public function getEncryptionKey(string $type): string {
428
427
* @throws Exception
429
428
*/
430
429
private function generateKey (string $ type ): string {
430
+ if (!$ this ->isSodiumAvailable ()) {
431
+ return $ this ->generateKeyNative ($ type );
432
+ }
433
+
431
434
switch ($ type ) {
432
435
case self ::CHACHA :
433
436
return base64_encode (random_bytes (SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES ));
@@ -437,6 +440,34 @@ private function generateKey(string $type): string {
437
440
case self ::AES_CBC_IV :
438
441
case self ::AES_GCM_NONCE :
439
442
return base64_encode (random_bytes (SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES ));
443
+ case self ::STRING :
444
+ return base64_encode (random_bytes (SODIUM_CRYPTO_SECRETBOX_KEYBYTES ));
445
+ case self ::STRING_NONCE :
446
+ return base64_encode (random_bytes (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES ));
447
+ }
448
+
449
+ throw new EncryptionKeyException ('unknown key type ' );
450
+ }
451
+
452
+
453
+ /**
454
+ * @param string $type
455
+ *
456
+ * @return string
457
+ * @throws EncryptionKeyException
458
+ */
459
+ private function generateKeyNative (string $ type ): string {
460
+ switch ($ type ) {
461
+ case self ::CHACHA :
462
+ case self ::AES_GCM :
463
+ case self ::AES_CBC :
464
+ case self ::STRING :
465
+ return base64_encode (random_bytes (32 ));
466
+ case self ::AES_CBC_IV :
467
+ case self ::AES_GCM_NONCE :
468
+ return base64_encode (random_bytes (12 ));
469
+ case self ::STRING_NONCE :
470
+ return base64_encode (random_bytes (24 ));
440
471
}
441
472
442
473
throw new EncryptionKeyException ('unknown key type ' );
@@ -451,6 +482,17 @@ private function useSodiumCryptoAead(): bool {
451
482
return false ;
452
483
}
453
484
485
+ if (!$ this ->isSodiumAvailable ()) {
486
+ return false ;
487
+ }
488
+
454
489
return sodium_crypto_aead_aes256gcm_is_available ();
455
490
}
491
+
492
+ /**
493
+ * @return bool
494
+ */
495
+ private function isSodiumAvailable (): bool {
496
+ return extension_loaded ('sodium ' );
497
+ }
456
498
}
0 commit comments