Не так давно передо мною встала задача закодировать переписку пользователей. Целью задачи было пересылать уже закодированную строку от пользователя А пользователю Б. Строка кодируется и декодируется с помощью ключа, который известен обоим. Подразумевается, что сообщение от пользователя А отсылается на сервер пользователю Б, где пользователь Б его и забирает. Чтобы избежать получение данных в случае получения сообщения третьим лицом путем перехвата сообщения, либо доступа к серверу, где оно хранится, функцию было решено организовать на JavaScript, что дает возможность пользователям отсылать закодированную строку прямо из окна браузера.

Бегло пробежавшись по некоторым способом шифрования, я решил написать собственный алгоритм. Суть алгоритма было решено свести к тому, чтобы перемешивать каждый отдельный символ в неким уникальным значением смешанным с ключом, причем так, чтобы значение, которое будет смешивать данные символы было уникальным и формировалось из заданного пароля или ключа. Творческой идеей для написания именно такого алгоритма послужил шифр Эль-Гамаля, метод преобразования Punycode и Base64. На нобелевскую премию я не претендую, но тем не менее решил поделиться собственным творением и…

passCode — пароль, ключ. Так как пользователь задает данный параметр самостоятельно, а он может быть достаточно простым, то я кодирую ее дополнительно в MD5.
Incode — кодируемая строка

function txtencode(Incode, passCode)
{
	//Так как сам результат может содержать нежелательные символы, эта переменная содержит символы с помощью которых мы будем выдавать закодированный результат, который вдальнейшем сможем отправить без особых хлопот
	var b52 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	//Эти переменные будут меняться в процессе кодировки и создавать мешанину каждого кодируемого символа в отдельности
	var maxPC = ifPC = 0;
	//Уникализируем переменную maxPC. Ее значение будет происходить от суммы каждого юникодного значения символов пароля
	for(var i=0; i<passCode.length; i++) maxPC += passCode.charCodeAt(i);
	//Значение maxPCmod будет меняться на убывание, опять же в зависимости от юникодного значения символа пароля
	//А вот значение maxPC сохраним, оно понадобиться вдальнейшем, чтобы присвоить переменной maxPCmod новое значение, когда то будет меньше 0.
	maxPCmod = maxPC;
	//Результат кодируемой строки. Изначально равно пустоте. 
	var rexcode = "";
	//Переменная содержит первый символ пароля: passCode.charCodeAt(numPC)
	//С ее помощью будем перебирать пароль и перемешивать его с символом строки
	var numPC = 0;
	//Перебираем каждый символ строки
	for(var i=0; i<Incode.length; i++) 
	{
		//Если все символы пароля перемешаны, начинаем перебор пароля с первого символа
		if(numPC == passCode.length) numPC = 0;
		//Присвоиваем переменной maxPCmod новое значение, если оно меньше нуля.
		if(maxPCmod < 1) maxPCmod = maxPC+ifPC;
		//Эта переменная нужна для создания уникального значения maxPCmod, и как следствие уникального символа, с которым будет перемешиваться символ исходной строки.
		// Получаем ее путем деления по модулю значений maxPCmod и текущего используемого юникодного значения символа пароля 
		//В целом постоянная мешанина переменных maxPCmod, maxPC и ifPC позволяет кодировать каждый отдельный символ исходной строки с уникальным значением, что подразумевает невозможность отследить какую-либо синхронизацию алгоритма
		ifPC += maxPCmod % passCode.charCodeAt(numPC);
		//Создаем непосредственно символ, с которым и будем перемешивать текущий символ строки
		var iscode = maxPCmod % passCode.charCodeAt(numPC);
		//Создаем мешанину, путем сложения предыдущей переменной с переменной текущего символа
		var nCode = (Incode.charCodeAt(i)+iscode);
		//Уменьшаем значение maxPCmod для ее дальнейшей уникализации
		maxPCmod -= passCode.charCodeAt(numPC);
		//Переходим к следующему символу пароля
		numPC++;
		//Это будет уникальный номер текущего символа.
		//При делении закодированного символа на 52 число означает неполное частно, а буква остаток. 
		//Например 22С означает 22*52+2, так как С второй по счету символ начиная с нуля.
		rexcode += parseInt(nCode / 52) + b52.charAt(parseInt(nCode % 52));
	}
	//Возвращаем закодированную строку
	return rexcode;
}


Функция декодировки практически аналогично предыдущей за небольшими исключениями

