Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. #!/usr/bin/ruby
  2.  
  3. # This script extends the functionality of the String#crypt method
  4. # for non-Linux environment. On Linux, String#crypt provides
  5. # all of the functionality of the standard library crypt method.
  6. # However, on other platforms only the "DES" hashing can be performed.
  7. # For example, on Windows with Ruby 1.9.3 you see this:
  8. # irb(main):001:0> "password".crypt("$5$salt")
  9. # => "$5vvFjbkJCcrk"
  10. # The expected output was the SHA256 hash of "password" with "salt".
  11. # On Linux, you instead see this (as should be expected):
  12. # irb(main):001:0> "password".crypt("$5$salt")
  13. # => "$5$salt$Gcm6FsVtF/Qa77ZKD.iwsJlCVPY0XSMgLJL0Hnww/c1"
  14.  
  15. # I'm not the first to implement this.  See also:
  16. # https://github.com/SJD/unix-crypt
  17.  
  18. # The author utilized the documentation provided at this URL:
  19. # http://www.akkadia.org/drepper/SHA-crypt.txt
  20.  
  21. # Copyright (C) 2012 Justin W Smith
  22.  
  23. # This program is free software: you can redistribute it and/or modify
  24. # it under the terms of the GNU General Public License as published by
  25. # the Free Software Foundation, either version 3 of the License, or
  26. # (at your option) any later version.
  27.  
  28. # This program is distributed in the hope that it will be useful,
  29. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  30. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  31. # GNU General Public License for more details.
  32.  
  33. # You should have received a copy of the GNU General Public License
  34. # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  35.  
  36. require 'digest'
  37.  
  38.  
  39. class String
  40.     @@sha_encodings = ['.', '/']
  41.     @@sha_encodings += ('0'..'9').to_a
  42.     @@sha_encodings += ('A'..'Z').to_a
  43.     @@sha_encodings += ('a'..'z').to_a
  44.  
  45.     @@groups256 =
  46.         [[0, 10, 20], [21, 1, 11], [12, 22, 2],
  47.         [3, 13, 23], [24, 4, 14], [15, 25, 5],
  48.         [6, 16, 26], [27, 7, 17], [18, 28, 8],
  49.         [9, 19, 29], [-1, 31, 30]]
  50.  
  51.     @@groups512 =
  52.         [[0, 21, 42], [22, 43, 1], [44, 2, 23],
  53.         [3, 24, 45], [25, 46, 4], [47, 5, 26],
  54.         [6, 27, 48], [28, 49, 7], [50, 8, 29],
  55.         [9, 30, 51], [31, 52, 10],
  56.         [53, 11, 32], [12, 33, 54], [34, 55, 13],
  57.         [56, 14, 35], [15, 36, 57], [37, 58, 16],
  58.         [59, 17, 38], [18, 39, 60], [40, 61, 19],
  59.         [62, 20, 41], [-1, -1, 63]]
  60.    
  61.     @@default_rounds = 5000
  62.  
  63.     def sha_crypt(digest, salt, rounds = @@default_rounds)
  64.         password = self
  65.  
  66.         a = digest.new.update(password).update(salt)
  67.         b_result = digest.new.update(password).update(salt).update(password).digest
  68.        
  69.         (password.length / b_result.length).times { a.update(b_result) }
  70.         a.update(b_result[0...(password.length % b_result.length)])
  71.  
  72.         password.length.to_s(2).reverse.each_char do |x|
  73.             a.update(x.to_i == 1 ? b_result: password)
  74.         end
  75.         a_result = a.digest
  76.  
  77.         dp = digest.new
  78.         password.length.times { dp.update(password) }
  79.         dp_result = dp.digest
  80.  
  81.         pe = (dp_result * (password.length / dp_result.length))
  82.         pe += dp_result[0...(password.length % dp_result.length)]
  83.        
  84.         ds = digest.new
  85.         (16+a_result[0].ord).times { ds.update salt }
  86.         ds_result = ds.digest
  87.  
  88.         s = (ds_result * (salt.length / ds_result.length))
  89.         s = ds_result[0...(salt.length % ds_result.length)]
  90.  
  91.         d21 = a_result
  92.         rounds.times do |i|
  93.             c = digest.new
  94.             c.update i.odd? ? pe : d21
  95.             c.update(s) unless (i % 3).zero?
  96.             c.update(pe) unless (i % 7).zero?
  97.             c.update i.odd? ? d21 : pe
  98.             d21 = c.digest
  99.         end
  100.         d21
  101.     end
  102.  
  103.  
  104.     def sha_encode cr, groups
  105.         text = ""
  106.         cr += "\x00"
  107.         groups.each do |group|
  108.             text += @@sha_encodings[cr[group[2]].ord & 0x3F]
  109.             text += @@sha_encodings[((cr[group[2]].ord & 0xC0)>>6) | ((cr[group[1]].ord & 0x0F)<<2)]
  110.             (text += @@sha_encodings[((cr[group[1]].ord & 0xF0)>>4) | ((cr[group[0]].ord & 0x03)<<4)]) unless group[0] < 0 && group[1] < 0
  111.             (text += @@sha_encodings[(cr[group[0]].ord & 0xFC)>>2]) unless group[0] < 0
  112.         end
  113.         text
  114.     end
  115.  
  116.     #cover my tracks
  117.     method_sha_crypt = instance_method(:sha_crypt)
  118.     method_sha_encode = instance_method(:sha_encode)
  119.     remove_method :sha_crypt
  120.     remove_method :sha_encode  
  121.    
  122.     orig_crypt = instance_method(:crypt)
  123.  
  124.     define_method(:crypt) do |id_salt|
  125.         if( (md = /^\$([0-9])\$([^\$]*)\$?$/.match id_salt).nil? )
  126.             return orig_crypt.bind(self).(id_salt)
  127.         end
  128.  
  129.         id = md[1]
  130.         salt = md[2][0...16]
  131.    
  132.         #p "id: #{id}"
  133.         #p "salt: #{salt}"
  134.    
  135.         case id.to_i
  136.         when 5
  137.             cr = method_sha_crypt.bind(self).(Digest::SHA256, salt)
  138.             text = method_sha_encode.bind(self).( cr, @@groups256)
  139.         when 6
  140.             cr = method_sha_crypt.bind(self).(Digest::SHA512, salt)
  141.             text = method_sha_encode.bind(self).( cr, @@groups512)
  142.         else
  143.             raise "Unsupported encryption method: #{id}"
  144.         end
  145.         "$#{id}$#{salt}$#{text}"
  146.     end
  147. end