opensecret 0.0.941 → 0.0.946

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+
5
+ =begin
6
+
7
+ Using BCrypt for password hashing has several advantages over the builtin Digest classes. First of all it has a decent interface:
8
+
9
+ gem "bcrypt-ruby"
10
+ require "bcrypt"
11
+ hashed_password = BCrypt::Password.create "my password"
12
+ hashed_password is now an instance of BCrypt::Password. You can check the password now with ==:
13
+
14
+ hashed_password == "my password" # => true
15
+ The second nice point is the built-in security. Passwords are automatically salted. Furthermore, BCrypt has a parameter cost which exponentially scales the computation time.
16
+
17
+ hashed_password1 = BCrypt::Password.create( "my password", cost: 1 )
18
+ hashed_password10 = BCrypt::Password.create( "my password", cost: 10 )
19
+ Computing hashedpassword10 is 2^9 times as expessive as computing hashedpassword1. This way you can adjust the hashing algorithm to your available resources and always use the most-expensive-to-crack hashing you can afford.
20
+
21
+ Last but definitly not least storing and restoring BCrypt::Passwords is simple as hell:
22
+
23
+ storable_string = hashed_password.to_s
24
+ restored_hash = BCrypt::Password.new storable_string
25
+ NICE!
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+ bcrypt-ruby
34
+ An easy way to keep your users' passwords secure.
35
+
36
+ http://github.com/codahale/bcrypt-ruby/tree/master
37
+ Build Status
38
+
39
+ Why you should use bcrypt()
40
+ If you store user passwords in the clear, then an attacker who steals a copy of your database has a giant list of emails and passwords. Some of your users will only have one password -- for their email account, for their banking account, for your application. A simple hack could escalate into massive identity theft.
41
+
42
+ It's your responsibility as a web developer to make your web application secure -- blaming your users for not being security experts is not a professional response to risk.
43
+
44
+ bcrypt() allows you to easily harden your application against these kinds of attacks.
45
+
46
+ Note: JRuby versions of the bcrypt gem <= 2.1.3 had a security vulnerability that was fixed in >= 2.1.4. If you used a vulnerable version to hash passwords with international characters in them, you will need to re-hash those passwords. This vulnerability only affected the JRuby gem.
47
+
48
+ How to install bcrypt
49
+ gem install bcrypt
50
+ The bcrypt gem is available on the following ruby platforms:
51
+
52
+ JRuby
53
+ RubyInstaller 1.8, 1.9, 2.0, 2.1, and 2.2 builds on win32
54
+ Any 1.8, 1.9, 2.0, 2.1, 2.2, or 2.3 Ruby on a BSD/OS X/Linux system with a compiler
55
+ How to use bcrypt() in your Rails application
56
+ Note: Rails versions >= 3 ship with ActiveModel::SecurePassword which uses bcrypt-ruby. has_secure_password docs implements a similar authentication strategy to the code below.
57
+
58
+ The User model
59
+ require 'bcrypt'
60
+
61
+ class User < ActiveRecord::Base
62
+ # users.password_hash in the database is a :string
63
+ include BCrypt
64
+
65
+ def password
66
+ @password ||= Password.new(password_hash)
67
+ end
68
+
69
+ def password=(new_password)
70
+ @password = Password.create(new_password)
71
+ self.password_hash = @password
72
+ end
73
+ end
74
+ Creating an account
75
+ def create
76
+ @user = User.new(params[:user])
77
+ @user.password = params[:password]
78
+ @user.save!
79
+ end
80
+ Authenticating a user
81
+ def login
82
+ @user = User.find_by_email(params[:email])
83
+ if @user.password == params[:password]
84
+ give_token
85
+ else
86
+ redirect_to home_url
87
+ end
88
+ end
89
+ How to use bcrypt-ruby in general
90
+ require 'bcrypt'
91
+
92
+ my_password = BCrypt::Password.create("my password")
93
+ #=> "$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa"
94
+
95
+ my_password.version #=> "2a"
96
+ my_password.cost #=> 10
97
+ my_password == "my password" #=> true
98
+ my_password == "not my password" #=> false
99
+
100
+ my_password = BCrypt::Password.new("$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa")
101
+ my_password == "my password" #=> true
102
+ my_password == "not my password" #=> false
103
+ Check the rdocs for more details -- BCrypt, BCrypt::Password.
104
+
105
+ How bcrypt() works
106
+ bcrypt() is a hashing algorithm designed by Niels Provos and David Mazières of the OpenBSD Project.
107
+
108
+ Background
109
+ Hash algorithms take a chunk of data (e.g., your user's password) and create a "digital fingerprint," or hash, of it. Because this process is not reversible, there's no way to go from the hash back to the password.
110
+
111
+ In other words:
112
+
113
+ hash(p) #=> <unique gibberish>
114
+ You can store the hash and check it against a hash made of a potentially valid password:
115
+
116
+ <unique gibberish> =? hash(just_entered_password)
117
+ Rainbow Tables
118
+ But even this has weaknesses -- attackers can just run lists of possible passwords through the same algorithm, store the results in a big database, and then look up the passwords by their hash:
119
+
120
+ PrecomputedPassword.find_by_hash(<unique gibberish>).password #=> "secret1"
121
+ Salts
122
+ The solution to this is to add a small chunk of random data -- called a salt -- to the password before it's hashed:
123
+
124
+ hash(salt + p) #=> <really unique gibberish>
125
+ The salt is then stored along with the hash in the database, and used to check potentially valid passwords:
126
+
127
+ <really unique gibberish> =? hash(salt + just_entered_password)
128
+ bcrypt-ruby automatically handles the storage and generation of these salts for you.
129
+
130
+ Adding a salt means that an attacker has to have a gigantic database for each unique salt -- for a salt made of 4 letters, that's 456,976 different databases. Pretty much no one has that much storage space, so attackers try a different, slower method -- throw a list of potential passwords at each individual password:
131
+
132
+ hash(salt + "aadvark") =? <really unique gibberish>
133
+ hash(salt + "abacus") =? <really unique gibberish>
134
+ etc.
135
+ This is much slower than the big database approach, but most hash algorithms are pretty quick -- and therein lies the problem. Hash algorithms aren't usually designed to be slow, they're designed to turn gigabytes of data into secure fingerprints as quickly as possible. bcrypt(), though, is designed to be computationally expensive:
136
+
137
+ Ten thousand iterations:
138
+ user system total real
139
+ md5 0.070000 0.000000 0.070000 ( 0.070415)
140
+ bcrypt 22.230000 0.080000 22.310000 ( 22.493822)
141
+ If an attacker was using Ruby to check each password, they could check ~140,000 passwords a second with MD5 but only ~450 passwords a second with bcrypt().
142
+
143
+ Cost Factors
144
+ In addition, bcrypt() allows you to increase the amount of work required to hash a password as computers get faster. Old passwords will still work fine, but new passwords can keep up with the times.
145
+
146
+ The default cost factor used by bcrypt-ruby is 10, which is fine for session-based authentication. If you are using a stateless authentication architecture (e.g., HTTP Basic Auth), you will want to lower the cost factor to reduce your server load and keep your request times down. This will lower the security provided you, but there are few alternatives.
147
+
148
+ To change the default cost factor used by bcrypt-ruby, use BCrypt::Engine.cost = new_value:
149
+
150
+ BCrypt::Password.create('secret').cost
151
+ #=> 10, the default provided by bcrypt-ruby
152
+
153
+ # set a new default cost
154
+ BCrypt::Engine.cost = 8
155
+ BCrypt::Password.create('secret').cost
156
+ #=> 8
157
+ The default cost can be overridden as needed by passing an options hash with a different cost:
158
+
159
+ BCrypt::Password.create('secret', :cost => 6).cost #=> 6
160
+ More Information
161
+ bcrypt() is currently used as the default password storage hash in OpenBSD, widely regarded as the most secure operating system available.
162
+
163
+ For a more technical explanation of the algorithm and its design criteria, please read Niels Provos and David Mazières' Usenix99 paper: http://www.usenix.org/events/usenix99/provos.html
164
+
165
+ If you'd like more down-to-earth advice regarding cryptography, I suggest reading Practical Cryptography by Niels Ferguson and Bruce Schneier: http://www.schneier.com/book-practical.html
166
+
167
+ Etc
168
+
169
+ =end
170
+
@@ -18,7 +18,7 @@ module OpenSecret
18
18
  # @param domain [String] the DOMAIN eg lecturers@harvard for your family or work group.
19
19
  # @param store_url [String] the STORE_URL for connecting to the backend storage service
20
20
  #
21
- def self.vreify_domain domain, store_url
21
+ def self.verify_domain domain, store_url
22
22
 
23
23
  # -> read config file map
24
24
  # -> create new domain in map
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
- module OpenSession
3
+ module OpenError
4
4
 
5
5
  # This class is the parent to all opensession errors
6
6
  # that originate from the command line.
@@ -10,7 +10,7 @@ module OpenSession
10
10
  # - a problem with the input or
11
11
  # - a problem with the current state or
12
12
  # - a predictable future problem
13
- class OpenSessionError < StandardError
13
+ class CliError < StandardError
14
14
 
15
15
 
16
16
  # Initialize the error and provide a culprit
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # XXXXXXXXXXXXXXXXXXXX is this printed by yard
4
+ # XXXXXXXXXXXXXXXXXXXX is this printed by yard
5
+ # XXXXXXXXXXXXXXXXXXXX is this printed by yard
6
+ # XXXXXXXXXXXXXXXXXXXX is this printed by yard
7
+ # @note are modules documented by yard
8
+ # or are they simply ignored.
9
+ module OpenError
10
+
11
+ =begin
12
+ # Throw this error if the configured safe directory points to a file.
13
+ class SafeDirectoryIsFile < OpenError::CliError; end;
14
+
15
+ # Throw this error if safe directory path is either nil or empty.
16
+ class SafeDirNotConfigured < OpenError::CliError; end;
17
+
18
+ # Throw this error if the email address is nil, empty or less than 5 characters.
19
+ class EmailAddrNotConfigured < OpenError::CliError; end;
20
+
21
+ # Throw this error if the store url is either nil or empty.
22
+ class StoreUrlNotConfigured < OpenError::CliError; end;
23
+
24
+ # Throw if "prime folder" name occurs 2 or more times in the path.
25
+ class SafePrimeNameRepeated < OpenError::CliError; end;
26
+
27
+ # Throw if "prime folder" name occurs 2 or more times in the path.
28
+ class SafePrimeNameNotAtEnd < OpenError::CliError; end;
29
+ =end
30
+
31
+ end
@@ -1,7 +1,7 @@
1
1
 
2
2
  [global]
3
3
 
4
- min.passwd.len = rb>> 16
4
+ min.passwd.len = rb>> 6
5
5
  nickname = godzilla
6
6
  root.domain = devopswiki.co.uk
7
7
  env.var.name = SECRET_MATERIAL
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+
5
+ ## ########################### ##
6
+ ## Trial and Error Scratch-Pad ##
7
+ ## ########################### ##
8
+
9
+ x = "messagess"
10
+
11
+ x += "x" until x.bytesize % 8 == 0
12
+
13
+ puts "Now x string is [#{x}]."
14
+ puts "x is now [#{x.length}] characters long."
@@ -1,18 +1,26 @@
1
1
  require "thor"
2
2
  require "fileutils"
3
+
3
4
  require "session/time.stamp"
4
5
  require "session/attributes"
5
6
  require "logging/gem.logging"
7
+ require "session/require.gem"
6
8
 
7
- require "usecase/usecases/safe"
8
- require "usecase/usecases/init"
9
-
9
+ # Include the logger mixins so that every class can enjoy "import free"
10
+ # logging through pointers to the (extended) log behaviour.
10
11
  include OpenLogger
11
12
 
12
13
  # This standard out sync command flushes text destined for STDOUT immediately,
13
14
  # without waiting either for a full cache or script completion.
14
15
  $stdout.sync = true
15
16
 
17
+ # Recursively require all gems that are either in or under the directory
18
+ # that this code is executing from. Only use this tool if your library is
19
+ # relatively small but highly interconnected. In these instances it raises
20
+ # productivity and reduces harassing "not found" exceptions.
21
+ OpenSession::RecursivelyRequire.now( __FILE__ )
22
+
23
+
16
24
  # This command line processor extends the Thor gem CLI tools in order to
17
25
  #
18
26
  # - read the posted commands, options and switches
@@ -17,7 +17,7 @@
17
17
  # --- and Inherits from => ProvisionEcoService --- #
18
18
  # --- Found in File => provision.services/provision.eco.service.rb --- #
19
19
  # --- --------------------------------------------------------------------------- --- #
20
- class EcoSystem < EcoFaculty
20
+ class EcoSystem
21
21
 
22
22
  # -- -------------------------------------------------------------- -- #
23
23
  # -- eco-system [provisioning] begins in earnest here. By making -- #
@@ -16,7 +16,7 @@
16
16
  # -- [3] - an open [public key] to be placed on web accessible destination
17
17
  # -- [4] - a message detailing that a new keypair is now created/installed
18
18
  # --
19
- class CryptKeys < EcoSystem
19
+ class CryptKeys
20
20
 
21
21
 
22
22
  def core_provisioning
@@ -16,7 +16,7 @@
16
16
  # -- [7] - unlock the private key with the amalgamated password
17
17
  # -- [8] - decrypt the text into the pre-configured destination
18
18
  # --
19
- class Decrypt < EcoSystem
19
+ class Decrypt
20
20
 
21
21
 
22
22
  def core_provisioning
@@ -42,7 +42,7 @@
42
42
  # -- Example 7 is the best for when exclamation marks and soft quotes exist.
43
43
  # -- Decrypted string is => no!and(oh)my
44
44
  # --
45
- class Encrypt < EcoSystem
45
+ class Encrypt
46
46
 
47
47
  def core_provisioning
48
48
 
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ module OpenSecret
5
+
6
+ require "base64"
7
+
8
+
9
+ # An {OpenSecret::Cipher} is a base class that enables cipher varieties
10
+ # to be plugged and played with minimal effort. This Cipher implements much
11
+ # of the use case functionality - all extension classes need to do, is
12
+ # to subclass and implement only the core behaviour that define its identity.
13
+ #
14
+ # == Double Encryption | Cipher Parent vs Cipher Child
15
+ #
16
+ # Double encryption first with a symmetric and then an asymmetric one fulfills
17
+ # the +opensecret+ promise of making the stored ciphertext utterly worthless.
18
+ #
19
+ # The child ciphers implement the inner symmetric encyption whilst the parent
20
+ # implements the outer asymmetric encryption algorithm.
21
+ #
22
+ # The process is done twice resulting in two stores that are mirrored in structure.
23
+ # The front end store holds doubly encrypted keys whist the backend store holds
24
+ # the doubly encrypted secrets.
25
+ #
26
+ # Attackers wouldn't be able to distinguish one from the other. Even if they
27
+ # theoretically cracked the asymmetric encryption - they would then be faced
28
+ # with a powerful symmetric encryption algorithm which could be any one of the
29
+ # leading ciphers such as TwoFish or the Advanced Encryption Standard (AES).
30
+ #
31
+ # == How to Implement a Cipher
32
+ #
33
+ # Extend this base class to inherit lots of +unexciting+ functionality
34
+ # that essentially
35
+ #
36
+ # - manages the main encryption and decryption use case flow
37
+ # - +concatenates+ the symmetric encryption meta data with ciphertext +after encryption+
38
+ # - _splits_ and objectifies the key/value metadata plus ciphertext +before decryption+
39
+ # - +handles file read/writes+ in conjunction with the store plugins
40
+ # - handles +exceptions+ and +malicious input detection+ and incubation
41
+ # - +_performs the asymmetric encryption_+ of the cipher's symmetrically encrypted output
42
+ #
43
+ # -------------------------------------
44
+ # What Behaviour Must Ciphers Implement
45
+ # -------------------------------------
46
+ #
47
+ # Ciphers bring the cryptographic mathematics and implementation algorithms
48
+ # to the table. So when at home they must implement
49
+ #
50
+ # - <tt>do_symmetric_encryption(plain_text)</tt> - resulting in ciphertext
51
+ # - <tt>do_symmetric_decryption(ciphertext, encryption_dictionary)</tt> &raquo; plaintext
52
+ #
53
+ # and also set the <tt>@encryption_dictionary</tt> hash (map) of pertinent
54
+ # key/value pairs including the encryption algorithm, the encryption key and
55
+ # the ciphertext signature to thwart any at-rest tampering.
56
+ #
57
+ # That's It. Cipher children can rely on the {OpenSecret::Cipher} parent to
58
+ # do the nitty gritty of file-handling plus managing stores and paths.
59
+ class Cipher
60
+
61
+ # Many ciphers (like Blowfish) constrains plain text lengths to multiples
62
+ # of 8 (or 16) and a common +right pad with spaces+ strategy is employed
63
+ # as a workaround. opensecret does it diferently.
64
+ #
65
+ # == No Space Padding? | Why Not?
66
+ #
67
+ # If opensecret padded plaintext (ending in one or more spaces) with
68
+ # spaces, the decrypt phase (after right stripping spaces) would return
69
+ # plain text string +shorter than the original+.
70
+ #
71
+ # == So How is Padding Done?
72
+ #
73
+ # Instead of single space padding - opensecret uses an unlikely 7 character
74
+ # padder which is repeated until the multiple is reached.
75
+ #
76
+ # <tt><-|@|-></tt>
77
+ #
78
+ # == So How is Padding Done?
79
+ #
80
+ # The +padder length must be a prime number+ or infinite loops could occur.
81
+ #
82
+ # If the padder string is likely to occur in the plain text, another
83
+ # padder (or strategy) should and could be employed.
84
+ #
85
+ TEXT_PADDER = "<-|@|->"
86
+
87
+ @@symmetric_cipher_keyname = "symmetric.cipher"
88
+ @@encryption_key_keyname = "encryption.key"
89
+ @@cipher_signature_keyname = "cipher.signature"
90
+
91
+ # Ciphers use +symmetric algorithms+ to encrypt the given text, which
92
+ # is then wrapped up along with the encryption key and other +metadata+
93
+ # pertinent to the algorithm, they then encrypt this bundle with the
94
+ # +public key+ provided and return the text that can safely be stored in
95
+ # a text file.
96
+ #
97
+ # Ciphers should never interact with the filesystem which makes them
98
+ # reusable in API and remote store scenarios.
99
+ #
100
+ # Binary files should be converted into the base64 format before being
101
+ # presented to ciphers.
102
+ #
103
+ # Every component in the pipeline bears the responsibility for nullifying
104
+ # and rejecting malicious content.
105
+ #
106
+ # @param public_key [String] used for encrypting the key/metadata bundle
107
+ # @param plain_text [String] the plain (or base64 encoded) text to encrypt
108
+ #
109
+ # @return [String] doubly (symmetric and asymmetric) encrypted cipher text
110
+ def encrypt_it public_key, plain_text
111
+
112
+ symmetric_ciphertext = do_symmetric_encryption plain_text
113
+ plain_paraphernalia = glue @encryption_dictionary, symmetric_ciphertext
114
+ return do_asymmetric_encryption public_key, plain_paraphernalia
115
+
116
+ end
117
+
118
+
119
+ # The decrypted cipher-text is actually a two part bundle consisting of
120
+ # a dictionary and then more cipher-text which is then decrypted using
121
+ # the symmetric key held within the dictionary.
122
+ #
123
+ # The private key revealed a dictionary holding the symmetric encryption
124
+ # key and other details pertinent to the cipher's encrypt/decrypt process.
125
+ # This data is used to work on the cipher text, eventually revealing the
126
+ # original plain text (which half the time is actually a private key).
127
+ #
128
+ # Ciphers should never interact with the filesystem which makes them
129
+ # reusable in API and remote store scenarios.
130
+ #
131
+ # @param private_key [String] reveals the dictionary and more ciphertext
132
+ # @param cipher_text [String] the crypted (base64 encoded) text bundle
133
+ #
134
+ # @return [String] the plain or encoded text first pushed into the cipher
135
+ def decrypt_it private_key, cipher_text
136
+
137
+ paraphernalia = do_asymmetric_decryption private_key, cipher_text
138
+ return do_symmetric_decryption get_dictionary(paraphernalia), get_crypt(paraphernalia)
139
+
140
+ end
141
+
142
+
143
+
144
+ def encrypt_usecase public_key, secret_path, stores, plain_text
145
+
146
+ key_pair = KeyPair.new
147
+
148
+ secret_bundle_crypt = encrypt_it key_pair.public_key, plain_text
149
+ stores[1].write_path( secret_path, secret_bundle_crypt )
150
+
151
+ secret_key_crypt = encrypt_it public_key, key_pair.private_key
152
+ stores[0].write_path( secret_path, secret_key_crypt )
153
+
154
+ end
155
+
156
+
157
+
158
+ def decrypt_usecase private_key, public_key, secret_path, stores
159
+
160
+ inner_private_key = decrypt_it( private_key, stores[0].read_path(secret_path) )
161
+ secret_text = decrypt_it( inner_private_key, stores[1].read_path(secret_path) )
162
+
163
+ encrypt_usecase public_key, secret_path, stores, secret_text
164
+ return secret_text
165
+
166
+ end
167
+
168
+
169
+
170
+ def rekey_usecase old_private_key, new_public_key, secret_path, store
171
+
172
+
173
+ end
174
+
175
+
176
+ end
177
+
178
+
179
+ end