Update TortoiseGitPlink to PuTTY Plink 0.78
[TortoiseGit.git] / src / TortoisePlink / crypto / hmac.c
blobae6bda371bc370dde8d547d814952ee2345e325f
1 /*
2 * Implementation of HMAC (RFC 2104) for PuTTY, in a general form that
3 * can wrap any underlying hash function.
4 */
6 #include "ssh.h"
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;
16 struct hmac_extra {
17 const ssh_hashalg *hashalg_base;
18 const char *suffix, *annotation;
21 static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher)
23 struct hmac *ctx = snew(struct hmac);
24 const struct hmac_extra *extra = (const struct hmac_extra *)alg->extra;
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);
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.
40 assert(ctx->hashalg->blocklen);
42 ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);
44 ctx->text_name = strbuf_new();
45 put_fmt(ctx->text_name, "HMAC-%s%s",
46 ctx->hashalg->text_basename, extra->suffix);
47 if (extra->annotation || ctx->hashalg->annotation) {
48 put_fmt(ctx->text_name, " (");
49 const char *sep = "";
50 if (extra->annotation) {
51 put_fmt(ctx->text_name, "%s%s", sep, extra->annotation);
52 sep = ", ";
54 if (ctx->hashalg->annotation) {
55 put_fmt(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);
56 sep = ", ";
58 put_fmt(ctx->text_name, ")");
61 ctx->mac.vt = alg;
62 BinarySink_DELEGATE_INIT(&ctx->mac, ctx->h_live);
64 return &ctx->mac;
67 static void hmac_free(ssh2_mac *mac)
69 struct hmac *ctx = container_of(mac, struct hmac, mac);
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);
78 smemclr(ctx, sizeof(*ctx));
79 sfree(ctx);
82 #define PAD_OUTER 0x5C
83 #define PAD_INNER 0x36
85 static void hmac_key(ssh2_mac *mac, ptrlen key)
87 struct hmac *ctx = container_of(mac, struct hmac, mac);
89 const uint8_t *kp;
90 size_t klen;
91 strbuf *sb = NULL;
93 if (key.len > ctx->hashalg->blocklen) {
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.
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 {
106 * A short enough key is used as is.
108 kp = (const uint8_t *)key.ptr;
109 klen = key.len;
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);
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);
124 if (sb)
125 strbuf_free(sb);
128 static void hmac_start(ssh2_mac *mac)
130 struct hmac *ctx = container_of(mac, struct hmac, mac);
131 ssh_hash_copyfrom(ctx->h_live, ctx->h_inner);
134 static void hmac_genresult(ssh2_mac *mac, unsigned char *output)
136 struct hmac *ctx = container_of(mac, struct hmac, mac);
137 ssh_hash *htmp;
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);
144 htmp = ssh_hash_copy(ctx->h_outer);
145 put_data(htmp, ctx->digest, ctx->hashalg->hlen);
146 ssh_hash_final(htmp, ctx->digest);
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.
153 memcpy(output, ctx->digest, mac->vt->len);
154 smemclr(ctx->digest, ctx->hashalg->hlen);
157 static const char *hmac_text_name(ssh2_mac *mac)
159 struct hmac *ctx = container_of(mac, struct hmac, mac);
160 return ctx->text_name->s;
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 .next_message = nullmac_next_message,
171 .text_name = hmac_text_name,
172 .name = "hmac-sha2-256",
173 .etm_name = "hmac-sha2-256-etm@openssh.com",
174 .len = 32,
175 .keylen = 32,
176 .extra = &ssh_hmac_sha256_extra,
179 static const struct hmac_extra ssh_hmac_md5_extra = { &ssh_md5, "" };
180 const ssh2_macalg ssh_hmac_md5 = {
181 .new = hmac_new,
182 .free = hmac_free,
183 .setkey = hmac_key,
184 .start = hmac_start,
185 .genresult = hmac_genresult,
186 .next_message = nullmac_next_message,
187 .text_name = hmac_text_name,
188 .name = "hmac-md5",
189 .etm_name = "hmac-md5-etm@openssh.com",
190 .len = 16,
191 .keylen = 16,
192 .extra = &ssh_hmac_md5_extra,
195 static const struct hmac_extra ssh_hmac_sha1_extra = { &ssh_sha1, "" };
197 const ssh2_macalg ssh_hmac_sha1 = {
198 .new = hmac_new,
199 .free = hmac_free,
200 .setkey = hmac_key,
201 .start = hmac_start,
202 .genresult = hmac_genresult,
203 .next_message = nullmac_next_message,
204 .text_name = hmac_text_name,
205 .name = "hmac-sha1",
206 .etm_name = "hmac-sha1-etm@openssh.com",
207 .len = 20,
208 .keylen = 20,
209 .extra = &ssh_hmac_sha1_extra,
212 static const struct hmac_extra ssh_hmac_sha1_96_extra = { &ssh_sha1, "-96" };
214 const ssh2_macalg ssh_hmac_sha1_96 = {
215 .new = hmac_new,
216 .free = hmac_free,
217 .setkey = hmac_key,
218 .start = hmac_start,
219 .genresult = hmac_genresult,
220 .next_message = nullmac_next_message,
221 .text_name = hmac_text_name,
222 .name = "hmac-sha1-96",
223 .etm_name = "hmac-sha1-96-etm@openssh.com",
224 .len = 12,
225 .keylen = 20,
226 .extra = &ssh_hmac_sha1_96_extra,
229 static const struct hmac_extra ssh_hmac_sha1_buggy_extra = {
230 &ssh_sha1, "", "bug-compatible"
233 const ssh2_macalg ssh_hmac_sha1_buggy = {
234 .new = hmac_new,
235 .free = hmac_free,
236 .setkey = hmac_key,
237 .start = hmac_start,
238 .genresult = hmac_genresult,
239 .next_message = nullmac_next_message,
240 .text_name = hmac_text_name,
241 .name = "hmac-sha1",
242 .len = 20,
243 .keylen = 16,
244 .extra = &ssh_hmac_sha1_buggy_extra,
247 static const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = {
248 &ssh_sha1, "-96", "bug-compatible"
251 const ssh2_macalg ssh_hmac_sha1_96_buggy = {
252 .new = hmac_new,
253 .free = hmac_free,
254 .setkey = hmac_key,
255 .start = hmac_start,
256 .genresult = hmac_genresult,
257 .next_message = nullmac_next_message,
258 .text_name = hmac_text_name,
259 .name = "hmac-sha1-96",
260 .len = 12,
261 .keylen = 16,
262 .extra = &ssh_hmac_sha1_96_buggy_extra,