zoukankan      html  css  js  c++  java
  • 信息摘要算法之六: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位都是相同的。

    欢迎关注:

  • 相关阅读:
    [MacOS]Sublime text3 安装(一)
    [RHEL8]开启BBR
    PAT Advanced 1136 A Delayed Palindrome (20分)
    PAT Advanced 1144 The Missing Number (20分)
    PAT Advanced 1041 Be Unique (20分)
    PAT Advanced 1025 PAT Ranking (25分)
    PAT Advanced 1022 Digital Library (30分)
    PAT Advanced 1019 General Palindromic Number (20分)
    PAT Advanced 1011 World Cup Betting (20分)
    PAT Advanced 1102 Invert a Binary Tree (25分)
  • 原文地址:https://www.cnblogs.com/foxclever/p/8642865.html
Copyright © 2011-2022 走看看