Идея использовать нейросети для шифрования информации витала в голове у меня давно (с конца 2000х). Но, как это водится, то времени не хватало, то желания. Так что пишу это только сейчас (хотя может материал уже и устарел).
Вводная
Суть идеи заключается в том что-б передавать не само зашифрованное сообщение а настроенные веса сети которая сможет данное сообщение воспроизвести будучи активированной кужным ключем.
Описание работы
Предположим у нас есть сообщение «Hello i'm EncryptNN and i'm here». Преобразуем его в пару массивов tc, letters; где tc = массив «таймкодов последовательности» а letters соответсвующие байты сообщения. Дальше учим сеть сопоставлять таймкоды определенным байтам сообщения.
Процесс обмена сообщениями выглядит так:
- Отправитель передает тело сообщения
- Отправитель передает сообщения (подсказка для генератора последовательностей таймкодов)
- Получатель загружает в сеть веса переданные в п.1
- Получатель загружает в сеть секрет и получает дешифрованное сообщение
Прототип
Сначала нам понядобятся утилитарные ф-ции преобразования из/в строку:
def toChars(s):
return np.array([ord(c) for c in s])
def toArr(c):
r = np.zeros(256);
r[c] = 1
return r
def toCharArr(s):
return np.array([toArr(c) for c in toChars(s)])
def fromChars(arr):
return ''.join(chr(i) for i in arr)
def fromArr(arr):
return np.argmax(arr)
def fromCharArr(arr):
return fromChars([fromArr(a) for a in arr])
def toTimeCode(i, l = 8):
val = [int(x) for x in bin(i)[2:]]
result = [0] * (l - len(val))
result.extend(val)
return result
def stringToSequence(s):
tc = []
letters = []
for t,c in enumerate(toCharArr(s)):
tc+=[toTimeCode(t)]
letters+=[c]
return np.array(tc),np.array(letters)
В текущем примере «секрет» — начало последовательности = 0, шаг = 1.
Далее опишем саму сеточку (я буду использовать Keras для краткости). В текущей реализации работает простейший персептрон. Сесть состоит из 2х слоев: входной слой принимающий таймкод и выходной — отдающий байт исходного сообщения закодированный как «one-hot».
inSize = 8 #выбрано просто под полную ASCII таблицу
model = Sequential()
model.add(Dense(inSize, input_shape = (inSize,),activation="relu"))
model.add(Dense(256))
model.add(Activation("softmax"))
model.compile(loss=keras.losses.categorical_crossentropy, optimizer='sgd')
Теперь нам остались только тренировка и экспорт данных для получателя:
#тренировка
tc,l = stringToSequence(testString)
res = 10
cnt = 0
epochs = 100
while( res > 0.1):
cnt+=1
model.fit(tc,l, epochs=epochs, verbose=False)
res = model.evaluate(tc,l, verbose=False)
print("Loss: ", res)
v = model.predict(tc)
print(fromCharArr(v))
print("epochs = ", cnt*epochs)
#экспорт
model.save_weights("/tmp/test1.data.w")
Для примера — обучение фразе «Hello i'm EncryptNN and i'm here» на моем ноутбуке заняло 22800 эпох.
Выводы
- Такое кодирование в принципе возможно
- Для кодирования требуются огромные ресурсы, для раскодирования — нет
- ИМХО (и тут я очень прошу помощи математиков) невозможно восстановить сообщения при налиции только 1 половины данных
- Все сообщения которым я смог научить сеть весили одинаково
- Можно щифровать в одну сеть несколько сообщений с разными секретами (немного не очевидно, но очень просто)
Вопросы и доработки
- Очевидо что секрет выбран простейшим и его надо переделать
- Какой предел памяти у такой сети (какой макс объем данных так можно зашифровать)?
Автор: Лунтик