Эх сурвалжийг харах

Add SHA-3 implementation. Closes #291

Marcin Krzyżanowski 9 жил өмнө
parent
commit
7f1e8390c7

+ 1 - 0
CHANGELOG

@@ -1,3 +1,4 @@
+- New: SHA-3 implementation (request #291)
 - SHA-2 conforms to Updatable protocol and may be calculated incrementally. 
 
 0.6.0

+ 17 - 5
CryptoSwift.xcodeproj/project.pbxproj

@@ -79,6 +79,7 @@
 		75D1F1F41D06B98B005A87A2 /* PBKDF1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D1F1F31D06B98B005A87A2 /* PBKDF1.swift */; };
 		75DB81A31CDBFDC700ED181A /* BlockModeWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75DB81A21CDBFDC700ED181A /* BlockModeWorker.swift */; };
 		75DB81A81CDC06B100ED181A /* Updatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75DB81A71CDC06B100ED181A /* Updatable.swift */; };
+		75F818231D9527CE00A78C92 /* SHA3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75F818221D9527CE00A78C92 /* SHA3.swift */; };
 		80545D131CA9FECD00474A99 /* Bit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80545D121CA9FECD00474A99 /* Bit.swift */; };
 		E3FD2D501D6B7E1100A9F35F /* CommonCryptoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3FD2D4E1D6B734A00A9F35F /* CommonCryptoTests.swift */; };
 		E3FD2D531D6B81CE00A9F35F /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3FD2D511D6B813C00A9F35F /* Error+Extension.swift */; };
@@ -225,6 +226,7 @@
 		75D1F1F31D06B98B005A87A2 /* PBKDF1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PBKDF1.swift; path = Sources/CryptoSwift/PKCS5/PBKDF1.swift; sourceTree = SOURCE_ROOT; };
 		75DB81A21CDBFDC700ED181A /* BlockModeWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BlockModeWorker.swift; path = Sources/CryptoSwift/BlockMode/BlockModeWorker.swift; sourceTree = SOURCE_ROOT; };
 		75DB81A71CDC06B100ED181A /* Updatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Updatable.swift; path = Sources/CryptoSwift/Updatable.swift; sourceTree = SOURCE_ROOT; };
+		75F818221D9527CE00A78C92 /* SHA3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SHA3.swift; path = Sources/CryptoSwift/SHA3.swift; sourceTree = SOURCE_ROOT; };
 		80545D121CA9FECD00474A99 /* Bit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bit.swift; path = Sources/CryptoSwift/Bit.swift; sourceTree = SOURCE_ROOT; };
 		E3FD2D4E1D6B734A00A9F35F /* CommonCryptoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonCryptoTests.swift; sourceTree = "<group>"; };
 		E3FD2D511D6B813C00A9F35F /* Error+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Error+Extension.swift"; sourceTree = "<group>"; };
@@ -300,11 +302,9 @@
 				75CB93291C8F5EC60087740D /* BlockMode */,
 				757BC9231C1CA5790093AAA9 /* Checksum.swift */,
 				757BC9261C1CA5790093AAA9 /* Generics.swift */,
-				757BC9271C1CA5790093AAA9 /* Digest.swift */,
-				757BC9281C1CA5790093AAA9 /* DigestType.swift */,
+				75F818241D9527DB00A78C92 /* Digest */,
 				757BC9291C1CA5790093AAA9 /* HMAC.swift */,
 				757BC92B1C1CA5790093AAA9 /* IntegerConvertible.swift */,
-				757BC92D1C1CA5790093AAA9 /* MD5.swift */,
 				757BC92E1C1CA5790093AAA9 /* Multiplatform.swift */,
 				757BC92F1C1CA5790093AAA9 /* Operators.swift */,
 				757BC9301C1CA5790093AAA9 /* Padding.swift */,
@@ -313,8 +313,6 @@
 				753881EA1CB06E390089101D /* NoPadding.swift */,
 				750D3ACA1D0EADCA00999299 /* ZeroPadding.swift */,
 				757BC9331C1CA5790093AAA9 /* Poly1305.swift */,
