bcrypt 3.1.3-x64-mingw32
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 +10 -0
 - data/.rspec +3 -0
 - data/.travis.yml +15 -0
 - data/CHANGELOG +63 -0
 - data/COPYING +28 -0
 - data/Gemfile +2 -0
 - data/Gemfile.lock +36 -0
 - data/README.md +203 -0
 - data/Rakefile +73 -0
 - data/bcrypt.gemspec +29 -0
 - data/ext/jruby/bcrypt_jruby/BCrypt.java +752 -0
 - data/ext/mri/bcrypt_ext.c +64 -0
 - data/ext/mri/crypt.c +57 -0
 - data/ext/mri/crypt.h +13 -0
 - data/ext/mri/crypt_blowfish.c +786 -0
 - data/ext/mri/crypt_gensalt.c +111 -0
 - data/ext/mri/extconf.rb +17 -0
 - data/ext/mri/ow-crypt.h +35 -0
 - data/ext/mri/wrapper.c +262 -0
 - data/lib/bcrypt.rb +21 -0
 - data/lib/bcrypt/engine.rb +116 -0
 - data/lib/bcrypt/error.rb +22 -0
 - data/lib/bcrypt/password.rb +87 -0
 - data/spec/TestBCrypt.java +194 -0
 - data/spec/bcrypt/engine_spec.rb +82 -0
 - data/spec/bcrypt/error_spec.rb +37 -0
 - data/spec/bcrypt/password_spec.rb +123 -0
 - data/spec/spec_helper.rb +2 -0
 - metadata +118 -0
 
| 
         @@ -0,0 +1,116 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module BCrypt
         
     | 
| 
      
 2 
     | 
    
         
            +
              # A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Engine
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The default computational expense parameter.
         
     | 
| 
      
 5 
     | 
    
         
            +
                DEFAULT_COST    = 10
         
     | 
| 
      
 6 
     | 
    
         
            +
                # The minimum cost supported by the algorithm.
         
     | 
| 
      
 7 
     | 
    
         
            +
                MIN_COST        = 4
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Maximum possible size of bcrypt() salts.
         
     | 
| 
      
 9 
     | 
    
         
            +
                MAX_SALT_LENGTH = 16
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                if RUBY_PLATFORM != "java"
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # C-level routines which, if they don't get the right input, will crash the
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # hell out of the Ruby process.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  private_class_method :__bc_salt
         
     | 
| 
      
 15 
     | 
    
         
            +
                  private_class_method :__bc_crypt
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                @cost = nil
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                # Returns the cost factor that will be used if one is not specified when
         
     | 
| 
      
 21 
     | 
    
         
            +
                # creating a password hash.  Defaults to DEFAULT_COST if not set.
         
     | 
| 
      
 22 
     | 
    
         
            +
                def self.cost
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @cost || DEFAULT_COST
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                # Set a default cost factor that will be used if one is not specified when
         
     | 
| 
      
 27 
     | 
    
         
            +
                # creating a password hash.
         
     | 
| 
      
 28 
     | 
    
         
            +
                #
         
     | 
| 
      
 29 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
      
 31 
     | 
    
         
            +
                #   BCrypt::Engine::DEFAULT_COST            #=> 10
         
     | 
| 
      
 32 
     | 
    
         
            +
                #   BCrypt::Password.create('secret').cost  #=> 10
         
     | 
| 
      
 33 
     | 
    
         
            +
                #
         
     | 
| 
      
 34 
     | 
    
         
            +
                #   BCrypt::Engine.cost = 8
         
     | 
| 
      
 35 
     | 
    
         
            +
                #   BCrypt::Password.create('secret').cost  #=> 8
         
     | 
| 
      
 36 
     | 
    
         
            +
                #
         
     | 
| 
      
 37 
     | 
    
         
            +
                #   # cost can still be overridden as needed
         
     | 
| 
      
 38 
     | 
    
         
            +
                #   BCrypt::Password.create('secret', :cost => 6).cost  #=> 6
         
     | 
| 
      
 39 
     | 
    
         
            +
                def self.cost=(cost)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @cost = cost
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                # Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
         
     | 
| 
      
 44 
     | 
    
         
            +
                # a bcrypt() password hash.
         
     | 
| 
      
 45 
     | 
    
         
            +
                def self.hash_secret(secret, salt, _ = nil)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  if valid_secret?(secret)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    if valid_salt?(salt)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      if RUBY_PLATFORM == "java"
         
     | 
| 
      
 49 
     | 
    
         
            +
                        Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      else
         
     | 
| 
      
 51 
     | 
    
         
            +
                        __bc_crypt(secret.to_s, salt)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      end
         
     | 
