rfc4346 p_hash


return func(result, secret, label, seed []byte) {
labelAndSeed := make([]byte, len(label)+len(seed))
copy(labelAndSeed, label)
copy(labelAndSeed[len(label):], seed)

pHash(result, secret, labelAndSeed, hashFunc)
}


func prf10(result, secret, label, seed []byte) {
hashSHA1 := sha1.New
hashMD5 := md5.New

labelAndSeed := make([]byte, len(label)+len(seed))
copy(labelAndSeed, label)
copy(labelAndSeed[len(label):], seed)

s1, s2 := splitPreMasterSecret(secret)
pHash(result, s1, labelAndSeed, hashMD5)
result2 := make([]byte, len(result))
pHash(result2, s2, labelAndSeed, hashSHA1)

for i, b := range result2 {
result[i] ^= b
}
}


// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
func pHash(result, secret, seed []byte, hash func() hash.Hash) {
h := hmac.New(hash, secret)
h.Write(seed)
a := h.Sum(nil)

j := 0
for j < len(result) {
h.Reset()
h.Write(a)
h.Write(seed)
b := h.Sum(nil)
copy(result[j:], b)
j += len(b)

h.Reset()
h.Write(a)
a = h.Sum(nil)
}
}

 

 

 

 

 

 

5. HMAC and the Pseudorandom Function



   A number of operations in the TLS record and handshake layer require
   a keyed MAC; this is a secure digest of some data protected by a
   secret.  Forging the MAC is infeasible without knowledge of the MAC
   secret.  The construction we use for this operation is known as HMAC,
   and is described in [HMAC].

   HMAC can be used with a variety of different hash algorithms.  TLS
   uses it in the handshake with two different algorithms, MD5 and SHA-
   1, denoting these as HMAC_MD5(secret, data) and HMAC_SHA(secret,
   data).  Additional hash algorithms can be defined by cipher suites



Dierks & Rescorla           Standards Track                    [Page 12]


RFC 4346                    The TLS Protocol                  April 2006


   and used to protect record data, but MD5 and SHA-1 are hard coded
   into the description of the handshaking for this version of the
   protocol.

   In addition, a construction is required to do expansion of secrets
   into blocks of data for the purposes of key generation or validation.
   This pseudo-random function (PRF) takes as input a secret, a seed,
   and an identifying label and produces an output of arbitrary length.

   In order to make the PRF as secure as possible, it uses two hash
   algorithms in a way that should guarantee its security if either
   algorithm remains secure.

   First, we define a data expansion function, P_hash(secret, data) that
   uses a single hash function to expand a secret and seed into an
   arbitrary quantity of output:

       P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                              HMAC_hash(secret, A(2) + seed) +
                              HMAC_hash(secret, A(3) + seed) + ...

   Where + indicates concatenation.

   A() is defined as:

       A(0) = seed
       A(i) = HMAC_hash(secret, A(i-1))

   P_hash can be iterated as many times as is necessary to produce the
   required quantity of data.  For example, if P_SHA-1 is being used to
   create 64 bytes of data, it will have to be iterated 4 times (through
   A(4)), creating 80 bytes of output data; the last 16 bytes of the
   final iteration will then be discarded, leaving 64 bytes of output
   data.

   TLS's PRF is created by splitting the secret into two halves and
   using one half to generate data with P_MD5 and the other half to
   generate data with P_SHA-1, then exclusive-ORing the outputs of these
   two expansion functions together.

   S1 and S2 are the two halves of the secret, and each is the same
   length.  S1 is taken from the first half of the secret, S2 from the
   second half.  Their length is created by rounding up the length of
   the overall secret, divided by two; thus, if the original secret is
   an odd number of bytes long, the last byte of S1 will be the same as
   the first byte of S2.





Dierks & Rescorla           Standards Track                    [Page 13]


RFC 4346                    The TLS Protocol                  April 2006


       L_S = length in bytes of secret;
       L_S1 = L_S2 = ceil(L_S / 2);


   The secret is partitioned into two halves (with the possibility of
   one shared byte) as described above, S1 taking the first L_S1 bytes,
   and S2 the last L_S2 bytes.

   The PRF is then defined as the result of mixing the two pseudorandom
   streams by exclusive-ORing them together.

       PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
                                  P_SHA-1(S2, label + seed);

   The label is an ASCII string.  It should be included in the exact
   form it is given without a length byte or trailing null character.
   For example, the label "slithy toves" would be processed by hashing
   the following bytes:

       73 6C 69 74 68 79 20 74 6F 76 65 73

   Note that because MD5 produces 16-byte outputs and SHA-1 produces
   20-byte outputs, the boundaries of their internal iterations will not
   be aligned.  Generating an 80-byte output will require that P_MD5
   iterate through A(5), while P_SHA-1 will only iterate through A(4).









