unix-crypt 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/LICENSE +10 -0
- data/README.rdoc +59 -0
- data/Rakefile +7 -0
- data/bin/mkunixcrypt +16 -0
- data/lib/unix_crypt.rb +32 -15
- data/lib/unix_crypt/command_line.rb +123 -0
- data/test/{unix_crypt_test.rb → test_unix_crypt.rb} +34 -1
- data/test/unix_crypt/test_command_line.rb +88 -0
- data/unix-crypt.gemspec +17 -0
- metadata +24 -14
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 1d23e0cd7fb0edc71372a52d4bd357dbbbbd0a8b
         | 
| 4 | 
            +
              data.tar.gz: 0b135e10570ae2b629f52f441997cd304663b728
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 769ec3053c3e3af385aa5feb6bee79607763a6691cf33362f08a1198296c57cb1c98d8b78472f4eb354ac0e22cba35fc10d680e7bfecb30820d7ef71eb0a1b6b
         | 
| 7 | 
            +
              data.tar.gz: a175438b7eedfe60a156aaaf003b3956cf70f7f0b054c181b29c9ca5879dbe421253b80d997331a2f095db1dcece3f5ed130df167c8dbaf82d037706cf31d317
         | 
    
        data/.gitignore
    ADDED
    
    
    
        data/LICENSE
    ADDED
    
    | @@ -0,0 +1,10 @@ | |
| 1 | 
            +
            Copyright (c) 2013, Roger Nesbitt
         | 
| 2 | 
            +
            All rights reserved.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
         | 
| 7 | 
            +
            Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
         | 
| 8 | 
            +
            Neither the name of the unix-crypt nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
         | 
    
        data/README.rdoc
    ADDED
    
    | @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            = unix-crypt
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            == Description
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            unix-crypt creates and checks passwords that you'd normally find in an /etc/shadow file on your UNIX box.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            It's written entirely in Ruby and has no external dependencies.
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            It handles:
         | 
| 10 | 
            +
            * DES passwords (the standard 13 character password with a 2 character salt)
         | 
| 11 | 
            +
            * MD5 passwords (starting with $1$)
         | 
| 12 | 
            +
            * SHA256 passwords (starting with $5$)
         | 
| 13 | 
            +
            * SHA512 passwords (starting with $6$)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            This library is compatible with Ruby 1.8.7 and above.  Tested on Ruby 2.0.0p0.
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            == Using the command line tool
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            An executable named +mkunixcrypt+ allows you to generate passwords from the command line.
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              Usage: mkunixcrypt [options]
         | 
| 22 | 
            +
              Encrypts password using the unix-crypt gem
         | 
| 23 | 
            +
              
         | 
| 24 | 
            +
              Options:
         | 
| 25 | 
            +
                  -h, --hash [HASH]                Set hash algorithm [SHA512 (default), SHA256, MD5]
         | 
| 26 | 
            +
                  -p, --password [PASSWORD]        Provide password on command line (insecure!)
         | 
| 27 | 
            +
                  -s, --salt [SALT]                Provide hash salt
         | 
| 28 | 
            +
                  -r, --rounds [ROUNDS]            Set number of hashing rounds (SHA256/SHA512 only)
         | 
| 29 | 
            +
                      --help                       Show this message
         | 
| 30 | 
            +
                  -v, --version                    Show version
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            == Using the library
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            You can either validate a password matches its hash:
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              >> UnixCrypt.valid?("Hello world!", "$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5")
         | 
| 37 | 
            +
              => true
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            Or you can generate a new hash, given a password and salt:
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              >> UnixCrypt::SHA256.build("Hello world!", "saltstring")
         | 
| 42 | 
            +
              => "$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5"
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            If a salt is not specified, one will be generated using random data:
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              >> UnixCrypt::SHA256.build("Hello world!")
         | 
| 47 | 
            +
              => "$5$v.fjb6lucDCZKjcf$90gzpr9HYo0eAeaN8rubElJdUUOcVYjTnGePBRvCgt1"
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            == License
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            Licensed under the BSD license.  See LICENSE file for details.
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            == Author
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            * Roger Nesbitt (roger@seriousorange.com)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            == Contributors
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            * Patrick Wyatt (pat@codeofhonor.com)
         | 
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/mkunixcrypt
    ADDED
    
    | @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            begin
         | 
