Тайное хеширование паролей [закрыто]

33

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

Конкурс

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

Ваш начальник хочет, чтобы вы реализовали следующую функцию:

f: String -> byte[16]

который преобразует пароль в 16-байтовое количество, подходящее для использования в качестве ключа AES. Ваш начальник хочет иметь безопасную функцию, которая в этом контексте означает, что разные строки пароля должны давать разные результаты с подавляющей вероятностью. Например, возврат хеша md5 для ввода будет простой реализацией f.

Конечно, ваш настоящий начальник в российском шпионском агентстве хотел бы, чтобы вы сорвали этот процесс. Ваша задача - реализовать fтак, чтобы россияне могли расшифровать все секретные сообщения, зашифрованные с помощью ключей, возвращаемых f.

Для этого необходимо реализовать fтак, чтобы он возвращал только небольшое подмножество из 2 ^ 128 возможных выходных данных. В частности, вы fдолжны вернуть не более 2 ^ 16 разных результатов, чтобы русские могли легко и просто перебрать правильный ключ для каждого зашифрованного сообщения, которое они хотят расшифровать.

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

судейство

Я и двое других, которых я найму, будут судить записи (напишите мне, если хотите судить). Я предлагаю награду в 200 репутаций за победу. Материалы должны быть загружены до 1 мая.

Судейство будет учитывать следующие критерии:

  • Есть ли fприлипают к спецификации, то есть делает его генерировать между 2 ^ 8 и 2 ^ 16 возможных выходов. Не думайте, что это жесткие ограничения, но мы будем вычитать очки, если вы слишком далеко за пределами диапазона.
  • Является ли ошибка вероятным результатом непреднамеренной ошибки?
  • Выходы fвыглядят случайными?
  • Чем короче ваша реализация f, тем лучше.
  • Чем яснее ваша реализация f, тем лучше.

Заметки

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

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

Входные строки будут напечатаны в формате ascii (от 32 до 126 включительно). Вы можете принять разумную максимальную длину, если хотите.

Кит Рэндалл
источник
1
Есть ли какие-либо ограничения на ввод строки? как будто он состоит только из алфавита?
Ali1S232
@Gajet: вы должны обрабатывать все печатные символы ascii (от 32 до 126 включительно).
Кит Рэндалл,
Выходной диапазон - все 16-байтовые строки или только для печати?
Boothby
@boothby: все возможные 16-байтовые значения (2 ^ 128 возможностей)
Кейт Рэндалл,
1
Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что скрытые проблемы больше не обсуждаются на этом сайте. meta.codegolf.stackexchange.com/a/8326/20469
кошка,

Ответы:

15

С

2 ^ 16 возможных выходов (или 2 ^ 8 раз количество используемых символов).
Использует Linux-реализацию MD5, что, AFAIK, отлично. Но это дает тот же хеш, например, для «40» и «42».
РЕДАКТИРОВАТЬ: переименован bcopyв memcpy(поменялся параметрами конечно).
РЕДАКТИРОВАТЬ: преобразованы из программы в функции, чтобы лучше соответствовать требованиям.

#include <string.h>
#include <openssl/md5.h>

void f(const unsigned char *input, unsigned char output[16]) {

    /* Put the input in a 32-byte buffer, padded with zeros. */
    unsigned char workbuf[32] = {0};
    strncpy(workbuf, input, sizeof(workbuf));

    unsigned char res[MD5_DIGEST_LENGTH];
    MD5(workbuf, sizeof(workbuf), res);

    /* NOTE: MD5 has known weaknesses, so using it isn't 100% secure.
     * To compensate, prefix the input buffer with its own MD5, and hash again. */
    memcpy(workbuf+1, workbuf, sizeof(workbuf)-1);
    workbuf[0] = res[0];
    MD5(workbuf, sizeof(workbuf), res);

    /* Copy the result to the output buffer */
    memcpy(output, res, 16);
}

