Змея / Говнокод #16926 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
def constant_time_compare(val1, val2):
    """
    Returns True if the two strings are equal, False otherwise.

    The time taken is independent of the number of characters that match.
    """
    if len(val1) != len(val2):
        return False
    result = 0
    for x, y in zip(val1, val2):
        result |= ord(x) ^ ord(y)
    return result == 0

Django.utils.crypto в Django 1.4

Запостил: american_idiot american_idiot, (Updated )

Комментарии (36) RSS

  • >The time taken is independent of the number of characters that match.
    Вообще охуеть. Не совпало в первом символе гигабайтной строки.
    А прочитаешь, на первый взгляд вроде как улучшение: алгоритм не зависит от некоего M.
    Жаль питонистам еще брейки не завезли.
    Ответить
    • > А прочитаешь, на первый взгляд вроде как улучшение
      utils.crypto же!
      Это функция для сравнения всяких ключей и паролей. Надо сравнивать все символы, чтобы было невозможно использовать время работы функции для определения числа правильных символов при переборе.
      Но косяк в ней всё равно есть. Если длины разные, то значение возвращается сразу.
      Ответить
      • > Если длины разные, то значение возвращается сразу.
        Да и хер с ним, если честно. Хакер, скорее всего, и без этого знает, что там какой-нибудь sha-256 длиной 256 бит.
        Ответить
    • Qwertiy и chtulhu правильно пишут, constant time это не O(1), как могло бы показаться, а независимость от количества совпавших/несовпавших символов. Для криптографии это важно. Из-за этого там и логические операции вместо тернарников или ветвлений.
      Ответить
      • Все равно это не константное время. Оно прямо зависит от количества символов в строке. Т.е. линейное. А вообще, тут сама по себе задумка херовая. В эту функцию нужно было передавать третьим параметром количество символов, которые нужно сравнивать, тогда все было бы замечательно, а так нужно принимать решения о том, длину какого из аргументов считать эталоном, и как именно заполнять недостающие / убирать лишние символы.
        Ответить
        • >Т.е. линейное. А вообще, тут сама по себе задумка херовая.
          Об этом и речь. Надо проверить как можно быстрее, и только если НЕ совпало - поставить sleep/сделать N итераций чтоб получилось фиксированное.
          Тут где-то уже обсуждали на примере линуксов.
          Ответить
        • >А вообще, тут сама по себе задумка херовая.
          Почему?

          >Все равно это не константное время. Оно прямо зависит от количества символов в строке.
          Оно не зависит от позиции первого несовпадающего символа, я хз как это правильно записать.
          Ответить
  • Django.utils.crypto как бы намекает на криптографию.
    Для криптографических задач важно, чтобы данные проверялись одинаковое время и хакер не мог с помощью статистики быстро подобрать токены, пароли итд
    Ответить
    • И что? Время зависит от длины строки (пороля).
      Вы или O(N) снимите или crypto наденьте. Константным временем там и пахнет.
      Ответить
      • > Время зависит от длины строки (пороля).
        Пароли сравнивать - хреновая идея (т.к. подразумевает хранение пароля в открытом или обратимо зашифрованном виде). А сравниваться будут, скорее всего, какие-нибудь хеши или рандомные блоки известной и постоянной (ну ок, зависящей от настроек) длины. Время тут константное в том смысле, что оно зависит только от этой константной длины, но никак не зависит от значений бит в самих строках.
        Ответить
        • я плюсанул, потому что описывает что это делает.

          но все равно ахинея. время сравнения скажем 256 бит хэшей (== 32 байта), тем более на современном железе, незначительно и сравнимо, например, с временем выполнения пролога/эпилога функции. вкинь один быстрый syscall и о длине совпадающих символом можно только гадать на картах.
          Ответить
          • Я судить не берусь, т.как деталей не знаю, и искать мне не хочется, но я сильно сомневаюсь, что строки в Питоне не интернированы, и сравниваются посимвольно обычным ==. многие скриптовые языки интернируют строки. Т.е. если уж мы создали две строки с одинаковым содержанием, то мы проверяли их на равенство на этапе создания. Дальнейшие сравнения будут сравнивать ссылки а не содержание. (По крайней мере в ж.скрипте так). Так что я думаю, что ничего хорошего эта функция в Питон не привнесла.
            Ответить
            • > строки в Питоне не интернированы
              Да не должны быть они интернированы... Это же довольно дорогая операция, и память жрёт. Часть строк - запросто может. Но никак не все.
              Ответить
                • > это можно было бы форсировать
                  Но не нужно. В чем смысл интернировать всё подряд, включая временные переменные, которые живут пару функций, и никто никогда не будет их сравнивать?

                  Для литералов это имеет смысл. Для остальных строк - нет.
                  Ответить
              • Ох, нет, вообще от интерпретатора зависит. Ну ладно, странно вобщем. Интернирование не нагружает память, оно ее экономит, так что с этим проблем нет (никто же не запрещает удалять строки на которые больше нет ссылок). Но это отнимает время при создании / реализация становится более сложной. Судя по каким-то тестам на стековерфлоу, не все ж.скрипт реализации интернируют (что примечательно, МСИЕ даже строку с самой собой будет сравнивать посимвольно).
                Ответить
                • Нагружает, т.к. weak ссылки на эти строки придется хранить в какой-нибудь структуре данных, пригодной для быстрого поиска/вставки. Вот эта структура запросто сожрет больше памяти, чем наэкономится на интернировании. Имхо, большая часть заинтернированных таким образом строк будут просто бесполезными, т.к. их никто не будет сравнивать.

                  > отнимает время при создании
                  Именно.
                  Ответить
              • А вот сравнение строк за константное время
                def const_compare_strings(a, b):
                    hasher = { }
                    hasher[a] = True
                    return hasher.has_key(b)
                Ответить
                • А ничего, что has_key по-любому включает в себя то самое обычное сравнение строки (для борьбы с коллизиями), от которого тут пытались избавиться? 🙂
                  Ответить
                  • Но ведь лукап в хеш-таблице это O(1)!!!

                    Другими словами: нахера такие хеш-таблицы вообще нужны, если из них нельзя получить то, о чем так долго говорили большевики? Деревья будут предпочтительнее всегда.
                    Ответить
                    • > Но ведь лукап в хеш-таблице это O(1)!!!
                      O(1) от количества записей в таблице. Но O(M) от длины строк-ключей (расчет хеша, допроверка для борьбы с коллизиями).

                      > Деревья будут предпочтительнее всегда.
                      А у деревьев еще больше сравнений. Так что не всегда.

                      > нахера такие хеш-таблицы вообще нужны
                      Ну хеш-таблицы ведь далеко не везде юзают. К примеру, в реалтаймовых приложениях, вставка в дерево за log(N) может быть предпочительней амортизированного O(1) у хеш табличек. Для огромных дисковых структур, не влезающих в память, деревья тоже предпочительней.
                      Ответить
                      • Нет, у дерева не будет в таком случае больше сравнений. Чтобы сгенерировать хеш, нужно прочитать всю строку целиком. Дереву хеш не нужно генерировать, т.е. есть шанс, что всю строку читать не прийдется.

                        Интернированые строки нужно хранить в структуре с быстрым доступом - а остальные объекты не нужно? Если это делается на уровне языка, а не силами добровольцев, то на памяти это вообще никак не отразится. Примеры: V8 (NodeJS) и Луа. И ничего, живут как-то.
                        Ответить
                        • Только несколько небольших уточнений:
                          > Чтобы сгенерировать хеш, нужно прочитать всю строку целиком.
                          На коротких строках, с которые обычно встречаются на практике в виде ключей это не сильно ощутимо.
                          Если же смотреть контексте интернирования, то тут выгоднее повторно использовать ссылки на строки большей длины.

                          Ну и если строка немутабельная, то посчитать хеш придется всего 1 раз, и таким образом за O(1) проверять отсутствие строки в кеше. А ведь можно сделать и джва хеша, что прилично снизит вероятность полного сопоставления.

                          Кстати мы не так давно тут с Тарасом обсуждали префиксные деревья.
                          Ответить
                          • >мы не так давно тут с Тарасом обсуждали

                            Орально обсуждали?
                            Ответить
                            • >>Ищу хуястых кавказцев
                              >>Очко сильно раздолбано

                              >>Орально обсуждали
                              Не, это не в стиле Тараса. Непохоже на него.
                              Ответить
                      • В CHMv8 автор скрестил хешмапу с деревом, заменив связный список в букете на дерево, если число коллизий >6 емнип.
                        Ответить
    • def not_a_constant_time_compare(val1, val2):
      """
      Returns True if the two strings are equal, False otherwise.

      The time taken is independent of the number of characters that match.
      """
      time.sleep(random(500))
      return val1==val2
      Ответить
  • Въебал всем по "плюсу", чтоб неповадно было c++-ничать. Будь моя воля, я бы отправил в отхожее место все разделы (кроме php разумеется).
    Ответить

Добавить комментарий

Семь раз отмерь — один отрежь, guest!

    А не использовать ли нам bbcode?


    8