Objective C SHA512 hash of two NSData - objective-c

Here is a Java code, which computes SHA512 hash of a byte array with salt:
private static String DIGEST_ALGORITHM = "SHA-512";
public static byte[] getHash(final byte[] data, final byte[] salt) throws NoSuchAlgorithmException
{
final MessageDigest md = MessageDigest.getInstance(DIGEST_ALGORITHM);
md.reset();
if (salt != null)
{
md.update(salt);
}
return md.digest(data);
In Objective C, I use this algorithm for compute the hash of an NSData:
#implementation NSData (CommonDigest)
- (NSData *) SHA512Hash {
unsigned char hash[CC_SHA512_DIGEST_LENGTH];
(void) CC_SHA512( [self bytes], (CC_LONG)[self length], hash );
return ( [NSData dataWithBytes: hash length: CC_SHA512_DIGEST_LENGTH] );
}
This works perfectly, computes the same hash, as the Java code, if I use the same single data (i.e. the salt is nil in the Java code). The problem is that, if I want to computes hash of two NSData, i.e. there is a salt (the second parameter in the Java code is not nil). You can see that in the Java code, if the salt is not null, it performs an update, and then call the digest method. Somewhere I read that, this operation is equal with merging the two byte array (the data and salt arrays with System.arraycopy), and call the digest on the result array.
However, if I do this in Objective C (with NSMutableData appendData method), I don't get the same result. How can I fix this?
I can see in the CommonDigest class, there are similar methods, but I don't know, how can I use these...I think of these methods:
extern int CC_SHA512_Init(CC_SHA512_CTX *c);
extern int CC_SHA512_Update(CC_SHA512_CTX *c, const void *data, CC_LONG len);
extern int CC_SHA512_Final(unsigned char *md, CC_SHA512_CTX *c);
extern unsigned char *CC_SHA512(const void *data, CC_LONG len, unsigned char *md);
So I would like to create a method like this:
#implementation NSData (CommonDigest)
- (NSData *)SHA512HashWithSalt:(NSData *)salt {...}

I haven’t run this code and compared it with a Java implementation but it should work:
#implementation NSData (CommonDigest)
- (NSData *)SHA512HashWithSalt:(NSData *)salt {
unsigned char hash[CC_SHA512_DIGEST_LENGTH];
CC_SHA512_CTX context;
CC_SHA512_Init(&context);
if ([salt length]) {
CC_SHA512_Update(&context, [salt bytes], (CC_LONG)[salt length]);
}
CC_SHA512_Update(&context, [self bytes], (CC_LONG)[self length]);
CC_SHA512_Final(hash, &context);
return [NSData dataWithBytes:hash length:CC_SHA512_DIGEST_LENGTH];
}
#end

Related

AES Encryption CryptLib in iOS 13 not working

My application uses AES 256 encryption to encrypt a string. The same code that was used before is generating a different result. This problem started when iOS 13 was released. And it happens only to applications that are shipped to the store or built with Xcode 11.
Here is the code used for the encryption:
- (NSData *)encrypt:(NSData *)plainText key:(NSString *)key iv:(NSString *)iv {
char keyPointer[kCCKeySizeAES256+2],// room for terminator (unused) ref: https://devforums.apple.com/message/876053#876053
ivPointer[kCCBlockSizeAES128+2];
BOOL patchNeeded;
bzero(keyPointer, sizeof(keyPointer)); // fill with zeroes for padding
patchNeeded= ([key length] > kCCKeySizeAES256+1);
if(patchNeeded)
{
NSLog(#"Key length is longer %lu", (unsigned long)[[self md5:key] length]);
key = [key substringToIndex:kCCKeySizeAES256]; // Ensure that the key isn't longer than what's needed (kCCKeySizeAES256)
}
//NSLog(#"md5 :%#", key);
[key getCString:keyPointer maxLength:sizeof(keyPointer) encoding:NSUTF8StringEncoding];
[iv getCString:ivPointer maxLength:sizeof(ivPointer) encoding:NSUTF8StringEncoding];
if (patchNeeded) {
keyPointer[0] = '\0'; // Previous iOS version than iOS7 set the first char to '\0' if the key was longer than kCCKeySizeAES256
}
NSUInteger dataLength = [plainText length];
//see https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CCryptorCreateFromData.3cc.html
// For block ciphers, the output size will always be less than or equal to the input size plus the size of one block.
size_t buffSize = dataLength + kCCBlockSizeAES128;
void *buff = malloc(buffSize);
size_t numBytesEncrypted = 0;
//refer to http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonCryptor.h
//for details on this function
//Stateless, one-shot encrypt or decrypt operation.
CCCryptorStatus status = CCCrypt(kCCEncrypt, /* kCCEncrypt, etc. */
kCCAlgorithmAES128, /* kCCAlgorithmAES128, etc. */
kCCOptionPKCS7Padding, /* kCCOptionPKCS7Padding, etc. */
keyPointer, kCCKeySizeAES256, /* key and its length */
ivPointer, /* initialization vector - use random IV everytime */
[plainText bytes], [plainText length], /* input */
buff, buffSize,/* data RETURNED here */
&numBytesEncrypted);
if (status == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];
}
free(buff);
return nil;
}
- (NSString *) encryptPlainTextWith:(NSString *)plainText key:(NSString *)key iv:(NSString *)iv {
return [[[[CryptLib alloc] init] encrypt:[plainText dataUsingEncoding:NSUTF8StringEncoding] key:[[CryptLib alloc] sha256:key length:32] iv:iv] base64EncodedStringWithOptions:0];
}
/**
* This function computes the SHA256 hash of input string
* #param key input text whose SHA256 hash has to be computed
* #param length length of the text to be returned
* #return returns SHA256 hash of input text
*/
- (NSString*) sha256:(NSString *)key length:(NSInteger) length{
const char *s=[key cStringUsingEncoding:NSASCIIStringEncoding];
NSData *keyData=[NSData dataWithBytes:s length:strlen(s)];
uint8_t digest[CC_SHA256_DIGEST_LENGTH]={0};
CC_SHA256(keyData.bytes, (CC_LONG)keyData.length, digest);
NSData *out=[NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
NSString *hash=[out description];
hash = [hash stringByReplacingOccurrencesOfString:#" " withString:#""];
hash = [hash stringByReplacingOccurrencesOfString:#"<" withString:#""];
hash = [hash stringByReplacingOccurrencesOfString:#">" withString:#""];
if(length > [hash length])
{
return hash;
}
else
{
return [hash substringToIndex:length];
}
}
##
I would like to know if something in the code path has changed in the way it works. The method called to do the encryptions is "encryptPlainTextWith". Thanks in advance.
Inside:
- (NSString*) sha256:(NSString *)key length:(NSInteger) length
I replaced
NSString *hash=[out description];
To
NSString *hash=[out debugDescription];
And everything got back to normal. Cheers Happy coding.
Alternative Solution as per #Rob Napier
create separate function for converting NSData to Hex
#pragma mark - String Conversion
-(NSString*)hex:(NSData*)data{
NSMutableData *result = [NSMutableData dataWithLength:2*data.length];
unsigned const char* src = data.bytes;
unsigned char* dst = result.mutableBytes;
unsigned char t0, t1;
for (int i = 0; i < data.length; i ++ ) {
t0 = src[i] >> 4;
t1 = src[i] & 0x0F;
dst[i*2] = 48 + t0 + (t0 / 10) * 39;
dst[i*2+1] = 48 + t1 + (t1 / 10) * 39;
}
return [[NSString alloc] initWithData:result encoding:NSASCIIStringEncoding];
}
After that Inside:
- (NSString*) sha256:(NSString *)key length:(NSInteger) length
I replaced
NSString *hash=[out description];
To
NSString *hash = [self hex:out];
I suspect that your key is longer than 32 UTF-8 bytes. In that case, this code is incorrect. Your patchNeeded conditional is basically creating a garbage key. The contents of buffer aren't promised if this function return returns false, but you're relying on them.
There is no secure way to truncate a key you were given, so I'm not really certain what behavior you want here. It depends on what kinds of strings you're passing.
This code is also incorrect if iv is shorter than 16 UTF-8 bytes. You'll wind up including random values from the stack. That part can be fixed with:
bzero(ivPointer, sizeof(ivPointer));
But if your previous version relied on random values, this will still be different.
Assuming you need to match the old behavior, the best way to debug this is to run your previous version in a debugger and see what keyPointer and ivPointer wind up being.
(Note that this approach to creating a key is very insecure. It's drastically shrinking the AES keyspace. How much depends on what kind of strings you're passing, but it's dramatic. You also should never reuse the same key+iv combination in two messages when using CBC, which this looks like it probably does. If possible, I recommend moving to a correct AES implementation. You can look at RNCryptor for one example of how to do that, or use RNCryptor directly if you prefer.)

Returning variable size byte array and free the memory

I am writing a proxy .c file for using OpenSSL library for RSA encryption. Usable for iPhone. Since encrypted data sized is not known to the calling class in Objective C, the container is not initialized and I want to keep it dynamic. The char array and the received size is called by reference. Its memory is dynamically allocated in ssl_encrypt_public_rsa and the caller has to free it. I don't like the idea to give the responsibility to the caller.
Is there any other robust method that you can suggest?
openssl implementation in .c (later compiled to static lib .a file)
// calling function must free cipherText memory
void ssl_encrypt_public_rsa(RSA *rsaKey, int plainLength, unsigned char *plainText,
int *cipherLength, unsigned char **cipherText)
{
int enc_len = RSA_size(rsaKey);
unsigned char *enc_bytes = malloc(enc_len * sizeof(char));
int encSize = RSA_public_encrypt(plainLength, plainText, enc_bytes, rsaKey, RSA_PKCS1_PADDING);
*cipherText = enc_bytes;
*cipherLength = encSize;
}
void ssl_encrypt_mips(int plainLength, unsigned char *plainText,
int *cipherLength, unsigned char **cipherText)
{
// rsaKeyMips is defined and initialized earlier
ssl_encrypt_public_rsa(rsaKeyMips, plainLength, plainText, cipherLength, cipherText);
}
Calling function Objective C .m file
-(NSData *) encryptMips:(NSData *)inData
{
int encSize = 0;
unsigned char *cipherBytes;
ssl_encrypt_mips((int)inData.length, (unsigned char *)inData.bytes, &encSize, &cipherBytes);
if (encSize == -1 ) return nil;
NSData * d = [NSData dataWithBytes:cipherBytes length:encSize];
free(cipherBytes);
return d;
}
It is unclear what you are asking, as you've already wrapped the C call in a wrapper which takes care of the free(). If you wish to remove the free() from your wrapper, and serendipitously avoid a copy, you can change your:
NSData *d = [NSData dataWithBytes:cipherBytes length:encSize];
free(cipherBytes);
to:
NSData *d = [NSData dataWithBytesNoCopy:cipherBytes length:encSize free:YES];
which hands ownership of the malloc() block to the NSData instance, which will free() it later when no longer needed.
HTH

How to Convert NSMutableArray objects to const char in xcode

To develop a calculator in xcode I am using a c class for converting infix to postfix expression and its evaluation. But I have an NSString in my View controller class and I need to pass this NSString to a C class where the conversion and evaluation happens. How can I do this?
I think you need to convert NSString to cString. It can be done by
[str UTF8String]
Assuming you have an NSMutableArray containing NSString objects, and you want to convert this to a C array containing C strings, you need to allocate memory for a C array of char * of suitable size (e.g., count of the NSMutableArray + maybe 1 extra if you want a NULL terminator for the array, otherwise you need to pass along the array's element count everywhere). Then for each element of the NSMutableArray, populate the corresponding index in the C array with the C string returned by UTF8String of the NSString object.
Note that the C strings returned by UTF8String are “autoreleased”, so if you need to keep them around for longer than the current autorelease context, you need to duplicate them (e.g., strdup) and then free them (and the C array, which you need to free in any case) after you're done with them.
For example:
#include <Foundation/Foundation.h>
#include <stdio.h>
#include <string.h>
void printCStringsInArray(const char **arr) {
int i = 0;
while (*arr) { printf("%d: %s\n", i++, *arr++); }
}
int main () {
const char **carr;
#autoreleasepool {
NSMutableArray *arr;
const char **p;
arr = [NSMutableArray arrayWithObjects:#"11", #"+", #"12", nil];
p = carr = malloc(sizeof(*carr) * ([arr count] + 1));
for (NSString *s in arr) {
*p++ = strdup([s UTF8String]);
}
*p = NULL; // mark the end of carr
}
printCStringsInArray(carr);
{ // free the C strings and array
const char **p = carr;
while (*p) { free(*p++); };
free(carr);
}
return 0;
}
This prints:
0: 11
1: +
2: 12
edit: Of course if you just want to call some C function taking individual strings as const char * from your otherwise Objective-C code, you don't need to do anything this complicated, just convert each string on the fly with UTF8String and use the NSMutableArray for iteration, etc. But then one might wonder what it is that you can do with C strings that you couldn't do directly with NSStrings.
It's easy because you can use NSString's UTF8String method, but you have to handle memory allocation: allocate a block to contain the string.Choose if to wrap it into a NSData so that it will be released automatically when out of the pool block, or if to free it manually:
#autoreleasepool
{
NSArray* strings= #[ #"Hello", #"Hey" ]; // Your strings
// This could be also a NSMutableArray if you need it
NSMutableArray* cstrings=[NSMutableArray new]; // C strings
for(NSString* string in strings) // Iterate over all the strings
{
const char* temp=[string UTF8String]; // Get C string
const unsigned long length=strlen(temp); // Allocate memory for it
char* string=(char*)malloc((length+1)*sizeof(char));
strcpy(string,temp); // Copy it
// Store it inside NSData
[cstrings addObject: [NSData dataWithBytesNoCopy: string length: (length+1)*sizeof(char)]];
}
}

AES256 NSString Encryption in iOS

My app encrypts and decrypts (or it should) an NSString (the text to be encrypted / decrypted) with another NSString (the keyword) using aes 256-Bit Encryption. When I run my project and run the encrypt method, nothing gets encrypted the textfield just clears itself. Here is the code I have:
-(void)EncryptText {
//Declare Keyword and Text
NSString *plainText = DataBox.text;
NSString *keyword = Keyword.text;
//Convert NSString to NSData
NSData *plainData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
//Encrypt the Data
NSData *encryptedData = [plainData AESEncryptWithPassphrase:keyword];
//Convert the NSData back to NSString
NSString* cypherText = [[NSString alloc] initWithData:encryptedData encoding:NSUTF8StringEncoding];
//Place the encrypted sting inside the Data Box
NSLog(#"Cipher Text: %#", cypherText);
}
The header files can be downloaded by clicking this link: ZIP File containing AES Implementation
I have been told that I need to use Base-64 encoding of my string to get any result. If this is true, then how do I do it?
I have also been told that encryption changed in iOS 5, and my app is an iOS 5+ ONLY app. If this is true, then what do I have to do to make this encryption work on iOS 5 or where can I find another AES 256-bit implementation that will work on NSString.
Why doesn't this code produce a result?
EDIT: The links below refer to an older implementation. The latest version is called RNCryptor.
Your code doesn't use iOS's built-in AES implementation. It has its own custom implementation. AESEncryptWithPassphrase: also incorrectly generates the key, throwing away most of the entropy in the passphrase.
On iOS, you should be using the CCCrypt*() functions for AES. You should also make sure that you understand what is happening in your encryption and decryption routines. It is very easy to write encryption code that looks correct (in that you cannot read the output by inspection), but is extremely insecure.
See Properly encrypting with AES with CommonCrypto for an explanation of the problems with the above implementation, and how to properly use AES on iOS. Note that iOS 5 now has CCKeyDerivationPBKDF available.
There is no requirement to Base-64 encode your string prior to encryption. Base-64 encoding is used in cases where you need to convert binary data into a form that can be easily sent over email or other places where control characters would be a problem. It converts 8-bit binary data in 7-bit ASCII data. That's not necessary or useful here.
EDIT: It is critical that you carefully read the explanation of how to use this code. It is dangerous to simply cut and paste security code and hope it works. That said, the full source to RNCryptManager is available as part of the Chapter 11 example code for iOS 5 Programming Pushing the Limits and may be helpful [EDIT: This is old code; I recommend RNCryptor now, linked at the top of the answer]. The book (which should be available next week despite what the site says) includes a much longer discussion of how to use this code, including how to improve performance and deal with very large datasets.
NSData with category just fine for AES encryption, I didnt check zip file but this should work for you;
#import <CommonCrypto/CommonCryptor.h>
#implementation NSData (AESAdditions)
- (NSData*)AES256EncryptWithKey:(NSString*)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void* buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess)
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
- (NSData*)AES256DecryptWithKey:(NSString*)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void* buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
if (cryptStatus == kCCSuccess)
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free(buffer); //free the buffer;
return nil;
}
#end
Use it wrapper functions like ;
- (NSData*) encryptString:(NSString*)plaintext withKey:(NSString*)key {
return [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
}
- (NSString*) decryptData:(NSData*)ciphertext withKey:(NSString*)key {
return [[[NSString alloc] initWithData:[ciphertext AES256DecryptWithKey:key]
encoding:NSUTF8StringEncoding] autorelease];
}

Packing ints to bytes (NSData)

I want to pack a MIDI message into an NSData object.
int messageType = 3; // 0-15
int channel = 5; // 0-15
int data1 = 56; // 0-127
int data2 = 78; // 0-127
int packed = data2;
packed += data1 * 127;
packed += channel * 16129; // 127^2
packed += messageType * 258064; // 127^2 * 16
NSLog(#"packed %d", packed);
NSData *packedData = [NSData dataWithBytes:&packed length:sizeof(packed)];
int recovered;
[packedData getBytes:&recovered];
NSLog(#"recovered %d", recovered);
This works wonderfully and while I'm proud of myself, I know that the conversion to bytes is not done correctly: it should be a direct conversion without a lot of addition and multiplication. How can that be done?
Edit: I'm now aware that I can just do this
char theBytes[] = {messageType, channel, data1, data2};
NSData *packedData = [NSData dataWithBytes:&theBytes length:sizeof(theBytes)];
and on the Java side
byte[] byteBuffer = new byte[4]; // Receive buffer
while (in.read(byteBuffer) != -1) {
System.out.println("data2=" + byteBuffer[3]);
}
and it will work, but I'd like the solution to get an NSData with just 3 bytes.
Personally, I would go for an NSString:
NSString *dataString = [NSString stringWithFormat:#"%i+%i+%i+%i", messageType, channel, data1, data2];
NSData *packedData = [dataString dataUsingEncoding:NSUTF8StringEncoding];
Easy to use, and easy to transfer. Unpacking is a tiny bit more complicated, but not difficult at all either.
NSScanner *scanner = [NSScanner scannerWithString:[[[NSString alloc] initWithData:packedData encoding:NSUTF8StringEncoding] autorelease]];
int messageType, channel, data1, data2;
[scanner scanInt:&messageType];
[scanner scanInt:&channel];
[scanner scanInt:&data1];
[scanner scanInt:&data2];
Here's a 3-byte solution that I put together.
char theBytes[] = {message_type * 16 + channel, data1, data2};
NSData *packedData = [NSData dataWithBytes:&theBytes length:sizeof(theBytes)];
char theBytesRecovered[3];
[packedData getBytes:theBytesRecovered];
int messageTypeAgain = (int)theBytesRecovered[0]/16;
int channelAgain = (int)theBytesRecovered[0] % 16;
int data1Again = (int)theBytesRecovered[1];
int data2Again = (int)theBytesRecovered[2];
NSLog(#"packed %d %d %d %d", messageTypeAgain, channelAgain, data1Again, data2Again);
and on the other side of the wire, this is just as easy to pick up, because each byte is a byte. I just finished trying this on the iOS side and the Java side, and there are no problems on either. There is no problem with endian-ness, because each integer fits into one single byte (or two in one byte, in one case).
you have several options.
since it looks like you want a contiguous glob of data in the NSData representation...
you'll want to create a packed struct, and pass the data to the NSData call as a predefined endianness (so both ends know how to unarchive the data glob).
/* pack this struct's ivars and and enable -Wreorder to sanity check that the compiler does not reorder members -- i see no reason for the compiler to do this since the fields are equal size/type */
struct t_midi_message {
UInt8 message_type; /* 0-15 */
UInt8 channel; /* 0-15 */
UInt8 data1; /* 0-127 */
UInt8 data2; /* 0-127 */
};
union t_midi_message_archive {
/* members - as a union for easy endian swapping */
SInt32 glob;
t_midi_message message;
enum { ValidateSize = 1 / (4 == sizeof(t_midi_message)) };
/* nothing unusual here, although you may want a ctor which takes NSData as an argument */
t_midi_message_archive();
t_midi_message_archive(const t_midi_message&);
t_midi_message_archive(const t_midi_message_archive&);
t_midi_message_archive& operator=(const t_midi_message_archive&);
/* swap routines -- just pass #member glob to the system's endian routines */
void swapToNativeEndianFromTransferEndian();
void swapToTransferEndianFromNativeEndian();
};
void a(const t_midi_message_archive& msg) {
t_midi_message_archive copy(msg);
copy.swapToTransferEndianFromNativeEndian();
NSData * packedData([NSData dataWithBytes:&copy.glob length:sizeof(copy.glob)]);
assert(packedData);
t_midi_message_archive recovered;
[packedData getBytes:&recovered.glob];
recovered.swapToNativeEndianFromTransferEndian();
/* recovered may now be used safely */
}