Initial commit

This commit is contained in:
oscarz
2024-08-12 10:49:20 +08:00
parent 3002510aaf
commit 00fd0adf89
331 changed files with 53210 additions and 130 deletions

128
Pods/BlueCryptor/Sources/Cryptor/Crypto.swift generated Executable file
View File

@ -0,0 +1,128 @@
//
// 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.
#if swift(>=5.0)
return self.withUnsafeBytes() {
let result = (Digest(using: algorithm).update(from: $0.baseAddress!, byteCount: self.count)?.final())!
let data = type(of: self).init(bytes: result, count: result.count)
return data
}
#else
return self.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) -> 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
}
#endif
}
}
///
/// 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)
}
}

71
Pods/BlueCryptor/Sources/Cryptor/Cryptor.swift generated Executable file
View File

@ -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<UInt8>(repeating: 0, count:byteCount)
var dataOutMoved = 0
(dataOutMoved, self.status) = final(byteArrayOut: &dataOut)
if self.status != .success {
return nil
}
accumulator += dataOut[0..<Int(dataOutMoved)]
return accumulator
}
///
/// Upates the accumulated encrypted/decrypted data with the contents
/// of a raw byte buffer.
///
/// It is not envisaged the users of the framework will need to call this directly.
///
/// - Returns: this Cryptor object or nil if an error occurs (for optional chaining)
///
public func update(from buffer: UnsafeRawPointer, byteCount: Int) -> Self? {
let outputLength = Int(self.getOutputLength(inputByteCount: byteCount, isFinal: false))
var dataOut = Array<UInt8>(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..<Int(dataOutMoved)]
return self
}
}

279
Pods/BlueCryptor/Sources/Cryptor/Digest.swift generated Executable file
View File

@ -0,0 +1,279 @@
//
// Digest.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
typealias CC_LONG = size_t
#endif
///
/// Public API for message digests.
///
/// Usage is straightforward
///
/// ```
/// let s = "The quick brown fox jumps over the lazy dog."
/// var md5: Digest = Digest(using:.md5)
/// md5.update(s)
/// let digest = md5.final()
///```
///
public class Digest: Updatable {
///
/// The status of the Digest.
/// For CommonCrypto this will always be `.Success`.
/// It is here to provide for engines which can fail.
///
public var status = Status.success
///
/// Enumerates available Digest algorithms
///
public enum Algorithm {
/// Message Digest 2 See: http://en.wikipedia.org/wiki/MD2_(cryptography)
case md2
/// Message Digest 4
case md4
/// 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
}
private var engine: DigestEngine
///
/// Create an algorithm-specific digest calculator
///
/// - Parameter alrgorithm: the desired message digest algorithm
///
public init(using algorithm: Algorithm) {
switch algorithm {
case .md2:
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
engine = DigestEngineCC<CC_MD2_CTX>(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<CC_MD4_CTX>(initializer:CC_MD4_Init, updater:CC_MD4_Update, finalizer:CC_MD4_Final, length:CC_MD4_DIGEST_LENGTH)
#elseif os(Linux)
engine = DigestEngineCC<MD4_CTX>(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<CC_MD5_CTX>(initializer:CC_MD5_Init, updater:CC_MD5_Update, finalizer:CC_MD5_Final, length:CC_MD5_DIGEST_LENGTH)
#elseif os(Linux)
engine = DigestEngineCC<MD5_CTX>(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<CC_SHA1_CTX>(initializer:CC_SHA1_Init, updater:CC_SHA1_Update, finalizer:CC_SHA1_Final, length:CC_SHA1_DIGEST_LENGTH)
#elseif os(Linux)
engine = DigestEngineCC<SHA_CTX>(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<CC_SHA256_CTX>(initializer:CC_SHA224_Init, updater:CC_SHA224_Update, finalizer:CC_SHA224_Final, length:CC_SHA224_DIGEST_LENGTH)
#elseif os(Linux)
engine = DigestEngineCC<SHA256_CTX>(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<CC_SHA256_CTX>(initializer:CC_SHA256_Init, updater:CC_SHA256_Update, finalizer:CC_SHA256_Final, length:CC_SHA256_DIGEST_LENGTH)
#elseif os(Linux)
engine = DigestEngineCC<SHA256_CTX>(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<CC_SHA512_CTX>(initializer:CC_SHA384_Init, updater:CC_SHA384_Update, finalizer:CC_SHA384_Final, length:CC_SHA384_DIGEST_LENGTH)
#elseif os(Linux)
engine = DigestEngineCC<SHA512_CTX>(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<CC_SHA512_CTX>(initializer:CC_SHA512_Init, updater:CC_SHA512_Update, finalizer:CC_SHA512_Final, length:CC_SHA512_DIGEST_LENGTH)
#elseif os(Linux)
engine = DigestEngineCC<SHA512_CTX>(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<CTX>: DigestEngine {
typealias Context = UnsafeMutablePointer<CTX>
typealias Buffer = UnsafeRawPointer
typealias Digest = UnsafeMutablePointer<UInt8>
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<UInt8>(repeating: 0, count:digestLength)
_ = finalizer(&digest, context)
return digest
}
}

335
Pods/BlueCryptor/Sources/Cryptor/HMAC.swift generated Executable file
View File

@ -0,0 +1,335 @@
//
// 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)
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())
}
}
#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<CCHmacContext>
#elseif os(Linux)
typealias Context = OpaquePointer?
#endif
/// Status of the calculation
public internal(set) var status: Status = .success
#if 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)
HMAC_Init_wrapper(context, keyBuffer, Int32(keyByteCount), .make(optional: algorithm.nativeValue()))
#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 swift(>=5.0)
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
key.withUnsafeBytes() {
CCHmacInit(context, algorithm.nativeValue(), $0.baseAddress, size_t(key.count))
}
#elseif os(Linux)
_ = key.withUnsafeBytes() {
HMAC_Init_wrapper(context, $0.baseAddress, Int32(key.count), .make(optional: algorithm.nativeValue()))
}
#endif
#else
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
key.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
CCHmacInit(context, algorithm.nativeValue(), buffer, size_t(key.count))
}
#elseif os(Linux)
_ = key.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
HMAC_Init_wrapper(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)
HMAC_Init_wrapper(context, key.bytes, Int32(key.length), .make(optional: algorithm.nativeValue()))
#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)
HMAC_Init_wrapper(context, key, Int32(key.count), .make(optional: algorithm.nativeValue()))
#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)
HMAC_Init_wrapper(context, key, Int32(key.utf8.count), .make(optional: algorithm.nativeValue()))
#endif
}
///
/// Cleanup
///
deinit {
#if os(Linux)
HMAC_CTX_free_wrapper(.make(optional: context))
#else
#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<UInt8>(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
}
}

View File

@ -0,0 +1,211 @@
//
// 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)
/// 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())
}
}
#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<UInt8>(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<UInt8>(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<Int8>, passwordLen: Int, salt: UnsafePointer<UInt8>, saltLen: Int, prf: PseudoRandomAlgorithm, rounds: uint, derivedKey: UnsafeMutablePointer<UInt8>, 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
}
}

