SHA1.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. public final class SHA1: DigestType {
  16. @usableFromInline
  17. static let digestLength: Int = 20 // 160 / 8
  18. @usableFromInline
  19. static let blockSize: Int = 64
  20. @usableFromInline
  21. static let hashInitialValue: ContiguousArray<UInt32> = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]
  22. @usableFromInline
  23. var accumulated = Array<UInt8>()
  24. @usableFromInline
  25. var processedBytesTotalCount: Int = 0
  26. @usableFromInline
  27. var accumulatedHash: ContiguousArray<UInt32> = SHA1.hashInitialValue
  28. public init() {
  29. }
  30. @inlinable
  31. public func calculate(for bytes: Array<UInt8>) -> Array<UInt8> {
  32. do {
  33. return try update(withBytes: bytes.slice, isLast: true)
  34. } catch {
  35. return []
  36. }
  37. }
  38. public func callAsFunction(_ bytes: Array<UInt8>) -> Array<UInt8> {
  39. calculate(for: bytes)
  40. }
  41. @usableFromInline
  42. func process(block chunk: ArraySlice<UInt8>, currentHash hh: inout ContiguousArray<UInt32>) {
  43. // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15, big-endian
  44. // Extend the sixteen 32-bit words into eighty 32-bit words:
  45. let M = UnsafeMutablePointer<UInt32>.allocate(capacity: 80)
  46. M.initialize(repeating: 0, count: 80)
  47. defer {
  48. M.deinitialize(count: 80)
  49. M.deallocate()
  50. }
  51. for x in 0..<80 {
  52. switch x {
  53. case 0...15:
  54. let start = chunk.startIndex.advanced(by: x * 4) // * MemoryLayout<UInt32>.size
  55. M[x] = UInt32(bytes: chunk, fromIndex: start)
  56. default:
  57. M[x] = rotateLeft(M[x - 3] ^ M[x - 8] ^ M[x - 14] ^ M[x - 16], by: 1)
  58. }
  59. }
  60. var A = hh[0]
  61. var B = hh[1]
  62. var C = hh[2]
  63. var D = hh[3]
  64. var E = hh[4]
  65. // Main loop
  66. for j in 0...79 {
  67. var f: UInt32 = 0
  68. var k: UInt32 = 0
  69. switch j {
  70. case 0...19:
  71. f = (B & C) | ((~B) & D)
  72. k = 0x5a827999
  73. case 20...39:
  74. f = B ^ C ^ D
  75. k = 0x6ed9eba1
  76. case 40...59:
  77. f = (B & C) | (B & D) | (C & D)
  78. k = 0x8f1bbcdc
  79. case 60...79:
  80. f = B ^ C ^ D
  81. k = 0xca62c1d6
  82. default:
  83. break
  84. }
  85. let temp = rotateLeft(A, by: 5) &+ f &+ E &+ M[j] &+ k
  86. E = D
  87. D = C
  88. C = rotateLeft(B, by: 30)
  89. B = A
  90. A = temp
  91. }
  92. hh[0] = hh[0] &+ A
  93. hh[1] = hh[1] &+ B
  94. hh[2] = hh[2] &+ C
  95. hh[3] = hh[3] &+ D
  96. hh[4] = hh[4] &+ E
  97. }
  98. }
  99. extension SHA1: Updatable {
  100. @discardableResult @inlinable
  101. public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
  102. self.accumulated += bytes
  103. if isLast {
  104. let lengthInBits = (processedBytesTotalCount + self.accumulated.count) * 8
  105. let lengthBytes = lengthInBits.bytes(totalBytes: 64 / 8) // A 64-bit representation of b
  106. // Step 1. Append padding
  107. bitPadding(to: &self.accumulated, blockSize: SHA1.blockSize, allowance: 64 / 8)
  108. // Step 2. Append Length a 64-bit representation of lengthInBits
  109. self.accumulated += lengthBytes
  110. }
  111. var processedBytes = 0
  112. for chunk in self.accumulated.batched(by: SHA1.blockSize) {
  113. if isLast || (self.accumulated.count - processedBytes) >= SHA1.blockSize {
  114. self.process(block: chunk, currentHash: &self.accumulatedHash)
  115. processedBytes += chunk.count
  116. }
  117. }
  118. self.accumulated.removeFirst(processedBytes)
  119. self.processedBytesTotalCount += processedBytes
  120. // output current hash
  121. var result = Array<UInt8>(repeating: 0, count: SHA1.digestLength)
  122. var pos = 0
  123. for idx in 0..<self.accumulatedHash.count {
  124. let h = self.accumulatedHash[idx]
  125. result[pos + 0] = UInt8((h >> 24) & 0xff)
  126. result[pos + 1] = UInt8((h >> 16) & 0xff)
  127. result[pos + 2] = UInt8((h >> 8) & 0xff)
  128. result[pos + 3] = UInt8(h & 0xff)
  129. pos += 4
  130. }
  131. // reset hash value for instance
  132. if isLast {
  133. self.accumulatedHash = SHA1.hashInitialValue
  134. }
  135. return result
  136. }
  137. }