| 
      
 53 
     | 
    
         
            +
                    else
         
     | 
| 
      
 54 
     | 
    
         
            +
                      raise Errors::InvalidSalt.new("invalid salt")
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  else
         
     | 
| 
      
 57 
     | 
    
         
            +
                    raise Errors::InvalidSecret.new("invalid secret")
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                # Generates a random salt with a given computational cost.
         
     | 
| 
      
 62 
     | 
    
         
            +
                def self.generate_salt(cost = self.cost)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  cost = cost.to_i
         
     | 
| 
      
 64 
     | 
    
         
            +
                  if cost > 0
         
     | 
| 
      
 65 
     | 
    
         
            +
                    if cost < MIN_COST
         
     | 
| 
      
 66 
     | 
    
         
            +
                      cost = MIN_COST
         
     | 
| 
      
 67 
     | 
    
         
            +
                    end
         
     | 
| 
      
 68 
     | 
    
         
            +
                    if RUBY_PLATFORM == "java"
         
     | 
| 
      
 69 
     | 
    
         
            +
                      Java.bcrypt_jruby.BCrypt.gensalt(cost)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    else
         
     | 
| 
      
 71 
     | 
    
         
            +
                      prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
         
     | 
| 
      
 72 
     | 
    
         
            +
                      __bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  else
         
     | 
| 
      
 75 
     | 
    
         
            +
                    raise Errors::InvalidCost.new("cost must be numeric and > 0")
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                # Returns true if +salt+ is a valid bcrypt() salt, false if not.
         
     | 
| 
      
 80 
     | 
    
         
            +
                def self.valid_salt?(salt)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  !!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                # Returns true if +secret+ is a valid bcrypt() secret, false if not.
         
     | 
| 
      
 85 
     | 
    
         
            +
                def self.valid_secret?(secret)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  secret.respond_to?(:to_s)
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                # Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
         
     | 
| 
      
 90 
     | 
    
         
            +
                #
         
     | 
| 
      
 91 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 92 
     | 
    
         
            +
                #
         
     | 
| 
      
 93 
     | 
    
         
            +
                #   BCrypt::Engine.calibrate(200)  #=> 10
         
     | 
| 
      
 94 
     | 
    
         
            +
                #   BCrypt::Engine.calibrate(1000) #=> 12
         
     | 
| 
      
 95 
     | 
    
         
            +
                #
         
     | 
| 
      
 96 
     | 
    
         
            +
                #   # should take less than 200ms
         
     | 
| 
      
 97 
     | 
    
         
            +
                #   BCrypt::Password.create("woo", :cost => 10)
         
     | 
| 
      
 98 
     | 
    
         
            +
                #
         
     | 
| 
      
 99 
     | 
    
         
            +
                #   # should take less than 1000ms
         
     | 
| 
      
 100 
     | 
    
         
            +
                #   BCrypt::Password.create("woo", :cost => 12)
         
     | 
| 
      
 101 
     | 
    
         
            +
                def self.calibrate(upper_time_limit_in_ms)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  40.times do |i|
         
     | 
| 
      
 103 
     | 
    
         
            +
                    start_time = Time.now
         
     | 
| 
      
 104 
     | 
    
         
            +
                    Password.create("testing testing", :cost => i+1)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    end_time = Time.now - start_time
         
     | 
| 
      
 106 
     | 
    
         
            +
                    return i if end_time * 1_000 > upper_time_limit_in_ms
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                # Autodetects the cost from the salt string.
         
     | 
| 
      
 111 
     | 
    
         
            +
                def self.autodetect_cost(salt)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  salt[4..5].to_i
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
              end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/bcrypt/error.rb
    ADDED
    
    | 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module BCrypt
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              class Error < StandardError  # :nodoc:
         
     | 
| 
      
 4 
     | 
    
         
            +
              end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              module Errors  # :nodoc:
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                # The salt parameter provided to bcrypt() is invalid.
         
     | 
| 
      
 9 
     | 
    
         
            +
                class InvalidSalt < BCrypt::Error; end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # The hash parameter provided to bcrypt() is invalid.
         
     | 
| 
      
 12 
     | 
    
         
            +
                class InvalidHash < BCrypt::Error; end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                # The cost parameter provided to bcrypt() is invalid.
         
     | 
| 
      
 15 
     | 
    
         
            +
                class InvalidCost < BCrypt::Error; end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # The secret parameter provided to bcrypt() is invalid.
         
     | 
| 
      
 18 
     | 
    
         
            +
                class InvalidSecret < BCrypt::Error; end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,87 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module BCrypt
         
     | 
