C ulong операция с массивом CRC32

Я действительно устарел, когда он идет с C, и мне нужно понимать код, сделанный другим разработчиком, основанный на CRC32. У меня есть массив ulong

static const ulong crc32_table[256] =
{
      0x00000000ul, 0x04c11db7ul, 0x09823b6eul, 0x0d4326d9ul,
      0x130476dcul, 0x17c56b6bul, 0x1a864db2ul, 0x1e475005ul,
      0x2608edb8ul, 0x22c9f00ful, 0x2f8ad6d6ul, 0x2b4bcb61ul,
      0x350c9b64ul, 0x31cd86d3ul, 0x3c8ea00aul, 0x384fbdbdul,
      ...
};

Затем этот массив используется для шифрования данных следующим образом:

void CRC32(const byte *buf, uint len, const byte init[4], byte crc[4]) {
    memcpy(crc, init, 4);
    while (len--) {
        const byte * tmp = (const byte *)(crc32_table + (crc[3] ^ *buf));
        crc[3] = crc[2] ^ tmp[3];
        crc[2] = crc[1] ^ tmp[2];
        crc[1] = crc[0] ^ tmp[1];
        crc[0] = tmp[0];
        ++buf;
    }
}

Чего я не понимаю, так это строки:

const byte * tmp = (const byte *)(crc32_table + (crc[3] ^ *buf));

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

Кто-нибудь может мне помочь?

Мне нужно написать эквивалент функции CRC32, но на C # Сработает ли это:

            uint[] crc = sharedkey;
            uint[] buff = new uint[] { 0x4fu, 0xaeu, 0x07u, 0x0bu, 0x68u, 0x56u, 0x34u, 0x12u };

            for(int i=0; i < len; i++)
            {
                byte[] tmp = BitConverter.GetBytes(crc32_table[(int)(crc[3] ^ buff[i])]);            

                crc[3] = crc[2] ^ tmp[3];
                crc[2] = crc[1] ^ tmp[2];
                crc[1] = crc[0] ^ tmp[1];
                crc[0] = tmp[0];
            } 

  

person momone50    schedule 23.02.2021    source источник
comment
шифр, существительное: секретный или замаскированный способ письма; код. глагол: вложить (сообщение) в секретное письмо; кодировать. - CRC - это не шифр, это просто код обнаружения ошибки.   -  person ilkkachu    schedule 23.02.2021
comment
В моем случае он используется для связи по Bluetooth между устройством и планшетом, чтобы защитить его (каким-то образом). Параметр зашифрован с помощью CRC32 и должен совпадать между периферийным устройством и контроллером. Это не мой дизайн   -  person momone50    schedule 23.02.2021
comment
Цитата Шнайера из «Прикладной криптографии»: LFSR сами по себе являются компетентными генераторами псевдослучайных последовательностей, но у них есть некоторые раздражающие неслучайные свойства. Последовательные биты линейны, что делает их бесполезными для шифрования. Хотя я не уверен, что то, что вы говорите, больше похоже на попытку аутентификации, но да, им, вероятно, не следует использовать CRC / LFSR для что.   -  person ilkkachu    schedule 23.02.2021
comment
Ага! Как я уже сказал, это не мой дизайн, и я все равно должен с ним работать ... Это какая-то аутентификация, вы правы, не совсем шифрование   -  person momone50    schedule 23.02.2021
comment
ulong, uint не являются стандартными типами. Выложите, пожалуйста, их определения.   -  person chux - Reinstate Monica    schedule 23.02.2021
comment
@ momone50 CRC - это проверка целостности, а не шифрование или аутентификация. Учитывая сообщение (например: const byte *buf) и CRC (например: byte crc[4]), вы можете определить, было ли изменено исходное сообщение, пересчитав CRC.   -  person h0r53    schedule 23.02.2021
comment
Я согласен ... но, как я уже сказал, мне нужно с этим поработать ... Сначала разработчик этой конструкции bluetooth использовал шифрование AES, но переключился на CRC по причине размера кода ... Вы думаете, что он действительно должен пересмотреть это?   -  person momone50    schedule 23.02.2021
comment
@ momone50 ну это зависит от ваших целей. Начнем с того, что это не совсем стандартный CRC, но он определенно ничего не шифрует. Если ваша цель - конфиденциальность (предотвратить понимание сообщения неавторизованными пользователями), вам необходимо шифрование, и этого будет недостаточно. Если ваша цель - целостность (определить, когда сообщение было изменено по сравнению с его исходной формой), тогда CRC или хеш должны работать. Мы не можем делать предположений о потребностях вашего бизнеса или брать на себя ответственность за советы. Я думаю, вам и вашей организации необходимо обсудить свои проблемы безопасности и действовать дальше.   -  person h0r53    schedule 23.02.2021
comment
Еще раз спасибо за свет. Я отмечу свой вопрос как ответ, поскольку здесь достаточно информации.   -  person momone50    schedule 23.02.2021
comment
@ h0r53, почему ты говоришь, что это не стандартный CRC? Для меня это похоже на единицу, и эта константа 0x04c11db7 совпадает с одной из обычных 32-битных CRC.   -  person ilkkachu    schedule 23.02.2021
comment
@ momone50, и, кстати, способ пометить вопрос как ответ - это использовать одну из галочек рядом с ответами, чтобы выбрать этот ответ как принятый. Это делает вопрос отмеченным как отвеченный также в списках результатов поиска.   -  person ilkkachu    schedule 23.02.2021


