Bladeren bron

clean branch for scrypt pull-request

Alex Vlasov 6 jaren geleden
bovenliggende
commit
2ed3b857a5

+ 14 - 0
CryptoSwift.xcodeproj/project.pbxproj

@@ -118,6 +118,10 @@
 		75EC52B91EE8B83D0048EB3B /* ZeroPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75EC52791EE8B6CA0048EB3B /* ZeroPadding.swift */; };
 		75F4E434216C93EF00F09710 /* CCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75F4E433216C93EF00F09710 /* CCM.swift */; };
 		75F4E436216C98DE00F09710 /* CBCMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75F4E435216C98DE00F09710 /* CBCMAC.swift */; };
+		81F279DD2181F58300449EDA /* Scrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F279DC2181F58300449EDA /* Scrypt.swift */; };
+		81F279DF2181F5A000449EDA /* ScryptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F279DE2181F5A000449EDA /* ScryptTests.swift */; };
+		81F279E12181F5C500449EDA /* ScryptTestsPerf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F279E02181F5C500449EDA /* ScryptTestsPerf.swift */; };
+		81F279E22181F5C500449EDA /* ScryptTestsPerf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F279E02181F5C500449EDA /* ScryptTestsPerf.swift */; };
 		E3FD2D531D6B81CE00A9F35F /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3FD2D511D6B813C00A9F35F /* Error+Extension.swift */; };
 		E6200E141FB9A7AE00258382 /* HKDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6200E131FB9A7AE00258382 /* HKDF.swift */; };
 		E6200E171FB9B68C00258382 /* HKDFTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6200E151FB9B67C00258382 /* HKDFTests.swift */; };
@@ -363,6 +367,9 @@
 		75F4E435216C98DE00F09710 /* CBCMAC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBCMAC.swift; sourceTree = "<group>"; };
 		75F4E437216C9B5D00F09710 /* CHANGELOG */ = {isa = PBXFileReference; lastKnownFileType = text; path = CHANGELOG; sourceTree = SOURCE_ROOT; };
 		75F4E438216C9B6900F09710 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
+		81F279DC2181F58300449EDA /* Scrypt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scrypt.swift; sourceTree = "<group>"; };
+		81F279DE2181F5A000449EDA /* ScryptTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScryptTests.swift; sourceTree = "<group>"; };
+		81F279E02181F5C500449EDA /* ScryptTestsPerf.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScryptTestsPerf.swift; sourceTree = "<group>"; };
 		E3FD2D511D6B813C00A9F35F /* Error+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Error+Extension.swift"; sourceTree = "<group>"; };
 		E6200E131FB9A7AE00258382 /* HKDF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HKDF.swift; sourceTree = "<group>"; };
 		E6200E151FB9B67C00258382 /* HKDFTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKDFTests.swift; sourceTree = "<group>"; };
@@ -466,6 +473,7 @@
 		754BE46419693E190098E6F3 /* Tests */ = {
 			isa = PBXGroup;
 			children = (
+				81F279DE2181F5A000449EDA /* ScryptTests.swift */,
 				14156CE42011422400DDCFBC /* ChaCha20Poly1305Tests.swift */,
 				E3FD2D511D6B813C00A9F35F /* Error+Extension.swift */,
 				754BE46719693E190098E6F3 /* DigestTests.swift */,
@@ -491,6 +499,7 @@
 				75C2E76C1D55F097003D2BCA /* Access.swift */,
 				756BFDCA1A82B87300B9D9A4 /* Bridging.h */,
 				754BE46519693E190098E6F3 /* Supporting Files */,
+				81F279E02181F5C500449EDA /* ScryptTestsPerf.swift */,
 			);
 			name = Tests;
 			path = Tests/Tests;
@@ -587,6 +596,7 @@
 				75EC526C1EE8B6CA0048EB3B /* Cryptor.swift */,
 				75EC526D1EE8B6CA0048EB3B /* RandomBytesSequence.swift */,
 				75EC526E1EE8B6CA0048EB3B /* SecureBytes.swift */,
+				81F279DC2181F58300449EDA /* Scrypt.swift */,
 				75EC526F1EE8B6CA0048EB3B /* SHA1.swift */,
 				75EC52701EE8B6CA0048EB3B /* SHA2.swift */,
 				75EC52711EE8B6CA0048EB3B /* SHA3.swift */,
@@ -953,6 +963,7 @@
 				75EC52981EE8B8200048EB3B /* Array+Foundation.swift in Sources */,
 				75EC52B11EE8B83D0048EB3B /* SHA3.swift in Sources */,
 				75EC52A31EE8B8290048EB3B /* NoPadding.swift in Sources */,
+				81F279DD2181F58300449EDA /* Scrypt.swift in Sources */,
 				75EC52931EE8B81A0048EB3B /* Digest.swift in Sources */,
 				75EC52AD1EE8B83D0048EB3B /* RandomBytesSequence.swift in Sources */,
 			);
