Index: src/OFHMAC.h ================================================================== --- src/OFHMAC.h +++ src/OFHMAC.h @@ -24,13 +24,18 @@ * * @brief A class which provides methods to calculate an HMAC. */ @interface OFHMAC: OFObject { + Class _hashClass; id _outerHash, _innerHash; - bool _keySet, _calculated; + id _outerHashCopy, _innerHashCopy; + bool _calculated; } + +/*! The class for the cryptographic hash used by the HMAC. */ +@property (assign, readonly) Class hashClass; /*! * @brief Returns a new OFHMAC with the specified hashing algorithm. * * @param class The class of the hashing algorithm @@ -48,10 +53,16 @@ - initWithHashClass: (Class )class; /*! * @brief Sets the key for the HMAC. * + * @note This resets the HMAC! + * + * @warning This invalidates any pointer previously returned by @ref digest. If + * you are still interested in the previous digest, you need to memcpy + * it yourself before calling @ref setKey:length:! + * * @param key The key for the HMAC * @param length The length of the key for the HMAC */ - (void)setKey: (const void*)key length: (size_t)length; @@ -81,15 +92,19 @@ * @return The size of the digest. */ - (size_t)digestSize; /*! - * @brief Resets all state so that a new HMAC can be calculated. + * @brief Resets the HMAC so that it can be calculated for a new message. + * + * @note This does not reset the key so that a new HMAC with the same key can + * be calculated efficiently. If you want to reset both, use + * @ref setKey:length:. * * @warning This invalidates any pointer previously returned by @ref digest. If * you are still interested in the previous digest, you need to memcpy * it yourself before calling @ref reset! */ - (void)reset; @end OF_ASSUME_NONNULL_END Index: src/OFHMAC.m ================================================================== --- src/OFHMAC.m +++ src/OFHMAC.m @@ -18,63 +18,61 @@ #import "OFHashAlreadyCalculatedException.h" #import "OFInvalidArgumentException.h" @implementation OFHMAC +@synthesize hashClass = _hashClass; + + (instancetype)HMACWithHashClass: (Class )class { return [[[self alloc] initWithHashClass: class] autorelease]; } -- initWithHashClass: (id )class +- initWithHashClass: (Class )class { self = [super init]; - @try { - void *pool = objc_autoreleasePoolPush(); - - _outerHash = [[class cryptoHash] retain]; - _innerHash = [[class cryptoHash] retain]; - - objc_autoreleasePoolPop(pool); - } @catch (id e) { - [self release]; - @throw e; - } + _hashClass = class; return self; } - (void)dealloc { [_outerHash release]; [_innerHash release]; + [_outerHashCopy release]; + [_innerHashCopy release]; [super dealloc]; } - (void)setKey: (const void*)key length: (size_t)length { - size_t blockSize = [[_outerHash class] blockSize]; + void *pool = objc_autoreleasePoolPush(); + size_t blockSize = [_hashClass blockSize]; uint8_t outerKeyPad[blockSize], innerKeyPad[blockSize]; + [_outerHash release]; + [_innerHash release]; + [_outerHashCopy release]; + [_innerHashCopy release]; + _outerHash = _innerHash = _outerHashCopy = _innerHashCopy = nil; + if (length > blockSize) { - void *pool = objc_autoreleasePoolPush(); - id hash = [[_outerHash class] cryptoHash]; + id hash = [_hashClass cryptoHash]; [hash updateWithBuffer: key length: length]; - length = [[hash class] digestSize]; + length = [_hashClass digestSize]; if OF_UNLIKELY (length > blockSize) length = blockSize; memcpy(outerKeyPad, [hash digest], length); memcpy(innerKeyPad, [hash digest], length); - - objc_autoreleasePoolPop(pool); } else { memcpy(outerKeyPad, key, length); memcpy(innerKeyPad, key, length); } @@ -84,22 +82,30 @@ for (size_t i = 0; i < blockSize; i++) { outerKeyPad[i] ^= 0x5C; innerKeyPad[i] ^= 0x36; } + _outerHash = [[_hashClass cryptoHash] retain]; + _innerHash = [[_hashClass cryptoHash] retain]; + [_outerHash updateWithBuffer: outerKeyPad length: blockSize]; [_innerHash updateWithBuffer: innerKeyPad length: blockSize]; - _keySet = true; + objc_autoreleasePoolPop(pool); + + _outerHashCopy = [_outerHash copy]; + _innerHashCopy = [_innerHash copy]; + + _calculated = false; } - (void)updateWithBuffer: (const void*)buffer length: (size_t)length { - if (!_keySet) + if (_innerHash == nil) @throw [OFInvalidArgumentException exception]; if (_calculated) @throw [OFHashAlreadyCalculatedException exceptionWithObject: self]; @@ -108,29 +114,35 @@ length: length]; } - (const unsigned char*)digest { + if (_outerHash == nil || _innerHash == nil) + @throw [OFInvalidArgumentException exception]; + if (_calculated) return [_outerHash digest]; [_outerHash updateWithBuffer: [_innerHash digest] - length: [[_innerHash class] digestSize]]; + length: [_hashClass digestSize]]; _calculated = true; return [_outerHash digest]; } - (size_t)digestSize { - return [[_outerHash class] digestSize]; + return [_hashClass digestSize]; } - (void)reset { - [_outerHash reset]; - [_innerHash reset]; + [_outerHash release]; + [_innerHash release]; + _outerHash = _innerHash = nil; - _keySet = false; + _outerHash = [_outerHashCopy copy]; + _innerHash = [_innerHashCopy copy]; + _calculated = false; } @end