Ответы (3)


Давайте проанализируем всю функцию. Функция объявлена ​​следующим образом:

void CRC32(const byte *buf, uint len, const byte init[4], byte crc[4]) 

const byte *buf - входной буфер, для которого вычисляется CRC

uint len - Длина вычисляемого CRC

const byte init[4] - вектор инициализации CRC

byte crc[4] - Вывод / результат вычисления CRC

Сначала мы видим, что crc инициализируется вектором инициализации в memcpy(crc, init, 4);. Затем у нас есть цикл, который повторяется len раз. В конце этого цикла указатель buf увеличивается на ++buf.

Теперь о той части, которую вы не понимаете.

Каждый байт buf подвергается операции XOR с crc[3], результат которой используется как смещение в crc32_table. Результирующее значение сохраняется в tmp, которое затем используется для шифрования четырех байтов crc. Ключевые факторы, лежащие в основе этой работы, включают crc32_table 256 записей и 256 возможных значений байта (что является результатом операции, которую вы пытаетесь понять). Таким образом, индекс смещения всегда действителен.

Остальные операции - это просто XOR, которые еще больше скремблируют crc.

ОБНОВЛЕНИЕ

Хотя я проанализировал функцию, основная путаница связана с концепцией, известной как арифметика указателей. В C значение неиндексированного массива (например, crc32_table) является указателем на ячейку памяти, в которой хранятся данные последовательных типов, в зависимости от типа и длины массива. Например,

Допустим, у нас есть следующий массив ulong crc32_table[256] в ячейке памяти 0x400000.

Тогда значение crc32_table равно 0x400000.

Кроме того, &crc32_table[0] также 0x400000 (это переводится в адрес первой записи в массиве crc32_table).

Однако &crc32_table[1] равно 0x400008 (если sizeof (ulong) в вашей системе составляет 8 байт)

Вот что интересно.

crc32_table + 1 также 0x400008 (то же, что и &crc32_table[1]).

Это известно как арифметика указателей, и это та же концепция, которая используется в предоставленном вами коде CRC. Этот код просто вычисляет смещение 0–255 в массиве размером 256, поэтому каждое возможное смещение является действительным и транслируется в соответствующий индекс массива.

