Skip to content

Commit

Permalink
Merge pull request #1 from AreroKetahi/AES
Browse files Browse the repository at this point in the history
AES Encryption
  • Loading branch information
AreroKetahi authored Aug 25, 2023
2 parents 01e01f3 + bb17d8d commit 7fc747e
Show file tree
Hide file tree
Showing 8 changed files with 535 additions and 1 deletion.
50 changes: 50 additions & 0 deletions Sources/SwiftlyCrypto/AES/AES.swift
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
}
}
68 changes: 68 additions & 0 deletions Sources/SwiftlyCrypto/AES/AESEncryptedValue.swift
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)
}
}
14 changes: 14 additions & 0 deletions Sources/SwiftlyCrypto/AES/AESError.swift
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
}
92 changes: 92 additions & 0 deletions Sources/SwiftlyCrypto/AES/AESIV.swift
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()
}
}
103 changes: 103 additions & 0 deletions Sources/SwiftlyCrypto/AES/AESKey.swift
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()
}
}
Loading

0 comments on commit 7fc747e

Please sign in to comment.