I am working on data encryption sample code provided by Apple in the "Certificate, Key and Trust Programming guide". The sample code for encrypting/decrypting data considers an uint8_t. However the real world application would be doing this on an NSString object. I have been trying to convert NSString object to uint8_t but every-time I try I get a compiler warning. Solutions given for 'almost' same problems given in various forums, don't seem to work for me.
Here is an example of turning any string value into a uint8_t*. The easiest way is to just cast the bytes of NSData as and uint8_t*. Other option is to allocate memory and copy the bytes but you will still need to track the length somehow.
NSData *someData = [#"SOME STRING VALUE" dataUsingEncoding:NSUTF8StringEncoding];
const void *bytes = [someData bytes];
int length = [someData length];
//Easy way
uint8_t *crypto_data = (uint8_t*)bytes;
Optional way
//If you plan on using crypto_data as a class variable
// you will need to do a memcpy since the NSData someData
// will get autoreleased
crypto_data = malloc(length);
memcpy(crypto_data, bytes, length);
//work with crypto_data
//free crypto_data most likely in dealloc
free(crypto_data);
NSString *stringToEncrypt = #"SOME STRING VALUE";
uint8_t *cString = (uint8_t *)stringToEncrypt.UTF8String;
Related
I'm having some really REALLY weird issues with NSString. When I read from an input stream and convert the data to a string I'm not able to set anything equal to that string. Here's the code:
NSString *name = r.URL.lastPathComponent;
NSString *data;
NSInputStream *stream = r.HTTPBodyStream;
uint8_t byteBuffer[1];
[stream open];
if (stream)
{
// Get the request body from the stream. Used for setting the file name
if (stream.hasBytesAvailable)
{
NSInteger bytesRead = [stream read:byteBuffer maxLength:4096];
NSString *temp = [[NSString alloc] initWithBytes:byteBuffer length:bytesRead encoding:NSUTF8StringEncoding];
data = temp; // EXC_BAD_ACCESS thrown here
}
}
I need to copy the string over to another string but I can't. Does anyone know why this is happening?
Your byte buffer is one byte big but you're reading 4096 bytes into it. This is likely to trigger a cascading sequence of events that culminates into the crash.
I have an Image that is converted into NSData and which in-turn is converted into base64 encoded NSString. Now i have a service that accepts this base64 encoded string in only Byte Array(Java Supported). I tried different options but i am not able to convert the encoded string into Byte Array type. Can someone please help me on how to convert the encoded string into Byte Array(Java supported)?
Below is my answer for this. I came to know that the easiest way is to convert the image data into byte array directly without base64encoding the data.
NSError* error;
NSString* str=[[NSBundle mainBundle] pathForResource:#"black-circle" ofType:#"png"];
NSData* data= [NSData dataWithContentsOfFile:str options:NSDataReadingUncached error:&error];
NSInteger length=[data length];
const char* dataBytes=[data bytes];
Now you can traverse each byte using dataBytes pointer.
Hope this helps.
You can try out :
Use-[NSString UTF8String] which returns const char*.
or this link will help you :
Convert NSString into byte array iphone
We can get the NSdata from the NSStirng and then convert NSData into Byte Array. The easier way is to convert the captured image data into Byte Array using uint8_t. Below is the code snippet that does the trick.
NSData to Byte Array:-
NSData *data = UIImagePNGRepresentation(imageCaptured);
uint8_t byteArray[[data length]];
[data getBytes:&byteArray];
NSMutableString *byteArrayString = [[NSMutableString alloc]init];
for (int i = 0 ; i < sizeof(byteArray); i++) {
[byteArrayString appendString:[NSString stringWithFormat:#"%d,",byteArray[i]]];
}
// Removing last extra comma
[byteArrayString deleteCharactersInRange:NSMakeRange([byteArrayString length]-1, 1)];
I'm trying to save and load a custom file format in an iOS app. The format stores various data, but among it is the name a user entered for a map. I found that at random, as I'm trying to read this map name (stored as chars in the file), my NSData object will read garbage. Here is my code for reading the map name (start is already set to the correct start location):
NSData* data = ....;
uint mapNameLength;
char* mapNameChar;
NSString* mapNameString;
[data getBytes:&mapNameLength range:NSMakeRange(start, 4)];
start += 4;
mapNameChar = (char *)malloc(sizeof(char) * mapNameLength);
[data getBytes:mapNameChar range:NSMakeRange(start, mapNameLength)];
mapNameString = [NSString stringWithUTF8String:mapNameChar];
NSLog(#"mapNameLength: %u, mapNameChar: %s, Map name string: %#", mapNameLength, mapNameChar, mapNameString);
As you can see, I am reading the length of the name, then reading that many char values, then converting it into an NSString. Here is the output of the NSLog when it works (I just hit the keyboard a bunch to make a long name):
mapNameLength: 49, mapNameChar: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdv, Map name string: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdv
Here is the output of the NSLog when it doesn't work:
mapNameLength: 49, mapNameChar: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdvfi?p?, Map name string: (null)
Those "?" are actually special characters that don't display here, so I added them. The first is "ETX" and the second is "SOH" in Sublime Text.
To create the file, this is what I am doing:
NSString* mapName = ....;
uint mapNameLength = (uint)mapName.length;
NSMutableData* data = ....;
//...
//Write file type and version here
//...
[data appendBytes:&mapNameLength length:4];
[data appendData:[mapName dataUsingEncoding:NSUTF8StringEncoding]];
//...
//Write other stuff
//...
NSString* path = [FileManager applicationDocumentsDirectory];
path = [path stringByAppendingFormat:#"/%#", fileName];
BOOL success = [data writeToFile:path options:NSDataWritingAtomic error:&error];
I've only written the file once, so I know the data is always the same. Why then, would my data object sometimes get random bytes from it?
I am not certain, however I don't think you are storing a terminating NUL character, so you need to add one to the buffer once you've read the string in:
mapNameChar = (char *)malloc(sizeof(char) * (mapNameLength + 1)); // Add +1
[data getBytes:mapNameChar range:NSMakeRange(start, mapNameLength)];
mapNameChar[mapNameLength] = '\0'; // Terminate with NUL
mapNameString = [NSString stringWithUTF8String:mapNameChar];
Better still forget about malloc() and the C-String and create the NSString directly from the NSData object:
mapNameString = [[NSString alloc] initWithBytes:(const char *)([data bytes]) + start
length:mapNameLength
encoding:NSUTF8StringEncoding];
I am using a technique outlined in the book Hacking and Securing iOS Applications (relevant section here) to wipe the underlying buffer of a NSString as shown below.
NSString *s = [NSString stringWithFormat:#"Hello"];
unsigned char *text = (unsigned char*)CFStringGetCStringPtr((CFStringRef)s, CFStringGetSystemEncoding());
if (text != NULL)
{
memset(text, 0, [s length]);
}
This works, unless the string is a certain value.
// The following crashes with EXC_ACCESS_ERROR on memset
NSString *s = [NSString stringWithFormat:#"No"];
NSString *s = [NSString stringWithFormat:#"Yes"];
// These work fine though
NSString *s = [NSString stringWithFormat:#"Hello"];
NSString *s = [NSString stringWithFormat:#"Do"];
NSString *s = [#"N" stringByAppendingString:#"o"];
It looks like certain strings are not created on the heap but is optimized by making it point to a read-only string table even if the string is created on the heap.
Indeed constant string are not created on the heap and are in read-only memory. This includes a few that look like runtime but are made compile-time constants, your examples are such statements.
With this statement fragment there is no reason not to make it a compile-time constant.
[NSString stringWithFormat:#"No"]
it is equivalent to:
#"No"
Suggestion, file a bug report requesting a secure string class, I have. Several have been filed and I have been told that if there are enough (whatever that amount might be) are files it will implement it.
It is possible to subclass NSString, not easy but you will have compete control of the actual buffer and it should not be subject to possible failure due to Apple changing the implementation detail. I have done that successfully.
NSString *s = [NSString stringWithFormat:#"No"]; will be optimised to NSString *s = #"No"; because there are no substitutions in the format. Assigning a literal will give you a pointer to the readonly text segment of the loaded binary.
NSString *s = [#"N" stringByAppendingString:#"o"]; will create a new string on the heap and return a reference to it. The heap is read-write even though the datatype NSString is readonly.
When you get the CString pointer to the underlying data it's pointing either into read-only data in the first case, or read-write data in the second. The memset will fail on the readonly memory, but succeed on the read-write.
I have some sensitive data I want to clear directly after use. Currently, the sensitive data is in the form of NSString. NSString is in my understanding immutable, meaning that I can't really clear the data. NSMutableString seems more appropriate, though, as it is mutable and has methods like replaceCharactersInRange and deleteCharactersInRange. I have no knowledge of the implementation details so I wonder if NSMutableString would serve my purpose?
I would be afraid NSMutableString would try to optimize and leave the string in memory. If you want more control try allocating your own memory then create an NSString with it. If you do that you can overwrite the memory before you release it.
char* block = malloc(200);
NSString* string = [[NSString alloc] initWithBytesNoCopy:length:encoding:freeWhenDone];
//use string
memset(block, 0, 200);// overwrite block with 0
[string release];
free(block);
You need to wipe the c pointer with zeros with a memset function however a memset call can be optimized out by the compiler, see What is the correct way to clear sensitive data from memory in iOS?
So the code could be something like this:
NSString *string = #"hi";
unsigned char *stringChars = (unsigned char *)CFStringGetCStringPtr((CFStringRef)string, CFStringGetSystemEncoding());
safeMemset(stringChars, 0, [string length]);
But be careful clearing the underlying c pointer of an NSString. On a device for example, if the string contains the word "password", the underlying c pointer just reuses or points to the same address as used by the system and you will crash by trying to wipe this area of memory.
To be safe you may want to use a char array, not the char pointer, to store your sensitive strings and wipe them after without ever putting it into an NSString object.
If an attacker can read the contents of memory, you are beyond hosed.
-release the string and be done with it. There's no way to know if you've deleted any possible copies of the string in various caches (such as if you draw it to screen, etc).
You probably have much more significant security issues to worry about.
As of iOS9, inner pointer of NSString obtained from the snippet below has become read-only and generates bad access when trying to set the bytes.
unsigned char *stringChars = (unsigned char *)CFStringGetCStringPtr((CFStringRef)string, CFStringGetSystemEncoding());
It is possible with NSMutableString but then if you have another NSString source, say from a textfield, that source will still be in memory and you're still out of luck.
If you are creating a new NSString, The best way is implement your own String class with underlying byte array. Provide a method to create NSString copies using the underlying byte array as the inner pointer.:
-(NSString *)string
{
return [[NSString alloc] initWithBytesNoCopy:_buff length:_length encoding:NSUTF8StringEncoding freeWhenDone:NO];
}
// Will prematurely wipe data and all its copies when called
- (void)clear
{
// Volatile keyword disables compiler's optimization
volatile unsigned char *t = (unsigned char *)_buff;
int len = _length;
while (len--) {
*t++ = 0;
}
}
// In case you forget to clear, it will cleared on dealloc
- (void)dealloc
{
[self clear];
free(_buff);
}