SHA1.swift 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. //
  2. // SHA1.swift
  3. // CryptoSwift
  4. //
  5. // Created by Marcin Krzyzanowski on 16/08/14.
  6. // Copyright (c) 2014 Marcin Krzyzanowski. All rights reserved.
  7. //
  8. public final class SHA1: DigestType {
  9. static let digestLength: Int = 20 // 160 / 8
  10. static let blockSize: Int = 64
  11. fileprivate static let hashInitialValue: ContiguousArray<UInt32> = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]
  12. fileprivate var accumulated = Array<UInt8>()
  13. fileprivate var processedBytesTotalCount: Int = 0
  14. fileprivate var accumulatedHash: ContiguousArray<UInt32> = SHA1.hashInitialValue
  15. public init() {
  16. }
  17. public func calculate(for bytes: Array<UInt8>) -> Array<UInt8> {
  18. do {
  19. return try self.update(withBytes: bytes, isLast: true)
  20. } catch {
  21. return []
  22. }
  23. }
  24. fileprivate func process(block chunk: ArraySlice<UInt8>, currentHash hh: inout ContiguousArray<UInt32>) {
  25. // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15, big-endian
  26. // Extend the sixteen 32-bit words into eighty 32-bit words:
  27. var M = ContiguousArray<UInt32>(repeating: 0, count: 80)
  28. for x in 0 ..< M.count {
  29. switch x {
  30. case 0 ... 15:
  31. let start = chunk.startIndex.advanced(by: x * 4) // * MemoryLayout<UInt32>.size
  32. M[x] = UInt32(bytes: chunk, fromIndex: start)
  33. break
  34. default:
  35. M[x] = rotateLeft(M[x - 3] ^ M[x - 8] ^ M[x - 14] ^ M[x - 16], by: 1)
  36. break
  37. }
  38. }
  39. var A = hh[0]
  40. var B = hh[1]
  41. var C = hh[2]
  42. var D = hh[3]
  43. var E = hh[4]
  44. // Main loop
  45. for j in 0 ... 79 {
  46. var f: UInt32 = 0
  47. var k: UInt32 = 0
  48. switch j {
  49. case 0 ... 19:
  50. f = (B & C) | ((~B) & D)
  51. k = 0x5A827999
  52. break
  53. case 20 ... 39:
  54. f = B ^ C ^ D
  55. k = 0x6ED9EBA1
  56. break
  57. case 40 ... 59:
  58. f = (B & C) | (B & D) | (C & D)
  59. k = 0x8F1BBCDC
  60. break
  61. case 60 ... 79:
  62. f = B ^ C ^ D
  63. k = 0xCA62C1D6
  64. break
  65. default:
  66. break
  67. }
  68. let temp = rotateLeft(A, by: 5) &+ f &+ E &+ M[j] &+ k
  69. E = D
  70. D = C
  71. C = rotateLeft(B, by: 30)
  72. B = A
  73. A = temp
  74. }
  75. hh[0] = hh[0] &+ A
  76. hh[1] = hh[1] &+ B
  77. hh[2] = hh[2] &+ C
  78. hh[3] = hh[3] &+ D
  79. hh[4] = hh[4] &+ E
  80. }
  81. }
  82. extension SHA1: Updatable {
  83. public func update<T: Collection>(withBytes bytes: T, isLast: Bool = false) throws -> Array<UInt8> where T.Iterator.Element == UInt8 {
  84. self.accumulated += bytes
  85. if isLast {
  86. let lengthInBits = (self.processedBytesTotalCount + self.accumulated.count) * 8
  87. let lengthBytes = lengthInBits.bytes(totalBytes: 64 / 8) // A 64-bit representation of b
  88. // Step 1. Append padding
  89. bitPadding(to: &self.accumulated, blockSize: SHA1.blockSize, allowance: 64 / 8)
  90. // Step 2. Append Length a 64-bit representation of lengthInBits
  91. self.accumulated += lengthBytes
  92. }
  93. var processedBytes = 0
  94. for chunk in self.accumulated.batched(by: SHA1.blockSize) {
  95. if (isLast || (self.accumulated.count - processedBytes) >= SHA1.blockSize) {
  96. self.process(block: chunk, currentHash: &self.accumulatedHash)
  97. processedBytes += chunk.count
  98. }
  99. }
  100. self.accumulated.removeFirst(processedBytes)
  101. self.processedBytesTotalCount += processedBytes
  102. // output current hash
  103. var result = Array<UInt8>(repeating: 0, count: SHA1.digestLength)
  104. var pos = 0
  105. for idx in 0 ..< self.accumulatedHash.count {
  106. let h = self.accumulatedHash[idx].bigEndian
  107. result[pos] = UInt8(h & 0xff)
  108. result[pos + 1] = UInt8((h >> 8) & 0xff)
  109. result[pos + 2] = UInt8((h >> 16) & 0xff)
  110. result[pos + 3] = UInt8((h >> 24) & 0xff)
  111. pos += 4
  112. }
  113. // reset hash value for instance
  114. if isLast {
  115. self.accumulatedHash = SHA1.hashInitialValue
  116. }
  117. return result
  118. }
  119. }