/* Some operating systems don't have memcpy(), so include a simple implementation */
void *
memcpy(void *_dest, const void *_src, size_t n)
{
    const unsigned char *src = _src;
    unsigned char *dest = _dest;
    while (n--) *dest++ = *src++;
    return _dest;
}
ugoren
источник
это недостаток с MD5?
Ali1S232
@Gajet, нет, я использовал Linux MD5, что совершенно нормально (AFAIK).
Угорен
почему они не генерируют больше возможного выхода?
Ali1S232
1
@Gajet: рассмотрим, что происходит на bcopyшаге ... это немного ошибочно, так как фактическая bcopyфункция BSD здесь будет работать правильно.
хань
@ Хан, На самом деле, теперь я вижу, что мой bcopyглючит. Я изменю это на memcpy, и тогда та же самая реализация станет действительной.
Угорен
13

С

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

#include <stdio.h>
#include <string.h>
#include <stdint.h>

void hash(const char* s, uint8_t* r, size_t n)
{
     uint32_t h = 123456789UL;
     for (size_t i = 0; i < n; i++) {
          for (const char* p = s; *p; p++) {
               h = h * 33 + *p;
          }
          *r++ = (h >> 3) & 0xff;
          h = h ^ 987654321UL;
     }
}

int main()
{
     size_t n = 1024;
     char s[n];
     size_t m = 16;
     uint8_t b[m];
     while (fgets(s, n, stdin)) {
          hash(s, b, m);
          for (size_t i = 0; i < m; ++i)
               printf("%02x", b[i]);
          printf("\n");
     }
}

На самом деле хеш-функция может возвращать не более L * 2048 разных результатов, где L - количество разных длин входной строки, которые могут возникнуть. На практике я протестировал код на 1,85 миллиона уникальных строк ввода со страниц справочника и HTML-документов на своем ноутбуке и получил только 85428 различных уникальных хэшей.

хань
источник
0

Scala:

// smaller values for more easy tests:
val len = 16
// make a 16 bytes fingerprint
def to16Bytes (l: BigInt, pos: Int=len) : List[Byte] = 
  if (pos == 1) List (l.toByte) else (l % 256L).toByte :: to16Bytes (l / 256L, pos-1)
/** if number isn't prime, take next */
def nextProbPrime (l: BigInt) : BigInt = 
  if (l.isProbablePrime (9)) l else nextProbPrime (l + 1)
/** Take every input, shift and add, but take primes */
def codify (s: String): BigInt = 
  (BigInt (17) /: s) ((a, b) => nextProbPrime (a * BigInt (257) + b))
/** very, very short Strings - less than 14 bytes - have to be filled, to obscure them a bit: */
def f (s: String) : Array [Byte] = {
  val filled = (if (s.size < 14) s + "secret" + s else s)
  to16Bytes (codify (filled + filled.reverse)).toArray.map (l => nextProbPrime (l).toByte) 
}

Проверьте, если результат не похож на аналогичный вход:

val samples = List ("a", "aa", "b", "", "longer example", "This is a foolish, fishy test") 

samples.map (f) 

 List[Array[Byte]] = List(
Array (-41, -113, -79, 127, 29, 127, 31, 67, -19, 83, -73, -31, -101, -113, 97, -113), 
Array (-19, 7, -43, 89, -97, -113, 47, -53, -113, -127, -31, -113, -67, -23, 127, 127), 
Array (-41, -113, -79, 127, 29, 127, 31, 67, -19, 83, -73, -31, -101, -113, 97, -113), 
Array (37, -19, -7, 67, -83, 89, 59, -11, -23, -47, 97, 83, 19, 2, 2, 2), 
Array (79, 101, -47, -103, 47, -13, 29, -37, -83, -3, -37, 59, 127, 97, -43, -43), 
Array (37, 53, -43, -73, -67, 5, 11, -89, -37, -103, 107, 97, 37, -71, 59, 67))

Ошибка использует только простые числа для кодирования. Вместо того

scala> math.pow (256, 16)
res5: Double = 3.4028236692093846E38

ценности, мы заканчиваем

scala> math.pow (54, 16)
res6: Double = 5.227573613485917E27

так как есть 54 простых числа ниже 256.

неизвестный пользователь
источник
2
5.22e27 >> 2^16, Там нет способа грубой силы, что много возможностей.
Кит Рэндалл
Вы забыли название языка
ajax333221
@ ajax333221: Скала. Я добавил это к вершине.
пользователь неизвестен
@KeithRandall: я мог «случайно» использовать только положительные байты, что уменьшило бы возможности для math.pow (27, 16), но это все еще около 8e22.
пользователь неизвестен