person h0r53    schedule 23.02.2021
comment
Привет, во-первых, позвольте мне поблагодарить вас за ответ со скоростью света! Я понимаю большую часть этого, и то, что вы написали, подтверждает, что я правильно понял. Это смещение от crc32_table, которое я не понимаю. Я не понимаю этого синтаксиса. Я привык к C # и Java. C далеко позади меня - person momone50; 23.02.2021
comment
crc32_table - это массив. В C массивы технически являются указателями на память, размер типа и длину массива. Например, ulong crc32_table[256] хранит ulong и имеет 256 записей, поэтому crc32_table является указателем на sizeof(ulong)*256 байта. Арифметика указателя включает добавление к указателю для получения смещения того, что может быть записью массива. Итак, в основном то, что вы нашли, является допустимым способом смещения в массиве на основе местоположения указателя базового массива. Это помогает? - person h0r53; 23.02.2021
comment
Кажется, я заржавел с указателями на! Думаю, у меня есть все ответы в вашем посте, но мне нужно немного времени, чтобы собрать все воедино. Я помечу как отвеченный, если больше не будет вопросов. Но, может быть, ты снова будешь здесь от меня. - person momone50; 23.02.2021
comment
Звучит неплохо. Я рада помочь. - person h0r53; 23.02.2021
comment
По какой магии tmp (байт?) Становится массивом, как мы видим tmp [3], tmp [2] и т. Д. - person momone50; 23.02.2021
comment
(const byte *) - финальный состав. Если вы заметили, технически это указатель на одно из значений crc32_table. Допустим, это crc32_table[1]. Тогда вы могли бы думать об этом как о const byte tmp[4] = 0x04c11db7. Несмотря на то, что указатель указывает на первый элемент записи crc32_table, технически в этом месте есть 4 байта, и вы можете просто получить к ним доступ так же, как и к массиву размером 4. - person h0r53; 23.02.2021
comment
Это то же самое, что преобразование байтового массива размера 4 в byte *, поскольку массивы технически являются указателями. Однако вы не можете явно привести что-либо к массиву, поэтому вместо этого вы приводите к указателю, и понимая, что указатель относится к массиву размера 4, вы не должны пытаться получить доступ к элементам за пределами индексов 0-3. - person h0r53; 23.02.2021
comment
Я обновил свой вопрос, так как мне нужно написать эквивалент на C #. Не могли бы вы взглянуть, пожалуйста? - person momone50; 23.02.2021
comment
@ momone50 Задать вопрос для уточнения исходного кода, который вы предоставили, немного отличается от того, чтобы задать вопрос о том, как воссоздать на C #. IMHO, это должен быть новый вопрос, поскольку исходный вопрос касался арифметики указателей в C. - person h0r53; 23.02.2021
comment
Ты прав. На самом деле не используется для переполнения стека. Извините - person momone50; 23.02.2021
comment
@ chux-ReinstateMonica да, есть тонкие различия, но поскольку путаница касалась арифметики указателей, здесь было показано, что значение неиндексированного массива C совпадает с адресом того же массива в элементе 0. - person h0r53; 23.02.2021
comment
Обновлено, чтобы обеспечить ясность и избежать путаницы - person h0r53; 23.02.2021

Здесь,

const byte * tmp = (const byte *)(crc32_table + (crc[3] ^ *buf));

с buf, указывающим на следующий байт ввода, это исключает следующий байт ввода со старшим байтом текущего значения CRC и ищет его в таблице. Или, скорее, он ищет адрес этого элемента в таблице и преобразует его в const byte *, чтобы байты можно было прочитать позже. (crc32_table + (foo) совпадает с &crc32_table[foo].)

Тогда это:

crc[3] = crc[2] ^ tmp[3];
crc[2] = crc[1] ^ tmp[2];
crc[1] = crc[0] ^ tmp[1];
crc[0] = tmp[0];

сдвигает значение CRC на 8 бит (обратите внимание на индексы) и xors в значении, выбранном из таблицы. Здесь tmp - это указатель на байт (вероятно, unsigned char *), который затем указывает на первый байт одного из значений в таблице. При доступе через tmp[0] к tmp[3] затем читается этот байт и следующие байты значения по отдельности. Если ulong равно unsigned long, это как минимум 32 бита или четыре байта, поэтому доступ не переходит к следующему значению. Однако для того, чтобы это работало, необходимо знать, что порядок байтов правильный.

Я бы сделал это, используя uint32_ts вместо массивов символов, но что угодно. Что-то вроде этого, то есть, хотя я не тестировал, вероятно, это неправильно:

uint32_t tmp, crc = init;
while (len--) { 
    tmp = crc32_table(((crc >> 24) & 0xff) ^ *buf++);
    crc = (crc << 8) ^ tmp; 
}

Использование этой предварительно рассчитанной таблицы является оптимизацией для вычисления CRC. Если вы посмотрите на определения, вы обычно увидите их как схемы, в которых один бит смещается с одного конца регистра, используемый для возможного переворачивания некоторых битов в регистре, а входной бит смещается одновременно. время. Поиск в таблице обрабатывает только эти 8 бит за раз; значения в таблице - это чистые биты, перевернутые в регистре при сдвиге этих 8 битов.

person ilkkachu    schedule 23.02.2021

Я отмечаю этот вопрос как ответ, так как здесь достаточно информации, чтобы решить мою проблему.

person momone50    schedule 23.02.2021
comment
Я понимаю, что вы здесь новичок, так что не беспокойтесь об изучении веревок. То, что вы здесь предоставили, является скорее комментарием, чем ответом. Правильные ответы должны быть полезны для других, у которых могут возникнуть аналогичные проблемы в будущем. Думайте о StackOverflow как о энциклопедии для вопросов, связанных с программированием. Я не говорю вам, что делать, но, на мой взгляд, вы должны удалить этот ответ и отметить другой ответ, который вам нравится, как решение. Надеюсь, ответы здесь будут вам полезны. - person h0r53; 23.02.2021