deattack.c revision 124208
1312872Shselasky/*
2312872Shselasky * Cryptographic attack detector for ssh - source code
3312872Shselasky *
4312872Shselasky * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.
5312872Shselasky *
6312872Shselasky * All rights reserved. Redistribution and use in source and binary
7312872Shselasky * forms, with or without modification, are permitted provided that
8312872Shselasky * this copyright notice is retained.
9312872Shselasky *
10312872Shselasky * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
11312872Shselasky * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE
12312872Shselasky * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
13312872Shselasky * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS
14312872Shselasky * SOFTWARE.
15312872Shselasky *
16312872Shselasky * Ariel Futoransky <futo@core-sdi.com>
17312872Shselasky * <http://www.core-sdi.com>
18312872Shselasky */
19312872Shselasky
20312872Shselasky#include "includes.h"
21312872ShselaskyRCSID("$OpenBSD: deattack.c,v 1.19 2003/09/18 08:49:45 markus Exp $");
22312872Shselasky
23312872Shselasky#include "deattack.h"
24312872Shselasky#include "log.h"
25312872Shselasky#include "crc32.h"
26312872Shselasky#include "getput.h"
27312872Shselasky#include "xmalloc.h"
28312872Shselasky#include "deattack.h"
29312872Shselasky
30312872Shselasky/* SSH Constants */
31312872Shselasky#define SSH_MAXBLOCKS	(32 * 1024)
32312872Shselasky#define SSH_BLOCKSIZE	(8)
33312872Shselasky
34312872Shselasky/* Hashing constants */
35312872Shselasky#define HASH_MINSIZE	(8 * 1024)
36322011Shselasky#define HASH_ENTRYSIZE	(2)
37312872Shselasky#define HASH_FACTOR(x)	((x)*3/2)
38312872Shselasky#define HASH_UNUSEDCHAR	(0xff)
39312872Shselasky#define HASH_UNUSED	(0xffff)
40312872Shselasky#define HASH_IV		(0xfffe)
41312872Shselasky
42312872Shselasky#define HASH_MINBLOCKS	(7*SSH_BLOCKSIZE)
43312872Shselasky
44312872Shselasky
45312872Shselasky/* Hash function (Input keys are cipher results) */
46312872Shselasky#define HASH(x)		GET_32BIT(x)
47312872Shselasky
48312872Shselasky#define CMP(a, b)	(memcmp(a, b, SSH_BLOCKSIZE))
49312872Shselasky
50312872Shselaskystatic void
51312872Shselaskycrc_update(u_int32_t *a, u_int32_t b)
52312872Shselasky{
53312872Shselasky	b ^= *a;
54312872Shselasky	*a = ssh_crc32((u_char *) &b, sizeof(b));
55312872Shselasky}
56312872Shselasky
57312872Shselasky/* detect if a block is used in a particular pattern */
58312872Shselaskystatic int
59312872Shselaskycheck_crc(u_char *S, u_char *buf, u_int32_t len,
60312872Shselasky	  u_char *IV)
61312872Shselasky{
62312872Shselasky	u_int32_t crc;
63312872Shselasky	u_char *c;
64312872Shselasky
65312872Shselasky	crc = 0;
66312872Shselasky	if (IV && !CMP(S, IV)) {
67312872Shselasky		crc_update(&crc, 1);
68312872Shselasky		crc_update(&crc, 0);
69312872Shselasky	}
70312872Shselasky	for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
71312872Shselasky		if (!CMP(S, c)) {
72312872Shselasky			crc_update(&crc, 1);
73312872Shselasky			crc_update(&crc, 0);
74312872Shselasky		} else {
75312872Shselasky			crc_update(&crc, 0);
76312872Shselasky			crc_update(&crc, 0);
77312872Shselasky		}
78312872Shselasky	}
79312872Shselasky	return (crc == 0);
80312872Shselasky}
81312872Shselasky
82312872Shselasky
83312872Shselasky/* Detect a crc32 compensation attack on a packet */
84312872Shselaskyint
85312872Shselaskydetect_attack(u_char *buf, u_int32_t len, u_char *IV)
86312872Shselasky{
87312872Shselasky	static u_int16_t *h = (u_int16_t *) NULL;
88312872Shselasky	static u_int32_t n = HASH_MINSIZE / HASH_ENTRYSIZE;
89312872Shselasky	u_int32_t i, j;
90312872Shselasky	u_int32_t l;
91312872Shselasky	u_char *c;
92312872Shselasky	u_char *d;
93312872Shselasky
94312872Shselasky	if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
95312872Shselasky	    len % SSH_BLOCKSIZE != 0) {
96312872Shselasky		fatal("detect_attack: bad length %d", len);
97312872Shselasky	}
98312872Shselasky	for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2)
99312872Shselasky		;
100312872Shselasky
101312872Shselasky	if (h == NULL) {
102312872Shselasky		debug("Installing crc compensation attack detector.");
103312872Shselasky		h = (u_int16_t *) xmalloc(l * HASH_ENTRYSIZE);
104312872Shselasky		n = l;
105312872Shselasky	} else {
106312872Shselasky		if (l > n) {
107312872Shselasky			h = (u_int16_t *) xrealloc(h, l * HASH_ENTRYSIZE);
108312872Shselasky			n = l;
109312872Shselasky		}
110312872Shselasky	}
111312872Shselasky
112312872Shselasky	if (len <= HASH_MINBLOCKS) {
113312872Shselasky		for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
114312872Shselasky			if (IV && (!CMP(c, IV))) {
115312872Shselasky				if ((check_crc(c, buf, len, IV)))
116312872Shselasky					return (DEATTACK_DETECTED);
117312872Shselasky				else
118312872Shselasky					break;
119312872Shselasky			}
120312872Shselasky			for (d = buf; d < c; d += SSH_BLOCKSIZE) {
121312872Shselasky				if (!CMP(c, d)) {
122312872Shselasky					if ((check_crc(c, buf, len, IV)))
123312872Shselasky						return (DEATTACK_DETECTED);
124312872Shselasky					else
125312872Shselasky						break;
126312872Shselasky				}
127312872Shselasky			}
128312872Shselasky		}
129312872Shselasky		return (DEATTACK_OK);
130312872Shselasky	}
131312872Shselasky	memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE);
132312872Shselasky
133312872Shselasky	if (IV)
134312872Shselasky		h[HASH(IV) & (n - 1)] = HASH_IV;
135312872Shselasky
136312872Shselasky	for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) {
137312872Shselasky		for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED;
138312872Shselasky		    i = (i + 1) & (n - 1)) {
139			if (h[i] == HASH_IV) {
140				if (!CMP(c, IV)) {
141					if (check_crc(c, buf, len, IV))
142						return (DEATTACK_DETECTED);
143					else
144						break;
145				}
146			} else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) {
147				if (check_crc(c, buf, len, IV))
148					return (DEATTACK_DETECTED);
149				else
150					break;
151			}
152		}
153		h[i] = j;
154	}
155	return (DEATTACK_OK);
156}
157