-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from AreroKetahi/AES
AES Encryption
- Loading branch information
Showing
8 changed files
with
535 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// | ||
// AES.swift | ||
// | ||
// | ||
// Created by Akivili Collindort on 2023/8/25. | ||
// | ||
|
||
import Foundation | ||
import SwCrypt | ||
|
||
/// Advanced Encryption Standard | ||
final class AES { | ||
internal static func encrypt( | ||
data: AESRawValue, | ||
key: AESKey, | ||
iv: AESIV, | ||
blockMode: CC.BlockMode = .cbc, | ||
padding: CC.Padding = .pkcs7Padding | ||
) throws -> AESEncryptedValue { | ||
let data = try CC.crypt(.encrypt, blockMode: blockMode, algorithm: .aes, padding: padding, data: data.rawValue, key: key.rawValue, iv: iv.rawValue) | ||
return AESEncryptedValue(data) | ||
} | ||
|
||
internal static func decrypt( | ||
data: AESEncryptedValue, | ||
key: AESKey, | ||
iv: AESIV, | ||
blockMode: CC.BlockMode = .cbc, | ||
padding: CC.Padding = .pkcs7Padding | ||
) throws -> AESRawValue { | ||
let data = try CC.crypt(.decrypt, blockMode: blockMode, algorithm: .aes, padding: padding, data: data.rawValue, key: key.rawValue, iv: iv.rawValue) | ||
return AESRawValue(data) | ||
} | ||
|
||
public static func generateRandomKey(keySize: Int = 32) throws -> AESKey { | ||
let rawValue = CC.generateRandom(keySize) | ||
guard let key = AESKey(rawValue) else { | ||
throw AESError.invaildGeneration | ||
} | ||
return key | ||
} | ||
|
||
public static func generateRandomIV() throws -> AESIV { | ||
let rawValue = CC.generateRandom(16) | ||
guard let iv = AESIV(rawValue) else { | ||
throw AESError.invaildGeneration | ||
} | ||
return iv | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// | ||
// AESEncryptedValue.swift | ||
// | ||
// | ||
// Created by Akivili Collindort on 2023/8/25. | ||
// | ||
|
||
import Foundation | ||
import SwCrypt | ||
|
||
/// Encrypted AES value | ||
struct AESEncryptedValue { | ||
private var data: Data | ||
} | ||
|
||
// Initializers | ||
extension AESEncryptedValue { | ||
/// Initialize from raw data | ||
/// - Parameter data: Raw data | ||
public init(_ data: Data) { | ||
self.data = data | ||
} | ||
|
||
/// Initialize AES excrypted value from Base-64 encoded string | ||
/// - Parameters: | ||
/// - base64Encoded: Base-64 encoded string | ||
/// - options: The options to use for the decoding. Default value is `[]`. | ||
public init?(base64Encoded: String, options: Data.Base64DecodingOptions = []) { | ||
if let data = Data(base64Encoded: base64Encoded, options: options) { | ||
self.data = data | ||
} else { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
// Values | ||
extension AESEncryptedValue { | ||
internal var rawValue: Data { | ||
data | ||
} | ||
} | ||
|
||
// Functions | ||
extension AESEncryptedValue { | ||
/// Return a Base-64 Encoded AES encrypted value | ||
/// - Parameter options: The options to use for the encoding. Default value is []. | ||
/// - Returns: Base-64 encoded AES encrypted value | ||
public func base64EncodedString(options: Data.Base64EncodingOptions = []) -> String { | ||
rawValue.base64EncodedString(options: options) | ||
} | ||
|
||
/// Decrypt | ||
/// - Parameters: | ||
/// - key: AES key | ||
/// - iv: AES initialization vector | ||
/// - blockMode: Block mode, `.cbc` for default | ||
/// - padding: Padding, `.pkcs7Padding` for default | ||
/// - Returns: Raw AES value | ||
public func decrypt( | ||
key: AESKey, | ||
iv: AESIV, | ||
blockMode: CC.BlockMode = .cbc, | ||
padding: CC.Padding = .pkcs7Padding | ||
) throws -> AESRawValue { | ||
try AES.decrypt(data: self, key: key, iv: iv, blockMode: blockMode, padding: padding) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// | ||
// AESError.swift | ||
// | ||
// | ||
// Created by Akivili Collindort on 2023/8/25. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Error that may occur in AES cipher | ||
enum AESError: Error { | ||
case unmatchedLength(Int), unexpectedHexString(String) | ||
case invaildGeneration | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// | ||
// AESIV.swift | ||
// | ||
// | ||
// Created by Akivili Collindort on 2023/8/25. | ||
// | ||
|
||
import Foundation | ||
|
||
/// AES Initialization Vector | ||
struct AESIV { | ||
private var data: Data | ||
|
||
/// Generate IV from raw data | ||
/// - Parameter data: IV data | ||
public init?(_ data: Data) { | ||
if data.count == 16 { | ||
self.data = data | ||
} else { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
// Initializers | ||
extension AESIV { | ||
/// Generate a empty IV | ||
/// Use this initializer when performing encryption without a specified IV. | ||
public init() { | ||
self.data = Data() | ||
} | ||
|
||
/// Generate IV from hexadecimal string | ||
/// - Parameters: | ||
/// - hexString: Hexadecimal string | ||
/// - length: IV length, for AES is 16 | ||
public init(fromHexString hexString: String, length: Int = 16) throws { | ||
if hexString.count != length * 2 { | ||
throw AESError.unmatchedLength(length) | ||
} | ||
var hexString = hexString | ||
var result = [UInt8]() | ||
while !hexString.isEmpty { | ||
let sub = hexString.prefix(2) | ||
guard let byte = UInt8(sub, radix: 16) else { | ||
throw AESError.unexpectedHexString(String(sub)) | ||
} | ||
result.append(byte) | ||
hexString = String(hexString.dropFirst(2)) | ||
} | ||
self.data = Data(result) | ||
} | ||
|
||
/// Generate IV from base-64 encoded string | ||
/// - Parameter string: Base-64 encoded string | ||
public init?(base64Encoded string: String) { | ||
if let data = Data(base64Encoded: string) { | ||
self.data = data | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
// Values | ||
extension AESIV { | ||
internal var rawValue: Data { | ||
data | ||
} | ||
/// A Boolean value indicating whether the IV is empty. | ||
public var isEmpty: Bool { | ||
self.rawValue.isEmpty | ||
} | ||
} | ||
|
||
// Functions | ||
extension AESIV { | ||
/// Get hexadecimal string | ||
/// - Returns: Hexadecimal string | ||
public func toHexString() -> String { | ||
var result = "" | ||
self.rawValue.forEach { element in | ||
result += String(format: "%02x", element) | ||
} | ||
return result | ||
} | ||
|
||
/// Get base-64 encoded string | ||
/// - Returns: Base-64 encoded string | ||
public func base64EncodedString() -> String { | ||
self.rawValue.base64EncodedString() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// | ||
// AESKey.swift | ||
// | ||
// | ||
// Created by Akivili Collindort on 2023/8/25. | ||
// | ||
|
||
import Foundation | ||
import SwCrypt | ||
|
||
/// AES Key | ||
struct AESKey { | ||
private var data: Data | ||
} | ||
|
||
// Initializers | ||
extension AESKey { | ||
/// Generate a empty key | ||
/// Use this initializer when performing encryption without a specified key. | ||
public init() { | ||
self.data = Data() | ||
} | ||
|
||
/// Create AES key from raw data | ||
/// - Parameter data: AES key data | ||
public init?(_ data: Data) { | ||
if [16, 24, 32].contains(data.count) { | ||
self.data = data | ||
} else { | ||
return nil | ||
} | ||
} | ||
|
||
/// Create AES key from hexadecimal string | ||
/// String length will effect what AES method that will use | ||
/// - Parameters: | ||
/// - hexString: Hexadecimal string | ||
/// - length: Passworf length | ||
public init(fromHexString hexString: String, length: Int = 32) throws { | ||
if hexString.count != length * 2 { | ||
throw AESError.unmatchedLength(length) | ||
} | ||
var hexString = hexString | ||
var result = [UInt8]() | ||
while !hexString.isEmpty { | ||
let sub = hexString.prefix(2) | ||
guard let byte = UInt8(sub, radix: 16) else { | ||
throw AESError.unexpectedHexString(String(sub)) | ||
} | ||
result.append(byte) | ||
hexString = String(hexString.dropFirst(2)) | ||
} | ||
self.data = Data(result) | ||
} | ||
|
||
public init?(_ key: String) { | ||
guard let key = key.data(using: .utf8) else { | ||
return nil | ||
} | ||
let digested = CC.digest(key, alg: .sha256) | ||
self.data = digested | ||
} | ||
|
||
/// Create AES key from base-64 encoded string | ||
/// - Parameter string: Base-64 encoded string | ||
public init?(base64Encoded string: String) { | ||
guard let data = Data(base64Encoded: string) else { | ||
return nil | ||
} | ||
self.data = data | ||
} | ||
} | ||
|
||
// Values | ||
extension AESKey { | ||
internal var rawValue: Data { | ||
data | ||
} | ||
|
||
/// A Boolean value indicating whether the Key is empty. | ||
public var isEmpty: Bool { | ||
self.rawValue.isEmpty | ||
} | ||
} | ||
|
||
// Functions | ||
extension AESKey { | ||
/// Get hexadecimal key | ||
/// - Returns: Hexadecimal key | ||
public func toHexString() -> String { | ||
var result = "" | ||
self.rawValue.forEach { element in | ||
result += String(format: "%02x", element) | ||
} | ||
return result | ||
} | ||
|
||
/// Get base-64 encoded key | ||
/// - Returns: Base-64 encoded key | ||
public func base64EncodedString() -> String { | ||
self.rawValue.base64EncodedString() | ||
} | ||
} |
Oops, something went wrong.