Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ All patterns and algorithms follow consistent implementation guidelines for main
- **Merkle Tree** - Hash tree for efficient data verification
- **Bloom Filter** - Probabilistic data structure for membership testing
- **Counting Bloom Filter** - Bloom Filter variant that supports element removal
- **Hash Algorithms** - SHA-256 and extensible hash algorithm protocol
- **Hash Computation** - Unified cryptographic hash functions (SHA-256, SHA-1, MD5, CRC32)

## Requirements

Expand Down Expand Up @@ -288,6 +288,30 @@ countingFilter.insert("item1")
countingFilter.remove("item1")
```

### Hash Computation

```swift
import DesignAlgorithmsKit

// Compute SHA256 hash
let data = "Hello, World!".data(using: .utf8)!
let hash = try HashComputation.computeHash(data: data, algorithm: .sha256)

// Get hash as hex string
let hexHash = try HashComputation.computeHashHex(data: data, algorithm: .sha256)
// Result: "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"

// Use string algorithm names
let sha1Hash = try HashComputation.computeHashHex(data: data, algorithm: "sha1")

// Convenience Data extensions
let quickHash = data.sha256Hex

// Supported algorithms: SHA-256, SHA-1, MD5, CRC32
let md5 = try HashComputation.computeHashHex(data: data, algorithm: .md5)
let crc = HashComputation.computeCRC32(data: data)
```

## Architecture

DesignAlgorithmsKit is organized into modules:
Expand All @@ -298,7 +322,7 @@ DesignAlgorithmsKit is organized into modules:
- **Behavioral** - Behavioral design patterns
- **Algorithms** - Algorithms and data structures
- **DataStructures** - Merkle Tree and other data structures
- **Hashing** - Hash algorithms and protocols
- **Cryptography** - Hash computation (SHA-256, SHA-1, MD5, CRC32)
- **Modern** - Modern patterns and extensions

## Thread Safety
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//
// HashComputation.swift
// DesignAlgorithmsKit
//
// Cryptographic hash computation utilities.
// Supports SHA256, SHA1, MD5, and CRC32 algorithms.
//

import Foundation
#if canImport(CryptoKit)
import CryptoKit
#endif
#if canImport(CommonCrypto)
import CommonCrypto
#endif

/// Supported hash algorithms
public enum HashAlgorithm: String, Sendable, CaseIterable {
case sha256 = "sha256"
case sha1 = "sha1"
case md5 = "md5"
case crc32 = "crc32"
}

/// Hash computation error
public enum HashComputationError: Error, LocalizedError {
case algorithmNotSupported(String)
case computationFailed(String)

public var errorDescription: String? {
switch self {
case .algorithmNotSupported(let algorithm):
return "Hash algorithm '\(algorithm)' is not supported on this platform"
case .computationFailed(let message):
return "Hash computation failed: \(message)"
}
}
}

/// Cryptographic hash computation utilities
///
/// Provides unified hash computation across platforms using CryptoKit when available,
/// falling back to CommonCrypto on older platforms.
///
/// **Example**:
/// ```swift
/// let data = "Hello, World!".data(using: .utf8)!
/// let hash = try HashComputation.computeHash(data: data, algorithm: .sha256)
/// let hex = try HashComputation.computeHashHex(data: data, algorithm: .sha256)
/// ```
public enum HashComputation {

/// Compute hash and return as Data
/// - Parameters:
/// - data: Data to hash
/// - algorithm: Hash algorithm to use
/// - Returns: Hash as Data
/// - Throws: HashComputationError if hashing fails or algorithm is unsupported
public static func computeHash(data: Data, algorithm: HashAlgorithm) throws -> Data {
#if canImport(CryptoKit)
let digest: any Digest
switch algorithm {
case .sha256:
digest = SHA256.hash(data: data)
case .sha1:
digest = Insecure.SHA1.hash(data: data)
case .md5:
digest = Insecure.MD5.hash(data: data)
case .crc32:
// CRC32 not in CryptoKit, use custom implementation
return computeCRC32(data: data)
}
return Data(digest)
#elseif canImport(CommonCrypto)
switch algorithm {
case .sha256:
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes { bytes in
_ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest)
}
return Data(digest)
case .sha1:
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes { bytes in
_ = CC_SHA1(bytes.baseAddress, CC_LONG(data.count), &digest)
}
return Data(digest)
case .md5:
// MD5 kept for legacy compatibility (companion files, existing checksums)
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
data.withUnsafeBytes { bytes in
digest.withUnsafeMutableBytes { digestBytes in
_ = CC_MD5(bytes.baseAddress, CC_LONG(data.count), digestBytes.baseAddress)
}
}
return Data(digest)
case .crc32:
return computeCRC32(data: data)
}
#else
throw HashComputationError.algorithmNotSupported(algorithm.rawValue)
#endif
}

/// Compute hash and return as hex string (lowercase)
/// - Parameters:
/// - data: Data to hash
/// - algorithm: Hash algorithm to use
/// - Returns: Hash as hex string (lowercase, no separators)
/// - Throws: HashComputationError if hashing fails or algorithm is unsupported
public static func computeHashHex(data: Data, algorithm: HashAlgorithm) throws -> String {
let hashData = try computeHash(data: data, algorithm: algorithm)
return hashData.map { String(format: "%02x", $0) }.joined()
}

/// Compute hash and return as hex string (lowercase) from algorithm name string
/// - Parameters:
/// - data: Data to hash
/// - algorithm: Hash algorithm name (e.g., "sha256", "sha1", "md5")
/// - Returns: Hash as hex string (lowercase, no separators)
/// - Throws: HashComputationError if hashing fails or algorithm is unsupported
public static func computeHashHex(data: Data, algorithm: String) throws -> String {
guard let hashAlgorithm = HashAlgorithm(rawValue: algorithm.lowercased()) else {
throw HashComputationError.algorithmNotSupported(algorithm)
}
return try computeHashHex(data: data, algorithm: hashAlgorithm)
}

/// Compute CRC32 checksum
/// - Parameter data: Data to checksum
/// - Returns: CRC32 as Data (4 bytes, big-endian)
public static func computeCRC32(data: Data) -> Data {
var crc: UInt32 = 0xFFFFFFFF
for byte in data {
crc = crc32Table[Int((crc ^ UInt32(byte)) & 0xFF)] ^ (crc >> 8)
}
crc = crc ^ 0xFFFFFFFF
return withUnsafeBytes(of: crc.bigEndian) { Data($0) }
}

// MARK: - Private Helpers

/// CRC32 lookup table
private static let crc32Table: [UInt32] = {
var table: [UInt32] = []
for i in 0..<256 {
var crc = UInt32(i)
for _ in 0..<8 {
crc = (crc & 1) != 0 ? (crc >> 1) ^ 0xEDB88320 : crc >> 1
}
table.append(crc)
}
return table
}()
}

// MARK: - Convenience Extensions

extension Data {
/// Compute SHA256 hash of this data
public var sha256: Data {
(try? HashComputation.computeHash(data: self, algorithm: .sha256)) ?? Data()
}

/// Compute SHA256 hash and return as hex string
public var sha256Hex: String {
(try? HashComputation.computeHashHex(data: self, algorithm: .sha256)) ?? ""
}
}
Loading