| 
      
 2 
     | 
    
         
            +
              # A password management class which allows you to safely store users' passwords and compare them.
         
     | 
| 
      
 3 
     | 
    
         
            +
              #
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Example usage:
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              #   include BCrypt
         
     | 
| 
      
 7 
     | 
    
         
            +
              #
         
     | 
| 
      
 8 
     | 
    
         
            +
              #   # hash a user's password
         
     | 
| 
      
 9 
     | 
    
         
            +
              #   @password = Password.create("my grand secret")
         
     | 
| 
      
 10 
     | 
    
         
            +
              #   @password #=> "$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG"
         
     | 
| 
      
 11 
     | 
    
         
            +
              #
         
     | 
| 
      
 12 
     | 
    
         
            +
              #   # store it safely
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   @user.update_attribute(:password, @password)
         
     | 
| 
      
 14 
     | 
    
         
            +
              #
         
     | 
| 
      
 15 
     | 
    
         
            +
              #   # read it back
         
     | 
| 
      
 16 
     | 
    
         
            +
              #   @user.reload!
         
     | 
| 
      
 17 
     | 
    
         
            +
              #   @db_password = Password.new(@user.password)
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   # compare it after retrieval
         
     | 
| 
      
 20 
     | 
    
         
            +
              #   @db_password == "my grand secret" #=> true
         
     | 
| 
      
 21 
     | 
    
         
            +
              #   @db_password == "a paltry guess"  #=> false
         
     | 
| 
      
 22 
     | 
    
         
            +
              #
         
     | 
| 
      
 23 
     | 
    
         
            +
              class Password < String
         
     | 
| 
      
 24 
     | 
    
         
            +
                # The hash portion of the stored password hash.
         
     | 
| 
      
 25 
     | 
    
         
            +
                attr_reader :checksum
         
     | 
| 
      
 26 
     | 
    
         
            +
                # The salt of the store password hash (including version and cost).
         
     | 
| 
      
 27 
     | 
    
         
            +
                attr_reader :salt
         
     | 
| 
      
 28 
     | 
    
         
            +
                # The version of the bcrypt() algorithm used to create the hash.
         
     | 
| 
      
 29 
     | 
    
         
            +
                attr_reader :version
         
     | 
| 
      
 30 
     | 
    
         
            +
                # The cost factor used to create the hash.
         
     | 
| 
      
 31 
     | 
    
         
            +
                attr_reader :cost
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # Hashes a secret, returning a BCrypt::Password instance. Takes an optional <tt>:cost</tt> option, which is a
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # logarithmic variable which determines how computational expensive the hash is to calculate (a <tt>:cost</tt> of
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # 4 is twice as much work as a <tt>:cost</tt> of 3). The higher the <tt>:cost</tt> the harder it becomes for
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check
         
     | 
| 
      
 38 
     | 
    
         
            +
                  # users' passwords.
         
     | 
| 
      
 39 
     | 
    
         
            +
                  #
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # Example:
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #   @password = BCrypt::Password.create("my secret", :cost => 13)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  def create(secret, options = {})
         
     | 
| 
      
 44 
     | 
    
         
            +
                    cost = options[:cost] || BCrypt::Engine.cost
         
     | 
| 
      
 45 
     | 
    
         
            +
                    raise ArgumentError if cost > 31
         
     | 
| 
      
 46 
     | 
    
         
            +
                    Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)))
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def valid_hash?(h)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                # Initializes a BCrypt::Password instance with the data from a stored hash.
         
     | 
| 
      
 55 
     | 
    
         
            +
                def initialize(raw_hash)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  if valid_hash?(raw_hash)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    self.replace(raw_hash)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @version, @cost, @salt, @checksum = split_hash(self)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  else
         
     | 
| 
      
 60 
     | 
    
         
            +
                    raise Errors::InvalidHash.new("invalid hash")
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                # Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
         
     | 
| 
      
 65 
     | 
    
         
            +
                def ==(secret)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  super(BCrypt::Engine.hash_secret(secret, @salt))
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
                alias_method :is_password?, :==
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
              private
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                # Returns true if +h+ is a valid hash.
         
     | 
| 
      
 73 
     | 
    
         
            +
                def valid_hash?(h)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  self.class.valid_hash?(h)
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                # call-seq:
         
     | 
| 
      
 78 
     | 
    
         
            +
                #   split_hash(raw_hash) -> version, cost, salt, hash
         
     | 
| 
      
 79 
     | 
    
         
            +
                #
         
     | 
| 
      
 80 
     | 
    
         
            +
                # Splits +h+ into version, cost, salt, and hash and returns them in that order.
         
     | 
