1 /*
2 * Implementation of HMAC (RFC 2104) for PuTTY, in a general form that
3 * can wrap any underlying hash function.
4 */
5
6 #include "ssh.h"
7
8 struct hmac {
9 const ssh_hashalg *hashalg;
10 ssh_hash *h_outer, *h_inner, *h_live;
11 uint8_t *digest;
12 strbuf *text_name;
13 ssh2_mac mac;
14 };
15
16 struct hmac_extra {
17 const ssh_hashalg *hashalg_base;
18 const char *suffix, *annotation;
19 };
20
hmac_new(const ssh2_macalg * alg,ssh_cipher * cipher)21 static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher)
22 {
23 struct hmac *ctx = snew(struct hmac);
24 const struct hmac_extra *extra = (const struct hmac_extra *)alg->extra;
25
26 ctx->h_outer = ssh_hash_new(extra->hashalg_base);
27 /* In case that hashalg was a selector vtable, we'll now switch to
28 * using whatever real one it selected, for all future purposes. */
29 ctx->hashalg = ssh_hash_alg(ctx->h_outer);
30 ctx->h_inner = ssh_hash_new(ctx->hashalg);
31 ctx->h_live = ssh_hash_new(ctx->hashalg);
32
33 /*
34 * HMAC is not well defined as a wrapper on an absolutely general
35 * hash function; it expects that the function it's wrapping will
36 * consume data in fixed-size blocks, and it's partially defined
37 * in terms of that block size. So we insist that the hash we're
38 * given must have defined a meaningful block size.
39 */
40 assert(ctx->hashalg->blocklen);
41
42 ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);
43
44 ctx->text_name = strbuf_new();
45 strbuf_catf(ctx->text_name, "HMAC-%s%s",
46 ctx->hashalg->text_basename, extra->suffix);
47 if (extra->annotation || ctx->hashalg->annotation) {
48 strbuf_catf(ctx->text_name, " (");
49 const char *sep = "";
50 if (extra->annotation) {
51 strbuf_catf(ctx->text_name, "%s%s", sep, extra->annotation);
52 sep = ", ";
53 }
54 if (ctx->hashalg->annotation) {
55 strbuf_catf(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);
56 sep = ", ";
57 }
58 strbuf_catf(ctx->text_name, ")");
59 }
60
61 ctx->mac.vt = alg;
62 BinarySink_DELEGATE_INIT(&ctx->mac, ctx->h_live);
63
64 return &ctx->mac;
65 }
66
hmac_free(ssh2_mac * mac)67 static void hmac_free(ssh2_mac *mac)
68 {
69 struct hmac *ctx = container_of(mac, struct hmac, mac);
70
71 ssh_hash_free(ctx->h_outer);
72 ssh_hash_free(ctx->h_inner);
73 ssh_hash_free(ctx->h_live);
74 smemclr(ctx->digest, ctx->hashalg->hlen);
75 sfree(ctx->digest);
76 strbuf_free(ctx->text_name);
77
78 smemclr(ctx, sizeof(*ctx));
79 sfree(ctx);
80 }
81
82 #define PAD_OUTER 0x5C
83 #define PAD_INNER 0x36
84
hmac_key(ssh2_mac * mac,ptrlen key)85 static void hmac_key(ssh2_mac *mac, ptrlen key)
86 {
87 struct hmac *ctx = container_of(mac, struct hmac, mac);
88
89 const uint8_t *kp;
90 size_t klen;
91 strbuf *sb = NULL;
92
93 if (key.len > ctx->hashalg->blocklen) {
94 /*
95 * RFC 2104 section 2: if the key exceeds the block length of
96 * the underlying hash, then we start by hashing the key, and
97 * use that hash as the 'true' key for the HMAC construction.
98 */
99 sb = strbuf_new_nm();
100 strbuf_append(sb, ctx->hashalg->hlen);
101 hash_simple(ctx->hashalg, key, sb->u);
102 kp = sb->u;
103 klen = sb->len;
104 } else {
105 /*
106 * A short enough key is used as is.
107 */
108 kp = (const uint8_t *)key.ptr;
109 klen = key.len;
110 }
111
112 ssh_hash_reset(ctx->h_outer);
113 for (size_t i = 0; i < klen; i++)
114 put_byte(ctx->h_outer, PAD_OUTER ^ kp[i]);
115 for (size_t i = klen; i < ctx->hashalg->blocklen; i++)
116 put_byte(ctx->h_outer, PAD_OUTER);
117
118 ssh_hash_reset(ctx->h_inner);
119 for (size_t i = 0; i < klen; i++)
120 put_byte(ctx->h_inner, PAD_INNER ^ kp[i]);
121 for (size_t i = klen; i < ctx->hashalg->blocklen; i++)
122 put_byte(ctx->h_inner, PAD_INNER);
123
124 if (sb)
125 strbuf_free(sb);
126 }
127
hmac_start(ssh2_mac * mac)128 static void hmac_start(ssh2_mac *mac)
129 {
130 struct hmac *ctx = container_of(mac, struct hmac, mac);
131 ssh_hash_copyfrom(ctx->h_live, ctx->h_inner);
132 }
133
hmac_genresult(ssh2_mac * mac,unsigned char * output)134 static void hmac_genresult(ssh2_mac *mac, unsigned char *output)
135 {
136 struct hmac *ctx = container_of(mac, struct hmac, mac);
137 ssh_hash *htmp;
138
139 /* Leave h_live and h_outer in place, so that the SSH-2 BPP can
140 * continue regenerating test results from different-length
141 * prefixes of the packet */
142 ssh_hash_digest_nondestructive(ctx->h_live, ctx->digest);
143
144 htmp = ssh_hash_copy(ctx->h_outer);
145 put_data(htmp, ctx->digest, ctx->hashalg->hlen);
146 ssh_hash_final(htmp, ctx->digest);
147
148 /*
149 * Some instances of HMAC truncate the output hash, so instead of
150 * writing it directly to 'output' we wrote it to our own
151 * full-length buffer, and now we copy the required amount.
152 */
153 memcpy(output, ctx->digest, mac->vt->len);
154 smemclr(ctx->digest, ctx->hashalg->hlen);
155 }
156
hmac_text_name(ssh2_mac * mac)157 static const char *hmac_text_name(ssh2_mac *mac)
158 {
159 struct hmac *ctx = container_of(mac, struct hmac, mac);
160 return ctx->text_name->s;
161 }
162
163 static const struct hmac_extra ssh_hmac_sha256_extra = { &ssh_sha256, "" };
164 const ssh2_macalg ssh_hmac_sha256 = {
165 .new = hmac_new,
166 .free = hmac_free,
167 .setkey = hmac_key,
168 .start = hmac_start,
169 .genresult = hmac_genresult,
170 .text_name = hmac_text_name,
171 .name = "hmac-sha2-256",
172 .etm_name = "hmac-sha2-256-etm@openssh.com",
173 .len = 32,
174 .keylen = 32,
175 .extra = &ssh_hmac_sha256_extra,
176 };
177
178 static const struct hmac_extra ssh_hmac_md5_extra = { &ssh_md5, "" };
179 const ssh2_macalg ssh_hmac_md5 = {
180 .new = hmac_new,
181 .free = hmac_free,
182 .setkey = hmac_key,
183 .start = hmac_start,
184 .genresult = hmac_genresult,
185 .text_name = hmac_text_name,
186 .name = "hmac-md5",
187 .etm_name = "hmac-md5-etm@openssh.com",
188 .len = 16,
189 .keylen = 16,
190 .extra = &ssh_hmac_md5_extra,
191 };
192
193 static const struct hmac_extra ssh_hmac_sha1_extra = { &ssh_sha1, "" };
194
195 const ssh2_macalg ssh_hmac_sha1 = {
196 .new = hmac_new,
197 .free = hmac_free,
198 .setkey = hmac_key,
199 .start = hmac_start,
200 .genresult = hmac_genresult,
201 .text_name = hmac_text_name,
202 .name = "hmac-sha1",
203 .etm_name = "hmac-sha1-etm@openssh.com",
204 .len = 20,
205 .keylen = 20,
206 .extra = &ssh_hmac_sha1_extra,
207 };
208
209 static const struct hmac_extra ssh_hmac_sha1_96_extra = { &ssh_sha1, "-96" };
210
211 const ssh2_macalg ssh_hmac_sha1_96 = {
212 .new = hmac_new,
213 .free = hmac_free,
214 .setkey = hmac_key,
215 .start = hmac_start,
216 .genresult = hmac_genresult,
217 .text_name = hmac_text_name,
218 .name = "hmac-sha1-96",
219 .etm_name = "hmac-sha1-96-etm@openssh.com",
220 .len = 12,
221 .keylen = 20,
222 .extra = &ssh_hmac_sha1_96_extra,
223 };
224
225 static const struct hmac_extra ssh_hmac_sha1_buggy_extra = {
226 &ssh_sha1, "", "bug-compatible"
227 };
228
229 const ssh2_macalg ssh_hmac_sha1_buggy = {
230 .new = hmac_new,
231 .free = hmac_free,
232 .setkey = hmac_key,
233 .start = hmac_start,
234 .genresult = hmac_genresult,
235 .text_name = hmac_text_name,
236 .name = "hmac-sha1",
237 .len = 20,
238 .keylen = 16,
239 .extra = &ssh_hmac_sha1_buggy_extra,
240 };
241
242 static const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = {
243 &ssh_sha1, "-96", "bug-compatible"
244 };
245
246 const ssh2_macalg ssh_hmac_sha1_96_buggy = {
247 .new = hmac_new,
248 .free = hmac_free,
249 .setkey = hmac_key,
250 .start = hmac_start,
251 .genresult = hmac_genresult,
252 .text_name = hmac_text_name,
253 .name = "hmac-sha1-96",
254 .len = 12,
255 .keylen = 16,
256 .extra = &ssh_hmac_sha1_96_buggy_extra,
257 };
258