66 * @author Björn Schießle <[email protected] > 77 * @author Christoph Wurst <[email protected] > 88 * @author Clark Tomlinson <[email protected] > 9+ * @author Côme Chilliet <[email protected] > 910 * @author Joas Schilling <[email protected] > 11+ * @author Kevin Niehage <[email protected] > 1012 * @author Lukas Reschke <[email protected] > 1113 * @author Morris Jobke <[email protected] > 1214 * @author Roeland Jago Douma <[email protected] > 4042use OCP \IL10N ;
4143use OCP \ILogger ;
4244use OCP \IUserSession ;
45+ use phpseclib \Crypt \RC4 ;
4346
4447/**
4548 * Class Crypt provides the encryption implementation of the default Nextcloud
@@ -517,12 +520,9 @@ public function symmetricDecryptFileContent($keyFileContents, $passPhrase, $ciph
517520 /**
518521 * check for valid signature
519522 *
520- * @param string $data
521- * @param string $passPhrase
522- * @param string $expectedSignature
523523 * @throws GenericEncryptionException
524524 */
525- private function checkSignature ($ data , $ passPhrase , $ expectedSignature ) {
525+ private function checkSignature (string $ data , string $ passPhrase , string $ expectedSignature ): void {
526526 $ enforceSignature = !$ this ->config ->getSystemValueBool ('encryption_skip_signature_check ' , false );
527527
528528 $ signature = $ this ->createSignature ($ data , $ passPhrase );
@@ -695,9 +695,9 @@ public function generateFileKey() {
695695 }
696696
697697 /**
698- * @param $encKeyFile
699- * @param $shareKey
700- * @param $privateKey
698+ * @param string $encKeyFile
699+ * @param string $shareKey
700+ * @param \OpenSSLAsymmetricKey|\OpenSSLCertificate|array|string $privateKey
701701 * @return string
702702 * @throws MultiKeyDecryptException
703703 */
@@ -706,7 +706,8 @@ public function multiKeyDecrypt($encKeyFile, $shareKey, $privateKey) {
706706 throw new MultiKeyDecryptException ('Cannot multikey decrypt empty plain content ' );
707707 }
708708
709- if (openssl_open ($ encKeyFile , $ plainContent , $ shareKey , $ privateKey , 'RC4 ' )) {
709+ $ plainContent = '' ;
710+ if ($ this ->opensslOpen ($ encKeyFile , $ plainContent , $ shareKey , $ privateKey , 'RC4 ' )) {
710711 return $ plainContent ;
711712 } else {
712713 throw new MultiKeyDecryptException ('multikeydecrypt with share key failed: ' . openssl_error_string ());
@@ -731,7 +732,7 @@ public function multiKeyEncrypt($plainContent, array $keyFiles) {
731732 $ shareKeys = [];
732733 $ mappedShareKeys = [];
733734
734- if (openssl_seal ($ plainContent , $ sealed , $ shareKeys , $ keyFiles , 'RC4 ' )) {
735+ if ($ this -> opensslSeal ($ plainContent , $ sealed , $ shareKeys , $ keyFiles , 'RC4 ' )) {
735736 $ i = 0 ;
736737
737738 // Ensure each shareKey is labelled with its corresponding key id
@@ -749,7 +750,105 @@ public function multiKeyEncrypt($plainContent, array $keyFiles) {
749750 }
750751 }
751752
753+ /**
754+ * returns the value of $useLegacyBase64Encoding
755+ *
756+ * @return bool
757+ */
752758 public function useLegacyBase64Encoding (): bool {
753759 return $ this ->useLegacyBase64Encoding ;
754760 }
761+
762+ /**
763+ * Uses phpseclib RC4 implementation
764+ */
765+ private function rc4Decrypt (string $ data , string $ secret ): string {
766+ $ rc4 = new RC4 ();
767+ /** @psalm-suppress InternalMethod */
768+ $ rc4 ->setKey ($ secret );
769+
770+ return $ rc4 ->decrypt ($ data );
771+ }
772+
773+ /**
774+ * Uses phpseclib RC4 implementation
775+ */
776+ private function rc4Encrypt (string $ data , string $ secret ): string {
777+ $ rc4 = new RC4 ();
778+ /** @psalm-suppress InternalMethod */
779+ $ rc4 ->setKey ($ secret );
780+
781+ return $ rc4 ->encrypt ($ data );
782+ }
783+
784+ /**
785+ * Custom implementation of openssl_open()
786+ *
787+ * @param \OpenSSLAsymmetricKey|\OpenSSLCertificate|array|string $private_key
788+ * @throws DecryptionFailedException
789+ */
790+ private function opensslOpen (string $ data , string &$ output , string $ encrypted_key , $ private_key , string $ cipher_algo ): bool {
791+ $ result = false ;
792+
793+ // check if RC4 is used
794+ if (strcasecmp ($ cipher_algo , "rc4 " ) === 0 ) {
795+ // decrypt the intermediate key with RSA
796+ if (openssl_private_decrypt ($ encrypted_key , $ intermediate , $ private_key , OPENSSL_PKCS1_PADDING )) {
797+ // decrypt the file key with the intermediate key
798+ // using our own RC4 implementation
799+ $ output = $ this ->rc4Decrypt ($ data , $ intermediate );
800+ $ result = (strlen ($ output ) === strlen ($ data ));
801+ }
802+ } else {
803+ throw new DecryptionFailedException ('Unsupported cipher ' .$ cipher_algo );
804+ }
805+
806+ return $ result ;
807+ }
808+
809+ /**
810+ * Custom implementation of openssl_seal()
811+ *
812+ * @throws EncryptionFailedException
813+ */
814+ private function opensslSeal (string $ data , string &$ sealed_data , array &$ encrypted_keys , array $ public_key , string $ cipher_algo ): int |false {
815+ $ result = false ;
816+
817+ // check if RC4 is used
818+ if (strcasecmp ($ cipher_algo , "rc4 " ) === 0 ) {
819+ // make sure that there is at least one public key to use
820+ if (count ($ public_key ) >= 1 ) {
821+ // generate the intermediate key
822+ $ intermediate = openssl_random_pseudo_bytes (16 , $ strong_result );
823+
824+ // check if we got strong random data
825+ if ($ strong_result ) {
826+ // encrypt the file key with the intermediate key
827+ // using our own RC4 implementation
828+ $ sealed_data = $ this ->rc4Encrypt ($ data , $ intermediate );
829+ if (strlen ($ sealed_data ) === strlen ($ data )) {
830+ // prepare the encrypted keys
831+ $ encrypted_keys = [];
832+
833+ // iterate over the public keys and encrypt the intermediate
834+ // for each of them with RSA
835+ foreach ($ public_key as $ tmp_key ) {
836+ if (openssl_public_encrypt ($ intermediate , $ tmp_output , $ tmp_key , OPENSSL_PKCS1_PADDING )) {
837+ $ encrypted_keys [] = $ tmp_output ;
838+ }
839+ }
840+
841+ // set the result if everything worked fine
842+ if (count ($ public_key ) === count ($ encrypted_keys )) {
843+ $ result = strlen ($ sealed_data );
844+ }
845+ }
846+ }
847+ }
848+ } else {
849+ throw new EncryptionFailedException ('Unsupported cipher ' .$ cipher_algo );
850+ }
851+
852+ return $ result ;
853+ }
755854}
0 commit comments