Files: 2ec9dacebb3345a9db2541e7dc7e7bea00147661 / lib / bcrypt / engine.rb
3600 bytesRaw
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 |
117 |
Built with git-ssb-web