iOS Common Crypto Decrypt RC4 Encrypted video file Memory crash - ios

I am decrypting a video file which works perfectly for small sizes files but for files above 300mb, there is memory crash. The code is as below :
I checked the start byte value it goes uptill 315mb and then crashes, my file is sized at 350mb.
It works for few iphones and crashes for few, The best solution was do it in chunks to avoid memory issue but it crashes doing that too.
#define kChunkSizeBytes (1024*1024) // 1 MB
#implementation NSMutableData (Crypto)
-(BOOL) doCrypto:(NSString *)key operation: (CCOperation) operation
{
//Keeping it 32 as per our key
char keyPtr[512 + 1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// Fetch key data
if (![key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]) {return FALSE;} // Length of 'key' is bigger than keyPtr
CCCryptorRef cryptor;
CCCryptorStatus cryptStatus = CCCryptorCreate(operation, kCCAlgorithmRC4, 0,
keyPtr, key.length,
NULL, // IV - needed?
&cryptor);
if (cryptStatus != kCCSuccess) { // Handle error here
return FALSE;
}
size_t dataOutMoved;
size_t dataInLength = kChunkSizeBytes; // #define kChunkSizeBytes (16)
size_t dataOutLength = CCCryptorGetOutputLength(cryptor, dataInLength, FALSE);
size_t totalLength = 0; // Keeps track of the total length of the output buffer
size_t filePtr = 0; // Maintains the file pointer for the output buffer
NSInteger startByte; // Maintains the file pointer for the input buffer
char *dataIn = malloc(dataInLength);
char *dataOut = malloc(dataOutLength);
NSRange bytesRange = NSMakeRange((NSUInteger) 0, (NSUInteger) 0);
for (startByte = 0; startByte <= [self length]; startByte += kChunkSizeBytes) {
if ((startByte + kChunkSizeBytes) > [self length]) {
dataInLength = [self length] - startByte;
}
else {
dataInLength = kChunkSizeBytes;
}
// Get the chunk to be ciphered from the input buffer
bytesRange = NSMakeRange((NSUInteger) startByte, (NSUInteger) dataInLength);
[self getBytes:dataIn range:bytesRange];
cryptStatus = CCCryptorUpdate(cryptor, dataIn, dataInLength, dataOut, dataOutLength, &dataOutMoved);
if (startByte >= 203728200) {
NSLog(#"%ld",(long)startByte);
}
if (dataOutMoved != dataOutLength) {
NSLog(#"dataOutMoved (%d) != dataOutLength (%d)", dataOutMoved, dataOutLength);
}
if ( cryptStatus != kCCSuccess)
{
NSLog(#"Failed CCCryptorUpdate: %d", cryptStatus);
}
// Write the ciphered buffer into the output buffer
bytesRange = NSMakeRange(filePtr, (NSUInteger) dataOutMoved);
[self replaceBytesInRange:bytesRange withBytes:dataOut];
totalLength += dataOutMoved;
filePtr += dataOutMoved;
}
// Finalize encryption/decryption.
cryptStatus = CCCryptorFinal(cryptor, dataOut, dataOutLength, &dataOutMoved);
totalLength += dataOutMoved;
if ( cryptStatus != kCCSuccess)
{
NSLog(#"Failed CCCryptorFinal: %d", cryptStatus);
}
// In the case of encryption, expand the buffer if it required some padding (an encrypted buffer will always be a multiple of 16).
// In the case of decryption, truncate our buffer in case the encrypted buffer contained some padding
[self setLength:totalLength];
// Finalize the buffer with data from the CCCryptorFinal call
NSRange bytesNewRange = NSMakeRange(filePtr, (NSUInteger) dataOutMoved);
[self replaceBytesInRange:bytesNewRange withBytes:dataOut];
CCCryptorRelease(cryptor);
free(dataIn);
free(dataOut);
return 1;
}
#end

If replaceBytesInRange:bytesRange is causing the crash, then my first suggestion for how to avoid the crash is to add error checking for the preceding function calls that it depends upon.
For example, in those cases where it crashes, maybe bytesRange is not a valid / useable value. Maybe its dataOut that is not valid / usable. Your code in your question sets those values from function calls, but does not check the return value for error conditions / error indicators / invalid values.
It might be a related dependent function call. e.g., cryptStatus is set with a call to CCCryptorUpdate(), which has dataOut as an input parameter. I don't know Objective-C, and I'm not familiar with the function CCCryptorUpdate(), but it looks like it would affect / populate dataOut. If it's actually returning an error, then dataOut is probably not in a usable state by the time you use it on the replaceBytesInRange line. Checking the return value of cryptStatus might flag conditions when you should not proceed to use dataOut in later calls.
Which brings me to another thing I noticed : you do check a couple things, but only log them. The checks for if (dataOutMoved != dataOutLength) and for (cryptStatus != kCCSuccess) look like things that should stop execution, or break out of the loop, or something like that, not just log the occurrence.
Another thing I see is that dataOut is malloc()'d once, not cleared, and used repeatedly. This can be entirely valid in some circumstances, but it can also cause exactly the kind of error you are seeing. Does anything in your code assume anything about the content of dataOut? I'm thinking along the lines of C string operations that assume null-termination. If one is not careful, null terminators that are assumed to be there (or maybe are in fact there at first) can be overwritten if, say, the entire buffer is filled. Again, I don't know Objective-C and these functions so well, so this is not meant as a specific statement, but more of an analogy of the kind of thing that might be happening.
TL; DR : Add error checking to each of your function calls, and respond accordingly (break, exit, retry, whatever) so that no later function call attempts to use values that indicate errors or are otherwise invalid.
I'll wager that with added error checking you will (1) stop the crashes, and (2) learn something specific about why it is that files larger than a certain amount yield these crashes.

Related

How does the IV is getting prefixed in the CommonCrypto Objective-C aes?

I am implementing the AES/CBC encryption using the CommonCrypto library for Objective C code base and i found a code as answered by Zaph in which he recommends to prefix the IV with the encrypted text which we receive at the end of CommonCrypto's kCCEncrypt operation method zaph's answer. The bunch of code for AES encryption as suggested by him in one of his answer looks like this :
if (key.length != 16 && key.length != 24 && key.length != 32) {
*error = [NSError errorWithDomain:#"keyLengthError" code:-1 userInfo:nil];
return nil;
}
CCCryptorStatus ccStatus = kCCSuccess;
int ivLength = kCCBlockSizeAES128;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:ivLength + data.length + kCCBlockSizeAES128];
int status = SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);
if (status != 0) {
*error = [NSError errorWithDomain:#"ivError" code:status userInfo:nil];
return nil;
}
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes, key.length,
dataOut.bytes,
data.bytes,
data.length,
dataOut.mutableBytes + ivLength,
dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes + ivLength;
}
else {
if (error) {
*error = [NSError errorWithDomain:#"kEncryptionError" code:ccStatus userInfo:nil];
}
dataOut = nil;
}
return dataOut;
My doubt is at which line and how the IV is getting prefixed with the Encrypted text in the above code? Any help in understanding the code will help me a lot.
Dear all after much head scratching and slacking with the help of a wonderful person i am able to decode what the above code is doing .
There are four parts to it.
1) The NSMutableData is initialized to size that's big enough to
hold the ciphertext and the IV (IV length + data length + block
size)
2) The random IV is generated in place, at the beginning of the
data, using SecRandomCopyBytes.
3) Using a tiny bit of pointer arithmetics, the ciphertext is also
written into the same Data, but the starting pointer is moved after
the IV (dataOut.mutableBytes + ivLength). Technically it means that the dataOut.mutableBytes pointer is moved forward by ivLength
bytes (so it points at immediately after the IV in the large buffer)
4) And finally, any excess noise is cut off by setting the length of
the Data to the ivLength + the length of the ciphertext that CCCrypt
reported
Final interpretation : When dataOut.bytes is passed as IV in CCCrypt then it means the CCCRypt gets the IV from the dataOut by its pointer as it has already the random IV in it which is generated by SecRandomCopyBytes . Also dataOut.mutableBytes + ivLength means the pointer is moved forward by ivLength bytes (so it points at immediately after the IV in the large buffer) of dataOut for storing cipher text as it has already the IV till that position.
cryptBytes which is returned just contains the length of the ciphered text. Thus the ciphered text as explained above is stored from the pointer position which was moved. Thus in short the NSMutableData was initialised with enough space by the code NSMutableData *dataOut = [NSMutableData dataWithLength:ivLength + data.length + kCCBlockSizeAES128];for writing IV and Ciphered text, in the next line of code int status = SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);the random IV of length 16 is written to dataOut.mutableBytes buffer. Now the NSMutableData has IV bytes in it. Next in CCCrypt dataOut.bytes can be passed as IV parameter as NSMutableData already has IV bytes. Moving next we have to give the buffer position for writing the crypted text , thus we are writing to NSMutableData by moving the pointer position defined by dataOut.mutableBytes + ivLength so this is actually the buffer position from where we have to write ciphered text after IV. At last cryptoBytes returns the length of the ciphered text(it does not has length of IV included as starting pointer was moved by +ivLength )
So finally NSMutableData has IV(length of 16 bytes) and Ciphered text(length given by cryptBytes) in it and thus the extra buffer space in NSMutableData is trimmed of by giving the length of dataOut as dataOut.length = cryptBytes + ivLength;So final dataOut variable has IV plus ciphered text and it is returned in return dataOut;
Note : it's important to consider that CCCrypt is a C function, it doesn't work on objects like NSData or NSStrings, but pointers to memory addresses. in other words, it doesn't care where you feed it inputs from and where it will write its output, as long as the pointed addresses are valid and there's enough space starting from that address allocated. (and of course, for inputs, the actual data is already there).