| 
      
 81 
     | 
    
         
            +
                def split_hash(h)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  _, v, c, mash = h.split('$')
         
     | 
| 
      
 83 
     | 
    
         
            +
                  return v, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,194 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            // Copyright (c) 2006 Damien Miller <djm@mindrot.org>
         
     | 
| 
      
 2 
     | 
    
         
            +
            //
         
     | 
| 
      
 3 
     | 
    
         
            +
            // Permission to use, copy, modify, and distribute this software for any
         
     | 
| 
      
 4 
     | 
    
         
            +
            // purpose with or without fee is hereby granted, provided that the above
         
     | 
| 
      
 5 
     | 
    
         
            +
            // copyright notice and this permission notice appear in all copies.
         
     | 
| 
      
 6 
     | 
    
         
            +
            //
         
     | 
| 
      
 7 
     | 
    
         
            +
            // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
         
     | 
| 
      
 8 
     | 
    
         
            +
            // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
         
     | 
| 
      
 9 
     | 
    
         
            +
            // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
         
     | 
| 
      
 10 
     | 
    
         
            +
            // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
         
     | 
| 
      
 11 
     | 
    
         
            +
            // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
         
     | 
| 
      
 12 
     | 
    
         
            +
            // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
         
     | 
| 
      
 13 
     | 
    
         
            +
            // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            import junit.framework.TestCase;
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            /**
         
     | 
| 
      
 18 
     | 
    
         
            +
             * JUnit unit tests for BCrypt routines
         
     | 
| 
      
 19 
     | 
    
         
            +
             * @author Damien Miller
         
     | 
| 
      
 20 
     | 
    
         
            +
             * @version 0.2
         
     | 
| 
      
 21 
     | 
    
         
            +
             */
         
     | 
