OpenSSL 1.0至3.0伪随机函数(PRF)移植后输出不一致问题
OpenSSL 3.0 PRF移植问题:第二次迭代输出与1.1版本不一致
将符合RFC5246第5节定义的*伪随机函数(PRF)*从OpenSSL 1.1版本移植到3.0版本时出现异常:使用相同输入数据调用新旧版本的PRF实现,第二次迭代的输出结果不一致。
代码实现
OpenSSL 1.1 API实现
#include <algorithm> #include <ios> #include <iomanip> #include <iostream> #include <sstream> #include <string> #include <vector> #include "openssl/core_names.h" #include "openssl/hmac.h" // Pseudo Random Function, see: RFC5246 Section 5 // use older v1.1 API bool PRF_Hash_11(const EVP_MD* hashmethod, const std::vector<uint8_t>& secret, const std::vector<uint8_t>& seed, size_t outputlen, std::vector<uint8_t>& output) { if (hashmethod == 0) return false; output.clear(); if (outputlen == 0) { return true; } //calculate how big the output needs to be int size = EVP_MD_size(hashmethod); size_t iterations = (outputlen / size); if ((outputlen % size) != 0) ++iterations; output.resize(size * iterations); std::vector<uint8_t> An(EVP_MAX_MD_SIZE); unsigned int accumulatorLen = 0; unsigned int offset = 0; unsigned int outputsize = 0; HMAC_CTX* ctx = HMAC_CTX_new(); HMAC_Init_ex(ctx, secret.data(), static_cast<int>(secret.size()), hashmethod, 0); //this context generates A(n) HMAC_CTX* ctx_hash = HMAC_CTX_new(); HMAC_Init_ex(ctx_hash, secret.data(), static_cast<int>(secret.size()), hashmethod, 0); //calculate A(1) HMAC_Update(ctx_hash, seed.data(), seed.size()); HMAC_Final(ctx_hash, An.data(), &accumulatorLen); for (size_t i = 1; i <= iterations; ++i) { //reinitialize the hash for the next iteration HMAC_Init_ex(ctx, 0, 0, 0, 0); HMAC_Update(ctx, An.data(), accumulatorLen); HMAC_Update(ctx, seed.data(), seed.size()); HMAC_Final(ctx, output.data() + offset, &outputsize); offset += outputsize; if (i < iterations) { //calculate the next A(i) HMAC_Init_ex(ctx_hash, 0, 0, 0, 0); HMAC_Update(ctx_hash, An.data(), accumulatorLen); HMAC_Final(ctx_hash, An.data(), &accumulatorLen); } } output.resize(outputlen); HMAC_CTX_free(ctx); HMAC_CTX_free(ctx_hash); return true; }
OpenSSL 3.0 API实现
// use 3.0 API bool PRF_Hash_30(const char* digname, int hash_size, const std::vector<uint8_t>& secret, const std::vector<uint8_t>& seed, size_t outputlen, std::vector<uint8_t>& output) { output.clear(); if (outputlen == 0) { return true; } //calculate how big the output needs to be size_t iterations = (outputlen / hash_size); if ((outputlen % hash_size) != 0) ++iterations; output.resize(hash_size * iterations); auto* ossl_ctxt = OSSL_LIB_CTX_new(); EVP_MAC* hmac = EVP_MAC_fetch(ossl_ctxt, "HMAC", NULL); if (hmac == NULL) { return false; } OSSL_PARAM params[2]; params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, (char*)digname, 0); params[1] = OSSL_PARAM_construct_end(); std::vector<uint8_t> An(EVP_MAX_MD_SIZE); size_t accumulatorLen = 0; size_t offset = 0; size_t outputsize = 0; // working context auto* wctx = EVP_MAC_CTX_new(hmac); if (wctx == NULL) { return false; } int res = EVP_MAC_init(wctx, secret.data(), secret.size(), params); // this context generates A(n) auto* hctx = EVP_MAC_CTX_new(hmac); if (hctx == NULL) { return false; } res = EVP_MAC_init(hctx, secret.data(), secret.size(), params); //calculate A(1) res = EVP_MAC_update(hctx, seed.data(), seed.size()); res = EVP_MAC_final(hctx, An.data(), &accumulatorLen, EVP_MAX_MD_SIZE); for (size_t i = 1; i <= iterations; ++i) { res = EVP_MAC_init(wctx, 0, 0, params); //reinitialize the hash for the next iteration res = EVP_MAC_update(wctx, An.data(), accumulatorLen); res = EVP_MAC_update(wctx, seed.data(), seed.size()); res = EVP_MAC_final(wctx, output.data() + offset, &outputsize, EVP_MAX_MD_SIZE); offset += outputsize; if (i < iterations) { //calculate the next A(i) res = EVP_MAC_init(hctx, 0, 0, params); //reinitialize the hash for the next iteration res = EVP_MAC_update(hctx, An.data(), accumulatorLen); res = EVP_MAC_final(hctx, An.data(), &accumulatorLen, EVP_MAX_MD_SIZE); } } output.resize(outputlen); EVP_MAC_CTX_free(hctx); EVP_MAC_CTX_free(wctx); EVP_MAC_free(hmac); OSSL_LIB_CTX_free(ossl_ctxt); return true; }
测试用例
int main() { const std::vector<uint8_t> secret { 0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; const std::vector<uint8_t> seed { 0x6b, 0x65, 0x79, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0xb1, 0x26, 0x07, 0x8d, 0xec, 0x68, 0x2e, 0x69, 0xe7, 0x6d, 0xc1, 0xaa, 0x88, 0xcb, 0xfe, 0xa5, 0x94, 0xd9, 0x82, 0xa6, 0x0f, 0x2d, 0x32, 0xa0, 0xde, 0x7e, 0x55, 0x8d, 0xf8, 0x1b, 0x83, 0x80, 0xfa, 0x42, 0xa3, 0x0b, 0x6c, 0xad, 0xbf, 0x24, 0x78, 0xb2, 0xbb, 0xfa, 0xa3, 0x5a, 0x98, 0x84, 0xe4, 0x54, 0x10, 0x2b, 0x46, 0xc0, 0x91, 0x34, 0x96, 0x2e, 0x64, 0x34, 0x73, 0xba, 0x3d, 0xa1}; // use SHA-384 and 72 bytes out for this test const EVP_MD* hashmethod = EVP_sha384(); int hash_size = EVP_MD_size(hashmethod); const char * digname = OSSL_DIGEST_NAME_SHA2_384; constexpr size_t bufsize = 0x48; // # output bytes needed std::vector<uint8_t> output_v1, output_v3; output_v1.reserve(bufsize); output_v3.reserve(bufsize); // generate output using old and new API bool ret = PRF_Hash_11(hashmethod, secret, seed, output_v1.capacity(), output_v1); ret = PRF_Hash_30(digname, hash_size, secret, seed, output_v3.capacity(), output_v3); // print output auto prnthex = [](std::vector<uint8_t> v) { std::stringstream stream; for (auto e : v) { stream << ' ' << std::hex << (e / 16) << (e & 0x0F); } std::cout << stream.str() << std::endl; }; prnthex(output_v1); prnthex(output_v3); auto pr = std::mismatch(output_v1.begin(), output_v1.end(), output_v3.begin(), output_v3.end()); std::cout << std::endl << " mismatch at output[" << std::distance(output_v1.begin(), pr.first) << ']' << std::endl; }
测试输出
21 50 34 fe f0 c6 de 4b e8 e6 df 65 21 36 11 a0 24 30 18 a5 42 74 d8 e5 5f 82 41 ed 4a ef 55 27 e0 b6 c4 fc 95 a8 10 be 35 b9 94 06 3a c9 59 56 4c 4c 2c 2a 01 d2 38 c3 a7 80 40 20 b3 5a 98 7f f3 9d 9c 72 96 b0 61 39 21 50 34 fe f0 c6 de 4b e8 e6 df 65 21 36 11 a0 24 30 18 a5 42 74 d8 e5 5f 82 41 ed 4a ef 55 27 e0 b6 c4 fc 95 a8 10 be 35 b9 94 06 3a c9 59 56 80 50 68 12 4e 46 25 e0 47 91 98 10 08 68 0c ba fa 35 73 9f b1 63 13 af mismatch at output[48]
作为OpenSSL新手,我无法定位问题所在,且感觉OpenSSL文档难以理解,希望能得到帮助。
内容的提问来源于stack exchange,提问作者darnell_a
相关产品推荐
相关产品推荐

