瀏覽代碼

HMAC authenticator (variants MD5, SHA1, SHA256)

Marcin Krzyżanowski 10 年之前
父節點
當前提交
c8ee331a31

+ 10 - 0
CryptoSwift.xcodeproj/project.pbxproj

@@ -31,6 +31,9 @@
 		757DA2571A4ED47B002BA3EF /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757DA2561A4ED47B002BA3EF /* Helpers.swift */; };
 		757DA2591A4ED4D7002BA3EF /* ChaCha20Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757DA2581A4ED4D7002BA3EF /* ChaCha20Tests.swift */; };
 		757EF7F519AAA82400586276 /* CRC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 757EF7F419AAA82400586276 /* CRC.swift */; };
+		758A94261A65AEB100E46135 /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758A94251A65AEB100E46135 /* HMAC.swift */; };
+		758A94281A65C59200E46135 /* HMACTests.swift in Resources */ = {isa = PBXBuildFile; fileRef = 758A94271A65C59200E46135 /* HMACTests.swift */; };
+		758A94291A65C67400E46135 /* HMACTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758A94271A65C59200E46135 /* HMACTests.swift */; };
 		758C764119B61AE500653BC6 /* Generics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758C764019B61AE500653BC6 /* Generics.swift */; };
 		758C764319B61DE900653BC6 /* UInt16Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 758C764219B61DE900653BC6 /* UInt16Extension.swift */; };
 		758F3F761992E57D0014BBDA /* Playground in Resources */ = {isa = PBXBuildFile; fileRef = 758F3F751992E57D0014BBDA /* Playground */; };
@@ -139,6 +142,8 @@
 		757DA2561A4ED47B002BA3EF /* Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
 		757DA2581A4ED4D7002BA3EF /* ChaCha20Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChaCha20Tests.swift; sourceTree = "<group>"; };
 		757EF7F419AAA82400586276 /* CRC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CRC.swift; sourceTree = "<group>"; };
+		758A94251A65AEB100E46135 /* HMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMAC.swift; sourceTree = "<group>"; };
+		758A94271A65C59200E46135 /* HMACTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMACTests.swift; sourceTree = "<group>"; };
 		758C764019B61AE500653BC6 /* Generics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generics.swift; sourceTree = "<group>"; };
 		758C764219B61DE900653BC6 /* UInt16Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UInt16Extension.swift; sourceTree = "<group>"; };
 		758F3F751992E57D0014BBDA /* Playground */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Playground; path = CryptoSwift/Playground; sourceTree = "<group>"; };
@@ -217,6 +222,7 @@
 				757EF7F419AAA82400586276 /* CRC.swift */,
 				75EB380019ABDD710002375A /* ChaCha20.swift */,
 				751C5C3C19B26B000094C75D /* Poly1305.swift */,
+				758A94251A65AEB100E46135 /* HMAC.swift */,
 				75A74B261A1FF6B2004419F1 /* AES.swift */,
 				757DA24E1A4E59F2002BA3EF /* PKCS7.swift */,
 				759D481019B517BC005FF7FC /* BitExtension.swift */,
@@ -250,6 +256,7 @@
 				757DA2561A4ED47B002BA3EF /* Helpers.swift */,
 				754BE46719693E190098E6F3 /* HashTests.swift */,
 				75100F8E19B0BC890005C5F5 /* Poly1305Tests.swift */,
+				758A94271A65C59200E46135 /* HMACTests.swift */,
 				757DA2541A4ED408002BA3EF /* AESTests.swift */,
 				757DA2581A4ED4D7002BA3EF /* ChaCha20Tests.swift */,
 				755FB1D9199E347D00475437 /* ExtensionsTest.swift */,
@@ -373,6 +380,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				758A94281A65C59200E46135 /* HMACTests.swift in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -394,6 +402,7 @@
 				75EB380119ABDD710002375A /* ChaCha20.swift in Sources */,
 				750A54601992D2680017DA75 /* MD5.swift in Sources */,
 				75164E4919AD30AC00737F30 /* Utils.swift in Sources */,
+				758A94261A65AEB100E46135 /* HMAC.swift in Sources */,
 				752DEF7719693EA000E17557 /* NSDataExtension.swift in Sources */,
 				759D481119B517BC005FF7FC /* BitExtension.swift in Sources */,
 				754C8FED19979F94005AD904 /* ArrayExtension.swift in Sources */,
