P_SHA-1 in Objective-C for WS-Trust - objective-c

I'm trying to compute a key for a SOAP signature using P_SHA-1 defined by WS-Trust. The WS-Trust specification says that
key = P_SHA1 (EntropyFromRequest, EntropyFromResponse)
and the TLS spec says P_SHA-1 is
P_SHA-1(secret, seed) =
HMAC_SHA-1(secret, A(1) + seed) +
HMAC_SHA-1(secret, A(2) + seed) +
HMAC_SHA-1(secret, A(3) + seed) + ...
Where + indicates concatenation.
A() is defined as:
A(0) = seed
A(i) = HMAC_SHA-1(secret, A(i-1))
My algorithm looks like so:
- (NSData*) psha1WithSize:(int)bytes;
{
int numberOfIterations = bytes/16;
NSData *label;
NSData *secret;
NSMutableData *seed;
NSData *reqEntropy = [NSData dataWithBase64EncodedString:requestEntropy];
NSData *resEntropy = [NSData dataWithBase64EncodedString:responseEntropy];
secret = reqEntropy;
seed = resEntropy;
NSData *aIMinusOne = seed;
NSData *aI = nil;
NSMutableData *currentData = [[NSMutableData alloc] init];
for( int i=1; i <= numberOfIterations; i++ )
{
aI = [self hmacSha1Data:aIMinusOne withKey:secret];
NSMutableData *aIPlusSeed = [NSMutableData dataWithData:aI];
[aIPlusSeed appendData:seed];
[currentData appendData:[self hmacSha1Data:aIPlusSeed withKey:secret]];
aIMinusOne = aI;
aI = nil;
}
return currentData;
}
My HMAC looks like these: iPhone and HMAC-SHA-1 encoding
This doesn't seem to be working, and I can't figure out what is wrong with my algorithm. One thought I had was that the service is implemented in .NET WCF, and to get it to accept the hashed password I had to use an NSUTF16LittleEndianStringEncoding, so maybe the response entropy is in the same encoding (prior to base64 encoding)?
Any help is greatly appreciated, thanks.

Related

Flutter how to convert NSData* to Byte* in objc

