TLS 1.2 PRF实施

时间:2018-07-02 20:10:33

标签: c++ hash tls1.2 hmac

当前,我正在尝试实现TLS PRF功能。 我将wolfcrypt库用于SHA哈希函数。 包括HMAC在内的所有测试结果均良好。 但是PRF函数产生了不同的字节。 我在做什么错了?

class SHA1
{
public:
    SHA1() {
        if(wc_InitSha(&sha_)) std::cerr << "wc_init_sha_failed" << std::endl;
    }
    template<typename It> std::array<unsigned char, 20> hash(const It begin, const It end) {
        std::array<unsigned char, 20> r;
        wc_ShaUpdate(&sha_, &*begin, end - begin);
        wc_ShaFinal(&sha_, r.data());
        return r;
    }
    static const int block_size = 64;
    static const int output_size = 20;
protected:
    Sha sha_;
};

class SHA2
{//sha256, sha2 due to some naming reason
public:
    SHA2() {
        if(wc_InitSha256(&sha_)) std::cerr << "wc_init_sha256_failed" << std::endl;
    }
    template<typename It> std::array<unsigned char, 32> hash(const It begin, const It end) {
        std::array<unsigned char, 32> r;
        wc_Sha256Update(&sha_, &*begin, end - begin);
        wc_Sha256Final(&sha_, r.data());
        return r;
    }
    static const int block_size = 64;
    static const int output_size = 32;
protected:
    Sha256 sha_;
};


template<class H> class HMAC
{//hmac using sha1
public:
    template<typename It> void key(const It begin, const It end)
    {//if less than block size(sha1 16? 64?) pad 0, more than block size hash -> 20
        int length = end - begin;
        std::valarray<unsigned char> key((int)0x0, H::block_size),
            out_xor(0x5c, H::block_size), in_xor(0x36, H::block_size);
        if(length > H::block_size) {
            auto h = sha_.hash(begin, end);
            for(int i=0; i<H::output_size; i++) key[i] = h[i];
        } else if(int i = 0; length < H::block_size)
            for(auto it = begin; it != end; it++) key[i++] = *it;

        auto o_key_pad = key ^ out_xor;
        auto i_key_pad = key ^ in_xor;
        for(int i=0; i<H::block_size; i++)
            o_key_pad_[i] = o_key_pad[i], i_key_pad_[i] = i_key_pad[i];
    }
    template<typename It> auto hash(const It begin, const It end)
    {
        std::vector<unsigned char> v;
        v.insert(v.begin(), i_key_pad_.begin(), i_key_pad_.end());
        v.insert(v.end(), begin, end);
        auto h = sha_.hash(v.begin(), v.end());
        v.clear();
        v.insert(v.begin(), o_key_pad_.begin(), o_key_pad_.end());
        v.insert(v.end(), h.begin(), h.end());
        return sha_.hash(v.begin(), v.end());
    }
protected:
    H sha_;
    std::array<unsigned char, H::block_size> o_key_pad_, i_key_pad_;
};
/***********************************
Function hmac
   Inputs:
      key:        Bytes     array of bytes
      message:    Bytes     array of bytes to be hashed
      hash:       Function  the hash function to use (e.g. SHA-1)
      blockSize:  Integer   the block size of the underlying hash function (e.g. 64 bytes for SHA-1)
      outputSize: Integer   the output size of the underlying hash function (e.g. 20 bytes for SHA-1)

   Keys longer than blockSize are shortened by hashing them
   if (length(key) > blockSize) then
      key ← hash(key) //Key becomes outputSize bytes long

   Keys shorter than blockSize are padded to blockSize by padding with zeros on the right
   if (length(key) < blockSize) then
      key ← Pad(key, blockSize)  //pad key with zeros to make it blockSize bytes long

   o_key_pad = key xor [0x5c * blockSize]   //Outer padded key
   i_key_pad = key xor [0x36 * blockSize]   //Inner padded key

   return hash(o_key_pad + hash(i_key_pad +message)) //Where +is concatenation
*************************************/

