RNDecryptor addData方法中的混淆

时间:2013-08-29 04:24:26

标签: ios objective-c encryption rncryptor

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个字节会发生什么?

可能我很困惑,因为这段代码已被证实,但我想理解这一点。

提前致谢。

1 个答案:

答案 0 :(得分:1)

问题是流通常不知道剩下多少数据。在您的情况下,似乎认证标签(HMAC值)被发送密文的一方放在密文的末尾。

现在问题是您应该只更新数据,而不是身份验证标记。由于您不知道还有多少数据可用,因此您可能已经在最后读取了身份验证标记。如果在计算中包含HMAC本身的输出,显然HMAC计算将失败。

所以基本上你读取流并更新HMAC状态直到结束。然后执行最后一次HMAC更新,直到密文结束。您从流的末尾提取给定的身份验证标记,并比较计算值和给定值。如果它们是相同的,则检查密文(以及明文)的完整性和身份验证 - 假设密钥当然从未向攻击者透露过。

如果代码是正确的(并且给出Rob的代码,那很可能),那么32个字节包含在MAC计算中,除非它们确实是认证标记的一部分。换句话说,如果您将身份验证标记放在最后,则始终必须至少缓冲身份验证标记的大小。

您可以通过预先知道密文长度的方式重写方案。例如,您可以通过表示密文长度的64位数字来启动流。然后你不必进行尴尬的缓冲,而是以64个额外的比特为代价。更高级别的协议依赖于ASN.1 / DER编码甚至XML来分离消息和身份验证标记。