Путаница в методе addData RNDecryptor

Класс RNDecryptor в ObjectiveC по адресу ЗДЕСЬ имеет функцию расшифровки файла по частям следующим образом:

- (IBAction)decryptWithSemaphore:(id)sender {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

__block int total = 0;
int blockSize = 32 * 1024;

NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *input = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.rncryptor"];
NSString *output = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.decrypted.pdf"];

NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:input];
__block NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:output append:NO];
__block NSError *decryptionError = nil;

[cryptedStream open];
[decryptedStream open];

RNDecryptor *decryptor = [[RNDecryptor alloc] initWithPassword:@"12345678901234567890123456789012" handler:^(RNCryptor *cryptor, NSData *data) {
    @autoreleasepool {
        NSLog(@"Decryptor recevied %d bytes", data.length);
        [decryptedStream write:data.bytes maxLength:data.length];
        dispatch_semaphore_signal(semaphore);

        data = nil;
        if (cryptor.isFinished) {
            [decryptedStream close];
            decryptionError = cryptor.error;
            // call my delegate that I'm finished with decrypting
        }
    }
}];

while (cryptedStream.hasBytesAvailable) {
    @autoreleasepool {
        uint8_t buf[blockSize];
        NSUInteger bytesRead = [cryptedStream read:buf maxLength:blockSize];
        if (bytesRead > 0) {
            NSData *data = [NSData dataWithBytes:buf length:bytesRead];

            total = total + bytesRead;
            [decryptor addData:data];
            NSLog(@"New bytes to decryptor: %d Total: %d", bytesRead, total);

            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        }
    }
}

[cryptedStream close];
[decryptor finish];

dispatch_release(semaphore);

}

И метод addData RNDecryptor выглядит следующим образом:

- (void)addData:(NSData *)theData
{
  if (self.isFinished) {
    return;
  }

  [self.inData appendData:theData];
  if (!self.engine) {
    [self consumeHeaderFromData:self.inData];
  }
  if (self.engine) {
    NSUInteger HMACLength = self.HMACLength;
    if (self.inData.length > HMACLength) {
      NSData *data = [self.inData _RNConsumeToIndex:self.inData.length - HMACLength];
      [self decryptData:data];
    }
  }
}

Я не понимаю, что на самом деле пытается сделать эта строка, которая вызывается для каждого фрагмента зашифрованного потока:

  NSData *data = [self.inData _RNConsumeToIndex:self.inData.length - HMACLength];

Допустим, у меня есть размер блока 1000 байт, а HMACLength — 32.

Если я попытаюсь расшифровать файл, размер которого превышает размер блока, скажем, 5000 байт, тогда этот метод addData запустит первую итерацию, как это

NSData *data = [self.inData _RNConsumeToIndex:1000 - 32];

то есть после потребления заголовков зашифрованные байты от индекса 0 до (1000-32), но хэш записывается в конце зашифрованного потока, последние несколько байтов, а не с каждым фрагментом. и на следующей итерации входной поток будет считывать следующие 1000 байтов, что произойдет с 32 байтами, которые были обрезаны из фрагмента первой итерации?

Может быть, я запутался, так как этот код проверен, но я хочу это понять.

Заранее спасибо.


person user2319247    schedule 29.08.2013    source источник


Ответы (1)


Проблема в том, что потоки обычно не знают, сколько данных осталось. В вашем случае кажется, что тег аутентификации (значение HMAC) был помещен в конец зашифрованного текста стороной, отправляющей зашифрованный текст.

Теперь проблема в том, что вы должны обновлять только данные, а не тег аутентификации. Поскольку вы не знаете, сколько данных еще доступно, возможно, вы уже читаете тег аутентификации в конце. Очевидно, что вычисление HMAC завершится ошибкой, если вы включите в расчет вывод самого HMAC.

Таким образом, вы читаете поток и обновляете состояние HMAC до конца. Затем вы выполняете последнее обновление HMAC до конца зашифрованного текста. Вы извлекаете данный тег аутентификации из конца потока и сравниваете вычисленное и заданное значение. Если они совпадают, то зашифрованный текст (и, следовательно, открытый текст) проверяется на целостность и аутентификацию, учитывая, что секретный ключ никогда не раскрывался злоумышленнику.

Если код правильный (а учитывая код Роба, это весьма вероятно), то 32 байта включаются в расчет MAC, если только они не являются частью тега аутентификации. Другими словами, вы всегда должны буферизовать как минимум размер тега аутентификации, если вы помещаете тег аутентификации в конец.

Вы можете переписать схему таким образом, чтобы длина зашифрованного текста была известна заранее. Например, вы можете начать поток с 64-битного числа, представляющего длину зашифрованного текста. Тогда вам не пришлось бы выполнять неудобную буферизацию за счет 64 дополнительных битов. Протоколы более высокого уровня используют кодирование ASN.1/DER или даже XML для разделения сообщения и тега аутентификации.

person Maarten Bodewes    schedule 29.08.2013
comment
Спасибо за ответ @owlstead. Теперь я пытаюсь написать размер зашифрованного текста внутри заголовка, чтобы мой расчет был простым и точным. Я сообщу вам, как только реализую это. Спасибо еще раз. - person user2319247; 29.08.2013
comment
Как вы это понимаете, не могли бы вы взглянуть на мой другой вопрос по адресу stackoverflow.com/questions/18458425/ - спасибо - person user2319247; 29.08.2013
comment
Сейчас я столкнулся с какой-то странной проблемой: хэш в ios вычисляется из разных байтов, а в .NET это что-то другое. Я думаю, что метод чтения CryptoStream (.NET) имеет некоторую несовместимость с записью ios (я подозреваю, что его длина блока создает некоторую проблему). Позиция зашифрованного файлового потока увеличивается кратно 32, даже если я читаю байт меньше 32. Скажите, пожалуйста, что я делаю неправильно. Дайте мне знать, если вы хотите, чтобы я поместил свой исходный код. Спасибо - person user2319247; 31.08.2013
comment
Для режимов работы блочных шифров нормальная обработка зашифрованного текста кратна размеру блока. Вы не можете обрабатывать частичные блоки, так как базовый шифр знает, как обрабатывать только один блок за раз. Байты, которые еще не требуются в качестве открытого текста, буферизуются реализацией потока. Однако, пожалуйста, не используйте комментарии для ответа на вопросы, просто задайте новый вопрос - если, конечно, вы уже не можете найти ответ на этом сайте. - person Maarten Bodewes; 31.08.2013
comment
Спасибо за ответ @owlstead. Я разместил новый вопрос, пожалуйста, ознакомьтесь с ним по адресу stackoverflow.com/questions/18590183/ - person user2319247; 03.09.2013