| 
      
 22 
     | 
    
         
            +
            public class TestBCrypt extends TestCase {
         
     | 
| 
      
 23 
     | 
    
         
            +
            	String test_vectors[][] = {
         
     | 
| 
      
 24 
     | 
    
         
            +
            			{ "", 
         
     | 
| 
      
 25 
     | 
    
         
            +
            			"$2a$06$DCq7YPn5Rq63x1Lad4cll.",
         
     | 
| 
      
 26 
     | 
    
         
            +
            			"$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s." },
         
     | 
| 
      
 27 
     | 
    
         
            +
            			{ "",
         
     | 
| 
      
 28 
     | 
    
         
            +
            			"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.",
         
     | 
| 
      
 29 
     | 
    
         
            +
            			"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye" },
         
     | 
| 
      
 30 
     | 
    
         
            +
            			{ "",
         
     | 
| 
      
 31 
     | 
    
         
            +
            			"$2a$10$k1wbIrmNyFAPwPVPSVa/ze",
         
     | 
| 
      
 32 
     | 
    
         
            +
            			"$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW" },
         
     | 
| 
      
 33 
     | 
    
         
            +
            			{ "",
         
     | 
| 
      
 34 
     | 
    
         
            +
            			"$2a$12$k42ZFHFWqBp3vWli.nIn8u",
         
     | 
| 
      
 35 
     | 
    
         
            +
            			"$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO" },
         
     | 
| 
      
 36 
     | 
    
         
            +
            			{ "a",
         
     | 
| 
      
 37 
     | 
    
         
            +
            			"$2a$06$m0CrhHm10qJ3lXRY.5zDGO",
         
     | 
| 
      
 38 
     | 
    
         
            +
            			"$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe" },
         
     | 
| 
      
 39 
     | 
    
         
            +
            			{ "a", 
         
     | 
| 
      
 40 
     | 
    
         
            +
            			"$2a$08$cfcvVd2aQ8CMvoMpP2EBfe",
         
     | 
| 
      
 41 
     | 
    
         
            +
            			"$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V." },
         
     | 
| 
      
 42 
     | 
    
         
            +
            			{ "a",
         
     | 
| 
      
 43 
     | 
    
         
            +
            			"$2a$10$k87L/MF28Q673VKh8/cPi.",
         
     | 
| 
      
 44 
     | 
    
         
            +
            			"$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u" },
         
     | 
| 
      
 45 
     | 
    
         
            +
            			{ "a",
         
     | 
| 
      
 46 
     | 
    
         
            +
            			"$2a$12$8NJH3LsPrANStV6XtBakCe",
         
     | 
| 
      
 47 
     | 
    
         
            +
            			"$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS" },
         
     | 
| 
      
 48 
     | 
    
         
            +
            			{ "abc",
         
     | 
| 
      
 49 
     | 
    
         
            +
            			"$2a$06$If6bvum7DFjUnE9p2uDeDu",
         
     | 
| 
      
 50 
     | 
    
         
            +
            			"$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i" },
         
     | 
| 
      
 51 
     | 
    
         
            +
            			{ "abc",
         
     | 
| 
      
 52 
     | 
    
         
            +
            			"$2a$08$Ro0CUfOqk6cXEKf3dyaM7O",
         
     | 
| 
      
 53 
     | 
    
         
            +
            			"$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm" },
         
     | 
| 
      
 54 
     | 
    
         
            +
            			{ "abc",
         
     | 
| 
      
 55 
     | 
    
         
            +
            			"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.",
         
     | 
| 
      
 56 
     | 
    
         
            +
            			"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi" },
         
     | 
| 
      
 57 
     | 
    
         
            +
            			{ "abc",
         
     | 
| 
      
 58 
     | 
    
         
            +
            			"$2a$12$EXRkfkdmXn2gzds2SSitu.",
         
     | 
| 
      
 59 
     | 
    
         
            +
            			"$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q" },
         
     | 
| 
      
 60 
     | 
    
         
            +
            			{ "abcdefghijklmnopqrstuvwxyz",
         
     | 
| 
      
 61 
     | 
    
         
            +
            			"$2a$06$.rCVZVOThsIa97pEDOxvGu",
         
     | 
| 
      
 62 
     | 
    
         
            +
            			"$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC" },
         
     | 
| 
      
 63 
     | 
    
         
            +
            			{ "abcdefghijklmnopqrstuvwxyz",
         
     | 
| 
      
 64 
     | 
    
         
            +
            			"$2a$08$aTsUwsyowQuzRrDqFflhge",
         
     | 
| 
      
 65 
     | 
    
         
            +
            			"$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz." },
         
     | 
| 
      
 66 
     | 
    
         
            +
            			{ "abcdefghijklmnopqrstuvwxyz",
         
     | 
| 
      
 67 
     | 
    
         
            +
            			"$2a$10$fVH8e28OQRj9tqiDXs1e1u",
         
     | 
| 
      
 68 
     | 
    
         
            +
            			"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq" },
         
     | 
| 
      
 69 
     | 
    
         
            +
            			{ "abcdefghijklmnopqrstuvwxyz",
         
     | 
| 
      
 70 
     | 
    
         
            +
            			"$2a$12$D4G5f18o7aMMfwasBL7Gpu",
         
     | 
| 
      
 71 
     | 
    
         
            +
            			"$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG" },
         
     | 
| 
      
 72 
     | 
    
         
            +
            			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD",
         
     | 
| 
      
 73 
     | 
    
         
            +
            			"$2a$06$fPIsBO8qRqkjj273rfaOI.",
         
     | 
| 
      
 74 
     | 
    
         
            +
            			"$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO" },
         
     | 
| 
      
 75 
     | 
    
         
            +
            			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD",
         
     | 
| 
      
 76 
     | 
    
         
            +
            			"$2a$08$Eq2r4G/76Wv39MzSX262hu",
         
     | 
| 
      
 77 
     | 
    
         
            +
            			"$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW" },
         
     | 
| 
      
 78 
     | 
    
         
            +
            			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD",
         
     | 
| 
      
 79 
     | 
    
         
            +
            			"$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
         
     | 
| 
      
 80 
     | 
    
         
            +
            			"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS" },
         
     | 
| 
      
 81 
     | 
    
         
            +
            			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD",
         
     | 
| 
      
 82 
     | 
    
         
            +
            			"$2a$12$WApznUOJfkEGSmYRfnkrPO",
         
     | 
| 
      
 83 
     | 
    
         
            +
            			"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC" },
         
     | 
| 
      
 84 
     | 
    
         
            +
            		};
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
            	/**
         
     | 
| 
      
 87 
     | 
    
         
            +
            	 * Entry point for unit tests
         
     | 
| 
      
 88 
     | 
    
         
            +
            	 * @param args unused
         
     | 
| 
      
 89 
     | 
    
         
            +
            	 */
         
     | 
| 
      
 90 
     | 
    
         
            +
            	public static void main(String[] args) {
         
     | 
| 
      
 91 
     | 
    
         
            +
            		junit.textui.TestRunner.run(TestBCrypt.class);
         
     | 
| 
      
 92 
     | 
    
         
            +
            	}
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
            	/**
         
     | 
| 
      
 95 
     | 
    
         
            +
            	 * Test method for 'BCrypt.hashpw(String, String)'
         
     | 
| 
      
 96 
     | 
    
         
            +
            	 */
         
     | 
