Index: ObjXMPP.xcodeproj/project.pbxproj ================================================================== --- ObjXMPP.xcodeproj/project.pbxproj +++ ObjXMPP.xcodeproj/project.pbxproj @@ -101,14 +101,10 @@ }; 4BC5599A1337A65400E345C7 /* ObjXMPP */ = { isa = PBXGroup; children = ( 4BC5599B1337A65400E345C7 /* Supporting Files */, - 4BC559FD1337AC1800E345C7 /* XMPPSCRAMAuth.m */, - 4BC559FE1337AC1800E345C7 /* XMPPStanza.h */, - 4BC559FF1337AC1800E345C7 /* XMPPStanza.m */, - 4BC559D91337AC0900E345C7 /* arc4random_uniform.m */, 4BC559DA1337AC0900E345C7 /* XMPPAuthenticator.h */, 4BC559DB1337AC0900E345C7 /* XMPPAuthenticator.m */, 4BC559DC1337AC0900E345C7 /* XMPPConnection.h */, 4BC559DD1337AC0900E345C7 /* XMPPConnection.m */, 4BC559DE1337AC0900E345C7 /* XMPPExceptions.h */, @@ -122,10 +118,14 @@ 4BC559E61337AC0900E345C7 /* XMPPPLAINAuth.h */, 4BC559E71337AC0900E345C7 /* XMPPPLAINAuth.m */, 4BC559E81337AC0900E345C7 /* XMPPPresence.h */, 4BC559E91337AC0900E345C7 /* XMPPPresence.m */, 4BC559EA1337AC0900E345C7 /* XMPPSCRAMAuth.h */, + 4BC559FD1337AC1800E345C7 /* XMPPSCRAMAuth.m */, + 4BC559FE1337AC1800E345C7 /* XMPPStanza.h */, + 4BC559FF1337AC1800E345C7 /* XMPPStanza.m */, + 4BC559D91337AC0900E345C7 /* arc4random_uniform.m */, ); path = ObjXMPP; sourceTree = ""; }; 4BC5599B1337A65400E345C7 /* Supporting Files */ = { Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -42,10 +42,23 @@ #define NS_CLIENT @"jabber:client" #define NS_SASL @"urn:ietf:params:xml:ns:xmpp-sasl" #define NS_STARTTLS @"urn:ietf:params:xml:ns:xmpp-tls" #define NS_SESSION @"urn:ietf:params:xml:ns:xmpp-session" #define NS_STREAM @"http://etherx.jabber.org/streams" + +@interface XMPPConnection () +- (void)XMPP_startStream; +- (void)XMPP_sendAuth: (OFString*)name; +- (void)XMPP_sendResourceBind; +- (void)XMPP_sendSession; +- (void)XMPP_handleResourceBind: (XMPPIQ*)iq; +- (void)XMPP_handleSession; +- (void)XMPP_handleFeatures: (OFXMLElement*)elem; +- (void)XMPP_handleIQ: (XMPPIQ*)iq; +- (void)XMPP_handleMessage: (XMPPMessage*)msg; +- (void)XMPP_handlePresence: (XMPPPresence*)pres; +@end @implementation XMPPConnection @synthesize username, password, server, resource, JID, port, useTLS, delegate; - init @@ -163,25 +176,17 @@ } [old release]; } -- (void)_startStream -{ - [sock writeFormat: @"\n" - @"", server]; -} - - (void)connect { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; [sock connectToHost: server onPort: port]; - [self _startStream]; + [self XMPP_startStream]; [pool release]; } - (void)handleConnection @@ -228,166 +233,10 @@ } parser.delegate = elementBuilder; } -- (void)_sendAuth: (OFString*)name -{ - OFXMLElement *authTag; - - authTag = [OFXMLElement elementWithName: @"auth" - namespace: NS_SASL]; - [authTag addAttributeWithName: @"mechanism" - stringValue: name]; - [authTag addChild: [OFXMLElement elementWithCharacters: - [[authModule clientFirstMessage] stringByBase64Encoding]]]; - - [self sendStanza: authTag]; -} - -- (void)_sendResourceBind -{ - XMPPIQ *iq = [XMPPIQ IQWithType: @"set" - ID: @"bind0"]; - OFXMLElement *bind = [OFXMLElement elementWithName: @"bind" - namespace: NS_BIND]; - if (resource) - [bind addChild: [OFXMLElement elementWithName: @"resource" - stringValue: resource]]; - [iq addChild: bind]; - - [self sendStanza: iq]; -} - -- (void)_sendSession -{ - XMPPIQ *iq = [XMPPIQ IQWithType: @"set" - ID: @"session0"]; - [iq addChild: [OFXMLElement elementWithName: @"session" - namespace: NS_SESSION]]; - [self sendStanza: iq]; -} - -- (void)_handleResourceBind: (XMPPIQ*)iq -{ - OFXMLElement *bindElem = iq.children.firstObject; - OFXMLElement *jidElem; - - if (![bindElem.name isEqual: @"bind"] || - ![bindElem.namespace isEqual: NS_BIND]) - assert(0); - - jidElem = bindElem.children.firstObject; - JID = [[XMPPJID alloc] initWithString: - [jidElem.children.firstObject stringValue]]; - - if (needsSession) { - [self _sendSession]; - return; - } - - if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) - [delegate connection: self - wasBoundToJID: JID]; -} - -- (void)_handleSession -{ - if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) - [delegate connection: self - wasBoundToJID: JID]; -} - -- (void)_handleFeatures: (OFXMLElement*)elem -{ - OFXMLElement *starttls = [elem - elementsForName: @"starttls" - namespace: NS_STARTTLS].firstObject; - OFXMLElement *bind = [elem elementsForName: @"bind" - namespace: NS_BIND].firstObject; - OFXMLElement *session = [elem elementsForName: @"session" - namespace: NS_SESSION].firstObject; - OFArray *mechs = [elem elementsForName: @"mechanisms" - namespace: NS_SASL]; - OFMutableArray *mechanisms = [OFMutableArray array]; - - if (starttls != nil) { - [self sendStanza: [OFXMLElement elementWithName: @"starttls" - namespace: NS_STARTTLS]]; - return; - } - - if ([mechs count] > 0) { - for (OFXMLElement *mech in [mechs.firstObject children]) - [mechanisms addObject: - [mech.children.firstObject stringValue]]; - - if ([mechanisms containsObject: @"SCRAM-SHA-1"]) { - authModule = [[XMPPSCRAMAuth alloc] - initWithAuthcid: username - password: password - hash: [OFSHA1Hash class]]; - [self _sendAuth: @"SCRAM-SHA-1"]; - return; - } - - if ([mechanisms containsObject: @"PLAIN"]) { - authModule = [[XMPPPLAINAuth alloc] - initWithAuthcid: username - password: password]; - [self _sendAuth: @"PLAIN"]; - return; - } - - assert(0); - } - - if (session != nil) - needsSession = YES; - - if (bind != nil) { - [self _sendResourceBind]; - return; - } - - assert(0); -} - -- (void)_handleIQ: (XMPPIQ*)iq -{ - // FIXME: More checking! - if ([iq.ID isEqual: @"bind0"] && [iq.type isEqual: @"result"]) { - [self _handleResourceBind: iq]; - return; - } - - if ([iq.ID isEqual: @"session0"] && [iq.type isEqual: @"result"]) { - [self _handleSession]; - return; - } - - if ([delegate respondsToSelector: @selector(connection:didReceiveIQ:)]) - [delegate connection: self - didReceiveIQ: iq]; -} - -- (void)_handleMessage: (XMPPMessage*)msg -{ - if ([delegate respondsToSelector: - @selector(connection:didReceiveMessage:)]) - [delegate connection: self - didReceiveMessage: msg]; -} - -- (void)_handlePresence: (XMPPPresence*)pres -{ - if ([delegate respondsToSelector: - @selector(connection:didReceivePresence:)]) - [delegate connection: self - didReceivePresence: pres]; -} - - (void)elementBuilder: (OFXMLElementBuilder*)b didBuildElement: (OFXMLElement*)elem { elem.defaultNamespace = NS_CLIENT; [elem setPrefix: @"stream" @@ -395,32 +244,32 @@ of_log(@"In: %@", elem); if ([elem.namespace isEqual: NS_CLIENT]) { if ([elem.name isEqual: @"iq"]) { - [self _handleIQ: [XMPPIQ stanzaWithElement: elem]]; + [self XMPP_handleIQ: [XMPPIQ stanzaWithElement: elem]]; return; } if ([elem.name isEqual: @"message"]) { - [self _handleMessage: + [self XMPP_handleMessage: [XMPPMessage stanzaWithElement: elem]]; return; } if ([elem.name isEqual: @"presence"]) { - [self _handlePresence: + [self XMPP_handlePresence: [XMPPPresence stanzaWithElement: elem]]; return; } assert(0); } if ([elem.namespace isEqual: NS_STREAM]) { if ([elem.name isEqual: @"features"]) { - [self _handleFeatures: elem]; + [self XMPP_handleFeatures: elem]; return; } assert(0); } @@ -430,11 +279,11 @@ /* FIXME: Catch errors here */ sock = [[GTLSSocket alloc] initWithSocket: sock]; /* Stream restart */ parser.delegate = self; - [self _startStream]; + [self XMPP_startStream]; return; } if ([elem.name isEqual: @"failure"]) /* TODO: Find/create an exception to throw here */ @@ -472,11 +321,11 @@ @selector(connectionWasAuthenticated:)]) [delegate connectionWasAuthenticated: self]; /* Stream restart */ parser.delegate = self; - [self _startStream]; + [self XMPP_startStream]; return; } if ([elem.name isEqual: @"failure"]) { of_log(@"Auth failed!"); @@ -498,6 +347,170 @@ withPrefix: (OFString*)prefix namespace: (OFString*)ns { // TODO } + +- (void)XMPP_startStream +{ + [sock writeFormat: @"\n" + @"", server]; +} + +- (void)XMPP_handleIQ: (XMPPIQ*)iq +{ + // FIXME: More checking! + if ([iq.ID isEqual: @"bind0"] && [iq.type isEqual: @"result"]) { + [self XMPP_handleResourceBind: iq]; + return; + } + + if ([iq.ID isEqual: @"session0"] && [iq.type isEqual: @"result"]) { + [self XMPP_handleSession]; + return; + } + + if ([delegate respondsToSelector: @selector(connection:didReceiveIQ:)]) + [delegate connection: self + didReceiveIQ: iq]; +} + +- (void)XMPP_handleMessage: (XMPPMessage*)msg +{ + if ([delegate respondsToSelector: + @selector(connection:didReceiveMessage:)]) + [delegate connection: self + didReceiveMessage: msg]; +} + +- (void)XMPP_handlePresence: (XMPPPresence*)pres +{ + if ([delegate respondsToSelector: + @selector(connection:didReceivePresence:)]) + [delegate connection: self + didReceivePresence: pres]; +} + +- (void)XMPP_handleFeatures: (OFXMLElement*)elem +{ + OFXMLElement *starttls = + [elem elementsForName: @"starttls" + namespace: NS_STARTTLS].firstObject; + OFXMLElement *bind = [elem elementsForName: @"bind" + namespace: NS_BIND].firstObject; + OFXMLElement *session = [elem elementsForName: @"session" + namespace: NS_SESSION].firstObject; + OFArray *mechs = [elem elementsForName: @"mechanisms" + namespace: NS_SASL]; + OFMutableArray *mechanisms = [OFMutableArray array]; + + if (starttls != nil) { + [self sendStanza: [OFXMLElement elementWithName: @"starttls" + namespace: NS_STARTTLS]]; + return; + } + + if ([mechs count] > 0) { + for (OFXMLElement *mech in [mechs.firstObject children]) + [mechanisms addObject: + [mech.children.firstObject stringValue]]; + + if ([mechanisms containsObject: @"SCRAM-SHA-1"]) { + authModule = [[XMPPSCRAMAuth alloc] + initWithAuthcid: username + password: password + hash: [OFSHA1Hash class]]; + [self XMPP_sendAuth: @"SCRAM-SHA-1"]; + return; + } + + if ([mechanisms containsObject: @"PLAIN"]) { + authModule = [[XMPPPLAINAuth alloc] + initWithAuthcid: username + password: password]; + [self XMPP_sendAuth: @"PLAIN"]; + return; + } + + assert(0); + } + + if (session != nil) + needsSession = YES; + + if (bind != nil) { + [self XMPP_sendResourceBind]; + return; + } + + assert(0); +} + +- (void)XMPP_sendAuth: (OFString*)name +{ + OFXMLElement *authTag; + + authTag = [OFXMLElement elementWithName: @"auth" + namespace: NS_SASL]; + [authTag addAttributeWithName: @"mechanism" + stringValue: name]; + [authTag addChild: [OFXMLElement elementWithCharacters: + [[authModule clientFirstMessage] stringByBase64Encoding]]]; + + [self sendStanza: authTag]; +} + +- (void)XMPP_sendResourceBind +{ + XMPPIQ *iq = [XMPPIQ IQWithType: @"set" + ID: @"bind0"]; + OFXMLElement *bind = [OFXMLElement elementWithName: @"bind" + namespace: NS_BIND]; + if (resource) + [bind addChild: [OFXMLElement elementWithName: @"resource" + stringValue: resource]]; + [iq addChild: bind]; + + [self sendStanza: iq]; +} + +- (void)XMPP_handleResourceBind: (XMPPIQ*)iq +{ + OFXMLElement *bindElem = iq.children.firstObject; + OFXMLElement *jidElem; + + if (![bindElem.name isEqual: @"bind"] || + ![bindElem.namespace isEqual: NS_BIND]) + assert(0); + + jidElem = bindElem.children.firstObject; + JID = [[XMPPJID alloc] initWithString: + [jidElem.children.firstObject stringValue]]; + + if (needsSession) { + [self XMPP_sendSession]; + return; + } + + if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) + [delegate connection: self + wasBoundToJID: JID]; +} + +- (void)XMPP_sendSession +{ + XMPPIQ *iq = [XMPPIQ IQWithType: @"set" + ID: @"session0"]; + [iq addChild: [OFXMLElement elementWithName: @"session" + namespace: NS_SESSION]]; + [self sendStanza: iq]; +} + +- (void)XMPP_handleSession +{ + if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) + [delegate connection: self + wasBoundToJID: JID]; +} @end Index: src/XMPPSCRAMAuth.m ================================================================== --- src/XMPPSCRAMAuth.m +++ src/XMPPSCRAMAuth.m @@ -30,10 +30,19 @@ #define HMAC_OPAD 0x5c #ifndef HAVE_ARC4RANDOM_UNIFORM extern uint32_t arc4random_uniform(uint32_t); #endif + +@interface XMPPSCRAMAuth () +- (OFString*)XMPP_genNonce; +- (uint8_t*)XMPP_HMACWithKey: (OFDataArray*)key + data: (OFDataArray*)data; +- (OFDataArray*)XMPP_hiWithData: (OFDataArray *)str + salt: (OFDataArray *)salt_ + iterationCount: (intmax_t)i; +@end @implementation XMPPSCRAMAuth + SCRAMAuthWithAuthcid: (OFString*)authcid password: (OFString*)password hash: (Class)hash; @@ -120,160 +129,32 @@ authcid = nil; [old release]; } -- (OFString*)_genNonce -{ - OFMutableString *nonce = [OFMutableString string]; - uint32_t res, i; - - for (i = 0; i < 64; i++) { - while ((res = arc4random_uniform('~' - '!' + 1) + '!') == ','); - [nonce appendFormat: @"%c", res]; - } - - return nonce; -} - -- (uint8_t*)_HMACWithKey: (OFDataArray*)key - data: (OFDataArray*)data -{ - OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - OFDataArray *k = [OFDataArray dataArrayWithItemSize: 1]; - size_t i, kSize, blockSize = [hashType blockSize]; - uint8_t *kCArray, *kI = NULL, *kO = NULL; - OFHash *hash; - - if (key.itemSize * key.count > blockSize) { - hash = [[[hashType alloc] init] autorelease]; - [hash updateWithBuffer: [key cArray] - ofSize: key.itemSize * key.count]; - [k addNItems: [hashType digestSize] - fromCArray: [hash digest]]; - } else - [k addNItems: key.itemSize * key.count - fromCArray: [key cArray]]; - - @try { - kI = [self allocMemoryWithSize: blockSize * sizeof(uint8_t)]; - memset(kI, HMAC_IPAD, blockSize * sizeof(uint8_t)); - - kO = [self allocMemoryWithSize: blockSize * sizeof(uint8_t)]; - memset(kO, HMAC_OPAD, blockSize * sizeof(uint8_t)); - - kCArray = [k cArray]; - kSize = k.count; - for (i = 0; i < kSize; i++) { - kI[i] ^= kCArray[i]; - kO[i] ^= kCArray[i]; - } - - k = [OFDataArray dataArrayWithItemSize: 1]; - [k addNItems: blockSize - fromCArray: kI]; - [k addNItems: data.itemSize * data.count - fromCArray: [data cArray]]; - - hash = [[[hashType alloc] init] autorelease]; - [hash updateWithBuffer: [k cArray] - ofSize: k.count]; - k = [OFDataArray dataArrayWithItemSize: 1]; - [k addNItems: blockSize - fromCArray: kO]; - [k addNItems: [hashType digestSize] - fromCArray: [hash digest]]; - } @finally { - [self freeMemory: kI]; - [self freeMemory: kO]; - } - - hash = [[[hashType alloc] init] autorelease]; - [hash updateWithBuffer: [k cArray] - ofSize: k.count]; - - [hash retain]; - [pool release]; - - return [hash digest]; -} - -- (OFDataArray*)_hiWithData: (OFDataArray *)str - salt: (OFDataArray *)salt_ - iterationCount: (intmax_t)i -{ - OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - size_t digestSize = [hashType digestSize]; - uint8_t *result = NULL, *u, *uOld; - intmax_t j, k; - OFDataArray *salty, *tmp, *ret; - - result = [self allocMemoryWithSize: digestSize]; - - @try { - memset(result, 0, digestSize); - - salty = [[salt_ copy] autorelease]; - [salty addNItems: 4 - fromCArray: "\0\0\0\1"]; - - uOld = [self _HMACWithKey: str - data: salty]; - - for (j = 0; j < digestSize; j++) - result[j] ^= uOld[j]; - - for (j = 0; j < i - 1; j++) { - tmp = [OFDataArray dataArrayWithItemSize: 1]; - [tmp addNItems: digestSize - fromCArray: uOld]; - - u = [self _HMACWithKey: str - data: tmp]; - - for (k = 0; k < digestSize; k++) - result[k] ^= u[k]; - - uOld = u; - - [pool releaseObjects]; - } - - ret = [OFDataArray dataArrayWithItemSize: 1]; - [ret addNItems: digestSize - fromCArray: result]; - } @finally { - [self freeMemory: result]; - } - - [ret retain]; - [pool release]; - - return [ret autorelease]; -} - - (OFDataArray*)clientFirstMessage { OFDataArray *ret = [OFDataArray dataArrayWithItemSize: 1]; [GS2Header release]; GS2Header = nil; if (authzid) - GS2Header = [[OFString alloc] - initWithFormat: @"n,a=%@,", authzid]; + GS2Header = [[OFString alloc] initWithFormat: @"n,a=%@,", + authzid]; else GS2Header = @"n,,"; [cNonce release]; cNonce = nil; - cNonce = [[self _genNonce] retain]; + cNonce = [[self XMPP_genNonce] retain]; [clientFirstMessageBare release]; clientFirstMessageBare = nil; - clientFirstMessageBare = [[OFString alloc] - initWithFormat: @"n=%@,r=%@", authcid, cNonce]; + clientFirstMessageBare = [[OFString alloc] initWithFormat: @"n=%@,r=%@", + authcid, + cNonce]; [ret addNItems: [GS2Header cStringLength] fromCArray: [GS2Header cString]]; [ret addNItems: [clientFirstMessageBare cStringLength] @@ -343,13 +224,13 @@ /* * IETF RFC 5802: * SaltedPassword := Hi(Normalize(password), salt, i) */ - saltedPassword = [self _hiWithData: tmpArray - salt: salt - iterationCount: iterCount]; + saltedPassword = [self XMPP_hiWithData: tmpArray + salt: salt + iterationCount: iterCount]; /* * IETF RFC 5802: * AuthMessage := client-first-message-bare + "," + * server-first-message + "," + @@ -369,12 +250,12 @@ * ClientKey := HMAC(SaltedPassword, "Client Key") */ tmpArray = [OFDataArray dataArrayWithItemSize: 1]; [tmpArray addNItems: 10 fromCArray: "Client Key"]; - clientKey = [self _HMACWithKey: saltedPassword - data: tmpArray]; + clientKey = [self XMPP_HMACWithKey: saltedPassword + data: tmpArray]; /* * IETF RFC 5802: * StoredKey := H(ClientKey) */ @@ -386,22 +267,22 @@ /* * IETF RFC 5802: * ClientSignature := HMAC(StoredKey, AuthMessage) */ - clientSignature = [self _HMACWithKey: tmpArray - data: authMessage]; + clientSignature = [self XMPP_HMACWithKey: tmpArray + data: authMessage]; /* * IETF RFC 5802: * ServerKey := HMAC(SaltedPassword, "Server Key") */ tmpArray = [OFDataArray dataArrayWithItemSize: 1]; [tmpArray addNItems: 10 fromCArray: "Server Key"]; - serverKey = [self _HMACWithKey: saltedPassword - data: tmpArray]; + serverKey = [self XMPP_HMACWithKey: saltedPassword + data: tmpArray]; /* * IETF RFC 5802: * ServerSignature := HMAC(ServerKey, AuthMessage) */ @@ -408,12 +289,12 @@ tmpArray = [OFDataArray dataArrayWithItemSize: 1]; [tmpArray addNItems: [hashType digestSize] fromCArray: serverKey]; serverSignature = [[OFDataArray alloc] initWithItemSize: 1]; [serverSignature addNItems: [hashType digestSize] - fromCArray: [self _HMACWithKey: tmpArray - data: authMessage]]; + fromCArray: [self XMPP_HMACWithKey: tmpArray + data: authMessage]]; /* * IETF RFC 5802: * ClientProof := ClientKey XOR ClientSignature */ @@ -457,6 +338,135 @@ connection: nil reason: value]; [pool release]; } + +- (OFString*)XMPP_genNonce +{ + OFMutableString *nonce = [OFMutableString string]; + uint32_t res, i; + + for (i = 0; i < 64; i++) { + while ((res = arc4random_uniform('~' - '!' + 1) + '!') == ','); + [nonce appendFormat: @"%c", res]; + } + + return nonce; +} + +- (uint8_t*)XMPP_HMACWithKey: (OFDataArray*)key + data: (OFDataArray*)data +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFDataArray *k = [OFDataArray dataArrayWithItemSize: 1]; + size_t i, kSize, blockSize = [hashType blockSize]; + uint8_t *kCArray, *kI = NULL, *kO = NULL; + OFHash *hash; + + if (key.itemSize * key.count > blockSize) { + hash = [[[hashType alloc] init] autorelease]; + [hash updateWithBuffer: [key cArray] + ofSize: key.itemSize * key.count]; + [k addNItems: [hashType digestSize] + fromCArray: [hash digest]]; + } else + [k addNItems: key.itemSize * key.count + fromCArray: [key cArray]]; + + @try { + kI = [self allocMemoryWithSize: blockSize * sizeof(uint8_t)]; + memset(kI, HMAC_IPAD, blockSize * sizeof(uint8_t)); + + kO = [self allocMemoryWithSize: blockSize * sizeof(uint8_t)]; + memset(kO, HMAC_OPAD, blockSize * sizeof(uint8_t)); + + kCArray = [k cArray]; + kSize = k.count; + for (i = 0; i < kSize; i++) { + kI[i] ^= kCArray[i]; + kO[i] ^= kCArray[i]; + } + + k = [OFDataArray dataArrayWithItemSize: 1]; + [k addNItems: blockSize + fromCArray: kI]; + [k addNItems: data.itemSize * data.count + fromCArray: [data cArray]]; + + hash = [[[hashType alloc] init] autorelease]; + [hash updateWithBuffer: [k cArray] + ofSize: k.count]; + k = [OFDataArray dataArrayWithItemSize: 1]; + [k addNItems: blockSize + fromCArray: kO]; + [k addNItems: [hashType digestSize] + fromCArray: [hash digest]]; + } @finally { + [self freeMemory: kI]; + [self freeMemory: kO]; + } + + hash = [[[hashType alloc] init] autorelease]; + [hash updateWithBuffer: [k cArray] + ofSize: k.count]; + + [hash retain]; + [pool release]; + + return [hash digest]; +} + +- (OFDataArray*)XMPP_hiWithData: (OFDataArray *)str + salt: (OFDataArray *)salt_ + iterationCount: (intmax_t)i +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + size_t digestSize = [hashType digestSize]; + uint8_t *result = NULL, *u, *uOld; + intmax_t j, k; + OFDataArray *salty, *tmp, *ret; + + result = [self allocMemoryWithSize: digestSize]; + + @try { + memset(result, 0, digestSize); + + salty = [[salt_ copy] autorelease]; + [salty addNItems: 4 + fromCArray: "\0\0\0\1"]; + + uOld = [self XMPP_HMACWithKey: str + data: salty]; + + for (j = 0; j < digestSize; j++) + result[j] ^= uOld[j]; + + for (j = 0; j < i - 1; j++) { + tmp = [OFDataArray dataArrayWithItemSize: 1]; + [tmp addNItems: digestSize + fromCArray: uOld]; + + u = [self XMPP_HMACWithKey: str + data: tmp]; + + for (k = 0; k < digestSize; k++) + result[k] ^= u[k]; + + uOld = u; + + [pool releaseObjects]; + } + + ret = [OFDataArray dataArrayWithItemSize: 1]; + [ret addNItems: digestSize + fromCArray: result]; + } @finally { + [self freeMemory: result]; + } + + [ret retain]; + [pool release]; + + return [ret autorelease]; +} @end