| 4 | 
            +
              require 'unix_crypt'
         | 
| 5 | 
            +
            rescue LoadError
         | 
| 6 | 
            +
              require 'rubygems'
         | 
| 7 | 
            +
              require 'unix_crypt'
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            require 'unix_crypt/command_line'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            begin
         | 
| 13 | 
            +
              UnixCrypt::CommandLine.new(ARGV).encrypt
         | 
| 14 | 
            +
            rescue UnixCrypt::CommandLine::Abort => e
         | 
| 15 | 
            +
              abort e.message
         | 
| 16 | 
            +
            end
         | 
    
        data/lib/unix_crypt.rb
    CHANGED
    
    | @@ -2,6 +2,8 @@ require 'digest' | |
| 2 2 | 
             
            require 'securerandom'
         | 
| 3 3 |  | 
| 4 4 | 
             
            module UnixCrypt
         | 
| 5 | 
            +
              VERSION = "1.1.1"
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
              def self.valid?(password, string)
         | 
| 6 8 | 
             
                # Handle the original DES-based crypt(3)
         | 
| 7 9 | 
             
                return password.crypt(string) == string if string.length == 13
         | 
| @@ -16,7 +18,11 @@ module UnixCrypt | |
| 16 18 | 
             
                def self.build(password, salt = nil, rounds = nil)
         | 
| 17 19 | 
             
                  salt ||= generate_salt
         | 
| 18 20 |  | 
| 19 | 
            -
                  "$#{identifier}$#{salt}$#{hash(password, salt, rounds)}"
         | 
| 21 | 
            +
                  "$#{identifier}$#{rounds_marker rounds}#{salt}$#{hash(password, salt, rounds)}"
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def self.hash(password, salt, rounds = nil)
         | 
| 25 | 
            +
                  bit_specified_base64encode internal_hash(prepare_password(password), salt, rounds)
         | 
| 20 26 | 
             
                end
         | 
| 21 27 |  | 
| 22 28 | 
             
                def self.generate_salt
         | 
| @@ -56,6 +62,10 @@ module UnixCrypt | |
| 56 62 |  | 
| 57 63 | 
             
                  password
         | 
| 58 64 | 
             
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def self.rounds_marker(rounds)
         | 
| 67 | 
            +
                  nil
         | 
| 68 | 
            +
                end
         | 
| 59 69 | 
             
              end
         | 
| 60 70 |  | 
| 61 71 | 
             
              class MD5 < Base
         | 
| @@ -68,11 +78,9 @@ module UnixCrypt | |
| 68 78 | 
             
                  [[0, 6, 12], [1, 7, 13], [2, 8, 14], [3, 9, 15], [4, 10, 5], [nil, nil, 11]]
         | 
| 69 79 | 
             
                end
         | 
| 70 80 |  | 
| 71 | 
            -
                def self. | 
| 81 | 
            +
                def self.internal_hash(password, salt, ignored = nil)
         | 
| 72 82 | 
             
                  salt = salt[0..7]
         | 
| 73 83 |  | 
| 74 | 
            -
                  password = prepare_password(password)
         | 
| 75 | 
            -
             | 
| 76 84 | 
             
                  b = digest.digest("#{password}#{salt}#{password}")
         | 
| 77 85 | 
             
                  a_string = "#{password}$1$#{salt}#{b * (password.length/length)}#{b[0...password.length % length]}"
         | 
| 78 86 |  | 
| @@ -92,22 +100,19 @@ module UnixCrypt | |
| 92 100 | 
             
                    input = digest.digest(c_string)
         | 
| 93 101 | 
             
                  end
         | 
| 94 102 |  | 
| 95 | 
            -
                   | 
| 103 | 
            +
                  input
         | 
| 96 104 | 
             
                end
         | 
| 97 105 | 
             
              end
         | 
| 98 106 |  | 
| 99 107 | 
             
              class SHABase < Base
         | 
| 108 | 
            +
              protected
         | 
| 100 109 | 
             
                def self.default_salt_length; 12; end
         | 
| 110 | 
            +
                def self.default_rounds; 5000; end
         | 
| 101 111 |  | 
| 102 | 
            -
                def self. | 
| 103 | 
            -
                  rounds  | 
