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