Skip to content

abstract SHA256 implementaiton differences behind a single implementation #274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 22, 2021
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
52 changes: 40 additions & 12 deletions Sources/TSCBasic/HashAlgorithms.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,29 @@ extension HashAlgorithm {

/// SHA-256 implementation from Secure Hash Algorithm 2 (SHA-2) set of
/// cryptographic hash functions (FIPS PUB 180-2).
/// Uses CryptoKit where available
public struct SHA256: HashAlgorithm {
private let underlying: HashAlgorithm

public init() {
#if canImport(CryptoKit)
if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) {
self.underlying = _CryptoKitSHA256()
} else {
self.underlying = InternalSHA256()
}
#else
self.underlying = InternalSHA256()
#endif
}
public func hash(_ bytes: ByteString) -> ByteString {
self.underlying.hash(bytes)
}
}

/// SHA-256 implementation from Secure Hash Algorithm 2 (SHA-2) set of
/// cryptographic hash functions (FIPS PUB 180-2).
struct InternalSHA256: HashAlgorithm {
/// The length of the output digest (in bits).
private static let digestLength = 256

Expand Down Expand Up @@ -65,18 +86,18 @@ public struct SHA256: HashAlgorithm {
pad(&input)

// Break the input into N 512-bit blocks.
let messageBlocks = input.blocks(size: SHA256.blockBitSize / 8)
let messageBlocks = input.blocks(size: Self.blockBitSize / 8)

/// The hash that is being computed.
var hash = SHA256.initalHashValue
var hash = Self.initalHashValue

// Process each block.
for block in messageBlocks {
process(block, hash: &hash)
}

// Finally, compute the result.
var result = [UInt8](repeating: 0, count: SHA256.digestLength / 8)
var result = [UInt8](repeating: 0, count: Self.digestLength / 8)
for (idx, element) in hash.enumerated() {
let pos = idx * 4
result[pos + 0] = UInt8((element >> 24) & 0xff)
Expand All @@ -92,7 +113,7 @@ public struct SHA256: HashAlgorithm {
private func process(_ block: ArraySlice<UInt8>, hash: inout [UInt32]) {

// Compute message schedule.
var W = [UInt32](repeating: 0, count: SHA256.konstants.count)
var W = [UInt32](repeating: 0, count: Self.konstants.count)
for t in 0..<W.count {
switch t {
case 0...15:
Expand All @@ -119,10 +140,10 @@ public struct SHA256: HashAlgorithm {
var h = hash[7]

// Run the main algorithm.
for t in 0..<SHA256.konstants.count {
for t in 0..<Self.konstants.count {
let Σ1 = e.rotateRight(by: 6) ^ e.rotateRight(by: 11) ^ e.rotateRight(by: 25)
let ch = (e & f) ^ (~e & g)
let t1 = h &+ Σ1 &+ ch &+ SHA256.konstants[t] &+ W[t]
let t1 = h &+ Σ1 &+ ch &+ Self.konstants[t] &+ W[t]

let Σ0 = a.rotateRight(by: 2) ^ a.rotateRight(by: 13) ^ a.rotateRight(by: 22)
let maj = (a & b) ^ (a & c) ^ (b & c)
Expand Down Expand Up @@ -173,23 +194,30 @@ public struct SHA256: HashAlgorithm {
}
}

/// Wraps CryptoKit.SHA256 to provide a HashAlgorithm conformance to it.
#if canImport(CryptoKit)
@available(*, deprecated, message: "use SHA256 which abstract over platform differences")
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct CryptoKitSHA256: HashAlgorithm {
public init() {
let underlying = _CryptoKitSHA256()
public init() {}
public func hash(_ bytes: ByteString) -> ByteString {
self.underlying.hash(bytes)
}
}

/// Wraps CryptoKit.SHA256 to provide a HashAlgorithm conformance to it.
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
struct _CryptoKitSHA256: HashAlgorithm {
public init() {
}
public func hash(_ bytes: ByteString) -> ByteString {
#if canImport(CryptoKit)
return bytes.withData { data in
let digest = CryptoKit.SHA256.hash(data: data)
return ByteString(digest)
}
#else
fatalError("not supported on this platform")
#endif
}
}
#endif

// MARK:- Helpers

Expand Down
14 changes: 11 additions & 3 deletions Tests/TSCBasicTests/SHA256Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

@testable import TSCBasic
import XCTest

import TSCBasic

class SHA256Tests: XCTestCase {

func testBasics() throws {
Expand Down Expand Up @@ -47,11 +46,20 @@ class SHA256Tests: XCTestCase {
#if canImport(Darwin)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
func testCryptoKitSHA256() {
let sha = CryptoKitSHA256()
let sha = _CryptoKitSHA256()
XCTAssertEqual(
sha.hash(ByteString("The quick brown fox jumps over the lazy dog")).hexadecimalRepresentation,
"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
)
}
#endif

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
func testInternalSHA256() {
let sha = InternalSHA256()
XCTAssertEqual(
sha.hash(ByteString("The quick brown fox jumps over the lazy dog")).hexadecimalRepresentation,
"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
)
}
}