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