-				757BC9351C1CA5790093AAA9 /* SHA1.swift */,
-				757BC9361C1CA5790093AAA9 /* SHA2.swift */,
 				757BC93B1C1CA5790093AAA9 /* Utils.swift */,
 				757BC91B1C1CA5790093AAA9 /* Array+Extension.swift */,
 				757BC9251C1CA5790093AAA9 /* CSArrayType+Extensions.swift */,
@@ -416,6 +414,19 @@
 			name = Ciphers;
 			sourceTree = "<group>";
 		};
+		75F818241D9527DB00A78C92 /* Digest */ = {
+			isa = PBXGroup;
+			children = (
+				757BC9281C1CA5790093AAA9 /* DigestType.swift */,
+				757BC9271C1CA5790093AAA9 /* Digest.swift */,
+				757BC92D1C1CA5790093AAA9 /* MD5.swift */,
+				757BC9351C1CA5790093AAA9 /* SHA1.swift */,
+				757BC9361C1CA5790093AAA9 /* SHA2.swift */,
+				75F818221D9527CE00A78C92 /* SHA3.swift */,
+			);
+			name = Digest;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -555,6 +566,7 @@
 				757BC96C1C1CA5790093AAA9 /* Generics.swift in Sources */,
 				757F44091CC172B6002B1F85 /* BlockCipher.swift in Sources */,
 				75558FCC1D4BA93C00CF6C18 /* RandomAccessCryptor.swift in Sources */,
+				75F818231D9527CE00A78C92 /* SHA3.swift in Sources */,
 				75CB93251C8F5EC10087740D /* CBC.swift in Sources */,
 				757BC9701C1CA5790093AAA9 /* Digest.swift in Sources */,
 				75CB93351C8F5FCE0087740D /* PCBC.swift in Sources */,

+ 1 - 0
README.md

@@ -29,6 +29,7 @@ Good mood
 - [SHA256](http://tools.ietf.org/html/rfc6234)
 - [SHA384](http://tools.ietf.org/html/rfc6234)
 - [SHA512](http://tools.ietf.org/html/rfc6234)
+- [SHA3](http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf)
 
 #### Cyclic Redundancy Check (CRC)
 - [CRC32](http://en.wikipedia.org/wiki/Cyclic_redundancy_check)

+ 5 - 1
Sources/CryptoSwift/CSArrayType+Extensions.swift

@@ -52,7 +52,11 @@ public extension CSArrayType where Iterator.Element == UInt8 {
     public func sha512() -> [Iterator.Element] {
         return Digest.sha512(cs_arrayValue())
     }
-    
+
+    public func sha3(_ variant: SHA3.Variant) -> [Iterator.Element] {
+        return Digest.sha3(cs_arrayValue(), variant: variant)
+    }
+
     public func crc32(seed: UInt32? = nil, reflect : Bool = true) -> UInt32 {
         return Checksum.crc32(cs_arrayValue(), seed: seed, reflect: reflect)
     }

+ 9 - 0
Sources/CryptoSwift/Digest.swift

@@ -53,4 +53,13 @@ public struct Digest {
     public static func sha512(_ bytes: Array<UInt8>) -> Array<UInt8> {
         return SHA2(variant: .sha512).calculate(for: bytes)
     }
+
+    /// Calculate SHA3 Digest
+    /// - parameter bytes: input message
+    /// - parameter variant: SHA-3 variant
+    /// - returns: Digest bytes
+    public static func sha3(_ bytes: Array<UInt8>, variant: SHA3.Variant) -> Array<UInt8> {
+        return SHA3(variant: variant).calculate(for: bytes)
+    }
+
 }

+ 5 - 0
Sources/CryptoSwift/Foundation/Data+Extension.swift

@@ -51,6 +51,11 @@ extension Data {
         return Data(bytes: result)
     }
 
+    public func sha3(_ variant: SHA3.Variant) -> Data? {
+        let result = Digest.sha3(self.bytes, variant: variant)
+        return Data(bytes: result)
+    }
+
     public func crc32(seed: UInt32? = nil, reflect : Bool = true) -> Data? {
         let result = Checksum.crc32(self.bytes, seed: seed, reflect: reflect)
         return Data(bytes: result.bytes())

+ 276 - 0
Sources/CryptoSwift/SHA3.swift

@@ -0,0 +1,276 @@
+//
+//  SHA3.swift
+//  CryptoSwift
+//
+//  Created by Marcin Krzyzanowski on 23/09/16.
+//  Copyright © 2016 Marcin Krzyzanowski. All rights reserved.
+//
+//  http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
+//  http://keccak.noekeon.org/specs_summary.html
+//
+
+#if os(Linux)
+    import Glibc
+#else
+    import Darwin
+#endif
+
+public final class SHA3: DigestType {
+    let round_constants: Array<UInt64> = [0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
+                                          0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
+                                          0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
+                                          0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
+                                          0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
+                                          0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008]
+
+    let variant: Variant
+
+    fileprivate var accumulated = Array<UInt8>()
+    fileprivate var accumulatedLength: Int = 0
+    fileprivate var accumulatedHash:Array<UInt64>
+
+    public enum Variant: RawRepresentable {
+        case sha224, sha256, sha384, sha512
+
+        public var digestLength:Int {
+            return 100 - (self.blockSize / 2)
+        }
+        
+        public var blockSize: Int {
+            return (1600 - self.rawValue * 2) / 8
+        }
+
+        public typealias RawValue = Int
+        public var rawValue: RawValue {
+            switch self {
+            case .sha224:
+                return 224
+            case .sha256:
+                return 256
+            case .sha384:
+                return 384
+            case .sha512:
+                return 512
+            }
+        }
+        
+        public init?(rawValue: RawValue) {
+            switch (rawValue) {
+            case 224:
+                self = .sha224
+                break;
+            case 256:
+                self = .sha256
+                break;
+            case 384:
+                self = .sha384
+                break;
+            case 512:
+                self = .sha512
+                break;
+            default:
+                return nil
+            }
+        }
+    }
+
+    public init(variant: SHA3.Variant) {
+        self.variant = variant
+        self.accumulatedHash = Array<UInt64>(repeating: 0, count: self.variant.digestLength)
+    }
+
+    ///  1. For all pairs (x,z) such that 0≤x<5 and 0≤z<w, let
+    ///     C[x,z]=A[x, 0,z] ⊕ A[x, 1,z] ⊕ A[x, 2,z] ⊕ A[x, 3,z] ⊕ A[x, 4,z].
+    ///  2. For all pairs (x, z) such that 0≤x<5 and 0≤z<w let
+    ///     D[x,z]=C[(x1) mod 5, z] ⊕ C[(x+1) mod 5, (z –1) mod w].
+    ///  3. For all triples (x, y, z) such that 0≤x<5, 0≤y<5, and 0≤z<w, let
+    ///     A′[x, y,z] = A[x, y,z] ⊕ D[x,z].
+    private func θ(_ a: inout Array<UInt64>) {
+        var c = Array<UInt64>(repeating: 0, count: 5)
+        var d = Array<UInt64>(repeating: 0, count: 5)
+
+        for i in 0..<5 {
+            c[i] = a[i] ^ a[i + 5] ^ a[i + 10] ^ a[i + 15] ^ a[i + 20]
+        }
+
+        d[0] = rotateLeft(c[1], by: 1) ^ c[4]
+        d[1] = rotateLeft(c[2], by: 1) ^ c[0]
+        d[2] = rotateLeft(c[3], by: 1) ^ c[1]
+        d[3] = rotateLeft(c[4], by: 1) ^ c[2]
+        d[4] = rotateLeft(c[0], by: 1) ^ c[3]
+
+        for i in 0..<5 {
+            a[i] ^= d[i]
+            a[i + 5]  ^= d[i]
+            a[i + 10] ^= d[i]
+            a[i + 15] ^= d[i]
+            a[i + 20] ^= d[i]
+        }
+    }
+
+    /// A′[x, y, z]=A[(x + 3y) mod 5, x, z]
+    private func π(_ a: inout Array<UInt64>) {
+        let a1 = a[1]
+        a[1] = a[6]
+        a[6] = a[9]
+        a[9] = a[22]
+        a[22] = a[14]
+        a[14] = a[20]
+        a[20] = a[2]
+        a[2] = a[12]
+        a[12] = a[13]
+        a[13] = a[19]
+        a[19] = a[23]
+        a[23] = a[15]
+        a[15] = a[4]
+        a[4] = a[24]
+        a[24] = a[21]
+        a[21] = a[8]
+        a[8] = a[16]
+        a[16] = a[5]
+        a[5] = a[3]
+        a[3] = a[18]
+        a[18] = a[17]
+        a[17] = a[11]
+        a[11] = a[7]
+        a[7] = a[10]
+        a[10] = a1
+    }
+
+    /// For all triples (x, y, z) such that 0≤x<5, 0≤y<5, and 0≤z<w, let
+    /// A′[x, y,z] = A[x, y,z] ⊕ ((A[(x+1) mod 5, y, z] ⊕ 1) ⋅ A[(x+2) mod 5, y, z])
+    private func χ(_ a: inout Array<UInt64>) {
+        for i in stride(from: 0, to: 25, by: 5) {
+            let a0 = a[0 + i]
+            let a1 = a[1 + i]
+            a[0 + i] ^= ~a1 & a[2 + i]
+            a[1 + i] ^= ~a[2 + i] & a[3 + i]
+            a[2 + i] ^= ~a[3 + i] & a[4 + i]
+            a[3 + i] ^= ~a[4 + i] & a0
+            a[4 + i] ^= ~a0 & a1
+        }
+    }
+
+    private func ι(_ a: inout Array<UInt64>, round: Int) {
+        a[0] ^= round_constants[round]
+    }
+
+    fileprivate func process<C: Collection>(block chunk: C, currentHash hh: inout Array<UInt64>) where C.Iterator.Element == UInt64, C.Index == Int {
+        // expand
+        hh[0] ^= chunk[0].littleEndian
+        hh[1] ^= chunk[1].littleEndian
+        hh[2] ^= chunk[2].littleEndian
+        hh[3] ^= chunk[3].littleEndian
+        hh[4] ^= chunk[4].littleEndian
+        hh[5] ^= chunk[5].littleEndian
+        hh[6] ^= chunk[6].littleEndian
+        hh[7] ^= chunk[7].littleEndian
+        hh[8] ^= chunk[8].littleEndian
+        if self.variant.blockSize > 72 { // 72 / 8, sha-512
+            hh[9]  ^= chunk[9 ].littleEndian
+            hh[10] ^= chunk[10].littleEndian
+            hh[11] ^= chunk[11].littleEndian
+            hh[12] ^= chunk[12].littleEndian
+            if self.variant.blockSize > 104 { // 104 / 8, sha-384
+                hh[13] ^= chunk[13].littleEndian
+                hh[14] ^= chunk[14].littleEndian
+                hh[15] ^= chunk[15].littleEndian
+                hh[16] ^= chunk[16].littleEndian
+                if self.variant.blockSize > 136 { // 136 / 8, sha-256
+                    hh[17] ^= chunk[17].littleEndian
+                    // FULL_SHA3_FAMILY_SUPPORT
+                    if self.variant.blockSize > 144 { // 144 / 8, sha-224
+                        hh[18] ^= chunk[18].littleEndian
+                        hh[19] ^= chunk[19].littleEndian
+                        hh[20] ^= chunk[20].littleEndian
+                        hh[21] ^= chunk[21].littleEndian
+                        hh[22] ^= chunk[22].littleEndian
+                        hh[23] ^= chunk[23].littleEndian
+                        hh[24] ^= chunk[24].littleEndian
+                    }
+                }
+            }
+        }
+
+        // Keccak-f
+        for round in 0..<24 {
+            θ(&hh)
+
+            hh[1] = rotateLeft(hh[1], by: 1)
+            hh[2] = rotateLeft(hh[2], by: 62)
+            hh[3] = rotateLeft(hh[3], by: 28)
+            hh[4] = rotateLeft(hh[4], by: 27)
+            hh[5] = rotateLeft(hh[5], by: 36)
+            hh[6] = rotateLeft(hh[6], by: 44)
+            hh[7] = rotateLeft(hh[7], by: 6)
+            hh[8] = rotateLeft(hh[8], by: 55)
+            hh[9] = rotateLeft(hh[9], by: 20)
+            hh[10] = rotateLeft(hh[10], by: 3)
+            hh[11] = rotateLeft(hh[11], by: 10)
+            hh[12] = rotateLeft(hh[12], by: 43)
+            hh[13] = rotateLeft(hh[13], by: 25)
+            hh[14] = rotateLeft(hh[14], by: 39)
+            hh[15] = rotateLeft(hh[15], by: 41)
+            hh[16] = rotateLeft(hh[16], by: 45)
+            hh[17] = rotateLeft(hh[17], by: 15)
+            hh[18] = rotateLeft(hh[18], by: 21)
+            hh[19] = rotateLeft(hh[19], by: 8)
+            hh[20] = rotateLeft(hh[20], by: 18)
+            hh[21] = rotateLeft(hh[21], by: 2)
+            hh[22] = rotateLeft(hh[22], by: 61)
+            hh[23] = rotateLeft(hh[23], by: 56)
+            hh[24] = rotateLeft(hh[24], by: 14)
+
+            π(&hh)
+            χ(&hh)
+            ι(&hh, round: round)
+        }
+    }
+
+    func calculate(for bytes: Array<UInt8>) -> Array<UInt8> {
+        do {
+            return try self.update(withBytes: bytes, isLast: true)
+        } catch {
+            return []
+        }
+    }
+}
+
+extension SHA3: Updatable {
+    public func update<T: Sequence>(withBytes bytes: T, isLast: Bool = false) throws -> Array<UInt8> where T.Iterator.Element == UInt8 {
+        let prevAccumulatedLength = self.accumulated.count
+        self.accumulated += bytes
+        self.accumulatedLength += self.accumulated.count - prevAccumulatedLength //avoid Array(bytes).count
+
+        if isLast {
+            // Add padding
+            if self.accumulated.count == 0 || self.accumulated.count % self.variant.blockSize != 0 {
+                let r = self.variant.blockSize * 8
+                let q = (r/8) - (self.accumulated.count % (r/8))
+                self.accumulated += Array<UInt8>(repeating: 0, count: q)
+            }
+
+            self.accumulated[self.accumulatedLength] |= 0x06 // 0x1F for SHAKE
+            self.accumulated[self.accumulated.count - 1] |= 0x80
+        }
+
+        for chunk in BytesSequence(chunkSize: self.variant.blockSize, data: self.accumulated) {
+            if (isLast || self.accumulated.count >= self.variant.blockSize) {
+                self.process(block: chunk.toUInt64Array(), currentHash: &self.accumulatedHash)
+                self.accumulated.removeFirst(chunk.count)
+            }
+        }
+
+        //TODO: verify performance, reduce vs for..in
+        let result = self.accumulatedHash.reduce(Array<UInt8>()) { (result, value) -> Array<UInt8> in
+            return result + arrayOfBytes(value: value.bigEndian)
+        }
+
+        // reset hash value for instance
+        if isLast {
+            self.accumulatedHash = Array<UInt64>(repeating: 0, count: self.variant.digestLength)
+        }
+        
+        return Array(result[0..<self.variant.digestLength])
+    }
+}

+ 4 - 0
Sources/CryptoSwift/String+Extension.swift

@@ -33,6 +33,10 @@ extension String {
         return self.utf8.lazy.map({ $0 as UInt8 }).sha512().toHexString()
     }
 
+    public func sha3(_ variant: SHA3.Variant) -> String {
+        return self.utf8.lazy.map({ $0 as UInt8 }).sha3(variant).toHexString()
+    }
+
     public func crc32(seed: UInt32? = nil, reflect : Bool = true) -> String {
         return self.utf8.lazy.map({ $0 as UInt8 }).crc32(seed: seed, reflect: reflect).bytes().toHexString()
     }

+ 4 - 0
Tests/CryptoSwiftTests/Access.swift

@@ -27,6 +27,7 @@ class Access: XCTestCase {
         let _ = Digest.sha256([1,2,3])
         let _ = Digest.sha384([1,2,3])
         let _ = Digest.sha512([1,2,3])
+        let _ = Digest.sha3([1,2,3], variant: .sha224)
 
         let _ = MD5()
     }
@@ -40,6 +41,7 @@ class Access: XCTestCase {
         let _ = array.sha256()
         let _ = array.sha384()
         let _ = array.sha512()
+        let _ = array.sha3(.sha224)
         let _ = array.crc32()
         let _ = array.crc16()
 
@@ -64,6 +66,7 @@ class Access: XCTestCase {
         let _ = string.sha256()
         let _ = string.sha384()
         let _ = string.sha512()
+        let _ = string.sha3(.sha224)
         let _ = string.crc16()
         let _ = string.crc32()
 
@@ -114,6 +117,7 @@ class Access: XCTestCase {
         let _ = data.sha256()
         let _ = data.sha384()
         let _ = data.sha512()
+        let _ = data.sha3(.sha224)
         let _ = data.crc16()
         let _ = data.crc32()
 

+ 29 - 1
Tests/CryptoSwiftTests/DigestTests.swift

@@ -98,7 +98,34 @@ final class DigestTests: XCTestCase {
 
         XCTAssertEqual(Array<UInt8>(repeating: 0x61, count: 1000000).sha512(), Array<UInt8>(hex: "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"), "One million (1,000,000) repetitions of the character 'a' (0x61)")
     }
-    
+
+    func testSHA3() {
+        XCTAssertEqual(SHA3(variant: .sha224).calculate(for: Array<UInt8>(hex: "616263")).toHexString(), "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf")
+        XCTAssertEqual(SHA3(variant: .sha256).calculate(for: Array<UInt8>(hex: "616263")).toHexString(), "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532")
+        XCTAssertEqual(SHA3(variant: .sha384).calculate(for: Array<UInt8>(hex: "616263")).toHexString(), "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25")
+        XCTAssertEqual(SHA3(variant: .sha512).calculate(for: Array<UInt8>(hex: "616263")).toHexString(), "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0")
+
+        XCTAssertEqual(SHA3(variant: .sha224).calculate(for: Array<UInt8>(hex: "")).toHexString(), "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7")
+        XCTAssertEqual(SHA3(variant: .sha256).calculate(for: Array<UInt8>(hex: "")).toHexString(), "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a")
+        XCTAssertEqual(SHA3(variant: .sha384).calculate(for: Array<UInt8>(hex: "")).toHexString(), "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004")
+        XCTAssertEqual(SHA3(variant: .sha512).calculate(for: Array<UInt8>(hex: "")).toHexString(), "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26")
+
+        XCTAssertEqual("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".sha3(.sha224), "8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33")
+        XCTAssertEqual("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".sha3(.sha256), "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376")
+        XCTAssertEqual("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".sha3(.sha384), "991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22")
+        XCTAssertEqual("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".sha3(.sha512), "04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e")
+
+        XCTAssertEqual("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".sha3(.sha224), "543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc")
+        XCTAssertEqual("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".sha3(.sha256), "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18")
+        XCTAssertEqual("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".sha3(.sha384), "79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7")
+        XCTAssertEqual("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".sha3(.sha512), "afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185")
+
+        XCTAssertEqual(Array<UInt8>(repeating: 0x61, count: 1000000).sha3(.sha224), Array<UInt8>(hex: "d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c"), "One million (1,000,000) repetitions of the character 'a' (0x61)")
+        XCTAssertEqual(Array<UInt8>(repeating: 0x61, count: 1000000).sha3(.sha256), Array<UInt8>(hex: "5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1"), "One million (1,000,000) repetitions of the character 'a' (0x61)")
+        XCTAssertEqual(Array<UInt8>(repeating: 0x61, count: 1000000).sha3(.sha384), Array<UInt8>(hex: "eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340"), "One million (1,000,000) repetitions of the character 'a' (0x61)")
+        XCTAssertEqual(Array<UInt8>(repeating: 0x61, count: 1000000).sha3(.sha512), Array<UInt8>(hex: "3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87"), "One million (1,000,000) repetitions of the character 'a' (0x61)")
+    }
+
     func testCRC32() {
         let data:Data = Data(bytes: UnsafePointer<UInt8>([49, 50, 51] as Array<UInt8>), count: 3)
         if let crc = data.crc32(seed: nil) {
@@ -138,6 +165,7 @@ final class DigestTests: XCTestCase {
         ("testSHA256", testSHA256),
         ("testSHA384", testSHA384),
         ("testSHA512", testSHA512),
+        ("testSHA3", testSHA3),
         ("testCRC32", testCRC32),
         ("testCRC32NotReflected", testCRC32NotReflected),
         ("testCRC15", testCRC16),