PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » PureBasic для Windows » Кодировать / декодировать файл


Кодировать / декодировать файл

Сообщений 1 страница 30 из 46

1

На оф.форуме выложил код небольшой прожки с GUI.
https://i.imgur.com/RKKtgiv.png
Ранее я писал это на AutoIt3, просто переписал на PureBasic в связи с поднятием темы шифрования на оф.форуме.
Из нового, в AutoIt3 была функция с паролем, а тут надо было пароль 16 байт и вектор 16 байт, пришлось писать функцию, которая имеет неявный пароль генерируемый рандомом с RandomSeed, а поверх "рандома" писать пароль из поля в GUI, если будет не хватать длины, она будет дополнена "рандомом" до 16 байт. Перечитал справку, 16 байт для 128 битного ключа, так что мне надо либо битность исправить, либо  увеличить ключ, но это уже мелочи, при тесте всё работает как есть.

0

2

AZJIO написал(а):

На оф.форуме выложил код небольшой прожки с GUI.

Файл читается целиком в память. Если он имеет большой объем может не получится выделить столько памяти. Лучше читать частями, скажем по 10 МБ и выполнять шифрование функциями StartAESCipher(), AddCipherBuffer() и FinishCipher().

AZJIO написал(а):

Из нового, в AutoIt3 была функция с паролем, а тут надо было пароль 16 байт и вектор 16 байт, пришлось писать функцию, которая имеет неявный пароль генерируемый рандомом с RandomSeed, а поверх "рандома" писать пароль из поля в GUI

Я не знаю как устроен AES в AutoIt3. Может он работает в режиме Electronic CodeBook при котором InitializationVector не требуется, но качество шифрования ниже.

С паролем можно сделать так.
Без InitializationVector

Код:
UseSHA1Fingerprint()

; Шифруем строку
String$ = "Здравствуйте, это тест для AES"
Pass.s = "1234" ; Пароль шифрования.

StringMemorySize = StringByteLength(String$) + SizeOf(Character)
*CipheredString = AllocateMemory(StringMemorySize)   
*DecipheredString = AllocateMemory(StringMemorySize) 