template<class H> class PRF
{//H is hash function usually sha256
public:
    template<class It> void secret(const It begin, const It end) {
        for(It it = begin; it != end; it++) secret_.push_back(*it);
    }
    void label(const char* p) {
        while(*p) label_.push_back(*p++);
    }
    template<class It> void seed(const It begin, const It end) {
        for(It it = begin; it != end; it++) seed_.push_back(*it);
    }
    std::vector<unsigned char> get_n_byte(int n) {
        auto seed = label_;//seed = label + seed_
        seed.insert(seed.end(), seed_.begin(), seed_.end());
        std::vector<unsigned char> r, v;
        std::vector<std::array<unsigned char, H::output_size>> vA;
        hmac_.key(seed.begin(), seed.end());
        vA.push_back(hmac_.hash(secret_.begin(), secret_.end()));//A(1)
        for(int i=1; r.size() < n; i++) {
            v.clear();
            v.insert(v.end(), vA.back().begin(), vA.back().end());
            v.insert(v.end(), seed.begin(), seed.end());
            hmac_.key(v.begin(), v.end());
            auto h = hmac_.hash(secret_.begin(), secret_.end());
            r.insert(r.end(), h.begin(), h.end());
            hmac_.key(vA.back().begin(), vA.back().end());
            vA.push_back(hmac_.hash(secret_.begin(), secret_.end()));//A(i+1)
        }
        while(r.size() != n) r.pop_back();
        return r;
    }

protected:
    HMAC<H> hmac_;
    std::vector<unsigned char> secret_, label_, seed_;
};

/*******************************
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 necessary to produce the
required quantity of data. For example, if P_SHA256 is being used to
create 80 bytes of data, it will have to be iterated three times
(through A(3)), creating 96 bytes of output data; the last 16 bytes
of the final iteration will then be discarded, leaving 80 bytes of
output data.
TLS’s PRF is created by applying P_hash to the secret as:
PRF(secret, label, seed) = P_<hash>(secret, 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
*******************************/

测试

TEST_CASE("sha1") {
    SHA1 sha;
    unsigned char c[] = "The quick brown fox jumps over the lazy dog";
    int i = 0;
    for(auto* p = c; *p; p++) i++;//find null position
    array<unsigned char, 20> ar = sha.hash(c, c+i);
    vector<unsigned char> v{ar.begin(), ar.end()};
    REQUIRE(base64_encode(v) == "L9ThxnotKPzthJ7hu3bnORuT6xI=");
    /************************
    SHA1("The quick brown fox jumps over the lazy dog")
    gives hexadecimal: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
    gives Base64 binary to ASCII text encoding: L9ThxnotKPzthJ7hu3bnORuT6xI=
    **************/
}

TEST_CASE("rsa") {
    RSA rsa{32};
    RSA rsa2{
        mpz_class{"42342423423"},//e
        mpz_class{"423423423423"},//d
        mpz_class{"634654636"}//K
    };
    mpz_class z{"31312333424"};
    auto a = rsa.encode(z);
    auto b = rsa.decode(a);
    REQUIRE(b == z);
}

TEST_CASE("hmac-sha1") {
    HMAC<SHA1> hmac;
    hmac.key(c, c);
    auto a = hmac.hash(c, c);
    mpz_class z{"0xfbdb1d1b18aa6c08324b7d64b71fb76370690e1d"};
    REQUIRE(z == bnd2mpz(a.begin(), a.end()));
}

TEST_CASE("hmac_sha256") {
    HMAC<SHA2> hmac;
    hmac.key(c, c);
    auto a  = hmac.hash(c, c);
    mpz_class z{"0xb613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"};
    REQUIRE(z == bnd2mpz(a.begin(), a.end()));
}

