Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5979e307b5 | |||
| 39ff0f9b35 |
@@ -36,8 +36,6 @@ fileprivate enum ApplicationSettingsKeys {
|
||||
static let apiBackendEnvironmentKey = "Settings.application.environment.backend"
|
||||
static let otherEnvironmentKey = "Settings.application.environment.other"
|
||||
static let smartEnvironmentKey = "Settings.application.environment.smart"
|
||||
/// Field in Settings for Firebase token lifetime
|
||||
static let firebaseTokenLifetimeKey = "Settings.application.firebase.token.lifetime"
|
||||
}
|
||||
|
||||
public enum ApplicationSettings {
|
||||
@@ -62,9 +60,7 @@ public enum ApplicationSettings {
|
||||
|
||||
public static var smartsEnvironment: String? { UserDefaults.standard.string(forKey: ApplicationSettingsKeys.smartEnvironmentKey) }
|
||||
|
||||
public static var firebaseTokenLifetime: Int? { UserDefaults.standard.integer(forKey: ApplicationSettingsKeys.firebaseTokenLifetimeKey) }
|
||||
|
||||
public static func clearApiEnvironment() {
|
||||
public static func clearApiEmvironment() {
|
||||
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.ignoreDeviceSatusKey)
|
||||
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.ignoreCreateAccountSatusKey)
|
||||
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.isNetworkLogEnabledKey)
|
||||
@@ -75,7 +71,6 @@ public enum ApplicationSettings {
|
||||
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.apiBackendEnvironmentKey)
|
||||
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.otherEnvironmentKey)
|
||||
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.smartEnvironmentKey)
|
||||
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.firebaseTokenLifetimeKey)
|
||||
|
||||
// TODO: - Need remove after 1.4.0 version
|
||||
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.ignoreDeviceSatusKeyOld)
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// KeychainProtocol.swift
|
||||
//
|
||||
//
|
||||
// Created by Nut.Tech on 16.02.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias Key = CommonKey
|
||||
|
||||
public protocol KeychainProtocol {
|
||||
|
||||
/// Checks if a given common key have a password-saved value
|
||||
/// - Parameter key: Common Key
|
||||
/// - Returns: is value exists
|
||||
func exist(_ key: Key) -> Bool
|
||||
/// Checks if a given common key have a biometric-saved value
|
||||
/// - Parameter key: Common Key
|
||||
/// - Returns: is value exists
|
||||
func bioExist(_ key: Key) -> Bool
|
||||
/// Access to common key value by biometric authenfication
|
||||
subscript(biometric key: Key) -> String? { get }
|
||||
/// Access to common key value by password authenfication
|
||||
subscript(_ key: Key, password password: String) -> String? { get set }
|
||||
}
|
||||
@@ -5,15 +5,18 @@
|
||||
// Created by Juraldinio on 11/27/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LocalAuthentication
|
||||
|
||||
final public class WalletKeychain: KeychainProtocol {
|
||||
|
||||
private enum KeychainLocals {
|
||||
public static let password = "PWD"
|
||||
public static let biometric = "BIO"
|
||||
}
|
||||
extension String {
|
||||
fileprivate static let password = "PWD"
|
||||
fileprivate static let biometric = "BIO"
|
||||
}
|
||||
|
||||
final public class WalletKeychain {
|
||||
|
||||
public typealias Key = CommonKey
|
||||
|
||||
public static let instance = WalletKeychain()
|
||||
|
||||
// MARK: - Init
|
||||
@@ -22,17 +25,17 @@ final public class WalletKeychain: KeychainProtocol {
|
||||
|
||||
// MARK: - Interface
|
||||
|
||||
public func exist(_ key: Key) -> Bool { self.checkProtectedExist(key: key.with(KeychainLocals.password)) }
|
||||
public func exist(_ key: Key) -> Bool { self.checkProtectedExist(key: key.with(.password)) }
|
||||
|
||||
public func bioExist(_ key: Key) -> Bool { self.checkProtectedExist(key: key.with(KeychainLocals.biometric) ) }
|
||||
public func bioExist(_ key: Key) -> Bool { self.checkProtectedExist(key: key.with(.biometric) ) }
|
||||
|
||||
public subscript(biometric key: Key) -> String? {
|
||||
self.loadBiometricProtected(key: key.with(KeychainLocals.biometric))
|
||||
self.loadBiometricProtected(key: key.with(.biometric))
|
||||
.map({ String(data: $0, encoding: .utf8) }) ?? nil
|
||||
}
|
||||
|
||||
public subscript(_ key: Key, password password: String) -> String? {
|
||||
get { loadPassProtected(key: key.with(KeychainLocals.password), password: password).map { String(data: $0, encoding: .utf8) } ?? nil }
|
||||
get { loadPassProtected(key: key.with(.password), password: password).map { String(data: $0, encoding: .utf8) } ?? nil }
|
||||
set { update(key, password: password, newValue: newValue) }
|
||||
}
|
||||
|
||||
@@ -149,13 +152,12 @@ final public class WalletKeychain: KeychainProtocol {
|
||||
// MARK: -
|
||||
|
||||
private func update(_ key: Key, password: String, newValue: String?) {
|
||||
//TODO: Refactor later - updation of biometric entry only when it is available and needed
|
||||
if let value = newValue {
|
||||
setPassProtected(key: key.with(KeychainLocals.password), data: value, password: password)
|
||||
setBiometricEntry(key: key.with(KeychainLocals.biometric), data: value)
|
||||
setPassProtected(key: key.with(.password), data: value, password: password)
|
||||
setBiometricEntry(key: key.with(.biometric), data: value)
|
||||
} else {
|
||||
removeProtected(key: key.with(KeychainLocals.password))
|
||||
removeProtected(key: key.with(KeychainLocals.biometric))
|
||||
removeProtected(key: key.with(.password))
|
||||
removeProtected(key: key.with(.biometric))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,5 @@ public extension Array {
|
||||
subscript(safe index: Index) -> Element? {
|
||||
return (self.startIndex..<self.endIndex) ~= index ? self[index] : nil
|
||||
}
|
||||
}
|
||||
|
||||
public extension Array where Element: Hashable {
|
||||
|
||||
func distinct() -> Array {
|
||||
let set = Set(self)
|
||||
return Array(set)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,7 @@ import Foundation
|
||||
|
||||
public extension Data {
|
||||
|
||||
func jsonDecoded<T: Decodable>(type: T.Type, userInfo: [CodingUserInfoKey: Any]? = nil) -> T? {
|
||||
try? self.makeDecoder(userInfo: userInfo).decode(type, from: self)
|
||||
}
|
||||
func jsonDecoded<T: Decodable>(type: T.Type) -> T? { try? JSONDecoder().decode(type, from: self) }
|
||||
|
||||
func jsonDecoded<T: Decodable>(type: T.Type, userInfo: [CodingUserInfoKey: Any]? = nil) -> [T]? {
|
||||
try? self.makeDecoder(userInfo: userInfo).decode([T].self, from: self)
|
||||
}
|
||||
|
||||
private func makeDecoder(userInfo: [CodingUserInfoKey: Any]?) -> JSONDecoder {
|
||||
let jsonDecoder = JSONDecoder()
|
||||
userInfo >>- { jsonDecoder.userInfo = $0 }
|
||||
return jsonDecoder
|
||||
}
|
||||
func jsonDecoded<T: Decodable>(type: T.Type) -> [T]? { try? JSONDecoder().decode([T].self, from: self) }
|
||||
}
|
||||
|
||||
@@ -34,11 +34,4 @@ final class ArrayTests: XCTestCase {
|
||||
XCTAssertEqual(value!, 1)
|
||||
}
|
||||
|
||||
func testDistinct() {
|
||||
let array = [1, 2, 5, 6, 5, 8, 9, 1, 2, 2, 5]
|
||||
let arrayWithUniqueElements = array.distinct()
|
||||
|
||||
XCTAssertEqual(arrayWithUniqueElements.sorted(), [1, 2, 5, 6, 8, 9])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,15 +16,14 @@ let package = Package(
|
||||
.package(name: "eosswift", path: "../../Vendors/spm/eos-swift"),
|
||||
.package(name: "KeyChainAccess", path: "../../Vendors/spm/KeyChainAccess"),
|
||||
.package(name: "WalletFoundation", path: "../WalletFoundation"),
|
||||
.package(name: "WalletNetwork", path: "../WalletNetwork"),
|
||||
.package(name: "CryptoSwift", path: "../../Vendors/spm/CryptoSwift-1.5.1")
|
||||
.package(name: "WalletNetwork", path: "../WalletNetwork")
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "WalletKit",
|
||||
dependencies: ["CryptoSwift", "eosswift", "KeyChainAccess", "WalletFoundation", "WalletNetwork"],
|
||||
dependencies: ["eosswift", "KeyChainAccess", "WalletFoundation", "WalletNetwork"],
|
||||
path: "./Sources"),
|
||||
.testTarget(
|
||||
name: "WalletKitTests",
|
||||
|
||||
@@ -9,9 +9,16 @@ import Foundation
|
||||
import WalletFoundation
|
||||
import WalletNetwork
|
||||
|
||||
public enum VillageError: Error {
|
||||
case passwordNotMatch
|
||||
}
|
||||
|
||||
final public class Village {
|
||||
|
||||
private static var keychainKeeper = PrivacyKeeper(keychain: WalletKeychain.instance)
|
||||
private enum Constants {
|
||||
static let passwordKey = CommonKey("Account.Service.password")
|
||||
}
|
||||
|
||||
private static var villages = [Village]()
|
||||
|
||||
private let environment: NetworkEnvironment
|
||||
@@ -34,7 +41,6 @@ final public class Village {
|
||||
|
||||
/// Create instance of Village.
|
||||
public static func get(with environment: NetworkEnvironment) -> Village {
|
||||
|
||||
if let village = Self.villages.first(where: { $0.environment.isEquals(other: environment) }) {
|
||||
return village
|
||||
}
|
||||
@@ -93,19 +99,17 @@ final public class Village {
|
||||
}
|
||||
|
||||
/// Create bank that contain Wallets.
|
||||
public func getBank(with password: String, on device: Device) throws -> Bank {
|
||||
public func getBank(with password: String, on device: Device) -> Bank {
|
||||
|
||||
guard Self.keychainKeeper.accept(password: password) else {
|
||||
throw WalletError.passwordNotMatch
|
||||
}
|
||||
|
||||
if let bank = self.houses.first(where: { $0.isEquals(device: device, environment: self.environment, password: password) }) {
|
||||
if let bank = self.houses.first(where: { $0.isEquals(password: password, device: device, environment: self.environment) }) {
|
||||
return bank
|
||||
}
|
||||
let house = VillageHouse.create(password: password,
|
||||
keeper: Self.keychainKeeper,
|
||||
on: device,
|
||||
environment: self.environment)
|
||||
|
||||
if Self.password != password {
|
||||
Self.password = password
|
||||
}
|
||||
|
||||
let house = VillageHouse.create(for: password, on: device, environment: self.environment)
|
||||
self.houses.append(house)
|
||||
|
||||
return house
|
||||
@@ -113,24 +117,31 @@ final public class Village {
|
||||
|
||||
public static func reset() {
|
||||
VillageHouse.removeAllVillagers()
|
||||
Self.keychainKeeper.reset()
|
||||
Self.password = nil
|
||||
}
|
||||
|
||||
// Keychain static methods
|
||||
public static var isPasswordExists: Bool { WalletKeychain.instance.exist(Constants.passwordKey) }
|
||||
|
||||
public static func accept(password: String) -> Bool {
|
||||
Self.keychainKeeper.accept(password: password)
|
||||
public static func getPassword(password: String?) -> String? {
|
||||
if let password {
|
||||
return WalletKeychain.instance[Constants.passwordKey, password: password]
|
||||
} else {
|
||||
return WalletKeychain.instance[biometric: Constants.passwordKey]
|
||||
}
|
||||
}
|
||||
|
||||
public static func passwordViaBiometrics() -> String? {
|
||||
Self.keychainKeeper.passwordByBiometrics()
|
||||
// TODO: Remove this function after refactoring passwords
|
||||
public static func updateCommonPassword(password: String, old: String) throws {
|
||||
if WalletKeychain.instance[Constants.passwordKey, password: old] != nil {
|
||||
Self.password = password
|
||||
} else {
|
||||
throw VillageError.passwordNotMatch
|
||||
}
|
||||
}
|
||||
|
||||
public static var isPasswordExists: Bool {
|
||||
Self.keychainKeeper.isPasswordExists
|
||||
private static var password: String? {
|
||||
get { WalletKeychain.instance[biometric: Constants.passwordKey] }
|
||||
set { WalletKeychain.instance[Constants.passwordKey, password: newValue ?? ""] = newValue }
|
||||
}
|
||||
|
||||
public static func set(password: String, old: String? = nil) throws {
|
||||
try Self.keychainKeeper.update(password: password, old: old)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
//
|
||||
// PrivacyKeeper.swift
|
||||
//
|
||||
//
|
||||
// Created by Nut.Tech on 07.02.2023.
|
||||
//
|
||||
|
||||
import WalletFoundation
|
||||
import Foundation
|
||||
import CryptoSwift
|
||||
|
||||
final class PrivacyKeeper {
|
||||
private typealias CipherData = (key: String, iv: String)
|
||||
private enum Constants {
|
||||
static let oldPasswordKey = CommonKey("PrivacyKeeper.password")
|
||||
static let passwordKey = "PrivacyKeeper.password"
|
||||
static let cipherKey = "PrivacyKeeper.ckey"
|
||||
static let ivKey = "PrivacyKeeper.ivkey"
|
||||
}
|
||||
|
||||
var isPasswordExists: Bool {
|
||||
if let encryptedPasswordKey {
|
||||
return self.keychain.exist(encryptedPasswordKey)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var encryptedPasswordKey: CommonKey?
|
||||
private var cipherData: CipherData
|
||||
private var keychain: KeychainProtocol
|
||||
|
||||
init(keychain: KeychainProtocol) {
|
||||
self.keychain = keychain
|
||||
self.cipherData = Self.initCipherData()
|
||||
self.encryptedPasswordKey = Self.makeEncryptedPasswordCommonKey(cipherData: self.cipherData)
|
||||
}
|
||||
|
||||
/// Cheks if password correct
|
||||
func accept(password: String) -> Bool {
|
||||
self.getPassword(password) == password
|
||||
}
|
||||
|
||||
/// Get password via biometry
|
||||
func passwordByBiometrics() -> String? {
|
||||
guard let encryptedPasswordKey else { return nil }
|
||||
return self.keychain.bioExist(encryptedPasswordKey) ?
|
||||
self.keychain[biometric: encryptedPasswordKey] : nil
|
||||
}
|
||||
|
||||
/// Get private key from keychain
|
||||
/// - Parameters:
|
||||
/// - key: common key in keychain
|
||||
/// - password: current password
|
||||
/// - Returns: private key (if exists)
|
||||
func privateKey(for key: String, password: String?) -> String? {
|
||||
let pwd = password ?? self.passwordByBiometrics()
|
||||
if let pwd {
|
||||
return self.keychain[self.encryptPK(commonKey: key), password: pwd]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Update password to new one.
|
||||
func update(password: String, old: String? = nil) throws {
|
||||
guard self.isPasswordExists else {
|
||||
try self.setPassword(password)
|
||||
return
|
||||
}
|
||||
guard let old, self.accept(password: old) else {
|
||||
throw WalletError.passwordNotMatch
|
||||
}
|
||||
|
||||
try self.setPassword(password)
|
||||
}
|
||||
|
||||
/// Update private key for current account. If you are using biometrics, pass nil in password.
|
||||
/// - Parameters:
|
||||
/// - privateKey: new private key
|
||||
/// - key: common key in keychain
|
||||
/// - password: current password
|
||||
func update(privateKey: String?, for key: String, password: String?) {
|
||||
let pwd = password ?? self.passwordByBiometrics()
|
||||
guard let pwd, self.accept(password: pwd) else { return }
|
||||
self.keychain[self.encryptPK(commonKey: key), password: pwd] = privateKey
|
||||
}
|
||||
|
||||
/// Migrates pasword and private key storage in keychain to new style
|
||||
/// - Parameters:
|
||||
/// - oldKey: old private key common key for keychain
|
||||
/// - newKey: new private key common key for keychain
|
||||
/// - password: current password
|
||||
func migrate(oldKey: CommonKey, newKey: CommonKey, password: String) {
|
||||
self.migratePassword(password: password)
|
||||
guard self.accept(password: password) else { return }
|
||||
self.migratePrivateKey(oldKey: oldKey, newKey: newKey, password: password)
|
||||
self.migrateToEncrypted(commonKey: newKey, password: password)
|
||||
}
|
||||
|
||||
/// Resets all data by making all old data inaccessible
|
||||
func reset() {
|
||||
self.cipherData = Self.createCipherData()
|
||||
self.encryptedPasswordKey = Self.makeEncryptedPasswordCommonKey(cipherData: self.cipherData)
|
||||
}
|
||||
|
||||
/// Gets password if exists. Pass nil to get biometrics password
|
||||
/// - Parameter password: current password
|
||||
/// - Returns: password string (if exists)
|
||||
private func getPassword(_ password: String? = nil) -> String? {
|
||||
if let password {
|
||||
guard let encryptedPasswordKey else { return nil }
|
||||
return self.keychain[encryptedPasswordKey, password: password]
|
||||
} else {
|
||||
return self.passwordByBiometrics()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set password
|
||||
/// - Parameter password: new password
|
||||
private func setPassword(_ password: String) throws {
|
||||
guard let encryptedPasswordKey else {
|
||||
throw WalletError.passwordKeyNotExist
|
||||
}
|
||||
self.keychain[encryptedPasswordKey, password: password] = password
|
||||
}
|
||||
|
||||
/// Migrate private key in keychain from old to new for old versions
|
||||
/// - Parameters:
|
||||
/// - oldKey: old-style key
|
||||
/// - newKey: new-style key
|
||||
/// - password: current password
|
||||
private func migratePrivateKey(oldKey: CommonKey, newKey: CommonKey, password: String) {
|
||||
guard let privateKey = self.keychain[oldKey, password: password] else { return }
|
||||
self.keychain[oldKey, password: password] = nil
|
||||
self.keychain[newKey, password: password] = privateKey
|
||||
}
|
||||
|
||||
/// Migrate to encrypted keys for keychain
|
||||
/// - Parameters:
|
||||
/// - commonKey: old-style common key, without encryption
|
||||
/// - password: current password
|
||||
private func migrateToEncrypted(commonKey: CommonKey, password: String) {
|
||||
guard let privateKey = self.keychain[commonKey, password: password] else { return }
|
||||
self.keychain[commonKey, password: password] = nil
|
||||
self.keychain[self.encryptPK(commonKey: commonKey.rawValue), password: password] = privateKey
|
||||
}
|
||||
|
||||
/// Migrating from an old keychain password key to a new one
|
||||
/// - Parameter password: current password
|
||||
private func migratePassword(password: String) {
|
||||
guard self.keychain[Constants.oldPasswordKey, password: password] != nil,
|
||||
let encryptedPasswordKey else { return }
|
||||
self.keychain[Constants.oldPasswordKey, password: password] = nil
|
||||
self.keychain[encryptedPasswordKey, password: password] = password
|
||||
}
|
||||
|
||||
/// Makes encrypted password key for keychain
|
||||
private static func makeEncryptedPasswordCommonKey(cipherData: CipherData) -> CommonKey? {
|
||||
guard let encryptedCKeyString = try? Self.aesEncrypt(string: Constants.passwordKey,
|
||||
key: cipherData.key,
|
||||
iv: cipherData.iv)
|
||||
else { return nil }
|
||||
return CommonKey(encryptedCKeyString)
|
||||
}
|
||||
|
||||
/// Encrypt key for use in keychain
|
||||
/// - Parameter commonKey: common key, e.g. "user@wallet.privatekey"
|
||||
/// - Returns: encrypted CommonKey object
|
||||
private func encryptPK(commonKey: String) -> CommonKey {
|
||||
let encryptedCKeyString = try? Self.aesEncrypt(string: commonKey,
|
||||
key: self.cipherData.key,
|
||||
iv: self.cipherData.iv)
|
||||
return CommonKey(encryptedCKeyString ?? commonKey)
|
||||
}
|
||||
|
||||
/// Generates data for encoding keys
|
||||
private static func makeCipherData() -> CipherData {
|
||||
let ckey = UUID().uuidString.replacingOccurrences(of:"-",
|
||||
with: "",
|
||||
options: .literal)
|
||||
let iv = String(UUID().uuidString.replacingOccurrences(of:"-",
|
||||
with: "",
|
||||
options: .literal)
|
||||
.dropLast(16))
|
||||
return CipherData(key: ckey, iv: iv)
|
||||
}
|
||||
|
||||
/// Tries to load cipher data and creates it, if doesn't exist
|
||||
private static func initCipherData() -> CipherData {
|
||||
if let ckey = UserDefaults.standard.string(forKey: Constants.cipherKey),
|
||||
let iv = UserDefaults.standard.string(forKey: Constants.ivKey) {
|
||||
return CipherData(key: ckey, iv: iv)
|
||||
} else {
|
||||
return Self.createCipherData()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new data for encoding keychain keys and saves it to defaults
|
||||
private static func createCipherData() -> CipherData {
|
||||
let cipherData = Self.makeCipherData()
|
||||
UserDefaults.standard.set(cipherData.key, forKey: Constants.cipherKey)
|
||||
UserDefaults.standard.set(cipherData.iv, forKey: Constants.ivKey)
|
||||
return cipherData
|
||||
}
|
||||
|
||||
static func aesEncrypt(string: String, key: String, iv: String) throws -> String {
|
||||
guard let data = string.data(using: .utf8) else {
|
||||
throw WalletError.cannotEncryptNonUTF8Data
|
||||
}
|
||||
let encrypted = try AES(key: Array(key.utf8),
|
||||
blockMode: CBC(iv: Array(iv.utf8)))
|
||||
.encrypt([UInt8](data))
|
||||
let encryptedData = Data(encrypted)
|
||||
return encryptedData.base64EncodedString()
|
||||
}
|
||||
|
||||
static func aesDecrypt(string: String, key: String, iv: String) throws -> String {
|
||||
guard let data = Data(base64Encoded: string) else {
|
||||
throw WalletError.cannotDecryptNonUTF8Data
|
||||
}
|
||||
let decrypted = try AES(key: Array(key.utf8),
|
||||
blockMode: CBC(iv: Array(iv.utf8)))
|
||||
.decrypt([UInt8](data))
|
||||
let decryptedData = Data(decrypted)
|
||||
guard let decryptedString = String(bytes: decryptedData.bytes, encoding: .utf8) else {
|
||||
throw WalletError.cannotDecryptData
|
||||
}
|
||||
return decryptedString
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import WalletFoundation
|
||||
import WalletNetwork
|
||||
|
||||
final class VillageHouse: Bank {
|
||||
|
||||
private enum Constants {
|
||||
static let oldKey = "Account.Service.collection"
|
||||
static let key = "Wallet.bank.service"
|
||||
@@ -23,34 +24,36 @@ final class VillageHouse: Bank {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var password: String
|
||||
private let device: Device
|
||||
private let environment: NetworkEnvironment
|
||||
private let keychainKeeper: PrivacyKeeper
|
||||
|
||||
private let activeSubject: CurrentValueSubject<Villager?, Never>
|
||||
private let villagersSubject: CurrentValueSubject<[Villager], Never>
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var cancelables = Set<AnyCancellable>()
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
private init(password: String, keychain: PrivacyKeeper, device: Device, environment: NetworkEnvironment) {
|
||||
private init(password: String, device: Device, environment: NetworkEnvironment) {
|
||||
|
||||
self.password = password
|
||||
self.device = device
|
||||
self.environment = environment
|
||||
self.keychainKeeper = keychain
|
||||
|
||||
let collection = Self.restore(keychain: keychain)
|
||||
Self.migrateIfNeeded(collection: collection, password: password)
|
||||
let collection = Self.restore()
|
||||
self.villagersSubject = CurrentValueSubject(collection)
|
||||
|
||||
let active = Self.active(in: collection, keychain: keychain)
|
||||
let active = Self.active(in: collection)
|
||||
self.activeSubject = CurrentValueSubject(active)
|
||||
|
||||
self.villagersSubject
|
||||
.sink { [weak self] villagers in
|
||||
self?.save(villagers: villagers)
|
||||
}
|
||||
.store(in: &self.cancellables)
|
||||
.store(in: &self.cancelables)
|
||||
|
||||
self.migrate(using: self.password)
|
||||
}
|
||||
|
||||
// MARK: - Bank
|
||||
@@ -62,6 +65,8 @@ final class VillageHouse: Bank {
|
||||
lazy var activePublisher: AnyPublisher<Wallet?, Never> = self.activeSubject.map { $0 }.eraseToAnyPublisher()
|
||||
|
||||
lazy var walletsPublisher: AnyPublisher<[Wallet], Never> = self.villagersSubject.map { $0 }.eraseToAnyPublisher()
|
||||
|
||||
func accept(password: String) -> Bool { self.password == password }
|
||||
|
||||
func remove(wallet: Wallet) throws {
|
||||
|
||||
@@ -102,29 +107,31 @@ final class VillageHouse: Bank {
|
||||
self.activeSubject.value = villager
|
||||
}
|
||||
|
||||
func add(using walletCase: WalletCase, password: String) async throws -> Wallet {
|
||||
let villager = try await Villager.create(walletCase: walletCase,
|
||||
on: self.device,
|
||||
using: self.environment,
|
||||
keychain: self.keychainKeeper)
|
||||
self.add(wallets: [villager], password: password)
|
||||
func add(using walletCase: WalletCase) async throws -> Wallet {
|
||||
let villager = try await Villager.create(walletCase: walletCase, on: self.device, using: self.environment)
|
||||
villager.updatePrivateKey(using: self.password)
|
||||
|
||||
let villagers = self.villagersSubject.value
|
||||
self.villagersSubject.value = villagers + [villager]
|
||||
|
||||
return villager
|
||||
}
|
||||
|
||||
func add(using purses: [Purse], password: String) throws {
|
||||
guard self.keychainKeeper.accept(password: password) else {
|
||||
throw WalletError.passwordNotMatch
|
||||
}
|
||||
func add(using purses: [Purse]) throws {
|
||||
|
||||
let wallets = purses
|
||||
.filter { $0.bank?.isEquals(other: self, password: password) ?? false }
|
||||
.filter { $0.bank?.isEquals(other: self) ?? false }
|
||||
.filter { purse in
|
||||
!self.wallets.contains(where: { $0.name == purse.name && $0.keyType.rawValue == purse.permission.permName })
|
||||
}
|
||||
.map { Villager.create(purse: $0,
|
||||
keeper: self.keychainKeeper) }
|
||||
.map { Villager.create(purse: $0) }
|
||||
|
||||
self.add(wallets: wallets, password: password)
|
||||
wallets
|
||||
.filter { $0.key.privateKey.isExist }
|
||||
.forEach { $0.updatePrivateKey(using: self.password) }
|
||||
|
||||
let villagers = self.villagersSubject.value
|
||||
self.villagersSubject.value = villagers + wallets
|
||||
}
|
||||
|
||||
func restore(using keys: WalletKey) async throws -> PurseHolder {
|
||||
@@ -169,21 +176,22 @@ final class VillageHouse: Bank {
|
||||
}
|
||||
}
|
||||
|
||||
func accept(password: String) -> Bool {
|
||||
self.keychainKeeper.accept(password: password)
|
||||
}
|
||||
|
||||
func isEquals(other: Bank, password: String) -> Bool {
|
||||
func isEquals(other: Bank) -> Bool {
|
||||
guard let house = other as? VillageHouse else { return false }
|
||||
return self.device.isEquals(other: house.device) &&
|
||||
self.environment.isEquals(other: house.environment) &&
|
||||
self.keychainKeeper.accept(password: password) &&
|
||||
other.accept(password: password)
|
||||
return self.password == house.password &&
|
||||
self.device.isEquals(other: house.device) &&
|
||||
self.environment.isEquals(other: house.environment)
|
||||
}
|
||||
|
||||
func switchPassword(_ new: String, old: String) throws {
|
||||
try self.keychainKeeper.update(password: new, old: old)
|
||||
self.wallets.forEach { $0.updatePrivateKeyEncryption(password: new, old: old) }
|
||||
func switchPassword(_ password: String, old: String) throws {
|
||||
|
||||
guard self.accept(password: old) else {
|
||||
throw BankError.passwordNotMatch
|
||||
}
|
||||
|
||||
self.password = password
|
||||
|
||||
old >>- { old in self.villagersSubject.value.forEach { $0.update(password: password, old: old) } }
|
||||
}
|
||||
|
||||
func update(_ keyUpdates: [WalletKeyUpdate], using password: String) async throws -> [WalletKeyUpdateResult] {
|
||||
@@ -208,48 +216,32 @@ final class VillageHouse: Bank {
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
func isEquals(device: Device, environment: NetworkEnvironment, password: String) -> Bool {
|
||||
return self.device.isEquals(other: device) &&
|
||||
self.environment.isEquals(other: environment) &&
|
||||
self.accept(password: password)
|
||||
func isEquals(password: String, device: Device, environment: NetworkEnvironment) -> Bool {
|
||||
return self.password == password &&
|
||||
self.device.isEquals(other: device) &&
|
||||
self.environment.isEquals(other: environment)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func add(wallets: [Villager], password: String) {
|
||||
wallets
|
||||
.filter { $0.key.privateKey.isExist }
|
||||
.forEach { $0.updateKeychain(key: $0.key, using: password) }
|
||||
|
||||
let villagers = self.villagersSubject.value
|
||||
self.villagersSubject.value = villagers + wallets
|
||||
}
|
||||
|
||||
private func save(villagers: [Villager]) {
|
||||
Settings.shared[Constants.Keys.collection] = villagers.jsonData()
|
||||
}
|
||||
|
||||
private static func restore(keychain: PrivacyKeeper) -> [Villager] {
|
||||
private static func restore() -> [Villager] {
|
||||
if let data: Data = Settings.shared[Constants.Keys.collection],
|
||||
let userInfoKey = Villager.keychainUserInfoKey,
|
||||
let villagers: [Villager] = data.jsonDecoded(type: Villager.self,
|
||||
userInfo: [userInfoKey: keychain]) {
|
||||
let villagers: [Villager] = data.jsonDecoded(type: Villager.self) {
|
||||
return villagers
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
private static func migrateIfNeeded(collection: [Villager], password: String) {
|
||||
guard collection.count > 0 else { return }
|
||||
collection.forEach { $0.migrate(password: password) }
|
||||
}
|
||||
|
||||
private func migrate() {
|
||||
private func migrate(using password: String) {
|
||||
|
||||
let migrateWallets: [Villager]
|
||||
if let data = UserDefaults.standard.value(forKey: Constants.oldKey) as? Data,
|
||||
let collection = try? JSONDecoder().decode([Villager.OldVillager].self, from: data) {
|
||||
migrateWallets = collection.compactMap { $0.covert(keeper: self.keychainKeeper) }
|
||||
migrateWallets = collection.compactMap { $0.covert(password: password) }
|
||||
} else {
|
||||
migrateWallets = []
|
||||
}
|
||||
@@ -271,20 +263,14 @@ final class VillageHouse: Bank {
|
||||
|
||||
// MARK: - Static
|
||||
|
||||
public static func create(password: String, keeper: PrivacyKeeper, on device: Device, environment: NetworkEnvironment) -> VillageHouse {
|
||||
VillageHouse(password: password, keychain: keeper, device: device, environment: environment)
|
||||
static func create(for password: String, on device: Device, environment: NetworkEnvironment) -> VillageHouse {
|
||||
VillageHouse(password: password, device: device, environment: environment)
|
||||
}
|
||||
|
||||
public static func removeAllVillagers() {
|
||||
Settings.shared[Constants.Keys.collection] = Data()
|
||||
}
|
||||
|
||||
private static func active(in collection: [Villager], keychain: PrivacyKeeper) -> Villager? {
|
||||
private static func active(in collection: [Villager]) -> Villager? {
|
||||
|
||||
if let data: Data = Settings.shared[Constants.Keys.current],
|
||||
let userInfoKey = Villager.keychainUserInfoKey,
|
||||
let villager: Villager = data.jsonDecoded(type: Villager.self,
|
||||
userInfo: [userInfoKey: keychain]),
|
||||
let villager: Villager = data.jsonDecoded(type: Villager.self),
|
||||
collection.contains(where: { $0 == villager }) {
|
||||
return villager
|
||||
}
|
||||
@@ -297,4 +283,8 @@ final class VillageHouse: Bank {
|
||||
|
||||
return collection.first(where: { $0.name == name && $0.keyType == keyType })
|
||||
}
|
||||
|
||||
static func removeAllVillagers() {
|
||||
Settings.shared[Constants.Keys.collection] = Data()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,13 @@ final class Villager: Wallet, Codable, CustomStringConvertible {
|
||||
var publicKey: String
|
||||
var keyType: String
|
||||
|
||||
func covert(keeper: PrivacyKeeper) -> Villager? {
|
||||
func covert(password: String) -> Villager? {
|
||||
guard let keyType = WalletKeyType(rawValue: self.keyType),
|
||||
let key = try? WalletKey.restore(using: self.username, type: keyType, publicKey: self.publicKey) else {
|
||||
let key = try? WalletKey.restore(using: self.username, type: keyType, publicKey: self.publicKey, password: password) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Villager(name: self.username,
|
||||
key: key,
|
||||
keyType: keyType,
|
||||
state: .accepted,
|
||||
keychain: keeper)
|
||||
return Villager(name: self.username, key: key, keyType: keyType, state: .accepted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,26 +35,17 @@ final class Villager: Wallet, Codable, CustomStringConvertible {
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
private init(walletCase: WalletCase,
|
||||
keyType: WalletKeyType,
|
||||
state: WalletState,
|
||||
keychain: PrivacyKeeper) {
|
||||
private init(walletCase: WalletCase, keyType: WalletKeyType, state: WalletState) {
|
||||
self.name = walletCase.name
|
||||
self.key = walletCase.key
|
||||
self.keyType = keyType
|
||||
self.keychain = keychain
|
||||
self.stateSubject = CurrentValueSubject<WalletState, Never>(state)
|
||||
}
|
||||
|
||||
private init(name: String,
|
||||
key: WalletKey,
|
||||
keyType: WalletKeyType,
|
||||
state: WalletState,
|
||||
keychain: PrivacyKeeper) {
|
||||
private init(name: String, key: WalletKey, keyType: WalletKeyType, state: WalletState) {
|
||||
self.name = name
|
||||
self.key = key
|
||||
self.keyType = keyType
|
||||
self.keychain = keychain
|
||||
self.stateSubject = CurrentValueSubject<WalletState, Never>(state)
|
||||
}
|
||||
|
||||
@@ -75,10 +62,6 @@ final class Villager: Wallet, Codable, CustomStringConvertible {
|
||||
case state
|
||||
}
|
||||
|
||||
static var keychainUserInfoKey: CodingUserInfoKey? {
|
||||
return CodingUserInfoKey(rawValue: "keychain")
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
@@ -94,11 +77,6 @@ final class Villager: Wallet, Codable, CustomStringConvertible {
|
||||
|
||||
let state = try container.decode(WalletState.self, forKey: .state)
|
||||
self.stateSubject = CurrentValueSubject<WalletState, Never>(state)
|
||||
guard let keychainKey = Self.keychainUserInfoKey,
|
||||
let keeper = decoder.userInfo[keychainKey] as? PrivacyKeeper else {
|
||||
throw WalletError.decodingWithoutKeychainContext
|
||||
}
|
||||
self.keychain = keeper
|
||||
|
||||
// If first version we hold key on flat structure!
|
||||
if let key = try? WalletKey(from: decoder) {
|
||||
@@ -106,7 +84,6 @@ final class Villager: Wallet, Codable, CustomStringConvertible {
|
||||
} else {
|
||||
self.key = try container.decode(WalletKey.self, forKey: .key)
|
||||
}
|
||||
|
||||
// TODO: - NEED COMPLETETASK
|
||||
// self.key = try WalletKey.restore(using: self.name, type: self.keyType, publicKey: publicKey, password: "")
|
||||
}
|
||||
@@ -125,7 +102,6 @@ final class Villager: Wallet, Codable, CustomStringConvertible {
|
||||
private(set) var key: WalletKey
|
||||
let keyType: WalletKeyType
|
||||
var state: WalletState { self.stateSubject.value }
|
||||
private let keychain: PrivacyKeeper
|
||||
|
||||
lazy var statePublisher: AnyPublisher<WalletState, Never> = self.stateSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -167,65 +143,52 @@ final class Villager: Wallet, Codable, CustomStringConvertible {
|
||||
self.stateSubject.value = state
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func updateKeychain(key: WalletKey, using password: String) -> Wallet {
|
||||
if self.keychain.accept(password: password) {
|
||||
self.key = key
|
||||
self.updatePrivateKey(password: password)
|
||||
}
|
||||
func update(key: WalletKey, using passrod: String) -> Wallet {
|
||||
self.key = key
|
||||
self.updatePrivateKey(using: passrod)
|
||||
return self
|
||||
}
|
||||
|
||||
func privateKey(password: String?) -> String? {
|
||||
guard let password, self.keychain.accept(password: password) else { return nil }
|
||||
let key = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
|
||||
return self.keychain.privateKey(for: key.rawValue, password: password)
|
||||
func privateKey(_ value: String?) -> String? {
|
||||
let privateKey: String?
|
||||
if let password = value {
|
||||
privateKey = WalletKeychain.instance[.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey), password: password]
|
||||
} else {
|
||||
privateKey = WalletKeychain.instance[biometric: .key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)]
|
||||
}
|
||||
return privateKey
|
||||
}
|
||||
|
||||
private func updatePrivateKey(password: String) {
|
||||
func updatePrivateKey(using password: String) {
|
||||
guard let privateKey = self.key.privateKey else { return }
|
||||
let key = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
|
||||
self.keychain.update(privateKey: privateKey, for: key.rawValue, password: password)
|
||||
WalletKeychain.instance[.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey),
|
||||
password: password] = privateKey
|
||||
}
|
||||
|
||||
// TODO: - Need review
|
||||
|
||||
func migrate(password: String) {
|
||||
let oldKey = CommonKey.key(self.name, suffix: .privateKey)
|
||||
let newKey = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
|
||||
self.keychain.migrate(oldKey: oldKey, newKey: newKey, password: password)
|
||||
let privateKey = WalletKeychain.instance[.key(self.name, suffix: .privateKey), password: password]
|
||||
WalletKeychain.instance[.key(self.name, suffix: .privateKey), password: ""] = nil
|
||||
WalletKeychain.instance[.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey), password: password] = privateKey
|
||||
}
|
||||
|
||||
/// Updates storage of private key in keychain for new password
|
||||
/// - Parameters:
|
||||
/// - password: new password
|
||||
/// - old: old password (which was stored with private key earlier)
|
||||
func updatePrivateKeyEncryption(password: String, old: String) {
|
||||
let key = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
|
||||
let privateKey = self.privateKey(password: old)
|
||||
self.keychain.update(privateKey: nil, for: key.rawValue, password: old)
|
||||
self.keychain.update(privateKey: privateKey, for: key.rawValue, password: password)
|
||||
func update(password: String, old: String) {
|
||||
WalletKeychain.instance[.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey),
|
||||
password: password] = self.privateKey(old)
|
||||
}
|
||||
|
||||
func clear() {
|
||||
let key = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
|
||||
self.keychain.update(privateKey: nil, for: key.rawValue, password: "")
|
||||
WalletKeychain.instance[.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey), password: ""] = nil
|
||||
}
|
||||
|
||||
// MARK: - Static
|
||||
|
||||
static func create(purse: Purse, keeper: PrivacyKeeper) -> Villager {
|
||||
Villager(name: purse.name,
|
||||
key: purse.key,
|
||||
keyType: purse.keyType,
|
||||
state: .accepted,
|
||||
keychain: keeper)
|
||||
static func create(purse: Purse) -> Villager {
|
||||
Villager(name: purse.name, key: purse.key, keyType: purse.keyType, state: .accepted)
|
||||
}
|
||||
|
||||
static func create(walletCase: WalletCase,
|
||||
on device: Device,
|
||||
using environment: NetworkEnvironment,
|
||||
keychain: PrivacyKeeper) async throws -> Villager {
|
||||
static func create(walletCase: WalletCase, on device: Device, using environment: NetworkEnvironment) async throws -> Villager {
|
||||
|
||||
let service = AccountService(environment: environment)
|
||||
do {
|
||||
@@ -242,18 +205,12 @@ final class Villager: Wallet, Codable, CustomStringConvertible {
|
||||
case .completed: state = .accepted
|
||||
}
|
||||
|
||||
return Villager(walletCase: walletCase,
|
||||
keyType: .owner,
|
||||
state: state,
|
||||
keychain: keychain)
|
||||
return Villager(walletCase: walletCase, keyType: .owner, state: state)
|
||||
|
||||
} catch let NetworkServiceError.gqlApplication(error) {
|
||||
|
||||
if ApplicationSettings.ignoreCreateAccountSatus {
|
||||
return Villager(walletCase: walletCase,
|
||||
keyType: .owner,
|
||||
state: .creating("HELLO"),
|
||||
keychain: keychain)
|
||||
return Villager(walletCase: walletCase, keyType: .owner, state: .creating("HELLO"))
|
||||
}
|
||||
|
||||
let walletError: WalletError
|
||||
|
||||
@@ -12,6 +12,7 @@ import WalletNetwork
|
||||
public enum BankError: Error {
|
||||
case empty
|
||||
case notOwned
|
||||
case passwordNotMatch
|
||||
}
|
||||
|
||||
/// Bank that contain wallets.
|
||||
@@ -24,12 +25,14 @@ public protocol Bank: AnyObject {
|
||||
var wallets: [Wallet] { get }
|
||||
/// Publisher for observe wallet changes.
|
||||
var walletsPublisher: AnyPublisher<[Wallet], Never> { get }
|
||||
/// Check password
|
||||
func accept(password: String) -> Bool
|
||||
/// Activate wallet
|
||||
func activate(wallet: Wallet?) throws
|
||||
/// Create new wallet in bank.
|
||||
func add(using walletCase: WalletCase, password: String) async throws -> Wallet
|
||||
func add(using walletCase: WalletCase) async throws -> Wallet
|
||||
/// Add Purse to bank with converting to Wallet
|
||||
func add(using purses: [Purse], password: String) throws
|
||||
func add(using purses: [Purse]) throws
|
||||
/// Restore wallet by key.
|
||||
func restore(using keys: WalletKey) async throws -> PurseHolder
|
||||
/// Remove wallet from bank.
|
||||
@@ -39,9 +42,7 @@ public protocol Bank: AnyObject {
|
||||
/// Update WalletKey for Wallet
|
||||
func update(_ keyUpdates: [WalletKeyUpdate], using password: String) async throws -> [WalletKeyUpdateResult]
|
||||
/// Compare two banks
|
||||
func isEquals(other: Bank, password: String) -> Bool
|
||||
func isEquals(other: Bank) -> Bool
|
||||
/// Switch password
|
||||
func switchPassword(_ password: String, old: String) throws
|
||||
/// Check if password valid for this bank
|
||||
func accept(password: String) -> Bool
|
||||
}
|
||||
|
||||
@@ -33,18 +33,6 @@ public enum WalletError: Error {
|
||||
case privateKeyNotExists
|
||||
/// Wallet with such name already exists.
|
||||
case exists
|
||||
/// Input password doesn't match keychain password
|
||||
case passwordNotMatch
|
||||
/// Error creating access key for password in keychain
|
||||
case passwordKeyNotExist
|
||||
/// Failed encryption access key for keychain
|
||||
case cannotEncryptNonUTF8Data
|
||||
/// Failed decryption access key for keychain
|
||||
case cannotDecryptNonUTF8Data
|
||||
/// Common decrypt error
|
||||
case cannotDecryptData
|
||||
/// Trying to decode Wallet-protocol object without keychain data
|
||||
case decodingWithoutKeychainContext
|
||||
}
|
||||
|
||||
/// Wallet state
|
||||
@@ -139,13 +127,12 @@ public protocol Wallet: AnyObject {
|
||||
var keyType: WalletKeyType { get }
|
||||
/// State
|
||||
var state: WalletState { get }
|
||||
|
||||
var statePublisher: AnyPublisher<WalletState, Never> { get }
|
||||
|
||||
@discardableResult
|
||||
func updateKeychain(key: WalletKey, using password: String) -> Wallet
|
||||
func privateKey(password: String?) -> String?
|
||||
func updatePrivateKeyEncryption(password: String, old: String)
|
||||
func update(key: WalletKey, using passrod: String) -> Wallet
|
||||
|
||||
func privateKey(_ value: String?) -> String?
|
||||
|
||||
/// Compare two Wallets
|
||||
func isEquals(other: Wallet) -> Bool
|
||||
|
||||
@@ -135,7 +135,7 @@ public enum WalletKey: Codable, Equatable {
|
||||
|
||||
// MARK: - Internal static
|
||||
|
||||
static func restore(using name: String, type: WalletKeyType, publicKey: String) throws -> WalletKey {
|
||||
static func restore(using name: String, type: WalletKeyType, publicKey: String, password: String) throws -> WalletKey {
|
||||
|
||||
throw WalletKeyError.unlock
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
//
|
||||
// JSONParsingTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Nut.Tech on 17.02.2023.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import WalletKit
|
||||
|
||||
fileprivate struct User: Decodable {
|
||||
let id: Int
|
||||
let name: String
|
||||
var employer: String?
|
||||
static var employerUserInfoKey: CodingUserInfoKey? {
|
||||
return CodingUserInfoKey(rawValue: "employer")
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.id = try keyedContainer.decode(Int.self, forKey: .id)
|
||||
self.name = try keyedContainer.decode(String.self, forKey: .name)
|
||||
|
||||
guard let employerKey = Self.employerUserInfoKey,
|
||||
let employer = decoder.userInfo[employerKey] as? String else {
|
||||
return
|
||||
}
|
||||
self.employer = employer
|
||||
}
|
||||
}
|
||||
|
||||
final class JSONParsingTests: XCTestCase {
|
||||
|
||||
private enum Locals {
|
||||
static let username1 = "Username1"
|
||||
static let username2 = "Username2"
|
||||
static let userId1 = 1
|
||||
static let userId2 = 2
|
||||
static let companyName = "Company Name"
|
||||
}
|
||||
|
||||
func testJsonDecoded() {
|
||||
let jsonData = """
|
||||
{"id": \(Locals.userId1), "name": "\(Locals.username1)"}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
guard let user: User = jsonData.jsonDecoded(type: User.self) else {
|
||||
XCTFail("Error decoding data")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(user.id, Locals.userId1)
|
||||
XCTAssertEqual(user.name, Locals.username1)
|
||||
}
|
||||
|
||||
func testJsonDecodedArray() {
|
||||
let jsonData = """
|
||||
[{"id": \(Locals.userId1), "name": "\(Locals.username1)"}, {"id": \(Locals.userId2), "name": "\(Locals.username2)"}]
|
||||
""".data(using: .utf8)!
|
||||
|
||||
guard let users: [User] = jsonData.jsonDecoded(type: User.self) else {
|
||||
XCTFail("Error decoding data")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(users.count, 2)
|
||||
XCTAssertEqual(users[0].id, Locals.userId1)
|
||||
XCTAssertEqual(users[0].name, Locals.username1)
|
||||
XCTAssertEqual(users[1].id, Locals.userId2)
|
||||
XCTAssertEqual(users[1].name, Locals.username2)
|
||||
}
|
||||
|
||||
func testJsonDecodedWithUserInfo() {
|
||||
let jsonData = """
|
||||
{"id": \(Locals.userId1), "name": "\(Locals.username1)"}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
guard let key = CodingUserInfoKey(rawValue: "employer") else {
|
||||
XCTFail("Error creating user key")
|
||||
return
|
||||
}
|
||||
|
||||
let userInfo = [key: Locals.companyName]
|
||||
|
||||
guard let user: User = jsonData.jsonDecoded(type: User.self, userInfo: userInfo) else {
|
||||
XCTFail("Error decoding data")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(user.id, Locals.userId1)
|
||||
XCTAssertEqual(user.name, Locals.username1)
|
||||
XCTAssertEqual(user.employer, Locals.companyName)
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
//
|
||||
// KeychainMock.swift
|
||||
//
|
||||
//
|
||||
// Created by NUT.TECH on 16.02.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WalletFoundation
|
||||
|
||||
final class KeychainMock: KeychainProtocol {
|
||||
|
||||
private enum KeychainMockLocals {
|
||||
static let password = "PWD"
|
||||
static let biometric = "BIO"
|
||||
}
|
||||
|
||||
typealias Value = (value: String, password: String)
|
||||
|
||||
private var storage: [String: Value]
|
||||
|
||||
init(storage: [String: Value]? = nil) {
|
||||
self.storage = storage ?? [:]
|
||||
}
|
||||
|
||||
func exist(_ key: Key) -> Bool {
|
||||
let commonKey = key.with(KeychainMockLocals.password)
|
||||
return self.storage[commonKey.rawValue].isExist
|
||||
}
|
||||
|
||||
func bioExist(_ key: Key) -> Bool {
|
||||
let commonKey = key.with(KeychainMockLocals.biometric)
|
||||
return self.storage[commonKey.rawValue] != nil
|
||||
}
|
||||
|
||||
subscript(biometric key: Key) -> String? {
|
||||
let commonKey = key.with(KeychainMockLocals.biometric).rawValue
|
||||
let object = self.storage[commonKey]
|
||||
return object?.value
|
||||
}
|
||||
|
||||
subscript(key: Key, password password: String) -> String? {
|
||||
get {
|
||||
let commonKey = key.with(KeychainMockLocals.password).rawValue
|
||||
let object = self.storage[commonKey]
|
||||
return object?.password == password ? object?.value : nil
|
||||
}
|
||||
set {
|
||||
self.update(key, password: password, newValue: newValue)
|
||||
}
|
||||
}
|
||||
|
||||
private func update(_ key: Key, password: String, newValue: String?) {
|
||||
if let value = newValue {
|
||||
self.updateStorage(key: key.with(KeychainMockLocals.password), password: password, value: value)
|
||||
self.updateStorage(key: key.with(KeychainMockLocals.biometric), password: password, value: value)
|
||||
} else {
|
||||
self.updateStorage(key: key.with(KeychainMockLocals.password), password: password, value: nil)
|
||||
self.updateStorage(key: key.with(KeychainMockLocals.biometric), password: password, value: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateStorage(key: Key, password: String, value: String?) {
|
||||
let stringKey = key.rawValue
|
||||
if self.storage[stringKey] != nil, self.storage[stringKey]?.password == password {
|
||||
if let value {
|
||||
self.storage[stringKey]?.value = value
|
||||
} else {
|
||||
self.storage.removeValue(forKey: stringKey)
|
||||
}
|
||||
} else if let value {
|
||||
self.storage[stringKey] = Value(value: value, password: password)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
//
|
||||
// PrivacyKeeperTests.swift
|
||||
//
|
||||
//
|
||||
// Created by user on 16.02.2023.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import WalletKit
|
||||
@testable import WalletFoundation
|
||||
|
||||
final class PrivacyKeeperTests: XCTestCase {
|
||||
|
||||
func testAES() throws {
|
||||
let commonKey = "PrivacyKeeper.password"
|
||||
let encryptedKey = "mdHBVh0lxLT8VBJnL9MYuM/pxWi/QmTCXmW8YZQO8Cs="
|
||||
let key = "96080AE5042241D9BFF30330C9625201"
|
||||
let iv = "7C515B5233314B42"
|
||||
do {
|
||||
let encryptedCommonKey = try PrivacyKeeper.aesEncrypt(string: commonKey, key: key, iv: iv)
|
||||
|
||||
let decryptedCommonKey = try PrivacyKeeper.aesDecrypt(string: encryptedCommonKey, key: key, iv: iv)
|
||||
|
||||
XCTAssertEqual(encryptedCommonKey, encryptedKey, "Incorrect encryption result")
|
||||
XCTAssertEqual(decryptedCommonKey, commonKey, "Incorrect decryption result")
|
||||
} catch {
|
||||
XCTFail(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
func testPassword() {
|
||||
let password = "0000"
|
||||
let keychainMock = KeychainMock()
|
||||
let keeper = PrivacyKeeper(keychain: keychainMock)
|
||||
do {
|
||||
XCTAssertFalse(keeper.isPasswordExists, "Incorrect isPasswordExists result")
|
||||
|
||||
try keeper.update(password: password)
|
||||
XCTAssertTrue(keeper.isPasswordExists, "Incorrect isPasswordExists result")
|
||||
XCTAssertTrue(keeper.accept(password: password), "Incorrect password")
|
||||
|
||||
let newPassword = "1111"
|
||||
try keeper.update(password: newPassword, old: password)
|
||||
XCTAssertTrue(keeper.accept(password: newPassword), "Incorrect password")
|
||||
|
||||
let incorrectPassword = "2222"
|
||||
XCTAssertFalse(keeper.accept(password: incorrectPassword), "Incorrect password check")
|
||||
|
||||
keeper.reset()
|
||||
XCTAssertFalse(keeper.accept(password: newPassword), "Incorrect password reset")
|
||||
XCTAssertFalse(keeper.isPasswordExists, "Incorrect isPasswordExists result")
|
||||
} catch {
|
||||
XCTFail(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
func testPrivateKey() {
|
||||
let password = "0000"
|
||||
let privateKey = "privateKey1111"
|
||||
let commonKey = "accountKey@malinka.privateKey"
|
||||
let keychainMock = KeychainMock()
|
||||
let keeper = PrivacyKeeper(keychain: keychainMock)
|
||||
|
||||
do {
|
||||
try keeper.update(password: password)
|
||||
XCTAssertTrue(keeper.accept(password: password), "Incorrect password")
|
||||
|
||||
keeper.update(privateKey: privateKey,
|
||||
for: commonKey,
|
||||
password: password)
|
||||
let readPrivateKey = keeper.privateKey(for: commonKey, password: password)
|
||||
XCTAssertNotNil(readPrivateKey, "Error setting private key to keychain")
|
||||
XCTAssertEqual(readPrivateKey, privateKey, "Incorrect private key stored in keychain")
|
||||
|
||||
keeper.reset()
|
||||
let nilPrivateKey = keeper.privateKey(for: commonKey, password: password)
|
||||
XCTAssertNil(nilPrivateKey, "Incorrect private key reset")
|
||||
} catch {
|
||||
XCTFail(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
func testMigration() {
|
||||
let password = "0000"
|
||||
let privateKey = "privateKey1111"
|
||||
let privateKey2 = "privateKey2222"
|
||||
let migratedCommonKey = "accountName@owner.privateKey"
|
||||
let keyToMigrate = CommonKey("accountKey2@malinka.privateKey")
|
||||
let oldKey = CommonKey.key("accountName", suffix: "privateKey")
|
||||
let newKey = CommonKey.key("accountName@owner", suffix: "privateKey")
|
||||
let storage = ["accountName.privateKey.PWD": KeychainMock.Value(value: privateKey, password: password),
|
||||
"PrivacyKeeper.password.PWD": KeychainMock.Value(value: password, password: password),
|
||||
"accountKey2@malinka.privateKey.PWD": KeychainMock.Value(value: privateKey2, password: password)]
|
||||
let keychainMock = KeychainMock(storage: storage)
|
||||
let keeper = PrivacyKeeper(keychain: keychainMock)
|
||||
keeper.migrate(oldKey: oldKey, newKey: newKey, password: password)
|
||||
XCTAssertNil(keeper.privateKey(for: oldKey.rawValue, password: password), "Incorrect private key")
|
||||
XCTAssertEqual(keeper.privateKey(for: migratedCommonKey, password: password), privateKey, "Incorrect private key")
|
||||
|
||||
keeper.migrate(oldKey: keyToMigrate, newKey: keyToMigrate, password: password)
|
||||
XCTAssertEqual(keeper.privateKey(for: keyToMigrate.rawValue, password: password), privateKey2, "Incorrect private key")
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// swift-tools-version:5.5
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "WalletUIComponents",
|
||||
platforms: [.iOS(.v13)],
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "WalletUIComponents",
|
||||
targets: ["WalletUIComponents"]),
|
||||
],
|
||||
dependencies: [ ],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "WalletUIComponents",
|
||||
dependencies: [ ],
|
||||
path: "./Sources"),
|
||||
.testTarget(
|
||||
name: "WalletUIComponentsTests",
|
||||
dependencies: ["WalletUIComponents"],
|
||||
path: "Tests"//, // Test files
|
||||
// resources: [.copy("TestData")] // The test data files, copy files without modifying them
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// TextField.swift
|
||||
// Malinka
|
||||
//
|
||||
// Created by NUT.Tech on 08.02.2023.
|
||||
// Copyright © 2023 NUT.Tech. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
/// Struct can be replaced to SwiftUI's native TextField after iOS 15.0.
|
||||
/// It was created for access to firstResponder logic which has no analogue
|
||||
/// until iOS 15.0 with @FocusState wrapper type and isFocused property of TextField.
|
||||
|
||||
public struct TextField: UIViewRepresentable {
|
||||
|
||||
@ObservedObject
|
||||
public var viewModel: TextFieldViewModel
|
||||
public let font: UIFont
|
||||
public let textColor: UIColor
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
public init(viewModel: TextFieldViewModel, font: UIFont, textColor: UIColor) {
|
||||
self.viewModel = viewModel
|
||||
self.font = font
|
||||
self.textColor = textColor
|
||||
}
|
||||
|
||||
// MARK: - UIViewRepresentable
|
||||
|
||||
public func makeCoordinator() -> UITextFieldDelegate {
|
||||
TextFieldCoordinator(viewModel: self.viewModel)
|
||||
}
|
||||
|
||||
public func makeUIView(context: Context) -> UITextField {
|
||||
let view = UITextField()
|
||||
view.autocapitalizationType = .none
|
||||
view.autocorrectionType = .no
|
||||
view.clearButtonMode = .never
|
||||
view.font = font
|
||||
view.textColor = self.textColor
|
||||
view.keyboardType = self.viewModel.keyboardType
|
||||
view.placeholder = self.viewModel.placeholder
|
||||
view.addTarget(context.coordinator,
|
||||
action: #selector(TextFieldCoordinator.textViewDidChange),
|
||||
for: .editingChanged)
|
||||
view.delegate = context.coordinator
|
||||
return view
|
||||
}
|
||||
|
||||
public func updateUIView(_ uiView: UITextField, context: Context) {
|
||||
uiView.text = self.viewModel.text
|
||||
self.viewModel.isFirstResponder
|
||||
? uiView.becomeFirstResponder()
|
||||
: uiView.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// TextFieldCoordinator.swift
|
||||
// Malinka
|
||||
//
|
||||
// Created by NUT.Tech on 14.02.2023.
|
||||
// Copyright © 2023 NUT.Tech. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
final class TextFieldCoordinator: NSObject, UITextFieldDelegate {
|
||||
|
||||
private let viewModel: TextFieldViewModel
|
||||
|
||||
init(viewModel: TextFieldViewModel) {
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
@objc
|
||||
func textViewDidChange(_ textField: UITextField) {
|
||||
self.viewModel.text = textField.text ?? ""
|
||||
}
|
||||
|
||||
// MARK: - UITextFieldDelegate
|
||||
|
||||
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
guard self.viewModel.isFirstResponder == textField.isFirstResponder else { return false }
|
||||
self.viewModel.isFirstResponder = true
|
||||
return true
|
||||
}
|
||||
|
||||
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
|
||||
self.viewModel.isFirstResponder = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
//
|
||||
// TextFieldViewModel.swift
|
||||
//
|
||||
//
|
||||
// Created by Juraldinio on 2/19/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
public final class TextFieldViewModel: ObservableObject {
|
||||
|
||||
@Published
|
||||
public var text = ""
|
||||
|
||||
@Published
|
||||
public var isFirstResponder = false
|
||||
|
||||
@Published
|
||||
public var shouldClear: Bool = false
|
||||
|
||||
public let placeholder: String
|
||||
public let keyboardType: UIKeyboardType
|
||||
|
||||
public var isCloseButtonVisible: Bool { !self.text.isEmpty }
|
||||
|
||||
private var canclellables = Set<AnyCancellable>()
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
public init(placeholder: String = "", keyboardType: UIKeyboardType = .default) {
|
||||
self.placeholder = placeholder
|
||||
self.keyboardType = keyboardType
|
||||
|
||||
self.$text
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] value in
|
||||
self?.shouldClear = value == ""
|
||||
}
|
||||
.store(in: &self.canclellables)
|
||||
}
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
public func clearButtonAction() {
|
||||
self.text = ""
|
||||
self.isFirstResponder = false
|
||||
self.shouldClear = false
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Juraldinio on 2/16/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@@ -1,26 +0,0 @@
|
||||
### Xcode ###
|
||||
.build/
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.xcuserstate
|
||||
timeline.xctimeline
|
||||
.swiftpm/xcode
|
||||
CryptoSwift.xcframework
|
||||
|
||||
/Framework
|
||||
.DS_Store
|
||||
Carthage/Build
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
@@ -1,5 +0,0 @@
|
||||
Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com> <758033+krzyzanowskim@users.noreply.github.com>
|
||||
Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com> <krzyzanowskim@users.noreply.github.com>
|
||||
Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com> <marcin@krzyzanowskim.com>
|
||||
Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com> <marcin.krzyzanowski@gmail.com>
|
||||
Luis Reisewitz <reisewitz@me.com> <zweigraf@users.noreply.github.com>
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
--exclude .build, Carthage, DerivedData, .git, Tests/LinuxMain.swift, Tests/CryptoSwiftTests/XCTestManifests.swift, Tests/TestsPerformance/XCTestManifests.swift
|
||||
|
||||
--swiftversion 5.0
|
||||
--allman false
|
||||
--commas always
|
||||
--comments indent
|
||||
--elseposition same-line
|
||||
--empty void
|
||||
--exponentcase lowercase
|
||||
--exponentgrouping disabled
|
||||
--fractiongrouping disabled
|
||||
--header ignore
|
||||
--octalgrouping 4,8
|
||||
--decimalgrouping 3,6
|
||||
--binarygrouping 4,8
|
||||
--hexgrouping ignore
|
||||
--hexliteralcase lowercase
|
||||
--ifdef indent
|
||||
--indent 2
|
||||
--indentcase true
|
||||
--importgrouping testable-bottom
|
||||
--linebreaks lf
|
||||
--operatorfunc spaced
|
||||
--patternlet inline
|
||||
--ranges no-space
|
||||
--self insert
|
||||
--semicolons inline
|
||||
--stripunusedargs closure-only
|
||||
--trimwhitespace always
|
||||
--wraparguments preserve
|
||||
--wrapcollections before-first
|
||||
|
||||
# rules
|
||||
|
||||
--rules indent, braces, isEmpty, redundantBreak, blankLinesAroundMark, blankLinesAtEndOfScope, blankLinesBetweenScopes, consecutiveBlankLines, consecutiveSpaces, duplicateImports, elseOnSameLine, leadingDelimiters, redundantBreak, redundantExtensionACL, redundantFileprivate, redundantGet, redundantInit, redundantLet, redundantNilInit, redundantObjc, redundantParens, redundantPattern, redundantRawValues, redundantReturn, redundantSelf, redundantVoidReturnType, semicolons, sortedImports, spaceAroundBraces, spaceAroundBrackets, spaceAroundComments, spaceAroundGenerics, spaceAroundOperators, spaceAroundParens, spaceInsideBraces, spaceInsideBrackets, specifiers, strongOutlets, strongifiedSelf, todos, void, wrapArguments, yodaConditions, trailingSpace
|
||||
@@ -1,13 +0,0 @@
|
||||
language: generic
|
||||
matrix:
|
||||
include:
|
||||
# Test Ubuntu Linux 14.04
|
||||
- os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
- os: osx
|
||||
osx_image: xcode11.4
|
||||
install:
|
||||
- eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
|
||||
script:
|
||||
- swift test -c release -Xswiftc -enable-testing -Xswiftc -DCI -Xswiftc -Xfrontend -Xswiftc -solver-memory-threshold -Xswiftc -Xfrontend -Xswiftc 999999999
|
||||
@@ -1,342 +0,0 @@
|
||||
1.5.1
|
||||
- Resolve type name clash by renaming BigInt -> BigInteger
|
||||
|
||||
1.5.0
|
||||
- RSA (@NathanFallet)
|
||||
- Workaround for Xcode 13.3.1
|
||||
|
||||
1.4.3
|
||||
- Fix PCBC mode.
|
||||
|
||||
1.4.2
|
||||
- Update Xcode project to Xcode 13
|
||||
- Add SHA3 support for HMAC
|
||||
- Update HMAC.Variant API (deprecate current cases)
|
||||
|
||||
1.4.1
|
||||
- Introduce ISO 10126 padding
|
||||
- fix various compiler warnings
|
||||
- Revert Xcode project deployment target
|
||||
|
||||
1.4.0
|
||||
- Customize CFB segment size (cfb8, cfb128).
|
||||
- Adapt Swift @inlineable for better code optimization
|
||||
|
||||
1.3.8
|
||||
- Revert xcframework revolution. Back to build from sources. (I'm sorry)
|
||||
|
||||
1.3.7
|
||||
- Re-release to workaround Swift Package Manager release
|
||||
|
||||
1.3.6
|
||||
- Fix macOS binary
|
||||
- Windows support
|
||||
|
||||
1.3.5
|
||||
- Re-release binary framework due to codesign issue
|
||||
|
||||
1.3.4
|
||||
- Distribute optimized binary (xcframework) via SPM for apple platforms
|
||||
|
||||
1.3.3
|
||||
- Adds OCB Authenticated-Encryption Algorithm (RFC7253)
|
||||
- build-framework.sh output CryptoSwift.xcframework
|
||||
- Xcode 12.2 maintenance updates
|
||||
- Removed CryptoSwift.playground (couldn't make it work since Xcode 12 update)
|
||||
|
||||
1.3.2
|
||||
- Swift 5.3 update (Xcode 12)
|
||||
- Bump target platform (iOS 9, macOS 10.12)
|
||||
- Allow CMAC with any Cipher
|
||||
- Remove CMAC key limit
|
||||
|
||||
1.3.1
|
||||
- Fix tests
|
||||
- Swift 5.2 update
|
||||
- Address possible timing issue
|
||||
|
||||
1.3.0
|
||||
- Adds ISO-78164 padding
|
||||
- Performance improvements
|
||||
- Swift 5.1 update
|
||||
|
||||
1.2.0
|
||||
- Performance improvements
|
||||
- Workaround Xcode test builds with Xcode 11
|
||||
|
||||
1.1.3
|
||||
- Fix build crash: https://bugs.swift.org/browse/SR-11630
|
||||
- Fixes Xcode project tests build
|
||||
- SwiftFormat all the things
|
||||
- Increase/fix SHA2 data length for big input by use Int64 for calculation
|
||||
|
||||
1.1.2
|
||||
- Fix Swift 5.0 build (for real this time)
|
||||
|
||||
1.1.1
|
||||
- Fix Swift 5.0 build
|
||||
|
||||
1.1.0
|
||||
- Replace RandomBytesSequence with Swift.RandomNumberGenerator
|
||||
- Fix CBC-MAC
|
||||
- Update SPM support
|
||||
- Update for Xcode 11 and Swift 5.1
|
||||
- Xcode: BUILD_LIBRARY_FOR_DISTRIBUTION = YES
|
||||
|
||||
1.0.0
|
||||
- Swift 5
|
||||
- Let's
|
||||
- Celebrate
|
||||
- This
|
||||
- Event
|
||||
- With
|
||||
- 1.0.0 release
|
||||
- After
|
||||
- 4 years
|
||||
- Thank you
|
||||
|
||||
0.15.0
|
||||
- Adds The scrypt Password-Based Key Derivation Function (https://tools.ietf.org/html/rfc7914)
|
||||
- Minor improvements
|
||||
|
||||
0.14.0
|
||||
- Fixed decryption of AES-GCM ciphertexts with custom tag length
|
||||
|
||||
0.13.1
|
||||
- Adds AES-GCM tag length configuration.
|
||||
- Fixes count check for initializing UInt64 from Data.
|
||||
|
||||
0.13.0
|
||||
- Adds CBC-MAC authenticator.
|
||||
- Adds AES-CCM operation mode.
|
||||
|
||||
0.12.0
|
||||
- Swift 4.2 maintenance update.
|
||||
|
||||
0.11.0
|
||||
- API: Cryptor.seek() is throwable
|
||||
- Adds proper stream support for CTR encryption with Updaptable interface.
|
||||
- Refactor internals for the stream cipher modes.
|
||||
- Set minimum deployment target to 8.0 (again).
|
||||
|
||||
0.10.0
|
||||
- API: BlockMode is no longer an enum. Please migrate to eg. CBC() etc...
|
||||
- Adds AES-GCM support. #97 - Feature sponsored by GesundheitsCloud (http://www.gesundheitscloud.de/)
|
||||
- Adds CRC32c support.
|
||||
- Improves AES variant validation.
|
||||
- Fixes empty password in PBKDF2.
|
||||
|
||||
0.9.0
|
||||
- Swift 4.1 compatibility
|
||||
- Added CMAC message authenticator https://tools.ietf.org/html/rfc4493
|
||||
- Added AEADChaCha20Poly1305 (AEAD_CHACHA20_POLY1305) https://tools.ietf.org/html/rfc7539#section-2.8.1
|
||||
|
||||
0.8.3
|
||||
- Fixes SHA3 padding.
|
||||
- Fixes Carthage builds.
|
||||
|
||||
0.8.2
|
||||
- Fixes SHA3 partial updates calculations.
|
||||
- Makes ChaCha20 processing faster again.
|
||||
|
||||
0.8.1
|
||||
- Adds Data(hex:) helper.
|
||||
- Adds HKDF (HMAC-based Extract-and-Expand Key Derivation Function)
|
||||
- Prevent ChaCha overflow error
|
||||
|
||||
0.8.0
|
||||
- Adds SHA3 Keccak variants
|
||||
- Adds String.bytes helper to convert String to array of bytes
|
||||
- Improves AES performance
|
||||
- Speeds up compilation times with Swift 4
|
||||
- Fixes: Blowfish minimum key size is 5
|
||||
- Removes Ciphers "iv" parameter (value moved to BlockMode)
|
||||
- BlockMode uses associated value for IV value where apply e.g. .CBC(iv: ivbytes)
|
||||
- Refactors internal hacks no longer needed with Swift 4
|
||||
|
||||
0.7.2
|
||||
- Adds Padding enum (.pkcs5, .pkcs7, .noPadding, .zeroPadding)
|
||||
- Removes Generics from the public API.
|
||||
- Slightly improves SHA1, SHA2, SHA3 performance.
|
||||
- Update SPM configuration for Swift 4
|
||||
|
||||
0.7.1
|
||||
- Swift 4.0 compatibility release
|
||||
|
||||
0.7.0
|
||||
- Swift 3.2 compatibility release
|
||||
|
||||
0.6.9
|
||||
- Fixed padding issue where padding was not properly added in CTR mode.
|
||||
- Fixed thrown error on decrypting empty string,
|
||||
- Fixed CI build script.
|
||||
- Added String.encryptToBase64()
|
||||
|
||||
0.6.8
|
||||
- Speed up MD5()
|
||||
- Faster Array(hex:)
|
||||
- Improve AES performance
|
||||
- Fix tvOS bitcode
|
||||
- Fix Blowfish CFB, OFB, CTR block modes.
|
||||
- Fix Blowfish for 32-bit arch.
|
||||
- Fix ChaCha20 preconditions
|
||||
|
||||
0.6.7
|
||||
- Release for Xcode 8.2
|
||||
- Fix playground example
|
||||
|
||||
0.6.6
|
||||
- Rework ChaCha20
|
||||
- Fix Poly1305
|
||||
|
||||
0.6.5
|
||||
- Significant performance improvement when processing lange amount of data.
|
||||
- Degeneric functions and change Sequence -> Collection in generic constraints.
|
||||
|
||||
0.6.4
|
||||
- More performance improvements
|
||||
- Add convenient Digest.sha2(bytes:variant)
|
||||
- New: Blowfish cipher
|
||||
|
||||
0.6.3
|
||||
- Hotfix release
|
||||
- Fixes bitPadding() that breaks Digests calculations, introduced in 0.6.2
|
||||
|
||||
0.6.2
|
||||
- SHA performance improvements by using less Swift in Swift
|
||||
- Fix public access to all digests classes
|
||||
|
||||
0.6.1
|
||||
- Update tests.
|
||||
- New: RandomBytesSequence urandom values on Linux.
|
||||
- Throw appropriate error for AES with invalid input where padding is needed.
|
||||
- Improve performance, especially to SHA-1, SHA-2, PBKDF and related.
|
||||
- Set deployment targets for all platform. Fixes Carthage builds.
|
||||
- New: SHA-3 implementation (request #291)
|
||||
- SHA-1 conforms to Updatable protocol and may be calculated incrementally.
|
||||
- SHA-2 conforms to Updatable protocol and may be calculated incrementally.
|
||||
|
||||
0.6.0
|
||||
- Remove bridge() workaround for Linux (not needed)
|
||||
- make MD5() public
|
||||
- Update README
|
||||
- Convenience HMAC initializer for String input
|
||||
|
||||
0.6.0-beta2
|
||||
- SHA-2 fix #319
|
||||
- HashProtocol -> Digest and refactor
|
||||
- MD5 conforms to Updatable protocol and may be calculated incrementally
|
||||
- Cipher protocol accepts Collection input now
|
||||
|
||||
0.6.0-beta1
|
||||
- Swift 3 compatibility
|
||||
- Multiplatform, Single-scheme Xcode Project
|
||||
- Swift Package Manager fully supported (build and tests)
|
||||
- Improved Linux support
|
||||
- Travis configuration added
|
||||
- Public interface tests added
|
||||
- enum Authenticator -> protocol Authenticator
|
||||
- CRC -> Checksum
|
||||
- String.encrypt() returns hex string instead of Array<UInt8>
|
||||
- removed String.decrypt()
|
||||
- enum Hash -> struct Hash
|
||||
- Convenience initializer of Array of bytes with Hex string. Array<UInt8>(hex: "0xb1b1b2b2")
|
||||
- Fix reusability of ChaCha20 instance
|
||||
- Replace optional initializers with throwable initializers
|
||||
- Allow to set initial counter explicitly (AES block modes). RandomAccessCryptor.seek()
|
||||
|
||||
0.5.2
|
||||
- Fix AES-CTR incremental updates. #287
|
||||
- Fixed PBKDF2 tests. #295
|
||||
- Fixed assertion check in PKCS7. #288
|
||||
- Updatable protocol accept SequenceType in place of Array
|
||||
|
||||
0.5.1
|
||||
- Fixed PBKDF2 not taking key length parameter into account
|
||||
- Switch to Array<> in code
|
||||
|
||||
0.5
|
||||
- Added PBKDF1 https://tools.ietf.org/html/rfc2898#section-5.1
|
||||
- Added PBKDF2 https://tools.ietf.org/html/rfc2898#section-5.2
|
||||
- UpdatableCryptor protocol allows incremental encryption stream of data
|
||||
- CryptoSwift.playground
|
||||
- Docs update
|
||||
- Added reflection control to CRC-32 (Luís Silva)
|
||||
- Fix AES.init() (Pascal Pfiffner)
|
||||
|
||||
0.4.1
|
||||
- fix NoPadding()
|
||||
|
||||
0.4
|
||||
- Padding setup is now part of cipher constructor
|
||||
- Added PBKDF2 http://tools.ietf.org/html/rfc2898#section-5.2
|
||||
- Add BlockCipher protocol
|
||||
- Rename Cipher -> CipherProtocol
|
||||
- Remove build-frameworks.sh script
|
||||
- Keep sensitive data in memory with SecureBytes
|
||||
- Allows direct use of HMAC and Poly1305
|
||||
- README update
|
||||
- Fix missing Foundation import on Linux
|
||||
|
||||
0.3.1
|
||||
- replace deprecated Bit with new enum.
|
||||
|
||||
0.3
|
||||
- Swift 2.2 support
|
||||
- use generators for cipher block modes should reduce memory overload.
|
||||
- add OFB block mode
|
||||
- add PCBC block mode
|
||||
- String.decryptBase64ToString to decrypt Base64 encoded strings
|
||||
- broke up complicated expressions which were taking ages to compile
|
||||
|
||||
0.2.3
|
||||
- enable bitcode setting for Debug on an Apple TV
|
||||
- faster compilation times
|
||||
- improve padding functions
|
||||
|
||||
0.2.2
|
||||
- Fix ChaCha20 cipher
|
||||
- Replace for(;;) with for-in
|
||||
- Workaround for "NSString are not yet implicitly convertible to String" on Linux
|
||||
|
||||
0.2.1
|
||||
- Fix linux build
|
||||
- re-add umbrella header
|
||||
|
||||
0.2
|
||||
- Rabbit cipher (RFC4503)
|
||||
- Linux Swift support
|
||||
- Swift Package Manager support
|
||||
- tvOS support
|
||||
- Add optional seed to CRC
|
||||
- Add umbrella header (CryptoSwift.h)
|
||||
- Fix AES in CTR mode
|
||||
- Fix no padding support for CTR and CFB block modes
|
||||
- Fix access to AES.Error and ChaCha20.Error
|
||||
|
||||
0.1.1
|
||||
- Fix Cococapods package (missing Foundation integration)
|
||||
|
||||
0.1.0
|
||||
- Major performance improvements.
|
||||
- Transition from Optionals to throw error.
|
||||
- Replace enum Cipher with protocol for ciphers.
|
||||
- Added CRC16
|
||||
- Fixed AES CFB decryption
|
||||
- Drop internal "Foundation" dependency, nonetheless it is supported as usual.
|
||||
|
||||
0.0.16
|
||||
- Critical fix for private "md5" selector issue (#135)
|
||||
|
||||
0.0.15
|
||||
- Fix 32-bit CTR block mode
|
||||
- Carthage support update
|
||||
- Mark as App-Extension-Safe API
|
||||
|
||||
0.0.14
|
||||
- hexString -> toHextString() #105
|
||||
- CTR (Counter mode)
|
||||
- Hex string is lowercase now
|
||||
- Carthage support
|
||||
- Tests update
|
||||
- Swift 2.0 support - overall update
|
||||
@@ -1 +0,0 @@
|
||||
cryptoswift.io
|
||||
@@ -1,46 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at marcin@krzyzanowskim.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -1,27 +0,0 @@
|
||||
By submitting a pull request, you represent that you have the right to license
|
||||
your contribution to Marcin Krzyżanowski and the community, and agree by submitting the patch that your contributions are licensed under the CryptoSwift project license.
|
||||
|
||||
---
|
||||
|
||||
Before submitting the pull request, please make sure you have tested your changes.
|
||||
|
||||
---
|
||||
|
||||
For new files, please use the correct file header:
|
||||
|
||||
```
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
```
|
||||
@@ -1,117 +0,0 @@
|
||||
For the purpose of tracking copyright, this is the list of individuals and
|
||||
organizations who have contributed source code to CryptoSwift.
|
||||
|
||||
### Contributors
|
||||
|
||||
- Adolfo Martinelli <adolfo@airmap.com>
|
||||
- Aidan Woods <aidantwoods@gmail.com>
|
||||
- Alejandro Isaza <alejandro.isaza@gmail.com>
|
||||
- Alex Binary <alexandre@bintz.io>
|
||||
- Alex Vlasov <alex.m.vlasov@gmail.com>
|
||||
- AlexDenisov <1101.debian@gmail.com>
|
||||
- Alexey Komnin <Interfere@users.noreply.github.com>
|
||||
- Alsey Coleman Miller <alseycmiller@gmail.com>
|
||||
- Andrew Wagner <845683+drewag@users.noreply.github.com>
|
||||
- André Berenguel <andre.berenguel@gmail.com>
|
||||
- Anton <antonmes@users.noreply.github.com>
|
||||
- Ayaka Nonaka <ayanonagon@gmail.com>
|
||||
- Bart Cone <cone.bart@gmail.com>
|
||||
- Bing Cheung <32565605+eungch@users.noreply.github.com>
|
||||
- Bogdan Bystritskiy <q80061@gmail.com>
|
||||
- Brice Cesarin <bc@dejamobile.com>
|
||||
- Bruce Geerdes <bruce@vdub.software>
|
||||
- Bryan Chen <xlchen1291@gmail.com>
|
||||
- Bryant Luk <bryant.luk@bryantluk.com>
|
||||
- C.W. Betts <computers57@hotmail.com>
|
||||
- Caio Mathielo <mathielo@gmail.com>
|
||||
- Cheng-Yu Hsu <m@cyhsu.me>
|
||||
- Chris Amanse <chris@chrisamanse.xyz>
|
||||
- Christian Steffens <cs@hibento.de>
|
||||
- Cihat Gündüz <github@cihatguenduez.de>
|
||||
- Cosmin Baies <cbaies@blockchain.com>
|
||||
- Dave Wood <dave@cerebralgardens.com>
|
||||
- Dima Kalachov <dima.kalachov@gmail.com>
|
||||
- Dusan Klinec <dusan.klinec@gmail.com>
|
||||
- Eneko Alonso <eneko.alonso@gmail.com>
|
||||
- Eugene Berdnikov <eberdnikov@outlook.com>
|
||||
- Evan Maloney <emaloney@gilt.com>
|
||||
- Evin Ugur <evinoog96@gmail.com>
|
||||
- Frank Langel <frank@frankjlangel.com>
|
||||
- Frank Langel <gr.markin@googlemail.com>
|
||||
- Franklin Schrans <f.schrans@me.com>
|
||||
- FreeThinker <59345560+Free-FreeThinker@users.noreply.github.com>
|
||||
- Grzegorz Nowicki <sp3esu@gmail.com>
|
||||
- Hamilton Chapman <hamchapman@gmail.com>
|
||||
- Honza Dvorsky <jan.dvorsky@yahoo.com>
|
||||
- Howtin <gonghao@ghsky.com>
|
||||
- Ibrahim Kteish <ibrahimk@vinelab.com>
|
||||
- Igor Camilo <igor.rcamilo@gmail.com>
|
||||
- Javier Soto <javiers@twitter.com>
|
||||
- Jeremy Greenwood <jeremy@mobelux.com>
|
||||
- Jimmie Johansson <jim@svep.se>
|
||||
- John Hammerlund <johnhammerlund@gmail.com>
|
||||
- Jonas Obrist <ojiidotch@gmail.com>
|
||||
- K.K. POON <noopkk@gmail.com>
|
||||
- Keith Smiley <keithbsmiley@gmail.com>
|
||||
- Koray Koska <koray@koska.at>
|
||||
- Kyle Fuller <kyle@fuller.li>
|
||||
- Lex Tang <lexrus@gmail.com>
|
||||
- Logan Wright <logan.william.wright@gmail.com>
|
||||
- Ludo Galabru <ludovic@galabru.com>
|
||||
- Luis Reisewitz <reisewitz@me.com>
|
||||
- Luís Silva <lm2s@gmx.com>
|
||||
- Madhava Jay <me@madhavajay.com>
|
||||
- Marc Prud'hommeaux <marc@prux.org>
|
||||
- Marcelo Fabri <me@marcelofabri.com>
|
||||
- Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com>
|
||||
- Masayuki Ono <mono0926@gmail.com>
|
||||
- Matias Cudich <mcudich@gmail.com>
|
||||
- Matias Piipari <matias.piipari@gmail.com>
|
||||
- Matthew Chung <matthewchung74@gmail.com>
|
||||
- Michael Ledin <m.ledin@appheads.ru>
|
||||
- Michael Redig <mredig@gmail.com>
|
||||
- Mikael LE GOFF <mikael@mercari.com>
|
||||
- Mo Ramezanpoor <me@mohsenr.com>
|
||||
- Nate West <nwest@detroitlabs.com>
|
||||
- Nathan Fallet <contact@nathanfallet.me>
|
||||
- Nicholas Maccharoli <nicko@screaming-cactus.com>
|
||||
- Nobutaka Yuasa <nobutaka.yuasa@gmail.com>
|
||||
- Oscar De Moya <oscar.demoya@koombea.com>
|
||||
- Pantelis Zirinis <info@paz-labs.com>
|
||||
- Paolo Musolino <info@codeido.com>
|
||||
- Pascal Pfiffner <phase.of.matter@gmail.com>
|
||||
- Pedro Silva <pjlsilva@gmail.com>
|
||||
- Pierpaolo Frasa <pfrasa@chegg.com>
|
||||
- Quinn McHenry <quinn@jqm.us>
|
||||
- R4v3nPr0 <m_117@outlook.com>
|
||||
- Rich Lowenberg <me@richlowenberg.com>
|
||||
- Roman Podymov <podymfrombryansk@yandex.ru>
|
||||
- Ronald Mannak <ronaldmannak@me.com>
|
||||
- SLboat <toaier@me.com>
|
||||
- Sali0m <jehan.vossen@gmail.com>
|
||||
- Sam Soffes <sam@soff.es>
|
||||
- Samuel GRAU <samuel.grau@gmail.com>
|
||||
- ScottieY <scottieyan@gmail.com>
|
||||
- Semen Zhydenko <simeon.zhidenko@gmail.com>
|
||||
- Simon Hartmann <simon.hartmann@andrena.de>
|
||||
- Stefan Hintz <stefan.hintz@bild.de>
|
||||
- Thomas Bibby <hello@ipadtills.com>
|
||||
- Thomas Haak <thomas.haak@teleboy.ch>
|
||||
- TictoDev <kjelldebaeremaecker@icloud.com>
|
||||
- Tomas Kraina <tomas@mailcloud.com>
|
||||
- Tomasz Szulc <mail@szulctomasz.com>
|
||||
- Tomasz Wierzbik <twierzbik@msn.com>
|
||||
- Valeriy Van <github@w7software.com>
|
||||
- Xavier Matos <matos.xav@gmail.com>
|
||||
- Yannick Loriot <yannick.loriot@gmail.com>
|
||||
- Yury Lapitsky <yury.lapitsky@gmail.com>
|
||||
- akreutz <27BBsan08!>
|
||||
- jose <nextgenappsllc@gmail.com>
|
||||
- mrahmiao <mrahmiao@gmail.com>
|
||||
- spatno <seanpatno@gmail.com>
|
||||
- sweepty <adie0423@gmail.com>
|
||||
- venj <im.venj@gmail.com>
|
||||
|
||||
**Updating this list**
|
||||
|
||||
Please do not edit this file manually. It is generated using `./scripts/generate-contributors-list.sh`. If a name is misspelled or appearing multiple times: add an entry in `./.mailmap`
|
||||
@@ -1,49 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,19 +0,0 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "CryptoSwift"
|
||||
s.version = "1.5.1"
|
||||
s.source = { :git => "https://github.com/krzyzanowskim/CryptoSwift.git", :tag => "#{s.version}" }
|
||||
s.summary = "Cryptography in Swift. SHA, MD5, CRC, PBKDF, Poly1305, HMAC, CMAC, HDKF, Scrypt, ChaCha20, Rabbit, Blowfish, AES."
|
||||
s.description = "Cryptography functions and helpers for Swift implemented in Swift. SHA-1, SHA-2, SHA-3, MD5, PBKDF1, PBKDF2, Scrypt, CRC, Poly1305, HMAC, ChaCha20, Rabbit, Blowfish, AES"
|
||||
s.homepage = "https://github.com/krzyzanowskim/CryptoSwift"
|
||||
s.license = {:type => "Attribution", :file => "LICENSE"}
|
||||
s.authors = {'Marcin Krzyżanowski' => 'marcin@krzyzanowskim.com'}
|
||||
s.social_media_url = "https://twitter.com/krzyzanowskim"
|
||||
s.cocoapods_version = '>= 1.10.0'
|
||||
s.swift_version = "5.3"
|
||||
s.ios.deployment_target = "9.0"
|
||||
s.osx.deployment_target = "10.12"
|
||||
s.watchos.deployment_target = "2.0"
|
||||
s.tvos.deployment_target = "9.0"
|
||||
s.source_files = "Sources/CryptoSwift/**/*.swift"
|
||||
s.requires_arc = true
|
||||
end
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>FILEHEADER</key>
|
||||
<string>// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-__YEAR__ Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,11 +0,0 @@
|
||||
Copyright (C) 2014-2017 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
|
||||
In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
- This notice may not be removed or altered from any source or binary distribution.
|
||||
- Redistributions of any form whatsoever must retain the following acknowledgment: 'This product includes software developed by the "Marcin Krzyzanowski" (http://krzyzanowskim.com/).'
|
||||
@@ -1,9 +0,0 @@
|
||||
.PHONY: frameworks
|
||||
|
||||
CWD := $(abspath $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))))
|
||||
|
||||
frameworks:
|
||||
$(CWD)/scripts/build-framework.sh
|
||||
@echo "Framework built in $(CWD)/CryptoSwift.xcframework"
|
||||
|
||||
all: frameworks
|
||||
@@ -1,22 +0,0 @@
|
||||
// swift-tools-version:5.3
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "CryptoSwift",
|
||||
platforms: [
|
||||
.macOS(.v10_12), .iOS(.v9), .tvOS(.v9), .watchOS(.v2)
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "CryptoSwift",
|
||||
targets: ["CryptoSwift"]
|
||||
)
|
||||
],
|
||||
targets: [
|
||||
.target(name: "CryptoSwift"),
|
||||
.testTarget(name: "CryptoSwiftTests", dependencies: ["CryptoSwift"]),
|
||||
.testTarget(name: "TestsPerformance", dependencies: ["CryptoSwift"])
|
||||
],
|
||||
swiftLanguageVersions: [.v5]
|
||||
)
|
||||
@@ -1,578 +0,0 @@
|
||||
[](#installation)
|
||||
|
||||
[](#swift-versions-support)
|
||||
[](https://github.com/apple/swift-package-manager)
|
||||
[](https://cocoapods.org/pods/CryptoSwift)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
|
||||
# CryptoSwift
|
||||
|
||||
Crypto related functions and helpers for [Swift](https://swift.org) implemented in Swift. ([#PureSwift](https://twitter.com/hashtag/pureswift))
|
||||
|
||||
**Note**: The `main` branch follows the latest currently released **version of Swift**. If you need an earlier version for an older version of Swift, you can specify its version in your `Podfile` or use the code on the branch for that version. Older branches are unsupported. Check [versions](#swift-versions-support) for details.
|
||||
|
||||
---
|
||||
|
||||
[Requirements](#requirements) | [Features](#features) | [Contribution](#contribution) | [Installation](#installation) | [Swift versions](#swift-versions-support) | [How-to](#how-to) | [Author](#author) | [License](#license) | [Changelog](#changelog)
|
||||
|
||||
## Sponsorship
|
||||
|
||||
It takes some time to keep it all for your convenience, so maybe spare $1, so I can keep working on that. There are more than 8000 clones daily. If I'd get $1/month from each company that uses my work here, I'd say we're even. Hurry up, find the [Sponsorship](https://github.com/users/krzyzanowskim/sponsorship) button, and fulfill your duty.
|
||||
|
||||
CryptoSwift isn't backed by any big company and is developer in my spare time that I also use to as a freelancer.
|
||||
|
||||
[](http://twitter.com/krzyzanowskim)
|
||||
|
||||
## Requirements
|
||||
Good mood
|
||||
|
||||
## Features
|
||||
|
||||
- Easy to use
|
||||
- Convenient extensions for String and Data
|
||||
- Support for incremental updates (stream, ...)
|
||||
- iOS, Android, macOS, AppleTV, watchOS, Linux support
|
||||
|
||||
#### Hash (Digest)
|
||||
[MD5](http://tools.ietf.org/html/rfc1321)
|
||||
| [SHA1](http://tools.ietf.org/html/rfc3174)
|
||||
| [SHA2-224](http://tools.ietf.org/html/rfc6234)
|
||||
| [SHA2-256](http://tools.ietf.org/html/rfc6234)
|
||||
| [SHA2-384](http://tools.ietf.org/html/rfc6234)
|
||||
| [SHA2-512](http://tools.ietf.org/html/rfc6234)
|
||||
| [SHA3](http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf)
|
||||
|
||||
#### Cyclic Redundancy Check (CRC)
|
||||
[CRC32](http://en.wikipedia.org/wiki/Cyclic_redundancy_check)
|
||||
| [CRC32C](http://en.wikipedia.org/wiki/Cyclic_redundancy_check)
|
||||
| [CRC16](http://en.wikipedia.org/wiki/Cyclic_redundancy_check)
|
||||
|
||||
#### Cipher
|
||||
[AES-128, AES-192, AES-256](http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf)
|
||||
| [ChaCha20](http://cr.yp.to/chacha/chacha-20080128.pdf)
|
||||
| [Rabbit](https://tools.ietf.org/html/rfc4503)
|
||||
| [Blowfish](https://www.schneier.com/academic/blowfish/)
|
||||
|
||||
#### Message authenticators
|
||||
[Poly1305](http://cr.yp.to/mac/poly1305-20050329.pdf)
|
||||
| [HMAC (MD5, SHA1, SHA256)](https://www.ietf.org/rfc/rfc2104.txt)
|
||||
| [CMAC](https://tools.ietf.org/html/rfc4493)
|
||||
| [CBC-MAC](https://en.wikipedia.org/wiki/CBC-MAC)
|
||||
|
||||
#### Cipher mode of operation
|
||||
- Electronic codebook ([ECB](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29))
|
||||
- Cipher-block chaining ([CBC](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29))
|
||||
- Propagating Cipher Block Chaining ([PCBC](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Propagating_Cipher_Block_Chaining_.28PCBC.29))
|
||||
- Cipher feedback ([CFB](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29))
|
||||
- Output Feedback ([OFB](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_Feedback_.28OFB.29))
|
||||
- Counter Mode ([CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29))
|
||||
- Galois/Counter Mode ([GCM](https://csrc.nist.gov/publications/detail/sp/800-38d/final))
|
||||
- Counter with Cipher Block Chaining-Message Authentication Code ([CCM](https://csrc.nist.gov/publications/detail/sp/800-38c/final))
|
||||
- OCB Authenticated-Encryption Algorithm ([OCB](https://tools.ietf.org/html/rfc7253))
|
||||
|
||||
#### Password-Based Key Derivation Function
|
||||
- [PBKDF1](http://tools.ietf.org/html/rfc2898#section-5.1) (Password-Based Key Derivation Function 1)
|
||||
- [PBKDF2](http://tools.ietf.org/html/rfc2898#section-5.2) (Password-Based Key Derivation Function 2)
|
||||
- [HKDF](https://tools.ietf.org/html/rfc5869) (HMAC-based Extract-and-Expand Key Derivation Function)
|
||||
- [Scrypt](https://tools.ietf.org/html/rfc7914) (The scrypt Password-Based Key Derivation Function)
|
||||
|
||||
#### Data padding
|
||||
PKCS#5
|
||||
| [PKCS#7](http://tools.ietf.org/html/rfc5652#section-6.3)
|
||||
| [Zero padding](https://en.wikipedia.org/wiki/Padding_(cryptography)#Zero_padding)
|
||||
| [ISO78164](http://www.embedx.com/pdfs/ISO_STD_7816/info_isoiec7816-4%7Bed21.0%7Den.pdf)
|
||||
| [ISO10126](https://en.wikipedia.org/wiki/Padding_(cryptography)#ISO_10126)
|
||||
| No padding
|
||||
|
||||
#### Authenticated Encryption with Associated Data (AEAD)
|
||||
- [AEAD\_CHACHA20\_POLY1305](https://tools.ietf.org/html/rfc7539#section-2.8)
|
||||
|
||||
## Why
|
||||
[Why?](https://github.com/krzyzanowskim/CryptoSwift/issues/5) [Because I can](https://github.com/krzyzanowskim/CryptoSwift/issues/5#issuecomment-53379391).
|
||||
|
||||
## How do I get involved?
|
||||
|
||||
You want to help, great! Go ahead and fork our repo, make your changes and send us a pull request.
|
||||
|
||||
## Contribution
|
||||
|
||||
Check out [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to help with CryptoSwift.
|
||||
|
||||
- If you found a bug, [open an issue](https://github.com/krzyzanowskim/CryptoSwift/issues).
|
||||
- If you have a feature request, [open an issue](https://github.com/krzyzanowskim/CryptoSwift/issues).
|
||||
|
||||
## Installation
|
||||
|
||||
### Hardened Runtime (macOS) and Xcode
|
||||
|
||||
Binary CryptoSwift.xcframework (Used by Swift Package Manager package integration) won't load properly in your app if the app uses **Sign to Run Locally** Signing Certificate with Hardened Runtime enabled. It is possible to setup Xcode like this. To solve the problem you have two options:
|
||||
- Use proper Signing Certificate, eg. *Development* <- this is the proper action
|
||||
- Use `Disable Library Validation` aka `com.apple.security.cs.disable-library-validation` entitlement
|
||||
|
||||
#### Xcode Project
|
||||
|
||||
To install CryptoSwift, add it as a submodule to your project (on the top level project directory):
|
||||
|
||||
git submodule add https://github.com/krzyzanowskim/CryptoSwift.git
|
||||
|
||||
It is recommended to enable [Whole-Module Optimization](https://swift.org/blog/whole-module-optimizations/) to gain better performance. Non-optimized build results in significantly worse performance.
|
||||
|
||||
#### Swift Package Manager
|
||||
|
||||
You can use [Swift Package Manager](https://swift.org/package-manager/) and specify dependency in `Package.swift` by adding this:
|
||||
|
||||
```swift
|
||||
.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMajor(from: "1.5.1"))
|
||||
```
|
||||
|
||||
See: [Package.swift - manual](http://blog.krzyzanowskim.com/2016/08/09/package-swift-manual/)
|
||||
|
||||
Notice: Swift Package Manager uses debug configuration for debug Xcode build, that may result in significant (up to x10000) worse performance. Performance characteristic is different in Release build. To overcome this prolem, consider embed `CryptoSwift.xcframework` described below.
|
||||
|
||||
#### CocoaPods
|
||||
|
||||
You can use [CocoaPods](https://cocoapods.org/pods/CryptoSwift).
|
||||
|
||||
```ruby
|
||||
pod 'CryptoSwift', '~> 1.4.1'
|
||||
```
|
||||
|
||||
Bear in mind that CocoaPods will build CryptoSwift without [Whole-Module Optimization](https://swift.org/blog/whole-module-optimizations/) that may impact performance. You can change it manually after installation, or use [cocoapods-wholemodule](https://github.com/jedlewison/cocoapods-wholemodule) plugin.
|
||||
|
||||
#### Carthage
|
||||
|
||||
You can use [Carthage](https://github.com/Carthage/Carthage).
|
||||
Specify in Cartfile:
|
||||
|
||||
```ruby
|
||||
github "krzyzanowskim/CryptoSwift"
|
||||
```
|
||||
|
||||
Run `carthage` to build the framework and drag the built CryptoSwift.framework into your Xcode project. Follow [build instructions](https://github.com/Carthage/Carthage#getting-started). [Common issues](https://github.com/krzyzanowskim/CryptoSwift/issues/492#issuecomment-330822874).
|
||||
|
||||
#### XCFramework
|
||||
|
||||
XCFrameworks require Xcode 11 or later and they can be integrated similarly to how we’re used to integrating the `.framework` format.
|
||||
Please use script [scripts/build-framework.sh](scripts/build-framework.sh) to generate binary `CryptoSwift.xcframework` archive that you can use as a dependency in Xcode.
|
||||
|
||||
CryptoSwift.xcframework is a Release (Optimized) binary that offer best available Swift code performance.
|
||||
|
||||
<img width="320" alt="Screen Shot 2020-10-27 at 00 06 32" src="https://user-images.githubusercontent.com/758033/97240586-f0878280-17ee-11eb-9119-e5a960417d04.png">
|
||||
|
||||
#### Embedded Framework
|
||||
|
||||
Embedded frameworks require a minimum deployment target of iOS 9 or macOS Sierra (10.12). Drag the `CryptoSwift.xcodeproj` file into your Xcode project, and add appropriate framework as a dependency to your target. Now select your App and choose the General tab for the app target. Find *Embedded Binaries* and press "+", then select `CryptoSwift.framework` (iOS, macOS, watchOS or tvOS)
|
||||
|
||||

|
||||
|
||||
Sometimes "embedded framework" option is not available. In that case, you have to add new build phase for the target.
|
||||
|
||||

|
||||
|
||||
##### iOS, macOS, watchOS, tvOS
|
||||
|
||||
In the project, you'll find [single scheme](https://mxcl.dev/PromiseKit/news/2016/08/Multiplatform-Single-Scheme-Xcode-Projects/) for all platforms:
|
||||
- CryptoSwift
|
||||
|
||||
#### Swift versions support
|
||||
|
||||
- Swift 1.2: branch [swift12](https://github.com/krzyzanowskim/CryptoSwift/tree/swift12) version <= 0.0.13
|
||||
- Swift 2.1: branch [swift21](https://github.com/krzyzanowskim/CryptoSwift/tree/swift21) version <= 0.2.3
|
||||
- Swift 2.2, 2.3: branch [swift2](https://github.com/krzyzanowskim/CryptoSwift/tree/swift2) version <= 0.5.2
|
||||
- Swift 3.1, branch [swift3](https://github.com/krzyzanowskim/CryptoSwift/tree/swift3) version <= 0.6.9
|
||||
- Swift 3.2, branch [swift32](https://github.com/krzyzanowskim/CryptoSwift/tree/swift32) version = 0.7.0
|
||||
- Swift 4.0, branch [swift4](https://github.com/krzyzanowskim/CryptoSwift/tree/swift4) version <= 0.12.0
|
||||
- Swift 4.2, branch [swift42](https://github.com/krzyzanowskim/CryptoSwift/tree/swift42) version <= 0.15.0
|
||||
- Swift 5.0, branch [swift5](https://github.com/krzyzanowskim/CryptoSwift/tree/swift5) version <= 1.2.0
|
||||
- Swift 5.1, branch [swift5](https://github.com/krzyzanowskim/CryptoSwift/tree/swift51) version <= 1.3.3
|
||||
- Swift 5.3 and newer, branch [main](https://github.com/krzyzanowskim/CryptoSwift/tree/main)
|
||||
|
||||
## How-to
|
||||
|
||||
* [Basics (data types, conversion, ...)](#basics)
|
||||
* [Digest (MD5, SHA...)](#calculate-digest)
|
||||
* [Message authenticators (HMAC, CMAC...)](#message-authenticators-1)
|
||||
* [Password-Based Key Derivation Function (PBKDF2, ...)](#password-based-key-derivation-functions)
|
||||
* [HMAC-based Key Derivation Function (HKDF)](#hmac-based-key-derivation-function)
|
||||
* [Data Padding](#data-padding)
|
||||
* [ChaCha20](#chacha20)
|
||||
* [Rabbit](#rabbit)
|
||||
* [Blowfish](#blowfish)
|
||||
* [AES - Advanced Encryption Standard](#aes)
|
||||
* [AES-GCM](#aes-gcm)
|
||||
* [Authenticated Encryption with Associated Data (AEAD)](#aead)
|
||||
|
||||
##### Basics
|
||||
|
||||
```swift
|
||||
import CryptoSwift
|
||||
```
|
||||
|
||||
CryptoSwift uses array of bytes aka `Array<UInt8>` as a base type for all operations. Every data may be converted to a stream of bytes. You will find convenience functions that accept `String` or `Data`, and it will be internally converted to the array of bytes.
|
||||
|
||||
##### Data types conversion
|
||||
|
||||
For your convenience, **CryptoSwift** provides two functions to easily convert an array of bytes to `Data` or `Data` to an array of bytes:
|
||||
|
||||
Data from bytes:
|
||||
|
||||
```swift
|
||||
let data = Data( [0x01, 0x02, 0x03])
|
||||
```
|
||||
|
||||
`Data` to `Array<UInt8>`
|
||||
|
||||
```swift
|
||||
let bytes = data.bytes // [1,2,3]
|
||||
```
|
||||
|
||||
[Hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) encoding:
|
||||
|
||||
```swift
|
||||
let bytes = Array<UInt8>(hex: "0x010203") // [1,2,3]
|
||||
let hex = bytes.toHexString() // "010203"
|
||||
```
|
||||
|
||||
Build bytes out of `String`
|
||||
```swift
|
||||
let bytes: Array<UInt8> = "cipherkey".bytes // Array("cipherkey".utf8)
|
||||
```
|
||||
|
||||
Also... check out helpers that work with **Base64** encoded data:
|
||||
```swift
|
||||
"aPf/i9th9iX+vf49eR7PYk2q7S5xmm3jkRLejgzHNJs=".decryptBase64ToString(cipher)
|
||||
"aPf/i9th9iX+vf49eR7PYk2q7S5xmm3jkRLejgzHNJs=".decryptBase64(cipher)
|
||||
bytes.toBase64()
|
||||
```
|
||||
|
||||
##### Calculate Digest
|
||||
|
||||
Hashing a data or array of bytes (aka `Array<UInt8>`)
|
||||
```swift
|
||||
/* Hash struct usage */
|
||||
let bytes: Array<UInt8> = [0x01, 0x02, 0x03]
|
||||
let digest = input.md5()
|
||||
let digest = Digest.md5(bytes)
|
||||
```
|
||||
|
||||
```swift
|
||||
let data = Data([0x01, 0x02, 0x03])
|
||||
|
||||
let hash = data.md5()
|
||||
let hash = data.sha1()
|
||||
let hash = data.sha224()
|
||||
let hash = data.sha256()
|
||||
let hash = data.sha384()
|
||||
let hash = data.sha512()
|
||||
```
|
||||
```swift
|
||||
do {
|
||||
var digest = MD5()
|
||||
let partial1 = try digest.update(withBytes: [0x31, 0x32])
|
||||
let partial2 = try digest.update(withBytes: [0x33])
|
||||
let result = try digest.finish()
|
||||
} catch { }
|
||||
```
|
||||
|
||||
Hashing a String and printing result
|
||||
|
||||
```swift
|
||||
let hash = "123".md5() // "123".bytes.md5()
|
||||
```
|
||||
|
||||
##### Calculate CRC
|
||||
|
||||
```swift
|
||||
bytes.crc16()
|
||||
data.crc16()
|
||||
|
||||
bytes.crc32()
|
||||
data.crc32()
|
||||
```
|
||||
|
||||
##### Message authenticators
|
||||
|
||||
```swift
|
||||
// Calculate Message Authentication Code (MAC) for message
|
||||
let key: Array<UInt8> = [1,2,3,4,5,6,7,8,9,10,...]
|
||||
|
||||
try Poly1305(key: key).authenticate(bytes)
|
||||
try HMAC(key: key, variant: .sha256).authenticate(bytes)
|
||||
try CMAC(key: key).authenticate(bytes)
|
||||
```
|
||||
|
||||
##### Password-Based Key Derivation Functions
|
||||
|
||||
```swift
|
||||
let password: Array<UInt8> = Array("s33krit".utf8)
|
||||
let salt: Array<UInt8> = Array("nacllcan".utf8)
|
||||
|
||||
let key = try PKCS5.PBKDF2(password: password, salt: salt, iterations: 4096, keyLength: 32, variant: .sha256).calculate()
|
||||
```
|
||||
|
||||
```swift
|
||||
let password: Array<UInt8> = Array("s33krit".utf8)
|
||||
let salt: Array<UInt8> = Array("nacllcan".utf8)
|
||||
// Scrypt implementation does not implement work parallelization, so `p` parameter will
|
||||
// increase the work time even in multicore systems
|
||||
let key = try Scrypt(password: password, salt: salt, dkLen: 64, N: 16384, r: 8, p: 1).calculate()
|
||||
```
|
||||
|
||||
##### HMAC-based Key Derivation Function
|
||||
|
||||
```swift
|
||||
let password: Array<UInt8> = Array("s33krit".utf8)
|
||||
let salt: Array<UInt8> = Array("nacllcan".utf8)
|
||||
|
||||
let key = try HKDF(password: password, salt: salt, variant: .sha256).calculate()
|
||||
```
|
||||
|
||||
|
||||
##### Data Padding
|
||||
|
||||
Some content-encryption algorithms assume the input length is a multiple of `k` octets, where `k` is greater than one. For such algorithms, the input shall be padded.
|
||||
|
||||
```swift
|
||||
Padding.pkcs7.add(to: bytes, blockSize: AES.blockSize)
|
||||
```
|
||||
|
||||
#### Working with Ciphers
|
||||
##### ChaCha20
|
||||
|
||||
```swift
|
||||
let encrypted = try ChaCha20(key: key, iv: iv).encrypt(message)
|
||||
let decrypted = try ChaCha20(key: key, iv: iv).decrypt(encrypted)
|
||||
```
|
||||
|
||||
##### Rabbit
|
||||
|
||||
```swift
|
||||
let encrypted = try Rabbit(key: key, iv: iv).encrypt(message)
|
||||
let decrypted = try Rabbit(key: key, iv: iv).decrypt(encrypted)
|
||||
```
|
||||
##### Blowfish
|
||||
|
||||
```swift
|
||||
let encrypted = try Blowfish(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).encrypt(message)
|
||||
let decrypted = try Blowfish(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).decrypt(encrypted)
|
||||
```
|
||||
|
||||
##### AES
|
||||
|
||||
Notice regarding padding: *Manual padding of data is optional, and CryptoSwift is using PKCS7 padding by default. If you need to manually disable/enable padding, you can do this by setting parameter for __AES__ class*
|
||||
|
||||
Variant of AES encryption (AES-128, AES-192, AES-256) depends on given key length:
|
||||
|
||||
- AES-128 = 16 bytes
|
||||
- AES-192 = 24 bytes
|
||||
- AES-256 = 32 bytes
|
||||
|
||||
AES-256 example
|
||||
|
||||
```swift
|
||||
let encryptedBytes = try AES(key: [1,2,3,...,32], blockMode: CBC(iv: [1,2,3,...,16]), padding: .pkcs7)
|
||||
```
|
||||
|
||||
Full example:
|
||||
|
||||
```swift
|
||||
let password: [UInt8] = Array("s33krit".utf8)
|
||||
let salt: [UInt8] = Array("nacllcan".utf8)
|
||||
|
||||
/* Generate a key from a `password`. Optional if you already have a key */
|
||||
let key = try PKCS5.PBKDF2(
|
||||
password: password,
|
||||
salt: salt,
|
||||
iterations: 4096,
|
||||
keyLength: 32, /* AES-256 */
|
||||
variant: .sha256
|
||||
).calculate()
|
||||
|
||||
/* Generate random IV value. IV is public value. Either need to generate, or get it from elsewhere */
|
||||
let iv = AES.randomIV(AES.blockSize)
|
||||
|
||||
/* AES cryptor instance */
|
||||
let aes = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7)
|
||||
|
||||
/* Encrypt Data */
|
||||
let inputData = Data()
|
||||
let encryptedBytes = try aes.encrypt(inputData.bytes)
|
||||
let encryptedData = Data(encryptedBytes)
|
||||
|
||||
/* Decrypt Data */
|
||||
let decryptedBytes = try aes.decrypt(encryptedData.bytes)
|
||||
let decryptedData = Data(decryptedBytes)
|
||||
```
|
||||
|
||||
###### All at once
|
||||
```swift
|
||||
do {
|
||||
let aes = try AES(key: "keykeykeykeykeyk", iv: "drowssapdrowssap") // aes128
|
||||
let ciphertext = try aes.encrypt(Array("Nullam quis risus eget urna mollis ornare vel eu leo.".utf8))
|
||||
} catch { }
|
||||
```
|
||||
|
||||
###### Incremental updates
|
||||
|
||||
Incremental operations use instance of Cryptor and encrypt/decrypt one part at a time, this way you can save on memory for large files.
|
||||
|
||||
```swift
|
||||
do {
|
||||
var encryptor = try AES(key: "keykeykeykeykeyk", iv: "drowssapdrowssap").makeEncryptor()
|
||||
|
||||
var ciphertext = Array<UInt8>()
|
||||
// aggregate partial results
|
||||
ciphertext += try encryptor.update(withBytes: Array("Nullam quis risus ".utf8))
|
||||
ciphertext += try encryptor.update(withBytes: Array("eget urna mollis ".utf8))
|
||||
ciphertext += try encryptor.update(withBytes: Array("ornare vel eu leo.".utf8))
|
||||
// finish at the end
|
||||
ciphertext += try encryptor.finish()
|
||||
|
||||
print(ciphertext.toHexString())
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
```
|
||||
|
||||
###### AES Advanced usage
|
||||
```swift
|
||||
let input: Array<UInt8> = [0,1,2,3,4,5,6,7,8,9]
|
||||
|
||||
let key: Array<UInt8> = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
|
||||
let iv: Array<UInt8> = // Random bytes of `AES.blockSize` length
|
||||
|
||||
do {
|
||||
let encrypted = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).encrypt(input)
|
||||
let decrypted = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).decrypt(encrypted)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
```
|
||||
|
||||
AES without data padding
|
||||
|
||||
```swift
|
||||
let input: Array<UInt8> = [0,1,2,3,4,5,6,7,8,9]
|
||||
let encrypted: Array<UInt8> = try! AES(key: Array("secret0key000000".utf8), blockMode: CBC(iv: Array("0123456789012345".utf8)), padding: .noPadding).encrypt(input)
|
||||
```
|
||||
|
||||
Using convenience extensions
|
||||
|
||||
```swift
|
||||
let plain = Data([0x01, 0x02, 0x03])
|
||||
let encrypted = try! plain.encrypt(ChaCha20(key: key, iv: iv))
|
||||
let decrypted = try! encrypted.decrypt(ChaCha20(key: key, iv: iv))
|
||||
```
|
||||
|
||||
##### AES-GCM
|
||||
|
||||
The result of Galois/Counter Mode (GCM) encryption is ciphertext and **authentication tag**, that is later used to decryption.
|
||||
|
||||
encryption
|
||||
|
||||
```swift
|
||||
do {
|
||||
// In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want.
|
||||
let gcm = GCM(iv: iv, mode: .combined)
|
||||
let aes = try AES(key: key, blockMode: gcm, padding: .noPadding)
|
||||
let encrypted = try aes.encrypt(plaintext)
|
||||
let tag = gcm.authenticationTag
|
||||
} catch {
|
||||
// failed
|
||||
}
|
||||
```
|
||||
|
||||
decryption
|
||||
|
||||
```swift
|
||||
do {
|
||||
// In combined mode, the authentication tag is appended to the encrypted message. This is usually what you want.
|
||||
let gcm = GCM(iv: iv, mode: .combined)
|
||||
let aes = try AES(key: key, blockMode: gcm, padding: .noPadding)
|
||||
return try aes.decrypt(encrypted)
|
||||
} catch {
|
||||
// failed
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: GCM instance is not intended to be reused. So you can't use the same `GCM` instance from encoding to also perform decoding.
|
||||
|
||||
##### AES-CCM
|
||||
|
||||
The result of Counter with Cipher Block Chaining-Message Authentication Code encryption is ciphertext and **authentication tag**, that is later used to decryption.
|
||||
|
||||
```swift
|
||||
do {
|
||||
// The authentication tag is appended to the encrypted message.
|
||||
let tagLength = 8
|
||||
let ccm = CCM(iv: iv, tagLength: tagLength, messageLength: ciphertext.count - tagLength, additionalAuthenticatedData: data)
|
||||
let aes = try AES(key: key, blockMode: ccm, padding: .noPadding)
|
||||
return try aes.decrypt(encrypted)
|
||||
} catch {
|
||||
// failed
|
||||
}
|
||||
```
|
||||
|
||||
Check documentation or CCM specification for valid parameters for CCM.
|
||||
|
||||
##### AEAD
|
||||
|
||||
```swift
|
||||
let encrypt = try AEADChaCha20Poly1305.encrypt(plaintext, key: key, iv: nonce, authenticationHeader: header)
|
||||
let decrypt = try AEADChaCha20Poly1305.decrypt(ciphertext, key: key, iv: nonce, authenticationHeader: header, authenticationTag: tagArr: tag)
|
||||
```
|
||||
|
||||
##### RSA
|
||||
|
||||
RSA initialization from parameters
|
||||
|
||||
```swift
|
||||
let input: Array<UInt8> = [0,1,2,3,4,5,6,7,8,9]
|
||||
|
||||
let n: Array<UInt8> = // RSA modulus
|
||||
let e: Array<UInt8> = // RSA public exponent
|
||||
let d: Array<UInt8> = // RSA private exponent
|
||||
|
||||
let rsa = RSA(n: n, e: e, d: d)
|
||||
|
||||
do {
|
||||
let encrypted = try rsa.encrypt(input)
|
||||
let decrypted = try rsa.decrypt(encrypted)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
```
|
||||
|
||||
RSA key generation
|
||||
|
||||
```swift
|
||||
let rsa = RSA(keySize: 2048) // This generates a modulus, public exponent and private exponent with the given size
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
CryptoSwift is owned and maintained by [Marcin Krzyżanowski](http://www.krzyzanowskim.com)
|
||||
|
||||
You can follow me on Twitter at [@krzyzanowskim](http://twitter.com/krzyzanowskim) for project updates and releases.
|
||||
|
||||
# Cryptography Notice
|
||||
|
||||
This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See http://www.wassenaar.org/ for more information.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
|
||||
In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, **an acknowledgment in the product documentation is required**.
|
||||
- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
- This notice may not be removed or altered from any source or binary distribution.
|
||||
- Redistributions of any form whatsoever must retain the following acknowledgment: 'This product includes software developed by the "Marcin Krzyzanowski" (http://krzyzanowskim.com/).'
|
||||
|
||||
## Changelog
|
||||
|
||||
See [CHANGELOG](./CHANGELOG) file.
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// CryptoSwift.h
|
||||
// CryptoSwift
|
||||
//
|
||||
// Created by Sam Soffes on 11/29/15.
|
||||
// Copyright © 2015 Marcin Krzyzanowski. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
//! Project version number for CryptoSwift.
|
||||
FOUNDATION_EXPORT double CryptoSwiftVersionNumber;
|
||||
|
||||
//! Project version string for CryptoSwift.
|
||||
FOUNDATION_EXPORT const unsigned char CryptoSwiftVersionString[];
|
||||
@@ -1,40 +0,0 @@
|
||||
//
|
||||
// AEAD.swift
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
//
|
||||
|
||||
// https://www.iana.org/assignments/aead-parameters/aead-parameters.xhtml
|
||||
|
||||
/// Authenticated Encryption with Associated Data (AEAD)
|
||||
public protocol AEAD {
|
||||
static var kLen: Int { get } // key length
|
||||
static var ivRange: Range<Int> { get } // nonce length
|
||||
}
|
||||
|
||||
extension AEAD {
|
||||
static func calculateAuthenticationTag(authenticator: Authenticator, cipherText: Array<UInt8>, authenticationHeader: Array<UInt8>) throws -> Array<UInt8> {
|
||||
let headerPadding = ((16 - (authenticationHeader.count & 0xf)) & 0xf)
|
||||
let cipherPadding = ((16 - (cipherText.count & 0xf)) & 0xf)
|
||||
|
||||
var mac = authenticationHeader
|
||||
mac += Array<UInt8>(repeating: 0, count: headerPadding)
|
||||
mac += cipherText
|
||||
mac += Array<UInt8>(repeating: 0, count: cipherPadding)
|
||||
mac += UInt64(bigEndian: UInt64(authenticationHeader.count)).bytes()
|
||||
mac += UInt64(bigEndian: UInt64(cipherText.count)).bytes()
|
||||
|
||||
return try authenticator.authenticate(mac)
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// ChaCha20Poly1305.swift
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc7539#section-2.8.1
|
||||
|
||||
/// AEAD_CHACHA20_POLY1305
|
||||
public final class AEADChaCha20Poly1305: AEAD {
|
||||
public static let kLen = 32 // key length
|
||||
public static var ivRange = Range<Int>(12...12)
|
||||
|
||||
/// Authenticated encryption
|
||||
public static func encrypt(_ plainText: Array<UInt8>, key: Array<UInt8>, iv: Array<UInt8>, authenticationHeader: Array<UInt8>) throws -> (cipherText: Array<UInt8>, authenticationTag: Array<UInt8>) {
|
||||
let cipher = try ChaCha20(key: key, iv: iv)
|
||||
|
||||
var polykey = Array<UInt8>(repeating: 0, count: kLen)
|
||||
var toEncrypt = polykey
|
||||
polykey = try cipher.encrypt(polykey)
|
||||
toEncrypt += polykey
|
||||
toEncrypt += plainText
|
||||
|
||||
let fullCipherText = try cipher.encrypt(toEncrypt)
|
||||
let cipherText = Array(fullCipherText.dropFirst(64))
|
||||
|
||||
let tag = try calculateAuthenticationTag(authenticator: Poly1305(key: polykey), cipherText: cipherText, authenticationHeader: authenticationHeader)
|
||||
return (cipherText, tag)
|
||||
}
|
||||
|
||||
/// Authenticated decryption
|
||||
public static func decrypt(_ cipherText: Array<UInt8>, key: Array<UInt8>, iv: Array<UInt8>, authenticationHeader: Array<UInt8>, authenticationTag: Array<UInt8>) throws -> (plainText: Array<UInt8>, success: Bool) {
|
||||
let chacha = try ChaCha20(key: key, iv: iv)
|
||||
|
||||
let polykey = try chacha.encrypt(Array<UInt8>(repeating: 0, count: self.kLen))
|
||||
let mac = try calculateAuthenticationTag(authenticator: Poly1305(key: polykey), cipherText: cipherText, authenticationHeader: authenticationHeader)
|
||||
guard mac == authenticationTag else {
|
||||
return (cipherText, false)
|
||||
}
|
||||
|
||||
var toDecrypt = Array<UInt8>(reserveCapacity: cipherText.count + 64)
|
||||
toDecrypt += polykey
|
||||
toDecrypt += polykey
|
||||
toDecrypt += cipherText
|
||||
let fullPlainText = try chacha.decrypt(toDecrypt)
|
||||
let plainText = Array(fullPlainText.dropFirst(64))
|
||||
return (plainText, true)
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// MARK: Cryptors
|
||||
|
||||
extension AES: Cryptors {
|
||||
@inlinable
|
||||
public func makeEncryptor() throws -> Cryptor & Updatable {
|
||||
let blockSize = blockMode.customBlockSize ?? AES.blockSize
|
||||
let worker = try blockMode.worker(blockSize: blockSize, cipherOperation: encrypt, encryptionOperation: encrypt)
|
||||
if worker is StreamModeWorker {
|
||||
return try StreamEncryptor(blockSize: blockSize, padding: padding, worker)
|
||||
}
|
||||
return try BlockEncryptor(blockSize: blockSize, padding: padding, worker)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func makeDecryptor() throws -> Cryptor & Updatable {
|
||||
let blockSize = blockMode.customBlockSize ?? AES.blockSize
|
||||
let cipherOperation: CipherOperationOnBlock = blockMode.options.contains(.useEncryptToDecrypt) == true ? encrypt : decrypt
|
||||
let worker = try blockMode.worker(blockSize: blockSize, cipherOperation: cipherOperation, encryptionOperation: encrypt)
|
||||
if worker is StreamModeWorker {
|
||||
return try StreamDecryptor(blockSize: blockSize, padding: padding, worker)
|
||||
}
|
||||
return try BlockDecryptor(blockSize: blockSize, padding: padding, worker)
|
||||
}
|
||||
}
|
||||
@@ -1,556 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// Implementation of Gladman algorithm http://www.gladman.me.uk/AES
|
||||
//
|
||||
|
||||
/// The Advanced Encryption Standard (AES)
|
||||
public final class AES: BlockCipher {
|
||||
public enum Error: Swift.Error {
|
||||
/// Invalid key
|
||||
case invalidKeySize
|
||||
/// Data padding is required
|
||||
case dataPaddingRequired
|
||||
/// Invalid Data
|
||||
case invalidData
|
||||
}
|
||||
|
||||
public enum Variant: Int {
|
||||
case aes128 = 1, aes192, aes256
|
||||
|
||||
var Nk: Int { // Nk words
|
||||
[4, 6, 8][self.rawValue - 1]
|
||||
}
|
||||
|
||||
var Nb: Int { // Nb words
|
||||
4
|
||||
}
|
||||
|
||||
var Nr: Int { // Nr
|
||||
self.Nk + 6
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal let variantNr: Int
|
||||
|
||||
@usableFromInline
|
||||
internal let variantNb: Int
|
||||
|
||||
@usableFromInline
|
||||
internal let variantNk: Int
|
||||
|
||||
public static let blockSize: Int = 16 // 128 /8
|
||||
public let keySize: Int
|
||||
|
||||
/// AES Variant
|
||||
public let variant: Variant
|
||||
|
||||
// Parameters
|
||||
let key: Key
|
||||
|
||||
@usableFromInline
|
||||
let blockMode: BlockMode
|
||||
|
||||
@usableFromInline
|
||||
let padding: Padding
|
||||
|
||||
//
|
||||
@usableFromInline
|
||||
internal lazy var expandedKey: Array<Array<UInt32>> = self.expandKey(self.key, variant: self.variant)
|
||||
|
||||
@usableFromInline
|
||||
internal lazy var expandedKeyInv: Array<Array<UInt32>> = self.expandKeyInv(self.key, variant: self.variant)
|
||||
|
||||
private lazy var sBoxes: (sBox: Array<UInt32>, invSBox: Array<UInt32>) = self.calculateSBox()
|
||||
private lazy var sBox: Array<UInt32> = self.sBoxes.sBox
|
||||
private lazy var sBoxInv: Array<UInt32> = self.sBoxes.invSBox
|
||||
|
||||
// Parameters for Linear Congruence Generators
|
||||
private static let Rcon: Array<UInt8> = [
|
||||
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
|
||||
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
|
||||
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
|
||||
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
|
||||
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
|
||||
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
|
||||
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
|
||||
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
|
||||
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
|
||||
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
|
||||
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
|
||||
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
|
||||
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
|
||||
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
|
||||
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
|
||||
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d
|
||||
]
|
||||
|
||||
@usableFromInline static let T0: Array<UInt32> = [0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c]
|
||||
@usableFromInline static let T0_INV: Array<UInt32> = [0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0]
|
||||
@usableFromInline static let T1: Array<UInt32> = [0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x1010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x5050a0f, 0x9a9a2fb5, 0x7070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x0, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x2020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0xc0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0xb0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0xa0a141e, 0x494992db, 0x6060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x8081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x3030605, 0xf6f6f701, 0xe0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0xd0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0xf0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a]
|
||||
@usableFromInline static let T1_INV: Array<UInt32> = [0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x3e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0xeea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x24b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x6dd963d, 0x53eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, 0xa7ca147, 0xf427ce9, 0x1e84f8c9, 0x0, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0xd090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0xbd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x99fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, 0xca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x1a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042]
|
||||
@usableFromInline static let T2: Array<UInt32> = [0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x1020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x4080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x50a0f05, 0x9a2fb59a, 0x70e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x9121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x0, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x2040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0xc18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0xb161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0xa141e0a, 0x4992db49, 0x60c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x8101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x3060503, 0xf6f701f6, 0xe1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0xd1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0xf1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16]
|
||||
@usableFromInline static let T2_INV: Array<UInt32> = [0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x2f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x8f9942b, 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x7f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x6046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x0, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0xefdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0xf0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0xa0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x90e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x1f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x4f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0xbfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0xdff4195, 0xa8397101, 0xc08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257]
|
||||
@usableFromInline static let T3: Array<UInt32> = [0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x2030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x80c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0xa0f0505, 0x2fb59a9a, 0xe090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x0, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x4060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x58a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0xb838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0xc0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x18c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0xd868b8b, 0xf858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x6050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x7898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x38f8c8c, 0x59f8a1a1, 0x9808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616]
|
||||
@usableFromInline static let T3_INV: Array<UInt32> = [0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x3e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x2ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x6d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x532e18a, 0xa475ebf6, 0xb39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x46fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x7888b89, 0xe7385b19, 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x0, 0x9838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0xf563885, 0x3d1ed5ae, 0x3627392d, 0xa64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0xcb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0xe0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0xdec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x15d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x8deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8]
|
||||
@usableFromInline static let U1: Array<UInt32> = [0x0, 0xb0d090e, 0x161a121c, 0x1d171b12, 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, 0xfe75793, 0x4ea5e9d, 0x19fd458f, 0x12f04c81, 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, 0x1ed5ae3d, 0x15d8a733, 0x8cfbc21, 0x3c2b52f, 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, 0x1132f9ae, 0x1a3ff0a0, 0x728ebb2, 0xc25e2bc, 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, 0x1f6234d1, 0x146f3ddf, 0x97826cd, 0x2752fc3, 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, 0x10856342, 0x1b886a4c, 0x69f715e, 0xd927850, 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, 0x1b79aec, 0xaba93e2, 0x17ad88f0, 0x1ca081fe, 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, 0xe50cd7f, 0x55dc471, 0x184adf63, 0x1347d66d, 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d]
|
||||
@usableFromInline static let U2: Array<UInt32> = [0x0, 0xd090e0b, 0x1a121c16, 0x171b121d, 0x3424382c, 0x392d3627, 0x2e36243a, 0x233f2a31, 0x68487058, 0x65417e53, 0x725a6c4e, 0x7f536245, 0x5c6c4874, 0x5165467f, 0x467e5462, 0x4b775a69, 0xd090e0b0, 0xdd99eebb, 0xca82fca6, 0xc78bf2ad, 0xe4b4d89c, 0xe9bdd697, 0xfea6c48a, 0xf3afca81, 0xb8d890e8, 0xb5d19ee3, 0xa2ca8cfe, 0xafc382f5, 0x8cfca8c4, 0x81f5a6cf, 0x96eeb4d2, 0x9be7bad9, 0xbb3bdb7b, 0xb632d570, 0xa129c76d, 0xac20c966, 0x8f1fe357, 0x8216ed5c, 0x950dff41, 0x9804f14a, 0xd373ab23, 0xde7aa528, 0xc961b735, 0xc468b93e, 0xe757930f, 0xea5e9d04, 0xfd458f19, 0xf04c8112, 0x6bab3bcb, 0x66a235c0, 0x71b927dd, 0x7cb029d6, 0x5f8f03e7, 0x52860dec, 0x459d1ff1, 0x489411fa, 0x3e34b93, 0xeea4598, 0x19f15785, 0x14f8598e, 0x37c773bf, 0x3ace7db4, 0x2dd56fa9, 0x20dc61a2, 0x6d76adf6, 0x607fa3fd, 0x7764b1e0, 0x7a6dbfeb, 0x595295da, 0x545b9bd1, 0x434089cc, 0x4e4987c7, 0x53eddae, 0x837d3a5, 0x1f2cc1b8, 0x1225cfb3, 0x311ae582, 0x3c13eb89, 0x2b08f994, 0x2601f79f, 0xbde64d46, 0xb0ef434d, 0xa7f45150, 0xaafd5f5b, 0x89c2756a, 0x84cb7b61, 0x93d0697c, 0x9ed96777, 0xd5ae3d1e, 0xd8a73315, 0xcfbc2108, 0xc2b52f03, 0xe18a0532, 0xec830b39, 0xfb981924, 0xf691172f, 0xd64d768d, 0xdb447886, 0xcc5f6a9b, 0xc1566490, 0xe2694ea1, 0xef6040aa, 0xf87b52b7, 0xf5725cbc, 0xbe0506d5, 0xb30c08de, 0xa4171ac3, 0xa91e14c8, 0x8a213ef9, 0x872830f2, 0x903322ef, 0x9d3a2ce4, 0x6dd963d, 0xbd49836, 0x1ccf8a2b, 0x11c68420, 0x32f9ae11, 0x3ff0a01a, 0x28ebb207, 0x25e2bc0c, 0x6e95e665, 0x639ce86e, 0x7487fa73, 0x798ef478, 0x5ab1de49, 0x57b8d042, 0x40a3c25f, 0x4daacc54, 0xdaec41f7, 0xd7e54ffc, 0xc0fe5de1, 0xcdf753ea, 0xeec879db, 0xe3c177d0, 0xf4da65cd, 0xf9d36bc6, 0xb2a431af, 0xbfad3fa4, 0xa8b62db9, 0xa5bf23b2, 0x86800983, 0x8b890788, 0x9c921595, 0x919b1b9e, 0xa7ca147, 0x775af4c, 0x106ebd51, 0x1d67b35a, 0x3e58996b, 0x33519760, 0x244a857d, 0x29438b76, 0x6234d11f, 0x6f3ddf14, 0x7826cd09, 0x752fc302, 0x5610e933, 0x5b19e738, 0x4c02f525, 0x410bfb2e, 0x61d79a8c, 0x6cde9487, 0x7bc5869a, 0x76cc8891, 0x55f3a2a0, 0x58faacab, 0x4fe1beb6, 0x42e8b0bd, 0x99fead4, 0x496e4df, 0x138df6c2, 0x1e84f8c9, 0x3dbbd2f8, 0x30b2dcf3, 0x27a9ceee, 0x2aa0c0e5, 0xb1477a3c, 0xbc4e7437, 0xab55662a, 0xa65c6821, 0x85634210, 0x886a4c1b, 0x9f715e06, 0x9278500d, 0xd90f0a64, 0xd406046f, 0xc31d1672, 0xce141879, 0xed2b3248, 0xe0223c43, 0xf7392e5e, 0xfa302055, 0xb79aec01, 0xba93e20a, 0xad88f017, 0xa081fe1c, 0x83bed42d, 0x8eb7da26, 0x99acc83b, 0x94a5c630, 0xdfd29c59, 0xd2db9252, 0xc5c0804f, 0xc8c98e44, 0xebf6a475, 0xe6ffaa7e, 0xf1e4b863, 0xfcedb668, 0x670a0cb1, 0x6a0302ba, 0x7d1810a7, 0x70111eac, 0x532e349d, 0x5e273a96, 0x493c288b, 0x44352680, 0xf427ce9, 0x24b72e2, 0x155060ff, 0x18596ef4, 0x3b6644c5, 0x366f4ace, 0x217458d3, 0x2c7d56d8, 0xca1377a, 0x1a83971, 0x16b32b6c, 0x1bba2567, 0x38850f56, 0x358c015d, 0x22971340, 0x2f9e1d4b, 0x64e94722, 0x69e04929, 0x7efb5b34, 0x73f2553f, 0x50cd7f0e, 0x5dc47105, 0x4adf6318, 0x47d66d13, 0xdc31d7ca, 0xd138d9c1, 0xc623cbdc, 0xcb2ac5d7, 0xe815efe6, 0xe51ce1ed, 0xf207f3f0, 0xff0efdfb, 0xb479a792, 0xb970a999, 0xae6bbb84, 0xa362b58f, 0x805d9fbe, 0x8d5491b5, 0x9a4f83a8, 0x97468da3]
|
||||
@usableFromInline static let U3: Array<UInt32> = [0x0, 0x90e0b0d, 0x121c161a, 0x1b121d17, 0x24382c34, 0x2d362739, 0x36243a2e, 0x3f2a3123, 0x48705868, 0x417e5365, 0x5a6c4e72, 0x5362457f, 0x6c48745c, 0x65467f51, 0x7e546246, 0x775a694b, 0x90e0b0d0, 0x99eebbdd, 0x82fca6ca, 0x8bf2adc7, 0xb4d89ce4, 0xbdd697e9, 0xa6c48afe, 0xafca81f3, 0xd890e8b8, 0xd19ee3b5, 0xca8cfea2, 0xc382f5af, 0xfca8c48c, 0xf5a6cf81, 0xeeb4d296, 0xe7bad99b, 0x3bdb7bbb, 0x32d570b6, 0x29c76da1, 0x20c966ac, 0x1fe3578f, 0x16ed5c82, 0xdff4195, 0x4f14a98, 0x73ab23d3, 0x7aa528de, 0x61b735c9, 0x68b93ec4, 0x57930fe7, 0x5e9d04ea, 0x458f19fd, 0x4c8112f0, 0xab3bcb6b, 0xa235c066, 0xb927dd71, 0xb029d67c, 0x8f03e75f, 0x860dec52, 0x9d1ff145, 0x9411fa48, 0xe34b9303, 0xea45980e, 0xf1578519, 0xf8598e14, 0xc773bf37, 0xce7db43a, 0xd56fa92d, 0xdc61a220, 0x76adf66d, 0x7fa3fd60, 0x64b1e077, 0x6dbfeb7a, 0x5295da59, 0x5b9bd154, 0x4089cc43, 0x4987c74e, 0x3eddae05, 0x37d3a508, 0x2cc1b81f, 0x25cfb312, 0x1ae58231, 0x13eb893c, 0x8f9942b, 0x1f79f26, 0xe64d46bd, 0xef434db0, 0xf45150a7, 0xfd5f5baa, 0xc2756a89, 0xcb7b6184, 0xd0697c93, 0xd967779e, 0xae3d1ed5, 0xa73315d8, 0xbc2108cf, 0xb52f03c2, 0x8a0532e1, 0x830b39ec, 0x981924fb, 0x91172ff6, 0x4d768dd6, 0x447886db, 0x5f6a9bcc, 0x566490c1, 0x694ea1e2, 0x6040aaef, 0x7b52b7f8, 0x725cbcf5, 0x506d5be, 0xc08deb3, 0x171ac3a4, 0x1e14c8a9, 0x213ef98a, 0x2830f287, 0x3322ef90, 0x3a2ce49d, 0xdd963d06, 0xd498360b, 0xcf8a2b1c, 0xc6842011, 0xf9ae1132, 0xf0a01a3f, 0xebb20728, 0xe2bc0c25, 0x95e6656e, 0x9ce86e63, 0x87fa7374, 0x8ef47879, 0xb1de495a, 0xb8d04257, 0xa3c25f40, 0xaacc544d, 0xec41f7da, 0xe54ffcd7, 0xfe5de1c0, 0xf753eacd, 0xc879dbee, 0xc177d0e3, 0xda65cdf4, 0xd36bc6f9, 0xa431afb2, 0xad3fa4bf, 0xb62db9a8, 0xbf23b2a5, 0x80098386, 0x8907888b, 0x9215959c, 0x9b1b9e91, 0x7ca1470a, 0x75af4c07, 0x6ebd5110, 0x67b35a1d, 0x58996b3e, 0x51976033, 0x4a857d24, 0x438b7629, 0x34d11f62, 0x3ddf146f, 0x26cd0978, 0x2fc30275, 0x10e93356, 0x19e7385b, 0x2f5254c, 0xbfb2e41, 0xd79a8c61, 0xde94876c, 0xc5869a7b, 0xcc889176, 0xf3a2a055, 0xfaacab58, 0xe1beb64f, 0xe8b0bd42, 0x9fead409, 0x96e4df04, 0x8df6c213, 0x84f8c91e, 0xbbd2f83d, 0xb2dcf330, 0xa9ceee27, 0xa0c0e52a, 0x477a3cb1, 0x4e7437bc, 0x55662aab, 0x5c6821a6, 0x63421085, 0x6a4c1b88, 0x715e069f, 0x78500d92, 0xf0a64d9, 0x6046fd4, 0x1d1672c3, 0x141879ce, 0x2b3248ed, 0x223c43e0, 0x392e5ef7, 0x302055fa, 0x9aec01b7, 0x93e20aba, 0x88f017ad, 0x81fe1ca0, 0xbed42d83, 0xb7da268e, 0xacc83b99, 0xa5c63094, 0xd29c59df, 0xdb9252d2, 0xc0804fc5, 0xc98e44c8, 0xf6a475eb, 0xffaa7ee6, 0xe4b863f1, 0xedb668fc, 0xa0cb167, 0x302ba6a, 0x1810a77d, 0x111eac70, 0x2e349d53, 0x273a965e, 0x3c288b49, 0x35268044, 0x427ce90f, 0x4b72e202, 0x5060ff15, 0x596ef418, 0x6644c53b, 0x6f4ace36, 0x7458d321, 0x7d56d82c, 0xa1377a0c, 0xa8397101, 0xb32b6c16, 0xba25671b, 0x850f5638, 0x8c015d35, 0x97134022, 0x9e1d4b2f, 0xe9472264, 0xe0492969, 0xfb5b347e, 0xf2553f73, 0xcd7f0e50, 0xc471055d, 0xdf63184a, 0xd66d1347, 0x31d7cadc, 0x38d9c1d1, 0x23cbdcc6, 0x2ac5d7cb, 0x15efe6e8, 0x1ce1ede5, 0x7f3f0f2, 0xefdfbff, 0x79a792b4, 0x70a999b9, 0x6bbb84ae, 0x62b58fa3, 0x5d9fbe80, 0x5491b58d, 0x4f83a89a, 0x468da397]
|
||||
@usableFromInline static let U4: Array<UInt32> = [0x0, 0xe0b0d09, 0x1c161a12, 0x121d171b, 0x382c3424, 0x3627392d, 0x243a2e36, 0x2a31233f, 0x70586848, 0x7e536541, 0x6c4e725a, 0x62457f53, 0x48745c6c, 0x467f5165, 0x5462467e, 0x5a694b77, 0xe0b0d090, 0xeebbdd99, 0xfca6ca82, 0xf2adc78b, 0xd89ce4b4, 0xd697e9bd, 0xc48afea6, 0xca81f3af, 0x90e8b8d8, 0x9ee3b5d1, 0x8cfea2ca, 0x82f5afc3, 0xa8c48cfc, 0xa6cf81f5, 0xb4d296ee, 0xbad99be7, 0xdb7bbb3b, 0xd570b632, 0xc76da129, 0xc966ac20, 0xe3578f1f, 0xed5c8216, 0xff41950d, 0xf14a9804, 0xab23d373, 0xa528de7a, 0xb735c961, 0xb93ec468, 0x930fe757, 0x9d04ea5e, 0x8f19fd45, 0x8112f04c, 0x3bcb6bab, 0x35c066a2, 0x27dd71b9, 0x29d67cb0, 0x3e75f8f, 0xdec5286, 0x1ff1459d, 0x11fa4894, 0x4b9303e3, 0x45980eea, 0x578519f1, 0x598e14f8, 0x73bf37c7, 0x7db43ace, 0x6fa92dd5, 0x61a220dc, 0xadf66d76, 0xa3fd607f, 0xb1e07764, 0xbfeb7a6d, 0x95da5952, 0x9bd1545b, 0x89cc4340, 0x87c74e49, 0xddae053e, 0xd3a50837, 0xc1b81f2c, 0xcfb31225, 0xe582311a, 0xeb893c13, 0xf9942b08, 0xf79f2601, 0x4d46bde6, 0x434db0ef, 0x5150a7f4, 0x5f5baafd, 0x756a89c2, 0x7b6184cb, 0x697c93d0, 0x67779ed9, 0x3d1ed5ae, 0x3315d8a7, 0x2108cfbc, 0x2f03c2b5, 0x532e18a, 0xb39ec83, 0x1924fb98, 0x172ff691, 0x768dd64d, 0x7886db44, 0x6a9bcc5f, 0x6490c156, 0x4ea1e269, 0x40aaef60, 0x52b7f87b, 0x5cbcf572, 0x6d5be05, 0x8deb30c, 0x1ac3a417, 0x14c8a91e, 0x3ef98a21, 0x30f28728, 0x22ef9033, 0x2ce49d3a, 0x963d06dd, 0x98360bd4, 0x8a2b1ccf, 0x842011c6, 0xae1132f9, 0xa01a3ff0, 0xb20728eb, 0xbc0c25e2, 0xe6656e95, 0xe86e639c, 0xfa737487, 0xf478798e, 0xde495ab1, 0xd04257b8, 0xc25f40a3, 0xcc544daa, 0x41f7daec, 0x4ffcd7e5, 0x5de1c0fe, 0x53eacdf7, 0x79dbeec8, 0x77d0e3c1, 0x65cdf4da, 0x6bc6f9d3, 0x31afb2a4, 0x3fa4bfad, 0x2db9a8b6, 0x23b2a5bf, 0x9838680, 0x7888b89, 0x15959c92, 0x1b9e919b, 0xa1470a7c, 0xaf4c0775, 0xbd51106e, 0xb35a1d67, 0x996b3e58, 0x97603351, 0x857d244a, 0x8b762943, 0xd11f6234, 0xdf146f3d, 0xcd097826, 0xc302752f, 0xe9335610, 0xe7385b19, 0xf5254c02, 0xfb2e410b, 0x9a8c61d7, 0x94876cde, 0x869a7bc5, 0x889176cc, 0xa2a055f3, 0xacab58fa, 0xbeb64fe1, 0xb0bd42e8, 0xead4099f, 0xe4df0496, 0xf6c2138d, 0xf8c91e84, 0xd2f83dbb, 0xdcf330b2, 0xceee27a9, 0xc0e52aa0, 0x7a3cb147, 0x7437bc4e, 0x662aab55, 0x6821a65c, 0x42108563, 0x4c1b886a, 0x5e069f71, 0x500d9278, 0xa64d90f, 0x46fd406, 0x1672c31d, 0x1879ce14, 0x3248ed2b, 0x3c43e022, 0x2e5ef739, 0x2055fa30, 0xec01b79a, 0xe20aba93, 0xf017ad88, 0xfe1ca081, 0xd42d83be, 0xda268eb7, 0xc83b99ac, 0xc63094a5, 0x9c59dfd2, 0x9252d2db, 0x804fc5c0, 0x8e44c8c9, 0xa475ebf6, 0xaa7ee6ff, 0xb863f1e4, 0xb668fced, 0xcb1670a, 0x2ba6a03, 0x10a77d18, 0x1eac7011, 0x349d532e, 0x3a965e27, 0x288b493c, 0x26804435, 0x7ce90f42, 0x72e2024b, 0x60ff1550, 0x6ef41859, 0x44c53b66, 0x4ace366f, 0x58d32174, 0x56d82c7d, 0x377a0ca1, 0x397101a8, 0x2b6c16b3, 0x25671bba, 0xf563885, 0x15d358c, 0x13402297, 0x1d4b2f9e, 0x472264e9, 0x492969e0, 0x5b347efb, 0x553f73f2, 0x7f0e50cd, 0x71055dc4, 0x63184adf, 0x6d1347d6, 0xd7cadc31, 0xd9c1d138, 0xcbdcc623, 0xc5d7cb2a, 0xefe6e815, 0xe1ede51c, 0xf3f0f207, 0xfdfbff0e, 0xa792b479, 0xa999b970, 0xbb84ae6b, 0xb58fa362, 0x9fbe805d, 0x91b58d54, 0x83a89a4f, 0x8da39746]
|
||||
|
||||
/// Initialize AES with variant calculated out of key length:
|
||||
/// - 16 bytes (AES-128)
|
||||
/// - 24 bytes (AES-192)
|
||||
/// - 32 bytes (AES-256)
|
||||
///
|
||||
/// - parameter key: Key. Length of the key decides on AES variant.
|
||||
/// - parameter iv: Initialization Vector (Optional for some blockMode values)
|
||||
/// - parameter blockMode: Cipher mode of operation
|
||||
/// - parameter padding: Padding method. .pkcs7, .noPadding, .zeroPadding, ...
|
||||
///
|
||||
/// - throws: AES.Error
|
||||
///
|
||||
/// - returns: Instance
|
||||
public init(key: Array<UInt8>, blockMode: BlockMode, padding: Padding = .pkcs7) throws {
|
||||
self.key = Key(bytes: key)
|
||||
self.blockMode = blockMode
|
||||
self.padding = padding
|
||||
self.keySize = self.key.count
|
||||
|
||||
// Validate key size
|
||||
switch self.keySize * 8 {
|
||||
case 128:
|
||||
self.variant = .aes128
|
||||
case 192:
|
||||
self.variant = .aes192
|
||||
case 256:
|
||||
self.variant = .aes256
|
||||
default:
|
||||
throw Error.invalidKeySize
|
||||
}
|
||||
|
||||
self.variantNb = self.variant.Nb
|
||||
self.variantNk = self.variant.Nk
|
||||
self.variantNr = self.variant.Nr
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func encrypt(block: ArraySlice<UInt8>) -> Array<UInt8>? {
|
||||
if self.blockMode.options.contains(.paddingRequired) && block.count != AES.blockSize {
|
||||
return Array(block)
|
||||
}
|
||||
|
||||
let rounds = self.variantNr
|
||||
let rk = self.expandedKey
|
||||
|
||||
let b00 = UInt32(block[block.startIndex.advanced(by: 0)])
|
||||
let b01 = UInt32(block[block.startIndex.advanced(by: 1)]) << 8
|
||||
let b02 = UInt32(block[block.startIndex.advanced(by: 2)]) << 16
|
||||
let b03 = UInt32(block[block.startIndex.advanced(by: 3)]) << 24
|
||||
var b0 = b00 | b01 | b02 | b03
|
||||
|
||||
let b10 = UInt32(block[block.startIndex.advanced(by: 4)])
|
||||
let b11 = UInt32(block[block.startIndex.advanced(by: 5)]) << 8
|
||||
let b12 = UInt32(block[block.startIndex.advanced(by: 6)]) << 16
|
||||
let b13 = UInt32(block[block.startIndex.advanced(by: 7)]) << 24
|
||||
var b1 = b10 | b11 | b12 | b13
|
||||
|
||||
let b20 = UInt32(block[block.startIndex.advanced(by: 8)])
|
||||
let b21 = UInt32(block[block.startIndex.advanced(by: 9)]) << 8
|
||||
let b22 = UInt32(block[block.startIndex.advanced(by: 10)]) << 16
|
||||
let b23 = UInt32(block[block.startIndex.advanced(by: 11)]) << 24
|
||||
var b2 = b20 | b21 | b22 | b23
|
||||
|
||||
let b30 = UInt32(block[block.startIndex.advanced(by: 12)])
|
||||
let b31 = UInt32(block[block.startIndex.advanced(by: 13)]) << 8
|
||||
let b32 = UInt32(block[block.startIndex.advanced(by: 14)]) << 16
|
||||
let b33 = UInt32(block[block.startIndex.advanced(by: 15)]) << 24
|
||||
var b3 = b30 | b31 | b32 | b33
|
||||
|
||||
let tLength = 4
|
||||
let t = UnsafeMutablePointer<UInt32>.allocate(capacity: tLength)
|
||||
t.initialize(repeating: 0, count: tLength)
|
||||
defer {
|
||||
t.deinitialize(count: tLength)
|
||||
t.deallocate()
|
||||
}
|
||||
|
||||
for r in 0..<rounds - 1 {
|
||||
t[0] = b0 ^ rk[r][0]
|
||||
t[1] = b1 ^ rk[r][1]
|
||||
t[2] = b2 ^ rk[r][2]
|
||||
t[3] = b3 ^ rk[r][3]
|
||||
|
||||
let lb00 = AES.T0[Int(t[0] & 0xff)]
|
||||
let lb01 = AES.T1[Int((t[1] >> 8) & 0xff)]
|
||||
let lb02 = AES.T2[Int((t[2] >> 16) & 0xff)]
|
||||
let lb03 = AES.T3[Int(t[3] >> 24)]
|
||||
b0 = lb00 ^ lb01 ^ lb02 ^ lb03
|
||||
|
||||
let lb10 = AES.T0[Int(t[1] & 0xff)]
|
||||
let lb11 = AES.T1[Int((t[2] >> 8) & 0xff)]
|
||||
let lb12 = AES.T2[Int((t[3] >> 16) & 0xff)]
|
||||
let lb13 = AES.T3[Int(t[0] >> 24)]
|
||||
b1 = lb10 ^ lb11 ^ lb12 ^ lb13
|
||||
|
||||
let lb20 = AES.T0[Int(t[2] & 0xff)]
|
||||
let lb21 = AES.T1[Int((t[3] >> 8) & 0xff)]
|
||||
let lb22 = AES.T2[Int((t[0] >> 16) & 0xff)]
|
||||
let lb23 = AES.T3[Int(t[1] >> 24)]
|
||||
b2 = lb20 ^ lb21 ^ lb22 ^ lb23
|
||||
|
||||
let lb30 = AES.T0[Int(t[3] & 0xff)]
|
||||
let lb31 = AES.T1[Int((t[0] >> 8) & 0xff)]
|
||||
let lb32 = AES.T2[Int((t[1] >> 16) & 0xff)]
|
||||
let lb33 = AES.T3[Int(t[2] >> 24)]
|
||||
b3 = lb30 ^ lb31 ^ lb32 ^ lb33
|
||||
}
|
||||
|
||||
// last round
|
||||
let r = rounds - 1
|
||||
|
||||
t[0] = b0 ^ rk[r][0]
|
||||
t[1] = b1 ^ rk[r][1]
|
||||
t[2] = b2 ^ rk[r][2]
|
||||
t[3] = b3 ^ rk[r][3]
|
||||
|
||||
// rounds
|
||||
b0 = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0]
|
||||
b1 = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1]
|
||||
b2 = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2]
|
||||
b3 = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3]
|
||||
|
||||
let encrypted: Array<UInt8> = [
|
||||
UInt8(b0 & 0xff), UInt8((b0 >> 8) & 0xff), UInt8((b0 >> 16) & 0xff), UInt8((b0 >> 24) & 0xff),
|
||||
UInt8(b1 & 0xff), UInt8((b1 >> 8) & 0xff), UInt8((b1 >> 16) & 0xff), UInt8((b1 >> 24) & 0xff),
|
||||
UInt8(b2 & 0xff), UInt8((b2 >> 8) & 0xff), UInt8((b2 >> 16) & 0xff), UInt8((b2 >> 24) & 0xff),
|
||||
UInt8(b3 & 0xff), UInt8((b3 >> 8) & 0xff), UInt8((b3 >> 16) & 0xff), UInt8((b3 >> 24) & 0xff)
|
||||
]
|
||||
return encrypted
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal func decrypt(block: ArraySlice<UInt8>) -> Array<UInt8>? {
|
||||
if self.blockMode.options.contains(.paddingRequired) && block.count != AES.blockSize {
|
||||
return Array(block)
|
||||
}
|
||||
|
||||
let rounds = self.variantNr
|
||||
let rk = self.expandedKeyInv
|
||||
|
||||
// Save miliseconds by not using `block.toUInt32Array()`
|
||||
let b00 = UInt32(block[block.startIndex.advanced(by: 0)])
|
||||
let b01 = UInt32(block[block.startIndex.advanced(by: 1)]) << 8
|
||||
let b02 = UInt32(block[block.startIndex.advanced(by: 2)]) << 16
|
||||
let b03 = UInt32(block[block.startIndex.advanced(by: 3)]) << 24
|
||||
var b0 = b00 | b01 | b02 | b03
|
||||
|
||||
let b10 = UInt32(block[block.startIndex.advanced(by: 4)])
|
||||
let b11 = UInt32(block[block.startIndex.advanced(by: 5)]) << 8
|
||||
let b12 = UInt32(block[block.startIndex.advanced(by: 6)]) << 16
|
||||
let b13 = UInt32(block[block.startIndex.advanced(by: 7)]) << 24
|
||||
var b1 = b10 | b11 | b12 | b13
|
||||
|
||||
let b20 = UInt32(block[block.startIndex.advanced(by: 8)])
|
||||
let b21 = UInt32(block[block.startIndex.advanced(by: 9)]) << 8
|
||||
let b22 = UInt32(block[block.startIndex.advanced(by: 10)]) << 16
|
||||
let b23 = UInt32(block[block.startIndex.advanced(by: 11)]) << 24
|
||||
var b2 = b20 | b21 | b22 | b23
|
||||
|
||||
let b30 = UInt32(block[block.startIndex.advanced(by: 12)])
|
||||
let b31 = UInt32(block[block.startIndex.advanced(by: 13)]) << 8
|
||||
let b32 = UInt32(block[block.startIndex.advanced(by: 14)]) << 16
|
||||
let b33 = UInt32(block[block.startIndex.advanced(by: 15)]) << 24
|
||||
var b3 = b30 | b31 | b32 | b33
|
||||
|
||||
let tLength = 4
|
||||
let t = UnsafeMutablePointer<UInt32>.allocate(capacity: tLength)
|
||||
t.initialize(repeating: 0, count: tLength)
|
||||
defer {
|
||||
t.deinitialize(count: tLength)
|
||||
t.deallocate()
|
||||
}
|
||||
|
||||
for r in (2...rounds).reversed() {
|
||||
t[0] = b0 ^ rk[r][0]
|
||||
t[1] = b1 ^ rk[r][1]
|
||||
t[2] = b2 ^ rk[r][2]
|
||||
t[3] = b3 ^ rk[r][3]
|
||||
|
||||
let b00 = AES.T0_INV[Int(t[0] & 0xff)]
|
||||
let b01 = AES.T1_INV[Int((t[3] >> 8) & 0xff)]
|
||||
let b02 = AES.T2_INV[Int((t[2] >> 16) & 0xff)]
|
||||
let b03 = AES.T3_INV[Int(t[1] >> 24)]
|
||||
b0 = b00 ^ b01 ^ b02 ^ b03
|
||||
|
||||
let b10 = AES.T0_INV[Int(t[1] & 0xff)]
|
||||
let b11 = AES.T1_INV[Int((t[0] >> 8) & 0xff)]
|
||||
let b12 = AES.T2_INV[Int((t[3] >> 16) & 0xff)]
|
||||
let b13 = AES.T3_INV[Int(t[2] >> 24)]
|
||||
b1 = b10 ^ b11 ^ b12 ^ b13
|
||||
|
||||
let b20 = AES.T0_INV[Int(t[2] & 0xff)]
|
||||
let b21 = AES.T1_INV[Int((t[1] >> 8) & 0xff)]
|
||||
let b22 = AES.T2_INV[Int((t[0] >> 16) & 0xff)]
|
||||
let b23 = AES.T3_INV[Int(t[3] >> 24)]
|
||||
b2 = b20 ^ b21 ^ b22 ^ b23
|
||||
|
||||
let b30 = AES.T0_INV[Int(t[3] & 0xff)]
|
||||
let b31 = AES.T1_INV[Int((t[2] >> 8) & 0xff)]
|
||||
let b32 = AES.T2_INV[Int((t[1] >> 16) & 0xff)]
|
||||
let b33 = AES.T3_INV[Int(t[0] >> 24)]
|
||||
b3 = b30 ^ b31 ^ b32 ^ b33
|
||||
}
|
||||
|
||||
// last round
|
||||
t[0] = b0 ^ rk[1][0]
|
||||
t[1] = b1 ^ rk[1][1]
|
||||
t[2] = b2 ^ rk[1][2]
|
||||
t[3] = b3 ^ rk[1][3]
|
||||
|
||||
// rounds
|
||||
|
||||
let lb00 = self.sBoxInv[Int(B0(t[0]))]
|
||||
let lb01 = (sBoxInv[Int(B1(t[3]))] << 8)
|
||||
let lb02 = (sBoxInv[Int(B2(t[2]))] << 16)
|
||||
let lb03 = (sBoxInv[Int(B3(t[1]))] << 24)
|
||||
b0 = lb00 | lb01 | lb02 | lb03 ^ rk[0][0]
|
||||
|
||||
let lb10 = self.sBoxInv[Int(B0(t[1]))]
|
||||
let lb11 = (sBoxInv[Int(B1(t[0]))] << 8)
|
||||
let lb12 = (sBoxInv[Int(B2(t[3]))] << 16)
|
||||
let lb13 = (sBoxInv[Int(B3(t[2]))] << 24)
|
||||
b1 = lb10 | lb11 | lb12 | lb13 ^ rk[0][1]
|
||||
|
||||
let lb20 = self.sBoxInv[Int(B0(t[2]))]
|
||||
let lb21 = (sBoxInv[Int(B1(t[1]))] << 8)
|
||||
let lb22 = (sBoxInv[Int(B2(t[0]))] << 16)
|
||||
let lb23 = (sBoxInv[Int(B3(t[3]))] << 24)
|
||||
b2 = lb20 | lb21 | lb22 | lb23 ^ rk[0][2]
|
||||
|
||||
let lb30 = self.sBoxInv[Int(B0(t[3]))]
|
||||
let lb31 = (sBoxInv[Int(B1(t[2]))] << 8)
|
||||
let lb32 = (sBoxInv[Int(B2(t[1]))] << 16)
|
||||
let lb33 = (sBoxInv[Int(B3(t[0]))] << 24)
|
||||
b3 = lb30 | lb31 | lb32 | lb33 ^ rk[0][3]
|
||||
|
||||
let result: Array<UInt8> = [
|
||||
UInt8(b0 & 0xff), UInt8((b0 >> 8) & 0xff), UInt8((b0 >> 16) & 0xff), UInt8((b0 >> 24) & 0xff),
|
||||
UInt8(b1 & 0xff), UInt8((b1 >> 8) & 0xff), UInt8((b1 >> 16) & 0xff), UInt8((b1 >> 24) & 0xff),
|
||||
UInt8(b2 & 0xff), UInt8((b2 >> 8) & 0xff), UInt8((b2 >> 16) & 0xff), UInt8((b2 >> 24) & 0xff),
|
||||
UInt8(b3 & 0xff), UInt8((b3 >> 8) & 0xff), UInt8((b3 >> 16) & 0xff), UInt8((b3 >> 24) & 0xff)
|
||||
]
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension AES {
|
||||
private func expandKeyInv(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
|
||||
let rounds = self.variantNr
|
||||
var rk2: Array<Array<UInt32>> = self.expandKey(key, variant: variant)
|
||||
|
||||
for r in 1..<rounds {
|
||||
for i in 0..<4 {
|
||||
let w = rk2[r][i]
|
||||
let u1 = AES.U1[Int(B0(w))]
|
||||
let u2 = AES.U2[Int(B1(w))]
|
||||
let u3 = AES.U3[Int(B2(w))]
|
||||
let u4 = AES.U4[Int(B3(w))]
|
||||
rk2[r][i] = u1 ^ u2 ^ u3 ^ u4
|
||||
}
|
||||
}
|
||||
|
||||
return rk2
|
||||
}
|
||||
|
||||
private func expandKey(_ key: Key, variant _: Variant) -> Array<Array<UInt32>> {
|
||||
func convertExpandedKey(_ expanded: Array<UInt8>) -> Array<Array<UInt32>> {
|
||||
expanded.batched(by: 4).map({ UInt32(bytes: $0.reversed()) }).batched(by: 4).map { Array($0) }
|
||||
}
|
||||
|
||||
/*
|
||||
* Function used in the Key Expansion routine that takes a four-byte
|
||||
* input word and applies an S-box to each of the four bytes to
|
||||
* produce an output word.
|
||||
*/
|
||||
func subWord(_ word: Array<UInt8>) -> Array<UInt8> {
|
||||
precondition(word.count == 4)
|
||||
|
||||
var result = word
|
||||
for i in 0..<4 {
|
||||
result[i] = UInt8(self.sBox[Int(word[i])])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func subWordInPlace(_ word: inout Array<UInt8>) {
|
||||
precondition(word.count == 4)
|
||||
word[0] = UInt8(self.sBox[Int(word[0])])
|
||||
word[1] = UInt8(self.sBox[Int(word[1])])
|
||||
word[2] = UInt8(self.sBox[Int(word[2])])
|
||||
word[3] = UInt8(self.sBox[Int(word[3])])
|
||||
}
|
||||
|
||||
let wLength = self.variantNb * (self.variantNr + 1) * 4
|
||||
let w = UnsafeMutablePointer<UInt8>.allocate(capacity: wLength)
|
||||
w.initialize(repeating: 0, count: wLength)
|
||||
defer {
|
||||
w.deinitialize(count: wLength)
|
||||
w.deallocate()
|
||||
}
|
||||
|
||||
for i in 0..<self.variantNk {
|
||||
for wordIdx in 0..<4 {
|
||||
w[(4 * i) + wordIdx] = key[(4 * i) + wordIdx]
|
||||
}
|
||||
}
|
||||
|
||||
var tmp: Array<UInt8>
|
||||
|
||||
for i in self.variantNk..<self.variantNb * (self.variantNr + 1) {
|
||||
tmp = Array<UInt8>(repeating: 0, count: 4)
|
||||
|
||||
for wordIdx in 0..<4 {
|
||||
tmp[wordIdx] = w[4 * (i - 1) + wordIdx]
|
||||
}
|
||||
if (i % self.variantNk) == 0 {
|
||||
tmp = subWord(rotateLeft(UInt32(bytes: tmp), by: 8).bytes(totalBytes: 4))
|
||||
tmp[0] = tmp.first! ^ AES.Rcon[i / variantNk]
|
||||
} else if self.variantNk > 6 && (i % self.variantNk) == 4 {
|
||||
subWordInPlace(&tmp)
|
||||
}
|
||||
|
||||
// xor array of bytes
|
||||
for wordIdx in 0..<4 {
|
||||
w[4 * i + wordIdx] = w[4 * (i - variantNk) + wordIdx] ^ tmp[wordIdx]
|
||||
}
|
||||
}
|
||||
return convertExpandedKey(Array(UnsafeBufferPointer(start: w, count: wLength)))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func B0(_ x: UInt32) -> UInt32 {
|
||||
x & 0xff
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func B1(_ x: UInt32) -> UInt32 {
|
||||
(x >> 8) & 0xff
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func B2(_ x: UInt32) -> UInt32 {
|
||||
(x >> 16) & 0xff
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func B3(_ x: UInt32) -> UInt32 {
|
||||
(x >> 24) & 0xff
|
||||
}
|
||||
|
||||
@inline(__always) @usableFromInline
|
||||
internal func F1(_ x0: UInt32, _ x1: UInt32, _ x2: UInt32, _ x3: UInt32) -> UInt32 {
|
||||
var result: UInt32 = 0
|
||||
result |= UInt32(self.B1(AES.T0[Int(x0 & 255)]))
|
||||
result |= UInt32(self.B1(AES.T0[Int((x1 >> 8) & 255)])) << 8
|
||||
result |= UInt32(self.B1(AES.T0[Int((x2 >> 16) & 255)])) << 16
|
||||
result |= UInt32(self.B1(AES.T0[Int(x3 >> 24)])) << 24
|
||||
return result
|
||||
}
|
||||
|
||||
private func calculateSBox() -> (sBox: Array<UInt32>, invSBox: Array<UInt32>) {
|
||||
let sboxLength = 256
|
||||
let sbox = UnsafeMutablePointer<UInt32>.allocate(capacity: sboxLength)
|
||||
let invsbox = UnsafeMutablePointer<UInt32>.allocate(capacity: sboxLength)
|
||||
sbox.initialize(repeating: 0, count: sboxLength)
|
||||
invsbox.initialize(repeating: 0, count: sboxLength)
|
||||
defer {
|
||||
sbox.deinitialize(count: sboxLength)
|
||||
sbox.deallocate()
|
||||
invsbox.deinitialize(count: sboxLength)
|
||||
invsbox.deallocate()
|
||||
}
|
||||
|
||||
sbox[0] = 0x63
|
||||
|
||||
var p: UInt8 = 1, q: UInt8 = 1
|
||||
|
||||
repeat {
|
||||
p = p ^ (UInt8(truncatingIfNeeded: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1b : 0))
|
||||
q ^= q << 1
|
||||
q ^= q << 2
|
||||
q ^= q << 4
|
||||
q ^= (q & 0x80) == 0x80 ? 0x09 : 0
|
||||
|
||||
let s = 0x63 ^ q ^ rotateLeft(q, by: 1) ^ rotateLeft(q, by: 2) ^ rotateLeft(q, by: 3) ^ rotateLeft(q, by: 4)
|
||||
|
||||
sbox[Int(p)] = UInt32(s)
|
||||
invsbox[Int(s)] = UInt32(p)
|
||||
} while p != 1
|
||||
|
||||
return (sBox: Array(UnsafeBufferPointer(start: sbox, count: sboxLength)), invSBox: Array(UnsafeBufferPointer(start: invsbox, count: sboxLength)))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Cipher
|
||||
|
||||
extension AES: Cipher {
|
||||
@inlinable
|
||||
public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
|
||||
let blockSize = self.blockMode.customBlockSize ?? AES.blockSize
|
||||
let chunks = bytes.batched(by: blockSize)
|
||||
|
||||
var oneTimeCryptor = try makeEncryptor()
|
||||
var out = Array<UInt8>(reserveCapacity: bytes.count)
|
||||
for chunk in chunks {
|
||||
out += try oneTimeCryptor.update(withBytes: chunk, isLast: false)
|
||||
}
|
||||
// Padding may be added at the very end
|
||||
out += try oneTimeCryptor.finish()
|
||||
|
||||
if self.blockMode.options.contains(.paddingRequired) && (out.count % AES.blockSize != 0) {
|
||||
throw Error.dataPaddingRequired
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
|
||||
if self.blockMode.options.contains(.paddingRequired) && (bytes.count % AES.blockSize != 0) {
|
||||
throw Error.dataPaddingRequired
|
||||
}
|
||||
|
||||
var oneTimeCryptor = try makeDecryptor()
|
||||
let chunks = bytes.batched(by: AES.blockSize)
|
||||
if chunks.isEmpty {
|
||||
throw Error.invalidData
|
||||
}
|
||||
|
||||
var out = Array<UInt8>(reserveCapacity: bytes.count)
|
||||
|
||||
var lastIdx = chunks.startIndex
|
||||
chunks.indices.formIndex(&lastIdx, offsetBy: chunks.count - 1)
|
||||
|
||||
// To properly remove padding, `isLast` has to be known when called with the last chunk of ciphertext
|
||||
// Last chunk of ciphertext may contains padded data so next call to update(..) won't be able to remove it
|
||||
for idx in chunks.indices {
|
||||
out += try oneTimeCryptor.update(withBytes: chunks[idx], isLast: idx == lastIdx)
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
extension Array {
|
||||
@inlinable
|
||||
init(reserveCapacity: Int) {
|
||||
self = Array<Element>()
|
||||
self.reserveCapacity(reserveCapacity)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var slice: ArraySlice<Element> {
|
||||
self[self.startIndex ..< self.endIndex]
|
||||
}
|
||||
|
||||
@inlinable
|
||||
subscript (safe index: Index) -> Element? {
|
||||
return indices.contains(index) ? self[index] : nil
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == UInt8 {
|
||||
public init(hex: String) {
|
||||
self.init(reserveCapacity: hex.unicodeScalars.lazy.underestimatedCount)
|
||||
var buffer: UInt8?
|
||||
var skip = hex.hasPrefix("0x") ? 2 : 0
|
||||
for char in hex.unicodeScalars.lazy {
|
||||
guard skip == 0 else {
|
||||
skip -= 1
|
||||
continue
|
||||
}
|
||||
guard char.value >= 48 && char.value <= 102 else {
|
||||
removeAll()
|
||||
return
|
||||
}
|
||||
let v: UInt8
|
||||
let c: UInt8 = UInt8(char.value)
|
||||
switch c {
|
||||
case let c where c <= 57:
|
||||
v = c - 48
|
||||
case let c where c >= 65 && c <= 70:
|
||||
v = c - 55
|
||||
case let c where c >= 97:
|
||||
v = c - 87
|
||||
default:
|
||||
removeAll()
|
||||
return
|
||||
}
|
||||
if let b = buffer {
|
||||
append(b << 4 | v)
|
||||
buffer = nil
|
||||
} else {
|
||||
buffer = v
|
||||
}
|
||||
}
|
||||
if let b = buffer {
|
||||
append(b)
|
||||
}
|
||||
}
|
||||
|
||||
public func toHexString() -> String {
|
||||
`lazy`.reduce(into: "") {
|
||||
var s = String($1, radix: 16)
|
||||
if s.count == 1 {
|
||||
s = "0" + s
|
||||
}
|
||||
$0 += s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == UInt8 {
|
||||
/// split in chunks with given chunk size
|
||||
@available(*, deprecated)
|
||||
public func chunks(size chunksize: Int) -> Array<Array<Element>> {
|
||||
var words = Array<Array<Element>>()
|
||||
words.reserveCapacity(count / chunksize)
|
||||
for idx in stride(from: chunksize, through: count, by: chunksize) {
|
||||
words.append(Array(self[idx - chunksize ..< idx])) // slow for large table
|
||||
}
|
||||
let remainder = suffix(count % chunksize)
|
||||
if !remainder.isEmpty {
|
||||
words.append(Array(remainder))
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
public func md5() -> [Element] {
|
||||
Digest.md5(self)
|
||||
}
|
||||
|
||||
public func sha1() -> [Element] {
|
||||
Digest.sha1(self)
|
||||
}
|
||||
|
||||
public func sha224() -> [Element] {
|
||||
Digest.sha224(self)
|
||||
}
|
||||
|
||||
public func sha256() -> [Element] {
|
||||
Digest.sha256(self)
|
||||
}
|
||||
|
||||
public func sha384() -> [Element] {
|
||||
Digest.sha384(self)
|
||||
}
|
||||
|
||||
public func sha512() -> [Element] {
|
||||
Digest.sha512(self)
|
||||
}
|
||||
|
||||
public func sha2(_ variant: SHA2.Variant) -> [Element] {
|
||||
Digest.sha2(self, variant: variant)
|
||||
}
|
||||
|
||||
public func sha3(_ variant: SHA3.Variant) -> [Element] {
|
||||
Digest.sha3(self, variant: variant)
|
||||
}
|
||||
|
||||
public func crc32(seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
|
||||
Checksum.crc32(self, seed: seed, reflect: reflect)
|
||||
}
|
||||
|
||||
public func crc32c(seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
|
||||
Checksum.crc32c(self, seed: seed, reflect: reflect)
|
||||
}
|
||||
|
||||
public func crc16(seed: UInt16? = nil) -> UInt16 {
|
||||
Checksum.crc16(self, seed: seed)
|
||||
}
|
||||
|
||||
public func encrypt(cipher: Cipher) throws -> [Element] {
|
||||
try cipher.encrypt(self.slice)
|
||||
}
|
||||
|
||||
public func decrypt(cipher: Cipher) throws -> [Element] {
|
||||
try cipher.decrypt(self.slice)
|
||||
}
|
||||
|
||||
public func authenticate<A: Authenticator>(with authenticator: A) throws -> [Element] {
|
||||
try authenticator.authenticate(self)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
/// Message authentication code.
|
||||
public protocol Authenticator {
|
||||
/// Calculate Message Authentication Code (MAC) for message.
|
||||
func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8>
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
@usableFromInline
|
||||
struct BatchedCollectionIndex<Base: Collection> {
|
||||
let range: Range<Base.Index>
|
||||
}
|
||||
|
||||
extension BatchedCollectionIndex: Comparable {
|
||||
@usableFromInline
|
||||
static func == <Base>(lhs: BatchedCollectionIndex<Base>, rhs: BatchedCollectionIndex<Base>) -> Bool {
|
||||
lhs.range.lowerBound == rhs.range.lowerBound
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
static func < <Base>(lhs: BatchedCollectionIndex<Base>, rhs: BatchedCollectionIndex<Base>) -> Bool {
|
||||
lhs.range.lowerBound < rhs.range.lowerBound
|
||||
}
|
||||
}
|
||||
|
||||
protocol BatchedCollectionType: Collection {
|
||||
associatedtype Base: Collection
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
struct BatchedCollection<Base: Collection>: Collection {
|
||||
let base: Base
|
||||
let size: Int
|
||||
|
||||
@usableFromInline
|
||||
init(base: Base, size: Int) {
|
||||
self.base = base
|
||||
self.size = size
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
typealias Index = BatchedCollectionIndex<Base>
|
||||
|
||||
private func nextBreak(after idx: Base.Index) -> Base.Index {
|
||||
self.base.index(idx, offsetBy: self.size, limitedBy: self.base.endIndex) ?? self.base.endIndex
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
var startIndex: Index {
|
||||
Index(range: self.base.startIndex..<self.nextBreak(after: self.base.startIndex))
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
var endIndex: Index {
|
||||
Index(range: self.base.endIndex..<self.base.endIndex)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func index(after idx: Index) -> Index {
|
||||
Index(range: idx.range.upperBound..<self.nextBreak(after: idx.range.upperBound))
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
subscript(idx: Index) -> Base.SubSequence {
|
||||
self.base[idx.range]
|
||||
}
|
||||
}
|
||||
|
||||
extension Collection {
|
||||
@inlinable
|
||||
func batched(by size: Int) -> BatchedCollection<Self> {
|
||||
BatchedCollection(base: self, size: size)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
public enum Bit: Int {
|
||||
case zero
|
||||
case one
|
||||
}
|
||||
|
||||
extension Bit {
|
||||
@inlinable
|
||||
func inverted() -> Bit {
|
||||
self == .zero ? .one : .zero
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
protocol BlockCipher: Cipher {
|
||||
static var blockSize: Int { get }
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
public class BlockDecryptor: Cryptor, Updatable {
|
||||
@usableFromInline
|
||||
let blockSize: Int
|
||||
|
||||
@usableFromInline
|
||||
let padding: Padding
|
||||
|
||||
@usableFromInline
|
||||
var worker: CipherModeWorker
|
||||
|
||||
@usableFromInline
|
||||
var accumulated = Array<UInt8>()
|
||||
|
||||
@usableFromInline
|
||||
init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
|
||||
self.blockSize = blockSize
|
||||
self.padding = padding
|
||||
self.worker = worker
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
|
||||
self.accumulated += bytes
|
||||
|
||||
// If a worker (eg GCM) can combine ciphertext + tag
|
||||
// we need to remove tag from the ciphertext.
|
||||
if !isLast && self.accumulated.count < self.blockSize + self.worker.additionalBufferSize {
|
||||
return []
|
||||
}
|
||||
|
||||
let accumulatedWithoutSuffix: Array<UInt8>
|
||||
if self.worker.additionalBufferSize > 0 {
|
||||
// FIXME: how slow is that?
|
||||
accumulatedWithoutSuffix = Array(self.accumulated.prefix(self.accumulated.count - self.worker.additionalBufferSize))
|
||||
} else {
|
||||
accumulatedWithoutSuffix = self.accumulated
|
||||
}
|
||||
|
||||
var processedBytesCount = 0
|
||||
var plaintext = Array<UInt8>(reserveCapacity: accumulatedWithoutSuffix.count)
|
||||
// Processing in a block-size manner. It's good for block modes, but bad for stream modes.
|
||||
for var chunk in accumulatedWithoutSuffix.batched(by: self.blockSize) {
|
||||
if isLast || (accumulatedWithoutSuffix.count - processedBytesCount) >= blockSize {
|
||||
let isLastChunk = processedBytesCount + chunk.count == accumulatedWithoutSuffix.count
|
||||
|
||||
if isLast, isLastChunk, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
|
||||
chunk = try finalizingWorker.willDecryptLast(bytes: chunk + accumulated.suffix(worker.additionalBufferSize)) // tag size
|
||||
}
|
||||
|
||||
if !chunk.isEmpty {
|
||||
plaintext += worker.decrypt(block: chunk)
|
||||
}
|
||||
|
||||
if isLast, isLastChunk, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
|
||||
plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice))
|
||||
}
|
||||
|
||||
processedBytesCount += chunk.count
|
||||
}
|
||||
}
|
||||
accumulated.removeFirst(processedBytesCount) // super-slow
|
||||
|
||||
if isLast {
|
||||
if accumulatedWithoutSuffix.isEmpty, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
|
||||
try finalizingWorker.willDecryptLast(bytes: self.accumulated.suffix(self.worker.additionalBufferSize))
|
||||
plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice))
|
||||
}
|
||||
plaintext = self.padding.remove(from: plaintext, blockSize: self.blockSize)
|
||||
}
|
||||
|
||||
return plaintext
|
||||
}
|
||||
|
||||
public func seek(to position: Int) throws {
|
||||
guard var worker = self.worker as? SeekableModeWorker else {
|
||||
fatalError("Not supported")
|
||||
}
|
||||
|
||||
try worker.seek(to: position)
|
||||
self.worker = worker
|
||||
|
||||
accumulated = []
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
@usableFromInline
|
||||
final class BlockEncryptor: Cryptor, Updatable {
|
||||
private let blockSize: Int
|
||||
private var worker: CipherModeWorker
|
||||
private let padding: Padding
|
||||
// Accumulated bytes. Not all processed bytes.
|
||||
private var accumulated = Array<UInt8>(reserveCapacity: 16)
|
||||
|
||||
private var lastBlockRemainder = 0
|
||||
|
||||
@usableFromInline
|
||||
init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
|
||||
self.blockSize = blockSize
|
||||
self.padding = padding
|
||||
self.worker = worker
|
||||
}
|
||||
|
||||
// MARK: Updatable
|
||||
|
||||
public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8> {
|
||||
self.accumulated += bytes
|
||||
|
||||
if isLast {
|
||||
self.accumulated = self.padding.add(to: self.accumulated, blockSize: self.blockSize)
|
||||
}
|
||||
|
||||
var encrypted = Array<UInt8>(reserveCapacity: accumulated.count)
|
||||
for chunk in self.accumulated.batched(by: self.blockSize) {
|
||||
if isLast || chunk.count == self.blockSize {
|
||||
encrypted += self.worker.encrypt(block: chunk)
|
||||
}
|
||||
}
|
||||
|
||||
// Stream encrypts all, so it removes all elements
|
||||
self.accumulated.removeFirst(encrypted.count)
|
||||
|
||||
if var finalizingWorker = worker as? FinalizingEncryptModeWorker, isLast == true {
|
||||
encrypted = Array(try finalizingWorker.finalize(encrypt: encrypted.slice))
|
||||
}
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func seek(to: Int) throws {
|
||||
fatalError("Not supported")
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
public typealias CipherOperationOnBlock = (_ block: ArraySlice<UInt8>) -> Array<UInt8>?
|
||||
|
||||
public protocol BlockMode {
|
||||
var options: BlockModeOption { get }
|
||||
//TODO: doesn't have to be public
|
||||
@inlinable func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker
|
||||
|
||||
var customBlockSize: Int? { get }
|
||||
}
|
||||
|
||||
typealias StreamMode = BlockMode
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
public struct BlockModeOption: OptionSet {
|
||||
public let rawValue: Int
|
||||
|
||||
public init(rawValue: Int) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
static let none = BlockModeOption(rawValue: 1 << 0)
|
||||
|
||||
@usableFromInline
|
||||
static let initializationVectorRequired = BlockModeOption(rawValue: 1 << 1)
|
||||
|
||||
@usableFromInline
|
||||
static let paddingRequired = BlockModeOption(rawValue: 1 << 2)
|
||||
|
||||
@usableFromInline
|
||||
static let useEncryptToDecrypt = BlockModeOption(rawValue: 1 << 3)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// Cipher-block chaining (CBC)
|
||||
//
|
||||
|
||||
public struct CBC: BlockMode {
|
||||
public enum Error: Swift.Error {
|
||||
/// Invalid IV
|
||||
case invalidInitializationVector
|
||||
}
|
||||
|
||||
public let options: BlockModeOption = [.initializationVectorRequired, .paddingRequired]
|
||||
|
||||
private let iv: Array<UInt8>
|
||||
|
||||
public let customBlockSize: Int? = nil
|
||||
|
||||
public init(iv: Array<UInt8>) {
|
||||
self.iv = iv
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
if self.iv.count != blockSize {
|
||||
throw Error.invalidInitializationVector
|
||||
}
|
||||
|
||||
return CBCModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation)
|
||||
}
|
||||
}
|
||||
|
||||
struct CBCModeWorker: BlockModeWorker {
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
var blockSize: Int
|
||||
let additionalBufferSize: Int = 0
|
||||
private let iv: ArraySlice<UInt8>
|
||||
private var prev: ArraySlice<UInt8>?
|
||||
|
||||
@inlinable
|
||||
init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
|
||||
self.blockSize = blockSize
|
||||
self.iv = iv
|
||||
self.cipherOperation = cipherOperation
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
|
||||
return Array(plaintext)
|
||||
}
|
||||
self.prev = ciphertext.slice
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
guard let plaintext = cipherOperation(ciphertext) else {
|
||||
return Array(ciphertext)
|
||||
}
|
||||
let result: Array<UInt8> = xor(prev ?? self.iv, plaintext)
|
||||
self.prev = ciphertext
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -1,365 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// CCM mode combines the well known CBC-MAC with the well known counter mode of encryption.
|
||||
// https://tools.ietf.org/html/rfc3610
|
||||
// https://csrc.nist.gov/publications/detail/sp/800-38c/final
|
||||
|
||||
#if canImport(Darwin)
|
||||
import Darwin
|
||||
#elseif canImport(Glibc)
|
||||
import Glibc
|
||||
#elseif canImport(ucrt)
|
||||
import ucrt
|
||||
#endif
|
||||
|
||||
/// Counter with Cipher Block Chaining-Message Authentication Code
|
||||
public struct CCM: StreamMode {
|
||||
public enum Error: Swift.Error {
|
||||
/// Invalid IV
|
||||
case invalidInitializationVector
|
||||
case invalidParameter
|
||||
case fail
|
||||
}
|
||||
|
||||
public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
|
||||
private let nonce: Array<UInt8>
|
||||
private let additionalAuthenticatedData: Array<UInt8>?
|
||||
private let tagLength: Int
|
||||
private let messageLength: Int // total message length. need to know in advance
|
||||
public let customBlockSize: Int? = nil
|
||||
|
||||
// `authenticationTag` nil for encryption, known tag for decryption
|
||||
/// For encryption, the value is set at the end of the encryption.
|
||||
/// For decryption, this is a known Tag to validate against.
|
||||
public var authenticationTag: Array<UInt8>?
|
||||
|
||||
/// Initialize CCM
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - iv: Initialization vector. Nonce. Valid length between 7 and 13 bytes.
|
||||
/// - tagLength: Authentication tag length, in bytes. Value of {4, 6, 8, 10, 12, 14, 16}.
|
||||
/// - messageLength: Plaintext message length (excluding tag if attached). Length have to be provided in advance.
|
||||
/// - additionalAuthenticatedData: Additional authenticated data.
|
||||
public init(iv: Array<UInt8>, tagLength: Int, messageLength: Int, additionalAuthenticatedData: Array<UInt8>? = nil) {
|
||||
self.nonce = iv
|
||||
self.tagLength = tagLength
|
||||
self.additionalAuthenticatedData = additionalAuthenticatedData
|
||||
self.messageLength = messageLength // - tagLength
|
||||
}
|
||||
|
||||
/// Initialize CCM
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - iv: Initialization vector. Nonce. Valid length between 7 and 13 bytes.
|
||||
/// - tagLength: Authentication tag length, in bytes. Value of {4, 6, 8, 10, 12, 14, 16}.
|
||||
/// - messageLength: Plaintext message length (excluding tag if attached). Length have to be provided in advance.
|
||||
/// - authenticationTag: Authentication Tag value if not concatenated to ciphertext.
|
||||
/// - additionalAuthenticatedData: Additional authenticated data.
|
||||
public init(iv: Array<UInt8>, tagLength: Int, messageLength: Int, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil) {
|
||||
self.init(iv: iv, tagLength: tagLength, messageLength: messageLength, additionalAuthenticatedData: additionalAuthenticatedData)
|
||||
self.authenticationTag = authenticationTag
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
if self.nonce.isEmpty {
|
||||
throw Error.invalidInitializationVector
|
||||
}
|
||||
|
||||
return CCMModeWorker(blockSize: blockSize, nonce: self.nonce.slice, messageLength: self.messageLength, additionalAuthenticatedData: self.additionalAuthenticatedData, tagLength: self.tagLength, cipherOperation: cipherOperation)
|
||||
}
|
||||
}
|
||||
|
||||
class CCMModeWorker: StreamModeWorker, SeekableModeWorker, CounterModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker {
|
||||
typealias Counter = Int
|
||||
var counter = 0
|
||||
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
let blockSize: Int
|
||||
private let tagLength: Int
|
||||
private let messageLength: Int // total message length. need to know in advance
|
||||
private let q: UInt8
|
||||
|
||||
let additionalBufferSize: Int
|
||||
private var keystreamPosIdx = 0
|
||||
private let nonce: Array<UInt8>
|
||||
private var last_y: ArraySlice<UInt8> = []
|
||||
private var keystream: Array<UInt8> = []
|
||||
// Known Tag used to validate during decryption
|
||||
private var expectedTag: Array<UInt8>?
|
||||
|
||||
public enum Error: Swift.Error {
|
||||
case invalidParameter
|
||||
}
|
||||
|
||||
init(blockSize: Int, nonce: ArraySlice<UInt8>, messageLength: Int, additionalAuthenticatedData: [UInt8]?, expectedTag: Array<UInt8>? = nil, tagLength: Int, cipherOperation: @escaping CipherOperationOnBlock) {
|
||||
self.blockSize = 16 // CCM is defined for 128 block size
|
||||
self.tagLength = tagLength
|
||||
self.additionalBufferSize = tagLength
|
||||
self.messageLength = messageLength
|
||||
self.expectedTag = expectedTag
|
||||
self.cipherOperation = cipherOperation
|
||||
self.nonce = Array(nonce)
|
||||
self.q = UInt8(15 - nonce.count) // n = 15-q
|
||||
|
||||
let hasAssociatedData = additionalAuthenticatedData != nil && !additionalAuthenticatedData!.isEmpty
|
||||
self.processControlInformation(nonce: self.nonce, tagLength: tagLength, hasAssociatedData: hasAssociatedData)
|
||||
|
||||
if let aad = additionalAuthenticatedData, hasAssociatedData {
|
||||
self.process(aad: aad)
|
||||
}
|
||||
}
|
||||
|
||||
// For the very first time setup new IV (aka y0) from the block0
|
||||
private func processControlInformation(nonce: [UInt8], tagLength: Int, hasAssociatedData: Bool) {
|
||||
let block0 = try! format(nonce: nonce, Q: UInt32(self.messageLength), q: self.q, t: UInt8(tagLength), hasAssociatedData: hasAssociatedData).slice
|
||||
let y0 = self.cipherOperation(block0)!.slice
|
||||
self.last_y = y0
|
||||
}
|
||||
|
||||
private func process(aad: [UInt8]) {
|
||||
let encodedAAD = format(aad: aad)
|
||||
|
||||
for block_i in encodedAAD.batched(by: 16) {
|
||||
let y_i = self.cipherOperation(xor(block_i, self.last_y))!.slice
|
||||
self.last_y = y_i
|
||||
}
|
||||
}
|
||||
|
||||
private func S(i: Int) throws -> [UInt8] {
|
||||
let ctr = try format(counter: i, nonce: nonce, q: q)
|
||||
return self.cipherOperation(ctr.slice)!
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func seek(to position: Int) throws {
|
||||
self.counter = position
|
||||
self.keystream = try self.S(i: position)
|
||||
let offset = position % self.blockSize
|
||||
self.keystreamPosIdx = offset
|
||||
}
|
||||
|
||||
func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
var result = Array<UInt8>(reserveCapacity: plaintext.count)
|
||||
|
||||
var processed = 0
|
||||
while processed < plaintext.count {
|
||||
// Need a full block here to update keystream and do CBC
|
||||
if self.keystream.isEmpty || self.keystreamPosIdx == self.blockSize {
|
||||
// y[i], where i is the counter. Can encrypt 1 block at a time
|
||||
self.counter += 1
|
||||
guard let S = try? S(i: counter) else { return Array(plaintext) }
|
||||
let plaintextP = addPadding(Array(plaintext), blockSize: blockSize)
|
||||
guard let y = cipherOperation(xor(last_y, plaintextP)) else { return Array(plaintext) }
|
||||
self.last_y = y.slice
|
||||
|
||||
self.keystream = S
|
||||
self.keystreamPosIdx = 0
|
||||
}
|
||||
|
||||
let xored: Array<UInt8> = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...])
|
||||
keystreamPosIdx += xored.count
|
||||
processed += xored.count
|
||||
result += xored
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// concatenate T at the end
|
||||
guard let S0 = try? S(i: 0) else { return ciphertext }
|
||||
|
||||
let computedTag = xor(last_y.prefix(self.tagLength), S0) as ArraySlice<UInt8>
|
||||
return ciphertext + computedTag
|
||||
}
|
||||
|
||||
// Decryption is stream
|
||||
// CBC is block
|
||||
private var accumulatedPlaintext: [UInt8] = []
|
||||
|
||||
func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
var output = Array<UInt8>(reserveCapacity: ciphertext.count)
|
||||
|
||||
do {
|
||||
var currentCounter = self.counter
|
||||
var processed = 0
|
||||
while processed < ciphertext.count {
|
||||
// Need a full block here to update keystream and do CBC
|
||||
// New keystream for a new block
|
||||
if self.keystream.isEmpty || self.keystreamPosIdx == self.blockSize {
|
||||
currentCounter += 1
|
||||
guard let S = try? S(i: currentCounter) else { return Array(ciphertext) }
|
||||
self.keystream = S
|
||||
self.keystreamPosIdx = 0
|
||||
}
|
||||
|
||||
let xored: Array<UInt8> = xor(ciphertext[ciphertext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...]) // plaintext
|
||||
keystreamPosIdx += xored.count
|
||||
processed += xored.count
|
||||
output += xored
|
||||
self.counter = currentCounter
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate plaintext for the MAC calculations at the end.
|
||||
// It would be good to process it together though, here.
|
||||
self.accumulatedPlaintext += output
|
||||
|
||||
// Shouldn't return plaintext until validate tag.
|
||||
// With incremental update, can't validate tag until all block are processed.
|
||||
return output
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// concatenate T at the end
|
||||
let computedTag = Array(last_y.prefix(self.tagLength))
|
||||
guard let expectedTag = self.expectedTag, expectedTag == computedTag else {
|
||||
throw CCM.Error.fail
|
||||
}
|
||||
|
||||
return plaintext
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// get tag of additionalBufferSize size
|
||||
// `ciphertext` contains at least additionalBufferSize bytes
|
||||
// overwrite expectedTag property used later for verification
|
||||
guard let S0 = try? S(i: 0) else { return ciphertext }
|
||||
self.expectedTag = xor(ciphertext.suffix(self.tagLength), S0) as [UInt8]
|
||||
return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(tagLength, ciphertext.count))]
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
|
||||
// Calculate Tag, from the last CBC block, for accumulated plaintext.
|
||||
var processed = 0
|
||||
for block in self.accumulatedPlaintext.batched(by: self.blockSize) {
|
||||
let blockP = addPadding(Array(block), blockSize: blockSize)
|
||||
guard let y = cipherOperation(xor(last_y, blockP)) else { return plaintext }
|
||||
self.last_y = y.slice
|
||||
processed += block.count
|
||||
}
|
||||
self.accumulatedPlaintext.removeFirst(processed)
|
||||
return plaintext
|
||||
}
|
||||
}
|
||||
|
||||
// Q - octet length of P
|
||||
// q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
|
||||
// t - octet length of T (MAC length). An element of {4,6,8,10,12,14,16}
|
||||
private func format(nonce N: [UInt8], Q: UInt32, q: UInt8, t: UInt8, hasAssociatedData: Bool) throws -> [UInt8] {
|
||||
var flags0: UInt8 = 0
|
||||
|
||||
if hasAssociatedData {
|
||||
// 7 bit
|
||||
flags0 |= (1 << 6)
|
||||
}
|
||||
|
||||
// 6,5,4 bit is t in 3 bits
|
||||
flags0 |= (((t - 2) / 2) & 0x07) << 3
|
||||
|
||||
// 3,2,1 bit is q in 3 bits
|
||||
flags0 |= ((q - 1) & 0x07) << 0
|
||||
|
||||
var block0: [UInt8] = Array<UInt8>(repeating: 0, count: 16)
|
||||
block0[0] = flags0
|
||||
|
||||
// N in 1...(15-q) octets, n = 15-q
|
||||
// n is an element of {7,8,9,10,11,12,13}
|
||||
let n = 15 - Int(q)
|
||||
guard (n + Int(q)) == 15 else {
|
||||
// n+q == 15
|
||||
throw CCMModeWorker.Error.invalidParameter
|
||||
}
|
||||
block0[1...n] = N[0...(n - 1)]
|
||||
|
||||
// Q in (16-q)...15 octets
|
||||
block0[(16 - Int(q))...15] = Q.bytes(totalBytes: Int(q)).slice
|
||||
|
||||
return block0
|
||||
}
|
||||
|
||||
/// Formatting of the Counter Blocks. Ctr[i]
|
||||
/// The counter generation function.
|
||||
/// Q - octet length of P
|
||||
/// q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
|
||||
private func format(counter i: Int, nonce N: [UInt8], q: UInt8) throws -> [UInt8] {
|
||||
var flags0: UInt8 = 0
|
||||
|
||||
// bit 8,7 is Reserved
|
||||
// bit 4,5,6 shall be set to 0
|
||||
// 3,2,1 bit is q in 3 bits
|
||||
flags0 |= ((q - 1) & 0x07) << 0
|
||||
|
||||
var block = Array<UInt8>(repeating: 0, count: 16) // block[0]
|
||||
block[0] = flags0
|
||||
|
||||
// N in 1...(15-q) octets, n = 15-q
|
||||
// n is an element of {7,8,9,10,11,12,13}
|
||||
let n = 15 - Int(q)
|
||||
guard (n + Int(q)) == 15 else {
|
||||
// n+q == 15
|
||||
throw CCMModeWorker.Error.invalidParameter
|
||||
}
|
||||
block[1...n] = N[0...(n - 1)]
|
||||
|
||||
// [i]8q in (16-q)...15 octets
|
||||
block[(16 - Int(q))...15] = i.bytes(totalBytes: Int(q)).slice
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
/// Resulting can be partitioned into 16-octet blocks
|
||||
private func format(aad: [UInt8]) -> [UInt8] {
|
||||
let a = aad.count
|
||||
|
||||
switch Double(a) {
|
||||
case 0..<65280: // 2^16-2^8
|
||||
// [a]16
|
||||
return addPadding(a.bytes(totalBytes: 2) + aad, blockSize: 16)
|
||||
case 65280..<4_294_967_296: // 2^32
|
||||
// [a]32
|
||||
return addPadding([0xFF, 0xFE] + a.bytes(totalBytes: 4) + aad, blockSize: 16)
|
||||
case 4_294_967_296..<pow(2, 64): // 2^64
|
||||
// [a]64
|
||||
return addPadding([0xFF, 0xFF] + a.bytes(totalBytes: 8) + aad, blockSize: 16)
|
||||
default:
|
||||
// Reserved
|
||||
return addPadding(aad, blockSize: 16)
|
||||
}
|
||||
}
|
||||
|
||||
// If data is not a multiple of block size bytes long then the remainder is zero padded
|
||||
// Note: It's similar to ZeroPadding, but it's not the same.
|
||||
private func addPadding(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
|
||||
if bytes.isEmpty {
|
||||
return Array<UInt8>(repeating: 0, count: blockSize)
|
||||
}
|
||||
|
||||
let remainder = bytes.count % blockSize
|
||||
if remainder == 0 {
|
||||
return bytes
|
||||
}
|
||||
|
||||
let paddingCount = blockSize - remainder
|
||||
if paddingCount > 0 {
|
||||
return bytes + Array<UInt8>(repeating: 0, count: paddingCount)
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// Cipher feedback (CFB)
|
||||
//
|
||||
|
||||
public struct CFB: BlockMode {
|
||||
public enum Error: Swift.Error {
|
||||
/// Invalid IV
|
||||
case invalidInitializationVector
|
||||
}
|
||||
|
||||
public enum SegmentSize: Int {
|
||||
case cfb8 = 1 // Encrypt byte per byte
|
||||
case cfb128 = 16 // Encrypt 16 bytes per 16 bytes (default)
|
||||
}
|
||||
|
||||
public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
|
||||
private let iv: Array<UInt8>
|
||||
private let segmentSize: SegmentSize
|
||||
public let customBlockSize: Int?
|
||||
|
||||
public init(iv: Array<UInt8>, segmentSize: SegmentSize = .cfb128) {
|
||||
self.iv = iv
|
||||
self.segmentSize = segmentSize
|
||||
self.customBlockSize = segmentSize.rawValue
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
if !(self.iv.count == blockSize || (segmentSize == .cfb8 && self.iv.count == AES.blockSize)) {
|
||||
throw Error.invalidInitializationVector
|
||||
}
|
||||
|
||||
return CFBModeWorker(blockSize: blockSize, iv: self.iv.slice, segmentSize: segmentSize, cipherOperation: cipherOperation)
|
||||
}
|
||||
}
|
||||
|
||||
struct CFBModeWorker: BlockModeWorker {
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
let blockSize: Int
|
||||
let additionalBufferSize: Int = 0
|
||||
private let iv: ArraySlice<UInt8>
|
||||
private let segmentSize: CFB.SegmentSize
|
||||
private var prev: ArraySlice<UInt8>?
|
||||
|
||||
init(blockSize: Int, iv: ArraySlice<UInt8>, segmentSize: CFB.SegmentSize, cipherOperation: @escaping CipherOperationOnBlock) {
|
||||
self.blockSize = blockSize
|
||||
self.iv = iv
|
||||
self.segmentSize = segmentSize
|
||||
self.cipherOperation = cipherOperation
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
switch segmentSize {
|
||||
case .cfb128:
|
||||
guard let ciphertext = cipherOperation(prev ?? iv) else {
|
||||
return Array(plaintext)
|
||||
}
|
||||
self.prev = xor(plaintext, ciphertext.slice)
|
||||
return Array(self.prev ?? [])
|
||||
case .cfb8:
|
||||
guard let ciphertext = cipherOperation(prev ?? iv) else {
|
||||
return Array(plaintext)
|
||||
}
|
||||
let result = [Array(plaintext)[0] ^ Array(ciphertext)[0]]
|
||||
self.prev = Array((prev ?? iv).dropFirst()) + [result[0]]
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
switch segmentSize {
|
||||
case .cfb128:
|
||||
guard let plaintext = cipherOperation(prev ?? iv) else {
|
||||
return Array(ciphertext)
|
||||
}
|
||||
let result: Array<UInt8> = xor(plaintext, ciphertext)
|
||||
prev = ciphertext
|
||||
return result
|
||||
case .cfb8:
|
||||
guard let plaintext = cipherOperation(prev ?? iv) else {
|
||||
return Array(ciphertext)
|
||||
}
|
||||
self.prev = Array((prev ?? iv).dropFirst()) + [Array(ciphertext)[0]]
|
||||
return [Array(ciphertext)[0] ^ Array(plaintext)[0]]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// Counter (CTR)
|
||||
|
||||
public struct CTR: StreamMode {
|
||||
public enum Error: Swift.Error {
|
||||
/// Invalid IV
|
||||
case invalidInitializationVector
|
||||
}
|
||||
|
||||
public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
|
||||
private let iv: Array<UInt8>
|
||||
private let counter: Int
|
||||
public let customBlockSize: Int? = nil
|
||||
|
||||
public init(iv: Array<UInt8>, counter: Int = 0) {
|
||||
self.iv = iv
|
||||
self.counter = counter
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
if self.iv.count != blockSize {
|
||||
throw Error.invalidInitializationVector
|
||||
}
|
||||
|
||||
return CTRModeWorker(blockSize: blockSize, iv: self.iv.slice, counter: self.counter, cipherOperation: cipherOperation)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct CTRModeWorker: StreamModeWorker, SeekableModeWorker, CounterModeWorker {
|
||||
typealias Counter = CTRCounter
|
||||
|
||||
final class CTRCounter {
|
||||
private let constPrefix: Array<UInt8>
|
||||
private var value: UInt64
|
||||
//TODO: make it an updatable value, computing is too slow
|
||||
var bytes: Array<UInt8> {
|
||||
self.constPrefix + self.value.bytes()
|
||||
}
|
||||
|
||||
@inlinable
|
||||
init(_ initialValue: Array<UInt8>) {
|
||||
let halfIndex = initialValue.startIndex.advanced(by: initialValue.count / 2)
|
||||
self.constPrefix = Array(initialValue[initialValue.startIndex..<halfIndex])
|
||||
|
||||
let suffixBytes = Array(initialValue[halfIndex...])
|
||||
value = UInt64(bytes: suffixBytes)
|
||||
}
|
||||
|
||||
convenience init(nonce: Array<UInt8>, startAt index: Int) {
|
||||
self.init(buildCounterValue(nonce, counter: UInt64(index)))
|
||||
}
|
||||
|
||||
static func += (lhs: CTRCounter, rhs: Int) {
|
||||
lhs.value += UInt64(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
let additionalBufferSize: Int = 0
|
||||
let iv: Array<UInt8>
|
||||
var counter: CTRCounter
|
||||
|
||||
private let blockSize: Int
|
||||
|
||||
// The same keystream is used for the block length plaintext
|
||||
// As new data is added, keystream suffix is used to xor operation.
|
||||
private var keystream: Array<UInt8>
|
||||
private var keystreamPosIdx = 0
|
||||
|
||||
init(blockSize: Int, iv: ArraySlice<UInt8>, counter: Int, cipherOperation: @escaping CipherOperationOnBlock) {
|
||||
self.cipherOperation = cipherOperation
|
||||
self.blockSize = blockSize
|
||||
self.iv = Array(iv)
|
||||
|
||||
// the first keystream is calculated from the nonce = initial value of counter
|
||||
self.counter = CTRCounter(nonce: Array(iv), startAt: counter)
|
||||
self.keystream = Array(cipherOperation(self.counter.bytes.slice)!)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func seek(to position: Int) throws {
|
||||
let offset = position % self.blockSize
|
||||
self.counter = CTRCounter(nonce: self.iv, startAt: position / self.blockSize)
|
||||
self.keystream = Array(self.cipherOperation(self.counter.bytes.slice)!)
|
||||
self.keystreamPosIdx = offset
|
||||
}
|
||||
|
||||
// plaintext is at most blockSize long
|
||||
@inlinable
|
||||
mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
var result = Array<UInt8>(reserveCapacity: plaintext.count)
|
||||
|
||||
var processed = 0
|
||||
while processed < plaintext.count {
|
||||
// Update keystream
|
||||
if self.keystreamPosIdx == self.blockSize {
|
||||
self.counter += 1
|
||||
self.keystream = Array(self.cipherOperation(self.counter.bytes.slice)!)
|
||||
self.keystreamPosIdx = 0
|
||||
}
|
||||
|
||||
let xored: Array<UInt8> = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...])
|
||||
keystreamPosIdx += xored.count
|
||||
processed += xored.count
|
||||
result += xored
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
self.encrypt(block: ciphertext)
|
||||
}
|
||||
}
|
||||
|
||||
private func buildCounterValue(_ iv: Array<UInt8>, counter: UInt64) -> Array<UInt8> {
|
||||
let noncePartLen = iv.count / 2
|
||||
let noncePrefix = iv[iv.startIndex..<iv.startIndex.advanced(by: noncePartLen)]
|
||||
let nonceSuffix = iv[iv.startIndex.advanced(by: noncePartLen)..<iv.startIndex.advanced(by: iv.count)]
|
||||
let c = UInt64(bytes: nonceSuffix) + counter
|
||||
return noncePrefix + c.bytes()
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
public protocol CipherModeWorker {
|
||||
var cipherOperation: CipherOperationOnBlock { get }
|
||||
|
||||
// Additional space needed when incrementally process data
|
||||
// eg. for GCM combined mode
|
||||
var additionalBufferSize: Int { get }
|
||||
|
||||
@inlinable
|
||||
mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8>
|
||||
|
||||
@inlinable
|
||||
mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8>
|
||||
}
|
||||
|
||||
/// Block workers use `BlockEncryptor`
|
||||
public protocol BlockModeWorker: CipherModeWorker {
|
||||
var blockSize: Int { get }
|
||||
}
|
||||
|
||||
public protocol CounterModeWorker: CipherModeWorker {
|
||||
associatedtype Counter
|
||||
var counter: Counter { get set }
|
||||
}
|
||||
|
||||
public protocol SeekableModeWorker: CipherModeWorker {
|
||||
mutating func seek(to position: Int) throws
|
||||
}
|
||||
|
||||
/// Stream workers use `StreamEncryptor`
|
||||
public protocol StreamModeWorker: CipherModeWorker {
|
||||
}
|
||||
|
||||
public protocol FinalizingEncryptModeWorker: CipherModeWorker {
|
||||
// Any final calculations, eg. calculate tag
|
||||
// Called after the last block is encrypted
|
||||
mutating func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8>
|
||||
}
|
||||
|
||||
public protocol FinalizingDecryptModeWorker: CipherModeWorker {
|
||||
// Called before decryption, hence input is ciphertext.
|
||||
// ciphertext is either a last block, or a tag (for stream workers)
|
||||
@discardableResult
|
||||
mutating func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8>
|
||||
// Called after decryption, hence input is ciphertext
|
||||
mutating func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8>
|
||||
// Any final calculations, eg. calculate tag
|
||||
// Called after the last block is encrypted
|
||||
mutating func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8>
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// Electronic codebook (ECB)
|
||||
//
|
||||
|
||||
public struct ECB: BlockMode {
|
||||
public let options: BlockModeOption = .paddingRequired
|
||||
public let customBlockSize: Int? = nil
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
ECBModeWorker(blockSize: blockSize, cipherOperation: cipherOperation)
|
||||
}
|
||||
}
|
||||
|
||||
struct ECBModeWorker: BlockModeWorker {
|
||||
typealias Element = Array<UInt8>
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
let blockSize: Int
|
||||
let additionalBufferSize: Int = 0
|
||||
|
||||
init(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) {
|
||||
self.blockSize = blockSize
|
||||
self.cipherOperation = cipherOperation
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
guard let ciphertext = cipherOperation(plaintext) else {
|
||||
return Array(plaintext)
|
||||
}
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
self.encrypt(block: ciphertext)
|
||||
}
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// Galois/Counter Mode (GCM)
|
||||
// https://csrc.nist.gov/publications/detail/sp/800-38d/final
|
||||
// ref: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.694.695&rep=rep1&type=pdf
|
||||
//
|
||||
|
||||
public final class GCM: BlockMode {
|
||||
public enum Mode {
|
||||
/// In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want.
|
||||
case combined
|
||||
/// Some applications may need to store the authentication tag and the encrypted message at different locations.
|
||||
case detached
|
||||
}
|
||||
|
||||
public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
|
||||
|
||||
public enum Error: Swift.Error {
|
||||
/// Invalid IV
|
||||
case invalidInitializationVector
|
||||
/// Special symbol FAIL that indicates that the inputs are not authentic.
|
||||
case fail
|
||||
}
|
||||
|
||||
private let iv: Array<UInt8>
|
||||
private let additionalAuthenticatedData: Array<UInt8>?
|
||||
private let mode: Mode
|
||||
public let customBlockSize: Int? = nil
|
||||
|
||||
/// Length of authentication tag, in bytes.
|
||||
/// For encryption, the value is given as init parameter.
|
||||
/// For decryption, the length of given authentication tag is used.
|
||||
private let tagLength: Int
|
||||
|
||||
// `authenticationTag` nil for encryption, known tag for decryption
|
||||
/// For encryption, the value is set at the end of the encryption.
|
||||
/// For decryption, this is a known Tag to validate against.
|
||||
public var authenticationTag: Array<UInt8>?
|
||||
|
||||
// encrypt
|
||||
/// Possible tag lengths: 4,8,12,13,14,15,16
|
||||
public init(iv: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, tagLength: Int = 16, mode: Mode = .detached) {
|
||||
self.iv = iv
|
||||
self.additionalAuthenticatedData = additionalAuthenticatedData
|
||||
self.mode = mode
|
||||
self.tagLength = tagLength
|
||||
}
|
||||
|
||||
// decrypt
|
||||
public convenience init(iv: Array<UInt8>, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, mode: Mode = .detached) {
|
||||
self.init(iv: iv, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode)
|
||||
self.authenticationTag = authenticationTag
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
if self.iv.isEmpty {
|
||||
throw Error.invalidInitializationVector
|
||||
}
|
||||
|
||||
let worker = GCMModeWorker(iv: iv.slice, aad: self.additionalAuthenticatedData?.slice, expectedTag: self.authenticationTag, tagLength: self.tagLength, mode: self.mode, cipherOperation: cipherOperation)
|
||||
worker.didCalculateTag = { [weak self] tag in
|
||||
self?.authenticationTag = tag
|
||||
}
|
||||
return worker
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Worker
|
||||
|
||||
final class GCMModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker {
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
|
||||
// Callback called when authenticationTag is ready
|
||||
var didCalculateTag: ((Array<UInt8>) -> Void)?
|
||||
|
||||
private let tagLength: Int
|
||||
// GCM nonce is 96-bits by default. It's the most effective length for the IV
|
||||
private static let nonceSize = 12
|
||||
|
||||
// GCM is designed for 128-bit ciphers like AES (but not really for Blowfish). 64-bit mode is not implemented.
|
||||
let blockSize = 16 // 128 bit
|
||||
let additionalBufferSize: Int
|
||||
private let iv: ArraySlice<UInt8>
|
||||
private let mode: GCM.Mode
|
||||
private var counter: UInt128
|
||||
private let eky0: UInt128 // move to GF?
|
||||
private let h: UInt128
|
||||
|
||||
// Additional authenticated data
|
||||
private let aad: ArraySlice<UInt8>?
|
||||
// Known Tag used to validate during decryption
|
||||
private var expectedTag: Array<UInt8>?
|
||||
|
||||
// Note: need new worker to reset instance
|
||||
// Use empty aad if not specified. AAD is optional.
|
||||
private lazy var gf: GF = {
|
||||
if let aad = aad {
|
||||
return GF(aad: Array(aad), h: h, blockSize: blockSize)
|
||||
}
|
||||
return GF(aad: [UInt8](), h: h, blockSize: blockSize)
|
||||
}()
|
||||
|
||||
init(iv: ArraySlice<UInt8>, aad: ArraySlice<UInt8>? = nil, expectedTag: Array<UInt8>? = nil, tagLength: Int, mode: GCM.Mode, cipherOperation: @escaping CipherOperationOnBlock) {
|
||||
self.cipherOperation = cipherOperation
|
||||
self.iv = iv
|
||||
self.mode = mode
|
||||
self.aad = aad
|
||||
self.expectedTag = expectedTag
|
||||
self.tagLength = tagLength
|
||||
self.h = UInt128(cipherOperation(Array<UInt8>(repeating: 0, count: self.blockSize).slice)!) // empty block
|
||||
|
||||
if mode == .combined {
|
||||
self.additionalBufferSize = tagLength
|
||||
} else {
|
||||
self.additionalBufferSize = 0
|
||||
}
|
||||
|
||||
// Assume nonce is 12 bytes long, otherwise initial counter would be calulated from GHASH
|
||||
// counter = GF.ghash(aad: [UInt8](), ciphertext: nonce)
|
||||
if iv.count == GCMModeWorker.nonceSize {
|
||||
self.counter = makeCounter(nonce: Array(self.iv))
|
||||
} else {
|
||||
self.counter = GF.ghash(h: self.h, aad: [UInt8](), ciphertext: Array(iv), blockSize: self.blockSize)
|
||||
}
|
||||
|
||||
// Set constants
|
||||
self.eky0 = UInt128(cipherOperation(self.counter.bytes.slice)!)
|
||||
}
|
||||
|
||||
func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
self.counter = incrementCounter(self.counter)
|
||||
|
||||
guard let ekyN = cipherOperation(counter.bytes.slice) else {
|
||||
return Array(plaintext)
|
||||
}
|
||||
|
||||
// plaintext block ^ ek1
|
||||
let ciphertext = xor(plaintext, ekyN) as Array<UInt8>
|
||||
|
||||
// update ghash incrementally
|
||||
gf.ghashUpdate(block: ciphertext)
|
||||
|
||||
return Array(ciphertext)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// Calculate MAC tag.
|
||||
let ghash = self.gf.ghashFinish()
|
||||
let tag = Array((ghash ^ self.eky0).bytes.prefix(self.tagLength))
|
||||
|
||||
// Notify handler
|
||||
self.didCalculateTag?(tag)
|
||||
|
||||
switch self.mode {
|
||||
case .combined:
|
||||
return (ciphertext + tag).slice
|
||||
case .detached:
|
||||
return ciphertext
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
self.counter = incrementCounter(self.counter)
|
||||
|
||||
// update ghash incrementally
|
||||
self.gf.ghashUpdate(block: Array(ciphertext))
|
||||
|
||||
guard let ekN = cipherOperation(counter.bytes.slice) else {
|
||||
return Array(ciphertext)
|
||||
}
|
||||
|
||||
// ciphertext block ^ ek1
|
||||
let plaintext = xor(ciphertext, ekN) as Array<UInt8>
|
||||
return plaintext
|
||||
}
|
||||
|
||||
// The authenticated decryption operation has five inputs: K, IV , C, A, and T. It has only a single
|
||||
// output, either the plaintext value P or a special symbol FAIL that indicates that the inputs are not
|
||||
// authentic.
|
||||
@discardableResult
|
||||
func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// Validate tag
|
||||
switch self.mode {
|
||||
case .combined:
|
||||
// overwrite expectedTag property used later for verification
|
||||
self.expectedTag = Array(ciphertext.suffix(self.tagLength))
|
||||
return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(tagLength, ciphertext.count))]
|
||||
case .detached:
|
||||
return ciphertext
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// Calculate MAC tag.
|
||||
let ghash = self.gf.ghashFinish()
|
||||
let computedTag = Array((ghash ^ self.eky0).bytes.prefix(self.tagLength))
|
||||
|
||||
// Validate tag
|
||||
guard let expectedTag = self.expectedTag, computedTag == expectedTag else {
|
||||
throw GCM.Error.fail
|
||||
}
|
||||
|
||||
return plaintext
|
||||
}
|
||||
|
||||
func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// do nothing
|
||||
plaintext
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Local utils
|
||||
|
||||
private func makeCounter(nonce: Array<UInt8>) -> UInt128 {
|
||||
UInt128(nonce + [0, 0, 0, 1])
|
||||
}
|
||||
|
||||
// Successive counter values are generated using the function incr(), which treats the rightmost 32
|
||||
// bits of its argument as a nonnegative integer with the least significant bit on the right
|
||||
private func incrementCounter(_ counter: UInt128) -> UInt128 {
|
||||
let b = counter.i.b + 1
|
||||
let a = (b == 0 ? counter.i.a + 1 : counter.i.a)
|
||||
return UInt128((a, b))
|
||||
}
|
||||
|
||||
// If data is not a multiple of block size bytes long then the remainder is zero padded
|
||||
// Note: It's similar to ZeroPadding, but it's not the same.
|
||||
private func addPadding(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
|
||||
if bytes.isEmpty {
|
||||
return Array<UInt8>(repeating: 0, count: blockSize)
|
||||
}
|
||||
|
||||
let remainder = bytes.count % blockSize
|
||||
if remainder == 0 {
|
||||
return bytes
|
||||
}
|
||||
|
||||
let paddingCount = blockSize - remainder
|
||||
if paddingCount > 0 {
|
||||
return bytes + Array<UInt8>(repeating: 0, count: paddingCount)
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
// MARK: - GF
|
||||
|
||||
/// The Field GF(2^128)
|
||||
private final class GF {
|
||||
static let r = UInt128(a: 0xE100000000000000, b: 0)
|
||||
|
||||
let blockSize: Int
|
||||
let h: UInt128
|
||||
|
||||
// AAD won't change
|
||||
let aadLength: Int
|
||||
|
||||
// Updated for every consumed block
|
||||
var ciphertextLength: Int
|
||||
|
||||
// Start with 0
|
||||
var x: UInt128
|
||||
|
||||
init(aad: [UInt8], h: UInt128, blockSize: Int) {
|
||||
self.blockSize = blockSize
|
||||
self.aadLength = aad.count
|
||||
self.ciphertextLength = 0
|
||||
self.h = h
|
||||
self.x = 0
|
||||
|
||||
// Calculate for AAD at the begining
|
||||
self.x = GF.calculateX(aad: aad, x: self.x, h: h, blockSize: blockSize)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func ghashUpdate(block ciphertextBlock: Array<UInt8>) -> UInt128 {
|
||||
self.ciphertextLength += ciphertextBlock.count
|
||||
self.x = GF.calculateX(block: addPadding(ciphertextBlock, blockSize: self.blockSize), x: self.x, h: self.h, blockSize: self.blockSize)
|
||||
return self.x
|
||||
}
|
||||
|
||||
func ghashFinish() -> UInt128 {
|
||||
// len(A) || len(C)
|
||||
let len = UInt128(a: UInt64(aadLength * 8), b: UInt64(ciphertextLength * 8))
|
||||
x = GF.multiply(self.x ^ len, self.h)
|
||||
return self.x
|
||||
}
|
||||
|
||||
// GHASH. One-time calculation
|
||||
static func ghash(x startx: UInt128 = 0, h: UInt128, aad: Array<UInt8>, ciphertext: Array<UInt8>, blockSize: Int) -> UInt128 {
|
||||
var x = self.calculateX(aad: aad, x: startx, h: h, blockSize: blockSize)
|
||||
x = self.calculateX(ciphertext: ciphertext, x: x, h: h, blockSize: blockSize)
|
||||
|
||||
// len(aad) || len(ciphertext)
|
||||
let len = UInt128(a: UInt64(aad.count * 8), b: UInt64(ciphertext.count * 8))
|
||||
x = self.multiply(x ^ len, h)
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
// Calculate Ciphertext part, for all blocks
|
||||
// Not used with incremental calculation.
|
||||
private static func calculateX(ciphertext: [UInt8], x startx: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
|
||||
let pciphertext = addPadding(ciphertext, blockSize: blockSize)
|
||||
let blocksCount = pciphertext.count / blockSize
|
||||
|
||||
var x = startx
|
||||
for i in 0..<blocksCount {
|
||||
let cpos = i * blockSize
|
||||
let block = pciphertext[pciphertext.startIndex.advanced(by: cpos)..<pciphertext.startIndex.advanced(by: cpos + blockSize)]
|
||||
x = self.calculateX(block: Array(block), x: x, h: h, blockSize: blockSize)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// block is expected to be padded with addPadding
|
||||
private static func calculateX(block ciphertextBlock: Array<UInt8>, x: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
|
||||
let k = x ^ UInt128(ciphertextBlock)
|
||||
return self.multiply(k, h)
|
||||
}
|
||||
|
||||
// Calculate AAD part, for all blocks
|
||||
private static func calculateX(aad: [UInt8], x startx: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
|
||||
let paad = addPadding(aad, blockSize: blockSize)
|
||||
let blocksCount = paad.count / blockSize
|
||||
|
||||
var x = startx
|
||||
for i in 0..<blocksCount {
|
||||
let apos = i * blockSize
|
||||
let k = x ^ UInt128(paad[paad.startIndex.advanced(by: apos)..<paad.startIndex.advanced(by: apos + blockSize)])
|
||||
x = self.multiply(k, h)
|
||||
}
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
// Multiplication GF(2^128).
|
||||
private static func multiply(_ x: UInt128, _ y: UInt128) -> UInt128 {
|
||||
var z: UInt128 = 0
|
||||
var v = x
|
||||
var k = UInt128(a: 1 << 63, b: 0)
|
||||
|
||||
for _ in 0..<128 {
|
||||
if y & k == k {
|
||||
z = z ^ v
|
||||
}
|
||||
|
||||
if v & 1 != 1 {
|
||||
v = v >> 1
|
||||
} else {
|
||||
v = (v >> 1) ^ self.r
|
||||
}
|
||||
|
||||
k = k >> 1
|
||||
}
|
||||
|
||||
return z
|
||||
}
|
||||
}
|
||||
@@ -1,398 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// The OCB Authenticated-Encryption Algorithm
|
||||
// https://tools.ietf.org/html/rfc7253
|
||||
//
|
||||
|
||||
public final class OCB: BlockMode {
|
||||
|
||||
public enum Mode {
|
||||
/// In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want.
|
||||
case combined
|
||||
/// Some applications may need to store the authentication tag and the encrypted message at different locations.
|
||||
case detached
|
||||
}
|
||||
|
||||
public let options: BlockModeOption = [.initializationVectorRequired]
|
||||
|
||||
public enum Error: Swift.Error {
|
||||
case invalidNonce
|
||||
case fail
|
||||
}
|
||||
|
||||
private let N: Array<UInt8>
|
||||
private let additionalAuthenticatedData: Array<UInt8>?
|
||||
private let mode: Mode
|
||||
public let customBlockSize: Int? = nil
|
||||
|
||||
/// Length of authentication tag, in bytes.
|
||||
/// For encryption, the value is given as init parameter.
|
||||
/// For decryption, the length of given authentication tag is used.
|
||||
private let tagLength: Int
|
||||
|
||||
// `authenticationTag` nil for encryption, known tag for decryption
|
||||
/// For encryption, the value is set at the end of the encryption.
|
||||
/// For decryption, this is a known Tag to validate against.
|
||||
public var authenticationTag: Array<UInt8>?
|
||||
|
||||
// encrypt
|
||||
public init(nonce N: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, tagLength: Int = 16, mode: Mode = .detached) {
|
||||
self.N = N
|
||||
self.additionalAuthenticatedData = additionalAuthenticatedData
|
||||
self.mode = mode
|
||||
self.tagLength = tagLength
|
||||
}
|
||||
|
||||
// decrypt
|
||||
@inlinable
|
||||
public convenience init(nonce N: Array<UInt8>, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, mode: Mode = .detached) {
|
||||
self.init(nonce: N, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode)
|
||||
self.authenticationTag = authenticationTag
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
if self.N.isEmpty || self.N.count > 15 {
|
||||
throw Error.invalidNonce
|
||||
}
|
||||
|
||||
let worker = OCBModeWorker(N: N.slice, aad: self.additionalAuthenticatedData?.slice, expectedTag: self.authenticationTag, tagLength: self.tagLength, mode: self.mode, cipherOperation: cipherOperation, encryptionOperation: encryptionOperation)
|
||||
worker.didCalculateTag = { [weak self] tag in
|
||||
self?.authenticationTag = tag
|
||||
}
|
||||
return worker
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Worker
|
||||
|
||||
final class OCBModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker {
|
||||
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
var hashOperation: CipherOperationOnBlock!
|
||||
|
||||
// Callback called when authenticationTag is ready
|
||||
var didCalculateTag: ((Array<UInt8>) -> Void)?
|
||||
|
||||
private let tagLength: Int
|
||||
|
||||
let blockSize = 16 // 128 bit
|
||||
var additionalBufferSize: Int
|
||||
private let mode: OCB.Mode
|
||||
|
||||
// Additional authenticated data
|
||||
private let aad: ArraySlice<UInt8>?
|
||||
// Known Tag used to validate during decryption
|
||||
private var expectedTag: Array<UInt8>?
|
||||
|
||||
/*
|
||||
* KEY-DEPENDENT
|
||||
*/
|
||||
// NOTE: elements are lazily calculated
|
||||
private var l = [Array<UInt8>]()
|
||||
private var lAsterisk: Array<UInt8>
|
||||
private var lDollar: Array<UInt8>
|
||||
|
||||
/*
|
||||
* PER-ENCRYPTION/DECRYPTION
|
||||
*/
|
||||
private var mainBlockCount: UInt64
|
||||
private var offsetMain: Array<UInt8>
|
||||
private var checksum: Array<UInt8>
|
||||
|
||||
init(N: ArraySlice<UInt8>, aad: ArraySlice<UInt8>? = nil, expectedTag: Array<UInt8>? = nil, tagLength: Int, mode: OCB.Mode, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) {
|
||||
|
||||
self.cipherOperation = cipherOperation
|
||||
self.hashOperation = encryptionOperation
|
||||
self.mode = mode
|
||||
self.aad = aad
|
||||
self.expectedTag = expectedTag
|
||||
self.tagLength = tagLength
|
||||
|
||||
if mode == .combined {
|
||||
self.additionalBufferSize = tagLength
|
||||
} else {
|
||||
self.additionalBufferSize = 0
|
||||
}
|
||||
|
||||
/*
|
||||
* KEY-DEPENDENT INITIALIZATION
|
||||
*/
|
||||
|
||||
let zeros = Array<UInt8>(repeating: 0, count: self.blockSize)
|
||||
self.lAsterisk = self.hashOperation(zeros.slice)! /// L_* = ENCIPHER(K, zeros(128))
|
||||
self.lDollar = double(self.lAsterisk) /// L_$ = double(L_*)
|
||||
self.l.append(double(self.lDollar)) /// L_0 = double(L_$)
|
||||
|
||||
/*
|
||||
* NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALIZATION
|
||||
*/
|
||||
|
||||
/// Nonce = num2str(TAGLEN mod 128,7) || zeros(120-bitlen(N)) || 1 || N
|
||||
var nonce = Array<UInt8>(repeating: 0, count: blockSize)
|
||||
nonce[(nonce.count - N.count)...] = N
|
||||
nonce[0] = UInt8(tagLength) << 4
|
||||
nonce[blockSize - 1 - N.count] |= 1
|
||||
|
||||
/// bottom = str2num(Nonce[123..128])
|
||||
let bottom = nonce[15] & 0x3F
|
||||
|
||||
/// Ktop = ENCIPHER(K, Nonce[1..122] || zeros(6))
|
||||
nonce[15] &= 0xC0
|
||||
let Ktop = self.hashOperation(nonce.slice)!
|
||||
|
||||
/// Stretch = Ktop || (Ktop[1..64] xor Ktop[9..72])
|
||||
let Stretch = Ktop + xor(Ktop[0..<8], Ktop[1..<9])
|
||||
|
||||
/// Offset_0 = Stretch[1+bottom..128+bottom]
|
||||
var offsetMAIN_0 = Array<UInt8>(repeating: 0, count: blockSize)
|
||||
let bits = bottom % 8
|
||||
let bytes = Int(bottom / 8)
|
||||
if bits == 0 {
|
||||
offsetMAIN_0[0..<blockSize] = Stretch[bytes..<(bytes + blockSize)]
|
||||
} else {
|
||||
for i in 0..<self.blockSize {
|
||||
let b1 = Stretch[bytes + i]
|
||||
let b2 = Stretch[bytes + i + 1]
|
||||
offsetMAIN_0[i] = ((b1 << bits) | (b2 >> (8 - bits)))
|
||||
}
|
||||
}
|
||||
|
||||
self.mainBlockCount = 0
|
||||
self.offsetMain = Array<UInt8>(offsetMAIN_0.slice)
|
||||
self.checksum = Array<UInt8>(repeating: 0, count: self.blockSize) /// Checksum_0 = zeros(128)
|
||||
}
|
||||
|
||||
/// L_i = double(L_{i-1}) for every integer i > 0
|
||||
func getLSub(_ n: Int) -> Array<UInt8> {
|
||||
while n >= self.l.count {
|
||||
self.l.append(double(self.l.last!))
|
||||
}
|
||||
return self.l[n]
|
||||
}
|
||||
|
||||
func computeTag() -> Array<UInt8> {
|
||||
|
||||
let sum = self.hashAAD()
|
||||
|
||||
/// Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A)
|
||||
return xor(self.hashOperation(xor(xor(self.checksum, self.offsetMain).slice, self.lDollar))!, sum)
|
||||
}
|
||||
|
||||
func hashAAD() -> Array<UInt8> {
|
||||
var sum = Array<UInt8>(repeating: 0, count: blockSize)
|
||||
|
||||
guard let aad = self.aad else {
|
||||
return sum
|
||||
}
|
||||
|
||||
var offset = Array<UInt8>(repeating: 0, count: blockSize)
|
||||
var blockCount: UInt64 = 1
|
||||
for aadBlock in aad.batched(by: self.blockSize) {
|
||||
|
||||
if aadBlock.count == self.blockSize {
|
||||
|
||||
/// Offset_i = Offset_{i-1} xor L_{ntz(i)}
|
||||
offset = xor(offset, self.getLSub(ntz(blockCount)))
|
||||
|
||||
/// Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i)
|
||||
sum = xor(sum, self.hashOperation(xor(aadBlock, offset))!)
|
||||
} else {
|
||||
if !aadBlock.isEmpty {
|
||||
|
||||
/// Offset_* = Offset_m xor L_*
|
||||
offset = xor(offset, self.lAsterisk)
|
||||
|
||||
/// CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_*
|
||||
let cipherInput: Array<UInt8> = xor(extend(aadBlock, size: blockSize), offset)
|
||||
|
||||
/// Sum = Sum_m xor ENCIPHER(K, CipherInput)
|
||||
sum = xor(sum, self.hashOperation(cipherInput.slice)!)
|
||||
}
|
||||
}
|
||||
blockCount += 1
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
|
||||
if plaintext.count == self.blockSize {
|
||||
return self.processBlock(block: plaintext, forEncryption: true)
|
||||
} else {
|
||||
return self.processFinalBlock(block: plaintext, forEncryption: true)
|
||||
}
|
||||
}
|
||||
|
||||
func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
|
||||
let tag = self.computeTag()
|
||||
|
||||
self.didCalculateTag?(tag)
|
||||
|
||||
switch self.mode {
|
||||
case .combined:
|
||||
return ciphertext + tag
|
||||
case .detached:
|
||||
return ciphertext
|
||||
}
|
||||
}
|
||||
|
||||
func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
|
||||
if ciphertext.count == self.blockSize {
|
||||
return self.processBlock(block: ciphertext, forEncryption: false)
|
||||
} else {
|
||||
return self.processFinalBlock(block: ciphertext, forEncryption: false)
|
||||
}
|
||||
}
|
||||
|
||||
func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// do nothing
|
||||
plaintext
|
||||
}
|
||||
|
||||
private func processBlock(block: ArraySlice<UInt8>, forEncryption: Bool) -> Array<UInt8> {
|
||||
|
||||
/*
|
||||
* OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
|
||||
*/
|
||||
|
||||
self.mainBlockCount += 1
|
||||
|
||||
/// Offset_i = Offset_{i-1} xor L_{ntz(i)}
|
||||
self.offsetMain = xor(self.offsetMain, self.getLSub(ntz(self.mainBlockCount)))
|
||||
|
||||
/// C_i = Offset_i xor ENCIPHER(K, P_i xor Offset_i)
|
||||
/// P_i = Offset_i xor DECIPHER(K, C_i xor Offset_i)
|
||||
var mainBlock = Array<UInt8>(block)
|
||||
mainBlock = xor(mainBlock, offsetMain)
|
||||
mainBlock = self.cipherOperation(mainBlock.slice)!
|
||||
mainBlock = xor(mainBlock, self.offsetMain)
|
||||
|
||||
/// Checksum_i = Checksum_{i-1} xor P_i
|
||||
if forEncryption {
|
||||
self.checksum = xor(self.checksum, block)
|
||||
} else {
|
||||
self.checksum = xor(self.checksum, mainBlock)
|
||||
}
|
||||
|
||||
return mainBlock
|
||||
}
|
||||
|
||||
private func processFinalBlock(block: ArraySlice<UInt8>, forEncryption: Bool) -> Array<UInt8> {
|
||||
|
||||
let out: Array<UInt8>
|
||||
|
||||
if block.isEmpty {
|
||||
/// C_* = <empty string>
|
||||
/// P_* = <empty string>
|
||||
out = []
|
||||
|
||||
} else {
|
||||
|
||||
/// Offset_* = Offset_m xor L_*
|
||||
self.offsetMain = xor(self.offsetMain, self.lAsterisk)
|
||||
|
||||
/// Pad = ENCIPHER(K, Offset_*)
|
||||
let Pad = self.hashOperation(self.offsetMain.slice)!
|
||||
|
||||
/// C_* = P_* xor Pad[1..bitlen(P_*)]
|
||||
/// P_* = C_* xor Pad[1..bitlen(C_*)]
|
||||
out = xor(block, Pad[0..<block.count])
|
||||
|
||||
/// Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*)))
|
||||
let plaintext = forEncryption ? block : out.slice
|
||||
self.checksum = xor(self.checksum, extend(plaintext, size: self.blockSize))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// The authenticated decryption operation has five inputs: K, IV , C, A, and T. It has only a single
|
||||
// output, either the plaintext value P or a special symbol FAIL that indicates that the inputs are not
|
||||
// authentic.
|
||||
@discardableResult
|
||||
func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// Validate tag
|
||||
switch self.mode {
|
||||
case .combined:
|
||||
// overwrite expectedTag property used later for verification
|
||||
self.expectedTag = Array(ciphertext.suffix(self.tagLength))
|
||||
return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(tagLength, ciphertext.count))]
|
||||
case .detached:
|
||||
return ciphertext
|
||||
}
|
||||
}
|
||||
|
||||
func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
|
||||
// Calculate MAC tag.
|
||||
let computedTag = self.computeTag()
|
||||
|
||||
// Validate tag
|
||||
guard let expectedTag = self.expectedTag, computedTag == expectedTag else {
|
||||
throw OCB.Error.fail
|
||||
}
|
||||
|
||||
return plaintext
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Local utils
|
||||
|
||||
private func ntz(_ x: UInt64) -> Int {
|
||||
if x == 0 {
|
||||
return 64
|
||||
}
|
||||
|
||||
var xv = x
|
||||
var n = 0
|
||||
while (xv & 1) == 0 {
|
||||
n += 1
|
||||
xv = xv >> 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
private func double(_ block: Array<UInt8>) -> Array<UInt8> {
|
||||
var ( carry, result) = shiftLeft(block)
|
||||
|
||||
/*
|
||||
* NOTE: This construction is an attempt at a constant-time implementation.
|
||||
*/
|
||||
result[15] ^= (0x87 >> ((1 - carry) << 3))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private func shiftLeft(_ block: Array<UInt8>) -> (UInt8, Array<UInt8>) {
|
||||
var output = Array<UInt8>(repeating: 0, count: block.count)
|
||||
|
||||
var bit: UInt8 = 0
|
||||
|
||||
for i in 0..<block.count {
|
||||
let b = block[block.count - 1 - i]
|
||||
output[block.count - 1 - i] = ((b << 1) | bit)
|
||||
bit = (b >> 7) & 1
|
||||
}
|
||||
return (bit, output)
|
||||
}
|
||||
|
||||
private func extend(_ block: ArraySlice<UInt8>, size: Int) -> Array<UInt8> {
|
||||
var output = Array<UInt8>(repeating: 0, count: size)
|
||||
output[0..<block.count] = block
|
||||
output[block.count] |= 0x80
|
||||
return output
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// Output Feedback (OFB)
|
||||
//
|
||||
|
||||
public struct OFB: BlockMode {
|
||||
public enum Error: Swift.Error {
|
||||
/// Invalid IV
|
||||
case invalidInitializationVector
|
||||
}
|
||||
|
||||
public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
|
||||
private let iv: Array<UInt8>
|
||||
public let customBlockSize: Int? = nil
|
||||
|
||||
public init(iv: Array<UInt8>) {
|
||||
self.iv = iv
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
if self.iv.count != blockSize {
|
||||
throw Error.invalidInitializationVector
|
||||
}
|
||||
|
||||
return OFBModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation)
|
||||
}
|
||||
}
|
||||
|
||||
struct OFBModeWorker: BlockModeWorker {
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
let blockSize: Int
|
||||
let additionalBufferSize: Int = 0
|
||||
private let iv: ArraySlice<UInt8>
|
||||
private var prev: ArraySlice<UInt8>?
|
||||
|
||||
init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
|
||||
self.blockSize = blockSize
|
||||
self.iv = iv
|
||||
self.cipherOperation = cipherOperation
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
guard let ciphertext = cipherOperation(prev ?? iv) else {
|
||||
return Array(plaintext)
|
||||
}
|
||||
self.prev = ciphertext.slice
|
||||
return xor(plaintext, ciphertext)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
guard let decrypted = cipherOperation(prev ?? iv) else {
|
||||
return Array(ciphertext)
|
||||
}
|
||||
let plaintext: Array<UInt8> = xor(decrypted, ciphertext)
|
||||
prev = decrypted.slice
|
||||
return plaintext
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// Propagating Cipher Block Chaining (PCBC)
|
||||
//
|
||||
|
||||
public struct PCBC: BlockMode {
|
||||
public enum Error: Swift.Error {
|
||||
/// Invalid IV
|
||||
case invalidInitializationVector
|
||||
}
|
||||
|
||||
public let options: BlockModeOption = [.initializationVectorRequired, .paddingRequired]
|
||||
private let iv: Array<UInt8>
|
||||
public let customBlockSize: Int? = nil
|
||||
|
||||
public init(iv: Array<UInt8>) {
|
||||
self.iv = iv
|
||||
}
|
||||
|
||||
public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
|
||||
if self.iv.count != blockSize {
|
||||
throw Error.invalidInitializationVector
|
||||
}
|
||||
|
||||
return PCBCModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation)
|
||||
}
|
||||
}
|
||||
|
||||
struct PCBCModeWorker: BlockModeWorker {
|
||||
let cipherOperation: CipherOperationOnBlock
|
||||
var blockSize: Int
|
||||
let additionalBufferSize: Int = 0
|
||||
private let iv: ArraySlice<UInt8>
|
||||
private var prev: ArraySlice<UInt8>?
|
||||
|
||||
@inlinable
|
||||
init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
|
||||
self.blockSize = blockSize
|
||||
self.iv = iv
|
||||
self.cipherOperation = cipherOperation
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
|
||||
return Array(plaintext)
|
||||
}
|
||||
self.prev = xor(plaintext, ciphertext.slice)
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
@inlinable
|
||||
mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
|
||||
guard let plaintext = cipherOperation(ciphertext) else {
|
||||
return Array(ciphertext)
|
||||
}
|
||||
let result: Array<UInt8> = xor(prev ?? self.iv, plaintext)
|
||||
self.prev = xor(result, ciphertext)
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -1,537 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// https://en.wikipedia.org/wiki/Blowfish_(cipher)
|
||||
// Based on Paul Kocher implementation
|
||||
//
|
||||
|
||||
public final class Blowfish {
|
||||
public enum Error: Swift.Error {
|
||||
/// Data padding is required
|
||||
case dataPaddingRequired
|
||||
/// Invalid key or IV
|
||||
case invalidKeyOrInitializationVector
|
||||
/// Invalid IV
|
||||
case invalidInitializationVector
|
||||
/// Invalid block mode
|
||||
case invalidBlockMode
|
||||
}
|
||||
|
||||
public static let blockSize: Int = 8 // 64 bit
|
||||
public let keySize: Int
|
||||
|
||||
private let blockMode: BlockMode
|
||||
private let padding: Padding
|
||||
private var decryptWorker: CipherModeWorker!
|
||||
private var encryptWorker: CipherModeWorker!
|
||||
|
||||
private let N = 16 // rounds
|
||||
private var P: Array<UInt32>
|
||||
private var S: Array<Array<UInt32>>
|
||||
private let origP: Array<UInt32> = [
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822,
|
||||
0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377,
|
||||
0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5,
|
||||
0xb5470917, 0x9216d5d9, 0x8979fb1b
|
||||
]
|
||||
|
||||
private let origS: Array<Array<UInt32>> = [
|
||||
[
|
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
|
||||
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
|
||||
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
|
||||
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
|
||||
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
|
||||
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
|
||||
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
|
||||
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
|
||||
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
|
||||
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
|
||||
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
|
||||
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
|
||||
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
|
||||
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
|
||||
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
|
||||
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
|
||||
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
|
||||
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
|
||||
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
|
||||
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
|
||||
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
|
||||
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
|
||||
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
|
||||
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
|
||||
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
|
||||
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
|
||||
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
|
||||
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
|
||||
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
|
||||
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
|
||||
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
|
||||
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
|
||||
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
|
||||
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
|
||||
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
|
||||
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
|
||||
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
|
||||
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
|
||||
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
|
||||
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
|
||||
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
|
||||
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
|
||||
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
|
||||
],
|
||||
[
|
||||
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
|
||||
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
|
||||
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
|
||||
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
|
||||
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
|
||||
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
|
||||
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
|
||||
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
|
||||
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
|
||||
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
|
||||
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
|
||||
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
|
||||
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
|
||||
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
|
||||
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
|
||||
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
|
||||
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
|
||||
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
|
||||
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
|
||||
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
|
||||
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
|
||||
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
|
||||
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
|
||||
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
|
||||
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
|
||||
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
|
||||
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
|
||||
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
|
||||
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
|
||||
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
|
||||
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
|
||||
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
|
||||
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
|
||||
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
|
||||
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
|
||||
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
|
||||
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
|
||||
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
|
||||
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
|
||||
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
|
||||
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
|
||||
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
|
||||
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
|
||||
],
|
||||
[
|
||||
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
|
||||
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
|
||||
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
|
||||
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
|
||||
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
|
||||
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
|
||||
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
|
||||
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
|
||||
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
|
||||
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
|
||||
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
|
||||
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
|
||||
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
|
||||
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
|
||||
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
|
||||
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
|
||||
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
|
||||
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
|
||||
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
|
||||
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
|
||||
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
|
||||
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
|
||||
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
|
||||
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
|
||||
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
|
||||
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
|
||||
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
|
||||
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
|
||||
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
|
||||
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
|
||||
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
|
||||
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
|
||||
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
|
||||
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
|
||||
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
|
||||
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
|
||||
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
|
||||
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
|
||||
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
|
||||
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
|
||||
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
|
||||
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
|
||||
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
|
||||
],
|
||||
[
|
||||
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
|
||||
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
|
||||
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
|
||||
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
|
||||
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
|
||||
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
|
||||
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
|
||||
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
|
||||
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
|
||||
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
|
||||
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
|
||||
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
|
||||
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
|
||||
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
|
||||
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
|
||||
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
|
||||
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
|
||||
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
|
||||
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
|
||||
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
|
||||
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
|
||||
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
|
||||
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
|
||||
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
|
||||
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
|
||||
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
|
||||
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
|
||||
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
|
||||
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
|
||||
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
|
||||
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
|
||||
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
|
||||
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
|
||||
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
|
||||
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
|
||||
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
|
||||
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
|
||||
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
|
||||
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
|
||||
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
|
||||
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
|
||||
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
|
||||
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
|
||||
]
|
||||
]
|
||||
|
||||
public init(key: Array<UInt8>, blockMode: BlockMode = CBC(iv: Array<UInt8>(repeating: 0, count: Blowfish.blockSize)), padding: Padding) throws {
|
||||
precondition(key.count >= 5 && key.count <= 56)
|
||||
|
||||
self.blockMode = blockMode
|
||||
self.padding = padding
|
||||
self.keySize = key.count
|
||||
|
||||
self.S = self.origS
|
||||
self.P = self.origP
|
||||
|
||||
self.expandKey(key: key)
|
||||
try self.setupBlockModeWorkers()
|
||||
}
|
||||
|
||||
private func setupBlockModeWorkers() throws {
|
||||
self.encryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: self.encrypt, encryptionOperation: self.encrypt)
|
||||
|
||||
if self.blockMode.options.contains(.useEncryptToDecrypt) {
|
||||
self.decryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: self.encrypt, encryptionOperation: self.encrypt)
|
||||
} else {
|
||||
self.decryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: self.decrypt, encryptionOperation: self.encrypt)
|
||||
}
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
self.S = self.origS
|
||||
self.P = self.origP
|
||||
// todo expand key
|
||||
}
|
||||
|
||||
private func expandKey(key: Array<UInt8>) {
|
||||
var j = 0
|
||||
for i in 0..<(self.N + 2) {
|
||||
var data: UInt32 = 0x0
|
||||
for _ in 0..<4 {
|
||||
data = (data << 8) | UInt32(key[j])
|
||||
j += 1
|
||||
if j >= key.count {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
self.P[i] ^= data
|
||||
}
|
||||
|
||||
var datal: UInt32 = 0
|
||||
var datar: UInt32 = 0
|
||||
|
||||
for i in stride(from: 0, to: self.N + 2, by: 2) {
|
||||
self.encryptBlowfishBlock(l: &datal, r: &datar)
|
||||
self.P[i] = datal
|
||||
self.P[i + 1] = datar
|
||||
}
|
||||
|
||||
for i in 0..<4 {
|
||||
for j in stride(from: 0, to: 256, by: 2) {
|
||||
self.encryptBlowfishBlock(l: &datal, r: &datar)
|
||||
self.S[i][j] = datal
|
||||
self.S[i][j + 1] = datar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func encrypt(block: ArraySlice<UInt8>) -> Array<UInt8>? {
|
||||
var result = Array<UInt8>()
|
||||
|
||||
var l = UInt32(bytes: block[block.startIndex..<block.startIndex.advanced(by: 4)])
|
||||
var r = UInt32(bytes: block[block.startIndex.advanced(by: 4)..<block.startIndex.advanced(by: 8)])
|
||||
|
||||
encryptBlowfishBlock(l: &l, r: &r)
|
||||
|
||||
// because everything is too complex to be solved in reasonable time o_O
|
||||
result += [
|
||||
UInt8((l >> 24) & 0xff),
|
||||
UInt8((l >> 16) & 0xff)
|
||||
]
|
||||
result += [
|
||||
UInt8((l >> 8) & 0xff),
|
||||
UInt8((l >> 0) & 0xff)
|
||||
]
|
||||
result += [
|
||||
UInt8((r >> 24) & 0xff),
|
||||
UInt8((r >> 16) & 0xff)
|
||||
]
|
||||
result += [
|
||||
UInt8((r >> 8) & 0xff),
|
||||
UInt8((r >> 0) & 0xff)
|
||||
]
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fileprivate func decrypt(block: ArraySlice<UInt8>) -> Array<UInt8>? {
|
||||
var result = Array<UInt8>()
|
||||
|
||||
var l = UInt32(bytes: block[block.startIndex..<block.startIndex.advanced(by: 4)])
|
||||
var r = UInt32(bytes: block[block.startIndex.advanced(by: 4)..<block.startIndex.advanced(by: 8)])
|
||||
|
||||
decryptBlowfishBlock(l: &l, r: &r)
|
||||
|
||||
// because everything is too complex to be solved in reasonable time o_O
|
||||
result += [
|
||||
UInt8((l >> 24) & 0xff),
|
||||
UInt8((l >> 16) & 0xff)
|
||||
]
|
||||
result += [
|
||||
UInt8((l >> 8) & 0xff),
|
||||
UInt8((l >> 0) & 0xff)
|
||||
]
|
||||
result += [
|
||||
UInt8((r >> 24) & 0xff),
|
||||
UInt8((r >> 16) & 0xff)
|
||||
]
|
||||
result += [
|
||||
UInt8((r >> 8) & 0xff),
|
||||
UInt8((r >> 0) & 0xff)
|
||||
]
|
||||
return result
|
||||
}
|
||||
|
||||
/// Encrypts the 8-byte padded buffer
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - l: left half
|
||||
/// - r: right half
|
||||
private func encryptBlowfishBlock(l: inout UInt32, r: inout UInt32) {
|
||||
var Xl = l
|
||||
var Xr = r
|
||||
|
||||
for i in 0..<self.N {
|
||||
Xl = Xl ^ self.P[i]
|
||||
Xr = self.F(x: Xl) ^ Xr
|
||||
|
||||
(Xl, Xr) = (Xr, Xl)
|
||||
}
|
||||
|
||||
(Xl, Xr) = (Xr, Xl)
|
||||
|
||||
Xr = Xr ^ self.P[self.N]
|
||||
Xl = Xl ^ self.P[self.N + 1]
|
||||
|
||||
l = Xl
|
||||
r = Xr
|
||||
}
|
||||
|
||||
/// Decrypts the 8-byte padded buffer
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - l: left half
|
||||
/// - r: right half
|
||||
private func decryptBlowfishBlock(l: inout UInt32, r: inout UInt32) {
|
||||
var Xl = l
|
||||
var Xr = r
|
||||
|
||||
for i in (2...self.N + 1).reversed() {
|
||||
Xl = Xl ^ self.P[i]
|
||||
Xr = self.F(x: Xl) ^ Xr
|
||||
|
||||
(Xl, Xr) = (Xr, Xl)
|
||||
}
|
||||
|
||||
(Xl, Xr) = (Xr, Xl)
|
||||
|
||||
Xr = Xr ^ self.P[1]
|
||||
Xl = Xl ^ self.P[0]
|
||||
|
||||
l = Xl
|
||||
r = Xr
|
||||
}
|
||||
|
||||
private func F(x: UInt32) -> UInt32 {
|
||||
let f1 = self.S[0][Int(x >> 24) & 0xff]
|
||||
let f2 = self.S[1][Int(x >> 16) & 0xff]
|
||||
let f3 = self.S[2][Int(x >> 8) & 0xff]
|
||||
let f4 = self.S[3][Int(x & 0xff)]
|
||||
return ((f1 &+ f2) ^ f3) &+ f4
|
||||
}
|
||||
}
|
||||
|
||||
extension Blowfish: Cipher {
|
||||
/// Encrypt the 8-byte padded buffer, block by block. Note that for amounts of data larger than a block, it is not safe to just call encrypt() on successive blocks.
|
||||
///
|
||||
/// - Parameter bytes: Plaintext data
|
||||
/// - Returns: Encrypted data
|
||||
public func encrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Element == UInt8, C.Index == Int {
|
||||
let bytes = self.padding.add(to: Array(bytes), blockSize: Blowfish.blockSize) // FIXME: Array(bytes) copies
|
||||
|
||||
var out = Array<UInt8>()
|
||||
out.reserveCapacity(bytes.count)
|
||||
|
||||
for chunk in bytes.batched(by: Blowfish.blockSize) {
|
||||
out += self.encryptWorker.encrypt(block: chunk)
|
||||
}
|
||||
|
||||
if self.blockMode.options.contains(.paddingRequired) && (out.count % Blowfish.blockSize != 0) {
|
||||
throw Error.dataPaddingRequired
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
/// Decrypt the 8-byte padded buffer
|
||||
///
|
||||
/// - Parameter bytes: Ciphertext data
|
||||
/// - Returns: Plaintext data
|
||||
public func decrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Element == UInt8, C.Index == Int {
|
||||
if self.blockMode.options.contains(.paddingRequired) && (bytes.count % Blowfish.blockSize != 0) {
|
||||
throw Error.dataPaddingRequired
|
||||
}
|
||||
|
||||
var out = Array<UInt8>()
|
||||
out.reserveCapacity(bytes.count)
|
||||
|
||||
for chunk in Array(bytes).batched(by: Blowfish.blockSize) {
|
||||
out += self.decryptWorker.decrypt(block: chunk) // FIXME: copying here is innefective
|
||||
}
|
||||
|
||||
out = self.padding.remove(from: out, blockSize: Blowfish.blockSize)
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
public final class CBCMAC: CMAC {
|
||||
public override func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
|
||||
var inBytes = bytes
|
||||
bitPadding(to: &inBytes, blockSize: CMAC.BlockSize)
|
||||
let blocks = inBytes.batched(by: CMAC.BlockSize)
|
||||
|
||||
var lastBlockEncryptionResult: [UInt8] = CBCMAC.Zero
|
||||
try blocks.forEach { block in
|
||||
let aes = try AES(key: Array(key), blockMode: CBC(iv: lastBlockEncryptionResult), padding: .noPadding)
|
||||
lastBlockEncryptionResult = try aes.encrypt(block)
|
||||
}
|
||||
|
||||
return lastBlockEncryptionResult
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
public class CMAC: Authenticator {
|
||||
public enum Error: Swift.Error {
|
||||
case wrongKeyLength
|
||||
}
|
||||
|
||||
internal let key: SecureBytes
|
||||
|
||||
internal static let BlockSize: Int = 16
|
||||
internal static let Zero: Array<UInt8> = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
private static let Rb: Array<UInt8> = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87]
|
||||
|
||||
public init(key: Array<UInt8>) throws {
|
||||
self.key = SecureBytes(bytes: key)
|
||||
}
|
||||
|
||||
// MARK: Authenticator
|
||||
|
||||
// AES-CMAC
|
||||
public func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
|
||||
let cipher = try AES(key: Array(key), blockMode: CBC(iv: CMAC.Zero), padding: .noPadding)
|
||||
return try self.authenticate(bytes, cipher: cipher)
|
||||
}
|
||||
|
||||
// CMAC using a Cipher
|
||||
public func authenticate(_ bytes: Array<UInt8>, cipher: Cipher) throws -> Array<UInt8> {
|
||||
let l = try cipher.encrypt(CMAC.Zero)
|
||||
var subKey1 = self.leftShiftOneBit(l)
|
||||
if (l[0] & 0x80) != 0 {
|
||||
subKey1 = xor(CMAC.Rb, subKey1)
|
||||
}
|
||||
var subKey2 = self.leftShiftOneBit(subKey1)
|
||||
if (subKey1[0] & 0x80) != 0 {
|
||||
subKey2 = xor(CMAC.Rb, subKey2)
|
||||
}
|
||||
|
||||
let lastBlockComplete: Bool
|
||||
let blockCount = (bytes.count + CMAC.BlockSize - 1) / CMAC.BlockSize
|
||||
if blockCount == 0 {
|
||||
lastBlockComplete = false
|
||||
} else {
|
||||
lastBlockComplete = bytes.count % CMAC.BlockSize == 0
|
||||
}
|
||||
var paddedBytes = bytes
|
||||
if !lastBlockComplete {
|
||||
bitPadding(to: &paddedBytes, blockSize: CMAC.BlockSize)
|
||||
}
|
||||
|
||||
var blocks = Array(paddedBytes.batched(by: CMAC.BlockSize))
|
||||
var lastBlock = blocks.popLast()!
|
||||
if lastBlockComplete {
|
||||
lastBlock = xor(lastBlock, subKey1)
|
||||
} else {
|
||||
lastBlock = xor(lastBlock, subKey2)
|
||||
}
|
||||
|
||||
var x = Array<UInt8>(repeating: 0x00, count: CMAC.BlockSize)
|
||||
var y = Array<UInt8>(repeating: 0x00, count: CMAC.BlockSize)
|
||||
for block in blocks {
|
||||
y = xor(block, x)
|
||||
x = try cipher.encrypt(y)
|
||||
}
|
||||
// the difference between CMAC and CBC-MAC is that CMAC xors the final block with a secret value
|
||||
y = self.process(lastBlock: lastBlock, with: x)
|
||||
return try cipher.encrypt(y)
|
||||
}
|
||||
|
||||
func process(lastBlock: ArraySlice<UInt8>, with x: [UInt8]) -> [UInt8] {
|
||||
xor(lastBlock, x)
|
||||
}
|
||||
|
||||
// MARK: Helper methods
|
||||
|
||||
/**
|
||||
Performs left shift by one bit to the bit string aquired after concatenating al bytes in the byte array
|
||||
- parameters:
|
||||
- bytes: byte array
|
||||
- returns: bit shifted bit string split again in array of bytes
|
||||
*/
|
||||
private func leftShiftOneBit(_ bytes: Array<UInt8>) -> Array<UInt8> {
|
||||
var shifted = Array<UInt8>(repeating: 0x00, count: bytes.count)
|
||||
let last = bytes.count - 1
|
||||
for index in 0..<last {
|
||||
shifted[index] = bytes[index] << 1
|
||||
if (bytes[index + 1] & 0x80) != 0 {
|
||||
shifted[index] += 0x01
|
||||
}
|
||||
}
|
||||
shifted[last] = bytes[last] << 1
|
||||
return shifted
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
//
|
||||
// Addition.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: Addition
|
||||
|
||||
/// Add `word` to this integer in place.
|
||||
/// `word` is shifted `shift` words to the left before being added.
|
||||
///
|
||||
/// - Complexity: O(max(count, shift))
|
||||
internal mutating func addWord(_ word: Word, shiftedBy shift: Int = 0) {
|
||||
precondition(shift >= 0)
|
||||
var carry = word
|
||||
var i = shift
|
||||
while carry > 0 {
|
||||
let (d, c) = self[i].addingReportingOverflow(carry)
|
||||
self[i] = d
|
||||
carry = (c ? 1 : 0)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the digit `d` to this integer and return the result.
|
||||
/// `d` is shifted `shift` words to the left before being added.
|
||||
///
|
||||
/// - Complexity: O(max(count, shift))
|
||||
internal func addingWord(_ word: Word, shiftedBy shift: Int = 0) -> CS.BigUInt {
|
||||
var r = self
|
||||
r.addWord(word, shiftedBy: shift)
|
||||
return r
|
||||
}
|
||||
|
||||
/// Add `b` to this integer in place.
|
||||
/// `b` is shifted `shift` words to the left before being added.
|
||||
///
|
||||
/// - Complexity: O(max(count, b.count + shift))
|
||||
internal mutating func add(_ b: CS.BigUInt, shiftedBy shift: Int = 0) {
|
||||
precondition(shift >= 0)
|
||||
var carry = false
|
||||
var bi = 0
|
||||
let bc = b.count
|
||||
while bi < bc || carry {
|
||||
let ai = shift + bi
|
||||
let (d, c) = self[ai].addingReportingOverflow(b[bi])
|
||||
if carry {
|
||||
let (d2, c2) = d.addingReportingOverflow(1)
|
||||
self[ai] = d2
|
||||
carry = c || c2
|
||||
}
|
||||
else {
|
||||
self[ai] = d
|
||||
carry = c
|
||||
}
|
||||
bi += 1
|
||||
}
|
||||
}
|
||||
|
||||
/// Add `b` to this integer and return the result.
|
||||
/// `b` is shifted `shift` words to the left before being added.
|
||||
///
|
||||
/// - Complexity: O(max(count, b.count + shift))
|
||||
internal func adding(_ b: CS.BigUInt, shiftedBy shift: Int = 0) -> CS.BigUInt {
|
||||
var r = self
|
||||
r.add(b, shiftedBy: shift)
|
||||
return r
|
||||
}
|
||||
|
||||
/// Increment this integer by one. If `shift` is non-zero, it selects
|
||||
/// the word that is to be incremented.
|
||||
///
|
||||
/// - Complexity: O(count + shift)
|
||||
internal mutating func increment(shiftedBy shift: Int = 0) {
|
||||
self.addWord(1, shiftedBy: shift)
|
||||
}
|
||||
|
||||
/// Add `a` and `b` together and return the result.
|
||||
///
|
||||
/// - Complexity: O(max(a.count, b.count))
|
||||
public static func +(a: CS.BigUInt, b: CS.BigUInt) -> CS.BigUInt {
|
||||
return a.adding(b)
|
||||
}
|
||||
|
||||
/// Add `a` and `b` together, and store the sum in `a`.
|
||||
///
|
||||
/// - Complexity: O(max(a.count, b.count))
|
||||
public static func +=(a: inout CS.BigUInt, b: CS.BigUInt) {
|
||||
a.add(b, shiftedBy: 0)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Add `a` to `b` and return the result.
|
||||
public static func +(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
|
||||
switch (a.sign, b.sign) {
|
||||
case (.plus, .plus):
|
||||
return CS.BigInt(sign: .plus, magnitude: a.magnitude + b.magnitude)
|
||||
case (.minus, .minus):
|
||||
return CS.BigInt(sign: .minus, magnitude: a.magnitude + b.magnitude)
|
||||
case (.plus, .minus):
|
||||
if a.magnitude >= b.magnitude {
|
||||
return CS.BigInt(sign: .plus, magnitude: a.magnitude - b.magnitude)
|
||||
}
|
||||
else {
|
||||
return CS.BigInt(sign: .minus, magnitude: b.magnitude - a.magnitude)
|
||||
}
|
||||
case (.minus, .plus):
|
||||
if b.magnitude >= a.magnitude {
|
||||
return CS.BigInt(sign: .plus, magnitude: b.magnitude - a.magnitude)
|
||||
}
|
||||
else {
|
||||
return CS.BigInt(sign: .minus, magnitude: a.magnitude - b.magnitude)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add `b` to `a` in place.
|
||||
public static func +=(a: inout CS.BigInt, b: CS.BigInt) {
|
||||
a = a + b
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
//
|
||||
// CS.BigInt.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2015-12-27.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
//MARK: CS.BigInt
|
||||
|
||||
extension CS {
|
||||
|
||||
/// An arbitary precision signed integer type, also known as a "big integer".
|
||||
///
|
||||
/// Operations on big integers never overflow, but they might take a long time to execute.
|
||||
/// The amount of memory (and address space) available is the only constraint to the magnitude of these numbers.
|
||||
///
|
||||
/// This particular big integer type uses base-2^64 digits to represent integers.
|
||||
///
|
||||
/// `BigInt` is essentially a tiny wrapper that extends `BigUInt` with a sign bit and provides signed integer
|
||||
/// operations. Both the underlying absolute value and the negative/positive flag are available as read-write
|
||||
/// properties.
|
||||
///
|
||||
/// Not all algorithms of `BigUInt` are available for `BigInt` values; for example, there is no square root or
|
||||
/// primality test for signed integers. When you need to call one of these, just extract the absolute value:
|
||||
///
|
||||
/// ```Swift
|
||||
/// CS.BigInt(255).abs.isPrime() // Returns false
|
||||
/// ```
|
||||
///
|
||||
public struct BigInt: SignedInteger {
|
||||
public enum Sign {
|
||||
case plus
|
||||
case minus
|
||||
}
|
||||
|
||||
public typealias Magnitude = BigUInt
|
||||
|
||||
/// The type representing a digit in `BigInt`'s underlying number system.
|
||||
public typealias Word = BigUInt.Word
|
||||
|
||||
public static var isSigned: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
/// The absolute value of this integer.
|
||||
public var magnitude: BigUInt
|
||||
|
||||
/// True iff the value of this integer is negative.
|
||||
public var sign: Sign
|
||||
|
||||
/// Initializes a new big integer with the provided absolute number and sign flag.
|
||||
public init(sign: Sign, magnitude: BigUInt) {
|
||||
self.sign = (magnitude.isZero ? .plus : sign)
|
||||
self.magnitude = magnitude
|
||||
}
|
||||
|
||||
/// Return true iff this integer is zero.
|
||||
///
|
||||
/// - Complexity: O(1)
|
||||
public var isZero: Bool {
|
||||
return magnitude.isZero
|
||||
}
|
||||
|
||||
/// Returns `-1` if this value is negative and `1` if it’s positive; otherwise, `0`.
|
||||
///
|
||||
/// - Returns: The sign of this number, expressed as an integer of the same type.
|
||||
public func signum() -> CS.BigInt {
|
||||
switch sign {
|
||||
case .plus:
|
||||
return isZero ? 0 : 1
|
||||
case .minus:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,389 +0,0 @@
|
||||
//
|
||||
// BigUInt.swift
|
||||
// BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2015-12-26.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS {
|
||||
|
||||
/// An arbitary precision unsigned integer type, also known as a "big integer".
|
||||
///
|
||||
/// Operations on big integers never overflow, but they may take a long time to execute.
|
||||
/// The amount of memory (and address space) available is the only constraint to the magnitude of these numbers.
|
||||
///
|
||||
/// This particular big integer type uses base-2^64 digits to represent integers; you can think of it as a wrapper
|
||||
/// around `Array<UInt64>`. (In fact, `BigUInt` only uses an array if there are more than two digits.)
|
||||
public struct BigUInt: UnsignedInteger {
|
||||
/// The type representing a digit in `BigUInt`'s underlying number system.
|
||||
public typealias Word = UInt
|
||||
|
||||
/// The storage variants of a `BigUInt`.
|
||||
enum Kind {
|
||||
/// Value consists of the two specified words (low and high). Either or both words may be zero.
|
||||
case inline(Word, Word)
|
||||
/// Words are stored in a slice of the storage array.
|
||||
case slice(from: Int, to: Int)
|
||||
/// Words are stored in the storage array.
|
||||
case array
|
||||
}
|
||||
|
||||
internal fileprivate (set) var kind: Kind // Internal for testing only
|
||||
internal fileprivate (set) var storage: [Word] // Internal for testing only; stored separately to prevent COW copies
|
||||
|
||||
/// Initializes a new BigUInt with value 0.
|
||||
public init() {
|
||||
self.kind = .inline(0, 0)
|
||||
self.storage = []
|
||||
}
|
||||
|
||||
internal init(word: Word) {
|
||||
self.kind = .inline(word, 0)
|
||||
self.storage = []
|
||||
}
|
||||
|
||||
internal init(low: Word, high: Word) {
|
||||
self.kind = .inline(low, high)
|
||||
self.storage = []
|
||||
}
|
||||
|
||||
/// Initializes a new BigUInt with the specified digits. The digits are ordered from least to most significant.
|
||||
public init(words: [Word]) {
|
||||
self.kind = .array
|
||||
self.storage = words
|
||||
normalize()
|
||||
}
|
||||
|
||||
internal init(words: [Word], from startIndex: Int, to endIndex: Int) {
|
||||
self.kind = .slice(from: startIndex, to: endIndex)
|
||||
self.storage = words
|
||||
normalize()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
public static var isSigned: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/// Return true iff this integer is zero.
|
||||
///
|
||||
/// - Complexity: O(1)
|
||||
var isZero: Bool {
|
||||
switch kind {
|
||||
case .inline(0, 0): return true
|
||||
case .array: return storage.isEmpty
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `1` if this value is, positive; otherwise, `0`.
|
||||
///
|
||||
/// - Returns: The sign of this number, expressed as an integer of the same type.
|
||||
public func signum() -> CS.BigUInt {
|
||||
return isZero ? 0 : 1
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
mutating func ensureArray() {
|
||||
switch kind {
|
||||
case let .inline(w0, w1):
|
||||
kind = .array
|
||||
storage = w1 != 0 ? [w0, w1]
|
||||
: w0 != 0 ? [w0]
|
||||
: []
|
||||
case let .slice(from: start, to: end):
|
||||
kind = .array
|
||||
storage = Array(storage[start ..< end])
|
||||
case .array:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var capacity: Int {
|
||||
guard case .array = kind else { return 0 }
|
||||
return storage.capacity
|
||||
}
|
||||
|
||||
mutating func reserveCapacity(_ minimumCapacity: Int) {
|
||||
switch kind {
|
||||
case let .inline(w0, w1):
|
||||
kind = .array
|
||||
storage.reserveCapacity(minimumCapacity)
|
||||
if w1 != 0 {
|
||||
storage.append(w0)
|
||||
storage.append(w1)
|
||||
}
|
||||
else if w0 != 0 {
|
||||
storage.append(w0)
|
||||
}
|
||||
case let .slice(from: start, to: end):
|
||||
kind = .array
|
||||
var words: [Word] = []
|
||||
words.reserveCapacity(Swift.max(end - start, minimumCapacity))
|
||||
words.append(contentsOf: storage[start ..< end])
|
||||
storage = words
|
||||
case .array:
|
||||
storage.reserveCapacity(minimumCapacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets rid of leading zero digits in the digit array and converts slices into inline digits when possible.
|
||||
internal mutating func normalize() {
|
||||
switch kind {
|
||||
case .slice(from: let start, to: var end):
|
||||
assert(start >= 0 && end <= storage.count && start <= end)
|
||||
while start < end, storage[end - 1] == 0 {
|
||||
end -= 1
|
||||
}
|
||||
switch end - start {
|
||||
case 0:
|
||||
kind = .inline(0, 0)
|
||||
storage = []
|
||||
case 1:
|
||||
kind = .inline(storage[start], 0)
|
||||
storage = []
|
||||
case 2:
|
||||
kind = .inline(storage[start], storage[start + 1])
|
||||
storage = []
|
||||
case storage.count:
|
||||
assert(start == 0)
|
||||
kind = .array
|
||||
default:
|
||||
kind = .slice(from: start, to: end)
|
||||
}
|
||||
case .array where storage.last == 0:
|
||||
while storage.last == 0 {
|
||||
storage.removeLast()
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/// Set this integer to 0 without releasing allocated storage capacity (if any).
|
||||
mutating func clear() {
|
||||
self.load(0)
|
||||
}
|
||||
|
||||
/// Set this integer to `value` by copying its digits without releasing allocated storage capacity (if any).
|
||||
mutating func load(_ value: CS.BigUInt) {
|
||||
switch kind {
|
||||
case .inline, .slice:
|
||||
self = value
|
||||
case .array:
|
||||
self.storage.removeAll(keepingCapacity: true)
|
||||
self.storage.append(contentsOf: value.words)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: Collection-like members
|
||||
|
||||
/// The number of digits in this integer, excluding leading zero digits.
|
||||
var count: Int {
|
||||
switch kind {
|
||||
case let .inline(w0, w1):
|
||||
return w1 != 0 ? 2
|
||||
: w0 != 0 ? 1
|
||||
: 0
|
||||
case let .slice(from: start, to: end):
|
||||
return end - start
|
||||
case .array:
|
||||
return storage.count
|
||||
}
|
||||
}
|
||||
|
||||
/// Get or set a digit at a given index.
|
||||
///
|
||||
/// - Note: Unlike a normal collection, it is OK for the index to be greater than or equal to `endIndex`.
|
||||
/// The subscripting getter returns zero for indexes beyond the most significant digit.
|
||||
/// Setting these extended digits automatically appends new elements to the underlying digit array.
|
||||
/// - Requires: index >= 0
|
||||
/// - Complexity: The getter is O(1). The setter is O(1) if the conditions below are true; otherwise it's O(count).
|
||||
/// - The integer's storage is not shared with another integer
|
||||
/// - The integer wasn't created as a slice of another integer
|
||||
/// - `index < count`
|
||||
subscript(_ index: Int) -> Word {
|
||||
get {
|
||||
precondition(index >= 0)
|
||||
switch (kind, index) {
|
||||
case (.inline(let w0, _), 0): return w0
|
||||
case (.inline(_, let w1), 1): return w1
|
||||
case (.slice(from: let start, to: let end), _) where index < end - start:
|
||||
return storage[start + index]
|
||||
case (.array, _) where index < storage.count:
|
||||
return storage[index]
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
set(word) {
|
||||
precondition(index >= 0)
|
||||
switch (kind, index) {
|
||||
case let (.inline(_, w1), 0):
|
||||
kind = .inline(word, w1)
|
||||
case let (.inline(w0, _), 1):
|
||||
kind = .inline(w0, word)
|
||||
case let (.slice(from: start, to: end), _) where index < end - start:
|
||||
replace(at: index, with: word)
|
||||
case (.array, _) where index < storage.count:
|
||||
replace(at: index, with: word)
|
||||
default:
|
||||
extend(at: index, with: word)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private mutating func replace(at index: Int, with word: Word) {
|
||||
ensureArray()
|
||||
precondition(index < storage.count)
|
||||
storage[index] = word
|
||||
if word == 0, index == storage.count - 1 {
|
||||
normalize()
|
||||
}
|
||||
}
|
||||
|
||||
private mutating func extend(at index: Int, with word: Word) {
|
||||
guard word != 0 else { return }
|
||||
reserveCapacity(index + 1)
|
||||
precondition(index >= storage.count)
|
||||
storage.append(contentsOf: repeatElement(0, count: index - storage.count))
|
||||
storage.append(word)
|
||||
}
|
||||
|
||||
/// Returns an integer built from the digits of this integer in the given range.
|
||||
internal func extract(_ bounds: Range<Int>) -> CS.BigUInt {
|
||||
switch kind {
|
||||
case let .inline(w0, w1):
|
||||
let bounds = bounds.clamped(to: 0 ..< 2)
|
||||
if bounds == 0 ..< 2 {
|
||||
return CS.BigUInt(low: w0, high: w1)
|
||||
}
|
||||
else if bounds == 0 ..< 1 {
|
||||
return CS.BigUInt(word: w0)
|
||||
}
|
||||
else if bounds == 1 ..< 2 {
|
||||
return CS.BigUInt(word: w1)
|
||||
}
|
||||
else {
|
||||
return CS.BigUInt()
|
||||
}
|
||||
case let .slice(from: start, to: end):
|
||||
let s = Swift.min(end, start + Swift.max(bounds.lowerBound, 0))
|
||||
let e = Swift.max(s, (bounds.upperBound > end - start ? end : start + bounds.upperBound))
|
||||
return CS.BigUInt(words: storage, from: s, to: e)
|
||||
case .array:
|
||||
let b = bounds.clamped(to: storage.startIndex ..< storage.endIndex)
|
||||
return CS.BigUInt(words: storage, from: b.lowerBound, to: b.upperBound)
|
||||
}
|
||||
}
|
||||
|
||||
internal func extract<Bounds: RangeExpression>(_ bounds: Bounds) -> CS.BigUInt where Bounds.Bound == Int {
|
||||
return self.extract(bounds.relative(to: 0 ..< Int.max))
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
internal mutating func shiftRight(byWords amount: Int) {
|
||||
assert(amount >= 0)
|
||||
guard amount > 0 else { return }
|
||||
switch kind {
|
||||
case let .inline(_, w1) where amount == 1:
|
||||
kind = .inline(w1, 0)
|
||||
case .inline(_, _):
|
||||
kind = .inline(0, 0)
|
||||
case let .slice(from: start, to: end):
|
||||
let s = start + amount
|
||||
if s >= end {
|
||||
kind = .inline(0, 0)
|
||||
}
|
||||
else {
|
||||
kind = .slice(from: s, to: end)
|
||||
normalize()
|
||||
}
|
||||
case .array:
|
||||
if amount >= storage.count {
|
||||
storage.removeAll(keepingCapacity: true)
|
||||
}
|
||||
else {
|
||||
storage.removeFirst(amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal mutating func shiftLeft(byWords amount: Int) {
|
||||
assert(amount >= 0)
|
||||
guard amount > 0 else { return }
|
||||
guard !isZero else { return }
|
||||
switch kind {
|
||||
case let .inline(w0, 0) where amount == 1:
|
||||
kind = .inline(0, w0)
|
||||
case let .inline(w0, w1):
|
||||
let c = (w1 == 0 ? 1 : 2)
|
||||
storage.reserveCapacity(amount + c)
|
||||
storage.append(contentsOf: repeatElement(0, count: amount))
|
||||
storage.append(w0)
|
||||
if w1 != 0 {
|
||||
storage.append(w1)
|
||||
}
|
||||
kind = .array
|
||||
case let .slice(from: start, to: end):
|
||||
var words: [Word] = []
|
||||
words.reserveCapacity(amount + count)
|
||||
words.append(contentsOf: repeatElement(0, count: amount))
|
||||
words.append(contentsOf: storage[start ..< end])
|
||||
storage = words
|
||||
kind = .array
|
||||
case .array:
|
||||
storage.insert(contentsOf: repeatElement(0, count: amount), at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: Low and High
|
||||
|
||||
/// Split this integer into a high-order and a low-order part.
|
||||
///
|
||||
/// - Requires: count > 1
|
||||
/// - Returns: `(low, high)` such that
|
||||
/// - `self == low.add(high, shiftedBy: middleIndex)`
|
||||
/// - `high.width <= floor(width / 2)`
|
||||
/// - `low.width <= ceil(width / 2)`
|
||||
/// - Complexity: Typically O(1), but O(count) in the worst case, because high-order zero digits need to be removed after the split.
|
||||
internal var split: (high: CS.BigUInt, low: CS.BigUInt) {
|
||||
precondition(count > 1)
|
||||
let mid = middleIndex
|
||||
return (self.extract(mid...), self.extract(..<mid))
|
||||
}
|
||||
|
||||
/// Index of the digit at the middle of this integer.
|
||||
///
|
||||
/// - Returns: The index of the digit that is least significant in `self.high`.
|
||||
internal var middleIndex: Int {
|
||||
return (count + 1) / 2
|
||||
}
|
||||
|
||||
/// The low-order half of this CS.BigUInt.
|
||||
///
|
||||
/// - Returns: `self[0 ..< middleIndex]`
|
||||
/// - Requires: count > 1
|
||||
internal var low: CS.BigUInt {
|
||||
return self.extract(0 ..< middleIndex)
|
||||
}
|
||||
|
||||
/// The high-order half of this CS.BigUInt.
|
||||
///
|
||||
/// - Returns: `self[middleIndex ..< count]`
|
||||
/// - Requires: count > 1
|
||||
internal var high: CS.BigUInt {
|
||||
return self.extract(middleIndex ..< count)
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
//
|
||||
// Bitwise Ops.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
//MARK: Bitwise Operations
|
||||
|
||||
extension CS.BigUInt {
|
||||
/// Return the ones' complement of `a`.
|
||||
///
|
||||
/// - Complexity: O(a.count)
|
||||
public static prefix func ~(a: CS.BigUInt) -> CS.BigUInt {
|
||||
return CS.BigUInt(words: a.words.map { ~$0 })
|
||||
}
|
||||
|
||||
/// Calculate the bitwise OR of `a` and `b`, and store the result in `a`.
|
||||
///
|
||||
/// - Complexity: O(max(a.count, b.count))
|
||||
public static func |= (a: inout CS.BigUInt, b: CS.BigUInt) {
|
||||
a.reserveCapacity(b.count)
|
||||
for i in 0 ..< b.count {
|
||||
a[i] |= b[i]
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the bitwise AND of `a` and `b` and return the result.
|
||||
///
|
||||
/// - Complexity: O(max(a.count, b.count))
|
||||
public static func &= (a: inout CS.BigUInt, b: CS.BigUInt) {
|
||||
for i in 0 ..< Swift.max(a.count, b.count) {
|
||||
a[i] &= b[i]
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the bitwise XOR of `a` and `b` and return the result.
|
||||
///
|
||||
/// - Complexity: O(max(a.count, b.count))
|
||||
public static func ^= (a: inout CS.BigUInt, b: CS.BigUInt) {
|
||||
a.reserveCapacity(b.count)
|
||||
for i in 0 ..< b.count {
|
||||
a[i] ^= b[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
public static prefix func ~(x: CS.BigInt) -> CS.BigInt {
|
||||
switch x.sign {
|
||||
case .plus:
|
||||
return CS.BigInt(sign: .minus, magnitude: x.magnitude + 1)
|
||||
case .minus:
|
||||
return CS.BigInt(sign: .plus, magnitude: x.magnitude - 1)
|
||||
}
|
||||
}
|
||||
|
||||
public static func &(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt {
|
||||
let left = lhs.words
|
||||
let right = rhs.words
|
||||
// Note we aren't using left.count/right.count here; we account for the sign bit separately later.
|
||||
let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count)
|
||||
var words: [UInt] = []
|
||||
words.reserveCapacity(count)
|
||||
for i in 0 ..< count {
|
||||
words.append(left[i] & right[i])
|
||||
}
|
||||
if lhs.sign == .minus && rhs.sign == .minus {
|
||||
words.twosComplement()
|
||||
return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
|
||||
public static func |(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt {
|
||||
let left = lhs.words
|
||||
let right = rhs.words
|
||||
// Note we aren't using left.count/right.count here; we account for the sign bit separately later.
|
||||
let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count)
|
||||
var words: [UInt] = []
|
||||
words.reserveCapacity(count)
|
||||
for i in 0 ..< count {
|
||||
words.append(left[i] | right[i])
|
||||
}
|
||||
if lhs.sign == .minus || rhs.sign == .minus {
|
||||
words.twosComplement()
|
||||
return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
|
||||
public static func ^(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt {
|
||||
let left = lhs.words
|
||||
let right = rhs.words
|
||||
// Note we aren't using left.count/right.count here; we account for the sign bit separately later.
|
||||
let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count)
|
||||
var words: [UInt] = []
|
||||
words.reserveCapacity(count)
|
||||
for i in 0 ..< count {
|
||||
words.append(left[i] ^ right[i])
|
||||
}
|
||||
if (lhs.sign == .minus) != (rhs.sign == .minus) {
|
||||
words.twosComplement()
|
||||
return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
|
||||
public static func &=(lhs: inout CS.BigInt, rhs: CS.BigInt) {
|
||||
lhs = lhs & rhs
|
||||
}
|
||||
|
||||
public static func |=(lhs: inout CS.BigInt, rhs: CS.BigInt) {
|
||||
lhs = lhs | rhs
|
||||
}
|
||||
|
||||
public static func ^=(lhs: inout CS.BigInt, rhs: CS.BigInt) {
|
||||
lhs = lhs ^ rhs
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// To avoid name conflict with BigInt library, I choose to rename
|
||||
// BigInt -> BigInteger
|
||||
// BigUInt -> BigUInteger
|
||||
|
||||
public typealias BigInteger = CS.BigInt
|
||||
public typealias BigUInteger = CS.BigUInt
|
||||
|
||||
public enum CS {
|
||||
// namespace
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
//
|
||||
// Codable.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2017-8-11.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS {
|
||||
|
||||
// Little-endian to big-endian
|
||||
struct Units<Unit: FixedWidthInteger, Words: RandomAccessCollection>: RandomAccessCollection
|
||||
where Words.Element: FixedWidthInteger, Words.Index == Int {
|
||||
typealias Word = Words.Element
|
||||
let words: Words
|
||||
init(of type: Unit.Type, _ words: Words) {
|
||||
precondition(Word.bitWidth % Unit.bitWidth == 0 || Unit.bitWidth % Word.bitWidth == 0)
|
||||
self.words = words
|
||||
}
|
||||
var count: Int { return (words.count * Word.bitWidth + Unit.bitWidth - 1) / Unit.bitWidth }
|
||||
var startIndex: Int { return 0 }
|
||||
var endIndex: Int { return count }
|
||||
subscript(_ index: Int) -> Unit {
|
||||
let index = count - 1 - index
|
||||
if Unit.bitWidth == Word.bitWidth {
|
||||
return Unit(words[index])
|
||||
}
|
||||
else if Unit.bitWidth > Word.bitWidth {
|
||||
let c = Unit.bitWidth / Word.bitWidth
|
||||
var unit: Unit = 0
|
||||
var j = 0
|
||||
for i in (c * index) ..< Swift.min(c * (index + 1), words.endIndex) {
|
||||
unit |= Unit(words[i]) << j
|
||||
j += Word.bitWidth
|
||||
}
|
||||
return unit
|
||||
}
|
||||
// Unit.bitWidth < Word.bitWidth
|
||||
let c = Word.bitWidth / Unit.bitWidth
|
||||
let i = index / c
|
||||
let j = index % c
|
||||
return Unit(truncatingIfNeeded: words[i] >> (j * Unit.bitWidth))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element: FixedWidthInteger {
|
||||
// Big-endian to little-endian
|
||||
init<Unit: FixedWidthInteger>(count: Int?, generator: () throws -> Unit?) rethrows {
|
||||
typealias Word = Element
|
||||
precondition(Word.bitWidth % Unit.bitWidth == 0 || Unit.bitWidth % Word.bitWidth == 0)
|
||||
self = []
|
||||
if Unit.bitWidth == Word.bitWidth {
|
||||
if let count = count {
|
||||
self.reserveCapacity(count)
|
||||
}
|
||||
while let unit = try generator() {
|
||||
self.append(Word(unit))
|
||||
}
|
||||
}
|
||||
else if Unit.bitWidth > Word.bitWidth {
|
||||
let wordsPerUnit = Unit.bitWidth / Word.bitWidth
|
||||
if let count = count {
|
||||
self.reserveCapacity(count * wordsPerUnit)
|
||||
}
|
||||
while let unit = try generator() {
|
||||
var shift = Unit.bitWidth - Word.bitWidth
|
||||
while shift >= 0 {
|
||||
self.append(Word(truncatingIfNeeded: unit >> shift))
|
||||
shift -= Word.bitWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
let unitsPerWord = Word.bitWidth / Unit.bitWidth
|
||||
if let count = count {
|
||||
self.reserveCapacity((count + unitsPerWord - 1) / unitsPerWord)
|
||||
}
|
||||
var word: Word = 0
|
||||
var c = 0
|
||||
while let unit = try generator() {
|
||||
word <<= Unit.bitWidth
|
||||
word |= Word(unit)
|
||||
c += Unit.bitWidth
|
||||
if c == Word.bitWidth {
|
||||
self.append(word)
|
||||
word = 0
|
||||
c = 0
|
||||
}
|
||||
}
|
||||
if c > 0 {
|
||||
self.append(word << c)
|
||||
var shifted: Word = 0
|
||||
for i in self.indices {
|
||||
let word = self[i]
|
||||
self[i] = shifted | (word >> c)
|
||||
shifted = word << (Word.bitWidth - c)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.reverse()
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
var container = try decoder.unkeyedContainer()
|
||||
|
||||
// Decode sign
|
||||
let sign: CS.BigInt.Sign
|
||||
switch try container.decode(String.self) {
|
||||
case "+":
|
||||
sign = .plus
|
||||
case "-":
|
||||
sign = .minus
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath,
|
||||
debugDescription: "Invalid big integer sign"))
|
||||
}
|
||||
|
||||
// Decode magnitude
|
||||
let words = try [UInt](count: container.count?.advanced(by: -1)) { () -> UInt64? in
|
||||
guard !container.isAtEnd else { return nil }
|
||||
return try container.decode(UInt64.self)
|
||||
}
|
||||
let magnitude = CS.BigUInt(words: words)
|
||||
|
||||
self.init(sign: sign, magnitude: magnitude)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.unkeyedContainer()
|
||||
try container.encode(sign == .plus ? "+" : "-")
|
||||
let units = CS.Units(of: UInt64.self, self.magnitude.words)
|
||||
if units.isEmpty {
|
||||
try container.encode(0 as UInt64)
|
||||
}
|
||||
else {
|
||||
try container.encode(contentsOf: units)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let value = try CS.BigInt(from: decoder)
|
||||
guard value.sign == .plus else {
|
||||
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath,
|
||||
debugDescription: "BigUInt cannot hold a negative value"))
|
||||
}
|
||||
self = value.magnitude
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
try CS.BigInt(sign: .plus, magnitude: self).encode(to: encoder)
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
//
|
||||
// Comparable.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CS.BigUInt: Comparable {
|
||||
//MARK: Comparison
|
||||
|
||||
/// Compare `a` to `b` and return an `NSComparisonResult` indicating their order.
|
||||
///
|
||||
/// - Complexity: O(count)
|
||||
public static func compare(_ a: CS.BigUInt, _ b: CS.BigUInt) -> ComparisonResult {
|
||||
if a.count != b.count { return a.count > b.count ? .orderedDescending : .orderedAscending }
|
||||
for i in (0 ..< a.count).reversed() {
|
||||
let ad = a[i]
|
||||
let bd = b[i]
|
||||
if ad != bd { return ad > bd ? .orderedDescending : .orderedAscending }
|
||||
}
|
||||
return .orderedSame
|
||||
}
|
||||
|
||||
/// Return true iff `a` is equal to `b`.
|
||||
///
|
||||
/// - Complexity: O(count)
|
||||
public static func ==(a: CS.BigUInt, b: CS.BigUInt) -> Bool {
|
||||
return CS.BigUInt.compare(a, b) == .orderedSame
|
||||
}
|
||||
|
||||
/// Return true iff `a` is less than `b`.
|
||||
///
|
||||
/// - Complexity: O(count)
|
||||
public static func <(a: CS.BigUInt, b: CS.BigUInt) -> Bool {
|
||||
return CS.BigUInt.compare(a, b) == .orderedAscending
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Return true iff `a` is equal to `b`.
|
||||
public static func ==(a: CS.BigInt, b: CS.BigInt) -> Bool {
|
||||
return a.sign == b.sign && a.magnitude == b.magnitude
|
||||
}
|
||||
|
||||
/// Return true iff `a` is less than `b`.
|
||||
public static func <(a: CS.BigInt, b: CS.BigInt) -> Bool {
|
||||
switch (a.sign, b.sign) {
|
||||
case (.plus, .plus):
|
||||
return a.magnitude < b.magnitude
|
||||
case (.plus, .minus):
|
||||
return false
|
||||
case (.minus, .plus):
|
||||
return true
|
||||
case (.minus, .minus):
|
||||
return a.magnitude > b.magnitude
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
//
|
||||
// Data Conversion.swift
|
||||
// BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-04.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: NSData Conversion
|
||||
|
||||
/// Initialize a BigInt from bytes accessed from an UnsafeRawBufferPointer
|
||||
public init(_ buffer: UnsafeRawBufferPointer) {
|
||||
// This assumes Word is binary.
|
||||
precondition(Word.bitWidth % 8 == 0)
|
||||
|
||||
self.init()
|
||||
|
||||
let length = buffer.count
|
||||
guard length > 0 else { return }
|
||||
let bytesPerDigit = Word.bitWidth / 8
|
||||
var index = length / bytesPerDigit
|
||||
var c = bytesPerDigit - length % bytesPerDigit
|
||||
if c == bytesPerDigit {
|
||||
c = 0
|
||||
index -= 1
|
||||
}
|
||||
|
||||
var word: Word = 0
|
||||
for byte in buffer {
|
||||
word <<= 8
|
||||
word += Word(byte)
|
||||
c += 1
|
||||
if c == bytesPerDigit {
|
||||
self[index] = word
|
||||
index -= 1
|
||||
c = 0
|
||||
word = 0
|
||||
}
|
||||
}
|
||||
assert(c == 0 && word == 0 && index == -1)
|
||||
}
|
||||
|
||||
|
||||
/// Initializes an integer from the bits stored inside a piece of `Data`.
|
||||
/// The data is assumed to be in network (big-endian) byte order.
|
||||
public init(_ data: Data) {
|
||||
// This assumes Word is binary.
|
||||
precondition(Word.bitWidth % 8 == 0)
|
||||
|
||||
self.init()
|
||||
|
||||
let length = data.count
|
||||
guard length > 0 else { return }
|
||||
let bytesPerDigit = Word.bitWidth / 8
|
||||
var index = length / bytesPerDigit
|
||||
var c = bytesPerDigit - length % bytesPerDigit
|
||||
if c == bytesPerDigit {
|
||||
c = 0
|
||||
index -= 1
|
||||
}
|
||||
let word: Word = data.withUnsafeBytes { buffPtr in
|
||||
var word: Word = 0
|
||||
let p = buffPtr.bindMemory(to: UInt8.self)
|
||||
for byte in p {
|
||||
word <<= 8
|
||||
word += Word(byte)
|
||||
c += 1
|
||||
if c == bytesPerDigit {
|
||||
self[index] = word
|
||||
index -= 1
|
||||
c = 0
|
||||
word = 0
|
||||
}
|
||||
}
|
||||
return word
|
||||
}
|
||||
assert(c == 0 && word == 0 && index == -1)
|
||||
}
|
||||
|
||||
/// Return a `Data` value that contains the base-256 representation of this integer, in network (big-endian) byte order.
|
||||
public func serialize() -> Data {
|
||||
// This assumes Digit is binary.
|
||||
precondition(Word.bitWidth % 8 == 0)
|
||||
|
||||
let byteCount = (self.bitWidth + 7) / 8
|
||||
|
||||
guard byteCount > 0 else { return Data() }
|
||||
|
||||
var data = Data(count: byteCount)
|
||||
data.withUnsafeMutableBytes { buffPtr in
|
||||
let p = buffPtr.bindMemory(to: UInt8.self)
|
||||
var i = byteCount - 1
|
||||
for var word in self.words {
|
||||
for _ in 0 ..< Word.bitWidth / 8 {
|
||||
p[i] = UInt8(word & 0xFF)
|
||||
word >>= 8
|
||||
if i == 0 {
|
||||
assert(word == 0)
|
||||
break
|
||||
}
|
||||
i -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
|
||||
/// Initialize a BigInt from bytes accessed from an UnsafeRawBufferPointer,
|
||||
/// where the first byte indicates sign (0 for positive, 1 for negative)
|
||||
public init(_ buffer: UnsafeRawBufferPointer) {
|
||||
// This assumes Word is binary.
|
||||
precondition(Word.bitWidth % 8 == 0)
|
||||
|
||||
self.init()
|
||||
|
||||
let length = buffer.count
|
||||
|
||||
// Serialized data for a BigInt should contain at least 2 bytes: one representing
|
||||
// the sign, and another for the non-zero magnitude. Zero is represented by an
|
||||
// empty Data struct, and negative zero is not supported.
|
||||
guard length > 1, let firstByte = buffer.first else { return }
|
||||
|
||||
// The first byte gives the sign
|
||||
// This byte is compared to a bitmask to allow additional functionality to be added
|
||||
// to this byte in the future.
|
||||
self.sign = firstByte & 0b1 == 0 ? .plus : .minus
|
||||
|
||||
self.magnitude = CS.BigUInt(UnsafeRawBufferPointer(rebasing: buffer.dropFirst(1)))
|
||||
}
|
||||
|
||||
/// Initializes an integer from the bits stored inside a piece of `Data`.
|
||||
/// The data is assumed to be in network (big-endian) byte order with a first
|
||||
/// byte to represent the sign (0 for positive, 1 for negative)
|
||||
public init(_ data: Data) {
|
||||
// This assumes Word is binary.
|
||||
// This is the same assumption made when initializing CS.BigUInt from Data
|
||||
precondition(Word.bitWidth % 8 == 0)
|
||||
|
||||
self.init()
|
||||
|
||||
// Serialized data for a BigInt should contain at least 2 bytes: one representing
|
||||
// the sign, and another for the non-zero magnitude. Zero is represented by an
|
||||
// empty Data struct, and negative zero is not supported.
|
||||
guard data.count > 1, let firstByte = data.first else { return }
|
||||
|
||||
// The first byte gives the sign
|
||||
// This byte is compared to a bitmask to allow additional functionality to be added
|
||||
// to this byte in the future.
|
||||
self.sign = firstByte & 0b1 == 0 ? .plus : .minus
|
||||
|
||||
// The remaining bytes are read and stored as the magnitude
|
||||
self.magnitude = CS.BigUInt(data.dropFirst(1))
|
||||
}
|
||||
|
||||
/// Return a `Data` value that contains the base-256 representation of this integer, in network (big-endian) byte order and a prepended byte to indicate the sign (0 for positive, 1 for negative)
|
||||
public func serialize() -> Data {
|
||||
// Create a data object for the magnitude portion of the BigInt
|
||||
let magnitudeData = self.magnitude.serialize()
|
||||
|
||||
// Similar to CS.BigUInt, a value of 0 should return an initialized, empty Data struct
|
||||
guard magnitudeData.count > 0 else { return magnitudeData }
|
||||
|
||||
// Create a new Data struct for the signed BigInt value
|
||||
var data = Data(capacity: magnitudeData.count + 1)
|
||||
|
||||
// The first byte should be 0 for a positive value, or 1 for a negative value
|
||||
// i.e., the sign bit is the LSB
|
||||
data.append(self.sign == .plus ? 0 : 1)
|
||||
|
||||
data.append(magnitudeData)
|
||||
return data
|
||||
}
|
||||
}
|
||||
@@ -1,375 +0,0 @@
|
||||
//
|
||||
// Division.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
//MARK: Full-width multiplication and division
|
||||
|
||||
// TODO: Return to `where Magnitude == Self` when SR-13491 is resolved
|
||||
extension FixedWidthInteger {
|
||||
private var halfShift: Self {
|
||||
return Self(Self.bitWidth / 2)
|
||||
|
||||
}
|
||||
private var high: Self {
|
||||
return self &>> halfShift
|
||||
}
|
||||
|
||||
private var low: Self {
|
||||
let mask: Self = 1 &<< halfShift - 1
|
||||
return self & mask
|
||||
}
|
||||
|
||||
private var upshifted: Self {
|
||||
return self &<< halfShift
|
||||
}
|
||||
|
||||
private var split: (high: Self, low: Self) {
|
||||
return (self.high, self.low)
|
||||
}
|
||||
|
||||
private init(_ value: (high: Self, low: Self)) {
|
||||
self = value.high.upshifted + value.low
|
||||
}
|
||||
|
||||
/// Divide the double-width integer `dividend` by `self` and return the quotient and remainder.
|
||||
///
|
||||
/// - Requires: `dividend.high < self`, so that the result will fit in a single digit.
|
||||
/// - Complexity: O(1) with 2 divisions, 6 multiplications and ~12 or so additions/subtractions.
|
||||
internal func fastDividingFullWidth(_ dividend: (high: Self, low: Self.Magnitude)) -> (quotient: Self, remainder: Self) {
|
||||
// Division is complicated; doing it with single-digit operations is maddeningly complicated.
|
||||
// This is a Swift adaptation for "divlu2" in Hacker's Delight,
|
||||
// which is in turn a C adaptation of Knuth's Algorithm D (TAOCP vol 2, 4.3.1).
|
||||
precondition(dividend.high < self)
|
||||
|
||||
// This replaces the implementation in stdlib, which is much slower.
|
||||
// FIXME: Speed up stdlib. It should use full-width idiv on Intel processors, and
|
||||
// fall back to a reasonably fast algorithm elsewhere.
|
||||
|
||||
// The trick here is that we're actually implementing a 4/2 long division using half-words,
|
||||
// with the long division loop unrolled into two 3/2 half-word divisions.
|
||||
// Luckily, 3/2 half-word division can be approximated by a single full-word division operation
|
||||
// that, when the divisor is normalized, differs from the correct result by at most 2.
|
||||
|
||||
/// Find the half-word quotient in `u / vn`, which must be normalized.
|
||||
/// `u` contains three half-words in the two halves of `u.high` and the lower half of
|
||||
/// `u.low`. (The weird distribution makes for a slightly better fit with the input.)
|
||||
/// `vn` contains the normalized divisor, consisting of two half-words.
|
||||
///
|
||||
/// - Requires: u.high < vn && u.low.high == 0 && vn.leadingZeroBitCount == 0
|
||||
func quotient(dividing u: (high: Self, low: Self), by vn: Self) -> Self {
|
||||
let (vn1, vn0) = vn.split
|
||||
// Get approximate quotient.
|
||||
let (q, r) = u.high.quotientAndRemainder(dividingBy: vn1)
|
||||
let p = q * vn0
|
||||
// q is often already correct, but sometimes the approximation overshoots by at most 2.
|
||||
// The code that follows checks for this while being careful to only perform single-digit operations.
|
||||
if q.high == 0 && p <= r.upshifted + u.low { return q }
|
||||
let r2 = r + vn1
|
||||
if r2.high != 0 { return q - 1 }
|
||||
if (q - 1).high == 0 && p - vn0 <= r2.upshifted + u.low { return q - 1 }
|
||||
//assert((r + 2 * vn1).high != 0 || p - 2 * vn0 <= (r + 2 * vn1).upshifted + u.low)
|
||||
return q - 2
|
||||
}
|
||||
/// Divide 3 half-digits by 2 half-digits to get a half-digit quotient and a full-digit remainder.
|
||||
///
|
||||
/// - Requires: u.high < v && u.low.high == 0 && vn.width = width(Digit)
|
||||
func quotientAndRemainder(dividing u: (high: Self, low: Self), by v: Self) -> (quotient: Self, remainder: Self) {
|
||||
let q = quotient(dividing: u, by: v)
|
||||
// Note that `uh.low` masks off a couple of bits, and `q * v` and the
|
||||
// subtraction are likely to overflow. Despite this, the end result (remainder) will
|
||||
// still be correct and it will fit inside a single (full) Digit.
|
||||
let r = Self(u) &- q &* v
|
||||
assert(r < v)
|
||||
return (q, r)
|
||||
}
|
||||
|
||||
// Normalize the dividend and the divisor (self) such that the divisor has no leading zeroes.
|
||||
let z = Self(self.leadingZeroBitCount)
|
||||
let w = Self(Self.bitWidth) - z
|
||||
let vn = self << z
|
||||
|
||||
let un32 = (z == 0 ? dividend.high : (dividend.high &<< z) | ((dividend.low as! Self) &>> w)) // No bits are lost
|
||||
let un10 = dividend.low &<< z
|
||||
let (un1, un0) = un10.split
|
||||
|
||||
// Divide `(un32,un10)` by `vn`, splitting the full 4/2 division into two 3/2 ones.
|
||||
let (q1, un21) = quotientAndRemainder(dividing: (un32, (un1 as! Self)), by: vn)
|
||||
let (q0, rn) = quotientAndRemainder(dividing: (un21, (un0 as! Self)), by: vn)
|
||||
|
||||
// Undo normalization of the remainder and combine the two halves of the quotient.
|
||||
let mod = rn >> z
|
||||
let div = Self((q1, q0))
|
||||
return (div, mod)
|
||||
}
|
||||
|
||||
/// Return the quotient of the 3/2-word division `x/y` as a single word.
|
||||
///
|
||||
/// - Requires: (x.0, x.1) <= y && y.0.high != 0
|
||||
/// - Returns: The exact value when it fits in a single word, otherwise `Self`.
|
||||
static func approximateQuotient(dividing x: (Self, Self, Self), by y: (Self, Self)) -> Self {
|
||||
// Start with q = (x.0, x.1) / y.0, (or Word.max on overflow)
|
||||
var q: Self
|
||||
var r: Self
|
||||
if x.0 == y.0 {
|
||||
q = Self.max
|
||||
let (s, o) = x.0.addingReportingOverflow(x.1)
|
||||
if o { return q }
|
||||
r = s
|
||||
}
|
||||
else {
|
||||
(q, r) = y.0.fastDividingFullWidth((x.0, (x.1 as! Magnitude)))
|
||||
}
|
||||
// Now refine q by considering x.2 and y.1.
|
||||
// Note that since y is normalized, q * y - x is between 0 and 2.
|
||||
let (ph, pl) = q.multipliedFullWidth(by: y.1)
|
||||
if ph < r || (ph == r && pl <= x.2) { return q }
|
||||
|
||||
let (r1, ro) = r.addingReportingOverflow(y.0)
|
||||
if ro { return q - 1 }
|
||||
|
||||
let (pl1, so) = pl.subtractingReportingOverflow((y.1 as! Magnitude))
|
||||
let ph1 = (so ? ph - 1 : ph)
|
||||
|
||||
if ph1 < r1 || (ph1 == r1 && pl1 <= x.2) { return q - 1 }
|
||||
return q - 2
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: Division
|
||||
|
||||
/// Divide this integer by the word `y`, leaving the quotient in its place and returning the remainder.
|
||||
///
|
||||
/// - Requires: y > 0
|
||||
/// - Complexity: O(count)
|
||||
internal mutating func divide(byWord y: Word) -> Word {
|
||||
precondition(y > 0)
|
||||
if y == 1 { return 0 }
|
||||
|
||||
var remainder: Word = 0
|
||||
for i in (0 ..< count).reversed() {
|
||||
let u = self[i]
|
||||
(self[i], remainder) = y.fastDividingFullWidth((remainder, u))
|
||||
}
|
||||
return remainder
|
||||
}
|
||||
|
||||
/// Divide this integer by the word `y` and return the resulting quotient and remainder.
|
||||
///
|
||||
/// - Requires: y > 0
|
||||
/// - Returns: (quotient, remainder) where quotient = floor(x/y), remainder = x - quotient * y
|
||||
/// - Complexity: O(x.count)
|
||||
internal func quotientAndRemainder(dividingByWord y: Word) -> (quotient: CS.BigUInt, remainder: Word) {
|
||||
var div = self
|
||||
let mod = div.divide(byWord: y)
|
||||
return (div, mod)
|
||||
}
|
||||
|
||||
/// Divide `x` by `y`, putting the quotient in `x` and the remainder in `y`.
|
||||
/// Reusing integers like this reduces the number of allocations during the calculation.
|
||||
static func divide(_ x: inout CS.BigUInt, by y: inout CS.BigUInt) {
|
||||
// This is a Swift adaptation of "divmnu" from Hacker's Delight, which is in
|
||||
// turn a C adaptation of Knuth's Algorithm D (TAOCP vol 2, 4.3.1).
|
||||
|
||||
precondition(!y.isZero)
|
||||
|
||||
// First, let's take care of the easy cases.
|
||||
if x < y {
|
||||
(x, y) = (0, x)
|
||||
return
|
||||
}
|
||||
if y.count == 1 {
|
||||
// The single-word case reduces to a simpler loop.
|
||||
y = CS.BigUInt(x.divide(byWord: y[0]))
|
||||
return
|
||||
}
|
||||
|
||||
// In the hard cases, we will perform the long division algorithm we learned in school.
|
||||
// It works by successively calculating the single-word quotient of the top y.count + 1
|
||||
// words of x divided by y, replacing the top of x with the remainder, and repeating
|
||||
// the process one word lower.
|
||||
//
|
||||
// The tricky part is that the algorithm needs to be able to do n+1/n word divisions,
|
||||
// but we only have a primitive for dividing two words by a single
|
||||
// word. (Remember that this step is also tricky when we do it on paper!)
|
||||
//
|
||||
// The solution is that the long division can be approximated by a single full division
|
||||
// using just the most significant words. We can then use multiplications and
|
||||
// subtractions to refine the approximation until we get the correct quotient word.
|
||||
//
|
||||
// We could do this by doing a simple 2/1 full division, but Knuth goes one step further,
|
||||
// and implements a 3/2 division. This results in an exact approximation in the
|
||||
// vast majority of cases, eliminating an extra subtraction over big integers.
|
||||
//
|
||||
// The function `approximateQuotient` above implements Knuth's 3/2 division algorithm.
|
||||
// It requires that the divisor's most significant word is larger than
|
||||
// Word.max / 2. This ensures that the approximation has tiny error bounds,
|
||||
// which is what makes this entire approach viable.
|
||||
// To satisfy this requirement, we will normalize the division by multiplying
|
||||
// both the divisor and the dividend by the same (small) factor.
|
||||
let z = y.leadingZeroBitCount
|
||||
y <<= z
|
||||
x <<= z // We'll calculate the remainder in the normalized dividend.
|
||||
var quotient = CS.BigUInt()
|
||||
assert(y.leadingZeroBitCount == 0)
|
||||
|
||||
// We're ready to start the long division!
|
||||
let dc = y.count
|
||||
let d1 = y[dc - 1]
|
||||
let d0 = y[dc - 2]
|
||||
var product: CS.BigUInt = 0
|
||||
for j in (dc ... x.count).reversed() {
|
||||
// Approximate dividing the top dc+1 words of `remainder` using the topmost 3/2 words.
|
||||
let r2 = x[j]
|
||||
let r1 = x[j - 1]
|
||||
let r0 = x[j - 2]
|
||||
let q = Word.approximateQuotient(dividing: (r2, r1, r0), by: (d1, d0))
|
||||
|
||||
// Multiply the entire divisor with `q` and subtract the result from remainder.
|
||||
// Normalization ensures the 3/2 quotient will either be exact for the full division, or
|
||||
// it may overshoot by at most 1, in which case the product will be greater
|
||||
// than the remainder.
|
||||
product.load(y)
|
||||
product.multiply(byWord: q)
|
||||
if product <= x.extract(j - dc ..< j + 1) {
|
||||
x.subtract(product, shiftedBy: j - dc)
|
||||
quotient[j - dc] = q
|
||||
}
|
||||
else {
|
||||
// This case is extremely rare -- it has a probability of 1/2^(Word.bitWidth - 1).
|
||||
x.add(y, shiftedBy: j - dc)
|
||||
x.subtract(product, shiftedBy: j - dc)
|
||||
quotient[j - dc] = q - 1
|
||||
}
|
||||
}
|
||||
// The remainder's normalization needs to be undone, but otherwise we're done.
|
||||
x >>= z
|
||||
y = x
|
||||
x = quotient
|
||||
}
|
||||
|
||||
/// Divide `x` by `y`, putting the remainder in `x`.
|
||||
mutating func formRemainder(dividingBy y: CS.BigUInt, normalizedBy shift: Int) {
|
||||
precondition(!y.isZero)
|
||||
assert(y.leadingZeroBitCount == 0)
|
||||
if y.count == 1 {
|
||||
let remainder = self.divide(byWord: y[0] >> shift)
|
||||
self.load(CS.BigUInt(remainder))
|
||||
return
|
||||
}
|
||||
self <<= shift
|
||||
if self >= y {
|
||||
let dc = y.count
|
||||
let d1 = y[dc - 1]
|
||||
let d0 = y[dc - 2]
|
||||
var product: CS.BigUInt = 0
|
||||
for j in (dc ... self.count).reversed() {
|
||||
let r2 = self[j]
|
||||
let r1 = self[j - 1]
|
||||
let r0 = self[j - 2]
|
||||
let q = Word.approximateQuotient(dividing: (r2, r1, r0), by: (d1, d0))
|
||||
product.load(y)
|
||||
product.multiply(byWord: q)
|
||||
if product <= self.extract(j - dc ..< j + 1) {
|
||||
self.subtract(product, shiftedBy: j - dc)
|
||||
}
|
||||
else {
|
||||
self.add(y, shiftedBy: j - dc)
|
||||
self.subtract(product, shiftedBy: j - dc)
|
||||
}
|
||||
}
|
||||
}
|
||||
self >>= shift
|
||||
}
|
||||
|
||||
|
||||
/// Divide this integer by `y` and return the resulting quotient and remainder.
|
||||
///
|
||||
/// - Requires: `y > 0`
|
||||
/// - Returns: `(quotient, remainder)` where `quotient = floor(self/y)`, `remainder = self - quotient * y`
|
||||
/// - Complexity: O(count^2)
|
||||
public func quotientAndRemainder(dividingBy y: CS.BigUInt) -> (quotient: CS.BigUInt, remainder: CS.BigUInt) {
|
||||
var x = self
|
||||
var y = y
|
||||
CS.BigUInt.divide(&x, by: &y)
|
||||
return (x, y)
|
||||
}
|
||||
|
||||
/// Divide `x` by `y` and return the quotient.
|
||||
///
|
||||
/// - Note: Use `divided(by:)` if you also need the remainder.
|
||||
public static func /(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt {
|
||||
return x.quotientAndRemainder(dividingBy: y).quotient
|
||||
}
|
||||
|
||||
/// Divide `x` by `y` and return the remainder.
|
||||
///
|
||||
/// - Note: Use `divided(by:)` if you also need the remainder.
|
||||
public static func %(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt {
|
||||
var x = x
|
||||
let shift = y.leadingZeroBitCount
|
||||
x.formRemainder(dividingBy: y << shift, normalizedBy: shift)
|
||||
return x
|
||||
}
|
||||
|
||||
/// Divide `x` by `y` and store the quotient in `x`.
|
||||
///
|
||||
/// - Note: Use `divided(by:)` if you also need the remainder.
|
||||
public static func /=(x: inout CS.BigUInt, y: CS.BigUInt) {
|
||||
var y = y
|
||||
CS.BigUInt.divide(&x, by: &y)
|
||||
}
|
||||
|
||||
/// Divide `x` by `y` and store the remainder in `x`.
|
||||
///
|
||||
/// - Note: Use `divided(by:)` if you also need the remainder.
|
||||
public static func %=(x: inout CS.BigUInt, y: CS.BigUInt) {
|
||||
let shift = y.leadingZeroBitCount
|
||||
x.formRemainder(dividingBy: y << shift, normalizedBy: shift)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Divide this integer by `y` and return the resulting quotient and remainder.
|
||||
///
|
||||
/// - Requires: `y > 0`
|
||||
/// - Returns: `(quotient, remainder)` where `quotient = floor(self/y)`, `remainder = self - quotient * y`
|
||||
/// - Complexity: O(count^2)
|
||||
public func quotientAndRemainder(dividingBy y: CS.BigInt) -> (quotient: CS.BigInt, remainder: CS.BigInt) {
|
||||
var a = self.magnitude
|
||||
var b = y.magnitude
|
||||
CS.BigUInt.divide(&a, by: &b)
|
||||
return ( CS.BigInt(sign: self.sign == y.sign ? .plus : .minus, magnitude: a),
|
||||
CS.BigInt(sign: self.sign, magnitude: b))
|
||||
}
|
||||
|
||||
/// Divide `a` by `b` and return the quotient. Traps if `b` is zero.
|
||||
public static func /(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
|
||||
return CS.BigInt(sign: a.sign == b.sign ? .plus : .minus, magnitude: a.magnitude / b.magnitude)
|
||||
}
|
||||
|
||||
/// Divide `a` by `b` and return the remainder. The result has the same sign as `a`.
|
||||
public static func %(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
|
||||
return CS.BigInt(sign: a.sign, magnitude: a.magnitude % b.magnitude)
|
||||
}
|
||||
|
||||
/// Return the result of `a` mod `b`. The result is always a nonnegative integer that is less than the absolute value of `b`.
|
||||
public func modulus(_ mod: CS.BigInt) -> CS.BigInt {
|
||||
let remainder = self.magnitude % mod.magnitude
|
||||
return CS.BigInt(
|
||||
self.sign == .minus && !remainder.isZero
|
||||
? mod.magnitude - remainder
|
||||
: remainder)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Divide `a` by `b` storing the quotient in `a`.
|
||||
public static func /=(a: inout CS.BigInt, b: CS.BigInt) { a = a / b }
|
||||
/// Divide `a` by `b` storing the remainder in `a`.
|
||||
public static func %=(a: inout CS.BigInt, b: CS.BigInt) { a = a % b }
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//
|
||||
// Exponentiation.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: Exponentiation
|
||||
|
||||
/// Returns this integer raised to the power `exponent`.
|
||||
///
|
||||
/// This function calculates the result by [successively squaring the base while halving the exponent][expsqr].
|
||||
///
|
||||
/// [expsqr]: https://en.wikipedia.org/wiki/Exponentiation_by_squaring
|
||||
///
|
||||
/// - Note: This function can be unreasonably expensive for large exponents, which is why `exponent` is
|
||||
/// a simple integer value. If you want to calculate big exponents, you'll probably need to use
|
||||
/// the modulo arithmetic variant.
|
||||
/// - Returns: 1 if `exponent == 0`, otherwise `self` raised to `exponent`. (This implies that `0.power(0) == 1`.)
|
||||
/// - SeeAlso: `BigUInt.power(_:, modulus:)`
|
||||
/// - Complexity: O((exponent * self.count)^log2(3)) or somesuch. The result may require a large amount of memory, too.
|
||||
public func power(_ exponent: Int) -> CS.BigUInt {
|
||||
if exponent == 0 { return 1 }
|
||||
if exponent == 1 { return self }
|
||||
if exponent < 0 {
|
||||
precondition(!self.isZero)
|
||||
return self == 1 ? 1 : 0
|
||||
}
|
||||
if self <= 1 { return self }
|
||||
var result = CS.BigUInt(1)
|
||||
var b = self
|
||||
var e = exponent
|
||||
while e > 0 {
|
||||
if e & 1 == 1 {
|
||||
result *= b
|
||||
}
|
||||
e >>= 1
|
||||
b *= b
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// Returns the remainder of this integer raised to the power `exponent` in modulo arithmetic under `modulus`.
|
||||
///
|
||||
/// Uses the [right-to-left binary method][rtlb].
|
||||
///
|
||||
/// [rtlb]: https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method
|
||||
///
|
||||
/// - Complexity: O(exponent.count * modulus.count^log2(3)) or somesuch
|
||||
public func power(_ exponent: CS.BigUInt, modulus: CS.BigUInt) -> CS.BigUInt {
|
||||
precondition(!modulus.isZero)
|
||||
if modulus == (1 as CS.BigUInt) { return 0 }
|
||||
let shift = modulus.leadingZeroBitCount
|
||||
let normalizedModulus = modulus << shift
|
||||
var result = CS.BigUInt(1)
|
||||
var b = self
|
||||
b.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift)
|
||||
for var e in exponent.words {
|
||||
for _ in 0 ..< Word.bitWidth {
|
||||
if e & 1 == 1 {
|
||||
result *= b
|
||||
result.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift)
|
||||
}
|
||||
e >>= 1
|
||||
b *= b
|
||||
b.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Returns this integer raised to the power `exponent`.
|
||||
///
|
||||
/// This function calculates the result by [successively squaring the base while halving the exponent][expsqr].
|
||||
///
|
||||
/// [expsqr]: https://en.wikipedia.org/wiki/Exponentiation_by_squaring
|
||||
///
|
||||
/// - Note: This function can be unreasonably expensive for large exponents, which is why `exponent` is
|
||||
/// a simple integer value. If you want to calculate big exponents, you'll probably need to use
|
||||
/// the modulo arithmetic variant.
|
||||
/// - Returns: 1 if `exponent == 0`, otherwise `self` raised to `exponent`. (This implies that `0.power(0) == 1`.)
|
||||
/// - SeeAlso: `BigUInt.power(_:, modulus:)`
|
||||
/// - Complexity: O((exponent * self.count)^log2(3)) or somesuch. The result may require a large amount of memory, too.
|
||||
public func power(_ exponent: Int) -> CS.BigInt {
|
||||
return CS.BigInt(sign: self.sign == .minus && exponent & 1 != 0 ? .minus : .plus,
|
||||
magnitude: self.magnitude.power(exponent))
|
||||
}
|
||||
|
||||
/// Returns the remainder of this integer raised to the power `exponent` in modulo arithmetic under `modulus`.
|
||||
///
|
||||
/// Uses the [right-to-left binary method][rtlb].
|
||||
///
|
||||
/// [rtlb]: https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method
|
||||
///
|
||||
/// - Complexity: O(exponent.count * modulus.count^log2(3)) or somesuch
|
||||
public func power(_ exponent: CS.BigInt, modulus: CS.BigInt) -> CS.BigInt {
|
||||
precondition(!modulus.isZero)
|
||||
if modulus.magnitude == 1 { return 0 }
|
||||
if exponent.isZero { return 1 }
|
||||
if exponent == 1 { return self.modulus(modulus) }
|
||||
if exponent < 0 {
|
||||
precondition(!self.isZero)
|
||||
guard magnitude == 1 else { return 0 }
|
||||
guard sign == .minus else { return 1 }
|
||||
guard exponent.magnitude[0] & 1 != 0 else { return 1 }
|
||||
return CS.BigInt(modulus.magnitude - 1)
|
||||
}
|
||||
let power = self.magnitude.power(exponent.magnitude,
|
||||
modulus: modulus.magnitude)
|
||||
if self.sign == .plus || exponent.magnitude[0] & 1 == 0 || power.isZero {
|
||||
return CS.BigInt(power)
|
||||
}
|
||||
return CS.BigInt(modulus.magnitude - power)
|
||||
}
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
//
|
||||
// Floating Point Conversion.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2017-08-11.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
public init?<T: BinaryFloatingPoint>(exactly source: T) {
|
||||
guard source.isFinite else { return nil }
|
||||
guard !source.isZero else { self = 0; return }
|
||||
guard source.sign == .plus else { return nil }
|
||||
let value = source.rounded(.towardZero)
|
||||
guard value == source else { return nil }
|
||||
assert(value.floatingPointClass == .positiveNormal)
|
||||
assert(value.exponent >= 0)
|
||||
let significand = value.significandBitPattern
|
||||
self = (CS.BigUInt(1) << value.exponent) + CS.BigUInt(significand) >> (T.significandBitCount - Int(value.exponent))
|
||||
}
|
||||
|
||||
public init<T: BinaryFloatingPoint>(_ source: T) {
|
||||
self.init(exactly: source.rounded(.towardZero))!
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
public init?<T: BinaryFloatingPoint>(exactly source: T) {
|
||||
switch source.sign{
|
||||
case .plus:
|
||||
guard let magnitude = CS.BigUInt(exactly: source) else { return nil }
|
||||
self = CS.BigInt(sign: .plus, magnitude: magnitude)
|
||||
case .minus:
|
||||
guard let magnitude = CS.BigUInt(exactly: -source) else { return nil }
|
||||
self = CS.BigInt(sign: .minus, magnitude: magnitude)
|
||||
}
|
||||
}
|
||||
|
||||
public init<T: BinaryFloatingPoint>(_ source: T) {
|
||||
self.init(exactly: source.rounded(.towardZero))!
|
||||
}
|
||||
}
|
||||
|
||||
extension BinaryFloatingPoint where RawExponent: FixedWidthInteger, RawSignificand: FixedWidthInteger {
|
||||
public init(_ value: CS.BigInt) {
|
||||
guard !value.isZero else { self = 0; return }
|
||||
let v = value.magnitude
|
||||
let bitWidth = v.bitWidth
|
||||
var exponent = bitWidth - 1
|
||||
let shift = bitWidth - Self.significandBitCount - 1
|
||||
var significand = value.magnitude >> (shift - 1)
|
||||
if significand[0] & 3 == 3 { // Handle rounding
|
||||
significand >>= 1
|
||||
significand += 1
|
||||
if significand.trailingZeroBitCount >= Self.significandBitCount {
|
||||
exponent += 1
|
||||
}
|
||||
}
|
||||
else {
|
||||
significand >>= 1
|
||||
}
|
||||
let bias = 1 << (Self.exponentBitCount - 1) - 1
|
||||
guard exponent <= bias else { self = Self.infinity; return }
|
||||
significand &= 1 << Self.significandBitCount - 1
|
||||
self = Self.init(sign: value.sign == .plus ? .plus : .minus,
|
||||
exponentBitPattern: RawExponent(bias + exponent),
|
||||
significandBitPattern: RawSignificand(significand))
|
||||
}
|
||||
|
||||
public init(_ value: CS.BigUInt) {
|
||||
self.init(CS.BigInt(sign: .plus, magnitude: value))
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
//
|
||||
// GCD.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: Greatest Common Divisor
|
||||
|
||||
/// Returns the greatest common divisor of `self` and `b`.
|
||||
///
|
||||
/// - Complexity: O(count^2) where count = max(self.count, b.count)
|
||||
public func greatestCommonDivisor(with b: CS.BigUInt) -> CS.BigUInt {
|
||||
// This is Stein's algorithm: https://en.wikipedia.org/wiki/Binary_GCD_algorithm
|
||||
if self.isZero { return b }
|
||||
if b.isZero { return self }
|
||||
|
||||
let az = self.trailingZeroBitCount
|
||||
let bz = b.trailingZeroBitCount
|
||||
let twos = Swift.min(az, bz)
|
||||
|
||||
var (x, y) = (self >> az, b >> bz)
|
||||
if x < y { swap(&x, &y) }
|
||||
|
||||
while !x.isZero {
|
||||
x >>= x.trailingZeroBitCount
|
||||
if x < y { swap(&x, &y) }
|
||||
x -= y
|
||||
}
|
||||
return y << twos
|
||||
}
|
||||
|
||||
/// Returns the [multiplicative inverse of this integer in modulo `modulus` arithmetic][inverse],
|
||||
/// or `nil` if there is no such number.
|
||||
///
|
||||
/// [inverse]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers
|
||||
///
|
||||
/// - Returns: If `gcd(self, modulus) == 1`, the value returned is an integer `a < modulus` such that `(a * self) % modulus == 1`. If `self` and `modulus` aren't coprime, the return value is `nil`.
|
||||
/// - Requires: modulus > 1
|
||||
/// - Complexity: O(count^3)
|
||||
public func inverse(_ modulus: CS.BigUInt) -> CS.BigUInt? {
|
||||
precondition(modulus > 1)
|
||||
var t1 = CS.BigInt(0)
|
||||
var t2 = CS.BigInt(1)
|
||||
var r1 = modulus
|
||||
var r2 = self
|
||||
while !r2.isZero {
|
||||
let quotient = r1 / r2
|
||||
(t1, t2) = (t2, t1 - CS.BigInt(quotient) * t2)
|
||||
(r1, r2) = (r2, r1 - quotient * r2)
|
||||
}
|
||||
if r1 > 1 { return nil }
|
||||
if t1.sign == .minus { return modulus - t1.magnitude }
|
||||
return t1.magnitude
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Returns the greatest common divisor of `a` and `b`.
|
||||
///
|
||||
/// - Complexity: O(count^2) where count = max(a.count, b.count)
|
||||
public func greatestCommonDivisor(with b: CS.BigInt) -> CS.BigInt {
|
||||
return CS.BigInt(self.magnitude.greatestCommonDivisor(with: b.magnitude))
|
||||
}
|
||||
|
||||
/// Returns the [multiplicative inverse of this integer in modulo `modulus` arithmetic][inverse],
|
||||
/// or `nil` if there is no such number.
|
||||
///
|
||||
/// [inverse]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers
|
||||
///
|
||||
/// - Returns: If `gcd(self, modulus) == 1`, the value returned is an integer `a < modulus` such that `(a * self) % modulus == 1`. If `self` and `modulus` aren't coprime, the return value is `nil`.
|
||||
/// - Requires: modulus.magnitude > 1
|
||||
/// - Complexity: O(count^3)
|
||||
public func inverse(_ modulus: CS.BigInt) -> CS.BigInt? {
|
||||
guard let inv = self.magnitude.inverse(modulus.magnitude) else { return nil }
|
||||
return CS.BigInt(self.sign == .plus || inv.isZero ? inv : modulus.magnitude - inv)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// Hashable.swift
|
||||
// BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt: Hashable {
|
||||
//MARK: Hashing
|
||||
|
||||
/// Append this `BigUInt` to the specified hasher.
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
for word in self.words {
|
||||
hasher.combine(word)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt: Hashable {
|
||||
/// Append this `BigInt` to the specified hasher.
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(sign)
|
||||
hasher.combine(magnitude)
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
//
|
||||
// Integer Conversion.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2017-08-11.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
public init?<T: BinaryInteger>(exactly source: T) {
|
||||
guard source >= (0 as T) else { return nil }
|
||||
if source.bitWidth <= 2 * Word.bitWidth {
|
||||
var it = source.words.makeIterator()
|
||||
self.init(low: it.next() ?? 0, high: it.next() ?? 0)
|
||||
precondition(it.next() == nil, "Length of BinaryInteger.words is greater than its bitWidth")
|
||||
}
|
||||
else {
|
||||
self.init(words: source.words)
|
||||
}
|
||||
}
|
||||
|
||||
public init<T: BinaryInteger>(_ source: T) {
|
||||
precondition(source >= (0 as T), "BigUInt cannot represent negative values")
|
||||
self.init(exactly: source)!
|
||||
}
|
||||
|
||||
public init<T: BinaryInteger>(truncatingIfNeeded source: T) {
|
||||
self.init(words: source.words)
|
||||
}
|
||||
|
||||
public init<T: BinaryInteger>(clamping source: T) {
|
||||
if source <= (0 as T) {
|
||||
self.init()
|
||||
}
|
||||
else {
|
||||
self.init(words: source.words)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
public init() {
|
||||
self.init(sign: .plus, magnitude: 0)
|
||||
}
|
||||
|
||||
/// Initializes a new signed big integer with the same value as the specified unsigned big integer.
|
||||
public init(_ integer: CS.BigUInt) {
|
||||
self.magnitude = integer
|
||||
self.sign = .plus
|
||||
}
|
||||
|
||||
public init<T>(_ source: T) where T : BinaryInteger {
|
||||
if source >= (0 as T) {
|
||||
self.init(sign: .plus, magnitude: CS.BigUInt(source))
|
||||
}
|
||||
else {
|
||||
var words = Array(source.words)
|
||||
words.twosComplement()
|
||||
self.init(sign: .minus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
}
|
||||
|
||||
public init?<T>(exactly source: T) where T : BinaryInteger {
|
||||
self.init(source)
|
||||
}
|
||||
|
||||
public init<T>(clamping source: T) where T : BinaryInteger {
|
||||
self.init(source)
|
||||
}
|
||||
|
||||
public init<T>(truncatingIfNeeded source: T) where T : BinaryInteger {
|
||||
self.init(source)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt: ExpressibleByIntegerLiteral {
|
||||
/// Initialize a new big integer from an integer literal.
|
||||
public init(integerLiteral value: UInt64) {
|
||||
self.init(value)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt: ExpressibleByIntegerLiteral {
|
||||
/// Initialize a new big integer from an integer literal.
|
||||
public init(integerLiteral value: Int64) {
|
||||
self.init(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
//
|
||||
// Multiplication.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
|
||||
//MARK: Multiplication
|
||||
|
||||
/// Multiply this big integer by a single word, and store the result in place of the original big integer.
|
||||
///
|
||||
/// - Complexity: O(count)
|
||||
public mutating func multiply(byWord y: Word) {
|
||||
guard y != 0 else { self = 0; return }
|
||||
guard y != 1 else { return }
|
||||
var carry: Word = 0
|
||||
let c = self.count
|
||||
for i in 0 ..< c {
|
||||
let (h, l) = self[i].multipliedFullWidth(by: y)
|
||||
let (low, o) = l.addingReportingOverflow(carry)
|
||||
self[i] = low
|
||||
carry = (o ? h + 1 : h)
|
||||
}
|
||||
self[c] = carry
|
||||
}
|
||||
|
||||
/// Multiply this big integer by a single Word, and return the result.
|
||||
///
|
||||
/// - Complexity: O(count)
|
||||
public func multiplied(byWord y: Word) -> CS.BigUInt {
|
||||
var r = self
|
||||
r.multiply(byWord: y)
|
||||
return r
|
||||
}
|
||||
|
||||
/// Multiply `x` by `y`, and add the result to this integer, optionally shifted `shift` words to the left.
|
||||
///
|
||||
/// - Note: This is the fused multiply/shift/add operation; it is more efficient than doing the components
|
||||
/// individually. (The fused operation doesn't need to allocate space for temporary big integers.)
|
||||
/// - Returns: `self` is set to `self + (x * y) << (shift * 2^Word.bitWidth)`
|
||||
/// - Complexity: O(count)
|
||||
public mutating func multiplyAndAdd(_ x: CS.BigUInt, _ y: Word, shiftedBy shift: Int = 0) {
|
||||
precondition(shift >= 0)
|
||||
guard y != 0 && x.count > 0 else { return }
|
||||
guard y != 1 else { self.add(x, shiftedBy: shift); return }
|
||||
var mulCarry: Word = 0
|
||||
var addCarry = false
|
||||
let xc = x.count
|
||||
var xi = 0
|
||||
while xi < xc || addCarry || mulCarry > 0 {
|
||||
let (h, l) = x[xi].multipliedFullWidth(by: y)
|
||||
let (low, o) = l.addingReportingOverflow(mulCarry)
|
||||
mulCarry = (o ? h + 1 : h)
|
||||
|
||||
let ai = shift + xi
|
||||
let (sum1, so1) = self[ai].addingReportingOverflow(low)
|
||||
if addCarry {
|
||||
let (sum2, so2) = sum1.addingReportingOverflow(1)
|
||||
self[ai] = sum2
|
||||
addCarry = so1 || so2
|
||||
}
|
||||
else {
|
||||
self[ai] = sum1
|
||||
addCarry = so1
|
||||
}
|
||||
xi += 1
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply this integer by `y` and return the result.
|
||||
///
|
||||
/// - Note: This uses the naive O(n^2) multiplication algorithm unless both arguments have more than
|
||||
/// `BigUInt.directMultiplicationLimit` words.
|
||||
/// - Complexity: O(n^log2(3))
|
||||
public func multiplied(by y: CS.BigUInt) -> CS.BigUInt {
|
||||
// This method is mostly defined for symmetry with the rest of the arithmetic operations.
|
||||
return self * y
|
||||
}
|
||||
|
||||
/// Multiplication switches to an asymptotically better recursive algorithm when arguments have more words than this limit.
|
||||
public static var directMultiplicationLimit: Int = 1024
|
||||
|
||||
/// Multiply `a` by `b` and return the result.
|
||||
///
|
||||
/// - Note: This uses the naive O(n^2) multiplication algorithm unless both arguments have more than
|
||||
/// `BigUInt.directMultiplicationLimit` words.
|
||||
/// - Complexity: O(n^log2(3))
|
||||
public static func *(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt {
|
||||
let xc = x.count
|
||||
let yc = y.count
|
||||
if xc == 0 { return CS.BigUInt() }
|
||||
if yc == 0 { return CS.BigUInt() }
|
||||
if yc == 1 { return x.multiplied(byWord: y[0]) }
|
||||
if xc == 1 { return y.multiplied(byWord: x[0]) }
|
||||
|
||||
if Swift.min(xc, yc) <= CS.BigUInt.directMultiplicationLimit {
|
||||
// Long multiplication.
|
||||
let left = (xc < yc ? y : x)
|
||||
let right = (xc < yc ? x : y)
|
||||
var result = CS.BigUInt()
|
||||
for i in (0 ..< right.count).reversed() {
|
||||
result.multiplyAndAdd(left, right[i], shiftedBy: i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
if yc < xc {
|
||||
let (xh, xl) = x.split
|
||||
var r = xl * y
|
||||
r.add(xh * y, shiftedBy: x.middleIndex)
|
||||
return r
|
||||
}
|
||||
else if xc < yc {
|
||||
let (yh, yl) = y.split
|
||||
var r = yl * x
|
||||
r.add(yh * x, shiftedBy: y.middleIndex)
|
||||
return r
|
||||
}
|
||||
|
||||
let shift = x.middleIndex
|
||||
|
||||
// Karatsuba multiplication:
|
||||
// x * y = <a,b> * <c,d> = <ac, ac + bd - (a-b)(c-d), bd> (ignoring carry)
|
||||
let (a, b) = x.split
|
||||
let (c, d) = y.split
|
||||
|
||||
let high = a * c
|
||||
let low = b * d
|
||||
let xp = a >= b
|
||||
let yp = c >= d
|
||||
let xm = (xp ? a - b : b - a)
|
||||
let ym = (yp ? c - d : d - c)
|
||||
let m = xm * ym
|
||||
|
||||
var r = low
|
||||
r.add(high, shiftedBy: 2 * shift)
|
||||
r.add(low, shiftedBy: shift)
|
||||
r.add(high, shiftedBy: shift)
|
||||
if xp == yp {
|
||||
r.subtract(m, shiftedBy: shift)
|
||||
}
|
||||
else {
|
||||
r.add(m, shiftedBy: shift)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
/// Multiply `a` by `b` and store the result in `a`.
|
||||
public static func *=(a: inout CS.BigUInt, b: CS.BigUInt) {
|
||||
a = a * b
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Multiply `a` with `b` and return the result.
|
||||
public static func *(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
|
||||
return CS.BigInt(sign: a.sign == b.sign ? .plus : .minus, magnitude: a.magnitude * b.magnitude)
|
||||
}
|
||||
|
||||
/// Multiply `a` with `b` in place.
|
||||
public static func *=(a: inout CS.BigInt, b: CS.BigInt) { a = a * b }
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
//
|
||||
// Prime Test.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-04.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
/// The first several [prime numbers][primes].
|
||||
///
|
||||
/// [primes]: https://oeis.org/A000040
|
||||
let primes: [CS.BigUInt.Word] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
|
||||
|
||||
/// The ith element in this sequence is the smallest composite number that passes the strong probable prime test
|
||||
/// for all of the first (i+1) primes.
|
||||
///
|
||||
/// This is sequence [A014233](http://oeis.org/A014233) on the [Online Encyclopaedia of Integer Sequences](http://oeis.org).
|
||||
let pseudoPrimes: [CS.BigUInt] = [
|
||||
/* 2 */ 2_047,
|
||||
/* 3 */ 1_373_653,
|
||||
/* 5 */ 25_326_001,
|
||||
/* 7 */ 3_215_031_751,
|
||||
/* 11 */ 2_152_302_898_747,
|
||||
/* 13 */ 3_474_749_660_383,
|
||||
/* 17 */ 341_550_071_728_321,
|
||||
/* 19 */ 341_550_071_728_321,
|
||||
/* 23 */ 3_825_123_056_546_413_051,
|
||||
/* 29 */ 3_825_123_056_546_413_051,
|
||||
/* 31 */ 3_825_123_056_546_413_051,
|
||||
/* 37 */ "318665857834031151167461",
|
||||
/* 41 */ "3317044064679887385961981",
|
||||
]
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: Primality Testing
|
||||
|
||||
/// Returns true iff this integer passes the [strong probable prime test][sppt] for the specified base.
|
||||
///
|
||||
/// [sppt]: https://en.wikipedia.org/wiki/Probable_prime
|
||||
public func isStrongProbablePrime(_ base: CS.BigUInt) -> Bool {
|
||||
precondition(base > (1 as CS.BigUInt))
|
||||
precondition(self > (0 as CS.BigUInt))
|
||||
let dec = self - 1
|
||||
|
||||
let r = dec.trailingZeroBitCount
|
||||
let d = dec >> r
|
||||
|
||||
var test = base.power(d, modulus: self)
|
||||
if test == 1 || test == dec { return true }
|
||||
|
||||
if r > 0 {
|
||||
let shift = self.leadingZeroBitCount
|
||||
let normalized = self << shift
|
||||
for _ in 1 ..< r {
|
||||
test *= test
|
||||
test.formRemainder(dividingBy: normalized, normalizedBy: shift)
|
||||
if test == 1 {
|
||||
return false
|
||||
}
|
||||
if test == dec { return true }
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns true if this integer is probably prime. Returns false if this integer is definitely not prime.
|
||||
///
|
||||
/// This function performs a probabilistic [Miller-Rabin Primality Test][mrpt], consisting of `rounds` iterations,
|
||||
/// each calculating the strong probable prime test for a random base. The number of rounds is 10 by default,
|
||||
/// but you may specify your own choice.
|
||||
///
|
||||
/// To speed things up, the function checks if `self` is divisible by the first few prime numbers before
|
||||
/// diving into (slower) Miller-Rabin testing.
|
||||
///
|
||||
/// Also, when `self` is less than 82 bits wide, `isPrime` does a deterministic test that is guaranteed to
|
||||
/// return a correct result.
|
||||
///
|
||||
/// [mrpt]: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test
|
||||
public func isPrime(rounds: Int = 10) -> Bool {
|
||||
if count <= 1 && self[0] < 2 { return false }
|
||||
if count == 1 && self[0] < 4 { return true }
|
||||
|
||||
// Even numbers above 2 aren't prime.
|
||||
if self[0] & 1 == 0 { return false }
|
||||
|
||||
// Quickly check for small primes.
|
||||
for i in 1 ..< primes.count {
|
||||
let p = primes[i]
|
||||
if self.count == 1 && self[0] == p {
|
||||
return true
|
||||
}
|
||||
if self.quotientAndRemainder(dividingByWord: p).remainder == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Give an exact answer when we can.
|
||||
if self < pseudoPrimes.last! {
|
||||
for i in 0 ..< pseudoPrimes.count {
|
||||
guard isStrongProbablePrime(CS.BigUInt(primes[i])) else {
|
||||
break
|
||||
}
|
||||
if self < pseudoPrimes[i] {
|
||||
// `self` is below the lowest pseudoprime corresponding to the prime bases we tested. It's a prime!
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Otherwise do as many rounds of random SPPT as required.
|
||||
for _ in 0 ..< rounds {
|
||||
let random = CS.BigUInt.randomInteger(lessThan: self - 2) + 2
|
||||
guard isStrongProbablePrime(random) else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Well, it smells primey to me.
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
//MARK: Primality Testing
|
||||
|
||||
/// Returns true iff this integer passes the [strong probable prime test][sppt] for the specified base.
|
||||
///
|
||||
/// [sppt]: https://en.wikipedia.org/wiki/Probable_prime
|
||||
public func isStrongProbablePrime(_ base: CS.BigInt) -> Bool {
|
||||
precondition(base.sign == .plus)
|
||||
if self.sign == .minus { return false }
|
||||
return self.magnitude.isStrongProbablePrime(base.magnitude)
|
||||
}
|
||||
|
||||
/// Returns true if this integer is probably prime. Returns false if this integer is definitely not prime.
|
||||
///
|
||||
/// This function performs a probabilistic [Miller-Rabin Primality Test][mrpt], consisting of `rounds` iterations,
|
||||
/// each calculating the strong probable prime test for a random base. The number of rounds is 10 by default,
|
||||
/// but you may specify your own choice.
|
||||
///
|
||||
/// To speed things up, the function checks if `self` is divisible by the first few prime numbers before
|
||||
/// diving into (slower) Miller-Rabin testing.
|
||||
///
|
||||
/// Also, when `self` is less than 82 bits wide, `isPrime` does a deterministic test that is guaranteed to
|
||||
/// return a correct result.
|
||||
///
|
||||
/// [mrpt]: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test
|
||||
public func isPrime(rounds: Int = 10) -> Bool {
|
||||
if self.sign == .minus { return false }
|
||||
return self.magnitude.isPrime(rounds: rounds)
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
//
|
||||
// Random.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-04.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
/// Create a big unsigned integer consisting of `width` uniformly distributed random bits.
|
||||
///
|
||||
/// - Parameter width: The maximum number of one bits in the result.
|
||||
/// - Parameter generator: The source of randomness.
|
||||
/// - Returns: A big unsigned integer less than `1 << width`.
|
||||
public static func randomInteger<RNG: RandomNumberGenerator>(withMaximumWidth width: Int, using generator: inout RNG) -> CS.BigUInt {
|
||||
var result = CS.BigUInt.zero
|
||||
var bitsLeft = width
|
||||
var i = 0
|
||||
let wordsNeeded = (width + Word.bitWidth - 1) / Word.bitWidth
|
||||
if wordsNeeded > 2 {
|
||||
result.reserveCapacity(wordsNeeded)
|
||||
}
|
||||
while bitsLeft >= Word.bitWidth {
|
||||
result[i] = generator.next()
|
||||
i += 1
|
||||
bitsLeft -= Word.bitWidth
|
||||
}
|
||||
if bitsLeft > 0 {
|
||||
let mask: Word = (1 << bitsLeft) - 1
|
||||
result[i] = (generator.next() as Word) & mask
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// Create a big unsigned integer consisting of `width` uniformly distributed random bits.
|
||||
///
|
||||
/// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness.
|
||||
///
|
||||
/// - Parameter width: The maximum number of one bits in the result.
|
||||
/// - Returns: A big unsigned integer less than `1 << width`.
|
||||
public static func randomInteger(withMaximumWidth width: Int) -> CS.BigUInt {
|
||||
var rng = SystemRandomNumberGenerator()
|
||||
return randomInteger(withMaximumWidth: width, using: &rng)
|
||||
}
|
||||
|
||||
/// Create a big unsigned integer consisting of `width-1` uniformly distributed random bits followed by a one bit.
|
||||
///
|
||||
/// - Note: If `width` is zero, the result is zero.
|
||||
///
|
||||
/// - Parameter width: The number of bits required to represent the answer.
|
||||
/// - Parameter generator: The source of randomness.
|
||||
/// - Returns: A random big unsigned integer whose width is `width`.
|
||||
public static func randomInteger<RNG: RandomNumberGenerator>(withExactWidth width: Int, using generator: inout RNG) -> CS.BigUInt {
|
||||
// width == 0 -> return 0 because there is no room for a one bit.
|
||||
// width == 1 -> return 1 because there is no room for any random bits.
|
||||
guard width > 1 else { return CS.BigUInt(width) }
|
||||
var result = randomInteger(withMaximumWidth: width - 1, using: &generator)
|
||||
result[(width - 1) / Word.bitWidth] |= 1 << Word((width - 1) % Word.bitWidth)
|
||||
return result
|
||||
}
|
||||
|
||||
/// Create a big unsigned integer consisting of `width-1` uniformly distributed random bits followed by a one bit.
|
||||
///
|
||||
/// - Note: If `width` is zero, the result is zero.
|
||||
/// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness.
|
||||
///
|
||||
/// - Returns: A random big unsigned integer whose width is `width`.
|
||||
public static func randomInteger(withExactWidth width: Int) -> CS.BigUInt {
|
||||
var rng = SystemRandomNumberGenerator()
|
||||
return randomInteger(withExactWidth: width, using: &rng)
|
||||
}
|
||||
|
||||
/// Create a uniformly distributed random unsigned integer that's less than the specified limit.
|
||||
///
|
||||
/// - Precondition: `limit > 0`.
|
||||
///
|
||||
/// - Parameter limit: The upper bound on the result.
|
||||
/// - Parameter generator: The source of randomness.
|
||||
/// - Returns: A random big unsigned integer that is less than `limit`.
|
||||
public static func randomInteger<RNG: RandomNumberGenerator>(lessThan limit: CS.BigUInt, using generator: inout RNG) -> CS.BigUInt {
|
||||
precondition(limit > 0, "\(#function): 0 is not a valid limit")
|
||||
let width = limit.bitWidth
|
||||
var random = randomInteger(withMaximumWidth: width, using: &generator)
|
||||
while random >= limit {
|
||||
random = randomInteger(withMaximumWidth: width, using: &generator)
|
||||
}
|
||||
return random
|
||||
}
|
||||
|
||||
/// Create a uniformly distributed random unsigned integer that's less than the specified limit.
|
||||
///
|
||||
/// - Precondition: `limit > 0`.
|
||||
/// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness.
|
||||
///
|
||||
/// - Parameter limit: The upper bound on the result.
|
||||
/// - Returns: A random big unsigned integer that is less than `limit`.
|
||||
public static func randomInteger(lessThan limit: CS.BigUInt) -> CS.BigUInt {
|
||||
var rng = SystemRandomNumberGenerator()
|
||||
return randomInteger(lessThan: limit, using: &rng)
|
||||
}
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
//
|
||||
// Shifts.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
|
||||
//MARK: Shift Operators
|
||||
|
||||
internal func shiftedLeft(by amount: Word) -> CS.BigUInt {
|
||||
guard amount > 0 else { return self }
|
||||
|
||||
let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words)
|
||||
let up = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift)
|
||||
let down = Word(Word.bitWidth) - up
|
||||
|
||||
var result = CS.BigUInt()
|
||||
if up > 0 {
|
||||
var i = 0
|
||||
var lowbits: Word = 0
|
||||
while i < self.count || lowbits > 0 {
|
||||
let word = self[i]
|
||||
result[i + ext] = word << up | lowbits
|
||||
lowbits = word >> down
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
else {
|
||||
for i in 0 ..< self.count {
|
||||
result[i + ext] = self[i]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal mutating func shiftLeft(by amount: Word) {
|
||||
guard amount > 0 else { return }
|
||||
|
||||
let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words)
|
||||
let up = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift)
|
||||
let down = Word(Word.bitWidth) - up
|
||||
|
||||
if up > 0 {
|
||||
var i = 0
|
||||
var lowbits: Word = 0
|
||||
while i < self.count || lowbits > 0 {
|
||||
let word = self[i]
|
||||
self[i] = word << up | lowbits
|
||||
lowbits = word >> down
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
if ext > 0 && self.count > 0 {
|
||||
self.shiftLeft(byWords: ext)
|
||||
}
|
||||
}
|
||||
|
||||
internal func shiftedRight(by amount: Word) -> CS.BigUInt {
|
||||
guard amount > 0 else { return self }
|
||||
guard amount < self.bitWidth else { return 0 }
|
||||
|
||||
let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words)
|
||||
let down = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift)
|
||||
let up = Word(Word.bitWidth) - down
|
||||
|
||||
var result = CS.BigUInt()
|
||||
if down > 0 {
|
||||
var highbits: Word = 0
|
||||
for i in (ext ..< self.count).reversed() {
|
||||
let word = self[i]
|
||||
result[i - ext] = highbits | word >> down
|
||||
highbits = word << up
|
||||
}
|
||||
}
|
||||
else {
|
||||
for i in (ext ..< self.count).reversed() {
|
||||
result[i - ext] = self[i]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal mutating func shiftRight(by amount: Word) {
|
||||
guard amount > 0 else { return }
|
||||
guard amount < self.bitWidth else { self.clear(); return }
|
||||
|
||||
let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words)
|
||||
let down = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift)
|
||||
let up = Word(Word.bitWidth) - down
|
||||
|
||||
if ext > 0 {
|
||||
self.shiftRight(byWords: ext)
|
||||
}
|
||||
if down > 0 {
|
||||
var i = self.count - 1
|
||||
var highbits: Word = 0
|
||||
while i >= 0 {
|
||||
let word = self[i]
|
||||
self[i] = highbits | word >> down
|
||||
highbits = word << up
|
||||
i -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func >>=<Other: BinaryInteger>(lhs: inout CS.BigUInt, rhs: Other) {
|
||||
if rhs < (0 as Other) {
|
||||
lhs <<= (0 - rhs)
|
||||
}
|
||||
else if rhs >= lhs.bitWidth {
|
||||
lhs.clear()
|
||||
}
|
||||
else {
|
||||
lhs.shiftRight(by: UInt(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
public static func <<=<Other: BinaryInteger>(lhs: inout CS.BigUInt, rhs: Other) {
|
||||
if rhs < (0 as Other) {
|
||||
lhs >>= (0 - rhs)
|
||||
return
|
||||
}
|
||||
lhs.shiftLeft(by: Word(exactly: rhs)!)
|
||||
}
|
||||
|
||||
public static func >><Other: BinaryInteger>(lhs: CS.BigUInt, rhs: Other) -> CS.BigUInt {
|
||||
if rhs < (0 as Other) {
|
||||
return lhs << (0 - rhs)
|
||||
}
|
||||
if rhs > Word.max {
|
||||
return 0
|
||||
}
|
||||
return lhs.shiftedRight(by: UInt(rhs))
|
||||
}
|
||||
|
||||
public static func <<<Other: BinaryInteger>(lhs: CS.BigUInt, rhs: Other) -> CS.BigUInt {
|
||||
if rhs < (0 as Other) {
|
||||
return lhs >> (0 - rhs)
|
||||
}
|
||||
return lhs.shiftedLeft(by: Word(exactly: rhs)!)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
func shiftedLeft(by amount: Word) -> CS.BigInt {
|
||||
return CS.BigInt(sign: self.sign, magnitude: self.magnitude.shiftedLeft(by: amount))
|
||||
}
|
||||
|
||||
mutating func shiftLeft(by amount: Word) {
|
||||
self.magnitude.shiftLeft(by: amount)
|
||||
}
|
||||
|
||||
func shiftedRight(by amount: Word) -> CS.BigInt {
|
||||
let m = self.magnitude.shiftedRight(by: amount)
|
||||
return CS.BigInt(sign: self.sign, magnitude: self.sign == .minus && m.isZero ? 1 : m)
|
||||
}
|
||||
|
||||
mutating func shiftRight(by amount: Word) {
|
||||
magnitude.shiftRight(by: amount)
|
||||
if sign == .minus, magnitude.isZero {
|
||||
magnitude.load(1)
|
||||
}
|
||||
}
|
||||
|
||||
public static func &<<(left: CS.BigInt, right: CS.BigInt) -> CS.BigInt {
|
||||
return left.shiftedLeft(by: right.words[0])
|
||||
}
|
||||
|
||||
public static func &<<=(left: inout CS.BigInt, right: CS.BigInt) {
|
||||
left.shiftLeft(by: right.words[0])
|
||||
}
|
||||
|
||||
public static func &>>(left: CS.BigInt, right: CS.BigInt) -> CS.BigInt {
|
||||
return left.shiftedRight(by: right.words[0])
|
||||
}
|
||||
|
||||
public static func &>>=(left: inout CS.BigInt, right: CS.BigInt) {
|
||||
left.shiftRight(by: right.words[0])
|
||||
}
|
||||
|
||||
public static func <<<Other: BinaryInteger>(lhs: CS.BigInt, rhs: Other) -> CS.BigInt {
|
||||
guard rhs >= (0 as Other) else { return lhs >> (0 - rhs) }
|
||||
return lhs.shiftedLeft(by: Word(rhs))
|
||||
}
|
||||
|
||||
public static func <<=<Other: BinaryInteger>(lhs: inout CS.BigInt, rhs: Other) {
|
||||
if rhs < (0 as Other) {
|
||||
lhs >>= (0 - rhs)
|
||||
}
|
||||
else {
|
||||
lhs.shiftLeft(by: Word(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
public static func >><Other: BinaryInteger>(lhs: CS.BigInt, rhs: Other) -> CS.BigInt {
|
||||
guard rhs >= (0 as Other) else { return lhs << (0 - rhs) }
|
||||
return lhs.shiftedRight(by: Word(rhs))
|
||||
}
|
||||
|
||||
public static func >>=<Other: BinaryInteger>(lhs: inout CS.BigInt, rhs: Other) {
|
||||
if rhs < (0 as Other) {
|
||||
lhs <<= (0 - rhs)
|
||||
}
|
||||
else {
|
||||
lhs.shiftRight(by: Word(rhs))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// Square Root.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
//MARK: Square Root
|
||||
|
||||
extension CS.BigUInt {
|
||||
/// Returns the integer square root of a big integer; i.e., the largest integer whose square isn't greater than `value`.
|
||||
///
|
||||
/// - Returns: floor(sqrt(self))
|
||||
public func squareRoot() -> CS.BigUInt {
|
||||
// This implementation uses Newton's method.
|
||||
guard !self.isZero else { return CS.BigUInt() }
|
||||
var x = CS.BigUInt(1) << ((self.bitWidth + 1) / 2)
|
||||
var y: CS.BigUInt = 0
|
||||
while true {
|
||||
y.load(self)
|
||||
y /= x
|
||||
y += x
|
||||
y >>= 1
|
||||
if x == y || x == y - 1 { break }
|
||||
x = y
|
||||
}
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Returns the integer square root of a big integer; i.e., the largest integer whose square isn't greater than `value`.
|
||||
///
|
||||
/// - Requires: self >= 0
|
||||
/// - Returns: floor(sqrt(self))
|
||||
public func squareRoot() -> CS.BigInt {
|
||||
precondition(self.sign == .plus)
|
||||
return CS.BigInt(sign: .plus, magnitude: self.magnitude.squareRoot())
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
//
|
||||
// Strideable.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2017-08-11.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt: Strideable {
|
||||
/// A type that can represent the distance between two values ofa `BigUInt`.
|
||||
public typealias Stride = CS.BigInt
|
||||
|
||||
/// Adds `n` to `self` and returns the result. Traps if the result would be less than zero.
|
||||
public func advanced(by n: CS.BigInt) -> CS.BigUInt {
|
||||
return n.sign == .minus ? self - n.magnitude : self + n.magnitude
|
||||
}
|
||||
|
||||
/// Returns the (potentially negative) difference between `self` and `other` as a `BigInt`. Never traps.
|
||||
public func distance(to other: CS.BigUInt) -> CS.BigInt {
|
||||
return CS.BigInt(other) - CS.BigInt(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt: Strideable {
|
||||
public typealias Stride = CS.BigInt
|
||||
|
||||
/// Returns `self + n`.
|
||||
public func advanced(by n: Stride) -> CS.BigInt {
|
||||
return self + n
|
||||
}
|
||||
|
||||
/// Returns `other - self`.
|
||||
public func distance(to other: CS.BigInt) -> Stride {
|
||||
return other - self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
//
|
||||
// String Conversion.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
|
||||
//MARK: String Conversion
|
||||
|
||||
/// Calculates the number of numerals in a given radix that fit inside a single `Word`.
|
||||
///
|
||||
/// - Returns: (chars, power) where `chars` is highest that satisfy `radix^chars <= 2^Word.bitWidth`. `power` is zero
|
||||
/// if radix is a power of two; otherwise `power == radix^chars`.
|
||||
fileprivate static func charsPerWord(forRadix radix: Int) -> (chars: Int, power: Word) {
|
||||
var power: Word = 1
|
||||
var overflow = false
|
||||
var count = 0
|
||||
while !overflow {
|
||||
let (p, o) = power.multipliedReportingOverflow(by: Word(radix))
|
||||
overflow = o
|
||||
if !o || p == 0 {
|
||||
count += 1
|
||||
power = p
|
||||
}
|
||||
}
|
||||
return (count, power)
|
||||
}
|
||||
|
||||
/// Initialize a big integer from an ASCII representation in a given radix. Numerals above `9` are represented by
|
||||
/// letters from the English alphabet.
|
||||
///
|
||||
/// - Requires: `radix > 1 && radix < 36`
|
||||
/// - Parameter `text`: A string consisting of characters corresponding to numerals in the given radix. (0-9, a-z, A-Z)
|
||||
/// - Parameter `radix`: The base of the number system to use, or 10 if unspecified.
|
||||
/// - Returns: The integer represented by `text`, or nil if `text` contains a character that does not represent a numeral in `radix`.
|
||||
public init?<S: StringProtocol>(_ text: S, radix: Int = 10) {
|
||||
precondition(radix > 1)
|
||||
let (charsPerWord, power) = CS.BigUInt.charsPerWord(forRadix: radix)
|
||||
|
||||
var words: [Word] = []
|
||||
var end = text.endIndex
|
||||
var start = end
|
||||
var count = 0
|
||||
while start != text.startIndex {
|
||||
start = text.index(before: start)
|
||||
count += 1
|
||||
if count == charsPerWord {
|
||||
guard let d = Word.init(text[start ..< end], radix: radix) else { return nil }
|
||||
words.append(d)
|
||||
end = start
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
if start != end {
|
||||
guard let d = Word.init(text[start ..< end], radix: radix) else { return nil }
|
||||
words.append(d)
|
||||
}
|
||||
|
||||
if power == 0 {
|
||||
self.init(words: words)
|
||||
}
|
||||
else {
|
||||
self.init()
|
||||
for d in words.reversed() {
|
||||
self.multiply(byWord: power)
|
||||
self.addWord(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
/// Initialize a big integer from an ASCII representation in a given radix. Numerals above `9` are represented by
|
||||
/// letters from the English alphabet.
|
||||
///
|
||||
/// - Requires: `radix > 1 && radix < 36`
|
||||
/// - Parameter `text`: A string optionally starting with "-" or "+" followed by characters corresponding to numerals in the given radix. (0-9, a-z, A-Z)
|
||||
/// - Parameter `radix`: The base of the number system to use, or 10 if unspecified.
|
||||
/// - Returns: The integer represented by `text`, or nil if `text` contains a character that does not represent a numeral in `radix`.
|
||||
public init?<S: StringProtocol>(_ text: S, radix: Int = 10) {
|
||||
var magnitude: CS.BigUInt?
|
||||
var sign: Sign = .plus
|
||||
if text.first == "-" {
|
||||
sign = .minus
|
||||
let text = text.dropFirst()
|
||||
magnitude = CS.BigUInt(text, radix: radix)
|
||||
}
|
||||
else if text.first == "+" {
|
||||
let text = text.dropFirst()
|
||||
magnitude = CS.BigUInt(text, radix: radix)
|
||||
}
|
||||
else {
|
||||
magnitude = CS.BigUInt(text, radix: radix)
|
||||
}
|
||||
guard let m = magnitude else { return nil }
|
||||
self.magnitude = m
|
||||
self.sign = sign
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
/// Initialize a new string with the base-10 representation of an unsigned big integer.
|
||||
///
|
||||
/// - Complexity: O(v.count^2)
|
||||
public init(_ v: CS.BigUInt) { self.init(v, radix: 10, uppercase: false) }
|
||||
|
||||
/// Initialize a new string representing an unsigned big integer in the given radix (base).
|
||||
///
|
||||
/// Numerals greater than 9 are represented as letters from the English alphabet,
|
||||
/// starting with `a` if `uppercase` is false or `A` otherwise.
|
||||
///
|
||||
/// - Requires: radix > 1 && radix <= 36
|
||||
/// - Complexity: O(count) when radix is a power of two; otherwise O(count^2).
|
||||
public init(_ v: CS.BigUInt, radix: Int, uppercase: Bool = false) {
|
||||
precondition(radix > 1)
|
||||
let (charsPerWord, power) = CS.BigUInt.charsPerWord(forRadix: radix)
|
||||
|
||||
guard !v.isZero else { self = "0"; return }
|
||||
|
||||
var parts: [String]
|
||||
if power == 0 {
|
||||
parts = v.words.map { String($0, radix: radix, uppercase: uppercase) }
|
||||
}
|
||||
else {
|
||||
parts = []
|
||||
var rest = v
|
||||
while !rest.isZero {
|
||||
let mod = rest.divide(byWord: power)
|
||||
parts.append(String(mod, radix: radix, uppercase: uppercase))
|
||||
}
|
||||
}
|
||||
assert(!parts.isEmpty)
|
||||
|
||||
self = ""
|
||||
var first = true
|
||||
for part in parts.reversed() {
|
||||
let zeroes = charsPerWord - part.count
|
||||
assert(zeroes >= 0)
|
||||
if !first && zeroes > 0 {
|
||||
// Insert leading zeroes for mid-Words
|
||||
self += String(repeating: "0", count: zeroes)
|
||||
}
|
||||
first = false
|
||||
self += part
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize a new string representing a signed big integer in the given radix (base).
|
||||
///
|
||||
/// Numerals greater than 9 are represented as letters from the English alphabet,
|
||||
/// starting with `a` if `uppercase` is false or `A` otherwise.
|
||||
///
|
||||
/// - Requires: radix > 1 && radix <= 36
|
||||
/// - Complexity: O(count) when radix is a power of two; otherwise O(count^2).
|
||||
public init(_ value: CS.BigInt, radix: Int = 10, uppercase: Bool = false) {
|
||||
self = String(value.magnitude, radix: radix, uppercase: uppercase)
|
||||
if value.sign == .minus {
|
||||
self = "-" + self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt: ExpressibleByStringLiteral {
|
||||
/// Initialize a new big integer from a Unicode scalar.
|
||||
/// The scalar must represent a decimal digit.
|
||||
public init(unicodeScalarLiteral value: UnicodeScalar) {
|
||||
self = CS.BigUInt(String(value), radix: 10)!
|
||||
}
|
||||
|
||||
/// Initialize a new big integer from an extended grapheme cluster.
|
||||
/// The cluster must consist of a decimal digit.
|
||||
public init(extendedGraphemeClusterLiteral value: String) {
|
||||
self = CS.BigUInt(value, radix: 10)!
|
||||
}
|
||||
|
||||
/// Initialize a new big integer from a decimal number represented by a string literal of arbitrary length.
|
||||
/// The string must contain only decimal digits.
|
||||
public init(stringLiteral value: StringLiteralType) {
|
||||
self = CS.BigUInt(value, radix: 10)!
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt: ExpressibleByStringLiteral {
|
||||
/// Initialize a new big integer from a Unicode scalar.
|
||||
/// The scalar must represent a decimal digit.
|
||||
public init(unicodeScalarLiteral value: UnicodeScalar) {
|
||||
self = CS.BigInt(String(value), radix: 10)!
|
||||
}
|
||||
|
||||
/// Initialize a new big integer from an extended grapheme cluster.
|
||||
/// The cluster must consist of a decimal digit.
|
||||
public init(extendedGraphemeClusterLiteral value: String) {
|
||||
self = CS.BigInt(value, radix: 10)!
|
||||
}
|
||||
|
||||
/// Initialize a new big integer from a decimal number represented by a string literal of arbitrary length.
|
||||
/// The string must contain only decimal digits.
|
||||
public init(stringLiteral value: StringLiteralType) {
|
||||
self = CS.BigInt(value, radix: 10)!
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt: CustomStringConvertible {
|
||||
/// Return the decimal representation of this integer.
|
||||
public var description: String {
|
||||
return String(self, radix: 10)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt: CustomStringConvertible {
|
||||
/// Return the decimal representation of this integer.
|
||||
public var description: String {
|
||||
return String(self, radix: 10)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt: CustomPlaygroundDisplayConvertible {
|
||||
|
||||
/// Return the playground quick look representation of this integer.
|
||||
public var playgroundDescription: Any {
|
||||
let text = String(self)
|
||||
return text + " (\(self.bitWidth) bits)"
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt: CustomPlaygroundDisplayConvertible {
|
||||
|
||||
/// Return the playground quick look representation of this integer.
|
||||
public var playgroundDescription: Any {
|
||||
let text = String(self)
|
||||
return text + " (\(self.magnitude.bitWidth) bits)"
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
//
|
||||
// Subtraction.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2016-01-03.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension CS.BigUInt {
|
||||
//MARK: Subtraction
|
||||
|
||||
/// Subtract `word` from this integer in place, returning a flag indicating if the operation
|
||||
/// caused an arithmetic overflow. `word` is shifted `shift` words to the left before being subtracted.
|
||||
///
|
||||
/// - Note: If the result indicates an overflow, then `self` becomes the two's complement of the absolute difference.
|
||||
/// - Complexity: O(count)
|
||||
internal mutating func subtractWordReportingOverflow(_ word: Word, shiftedBy shift: Int = 0) -> Bool {
|
||||
precondition(shift >= 0)
|
||||
var carry: Word = word
|
||||
var i = shift
|
||||
let count = self.count
|
||||
while carry > 0 && i < count {
|
||||
let (d, c) = self[i].subtractingReportingOverflow(carry)
|
||||
self[i] = d
|
||||
carry = (c ? 1 : 0)
|
||||
i += 1
|
||||
}
|
||||
return carry > 0
|
||||
}
|
||||
|
||||
/// Subtract `word` from this integer, returning the difference and a flag that is true if the operation
|
||||
/// caused an arithmetic overflow. `word` is shifted `shift` words to the left before being subtracted.
|
||||
///
|
||||
/// - Note: If `overflow` is true, then the returned value is the two's complement of the absolute difference.
|
||||
/// - Complexity: O(count)
|
||||
internal func subtractingWordReportingOverflow(_ word: Word, shiftedBy shift: Int = 0) -> (partialValue: CS.BigUInt, overflow: Bool) {
|
||||
var result = self
|
||||
let overflow = result.subtractWordReportingOverflow(word, shiftedBy: shift)
|
||||
return (result, overflow)
|
||||
}
|
||||
|
||||
/// Subtract a digit `d` from this integer in place.
|
||||
/// `d` is shifted `shift` digits to the left before being subtracted.
|
||||
///
|
||||
/// - Requires: self >= d * 2^shift
|
||||
/// - Complexity: O(count)
|
||||
internal mutating func subtractWord(_ word: Word, shiftedBy shift: Int = 0) {
|
||||
let overflow = subtractWordReportingOverflow(word, shiftedBy: shift)
|
||||
precondition(!overflow)
|
||||
}
|
||||
|
||||
/// Subtract a digit `d` from this integer and return the result.
|
||||
/// `d` is shifted `shift` digits to the left before being subtracted.
|
||||
///
|
||||
/// - Requires: self >= d * 2^shift
|
||||
/// - Complexity: O(count)
|
||||
internal func subtractingWord(_ word: Word, shiftedBy shift: Int = 0) -> CS.BigUInt {
|
||||
var result = self
|
||||
result.subtractWord(word, shiftedBy: shift)
|
||||
return result
|
||||
}
|
||||
|
||||
/// Subtract `other` from this integer in place, and return a flag indicating if the operation caused an
|
||||
/// arithmetic overflow. `other` is shifted `shift` digits to the left before being subtracted.
|
||||
///
|
||||
/// - Note: If the result indicates an overflow, then `self` becomes the twos' complement of the absolute difference.
|
||||
/// - Complexity: O(count)
|
||||
public mutating func subtractReportingOverflow(_ b: CS.BigUInt, shiftedBy shift: Int = 0) -> Bool {
|
||||
precondition(shift >= 0)
|
||||
var carry = false
|
||||
var bi = 0
|
||||
let bc = b.count
|
||||
let count = self.count
|
||||
while bi < bc || (shift + bi < count && carry) {
|
||||
let ai = shift + bi
|
||||
let (d, c) = self[ai].subtractingReportingOverflow(b[bi])
|
||||
if carry {
|
||||
let (d2, c2) = d.subtractingReportingOverflow(1)
|
||||
self[ai] = d2
|
||||
carry = c || c2
|
||||
}
|
||||
else {
|
||||
self[ai] = d
|
||||
carry = c
|
||||
}
|
||||
bi += 1
|
||||
}
|
||||
return carry
|
||||
}
|
||||
|
||||
/// Subtract `other` from this integer, returning the difference and a flag indicating arithmetic overflow.
|
||||
/// `other` is shifted `shift` digits to the left before being subtracted.
|
||||
///
|
||||
/// - Note: If `overflow` is true, then the result value is the twos' complement of the absolute value of the difference.
|
||||
/// - Complexity: O(count)
|
||||
public func subtractingReportingOverflow(_ other: CS.BigUInt, shiftedBy shift: Int) -> (partialValue: CS.BigUInt, overflow: Bool) {
|
||||
var result = self
|
||||
let overflow = result.subtractReportingOverflow(other, shiftedBy: shift)
|
||||
return (result, overflow)
|
||||
}
|
||||
|
||||
/// Subtracts `other` from `self`, returning the result and a flag indicating arithmetic overflow.
|
||||
///
|
||||
/// - Note: When the operation overflows, then `partialValue` is the twos' complement of the absolute value of the difference.
|
||||
/// - Complexity: O(count)
|
||||
public func subtractingReportingOverflow(_ other: CS.BigUInt) -> (partialValue: CS.BigUInt, overflow: Bool) {
|
||||
return self.subtractingReportingOverflow(other, shiftedBy: 0)
|
||||
}
|
||||
|
||||
/// Subtract `other` from this integer in place.
|
||||
/// `other` is shifted `shift` digits to the left before being subtracted.
|
||||
///
|
||||
/// - Requires: self >= other * 2^shift
|
||||
/// - Complexity: O(count)
|
||||
public mutating func subtract(_ other: CS.BigUInt, shiftedBy shift: Int = 0) {
|
||||
let overflow = subtractReportingOverflow(other, shiftedBy: shift)
|
||||
precondition(!overflow)
|
||||
}
|
||||
|
||||
/// Subtract `b` from this integer, and return the difference.
|
||||
/// `b` is shifted `shift` digits to the left before being subtracted.
|
||||
///
|
||||
/// - Requires: self >= b * 2^shift
|
||||
/// - Complexity: O(count)
|
||||
public func subtracting(_ other: CS.BigUInt, shiftedBy shift: Int = 0) -> CS.BigUInt {
|
||||
var result = self
|
||||
result.subtract(other, shiftedBy: shift)
|
||||
return result
|
||||
}
|
||||
|
||||
/// Decrement this integer by one.
|
||||
///
|
||||
/// - Requires: !isZero
|
||||
/// - Complexity: O(count)
|
||||
public mutating func decrement(shiftedBy shift: Int = 0) {
|
||||
self.subtract(1, shiftedBy: shift)
|
||||
}
|
||||
|
||||
/// Subtract `b` from `a` and return the result.
|
||||
///
|
||||
/// - Requires: a >= b
|
||||
/// - Complexity: O(a.count)
|
||||
public static func -(a: CS.BigUInt, b: CS.BigUInt) -> CS.BigUInt {
|
||||
return a.subtracting(b)
|
||||
}
|
||||
|
||||
/// Subtract `b` from `a` and store the result in `a`.
|
||||
///
|
||||
/// - Requires: a >= b
|
||||
/// - Complexity: O(a.count)
|
||||
public static func -=(a: inout CS.BigUInt, b: CS.BigUInt) {
|
||||
a.subtract(b)
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
public mutating func negate() {
|
||||
guard !magnitude.isZero else { return }
|
||||
self.sign = self.sign == .plus ? .minus : .plus
|
||||
}
|
||||
|
||||
/// Subtract `b` from `a` and return the result.
|
||||
public static func -(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
|
||||
return a + -b
|
||||
}
|
||||
|
||||
/// Subtract `b` from `a` in place.
|
||||
public static func -=(a: inout CS.BigInt, b: CS.BigInt) { a = a - b }
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
//
|
||||
// Words and Bits.swift
|
||||
// CS.BigInt
|
||||
//
|
||||
// Created by Károly Lőrentey on 2017-08-11.
|
||||
// Copyright © 2016-2017 Károly Lőrentey.
|
||||
//
|
||||
|
||||
extension Array where Element == UInt {
|
||||
mutating func twosComplement() {
|
||||
var increment = true
|
||||
for i in 0 ..< self.count {
|
||||
if increment {
|
||||
(self[i], increment) = (~self[i]).addingReportingOverflow(1)
|
||||
}
|
||||
else {
|
||||
self[i] = ~self[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
public subscript(bitAt index: Int) -> Bool {
|
||||
get {
|
||||
precondition(index >= 0)
|
||||
let (i, j) = index.quotientAndRemainder(dividingBy: Word.bitWidth)
|
||||
return self[i] & (1 << j) != 0
|
||||
}
|
||||
set {
|
||||
precondition(index >= 0)
|
||||
let (i, j) = index.quotientAndRemainder(dividingBy: Word.bitWidth)
|
||||
if newValue {
|
||||
self[i] |= 1 << j
|
||||
}
|
||||
else {
|
||||
self[i] &= ~(1 << j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
/// The minimum number of bits required to represent this integer in binary.
|
||||
///
|
||||
/// - Returns: floor(log2(2 * self + 1))
|
||||
/// - Complexity: O(1)
|
||||
public var bitWidth: Int {
|
||||
guard count > 0 else { return 0 }
|
||||
return count * Word.bitWidth - self[count - 1].leadingZeroBitCount
|
||||
}
|
||||
|
||||
/// The number of leading zero bits in the binary representation of this integer in base `2^(Word.bitWidth)`.
|
||||
/// This is useful when you need to normalize a `BigUInt` such that the top bit of its most significant word is 1.
|
||||
///
|
||||
/// - Note: 0 is considered to have zero leading zero bits.
|
||||
/// - Returns: A value in `0...(Word.bitWidth - 1)`.
|
||||
/// - SeeAlso: width
|
||||
/// - Complexity: O(1)
|
||||
public var leadingZeroBitCount: Int {
|
||||
guard count > 0 else { return 0 }
|
||||
return self[count - 1].leadingZeroBitCount
|
||||
}
|
||||
|
||||
/// The number of trailing zero bits in the binary representation of this integer.
|
||||
///
|
||||
/// - Note: 0 is considered to have zero trailing zero bits.
|
||||
/// - Returns: A value in `0...width`.
|
||||
/// - Complexity: O(count)
|
||||
public var trailingZeroBitCount: Int {
|
||||
guard count > 0 else { return 0 }
|
||||
let i = self.words.firstIndex { $0 != 0 }!
|
||||
return i * Word.bitWidth + self[i].trailingZeroBitCount
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
public var bitWidth: Int {
|
||||
guard !magnitude.isZero else { return 0 }
|
||||
return magnitude.bitWidth + 1
|
||||
}
|
||||
|
||||
public var trailingZeroBitCount: Int {
|
||||
// Amazingly, this works fine for negative numbers
|
||||
return magnitude.trailingZeroBitCount
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigUInt {
|
||||
public struct Words: RandomAccessCollection {
|
||||
private let value: CS.BigUInt
|
||||
|
||||
fileprivate init(_ value: CS.BigUInt) { self.value = value }
|
||||
|
||||
public var startIndex: Int { return 0 }
|
||||
public var endIndex: Int { return value.count }
|
||||
|
||||
public subscript(_ index: Int) -> Word {
|
||||
return value[index]
|
||||
}
|
||||
}
|
||||
|
||||
public var words: Words { return Words(self) }
|
||||
|
||||
public init<Words: Sequence>(words: Words) where Words.Element == Word {
|
||||
let uc = words.underestimatedCount
|
||||
if uc > 2 {
|
||||
self.init(words: Array(words))
|
||||
}
|
||||
else {
|
||||
var it = words.makeIterator()
|
||||
guard let w0 = it.next() else {
|
||||
self.init()
|
||||
return
|
||||
}
|
||||
guard let w1 = it.next() else {
|
||||
self.init(word: w0)
|
||||
return
|
||||
}
|
||||
if let w2 = it.next() {
|
||||
var words: [UInt] = []
|
||||
words.reserveCapacity(Swift.max(3, uc))
|
||||
words.append(w0)
|
||||
words.append(w1)
|
||||
words.append(w2)
|
||||
while let word = it.next() {
|
||||
words.append(word)
|
||||
}
|
||||
self.init(words: words)
|
||||
}
|
||||
else {
|
||||
self.init(low: w0, high: w1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CS.BigInt {
|
||||
public struct Words: RandomAccessCollection {
|
||||
public typealias Indices = CountableRange<Int>
|
||||
|
||||
private let value: CS.BigInt
|
||||
private let decrementLimit: Int
|
||||
|
||||
fileprivate init(_ value: CS.BigInt) {
|
||||
self.value = value
|
||||
switch value.sign {
|
||||
case .plus:
|
||||
self.decrementLimit = 0
|
||||
case .minus:
|
||||
assert(!value.magnitude.isZero)
|
||||
self.decrementLimit = value.magnitude.words.firstIndex(where: { $0 != 0 })!
|
||||
}
|
||||
}
|
||||
|
||||
public var count: Int {
|
||||
switch value.sign {
|
||||
case .plus:
|
||||
if let high = value.magnitude.words.last, high >> (Word.bitWidth - 1) != 0 {
|
||||
return value.magnitude.count + 1
|
||||
}
|
||||
return value.magnitude.count
|
||||
case .minus:
|
||||
let high = value.magnitude.words.last!
|
||||
if high >> (Word.bitWidth - 1) != 0 {
|
||||
return value.magnitude.count + 1
|
||||
}
|
||||
return value.magnitude.count
|
||||
}
|
||||
}
|
||||
|
||||
public var indices: Indices { return 0 ..< count }
|
||||
public var startIndex: Int { return 0 }
|
||||
public var endIndex: Int { return count }
|
||||
|
||||
public subscript(_ index: Int) -> UInt {
|
||||
// Note that indices above `endIndex` are accepted.
|
||||
if value.sign == .plus {
|
||||
return value.magnitude[index]
|
||||
}
|
||||
if index <= decrementLimit {
|
||||
return ~(value.magnitude[index] &- 1)
|
||||
}
|
||||
return ~value.magnitude[index]
|
||||
}
|
||||
}
|
||||
|
||||
public var words: Words {
|
||||
return Words(self)
|
||||
}
|
||||
|
||||
public init<S: Sequence>(words: S) where S.Element == Word {
|
||||
var words = Array(words)
|
||||
if (words.last ?? 0) >> (Word.bitWidth - 1) == 0 {
|
||||
self.init(sign: .plus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
else {
|
||||
words.twosComplement()
|
||||
self.init(sign: .minus, magnitude: CS.BigUInt(words: words))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
// https://tools.ietf.org/html/rfc7539
|
||||
//
|
||||
|
||||
public final class ChaCha20: BlockCipher {
|
||||
public enum Error: Swift.Error {
|
||||
case invalidKeyOrInitializationVector
|
||||
case notSupported
|
||||
}
|
||||
|
||||
public static let blockSize = 64 // 512 / 8
|
||||
public let keySize: Int
|
||||
|
||||
fileprivate let key: Key
|
||||
fileprivate var counter: Array<UInt8>
|
||||
|
||||
public init(key: Array<UInt8>, iv nonce: Array<UInt8>) throws {
|
||||
precondition(nonce.count == 12 || nonce.count == 8)
|
||||
|
||||
if key.count != 32 {
|
||||
throw Error.invalidKeyOrInitializationVector
|
||||
}
|
||||
|
||||
self.key = Key(bytes: key)
|
||||
self.keySize = self.key.count
|
||||
|
||||
if nonce.count == 8 {
|
||||
self.counter = [0, 0, 0, 0, 0, 0, 0, 0] + nonce
|
||||
} else {
|
||||
self.counter = [0, 0, 0, 0] + nonce
|
||||
}
|
||||
|
||||
assert(self.counter.count == 16)
|
||||
}
|
||||
|
||||
/// https://tools.ietf.org/html/rfc7539#section-2.3.
|
||||
fileprivate func core(block: inout Array<UInt8>, counter: Array<UInt8>, key: Array<UInt8>) {
|
||||
precondition(block.count == ChaCha20.blockSize)
|
||||
precondition(counter.count == 16)
|
||||
precondition(key.count == 32)
|
||||
|
||||
let j0: UInt32 = 0x61707865
|
||||
let j1: UInt32 = 0x3320646e // 0x3620646e sigma/tau
|
||||
let j2: UInt32 = 0x79622d32
|
||||
let j3: UInt32 = 0x6b206574
|
||||
let j4: UInt32 = UInt32(bytes: key[0..<4]).bigEndian
|
||||
let j5: UInt32 = UInt32(bytes: key[4..<8]).bigEndian
|
||||
let j6: UInt32 = UInt32(bytes: key[8..<12]).bigEndian
|
||||
let j7: UInt32 = UInt32(bytes: key[12..<16]).bigEndian
|
||||
let j8: UInt32 = UInt32(bytes: key[16..<20]).bigEndian
|
||||
let j9: UInt32 = UInt32(bytes: key[20..<24]).bigEndian
|
||||
let j10: UInt32 = UInt32(bytes: key[24..<28]).bigEndian
|
||||
let j11: UInt32 = UInt32(bytes: key[28..<32]).bigEndian
|
||||
let j12: UInt32 = UInt32(bytes: counter[0..<4]).bigEndian
|
||||
let j13: UInt32 = UInt32(bytes: counter[4..<8]).bigEndian
|
||||
let j14: UInt32 = UInt32(bytes: counter[8..<12]).bigEndian
|
||||
let j15: UInt32 = UInt32(bytes: counter[12..<16]).bigEndian
|
||||
|
||||
var (x0, x1, x2, x3, x4, x5, x6, x7) = (j0, j1, j2, j3, j4, j5, j6, j7)
|
||||
var (x8, x9, x10, x11, x12, x13, x14, x15) = (j8, j9, j10, j11, j12, j13, j14, j15)
|
||||
|
||||
for _ in 0..<10 { // 20 rounds
|
||||
x0 = x0 &+ x4
|
||||
x12 ^= x0
|
||||
x12 = (x12 << 16) | (x12 >> 16)
|
||||
x8 = x8 &+ x12
|
||||
x4 ^= x8
|
||||
x4 = (x4 << 12) | (x4 >> 20)
|
||||
x0 = x0 &+ x4
|
||||
x12 ^= x0
|
||||
x12 = (x12 << 8) | (x12 >> 24)
|
||||
x8 = x8 &+ x12
|
||||
x4 ^= x8
|
||||
x4 = (x4 << 7) | (x4 >> 25)
|
||||
x1 = x1 &+ x5
|
||||
x13 ^= x1
|
||||
x13 = (x13 << 16) | (x13 >> 16)
|
||||
x9 = x9 &+ x13
|
||||
x5 ^= x9
|
||||
x5 = (x5 << 12) | (x5 >> 20)
|
||||
x1 = x1 &+ x5
|
||||
x13 ^= x1
|
||||
x13 = (x13 << 8) | (x13 >> 24)
|
||||
x9 = x9 &+ x13
|
||||
x5 ^= x9
|
||||
x5 = (x5 << 7) | (x5 >> 25)
|
||||
x2 = x2 &+ x6
|
||||
x14 ^= x2
|
||||
x14 = (x14 << 16) | (x14 >> 16)
|
||||
x10 = x10 &+ x14
|
||||
x6 ^= x10
|
||||
x6 = (x6 << 12) | (x6 >> 20)
|
||||
x2 = x2 &+ x6
|
||||
x14 ^= x2
|
||||
x14 = (x14 << 8) | (x14 >> 24)
|
||||
x10 = x10 &+ x14
|
||||
x6 ^= x10
|
||||
x6 = (x6 << 7) | (x6 >> 25)
|
||||
x3 = x3 &+ x7
|
||||
x15 ^= x3
|
||||
x15 = (x15 << 16) | (x15 >> 16)
|
||||
x11 = x11 &+ x15
|
||||
x7 ^= x11
|
||||
x7 = (x7 << 12) | (x7 >> 20)
|
||||
x3 = x3 &+ x7
|
||||
x15 ^= x3
|
||||
x15 = (x15 << 8) | (x15 >> 24)
|
||||
x11 = x11 &+ x15
|
||||
x7 ^= x11
|
||||
x7 = (x7 << 7) | (x7 >> 25)
|
||||
x0 = x0 &+ x5
|
||||
x15 ^= x0
|
||||
x15 = (x15 << 16) | (x15 >> 16)
|
||||
x10 = x10 &+ x15
|
||||
x5 ^= x10
|
||||
x5 = (x5 << 12) | (x5 >> 20)
|
||||
x0 = x0 &+ x5
|
||||
x15 ^= x0
|
||||
x15 = (x15 << 8) | (x15 >> 24)
|
||||
x10 = x10 &+ x15
|
||||
x5 ^= x10
|
||||
x5 = (x5 << 7) | (x5 >> 25)
|
||||
x1 = x1 &+ x6
|
||||
x12 ^= x1
|
||||
x12 = (x12 << 16) | (x12 >> 16)
|
||||
x11 = x11 &+ x12
|
||||
x6 ^= x11
|
||||
x6 = (x6 << 12) | (x6 >> 20)
|
||||
x1 = x1 &+ x6
|
||||
x12 ^= x1
|
||||
x12 = (x12 << 8) | (x12 >> 24)
|
||||
x11 = x11 &+ x12
|
||||
x6 ^= x11
|
||||
x6 = (x6 << 7) | (x6 >> 25)
|
||||
x2 = x2 &+ x7
|
||||
x13 ^= x2
|
||||
x13 = (x13 << 16) | (x13 >> 16)
|
||||
x8 = x8 &+ x13
|
||||
x7 ^= x8
|
||||
x7 = (x7 << 12) | (x7 >> 20)
|
||||
x2 = x2 &+ x7
|
||||
x13 ^= x2
|
||||
x13 = (x13 << 8) | (x13 >> 24)
|
||||
x8 = x8 &+ x13
|
||||
x7 ^= x8
|
||||
x7 = (x7 << 7) | (x7 >> 25)
|
||||
x3 = x3 &+ x4
|
||||
x14 ^= x3
|
||||
x14 = (x14 << 16) | (x14 >> 16)
|
||||
x9 = x9 &+ x14
|
||||
x4 ^= x9
|
||||
x4 = (x4 << 12) | (x4 >> 20)
|
||||
x3 = x3 &+ x4
|
||||
x14 ^= x3
|
||||
x14 = (x14 << 8) | (x14 >> 24)
|
||||
x9 = x9 &+ x14
|
||||
x4 ^= x9
|
||||
x4 = (x4 << 7) | (x4 >> 25)
|
||||
}
|
||||
|
||||
x0 = x0 &+ j0
|
||||
x1 = x1 &+ j1
|
||||
x2 = x2 &+ j2
|
||||
x3 = x3 &+ j3
|
||||
x4 = x4 &+ j4
|
||||
x5 = x5 &+ j5
|
||||
x6 = x6 &+ j6
|
||||
x7 = x7 &+ j7
|
||||
x8 = x8 &+ j8
|
||||
x9 = x9 &+ j9
|
||||
x10 = x10 &+ j10
|
||||
x11 = x11 &+ j11
|
||||
x12 = x12 &+ j12
|
||||
x13 = x13 &+ j13
|
||||
x14 = x14 &+ j14
|
||||
x15 = x15 &+ j15
|
||||
|
||||
block.replaceSubrange(0..<4, with: x0.bigEndian.bytes())
|
||||
block.replaceSubrange(4..<8, with: x1.bigEndian.bytes())
|
||||
block.replaceSubrange(8..<12, with: x2.bigEndian.bytes())
|
||||
block.replaceSubrange(12..<16, with: x3.bigEndian.bytes())
|
||||
block.replaceSubrange(16..<20, with: x4.bigEndian.bytes())
|
||||
block.replaceSubrange(20..<24, with: x5.bigEndian.bytes())
|
||||
block.replaceSubrange(24..<28, with: x6.bigEndian.bytes())
|
||||
block.replaceSubrange(28..<32, with: x7.bigEndian.bytes())
|
||||
block.replaceSubrange(32..<36, with: x8.bigEndian.bytes())
|
||||
block.replaceSubrange(36..<40, with: x9.bigEndian.bytes())
|
||||
block.replaceSubrange(40..<44, with: x10.bigEndian.bytes())
|
||||
block.replaceSubrange(44..<48, with: x11.bigEndian.bytes())
|
||||
block.replaceSubrange(48..<52, with: x12.bigEndian.bytes())
|
||||
block.replaceSubrange(52..<56, with: x13.bigEndian.bytes())
|
||||
block.replaceSubrange(56..<60, with: x14.bigEndian.bytes())
|
||||
block.replaceSubrange(60..<64, with: x15.bigEndian.bytes())
|
||||
}
|
||||
|
||||
// XORKeyStream
|
||||
func process(bytes: ArraySlice<UInt8>, counter: inout Array<UInt8>, key: Array<UInt8>) -> Array<UInt8> {
|
||||
precondition(counter.count == 16)
|
||||
precondition(key.count == 32)
|
||||
|
||||
var block = Array<UInt8>(repeating: 0, count: ChaCha20.blockSize)
|
||||
var bytesSlice = bytes
|
||||
var out = Array<UInt8>(reserveCapacity: bytesSlice.count)
|
||||
|
||||
while bytesSlice.count >= ChaCha20.blockSize {
|
||||
self.core(block: &block, counter: counter, key: key)
|
||||
for (i, x) in block.enumerated() {
|
||||
out.append(bytesSlice[bytesSlice.startIndex + i] ^ x)
|
||||
}
|
||||
var u: UInt32 = 1
|
||||
for i in 0..<4 {
|
||||
u += UInt32(counter[i])
|
||||
counter[i] = UInt8(u & 0xff)
|
||||
u >>= 8
|
||||
}
|
||||
bytesSlice = bytesSlice[bytesSlice.startIndex + ChaCha20.blockSize..<bytesSlice.endIndex]
|
||||
}
|
||||
|
||||
if !bytesSlice.isEmpty {
|
||||
self.core(block: &block, counter: counter, key: key)
|
||||
for (i, v) in bytesSlice.enumerated() {
|
||||
out.append(v ^ block[i])
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Cipher
|
||||
|
||||
extension ChaCha20: Cipher {
|
||||
public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
|
||||
self.process(bytes: bytes, counter: &self.counter, key: Array(self.key))
|
||||
}
|
||||
|
||||
public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
|
||||
try self.encrypt(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Encryptor
|
||||
|
||||
extension ChaCha20 {
|
||||
public struct ChaChaEncryptor: Cryptor, Updatable {
|
||||
private var accumulated = Array<UInt8>()
|
||||
private let chacha: ChaCha20
|
||||
|
||||
init(chacha: ChaCha20) {
|
||||
self.chacha = chacha
|
||||
}
|
||||
|
||||
public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
|
||||
self.accumulated += bytes
|
||||
|
||||
var encrypted = Array<UInt8>()
|
||||
encrypted.reserveCapacity(self.accumulated.count)
|
||||
for chunk in self.accumulated.batched(by: ChaCha20.blockSize) {
|
||||
if isLast || self.accumulated.count >= ChaCha20.blockSize {
|
||||
encrypted += try self.chacha.encrypt(chunk)
|
||||
self.accumulated.removeFirst(chunk.count) // TODO: improve performance
|
||||
}
|
||||
}
|
||||
return encrypted
|
||||
}
|
||||
|
||||
public func seek(to: Int) throws {
|
||||
throw Error.notSupported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Decryptor
|
||||
|
||||
extension ChaCha20 {
|
||||
public struct ChaChaDecryptor: Cryptor, Updatable {
|
||||
private var accumulated = Array<UInt8>()
|
||||
|
||||
private var offset: Int = 0
|
||||
private var offsetToRemove: Int = 0
|
||||
private let chacha: ChaCha20
|
||||
|
||||
init(chacha: ChaCha20) {
|
||||
self.chacha = chacha
|
||||
}
|
||||
|
||||
public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = true) throws -> Array<UInt8> {
|
||||
// prepend "offset" number of bytes at the beginning
|
||||
if self.offset > 0 {
|
||||
self.accumulated += Array<UInt8>(repeating: 0, count: self.offset) + bytes
|
||||
self.offsetToRemove = self.offset
|
||||
self.offset = 0
|
||||
} else {
|
||||
self.accumulated += bytes
|
||||
}
|
||||
|
||||
var plaintext = Array<UInt8>()
|
||||
plaintext.reserveCapacity(self.accumulated.count)
|
||||
for chunk in self.accumulated.batched(by: ChaCha20.blockSize) {
|
||||
if isLast || self.accumulated.count >= ChaCha20.blockSize {
|
||||
plaintext += try self.chacha.decrypt(chunk)
|
||||
|
||||
// remove "offset" from the beginning of first chunk
|
||||
if self.offsetToRemove > 0 {
|
||||
plaintext.removeFirst(self.offsetToRemove) // TODO: improve performance
|
||||
self.offsetToRemove = 0
|
||||
}
|
||||
|
||||
self.accumulated.removeFirst(chunk.count)
|
||||
}
|
||||
}
|
||||
|
||||
return plaintext
|
||||
}
|
||||
|
||||
public func seek(to: Int) throws {
|
||||
throw Error.notSupported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Cryptors
|
||||
|
||||
extension ChaCha20: Cryptors {
|
||||
//TODO: Use BlockEncryptor/BlockDecryptor
|
||||
|
||||
public func makeEncryptor() -> Cryptor & Updatable {
|
||||
ChaCha20.ChaChaEncryptor(chacha: self)
|
||||
}
|
||||
|
||||
public func makeDecryptor() -> Cryptor & Updatable {
|
||||
ChaCha20.ChaChaDecryptor(chacha: self)
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
/// CRC - cyclic redundancy check code.
|
||||
public final class Checksum {
|
||||
|
||||
@usableFromInline
|
||||
static let table32: Array<UInt32> = [
|
||||
0x0000_0000, 0x7707_3096, 0xEE0E_612C, 0x9909_51BA, 0x076D_C419, 0x706A_F48F, 0xE963_A535, 0x9E64_95A3,
|
||||
0x0EDB_8832, 0x79DC_B8A4, 0xE0D5_E91E, 0x97D2_D988, 0x09B6_4C2B, 0x7EB1_7CBD, 0xE7B8_2D07, 0x90BF_1D91,
|
||||
0x1DB7_1064, 0x6AB0_20F2, 0xF3B9_7148, 0x84BE_41DE, 0x1ADA_D47D, 0x6DDD_E4EB, 0xF4D4_B551, 0x83D3_85C7,
|
||||
0x136C_9856, 0x646B_A8C0, 0xFD62_F97A, 0x8A65_C9EC, 0x1401_5C4F, 0x6306_6CD9, 0xFA0F_3D63, 0x8D08_0DF5,
|
||||
0x3B6E_20C8, 0x4C69_105E, 0xD560_41E4, 0xA267_7172, 0x3C03_E4D1, 0x4B04_D447, 0xD20D_85FD, 0xA50A_B56B,
|
||||
0x35B5_A8FA, 0x42B2_986C, 0xDBBB_C9D6, 0xACBC_F940, 0x32D8_6CE3, 0x45DF_5C75, 0xDCD6_0DCF, 0xABD1_3D59,
|
||||
0x26D9_30AC, 0x51DE_003A, 0xC8D7_5180, 0xBFD0_6116, 0x21B4_F4B5, 0x56B3_C423, 0xCFBA_9599, 0xB8BD_A50F,
|
||||
0x2802_B89E, 0x5F05_8808, 0xC60C_D9B2, 0xB10B_E924, 0x2F6F_7C87, 0x5868_4C11, 0xC161_1DAB, 0xB666_2D3D,
|
||||
0x76DC_4190, 0x01DB_7106, 0x98D2_20BC, 0xEFD5_102A, 0x71B1_8589, 0x06B6_B51F, 0x9FBF_E4A5, 0xE8B8_D433,
|
||||
0x7807_C9A2, 0x0F00_F934, 0x9609_A88E, 0xE10E_9818, 0x7F6A_0DBB, 0x086D_3D2D, 0x9164_6C97, 0xE663_5C01,
|
||||
0x6B6B_51F4, 0x1C6C_6162, 0x8565_30D8, 0xF262_004E, 0x6C06_95ED, 0x1B01_A57B, 0x8208_F4C1, 0xF50F_C457,
|
||||
0x65B0_D9C6, 0x12B7_E950, 0x8BBE_B8EA, 0xFCB9_887C, 0x62DD_1DDF, 0x15DA_2D49, 0x8CD3_7CF3, 0xFBD4_4C65,
|
||||
0x4DB2_6158, 0x3AB5_51CE, 0xA3BC_0074, 0xD4BB_30E2, 0x4ADF_A541, 0x3DD8_95D7, 0xA4D1_C46D, 0xD3D6_F4FB,
|
||||
0x4369_E96A, 0x346E_D9FC, 0xAD67_8846, 0xDA60_B8D0, 0x4404_2D73, 0x3303_1DE5, 0xAA0A_4C5F, 0xDD0D_7CC9,
|
||||
0x5005_713C, 0x2702_41AA, 0xBE0B_1010, 0xC90C_2086, 0x5768_B525, 0x206F_85B3, 0xB966_D409, 0xCE61_E49F,
|
||||
0x5EDE_F90E, 0x29D9_C998, 0xB0D0_9822, 0xC7D7_A8B4, 0x59B3_3D17, 0x2EB4_0D81, 0xB7BD_5C3B, 0xC0BA_6CAD,
|
||||
0xEDB8_8320, 0x9ABF_B3B6, 0x03B6_E20C, 0x74B1_D29A, 0xEAD5_4739, 0x9DD2_77AF, 0x04DB_2615, 0x73DC_1683,
|
||||
0xE363_0B12, 0x9464_3B84, 0x0D6D_6A3E, 0x7A6A_5AA8, 0xE40E_CF0B, 0x9309_FF9D, 0x0A00_AE27, 0x7D07_9EB1,
|
||||
0xF00F_9344, 0x8708_A3D2, 0x1E01_F268, 0x6906_C2FE, 0xF762_575D, 0x8065_67CB, 0x196C_3671, 0x6E6B_06E7,
|
||||
0xFED4_1B76, 0x89D3_2BE0, 0x10DA_7A5A, 0x67DD_4ACC, 0xF9B9_DF6F, 0x8EBE_EFF9, 0x17B7_BE43, 0x60B0_8ED5,
|
||||
0xD6D6_A3E8, 0xA1D1_937E, 0x38D8_C2C4, 0x4FDF_F252, 0xD1BB_67F1, 0xA6BC_5767, 0x3FB5_06DD, 0x48B2_364B,
|
||||
0xD80D_2BDA, 0xAF0A_1B4C, 0x3603_4AF6, 0x4104_7A60, 0xDF60_EFC3, 0xA867_DF55, 0x316E_8EEF, 0x4669_BE79,
|
||||
0xCB61_B38C, 0xBC66_831A, 0x256F_D2A0, 0x5268_E236, 0xCC0C_7795, 0xBB0B_4703, 0x2202_16B9, 0x5505_262F,
|
||||
0xC5BA_3BBE, 0xB2BD_0B28, 0x2BB4_5A92, 0x5CB3_6A04, 0xC2D7_FFA7, 0xB5D0_CF31, 0x2CD9_9E8B, 0x5BDE_AE1D,
|
||||
0x9B64_C2B0, 0xEC63_F226, 0x756A_A39C, 0x026D_930A, 0x9C09_06A9, 0xEB0E_363F, 0x7207_6785, 0x0500_5713,
|
||||
0x95BF_4A82, 0xE2B8_7A14, 0x7BB1_2BAE, 0x0CB6_1B38, 0x92D2_8E9B, 0xE5D5_BE0D, 0x7CDC_EFB7, 0x0BDB_DF21,
|
||||
0x86D3_D2D4, 0xF1D4_E242, 0x68DD_B3F8, 0x1FDA_836E, 0x81BE_16CD, 0xF6B9_265B, 0x6FB0_77E1, 0x18B7_4777,
|
||||
0x8808_5AE6, 0xFF0F_6A70, 0x6606_3BCA, 0x1101_0B5C, 0x8F65_9EFF, 0xF862_AE69, 0x616B_FFD3, 0x166C_CF45,
|
||||
0xA00A_E278, 0xD70D_D2EE, 0x4E04_8354, 0x3903_B3C2, 0xA767_2661, 0xD060_16F7, 0x4969_474D, 0x3E6E_77DB,
|
||||
0xAED1_6A4A, 0xD9D6_5ADC, 0x40DF_0B66, 0x37D8_3BF0, 0xA9BC_AE53, 0xDEBB_9EC5, 0x47B2_CF7F, 0x30B5_FFE9,
|
||||
0xBDBD_F21C, 0xCABA_C28A, 0x53B3_9330, 0x24B4_A3A6, 0xBAD0_3605, 0xCDD7_0693, 0x54DE_5729, 0x23D9_67BF,
|
||||
0xB366_7A2E, 0xC461_4AB8, 0x5D68_1B02, 0x2A6F_2B94, 0xB40B_BE37, 0xC30C_8EA1, 0x5A05_DF1B, 0x2D02_EF8D
|
||||
]
|
||||
|
||||
@usableFromInline
|
||||
static let table32c: Array<UInt32> = [
|
||||
0x0000_0000, 0xF26B_8303, 0xE13B_70F7, 0x1350_F3F4, 0xC79A_971F, 0x35F1_141C, 0x26A1_E7E8, 0xD4CA_64EB,
|
||||
0x8AD9_58CF, 0x78B2_DBCC, 0x6BE2_2838, 0x9989_AB3B, 0x4D43_CFD0, 0xBF28_4CD3, 0xAC78_BF27, 0x5E13_3C24,
|
||||
0x105E_C76F, 0xE235_446C, 0xF165_B798, 0x030E_349B, 0xD7C4_5070, 0x25AF_D373, 0x36FF_2087, 0xC494_A384,
|
||||
0x9A87_9FA0, 0x68EC_1CA3, 0x7BBC_EF57, 0x89D7_6C54, 0x5D1D_08BF, 0xAF76_8BBC, 0xBC26_7848, 0x4E4D_FB4B,
|
||||
0x20BD_8EDE, 0xD2D6_0DDD, 0xC186_FE29, 0x33ED_7D2A, 0xE727_19C1, 0x154C_9AC2, 0x061C_6936, 0xF477_EA35,
|
||||
0xAA64_D611, 0x580F_5512, 0x4B5F_A6E6, 0xB934_25E5, 0x6DFE_410E, 0x9F95_C20D, 0x8CC5_31F9, 0x7EAE_B2FA,
|
||||
0x30E3_49B1, 0xC288_CAB2, 0xD1D8_3946, 0x23B3_BA45, 0xF779_DEAE, 0x0512_5DAD, 0x1642_AE59, 0xE429_2D5A,
|
||||
0xBA3A_117E, 0x4851_927D, 0x5B01_6189, 0xA96A_E28A, 0x7DA0_8661, 0x8FCB_0562, 0x9C9B_F696, 0x6EF0_7595,
|
||||
0x417B_1DBC, 0xB310_9EBF, 0xA040_6D4B, 0x522B_EE48, 0x86E1_8AA3, 0x748A_09A0, 0x67DA_FA54, 0x95B1_7957,
|
||||
0xCBA2_4573, 0x39C9_C670, 0x2A99_3584, 0xD8F2_B687, 0x0C38_D26C, 0xFE53_516F, 0xED03_A29B, 0x1F68_2198,
|
||||
0x5125_DAD3, 0xA34E_59D0, 0xB01E_AA24, 0x4275_2927, 0x96BF_4DCC, 0x64D4_CECF, 0x7784_3D3B, 0x85EF_BE38,
|
||||
0xDBFC_821C, 0x2997_011F, 0x3AC7_F2EB, 0xC8AC_71E8, 0x1C66_1503, 0xEE0D_9600, 0xFD5D_65F4, 0x0F36_E6F7,
|
||||
0x61C6_9362, 0x93AD_1061, 0x80FD_E395, 0x7296_6096, 0xA65C_047D, 0x5437_877E, 0x4767_748A, 0xB50C_F789,
|
||||
0xEB1F_CBAD, 0x1974_48AE, 0x0A24_BB5A, 0xF84F_3859, 0x2C85_5CB2, 0xDEEE_DFB1, 0xCDBE_2C45, 0x3FD5_AF46,
|
||||
0x7198_540D, 0x83F3_D70E, 0x90A3_24FA, 0x62C8_A7F9, 0xB602_C312, 0x4469_4011, 0x5739_B3E5, 0xA552_30E6,
|
||||
0xFB41_0CC2, 0x092A_8FC1, 0x1A7A_7C35, 0xE811_FF36, 0x3CDB_9BDD, 0xCEB0_18DE, 0xDDE0_EB2A, 0x2F8B_6829,
|
||||
0x82F6_3B78, 0x709D_B87B, 0x63CD_4B8F, 0x91A6_C88C, 0x456C_AC67, 0xB707_2F64, 0xA457_DC90, 0x563C_5F93,
|
||||
0x082F_63B7, 0xFA44_E0B4, 0xE914_1340, 0x1B7F_9043, 0xCFB5_F4A8, 0x3DDE_77AB, 0x2E8E_845F, 0xDCE5_075C,
|
||||
0x92A8_FC17, 0x60C3_7F14, 0x7393_8CE0, 0x81F8_0FE3, 0x5532_6B08, 0xA759_E80B, 0xB409_1BFF, 0x4662_98FC,
|
||||
0x1871_A4D8, 0xEA1A_27DB, 0xF94A_D42F, 0x0B21_572C, 0xDFEB_33C7, 0x2D80_B0C4, 0x3ED0_4330, 0xCCBB_C033,
|
||||
0xA24B_B5A6, 0x5020_36A5, 0x4370_C551, 0xB11B_4652, 0x65D1_22B9, 0x97BA_A1BA, 0x84EA_524E, 0x7681_D14D,
|
||||
0x2892_ED69, 0xDAF9_6E6A, 0xC9A9_9D9E, 0x3BC2_1E9D, 0xEF08_7A76, 0x1D63_F975, 0x0E33_0A81, 0xFC58_8982,
|
||||
0xB215_72C9, 0x407E_F1CA, 0x532E_023E, 0xA145_813D, 0x758F_E5D6, 0x87E4_66D5, 0x94B4_9521, 0x66DF_1622,
|
||||
0x38CC_2A06, 0xCAA7_A905, 0xD9F7_5AF1, 0x2B9C_D9F2, 0xFF56_BD19, 0x0D3D_3E1A, 0x1E6D_CDEE, 0xEC06_4EED,
|
||||
0xC38D_26C4, 0x31E6_A5C7, 0x22B6_5633, 0xD0DD_D530, 0x0417_B1DB, 0xF67C_32D8, 0xE52C_C12C, 0x1747_422F,
|
||||
0x4954_7E0B, 0xBB3F_FD08, 0xA86F_0EFC, 0x5A04_8DFF, 0x8ECE_E914, 0x7CA5_6A17, 0x6FF5_99E3, 0x9D9E_1AE0,
|
||||
0xD3D3_E1AB, 0x21B8_62A8, 0x32E8_915C, 0xC083_125F, 0x1449_76B4, 0xE622_F5B7, 0xF572_0643, 0x0719_8540,
|
||||
0x590A_B964, 0xAB61_3A67, 0xB831_C993, 0x4A5A_4A90, 0x9E90_2E7B, 0x6CFB_AD78, 0x7FAB_5E8C, 0x8DC0_DD8F,
|
||||
0xE330_A81A, 0x115B_2B19, 0x020B_D8ED, 0xF060_5BEE, 0x24AA_3F05, 0xD6C1_BC06, 0xC591_4FF2, 0x37FA_CCF1,
|
||||
0x69E9_F0D5, 0x9B82_73D6, 0x88D2_8022, 0x7AB9_0321, 0xAE73_67CA, 0x5C18_E4C9, 0x4F48_173D, 0xBD23_943E,
|
||||
0xF36E_6F75, 0x0105_EC76, 0x1255_1F82, 0xE03E_9C81, 0x34F4_F86A, 0xC69F_7B69, 0xD5CF_889D, 0x27A4_0B9E,
|
||||
0x79B7_37BA, 0x8BDC_B4B9, 0x988C_474D, 0x6AE7_C44E, 0xBE2D_A0A5, 0x4C46_23A6, 0x5F16_D052, 0xAD7D_5351
|
||||
]
|
||||
|
||||
@usableFromInline
|
||||
static let table16: Array<UInt16> = [
|
||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
|
||||
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
|
||||
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
|
||||
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
|
||||
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
|
||||
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
|
||||
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
|
||||
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
|
||||
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
|
||||
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
|
||||
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
|
||||
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
|
||||
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
|
||||
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
|
||||
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
|
||||
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
|
||||
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
|
||||
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
|
||||
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
|
||||
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
|
||||
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
|
||||
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
|
||||
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
|
||||
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
|
||||
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
|
||||
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
|
||||
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
|
||||
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
|
||||
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
|
||||
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
|
||||
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
|
||||
]
|
||||
|
||||
@usableFromInline
|
||||
init() {
|
||||
//
|
||||
}
|
||||
|
||||
/// Polynomial: 0xEDB88320 (Reversed) - IEEE
|
||||
@inlinable
|
||||
func crc32(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
|
||||
var crc: UInt32 = seed != nil ? seed! : 0xFFFF_FFFF
|
||||
for chunk in message.batched(by: 256) {
|
||||
for b in chunk {
|
||||
let idx = Int((crc ^ UInt32(reflect ? b : reversed(b))) & 0xFF)
|
||||
crc = (crc >> 8) ^ Checksum.table32[idx]
|
||||
}
|
||||
}
|
||||
return (reflect ? crc : reversed(crc)) ^ 0xFFFF_FFFF
|
||||
}
|
||||
|
||||
/// Polynomial: 0x82F63B78 (Reversed) - Castagnoli
|
||||
@inlinable
|
||||
func crc32c(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
|
||||
var crc: UInt32 = seed != nil ? seed! : 0xFFFF_FFFF
|
||||
for chunk in message.batched(by: 256) {
|
||||
for b in chunk {
|
||||
let idx = Int((crc ^ UInt32(reflect ? b : reversed(b))) & 0xFF)
|
||||
crc = (crc >> 8) ^ Checksum.table32c[idx]
|
||||
}
|
||||
}
|
||||
return (reflect ? crc : reversed(crc)) ^ 0xFFFF_FFFF
|
||||
}
|
||||
|
||||
/// Polynomial: 0xA001 (Reversed) - IBM
|
||||
@inlinable
|
||||
func crc16(_ message: Array<UInt8>, seed: UInt16? = nil) -> UInt16 {
|
||||
var crc: UInt16 = seed != nil ? seed! : 0x0000
|
||||
for chunk in message.batched(by: 256) {
|
||||
for b in chunk {
|
||||
crc = (crc >> 8) ^ Checksum.table16[Int((crc ^ UInt16(b)) & 0xFF)]
|
||||
}
|
||||
}
|
||||
return crc
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public interface
|
||||
|
||||
public extension Checksum {
|
||||
/// Calculate CRC32.
|
||||
///
|
||||
/// - parameter message: Message
|
||||
/// - parameter seed: Seed value (Optional)
|
||||
/// - parameter reflect: is reflect (default true)
|
||||
///
|
||||
/// - returns: Calculated code
|
||||
@inlinable
|
||||
static func crc32(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
|
||||
Checksum().crc32(message, seed: seed, reflect: reflect)
|
||||
}
|
||||
|
||||
/// Calculate CRC32C
|
||||
///
|
||||
/// - parameter message: Message
|
||||
/// - parameter seed: Seed value (Optional)
|
||||
/// - parameter reflect: is reflect (default true)
|
||||
///
|
||||
/// - returns: Calculated code
|
||||
@inlinable
|
||||
static func crc32c(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
|
||||
Checksum().crc32c(message, seed: seed, reflect: reflect)
|
||||
}
|
||||
|
||||
/// Calculate CRC16
|
||||
///
|
||||
/// - parameter message: Message
|
||||
/// - parameter seed: Seed value (Optional)
|
||||
///
|
||||
/// - returns: Calculated code
|
||||
@inlinable
|
||||
static func crc16(_ message: Array<UInt8>, seed: UInt16? = nil) -> UInt16 {
|
||||
Checksum().crc16(message, seed: seed)
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
public enum CipherError: Error {
|
||||
case encrypt
|
||||
case decrypt
|
||||
}
|
||||
|
||||
public protocol Cipher: AnyObject {
|
||||
var keySize: Int { get }
|
||||
|
||||
/// Encrypt given bytes at once
|
||||
///
|
||||
/// - parameter bytes: Plaintext data
|
||||
/// - returns: Encrypted data
|
||||
func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8>
|
||||
func encrypt(_ bytes: Array<UInt8>) throws -> Array<UInt8>
|
||||
|
||||
/// Decrypt given bytes at once
|
||||
///
|
||||
/// - parameter bytes: Ciphertext data
|
||||
/// - returns: Plaintext data
|
||||
func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8>
|
||||
func decrypt(_ bytes: Array<UInt8>) throws -> Array<UInt8>
|
||||
}
|
||||
|
||||
extension Cipher {
|
||||
public func encrypt(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
|
||||
try self.encrypt(bytes.slice)
|
||||
}
|
||||
|
||||
public func decrypt(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
|
||||
try self.decrypt(bytes.slice)
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
extension Collection where Self.Element == UInt8, Self.Index == Int {
|
||||
// Big endian order
|
||||
@inlinable
|
||||
func toUInt32Array() -> Array<UInt32> {
|
||||
guard !isEmpty else {
|
||||
return []
|
||||
}
|
||||
|
||||
let c = strideCount(from: startIndex, to: endIndex, by: 4)
|
||||
return Array<UInt32>(unsafeUninitializedCapacity: c) { buf, count in
|
||||
var counter = 0
|
||||
for idx in stride(from: startIndex, to: endIndex, by: 4) {
|
||||
let val = UInt32(bytes: self, fromIndex: idx).bigEndian
|
||||
buf[counter] = val
|
||||
counter += 1
|
||||
}
|
||||
count = counter
|
||||
assert(counter == c)
|
||||
}
|
||||
}
|
||||
|
||||
// Big endian order
|
||||
@inlinable
|
||||
func toUInt64Array() -> Array<UInt64> {
|
||||
guard !isEmpty else {
|
||||
return []
|
||||
}
|
||||
|
||||
let c = strideCount(from: startIndex, to: endIndex, by: 8)
|
||||
return Array<UInt64>(unsafeUninitializedCapacity: c) { buf, count in
|
||||
var counter = 0
|
||||
for idx in stride(from: startIndex, to: endIndex, by: 8) {
|
||||
let val = UInt64(bytes: self, fromIndex: idx).bigEndian
|
||||
buf[counter] = val
|
||||
counter += 1
|
||||
}
|
||||
count = counter
|
||||
assert(counter == c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func strideCount(from: Int, to: Int, by: Int) -> Int {
|
||||
let count = to - from
|
||||
return count / by + (count % by > 0 ? 1 : 0)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
#if swift(>=4.1)
|
||||
// TODO: remove this file when Xcode 9.2 is no longer used
|
||||
#else
|
||||
extension Sequence {
|
||||
@inlinable
|
||||
public func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
|
||||
try flatMap(transform)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,22 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
/// Cryptor (Encryptor or Decryptor)
|
||||
public protocol Cryptor {
|
||||
/// Seek to position in file, if block mode allows random access.
|
||||
///
|
||||
/// - parameter to: new value of counter
|
||||
mutating func seek(to: Int) throws
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
#if canImport(Darwin)
|
||||
import Darwin
|
||||
#elseif canImport(Glibc)
|
||||
import Glibc
|
||||
#elseif canImport(ucrt)
|
||||
import ucrt
|
||||
#endif
|
||||
|
||||
/// Worker cryptor/decryptor of `Updatable` types
|
||||
public protocol Cryptors: AnyObject {
|
||||
|
||||
/// Cryptor suitable for encryption
|
||||
func makeEncryptor() throws -> Cryptor & Updatable
|
||||
|
||||
/// Cryptor suitable for decryption
|
||||
func makeDecryptor() throws -> Cryptor & Updatable
|
||||
|
||||
/// Generate array of random bytes. Helper function.
|
||||
static func randomIV(_ blockSize: Int) -> Array<UInt8>
|
||||
}
|
||||
|
||||
extension Cryptors {
|
||||
/// Generate array of random values.
|
||||
/// Convenience helper that uses `Swift.RandomNumberGenerator`.
|
||||
/// - Parameter count: Length of array
|
||||
public static func randomIV(_ count: Int) -> Array<UInt8> {
|
||||
(0..<count).map({ _ in UInt8.random(in: 0...UInt8.max) })
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
@available(*, renamed: "Digest")
|
||||
public typealias Hash = Digest
|
||||
|
||||
/// Hash functions to calculate Digest.
|
||||
public struct Digest {
|
||||
/// Calculate MD5 Digest
|
||||
/// - parameter bytes: input message
|
||||
/// - returns: Digest bytes
|
||||
public static func md5(_ bytes: Array<UInt8>) -> Array<UInt8> {
|
||||
MD5().calculate(for: bytes)
|
||||
}
|
||||
|
||||
/// Calculate SHA1 Digest
|
||||
/// - parameter bytes: input message
|
||||
/// - returns: Digest bytes
|
||||
public static func sha1(_ bytes: Array<UInt8>) -> Array<UInt8> {
|
||||
SHA1().calculate(for: bytes)
|
||||
}
|
||||
|
||||
/// Calculate SHA2-224 Digest
|
||||
/// - parameter bytes: input message
|
||||
/// - returns: Digest bytes
|
||||
public static func sha224(_ bytes: Array<UInt8>) -> Array<UInt8> {
|
||||
self.sha2(bytes, variant: .sha224)
|
||||
}
|
||||
|
||||
/// Calculate SHA2-256 Digest
|
||||
/// - parameter bytes: input message
|
||||
/// - returns: Digest bytes
|
||||
public static func sha256(_ bytes: Array<UInt8>) -> Array<UInt8> {
|
||||
self.sha2(bytes, variant: .sha256)
|
||||
}
|
||||
|
||||
/// Calculate SHA2-384 Digest
|
||||
/// - parameter bytes: input message
|
||||
/// - returns: Digest bytes
|
||||
public static func sha384(_ bytes: Array<UInt8>) -> Array<UInt8> {
|
||||
self.sha2(bytes, variant: .sha384)
|
||||
}
|
||||
|
||||
/// Calculate SHA2-512 Digest
|
||||
/// - parameter bytes: input message
|
||||
/// - returns: Digest bytes
|
||||
public static func sha512(_ bytes: Array<UInt8>) -> Array<UInt8> {
|
||||
self.sha2(bytes, variant: .sha512)
|
||||
}
|
||||
|
||||
/// Calculate SHA2 Digest
|
||||
/// - parameter bytes: input message
|
||||
/// - parameter variant: SHA-2 variant
|
||||
/// - returns: Digest bytes
|
||||
public static func sha2(_ bytes: Array<UInt8>, variant: SHA2.Variant) -> Array<UInt8> {
|
||||
SHA2(variant: variant).calculate(for: bytes)
|
||||
}
|
||||
|
||||
/// Calculate SHA3 Digest
|
||||
/// - parameter bytes: input message
|
||||
/// - parameter variant: SHA-3 variant
|
||||
/// - returns: Digest bytes
|
||||
public static func sha3(_ bytes: Array<UInt8>, variant: SHA3.Variant) -> Array<UInt8> {
|
||||
SHA3(variant: variant).calculate(for: bytes)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
internal protocol DigestType {
|
||||
func calculate(for bytes: Array<UInt8>) -> Array<UInt8>
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension AES {
|
||||
/// Initialize with CBC block mode.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - key: Key as a String.
|
||||
/// - iv: IV as a String.
|
||||
/// - padding: Padding
|
||||
/// - Throws: Error
|
||||
///
|
||||
/// The input is a String, that is treat as sequence of bytes made directly out of String.
|
||||
/// If input is Base64 encoded data (which is a String technically) it is not decoded automatically for you.
|
||||
public convenience init(key: String, iv: String, padding: Padding = .pkcs7) throws {
|
||||
try self.init(key: key.bytes, blockMode: CBC(iv: iv.bytes), padding: padding)
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// CryptoSwift
|
||||
//
|
||||
// Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension Array where Element == UInt8 {
|
||||
func toBase64() -> String {
|
||||
Data(self).base64EncodedString()
|
||||
}
|
||||
|
||||
init(base64: String) {
|
||||
self.init()
|
||||
|
||||
guard let decodedData = Data(base64Encoded: base64) else {
|
||||
return
|
||||
}
|
||||
|
||||
append(contentsOf: decodedData.bytes)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user