| 
      
 97 
     | 
    
         
            +
            	public void testHashpw() {
         
     | 
| 
      
 98 
     | 
    
         
            +
            		System.out.print("BCrypt.hashpw(): ");
         
     | 
| 
      
 99 
     | 
    
         
            +
            		for (int i = 0; i < test_vectors.length; i++) {
         
     | 
| 
      
 100 
     | 
    
         
            +
            			String plain = test_vectors[i][0];
         
     | 
| 
      
 101 
     | 
    
         
            +
            			String salt = test_vectors[i][1];
         
     | 
| 
      
 102 
     | 
    
         
            +
            			String expected = test_vectors[i][2];
         
     | 
| 
      
 103 
     | 
    
         
            +
            			String hashed = BCrypt.hashpw(plain, salt);
         
     | 
| 
      
 104 
     | 
    
         
            +
            			assertEquals(hashed, expected);
         
     | 
| 
      
 105 
     | 
    
         
            +
            			System.out.print(".");
         
     | 
| 
      
 106 
     | 
    
         
            +
            		}
         
     | 
| 
      
 107 
     | 
    
         
            +
            		System.out.println("");
         
     | 
| 
      
 108 
     | 
    
         
            +
            	}
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
            	/**
         
     | 
| 
      
 111 
     | 
    
         
            +
            	 * Test method for 'BCrypt.gensalt(int)'
         
     | 
| 
      
 112 
     | 
    
         
            +
            	 */
         
     | 
| 
      
 113 
     | 
    
         
            +
            	public void testGensaltInt() {
         
     | 
| 
      
 114 
     | 
    
         
            +
            		System.out.print("BCrypt.gensalt(log_rounds):");
         
     | 
| 
      
 115 
     | 
    
         
            +
            		for (int i = 4; i <= 12; i++) {
         
     | 
| 
      
 116 
     | 
    
         
            +
            			System.out.print(" " + Integer.toString(i) + ":");
         
     | 
| 
      
 117 
     | 
    
         
            +
            			for (int j = 0; j < test_vectors.length; j += 4) {
         
     | 
| 
      
 118 
     | 
    
         
            +
            				String plain = test_vectors[j][0];
         
     | 
| 
      
 119 
     | 
    
         
            +
            				String salt = BCrypt.gensalt(i);
         
     | 
| 
      
 120 
     | 
    
         
            +
            				String hashed1 = BCrypt.hashpw(plain, salt);
         
     | 
| 
      
 121 
     | 
    
         
            +
            				String hashed2 = BCrypt.hashpw(plain, hashed1);
         
     | 
| 
      
 122 
     | 
    
         
            +
            				assertEquals(hashed1, hashed2);
         
     | 
| 
      
 123 
     | 
    
         
            +
            				System.out.print(".");
         
     | 
| 
      
 124 
     | 
    
         
            +
            			}
         
     | 
| 
      
 125 
     | 
    
         
            +
            		}
         
     | 
| 
      
 126 
     | 
    
         
            +
            		System.out.println("");
         
     | 
| 
      
 127 
     | 
    
         
            +
            	}
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
            	/**
         
     | 
| 
      
 130 
     | 
    
         
            +
            	 * Test method for 'BCrypt.gensalt()'
         
     | 
| 
      
 131 
     | 
    
         
            +
            	 */
         
     | 
| 
      
 132 
     | 
    
         
            +
            	public void testGensalt() {
         
     | 
| 
      
 133 
     | 
    
         
            +
            		System.out.print("BCrypt.gensalt(): ");
         
     | 
| 
      
 134 
     | 
    
         
            +
            		for (int i = 0; i < test_vectors.length; i += 4) {
         
     | 
| 
      
 135 
     | 
    
         
            +
            			String plain = test_vectors[i][0];
         
     | 
| 
      
 136 
     | 
    
         
            +
            			String salt = BCrypt.gensalt();
         
     | 
| 
      
 137 
     | 
    
         
            +
            			String hashed1 = BCrypt.hashpw(plain, salt);
         
     | 
| 
      
 138 
     | 
    
         
            +
            			String hashed2 = BCrypt.hashpw(plain, hashed1);
         
     | 
| 
      
 139 
     | 
    
         
            +
            			assertEquals(hashed1, hashed2);
         
     | 
| 
      
 140 
     | 
    
         
            +
            			System.out.print(".");
         
     | 
| 
      
 141 
     | 
    
         
            +
            		}
         
     | 
| 
      
 142 
     | 
    
         
            +
            		System.out.println("");
         
     | 
| 
      
 143 
     | 
    
         
            +
            	}
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
            	/**
         
     | 
| 
      
 146 
     | 
    
         
            +
            	 * Test method for 'BCrypt.checkpw(String, String)'
         
     | 
| 
      
 147 
     | 
    
         
            +
            	 * expecting success
         
     | 
| 
      
 148 
     | 
    
         
            +
            	 */
         
     | 
