Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -324,10 +324,12 @@ 4B6C8AD917BD5C2E00B194F2 /* OFStream+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD217BD5C2E00B194F2 /* OFStream+Private.h */; }; 4B6C8ADB17BD5C2E00B194F2 /* OFString_UTF8+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD417BD5C2E00B194F2 /* OFString_UTF8+Private.h */; }; 4B6C8ADC17BD5C2E00B194F2 /* OFThread+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD517BD5C2E00B194F2 /* OFThread+Private.h */; }; 4B6C8ADD17BD5C2E00B194F2 /* OFTimer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD617BD5C2E00B194F2 /* OFTimer+Private.h */; }; 4B6C8ADE17BD5C2E00B194F2 /* OFZIPArchiveEntry+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD717BD5C2E00B194F2 /* OFZIPArchiveEntry+Private.h */; }; + 4B6D0A891D4459D900901D8D /* OFHMAC.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6D0A871D4459D900901D8D /* OFHMAC.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B6D0A8A1D4459D900901D8D /* OFHMAC.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6D0A881D4459D900901D8D /* OFHMAC.m */; }; 4B7161AD17A6FC7600B74970 /* OFHTTPResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B7161AB17A6FC7600B74970 /* OFHTTPResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B7161AE17A6FC7600B74970 /* OFHTTPResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B7161AC17A6FC7600B74970 /* OFHTTPResponse.m */; }; 4B72F7DE1AD9311B00CE253C /* OFStatItemFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B72F7DC1AD9311B00CE253C /* OFStatItemFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B72F7DF1AD9311B00CE253C /* OFStatItemFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B72F7DD1AD9311B00CE253C /* OFStatItemFailedException.m */; }; 4B745BA5168B25E600A6C20E /* OFSystemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B745BA3168B25E600A6C20E /* OFSystemInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -819,10 +821,12 @@ 4B6C8AD217BD5C2E00B194F2 /* OFStream+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFStream+Private.h"; path = "src/OFStream+Private.h"; sourceTree = ""; }; 4B6C8AD417BD5C2E00B194F2 /* OFString_UTF8+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFString_UTF8+Private.h"; path = "src/OFString_UTF8+Private.h"; sourceTree = ""; }; 4B6C8AD517BD5C2E00B194F2 /* OFThread+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFThread+Private.h"; path = "src/OFThread+Private.h"; sourceTree = ""; }; 4B6C8AD617BD5C2E00B194F2 /* OFTimer+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFTimer+Private.h"; path = "src/OFTimer+Private.h"; sourceTree = ""; }; 4B6C8AD717BD5C2E00B194F2 /* OFZIPArchiveEntry+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFZIPArchiveEntry+Private.h"; path = "src/OFZIPArchiveEntry+Private.h"; sourceTree = ""; }; + 4B6D0A871D4459D900901D8D /* OFHMAC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFHMAC.h; path = src/OFHMAC.h; sourceTree = ""; }; + 4B6D0A881D4459D900901D8D /* OFHMAC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFHMAC.m; path = src/OFHMAC.m; sourceTree = ""; }; 4B6EF66E1235358D0076B512 /* OFArrayTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFArrayTests.m; path = tests/OFArrayTests.m; sourceTree = SOURCE_ROOT; }; 4B6EF66F1235358D0076B512 /* OFDataArrayTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFDataArrayTests.m; path = tests/OFDataArrayTests.m; sourceTree = SOURCE_ROOT; }; 4B6EF6701235358D0076B512 /* OFDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFDictionaryTests.m; path = tests/OFDictionaryTests.m; sourceTree = SOURCE_ROOT; }; 4B6EF6721235358D0076B512 /* OFListTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFListTests.m; path = tests/OFListTests.m; sourceTree = SOURCE_ROOT; }; 4B6EF6731235358D0076B512 /* OFMD5HashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFMD5HashTests.m; path = tests/OFMD5HashTests.m; sourceTree = SOURCE_ROOT; }; @@ -1344,10 +1348,12 @@ 4B6799671099E7C50041064A /* OFFile.m */, 4B2C72881B888B6900717583 /* OFFileManager.h */, 4B2C72891B888B6900717583 /* OFFileManager.m */, 4BD1125E1CCB739A0076FDB9 /* OFGZIPStream.h */, 4BD1125F1CCB739A0076FDB9 /* OFGZIPStream.m */, + 4B6D0A871D4459D900901D8D /* OFHMAC.h */, + 4B6D0A881D4459D900901D8D /* OFHMAC.m */, 4BB4B53F16775FF4002A2DCE /* OFHTTPClient.h */, 4BB4B54016775FF4002A2DCE /* OFHTTPClient.m */, 4B99250F12E0780000215DBE /* OFHTTPRequest.h */, 4B99251012E0780000215DBE /* OFHTTPRequest.m */, 4B7161AB17A6FC7600B74970 /* OFHTTPResponse.h */, @@ -1696,10 +1702,11 @@ 4B3D23C61337FCB000DD29B8 /* OFEnumerator.h in Headers */, 4B17FF74133A2AAB003E6DCD /* OFException.h in Headers */, 4B3D23C81337FCB000DD29B8 /* OFFile.h in Headers */, 4B2C728C1B888B8700717583 /* OFFileManager.h in Headers */, 4BD112621CCB73A90076FDB9 /* OFGZIPStream.h in Headers */, + 4B6D0A891D4459D900901D8D /* OFHMAC.h in Headers */, 4BB4B54416775FF4002A2DCE /* OFHTTPClient.h in Headers */, 4B3D23CA1337FCB000DD29B8 /* OFHTTPRequest.h in Headers */, 4B7161AD17A6FC7600B74970 /* OFHTTPResponse.h in Headers */, 4BB4B54616775FF4002A2DCE /* OFHTTPServer.h in Headers */, 4B06855318B2AD3800FC731A /* OFINICategory.h in Headers */, @@ -2084,10 +2091,11 @@ 4B3D23931337FC0D00DD29B8 /* OFDictionary.m in Sources */, 4B2B3E80140D430500EC2F7C /* OFDictionary_hashtable.m in Sources */, 4B3D23941337FC0D00DD29B8 /* OFEnumerator.m in Sources */, 4B3D23961337FC0D00DD29B8 /* OFFile.m in Sources */, 4B2C728B1B888B8300717583 /* OFFileManager.m in Sources */, + 4B6D0A8A1D4459D900901D8D /* OFHMAC.m in Sources */, 4BB4B54516775FF4002A2DCE /* OFHTTPClient.m in Sources */, 4B3D23981337FC0D00DD29B8 /* OFHTTPRequest.m in Sources */, 4B7161AE17A6FC7600B74970 /* OFHTTPResponse.m in Sources */, 4BB4B54716775FF4002A2DCE /* OFHTTPServer.m in Sources */, 4B06855418B2AD3800FC731A /* OFINICategory.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -24,10 +24,11 @@ OFDeflateStream.m \ OFDeflate64Stream.m \ OFDictionary.m \ OFEnumerator.m \ OFGZIPStream.m \ + OFHMAC.m \ OFIntrospection.m \ OFList.m \ OFMapTable.m \ OFMD5Hash.m \ OFMessagePackExtension.m \ ADDED src/OFHMAC.h Index: src/OFHMAC.h ================================================================== --- src/OFHMAC.h +++ src/OFHMAC.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFObject.h" +#import "OFCryptoHash.h" + +OF_ASSUME_NONNULL_BEGIN + +/*! + * @class OFHMAC OFHMAC.h ObjFW/OFHMAC.h + * + * @brief A class which provides methods to calculate an HMAC. + */ +@interface OFHMAC: OFObject +{ + id _outerHash, _innerHash; + bool _keySet, _calculated; +} + +/*! + * @brief Returns a new OFHMAC with the specified hashing algorithm. + * + * @param class The class of the hashing algorithm + * @return A new, autoreleased OFHMAC + */ ++ (instancetype)HMACWithHashClass: (Class )class; + +/*! + * @brief Initialized an already allocated OFHMAC with the specified hashing + * algorithm. + * + * @param class The class of the hashing algorithm + * @return An initialized OFHMAC + */ +- initWithHashClass: (Class )class; + +/*! + * @brief Sets the key for the HMAC. + * + * @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; + +/*! + * @brief Adds a buffer to the HMAC to be calculated. + * + * @param buffer The buffer which should be included into the calculation + * @param length The length of the buffer + */ +- (void)updateWithBuffer: (const void*)buffer + length: (size_t)length; + +/*! + * @brief Returns a buffer containing the HMAC. + * + * The size of the buffer depends on the hash used. The buffer is part of the + * receiver's memory pool. + * + * @return A buffer containing the hash + */ +- (const uint8_t*)digest OF_RETURNS_INNER_POINTER; + +/*! + * @brief Returns the size of the digest. + * + * @return The size of the digest. + */ +- (size_t)digestSize; + +/*! + * @brief Resets all state so that a new HMAC can be calculated. + * + * @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 ADDED src/OFHMAC.m Index: src/OFHMAC.m ================================================================== --- src/OFHMAC.m +++ src/OFHMAC.m @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFHMAC.h" + +#import "OFHashAlreadyCalculatedException.h" +#import "OFInvalidArgumentException.h" + +@implementation OFHMAC ++ (instancetype)HMACWithHashClass: (Class )class +{ + return [[[self alloc] initWithHashClass: class] autorelease]; +} + +- initWithHashClass: (id )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; + } + + return self; +} + +- (void)dealloc +{ + [_outerHash release]; + [_innerHash release]; + + [super dealloc]; +} + +- (void)setKey: (const void*)key + length: (size_t)length +{ + size_t blockSize = [[_outerHash class] blockSize]; + uint8_t outerKeyPad[blockSize], innerKeyPad[blockSize]; + + if (length > blockSize) { + void *pool = objc_autoreleasePoolPush(); + id hash = [[_outerHash class] cryptoHash]; + + [hash updateWithBuffer: key + length: length]; + + length = [[hash class] 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); + } + + memset(outerKeyPad + length, 0, blockSize - length); + memset(innerKeyPad + length, 0, blockSize - length); + + for (size_t i = 0; i < blockSize; i++) { + outerKeyPad[i] ^= 0x5C; + innerKeyPad[i] ^= 0x36; + } + + [_outerHash updateWithBuffer: outerKeyPad + length: blockSize]; + [_innerHash updateWithBuffer: innerKeyPad + length: blockSize]; + + _keySet = true; +} + +- (void)updateWithBuffer: (const void*)buffer + length: (size_t)length +{ + if (!_keySet) + @throw [OFInvalidArgumentException exception]; + + if (_calculated) + @throw [OFHashAlreadyCalculatedException + exceptionWithObject: self]; + + [_innerHash updateWithBuffer: buffer + length: length]; +} + +- (const uint8_t*)digest +{ + if (_calculated) + return [_outerHash digest]; + + [_outerHash updateWithBuffer: [_innerHash digest] + length: [[_innerHash class] digestSize]]; + _calculated = true; + + return [_outerHash digest]; +} + +- (size_t)digestSize +{ + return [[_outerHash class] digestSize]; +} + +- (void)reset +{ + [_outerHash reset]; + [_innerHash reset]; + + _keySet = false; + _calculated = false; +} +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -81,10 +81,12 @@ #import "OFSHA1Hash.h" #import "OFSHA224Hash.h" #import "OFSHA256Hash.h" #import "OFSHA384Hash.h" #import "OFSHA512Hash.h" + +#import "OFHMAC.h" #import "OFXMLAttribute.h" #import "OFXMLElement.h" #import "OFXMLAttribute.h" #import "OFXMLCharacters.h" Index: src/exceptions/OFHashAlreadyCalculatedException.h ================================================================== --- src/exceptions/OFHashAlreadyCalculatedException.h +++ src/exceptions/OFHashAlreadyCalculatedException.h @@ -13,11 +13,10 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #import "OFException.h" -#import "OFCryptoHash.h" /*! * @class OFHashAlreadyCalculatedException \ * OFHashAlreadyCalculatedException.h \ * ObjFW/OFHashAlreadyCalculatedException.h @@ -24,29 +23,29 @@ * * @brief An exception indicating that the hash has already been calculated. */ @interface OFHashAlreadyCalculatedException: OFException { - id _object; + id _object; } /*! * The hash which has already been calculated. */ -@property (readonly, retain) id object; +@property (readonly, retain) id object; /*! * @brief Creates a new, autoreleased hash already calculated exception. * * @param object The hash which has already been calculated * @return A new, autoreleased hash already calculated exception */ -+ (instancetype)exceptionWithObject: (id )object; ++ (instancetype)exceptionWithObject: (id)object; /*! * @brief Initializes an already allocated hash already calculated exception. * * @param object The hash which has already been calculated * @return An initialized hash already calculated exception */ -- initWithObject: (id )object; +- initWithObject: (id)object; @end Index: src/exceptions/OFHashAlreadyCalculatedException.m ================================================================== --- src/exceptions/OFHashAlreadyCalculatedException.m +++ src/exceptions/OFHashAlreadyCalculatedException.m @@ -20,21 +20,21 @@ #import "OFString.h" @implementation OFHashAlreadyCalculatedException @synthesize object = _object; -+ (instancetype)exceptionWithObject: (id )object ++ (instancetype)exceptionWithObject: (id)object { return [[[self alloc] initWithObject: object] autorelease]; } - init { OF_INVALID_INIT_METHOD } -- initWithObject: (id )object +- initWithObject: (id)object { self = [super init]; _object = [object retain];