HERE中ObjectiveC中的RNDecryptor类具有按块解密文件的功能,如下所示:
- (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);
}
RNDecryptor的addData方法如下:
- (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个字节会发生什么?
可能我很困惑,因为这段代码已被证实,但我想理解这一点。
提前致谢。
答案 0 :(得分:1)
问题是流通常不知道剩下多少数据。在您的情况下,似乎认证标签(HMAC值)被发送密文的一方放在密文的末尾。
现在问题是您应该只更新数据,而不是身份验证标记。由于您不知道还有多少数据可用,因此您可能已经在最后读取了身份验证标记。如果在计算中包含HMAC本身的输出,显然HMAC计算将失败。
所以基本上你读取流并更新HMAC状态直到结束。然后执行最后一次HMAC更新,直到密文结束。您从流的末尾提取给定的身份验证标记,并比较计算值和给定值。如果它们是相同的,则检查密文(以及明文)的完整性和身份验证 - 假设密钥当然从未向攻击者透露过。
如果代码是正确的(并且给出Rob的代码,那很可能),那么32个字节包含在MAC计算中,除非它们确实是认证标记的一部分。换句话说,如果您将身份验证标记放在最后,则始终必须至少缓冲身份验证标记的大小。
您可以通过预先知道密文长度的方式重写方案。例如,您可以通过表示密文长度的64位数字来启动流。然后你不必进行尴尬的缓冲,而是以64个额外的比特为代价。更高级别的协议依赖于ASN.1 / DER编码甚至XML来分离消息和身份验证标记。