trocla 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0fe321c3e5f61203499ad978f32fef9b9617bf54
4
- data.tar.gz: 276a6191e0342e7d26c185a06b383f357483b300
2
+ SHA256:
3
+ metadata.gz: 2d6d84b42cc62b4ab04eea0478d9e4801939c1d31accd0dba4a27e272f56dad2
4
+ data.tar.gz: 6a322a8ae343b6723476b53e9ff7158adaa8d1537bbb46fbc81ef5ebd5a6cff2
5
5
  SHA512:
6
- metadata.gz: cda445d5bab3d8b96a56990b2b9e447869a44e02bb4f28ca9d29ffce8578aa1e1cb4d5dedb7bec0e0102cc42416fed6361f1c9ea276c2b9fa2b1b7e1ab99698a
7
- data.tar.gz: a59f1905c9877eda6ff3614b84f50e6598c75be420ba78c4e324f0b5c6c72584efda35a711477c35571aeac804de4e8f6ff1a3de80e3f2f220a825ce0d52056d
6
+ metadata.gz: 9bf44101c733c550f2ca6cd76e9a3788e8e2ac9f44576af255f6ea42271b0cb0cc6c004c6d66c9a4c49e5d74ab647e1cfc8f52bf1c0944176e5e529334effd11
7
+ data.tar.gz: d8e988c515297525975ca3b2339cfe81ba21444cf45c42637131d3f949871ab6a993f30b62ac2992d8388a2874bf8d4d43fcc9714383ac55fb22ad176f340dbc
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Ruby
3
+ on: [push, pull_request]
4
+ jobs:
5
+ spec:
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - name: check out repository
9
+ uses: actions/checkout@v3
10
+ - name: set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ bundler-cache: true
14
+ ruby-version: ${{ matrix.ruby }}
15
+ - name: install dependencies
16
+ run: bundle install
17
+ - name: install wireguard
18
+ run: sudo apt install -y wireguard
19
+ - name: run rspec
20
+ run: bundle exec rake spec
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ ruby: [2.5, 2.7, 3.0, 3.1]
data/.rspec CHANGED
@@ -1 +1 @@
1
- --color
1
+ --color --format documentation
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## to 0.5.0
4
+
5
+ * moved from travis ci to github actions (#73) - Thank you [Georg-g](https://github.com/geor-g)
6
+ * Support expire in vault (#71) - Thank you [Steffy Fort](https://github.com/fe80)
7
+ * Syntax improvements (#70) - Thank you [Steffy Fort](https://github.com/fe80)
8
+ * Add SCRAM-SHA-256 postgres support (#69) - Thank you [Steffy Fort](https://github.com/fe80)
9
+ * Support destroying and entry to properly clean up in vault (#68) - Thank you [Steffy Fort](https://github.com/fe80)
10
+ * Support search with vault backend (#67) - Thank you [Steffy Fort](https://github.com/fe80)
11
+ * Add wireguard format (#65) - Thank you [Jonas Genannt](https://github.com/hggh)
12
+ * Expand search path for sample config - Thank you [Anarcat](https://github.com/anarcat)
13
+
14
+ ## to 0.4.0
15
+
16
+ * Add vault backend (#61) - Thank you [Steffy Fort](https://github.com/fe80)
17
+ * Add sshkey format similar to the OpenSSL - Thank you [Raphaël Rondeau](https://github.com/rrondeau)
18
+ * format/x509 allow to render 'publickeyonly' (#62) - Thank you [Thomas Weißschuh](https://github.com/t-8ch)
19
+ * Add a method to search for keys and list all formats of a key (#49) - Thank you - [Steffy Fort](https://github.com/fe80)
20
+ * Proper return code on cli (#57) - Thank you [Steffy Fort](https://github.com/fe80)
21
+ * expand search path for sample config file to fix autopkgtest (#64) - Thank you [anarcat](https://github.com/anarcat)
22
+ * drop support for ruby < 2.7 & update dependencies
23
+ * skip self-signed cert verification test on newer openssl version (#63)
24
+ * Fix reseting passwords when using SSL encryption (#52)
25
+
3
26
  ## to 0.3.0
4
27
 
5
28
  * Add open method to be able to immediately close a trocla store after using it - thanks martinpfeiffer
data/Gemfile CHANGED
@@ -1,50 +1,24 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
  # Add dependencies required to use your gem here.
3
3
  # Example:
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
 
6
- if RUBY_VERSION.to_f <= 2.2
7
- gem 'rack', '< 2.0'
8
- end
9
-
10
- if RUBY_VERSION.to_f < 2.1
11
- gem 'nokogiri', '< 1.7'
12
- end
13
-
14
- if RUBY_VERSION.to_f > 1.8
15
- gem "moneta"
16
- gem "highline"
17
- else
18
- gem "moneta", "~> 0.7.20"
19
- gem "highline", "~> 1.6.2"
20
- gem 'rake', '< 11'
21
- gem 'git', '< 1.3'
22
- end
6
+ gem 'highline', '~> 2.0.0'
7
+ gem 'moneta', '~> 1.4.0'
23
8
 
24
9
  if defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'jruby')
25
10
  gem 'jruby-openssl'
26
11
  end
27
- gem "bcrypt"
12
+ gem 'bcrypt'
13
+ gem 'openssl'
14
+ gem 'sshkey'
28
15
 
29
16
  # Add dependencies to develop your gem here.
30
17
  # Include everything needed to run rake, tests, features, etc.
31
18
  group :development do
32
- if RUBY_VERSION.to_f > 1.8
33
- gem "rspec"
34
- gem "rdoc"
35
- if RUBY_VERSION.to_f < 2.2
36
- gem 'jeweler', '< 2.2'
37
- else
38
- gem "jeweler"
39
- end
40
- if RUBY_VERSION.to_f < 2.0
41
- gem 'public_suffix', '~> 1.4.6'
42
- end
43
- else
44
- gem "rspec", "~> 2.4"
45
- gem "rdoc", "~> 3.8"
46
- gem "jeweler", "~> 1.6"
47
- gem "addressable", "~> 2.3.8"
48
- end
19
+ gem 'addressable'
20
+ gem 'jeweler'
21
+ gem 'rdoc'
22
+ gem 'rspec'
49
23
  gem 'rspec-pending_for'
50
24
  end
data/README.md CHANGED
@@ -24,7 +24,7 @@ retrieve (by deleting) the plain password and send it to the user. Puppet
24
24
  will still simply retrieve the hashed password that is stored in trocla,
25
25
  while the plain password is not anymore stored on the server.
26
26
 
27
- Be default trocla uses moneta to store the passwords and can use any kind of
27
+ By default trocla uses moneta to store the passwords and can use any kind of
28
28
  key/value based storage supported by moneta for trocla. By default it uses a
29
29
  simple yaml file.
30
30
  However, since version 0.2.0 trocla also supports a pluggable storage backend
@@ -165,8 +165,9 @@ options to work properly. These are documented here:
165
165
 
166
166
  ### pgsql
167
167
 
168
- Password hashes for PostgreSQL servers. Requires the option `username` to be set
169
- to the username to which the password will be assigned.
168
+ Password hashes for PostgreSQL servers. Since postgesql 10 you can use the sha256 hash, you have two options:
169
+ * Create a ssh256 hash password with option `encode: sha256` (default value)
170
+ * Create a md5 hash, the username is require for the salt key, with option `encode: md5` and `username: your_user`
170
171
 
171
172
  ### bcrypt
172
173
 
@@ -212,8 +213,36 @@ Additional options are:
212
213
 
213
214
  Output render options are:
214
215
 
215
- certonly If set to true the x509 format will return only the certificate
216
- keyonly If set to true the x509 format will return only the private key
216
+ certonly If set to true the x509 format will return only the certificate
217
+ keyonly If set to true the x509 format will return only the private key
218
+ publickeyonly If set to true the x509 format will return only the public key
219
+
220
+ ### sshkey
221
+
222
+ This format generate a ssh keypair
223
+
224
+ Additional options are:
225
+
226
+ type The ssh key type (rsa, dsa). Default: rsa
227
+ bits Specifies the number of bits in the key to create. Default: 2048
228
+ comment Specifies a comment.
229
+ passphrase Specifies a passphrase.
230
+
231
+ Output render options are:
232
+
233
+ pubonly If set to true the sshkey format will return only the ssh public key
234
+ privonly If set to true the sshkey format will return only the ssh private key
235
+
236
+ ### wireguard
237
+
238
+ This format generate a keypair for WireGuard.
239
+
240
+ The format requires the wg binary from WireGuard userland utilities.
241
+
242
+ Output render options are:
243
+
244
+ pubonly If set to true the wireguard format will return only the public key
245
+ privonly If set to true the wireguard format will return only the private key
217
246
 
218
247
  ## Installation
219
248
 
@@ -256,6 +285,7 @@ Such a store is a simple class that implements Trocla::Store and at the moment t
256
285
 
257
286
  * Moneta - the default store using [moneta](https://rubygems.org/gems/moneta) to delegate storing the values
258
287
  * Memory - simple inmemory backend. Mainly used for testing.
288
+ * Vault - modern secrets storage by HashiCorp, require the ruby gem [vault](https://github.com/hashicorp/vault-ruby)
259
289
 
260
290
  The backend is chosen based on the `store` configuration option. If it is a symbol, we expect it to be a store that we ship with trocla. Otherwise, we assume it to be a fully qualified ruby class name, that inherits from Trocla::Store. If trocla should load an additional library to be able to find your custom store class, you can set `store_require` to whatever should be passed to a ruby require statement.
261
291
 
@@ -272,6 +302,8 @@ We expect storage backends to implement support for the `expires` option, so tha
272
302
 
273
303
  New backends should be tested using the provided shared example.
274
304
 
305
+ > **WARNING**: Vault backend use metadatas. It's set if an option is define. `expire` is automaticly change to `delete_version_after`, and you can use an interger or [format string](https://www.vaultproject.io/api-docs/secret/kv/kv-v2#parameters)
306
+
275
307
  #### Moneta backends
276
308
 
277
309
  Trocla uses moneta as its default storage backend and hence can store your passwords in any of moneta's supported backends. By default it uses the yaml backend, which is configured as followed:
@@ -298,6 +330,33 @@ store_options:
298
330
 
299
331
  These examples are by no way complete, moneta has much more to offer. Please have a look at [moneta's documentation](https://github.com/minad/moneta/blob/master/README.md) for further information.
300
332
 
333
+ #### Vault backend
334
+
335
+ [Vault](https://www.vaultproject.io/) is a modern secret storage supported by HashiCorp, which works with a REST API. You can create multiple storage engine.
336
+
337
+ To use vault with trocla you need to create a kv (key/value) storage engine on the vault side. Trocla can use [v1](https://www.vaultproject.io/docs/secrets/kv/kv-v1) and [v2](https://www.vaultproject.io/docs/secrets/kv/kv-v2) API endpoints, but it's recommended to use the v2 (native hash object, history, acl...).
338
+
339
+ You need to install the `vault` gem to be able to use the vault backend, which is not included in the default dependencies for trocla.
340
+
341
+ With vault storage, the terminology changes:
342
+ * `mount`, this is the name of your kv engine
343
+ * `key`, this is the biggest change. As usual with trocla, the key is a simple string. With the vault kv engine, the key map to a path, so you can have a key like `my/path/key` for structured your data
344
+ * `secret`, is the data content of your key. This is a simple hash with key (format) and value (the secret content of your format)
345
+
346
+ The trocla mapping works the same way as with a moneta or file backend.
347
+
348
+ The `store_options` are a dynamic argument for initializer [Vault::Client](https://github.com/hashicorp/vault-ruby/blob/master/lib/vault/client.rb) class (except `:mount`, used to defined the kv name). You can define only one kv mount.
349
+
350
+ ```YAML
351
+ store: :vault
352
+ store_options:
353
+ :mount: kv
354
+ :token: s.Tok3n
355
+ :address: https://vault.local
356
+ ```
357
+
358
+ With Vault when you delete a key, you don't delete all key content. The metadatas, like history, are still here and the endpoint are not delete. If you prefere to destroy all key content you can add `:destroy: true` in the `store_options:` hash.
359
+
301
360
  ### Backend encryption
302
361
 
303
362
  By default trocla does not encrypt anything it stores. You might want to let Trocla encrypt all your passwords, at the moment the only supported way is SSL.
data/bin/trocla CHANGED
@@ -43,25 +43,25 @@ OptionParser.new do |opts|
43
43
  options[:ask_password] = false
44
44
  options[:password] = pass
45
45
  end
46
-
47
46
  end.parse!
48
47
 
49
48
  def create(options)
50
- [ Trocla.new(options.delete(:config_file)).password(
49
+ [Trocla.new(options.delete(:config_file)).password(
51
50
  options.delete(:trocla_key),
52
51
  options.delete(:trocla_format),
53
- options.merge(YAML.load(options.delete(:other_options).shift.to_s)||{})
54
- ) , 0 ]
52
+ options.merge(YAML.safe_load(options.delete(:other_options).shift.to_s) || {})
53
+ ), 0]
55
54
  end
56
55
 
57
56
  def get(options)
58
57
  res = Trocla.new(options.delete(:config_file)).get_password(
59
58
  options.delete(:trocla_key),
60
59
  options.delete(:trocla_format),
61
- options.merge(YAML.load(options.delete(:other_options).shift.to_s)||{})
60
+ options.merge(YAML.safe_load(options.delete(:other_options).shift.to_s) || {})
62
61
  )
63
- [ res, res.nil? ? 1 : 0 ]
62
+ [res, res.nil? ? 1 : 0]
64
63
  end
64
+
65
65
  def set(options)
66
66
  if options.delete(:ask_password)
67
67
  require 'highline/import'
@@ -69,7 +69,7 @@ def set(options)
69
69
  pwd2 = ask('Repeat password: ') { |q| q.echo = 'x' }.to_s
70
70
  unless password == pwd2
71
71
  STDERR.puts 'Passwords did not match, exiting!'
72
- return [ nil, 1 ]
72
+ return [nil, 1]
73
73
  end
74
74
  else
75
75
  password = options.delete(:password) || STDIN.read.chomp
@@ -78,35 +78,52 @@ def set(options)
78
78
  no_format = options.delete('no_format')
79
79
  trocla = Trocla.new(options.delete(:config_file))
80
80
  value = if no_format
81
- password
82
- else
83
- trocla.formats(format).format(password, (YAML.load(options.delete(:other_options).shift.to_s)||{}))
84
- end
81
+ password
82
+ else
83
+ trocla.formats(format).format(password, (YAML.safe_load(options.delete(:other_options).shift.to_s) || {}))
84
+ end
85
85
  trocla.set_password(
86
86
  options.delete(:trocla_key),
87
87
  format,
88
88
  value
89
89
  )
90
- [ '', 0 ]
90
+ ['', 0]
91
91
  end
92
92
 
93
93
  def reset(options)
94
- [ Trocla.new(options.delete(:config_file)).reset_password(
94
+ [Trocla.new(options.delete(:config_file)).reset_password(
95
95
  options.delete(:trocla_key),
96
96
  options.delete(:trocla_format),
97
- options.merge(YAML.load(options.delete(:other_options).shift.to_s)||{})
98
- ), 0 ]
97
+ options.merge(YAML.safe_load(options.delete(:other_options).shift.to_s) || {})
98
+ ), 0]
99
99
  end
100
100
 
101
101
  def delete(options)
102
- [ Trocla.new(options.delete(:config_file)).delete_password(
102
+ res = Trocla.new(options.delete(:config_file)).delete_password(
103
103
  options.delete(:trocla_key),
104
104
  options.delete(:trocla_format)
105
- ), 0 ]
105
+ )
106
+ [res, res.nil? ? 1 : 0]
106
107
  end
107
108
 
108
109
  def formats(options)
109
- "Available formats: #{Trocla::Formats.all.join(', ')}"
110
+ key = (options.delete(:trocla_key) || '')
111
+ if key.empty?
112
+ "Available formats: #{Trocla::Formats.all.join(', ')}"
113
+ else
114
+ res = Trocla.new(options.delete(:config_file)).available_format(
115
+ key,
116
+ options.merge(YAML.safe_load(options.delete(:other_options).shift.to_s) || {})
117
+ )
118
+ [res.nil? ? res : res.join(', '), res.nil? ? 1 : 0]
119
+ end
120
+ end
121
+
122
+ def search(options)
123
+ res = Trocla.new(options.delete(:config_file)).search_key(
124
+ options.delete(:trocla_key)
125
+ )
126
+ [res.nil? ? res : res.join("\n"), res.nil? ? 1 : 0]
110
127
  end
111
128
 
112
129
  def check_format(format_name)
@@ -119,30 +136,30 @@ def check_format(format_name)
119
136
  end
120
137
  end
121
138
 
122
- actions=['create','get','set','reset','delete', 'formats' ]
139
+ actions = ['create', 'get', 'set', 'reset', 'delete', 'formats', 'search']
123
140
 
124
141
  if (action=ARGV.shift) && actions.include?(action)
125
- options[:trocla_key] = ARGV.shift
126
- options[:trocla_format] = ARGV.shift
127
- options[:other_options] = ARGV
128
- check_format(options[:trocla_format]) unless ['delete','formats'].include?(action)
129
- begin
130
- result, excode = send(action,options)
131
- if result
132
- puts result.is_a?(String) ? result : result.inspect
133
- end
134
- rescue Exception => e
135
- unless e.message == 'exit'
136
- STDERR.puts "Action failed with the following message: #{e.message}"
137
- STDERR.puts '(See full trace by running task with --trace)'
138
- end
139
- raise e if options[:trace]
140
- exit 1
142
+ options[:trocla_key] = ARGV.shift
143
+ options[:trocla_format] = ARGV.shift
144
+ options[:other_options] = ARGV
145
+ check_format(options[:trocla_format]) unless ['delete','formats','search'].include?(action)
146
+ begin
147
+ result, excode = send(action, options)
148
+ if result
149
+ puts result.is_a?(String) ? result : result.inspect
141
150
  end
142
- exit excode.nil? ? 0 : excode
143
- else
144
- STDERR.puts "Please supply one of the following actions: #{actions.join(', ')}"
145
- STDERR.puts "Use #{$0} --help to get a list of options for these actions"
151
+ rescue Exception => e
152
+ unless e.message == 'exit'
153
+ STDERR.puts "Action failed with the following message: #{e.message}"
154
+ STDERR.puts '(See full trace by running task with --trace)'
155
+ end
156
+ raise e if options[:trace]
157
+
146
158
  exit 1
159
+ end
160
+ exit excode.nil? ? 0 : excode
161
+ else
162
+ STDERR.puts "Please supply one of the following actions: #{actions.join(', ')}"
163
+ STDERR.puts "Use #{$0} --help to get a list of options for these actions"
164
+ exit 1
147
165
  end
148
-
data/lib/VERSION CHANGED
@@ -1,4 +1,4 @@
1
1
  major:0
2
- minor:3
2
+ minor:5
3
3
  patch:0
4
4
  build:
@@ -7,9 +7,9 @@ store_options:
7
7
 
8
8
  encryption: :none
9
9
  options:
10
- random: true
11
- length: 16
12
- charset: default
10
+ random: true
11
+ length: 16
12
+ charset: default
13
13
 
14
14
  profiles:
15
15
  rootpw:
@@ -44,4 +44,3 @@ profiles:
44
44
  days: 2
45
45
  # 1 day
46
46
  expires: 86400
47
-
@@ -7,4 +7,3 @@ class Trocla::Encryptions::None < Trocla::Encryptions::Base
7
7
  value
8
8
  end
9
9
  end
10
-
@@ -5,7 +5,7 @@ class Trocla::Encryptions::Ssl < Trocla::Encryptions::Base
5
5
  def encrypt(value)
6
6
  ciphertext = ''
7
7
  value.scan(/.{0,#{chunksize}}/m).each do |chunk|
8
- ciphertext += Base64.encode64(public_key.public_encrypt(chunk)).gsub("\n",'')+"\n" if chunk
8
+ ciphertext += Base64.encode64(public_key.public_encrypt(chunk)).gsub("\n", '') + "\n" if chunk
9
9
  end
10
10
  ciphertext
11
11
  end
@@ -21,21 +21,21 @@ class Trocla::Encryptions::Ssl < Trocla::Encryptions::Base
21
21
  private
22
22
 
23
23
  def chunksize
24
- public_key.n.num_bytes - 11
24
+ public_key.n.num_bytes - 11
25
25
  end
26
26
 
27
27
  def private_key
28
- @private_key ||= begin
29
- file = require_option(:private_key)
30
- OpenSSL::PKey::RSA.new(File.read(file), nil)
31
- end
28
+ @private_key ||= begin
29
+ file = require_option(:private_key)
30
+ OpenSSL::PKey::RSA.new(File.read(file), nil)
31
+ end
32
32
  end
33
33
 
34
34
  def public_key
35
- @public_key ||= begin
36
- file = require_option(:public_key)
37
- OpenSSL::PKey::RSA.new(File.read(file), nil)
38
- end
35
+ @public_key ||= begin
36
+ file = require_option(:public_key)
37
+ OpenSSL::PKey::RSA.new(File.read(file), nil)
38
+ end
39
39
  end
40
40
 
41
41
  def option(key)
@@ -45,7 +45,7 @@ class Trocla::Encryptions::Ssl < Trocla::Encryptions::Base
45
45
  def require_option(key)
46
46
  val = option(key)
47
47
  raise "Config error: 'ssl_options' => :#{key} is not defined" if val.nil?
48
+
48
49
  val
49
50
  end
50
51
  end
51
-
@@ -1,18 +1,24 @@
1
- class Trocla::Encryptions
1
+ # frozen_string_literal: true
2
2
 
3
+ # Trocla::Encryptions
4
+ class Trocla::Encryptions
5
+ # Base
3
6
  class Base
4
7
  attr_reader :trocla, :config
8
+
5
9
  def initialize(config, trocla)
6
10
  @trocla = trocla
7
11
  @config = config
8
12
  end
9
13
 
10
- def encrypt(value)
14
+ def encrypt(_)
11
15
  raise NoMethodError.new("#{self.class.name} needs to implement 'encrypt()'")
16
+
12
17
  end
13
18
 
14
- def decrypt(value)
19
+ def decrypt(_)
15
20
  raise NoMethodError.new("#{self.class.name} needs to implement 'decrypt()'")
21
+
16
22
  end
17
23
  end
18
24
 
@@ -22,7 +28,7 @@ class Trocla::Encryptions
22
28
  end
23
29
 
24
30
  def all
25
- Dir[ path '*' ].collect do |enc|
31
+ Dir[path '*'].collect do |enc|
26
32
  File.basename(enc, '.rb').downcase
27
33
  end
28
34
  end
@@ -32,10 +38,11 @@ class Trocla::Encryptions
32
38
  end
33
39
 
34
40
  private
41
+
35
42
  def encryptions
36
43
  @@encryptions ||= Hash.new do |hash, encryption|
37
44
  encryption = encryption.to_s.downcase
38
- if File.exists?( path encryption )
45
+ if File.exist?(path encryption)
39
46
  require "trocla/encryptions/#{encryption}"
40
47
  class_name = "Trocla::Encryptions::#{encryption.capitalize}"
41
48
  hash[encryption] = (eval class_name)
@@ -1,7 +1,7 @@
1
1
  class Trocla::Formats::Bcrypt < Trocla::Formats::Base
2
2
  expensive true
3
3
  require 'bcrypt'
4
- def format(plain_password,options={})
5
- BCrypt::Password.create(plain_password, :cost => options['cost']||BCrypt::Engine.cost).to_s
4
+ def format(plain_password, options = {})
5
+ BCrypt::Password.create(plain_password, :cost => options['cost'] || BCrypt::Engine.cost).to_s
6
6
  end
7
7
  end
@@ -1,6 +1,6 @@
1
1
  # salted crypt
2
2
  class Trocla::Formats::Md5crypt < Trocla::Formats::Base
3
- def format(plain_password,options={})
4
- plain_password.crypt('$1$' << Trocla::Util.salt << '$')
3
+ def format(plain_password, options = {})
4
+ plain_password.crypt('$1$' << Trocla::Util.salt << '$')
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  class Trocla::Formats::Mysql < Trocla::Formats::Base
2
2
  require 'digest/sha1'
3
- def format(plain_password,options={})
4
- "*" + Digest::SHA1.hexdigest(Digest::SHA1.digest(plain_password)).upcase
3
+ def format(plain_password, options = {})
4
+ '*' + Digest::SHA1.hexdigest(Digest::SHA1.digest(plain_password)).upcase
5
5
  end
6
6
  end
@@ -1,7 +1,54 @@
1
1
  class Trocla::Formats::Pgsql < Trocla::Formats::Base
2
2
  require 'digest/md5'
3
- def format(plain_password,options={})
4
- raise "You need pass the username as an option to use this format" unless options['username']
5
- "md5" + Digest::MD5.hexdigest(plain_password + options['username'])
3
+ require 'openssl'
4
+ require 'base64'
5
+ def format(plain_password, options = {})
6
+ encode = (options['encode'] || 'sha256')
7
+ case encode
8
+ when 'md5'
9
+ raise 'You need pass the username as an option to use this format' unless options['username']
10
+
11
+ 'md5' + Digest::MD5.hexdigest(plain_password + options['username'])
12
+ when 'sha256'
13
+ pg_sha256(plain_password)
14
+ else
15
+ raise 'Unkmow encode %s for pgsql password' % [encode]
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def pg_sha256(password)
22
+ salt = OpenSSL::Random.random_bytes(16)
23
+ digest = digest_key(password, salt)
24
+ 'SCRAM-SHA-256$%s:%s$%s:%s' % [
25
+ '4096',
26
+ Base64.strict_encode64(salt),
27
+ Base64.strict_encode64(client_key(digest)),
28
+ Base64.strict_encode64(server_key(digest))
29
+ ]
30
+ end
31
+
32
+ def digest_key(password, salt)
33
+ OpenSSL::KDF.pbkdf2_hmac(
34
+ password,
35
+ salt: salt,
36
+ iterations: 4096,
37
+ length: 32,
38
+ hash: OpenSSL::Digest::SHA256.new
39
+ )
40
+ end
41
+
42
+ def client_key(digest_key)
43
+ hmac = OpenSSL::HMAC.new(digest_key, OpenSSL::Digest::SHA256.new)
44
+ hmac << 'Client Key'
45
+ hmac.digest
46
+ OpenSSL::Digest.new('SHA256').digest hmac.digest
47
+ end
48
+
49
+ def server_key(digest_key)
50
+ hmac = OpenSSL::HMAC.new(digest_key, OpenSSL::Digest::SHA256.new)
51
+ hmac << 'Server Key'
52
+ hmac.digest
6
53
  end
7
54
  end
@@ -1,7 +1,5 @@
1
1
  class Trocla::Formats::Plain < Trocla::Formats::Base
2
-
3
- def format(plain_password,options={})
2
+ def format(plain_password, options = {})
4
3
  plain_password
5
4
  end
6
-
7
5
  end
@@ -1,7 +1,7 @@
1
1
  class Trocla::Formats::Sha1 < Trocla::Formats::Base
2
2
  require 'digest/sha1'
3
3
  require 'base64'
4
- def format(plain_password,options={})
4
+ def format(plain_password, options = {})
5
5
  '{SHA}' + Base64.encode64(Digest::SHA1.digest(plain_password))
6
6
  end
7
7
  end
@@ -1,6 +1,6 @@
1
1
  # salted crypt
2
2
  class Trocla::Formats::Sha256crypt < Trocla::Formats::Base
3
- def format(plain_password,options={})
4
- plain_password.crypt('$5$' << Trocla::Util.salt << '$')
3
+ def format(plain_password, options = {})
4
+ plain_password.crypt('$5$' << Trocla::Util.salt << '$')
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # salted crypt
2
2
  class Trocla::Formats::Sha512crypt < Trocla::Formats::Base
3
- def format(plain_password,options={})
4
- plain_password.crypt('$6$' << Trocla::Util.salt << '$')
3
+ def format(plain_password, options = {})
4
+ plain_password.crypt('$6$' << Trocla::Util.salt << '$')
5
5
  end
6
6
  end