| 104 | 
            -
                  rounds = 1000        if rounds < 1000
         | 
| 105 | 
            -
                  rounds = 999_999_999 if rounds > 999_999_999
         | 
| 106 | 
            -
             | 
| 112 | 
            +
                def self.internal_hash(password, salt, rounds = nil)
         | 
| 113 | 
            +
                  rounds = apply_rounds_bounds(rounds || default_rounds)
         | 
| 107 114 | 
             
                  salt = salt[0..15]
         | 
| 108 115 |  | 
| 109 | 
            -
                  password = prepare_password(password)
         | 
| 110 | 
            -
             | 
| 111 116 | 
             
                  b = digest.digest("#{password}#{salt}#{password}")
         | 
| 112 117 |  | 
| 113 118 | 
             
                  a_string = password + salt + b * (password.length/length) + b[0...password.length % length]
         | 
| @@ -118,12 +123,12 @@ module UnixCrypt | |
| 118 123 | 
             
                    password_length >>= 1
         | 
| 119 124 | 
             
                  end
         | 
| 120 125 |  | 
| 121 | 
            -
                  input =  | 
| 126 | 
            +
                  input = digest.digest(a_string)
         | 
| 122 127 |  | 
| 123 128 | 
             
                  dp = digest.digest(password * password.length)
         | 
| 124 129 | 
             
                  p = dp * (password.length/length) + dp[0...password.length % length]
         | 