/********************
Here are some empty HMAC values:

HMAC_MD5("", "")    = 74e6f7298a9c2d168935f58c001bad88
HMAC_SHA1("", "")   = fbdb1d1b18aa6c08324b7d64b71fb76370690e1d
HMAC_SHA256("", "") = b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad

Here are some non-empty HMAC values, assuming 8-bit ASCII or UTF-8 encoding:

HMAC_MD5("key", "The quick brown fox jumps over the lazy dog")    = 80070713463e7749b90c2dc24911e275
HMAC_SHA1("key", "The quick brown fox jumps over the lazy dog")   = de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9
HMAC_SHA256("key", "The quick brown fox jumps over the lazy dog") = f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
****************************/

TEST_CASE("base64") {
    vector<unsigned char> v{c, c+20};
    string s = base64_encode(v);
    auto v2 = base64_decode(s);
    REQUIRE(v == v2);
}

TEST_CASE("prf") {
    unsigned char secret[] = {0x9b, 0xbe, 0x43, 0x6b, 0xa9, 0x40, 0xf0, 0x17,
                              0xb1, 0x76, 0x52, 0x84, 0x9a, 0x71, 0xdb, 0x35};
    unsigned char seed[] = {0xa0, 0xba, 0x9f, 0x93, 0x6c, 0xda, 0x31, 0x18,
                            0x27, 0xa6, 0xf7, 0x96, 0xff, 0xd5, 0x19, 0x8c};
    PRF<SHA2> prf;
    prf.label("test label");
    prf.seed(seed, seed + 16);
    prf.secret(secret, secret + 16);
    auto v = prf.get_n_byte(100);
    for(auto a : v) cout << hex << +a << ' ';
}
/***********************
# Generating 100 bytes of pseudo-randomness using TLS1.2PRF-SHA256
Secret (16 bytes):
0000    9b be 43 6b a9 40 f0 17    ..Ck....
0008    b1 76 52 84 9a 71 db 35    .vR..q.5

Seed (16 bytes):
0000    a0 ba 9f 93 6c da 31 18    ....l.1.
0008    27 a6 f7 96 ff d5 19 8c    ........

Label (10 bytes):
0000    74 65 73 74 20 6c 61 62    test lab
0008    65 6c                      el

Output (100 bytes):
0000    e3 f2 29 ba 72 7b e1 7b    ....r...
0008    8d 12 26 20 55 7c d4 53    ... U..S
0010    c2 aa b2 1d 07 c3 d4 95    ........
0018    32 9b 52 d4 e6 1e db 5a    2.R....Z
0020    6b 30 17 91 e9 0d 35 c9    k0....5.
0028    c9 a4 6b 4e 14 ba f9 af    ..kN....
0030    0f a0 22 f7 07 7d ef 17    ........
0038    ab fd 37 97 c0 56 4b ab    ..7..VK.
0040    4f bc 91 66 6e 9d ef 9b    O..fn...
0048    97 fc e3 4f 79 67 89 ba    ...Oyg..
0050    a4 80 82 d1 22 ee 42 c5    ......B.
0058    a7 2e 5a 51 10 ff f7 01    ..ZQ....
0060    87 34 7b 66                .4.f




[TLS-12-PRF(HMAC(SHA-256))]

Secret = 9bbe436ba940f017b17652849a71db35
Salt = a0ba9f936cda311827a6f796ffd5198c
Label = 74657374206c6162656c
Output = e3f229ba727be17b8d122620557cd453c2aab21d07c3d495329b52d4e61edb5a6b301791e90d35c9c9a46b4e14baf9af0fa022f7077def17abfd3797c0564bab4fbc91666e9def9b97fce34f796789baa48082d122ee42c5a72e5a5110fff70187347b66
***************************/

我认为PRF类的get_n_byte函数是问题。 它在第一个代码块的末尾。 这是一个很长的代码,但是其他所有东西只是补充信息。

0 个答案:

没有答案