GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ssh/lib/../mac.c Lines: 6 99 6.1 %
Date: 2017-11-07 Branches: 3 78 3.8 %

Line Branch Exec Source
1
/* $OpenBSD: mac.c,v 1.34 2017/05/08 22:57:38 djm Exp $ */
2
/*
3
 * Copyright (c) 2001 Markus Friedl.  All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
 */
25
26
#include <sys/types.h>
27
28
#include <string.h>
29
#include <stdio.h>
30
31
#include "digest.h"
32
#include "hmac.h"
33
#include "umac.h"
34
#include "mac.h"
35
#include "misc.h"
36
#include "ssherr.h"
37
#include "sshbuf.h"
38
39
#define SSH_DIGEST	1	/* SSH_DIGEST_XXX */
40
#define SSH_UMAC	2	/* UMAC (not integrated with OpenSSL) */
41
#define SSH_UMAC128	3
42
43
struct macalg {
44
	char		*name;
45
	int		type;
46
	int		alg;
47
	int		truncatebits;	/* truncate digest if != 0 */
48
	int		key_len;	/* just for UMAC */
49
	int		len;		/* just for UMAC */
50
	int		etm;		/* Encrypt-then-MAC */
51
};
52
53
static const struct macalg macs[] = {
54
	/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
55
	{ "hmac-sha1",				SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 },
56
	{ "hmac-sha1-96",			SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 },
57
	{ "hmac-sha2-256",			SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 },
58
	{ "hmac-sha2-512",			SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 },
59
	{ "hmac-md5",				SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 },
60
	{ "hmac-md5-96",			SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 0 },
61
	{ "umac-64@openssh.com",		SSH_UMAC, 0, 0, 128, 64, 0 },
62
	{ "umac-128@openssh.com",		SSH_UMAC128, 0, 0, 128, 128, 0 },
63
64
	/* Encrypt-then-MAC variants */
65
	{ "hmac-sha1-etm@openssh.com",		SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 },
66
	{ "hmac-sha1-96-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 1 },
67
	{ "hmac-sha2-256-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 },
68
	{ "hmac-sha2-512-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 },
69
	{ "hmac-md5-etm@openssh.com",		SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 1 },
70
	{ "hmac-md5-96-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 1 },
71
	{ "umac-64-etm@openssh.com",		SSH_UMAC, 0, 0, 128, 64, 1 },
72
	{ "umac-128-etm@openssh.com",		SSH_UMAC128, 0, 0, 128, 128, 1 },
73
74
	{ NULL,					0, 0, 0, 0, 0, 0 }
75
};
76
77
/* Returns a list of supported MACs separated by the specified char. */
78
char *
79
mac_alg_list(char sep)
80
{
81
	char *ret = NULL, *tmp;
82
	size_t nlen, rlen = 0;
83
	const struct macalg *m;
84
85
	for (m = macs; m->name != NULL; m++) {
86
		if (ret != NULL)
87
			ret[rlen++] = sep;
88
		nlen = strlen(m->name);
89
		if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
90
			free(ret);
91
			return NULL;
92
		}
93
		ret = tmp;
94
		memcpy(ret + rlen, m->name, nlen + 1);
95
		rlen += nlen;
96
	}
97
	return ret;
98
}
99
100
static int
101
mac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg)
102
{
103
	mac->type = macalg->type;
104
	if (mac->type == SSH_DIGEST) {
105
		if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL)
106
			return SSH_ERR_ALLOC_FAIL;
107
		mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg);
108
	} else {
109
		mac->mac_len = macalg->len / 8;
110
		mac->key_len = macalg->key_len / 8;
111
		mac->umac_ctx = NULL;
112
	}
113
	if (macalg->truncatebits != 0)
114
		mac->mac_len = macalg->truncatebits / 8;
115
	mac->etm = macalg->etm;
116
	return 0;
117
}
118
119
int
120
mac_setup(struct sshmac *mac, char *name)
121
{
122
	const struct macalg *m;
123
124
	for (m = macs; m->name != NULL; m++) {
125
		if (strcmp(name, m->name) != 0)
126
			continue;
127
		if (mac != NULL)
128
			return mac_setup_by_alg(mac, m);
129
		return 0;
130
	}
131
	return SSH_ERR_INVALID_ARGUMENT;
132
}
133
134
int
135
mac_init(struct sshmac *mac)
136
{
137
	if (mac->key == NULL)
138
		return SSH_ERR_INVALID_ARGUMENT;
139
	switch (mac->type) {
140
	case SSH_DIGEST:
141
		if (mac->hmac_ctx == NULL ||
142
		    ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0)
143
			return SSH_ERR_INVALID_ARGUMENT;
144
		return 0;
145
	case SSH_UMAC:
146
		if ((mac->umac_ctx = umac_new(mac->key)) == NULL)
147
			return SSH_ERR_ALLOC_FAIL;
148
		return 0;
149
	case SSH_UMAC128:
150
		if ((mac->umac_ctx = umac128_new(mac->key)) == NULL)
151
			return SSH_ERR_ALLOC_FAIL;
152
		return 0;
153
	default:
154
		return SSH_ERR_INVALID_ARGUMENT;
155
	}
156
}
157
158
int
159
mac_compute(struct sshmac *mac, u_int32_t seqno,
160
    const u_char *data, int datalen,
161
    u_char *digest, size_t dlen)
