Преглед изворни кода

Add String.bytes helper for String -> Array<UInt8> conversion

Marcin Krzyżanowski пре 7 година
родитељ
комит
68d9a98dc6

+ 9 - 0
CHANGELOG

@@ -1,3 +1,12 @@
+- Adds SHA3 Keccak variants
+- Adds String.bytes helper to convert String to array of bytes
+- Improves AES performance
+- Speeds up compilation times with Swift 4
+- Fixes: Blowfish minimum key size is 5
+- Removes Ciphers "iv" parameter (value moved to BlockMode)
+- BlockMode uses associated value for IV value where apply e.g. .CBC(iv: ivbytes)
+- Refactors internal hacks no longer needed with Swift 4
+
 0.7.2
 - Adds Padding enum (.pkcs5, .pkcs7, .noPadding, .zeroPadding)
 - Removes Generics from the public API.

+ 4 - 4
README.md

@@ -186,11 +186,11 @@ also check [Playground](/CryptoSwift.playground/Contents.swift)
 import CryptoSwift
 ```
 
-CryptoSwift uses array of bytes aka `Array<UInt8>` as a base type for all operations. Every data may be converted to a stream of bytes. You will find convenience functions that accept String or NSData, and it will be internally converted to the array of bytes.
+CryptoSwift uses array of bytes aka `Array<UInt8>` as a base type for all operations. Every data may be converted to a stream of bytes. You will find convenience functions that accept `String` or `Data`, and it will be internally converted to the array of bytes.
 
 ##### Data types conversion
 
-For you convenience **CryptoSwift** provides two functions to easily convert array of bytes to NSData and another way around:
+For you convenience **CryptoSwift** provides two functions to easily convert array of bytes to `Data` and another way around:
 
 Data from bytes:
 
@@ -213,7 +213,7 @@ let hex   = bytes.toHexString()            // "010203"
 
 Build bytes out of `String`
 ```swift
-let bytes = Array("string".utf8)
+let bytes: Array<UInt8> = "password".bytes  // Array("password".utf8)
 ```
 
 Also... check out helpers that work with **Base64** encoded data:
@@ -255,7 +255,7 @@ do {
 Hashing a String and printing result
 
 ```swift
