I have void* buffer...
I need to encapsulate it as fast as possible in a NSData structure (NSData or NSMutableData)
I'm guessing using only one NSMutableData (defined in the class) with
- (void)resetBytesInRange:(NSRange)range
- (void)appendBytes:(const void *)bytes length:(NSUInteger)length
or even
- (void)replaceBytesInRange:(NSRange)range withBytes:(const void *)bytes
will be much faster than using [NSData dataWithData:] every function call...
This is for audio process, so it needs to be the fastest it can be.
Does anyone can confirm that i'll be faster to use NSMutableData instead of NSData constructor ?
EDIT :
the buffer is always the same size, the NSMutableData won't need to re-allocate memory...
If you just want to replace the contents of an (already initialized) NSMutableData *data object with new data from buffer of the same length, then the fastest method is probably
void *mutableBytes = [data mutableBytes];
memcpy(mutableBytes, buffer, length);
because no range check and no (re-)allocation is done. See also the examples in Working With Mutable Binary Data.
But of course, as it is often said in this forum, you should profile your app with Instruments to find out where to optimize the program.
Related
For security reasons we need Always to wipe sensitive data from memory.
Usually it is not something that i see done in IOS but for apps and need extended security it is very important.
The Data that Usually needs to be wiped if NSData and NSString objects (pointing to nil does not wipe the data and it is a security breach)
I've managed to wipe my NSStrings with the code below (When password is NSString):
unsigned char *charPass;
if (password != nil) {
charPass = (unsigned char*) CFStringGetCStringPtr((CFStringRef) password, CFStringGetSystemEncoding());
memset(charPass, 0, [password length]);
password = nil;
}
Big remark on this implementation: You HAVE to check for NULL before calling the charPass or it might crash. There is NO guarantee that CFStringGetCStringPtr will return a value!
When password is NSData It suppose to be even more strait forward and the code bellow suppose to work:
memset([password bytes], 0, [password length]);
But this gives me a compilation error:
No matching function for call to 'memset'
I can't find a workaround to point to the password address and wipe the bytes over there like I did with the string (bytes method should let me do just that from what I understand but it doesn't compile for some reason that I cant figure out)
Any one has an idea for this?
10x
Your string deallocator is fragile. You write:
Big remark on this implementation: You HAVE to check for NULL before calling the charPass or it might crash. There is NO guarantee that CFStringGetCStringPtr will return a value!
This is documented behaviour as CFString (and hence NSString) does not guarantee you direct access to its internal buffer. You don't say what how you handle this situation, but if you don't erase the memory you presumably have a security problem.
In the case you do get a valid pointer back you are using the wrong byte count. The call [password length] returns:
The number of UTF-16 code units in the receiver.
which is not the same as the number of bytes. However CFStringGetCStringPtr returns:
A pointer to a C string or NULL if the internal storage of theString does not allow this to be returned efficiently.
If you have a C string you can use C library function strlen() to find its length.
To address the case when CFStringGetCStringPtr returns NULL you could create the string yourself as a CFString and supply a custom CFAllocater. You shouldn't need to write a complete allocator yourself, instead you could build one based on the system one. You can get the default allocators CFAllocatorContext which will return you the function pointers the system uses. You can then create a new CFAllocator based of a CFAllocatorContext which is a copy of the default one except you've changed the deallocate and reallocate pointers to functions which you have implemented in terms of the default allocate, reallocate and deallocate but also call memset appropriately to clear out memory.
Once you've done that doing your security wipe comes down to making sure these custom created CFString objects, aka NSString objects, are deallocated before your app quits.
You can find out about CFAllocator, CFAllocatorContext etc. in Memory Management Programming Guide for Core Foundation.
Which brings us to your actual question, how to zero an NSData. Here you are in luck an NSData object is a CFData object, and CFData's CFDataGetBytePtr, unlike CFStringGetCStringPtr, is guaranteed to return a pointer to the actual bytes, straight from the documentation:
This function is guaranteed to return a pointer to a CFData object's internal bytes. CFData, unlike CFString, does not hide its internal storage.
So code following your pattern for CFString will work here. Note that using NSData's bytes is not guaranteed in the documentation to call CFDataGetBytePtr, it could for example call CFDataGetBytes and return a copy of the bytes, use the CFData functions.
HTH
While I cannot speak for the actual safety of doing this, your problem is that NSData's bytes method returns a const void *
https://developer.apple.com/documentation/foundation/nsdata/1410616-bytes?language=objc
You can cast it to a void * if you want by
memset((void *)[password bytes], 0, [password length]);
If you use a NSMutableData, you won't have to do this.
WHAT IM DOING I am trying to get an audio file (could be up to an hour long. eg. a Podcast) that I've recorded with AVAudioRecorder to be uploaded to our backend. In addition to being uploaded to the server it needs to be able to be "Paused" and "Resumed" if the user chooses. Because of this, I believe, I need to use dataWithBytesNoCopy:buffer on the NSData class to achieve this.
WHERE IM AT I know for a fact I can get the data with using the passed self.mediaURL property:
if (self.mediaURL) {
NSData *audioData = [NSData dataWithContentsOfURL:self.mediaURL];
if (audioData) {
[payloadDic setObject:audioData forKey:#"audioData"];
}
}
However, this will not give me the desired functionality. I am trying to keep track of the bytes uploaded so that I can resume if the user pauses.
QUESTION How can I use the provided self.mediaURL so that I can retrieve the file and be able to calculate the byte length like this example?
Byte *buffer = (Byte*)malloc((long)audioFile.size);
NSUInteger buffered =[rep getBytes:buffer fromOffset:0.0 length:(long)rep.size error:nil];
NSMutableData *body = [[NSMutableData alloc] init];
body = [NSMutableData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
Instead of making things more complicated for yourself by trying to reinvent the wheel, use what the system gives you. NSURLSession lets you do a background upload. You hand the task to the session (created using the background session configuration) and just walk away. The upload takes place in pieces, when it can. No "pause" or "resume" needed; the system takes care of everything. Your app doesn't even have to be running. If authentication is needed, your app will be woken up in the background as required. This architecture is just made for the situation you describe.
If the problem is that you want random access to file data without having to read the whole thing into a massive NSData, use NSFileHandle.
I have implemented publickey privatekey RSA encryption in iOS application based on the examples provided on the Apple Developer site.
It works perfectly if I encrypt and return the uint8_t cipherBuffer, and then decrypt from the uint8_t cipherBuffer. However I need to store the encrypted data to an .xcdata model as NSData.
The problem I'm having is reliably converting the uint8_t cipherBuffer to NSData and/or converting the NSData back to uint8_t when it's time to decrypt. The decrypted data appears to be truncated.
This is how I'm converting the uint8_t encrypted buffer to NSData:
return [NSData dataWithBytesNoCopy:cipherBuffer length:BUFFER_SIZE];
This is how I'm converting the encrypted NSData back to a uint8_t buffer when it is time to decrypt it:
uint8_t *cipherBuffer = (uint8_t*)[cipherText bytes];
Thanks jgh and Jody;
I changed the encryption method to "malloc" the buffer and tried several approaches to write the bytes to NSData, wound up with:
return [NSData dataWithBytes:(const void *)cipherBuffer length:CIPHER_BUFFER_SIZE];
What finally fixed the issue was changing the way I was creating the uint8_t in the decryption method:
const uint8_t *cipherBuffer = (const uint8_t*)[data bytes];
Without seeing how you're creating cipherBuffer, it's difficult to say exactly why it's not working. However, from the documentation on dataWithBytesNoCopy:
The returned object takes ownership of the bytes pointer and frees it on deallocation. Therefore, bytes must point to a memory block allocated with malloc.
If you're just declaring cipherBuffer as
uint8_t cipherBuffer[BUFFER_SIZE];
it may explain your problems. Instead, use malloc:
uint8_t* cipherBuffer = malloc(BUFFER_SIZE);
It sounds like you are giving it a raw pointer, then re-using that pointer.
dataWithBytesNoCopy: wants to keep the pointer you give it. In fact, you must give it a pointer that you created with malloc, because it will free it when it's done with the data.
If you do not want the NSData object to take ownership, you should use dataWithBytesNoCopy:length:freeWhenDone:.
I want to get the first 8 bytes or so of a file without reading the whole file. I'm using NSData to operate on the data and such, but I don't want to slow down my application with excessive file reads and writes because in some cases I'm having to read a 200 kilobyte file just to extract the first 2 bytes of data from the file. Is there any way to only read or write a part of the file without reading or overwriting the whole thing in Xcode with the iOS SDK?
The file system that I'm using is just the default one that's accessible through the NSFileManager class (I don't know of any other iOS file system).
You may take advantage of the higher level NSFileHandle class. The NSFileHandle class is an object-oriented wrapper for a file descriptor. You use file handle objects to access data associated with files, sockets, pipes, and devices. For files, you can read, write, and seek within the file. For sockets, pipes, and devices, you can use a file handle object to monitor the device and process data asynchronously.
- (NSData *)readDataOfLength:(NSUInteger)length
You can get more info in official documentation NSFileHandle Class Reference
Use the standard C file API (either FILE* or int file descriptors). The caveat is that you have to properly convert the string path to a correct char* file path. Also, don't forget to close the file when done. Consider a category on NSData, something kinda like this...
+ (id)dataWithContentsOfFile:(NSString *)filePath numBytes:(NSUInteger)numBytes
{
void *bytes = malloc(numBytes);
NSData *result = [NSData dataWithBytesNoCopy:bytes length:numBytes];
char const *path = [[NSFileManager defaultManager] fileSystemRepresentationWithPath:filePath];
int fd;
if ((fd = open(path, O_RDONLY)) < 0 || read(fd, bytes, numBytes) != numBytes) {
result = nil;
}
close(fd);
return result;
}
I want to clear sensitive data from memory in my iOS app.
In Windows I used to use SecureZeroMemory. Now, in iOS, I use plain old memset, but I'm a little worried the compiler might optimize it:
https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/771-BSI.html
code snippet:
NSData *someSensitiveData;
memset((void *)someSensitiveData.bytes, 0, someSensitiveData.length);
Paraphrasing 771-BSI (link see OP):
A way to avoid having the memset call optimized out by the compiler is to access the buffer again after the memset call in a way that would force the compiler not to optimize the location. This can be achieved by
*(volatile char*)buffer = *(volatile char*)buffer;
after the memset() call.
In fact, you could write a secure_memset() function
void* secure_memset(void *v, int c, size_t n) {
volatile char *p = v;
while (n--) *p++ = c;
return v;
}
(Code taken from 771-BSI. Thanks to Daniel Trebbien for pointing out for a possible defect of the previous code proposal.)
Why does volatile prevent optimization? See https://stackoverflow.com/a/3604588/220060
UPDATE Please also read Sensitive Data In Memory because if you have an adversary on your iOS system, your are already more or less screwed even before he tries to read that memory. In a summary SecureZeroMemory() or secure_memset() do not really help.
The problem is NSData is immutable and you do not have control over what happens. If the buffer is controlled by you, you could use dataWithBytesNoCopy:length: and NSData will act as a wrapper. When finished you could memset your buffer.