Я пытаюсь создать подпись ECDSA, и большую часть времени она создается идеально, но иногда она усекается случайным образом, например, значения r и s должны быть равны 48, но иногда я получаю 47 для одного и 48 для другого. Я не могу сказать, в чем проблема: проблема с подписью или проблема с процедурой, используемой для получения значений r и s из подписи?
const BIGNUM *r;
const BIGNUM *s;
ECDSA_SIG_get0(signature, &r, &s);
char *R = BN_bn2hex(r);
char *S = BN_bn2hex(s);
int r_len = BN_num_bytes(r);
int s_len = BN_num_bytes(s);
uint8_t *r_bytes = (uint8_t *)malloc(r_len);
uint8_t *s_bytes = (uint8_t *)malloc(s_len);
BN_bn2bin(r, r_bytes);
BN_bn2bin(s, s_bytes);
int signature_len = r_len + s_len;
uint8_t *signature2 = (uint8_t *)malloc(signature_len);
memcpy(signature2, r_bytes, r_len);
memcpy(signature2 + r_len, s_bytes, s_len);
int max_encoded_len = EVP_ENCODE_LENGTH(signature_len);
*ppcSignature1 = (char *)calloc(max_encoded_len,1);
Я попытался снова получить значения r и s из той же сигнатуры, добавив этот блок кода:
if (r_len != 48 || s_len != 48){
const BIGNUM *r_temp = ECDSA_SIG_get0_r(signature);
const BIGNUM *s_temp = ECDSA_SIG_get0_s(signature);
char *R_temp = BN_bn2hex(r_temp);
char *S_temp = BN_bn2hex(s_temp);
printf("R_LEN : %s\n",R_temp);
printf("S_LEN : %s\n",S_temp);
}
И все же значения рлена и слена были одинаковыми.
@ThomasJager Я думаю, что это примерно 1 раз из 256.
Похоже, проблема не в усечении подписи, а скорее в интерпретации значения, возвращаемого BN_num_bytes
(обратите внимание, что это макрос, похожий на функцию).
BN_num_bytes
указывает, сколько байтов потребуется целочисленному значению для хранения числа в BIGNUM
. Это не означает, что подпись состоит из меньшего количества байт, это все равно 48-байтовое значение. Но если BN_num_bytes
возвращает 47, то вы знаете, что при интерпретации этого числа как имеющего 48 байт старшие 8–15 бит значения будут равны 0.
Если у вас есть подпись со значением 0x34529
, вы получите значение 3 из BN_num_bytes
, поскольку для представления этого числа вам нужно всего три байта. Другими словами, он имеет 3 значащих байта.
Из примечаний на странице с описанием BN_num_bytes
:
Некоторые пытались использовать BN_num_bits() для отдельных чисел в ключах RSA, ключах DH и ключах DSA и обнаружили, что они не всегда получают ожидаемое количество бит (что-то вроде 512, 1024, 2048,...). . Это связано с тем, что генерация числа с определенным количеством битов не всегда устанавливает старшие биты, тем самым делая количество значимых битов немного меньшим. Если вы хотите узнать «размер ключа» такого ключа, либо используйте такие функции, как RSA_size(), DH_size() и DSA_size(), либо используйте BN_num_bytes() и умножьте на 8 (хотя нет реальной гарантии, что это будет соответствовать размеру ключа). «размер ключа», просто гораздо больше вероятности).
В документации отмечается проблема с использованием BN_num_bits
для получения размера ключа в битах, что имеет вероятность 50/50 фактического соответствия ожидаемому размеру ключа. Затем он отмечает, что вы можете использовать BN_num_bytes
, но это лишь с большей вероятностью даст вам правильный результат, а не гарантировано (вероятность неправильного результата от 1/2 до 1/256).
BN_num_bytes
может быть полезен для распределения места, но не даст вам информации о характере подписи. Это универсальный инструмент, связанный с BIGNUM
, он не «знает», насколько велика ваша подпись, он просто видит число.
Спасибо за вашу помощь! Я хотел получить значения BIGNUM r и s подписи ecdsa в структуре ECDSA_SIG, как мне тогда это сделать? Есть ли альтернативный способ?
@AjithNair Разве ты не этим уже занимаешься? Я сосредоточился на проблеме BN_num_bytes
, но похоже, что вы правильно получаете подписи в первом блоке кода. Если проблема в том, что разные размеры нарушают кодировку, которую вы делаете после этого, я бы предложил вместо этого выделить новый signature2
для подписей размером 48 байт (игнорируя значения, которые вы получаете от BN_num_bytes
, вместо этого signature_len = 48 + 48
), а затем скопировать что-то вроде signature2 + (48 - r_len)
и signature2 + r_len + (48 - s_len)
(или какой-нибудь более чистый вариант).
(Также обязательно обнулите те байты, которые не заполнены подписями.)
@AjithNair Что вы подразумеваете под словом «усечено»? Было ли около 96 символов? Вы также не используете значения, возвращаемые BN_bn2hex
, они показаны только для использования во втором блоке кода.
Да, вы правы, спасибо за помощь
Как часто кажется, что его усекают? Может ли это быть 1 раз из 256? Вы не считаете «размер» подписи,
BN_num_bytes
дает количество значащих байтов. Если самый старший байт окажется равным 0, чего вы ожидаете?