Files
swiftGrammar/Pods/BlueCryptor/Sources/Cryptor/KeyDerivation.swift
2024-08-12 10:49:20 +08:00

212 lines
7.7 KiB
Swift
Executable File

//
// 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
}
}