首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在P1363 ECDSA签名中区分ASN.1和OpenSSL编码

如何在P1363 ECDSA签名中区分ASN.1和OpenSSL编码
EN

Stack Overflow用户
提问于 2022-10-27 10:38:31
回答 1查看 60关注 0票数 0

ECDSA签名编码r值和s值的方式没有很好的定义:例如,OpenSSL专用于使用DER编码的ASN.1序列,而Windows则使用IEEE P1363编码(详见this excellent SO answer )。

为了使用OpenSSL启用ECDSA签名验证,我修补了ossl_ecdsa_verify(..)在ec/ecdsa_assl.c中(在OpenSSL 3中,即;在1.0.2中是ECDSA_verify(.)在ecdsa/ecs_vrf.c中)。(有关调试修补程序的相关问题,请参见this question of mine。)

股票代码是这样做的:

代码语言:javascript
运行
复制
/*-
 * returns
 *      1: correct signature
 *      0: incorrect signature
 *     -1: error
 */
int ossl_ecdsa_verify(int type, const unsigned char *dgst, int dgst_len,
                      const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
{
    ECDSA_SIG *s;
    const unsigned char *p = sigbuf;
    unsigned char *der = NULL;
    int derlen = -1;
    int ret = -1;

    s = ECDSA_SIG_new();
    if (s == NULL)
        return ret;
    if (d2i_ECDSA_SIG(&s, &p, sig_len) == NULL)
        goto err;
    /* Ensure signature uses DER and doesn't have trailing garbage */
    derlen = i2d_ECDSA_SIG(s, &der);
    if (derlen != sig_len || memcmp(sigbuf, der, derlen) != 0)
        goto err;
    ret = ECDSA_do_verify(dgst, dgst_len, s, eckey);
 err:
    OPENSSL_free(der);
    ECDSA_SIG_free(s);
    return ret;
}

我的补丁是这样的:

代码语言:javascript
运行
复制
int ossl_ecdsa_verify(int type, const unsigned char *dgst, int dgst_len,
                      const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
{
    ECDSA_SIG *s;
    const unsigned char *p = sigbuf;
    unsigned char *der = NULL;
    int derlen = -1;
    int ret = -1;

    s = ECDSA_SIG_new();
    if (s == NULL)
        return ret;
#ifdef P1363_PATCH
    if (d2i_ECDSA_SIG(&s, &p, sig_len) == NULL) {
        /*
         * ASN.1 decoding failed, see crypto/asn1/tasn_dec.c line 515ff.
         * Assume s is encoded as IEEE P1363. for a comprehensive description see
         * ttps://stackoverflow.com/questions/36542645/does-openssl-sign-for-ecdsa-apply-asn1-encoding-to-the-hash-before-signing
         * Fill the ECDSA_SIG from the P1363.
         */
        if ((sig_len % 2) != 0)
            return (ret);
        if (s == NULL)
            s = ECDSA_SIG_new();
        if (s == NULL)
            return (ret);
        /*
         * Prepare a buffer large enough to hold either r or s part of the P1363.
         * Add 1 to the size to allow for padding if needed.
         * Define some variables for pointer arithmetic.
         */
        int buf_size = sig_len / 2 + 1;
        void *buf = malloc(buf_size);
        const unsigned char *sigbuf_half = sigbuf + sig_len / 2;
        const unsigned char *sigbuf_full = sigbuf + sig_len;
        /*
         * Skip possible padding of the r part of the P1363.
         * I /think/ only the s part may be padded, but it does no harm to skip them
         * for the r part, too.
         */
        const unsigned char *q = sigbuf;
        while (*q == '\0' && q < sigbuf_half)
            q++;
        int buf_len = sigbuf_half - q;
        /*
         * Prepare buf for BIGNUM creation.
         */
        memcpy(buf, q, buf_len);
        if (*(char*)buf & 0x80) {
            /* Add padding if needed to assert positive integer. */
            memmove((char*)buf + 1, buf, buf_len);
            memset(buf, '\0', 1);
            buf_len++;
        }
        /*
         * Finally create the BIGNUM and put it in the r part of the ECDSA_SIG.
         */
        s->r = BN_bin2bn((const unsigned char *)buf, buf_len, NULL);

        /*
         * Now do the same for the s part...
         */
        q = sigbuf_half;
        while (*q == '\0' && q < sigbuf_full)
            q++;
        buf_len = sigbuf_full - q;
        memcpy(buf, q, buf_len);
        if (*(char*)buf & 0x80) {
            /*Add padding if needed to assert positive integer.  */
            memmove((char*)buf + 1, buf, buf_len);
            memset(buf, '\0', 1);
            buf_len++;
        }
        s->s = BN_bin2bn((const unsigned char *)buf, buf_len, NULL);

        free(buf);
    }
    else {
        /* Ensure signature uses DER and doesn't have trailing garbage */
        derlen = i2d_ECDSA_SIG(s, &der);
        if (derlen != sig_len || memcmp(sigbuf, der, derlen))
            goto err;
    }
    ret = ECDSA_do_verify(dgst, dgst_len, s, eckey);
 err:
    if (derlen > 0) {
        OPENSSL_cleanse(der, derlen);
        OPENSSL_free(der);
    }
#else
    if (d2i_ECDSA_SIG(&s, &p, sig_len) == NULL)
        goto err;
    /* Ensure signature uses DER and doesn't have trailing garbage */
    derlen = i2d_ECDSA_SIG(s, &der);
    if (derlen != sig_len || memcmp(sigbuf, der, derlen) != 0)
        goto err;
    ret = ECDSA_do_verify(dgst, dgst_len, s, eckey);
 err:
    OPENSSL_free(der);
#endif /* P1363_PATCH */
    ECDSA_SIG_free(s);
    return ret;
}

这允许我验证P1363编码的ECDSA签名。sigbuf中的垃圾仍然被ECDSA_do_verify(..)捕获。

但是,OpenSSL提供的ecdsa测试在修补程序中失败:

代码语言:javascript
运行
复制
15-test_ecdsa.t ....................
        # INFO:
        # testing ECDSA for curve secp112r1 as EC key type
        # ERROR: (int) 'EVP_DigestVerify(mctx, sig, sig_len - 1, tbs, sizeof(tbs)) == -1' failed @ ..\..\..\3rdparty\openssl-3.0.5-RIB\test\ecdsatest.c:262
        # [0] compared to [-1]
        # 442C0000:error:0800009C:elliptic curve routines:ossl_ecdsa_simple_verify_sig:bad signature:..\..\..\3rdparty\openssl-3.0.5-RIB\crypto\ec\ecdsa_ossl.c:482:
        # OPENSSL_TEST_RAND_ORDER=1666859286
        not ok 1 - iteration 1

对每一条曲线都是如此。我还不能完全分析,但我认为这是由于阴性测试失败造成的。这可能是因为测试提供的垃圾现在在我的代码中运行,并且不会绕过对ECDSA_do_verify(..)的调用。对吗?

为了使测试成功,并且作为对我的代码的一般改进,库存OpenSSL只区分2种情况(ASN.1或垃圾),现在我需要区分3种情况(ASN.1、P1363或垃圾)。,一旦ASN.1解码失败,有没有办法区分P1363和垃圾?

EN

回答 1

Stack Overflow用户

发布于 2022-11-03 20:35:09

显而易见的事情要做(我想!)尝试按OpenSSL的方式解码,检查是否消耗了整个八进制字符串,并确保使用最小字节数正确编码了两个INTEGER值(这意味着它们不能让前9位全部为零或全部为零)。如果是,那么假设ASN.1/DER,否则尝试MSFT方式。您可以做的另一件事是检查八进制字符串的长度是否为64字节:如果不是64字节,则不能用MSFT方式对其进行编码,但是,如果是64个字节,那么r的前导字节可能看起来就像SEQUENCE的标记长度,然后是r INTEGER的标记长度(它必须足够小到需要在MSFT编码中填充零),然后s也需要它的前两个字节看起来像INTEGER的标签长度,而这个值也需要30个字节+两个零填充的零填充。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74220741

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档