Skip to content

Commit 4ac04c4

Browse files
authored
abstract SHA256 implementaiton differences behind a single implementation (swiftlang#274)
motivation: reduce boilerplate on call sites changes: * rename the internal implementation of SHA256 to InternalSHA256 and make it internal * rename the CryptoKit implementation of SHA256 to _CryptoKitSHA256 and make it internal * create a public SHA256 that delegates to CryptoKit / Internal based on platform availability * mark CryptoKitSHA256 as deprecated encouraging users to move to the SHA256 abstraction * adjust and add tests
1 parent 3b5b1e5 commit 4ac04c4

File tree

2 files changed

+51
-15
lines changed

2 files changed

+51
-15
lines changed

Sources/TSCBasic/HashAlgorithms.swift

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,29 @@ extension HashAlgorithm {
3030

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

37+
public init() {
38+
#if canImport(CryptoKit)
39+
if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) {
40+
self.underlying = _CryptoKitSHA256()
41+
} else {
42+
self.underlying = InternalSHA256()
43+
}
44+
#else
45+
self.underlying = InternalSHA256()
46+
#endif
47+
}
48+
public func hash(_ bytes: ByteString) -> ByteString {
49+
self.underlying.hash(bytes)
50+
}
51+
}
52+
53+
/// SHA-256 implementation from Secure Hash Algorithm 2 (SHA-2) set of
54+
/// cryptographic hash functions (FIPS PUB 180-2).
55+
struct InternalSHA256: HashAlgorithm {
3556
/// The length of the output digest (in bits).
3657
private static let digestLength = 256
3758

@@ -65,18 +86,18 @@ public struct SHA256: HashAlgorithm {
6586
pad(&input)
6687

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

7091
/// The hash that is being computed.
71-
var hash = SHA256.initalHashValue
92+
var hash = Self.initalHashValue
7293

7394
// Process each block.
7495
for block in messageBlocks {
7596
process(block, hash: &hash)
7697
}
7798

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

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

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

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

176-
/// Wraps CryptoKit.SHA256 to provide a HashAlgorithm conformance to it.
197+
#if canImport(CryptoKit)
198+
@available(*, deprecated, message: "use SHA256 which abstract over platform differences")
177199
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
178200
public struct CryptoKitSHA256: HashAlgorithm {
179-
public init() {
201+
let underlying = _CryptoKitSHA256()
202+
public init() {}
203+
public func hash(_ bytes: ByteString) -> ByteString {
204+
self.underlying.hash(bytes)
180205
}
206+
}
181207

208+
/// Wraps CryptoKit.SHA256 to provide a HashAlgorithm conformance to it.
209+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
210+
struct _CryptoKitSHA256: HashAlgorithm {
211+
public init() {
212+
}
182213
public func hash(_ bytes: ByteString) -> ByteString {
183-
#if canImport(CryptoKit)
184214
return bytes.withData { data in
185215
let digest = CryptoKit.SHA256.hash(data: data)
186216
return ByteString(digest)
187217
}
188-
#else
189-
fatalError("not supported on this platform")
190-
#endif
191218
}
192219
}
220+
#endif
193221

194222
// MARK:- Helpers
195223

Tests/TSCBasicTests/SHA256Tests.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11+
@testable import TSCBasic
1112
import XCTest
1213

13-
import TSCBasic
14-
1514
class SHA256Tests: XCTestCase {
1615

1716
func testBasics() throws {
@@ -47,11 +46,20 @@ class SHA256Tests: XCTestCase {
4746
#if canImport(Darwin)
4847
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
4948
func testCryptoKitSHA256() {
50-
let sha = CryptoKitSHA256()
49+
let sha = _CryptoKitSHA256()
5150
XCTAssertEqual(
5251
sha.hash(ByteString("The quick brown fox jumps over the lazy dog")).hexadecimalRepresentation,
5352
"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
5453
)
5554
}
5655
#endif
56+
57+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
58+
func testInternalSHA256() {
59+
let sha = InternalSHA256()
60+
XCTAssertEqual(
61+
sha.hash(ByteString("The quick brown fox jumps over the lazy dog")).hexadecimalRepresentation,
62+
"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
63+
)
64+
}
5765
}

0 commit comments

Comments
 (0)