| 125 130 |  | 
| 126 | 
            -
                  ds = digest.digest(salt * (16 +  | 
| 131 | 
            +
                  ds = digest.digest(salt * (16 + input.bytes.first))
         | 
| 127 132 | 
             
                  s = ds * (salt.length/length) + ds[0...salt.length % length]
         | 
| 128 133 |  | 
| 129 134 | 
             
                  rounds.times do |index|
         | 
| @@ -134,7 +139,19 @@ module UnixCrypt | |
| 134 139 | 
             
                    input = digest.digest(c_string)
         | 
| 135 140 | 
             
                  end
         | 
| 136 141 |  | 
| 137 | 
            -
                   | 
| 142 | 
            +
                  input
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                def self.apply_rounds_bounds(rounds)
         | 
| 146 | 
            +
                  rounds = 1000        if rounds < 1000
         | 
| 147 | 
            +
                  rounds = 999_999_999 if rounds > 999_999_999
         | 
| 148 | 
            +
                  rounds
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                def self.rounds_marker(rounds)
         | 
| 152 | 
            +
                  if rounds && rounds != default_rounds
         | 
| 153 | 
            +
                    "rounds=#{apply_rounds_bounds(rounds)}$"
         | 
| 154 | 
            +
                  end
         | 
| 138 155 | 
             
                end
         | 
| 139 156 | 
             
              end
         | 
| 140 157 |  | 
| @@ -0,0 +1,123 @@ | |
| 1 | 
            +
            require 'optparse'
         | 
| 2 | 
            +
            require 'ostruct'
         | 
| 3 | 
            +
            begin
         | 
| 4 | 
            +
              require 'io/console'
         | 
| 5 | 
            +
            rescue LoadError
         | 
| 6 | 
            +
              $no_io_console = true
         | 
| 7 | 
            +
            end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            class UnixCrypt::CommandLine
         | 
| 10 | 
            +
              Abort = Class.new(StandardError)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              attr_reader :options
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def initialize(argv)
         | 
| 15 | 
            +
                @options = Opts.parse(argv)
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def encrypt
         | 
| 19 | 
            +
                if @options.password.nil?
         | 
| 20 | 
            +
                  @options.password = ask_password
         | 
| 21 | 
            +
                else
         | 
| 22 | 
            +
                  password_warning
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                puts @options.hasher.build(@options.password, @options.salt, @options.rounds)
         | 
| 26 | 
            +
                clear_string(@options.password)
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              private
         | 
| 30 | 
            +
              class Opts
         | 
| 31 | 
            +
                HASHERS = {
         | 
| 32 | 
            +
                  :SHA512 => UnixCrypt::SHA512,
         | 
| 33 | 
            +
                  :SHA256 => UnixCrypt::SHA256,
         | 
| 34 | 
            +
                  :MD5    => UnixCrypt::MD5
         | 
| 35 | 
            +
                }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def self.parse(args)
         | 
| 38 | 
            +
                  options            = OpenStruct.new
         | 
| 39 | 
            +
                  options.hashmethod = :SHA512
         | 
| 40 | 
            +
                  options.hasher     = HASHERS[options.hashmethod]
         | 
| 41 | 
            +
                  options.password   = nil
         | 
| 42 | 
            +
                  options.salt       = nil
         | 
| 43 | 
            +
                  options.rounds     = nil
         | 
| 44 | 
            +
                  options.leftovers  = OptionParser.new do |opts|
         | 
| 45 | 
            +
                    opts.banner = "Usage: #{File.basename $0} [options]"
         | 
| 46 | 
            +
                    opts.separator "Encrypts password using the unix-crypt gem"
         | 
| 47 | 
            +
                    opts.separator ""
         | 
| 48 | 
            +
                    opts.separator "Options:"
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    opts.on("-h", "--hash [HASH]", String, "Set hash algorithm [SHA512 (default), SHA256, MD5]") do |hasher|
         | 
| 51 | 
            +
                      options.hashmethod = hasher.to_s.upcase.to_sym
         | 
| 52 | 
            +
                      options.hasher     = HASHERS[options.hashmethod]
         | 
| 53 | 
            +
                      raise Abort, "Invalid hash algorithm for -h/--hash" if options.hasher.nil?
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    opts.on("-p", "--password [PASSWORD]", String, "Provide password on command line (insecure!)") do |password|
         | 
| 57 | 
            +
                      raise Abort, "Invalid password for -p/--password" if password.nil?
         | 
| 58 | 
            +
                      options.password = password
         | 
| 59 | 
            +
                      $0 = $0 # this invocation will get rid of the command line arguments from the process list
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    opts.on("-s", "--salt [SALT]", String, "Provide hash salt") do |salt|
         | 
| 63 | 
            +
                      raise Abort, "Invalid salt for -s/--salt" if salt.nil?
         | 
| 64 | 
            +
                      options.salt = salt
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    opts.on("-r", "--rounds [ROUNDS]", Integer, "Set number of hashing rounds (SHA256/SHA512 only)") do |rounds|
         | 
| 68 | 
            +
                      raise Abort, "Invalid hashing rounds for -r/--rounds" if rounds.nil? || rounds.to_i <= 0
         | 
| 69 | 
            +
                      options.rounds = rounds
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    opts.on_tail("-h", "--help", "Show this message") do
         | 
| 73 | 
            +
                      puts opts
         | 
| 74 | 
            +
                      exit
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    opts.on_tail("-v", "--version", "Show version") do
         | 
| 78 | 
            +
                      puts UnixCrypt::VERSION
         | 
| 79 | 
            +
                      exit
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
                  end.parse!(args)
         | 
| 82 | 
            +
                  options
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              def ask_noecho(message)
         | 
| 87 | 
            +
                $stderr.print message
         | 
| 88 | 
            +
                if $no_io_console
         | 
| 89 | 
            +
                  begin
         | 
| 90 | 
            +
                    `stty -echo`
         | 
| 91 | 
            +
                    result = gets
         | 
| 92 | 
            +
                  ensure
         | 
| 93 | 
            +
                    `stty echo`
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                else
         | 
| 96 | 
            +
                  result = $stdin.noecho(&:gets)
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
                $stderr.puts
         | 
| 99 | 
            +
                result
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
              def password_warning
         | 
| 103 | 
            +
                $stderr.puts "warning: providing a password on the command line is insecure"
         | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              def clear_string(string)
         | 
| 107 | 
            +
                string.replace(" " * string.length)
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
              def ask_password
         | 
| 111 | 
            +
                password = ask_noecho("Enter password: ")
         | 
| 112 | 
            +
                twice    = ask_noecho("Verify password: ")
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                if password != twice
         | 
| 115 | 
            +
                  clear_string(password)
         | 
| 116 | 
            +
                  clear_string(twice)
         | 
| 117 | 
            +
                  raise Abort, "Passwords don't match"
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                clear_string(twice)
         | 
| 121 | 
            +
                password.chomp!
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
            end
         | 
| @@ -8,7 +8,7 @@ | |
| 8 8 | 
             
            #
         | 
| 9 9 |  | 
| 10 10 | 
             
            require 'test/unit'
         | 
| 11 | 
            -
            require ' | 
| 11 | 
            +
            require File.expand_path('../../lib/unix_crypt', __FILE__)
         | 
| 12 12 |  | 
| 13 13 | 
             
            class UnixCryptTest < Test::Unit::TestCase
         | 
| 14 14 | 
             
              def test_password_validity
         | 
| @@ -50,9 +50,42 @@ class UnixCryptTest < Test::Unit::TestCase | |
| 50 50 | 
             
                end
         | 
| 51 51 | 
             
              end
         | 
| 52 52 |  | 
| 53 | 
            +
              def test_md5_password_generation
         | 
| 54 | 
            +
                hash = UnixCrypt::MD5.build("test")
         | 
| 55 | 
            +
                assert UnixCrypt.valid?("test", hash)
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              def test_sha256_password_generation
         | 
| 59 | 
            +
                hash = UnixCrypt::SHA256.build("test")
         | 
| 60 | 
            +
                assert UnixCrypt.valid?("test", hash)
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def test_sha512_password_generation
         | 
| 64 | 
            +
                hash = UnixCrypt::SHA512.build("test")
         | 
| 65 | 
            +
                assert UnixCrypt.valid?("test", hash)
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 53 68 | 
             
              def test_salt_generation
         | 
| 54 69 | 
             
                assert_match %r{\A\$1\$[a-zA-Z0-9./]{8}\$[a-zA-Z0-9./]{22}\z}, UnixCrypt::MD5.build("test password")
         | 
| 55 70 | 
             
                assert_match %r{\A\$5\$[a-zA-Z0-9./]{16}\$[a-zA-Z0-9./]{43}\z}, UnixCrypt::SHA256.build("test password")
         | 
| 56 71 | 
             
                assert_match %r{\A\$6\$[a-zA-Z0-9./]{16}\$[a-zA-Z0-9./]{86}\z}, UnixCrypt::SHA512.build("test password")
         | 
| 57 72 | 
             
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              def test_password_generation_with_rounds
         | 
| 75 | 
            +
                hash = UnixCrypt::SHA512.build("test password", nil, 5678)
         | 
| 76 | 
            +
                assert_match %r{\A\$6\$rounds=5678\$[a-zA-Z0-9./]{16}\$[a-zA-Z0-9./]{86}\z}, hash
         | 
| 77 | 
            +
                assert UnixCrypt.valid?("test password", hash)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                assert_match %r{\A\$6\$rounds=5678\$salted\$[a-zA-Z0-9./]{86}\z}, UnixCrypt::SHA512.build("test password", "salted", 5678)
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              def test_default_rounds_does_not_add_rounds_marker
         | 
| 83 | 
            +
                assert_match %r{\A\$6\$salted\$[a-zA-Z0-9./]{86}\z}, UnixCrypt::SHA512.build("test password", "salted", 5000) # the default number of rounds
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              def test_rounds_bounds
         | 
| 87 | 
            +
                hash = UnixCrypt::SHA512.build("test password", nil, 567)
         | 
| 88 | 
            +
                assert_match %r{\A\$6\$rounds=1000\$[a-zA-Z0-9./]{16}\$[a-zA-Z0-9./]{86}\z}, hash
         | 
| 89 | 
            +
                assert UnixCrypt.valid?("test password", hash)
         | 
| 90 | 
            +
              end
         | 
| 58 91 | 
             
            end
         | 
| @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            require 'test/unit'
         | 
| 2 | 
            +
            require File.expand_path('../../../lib/unix_crypt', __FILE__)
         | 
| 3 | 
            +
            require File.expand_path('../../../lib/unix_crypt/command_line', __FILE__)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class CommandLineTest < Test::Unit::TestCase
         | 
| 6 | 
            +
              class CaptureIO
         | 
| 7 | 
            +
                def initialize(name, buffer, input = [])
         | 
| 8 | 
            +
                  @name = name
         | 
| 9 | 
            +
                  @buffer = buffer
         | 
| 10 | 
            +
                  @input = input
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def noecho
         | 
| 14 | 
            +
                  yield self
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def gets
         | 
| 18 | 
            +
                  @buffer << [@name, @input.first.dup]
         | 
| 19 | 
            +
                  @input.shift
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def write(data)
         | 
| 23 | 
            +
                  @buffer << [@name, data]
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def print(data)
         | 
| 27 | 
            +
                  write data
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def puts(data = "")
         | 
| 31 | 
            +
                  write "#{data}\n"
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def self.redirect(input = [])
         | 
| 35 | 
            +
                  buffer = []
         | 
| 36 | 
            +
                  $stdin = new("stdin", buffer, input)
         | 
| 37 | 
            +
                  $stdout = new("stdout", buffer)
         | 
| 38 | 
            +
                  $stderr = new("stderr", buffer)
         | 
| 39 | 
            +
                  yield
         | 
| 40 | 
            +
                  buffer
         | 
| 41 | 
            +
                ensure
         | 
| 42 | 
            +
                  $stdin = STDIN
         | 
| 43 | 
            +
                  $stdout = STDOUT
         | 
| 44 | 
            +
                  $stderr = STDERR
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def test_no_parameter_password_creation
         | 
| 49 | 
            +
                result = CaptureIO.redirect(["hello\n", "hello\n"]) do
         | 
| 50 | 
            +
                  UnixCrypt::CommandLine.new([]).encrypt
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                expected = [
         | 
| 54 | 
            +
                  ["stderr", "Enter password: "],
         | 
| 55 | 
            +
                  ["stdin", "hello\n"],
         | 
| 56 | 
            +
                  ["stderr", "\n"],
         | 
| 57 | 
            +
                  ["stderr", "Verify password: "],
         | 
| 58 | 
            +
                  ["stdin", "hello\n"],
         | 
| 59 | 
            +
                  ["stderr", "\n"]
         | 
| 60 | 
            +
                ]
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                assert_equal expected, result[0..-2]
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                channel, password = result[-1]
         | 
| 65 | 
            +
                assert_equal "stdout", channel
         | 
| 66 | 
            +
                assert_match %r{\A\$6\$[a-zA-Z0-9./]{16}\$[a-zA-Z0-9./]{86}\n\z}, password
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                assert UnixCrypt.valid?("hello", password)
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def test_parameters_provided_password_creation
         | 
| 72 | 
            +
                result = CaptureIO.redirect do
         | 
| 73 | 
            +
                  UnixCrypt::CommandLine.new(%w(-h sha256 -p hello -s salty -r 1234)).encrypt
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                expected = [
         | 
| 77 | 
            +
                  ["stderr", "warning: providing a password on the command line is insecure\n"]
         | 
| 78 | 
            +
                ]
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                assert_equal expected, result[0..-2]
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                channel, password = result[-1]
         | 
| 83 | 
            +
                assert_equal "stdout", channel
         | 
| 84 | 
            +
                assert_match %r{\A\$5\$rounds=1234\$salty\$[a-zA-Z0-9./]{43}\n\z}, password
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                assert UnixCrypt.valid?("hello", password)
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
            end
         | 
    
        data/unix-crypt.gemspec
    ADDED
    
    | @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            require './lib/unix_crypt'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            spec = Gem::Specification.new do |s|
         | 
| 4 | 
            +
              s.name         = 'unix-crypt'
         | 
| 5 | 
            +
              s.version      = UnixCrypt::VERSION
         | 
| 6 | 
            +
              s.summary      = "Performs the UNIX crypt(3) algorithm using DES, MD5, SHA256 or SHA512"
         | 
| 7 | 
            +
              s.description  = %{Performs the UNIX crypt(3) algorithm using DES (standard 13 character passwords), MD5 (starting with $1$), SHA256 (starting with $5$) and SHA512 (starting with $6$)}
         | 
| 8 | 
            +
              s.files        = `git ls-files`.split($\)
         | 
| 9 | 
            +
              s.test_files   = s.files.grep(%r{^(test|spec|features)/})
         | 
| 10 | 
            +
              s.executables  = ["mkunixcrypt"]
         | 
| 11 | 
            +
              s.require_path = 'lib'
         | 
| 12 | 
            +
              s.has_rdoc     = false
         | 
| 13 | 
            +
              s.author       = "Roger Nesbitt"
         | 
| 14 | 
            +
              s.email        = "roger@seriousorange.com"
         | 
| 15 | 
            +
              s.homepage     = "https://github.com/mogest/unix-crypt"
         | 
| 16 | 
            +
              s.license      = "BSD"
         | 
| 17 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,48 +1,58 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: unix-crypt
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.1. | 
| 5 | 
            -
              prerelease: 
         | 
| 4 | 
            +
              version: 1.1.1
         | 
| 6 5 | 
             
            platform: ruby
         | 
| 7 6 | 
             
            authors:
         | 
| 8 7 | 
             
            - Roger Nesbitt
         | 
| 9 8 | 
             
            autorequire: 
         | 
| 10 9 | 
             
            bindir: bin
         | 
| 11 10 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2013- | 
| 11 | 
            +
            date: 2013-09-23 00:00:00.000000000 Z
         | 
| 13 12 | 
             
            dependencies: []
         | 
| 14 13 | 
             
            description: Performs the UNIX crypt(3) algorithm using DES (standard 13 character
         | 
| 15 14 | 
             
              passwords), MD5 (starting with $1$), SHA256 (starting with $5$) and SHA512 (starting
         | 
| 16 15 | 
             
              with $6$)
         | 
| 17 16 | 
             
            email: roger@seriousorange.com
         | 
| 18 | 
            -
            executables: | 
| 17 | 
            +
            executables:
         | 
| 18 | 
            +
            - mkunixcrypt
         | 
| 19 19 | 
             
            extensions: []
         | 
| 20 20 | 
             
            extra_rdoc_files: []
         | 
| 21 21 | 
             
            files:
         | 
| 22 | 
            +
            - .gitignore
         | 
| 23 | 
            +
            - LICENSE
         | 
| 24 | 
            +
            - README.rdoc
         | 
| 25 | 
            +
            - Rakefile
         | 
| 26 | 
            +
            - bin/mkunixcrypt
         | 
| 22 27 | 
             
            - lib/unix_crypt.rb
         | 
| 23 | 
            -
            -  | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 28 | 
            +
            - lib/unix_crypt/command_line.rb
         | 
| 29 | 
            +
            - test/test_unix_crypt.rb
         | 
| 30 | 
            +
            - test/unix_crypt/test_command_line.rb
         | 
| 31 | 
            +
            - unix-crypt.gemspec
         | 
| 32 | 
            +
            homepage: https://github.com/mogest/unix-crypt
         | 
| 33 | 
            +
            licenses:
         | 
| 34 | 
            +
            - BSD
         | 
| 35 | 
            +
            metadata: {}
         | 
| 26 36 | 
             
            post_install_message: 
         | 
| 27 37 | 
             
            rdoc_options: []
         | 
| 28 38 | 
             
            require_paths:
         | 
| 29 39 | 
             
            - lib
         | 
| 30 40 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 31 | 
            -
              none: false
         | 
| 32 41 | 
             
              requirements:
         | 
| 33 | 
            -
              - -  | 
| 42 | 
            +
              - - '>='
         | 
| 34 43 | 
             
                - !ruby/object:Gem::Version
         | 
| 35 44 | 
             
                  version: '0'
         | 
| 36 45 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 37 | 
            -
              none: false
         | 
| 38 46 | 
             
              requirements:
         | 
| 39 | 
            -
              - -  | 
| 47 | 
            +
              - - '>='
         | 
| 40 48 | 
             
                - !ruby/object:Gem::Version
         | 
| 41 49 | 
             
                  version: '0'
         | 
| 42 50 | 
             
            requirements: []
         | 
| 43 51 | 
             
            rubyforge_project: 
         | 
| 44 | 
            -
            rubygems_version:  | 
| 52 | 
            +
            rubygems_version: 2.0.2
         | 
| 45 53 | 
             
            signing_key: 
         | 
| 46 | 
            -
            specification_version:  | 
| 54 | 
            +
            specification_version: 4
         | 
| 47 55 | 
             
            summary: Performs the UNIX crypt(3) algorithm using DES, MD5, SHA256 or SHA512
         | 
| 48 | 
            -
            test_files: | 
| 56 | 
            +
            test_files:
         | 
| 57 | 
            +
            - test/test_unix_crypt.rb
         | 
| 58 | 
            +
            - test/unix_crypt/test_command_line.rb
         |