static int tls1_prf_P_hash(const EVP_MD *md,

                           const unsigned char *sec, size_t sec_len,

                           const unsigned char *seed, size_t seed_len,

                           unsigned char *out, size_t olen)

{

    int chunk;

    EVP_MD_CTX *ctx = NULL, *ctx_tmp = NULL, *ctx_init = NULL;

    EVP_PKEY *mac_key = NULL;

    unsigned char A1[EVP_MAX_MD_SIZE];

    size_t A1_len;

    int ret = 0;

 

    chunk = EVP_MD_size(md);

    OPENSSL_assert(chunk >= 0);

 

    ctx = EVP_MD_CTX_new();

    ctx_tmp = EVP_MD_CTX_new();

    ctx_init = EVP_MD_CTX_new();

    if (ctx == NULL || ctx_tmp == NULL || ctx_init == NULL)

        goto err;

    EVP_MD_CTX_set_flags(ctx_init, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);

    mac_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, sec, sec_len);

    if (mac_key == NULL)

        goto err;

    if (!EVP_DigestSignInit(ctx_init, NULL, md, NULL, mac_key))

        goto err;

    if (!EVP_MD_CTX_copy_ex(ctx, ctx_init))

        goto err;

    if (seed != NULL && !EVP_DigestSignUpdate(ctx, seed, seed_len))

        goto err;

    if (!EVP_DigestSignFinal(ctx, A1, &A1_len))

        goto err;

 

    for (;;) {

        /* Reinit mac contexts */

        if (!EVP_MD_CTX_copy_ex(ctx, ctx_init))

            goto err;

        if (!EVP_DigestSignUpdate(ctx, A1, A1_len))

            goto err;

        if (olen > (size_t)chunk && !EVP_MD_CTX_copy_ex(ctx_tmp, ctx))

            goto err;

        if (seed && !EVP_DigestSignUpdate(ctx, seed, seed_len))

            goto err;

 

        if (olen > (size_t)chunk) {

            size_t mac_len;

            if (!EVP_DigestSignFinal(ctx, out, &mac_len))

                goto err;

            out += mac_len;

            olen -= mac_len;

            /* calc the next A1 value */

            if (!EVP_DigestSignFinal(ctx_tmp, A1, &A1_len))

                goto err;

        } else {                /* last one */

 

            if (!EVP_DigestSignFinal(ctx, A1, &A1_len))

                goto err;

            memcpy(out, A1, olen);

            break;

        }

    }

    ret = 1;

 err:

    EVP_PKEY_free(mac_key);

    EVP_MD_CTX_free(ctx);

    EVP_MD_CTX_free(ctx_tmp);

    EVP_MD_CTX_free(ctx_init);

    OPENSSL_cleanse(A1, sizeof(A1));

    return ret;

}

 

static int tls1_prf_alg(const EVP_MD *md,

                        const unsigned char *sec, size_t slen,

                        const unsigned char *seed, size_t seed_len,

                        unsigned char *out, size_t olen)

{

#if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_SHA)

    if (EVP_MD_type(md) == NID_md5_sha1) {

        size_t i;

        unsigned char *tmp;

        if (!tls1_prf_P_hash(EVP_md5(), sec, slen/2 + (slen & 1),

                         seed, seed_len, out, olen))

            return 0;

 

        tmp = OPENSSL_malloc(olen);

        if (tmp == NULL)

            return 0;

        if (!tls1_prf_P_hash(EVP_sha1(), sec + slen/2, slen/2 + (slen & 1),

                         seed, seed_len, tmp, olen)) {

            OPENSSL_clear_free(tmp, olen);

            return 0;

        }

        for (i = 0; i < olen; i++)

            out[i] ^= tmp[i];

        OPENSSL_clear_free(tmp, olen);

        return 1;

    }

#endif

    if (!tls1_prf_P_hash(md, sec, slen, seed, seed_len, out, olen))

        return 0;

 

    return 1;

}

posted @ 2020-10-15 15:25  zJanly  阅读(289)  评论(0编辑  收藏  举报