@@ -972,6 +983,7 @@
 				E3FD2D531D6B81CE00A9F35F /* Error+Extension.swift in Sources */,
 				757DA2591A4ED4D7002BA3EF /* ChaCha20Tests.swift in Sources */,
 				755FB1DA199E347D00475437 /* ExtensionsTest.swift in Sources */,
+				81F279DF2181F5A000449EDA /* ScryptTests.swift in Sources */,
 				674A736F1BF5D85B00866C5B /* RabbitTests.swift in Sources */,
 				0EE73E74204D59C200110E11 /* CMACTests.swift in Sources */,
 				750CC3EB1DC0CACE0096BE6E /* BlowfishTests.swift in Sources */,
@@ -991,6 +1003,7 @@
 				7564F0542072EAEB00CA5A96 /* RabbitTestsPerf.swift in Sources */,
 				7564F0552072EAEB00CA5A96 /* ExtensionsTestPerf.swift in Sources */,
 				7564F0562072EAEB00CA5A96 /* DigestTestsPerf.swift in Sources */,
+				81F279E22181F5C500449EDA /* ScryptTestsPerf.swift in Sources */,
 				7564F0572072EAEB00CA5A96 /* TestsPerformance.swift in Sources */,
 				7564F0582072EAEB00CA5A96 /* AESTestsPerf.swift in Sources */,
 			);
@@ -1005,6 +1018,7 @@
 				7595C15B2072E5B900EA1A5F /* RabbitTestsPerf.swift in Sources */,
 				7595C15C2072E5B900EA1A5F /* ExtensionsTestPerf.swift in Sources */,
 				7595C1582072E5B900EA1A5F /* DigestTestsPerf.swift in Sources */,
+				81F279E12181F5C500449EDA /* ScryptTestsPerf.swift in Sources */,
 				7595C14D2072E48C00EA1A5F /* TestsPerformance.swift in Sources */,
 				7595C1592072E5B900EA1A5F /* AESTestsPerf.swift in Sources */,
 			);

+ 252 - 0
Sources/CryptoSwift/Scrypt.swift