162
{
163
	static union {
164
		u_char m[SSH_DIGEST_MAX_LENGTH];
165
		u_int64_t for_align;
166
	} u;
167
	u_char b[4];
168
	u_char nonce[8];
169
170
	if (mac->mac_len > sizeof(u))
171
		return SSH_ERR_INTERNAL_ERROR;
172
173
	switch (mac->type) {
174
	case SSH_DIGEST:
175
		put_u32(b, seqno);
176
		/* reset HMAC context */
177
		if (ssh_hmac_init(mac->hmac_ctx, NULL, 0) < 0 ||
178
		    ssh_hmac_update(mac->hmac_ctx, b, sizeof(b)) < 0 ||
179
		    ssh_hmac_update(mac->hmac_ctx, data, datalen) < 0 ||
180
		    ssh_hmac_final(mac->hmac_ctx, u.m, sizeof(u.m)) < 0)
181
			return SSH_ERR_LIBCRYPTO_ERROR;
182
		break;
183
	case SSH_UMAC:
184
		POKE_U64(nonce, seqno);
185
		umac_update(mac->umac_ctx, data, datalen);
186
		umac_final(mac->umac_ctx, u.m, nonce);
187
		break;
188
	case SSH_UMAC128:
189
		put_u64(nonce, seqno);
190
		umac128_update(mac->umac_ctx, data, datalen);
191
		umac128_final(mac->umac_ctx, u.m, nonce);
192
		break;
193
	default:
194
		return SSH_ERR_INVALID_ARGUMENT;
195
	}
196
	if (digest != NULL) {
197
		if (dlen > mac->mac_len)
198
			dlen = mac->mac_len;
199
		memcpy(digest, u.m, dlen);
200
	}
201
	return 0;
202
}
203
204
int
205
mac_check(struct sshmac *mac, u_int32_t seqno,
206
    const u_char *data, size_t dlen,
207
    const u_char *theirmac, size_t mlen)
208
{
209
	u_char ourmac[SSH_DIGEST_MAX_LENGTH];
210
	int r;
211
212
	if (mac->mac_len > mlen)
213
		return SSH_ERR_INVALID_ARGUMENT;
214
	if ((r = mac_compute(mac, seqno, data, dlen,
215
	    ourmac, sizeof(ourmac))) != 0)
216
		return r;
217
	if (timingsafe_bcmp(ourmac, theirmac, mac->mac_len) != 0)
218
		return SSH_ERR_MAC_INVALID;
219
	return 0;
220
}
221
222
void
223
mac_clear(struct sshmac *mac)
224
{
225
4
	if (mac->type == SSH_UMAC) {
226
		if (mac->umac_ctx != NULL)
227
			umac_delete(mac->umac_ctx);
228
2
	} else if (mac->type == SSH_UMAC128) {
229
		if (mac->umac_ctx != NULL)
230
			umac128_delete(mac->umac_ctx);
231
2
	} else if (mac->hmac_ctx != NULL)
232
		ssh_hmac_free(mac->hmac_ctx);
233
2
	mac->hmac_ctx = NULL;
234
2
	mac->umac_ctx = NULL;
235
2
}
236
237
/* XXX copied from ciphers_valid */
238
#define	MAC_SEP	","
239
int
240
mac_valid(const char *names)
241
{
242
	char *maclist, *cp, *p;
243
244
	if (names == NULL || strcmp(names, "") == 0)
245
		return 0;
246
	if ((maclist = cp = strdup(names)) == NULL)
247
		return 0;
248
	for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0';
249
	    (p = strsep(&cp, MAC_SEP))) {
250
		if (mac_setup(NULL, p) < 0) {
251
			free(maclist);
252
			return 0;
253
		}
254
	}
255
	free(maclist);
256
	return 1;
257
}