SHA3.swift 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. //
  2. // CryptoSwift
  3. //
  4. // Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
  5. // This software is provided 'as-is', without any express or implied warranty.
  6. //
  7. // In no event will the authors be held liable for any damages arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
  10. //
  11. // - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
  12. // - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  13. // - This notice may not be removed or altered from any source or binary distribution.
  14. //
  15. // http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
  16. // http://keccak.noekeon.org/specs_summary.html
  17. //
  18. #if canImport(Darwin)
  19. import Darwin
  20. #elseif canImport(Glibc)
  21. import Glibc
  22. #elseif canImport(Musl)
  23. import Musl
  24. #elseif canImport(ucrt)
  25. import ucrt
  26. #endif
  27. public final class SHA3: DigestType {
  28. let round_constants: Array<UInt64> = [
  29. 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
  30. 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
  31. 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
  32. 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
  33. 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
  34. 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
  35. ]
  36. public let blockSize: Int
  37. public let digestLength: Int
  38. public let markByte: UInt8
  39. @usableFromInline
  40. var accumulated = Array<UInt8>()
  41. @usableFromInline
  42. var accumulatedHash: Array<UInt64>
  43. public enum Variant {
  44. case sha224, sha256, sha384, sha512, keccak224, keccak256, keccak384, keccak512
  45. var digestLength: Int {
  46. 100 - (self.blockSize / 2)
  47. }
  48. var blockSize: Int {
  49. (1600 - self.outputLength * 2) / 8
  50. }
  51. var markByte: UInt8 {
  52. switch self {
  53. case .sha224, .sha256, .sha384, .sha512:
  54. return 0x06 // 0x1F for SHAKE
  55. case .keccak224, .keccak256, .keccak384, .keccak512:
  56. return 0x01
  57. }
  58. }
  59. public var outputLength: Int {
  60. switch self {
  61. case .sha224, .keccak224:
  62. return 224
  63. case .sha256, .keccak256:
  64. return 256
  65. case .sha384, .keccak384:
  66. return 384
  67. case .sha512, .keccak512:
  68. return 512
  69. }
  70. }
  71. }
  72. public init(variant: SHA3.Variant) {
  73. self.blockSize = variant.blockSize
  74. self.digestLength = variant.digestLength
  75. self.markByte = variant.markByte
  76. self.accumulatedHash = Array<UInt64>(repeating: 0, count: self.digestLength)
  77. }
  78. @inlinable
  79. public func calculate(for bytes: Array<UInt8>) -> Array<UInt8> {
  80. do {
  81. return try update(withBytes: bytes.slice, isLast: true)
  82. } catch {
  83. return []
  84. }
  85. }
  86. public func callAsFunction(_ bytes: Array<UInt8>) -> Array<UInt8> {
  87. calculate(for: bytes)
  88. }
  89. /// 1. For all pairs (x,z) such that 0≤x<5 and 0≤z<w, let
  90. /// C[x,z]=A[x, 0,z] ⊕ A[x, 1,z] ⊕ A[x, 2,z] ⊕ A[x, 3,z] ⊕ A[x, 4,z].
  91. /// 2. For all pairs (x, z) such that 0≤x<5 and 0≤z<w let
  92. /// D[x,z]=C[(x1) mod 5, z] ⊕ C[(x+1) mod 5, (z –1) mod w].
  93. /// 3. For all triples (x, y, z) such that 0≤x<5, 0≤y<5, and 0≤z<w, let
  94. /// A′[x, y,z] = A[x, y,z] ⊕ D[x,z].
  95. private func θ(_ a: inout Array<UInt64>) {
  96. let c = UnsafeMutablePointer<UInt64>.allocate(capacity: 5)
  97. c.initialize(repeating: 0, count: 5)
  98. defer {
  99. c.deinitialize(count: 5)
  100. c.deallocate()
  101. }
  102. let d = UnsafeMutablePointer<UInt64>.allocate(capacity: 5)
  103. d.initialize(repeating: 0, count: 5)
  104. defer {
  105. d.deinitialize(count: 5)
  106. d.deallocate()
  107. }
  108. for i in 0..<5 {
  109. c[i] = a[i] ^ a[i &+ 5] ^ a[i &+ 10] ^ a[i &+ 15] ^ a[i &+ 20]
  110. }
  111. d[0] = rotateLeft(c[1], by: 1) ^ c[4]
  112. d[1] = rotateLeft(c[2], by: 1) ^ c[0]
  113. d[2] = rotateLeft(c[3], by: 1) ^ c[1]
  114. d[3] = rotateLeft(c[4], by: 1) ^ c[2]
  115. d[4] = rotateLeft(c[0], by: 1) ^ c[3]
  116. for i in 0..<5 {
  117. a[i] ^= d[i]
  118. a[i &+ 5] ^= d[i]
  119. a[i &+ 10] ^= d[i]
  120. a[i &+ 15] ^= d[i]
  121. a[i &+ 20] ^= d[i]
  122. }
  123. }
  124. /// A′[x, y, z]=A[(x &+ 3y) mod 5, x, z]
  125. private func π(_ a: inout Array<UInt64>) {
  126. let a1 = a[1]
  127. a[1] = a[6]
  128. a[6] = a[9]
  129. a[9] = a[22]
  130. a[22] = a[14]
  131. a[14] = a[20]
  132. a[20] = a[2]
  133. a[2] = a[12]
  134. a[12] = a[13]
  135. a[13] = a[19]
  136. a[19] = a[23]
  137. a[23] = a[15]
  138. a[15] = a[4]
  139. a[4] = a[24]
  140. a[24] = a[21]
  141. a[21] = a[8]
  142. a[8] = a[16]
  143. a[16] = a[5]
  144. a[5] = a[3]
  145. a[3] = a[18]
  146. a[18] = a[17]
  147. a[17] = a[11]
  148. a[11] = a[7]
  149. a[7] = a[10]
  150. a[10] = a1
  151. }
  152. /// For all triples (x, y, z) such that 0≤x<5, 0≤y<5, and 0≤z<w, let
  153. /// A′[x, y,z] = A[x, y,z] ⊕ ((A[(x+1) mod 5, y, z] ⊕ 1) ⋅ A[(x+2) mod 5, y, z])
  154. private func χ(_ a: inout Array<UInt64>) {
  155. for i in stride(from: 0, to: 25, by: 5) {
  156. let a0 = a[0 &+ i]
  157. let a1 = a[1 &+ i]
  158. a[0 &+ i] ^= ~a1 & a[2 &+ i]
  159. a[1 &+ i] ^= ~a[2 &+ i] & a[3 &+ i]
  160. a[2 &+ i] ^= ~a[3 &+ i] & a[4 &+ i]
  161. a[3 &+ i] ^= ~a[4 &+ i] & a0
  162. a[4 &+ i] ^= ~a0 & a1
  163. }
  164. }
  165. private func ι(_ a: inout Array<UInt64>, round: Int) {
  166. a[0] ^= self.round_constants[round]
  167. }
  168. @usableFromInline
  169. func process(block chunk: ArraySlice<UInt64>, currentHash hh: inout Array<UInt64>) {
  170. // expand
  171. hh[0] ^= chunk[0].littleEndian
  172. hh[1] ^= chunk[1].littleEndian
  173. hh[2] ^= chunk[2].littleEndian
  174. hh[3] ^= chunk[3].littleEndian
  175. hh[4] ^= chunk[4].littleEndian
  176. hh[5] ^= chunk[5].littleEndian
  177. hh[6] ^= chunk[6].littleEndian
  178. hh[7] ^= chunk[7].littleEndian
  179. hh[8] ^= chunk[8].littleEndian
  180. if self.blockSize > 72 { // 72 / 8, sha-512
  181. hh[9] ^= chunk[9].littleEndian
  182. hh[10] ^= chunk[10].littleEndian
  183. hh[11] ^= chunk[11].littleEndian
  184. hh[12] ^= chunk[12].littleEndian
  185. if self.blockSize > 104 { // 104 / 8, sha-384
  186. hh[13] ^= chunk[13].littleEndian
  187. hh[14] ^= chunk[14].littleEndian
  188. hh[15] ^= chunk[15].littleEndian
  189. hh[16] ^= chunk[16].littleEndian
  190. if self.blockSize > 136 { // 136 / 8, sha-256
  191. hh[17] ^= chunk[17].littleEndian
  192. // FULL_SHA3_FAMILY_SUPPORT
  193. if self.blockSize > 144 { // 144 / 8, sha-224
  194. hh[18] ^= chunk[18].littleEndian
  195. hh[19] ^= chunk[19].littleEndian
  196. hh[20] ^= chunk[20].littleEndian
  197. hh[21] ^= chunk[21].littleEndian
  198. hh[22] ^= chunk[22].littleEndian
  199. hh[23] ^= chunk[23].littleEndian
  200. hh[24] ^= chunk[24].littleEndian
  201. }
  202. }
  203. }
  204. }
  205. // Keccak-f
  206. for round in 0..<24 {
  207. self.θ(&hh)
  208. hh[1] = rotateLeft(hh[1], by: 1)
  209. hh[2] = rotateLeft(hh[2], by: 62)
  210. hh[3] = rotateLeft(hh[3], by: 28)
  211. hh[4] = rotateLeft(hh[4], by: 27)
  212. hh[5] = rotateLeft(hh[5], by: 36)
  213. hh[6] = rotateLeft(hh[6], by: 44)
  214. hh[7] = rotateLeft(hh[7], by: 6)
  215. hh[8] = rotateLeft(hh[8], by: 55)
  216. hh[9] = rotateLeft(hh[9], by: 20)
  217. hh[10] = rotateLeft(hh[10], by: 3)
  218. hh[11] = rotateLeft(hh[11], by: 10)
  219. hh[12] = rotateLeft(hh[12], by: 43)
  220. hh[13] = rotateLeft(hh[13], by: 25)
  221. hh[14] = rotateLeft(hh[14], by: 39)
  222. hh[15] = rotateLeft(hh[15], by: 41)
  223. hh[16] = rotateLeft(hh[16], by: 45)
  224. hh[17] = rotateLeft(hh[17], by: 15)
  225. hh[18] = rotateLeft(hh[18], by: 21)
  226. hh[19] = rotateLeft(hh[19], by: 8)
  227. hh[20] = rotateLeft(hh[20], by: 18)
  228. hh[21] = rotateLeft(hh[21], by: 2)
  229. hh[22] = rotateLeft(hh[22], by: 61)
  230. hh[23] = rotateLeft(hh[23], by: 56)
  231. hh[24] = rotateLeft(hh[24], by: 14)
  232. self.π(&hh)
  233. self.χ(&hh)
  234. self.ι(&hh, round: round)
  235. }
  236. }
  237. }
  238. extension SHA3: Updatable {
  239. @inlinable
  240. public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
  241. self.accumulated += bytes
  242. if isLast {
  243. // Add padding
  244. let markByteIndex = self.accumulated.count
  245. // We need to always pad the input. Even if the input is a multiple of blockSize.
  246. let r = self.blockSize * 8
  247. let q = (r / 8) - (accumulated.count % (r / 8))
  248. self.accumulated += Array<UInt8>(repeating: 0, count: q)
  249. self.accumulated[markByteIndex] |= self.markByte
  250. self.accumulated[self.accumulated.count - 1] |= 0x80
  251. }
  252. var processedBytes = 0
  253. for chunk in self.accumulated.batched(by: self.blockSize) {
  254. if isLast || (self.accumulated.count - processedBytes) >= self.blockSize {
  255. self.process(block: chunk.toUInt64Array().slice, currentHash: &self.accumulatedHash)
  256. processedBytes += chunk.count
  257. }
  258. }
  259. self.accumulated.removeFirst(processedBytes)
  260. // TODO: verify performance, reduce vs for..in
  261. let result = self.accumulatedHash.reduce(into: Array<UInt8>()) { (result, value) in
  262. result += value.bigEndian.bytes()
  263. }
  264. // reset hash value for instance
  265. if isLast {
  266. self.accumulatedHash = Array<UInt64>(repeating: 0, count: self.digestLength)
  267. }
  268. return Array(result[0..<self.digestLength])
  269. }
  270. }