@@ -0,0 +1,252 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2017 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+//
+// https://tools.ietf.org/html/rfc7914
+//
+
+/// Implementation of the scrypt key derivation function.
+public final class Scrypt {
+    enum Error: Swift.Error {
+        case nIsTooLarge
+        case rIsTooLarge
+        case nMustBeAPowerOf2GreaterThan1
+        case invalidInput
+    }
+
+    /// Configuration parameters.
+    private let salt: Array<UInt8>
+    private let password: Array<UInt8>
+    fileprivate let blocksize: Int // 128 * r
+    fileprivate let salsaBlock = UnsafeMutableRawPointer.allocate(byteCount: 64, alignment: 64)
+    private let dkLen: Int
+    private let N: Int
+    private let r: Int
+    private let p: Int
+
+    /// - parameters:
+    ///   - password: password
+    ///   - salt: salt
+    ///   - dkLen: output length
+    ///   - N: determines extra memory used
+    ///   - r: determines a block size
+    ///   - p: determines parallelicity degree
+    public init(password: Array<UInt8>, salt: Array<UInt8>, dkLen: Int, N: Int, r: Int, p: Int) throws {
+        precondition(dkLen > 0)
+        precondition(N > 0)
+        precondition(r > 0)
+        precondition(p > 0)
+
+        guard !(N < 2 || (N & (N - 1)) != 0) else { throw Error.nMustBeAPowerOf2GreaterThan1 }
+
+        guard N <= .max / 128 / r else { throw Error.nIsTooLarge }
+        guard r <= .max / 128 / p else { throw Error.rIsTooLarge }
+
+        guard !salt.isEmpty else {
+            throw Error.invalidInput
+        }
+
+        blocksize = 128 * r
+        self.N = N
+        self.r = r
+        self.p = p
+        self.password = password
+        self.salt = salt
+        self.dkLen = dkLen
+    }
+
+    /// Runs the key derivation function with a specific password.
+    public func calculate() throws -> [UInt8] {
+        // Allocate memory (as bytes for now) for further use in mixing steps
+        let B = UnsafeMutableRawPointer.allocate(byteCount: 128 * r * p, alignment: 64)
+        let XY = UnsafeMutableRawPointer.allocate(byteCount: 256 * r + 64, alignment: 64)
+        let V = UnsafeMutableRawPointer.allocate(byteCount: 128 * r * N, alignment: 64)
+
+        // Deallocate memory when done
+        defer {
+            B.deallocate()
+            XY.deallocate()
+            V.deallocate()
+        }
+
+        /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
+        // Expand the initial key
+        let barray = try PKCS5.PBKDF2(password: password, salt: salt, iterations: 1, keyLength: p * 128 * r, variant: .sha256).calculate()
+        barray.withUnsafeBytes { p in
+            B.copyMemory(from: p.baseAddress!, byteCount: barray.count)
+        }
+
+        /* 2: for i = 0 to p - 1 do */
+        // do the mixing
+        for i in 0 ..< p {
+            /* 3: B_i <-- MF(B_i, N) */
+            smix(B + i * 128 * r, V.assumingMemoryBound(to: UInt32.self), XY.assumingMemoryBound(to: UInt32.self))
+        }
+
+        /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
+        let pointer = B.assumingMemoryBound(to: UInt8.self)
+        let bufferPointer = UnsafeBufferPointer(start: pointer, count: p * 128 * r)
+        let block = [UInt8](bufferPointer)
+        return try PKCS5.PBKDF2(password: password, salt: block, iterations: 1, keyLength: dkLen, variant: .sha256).calculate()
+    }
+}
+
+fileprivate extension Scrypt {
+    /// Computes `B = SMix_r(B, N)`.
+    ///
+    /// The input `block` must be `128*r` bytes in length; the temporary storage `v` must be `128*r*n` bytes in length;
+    /// the temporary storage `xy` must be `256*r + 64` bytes in length. The arrays `block`, `v`, and `xy` must be
+    /// aligned to a multiple of 64 bytes.
+    @inline(__always) func smix(_ block: UnsafeMutableRawPointer, _ v: UnsafeMutablePointer<UInt32>, _ xy: UnsafeMutablePointer<UInt32>) {
+        let X = xy
+        let Y = xy + 32 * r
+        let Z = xy + 64 * r
+
+        /* 1: X <-- B */
+        let typedBlock = block.assumingMemoryBound(to: UInt32.self)
+        X.assign(from: typedBlock, count: 32 * r)
+
+        /* 2: for i = 0 to N - 1 do */
+        for i in stride(from: 0, to: N, by: 2) {
+            /* 3: V_i <-- X */
+            UnsafeMutableRawPointer(v + i * (32 * r)).copyMemory(from: X, byteCount: 128 * r)
+
+            /* 4: X <-- H(X) */
+            blockMixSalsa8(X, Y, Z)
+
+            /* 3: V_i <-- X */
+            UnsafeMutableRawPointer(v + (i + 1) * (32 * r)).copyMemory(from: Y, byteCount: 128 * r)
+
+            /* 4: X <-- H(X) */
+            blockMixSalsa8(Y, X, Z)
+        }
+
+        /* 6: for i = 0 to N - 1 do */
+        for _ in stride(from: 0, to: N, by: 2) {
+            /* 7: j <-- Integerify(X) mod N */
+            var j = Int(integerify(X) & UInt64(N - 1))
+
+            /* 8: X <-- H(X \xor V_j) */
+            blockXor(X, v + j * 32 * r, 128 * r)
+            blockMixSalsa8(X, Y, Z)
+
+            /* 7: j <-- Integerify(X) mod N */
+            j = Int(integerify(Y) & UInt64(N - 1))
+
+            /* 8: X <-- H(X \xor V_j) */
+            blockXor(Y, v + j * 32 * r, 128 * r)
+            blockMixSalsa8(Y, X, Z)
+        }
+
+        /* 10: B' <-- X */
+        for k in 0 ..< 32 * r {
+            UnsafeMutableRawPointer(block + 4 * k).storeBytes(of: X[k], as: UInt32.self)
+        }
+    }
+
+    /// Returns the result of parsing `B_{2r-1}` as a little-endian integer.
+    @inline(__always) func integerify(_ block: UnsafeRawPointer) -> UInt64 {
+        let bi = block + (2 * r - 1) * 64
+        return bi.load(as: UInt64.self).littleEndian
+    }
+
+    /// Compute `bout = BlockMix_{salsa20/8, r}(bin)`.
+    ///
+    /// The input `bin` must be `128*r` bytes in length; the output `bout` must also be the same size. The temporary
+    /// space `x` must be 64 bytes.
+    @inline(__always) func blockMixSalsa8(_ bin: UnsafePointer<UInt32>, _ bout: UnsafeMutablePointer<UInt32>, _ x: UnsafeMutablePointer<UInt32>) {
+        /* 1: X <-- B_{2r - 1} */
+        UnsafeMutableRawPointer(x).copyMemory(from: bin + (2 * r - 1) * 16, byteCount: 64)
+
+        /* 2: for i = 0 to 2r - 1 do */
+        for i in stride(from: 0, to: 2 * r, by: 2) {
+            /* 3: X <-- H(X \xor B_i) */
+            blockXor(x, bin + i * 16, 64)
+            salsa20_8_typed(x)
+
+            /* 4: Y_i <-- X */
+            /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+            UnsafeMutableRawPointer(bout + i * 8).copyMemory(from: x, byteCount: 64)
+
+            /* 3: X <-- H(X \xor B_i) */
+            blockXor(x, bin + i * 16 + 16, 64)
+            salsa20_8_typed(x)
+
+            /* 4: Y_i <-- X */
+            /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+            UnsafeMutableRawPointer(bout + i * 8 + r * 16).copyMemory(from: x, byteCount: 64)
+        }
+    }
+
+    @inline(__always) func salsa20_8_typed(_ block: UnsafeMutablePointer<UInt32>) {
+        salsaBlock.copyMemory(from: UnsafeRawPointer(block), byteCount: 64)
+        let salsaBlockTyped = salsaBlock.assumingMemoryBound(to: UInt32.self)
+
+        for _ in stride(from: 0, to: 8, by: 2) {
+            salsaBlockTyped[4] ^= rotateLeft(salsaBlockTyped[0] &+ salsaBlockTyped[12], by: 7)
+            salsaBlockTyped[8] ^= rotateLeft(salsaBlockTyped[4] &+ salsaBlockTyped[0], by: 9)
+            salsaBlockTyped[12] ^= rotateLeft(salsaBlockTyped[8] &+ salsaBlockTyped[4], by: 13)
+            salsaBlockTyped[0] ^= rotateLeft(salsaBlockTyped[12] &+ salsaBlockTyped[8], by: 18)
+
+            salsaBlockTyped[9] ^= rotateLeft(salsaBlockTyped[5] &+ salsaBlockTyped[1], by: 7)
+            salsaBlockTyped[13] ^= rotateLeft(salsaBlockTyped[9] &+ salsaBlockTyped[5], by: 9)
+            salsaBlockTyped[1] ^= rotateLeft(salsaBlockTyped[13] &+ salsaBlockTyped[9], by: 13)
+            salsaBlockTyped[5] ^= rotateLeft(salsaBlockTyped[1] &+ salsaBlockTyped[13], by: 18)
+
+            salsaBlockTyped[14] ^= rotateLeft(salsaBlockTyped[10] &+ salsaBlockTyped[6], by: 7)
+            salsaBlockTyped[2] ^= rotateLeft(salsaBlockTyped[14] &+ salsaBlockTyped[10], by: 9)
+            salsaBlockTyped[6] ^= rotateLeft(salsaBlockTyped[2] &+ salsaBlockTyped[14], by: 13)
+            salsaBlockTyped[10] ^= rotateLeft(salsaBlockTyped[6] &+ salsaBlockTyped[2], by: 18)
+
+            salsaBlockTyped[3] ^= rotateLeft(salsaBlockTyped[15] &+ salsaBlockTyped[11], by: 7)
+            salsaBlockTyped[7] ^= rotateLeft(salsaBlockTyped[3] &+ salsaBlockTyped[15], by: 9)
+            salsaBlockTyped[11] ^= rotateLeft(salsaBlockTyped[7] &+ salsaBlockTyped[3], by: 13)
+            salsaBlockTyped[15] ^= rotateLeft(salsaBlockTyped[11] &+ salsaBlockTyped[7], by: 18)
+
+            salsaBlockTyped[1] ^= rotateLeft(salsaBlockTyped[0] &+ salsaBlockTyped[3], by: 7)
+            salsaBlockTyped[2] ^= rotateLeft(salsaBlockTyped[1] &+ salsaBlockTyped[0], by: 9)
+            salsaBlockTyped[3] ^= rotateLeft(salsaBlockTyped[2] &+ salsaBlockTyped[1], by: 13)
+            salsaBlockTyped[0] ^= rotateLeft(salsaBlockTyped[3] &+ salsaBlockTyped[2], by: 18)
+
+            salsaBlockTyped[6] ^= rotateLeft(salsaBlockTyped[5] &+ salsaBlockTyped[4], by: 7)
+            salsaBlockTyped[7] ^= rotateLeft(salsaBlockTyped[6] &+ salsaBlockTyped[5], by: 9)
+            salsaBlockTyped[4] ^= rotateLeft(salsaBlockTyped[7] &+ salsaBlockTyped[6], by: 13)
+            salsaBlockTyped[5] ^= rotateLeft(salsaBlockTyped[4] &+ salsaBlockTyped[7], by: 18)
+
+            salsaBlockTyped[11] ^= rotateLeft(salsaBlockTyped[10] &+ salsaBlockTyped[9], by: 7)
+            salsaBlockTyped[8] ^= rotateLeft(salsaBlockTyped[11] &+ salsaBlockTyped[10], by: 9)
+            salsaBlockTyped[9] ^= rotateLeft(salsaBlockTyped[8] &+ salsaBlockTyped[11], by: 13)
+            salsaBlockTyped[10] ^= rotateLeft(salsaBlockTyped[9] &+ salsaBlockTyped[8], by: 18)
+
+            salsaBlockTyped[12] ^= rotateLeft(salsaBlockTyped[15] &+ salsaBlockTyped[14], by: 7)
+            salsaBlockTyped[13] ^= rotateLeft(salsaBlockTyped[12] &+ salsaBlockTyped[15], by: 9)
+            salsaBlockTyped[14] ^= rotateLeft(salsaBlockTyped[13] &+ salsaBlockTyped[12], by: 13)
+            salsaBlockTyped[15] ^= rotateLeft(salsaBlockTyped[14] &+ salsaBlockTyped[13], by: 18)
+        }
+        for i in 0 ..< 16 {
+            block[i] = block[i] &+ salsaBlockTyped[i]
+        }
+    }
+
+    @inline(__always) fileprivate func blockXor(_ dest: UnsafeMutableRawPointer, _ src: UnsafeRawPointer, _ len: Int) {
+        let D = dest.assumingMemoryBound(to: UInt64.self)
+        let S = src.assumingMemoryBound(to: UInt64.self)
+        let L = len / MemoryLayout<UInt64>.size
+
+        for i in 0 ..< L {
+            D[i] ^= S[i]
+        }
+    }
+}

