1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis */
2 /* SPDX-License-Identifier: Unlicense */
3 
4 #include <assert.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 
8 #include "tomcrypt_private.h"
9 
10 #ifdef LTC_HKDF
11 
12 /* This is mostly just a wrapper around hmac_memory */
hkdf_extract(int hash_idx,const unsigned char * salt,unsigned long saltlen,const unsigned char * in,unsigned long inlen,unsigned char * out,unsigned long * outlen)13 int hkdf_extract(int hash_idx, const unsigned char *salt, unsigned long  saltlen,
14                                const unsigned char *in,   unsigned long  inlen,
15                                      unsigned char *out,  unsigned long *outlen)
16 {
17    /* libtomcrypt chokes on a zero length HMAC key, so we need to check for
18       that.  HMAC specifies that keys shorter than the hash's blocksize are
19       0 padded to the block size.  HKDF specifies that a NULL salt is to be
20       substituted with a salt comprised of hashLen 0 bytes.  HMAC's padding
21       means that in either case the HMAC is actually using a blocksize long
22       zero filled key.  Unless blocksize < hashLen (which wouldn't make any
23       sense), we can use a single 0 byte as the HMAC key and still generate
24       valid results for HKDF. */
25    if (salt == NULL || saltlen == 0) {
26       return hmac_memory(hash_idx, (const unsigned char *)"",   1,       in, inlen, out, outlen);
27    }
28    return hmac_memory(hash_idx, salt, saltlen, in, inlen, out, outlen);
29 }
30 
hkdf_expand(int hash_idx,const unsigned char * info,unsigned long infolen,const unsigned char * in,unsigned long inlen,unsigned char * out,unsigned long outlen)31 int hkdf_expand(int hash_idx, const unsigned char *info, unsigned long infolen,
32                               const unsigned char *in,   unsigned long inlen,
33                                     unsigned char *out,  unsigned long outlen)
34 {
35    unsigned long hashsize;
36    int err;
37    unsigned char N;
38    unsigned long Noutlen, outoff;
39 
40    unsigned char *T,  *dat;
41    unsigned long Tlen, datlen;
42 
43    /* make sure hash descriptor is valid */
44    if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
45       return err;
46    }
47 
48    hashsize = hash_descriptor[hash_idx]->hashsize;
49 
50    /* RFC5869 parameter restrictions */
51    if (inlen < hashsize || outlen > hashsize * 255) {
52       return CRYPT_INVALID_ARG;
53    }
54    if (info == NULL && infolen != 0) {
55       return CRYPT_INVALID_ARG;
56    }
57    LTC_ARGCHK(out != NULL);
58 
59    Tlen = hashsize + infolen + 1;
60    T = XMALLOC(Tlen); /* Replace with static buffer? */
61    if (T == NULL) {
62       return CRYPT_MEM;
63    }
64    if (info != NULL) {
65       XMEMCPY(T + hashsize, info, infolen);
66    }
67 
68    /* HMAC data T(1) doesn't include a previous hash value */
69    dat    = T    + hashsize;
70    datlen = Tlen - hashsize;
71 
72    N = 0;
73    outoff = 0; /* offset in out to write to */
74    while (1) { /* an exit condition breaks mid-loop */
75       Noutlen = MIN(hashsize, outlen - outoff);
76       T[Tlen - 1] = ++N;
77       if ((err = hmac_memory(hash_idx, in, inlen, dat, datlen,
78                              out + outoff, &Noutlen)) != CRYPT_OK) {
79          zeromem(T, Tlen);
80          XFREE(T);
81          return err;
82       }
83       outoff += Noutlen;
84 
85       if (outoff >= outlen) { /* loop exit condition */
86          break;
87       }
88 
89       /* All subsequent HMAC data T(N) DOES include the previous hash value */
90       XMEMCPY(T, out + hashsize * (N-1), hashsize);
91       if (N == 1) {
92          dat = T;
93          datlen = Tlen;
94       }
95    }
96    zeromem(T, Tlen);
97    XFREE(T);
98    return CRYPT_OK;
99 }
100 
101 /* all in one step */
hkdf(int hash_idx,const unsigned char * salt,unsigned long saltlen,const unsigned char * info,unsigned long infolen,const unsigned char * in,unsigned long inlen,unsigned char * out,unsigned long outlen)102 int hkdf(int hash_idx, const unsigned char *salt, unsigned long saltlen,
103                        const unsigned char *info, unsigned long infolen,
104                        const unsigned char *in,   unsigned long inlen,
105                              unsigned char *out,  unsigned long outlen)
106 {
107    unsigned long hashsize;
108    int err;
109    unsigned char *extracted;
110 
111    /* make sure hash descriptor is valid */
112    if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
113       return err;
114    }
115 
116    hashsize = hash_descriptor[hash_idx]->hashsize;
117 
118    extracted = XMALLOC(hashsize); /* replace with static buffer? */
119    if (extracted == NULL) {
120       return CRYPT_MEM;
121    }
122    if ((err = hkdf_extract(hash_idx, salt, saltlen, in, inlen, extracted, &hashsize)) != 0) {
123       zeromem(extracted, hashsize);
124       XFREE(extracted);
125       return err;
126    }
127    err = hkdf_expand(hash_idx, info, infolen, extracted, hashsize, out, outlen);
128    zeromem(extracted, hashsize);
129    XFREE(extracted);
130    return err;
131 }
132 #endif /* LTC_HKDF */
133 
134 
135 /* vim: set ts=2 sw=2 et ai si: */
136