-
Notifications
You must be signed in to change notification settings - Fork 131
/
Copy pathHashAlgorithms.swift
251 lines (213 loc) · 8.3 KB
/
HashAlgorithms.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
This source file is part of the Swift.org open source project
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
#if canImport(CryptoKit)
import CryptoKit
#endif
public protocol HashAlgorithm: Sendable {
/// Hashes the input bytes, returning the digest.
///
/// - Parameters:
/// - bytes: The input bytes.
/// - Returns: The output digest.
func hash(_ bytes: ByteString) -> ByteString
}
extension HashAlgorithm {
public func hash(_ string: String) -> ByteString {
hash(ByteString([UInt8](string.utf8)))
}
}
/// 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, Sendable {
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
/// The size of each blocks (in bits).
private static let blockBitSize = 512
/// The initial hash value.
private static let initialHashValue: [UInt32] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]
/// The constants in the algorithm (K).
private static let konstants: [UInt32] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]
public init() {
}
public func hash(_ bytes: ByteString) -> ByteString {
var input = bytes.contents
// Pad the input.
pad(&input)
// Break the input into N 512-bit blocks.
let messageBlocks = input.blocks(size: Self.blockBitSize / 8)
/// The hash that is being computed.
var hash = Self.initialHashValue
// Process each block.
for block in messageBlocks {
process(block, hash: &hash)
}
// Finally, compute the result.
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)
result[pos + 1] = UInt8((element >> 16) & 0xff)
result[pos + 2] = UInt8((element >> 8) & 0xff)
result[pos + 3] = UInt8(element & 0xff)
}
return ByteString(result)
}
/// Process and compute hash from a block.
private func process(_ block: ArraySlice<UInt8>, hash: inout [UInt32]) {
// Compute message schedule.
var W = [UInt32](repeating: 0, count: Self.konstants.count)
for t in 0..<W.count {
switch t {
case 0...15:
let index = block.startIndex.advanced(by: t * 4)
// Put 4 bytes in each message.
W[t] = UInt32(block[index + 0]) << 24
W[t] |= UInt32(block[index + 1]) << 16
W[t] |= UInt32(block[index + 2]) << 8
W[t] |= UInt32(block[index + 3])
default:
let σ1 = W[t-2].rotateRight(by: 17) ^ W[t-2].rotateRight(by: 19) ^ (W[t-2] >> 10)
let σ0 = W[t-15].rotateRight(by: 7) ^ W[t-15].rotateRight(by: 18) ^ (W[t-15] >> 3)
W[t] = σ1 &+ W[t-7] &+ σ0 &+ W[t-16]
}
}
var a = hash[0]
var b = hash[1]
var c = hash[2]
var d = hash[3]
var e = hash[4]
var f = hash[5]
var g = hash[6]
var h = hash[7]
// Run the main algorithm.
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 &+ 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)
let t2 = Σ0 &+ maj
h = g
g = f
f = e
e = d &+ t1
d = c
c = b
b = a
a = t1 &+ t2
}
hash[0] = a &+ hash[0]
hash[1] = b &+ hash[1]
hash[2] = c &+ hash[2]
hash[3] = d &+ hash[3]
hash[4] = e &+ hash[4]
hash[5] = f &+ hash[5]
hash[6] = g &+ hash[6]
hash[7] = h &+ hash[7]
}
/// Pad the given byte array to be a multiple of 512 bits.
private func pad(_ input: inout [UInt8]) {
// Find the bit count of input.
let inputBitLength = input.count * 8
// Append the bit 1 at end of input.
input.append(0x80)
// Find the number of bits we need to append.
//
// inputBitLength + 1 + bitsToAppend ≡ 448 mod 512
let mod = inputBitLength % 512
let bitsToAppend = mod < 448 ? 448 - 1 - mod : 512 + 448 - mod - 1
// We already appended first 7 bits with 0x80 above.
input += [UInt8](repeating: 0, count: (bitsToAppend - 7) / 8)
// We need to append 64 bits of input length.
for byte in UInt64(inputBitLength).toByteArray().lazy.reversed() {
input.append(byte)
}
assert((input.count * 8) % 512 == 0, "Expected padded length to be 512.")
}
}
#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, Sendable {
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 {
return bytes.withData { data in
let digest = CryptoKit.SHA256.hash(data: data)
return ByteString(digest)
}
}
}
#endif
// MARK:- Helpers
private extension UInt64 {
/// Converts the 64 bit integer into an array of single byte integers.
func toByteArray() -> [UInt8] {
var value = self.littleEndian
return withUnsafeBytes(of: &value, Array.init)
}
}
private extension UInt32 {
/// Rotates self by given amount.
func rotateRight(by amount: UInt32) -> UInt32 {
return (self >> amount) | (self << (32 - amount))
}
}
private extension Array {
/// Breaks the array into the given size.
func blocks(size: Int) -> AnyIterator<ArraySlice<Element>> {
var currentIndex = startIndex
return AnyIterator {
if let nextIndex = self.index(currentIndex, offsetBy: size, limitedBy: self.endIndex) {
defer { currentIndex = nextIndex }
return self[currentIndex..<nextIndex]
}
return nil
}
}
}