1 /*
2  * Copyright (C) 2011 Collabora Ltd.
3  * Copyright (C) 2018 Alexander Volkov <a.volkov@rusbitech.ru>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  * MA 02110-1301 USA
19  *
20  * Author: Stef Walter <stefw@collabora.co.uk>
21  */
22 
23 #include <gcrypt.h>
24 
gcry_hkdf(int algo,const char * input,size_t n_input,const char * salt,size_t n_salt,const char * info,size_t n_info,char * output,size_t n_output)25 static gcry_error_t gcry_hkdf(int         algo,
26                               const char *input,
27                               size_t      n_input,
28                               const char *salt,
29                               size_t      n_salt,
30                               const char *info,
31                               size_t      n_info,
32                               char *      output,
33                               size_t      n_output)
34 {
35     void *       alloc  = nullptr;
36     void *       buffer = nullptr;
37     gcry_md_hd_t md1, md2;
38     unsigned int hash_len;
39     int          i;
40     size_t       step, n_buffer;
41     char *       at;
42     gcry_error_t gcry;
43 
44     hash_len = gcry_md_get_algo_dlen(algo);
45     if (hash_len == 0) {
46         return GPG_ERR_UNSUPPORTED_ALGORITHM;
47     }
48 
49     if (n_output > 255 * hash_len) {
50         return GPG_ERR_TOO_LARGE;
51     }
52 
53     /* Buffer we need to for intermediate stuff */
54     buffer = gcry_malloc_secure(hash_len);
55     if (!buffer) {
56         return GPG_ERR_ENOMEM;
57     }
58     n_buffer = 0;
59 
60     /* Salt defaults to hash_len zeros */
61     if (!salt) {
62         alloc = gcry_calloc_secure(hash_len, 1);
63         if (!alloc) {
64             return GPG_ERR_ENOMEM;
65         }
66         salt   = (const char *)alloc;
67         n_salt = hash_len;
68     }
69 
70     /* Step 1: Extract */
71     gcry = gcry_md_open(&md1, algo, GCRY_MD_FLAG_HMAC | GCRY_MD_FLAG_SECURE);
72     if (gcry != GPG_ERR_NO_ERROR) {
73         goto done;
74     }
75     gcry = gcry_md_setkey(md1, salt, n_salt);
76     if (gcry != GPG_ERR_NO_ERROR) {
77         gcry_md_close(md1);
78         goto done;
79     }
80     gcry_md_write(md1, input, n_input);
81 
82     /* Step 2: Expand */
83     gcry = gcry_md_open(&md2, algo, GCRY_MD_FLAG_HMAC | GCRY_MD_FLAG_SECURE);
84     if (gcry != GPG_ERR_NO_ERROR) {
85         gcry_md_close(md1);
86         goto done;
87     }
88     gcry = gcry_md_setkey(md2, gcry_md_read(md1, algo), hash_len);
89     if (gcry != GPG_ERR_NO_ERROR) {
90         gcry_md_close(md2);
91         gcry_md_close(md1);
92         goto done;
93     }
94     gcry_md_close(md1);
95 
96     at = output;
97     for (i = 1; i < 256; ++i) {
98         gcry_md_reset(md2);
99         gcry_md_write(md2, buffer, n_buffer);
100         gcry_md_write(md2, info, n_info);
101         gcry_md_putc(md2, i);
102 
103         n_buffer = hash_len;
104         memcpy(buffer, gcry_md_read(md2, algo), n_buffer);
105 
106         step = n_buffer < n_output ? n_buffer : n_output;
107         memcpy(at, buffer, step);
108         n_output -= step;
109         at += step;
110 
111         if (!n_output)
112             break;
113     }
114     gcry_md_close(md2);
115 
116 done:
117     gcry_free(alloc);
118     gcry_free(buffer);
119     return gcry;
120 }
121