Использование функции MessageDigest.isEqual в Java
У меня есть два вопроса, которые я не понимаю. Пожалуйста, помогите мне взглянуть. Спасибо.
Какая польза от функции MessageDigest.isEqual в Java?
Объясните, почему в некоторых версиях до Java SE 6 Update 17 он был уязвим для временной атаки.
2 ответа
Рассматривая реализацию Java SE 6 Update 10, мы видим:
public static boolean isEqual(byte digesta[], byte digestb[]) {
if (digesta.length != digestb.length)
return false;
for (int i = 0; i < digesta.length; i++) {
if (digesta[i] != digestb[i]) {
return false;
}
}
return true;
}
Хотя после исправления мы видим:
public static boolean isEqual(byte[] digesta, byte[] digestb) {
if (digesta.length != digestb.length) {
return false;
}
int result = 0;
// time-constant comparison
for (int i = 0; i < digesta.length; i++) {
result |= digesta[i] ^ digestb[i];
}
return result == 0;
}
Старая реализация кажется более эффективной, так как она возвращает false
когда найден первый неравный байт, но я предполагаю, что он был заменен, потому что это может позволить вызывающей стороне проверить, насколько похожи два входных байтовых массива друг на друга, основываясь на времени выполнения метода.
Новая реализация всегда имеет одинаковое время выполнения (для массивов одинаковой длины), поскольку она выполняет итерацию по всем массивам (даже если массивы различаются по самому первому байту).
Я искал, где этот метод вызывается. Одним из примеров является engineVerify(byte[] signature)
в com.sun.org.apache.xml.internal.security.algorithms.implementations.IntegrityHmac
класс, который проверяет, является ли байтовый массив сигнатуры, переданный ему, действительным, сравнивая его с некоторым внутренним байтовым массивом. До исправления, измерив время выполнения этого метода, вы могли бы попытаться сгенерировать байтовый массив, который прошел бы сравнение (чем дольше выполняется метод, тем подразумевается, что больший префикс двух массивов равен).
Обратитесь к ссылке на веб-страницу, как показано ниже: http://codahale.com/a-lesson-in-timing-attacks/
Я могу выбрать, какое сообщение я хочу аутентифицировать - скажем, файл cookie сеанса с определенным идентификатором пользователя - и затем рассчитать 256 возможных значений:
0000000000000000000000000000000000000000
0100000000000000000000000000000000000000
0200000000000000000000000000000000000000
... snip 250 ...
FD00000000000000000000000000000000000000
FE00000000000000000000000000000000000000
FF00000000000000000000000000000000000000
Я перебираю каждое из этих значений, пока не найду одно - A100000000000000000000000000000000000000 - которое на долю миллисекунды дольше остальных. Теперь я знаю, что первым байтом HMAC для этого сообщения является A1. Повторите процесс для оставшихся 19 байтов, и внезапно я вошел как вы.