diff --git a/BlueCryptor.podspec b/BlueCryptor.podspec new file mode 100644 index 0000000..594bce3 --- /dev/null +++ b/BlueCryptor.podspec @@ -0,0 +1,20 @@ +Pod::Spec.new do |s| +s.name = "BlueCryptor" +s.version = "1.0.23" +s.summary = "Swift cross-platform crypto library using CommonCrypto/libcrypto via Package Manager." +s.homepage = "https://github.com/IBM-Swift/BlueCryptor" +s.license = { :type => "Apache License, Version 2.0" } +s.author = "IBM" +s.module_name = 'Cryptor' + +s.requires_arc = true +s.ios.deployment_target = "8.0" +s.osx.deployment_target = "10.10" +s.tvos.deployment_target = "9.0" +s.watchos.deployment_target = "2.0" +s.source = { :git => "https://github.com/v57/BlueCryptor.git", :tag => s.version } +s.source_files = "Sources/Cryptor/*.swift" +s.pod_target_xcconfig = { +'SWIFT_VERSION' => '4.2', +} +end diff --git a/Cartfile b/Cartfile index 7176f40..1751663 100644 --- a/Cartfile +++ b/Cartfile @@ -2,4 +2,3 @@ github "mxcl/PromiseKit" ~> 6.0 github "attaswift/BigInt" ~> 3.1 github "krzyzanowskim/CryptoSwift" github "Boilertalk/secp256k1.swift" -github "v57/BlueCryptor" ~> 1.0 diff --git a/Package.swift b/Package.swift index 1af2d35..71fefc6 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,6 @@ let package = Package( .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "0.12.0"), .package(url: "https://github.com/Boilertalk/secp256k1.swift.git", from: "0.1.1"), .package(url: "https://github.com/mxcl/PromiseKit.git", from: "6.4.0"), - .package(url: "https://github.com/v57/BlueCryptor.git", from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. diff --git a/Sources/Convenience/CryptoExtensions.swift b/Sources/Convenience/CryptoExtensions.swift index 45af02a..65596be 100644 --- a/Sources/Convenience/CryptoExtensions.swift +++ b/Sources/Convenience/CryptoExtensions.swift @@ -6,7 +6,7 @@ // Copyright © 2017 Alexander Vlasov. All rights reserved. // -import Cryptor +//import Cryptor import Foundation /** diff --git a/Sources/Convenience/Data+Extension.swift b/Sources/Convenience/Data+Extension.swift index b7f5c25..d114bb6 100644 --- a/Sources/Convenience/Data+Extension.swift +++ b/Sources/Convenience/Data+Extension.swift @@ -8,7 +8,6 @@ import Foundation import CryptoSwift -import Cryptor /// Data errors diff --git a/Sources/Convenience/LibSecp256k1Extension.swift b/Sources/Convenience/LibSecp256k1Extension.swift index 2c66dae..d0df5e7 100644 --- a/Sources/Convenience/LibSecp256k1Extension.swift +++ b/Sources/Convenience/LibSecp256k1Extension.swift @@ -222,7 +222,7 @@ struct SECP256K1 { try privateKey.checkPrivateKeySize() var publicKey = secp256k1_pubkey() let result = privateKey.withUnsafeBytes { (privateKeyPointer: UnsafePointer) in - secp256k1_ec_pubkey_create(context!, UnsafeMutablePointer(&publicKey), privateKeyPointer) + secp256k1_ec_pubkey_create(context!, &publicKey, privateKeyPointer) } guard result != 0 else { throw SECP256DataError.cannotExtractPublicKeyFromPrivateKey } return publicKey @@ -248,7 +248,7 @@ struct SECP256K1 { let keyLen: Int = Int(serializedKey.count) var publicKey = secp256k1_pubkey() let result = serializedKey.withUnsafeBytes { (serializedKeyPointer: UnsafePointer) in - secp256k1_ec_pubkey_parse(context!, UnsafeMutablePointer(&publicKey), serializedKeyPointer, keyLen) + secp256k1_ec_pubkey_parse(context!, &publicKey, serializedKeyPointer, keyLen) } guard result != 0 else { throw SECP256DataError.cannotParsePublicKey } return publicKey diff --git a/Sources/Encryption/Cryptor/Crypto.swift b/Sources/Encryption/Cryptor/Crypto.swift new file mode 100755 index 0000000..90c536f --- /dev/null +++ b/Sources/Encryption/Cryptor/Crypto.swift @@ -0,0 +1,119 @@ +// +// Crypto.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// Implements a simplified API for calculating digests over single buffers +/// +public protocol CryptoDigest { + + /// Calculates a message digest + func digest(using algorithm: Digest.Algorithm) -> Self +} + +/// +/// Extension to the CryptoDigest to return the digest appropriate to the selected algorithm. +/// +extension CryptoDigest { + + /// An MD2 digest of this object + public var md2: Self { + return self.digest(using: .md2) + } + + /// An MD4 digest of this object + public var md4: Self { + return self.digest(using: .md4) + } + + /// An MD5 digest of this object + public var md5: Self { + return self.digest(using: .md5) + } + + /// An SHA1 digest of this object + public var sha1: Self { + return self.digest(using: .sha1) + } + + /// An SHA224 digest of this object + public var sha224: Self { + return self.digest(using: .sha224) + } + + /// An SHA256 digest of this object + public var sha256: Self { + return self.digest(using: .sha256) + } + + /// An SHA384 digest of this object + public var sha384: Self { + return self.digest(using: .sha384) + } + + /// An SHA512 digest of this object + public var sha512: Self { + return self.digest(using: .sha512) + } +} + +/// +/// Extension for Data to return an Data object containing the digest. +/// +extension Data: CryptoDigest { + /// + /// Calculates the Message Digest for this data. + /// + /// - Parameter algorithm: The digest algorithm to use + /// + /// - Returns: An `Data` object containing the message digest + /// + public func digest(using algorithm: Digest.Algorithm) -> Data { + + // This force unwrap may look scary but for CommonCrypto this cannot fail. + // The API allows for optionals to support the OpenSSL implementation which can. + return self.withUnsafeBytes() { (buffer: UnsafePointer) -> Data in + + let result = (Digest(using: algorithm).update(from: buffer, byteCount: self.count)?.final())! + let data = type(of: self).init(bytes: result, count: result.count) + return data + } + } +} + +/// +/// Extension for String to return a String containing the digest. +/// +extension String: CryptoDigest { + /// + /// Calculates the Message Digest for this string. + /// The string is converted to raw data using UTF8. + /// + /// - Parameter algorithm: The digest algorithm to use + /// + /// - Returns: A hex string of the calculated digest + /// + public func digest(using algorithm: Digest.Algorithm) -> String { + + // This force unwrap may look scary but for CommonCrypto this cannot fail. + // The API allows for optionals to support the OpenSSL implementation which can. + let result = (Digest(using: algorithm).update(string: self as String)?.final())! + return CryptoUtils.hexString(from: result) + + } +} diff --git a/Sources/Encryption/Cryptor/Cryptor.swift b/Sources/Encryption/Cryptor/Cryptor.swift new file mode 100755 index 0000000..1f3bf25 --- /dev/null +++ b/Sources/Encryption/Cryptor/Cryptor.swift @@ -0,0 +1,71 @@ +// +// Cryptor.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// Encrypts or decrypts, accumulating result. +/// +/// Useful for small in-memory buffers. +/// +/// For large files or network streams use StreamCryptor. +/// +public class Cryptor: StreamCryptor, Updatable { + + /// Internal accumulator for gathering data from the update() and final() functions. + var accumulator: [UInt8] = [] + + /// + /// Retrieves the encrypted or decrypted data. + /// + ///- Returns: the encrypted or decrypted data or nil if an error occured. + /// + public func final() -> [UInt8]? { + + let byteCount = Int(self.getOutputLength(inputByteCount: 0, isFinal: true)) + var dataOut = Array(repeating: 0, count:byteCount) + var dataOutMoved = 0 + (dataOutMoved, self.status) = final(byteArrayOut: &dataOut) + if self.status != .success { + return nil + } + accumulator += dataOut[0.. Self? { + + let outputLength = Int(self.getOutputLength(inputByteCount: byteCount, isFinal: false)) + var dataOut = Array(repeating: 0, count:outputLength) + var dataOutMoved = 0 + _ = update(bufferIn: buffer, byteCountIn: byteCount, bufferOut: &dataOut, byteCapacityOut: dataOut.count, byteCountOut: &dataOutMoved) + if self.status != .success { + return nil + } + accumulator += dataOut[0..(initializer:CC_MD2_Init, updater:CC_MD2_Update, finalizer:CC_MD2_Final, length:CC_MD2_DIGEST_LENGTH) + #elseif os(Linux) + fatalError("MD2 digest not supported by OpenSSL") + #endif + + case .md4: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_MD4_Init, updater:CC_MD4_Update, finalizer:CC_MD4_Final, length:CC_MD4_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:MD4_Init, updater:MD4_Update, finalizer:MD4_Final, length:MD4_DIGEST_LENGTH) + #endif + + case .md5: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_MD5_Init, updater:CC_MD5_Update, finalizer:CC_MD5_Final, length:CC_MD5_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:MD5_Init, updater:MD5_Update, finalizer:MD5_Final, length:MD5_DIGEST_LENGTH) + #endif + + case .sha1: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA1_Init, updater:CC_SHA1_Update, finalizer:CC_SHA1_Final, length:CC_SHA1_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:SHA1_Init, updater:SHA1_Update, finalizer:SHA1_Final, length:SHA_DIGEST_LENGTH) + #endif + + case .sha224: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA224_Init, updater:CC_SHA224_Update, finalizer:CC_SHA224_Final, length:CC_SHA224_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:SHA224_Init, updater:SHA224_Update, finalizer:SHA224_Final, length:SHA224_DIGEST_LENGTH) + #endif + + case .sha256: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA256_Init, updater:CC_SHA256_Update, finalizer:CC_SHA256_Final, length:CC_SHA256_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer: SHA256_Init, updater:SHA256_Update, finalizer:SHA256_Final, length:SHA256_DIGEST_LENGTH) + #endif + + case .sha384: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA384_Init, updater:CC_SHA384_Update, finalizer:CC_SHA384_Final, length:CC_SHA384_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:SHA384_Init, updater:SHA384_Update, finalizer:SHA384_Final, length:SHA384_DIGEST_LENGTH) + #endif + + case .sha512: + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + engine = DigestEngineCC(initializer:CC_SHA512_Init, updater:CC_SHA512_Update, finalizer:CC_SHA512_Final, length:CC_SHA512_DIGEST_LENGTH) + #elseif os(Linux) + engine = DigestEngineCC(initializer:SHA512_Init, updater:SHA512_Update, finalizer:SHA512_Final, length:SHA512_DIGEST_LENGTH) + #endif + } + } + + /// + /// Low-level update routine. Updates the message digest calculation with + /// the contents of a byte buffer. + /// + /// - Parameters: + /// - buffer: The buffer + /// - byteCount: Number of bytes in buffer + /// + /// - Returns: This Digest object (for optional chaining) + /// + public func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self? { + + engine.update(buffer: buffer, byteCount: CC_LONG(byteCount)) + return self + } + + /// + /// Completes the calculate of the messge digest + /// + /// - Returns: The message digest + /// + public func final() -> [UInt8] { + + return engine.final() + } +} + +// MARK: Internal Classes + +/// +/// Defines the interface between the Digest class and an +/// algorithm specific DigestEngine +/// +private protocol DigestEngine { + + /// + /// Update method + /// + /// - Parameters: + /// - buffer: The buffer to add. + /// - byteCount: The length of the buffer. + /// + func update(buffer: UnsafeRawPointer, byteCount: CC_LONG) + + /// + /// Finalizer routine + /// + /// - Returns: Byte array containing the digest. + /// + func final() -> [UInt8] +} + +/// +/// Wraps the underlying algorithm specific structures and calls +/// in a generic interface. +/// +/// - Parameter CTX: The context for the digest. +/// +private class DigestEngineCC: DigestEngine { + + typealias Context = UnsafeMutablePointer + typealias Buffer = UnsafeRawPointer + typealias Digest = UnsafeMutablePointer + typealias Initializer = (Context) -> (Int32) + typealias Updater = (Context, Buffer, CC_LONG) -> (Int32) + typealias Finalizer = (Digest, Context) -> (Int32) + + let context = Context.allocate(capacity: 1) + var initializer: Initializer + var updater: Updater + var finalizer: Finalizer + var length: Int32 + + /// + /// Default initializer + /// + /// - Parameters: + /// - initializer: The digest initializer routine. + /// - updater: The digest updater routine. + /// - finalizer: The digest finalizer routine. + /// - length: The digest length. + /// + init(initializer: @escaping Initializer, updater: @escaping Updater, finalizer: @escaping Finalizer, length: Int32) { + + self.initializer = initializer + self.updater = updater + self.finalizer = finalizer + self.length = length + _ = initializer(context) + } + + /// + /// Cleanup + /// + deinit { + + #if swift(>=4.1) + context.deallocate() + #else + context.deallocate(capacity: 1) + #endif + } + + /// + /// Update method + /// + /// - Parameters: + /// - buffer: The buffer to add. + /// - byteCount: The length of the buffer. + /// + func update(buffer: Buffer, byteCount: CC_LONG) { + + _ = updater(context, buffer, byteCount) + } + + /// + /// Finalizer routine + /// + /// - Returns: Byte array containing the digest. + /// + func final() -> [UInt8] { + + let digestLength = Int(self.length) + var digest = Array(repeating: 0, count:digestLength) + _ = finalizer(&digest, context) + return digest + } +} + + + + + diff --git a/Sources/Encryption/Cryptor/HMAC.swift b/Sources/Encryption/Cryptor/HMAC.swift new file mode 100755 index 0000000..3ffc26f --- /dev/null +++ b/Sources/Encryption/Cryptor/HMAC.swift @@ -0,0 +1,379 @@ +// +// HMAC.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +/// +/// Calculates a cryptographic Hash-Based Message Authentication Code (HMAC). +/// +public class HMAC: Updatable { + + /// + /// Enumerates available algorithms. + /// + public enum Algorithm { + + /// Message Digest 5 + case md5 + + /// Secure Hash Algorithm 1 + case sha1 + + /// Secure Hash Algorithm 2 224-bit + case sha224 + + /// Secure Hash Algorithm 2 256-bit + case sha256 + + /// Secure Hash Algorithm 2 384-bit + case sha384 + + /// Secure Hash Algorithm 2 512-bit + case sha512 + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + static let fromNative: [CCHmacAlgorithm: Algorithm] = [ + CCHmacAlgorithm(kCCHmacAlgSHA1): .sha1, + CCHmacAlgorithm(kCCHmacAlgSHA1): .md5, + CCHmacAlgorithm(kCCHmacAlgSHA256): .sha256, + CCHmacAlgorithm(kCCHmacAlgSHA384): .sha384, + CCHmacAlgorithm(kCCHmacAlgSHA512): .sha512, + CCHmacAlgorithm(kCCHmacAlgSHA224): .sha224 + ] + + static func fromNativeValue(nativeAlg: CCHmacAlgorithm) -> Algorithm? { + + return fromNative[nativeAlg] + } + + func nativeValue() -> CCHmacAlgorithm { + + switch self { + + case .sha1: + return CCHmacAlgorithm(kCCHmacAlgSHA1) + case .md5: + return CCHmacAlgorithm(kCCHmacAlgMD5) + case .sha224: + return CCHmacAlgorithm(kCCHmacAlgSHA224) + case .sha256: + return CCHmacAlgorithm(kCCHmacAlgSHA256) + case .sha384: + return CCHmacAlgorithm(kCCHmacAlgSHA384) + case .sha512: + return CCHmacAlgorithm(kCCHmacAlgSHA512) + } + } + + #elseif os(Linux) + + #if swift(>=4.2) + + func nativeValue() -> OpaquePointer? { + + switch self { + + case .sha1: + return .init(EVP_sha1()) + case .md5: + return .init(EVP_md5()) + case .sha224: + return .init(EVP_sha224()) + case .sha256: + return .init(EVP_sha256()) + case .sha384: + return .init(EVP_sha384()) + case .sha512: + return .init(EVP_sha512()) + } + } + + #else + + func nativeValue() -> UnsafePointer { + + switch self { + + case .sha1: + return EVP_sha1() + case .md5: + return EVP_md5() + case .sha224: + return EVP_sha224() + case .sha256: + return EVP_sha256() + case .sha384: + return EVP_sha384() + case .sha512: + return EVP_sha512() + } + } + + #endif + + #endif + + /// + /// Obtains the digest length produced by this algorithm (in bytes). + /// + public func digestLength() -> Int { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + switch self { + + case .sha1: + return Int(CC_SHA1_DIGEST_LENGTH) + case .md5: + return Int(CC_MD5_DIGEST_LENGTH) + case .sha224: + return Int(CC_SHA224_DIGEST_LENGTH) + case .sha256: + return Int(CC_SHA256_DIGEST_LENGTH) + case .sha384: + return Int(CC_SHA384_DIGEST_LENGTH) + case .sha512: + return Int(CC_SHA512_DIGEST_LENGTH) + } + + #elseif os(Linux) + + switch self { + + case .sha1: + return Int(SHA_DIGEST_LENGTH) + case .md5: + return Int(MD5_DIGEST_LENGTH) + case .sha224: + return Int(SHA224_DIGEST_LENGTH) + case .sha256: + return Int(SHA256_DIGEST_LENGTH) + case .sha384: + return Int(SHA384_DIGEST_LENGTH) + case .sha512: + return Int(SHA512_DIGEST_LENGTH) + } + + #endif + + } + } + + /// Context + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + typealias Context = UnsafeMutablePointer + + #elseif os(Linux) + + #if swift(>=4.2) + + typealias Context = OpaquePointer? + + #else + + typealias Context = UnsafeMutablePointer + + #endif + + #endif + + /// Status of the calculation + public internal(set) var status: Status = .success + + #if swift(>=4.2) && os(Linux) + private let context = HMAC_CTX_new_wrapper() + #else + private let context = Context.allocate(capacity: 1) + #endif + private var algorithm: Algorithm + + // MARK: Lifecycle Methods + + /// + /// Creates a new HMAC instance with the specified algorithm and key. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - keyBuffer: Specifies pointer to the key + /// - keyByteCount: Number of bytes on keyBuffer + /// + init(using algorithm: Algorithm, keyBuffer: UnsafeRawPointer, keyByteCount: Int) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacInit(context, algorithm.nativeValue(), keyBuffer, size_t(keyByteCount)) + #elseif os(Linux) + #if swift(>=4.2) + HMAC_Init_wrapper(context, keyBuffer, Int32(keyByteCount), .make(optional: algorithm.nativeValue())) + #else + HMAC_Init(context, keyBuffer, Int32(keyByteCount), .make(optional: algorithm.nativeValue())) + #endif + #endif + } + + /// + /// Creates a new HMAC instance with the specified algorithm and key. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - key: Specifies the key as Data + /// + public init(using algorithm: Algorithm, key: Data) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + key.withUnsafeBytes() { (buffer: UnsafePointer) in + CCHmacInit(context, algorithm.nativeValue(), buffer, size_t(key.count)) + } + #elseif os(Linux) + _ = key.withUnsafeBytes() { (buffer: UnsafePointer) in + #if swift(>=4.2) + HMAC_Init_wrapper(context, buffer, Int32(key.count), .make(optional: algorithm.nativeValue())) + #else + HMAC_Init(context, buffer, Int32(key.count), .make(optional: algorithm.nativeValue())) + #endif + } + #endif + } + + /// + /// Creates a new HMAC instance with the specified algorithm and key. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - key: Specifies the key as NSData + /// + public init(using algorithm: Algorithm, key: NSData) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacInit(context, algorithm.nativeValue(), key.bytes, size_t(key.length)) + #elseif os(Linux) + #if swift(>=4.2) + HMAC_Init_wrapper(context, key.bytes, Int32(key.length), .make(optional: algorithm.nativeValue())) + #else + HMAC_Init(context, key.bytes, Int32(key.length), .make(optional: algorithm.nativeValue())) + #endif + #endif + } + + /// + /// Creates a new HMAC instance with the specified algorithm and key. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - key: Specifies the key as byte array. + /// + public init(using algorithm: Algorithm, key: [UInt8]) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacInit(context, algorithm.nativeValue(), key, size_t(key.count)) + #elseif os(Linux) + #if swift(>=4.2) + HMAC_Init_wrapper(context, key, Int32(key.count), .make(optional: algorithm.nativeValue())) + #else + HMAC_Init(context, key, Int32(key.count), .make(optional: algorithm.nativeValue())) + #endif + #endif + } + + /// + /// Creates a new HMAC instance with the specified algorithm and key string. + /// The key string is converted to bytes using UTF8 encoding. + /// + /// - Parameters: + /// - algorithm: Selects the algorithm + /// - key: Specifies the key as String + /// + public init(using algorithm: Algorithm, key: String) { + + self.algorithm = algorithm + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacInit(context, algorithm.nativeValue(), key, size_t(key.lengthOfBytes(using: String.Encoding.utf8))) + #elseif os(Linux) + #if swift(>=4.2) + HMAC_Init_wrapper(context, key, Int32(key.utf8.count), .make(optional: algorithm.nativeValue())) + #else + HMAC_Init(context, key, Int32(key.utf8.count), .make(optional: algorithm.nativeValue())) + #endif + #endif + } + + /// + /// Cleanup + /// + deinit { + #if os(Linux) && swift(>=4.2) + HMAC_CTX_free_wrapper(.make(optional: context)) + #else + #if os(Linux) + HMAC_CTX_cleanup(context) + #endif + #if swift(>=4.1) + context.deallocate() + #else + context.deallocate(capacity: 1) + #endif + #endif + } + + // MARK: Public Methods + + /// + /// Updates the calculation of the HMAC with the contents of a buffer. + /// + /// - Parameter buffer: Update buffer + /// + /// - Returns: The 'in-progress' calculated HMAC + /// + public func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self? { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacUpdate(context, buffer, byteCount) + #elseif os(Linux) + HMAC_Update(context, buffer.assumingMemoryBound(to: UInt8.self), byteCount) + #endif + return self + } + + /// + /// Finalizes the HMAC calculation + /// + /// - Returns: The final calculated HMAC + /// + public func final() -> [UInt8] { + + var hmac = Array(repeating: 0, count:algorithm.digestLength()) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + CCHmacFinal(context, &hmac) + #elseif os(Linux) + var length: UInt32 = 0 + HMAC_Final(context, &hmac, &length) + #endif + return hmac + } +} + diff --git a/Sources/Encryption/Cryptor/KeyDerivation.swift b/Sources/Encryption/Cryptor/KeyDerivation.swift new file mode 100755 index 0000000..133567c --- /dev/null +++ b/Sources/Encryption/Cryptor/KeyDerivation.swift @@ -0,0 +1,234 @@ +// +// KeyDerivation.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +/// +/// Derives key material from a password or passphrase. +/// +public class PBKDF { + + /// Enumerates available pseudo random algorithms + public enum PseudoRandomAlgorithm { + + /// Secure Hash Algorithm 1 + case sha1 + + /// Secure Hash Algorithm 2 224-bit + case sha224 + + /// Secure Hash Algorithm 2 256-bit + case sha256 + + /// Secure Hash Algorithm 2 384-bit + case sha384 + + /// Secure Hash Algorithm 2 512-bit + case sha512 + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + /// Return the OS native value + func nativeValue() -> CCPseudoRandomAlgorithm { + + switch self { + + case .sha1: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1) + case .sha224: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA224) + case .sha256: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256) + case .sha384: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA384) + case .sha512: + return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA512) + } + } + + #elseif os(Linux) + + #if swift(>=4.2) + + /// Return the OS native value + func nativeValue() -> OpaquePointer? { + + switch self { + + case .sha1: + return .init(EVP_sha1()) + case .sha224: + return .init(EVP_sha224()) + case .sha256: + return .init(EVP_sha256()) + case .sha384: + return .init(EVP_sha384()) + case .sha512: + return .init(EVP_sha512()) + } + } + + #else + + /// Return the OS native value + func nativeValue() -> UnsafePointer { + + switch self { + + case .sha1: + return EVP_sha1() + case .sha224: + return EVP_sha224() + case .sha256: + return EVP_sha256() + case .sha384: + return EVP_sha384() + case .sha512: + return EVP_sha512() + } + } + + #endif + #endif + } + + /// + /// Determines the (approximate) number of iterations of the key derivation algorithm that need + /// to be run to achieve a particular delay (or calculation time). + /// + /// - Parameters: + /// - passwordLength: Password length in bytes + /// - saltLength: Salt length in bytes + /// - algorithm: The PseudoRandomAlgorithm to use + /// - derivedKeyLength: The desired key length + /// - msec: The desired calculation time + /// + /// - Returns: The number of times the algorithm should be run + /// + public class func calibrate(passwordLength: Int, saltLength: Int, algorithm: PseudoRandomAlgorithm, derivedKeyLength: Int, msec: UInt32) -> UInt { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + return UInt(CCCalibratePBKDF(CCPBKDFAlgorithm(kCCPBKDF2), passwordLength, saltLength, algorithm.nativeValue(), derivedKeyLength, msec)) + #elseif os(Linux) + // Value as per RFC 2898. + return UInt(1000 * UInt(msec)) + #endif + } + + + /// + /// Derives key material from a password and salt. + /// + /// - Parameters: + /// - password: The password string, will be converted using UTF8 + /// - salt: The salt string will be converted using UTF8 + /// - prf: The pseudo random function + /// - round: The number of rounds + /// - derivedKeyLength: The length of the desired derived key, in bytes. + /// + /// - Returns: The derived key + /// + public class func deriveKey(fromPassword password: String, salt: String, prf: PseudoRandomAlgorithm, rounds: uint, derivedKeyLength: UInt) throws -> [UInt8] { + + var derivedKey = Array(repeating: 0, count:Int(derivedKeyLength)) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, password.utf8.count, salt, salt.utf8.count, prf.nativeValue(), rounds, &derivedKey, derivedKey.count) + if status != Int32(kCCSuccess) { + + throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).") + } + #elseif os(Linux) + let status = PKCS5_PBKDF2_HMAC(password, Int32(password.utf8.count), salt, Int32(salt.utf8.count), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKey.count), &derivedKey) + if status != 1 { + let error = ERR_get_error() + + throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))") + } + #endif + return derivedKey + } + + /// + /// Derives key material from a password and salt. + /// + /// - Parameters: + /// - password: The password string, will be converted using UTF8 + /// - salt: The salt array of bytes + /// - prf: The pseudo random function + /// - round: The number of rounds + /// - derivedKeyLength: The length of the desired derived key, in bytes. + /// + /// - Returns: The derived key + /// + public class func deriveKey(fromPassword password: String, salt: [UInt8], prf: PseudoRandomAlgorithm, rounds: uint, derivedKeyLength: UInt) throws -> [UInt8] { + + var derivedKey = Array(repeating: 0, count:Int(derivedKeyLength)) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, password.utf8.count, salt, salt.count, prf.nativeValue(), rounds, &derivedKey, derivedKey.count) + if status != Int32(kCCSuccess) { + + throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).") + } + #elseif os(Linux) + let status = PKCS5_PBKDF2_HMAC(password, Int32(password.utf8.count), salt, Int32(salt.count), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKey.count), &derivedKey) + if status != 1 { + let error = ERR_get_error() + + throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))") + } + #endif + return derivedKey + } + + /// + /// Derives key material from a password buffer. + /// + /// - Parameters: + /// - password: Pointer to the password buffer + /// - passwordLength: Password length in bytes + /// - salt: Pointer to the salt buffer + /// - saltLength: Salt length in bytes + /// - prf: The PseudoRandomAlgorithm to use + /// - rounds: The number of rounds of the algorithm to use + /// - derivedKey: Pointer to the derived key buffer. + /// - derivedKeyLength: The desired key length + /// + /// - Returns: The number of times the algorithm should be run + /// + public class func deriveKey(fromPassword password: UnsafePointer, passwordLen: Int, salt: UnsafePointer, saltLen: Int, prf: PseudoRandomAlgorithm, rounds: uint, derivedKey: UnsafeMutablePointer, derivedKeyLen: Int) throws { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, passwordLen, salt, saltLen, prf.nativeValue(), rounds, derivedKey, derivedKeyLen) + if status != Int32(kCCSuccess) { + + throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).") + } + #elseif os(Linux) + let status = PKCS5_PBKDF2_HMAC(password, Int32(passwordLen), salt, Int32(saltLen), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKeyLen), derivedKey) + if status != 1 { + let error = ERR_get_error() + + throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))") + } + #endif + } +} diff --git a/Sources/Encryption/Cryptor/Random.swift b/Sources/Encryption/Cryptor/Random.swift new file mode 100755 index 0000000..33266c6 --- /dev/null +++ b/Sources/Encryption/Cryptor/Random.swift @@ -0,0 +1,106 @@ +// +// Random.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +public typealias RNGStatus = Status + +/// +/// Generates buffers of random bytes. +/// +public class Random { + + /// + /// Wraps native call. + /// + /// - Note: CCRNGStatus is typealiased to CCStatus but this routine can only return kCCSuccess or kCCRNGFailure + /// + /// - Parameter bytes: A pointer to the buffer that will receive the bytes + /// + /// - Returns: `.success` or `.rngFailure` as appropriate. + /// + public class func generate(bytes: UnsafeMutablePointer, byteCount: Int) -> RNGStatus { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let statusCode = CCRandomGenerateBytes(bytes, byteCount) + guard let status = Status(rawValue: statusCode) else { + fatalError("CCRandomGenerateBytes returned unexpected status code: \(statusCode)") + } + return status + #elseif os(Linux) + let statusCode = RAND_bytes(bytes, Int32(byteCount)) + if statusCode != 1 { + + let errCode = ERR_get_error() + return Status.rngFailure(errCode) + } + return Status.success + #endif + } + + /// + /// Generates an array of random bytes. + /// + /// - Parameter bytesCount: Number of random bytes to generate + /// + /// - Returns: an array of random bytes + /// + /// - Throws: `.success` or an `.rngFailure` on failure + /// + public class func generate(byteCount: Int) throws -> [UInt8] { + + guard byteCount > 0 else { + throw RNGStatus.paramError + } + + var bytes = Array(repeating: UInt8(0), count:byteCount) + let status = generate(bytes: &bytes, byteCount: byteCount) + + if status != .success { + throw status + } + + return bytes + } + + /// + /// A version of generateBytes that always throws an error. + /// + /// Use it to test that code handles this. + /// + /// - Parameter bytesCount: Number of random bytes to generate + /// + /// - Returns: An array of random bytes + /// + public class func generateBytesThrow(byteCount: Int) throws -> [UInt8] { + + if byteCount <= 0 { + + fatalError("generate: byteCount must be positve and non-zero") + } + var bytes: [UInt8] = Array(repeating: UInt8(0), count:byteCount) + let status = generate(bytes: &bytes, byteCount: byteCount) + throw status + //return bytes + } +} diff --git a/Sources/Encryption/Cryptor/SSLPointerTricks.swift b/Sources/Encryption/Cryptor/SSLPointerTricks.swift new file mode 100644 index 0000000..d986598 --- /dev/null +++ b/Sources/Encryption/Cryptor/SSLPointerTricks.swift @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// This source file is taken from SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +//===----------------------------------------------------------------------===// + +// MARK:- Awful code begins here +// Hello dear reader. Let me explain what we're doing here. +// +// From OpenSSL 1.0 to OpenSSL 1.1 one of the major breaking changes was the so-called +// "great opaquifiying". Essentially, OpenSSL took all of its public structures and made +// them opaque, such that they cannot be introspected from client code. This is a great +// forward step, and brings them more in line with modern C library practices. +// +// However, it's an *enormous* inconvenience from Swift code. This is because the Swift +// translation of the C type `SSL_CTX *` changed from `UnsafeMutablePointer` to +// `OpaquePointer`. +// +// This change exists for reasonable enough reasons in Swift land (see +// https://forums.swift.org/t/opaque-pointers-in-swift/6875 for a discussion), but +// nonetheless causes enormous problems in our codebase. +// +// Our cheap way out is to make everything an OpaquePointer, and then provide initializers +// between OpaquePointer and the typed pointers. This allows us to tolerate either pointer +// type in our Swift code by bridging them over to OpaquePointer and back, and lets the +// compiler worry about how exactly to make that work. +// +// Now, in fact, Swift already has initializers between the pointer types. What it does +// not have is self-initializers: the ability to create an `OpaquePointer` from an `OpaquePointer`, +// or an `UnsafePointer` from an `UnsafePointer`. We add those two initializers here. +// We also add a special "make" function that exists to handle the special case of optional pointer +// values, which we mostly encounter in the ALPN callbacks. +// +// The *downside* of this approach is that we totally break the pointer type system. It becomes +// trivially possible to alias a pointer of type T to type U through two calls to init. This +// is not a thing we want to widely promote. For this reason, these extensions are hidden in +// this file, where we can laugh and jeer at them and generally make them feel bad about +// themselves. +// +// Hopefully, in time, these extensions can be removed. + +extension UnsafePointer { + init(_ ptr: UnsafePointer) { + self = ptr + } + + static func make(optional ptr: UnsafePointer?) -> UnsafePointer? { + return ptr.map(UnsafePointer.init) + } + + static func make(optional ptr: OpaquePointer?) -> UnsafePointer? { + return ptr.map(UnsafePointer.init) + } +} + +extension UnsafeMutablePointer { + init(_ ptr: UnsafeMutableRawPointer) { + let x = UnsafeMutablePointer(bitPattern: UInt(bitPattern: ptr))! + self = x + } + + static func make(optional ptr: UnsafeMutablePointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } + + static func make(optional ptr: UnsafeMutableRawPointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } + + static func make(optional ptr: OpaquePointer?) -> UnsafeMutablePointer? { + return ptr.map(UnsafeMutablePointer.init) + } +} + +extension UnsafeMutableRawPointer { + static func make(optional ptr: OpaquePointer?) -> UnsafeMutableRawPointer? { + return ptr.map(UnsafeMutableRawPointer.init) + } +} + +extension OpaquePointer { + init(_ ptr: OpaquePointer) { + self = ptr + } + + static func make(optional ptr: OpaquePointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } + + static func make(optional ptr: UnsafeMutableRawPointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } + + static func make(optional ptr: UnsafeMutablePointer?) -> OpaquePointer? { + return ptr.map(OpaquePointer.init) + } +} diff --git a/Sources/Encryption/Cryptor/Status.swift b/Sources/Encryption/Cryptor/Status.swift new file mode 100755 index 0000000..b98214f --- /dev/null +++ b/Sources/Encryption/Cryptor/Status.swift @@ -0,0 +1,288 @@ +// +// Status.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +/// +/// Links the native CommonCryptoStatus enumeration to Swift versions. +/// +public enum Status: CCCryptorStatus, Swift.Error, CustomStringConvertible { + + /// Successful + case success + + /// Parameter Error + case paramError + + /// Buffer too Small + case bufferTooSmall + + /// Memory Failure + case memoryFailure + + /// Alignment Error + case alignmentError + + /// Decode Error + case decodeError + + /// Unimplemented + case unimplemented + + /// Overflow + case overflow + + /// Random Number Generator Err + case rngFailure + + /// + /// Converts this value to a native `CCCryptorStatus` value. + /// + public func toRaw() -> CCCryptorStatus { + + switch self { + + case .success: + return CCCryptorStatus(kCCSuccess) + case .paramError: + return CCCryptorStatus(kCCParamError) + case .bufferTooSmall: + return CCCryptorStatus(kCCBufferTooSmall) + case .memoryFailure: + return CCCryptorStatus(kCCMemoryFailure) + case .alignmentError: + return CCCryptorStatus(kCCAlignmentError) + case .decodeError: + return CCCryptorStatus(kCCDecodeError) + case .unimplemented: + return CCCryptorStatus(kCCUnimplemented) + case .overflow: + return CCCryptorStatus(kCCOverflow) + case .rngFailure: + return CCCryptorStatus(kCCRNGFailure) + } + } + + /// + /// Human readable descriptions of the values. (Not needed in Swift 2.0?) + /// + static let descriptions = [ + success: "Success", + paramError: "ParamError", + bufferTooSmall: "BufferTooSmall", + memoryFailure: "MemoryFailure", + alignmentError: "AlignmentError", + decodeError: "DecodeError", + unimplemented: "Unimplemented", + overflow: "Overflow", + rngFailure: "RNGFailure" + ] + + /// + /// Obtain human-readable string from enum value. + /// + public var description: String { + + return (Status.descriptions[self] != nil) ? Status.descriptions[self]! : "" + } + + /// + /// Create enum value from raw `CCCryptorStatus` value. + /// + public static func fromRaw(status: CCCryptorStatus) -> Status? { + + var from = [ + kCCSuccess: success, + kCCParamError: paramError, + kCCBufferTooSmall: bufferTooSmall, + kCCMemoryFailure: memoryFailure, + kCCAlignmentError: alignmentError, + kCCDecodeError: decodeError, + kCCUnimplemented: unimplemented, + kCCOverflow: overflow, + kCCRNGFailure: rngFailure + ] + + return from[Int(status)] + + } +} + +#elseif os(Linux) + +/// +/// Error status +/// +public enum Status: Swift.Error, CustomStringConvertible { + + /// Success + case success + + /// Unimplemented with reason + case unimplemented(String) + + /// Not supported with reason + case notSupported(String) + + /// Parameter Error + case paramError + + /// Failure with error code + case fail(UInt) + + /// Random Byte Generator Failure with error code + case rngFailure(UInt) + + /// The error code itself + public var code: Int { + + switch self { + + case .success: + return 0 + + case .notSupported: + return -1 + + case .unimplemented: + return -2 + + case .paramError: + return -3 + + case .fail(let code): + return Int(code) + + case .rngFailure(let code): + return Int(code) + } + } + + /// + /// Create enum value from raw `SSL error code` value. + /// + public static func fromRaw(status: UInt) -> Status? { + + return Status.fail(status) + } + + /// + /// Obtain human-readable string for the error code. + /// + public var description: String { + + switch self { + + case .success: + return "No error" + + case .notSupported(let reason): + return "Not supported: \(reason)" + + case .unimplemented(let reason): + return "Not implemented: \(reason)" + + case .paramError: + return "Invalid parameters passed" + + case .fail(let errorCode): + return "ERROR: code: \(errorCode), reason: \(errToString(ERR_error_string(UInt(errorCode), nil)))" + + case .rngFailure(let errorCode): + return "Random Byte Generator ERROR: code: \(errorCode), reason: \(errToString(ERR_error_string(UInt(errorCode), nil)))" + } + } +} + +// MARK: Operators + +func == (lhs: Status, rhs: Status) -> Bool { + + return lhs.code == rhs.code +} + +func != (lhs: Status, rhs: Status) -> Bool { + + return lhs.code != rhs.code +} + +#endif + +/// +/// CryptorError +/// Thrown in caaes where a _fatalError()_ is **NOT** appropriate. +/// +public enum CryptorError: Swift.Error, CustomStringConvertible { + + /// Success + case success + + /// Invalid key size + case invalidKeySize + + /// Invalid IV size + case invalidIVSizeOrLength + + /// Fail with code and string + case fail(Int32, String) + + /// The error code itself + public var errCode: Int32 { + + switch self { + + case .success: + return 0 + + case .invalidKeySize: + return -1 + + case .invalidIVSizeOrLength: + return -2 + + case .fail(let errCode, _): + return Int32(errCode) + } + } + + /// Error Description + public var description: String { + + switch self { + + case .success: + return "Success" + + case .invalidKeySize: + return "Invalid key size." + + case .invalidIVSizeOrLength: + return "Invalid IV size or length." + + case .fail(let (_, reason)): + return reason + } + } +} + diff --git a/Sources/Encryption/Cryptor/StreamCryptor.swift b/Sources/Encryption/Cryptor/StreamCryptor.swift new file mode 100755 index 0000000..2243af5 --- /dev/null +++ b/Sources/Encryption/Cryptor/StreamCryptor.swift @@ -0,0 +1,1040 @@ +// +// StreamCryptor.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + import CommonCrypto +#elseif os(Linux) + import OpenSSL +#endif + +/// +/// Encrypts or decrypts return results as they become available. +/// +/// - Note: The underlying cipher may be a block or a stream cipher. +/// +/// Use for large files or network streams. +/// +/// For small, in-memory buffers Cryptor may be easier to use. +/// +public class StreamCryptor { + + #if os(Linux) + + // + // Key sizes + // + static let kCCKeySizeAES128 = 16 + static let kCCKeySizeAES192 = 24 + static let kCCKeySizeAES256 = 32 + static let kCCKeySizeDES = 8 + static let kCCKeySize3DES = 24 + static let kCCKeySizeMinCAST = 5 + static let kCCKeySizeMaxCAST = 16 + static let kCCKeySizeMinRC2 = 1 + static let kCCKeySizeMaxRC2 = 128 + static let kCCKeySizeMinBlowfish = 8 + static let kCCKeySizeMaxBlowfish = 56 + + // + // Block sizes + // + static let kCCBlockSizeAES128 = 16 + static let kCCBlockSizeDES = 8 + static let kCCBlockSize3DES = 8 + static let kCCBlockSizeCAST = 8 + static let kCCBlockSizeRC2 = 8 + static let kCCBlockSizeBlowfish = 8 + + #endif + + /// + /// Enumerates Cryptor operations + /// + public enum Operation { + + /// Encrypting + case encrypt + + /// Decrypting + case decrypt + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// Convert to native `CCOperation` + func nativeValue() -> CCOperation { + + switch self { + + case .encrypt: + return CCOperation(kCCEncrypt) + + case .decrypt: + return CCOperation(kCCDecrypt) + } + } + + #elseif os(Linux) + + /// Convert to native value + func nativeValue() -> UInt32 { + + switch self { + + case .encrypt: + return 0 + + case .decrypt: + return 1 + } + } + + #endif + } + + /// + /// Enumerates valid key sizes. + /// + public enum ValidKeySize { + + case fixed(Int) + case discrete([Int]) + case range(Int, Int) + + /// + /// Determines if a given `keySize` is valid for this algorithm. + /// + /// - Parameter keySize: The size to test for validity. + /// + /// - Returns: True if valid, false otherwise. + /// + func isValidKeySize(keySize: Int) -> Bool { + + switch self { + + case .fixed(let fixed): + return (fixed == keySize) + + case .range(let min, let max): + return ((keySize >= min) && (keySize <= max)) + + case .discrete(let values): + return values.contains(keySize) + } + } + + /// + /// Determines the next valid key size; that is, the first valid key size larger + /// than the given value. + /// + /// - Parameter keySize: The size for which the `next` size is desired. + /// + /// - Returns: Will return `nil` if the passed in `keySize` is greater than the max. + /// + func paddedKeySize(keySize: Int) -> Int? { + + switch self { + + case .fixed(let fixed): + return (keySize <= fixed) ? fixed : nil + + case .range(let min, let max): + return (keySize > max) ? nil : ((keySize < min) ? min : keySize) + + case .discrete(let values): + return values.sorted().reduce(nil) { answer, current in + return answer ?? ((current >= keySize) ? current : nil) + } + } + } + + + } + + /// + /// Maps CommonCryptoOptions onto a Swift struct. + /// + public struct Options: OptionSet { + + public typealias RawValue = Int + public let rawValue: RawValue + + /// Convert from a native value (i.e. `0`, `kCCOptionpkcs7Padding`, `kCCOptionECBMode`) + public init(rawValue: RawValue) { + self.rawValue = rawValue + } + + /// Convert from a native value (i.e. `0`, `kCCOptionpkcs7Padding`, `kCCOptionECBMode`) + public init(_ rawValue: RawValue) { + self.init(rawValue: rawValue) + } + + /// No options + public static let none = Options(rawValue: 0) + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// Use padding. Needed unless the input is a integral number of blocks long. + public static var pkcs7Padding = Options(rawValue:kCCOptionPKCS7Padding) + + /// Electronic Code Book Mode. Don't use this. + public static var ecbMode = Options(rawValue:kCCOptionECBMode) + + #elseif os(Linux) + + /// Use padding. Needed unless the input is a integral number of blocks long. + public static var pkcs7Padding = Options(rawValue:0x0001) + + /// Electronic Code Book Mode. Don't use this. + public static var ecbMode = Options(rawValue:0x0002) + + #endif + } + + /// + /// Enumerates available algorithms + /// + public enum Algorithm { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// Advanced Encryption Standard + /// - Note: aes and aes128 are equivalent. + case aes, aes128, aes192, aes256 + + /// Data Encryption Standard + case des + + /// Triple des + case tripleDes + + /// cast + case cast + + /// rc2 + case rc2 + + /// blowfish + case blowfish + + #elseif os(Linux) + + /// Advanced Encryption Standard + /// - Note: aes and aes128 are equivalent. + case aes, aes128, aes192, aes256 + + /// Data Encryption Standard + case des + + /// Triple des + case tripleDes + + /// cast + case cast + + /// rc2 + case rc2 + + /// blowfish + case blowfish + + #endif + + /// Blocksize, in bytes, of algorithm. + public var blockSize: Int { + + switch self { + + case .aes, .aes128, .aes192, .aes256: + return kCCBlockSizeAES128 + + case .des: + return kCCBlockSizeDES + + case .tripleDes: + return kCCBlockSize3DES + + case .cast: + return kCCBlockSizeCAST + + case .rc2: + return kCCBlockSizeRC2 + + case .blowfish: + return kCCBlockSizeBlowfish + } + } + + public var defaultKeySize: Int { + + switch self { + + case .aes, .aes128: + return kCCKeySizeAES128 + + case .aes192: + return kCCKeySizeAES192 + + case .aes256: + return kCCKeySizeAES256 + + case .des: + return kCCKeySizeDES + + case .tripleDes: + return kCCKeySize3DES + + case .cast: + return kCCKeySizeMinCAST + + case .rc2: + return kCCKeySizeMinRC2 + + case .blowfish: + return kCCKeySizeMinBlowfish + } + } + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// Native, CommonCrypto constant for algorithm. + func nativeValue() -> CCAlgorithm { + + switch self { + + case .aes, .aes128, .aes192, .aes256: + return CCAlgorithm(kCCAlgorithmAES) + + case .des: + return CCAlgorithm(kCCAlgorithmDES) + + case .tripleDes: + return CCAlgorithm(kCCAlgorithm3DES) + + case .cast: + return CCAlgorithm(kCCAlgorithmCAST) + + case .rc2: + return CCAlgorithm(kCCAlgorithmRC2) + + case .blowfish: + return CCAlgorithm(kCCAlgorithmBlowfish) + } + } + + #elseif os(Linux) + + #if swift(>=4.2) + + /// Native, OpenSSL function for algorithm. + func nativeValue(options: Options) -> OpaquePointer? { + + if options == .pkcs7Padding || options == .none { + + switch self { + + case .aes, .aes128: + return .init(EVP_aes_128_cbc()) + + case .aes192: + return .init(EVP_aes_192_cbc()) + + case .aes256: + return .init(EVP_aes_256_cbc()) + + case .des: + return .init(EVP_des_cbc()) + + case .tripleDes: + return .init(EVP_des_ede3_cbc()) + + case .cast: + return .init(EVP_cast5_cbc()) + + case .rc2: + return .init(EVP_rc2_cbc()) + + case .blowfish: + return .init(EVP_bf_cbc()) + } + } + + if options == .ecbMode { + + switch self { + + case .aes, .aes128: + return .init(EVP_aes_128_ecb()) + + case .aes192: + return .init(EVP_aes_192_ecb()) + + case .aes256: + return .init(EVP_aes_256_ecb()) + + case .des: + return .init(EVP_des_ecb()) + + case .tripleDes: + return .init(EVP_des_ede3_ecb()) + + case .cast: + return .init(EVP_cast5_ecb()) + + case .rc2: + return .init(EVP_rc2_ecb()) + + case .blowfish: + return .init(EVP_bf_ecb()) + } + } + + fatalError("Unsupported options and/or algorithm.") + } + + #else + + /// Native, OpenSSL function for algorithm. + func nativeValue(options: Options) -> UnsafePointer { + + if options == .pkcs7Padding || options == .none { + + switch self { + + case .aes, .aes128: + return EVP_aes_128_cbc() + + case .aes192: + return EVP_aes_192_cbc() + + case .aes256: + return EVP_aes_256_cbc() + + case .des: + return EVP_des_cbc() + + case .tripleDes: + return EVP_des_ede3_cbc() + + case .cast: + return EVP_cast5_cbc() + + case .rc2: + return EVP_rc2_cbc() + + case .blowfish: + return EVP_bf_cbc() + } + } + + if options == .ecbMode { + + switch self { + + case .aes, .aes128: + return EVP_aes_128_ecb() + + case .aes192: + return EVP_aes_192_ecb() + + case .aes256: + return EVP_aes_256_ecb() + + case .des: + return EVP_des_ecb() + + case .tripleDes: + return EVP_des_ede3_ecb() + + case .cast: + return EVP_cast5_ecb() + + case .rc2: + return EVP_rc2_ecb() + + case .blowfish: + return EVP_bf_ecb() + } + } + + fatalError("Unsupported options and/or algorithm.") + } + + #endif + + #endif + + /// + /// Determines the valid key size for this algorithm + /// + /// - Returns: Valid key size for this algorithm. + /// + func validKeySize() -> ValidKeySize { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + switch self { + + case .aes, .aes128, .aes192, .aes256: + return .discrete([kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]) + + case .des: + return .fixed(kCCKeySizeDES) + + case .tripleDes: + return .fixed(kCCKeySize3DES) + + case .cast: + return .range(kCCKeySizeMinCAST, kCCKeySizeMaxCAST) + + case .rc2: + return .range(kCCKeySizeMinRC2, kCCKeySizeMaxRC2) + + case .blowfish: + return .range(kCCKeySizeMinBlowfish, kCCKeySizeMaxBlowfish) + } + + #elseif os(Linux) + + switch self { + + case .aes, .aes128: + return .fixed(kCCKeySizeAES128) + + case .aes192: + return .fixed(kCCKeySizeAES192) + + case .aes256: + return .fixed(kCCKeySizeAES256) + + case .des: + return .fixed(kCCKeySizeDES) + + case .tripleDes: + return .fixed(kCCKeySize3DES) + + case .cast: + return .range(kCCKeySizeMinCAST, kCCKeySizeMaxCAST) + + case .rc2: + return .range(kCCKeySizeMinRC2, kCCKeySizeMaxRC2) + + case .blowfish: + return .range(kCCKeySizeMinBlowfish, kCCKeySizeMaxBlowfish) + } + + #endif + } + + /// + /// Tests if a given keySize is valid for this algorithm + /// + /// - Parameter keySize: The key size to be validated. + /// + /// - Returns: True if valid, false otherwise. + /// + func isValidKeySize(keySize: Int) -> Bool { + return self.validKeySize().isValidKeySize(keySize: keySize) + } + + /// + /// Calculates the next, if any, valid keySize greater or equal to a given `keySize` for this algorithm + /// + /// - Parameter keySize: Key size for which the next size is requested. + /// + /// - Returns: Next key size or nil + /// + func paddedKeySize(keySize: Int) -> Int? { + return self.validKeySize().paddedKeySize(keySize: keySize) + } + } + + /// + /// The status code resulting from the last method call to this Cryptor. + /// Used to get additional information when optional chaining collapes. + /// + public internal(set) var status: Status = .success + + /// + /// Context obtained. True if we have it, false otherwise. + /// + private var haveContext: Bool = false + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + /// CommonCrypto Context + private var context = UnsafeMutablePointer.allocate(capacity: 1) + + #elseif os(Linux) + + #if swift(>=4.2) + + /// OpenSSL Cipher Context + private let context: OpaquePointer? = .init(EVP_CIPHER_CTX_new()) + + #else + + /// OpenSSL Cipher Context + private let context: UnsafeMutablePointer = EVP_CIPHER_CTX_new() + + #endif + + + /// Operation + private var operation: Operation = .encrypt + + /// The algorithm + private var algorithm: Algorithm + + #endif + + + // MARK: Lifecycle Methods + + /// + /// Default Initializer + /// + /// - Parameters: + /// - operation: The operation to perform see Operation (Encrypt, Decrypt) + /// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish) + /// - keyBuffer: Pointer to key buffer + /// - keyByteCount: Number of bytes in the key + /// - ivBuffer: Initialization vector buffer + /// - ivLength: Length of the ivBuffer + /// + /// - Returns: New StreamCryptor instance. + /// + public init(operation: Operation, algorithm: Algorithm, options: Options, keyBuffer: [UInt8], keyByteCount: Int, ivBuffer: UnsafePointer, ivLength: Int = 0) throws { + + guard algorithm.isValidKeySize(keySize: keyByteCount) else { + throw CryptorError.invalidKeySize + } + + guard options.contains(.ecbMode) || ivLength == algorithm.blockSize else { + throw CryptorError.invalidIVSizeOrLength + } + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + let rawStatus = CCCryptorCreate(operation.nativeValue(), algorithm.nativeValue(), CCOptions(options.rawValue), keyBuffer, keyByteCount, ivBuffer, self.context) + + if let status = Status.fromRaw(status: rawStatus) { + + self.status = status + + } else { + + throw CryptorError.fail(rawStatus, "Cryptor init returned unexpected status.") + } + + self.haveContext = true + + #elseif os(Linux) + + self.algorithm = algorithm + self.operation = operation + + var rawStatus: Int32 + + switch self.operation { + + case .encrypt: + rawStatus = EVP_EncryptInit_ex(.make(optional: self.context), .make(optional: algorithm.nativeValue(options: options)), nil, keyBuffer, ivBuffer) + + case .decrypt: + rawStatus = EVP_DecryptInit(.make(optional: self.context), .make(optional: algorithm.nativeValue(options: options)), keyBuffer, ivBuffer) + } + + if rawStatus == 0 { + + let errorCode = ERR_get_error() + if let status = Status.fromRaw(status: errorCode) { + self.status = status + } else { + + throw CryptorError.fail(Int32(errorCode), "Cryptor init returned unexpected status.") + } + } + + self.haveContext = true + + // Default to no padding... + var needPadding: Int32 = 0 + if options == .pkcs7Padding { + needPadding = 1 + } + + // Note: This call must be AFTER the init call above... + EVP_CIPHER_CTX_set_padding(.make(optional: self.context), needPadding) + + self.status = Status.success + + #endif + + } + + /// + /// Creates a new StreamCryptor + /// + /// - Parameters: + /// - operation: The operation to perform see Operation (Encrypt, Decrypt) + /// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish) + /// - key: A byte array containing key data + /// - iv: A byte array containing initialization vector + /// + /// - Returns: New StreamCryptor instance. + /// + public convenience init(operation: Operation, algorithm: Algorithm, options: Options, key: [UInt8], iv: [UInt8]) throws { + + guard let paddedKeySize = algorithm.paddedKeySize(keySize: key.count) else { + throw CryptorError.invalidKeySize + } + + try self.init(operation:operation, + algorithm:algorithm, + options:options, + keyBuffer:CryptoUtils.zeroPad(byteArray:key, blockSize: paddedKeySize), + keyByteCount:paddedKeySize, + ivBuffer:iv, + ivLength:iv.count) + } + + /// + /// Creates a new StreamCryptor + /// + /// - Parameters: + /// - operation: The operation to perform see Operation (Encrypt, Decrypt) + /// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish) + /// - key: A string containing key data (will be interpreted as UTF8) + /// - iv: A string containing initialization vector data (will be interpreted as UTF8) + /// + /// - Returns: New StreamCryptor instance. + /// + public convenience init(operation: Operation, algorithm: Algorithm, options: Options, key: String, iv: String) throws { + + let keySize = key.utf8.count + guard let paddedKeySize = algorithm.paddedKeySize(keySize: keySize) else { + throw CryptorError.invalidKeySize + } + + try self.init(operation:operation, + algorithm:algorithm, + options:options, + keyBuffer:CryptoUtils.zeroPad(string: key, blockSize: paddedKeySize), + keyByteCount:paddedKeySize, + ivBuffer:iv, + ivLength:iv.utf8.count) + } + + /// + /// Cleanup + /// + deinit { + + // Ensure we've got a context before attempting to get rid of it... + if self.haveContext == false { + return + } + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + // Ensure we've got a context before attempting to get rid of it... + if self.context.pointee == nil { + return + } + + let rawStatus = CCCryptorRelease(self.context.pointee) + if let status = Status.fromRaw(status: rawStatus) { + + if status != .success { + + NSLog("WARNING: CCCryptoRelease failed with status \(rawStatus).") + } + + } else { + + fatalError("CCCryptorUpdate returned unexpected status.") + } + + #if swift(>=4.1) + context.deallocate() + #else + context.deallocate(capacity: 1) + #endif + + self.haveContext = false + + #elseif os(Linux) + + EVP_CIPHER_CTX_free(.make(optional: self.context)) + self.haveContext = false + + #endif + } + + // MARK: Public Methods + + /// + /// Add the contents of an Data buffer to the current encryption/decryption operation. + /// + /// - Parameters: + /// - dataIn: The input data + /// - byteArrayOut: Output data + /// + /// - Returns: A tuple containing the number of output bytes produced and the status (see Status) + /// + public func update(dataIn: Data, byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + dataIn.withUnsafeBytes() { (buffer: UnsafePointer) in + _ = update(bufferIn: buffer, byteCountIn: dataIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + } + return (dataOutMoved, self.status) + } + + /// + /// Add the contents of an NSData buffer to the current encryption/decryption operation. + /// + /// - Parameters: + /// - dataIn: The input data + /// - byteArrayOut: Output data + /// + /// - Returns: A tuple containing the number of output bytes produced and the status (see Status) + /// + public func update(dataIn: NSData, byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + var ptr = dataIn.bytes.assumingMemoryBound(to: UInt8.self).pointee + _ = update(bufferIn: &ptr, byteCountIn: dataIn.length, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + return (dataOutMoved, self.status) + } + + /// + /// Add the contents of a byte array to the current encryption/decryption operation. + /// + /// - Parameters: + /// - byteArrayIn: The input data + /// - byteArrayOut: Output data + /// + /// - Returns: A tuple containing the number of output bytes produced and the status (see Status) + /// + public func update(byteArrayIn: [UInt8], byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + _ = update(bufferIn: byteArrayIn, byteCountIn: byteArrayIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + return (dataOutMoved, self.status) + } + + /// + /// Add the contents of a string (interpreted as UTF8) to the current encryption/decryption operation. + /// + /// - Parameters: + /// - byteArrayIn: The input data + /// - byteArrayOut: Output data + /// + /// - Returns: A tuple containing the number of output bytes produced and the status (see Status) + /// + public func update(stringIn: String, byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + _ = update(bufferIn: stringIn, byteCountIn: stringIn.utf8.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + return (dataOutMoved, self.status) + } + + /// + /// Retrieves all remaining encrypted or decrypted data from this cryptor. + /// + /// - Note: If the underlying algorithm is an block cipher and the padding option has + /// not been specified and the cumulative input to the cryptor has not been an integral + /// multiple of the block length this will fail with an alignment error. + /// + /// - Note: This method updates the status property + /// + /// - Parameter byteArrayOut: The output bffer + /// + /// - Returns: a tuple containing the number of output bytes produced and the status (see Status) + /// + public func final(byteArrayOut: inout [UInt8]) -> (Int, Status) { + + let dataOutAvailable = byteArrayOut.count + var dataOutMoved = 0 + _ = final(bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved) + return (dataOutMoved, self.status) + } + + // MARK: - Low-level interface + + /// + /// Update the buffer + /// + /// - Parameters: + /// - bufferIn: Pointer to input buffer + /// - inByteCount: Number of bytes contained in input buffer + /// - bufferOut: Pointer to output buffer + /// - outByteCapacity: Capacity of the output buffer in bytes + /// - outByteCount: On successful completion, the number of bytes written to the output buffer + /// + /// - Returns: Status of the update + /// + public func update(bufferIn: UnsafeRawPointer, byteCountIn: Int, bufferOut: UnsafeMutablePointer, byteCapacityOut: Int, byteCountOut: inout Int) -> Status { + + if self.status == .success { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + let rawStatus = CCCryptorUpdate(self.context.pointee, bufferIn, byteCountIn, bufferOut, byteCapacityOut, &byteCountOut) + if let status = Status.fromRaw(status: rawStatus) { + self.status = status + } else { + fatalError("CCCryptorUpdate returned unexpected status.") + } + + #elseif os(Linux) + + var rawStatus: Int32 + var outLength: Int32 = 0 + + switch self.operation { + + case .encrypt: + rawStatus = EVP_EncryptUpdate(.make(optional: self.context), bufferOut, &outLength, bufferIn.assumingMemoryBound(to: UInt8.self), Int32(byteCountIn)) + + case .decrypt: + rawStatus = EVP_DecryptUpdate(.make(optional: self.context), bufferOut, &outLength, bufferIn.assumingMemoryBound(to: UInt8.self), Int32(byteCountIn)) + } + + byteCountOut = Int(outLength) + + if rawStatus == 0 { + + let errorCode = ERR_get_error() + if let status = Status.fromRaw(status: errorCode) { + self.status = status + } else { + fatalError("Cryptor update returned unexpected status.") + } + + } else { + + self.status = Status.success + } + + #endif + + } + + return self.status + } + + /// + /// Retrieves all remaining encrypted or decrypted data from this cryptor. + /// + /// - Note: If the underlying algorithm is an block cipher and the padding option has + /// not been specified and the cumulative input to the cryptor has not been an integral + /// multiple of the block length this will fail with an alignment error. + /// + /// - Note: This method updates the status property + /// + /// - Parameters: + /// - bufferOut: Pointer to output buffer + /// - outByteCapacity: Capacity of the output buffer in bytes + /// - outByteCount: On successful completion, the number of bytes written to the output buffer + /// + /// - Returns: Status of the update + /// + public func final(bufferOut: UnsafeMutablePointer, byteCapacityOut: Int, byteCountOut: inout Int) -> Status { + + if self.status == Status.success { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + let rawStatus = CCCryptorFinal(self.context.pointee, bufferOut, byteCapacityOut, &byteCountOut) + if let status = Status.fromRaw(status: rawStatus) { + self.status = status + } else { + fatalError("CCCryptorUpdate returned unexpected status.") + } + + #elseif os(Linux) + + var rawStatus: Int32 + var outLength: Int32 = Int32(byteCapacityOut) + + switch self.operation { + + case .encrypt: + rawStatus = EVP_EncryptFinal(.make(optional: self.context), bufferOut, &outLength) + + case .decrypt: + rawStatus = EVP_DecryptFinal(.make(optional: self.context), bufferOut, &outLength) + } + + byteCountOut = Int(outLength) + + if rawStatus == 0 { + + let errorCode = ERR_get_error() + if let status = Status.fromRaw(status: errorCode) { + self.status = status + } else { + fatalError("Cryptor final returned unexpected status.") + } + + } else { + + self.status = Status.success + } + + #endif + } + + return self.status + } + + /// + /// Determines the number of bytes that will be output by this Cryptor if inputBytes of additional + /// data is input. + /// + /// - Parameters: + /// - inputByteCount: Number of bytes that will be input. + /// - isFinal: True if buffer to be input will be the last input buffer, false otherwise. + /// + /// - Returns: The final output length + /// + public func getOutputLength(inputByteCount: Int, isFinal: Bool = false) -> Int { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + return CCCryptorGetOutputLength(self.context.pointee, inputByteCount, isFinal) + + #elseif os(Linux) + + if inputByteCount == 0 { + return self.algorithm.blockSize + } + + return (inputByteCount + self.algorithm.blockSize - (inputByteCount % self.algorithm.blockSize)) + + #endif + } + +} diff --git a/Sources/Encryption/Cryptor/Updatable.swift b/Sources/Encryption/Cryptor/Updatable.swift new file mode 100755 index 0000000..0c01ce2 --- /dev/null +++ b/Sources/Encryption/Cryptor/Updatable.swift @@ -0,0 +1,100 @@ +// +// Updateable.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// A protocol for calculations that can be updated with incremental data buffers. +/// +public protocol Updatable { + + /// Status of the calculation. + var status: Status { get } + + /// + /// Low-level update routine. + /// Updates the calculation with the contents of a data buffer. + /// + /// - Parameters: + /// - buffer: Pointer to the data buffer + /// - byteCount: Length of the buffer in bytes + /// + /// - Returns: `Self` if no error for optional chaining, nil otherwise + /// + func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self? +} + +/// +/// Factors out common update code from Digest, HMAC and Cryptor. +/// +extension Updatable { + /// + /// Updates the current calculation with data contained in an `NSData` object. + /// + /// - Parameter data: The `NSData` object + /// + /// - Returns: Optional `Self` or nil + /// + public func update(data: NSData) -> Self? { + + _ = update(from: data.bytes, byteCount: size_t(data.length)) + return self.status == .success ? self : nil + } + + /// + /// Updates the current calculation with data contained in an `Data` object. + /// + /// - Parameters data: The `Data` object + /// + /// - Returns: Optional `Self` or nil + /// + public func update(data: Data) -> Self? { + + _ = data.withUnsafeBytes() { (buffer: UnsafePointer) in + + _ = update(from: buffer, byteCount: size_t(data.count)) + } + return self.status == .success ? self : nil + } + + /// + /// Updates the current calculation with data contained in a byte array. + /// + /// - Parameter byteArray: The byte array + /// + /// - Returns: Optional `Self` or nil + /// + public func update(byteArray: [UInt8]) -> Self? { + + _ = update(from: byteArray, byteCount: size_t(byteArray.count)) + return self.status == .success ? self : nil + } + + /// + /// Updates the current calculation with data contained in a String. + /// The corresponding data will be generated using UTF8 encoding. + /// + /// - Parameter string: The string of data + /// + /// - Returns: Optional `Self` or nil + /// + public func update(string: String) -> Self? { + + _ = update(from: string, byteCount: size_t(string.utf8.count)) + return self.status == .success ? self : nil + } +} diff --git a/Sources/Encryption/Cryptor/Utilities.swift b/Sources/Encryption/Cryptor/Utilities.swift new file mode 100755 index 0000000..30ab958 --- /dev/null +++ b/Sources/Encryption/Cryptor/Utilities.swift @@ -0,0 +1,259 @@ +// +// Utilities.swift +// Cryptor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// +// Replaces Swift's native `fatalError` function to allow redirection +// For more details about how this all works see: +// https://marcosantadev.com/test-swift-fatalerror/ +// +func fatalError(_ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) -> Never { + + FatalErrorUtil.fatalErrorClosure(message(), file, line) +} + +// Convert an UnsafeMutablePointer? to a String, providing a +// default value of empty string if the pointer is nil. +// +// - Parameter ptr: Pointer to string to be converted. +// +// - Returns: Converted string. +// +func errToString(_ ptr: UnsafeMutablePointer?) -> String { + if let ptr = ptr { + return String(cString: ptr) + } else { + return "" + } +} + +/// +/// Allows redirection of `fatalError` for Unit Testing or for +/// library users that want to handle such errors in another way. +/// +struct FatalErrorUtil { + + static var fatalErrorClosure: (String, StaticString, UInt) -> Never = defaultFatalErrorClosure + private static let defaultFatalErrorClosure = { Swift.fatalError($0, file: $1, line: $2) } + static func replaceFatalError(closure: @escaping (String, StaticString, UInt) -> Never) { + fatalErrorClosure = closure + } + static func restoreFatalError() { + fatalErrorClosure = defaultFatalErrorClosure + } + +} + +/// +/// Various utility functions for conversions +/// +public struct CryptoUtils { + + /// + /// Converts a single hexadecimal digit encoded as a Unicode Scalar to it's corresponding value. + /// + /// - Parameter digit: A Unicode scalar in the set 0..9a..fA..F + /// + /// - Returns: The hexadecimal value of the digit + /// + static func convert(hexDigit digit: UnicodeScalar) -> UInt8 { + + switch digit { + + case UnicodeScalar(unicodeScalarLiteral:"0")...UnicodeScalar(unicodeScalarLiteral:"9"): + return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"0").value) + + case UnicodeScalar(unicodeScalarLiteral:"a")...UnicodeScalar(unicodeScalarLiteral:"f"): + return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"a").value + 0xa) + + case UnicodeScalar(unicodeScalarLiteral:"A")...UnicodeScalar(unicodeScalarLiteral:"F"): + return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"A").value + 0xa) + + default: + fatalError("convertHexDigit: Invalid hex digit") + } + } + + /// + /// Converts a string of hexadecimal digits to a byte array. + /// + /// - Parameter string: The hex string (must contain an even number of digits) + /// + /// - Returns: A byte array + /// + public static func byteArray(fromHex string: String) -> [UInt8] { + + var iterator = string.unicodeScalars.makeIterator() + var byteArray: [UInt8] = [] + while let msn = iterator.next() { + + if let lsn = iterator.next() { + + byteArray += [ (convert(hexDigit: msn) << 4 | convert(hexDigit: lsn)) ] + + } else { + + fatalError("arrayFromHexString: String must contain even number of characters") + } + } + return byteArray + } + + /// + /// Converts a UTF-8 String to a byte array. + /// + /// - Parameter string: the string + /// + /// - Returns: A byte array + /// + public static func byteArray(from string: String) -> [UInt8] { + + let array = [UInt8](string.utf8) + return array + } + + /// + /// Converts a string of hexadecimal digits to an `NSData` object. + /// + /// - Parameter string: The hex string (must contain an even number of digits) + /// + /// - Returns: An `NSData` object + /// + public static func data(fromHex string: String) -> NSData { + + let a = byteArray(fromHex: string) + return NSData(bytes:a, length:a.count) + } + + /// + /// Converts a string of hexadecimal digits to an `Data` object. + /// + /// - Parameter string: The hex string (must contain an even number of digits) + /// + /// - Returns: An `Data` object + /// + public static func data(fromHex string: String) -> Data { + + let a = byteArray(fromHex: string) + return Data(bytes: a, count: a.count) + } + + /// + /// Converts a byte array to an `NSData` object. + /// + /// - Parameter byteArray: The byte array + /// + /// - Returns: An `NSData` object + /// + public static func data(from byteArray: [UInt8]) -> NSData { + + return NSData(bytes:byteArray, length:byteArray.count) + } + + /// + /// Converts a byte array to an `Data` object. + /// + /// - Parameter byteArray: The byte array + /// + /// - Returns: An `Data` object + /// + public static func data(from byteArray: [UInt8]) -> Data { + + return Data(bytes: byteArray, count: byteArray.count) + } + + /// + /// Converts a byte array to a string of hexadecimal digits. + /// + /// - Parameters: + /// - byteArray: The Swift array + /// - uppercase: True to use uppercase for letter digits, lowercase otherwise + /// + /// - Returns: A String + /// + public static func hexString(from byteArray: [UInt8], uppercase: Bool = false) -> String { + + return byteArray.map() { String(format: (uppercase) ? "%02X" : "%02x", $0) }.reduce("", +) + } + + /// + /// Converts a Swift array to an `NSString` object. + /// + /// - Parameters: + /// - byteArray: The Swift array + /// - uppercase: True to use uppercase for letter digits, lowercase otherwise + /// + /// - Returns: An `NSString` object + /// + public static func hexNSString(from byteArray: [UInt8], uppercase: Bool = false) -> NSString { + + let formatString = (uppercase) ? "%02X" : "%02x" + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + return byteArray.map() { String(format: formatString, $0) }.reduce("", +) as NSString + #else + let aString = byteArray.map() { String(format: formatString, $0) }.reduce("", +) + return NSString(string: aString) + #endif + } + + /// + /// Converts a byte array to a String containing a comma separated list of bytes. + /// This is used to generate test data programmatically. + /// + /// - Parameter byteArray: The byte array + /// + /// - Returns: A String + /// + public static func hexList(from byteArray: [UInt8]) -> String { + + return byteArray.map() { String(format:"0x%02x, ", $0) }.reduce("", +) + } + + /// + /// Zero pads a byte array such that it is an integral number of `blockSizeinBytes` long. + /// + /// - Parameters: + /// - byteArray: The byte array + /// - blockSizeInBytes: The block size in bytes. + /// + /// - Returns: A Swift string + /// + public static func zeroPad(byteArray: [UInt8], blockSize: Int) -> [UInt8] { + + let pad = blockSize - (byteArray.count % blockSize) + guard pad != 0 else { + return byteArray + } + return byteArray + Array(repeating: 0, count: pad) + } + + /// + /// Zero pads a String (after UTF8 conversion) such that it is an integral number of `blockSizeinBytes` long. + /// + /// - Parameters: + /// - string: The String + /// - blockSizeInBytes: The block size in bytes + /// + /// - Returns: A byte array + /// + public static func zeroPad(string: String, blockSize: Int) -> [UInt8] { + + return zeroPad(byteArray: Array(string.utf8), blockSize: blockSize) + } + +} diff --git a/Sources/Encryption/DerivedKey.swift b/Sources/Encryption/DerivedKey.swift index a23c093..7a94bc7 100644 --- a/Sources/Encryption/DerivedKey.swift +++ b/Sources/Encryption/DerivedKey.swift @@ -7,7 +7,6 @@ // import Foundation -import Cryptor protocol DerivedKey { func calculate(password: Data) throws -> Data diff --git a/Sources/KeystoreManager/BIP32HDNode.swift b/Sources/KeystoreManager/BIP32HDNode.swift index c7c0385..21eab1b 100644 --- a/Sources/KeystoreManager/BIP32HDNode.swift +++ b/Sources/KeystoreManager/BIP32HDNode.swift @@ -7,7 +7,7 @@ // import BigInt -import Cryptor +//import Cryptor import Foundation extension UInt32 { diff --git a/Sources/KeystoreManager/BIP39.swift b/Sources/KeystoreManager/BIP39.swift index 3f8e8a3..934643b 100644 --- a/Sources/KeystoreManager/BIP39.swift +++ b/Sources/KeystoreManager/BIP39.swift @@ -6,7 +6,7 @@ // Copyright © 2018 Bankex Foundation. All rights reserved. // -import Cryptor +//import Cryptor import Foundation /// Mnemonics language diff --git a/Tests/KeystoreTests.swift b/Tests/KeystoreTests.swift index 8419c66..dad9518 100644 --- a/Tests/KeystoreTests.swift +++ b/Tests/KeystoreTests.swift @@ -7,7 +7,7 @@ // import XCTest -import Cryptor +//import Cryptor @testable import web3swift diff --git a/web3swift.podspec b/web3swift.podspec index 8602a01..02fcf9d 100644 --- a/web3swift.podspec +++ b/web3swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'web3swift' - spec.version = '2.1' + spec.version = '2.1.1' spec.ios.deployment_target = "8.0" spec.osx.deployment_target = "10.10" spec.tvos.deployment_target = "9.0" @@ -13,7 +13,5 @@ Pod::Spec.new do |spec| spec.source_files = 'Sources/**/*.swift' spec.dependency 'PromiseKit', '~> 6.4' spec.dependency 'BigInt', '~> 3.1' - spec.dependency 'CryptoSwift', '~> 0.12' spec.dependency 'secp256k1.swift' - spec.dependency 'BlueCryptor', :git => 'https://github.com/v57/BlueCryptor.git' end diff --git a/web3swift.xcodeproj/project.pbxproj b/web3swift.xcodeproj/project.pbxproj index 9ed4bae..f9b4aa5 100644 --- a/web3swift.xcodeproj/project.pbxproj +++ b/web3swift.xcodeproj/project.pbxproj @@ -123,6 +123,17 @@ 13A93F5A21B0A36900F115E4 /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13A93F5921B0A36900F115E4 /* AES.swift */; }; 13A93F6021B172D900F115E4 /* Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13A93F5F21B172D900F115E4 /* Transaction.swift */; }; 13AC5D3E21A5D109009D0309 /* SolidityDataReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AC5D3D21A5D109009D0309 /* SolidityDataReader.swift */; }; + 13C335F521B5E00E00F33F5E /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335EA21B5E00D00F33F5E /* Random.swift */; }; + 13C335F621B5E00E00F33F5E /* SSLPointerTricks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335EB21B5E00D00F33F5E /* SSLPointerTricks.swift */; }; + 13C335F721B5E00E00F33F5E /* StreamCryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335EC21B5E00D00F33F5E /* StreamCryptor.swift */; }; + 13C335F821B5E00E00F33F5E /* Updatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335ED21B5E00E00F33F5E /* Updatable.swift */; }; + 13C335F921B5E00E00F33F5E /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335EE21B5E00E00F33F5E /* Utilities.swift */; }; + 13C335FA21B5E00E00F33F5E /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335EF21B5E00E00F33F5E /* Status.swift */; }; + 13C335FB21B5E00E00F33F5E /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335F021B5E00E00F33F5E /* Cryptor.swift */; }; + 13C335FC21B5E00E00F33F5E /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335F121B5E00E00F33F5E /* HMAC.swift */; }; + 13C335FD21B5E00E00F33F5E /* Digest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335F221B5E00E00F33F5E /* Digest.swift */; }; + 13C335FE21B5E00E00F33F5E /* Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335F321B5E00E00F33F5E /* Crypto.swift */; }; + 13C335FF21B5E00E00F33F5E /* KeyDerivation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C335F421B5E00E00F33F5E /* KeyDerivation.swift */; }; 13D2576E21ADCD4400D382D1 /* DerivedKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13D2576D21ADCD4400D382D1 /* DerivedKey.swift */; }; /* End PBXBuildFile section */ @@ -252,6 +263,17 @@ 13A93F5921B0A36900F115E4 /* AES.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = ""; }; 13A93F5F21B172D900F115E4 /* Transaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transaction.swift; sourceTree = ""; }; 13AC5D3D21A5D109009D0309 /* SolidityDataReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolidityDataReader.swift; sourceTree = ""; }; + 13C335EA21B5E00D00F33F5E /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = ""; }; + 13C335EB21B5E00D00F33F5E /* SSLPointerTricks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSLPointerTricks.swift; sourceTree = ""; }; + 13C335EC21B5E00D00F33F5E /* StreamCryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamCryptor.swift; sourceTree = ""; }; + 13C335ED21B5E00E00F33F5E /* Updatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Updatable.swift; sourceTree = ""; }; + 13C335EE21B5E00E00F33F5E /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; + 13C335EF21B5E00E00F33F5E /* Status.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = ""; }; + 13C335F021B5E00E00F33F5E /* Cryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = ""; }; + 13C335F121B5E00E00F33F5E /* HMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMAC.swift; sourceTree = ""; }; + 13C335F221B5E00E00F33F5E /* Digest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Digest.swift; sourceTree = ""; }; + 13C335F321B5E00E00F33F5E /* Crypto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Crypto.swift; sourceTree = ""; }; + 13C335F421B5E00E00F33F5E /* KeyDerivation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyDerivation.swift; sourceTree = ""; }; 13D2576D21ADCD4400D382D1 /* DerivedKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DerivedKey.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -539,9 +561,28 @@ path = Transactions; sourceTree = ""; }; + 13C335E921B5DFDD00F33F5E /* Cryptor */ = { + isa = PBXGroup; + children = ( + 13C335F321B5E00E00F33F5E /* Crypto.swift */, + 13C335F021B5E00E00F33F5E /* Cryptor.swift */, + 13C335F221B5E00E00F33F5E /* Digest.swift */, + 13C335F121B5E00E00F33F5E /* HMAC.swift */, + 13C335F421B5E00E00F33F5E /* KeyDerivation.swift */, + 13C335EA21B5E00D00F33F5E /* Random.swift */, + 13C335EB21B5E00D00F33F5E /* SSLPointerTricks.swift */, + 13C335EF21B5E00E00F33F5E /* Status.swift */, + 13C335EC21B5E00D00F33F5E /* StreamCryptor.swift */, + 13C335ED21B5E00E00F33F5E /* Updatable.swift */, + 13C335EE21B5E00E00F33F5E /* Utilities.swift */, + ); + path = Cryptor; + sourceTree = ""; + }; 13D2577021ADEE3D00D382D1 /* Encryption */ = { isa = PBXGroup; children = ( + 13C335E921B5DFDD00F33F5E /* Cryptor */, 13A93F5921B0A36900F115E4 /* AES.swift */, 13D2576D21ADCD4400D382D1 /* DerivedKey.swift */, 139947F121B0586200EA5DB6 /* PrivateKey.swift */, @@ -710,6 +751,7 @@ 1317BD84218C526300D6D095 /* Web3+Methods.swift in Sources */, 1317BDC9218C526300D6D095 /* Array+Extension.swift in Sources */, 1317BD7F218C526300D6D095 /* Web3+JSONRPC.swift in Sources */, + 13C335F721B5E00E00F33F5E /* StreamCryptor.swift in Sources */, 13765D6E21964A9B005C483C /* W3TransactionIntermediate.swift in Sources */, 1317BDC8218C526300D6D095 /* Base58.swift in Sources */, 13765D7621964A9B005C483C /* W3Personal.swift in Sources */, @@ -723,6 +765,7 @@ 1317BD75218C526300D6D095 /* EthereumFilterEncodingExtensions.swift in Sources */, 13765D6A21964A9B005C483C /* W3TxPool.swift in Sources */, 1317BD82218C526300D6D095 /* Web3+EventParser.swift in Sources */, + 13C335FB21B5E00E00F33F5E /* Cryptor.swift in Sources */, 1317BDB1218C526300D6D095 /* IBAN.swift in Sources */, 1317BD7B218C526300D6D095 /* Web3+HttpProvider.swift in Sources */, 1317BDA5218C526300D6D095 /* ABIv2Elements.swift in Sources */, @@ -730,6 +773,7 @@ 1317BD7A218C526300D6D095 /* ERC777.swift in Sources */, 1317BDB6218C526300D6D095 /* EthereumKeystoreV3.swift in Sources */, 13765D7821964A9B005C483C /* W3Transaction.swift in Sources */, + 13C335F821B5E00E00F33F5E /* Updatable.swift in Sources */, 1317BDC6218C526300D6D095 /* NativeTypesEncoding+Extensions.swift in Sources */, 1317BD76218C526300D6D095 /* ContractProtocol.swift in Sources */, 1317BDA2218C526300D6D095 /* Web3+Wallet.swift in Sources */, @@ -739,19 +783,25 @@ 1317BD86218C526300D6D095 /* Web3+TransactionIntermediate.swift in Sources */, 1317BDA3218C526300D6D095 /* Web3+BrowserFunctions.swift in Sources */, 1317BDB4218C526300D6D095 /* BIP39+WordLists.swift in Sources */, + 13C335F921B5E00E00F33F5E /* Utilities.swift in Sources */, 1317BDB9218C526300D6D095 /* EthereumAddress.swift in Sources */, + 13C335FD21B5E00E00F33F5E /* Digest.swift in Sources */, 1317BDA9218C526300D6D095 /* ABIv2ParameterTypes.swift in Sources */, 1317BD89218C526300D6D095 /* Web3+Options.swift in Sources */, 1317BDA7218C526300D6D095 /* ABIv2TypeParser.swift in Sources */, 1317BDAF218C526300D6D095 /* BlockExplorer.swift in Sources */, 1317BDAC218C526300D6D095 /* SolidityDataWriter.swift in Sources */, + 13C335FE21B5E00E00F33F5E /* Crypto.swift in Sources */, 1317BD83218C526300D6D095 /* Web3+Protocols.swift in Sources */, 13A93F5A21B0A36900F115E4 /* AES.swift in Sources */, 1317BD85218C526300D6D095 /* Web3+Eth.swift in Sources */, 1317BD70218C526300D6D095 /* TransactionSigner.swift in Sources */, + 13C335F521B5E00E00F33F5E /* Random.swift in Sources */, 13397055219984480027E27C /* SecurityToken.swift in Sources */, + 13C335FF21B5E00E00F33F5E /* KeyDerivation.swift in Sources */, 13765D6B21964A9B005C483C /* W3Contracts.swift in Sources */, 1317BDB8218C526300D6D095 /* HDPath.swift in Sources */, + 13C335FA21B5E00E00F33F5E /* Status.swift in Sources */, 13765D7021964A9B005C483C /* W3Eth.swift in Sources */, 13D2576E21ADCD4400D382D1 /* DerivedKey.swift in Sources */, 1317BDAA218C526300D6D095 /* SolidityFunction.swift in Sources */, @@ -763,6 +813,7 @@ 1317BD77218C526300D6D095 /* ContractABIv2.swift in Sources */, 1317BDBA218C526300D6D095 /* PlainKeystore.swift in Sources */, 1317BDC1218C526300D6D095 /* String+Extension.swift in Sources */, + 13C335F621B5E00E00F33F5E /* SSLPointerTricks.swift in Sources */, 1317BDA8218C526300D6D095 /* ABIv2Decoding.swift in Sources */, 1317BDC0218C526300D6D095 /* CryptoExtensions.swift in Sources */, 1317BD80218C526300D6D095 /* Web3+Instance.swift in Sources */, @@ -782,6 +833,7 @@ 1317BD88218C526300D6D095 /* Web3+Structures.swift in Sources */, 1317BD81218C526300D6D095 /* Web3+Contract.swift in Sources */, 1317BDB2218C526300D6D095 /* BIP39.swift in Sources */, + 13C335FC21B5E00E00F33F5E /* HMAC.swift in Sources */, 1317BDB5218C526300D6D095 /* BIP32Keystore.swift in Sources */, 1317BD7C218C526300D6D095 /* Web3.swift in Sources */, 1317BDAB218C526300D6D095 /* ABIv2Encoding.swift in Sources */,