• 信息摘要算法之六:HKDF算法分析与实现


    HKDF是一种特定的键衍生函数(KDF),即初始键控材料的功能,KDF从其中派生出一个或多个密码强大的密钥。在此我们想要描述的是基于HMAC的HKDF。

    1HKDF概述

    密钥派生函数(KDF)是密码系统的基本组成部分。它的目标是获取一些初始的密钥材料,并从中派生出一个或多个安全强度很大的密钥。

    我们将要介绍的是基于HMAC的KDF,称之为HKDF,它可以应用于各种协议和应用程序的构建。HKDF在逻辑上由两个部分组成。第一个部分采用输入密钥材料和“提取”,它是一个固定长度的伪随机密钥K。第二部分则将密钥扩展为几个随机密钥,并以我们需要的长度输出。

    在许多应用程序中,输入的密钥材料不一定是均匀分布的,而且攻击者可能对它有一定的了解,甚至可以部分控制它。因此,“提取”阶段的目标是将输入密钥材料的可能分散的熵“集中”到一个短的,但强度高的伪随机密钥。在某些应用程序中,输入可能已经是一个好的伪随机密钥;在这些情况下,“提取”部分是不必要的,“扩展”部分可以单独使用。

    第二阶段“扩展”伪随机密钥到所需的长度;输出密钥的数量和长度取决于所需密钥的具体加密算法。

    2、算法描述

    HKDF就是利用实例化了的哈希函数的HMAC来实现密钥生成。HMAC有两个必要的参数:第一个是密钥,第二个是输入(或消息)。当消息由几个元素组成时,我们在第二个参数中使用串联(表示|);例如,HMAC(K, elem1|elem2|elem3)。

    一般来说,HKDF算法所做的工作分为2步,其一称之为提取;其二称之为扩展。接下来我们说明这两步。

    (1)、提取

    所谓提取就是将用户输入的密钥尽量的伪随机化。一般是使用一个哈希函数来实现,具体那种更具需要在HMAC中确定。这一过程可以表示如下:

    HKDF-Extract(salt, IKM)得到PRK,而PRK的计算公式如下:

    PRK = HMAC-Hash(salt, IKM) ,这其中有2个输入和1个输出,

    Salt,输入,加盐操作的盐,如果不提供则全部初始化为0的字符串,长度则为所采用哈希函数的散列值长度。

    IKM,输入,数额udemiyao材料。

    PRK,输出,伪随机化后的密钥,长度则为所采用哈希函数的散列值长度。

    需要注意的是,在这一步中“IKM”被用作HMAC输入,而不是HMAC密钥。

    (2)、扩展

    所谓扩展,其实就是通过一系列的哈希运算将密钥扩展到我们需要的长度。这个长度我们记为L。这一过程标识如下:

    HKDF-Expand(PRK, info, L) -> OKM,其中有3个输入量和1个输出量,其中:

    PRK,输入,一般是在提取阶段得到的输出,是一个伪随机的密钥,长度不小于所采用的哈希算法的输出摘要长度。

    Info,输入,可选上下文和应用程序特定信息(可以是零长度字符串)

    L,输入,以字节计算的密钥原料的长度,一般不长于哈希函数输出摘要长度的255倍。

    OKM,输出,长度为L的密钥材料输出,其计算方式如下:

    计算一系列的哈希值,我们记为T(n),其中n为0-255的整数,具体取值由所计算的长度L来确定。n的最大值为:L除以所用哈希函数输出摘要的长度,再向上取整,我们将其记为N。

    T(0) = 长度为0的空字符串

    T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)

    T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)

    T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)

    直到T(N)为止,需要注意的是,每个哈希操作都会串接一个字节的常量,从1到N,所以最大的N值只能到255。然后将计算得到的这些T(n)串接起来,我们记为T,即:

    T = T(1) | T(2) | T(3) | ... | T(N)

    而我们想要获得的OKM正是取T的前L个字节组成。这要我们就得到了我们想要的密钥材料。

    3、代码实现

    上面我们已经描述了HKDF的实现过程,接下来我们将实现这一过程。首先,我们任然是定义一个上下文结构。

    /** 该结构将为HKDF的提取-扩展秘钥派生函数提供上下文信息*/

    typedef struct HKDFContext {

      int whichSha;

      HMACContext hmacContext;

      int hashSize; 

      unsigned char prk[SHAMaxHashSize];

      int Computed;

      int Corrupted; 

    } HKDFContext;

    接着我们实现HDKF的抽取函数,该函数的实现如下:

    int hkdfExtract(SHAversion whichSha,

                    const unsigned char *salt, int salt_len,

                    const unsigned char *ikm, int ikm_len,

                    uint8_t prk[SHAMaxHashSize])

    {

      unsigned char nullSalt[SHAMaxHashSize];

      if (salt == 0)

      {

        salt = nullSalt;

        salt_len = SHAHashSize(whichSha);

        memset(nullSalt,'',salt_len);

      }

      else if (salt_len < 0)

      {

        return shaBadParam;

      }

      return hmac(whichSha, ikm, ikm_len, salt, salt_len, prk);

    }

    还需要实现HKDF扩展函数,该函数的实现如下:

    int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ], int prk_len,

                   const unsigned char *info, int info_len,

                   uint8_t okm[ ], int okm_len)

    {

      int hash_len, N;

      unsigned char T[SHAMaxHashSize];

      int Tlen, where, i;

      if (info == 0)

      {

        info = (const unsigned char *)"";

        info_len = 0;

      }

      else if (info_len < 0)

      {

        return shaBadParam;

      }

      if (okm_len <= 0) return shaBadParam;

      if (!okm) return shaBadParam;

      hash_len = SHAHashSize(whichSha);

      if (prk_len < hash_len) return shaBadParam;

      N = okm_len / hash_len;

      if ((okm_len % hash_len) != 0) N++;

      if (N > 255) return shaBadParam;

      Tlen = 0;

      where = 0;

      for (i = 1; i <= N; i++)

      {

        HMACContext context;

        unsigned char c = i;

        int ret = hmacReset(&context, whichSha, prk, prk_len) ||

                  hmacInput(&context, T, Tlen) ||

                  hmacInput(&context, info, info_len) ||

                  hmacInput(&context, &c, 1) ||

                  hmacResult(&context, T);

        if (ret != shaSuccess) return ret;

        memcpy(okm + where, T,(i != N) ? hash_len : (okm_len - where));

        where += hash_len;

        Tlen = hash_len;

      }

      return shaSuccess;

    }

    至此我们就实现了HKDF的全部功能。

    4、结果

    前面我们已经实现了HKDF,我们还需要对其进行验证,我们采用SHA-256哈希函数,分别生成40位和80为的密钥。

    40位密钥:

    80位密钥:

     

    很明显,40位的密钥和80位的密钥前40位都是相同的。

    欢迎关注:

  • 相关阅读:
    P1036 选数(python)解题报告
    P1009 阶乘之和(python)解题报告
    P1002 过河卒(python) 解题报告
    来一波数据结构
    KMP算法
    链表实现约瑟夫
    闲谈Tampermonkey
    CF#574E. OpenStreetMap 题解
    洛谷 P2033 Chessboard Dance
    洛谷 P2056 采花
  • 原文地址:https://www.cnblogs.com/foxclever/p/8642865.html
Copyright © 2020-2023  润新知