1

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

可能我很困惑,因为此代码已被证明,但我想了解这一点。

提前致谢。

4

1 回答 1

1

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

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

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

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

您可以重写方案,使密文长度的长度预先知道。例如,您可以通过表示密文长度的 64 位数字开始流。这样您就不必以额外的 64 位为代价进行笨拙的缓冲了。更高级别的协议依赖 ASN.1/DER 编码甚至 XML 来分​​离消息和身份验证标签。

于 2013-08-29T07:48:21.123 回答