Zlib decompression method warning using ios 64bit Architecture

I am just updating one of my applications and I have an error with my decompression method.
This is the warning I am experiencing
Implicit conversion loses integer precision: 'unsigned long' to 'unit' (aka 'unsigned int')
this is the line of code its happening on
stream.avail_in = len - stream.total_in;
And this is what the whole method looks like
#pragma mark - ZLib Compression Methods
// Returns the decompressed version if the zlib compressed input data or nil if there was an error
- (NSData*) dataByDecompressingData:(NSData*)data {
NSLog(#"%lu", (unsigned long)data.length);
Byte* bytes = (Byte*)[data bytes];
NSInteger len = [data length];
NSMutableData *decompressedData = [[NSMutableData alloc] initWithCapacity:COMPRESSION_BLOCK];
Byte* decompressedBytes = (Byte*) malloc(COMPRESSION_BLOCK);
z_stream stream;
int err;
stream.zalloc = (alloc_func)0;
stream.zfree = (free_func)0;
stream.opaque = (voidpf)0;
stream.next_in = bytes;
err = inflateInit(&stream);
CHECK_ERR(err, #"inflateInit");
while (true) {
stream.avail_in = len - stream.total_in;
stream.next_out = decompressedBytes;
stream.avail_out = COMPRESSION_BLOCK;
err = inflate(&stream, Z_NO_FLUSH);
[decompressedData appendBytes:decompressedBytes length:(stream.total_out-[decompressedData length])];
if(err == Z_STREAM_END)
break;
CHECK_ERR(err, #"inflate");
}
err = inflateEnd(&stream);
CHECK_ERR(err, #"inflateEnd");
free(decompressedBytes);
return decompressedData;
}
First off, you should not use stream.total_in. It may or may not be a large enough type for your application. It is always unsigned long. Use your own total input counter sized for your application and ignore stream.total_in.
Second, I'm guessing that your CHECK_ERR() aborts somehow. You should not abort in the event of a Z_BUF_ERROR. In that case, you can continue by simply providing more input and/or more output space.
Third, the problem here is that you need to pick a stream.avail_in that is assured to fit in unsigned. You should be comparing the amount of remaining input to the largest value of unsigned, e.g. UINT_MAX or (unsigned)0 - 1. If the remaining data is larger, use the max value and deduct that from the remaining input. If smaller or equal, use all of it and set the remaining input to zero.

Is there a more efficient way to compare an array of bytes in C

I'm trying to detect if a given file is a ZIP file by looking at the first four bytes. This is in an iOS app so the file handle stuff is handled by the Cocoa framework, but the actual byte comparison stuff is straight up C, which I don't really know.
unsigned char aBuffer[4];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *data = [fileHandle readDataOfLength:4];
[data getBytes:aBuffer];
if (aBuffer[0] == 0x50 && aBuffer[1] == 0x4b && aBuffer[2] == 0x03 && aBuffer[3] == 0x04) {
archiveType = ARCHIVE_TYPE_ZIP;
}
It works but strikes me as ungainly. Is there a better way to compare those 4 bytes? (And yes I know it needs more error checking.)
You chould use memcmp. It's like strcmp, but for memory.
if (memcmp([data bytes],"PK\3\4",4) == 0) {
// success
}
That said, since you're using Objective-C you should be looking for a higher-level implementation than C. I suggest building an NSData with the data you expect, then using [data isEqual: expectedData].
NSData *expectedHeader = [NSData dataWithBytes: "PK\3\4" length: 4];
if ([expectedHeader isEqual: data]) {
// success
}
You can also use isEqualToData: instead if isEqual:. I prefer the short identifier, but isEqualToData: is more efficient and throws when exposed to mismatched types.
You're now very near your intent, rather than the actual mechanics.
#jsd clarified that he was looking for programmer efficiency, rather than runtime efficiency. But for anyone reading this in the future: Forget about runtime efficiency. How often are you checking for a zip header? Instead, worry about how simple the code is and how many ways it can go wrong. And always favour higher-level abstractions when they fit.
You could always put aBuffer in a union, so you can check with just one comparison:
union {
unsigned char asBytes[4];
uint32_t asInt;
} aBuffer;
...
[data getBytes:aBuffer.asBytes];
if (aBuffer.asInt == 0x504b0304) { ... } // or 0x04034b50, depending on endianness
There is not really any more efficient way that I can think of.
The compiler will probably do a fine job of optimizing this for you.
And since it is only a single statement that is not in a loop or anything, I am not sure if there is a reason to try to optimize it by hand.
One thing that you could do would be to do an unsigned long compare as in
unsigned char fileCheck [4] = {0x50, 0x4b, 0x03, 0x04};
unsigned char aBuffer[4];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *data = [fileHandle readDataOfLength:4];
[data getBytes:aBuffer];
if (*(unsigned long *)aBuffer == *(unsigned long *)fileCheck) {
// it is a file
archiveType = ARCHIVE_TYPE_ZIP;
}
That's ungainly but unlikely to be inefficient.
An alternative would be to use C's memcmp function. For example:
if(!memcmp(aBuffer, "PK\003\004", 4))
{
archiveType = ARCHIVE_TYPE_ZIP;
}
You might also prefer to cut the extra buffer:
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *data = [fileHandle readDataOfLength:4];
if ([data length] >= 4 && !memcmp([data bytes], "PK\003\004", 4))
{
archiveType = ARCHIVE_TYPE_ZIP;
}
C (and, hence, Objective-C) guarantees left to right evaluation of if statements and early exit if possible so checking the length first like that will explicitly avoid the memcmp wandering out of bounds.
Have you tried the following?
if (*((unsigned int *) aBuffer) == 'PK\3\4') {
archiveType = ARCHIVE_TYPE_ZIP;
}
This should work on iOS devices like the iPad and iPhone, where data type sizes are consistent.
Those downvoting me must not be familiar with multicharacter literals.

How to store CFBitVector (or any CFType) in Core Data using CFData/NSData?

I have limited experience working with the Core Foundation types & collections, so apologies if this is obvious.
I'm using the CFBitVector type to store some bit sequences, and I need to store it in a binary data format (so that it can be added to a Core Data store). The most sensible thing seems to be to store this in a CFData type, which can be toll-free bridged with an NSData and added to the store, but I am unsure of how to do this.
Can anybody help me out with a simple example of storing CFTypes in CF/NSData?
Edit:
Is this even the right approach? Should I try converting the CFBitVector into a series of ints which can then be stored in the data model? Or perhaps a transformable attribute?
The way I ended up doing this was to roll my own attribute transformer in order to convert a CFBitVectorRef into an NSData instance. The benefit of this is that I can really cram the bit array tightly into a block of binary data, as in my case I really need to keep the storage size to a minimum.
Below is the implementation of my CFBitVectorTransformer class. It essentially reads each bit and packs them into unsigned chars ("segments" in the code below), which are then appended to a mutable NSData buffer. The code would work with types larger than unsigned chars, however I wanted the smallest chunks possible in order to really minimise the size of the resulting data.
#define kBitsPerByte 8
#implementation CFBitVectorTransformer
+ (Class)transformedValueClass
{
return [NSData class];
}
+ (BOOL)allowsReverseTransformation
{
return YES;
}
/* CFBitVectorRef -> NSData */
- (id)transformedValue:(id)value
{
if (!value) return nil;
if ([value isKindOfClass:[NSData class]]) return value;
/* Prepare the bit vector. */
CFBitVectorRef bitVector = (__bridge CFBitVectorRef)value;
CFIndex bitVectorCount = CFBitVectorGetCount(bitVector);
/* Prepare the data buffer. */
NSMutableData *bitData = [NSMutableData data];
unsigned char bitVectorSegment = 0;
NSUInteger bytesPerSegment = sizeof(char);
NSUInteger bitsPerSegment = bytesPerSegment * kBitsPerByte;
for (CFIndex bitIndex = 0; bitIndex < bitVectorCount; bitIndex++) {
/* Shift the bit into the segment the appropriate number of places. */
CFBit bit = CFBitVectorGetBitAtIndex(bitVector, bitIndex);
int segmentShift = bitIndex % bitsPerSegment;
bitVectorSegment |= bit << segmentShift;
/* If this is the last bit we can squeeze into the segment, or it's the final bit, append the segment to the data buffer. */
if (segmentShift == bitsPerSegment - 1 || bitIndex == bitVectorCount - 1) {
[bitData appendBytes:&bitVectorSegment length:bytesPerSegment];
bitVectorSegment = 0;
}
}
return [NSData dataWithData:bitData];
}
/* NSData -> CFBitVectorRef */
- (id)reverseTransformedValue:(id)value
{
if (!value) return NULL;
if (![value isKindOfClass:[NSData class]]) return NULL;
/* Prepare the data buffer. */
NSData *bitData = (NSData *)value;
char *bitVectorSegments = (char *)[bitData bytes];
NSUInteger bitDataLength = [bitData length];
/* Prepare the bit vector. */
CFIndex bitVectorCapacity = bitDataLength * kBitsPerByte;
CFMutableBitVectorRef bitVector = CFBitVectorCreateMutable(kCFAllocatorDefault, bitVectorCapacity);
CFBitVectorSetCount(bitVector, bitVectorCapacity);
for (NSUInteger byteIndex = 0; byteIndex < bitDataLength; byteIndex++) {
unsigned char bitVectorSegment = bitVectorSegments[byteIndex];
/* Store each bit of this byte in the bit vector. */
for (NSUInteger bitIndex = 0; bitIndex < kBitsPerByte; bitIndex++) {
CFBit bit = bitVectorSegment & 1 << bitIndex;
CFIndex bitVectorBitIndex = (byteIndex * kBitsPerByte) + bitIndex;
CFBitVectorSetBitAtIndex(bitVector, bitVectorBitIndex, bit);
}
}
return (__bridge_transfer id)bitVector;
}
#end
This nicely abstracts the conversion of data, allowing you to just set the CFBitVectorRef as an attribute in the data model, and should be plenty fast enough for most purposes.
I hope this helps somebody else in a similar situation.

Find Character String In Binary Data

I have a binary file I've loaded using an NSData object. Is there a way to locate a sequence of characters, 'abcd' for example, within that binary data and return the offset without converting the entire file to a string? Seems like it should be a simple answer, but I'm not sure how to do it. Any ideas?
I'm doing this on iOS 3 so I don't have -rangeOfData:options:range: available.
I'm going to award this one to Sixteen Otto for suggesting strstr. I went and found the source code for the C function strstr and rewrote it to work on a fixed length Byte array--which incidentally is different from a char array as it is not null terminated. Here is the code I ended up with:
- (Byte*)offsetOfBytes:(Byte*)bytes inBuffer:(const Byte*)buffer ofLength:(int)len;
{
Byte *cp = bytes;
Byte *s1, *s2;
if ( !*buffer )
return bytes;
int i = 0;
for (i=0; i < len; ++i)
{
s1 = cp;
s2 = (Byte*)buffer;
while ( *s1 && *s2 && !(*s1-*s2) )
s1++, s2++;
if (!*s2)
return cp;
cp++;
}
return NULL;
}
This returns a pointer to the first occurrence of bytes, the thing I'm looking for, in buffer, the byte array that should contain bytes.
I call it like this:
// data is the NSData object
const Byte *bytes = [data bytes];
Byte* index = [self offsetOfBytes:tag inBuffer:bytes ofLength:[data length]];
Convert your substring to an NSData object, and search for those bytes in the larger NSData using rangeOfData:options:range:. Make sure that the string encodings match!
On iPhone, where that isn't available, you may have to do this yourself. The C function strstr() will give you a pointer to the first occurrence of a pattern within the buffer (as long as neither contain nulls!), but not the index. Here's a function that should do the job (but no promises, since I haven't tried actually running it...):
- (NSUInteger)indexOfData:(NSData*)needle inData:(NSData*)haystack
{
const void* needleBytes = [needle bytes];
const void* haystackBytes = [haystack bytes];
// walk the length of the buffer, looking for a byte that matches the start
// of the pattern; we can skip (|needle|-1) bytes at the end, since we can't
// have a match that's shorter than needle itself
for (NSUInteger i=0; i < [haystack length]-[needle length]+1; i++)
{
// walk needle's bytes while they still match the bytes of haystack
// starting at i; if we walk off the end of needle, we found a match
NSUInteger j=0;
while (j < [needle length] && needleBytes[j] == haystackBytes[i+j])
{
j++;
}
if (j == [needle length])
{
return i;
}
}
return NSNotFound;
}
This runs in something like O(nm), where n is the buffer length, and m is the size of the substring. It's written to work with NSData for two reasons: 1) that's what you seem to have in hand, and 2) those objects already encapsulate both the actual bytes, and the length of the buffer.
If you're using Snow Leopard, a convenient way is the new -rangeOfData:options:range: method in NSData that returns the range of the first occurrence of a piece of data. Otherwise, you can access the NSData's contents yourself using its -bytes method to perform your own search.
I had the same problem.
I solved it doing the other way round, compared to the suggestions.
first, I reformat the data (assume your NSData is stored in var rawFile) with:
NSString *ascii = [[NSString alloc] initWithData:rawFile encoding:NSAsciiStringEncoding];
Now, you can easily do string searches like 'abcd' or whatever you want using the NSScanner class and passing the ascii string to the scanner. Maybe this is not really efficient, but it works until the -rangeOfData method will be available for iPhone also.

Resources