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