How to create a CMBlockBufferRef from NSData - ios

I am struggling (getting memory errors, or apparently not correctly deallocating memory) trying to create a CMBlockBufferRef filled with data from an existing NSData (or NSMutableData).
I would be happy with a solution that copies data, but ideally I would be looking at a solution that would use the underlying NSData bytes and keep a strong reference to the NSData object until the CMBlockBuffer is deallocated.

Only for a read-only buffer referring a NSData (of course, without copying), I've just found a way to achieve it.
static void releaseNSData(void *o, void *block, size_t size)
{
NSData *data = (__bridge_transfer NSData*) o;
data = nil; // Assuming ARC is enabled
}
OSStatus createReadonlyBlockBuffer(CMBlockBufferRef *result, NSData *data)
{
CMBlockBufferCustomBlockSource blockSource =
{
.version = kCMBlockBufferCustomBlockSourceVersion,
.AllocateBlock = NULL,
.FreeBlock = &releaseNSData,
.refCon = (__bridge_retained void*) data,
};
return CMBlockBufferCreateWithMemoryBlock(NULL, (uint8_t*) data.bytes, data.length, NULL, &blockSource, 0, data.length, 0, result);
}

Related

Veracode CWE ID 416: Use After Free

How to fix Veracode Use After Free (CWE ID 416)
Recommendations from Veracode: Ensure that all pointers are set to NULL once the memory they point to has been freed.
Error pointed on: Line 8 "return result;"
+ (NSData *)dataFromBase64String:(NSString *)aString
{
NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];
size_t outputLength = 0;
void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];
free(outputBuffer);
return result;
}
Veracode scan does just help you to find places in code where you could improve security related coding. It does not stop attacks of course, but if your application is really that much security oriented you can make it more difficult to read out memory that is left after processing.
The word "Error" in the logging of Veracode is maybe a bit overused..
But my suggestion to address Veracodes Error pointed on: Line 8 "return result;" would be..
+ (NSData *)dataFromBase64String:(NSString *)aString
{
if (aString!=nil && [aString length]) {
size_t outputLength = 0;
void *outputBuffer = NULL;
NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];
outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
if (outputBuffer==NULL) return nil; //if NewBase64Decode() failed there is nothing to free..
NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];
free(outputBuffer);
outputBuffer = NULL;
return result;
}
return nil;
}
This is because free'd memory is not set to NULL without your intent, so someone scanning memory for left overs would maybe find some clues about the former content of address.
here some nice discussion if NULL after free is really needed.
If you go that much into detail to avoid any kind of risk then you could also initiate outputBuffer with NULL (void* outputBuffer = NULL;) before you even use it.
Well it is another discussion if this is a bit overdo for some objC code where just swizzling could override the whole method.
EDIT: even more spagetti code, trying to avoid returning any value other than void and change a passed argument instead.
+ (void)dataFromBase64String:(NSString *)aString toResult:(NSData**)result
{
if (aString!=nil && [aString length]) {
size_t outputLength = 0;
void *outputBuffer = NULL;
NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];
outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
if (outputBuffer==NULL) return; //if NewBase64Decode() failed there is nothing to do
*result = [NSData dataWithBytes:outputBuffer length:outputLength];
free(outputBuffer);
outputBuffer = NULL;
}
}
//and call like..
NSData *myresult = nil;
[YOURCLASS dataFromBase64String:#"someString" toResult:&myresult];
NSLog(#"result=%#",myresult);
Now i wonder what Veracode is reporting with the edit above..

How to resolve this error. reason: '-[NSConcreteData AES256EncryptWithKey:]: unrecognized selector sent to instance 0x7ab084a0'?

I'm using AES Encryption and Decryption in my project which is an apple watch project. So for encryption and decryption I created one category for NSData, And I'm calling those methods in my sample view controller class. Its working fine.
And then Now I copied the same code in to my ServerManager class where I should do actual encryption. But it is showing me this error.
Crash with Error :
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteData AES256EncryptWithKey:]: unrecognized selector sent to instance 0x7ab084a0'
The same code is working in sample code and it is showing me this error in my actual project. I didn't understand what could be the reason. I never heard about NSConcreteData. also. Here is my code follows.
In My ServerManager Class :
NSError *err;
NSDictionary *dict = #{#"accountType":#"ALL",#"uId":#"c8ff46be-a083-40
09-8a33-fc2d22cc40e3|123456784",#"deviceId":#"qvxy1234"};
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&err];
NSString *encryptedData = [[jsonData AES256EncryptWithKey:kABAESCryptionKey] base64EncodedStringWithOptions:0];
NSLog(#"%#",encryptedData);
NSData *nsdataFromBase64String = [[NSData alloc]
initWithBase64EncodedString:encryptedData options:0];
and NSData+CustomCategory Class is like this :
- (NSData *)AES256EncryptWithKey:(NSString *)key {
return [self AES256Operation:kCCEncrypt key:key];
}
- (NSData *)AES256DecryptWithKey:(NSString *)key {
return [self AES256Operation:kCCDecrypt key:key];
}
/*!
* #brief Common method for encrypt and decrypt
* #param Operation : pass required crypto CCOperation type
* #param key : pass key based on which crption is done
* #returns NSData object : Encrypted or Decrypted data
*/
- (NSData *)AES256Operation:(CCOperation)operation key:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1];
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesCrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesCrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
}
free(buffer); //free the buffer
return nil;
}
The same code is working in sample but not in my real project...
Addition :
It is not because of base64EncodedStringWithOptions: method.
I tried like this also
NSData *encryptedData = [jsonData AES256EncryptWithKey:kABAESCryptionKey];
So this code is also crashing, So I'm sure the problem is not with base64EncodedStringWithOptions:? some thing is going wrong while I'm using in IWatch extension classes.
Here the kABAESCryptionKey is a proper key which is working fine. in Sample example.
Yeah I got the answer. Its so dumb thing. This category is not added to my watch build target. So it is showing error. Now I added NSData+CustomCategory.m means i checked this file for my watch build target its working fine.
i think the problem is in AES256EncryptWithKey ..see link this may sure help