+ 69 - 0
Tests/Tests/ScryptTests.swift

@@ -0,0 +1,69 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2017 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+@testable import CryptoSwift
+import XCTest
+
+class Scrypt: XCTestCase {
+    
+    func testScrypt_0() {
+        let password = Array("password".data(using: .ascii)!)
+        let salt = Array("NaCl".data(using: .ascii)!)
+        let deriver = try! CryptoSwift.Scrypt(password: password, salt: salt, dkLen: 64, N: 1024, r: 8, p: 16)
+        let derived = try! deriver.calculate()
+        let expected: [UInt8] = Array<UInt8>.init(hex: """
+                fd ba be 1c 9d 34 72 00 78 56 e7 19 0d 01 e9 fe
+                7c 6a d7 cb c8 23 78 30 e7 73 76 63 4b 37 31 62
+                2e af 30 d9 2e 22 a3 88 6f f1 09 27 9d 98 30 da
+                c7 27 af b9 4a 83 ee 6d 83 60 cb df a2 cc 06 40
+""".replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: "\t", with: ""))
+        XCTAssertEqual(derived, expected)
+    }
+    
+    func testScrypt_1() {
+        let password = Array("pleaseletmein".data(using: .ascii)!)
+        let salt = Array("SodiumChloride".data(using: .ascii)!)
+        let deriver = try! CryptoSwift.Scrypt(password: password, salt: salt, dkLen: 64, N: 16384, r: 8, p: 1)
+        let derived = try! deriver.calculate()
+        let expected: [UInt8] = Array<UInt8>.init(hex: """
+                70 23 bd cb 3a fd 73 48 46 1c 06 cd 81 fd 38 eb
+                fd a8 fb ba 90 4f 8e 3e a9 b5 43 f6 54 5d a1 f2
+                d5 43 29 55 61 3f 0f cf 62 d4 97 05 24 2a 9a f9
+                e6 1e 85 dc 0d 65 1e 40 df cf 01 7b 45 57 58 87
+""".replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: "\t", with: ""))
+        XCTAssertEqual(derived, expected)
+    }
+    
+    //      Takes too long to run in debug mode!
+    //    func testScrypt_2() {
+    //        let password = Array("pleaseletmein".data(using: .ascii)!)
+    //        let salt = Array("SodiumChloride".data(using: .ascii)!)
+    //        let deriver = try! CryptoSwift.Scrypt(password: password, salt: salt, dkLen: 64, N: 1048576, r: 8, p: 16)
+    //        let derived = try! deriver.calculate()
+    //        let expected: [UInt8] = Array<UInt8>.init(hex: """
+    //                21 01 cb 9b 6a 51 1a ae ad db be 09 cf 70 f8 81
+    //                ec 56 8d 57 4a 2f fd 4d ab e5 ee 98 20 ad aa 47
+    //                8e 56 fd 8f 4b a5 d0 9f fa 1c 6d 92 7c 40 f4 c3
+    //                37 30 40 49 e8 a9 52 fb cb f4 5c 6f a7 7a 41 a4
+    //""".replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: "\t", with: ""))
+    //        XCTAssertEqual(derived, expected)
+    //    }
+    
+    static let allTests = [
+        ("testScrypt_0", testScrypt_0),
+        ("testScrypt_1", testScrypt_1),
+        //        ("testScrypt_2", testScrypt_2)
+    ]
+}

+ 39 - 0
Tests/Tests/ScryptTestsPerf.swift

@@ -0,0 +1,39 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2017 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+
+@testable import CryptoSwift
+import XCTest
+
+class ScryptTestsPeft: XCTestCase {
+    func testScryptPerformance() {
+        let N = 16384
+        let password: Array<UInt8> = Array<UInt8>(repeating: 0, count: 32)
+        let salt: Array<UInt8> = Array<UInt8>(repeating: 0, count: 32)
+        measure {
+            _ = try! CryptoSwift.Scrypt(password: password, salt: salt, dkLen: 64, N: N, r: 8, p: 1).calculate()
+        }
+    }
+    
+}
+
+extension ScryptTestsPeft {
+    static func allTests() -> [(String, (ScryptTestsPeft) -> () -> Void)] {
+        let tests = [
+            ("testScryptPerformance", testScryptPerformance),
+            ]
+        return tests
+    }
+}