openSSL: импорт подписи ECDSA из необработанных данных со сжатым CurvePoint R вместо r

Я хочу проверить некоторые пакеты данных, которые содержат подпись ECDSA, не закодированную ASN.1 DER.

Я пытаюсь выполнить задачу с помощью openSSL (версия 1.1.0), поскольку он, кажется, обеспечивает все необходимые функции.

В настоящее время я застрял с импортом подписи из пакета данных. Подпись выглядит так:

подпись, показанная в Wireshark

(Последние 4 байта не должны быть отмечены как часть подписи)

TShark показывает соответствующую часть следующим образом:

    "gn.sh.sig": {
      "gn.st.pka": "0",
      "gn.st.eccpointtype": "3",
      "gn.st.opaque": "1f:f8:ad:a7:fd:99:b2:a8:a2:69:d4:d6:67:e5:ee:5b:3f:8b:e8:7f:49:fc:b4:79:95:98:15:0d:cb:bd:06:3c",
      "gn.st.opaque": "61:a4:6a:c9:a7:dd:bd:0d:9c:0a:0e:73:db:52:06:bb:79:53:99:51:16:45:45:81:4e:df:94:5c:58:59:40:14"
    }

Кодировка соответствует ETSI TS 103 097 v1.2.1.

Первая запись opaque вместе с ECC Point type образуют EccPoint с именем R, а второе поле opaque называется s.

Мой первый шаг — создать подпись в openSSL и импортировать в нее данные.

Мой код (обработка ошибок удалена для уменьшения размера) выглядит так:

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/bn.h>

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

void verify_signature(void)
{
  int res;

  // Which value is correct for r-part?
  static const char sig_r_hex[] = "1ff8ada7fd99b2a8a269d4d667e5ee5b3f8be87f49fcb4799598150dcbbd063c";
  static const char sig_s_hex[] = "61a46ac9a7ddbd0d9c0a0e73db5206bb79539951164545814edf945c58594014";

  BN_CTX *bn_ctx = BN_CTX_new();
  BN_CTX_start(bn_ctx);

  // Create signature and import from packet.
  BIGNUM *bn_r = BN_CTX_get(bn_ctx);
  BIGNUM *bn_s = BN_CTX_get(bn_ctx);

  res = BN_hex2bn(&bn_r, sig_r_hex);
  res = BN_hex2bn(&bn_s, sig_s_hex);

  ECDSA_SIG *signature = ECDSA_SIG_new();
  res = ECDSA_SIG_set0(signature, bn_r, bn_s);
  printf("ECDSA_SIG_set0(): %d\n", res);

  // ...
  // Create hash of message and verify signature with public key
  //...
}

int main(int arc, char *argv[])
{ 
  ERR_load_crypto_strings();
  OpenSSL_add_all_algorithms();
  OPENSSL_config(NULL);

  verify_signature();

  EVP_cleanup();
  CRYPTO_cleanup_all_ex_data();
  ERR_free_strings();
  return 0;
}

Чего мне сейчас не хватает, так это того, как правильно обрабатывать значение R. Какие части сжатого CurvePoint следует использовать как r для инициализации подписи? Нужно ли распаковывать часть y? Должен ли я добавить некоторое указание для сжатого значения y?

Любые подсказки приветствуются.

Редактировать:

Из того, что я прочитал в IEEE1609.2 и SEC-1, можно добавить дополнительную информацию, чтобы обеспечить более быстрое вычисление R из r. Это может быть сама точка R. Поскольку r = xR mod n должно быть возможно вычислить r из EccPoint.

Поддерживает ли openSSL этот механизм для ускорения проверки или мне действительно нужно удалить дополнительную информацию (и узнать, как это сделать), чтобы передать r в openSSL?


person Gerhardh    schedule 06.11.2017    source источник


Ответы (1)


Разделы 4.2.5 -- 4.2.8 в http://www.etsi.org/deliver/etsi_ts/103000_103099/103097/01.02.01_60/ts_103097v010201p.pdf укажите следующие структуры (где extern означает, что данные не представлены буквально):

enum {
     x_coordinate_only(0),
     compressed_lsb_y_0(2),
     compressed_lsb_y_1(3),
     uncompressed(4),
     (2^8-1)
} EccPointType;

struct {
 extern PublicKeyAlgorithm algorithm;
 extern uint8 field_size;
 EccPointType type;
 opaque x[field_size];
 select(type) {
     case x_coordinate_only:
     case compressed_lsb_y_0:
     case compressed_lsb_y_1:
         ;
     case uncompressed:
         opaque y[field_size];
     unknown:
         opaque data<var>;
     }
} EccPoint;

struct {  
    extern PublicKeyAlgorithm algorithm;  
    extern uint8 field_size;  
    EccPoint R;  
    opaque s[field_size]; 
} EcdsaSignature;

struct {
    PublicKeyAlgorithm algorithm;
    select(algorithm) {
        case ecdsa_nistp256_with_sha256:
            EcdsaSignature ecdsa_signature;
        unknown:
            opaque signature<var>;
    }
} Signature;

Вы знаете, что это ECDSA на P-256, использующий SHA256, и что у нас есть "сжатая точка y-1". Давайте сначала разрешим все «выборочные» части этих структур, используя то, что мы знаем, и удалим все «внешние» поля:

struct {
    PublicKeyAlgorithm algorithm; // 1 byte = 0x00 ("ecdsa, P-256, sha256")
    {                             //EcdsaSignature ecdsa_signature
        {                         //EccPoint R
            EccPointType type;        //1 byte = 0x03 ("compressed lsb y-1")
            opaque x[field_size];     //these are the first 32 opaque bytes
            ;                         // select(type) resolves to nothing.
        }  
        opaque s[field_size];     //these are the second 32 opaque bytes
    }
} Signature;

Хорошо. Это проще. Теперь мы знаем, что такое все байты. Так что же это за дело о «сжатых точках»? Суть точечного сжатия заключается в том, чтобы свести к минимуму количество данных, которые должны быть отправлены по сети. Получается, что, зная координату X точки на кривой NIST, вы можете найти координату Y, но есть 2 возможных ответа. Некоторые умные люди в Certicom заметили, что две возможные координаты Y, которые идут с заданной координатой X, всегда отличаются своим младшим значащим битом — таким образом, хотя они должны отправить все X, они могут обойтись только отправкой LSb из Y (т. е. достаточно информации, чтобы вы знали, какое из двух решений для Y является правильным). Таким образом, EccPointType «сжатого младшего бита y-1» говорит вам, что для вашей координаты X правильной координатой Y является та, которая имеет 1 в младшем бите.

Лучшая новость об этом заключается в том, что вам не нужно заботиться. ECDSA использует только координату X точки R, которая находится в первом непрозрачном двоичном объекте с пометкой «x».

Таким образом, это было многословное объяснение, чтобы сказать: «Вам не нужно ничего делать. Вы уже держите правильные r (= X координата R) и s для ввода в ECDSA_do_verify OpenSSL.

person lockcmpxchg8b    schedule 22.11.2017