1
0

String+SHA1.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. //
  2. // String+SHA1.swift
  3. // Swifter
  4. //
  5. // Copyright 2014-2016 Damian Kołakowski. All rights reserved.
  6. //
  7. import Foundation
  8. // swiftlint:disable identifier_name function_body_length
  9. public struct SHA1 {
  10. public static func hash(_ input: [UInt8]) -> [UInt8] {
  11. // Alghorithm from: https://en.wikipedia.org/wiki/SHA-1
  12. var message = input
  13. var h0 = UInt32(littleEndian: 0x67452301)
  14. var h1 = UInt32(littleEndian: 0xEFCDAB89)
  15. var h2 = UInt32(littleEndian: 0x98BADCFE)
  16. var h3 = UInt32(littleEndian: 0x10325476)
  17. var h4 = UInt32(littleEndian: 0xC3D2E1F0)
  18. // ml = message length in bits (always a multiple of the number of bits in a character).
  19. let ml = UInt64(message.count * 8)
  20. // append the bit '1' to the message e.g. by adding 0x80 if message length is a multiple of 8 bits.
  21. message.append(0x80)
  22. // append 0 ≤ k < 512 bits '0', such that the resulting message length in bits is congruent to −64 ≡ 448 (mod 512)
  23. let padBytesCount = ( message.count + 8 ) % 64
  24. message.append(contentsOf: [UInt8](repeating: 0, count: 64 - padBytesCount))
  25. // append ml, in a 64-bit big-endian integer. Thus, the total length is a multiple of 512 bits.
  26. var mlBigEndian = ml.bigEndian
  27. withUnsafePointer(to: &mlBigEndian) {
  28. message.append(contentsOf: Array(UnsafeBufferPointer<UInt8>(start: UnsafePointer(OpaquePointer($0)), count: 8)))
  29. }
  30. // Process the message in successive 512-bit chunks ( 64 bytes chunks ):
  31. for chunkStart in 0..<message.count/64 {
  32. var words = [UInt32]()
  33. let chunk = message[chunkStart*64..<chunkStart*64+64]
  34. // break chunk into sixteen 32-bit big-endian words w[i], 0 ≤ i ≤ 15
  35. for index in 0...15 {
  36. let value = chunk.withUnsafeBufferPointer({ UnsafePointer<UInt32>(OpaquePointer($0.baseAddress! + (index*4))).pointee})
  37. words.append(value.bigEndian)
  38. }
  39. // Extend the sixteen 32-bit words into eighty 32-bit words:
  40. for index in 16...79 {
  41. let value: UInt32 = ((words[index-3]) ^ (words[index-8]) ^ (words[index-14]) ^ (words[index-16]))
  42. words.append(rotateLeft(value, 1))
  43. }
  44. // Initialize hash value for this chunk:
  45. var a = h0
  46. var b = h1
  47. var c = h2
  48. var d = h3
  49. var e = h4
  50. for i in 0..<80 {
  51. var f = UInt32(0)
  52. var k = UInt32(0)
  53. switch i {
  54. case 0...19:
  55. f = (b & c) | ((~b) & d)
  56. k = 0x5A827999
  57. case 20...39:
  58. f = b ^ c ^ d
  59. k = 0x6ED9EBA1
  60. case 40...59:
  61. f = (b & c) | (b & d) | (c & d)
  62. k = 0x8F1BBCDC
  63. case 60...79:
  64. f = b ^ c ^ d
  65. k = 0xCA62C1D6
  66. default: break
  67. }
  68. let temp = (rotateLeft(a, 5) &+ f &+ e &+ k &+ words[i]) & 0xFFFFFFFF
  69. e = d
  70. d = c
  71. c = rotateLeft(b, 30)
  72. b = a
  73. a = temp
  74. }
  75. // Add this chunk's hash to result so far:
  76. h0 = ( h0 &+ a ) & 0xFFFFFFFF
  77. h1 = ( h1 &+ b ) & 0xFFFFFFFF
  78. h2 = ( h2 &+ c ) & 0xFFFFFFFF
  79. h3 = ( h3 &+ d ) & 0xFFFFFFFF
  80. h4 = ( h4 &+ e ) & 0xFFFFFFFF
  81. }
  82. // Produce the final hash value (big-endian) as a 160 bit number:
  83. var digest = [UInt8]()
  84. [h0, h1, h2, h3, h4].forEach { value in
  85. var bigEndianVersion = value.bigEndian
  86. withUnsafePointer(to: &bigEndianVersion) {
  87. digest.append(contentsOf: Array(UnsafeBufferPointer<UInt8>(start: UnsafePointer(OpaquePointer($0)), count: 4)))
  88. }
  89. }
  90. return digest
  91. }
  92. private static func rotateLeft(_ v: UInt32, _ n: UInt32) -> UInt32 {
  93. return ((v << n) & 0xFFFFFFFF) | (v >> (32 - n))
  94. }
  95. }
  96. extension String {
  97. public func sha1() -> [UInt8] {
  98. return SHA1.hash([UInt8](self.utf8))
  99. }
  100. public func sha1() -> String {
  101. return self.sha1().reduce("") { $0 + String(format: "%02x", $1) }
  102. }
  103. }