-let hash = "123".md5()
+let hash = "123".md5() // "123".bytes.md5()
 ```
 
 ##### Calculate CRC

+ 1 - 1
Sources/CryptoSwift/Foundation/AES+Foundation.swift

@@ -20,6 +20,6 @@ extension AES {
 
     /// Initialize with CBC block mode.
     public convenience init(key: String, iv: String, padding: Padding = .pkcs7) throws {
-        try self.init(key: Array(key.utf8), blockMode: .CBC(iv: Array(iv.utf8)), padding: padding)
+        try self.init(key: key.bytes, blockMode: .CBC(iv: iv.bytes), padding: padding)
     }
 }

+ 1 - 5
Sources/CryptoSwift/Foundation/Blowfish+Foundation.swift

@@ -19,10 +19,6 @@ import Foundation
 extension Blowfish {
 
     public convenience init(key: String, iv: String, blockMode: BlockMode = .CBC(iv: Array<UInt8>(repeating: 0, count: Blowfish.blockSize)), padding: Padding = .pkcs7) throws {
-        guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes, let iiv = iv.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes else {
-            throw Error.invalidKeyOrInitializationVector
-        }
-
-        try self.init(key: kkey, iv: iiv, blockMode: blockMode, padding: padding)
+        try self.init(key: key.bytes, iv: iv.bytes, blockMode: blockMode, padding: padding)
     }
 }

+ 1 - 4
Sources/CryptoSwift/Foundation/ChaCha20+Foundation.swift

@@ -19,9 +19,6 @@ import Foundation
 extension ChaCha20 {
 
     public convenience init(key: String, iv: String) throws {
-        guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes, let iiv = iv.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes else {
-            throw Error.invalidKeyOrInitializationVector
-        }
-        try self.init(key: kkey, iv: iiv)
+        try self.init(key: key.bytes, iv: iv.bytes)
     }
 }

+ 1 - 5
Sources/CryptoSwift/Foundation/HMAC+Foundation.swift

@@ -19,10 +19,6 @@ import Foundation
 extension HMAC {
 
     public convenience init(key: String, variant: HMAC.Variant = .md5) throws {
-        guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes else {
-            throw Error.invalidInput
-        }
-
-        self.init(key: kkey, variant: variant)
+        self.init(key: key.bytes, variant: variant)
     }
 }

+ 2 - 11
Sources/CryptoSwift/Foundation/Rabbit+Foundation.swift

@@ -19,19 +19,10 @@ import Foundation
 extension Rabbit {
 
     public convenience init(key: String) throws {
-        guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes else {
-            throw Error.invalidKeyOrInitializationVector
-        }
-        try self.init(key: kkey)
+        try self.init(key: key.bytes)
     }
 
     public convenience init(key: String, iv: String) throws {
-        guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes,
-            let iiv = iv.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes
-        else {
-            throw Error.invalidKeyOrInitializationVector
-        }
-
-        try self.init(key: kkey, iv: iiv)
+        try self.init(key: key.bytes, iv: iv.bytes)
     }
 }

+ 16 - 12
Sources/CryptoSwift/String+Extension.swift

@@ -17,52 +17,56 @@
 /** String extension */
 extension String {
 
+    public var bytes: Array<UInt8> {
+        return self.data(using: String.Encoding.utf8, allowLossyConversion: true)?.bytes ?? Array(self.utf8)
+    }
+
     public func md5() -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).md5().toHexString()
+        return bytes.md5().toHexString()
     }
 
     public func sha1() -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).sha1().toHexString()
+        return bytes.sha1().toHexString()
     }
 
     public func sha224() -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).sha224().toHexString()
+        return bytes.sha224().toHexString()
     }
 
     public func sha256() -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).sha256().toHexString()
+        return bytes.sha256().toHexString()
     }
 
     public func sha384() -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).sha384().toHexString()
+        return bytes.sha384().toHexString()
     }
 
     public func sha512() -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).sha512().toHexString()
+        return bytes.sha512().toHexString()
     }
 
     public func sha3(_ variant: SHA3.Variant) -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).sha3(variant).toHexString()
+        return bytes.sha3(variant).toHexString()
     }
 
     public func crc32(seed: UInt32? = nil, reflect: Bool = true) -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).crc32(seed: seed, reflect: reflect).bytes().toHexString()
+        return bytes.crc32(seed: seed, reflect: reflect).bytes().toHexString()
     }
 
     public func crc16(seed: UInt16? = nil) -> String {
-        return utf8.lazy.map({ $0 as UInt8 }).crc16(seed: seed).bytes().toHexString()
+        return bytes.crc16(seed: seed).bytes().toHexString()
     }
 
     /// - parameter cipher: Instance of `Cipher`
     /// - returns: hex string of bytes
     public func encrypt(cipher: Cipher) throws -> String {
-        return try Array(utf8).encrypt(cipher: cipher).toHexString()
+        return try bytes.encrypt(cipher: cipher).toHexString()
     }
 
     /// - parameter cipher: Instance of `Cipher`
     /// - returns: base64 encoded string of encrypted bytes
     public func encryptToBase64(cipher: Cipher) throws -> String? {
-        return try Array(utf8).encrypt(cipher: cipher).toBase64()
+        return try bytes.encrypt(cipher: cipher).toBase64()
     }
 
     // decrypt() does not make sense for String
@@ -70,6 +74,6 @@ extension String {
     /// - parameter authenticator: Instance of `Authenticator`
     /// - returns: hex string of string
     public func authenticate<A: Authenticator>(with authenticator: A) throws -> String {
-        return try Array(utf8).authenticate(with: authenticator).toHexString()
+        return try bytes.authenticate(with: authenticator).toHexString()
     }
 }

+ 18 - 14
Tests/CryptoSwiftTests/AESTests.swift

@@ -48,11 +48,15 @@ final class AESTests: XCTestCase {
         let input: Array<UInt8> = [0x62, 0x72, 0x61, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
         let expected: Array<UInt8> = [0xae, 0x8c, 0x59, 0x95, 0xb2, 0x6f, 0x8e, 0x3d, 0xb0, 0x6f, 0x0a, 0xa5, 0xfe, 0xc4, 0xf0, 0xc2]
 
-        let aes = try! AES(key: key, iv: iv, padding: .noPadding)
-        let encrypted = try! aes.encrypt(input)
-        XCTAssertEqual(encrypted, expected, "encryption failed")
-        let decrypted = try! aes.decrypt(encrypted)
-        XCTAssertEqual(decrypted, input, "decryption failed")
+        do {
+            let aes = try AES(key: key, iv: iv, padding: .noPadding)
+            let encrypted = try aes.encrypt(input)
+            XCTAssertEqual(encrypted, expected, "encryption failed")
+            let decrypted = try aes.decrypt(encrypted)
+            XCTAssertEqual(decrypted, input, "decryption failed")
+        } catch {
+            XCTFail("\(error)")
+        }
     }
 
     func testAESEncryptCBCNoPadding() {
@@ -101,12 +105,12 @@ final class AESTests: XCTestCase {
         do {
             var ciphertext = Array<UInt8>()
             let plaintext = "Today Apple launched the open source Swift community, as well as amazing new tools and resources."
-            let aes = try AES(key: Array("passwordpassword".utf8), blockMode: .CBC(iv: Array("drowssapdrowssap".utf8)))
+            let aes = try AES(key: "passwordpassword".bytes, blockMode: .CBC(iv: "drowssapdrowssap".bytes))
             var encryptor = try! aes.makeEncryptor()
 
-            ciphertext += try encryptor.update(withBytes: Array(plaintext.utf8))
+            ciphertext += try encryptor.update(withBytes: plaintext.bytes)
             ciphertext += try encryptor.finish()
-            XCTAssertEqual(try aes.encrypt(Array(plaintext.utf8)), ciphertext, "encryption failed")
+            XCTAssertEqual(try aes.encrypt(plaintext.bytes), ciphertext, "encryption failed")
         } catch {
             XCTAssert(false, "\(error)")
         }
@@ -238,7 +242,7 @@ final class AESTests: XCTestCase {
         var plaintext: Array<UInt8> = Array<UInt8>(repeating: 0, count: 6000)
 
         for i in 0..<plaintext.count / 6 {
-            let s = Array(String(format: "%05d", i).utf8)
+            let s = String(format: "%05d", i).bytes
             plaintext[i * 6 + 0] = s[0]
             plaintext[i * 6 + 1] = s[1]
             plaintext[i * 6 + 2] = s[2]
@@ -328,9 +332,9 @@ final class AESTests: XCTestCase {
 
     // https://github.com/krzyzanowskim/CryptoSwift/issues/394
     func testIssue394() {
-        let plaintext = Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8)
-        let key = Array("passwordpassword".utf8).md5() // -md md5
-        let iv = Array("drowssapdrowssap".utf8) // -iv 64726f777373617064726f7773736170
+        let plaintext = "Nullam quis risus eget urna mollis ornare vel eu leo.".bytes
+        let key = "passwordpassword".bytes.md5() // -md md5
+        let iv = "drowssapdrowssap".bytes // -iv 64726f777373617064726f7773736170
         let aes = try! AES(key: key, blockMode: .CBC(iv: iv), padding: .pkcs7) // -aes-128-cbc
         let ciphertext = try! aes.encrypt(plaintext) // enc
 
@@ -343,8 +347,8 @@ final class AESTests: XCTestCase {
     // https://github.com/krzyzanowskim/CryptoSwift/issues/411
     func testIssue411() {
         let ciphertext: Array<UInt8> = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x46, 0x58, 0x2d, 0x66, 0x52, 0x10, 0xae, 0x86, 0xd3, 0x8e, 0x8f] // test
-        let key = Array("passwordpassword".utf8).md5() // -md md5
-        let iv = Array("drowssapdrowssap".utf8) // -iv 64726f777373617064726f7773736170
+        let key = "passwordpassword".bytes.md5() // -md md5
+        let iv = "drowssapdrowssap".bytes // -iv 64726f777373617064726f7773736170
         let aes = try! AES(key: key, blockMode: .CBC(iv: iv), padding: .pkcs7) // -aes-128-cbc
         let plaintext = try! ciphertext.decrypt(cipher: aes)
         XCTAssertEqual("74657374", plaintext.toHexString())

+ 3 - 3
Tests/CryptoSwiftTests/BlowfishTests.swift

@@ -191,9 +191,9 @@ class BlowfishTests: XCTestCase {
     // https://github.com/krzyzanowskim/CryptoSwift/issues/415
     func testDecryptCFB415() {
         do {
-            let plaintext = Array("secret12".utf8)
-            let key = Array("passwordpassword".utf8)
-            let iv = Array("12345678".utf8)
+            let plaintext = "secret12".bytes
+            let key = "passwordpassword".bytes
+            let iv = "12345678".bytes
             let encrypted = try Blowfish(key: key, blockMode: .CFB(iv: iv), padding: .noPadding).encrypt(plaintext)
             let decrypted = try Blowfish(key: key, blockMode: .CFB(iv: iv), padding: .noPadding).decrypt(encrypted)
             XCTAssertEqual(plaintext, decrypted)

+ 1 - 1
Tests/CryptoSwiftTests/ExtensionsTest.swift

@@ -49,7 +49,7 @@ final class ExtensionsTest: XCTestCase {
 
     func testEmptyStringEncrypt() {
         do {
-            let cipher = try AES(key: Array("secret0key000000".utf8).md5(), blockMode: .ECB)
+            let cipher = try AES(key: "secret0key000000".bytes.md5(), blockMode: .ECB)
             let encrypted = try "".encryptToBase64(cipher: cipher)
             let decrypted = try encrypted?.decryptBase64ToString(cipher: cipher)
             XCTAssertEqual("", decrypted)

+ 4 - 4
Tests/CryptoSwiftTests/PBKDF.swift

@@ -46,16 +46,16 @@ class PBKDF: XCTestCase {
     }
 
     func testPBKDF2Length() {
-        let password: Array<UInt8> = Array("s33krit".utf8)
-        let salt: Array<UInt8> = Array("nacl".utf8)
+        let password: Array<UInt8> = "s33krit".bytes
+        let salt: Array<UInt8> = "nacl".bytes
         let value = try! PKCS5.PBKDF2(password: password, salt: salt, iterations: 2, keyLength: 8, variant: .sha1).calculate()
         XCTAssert(value.toHexString() == "a53cf3df485e5cd9", "PBKDF2 fail")
     }
 
     #if !CI
         func testPerformance() {
-            let password: Array<UInt8> = Array("s33krit".utf8)
-            let salt: Array<UInt8> = Array("nacl".utf8)
+            let password: Array<UInt8> = "s33krit".bytes
+            let salt: Array<UInt8> = "nacl".bytes
             measureMetrics([XCTPerformanceMetric.wallClockTime], automaticallyStartMeasuring: true, for: { () -> Void in
                 _ = try! PKCS5.PBKDF2(password: password, salt: salt, iterations: 65536, keyLength: 32, variant: .sha1).calculate()
             })