106
Pods/BlueCryptor/Sources/Cryptor/Random.swift generated Executable file
View File

@ -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<UInt8>, 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
}
}

View File

@ -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<SSL_CTX>` 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<T>` from an `UnsafePointer<T>`. 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<Pointee>) {
self = ptr
}
static func make(optional ptr: UnsafePointer<Pointee>?) -> UnsafePointer<Pointee>? {
return ptr.map(UnsafePointer<Pointee>.init)
}
static func make(optional ptr: OpaquePointer?) -> UnsafePointer<Pointee>? {
return ptr.map(UnsafePointer<Pointee>.init)
}
}
extension UnsafeMutablePointer {
init(_ ptr: UnsafeMutableRawPointer) {
let x = UnsafeMutablePointer<Pointee>(bitPattern: UInt(bitPattern: ptr))!
self = x
}
static func make(optional ptr: UnsafeMutablePointer<Pointee>?) -> UnsafeMutablePointer<Pointee>? {
return ptr.map(UnsafeMutablePointer<Pointee>.init)
}
static func make(optional ptr: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<Pointee>? {
return ptr.map(UnsafeMutablePointer<Pointee>.init)
}
static func make(optional ptr: OpaquePointer?) -> UnsafeMutablePointer<Pointee>? {
return ptr.map(UnsafeMutablePointer<Pointee>.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<Pointee>(optional ptr: UnsafeMutablePointer<Pointee>?) -> OpaquePointer? {
return ptr.map(OpaquePointer.init)
}
}

288
Pods/BlueCryptor/Sources/Cryptor/Status.swift generated Executable file
View File

@ -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? {
let 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
}
}
}

View File

@ -0,0 +1,939 @@
//
// 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 {
/// 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
/// 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)
/// 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.")
}
#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<CCCryptorRef?>.allocate(capacity: 1)
#elseif os(Linux)
/// OpenSSL Cipher Context
private let context: OpaquePointer? = .init(EVP_CIPHER_CTX_new())
/// 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<UInt8>, 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_ex(.make(optional: self.context), .make(optional: algorithm.nativeValue(options: options)), nil, 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
#if swift(>=5.0)
dataIn.withUnsafeBytes() {
_ = update(bufferIn: $0.baseAddress!, byteCountIn: dataIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved)
}
#else
dataIn.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
_ = update(bufferIn: buffer, byteCountIn: dataIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved)
}
#endif
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<UInt8>, 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<UInt8>, 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_ex(.make(optional: self.context), bufferOut, &outLength)
case .decrypt:
rawStatus = EVP_DecryptFinal_ex(.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
}
}

107
Pods/BlueCryptor/Sources/Cryptor/Updatable.swift generated Executable file
View File

@ -0,0 +1,107 @@
//
// 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? {
#if swift(>=5.0)
_ = data.withUnsafeBytes() {
_ = update(from: $0.baseAddress!, byteCount: size_t(data.count))
}
#else
_ = data.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
_ = update(from: buffer, byteCount: size_t(data.count))
}
#endif
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
}
}

262
Pods/BlueCryptor/Sources/Cryptor/Utilities.swift generated Executable file
View File

@ -0,0 +1,262 @@
//
// 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<Int8>? 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<Int8>?) -> 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:
return nil
}
}
///
/// 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 or [] if the input is not valid hexadecimal
///
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(),
let hexMSN = convert(hexDigit: msn) ,
let hexLSN = convert(hexDigit: lsn) {
byteArray += [ (hexMSN << 4 | hexLSN) ]
} else {
// @TODO: In the next major release this function should throw instead of returning an empty array.
return []
}
}
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<UInt8>(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<UInt8>(string.utf8), blockSize: blockSize)
}
}