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 に答える