I am trying to use c++ api with objc native code in flutter.
https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-obj-c-tab
flutter documentation says Uint8List should be stored as FlutterStandardTypedData typedDataWithBytes: in objc do
send argument in flutter
var data = <String, Uint8List>{
"key": byte, //data type is Uint8List
"value": byteBuffer, //data type is Uint8List
};
Uint8List? byteRes;
byteRes = await platform.invokeMethod('SeedDecrypt', data);
get argument in objc (AppDelegate.m)
NSData* key = call.arguments[#"key"];
NSData* value = call.arguments[#"value"];
NSUInteger keyLength = [key length];
NSUInteger valueLength = [value length];
Byte* byteKey = (Byte*)malloc(keyLength);
Byte* byteValue = (Byte*)malloc(valueLength);
memcpy(byteKey, [key bytes], keyLength);
memcpy(byteValue, [value bytes], byteLength);
DWORD roundKey[32];
//Call C++ API
//prototype : void SeedKey(DWORD* roundKey, BYTE* byteKey);
SeedKey(roundKey, byteKey);
//protoType : void Decrypt(BYTE* byteValue, DWORD* roundKey);
Decrypt(byteValue, roundKey);
NSData* res = [NSData dataWithBytes: byteValue length: sizeof(byteValue)];
result(res);
Store the argument as NSData* and copy the memory to a Byte* variable. After executing the C API, it is converted to NSData type. The problem is that when I run it, the device shuts down. I wrote this source referring to the article below. Do you know what my mistake is?
How to convert NSData to byte array in iPhone?
thanks.
Solved
NSNumber* keyLength = call.arguments[#"keyLen"];
NSNumber* valueLength = call.arguments[#"valueLen"];
NSUInteger keyLen = [keyLength integerValue];
NSUInteger valueLen = [valueLength integerValue];
FlutterStandardTypedData* key = call.arguments[#"key"];
FlutterStandardTypedData* value = call.arguments[#"value"];
Byte* byteKey = (Byte*)malloc(keyLen);
Byte* byteValye = (Byte*)malloc(valueLen);
memcpy(byteKey, [key.data bytes], keyLen);
memcpy(byteValue, [value.data bytes], valueLen);
DWORD roundKey[32];
//Call C++ API
NSData* res = [NSData dataWithBytes:keyValue length:keyLen];
FlutterStandardTypedData* rest = [FlutterStandardTypedData typedDataWithBytes: res];
free(byteKey);
free(byteValue);
result(rest);
See https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-obj-c-tab. After matching the data type, match the OBJC data type with the C data type and return the result.

Create code challenge (base64 encoded, sha 256 ascii) from string

For some code challenge used in the oauth2 login process I need to do the following:
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
How can I do this from my random string contained in code_verifier?
UPDATE: Can you check if this is correct? Or is some stuff unneccesary/deprecated? I actually have not really an idea what I am doing here, I just copied code from everywhere to solve it...
- (NSString *)createCodeChallengeWithVerifier:(NSString *)codeVerifier {
//Create ASCII
const char *asciiString = [codeVerifier cStringUsingEncoding:NSASCIIStringEncoding];
//Sha256
unsigned char buf[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(asciiString, strlen(asciiString), buf);
NSMutableString * shaString = [NSMutableString stringWithCapacity:(CC_SHA256_DIGEST_LENGTH * 2)];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i) {
[shaString appendFormat:#"%02x", buf[i]];
}
//Base 64 encode
NSData *dataFromShaString = [shaString dataUsingEncoding:NSUTF8StringEncoding];
return([dataFromShaString base64EncodedStringWithOptions:0]);
}

implementing PBEKeySpec encryption into IOS

This is my java code. Now I want to implement same functionality in Objective-C.
int dkLen = 16;
int rounds = 1000;
PBEKeySpec keySpec = new PBEKeySpec(hashKey.toCharArray(),salt.getBytes(), rounds, dkLen * 8);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return factory.generateSecret(keySpec).getEncoded();
This is my iOS implementation
- (void)getHashKey {
NSString *hash_key=#"MY_HASHKEY";
NSString *saltKey = #"MY_SALTKEY";
int dkLen = 16;
NSData *keyData = [hash_key dataUsingEncoding:NSUTF8StringEncoding];
NSData *salt = [saltKey dataUsingEncoding:NSUTF8StringEncoding];
uint rounds = 1000;
uint keySize = kCCKeySizeAES128;
NSMutableData *derivedKey = [NSMutableData dataWithLength:keySize];
CCKeyDerivationPBKDF(kCCPBKDF2, // algorithm
keyData.bytes, // password
keyData.length, // passwordLength
salt.bytes, // salt
salt.length, // saltLen
kCCPRFHmacAlgSHA1, // PRF
rounds, // rounds
derivedKey.mutableBytes, // derivedKey
dkLen*8); // derivedKeyLen
NSString *myString = [[NSString alloc] initWithData:derivedKey encoding:NSASCIIStringEncoding];
NSLog(#"derivedKey: %#", myString);
}
Is there any problem with algorithm which i am using in iOS
Use the Common Crypto CCKeyDerivationPBKDF function with the option kCCPRFHmacAlgSHA1.
Note PBEKeySpec keyLength is in bits, CCKeyDerivationPBKDF derivedKeyLen is in bytes.
For a more detailed answer provide all input (hashKey, salt) and the output in hex dump format plus the number of rounds, output length in bytes.
See this SO answer for sample code.
Update for revised question code:
CCKeyDerivationPBKDF returns 8-bit data bytes that is essentially not characters and many are not printable even if forced into NSASCIIStringEncoding. Forcing to NSASCIIStringEncoding even if there is no error returned is incorrect and non-useful. Instead either use the returned NSData or convert to Base64 or HexASCII encoding.
Change
NSString *myString = [[NSString alloc] initWithData:derivedKey encoding:NSASCIIStringEncoding];
Output: A´Öº÷"ùïó
to
NSString * myString = [derivedKey base64EncodedStringWithOptions:0];
Output: QbTWgbr3FSL57/MfBQAz4A==
Note: 1000 rounds is generally considered insufficient, something in the 10,000 to 100,000 range should be used.
Timings on an iPhone 6S:
rounds seconds
1000 0.003
10000 0.032
100000 0.309
1000000 3.047

byte array in ios

I'm trying to convert this Javascript code:
self.userSerialEvent = function (join, value, tokens) {
var type = join.charCodeAt(0);
var rawJoin = parseInt(join.substr(1)) - 1;
var rawValue = parseInt(value);
self.SJValues[join] = value;
var payload = "\x00\x00" + String.fromCharCode(value.length + 2) + "\x12" + String.fromCharCode(rawJoin) + value;
self.sendMsg("\x05\x00" + String.fromCharCode(payload.length) + payload);
};
to objective c code for an ipad app.
However I cannot figure out how to properly form this
If I do a char array I cannot have variable length (which will happen when the value is added to the array). And when I try to use NSMutableArray I cant insert bytes, plus my network send operation takes an NSData and I cannot convert a NSMutableArray to data. I have also tried NSString but when I do:
NSString * payload = [NSString stringWithFormat:#"0000%d12%d%#",value.length+2,rawJoin,[value dataUsingEncoding:NSASCIIStringEncoding]];
I get the < > around the data in the string. I have tried to create a character set and remove "<>" from the string but that only removed the end one (leaving the beginning < there)
My question is this: How can I form an array of bytes, that is of variable length and able to convert that array to NSData
Sounds like you are looking for NSMutableData.
NSMutableData *payload = [[NSMutableData alloc] init];
[payload appendBytes:"\000\000" length:2];
uint8_t length = value.length + 2;
[payload appendBytes:&length length:1];
[payload appendBytes:"\022" length:1];
// etc.

Confusion regarding Rijndael/SHA256 encryption

I need to work out a decryption/encryption algorithm, but I'm confused regarding SHA256 / CBC / Salt / IV etc.
An example of a correctly encrypted string is:
U2FsdGVkX19IfIZtJ/48wk8z3ZRGDK8RD8agyQRhMrsOMsoIlVEcrzraOLo5IRBXjDkN1JUFnNrkvi2NA22IOTv00U97065tUNBQKEVXcaL0UJirtcqHlq8lN4pEm14ZokKXv8mUP8GkUKrOf37GhOugi/F/CQiILb57kIPrYPk=
It is Base64 encoded then Rijndael encoded. The first 8 characters are 'Salted__' and the next 8 characters I assume is some sort of salt (randomly generated).
The key I provided to encrypt this data is '12345678'.
The decrypted data should be:
2358442189442905:ZGF2aWQ=:1324515293:1.9.12:1:MC4wLjAuMCxub25lLzA=:LfcTMMYyUcwgL8keu3sMoNC/PFEKZy8fWFvo3rJvSdo
Apparently it is following Crypt::CBC::VERSION 2.29
I can't seem to decrypt the correctly encrypted string above. I have tried the following:
NSString *key = #"12345678";
NSData *test = [NSData dataFromBase64String:#"U2FsdGVkX19IfIZtJ/48wk8z3ZRGDK8RD8agyQRhMrsOMsoIlVEcrzraOLo5IRBXjDkN1JUFnNrkvi2NA22IOTv00U97065tUNBQKEVXcaL0UJirtcqHlq8lN4pEm14ZokKXv8mUP8GkUKrOf37GhOugi/F/CQiILb57kIPrYPk="];
unsigned char salt[8]; //get the salt out
[test getBytes:salt range:NSMakeRange(8, 8)];
NSData *saltData = [NSData dataWithBytes:salt length:8];
unsigned char data[128-16]; // remove the Salted__ and the 8 character salt
[test getBytes:data range:NSMakeRange(8, 128-8)];
test = [NSData dataWithBytes:data length:128-8];
NSMutableData *aeskey = [NSMutableData dataWithData:[key dataUsingEncoding:NSUTF8StringEncoding]];
[aeskey appendData:saltData]; // add the salt to the end of the key?
NSData *test2 = [test decryptedAES256DataUsingKey:key error:nil]; //Using a NSData+CommonCrypto library
Any ideas on how to decrypt this properly?
EDIT: more information: this is code related to what I am trying to implement.
elsif ($header_mode eq 'salt') {
$self->{salt} = $self->_get_random_bytes(8) if $self->{make_random_salt};
defined (my $salt = $self->{salt}) or croak "No header_mode of 'salt' specified, but no salt value provided"; # shouldn't happen
length($salt) == 8 or croak "Salt must be exactly 8 bytes long";
my ($key,$iv) = $self->_salted_key_and_iv($self->{passphrase},$salt);
$self->{key} = $key;
$self->{civ} = $self->{iv} = $iv;
$result = "Salted__${salt}";
}
my $self = shift;
my ($pass,$salt) = #_;
croak "Salt must be 8 bytes long" unless length $salt == 8;
my $key_len = $self->{keysize};
my $iv_len = $self->{blocksize};
my $desired_len = $key_len+$iv_len;
my $data = '';
my $d = '';
while (length $data < $desired_len) {
$d = md5($d . $pass . $salt);
$data .= $d;
}
return (substr($data,0,$key_len),substr($data,$key_len,$iv_len));
Here is an implementation that I don't fully understand: http://pastebin.com/R0b1Z7GH http://pastebin.com/aYWFXesP
unsigned char salt[8]; //get the salt out
[test getBytes:salt range:NSMakeRange(8, 8)];
NSData *saltData = [NSData dataWithBytes:salt length:8];
unsigned char data[128-16]; // remove the Salted__ and the 8 character salt
[test getBytes:data range:NSMakeRange(8, 128-8)];
test = [NSData dataWithBytes:data length:128-8];
I think in your second block of code you are copying the wrong data. Try this:
unsigned char data[128-16]; // remove the Salted__ and the 8 character salt
[test getBytes:data range:NSMakeRange(16, 128-16)];
test = [NSData dataWithBytes:data length:128-16];
Your comment indicates that you want to skip both the Salted__ and the salt itself.
Note that I haven't got a clue where the salt should go -- that's up to the protocol that you're trying to integrate with -- so I hope you've got that well-documented from another source.