function txtdecode(Incode, passCode)
{
	var b52 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	var maxPC = 0;
	for(var i=0; i<passCode.length; i++) maxPC += passCode.charCodeAt(i);
	maxPCmod = maxPC;
	ifPC = 0;
	//Разбиваем строку на массив, который будет состоять из каждого закодированного символа
	var Incode = Incode.match(/\d+\w/g);
	var rexcode = "";
	var numPC = 0;
	for(var i=0; i<Incode.length; i++) 
	{
		if(numPC == passCode.length) numPC = 0;
		if(maxPCmod < 1) maxPCmod = maxPC+ifPC;
		ifPC += maxPCmod % passCode.charCodeAt(numPC);
		var iscode = maxPCmod % passCode.charCodeAt(numPC);
		//В отличии от фунции кодирования, тут дейтсвие происходит в обратную сторону
		var nCode = (parseInt(Incode[i])*52)+parseInt(b52.indexOf(Incode[i].substr(-1)));
		maxPCmod -= passCode.charCodeAt(numPC);
		numPC++;
		//И в результате соответственно уже не сложение, а вычитание
		rexcode += String.fromCharCode(nCode-iscode);
	}
	//Уже можно вернуть return rexcode.
	//Но для корректного отображения в браузере, я преобразую некоторые символы во мнемоники, а урлы преобразую в ссылки.
	return rexcode.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/ /g, " ").replace(/\r\n|\r|\n/g,"<br />").replace(/(https?\:\/\/|www\.)([а-яА-Я\d\w#!:.?+=&%@!\-\/]+)/gi, function(url) 
	{
		return '<a target="_blank" href="'+ (( url.match('^https?:\/\/') )?url:'http://' + url) +'">'+ url +'</a>';
	}); 
}

Комментарии (29)


  1. lair
    08.07.2015 14:56
    +4

    А чем вас не устроили существующие крипто-решения для JavaScript?


    1. mmotor Автор
      08.07.2015 14:58
      -7

      Просто было интересно создать свое.


      1. kahi4
        08.07.2015 15:28
        +8

        Создать свое без опыта 10 лет работы в области — можно не пытаться. Точнее, пытаться то можно, но использовать в реальных системах мягко говоря не рекомендуется.

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


        1. Dink
          08.07.2015 17:02
          +3

          Вероятно вы имели ввиду шифр Виженера? В Цезаре нет кодового слова.


          1. kahi4
            08.07.2015 17:17
            +7

            Да, точно. Я вообще очень слаб в криптографии, какие-то основы знаю, но сам предпочитаю не касаться этой темы и 12-ти метровой палкой, что обычно и советую всем, если только у них нет цели освоить криптографию. Особенно не касаюсь, если это продуктовые решения. Настроить https на nginx через сертификат — и то обычно предпочитаю, чтобы кто-то более компетентный проверил.
            Но даже моих познаний достаточно, чтобы понять, что в статье очень слабый метод.

            Вот если бы автор не рекомендовал эту поделку, а сделал как небольшой пример, в стиле «урок», потом дописал бы «так делать ни в коем случае нельзя», да и реализовал один из более стойких алгоритмов симметричного шифрования — думаю, у него бы не получился такой хабра-суицидальный пост, а пополнение в коллекции уроков. Жаль, что mmotor предпочел иной путь.


  1. oaons
    08.07.2015 15:00
    +20

    просто оставлю это здесь
    habrahabr.ru/post/181372


    1. a553
      08.07.2015 15:12
      +2

      Кроме того, шифрование джаваскриптом в браузере это проблема курицы и яйца.


  1. Dink
    08.07.2015 15:03
    +6

    Не так давно передо мною встала задача закодировать переписку пользователей.

    Кодирование != шифрование


    1. NonGrate
      08.07.2015 18:26
      +2

      Кодирую переписку пользователей от алкоголя и курения. Дорого. В лс.


  1. blind_oracle
    08.07.2015 15:25
    +1

    Очередное изобретение велосипеда.

    code.google.com/p/crypto-js


  1. Dink
    08.07.2015 15:40
    +1

    Кстати, n-ый символ (где n — длина passCode) у вас не зашифрован. Так как в конце первого прохода по паролю maxPCmod равен коду последнего символа пароля, и по его же модулю это ноль.


  1. drayv
    08.07.2015 15:45

    А каким каналом между пользователями передается симметричный ключ?


  1. truezemez
    08.07.2015 16:13
    +7

    RLY?

    var top_secret_info = txtencode('hello', 'qqqqq');
    print(txtdecode(top_secret_info, 'xxxxx')); // hello
    

    ideone.com/59CyJK


    1. grossws
      08.07.2015 16:28
      +2

      Ну, во-первых — это красиво.


    1. grossws
      08.07.2015 16:28

      deleted


    1. Dink
      08.07.2015 16:36
      +1

      Ну так у автора все-таки md5 берется от пароля=)


  1. grossws
    08.07.2015 16:29
    +1

    Казалось бы, не май месяц, а весене-осенние посты постоянно пребывают


  1. dkukushkin
    08.07.2015 16:35
    +11

    Вы показали хороший пример как делать не нужно.


  1. hellman
    08.07.2015 17:10
    +2

    Эль-Гамаль то тут при чем?

    Не так давно передо мною встала задача закодировать переписку пользователей.


    Может base64 было бы достаточно? Или недостаточно «мешанины»?


  1. nazarpc
    08.07.2015 18:58
    +2

    Первое правило криптографии: не изобретайте свой шифр.
    Лучше пройдите курс на Coursera, там есть хороший по криптографии, потом посмеетесь над своей поделкой)
    Количество минусов как бы намекает на то, что подобные решения никаким образом не должны попадать в рабочие продукты, и вообще статью лучше спрятать, чтобы никто случайно не воспользовался.


    1. lair
      08.07.2015 19:09

      Кстати, а какой именно курс на курсере вы имеете в виду?


      1. nazarpc
        08.07.2015 19:10

        1. lair
          08.07.2015 19:12

          Спасибо!


  1. Semmaz
    08.07.2015 19:25

    IMHO, ваш алгоритм недалеко ушёл от ROT13.


  1. alekseev_ap
    08.07.2015 21:46
    -17

    Я полагаю, что любые попытки написать свой алгоритм шифрования достойны похвалы. Даже если Ваш алгоритм и не является крипто стойким, само наличие разных алгоритмов даёт пользователям возможность выбора, а злоумышленникам усложняет жизнь. Что касается высказываний большинства комментаторов, то, возможно, они «типа шибко умные», но никто особо не заморачивается тем, чтобы указать на ошибки. А может они просто не могут это сделать? Может, ума не хватает?


    1. frol
      08.07.2015 22:22
      +5

      Вы, видимо, решили не читать комментарии вовсе, иначе бы заметили, что создание «своего криптоалгоритма» без опыта — это одна большая ошибка, а в конкретном опусе вообще нет ничего правильного — одна сплошная дыра, видимость защиты. Так как вы не читали комментарии, позволю себе повторить лично для вас ссылку: habrahabr.ru/post/181372

      На счёт «наличие разных алгоритмов даёт возможность выбора» — алгоритмов с математически доказанными свойствами стойкости масса, среди них на специальных конкурсах, где проверяют математическую базу и все доказательства годами (!), например, AES и SHA, выбираются лучшие из лучших. Поверьте, не нужны человечествую такие «альтернативы», которую автор предложил. Изобретение криптографических примитивов — это не развлечение на выходные.


      1. kahi4
        08.07.2015 23:51
        +2

        Как-то у меня была идея, как раз как развлечение на выходные: сделать шифрование на основе фильтра Калмана вдруг неожиданно. Точнее само шифрование на основе системы диф. уравнений, где кодовое слово определяло бы её вид, а входные данные — были бы возмущениями этих дифуров, затем подмешивался бы случайный шум, а дешифрация осуществлялась бы фильтром. Но немного подумав пришел к нескольким выводам: превратить это из идеи в работающий алгоритм займет уже несколько месяцев, вот почти наверное что-то подобное уже есть, а так же — скорее всего, есть глупый и смешной способ взлома, благодаря чему окажется, что время потрачено впустую. Хотя идея звучит круто: один и тот же поток каждый раз будет давать разный результат, причем случайный, шифрация поточная, можно подмешивать так же несколько случайных входов — есть место для фантазии. Правда объем данных увеличится сильно, ну да ладно.
        Раз я уже тут сказал эту идейку — вот просто ради интереса — есть ли что-то подобное и какой самый очевидный недостаток этого метода?

        Но мораль такая, что прежде чем алгоритм пополнит возможность выбора, пройдет много времени исследований, закрытия дырок, доказательства стойкости, а потом к вам постучится МВД и потребуют лицензирования (если захотите заработать денежек на своих наработках).


        1. Carcharodon
          09.07.2015 11:28

          ФСБ скорее только, а не МВД. Лицензированием по криптографии они у нас в стране занимаются.


        1. hellman
          14.07.2015 14:42

          Нечто похожее, но на системах линейных уравнений, уже есть — Learning With Errors. Насколько я помню, LWE считается (но не доказано) сложной проблемой для квантовых компьютеров, поэтому активно продвигается в post-quantum криптосистемах.