| 
      
 149 
     | 
    
         
            +
            	public void testCheckpw_success() {
         
     | 
| 
      
 150 
     | 
    
         
            +
            		System.out.print("BCrypt.checkpw w/ good passwords: ");
         
     | 
| 
      
 151 
     | 
    
         
            +
            		for (int i = 0; i < test_vectors.length; i++) {
         
     | 
| 
      
 152 
     | 
    
         
            +
            			String plain = test_vectors[i][0];
         
     | 
| 
      
 153 
     | 
    
         
            +
            			String expected = test_vectors[i][2];
         
     | 
| 
      
 154 
     | 
    
         
            +
            			assertTrue(BCrypt.checkpw(plain, expected));
         
     | 
| 
      
 155 
     | 
    
         
            +
            			System.out.print(".");
         
     | 
| 
      
 156 
     | 
    
         
            +
            		}
         
     | 
| 
      
 157 
     | 
    
         
            +
            		System.out.println("");
         
     | 
| 
      
 158 
     | 
    
         
            +
            	}
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
            	/**
         
     | 
| 
      
 161 
     | 
    
         
            +
            	 * Test method for 'BCrypt.checkpw(String, String)'
         
     | 
| 
      
 162 
     | 
    
         
            +
            	 * expecting failure
         
     | 
| 
      
 163 
     | 
    
         
            +
            	 */
         
     | 
| 
      
 164 
     | 
    
         
            +
            	public void testCheckpw_failure() {
         
     | 
| 
      
 165 
     | 
    
         
            +
            		System.out.print("BCrypt.checkpw w/ bad passwords: ");
         
     | 
| 
      
 166 
     | 
    
         
            +
            		for (int i = 0; i < test_vectors.length; i++) {
         
     | 
| 
      
 167 
     | 
    
         
            +
            			int broken_index = (i + 4) % test_vectors.length;
         
     | 
| 
      
 168 
     | 
    
         
            +
            			String plain = test_vectors[i][0];
         
     | 
| 
      
 169 
     | 
    
         
            +
            			String expected = test_vectors[broken_index][2];
         
     | 
| 
      
 170 
     | 
    
         
            +
            			assertFalse(BCrypt.checkpw(plain, expected));
         
     | 
| 
      
 171 
     | 
    
         
            +
            			System.out.print(".");
         
     | 
| 
      
 172 
     | 
    
         
            +
            		}
         
     | 
| 
      
 173 
     | 
    
         
            +
            		System.out.println("");
         
     | 
| 
      
 174 
     | 
    
         
            +
            	}
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
            	/**
         
     | 
| 
      
 177 
     | 
    
         
            +
            	 * Test for correct hashing of non-US-ASCII passwords
         
     | 
| 
      
 178 
     | 
    
         
            +
            	 */
         
     | 
| 
      
 179 
     | 
    
         
            +
            	public void testInternationalChars() {
         
     | 
| 
      
 180 
     | 
    
         
            +
            		System.out.print("BCrypt.hashpw w/ international chars: ");
         
     | 
| 
      
 181 
     | 
    
         
            +
            		String pw1 = "ππππππππ";
         
     | 
| 
      
 182 
     | 
    
         
            +
            		String pw2 = "????????";
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
            		String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());
         
     | 
| 
      
 185 
     | 
    
         
            +
            		assertFalse(BCrypt.checkpw(pw2, h1));
         
     | 
| 
      
 186 
     | 
    
         
            +
            		System.out.print(".");
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
            		String h2 = BCrypt.hashpw(pw2, BCrypt.gensalt());
         
     | 
| 
      
 189 
     | 
    
         
            +
            		assertFalse(BCrypt.checkpw(pw1, h2));
         
     | 
| 
      
 190 
     | 
    
         
            +
            		System.out.print(".");
         
     | 
| 
      
 191 
     | 
    
         
            +
            		System.out.println("");
         
     | 
| 
      
 192 
     | 
    
         
            +
            	}
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,82 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "The BCrypt engine" do
         
     | 
| 
      
 4 
     | 
    
         
            +
              specify "should calculate the optimal cost factor to fit in a specific time" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                first = BCrypt::Engine.calibrate(100)
         
     | 
| 
      
 6 
     | 
    
         
            +
                second = BCrypt::Engine.calibrate(400)
         
     | 
| 
      
 7 
     | 
    
         
            +
                second.should > first
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            describe "Generating BCrypt salts" do
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              specify "should produce strings" do
         
     | 