@@ -416,6 +425,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				758A94291A65C67400E46135 /* HMACTests.swift in Sources */,
 				75100F8F19B0BC890005C5F5 /* Poly1305Tests.swift in Sources */,
 				757DA2571A4ED47B002BA3EF /* Helpers.swift in Sources */,
 				754BE46819693E190098E6F3 /* HashTests.swift in Sources */,

+ 3 - 0
CryptoSwift/Authenticator.swift

@@ -18,6 +18,7 @@ public enum Authenticator {
     :param: key 256-bit key
     */
     case Poly1305(key: NSData)
+    case HMAC(key: NSData, variant:CryptoSwift.HMAC.Variant)
     
     /**
     Generates an authenticator for message using a one-time key and returns the 16-byte result
@@ -28,6 +29,8 @@ public enum Authenticator {
         switch (self) {
         case .Poly1305(let key):
             return CryptoSwift.Poly1305.authenticate(key: key, message: message)
+        case .HMAC(let key, let variant):
+            return CryptoSwift.HMAC.authenticate(key: key, message: message, variant: variant)
         }
     }
 }

+ 79 - 0
CryptoSwift/HMAC.swift

@@ -0,0 +1,79 @@
+//
+//  HMAC.swift
+//  CryptoSwift
+//
+//  Created by Marcin Krzyzanowski on 13/01/15.
+//  Copyright (c) 2015 Marcin Krzyzanowski. All rights reserved.
+//
+
+import Foundation
+
+public class HMAC {
+    
+    public enum Variant {
+        case sha1, sha256, md5
+        
+        func calculateHash(# bytes:[Byte]) -> [Byte]? {
+            switch (self) {
+            case .sha1:
+                return NSData.withBytes(bytes).sha1()?.bytes()
+            case .sha256:
+                return NSData.withBytes(bytes).sha256()?.bytes()
+            case .md5:
+                return NSData.withBytes(bytes).md5()?.bytes();
+            }
+        }
+        
+        func blockSize() -> Int {
+            return 64
+        }
+    }
+    
+    let key:[Byte]
+    let variant:Variant
+    
+    class internal func authenticate(# key: NSData, message: NSData, variant:HMAC.Variant = .md5) -> NSData? {
+        if let mac = HMAC.authenticate(key: key.bytes(), message: message.bytes(), variant: variant) {
+            return NSData(bytes: mac, length: mac.count)
+        }
+        return nil
+    }
+    
+    class internal func authenticate(# key: [Byte], message: [Byte], variant:HMAC.Variant = .md5) -> [Byte]? {
+        return HMAC(key, variant: variant)?.authenticate(message: message)
+    }
+
+    // MARK: - Private
+    
+    private init? (_ key: [Byte], variant:HMAC.Variant = .md5) {
+        self.variant = variant
+        self.key = key
+
+        if (key.count > variant.blockSize()) {
+            if let hash = variant.calculateHash(bytes: key) {
+                self.key = hash
+            }
+        }
+        
+        if (key.count < variant.blockSize()) { // keys shorter than blocksize are zero-padded
+            self.key = key + [Byte](count: variant.blockSize() - key.count, repeatedValue: 0)
+        }
+    }
+    
+    private func authenticate(# message:[Byte]) -> [Byte]? {
+        var opad = [Byte](count: variant.blockSize(), repeatedValue: 0x5c)
+        for (idx, val) in enumerate(key) {
+            opad[idx] = key[idx] ^ opad[idx]
+        }
+        var ipad = [Byte](count: variant.blockSize(), repeatedValue: 0x36)
+        for (idx, val) in enumerate(key) {
+            ipad[idx] = key[idx] ^ ipad[idx]
+        }
+
+        var finalHash:[Byte]? = nil;
+        if let ipadAndMessageHash = variant.calculateHash(bytes: ipad + message) {
+            finalHash = variant.calculateHash(bytes: opad + ipadAndMessageHash);
+        }
+        return finalHash
+    }
+}

+ 19 - 0
CryptoSwift/Playground/MyPlayground.playground/section-1.swift

@@ -3,3 +3,22 @@
 import Foundation
 
 reverse(1..<4)
+
+var arr:[UInt8] = [0x06];
+let repeat = Repeat(count: 5, repeatedValue: 0)
+for a in repeat {
+    arr = arr + [Byte(a)]
+}
+
+arr + [Byte](count: 5, repeatedValue: 0)
+
+
+arr[0..<3]
+
+
+var key:[Byte] = [1,2,3,4,5,6,7,8,9,0]
+var opad = [Byte](count: 64, repeatedValue: 0x5c)
+
+opad.map({ (val:Byte) -> (Byte) in
+    return val ^ 56
+})

+ 3 - 0
CryptoSwift/Playground/MyPlayground.playground/timeline.xctimeline

@@ -2,5 +2,8 @@
 <Timeline
    version = "3.0">
    <TimelineItems>
+      <LoggerValueHistoryTimelineItem
+         documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=206&amp;EndingColumnNumber=6&amp;EndingLineNumber=15&amp;StartingColumnNumber=5&amp;StartingLineNumber=15&amp;Timestamp=442876557.94963">
+      </LoggerValueHistoryTimelineItem>
    </TimelineItems>
 </Timeline>

+ 51 - 0
CryptoSwiftTests/HMACTests.swift

@@ -0,0 +1,51 @@
+//
+//  HMACTests.swift
+//  CryptoSwift
+//
+//  Created by Marcin Krzyzanowski on 29/08/14.
+//  Copyright (c) 2015 Marcin Krzyzanowski. All rights reserved.
+//
+
+import Foundation
+import XCTest
+import CryptoSwift
+
+class HMACTests: XCTestCase {
+    
+    override func setUp() {
+        super.setUp()
+    }
+    
+    override func tearDown() {
+        super.tearDown()
+    }
+    
+    
+    func testMD5() {
+        let key:[Byte] = []
+        let msg:[Byte] = []
+        let expectedMac:[Byte] = [0x74,0xe6,0xf7,0x29,0x8a,0x9c,0x2d,0x16,0x89,0x35,0xf5,0x8c,0x00,0x1b,0xad,0x88]
+        
+        let hmac = Authenticator.HMAC(key: NSData.withBytes(key), variant: .md5).authenticate(NSData.withBytes(msg))
+        XCTAssertEqual(hmac!, NSData.withBytes(expectedMac), "Invalid authentication result")
+    }
+    
+    func testSHA1() {
+        let key:[Byte] = []
+        let msg:[Byte] = []
+        let expectedMac:[Byte] = [0xfb,0xdb,0x1d,0x1b,0x18,0xaa,0x6c,0x08,0x32,0x4b,0x7d,0x64,0xb7,0x1f,0xb7,0x63,0x70,0x69,0x0e,0x1d]
+        
+        let hmac = Authenticator.HMAC(key: NSData.withBytes(key), variant: .sha1).authenticate(NSData.withBytes(msg))
+        XCTAssertEqual(hmac!, NSData.withBytes(expectedMac), "Invalid authentication result")
+    }
+
+    func testSHA256() {
+        let key:[Byte] = []
+        let msg:[Byte] = []
+        let expectedMac:[Byte] = [0xb6,0x13,0x67,0x9a,0x08,0x14,0xd9,0xec,0x77,0x2f,0x95,0xd7,0x78,0xc3,0x5f,0xc5,0xff,0x16,0x97,0xc4,0x93,0x71,0x56,0x53,0xc6,0xc7,0x12,0x14,0x42,0x92,0xc5,0xad]
+        
+        let hmac = Authenticator.HMAC(key: NSData.withBytes(key), variant: .sha256).authenticate(NSData.withBytes(msg))
+        XCTAssertEqual(hmac!, NSData.withBytes(expectedMac), "Invalid authentication result")
+    }
+
+}

+ 1 - 0
README.md

@@ -26,6 +26,7 @@ Good mood
 
 #####Message authenticators
 - [Poly1305](http://cr.yp.to/mac/poly1305-20050329.pdf)
+- [HMAC](https://www.ietf.org/rfc/rfc2104.txt) MD5, SHA1, SHA256
 
 #####Cipher block mode
 - Electronic codebook ([ECB](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29))