首页 > 解决方案 > 使用 OpenSSL 的 KAS-ECC OnepassDH

问题描述

我正在寻找使用 OpenSSL 为 KAS-ECC OnepassDH 模式开发代码。这样做的逻辑可以在SP-800-56A中找到。附图显示了完成此操作的步骤:

在此处输入图像描述

我试过这样做,在下面显示了我的步骤:

make_peer() {
    peer = EC_POINT_new(group);
    c = BN_CTX_new();
    EC_POINT_set_affine_coordinates_GFp(group, peer, x, y, c);
}

onepassDH() {
                unsigned char Zs[256];
                unsigned char Ze[256];

                // derive private key from input public key
                qsCAVSx = BN_bin2bn(qsCAVSx, qsCAVSxLength, qsCAVSx);
                qsCAVSy = BN_bin2bn(qsCAVSy, qsCAVSyLength, qsCAVSy);
                peerkey = make_peer(group, qsCAVSx, qsCAVSy);
                if(NULL == peerkey)
                   goto fail;

                // Other party key
                ecKey = EC_KEY_new();
                if ( ecKey == NULL  ) {
                   LOG_ERROR("failed to create ecKey\n");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }
                EC_KEY_set_flags(ecKey, EC_FLAG_COFACTOR_ECDH);  

                if ( 0 == EC_KEY_set_group(ecKey, group) ) {
                   LOG_ERROR("cannot set group\n");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                if ( 0 == EC_KEY_generate_key(ecKey) ) {
                   LOG_ERROR("cannot generate key\n");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                //extract public key from other party's key pair

                ec_ctx = BN_CTX_new();
                if ( !ec_ctx ) {
                   LOG_ERROR("BN_CTX_new() returned NULL in %s at line %d\n", __FILE__, __LINE__);
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }
                qeIUTx = BN_CTX_get(ec_ctx);
                qeIUTy = BN_CTX_get(ec_ctx);
                if ( !qeIUTx || !qeIUTy ) {
                   LOG_ERROR("BN_CTX_get() returned NULL in %s at line %d\n", __FILE__, __LINE__);
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                grp = EC_KEY_get0_group(ecKey);
                if ( grp == NULL ) {
                   LOG_ERROR("EC_KEY_get0_group() returned NULL!");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                pt = EC_KEY_get0_public_key(ecKey);
                if ( pt == NULL ) {
                   LOG_ERROR("EC_KEY_get0_public_key() returned NULL!");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                meth = EC_GROUP_method_of(grp);
                if ( meth == NULL ) {
                   LOG_ERROR("EC_GROUP_method_of() returned NULL\n");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                // this uses "grp", "pt", and "ctx" to set "tx" and "ty"
                EC_POINT_get_affine_coordinates_GFp(grp, pt, qeIUTx, qeIUTy, ec_ctx);


                qeIUTxLength  = BN_num_bytes(qeIUTx);

                qeIUTyLength  = BN_num_bytes(qeIUTy);

                zLength = (EC_GROUP_get_degree(group) + 7)/8;

                ECDH_compute_key(Zs, zLength, peerkey, ecKey, 0);

                // Generate ephemeral key
                de_Key = EC_KEY_new();
                if ( de_Key == NULL  ) {
                   LOG_ERROR("failed to create ecKey\n");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }
                EC_KEY_set_flags(de_Key, EC_FLAG_COFACTOR_ECDH);  // <--- this is the FIPS-dependant part

                if ( 0 == EC_KEY_set_group(de_Key, group) ) {
                   LOG_ERROR("cannot set group\n");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                if ( 0 == EC_KEY_generate_key(de_Key) ) {
                   LOG_ERROR("cannot generate key\n");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                ec_ctx = BN_CTX_new();
                if ( !ec_ctx ) {
                   LOG_ERROR("BN_CTX_new() returned NULL in %s at line %d\n", __FILE__, __LINE__);
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }
                qeIUTx = BN_CTX_get(ec_ctx);
                qeIUTy = BN_CTX_get(ec_ctx);
                if ( !qeIUTx || !qeIUTy ) {
                   LOG_ERROR("BN_CTX_get() returned NULL in %s at line %d\n", __FILE__, __LINE__);
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                grp = EC_KEY_get0_group(de_Key);
                if ( grp == NULL ) {
                   LOG_ERROR("EC_KEY_get0_group() returned NULL!");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                pt = EC_KEY_get0_public_key(de_Key);
                if ( pt == NULL ) {
                   LOG_ERROR("EC_KEY_get0_public_key() returned NULL!");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                meth = EC_GROUP_method_of(grp);
                if ( meth == NULL ) {
                   LOG_ERROR("EC_GROUP_method_of() returned NULL\n");
                   LOG_ERROR("OpenSSL reports:   %s\n", ERR_error_string(ERR_get_error(), NULL));
                   goto fail;
                }

                // this uses "grp", "pt", and "ctx" to set "tx" and "ty"
                EC_POINT_get_affine_coordinates_GFp(grp, pt, qeIUTx, qeIUTy, ec_ctx);
                ECDH_compute_key(Ze, zLength, de_priv_key, ecKey, 0);

                zLength = 2 * ((EC_GROUP_get_degree(group) + 7)/8);
                memcpy(Z, Ze, zLength/2);
                memcpy(Z+(zLength/2), Zs, zLength/2);

                ECDH_compute_key(Z, zLength, peerkey, ecKey, 0);
                // Hash the sharesecret
                ctx = EVP_MD_CTX_create();
                if ( 0 == EVP_DigestInit_ex(ctx, md, NULL) ) {
                   LOG_ERROR("error in %s, line%d; sha_type %d\n", __FILE__, __LINE__, md->md_size * 8);
                   ERR_print_errors_fp(stderr);
                   goto fail;
                }
                if ( 0 == EVP_DigestUpdate(ctx, Z, zLength) ) {
                   LOG_ERROR("error in %s, line%d\n", __FILE__, __LINE__);
                   ERR_print_errors_fp(stderr);
                   goto fail;
                }
                if ( 0 == EVP_DigestFinal_ex(ctx, iutTag, &count) ) {
                   LOG_ERROR("error in %s, line%d\n", __FILE__, __LINE__);
                   ERR_print_errors_fp(stderr);
                   goto fail;
                }
        }
}

此测试的输入是:

[EC - SHA256]

COUNT = 0
QsCAVSx = f49fdbdc47a34d17005690ff1d66cdc6862276fdf819a1da3595b0d5e04ecbba
QsCAVSy = 4e182b6e7a644adab028ad089273eb72ac8c2b3834f80b198882bab53c9f856d

KAS-ECC alg 的问题是我在网上看不到太多帮助来实现这一点。我找到了几个链接:link1link2 ,但对我为 OnePassDH 编写代码没有多大帮助。有没有人可以告诉我我的代码似乎有什么问题?现在,代码本身不会引发错误,但会为任何输入提供相同的结果。

标签: openssl

解决方案


概括

我已经设法通过 OpenSSL 实现 ECDH 并使用CAVP测试向量进行测试。我希望这个答案会有所帮助。


算法描述

注意:下面的伪代码只是一个可以遵循的路径,因为它是从更复杂的系统简化而来的。不会有很多确切的类型被提及或错误检查完成。

摘要:从曲线创建EC_GROUP,从字节数组中创建EC_KEY,EVP_PKEY结构用于公钥和私钥,创建EVP_PKEY_CONTEXT用于DH操作,派生密钥。

  1. 按曲线名称创建 EC_GROUP:

    auto order = EC_GROUP_new_by_curve_name(NID_curve)
    
  2. 创建私钥(这只是一个随机数):

    auto prvKey = BN_new();
    auto order = BN_new();
    EC_GROUP_get_order(group, order, nullptr);
    BN_rand_range(prvKey, order);
    
  3. 获取对方公钥:

    std::vector<uint8_t> otherKey = getOtherKey();
    
  4. 创建和初始化上下文:

    //Create EC_KEY
    auto key = EC_KEY_new();
    EC_KEY_set_group(key, group);
    ECDSA_set_method(key, ECDSA_get_default_method());
    EC_KEY_set_private_key(key, prvKey);
    
    //Create EVP_PKEY and set EC_KEY there
    auto pkey = EVP_PKEY_new();
    EVP_PKEY_set1_EC_KEY(pkey, key);
    
    //Create EVP_PKEY for public key:
    auto publicEcKey = EC_KEY_new();
    EC_KEY_set_group(publicEcKey, group);
    ECDSA_set_method(publicEcKey, ECDSA_get_default_method());
    EC_POINT point = EC_POINT_new(group);
    EC_POINT_oct2point(group, point, otherKey.data(), otherKey.size());
    EC_KEY_set_public_key(publicEcKey, point);
    auto publicEvpPkey = EVP_PKEY_new();
    EVP_PKEY_set1_EC_KEY(publicEvpPkey, publicEcKey);
    
    
    //Create EVP_PKEY_CTX
    auto pkeyCtx = EVP_PKEY_CTX_new(pkey, nullptr);
    EVP_PKEY_derive_init(pkeyCtx);
    
  5. 导出秘籍:

    EVP_PKEY_derive_set_peer(pkeyCtx, publicEvpPkey);
    size_t length = 0;
    EVP_PKEY_derive(pkeyCtx, nullptr, &length);
    std::vector<uint8_t> secret(length);
    EVP_PKEY_derive(pkeyCtx, secret.data(), &length);
    

如何使用 CAVP 测试向量 示例如下所示:

[P-192]

COUNT = 0
QCAVSx = 42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0
QCAVSy = dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523
dIUT = f17d3fea367b74d340851ca4270dcb24c271f445bed9d527
QIUTx = b15053401f57285637ec324c1cd2139e3a67de3739234b37
QIUTy = f269c158637482aad644cd692dd1d3ef2c8a7c49e389f7f6
ZIUT = 803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0

这里 [QCAVSx, QCAVsy] 和 [QIUTx, QIUTy] - 分别是 CAVS(distant user) 和 IUT(user) 的公共向量。为了将此数据用作 OpenSSL 的公钥,我通过连接它们并在开头添加“04”(04 是未压缩形式的标记)从两个坐标创建了未压缩形式:04[x-coordinate][y-ccordinate ]。dIUT 是 IUT 的私钥。ZIUT 是一个秘密,它是参考结果。

我已经将 dIUT 和未压缩的 QCAVS 公钥视为字节数组,通过 BN_bin2bn() 函数与 OpenSSL 一起使用,并且在上述算法的帮助下,我的测试成功了。


推荐阅读