Using RNCryptor to encrypt and decrypt a file and have an issue that I don't seem to be getting the complete file back.
My encrypt is as follows
- (void) encryptDownloadedFile:(NSString*)filename
{
NSString *outputTmpFilePath = [downloadCacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mov", filename]];
int blockSize = 32 * 1024;
__block NSInputStream *plainTextStream = [NSInputStream inputStreamWithData:[downloadFileStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]];
__block NSOutputStream *encryptedStream = [NSOutputStream outputStreamToFileAtPath:outputTmpFilePath append:NO];
__block NSMutableData *downloadedFileData = [NSMutableData data];
[plainTextStream open];
[encryptedStream open];
__block NSMutableData *data = [NSMutableData dataWithLength:blockSize];
__block RNEncryptor *encryptor = nil;
dispatch_block_t readStreamBlock = ^{
[data setLength:blockSize];
NSInteger bytesRead = [plainTextStream read:[data mutableBytes] maxLength:blockSize];
if (bytesRead < 0) {
// Throw an error
}
else if (bytesRead == 0) {
[encryptor finish];
[downloadedFileData writeToFile:outputTmpFilePath atomically:YES];
[plainTextStream close];
[encryptedStream close];
[downloadFileStream close];
plainTextStream = nil;
encryptedStream = nil;
downloadFileStream = nil;
}
else {
[data setLength:bytesRead];
[encryptor addData:data];
}
};
encryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings
password:@"blah"
handler:^(RNCryptor *cryptor, NSData *data) {
[downloadedFileData appendBytes:data.bytes length:data.length];
if (cryptor.isFinished) {
}
else {
readStreamBlock();
}
}];
readStreamBlock();
}
Pretty much standard from the example on the RNCryptor git page. The input file is a downloaded file that was grabbed earlier with
downloadFileStream = [[NSOutputStream alloc] initToMemory];
Decrypt for completeness is here
- (void) decryptDownloadedFile:(NSString*)filename
{
NSString *inputTmpFilePath = [downloadCacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mov", filename]];
NSString *outputTmpFilePath = [downloadCacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"decrypted%@.mov", filename]];
int blockSize = 32 * 1024;
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:inputTmpFilePath];
NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:outputTmpFilePath append:NO];
[cryptedStream open];
[decryptedStream open];
__block NSMutableData *data = [NSMutableData dataWithLength:blockSize];
__block RNDecryptor *decryptor = nil;
dispatch_block_t readStreamBlock = ^{
[data setLength:blockSize];
NSInteger bytesRead = [cryptedStream read:[data mutableBytes] maxLength:blockSize];
if (bytesRead < 0) {
// Throw an error
}
else if (bytesRead == 0) {
[decryptor finish];
[decryptedStream close];
}
else {
[data setLength:bytesRead];
[decryptor addData:data];
}
};
decryptor = [[RNDecryptor alloc] initWithPassword:@"blah"
handler:^(RNCryptor *cryptor, NSData *data) {
[decryptedStream write:data.bytes maxLength:data.length];
if (cryptor.isFinished) {
}
else {
readStreamBlock();
}
}];
readStreamBlock();
}
Again very similar to the git page.
However, I am 60 bytes short at the tail when I encrypt and decrypt the same file. Not a huge issue until I start to finesse this to support resumable downloads. Then the missing bytes are critical as they appear in the centre of the file.
I have checked what is coming in and out as below
- Bytes being sent to the encryptor 19,615,005 (the size of the file)
- Encrypted bytes created by the encryptor 19,615,026
- Bytes received by the decrypt or 19,615,026
- Decrypted bytes from the decryptor 19,614,944
I have tried
- Changing the .options = kCCOptionPKCS7Padding in RNCryptor.h
- Tried padding to the block size and to a 16 byte boundary myself
- Changed the encryptor finish and readStreamBlock calls to make sure its not a timing issue
- Investigated finishWithError in RNCryptorEngine, but there are no errors
Padding to the block size sort of works as I can then clean up the zeros that I padded with, but its not ideal.
I am scratching my head. Anyone been through this loop before?
Update:
Spent a long time scratching my head and realised I don't have the capability to figure out the issue in a timely fashion. However, I stepped backwards to the simple solution and this works, but not for me as this gives me memory issues.
- (void) simpleEncrypt:(NSString*)filename
{
NSLogDebug(@"simpleEncrypt");
NSString *outputTmpFilePath = [downloadCacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mov", filename]];
NSData *data = [downloadFileStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
NSError *error;
NSData *encryptedData = [RNEncryptor encryptData:data
withSettings:kRNCryptorAES256Settings
password:@"blah"
error:&error];
[encryptedData writeToFile:outputTmpFilePath options:NSDataWritingAtomic error:&error];
NSLogDebug(@"simpleEncrypt isFinished");
}
- (void) simpleDecrypt:(NSString*)filename
{
NSLogDebug(@"simpleDecrypt");
NSString *inputTmpFilePath = [downloadCacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mov", filename]];
NSString *outputTmpFilePath = [downloadCacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"decrypted%@.mov", filename]];
NSData *encryptedData = [[NSFileManager defaultManager] contentsAtPath:inputTmpFilePath];
NSError *error;
NSData *decryptedData = [RNDecryptor decryptData:encryptedData
withPassword:@"blah"
error:&error];
[decryptedData writeToFile:outputTmpFilePath options:NSDataWritingAtomic error:&error];
NSLogDebug(@"simpleDecrypt isFinished");
}
If I mix it with the previous solution I get the same problem.