| 
      
 14 
     | 
    
         
            +
                BCrypt::Engine.generate_salt.should be_an_instance_of(String)
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              specify "should produce random data" do
         
     | 
| 
      
 18 
     | 
    
         
            +
                BCrypt::Engine.generate_salt.should_not equal(BCrypt::Engine.generate_salt)
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              specify "should raise a InvalidCostError if the cost parameter isn't numeric" do
         
     | 
| 
      
 22 
     | 
    
         
            +
                lambda { BCrypt::Engine.generate_salt('woo') }.should raise_error(BCrypt::Errors::InvalidCost)
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              specify "should raise a InvalidCostError if the cost parameter isn't greater than 0" do
         
     | 
| 
      
 26 
     | 
    
         
            +
                lambda { BCrypt::Engine.generate_salt(-1) }.should raise_error(BCrypt::Errors::InvalidCost)
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            describe "Autodetecting of salt cost" do
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              specify "should work" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                BCrypt::Engine.autodetect_cost("$2a$08$hRx2IVeHNsTSYYtUWn61Ou").should eq 8
         
     | 
| 
      
 34 
     | 
    
         
            +
                BCrypt::Engine.autodetect_cost("$2a$05$XKd1bMnLgUnc87qvbAaCUu").should eq 5
         
     | 
| 
      
 35 
     | 
    
         
            +
                BCrypt::Engine.autodetect_cost("$2a$13$Lni.CZ6z5A7344POTFBBV.").should eq 13
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            describe "Generating BCrypt hashes" do
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              class MyInvalidSecret
         
     | 
| 
      
 43 
     | 
    
         
            +
                undef to_s
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              before :each do
         
     | 
| 
      
 47 
     | 
    
         
            +
                @salt = BCrypt::Engine.generate_salt(4)
         
     | 
| 
      
 48 
     | 
    
         
            +
                @password = "woo"
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              specify "should produce a string" do
         
     | 
| 
      
 52 
     | 
    
         
            +
                BCrypt::Engine.hash_secret(@password, @salt).should be_an_instance_of(String)
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              specify "should raise an InvalidSalt error if the salt is invalid" do
         
     | 
| 
      
 56 
     | 
    
         
            +
                lambda { BCrypt::Engine.hash_secret(@password, 'nino') }.should raise_error(BCrypt::Errors::InvalidSalt)
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              specify "should raise an InvalidSecret error if the secret is invalid" do
         
     | 
| 
      
 60 
     | 
    
         
            +
                lambda { BCrypt::Engine.hash_secret(MyInvalidSecret.new, @salt) }.should raise_error(BCrypt::Errors::InvalidSecret)
         
     | 
| 
      
 61 
     | 
    
         
            +
                lambda { BCrypt::Engine.hash_secret(nil, @salt) }.should_not raise_error(BCrypt::Errors::InvalidSecret)
         
     | 
| 
      
 62 
     | 
    
         
            +
                lambda { BCrypt::Engine.hash_secret(false, @salt) }.should_not raise_error(BCrypt::Errors::InvalidSecret)
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              specify "should call #to_s on the secret and use the return value as the actual secret data" do
         
     | 
| 
      
 66 
     | 
    
         
            +
                BCrypt::Engine.hash_secret(false, @salt).should == BCrypt::Engine.hash_secret("false", @salt)
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
              specify "should be interoperable with other implementations" do
         
     | 
| 
      
 70 
     | 
    
         
            +
                # test vectors from the OpenWall implementation <http://www.openwall.com/crypt/>
         
     | 
| 
      
 71 
     | 
    
         
            +
                test_vectors = [
         
     | 
| 
      
 72 
     | 
    
         
            +
                  ["U*U", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"],
         
     | 
| 
      
 73 
     | 
    
         
            +
                  ["U*U*", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK"],
         
     | 
| 
      
 74 
     | 
    
         
            +
                  ["U*U*U", "$2a$05$XXXXXXXXXXXXXXXXXXXXXO", "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a"],
         
     | 
| 
      
 75 
     | 
    
         
            +
                  ["", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy"],
         
     | 
| 
      
 76 
     | 
    
         
            +
                  ["0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", "$2a$05$abcdefghijklmnopqrstuu", "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui"]
         
     | 
| 
      
 77 
     | 
    
         
            +
                ]
         
     | 
| 
      
 78 
     | 
    
         
            +
                for secret, salt, test_vector in test_vectors
         
     | 
| 
      
 79 
     | 
    
         
            +
                  BCrypt::Engine.hash_secret(secret, salt).should eql(test_vector)
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
            end
         
     |