RSA encryption using SecKeyEncrypt gives error -4 (errSecUnimplemented)

I'm trying to encrypt some data with RSA using the Security framework on iOS. I want to encrypt a simple base64-encoded string as follows:
NSData *data = [[NSData alloc] initWithBase64EncodedString:#"aGFsbG8=" options:0x0];
NSData *encrypted = [pair encrypt:data];
The pair variable holds references to a private key and a public key that were succesfully generated before using SecKeyGeneratePair.
The encrypt function looks like this:
- (NSData *)encrypt:(NSData *)data {
void *buffer = malloc([self blockSize] * sizeof(uint8_t));
memset(buffer, 0x0, [self blockSize]);
size_t ciphertextBufferLength = [data length];
OSStatus res = SecKeyEncrypt([self keyRef], 0x1, [data bytes], [data length], &buffer[0], &ciphertextBufferLength);
NSLog(#"Result of encryption: %d", res);
return [NSData dataWithBytesNoCopy:buffer length:[self blockSize] freeWhenDone:YES];
}
The implementation of [self blockSize] is rather straightforward:
- (unsigned long)blockSize {
return SecKeyGetBlockSize(keyRef);
}
I generate my keys with the following function:
- (BOOL)generateNewKeyPairOfSize:(unsigned int)keySize
{
SecKeyRef privKey = [[self publicKey] keyRef];
SecKeyRef pubKey = [[self publicKey] keyRef];
NSDictionary *privateKeyDict = #{ (__bridge id)kSecAttrIsPermanent : #(YES), (__bridge id)kSecAttrApplicationTag : [[self privateKey] tag] };
NSDictionary *publicKeyDict = #{ (__bridge id)kSecAttrIsPermanent : #(YES), (__bridge id)kSecAttrApplicationTag : [[self publicKey] tag] };
NSDictionary *keyDict = #{ (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeRSA, (__bridge id)kSecAttrKeySizeInBits : #(keySize), (__bridge id)kSecPublicKeyAttrs : publicKeyDict, (__bridge id)kSecPrivateKeyAttrs : privateKeyDict };
OSStatus res = SecKeyGeneratePair((__bridge CFDictionaryRef)keyDict, &privKey, &pubKey);
NSLog(#"Result of generating keys: %d", res);
[[self publicKey] setKeyRef:pubKey];
[[self privateKey] setKeyRef:privKey];
return YES;
}
The problem is that the value of res is -4, meaning errSecUnimplemented according to the documentation. I'm not sure what I'm doing wrong here since I have all parameters required. I'm not sure whether there is a mistake in the parameters and where. The call the [self blockSize] return 128.
Can anyone help me with this?
From documentation:
cipherTextLen - On entry, the size of the buffer provided in the
cipherText parameter. On return, the amount of data actually placed in
the buffer.
You don't set any value to ciphertextBufferLength.
Update #1
In SecKeyGeneratePair() you have wrong arguments: public key argument must be first, private key is the second. I think that is the reason why you have error code -4.
Update #2
When you fix problem from Update #1 you will see error code -50 (errSecParam) because your cipher text length is wrong. Here is how correct encryption/decryption looks like:
[self generateNewKeyPairOfSize:1024];
NSData *data = [[NSData alloc] initWithBase64EncodedString:#"aGFsbG8=" options:0x0];
size_t cipherTextSize = [self blockSize];
uint8_t *cipherText = malloc(cipherTextSize);
memset(cipherText, 0, cipherTextSize);
OSStatus res = SecKeyEncrypt(_publicKey, kSecPaddingPKCS1, [data bytes], data.length, cipherText, &cipherTextSize);
NSLog(#"Result of encryption: %d", res);
size_t plainTextSize = cipherTextSize;
uint8_t *plainText = malloc(plainTextSize);
res = SecKeyDecrypt(_privateKey, kSecPaddingPKCS1, cipherText, cipherTextSize, plainText, &plainTextSize);
NSLog(#"Result of decryption: %d", res);
In addition to the correct answer above, what solved it for me was the following knowledge:
You will get -4 if you are trying to encrypt anything using a SecKeyRef that refers to a private key.
Think about it. Nothing that was encrypted with a private key would be secure because the public key is, well, public. /facepalm
So yeah, Apple's framework does the responsible thing, and simply blocks you from encrypting something with a private key. Because if it allowed you to do something that stupid, then it would be giving you a false sense of security, which would be irresponsible.

Convert NSData to CMSampleBufferRef

Is there any way to create CMSampleBufferRef from NSData? NSData object has also be constructed from CMSampleBufferRef formerly. I need that conversion because I want to save the CMSampleBufferRef frames (as NSData) that are taken from live camera using AVFoundation, then be able to use the CMSampleBufferRef frames (by converting NSData objects to CMSampleBufferRef) to construct a video.
Thanks in advance...
-(AudioBufferList *) getBufferListFromData: (NSData *) data
{
if (data.length > 0)
{
NSUInteger len = [data length];
//I guess you can use Byte*, void* or Float32*. I am not sure if that makes any difference.
Byte * byteData = (Byte*) malloc (len);
memcpy (byteData, [data bytes], len);
if (byteData)
{
AudioBufferList * theDataBuffer =(AudioBufferList*)malloc(sizeof(AudioBufferList) * 1);
theDataBuffer->mNumberBuffers = 1;
theDataBuffer->mBuffers[0].mDataByteSize = len;
theDataBuffer->mBuffers[0].mNumberChannels = 1;
theDataBuffer->mBuffers[0].mData = byteData;
// Read the data into an AudioBufferList
return theDataBuffer;
}
}
return nil;
}

RSA Decryption on ios

I have two scenarios of RSA encryption:
Key pair generated by c# app. Public key sent to iOS. iOS app encrypts data with the key and sends it back. The c# app decrypts using the private key. --- Completely implemented and working.
Key pair generated by ios app. Public key sent to c# app. The c# app encrypts the data, base 64 encodes it and sends it to the ios app. The ios app then decrypts the data using the private key. --- Not working.
If I encrypt using the public and private key pair generated on ios it all works, but as soon as I replace it with the base 64 encoded string from c# it fails. I have tried all combinations of padding.
The code is as follows:
SecPadding padding = kSecPaddingPKCS1;
NSMutableArray *keys = [self generateRSAKeyWithKeySizeInBits:1024 publicKeyTag:#"RSA Public Key" privateKeyTag:#"RSA Private Key"];
SecKeyRef test_publicKey = (__bridge SecKeyRef)(keys[0]);
SecKeyRef test_privateKey = (__bridge SecKeyRef)(keys[1]);
const char *sampled_utf8 = "VnWBW/xRyJB48Uxjdl99apczCuS07zhnLvIjnqyZIQqbI4F7kyAezfD1MNlgeTefkHuCRuzogaQTamk2XRwXoBoGy3Agj4ocPK2Wa7vWNGip8X3FAo1eJL+xKoVoqre/ipDjnZNfEUbX91Ru+IqWkbZXD2POlFfuMaTatCl50+U=";
NSString *sampled = [[NSString alloc] initWithUTF8String:sampled_utf8];
Byte sampled_inputData [[sampled lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];//prepare a Byte[]
[[sampled dataUsingEncoding:NSUTF8StringEncoding] getBytes:sampled_inputData];//get the pointer of the data
size_t sampled_inputDataSize = (size_t)[sampled length];
size_t sampled_outputDataSize = EstimateBas64DecodedDataSize(sampled_inputDataSize);//calculate the decoded data size
Byte sampled_outputData[sampled_outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(sampled_inputData, sampled_inputDataSize, sampled_outputData, &sampled_outputDataSize);//decode the data
NSData *sampled_Data = [[NSData alloc] initWithBytes:sampled_outputData length:sampled_outputDataSize];//create a NSData
NSData *test_encrypted_Data = [self encryptString:encoded_toEncrypt RSAPublicKey:test_publicKey padding:padding];
size_t test_encrypted_inputDataSize = (size_t)[test_encrypted_Data length];
Byte *test_encrypted_inputData = (Byte*) malloc(test_encrypted_inputDataSize);
memcpy(test_encrypted_inputData,[test_encrypted_Data bytes],test_encrypted_inputDataSize);
size_t test_encrypted_outputDataSize = EstimateBas64EncodedDataSize(test_encrypted_inputDataSize);//calculate the encoded data size
char* test_encrypted_outputData[test_encrypted_outputDataSize];//prepare a char for the encoded data
Base64EncodeData(test_encrypted_inputData, test_encrypted_inputDataSize, test_encrypted_outputData, &test_encrypted_outputDataSize,false);//encode the data
NSData *test_encrypted_Encoded = [[NSData alloc] initWithBytes:test_encrypted_outputData length:test_encrypted_outputDataSize];//create a NSData object from the decoded data
size_t input_Size = (size_t)[test_encrypted_Encoded length];
Byte *input = (Byte*) malloc(input_Size);
memcpy(input,[test_encrypted_Encoded bytes],input_Size);
size_t output_Size = EstimateBas64DecodedDataSize(input_Size);
char* output[output_Size];
Base64DecodeData(input, input_Size, output, &output_Size);
NSData *res = [[NSData alloc] initWithBytes:output length:output_Size];
NSData *test_decryptedData = [self decryptString:sampled_Data RSAPrivateKey:test_privateKey padding:padding];
-(NSData*)decryptString:(NSData*)original RSAPrivateKey:(SecKeyRef)privateKey padding: (SecPadding)padding
{
#try
{
const unsigned char* original_String = (unsigned char *)[original bytes];
size_t decryptedLength = SecKeyGetBlockSize(privateKey);
uint8_t decrypted[decryptedLength];
OSStatus status = SecKeyDecrypt(privateKey,
padding,
original_String,
[original length],
decrypted,
&decryptedLength);
NSLog(#"result = %#", [self fetchStatus:status]);
if(status == noErr)
{
NSData* decryptedData = [[NSData alloc] initWithBytes:(const void*)decrypted length:decryptedLength];
return decryptedData;
}
else
return nil;
}
#catch (NSException * e)
{
//do nothing
NSLog(#"exception: %#", [e reason]);
}
return nil;
}
- (NSData*)encryptString:(NSString*)original RSAPublicKey:(SecKeyRef)publicKey1 padding:(SecPadding)padding
{
#try
{
Byte encrypt_inputData [[original lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];//prepare a Byte[]
[[original dataUsingEncoding:NSUTF8StringEncoding] getBytes:encrypt_inputData];//get the pointer of the data
size_t encrypt_inputDataSize = (size_t)[original length];
size_t encrypt_outputDataSize = EstimateBas64DecodedDataSize(encrypt_inputDataSize);//calculate the decoded data size
Byte encrypt_outputData[encrypt_outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(encrypt_inputData, encrypt_inputDataSize, encrypt_outputData, &encrypt_outputDataSize);//decode the data
NSData *encryption_Data = [[NSData alloc] initWithBytes:encrypt_outputData length:encrypt_outputDataSize];//create a NSData object from the decoded data
size_t encryptedLength = SecKeyGetBlockSize(publicKey1);
uint8_t* sample = (uint8_t*)[encryption_Data bytes];
size_t text_Size = [encryption_Data length];
uint8_t *encrypted_Data_Bytes;
encrypted_Data_Bytes = malloc(sizeof(uint8_t)*encryptedLength);
memset(encrypted_Data_Bytes,0,encryptedLength);
OSStatus status = SecKeyEncrypt(publicKey1,
padding,
sample,
text_Size,
&encrypted_Data_Bytes[0],
&encryptedLength);
if(status == noErr)
{
NSData* encryptedData = [[NSData alloc] initWithBytes:(const void*)encrypted_Data_Bytes length:encryptedLength];
return encryptedData;
}
else
return nil;
}
#catch (NSException * e)
{
//do nothing
NSLog(#"exception: %#", [e reason]);
}
return nil;
}

Resources