Одноразовое шифрование - PullRequest
0 голосов
/ 09 марта 2019

Я знаю, что это старая тема, но я немного поиграл в мою маленькую OTP-библиотеку, и я хотел бы попросить вас совета.Все идеально, но я уверен, что ни один администратор не хочет видеть что-то вроде "php_value memory_limit 500000M": D.

Я не собираюсь изобретать колесо, но я действительно пытался найти библиотеку для шифрования данных, и яне будут удовлетворены AES, mcrypt и т. д., потому что нет 100% безопасности, если размер зашифрованных данных меньше размера ключа.Я буду очень рад, если кто-то покажет мне правильное направление.

Моя библиотека работает отлично, но похоже, что для файла размером 1 ГБ мне понадобится как минимум одна серверная комната;) И поскольку я работаю над коммерческим решением,с "немного" более высоким уровнем безопасности, я не буду удовлетворен только другой библиотекой.

Большое спасибо за все ответы.

Так вот оно:

    <?php

/** OtpFile - One time pad base64 file encryption
* @author Tomas Stofik, https://www.tomasstofik.com/
* @copyright 2018 Tomas Stofik
*/

final class OtpFile
{

    private static $charSet = array(
        '+','/','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G',
        'H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
        't','u','v','w','x','y','z'
    );

    public static function encryptFile(
        $originalFilePath, 
        $encryptedFilePath, 
        $keyFilePath)
    {

        if(!self::existsFile($keyFilePath) || !self::existsFile($encryptedFilePath)) {

            if($originalFileData = self::existsFile($originalFilePath)) {

                $originalFileBase64Data = base64_encode($originalFileData);

                $originalFileBase64DataLength = strlen($originalFileBase64Data) - 1;

                $originalFileBase64DataArray = str_split($originalFileBase64Data);

                $encryptedData = NULL;

                $encryptedDataKey = NULL;

                for ($i=0; $i <= $originalFileBase64DataLength; $i++) {

                    $randKey = rand(0, sizeOf(self::$charSet) - 1);

                    $arrayKey = array_search(
                        $originalFileBase64DataArray[$i], 
                        self::$charSet
                    );

                    if($randKey > $arrayKey) {
                        $str='-'.($randKey - $arrayKey);
                    } elseif($randKey < $arrayKey) {
                        $str = ($randKey + $arrayKey);
                    } else {
                        $str = $randKey;
                    }

                    $encryptedData .= self::$charSet[$randKey];

                    $encryptedDataKey .= $str.';';

                }

                $encryptedDataString = $encryptedData;

                $encryptedDataKeyString = $encryptedDataKey;

                if(!self::existsFile($keyFilePath)) {
                    file_put_contents($keyFilePath, $encryptedDataKeyString);
                }

                if(!self::existsFile($encryptedFilePath)) {
                    file_put_contents($encryptedFilePath, $encryptedDataString);
                }

                return 'OK';

            } else {

                return 'Source file not exists';

            }

        } else {

            return 'Encrypted data already exists';

        }

    }

    public static function decryptFile(
        $encryptedFilePath, 
        $keyFilePath, 
        $decryptedFilePath)
    {

        $keyFileData = self::existsFile($keyFilePath);

        $encryptedFileData = self::existsFile($encryptedFilePath);

        $encryptedFileDataLength = strlen($encryptedFileData) - 1;

        if($encryptedFileData && $keyFileData) {

            $encryptedFileDataArray = str_split($encryptedFileData);

            $keyFileDataArray = explode(';',$keyFileData);

            $decryptedData = NULL;

            for ($i=0; $i <= $encryptedFileDataLength; $i++) {

                $positionCurrent = array_search($encryptedFileDataArray[$i], self::$charSet);

                $positionEncrypted = $keyFileDataArray[$i];

                if ($positionEncrypted == $positionCurrent) {
                    $move = $positionEncrypted;
                } elseif($positionEncrypted < 0) {
                    $move=$positionEncrypted + $positionCurrent;
                } elseif($positionEncrypted > 0) {
                    $move=$positionEncrypted - $positionCurrent;
                } else {
                    $move='0';
                }

                $decryptedData .= self::$charSet[$move];

            }

            if(!self::existsFile($decryptedFilePath)) {

                file_put_contents(
                    $decryptedFilePath, 
                    base64_decode(
                        $decryptedData
                    )
                );

                return 'OK';

            } else {

                return 'Decrypted data already exists';

            }

        }

    }

    private static function existsFile($filePath)
    {

        $fileData = @file_get_contents($filePath);

        if($fileData) {

            return $fileData;

        }

        return FALSE;

    }

}

/* Using

$originalFilePath = 'original.jpg';
$keyFilePath = 'Otp_Key_' . $originalFilePath;
$encryptedFilePath = 'Otp_Data_' . $originalFilePath;
$decryptedFilePath = 'Otp_Decrypted_' . $originalFilePath;

echo OtpFile::encryptFile($originalFilePath, $encryptedFilePath, $keyFilePath);
echo OtpFile::decryptFile($encryptedFilePath, $keyFilePath, $decryptedFilePath);

*/

1 Ответ

2 голосов
/ 09 марта 2019

Это очень плохая реализация одноразового пэда. Это связано с рядом серьезных проблем, как криптографических, так и практических.

  1. rand() не является источником криптографически безопасных случайных чисел. В зависимости от того, в какой операционной системе вы используете PHP, он может иметь менее 32 бит состояния, что делает его довольно простым для обратного проектирования его состояния и дешифрования файла без ключа.

  2. Придуманный вами процесс, в котором зашифрованные данные преобразуются в Base64 перед объединением со случайными данными, является криптологически ненужным и служит только для массового увеличения размера зашифрованных файлов. (Если моя математика верна, то «ключ с зашифрованными данными» может быть в 2,6–8 раз больше, чем исходные данные.)

  3. Ваша терминология для «зашифрованных данных» и «ключа зашифрованных данных» на самом деле в обратном направлении . Файл «зашифрованные данные» фактически содержит случайные данные, а «ключ зашифрованных данных» содержит данные, полученные из исходного файла.

  4. В вашем процессе "шифрования" присутствует криптографическая ошибка. Поскольку исходные данные и «случайный ключ» являются положительными целыми числами, но их разность сохраняется как целое число со знаком , можно вывести некоторую информацию о старших битах исходных данных из вывода (в файл «ключа зашифрованных данных»).

  5. Чрезвычайный размер файла ключа делает нецелесообразным надежное хранение ключа. Это фундаментальный недостаток любой схемы шифрования с одноразовым блокнотом, и это основная причина, по которой вы должны использовать вместо этого стандартный блочный шифр.

Одноразовые пэды не являются подходящей схемой шифрования для большинства приложений. Однако, если вы не готовы к их реализации, вот основа более подходящей реализации:

$key_data = random_bytes(strlen($cleartext_data));
$encrypted_data = $cleartext_data ^ $key_data;
…
$cleartext_data = $encrypted_data ^ $key_data;

Нет необходимости конвертировать ваши данные в Base64 в любой момент процесса.

...