*Key = Ascii(StringFingerprint(Pass, #PB_Cipher_SHA1, 0, #PB_Unicode))
ShowMemoryViewer(*Key, MemorySize(*Key))

If AESEncoder(@String$, *CipheredString, StringByteLength(String$), *Key, 256, 0, #PB_Cipher_ECB)
  Debug "Ciphered: "+PeekS(*CipheredString) ; Предупреждение, это остановится на первом нулевом байте, только для демонстрации.
  
  AESDecoder(*CipheredString, *DecipheredString, StringByteLength(String$), *Key, 256, 0, #PB_Cipher_ECB)
  Debug "Deciphered: "+PeekS(*DecipheredString)
EndIf

FreeMemory(*CipheredString)
FreeMemory(*DecipheredString)
FreeMemory(*Key)

С InitializationVector

Код:
UseSHA1Fingerprint()
UseMD5Fingerprint()

; Шифруем строку
String$ = "Здравствуйте, это тест для AES"
Pass.s = "1234" ; Пароль шифрования.

StringMemorySize = StringByteLength(String$) + SizeOf(Character)
*CipheredString = AllocateMemory(StringMemorySize)   
*DecipheredString = AllocateMemory(StringMemorySize) 

*Key = Ascii(StringFingerprint(Pass, #PB_Cipher_SHA1, 0, #PB_Unicode))
*InitializationVector = Ascii(StringFingerprint(Pass, #PB_Cipher_MD5, 0, #PB_Unicode))
If AESEncoder(@String$, *CipheredString, StringByteLength(String$), *Key, 256, *InitializationVector, #PB_Cipher_CBC)
  Debug "Ciphered: "+PeekS(*CipheredString) ; Предупреждение, это остановится на первом нулевом байте, только для демонстрации.
  
  AESDecoder(*CipheredString, *DecipheredString, StringByteLength(String$), *Key, 256, *InitializationVector, #PB_Cipher_CBC)
  Debug "Deciphered: "+PeekS(*DecipheredString)
EndIf

FreeMemory(*CipheredString)
FreeMemory(*DecipheredString)
FreeMemory(*Key)
FreeMemory(*InitializationVector)

Здесь используется ascii строка хеша, а лучше перевести в бинарные данные, заменив SHA1 на SHA2 или SHA3 с 256 битным результатом.

0

3

Пётр
С SHA3 более проще. Была идея, если открыть путь, то зашифровать все файлы в папке. И добавить поддержку ком.строки, чтобы в конт.меню проводника можно было добавить. Про открывать блоками я знал, в AutoIt3 так и делал, но для MD5.

0

4

Чем вас не устроил "Исключающее или"?! Там можно для каждого файла гонять ключ в виде файла и получать результат.

0

5

У Aes уровень шифрования выше.

0

6

Код:
InputFile$=OpenFileRequester("Выберите файл для шифровки/дешифровки.","","",0)
InputFile=ReadFile(#PB_Any,InputFile$)
If InputFile=0:End:EndIf
KeyFile$=OpenFileRequester("Выберите файл-ключ.","","",0)
KeyFile=ReadFile(#PB_Any,KeyFile$)
If KeyFile=0:End:EndIf
*Key.Ascii=AllocateMemory(Lof(KeyFile))
If	ReadData(KeyFile,*Key,Lof(KeyFile))<>Lof(KeyFile):End:EndIf
OutputFile$=SaveFileRequester("Выберите файл-результат.","","",0)
OutputFile=CreateFile(#PB_Any,OutputFile$)
If OutputFile=0:End:EndIf
*k.Ascii=*Key
*Overflow=*k+Lof(KeyFile)
While Eof(InputFile)=0
	WriteAsciiCharacter(OutputFile,ReadAsciiCharacter(InputFile)!*k\a)
	*k+SizeOf(Ascii)
	If *k=*Overflow:*k=*Key:EndIf
	Wend

Не знаю... Что-то подобное я уже писал, вот и набросал то чё помню. Принцип простой, но для ключа надо выделять память. Можно конечно и из файла напрямую читать и бегать до начала файла, но так-быстрее.

0

7

PSY написал(а):

"Исключающее или"?!

Я и так знал, что степень защищенности максимальная, даже закон у нас есть проги шифрования с ключом не выше 128, иначе даже супер комп не расшифрует. Так на хабре статья, что шифрование блоками и предыдущий блок содержит ключ возможно вектор это и есть он для следующего блока, то есть не имея какой то части невозможно расшифровать последующее. Исключающее или это уровень детсада.

0

8

Зато можно просто выбрать входной файл, выбрать файл-ключ например изображение(щёлкнуть "фотку") и ей шифрануть чё хочешь. Работает же, Там даже больше уровня детского сада - если размер ключа равен размеру входного файла.

0

9

Код:
ReadFile(0,OpenFileRequester("Select input.","","",0))
ReadFile(1,OpenFileRequester("Select key.","","",0))
CreateFile(2,SaveFileRequester("Select output.","","",0))
While Eof(0)=0
	WriteAsciiCharacter(2,ReadAsciiCharacter(0)!ReadAsciiCharacter(1))
	If Eof(1)=0:FileSeek(1,0):EndIf:Wend

С минимальным потреблением памяти то есть ключ-файл быть может любой длины. Всё зависит только от накопителя и его размера. Сам воспользовался и не пожалел.

0

10

PSY написал(а):

Зато можно просто выбрать входной файл, выбрать файл-ключ например изображение(щёлкнуть "фотку") и ей шифрануть чё хочешь. Работает же, Там даже больше уровня детского сада - если размер ключа равен размеру входного файла.

Если используется XOR и и известна начальная последовательность байтов(для PNG/JPG), то можно вычислить и ключ и все такое.

0

11

Ну попробуй вычисли. Я щёлкни две фотки - одна истинная, вторая-ключ и пришлю тебе зашифрованный файл, а ты мне вышли результат который у меня истинный чтобы он совпал.

Отредактировано PSY (19.04.2025 18:25:35)

0

12

PSY
Ну не будете же вы с собой картинку таскать, теряется весь смысл пароля.

0

13

А если у меня много изображений то я могу просто знать какое из них - ключ. Или Музыка и видео. Кто мешает это использовать во качестве ключа.

0

14

PSY написал(а):

Ну попробуй вычисли. Я щёлкни две фотки - одна истинная, вторая-ключ и пришлю тебе зашифрованный файл, а ты мне вышли результат который у меня истинный чтобы он совпал.

Больно ты нужен что-то доказывать.

0

15

egons
Его вариант реальный, так как текстовая книга будет максимум 2 Мб, а тут на неё "картинку наложи" размером 4 Мб. По смыслу получится как шифровка, с разницей что каждый символ будет свой собственный, даже "а" в позиции 5 и "а" в позиции 15 будет ни одним и тем же символом, тут даже повторений не будет. Одно случайное число "сольётся" с другим случайным числом. По сути каждая буква-позиция имеет свой код. Нет даже алгоритма расшифровки такого.

Только алгоритм, который мы обсуждаем имеет короткий пароль, а метод с картинкой это необходимость иметь картинку.

Отредактировано AZJIO (19.04.2025 23:35:12)

0

16

AZJIO написал(а):

Его вариант реальный, так как текстовая книга будет максимум 2 Мб, а тут на неё "картинку наложи" размером 4 Мб.

Объясните мне, скудоумному, на кой ляд понадобились такие громадные картинки с громадными ключами шифрования?

0

17

Ну дык можно вообще использовать не только изображение во качестве ключа шифровки/расшифровки, а можно даже выбрать что угодно, даже какой-нибудь фильм или любой файл. Смысл исключающего ИЛИ в самом надёжном варианте - это размер ключа равен размеру входных данных или размер ключа более размера входных данных. Тогда результат шифрования - будет очень надёжным.

Отредактировано PSY (20.04.2025 12:19:20)

0

18

Пётр
Сделал чтение по 10 Мб.

Обновление
Добавлена командная строка, для добавления программы в контекстное меню файлового менеджера.
Добавлено шифрование блоками по 10 Мб, не загружая весь файл в память.
Изменён формат ключа - хеш-сумма, вместо генерации байтов.
Исходник для Linux пока от предыдущей версии

0

19

AZJIO написал(а):

Исключающее или это уровень детсада

Шифрование - интересная тема, но и опасная тоже. Поиграешь с ним, а потом получается программа шифрования, после которой и сам уже не знаешь, как расшифровать текст, или бинарный файл, не зная входных параметров для шифрования.

И главное, даже зная, как я его зашифровал с несложным алгоритмом, потом сложно придумать алгоритм расшифровки, который бы даже на суперкомпьютере расшифровал бы полученный результат, потому что и для брутфорса нужно же с чем-нибудь сравнивать - получилась ли расшифровка в текущем варианте.

Так вот, играя с шифрованием можно получить результат, который потом какой-нибудь гад сможет использовать для злых дел. А ты только и сможешь потом рассказать свой алгоритм шифрования, а как расшифровать такую переписку, или такие файлы, и сам не придумаешь.

Тем более, что без проблем ведь в такой программе шифрования поставить еще один параметр: количество циклов шифрования. И все. На что тут потом натравливать суперкомпьютер для расшифровки, с чем ему сравнивать полученные переборы вариантов - всё равно получается сплошная бинарная каша и довольно равномерная. А если знаешь входные параметры для шифрования - то секунда работы, и текст/файл расшифрован.

Интересная тема, но похожая на изготовление чего-то опасного. Как у тех физиков в 30-е годах, которым было просто интересно, получится ли у них самоподдерживающаяся реакция расщепления атомов. Получилась. То-то обрадовались этому потом жители Хиросимы.

Отредактировано Nemo3001 (20.04.2025 15:27:55)

0

20

Не увидел в коде функцию FinishCipher(), а должна быть.
Все же лучше переводить строку в бинарный вид о чем я писал выше. Это увеличит стойкость шифрования.
Сейчас в ключе и векторе могут быть только цифры от 0 до 9 и буквы от a до f.

Код:
UseMD5Fingerprint()

MD5.s = StringFingerprint("1234", #PB_Cipher_MD5, 0, #PB_Unicode)
Debug MD5

*MD5 = Ascii(MD5)
ShowMemoryViewer(*MD5, MemorySize(*MD5))

В бинарном виде будет весь диапазон значений байта - 0 - 255.

Код:
UseMD5Fingerprint()

Procedure HexToBin(s.s)
  Protected *Point=0, Pos=0, i, Count
   
  Count = Len(s)
  
  If Count > 0
    If Count % 2 <> 0 : Debug "Не кратно 2!!!! Возможна запись за пределы выделенной памяти!!!!" : EndIf
    *Point = AllocateMemory(Count / 2)
    If *Point
      For i=1 To Count Step 2
        PokeA(*Point+Pos, Val("$"+Mid(s, i, 2)))
        Pos+1
      Next
    EndIf
  EndIf
  
  ProcedureReturn *Point
EndProcedure

MD5.s = StringFingerprint("1234", #PB_Cipher_MD5, 0, #PB_Unicode)
Debug MD5

*MD5 = HexToBin(MD5)
ShowMemoryViewer(*MD5, MemorySize(*MD5))

0

21

Пётр написал(а):

Не увидел в коде функцию FinishCipher(), а должна быть.

Я так увлёкся победой, что просто забыл про неё.

Пётр написал(а):

HexToBin

а у меня уже есть её аналог BinaryHex() с посимвольным разбором (средняя), только там поменять нужно для шестнадцатеричных нижнего регистра.

#PB_Cipher_MD5 возвращает длину 18, #PB_Cipher_SHA3 - 34, даже в этом случае хватает только на пароль, а вектор либо дублировать пароль, либо ещё что-то придумывать (34 * 8 = 272).

Перезалил с исправлениями.

Отредактировано AZJIO (20.04.2025 19:02:40)

0

22

AZJIO написал(а):

#PB_Cipher_MD5 возвращает длину 18

16 байт (128 бит).

AZJIO написал(а):

#PB_Cipher_SHA3 - 34

32 байта (256 бит).

Код:
UseMD5Fingerprint()
UseSHA2Fingerprint()

Procedure HexToBin(s.s)
  Protected *Point=0, Pos=0, i, Count
   
  Count = Len(s)
  
  If Count > 0
    If Count % 2 <> 0 : Debug "Не кратно 2!!!! Возможна запись за пределы выделенной памяти!!!!" : EndIf
    *Point = AllocateMemory(Count / 2)
    If *Point
      For i=1 To Count Step 2
        PokeA(*Point+Pos, Val("$"+Mid(s, i, 2)))
        Pos+1
      Next
    EndIf
  EndIf
  
  ProcedureReturn *Point
EndProcedure

MD5.s = StringFingerprint("1234", #PB_Cipher_MD5, 0, #PB_Unicode)
Debug MD5
*MD5 = HexToBin(MD5)
ShowMemoryViewer(*MD5, MemorySize(*MD5))
Debug "MD5 Size="+MemorySize(*MD5)
FreeMemory(*MD5)

SHA.s = StringFingerprint("1234", #PB_Cipher_SHA2, 256, #PB_Unicode)
Debug SHA
*SHA = HexToBin(SHA)
ShowMemoryViewer(*SHA, MemorySize(*SHA))
Debug "SHA2 256 bit Size="+MemorySize(*SHA)
FreeMemory(*SHA)

*SHA использовать как ключ, а *MD5 как вектор.
Размер соответствует AES 256 бит.

0

23

Пётр написал(а):

а *MD5 как вектор

Вектор тоже должен быть 32 байта для 256-битного. Если на винде работает укороченный, но на линукс я сразу получал ошибку и временно вставлял 128 бит, чтобы работало с 16.

Кстати, мне понравился пример от wilbert, где получение символа из бинарного представления без использования Chr()

Вот, сделал адаптацию

Код:
EnableExplicit

Structure CharStr
  StructureUnion
    c.c
    s.s{1}
  EndStructureUnion
EndStructure

Procedure BinaryHex(String$)
    Protected *m, *r.Ascii, *c.CharStr
    Protected tmp.s{3}
    Protected Size

    If Not Asc(String$) ; пустая строка
        ProcedureReturn -1
    EndIf
    Size = Len(String$)
    If Size & 1 ; нечётное число
        ProcedureReturn -2
    EndIf
    *c = @String$

    *m = AllocateMemory(Size / 2 + 2)
    If *m
        *r = *m
        ; 0-9 и a-f (только в нижнем регистре)
        While (*c\c > 47 And *c\c < 58) Or (*c\c > 96 And *c\c < 103) ; Or (*c\c > 64 And *c\c < 71) ; A-F верхний регистр не берём во внимание
            tmp = "$"
            tmp + *c\s
            *c + 2
            If *c\c
                tmp + *c\s
                *r\a = Val(tmp)
                *r + 1
                *c + 2
            Else ; повторная проверка чётности, нужна ли она
                FreeMemory(*m)
                ProcedureReturn -3
            EndIf
        Wend
        ; проверка дошли ли мы до конца строки, если не 0,
    ; то не дошли и в строке есть не шестнадцатеричные символы
        If *c\c <> 0
            ProcedureReturn -4
        EndIf
        If *r > *m
            *r\a = 0
            ; Добавляем ещё 0, чтобы можно было использовать UTF8, UTF16
            *r + 1
            *r\a = 0
        EndIf
    EndIf

    ProcedureReturn *m
EndProcedure

UseSHA3Fingerprint()

Define *s, *u, tmp$
; tmp$ = StringFingerprint("1234", #PB_Cipher_MD5, 0, #PB_Unicode)
tmp$ = StringFingerprint("1234", #PB_Cipher_SHA3, 256, #PB_Unicode)
; Debug Len(tmp$)
Debug tmp$
*s = BinaryHex(tmp$)
Debug MemorySize(*s)
If *s > 0
    ShowMemoryViewer(*s, MemorySize(*s) - 1)
;     Debug PeekS(*s, -1, #PB_Ascii) ; Ascii
;     Debug PeekS(*s) ; UTF8
    FreeMemory(*s)
EndIf

Отредактировано AZJIO (20.04.2025 19:58:56)

0

24

AZJIO написал(а):

Вектор тоже должен быть 32 байта для 256-битного.

Тогда в справке ошибка https://www.purebasic.com/documentation … coder.html

*InitializationVector The InitializationVector is a random data block, used to initialize the ciphering to avoid breach in decoding (only needed when using the #PB_Cipher_CBC mode). Its size is always 16 bytes long. The contents of this data block must match the one which was used when encoding the data.

Проверил на вируталке, и ошибки не было.

AZJIO написал(а):

Вот, сделал адаптацию

Размер должен быть 32 байта. Где в строке хеша есть нулевые байты?
Завершающие строку нулевые байты не имеют отношения к содержимому строки.

0

25

Пётр написал(а):

Тогда в справке ошибка

В CHM версии 6.20 всё норм.

*InitializationVector The InitializationVector is a random data block, used to initialize the ciphering to avoid breach in decoding (only needed when using the #PB_Cipher_CBC mode). Its size depends of the 'Bits' parameter: 16 bytes for 128-bit encryption, 24 bytes for 196-bit and 32 bytes for 256-bit.

Пётр написал(а):

Проверил на вируталке, и ошибки не было.

Возможно это был пароль, так как я изменил 256 на 128 в функции шифрования. Иначе бы мне пришлось переписывать функцию GetKey() где было больше телодвижений. Так если для вектора исправлено, так может и раньше это было просто ошибка справки.

Пётр написал(а):

Где в строке хеша есть нулевые байты?

ок, урежу, хотя они не попадают, так как берётся всё равно 32 байта.
Ну вообще-то это ShowMemoryViewer() возвращает из-за MemorySize(), а AllocateMemory() выделяет на 2 байта больше, чтобы данные завершились нультерминированной строкой и их без проблем можно было прочитать с помощью PeekS(). Так что может не стоит заморачиваться с этим...

Отредактировано AZJIO (20.04.2025 22:17:45)

0

26

AZJIO написал(а):

В CHM версии 6.20 всё норм.

В справке 6.20 тоже что в олнайн справке.

0

27

Пётр
Я использовал из StartAESCipher(), так как он и в коде у меня, там именно так.

0

28

Тогда 256 битный SHA2 для ключа и 256 битный SHA3 для вектора или наоборот. Можно также сгенерировать ключ и/или вектор функцией DeriveCipherKey().

0

29

Пётр
Собственно функция DeriveCipherKey() создана специально для этого:

Код:
If Not DeriveCipherKey(Pass$, Pass$, 5, *Key, 512, #PB_Cipher_SHA3, 512)
	ProcedureReturn
EndIf
*InitializationVector = *Key + 32

InitializationVector можно было бы создать с помощью DeriveCipherKey() с другим числом итераций, дабы не добавлять модуль MD5, но при наличии ключа 512 проще взять по пол-ключа.
Но DeriveCipherKey() не дал бы использовать прогу на WinXP, а так как я хочу пока поддерживать её, то сделал имитацию DeriveCipherKey()

Код:
For i = 0 To 2 ; трёх кратный хеш пароля, вот она итерация
	Pass$ = StringFingerprint(Pass$, #PB_Cipher_SHA3, 512, #PB_Unicode)
Next
*Key = BinaryHex(Pass$) ; не бинарный? Делаем бинарный
If Not *Key
	ProcedureReturn
EndIf
*InitializationVector = *Key + 32

Перезалил

Отредактировано AZJIO (23.04.2025 22:03:26)

0

30

egons написал(а):

Объясните мне, скудоумному, на кой ляд понадобились такие громадные картинки с громадными ключами шифрования?

Паранойя

0


Вы здесь » PureBasic - форум » PureBasic для Windows » Кодировать / декодировать файл