sorcery-argon2 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +1 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
- data/.github/ISSUE_TEMPLATE/need_help.md +24 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- data/.github/workflows/ruby.yml +66 -0
- data/.gitignore +68 -0
- data/.gitmodules +4 -0
- data/.rubocop.yml +208 -0
- data/CHANGELOG.md +20 -0
- data/CODE_OF_CONDUCT.md +14 -0
- data/Gemfile +6 -0
- data/LICENSE.md +23 -0
- data/MAINTAINING.md +65 -0
- data/README.md +164 -0
- data/Rakefile +16 -0
- data/SECURITY.md +17 -0
- data/bin/console +15 -0
- data/bin/setup +11 -0
- data/bin/test +10 -0
- data/ext/argon2_wrap/Makefile +74 -0
- data/ext/argon2_wrap/argon_wrap.c +167 -0
- data/ext/argon2_wrap/extconf.rb +2 -0
- data/ext/argon2_wrap/test.c +117 -0
- data/lib/argon2.rb +17 -0
- data/lib/argon2/constants.rb +12 -0
- data/lib/argon2/engine.rb +18 -0
- data/lib/argon2/errors.rb +121 -0
- data/lib/argon2/ffi_engine.rb +114 -0
- data/lib/argon2/password.rb +220 -0
- data/lib/argon2/version.rb +8 -0
- data/sorcery-argon2.gemspec +51 -0
- metadata +191 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
require 'ffi-compiler/loader'
|
5
|
+
|
6
|
+
module Argon2
|
7
|
+
##
|
8
|
+
# Direct external bindings. Call these methods via the Engine class to ensure
|
9
|
+
# points are dealt with.
|
10
|
+
#
|
11
|
+
module Ext
|
12
|
+
extend FFI::Library
|
13
|
+
ffi_lib FFI::Compiler::Loader.find(FFI::Platform.windows? ? 'libargon2_wrap' : 'argon2_wrap')
|
14
|
+
|
15
|
+
# int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
|
16
|
+
# const uint32_t parallelism, const void *pwd,
|
17
|
+
# const size_t pwdlen, const void *salt,
|
18
|
+
# const size_t saltlen, void *hash, const size_t hashlen);
|
19
|
+
|
20
|
+
attach_function :argon2i_hash_raw, %i[
|
21
|
+
uint uint uint pointer
|
22
|
+
size_t pointer size_t pointer size_t
|
23
|
+
], :int, :blocking => true
|
24
|
+
|
25
|
+
# int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
|
26
|
+
# const uint32_t parallelism, const void *pwd,
|
27
|
+
# const size_t pwdlen, const void *salt,
|
28
|
+
# const size_t saltlen, void *hash, const size_t hashlen)
|
29
|
+
attach_function :argon2id_hash_raw, %i[
|
30
|
+
uint uint uint pointer
|
31
|
+
size_t pointer size_t pointer size_t
|
32
|
+
], :int, :blocking => true
|
33
|
+
|
34
|
+
# void argon2_wrap(uint8_t *out, char *pwd, size_t pwdlen,
|
35
|
+
# uint8_t *salt, uint32_t saltlen, uint32_t t_cost,
|
36
|
+
# uint32_t m_cost, uint32_t lanes,
|
37
|
+
# uint8_t *secret, uint32_t secretlen)
|
38
|
+
attach_function :argon2_wrap, %i[
|
39
|
+
pointer pointer size_t pointer uint uint
|
40
|
+
uint uint pointer size_t
|
41
|
+
], :int, :blocking => true
|
42
|
+
|
43
|
+
# int argon2i_verify(const char *encoded, const void *pwd,
|
44
|
+
# const size_t pwdlen);
|
45
|
+
attach_function :wrap_argon2_verify, %i[pointer pointer size_t
|
46
|
+
pointer size_t], :int, :blocking => true
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# The engine class shields users from the FFI interface.
|
51
|
+
# It is generally not advised to directly use this class.
|
52
|
+
#
|
53
|
+
class Engine
|
54
|
+
def self.hash_argon2i(password, salt, t_cost, m_cost, out_len = nil)
|
55
|
+
out_len = (out_len || Constants::OUT_LEN).to_i
|
56
|
+
raise ::Argon2::Errors::InvalidOutputLength if out_len < 1
|
57
|
+
|
58
|
+
result = ''
|
59
|
+
FFI::MemoryPointer.new(:char, out_len) do |buffer|
|
60
|
+
ret = Ext.argon2i_hash_raw(t_cost, 1 << m_cost, 1, password,
|
61
|
+
password.length, salt, salt.length,
|
62
|
+
buffer, out_len)
|
63
|
+
raise ::Argon2::Errors::ExtError, ERRORS[ret.abs] unless ret.zero?
|
64
|
+
|
65
|
+
result = buffer.read_string(out_len)
|
66
|
+
end
|
67
|
+
result.unpack('H*').join
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.hash_argon2id(password, salt, t_cost, m_cost, out_len = nil)
|
71
|
+
out_len = (out_len || Constants::OUT_LEN).to_i
|
72
|
+
raise ::Argon2::Errors::InvalidOutputLength if out_len < 1
|
73
|
+
|
74
|
+
result = ''
|
75
|
+
FFI::MemoryPointer.new(:char, out_len) do |buffer|
|
76
|
+
ret = Ext.argon2id_hash_raw(t_cost, 1 << m_cost, 1, password,
|
77
|
+
password.length, salt, salt.length,
|
78
|
+
buffer, out_len)
|
79
|
+
raise ::Argon2::Errors::ExtError, ERRORS[ret.abs] unless ret.zero?
|
80
|
+
|
81
|
+
result = buffer.read_string(out_len)
|
82
|
+
end
|
83
|
+
result.unpack('H*').join
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.hash_argon2id_encode(password, salt, t_cost, m_cost, secret)
|
87
|
+
result = ''
|
88
|
+
secretlen = secret.nil? ? 0 : secret.bytesize
|
89
|
+
passwordlen = password.nil? ? 0 : password.bytesize
|
90
|
+
raise ::Argon2::Errors::InvalidSaltSize if salt.length != Constants::SALT_LEN
|
91
|
+
|
92
|
+
FFI::MemoryPointer.new(:char, Constants::ENCODE_LEN) do |buffer|
|
93
|
+
ret = Ext.argon2_wrap(buffer, password, passwordlen,
|
94
|
+
salt, salt.length, t_cost, (1 << m_cost),
|
95
|
+
1, secret, secretlen)
|
96
|
+
raise ::Argon2::Errors::ExtError, ERRORS[ret.abs] unless ret.zero?
|
97
|
+
|
98
|
+
result = buffer.read_string(Constants::ENCODE_LEN)
|
99
|
+
end
|
100
|
+
result.delete "\0"
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.argon2_verify(pwd, hash, secret)
|
104
|
+
secretlen = secret.nil? ? 0 : secret.bytesize
|
105
|
+
passwordlen = pwd.nil? ? 0 : pwd.bytesize
|
106
|
+
|
107
|
+
ret = Ext.wrap_argon2_verify(hash, pwd, passwordlen, secret, secretlen)
|
108
|
+
return false if ERRORS[ret.abs] == 'ARGON2_DECODING_FAIL'
|
109
|
+
raise ::Argon2::Errors::ExtError, ERRORS[ret.abs] unless ret.zero?
|
110
|
+
|
111
|
+
true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Argon2
|
4
|
+
##
|
5
|
+
# Front-end API for the Argon2 module.
|
6
|
+
#
|
7
|
+
class Password
|
8
|
+
# Used as the default time cost if one isn't provided when calling
|
9
|
+
# Argon2::Password.create
|
10
|
+
DEFAULT_T_COST = 2
|
11
|
+
# Used to validate the minimum acceptable time cost
|
12
|
+
MIN_T_COST = 1
|
13
|
+
# Used to validate the maximum acceptable time cost
|
14
|
+
MAX_T_COST = 750
|
15
|
+
# Used as the default memory cost if one isn't provided when calling
|
16
|
+
# Argon2::Password.create
|
17
|
+
DEFAULT_M_COST = 16
|
18
|
+
# Used to validate the minimum acceptable memory cost
|
19
|
+
MIN_M_COST = 3
|
20
|
+
# Used to validate the maximum acceptable memory cost
|
21
|
+
MAX_M_COST = 31
|
22
|
+
# The complete Argon2 digest string (not to be confused with the checksum).
|
23
|
+
attr_reader :digest
|
24
|
+
# The hash portion of the stored password hash.
|
25
|
+
attr_reader :checksum
|
26
|
+
# The salt of the stored password hash.
|
27
|
+
attr_reader :salt
|
28
|
+
# Variant used (argon2i / argon2d / argon2id)
|
29
|
+
attr_reader :variant
|
30
|
+
# The version of the argon2 algorithm used to create the hash.
|
31
|
+
attr_reader :version
|
32
|
+
# The time cost factor used to create the hash.
|
33
|
+
attr_reader :t_cost
|
34
|
+
# The memory cost factor used to create the hash.
|
35
|
+
attr_reader :m_cost
|
36
|
+
# The parallelism cost factor used to create the hash.
|
37
|
+
attr_reader :p_cost
|
38
|
+
|
39
|
+
##
|
40
|
+
# Class methods
|
41
|
+
#
|
42
|
+
class << self
|
43
|
+
##
|
44
|
+
# Takes a user provided password and returns an Argon2::Password instance
|
45
|
+
# with the resulting Argon2 hash.
|
46
|
+
#
|
47
|
+
# Usage:
|
48
|
+
#
|
49
|
+
# Argon2::Password.create(password)
|
50
|
+
# Argon2::Password.create(password, t_cost: 4, m_cost: 20)
|
51
|
+
# Argon2::Password.create(password, secret: pepper)
|
52
|
+
# Argon2::Password.create(password, m_cost: 17, secret: pepper)
|
53
|
+
#
|
54
|
+
# Currently available options:
|
55
|
+
#
|
56
|
+
# * :t_cost
|
57
|
+
# * :m_cost
|
58
|
+
# * :secret
|
59
|
+
#
|
60
|
+
def create(password, options = {})
|
61
|
+
raise Argon2::Errors::InvalidPassword unless password.is_a?(String)
|
62
|
+
|
63
|
+
t_cost = options[:t_cost] || DEFAULT_T_COST
|
64
|
+
m_cost = options[:m_cost] || DEFAULT_M_COST
|
65
|
+
|
66
|
+
raise Argon2::Errors::InvalidTCost if t_cost < MIN_T_COST || t_cost > MAX_T_COST
|
67
|
+
raise Argon2::Errors::InvalidMCost if m_cost < MIN_M_COST || m_cost > MAX_M_COST
|
68
|
+
|
69
|
+
# TODO: Add support for changing the p_cost
|
70
|
+
|
71
|
+
salt = Engine.saltgen
|
72
|
+
secret = options[:secret]
|
73
|
+
|
74
|
+
Argon2::Password.new(
|
75
|
+
Argon2::Engine.hash_argon2id_encode(
|
76
|
+
password, salt, t_cost, m_cost, secret
|
77
|
+
)
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Regex to validate if the provided String is a valid Argon2 hash output.
|
83
|
+
#
|
84
|
+
# Supports 1 and argon2id formats.
|
85
|
+
#
|
86
|
+
def valid_hash?(digest)
|
87
|
+
/^\$argon2(id?|d).{,113}/ =~ digest
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Takes a password, Argon2 hash, and optionally a secret, then uses the
|
92
|
+
# Argon2 C Library to verify if they match.
|
93
|
+
#
|
94
|
+
# Also accepts passing another Argon2::Password instance as the password,
|
95
|
+
# in which case it will compare the final Argon2 hash for each against
|
96
|
+
# each other.
|
97
|
+
#
|
98
|
+
# Usage:
|
99
|
+
#
|
100
|
+
# Argon2::Password.verify_password(password, argon2_hash)
|
101
|
+
# Argon2::Password.verify_password(password, argon2_hash, secret)
|
102
|
+
#
|
103
|
+
def verify_password(password, digest, secret = nil)
|
104
|
+
digest = digest.to_s
|
105
|
+
if password.is_a?(Argon2::Password)
|
106
|
+
password == Argon2::Password.new(digest)
|
107
|
+
else
|
108
|
+
Argon2::Engine.argon2_verify(password, digest, secret)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
######################
|
114
|
+
## Instance Methods ##
|
115
|
+
######################
|
116
|
+
|
117
|
+
##
|
118
|
+
# Initialize an Argon2::Password instance using any valid Argon2 digest.
|
119
|
+
#
|
120
|
+
def initialize(digest)
|
121
|
+
digest = digest.to_s
|
122
|
+
|
123
|
+
raise Argon2::Errors::InvalidHash unless valid_hash?(digest)
|
124
|
+
|
125
|
+
# Split the digest into its component pieces
|
126
|
+
split_digest = split_hash(digest)
|
127
|
+
# Assign each piece to the Argon2::Password instance
|
128
|
+
@digest = digest
|
129
|
+
@variant = split_digest[:variant]
|
130
|
+
@version = split_digest[:version]
|
131
|
+
@t_cost = split_digest[:t_cost]
|
132
|
+
@m_cost = split_digest[:m_cost]
|
133
|
+
@p_cost = split_digest[:p_cost]
|
134
|
+
@salt = split_digest[:salt]
|
135
|
+
@checksum = split_digest[:checksum]
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Helper function to allow easily comparing an Argon2::Password against the
|
140
|
+
# provided password and secret.
|
141
|
+
#
|
142
|
+
def matches?(password, secret = nil)
|
143
|
+
self.class.verify_password(password, digest, secret)
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Compares two Argon2::Password instances to see if they come from the same
|
148
|
+
# digest/hash.
|
149
|
+
#
|
150
|
+
def ==(other)
|
151
|
+
# TODO: Should this return false instead of raising an error?
|
152
|
+
unless other.is_a?(Argon2::Password)
|
153
|
+
raise ArgumentError,
|
154
|
+
'Can only compare an Argon2::Password against another Argon2::Password'
|
155
|
+
end
|
156
|
+
|
157
|
+
digest == other.digest
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Converts an Argon2::Password instance into a String.
|
162
|
+
#
|
163
|
+
def to_s
|
164
|
+
digest.to_s
|
165
|
+
end
|
166
|
+
|
167
|
+
##
|
168
|
+
# Converts an Argon2::Password instance into a String.
|
169
|
+
#
|
170
|
+
def to_str
|
171
|
+
digest.to_str
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
##
|
177
|
+
# Helper method to allow checking if a hash is valid in the initializer.
|
178
|
+
#
|
179
|
+
def valid_hash?(digest)
|
180
|
+
self.class.valid_hash?(digest)
|
181
|
+
end
|
182
|
+
|
183
|
+
# FIXME: Reduce complexity/AbcSize
|
184
|
+
# rubocop:disable Metrics/AbcSize
|
185
|
+
|
186
|
+
##
|
187
|
+
# Helper method to extract the various values from a digest into attributes.
|
188
|
+
#
|
189
|
+
def split_hash(digest)
|
190
|
+
# TODO: Is there a better way to explode the digest into attributes?
|
191
|
+
_, variant, version, config, salt, checksum = digest.split('$')
|
192
|
+
# Regex magic to extract the values for each setting
|
193
|
+
version = /v=(\d+)/.match(version)
|
194
|
+
t_cost = /t=(\d+),/.match(config)
|
195
|
+
m_cost = /m=(\d+),/.match(config)
|
196
|
+
p_cost = /p=(\d+)/.match(config)
|
197
|
+
|
198
|
+
# Make sure none of the values are missing
|
199
|
+
raise Argon2::Errors::InvalidVersion if version.nil?
|
200
|
+
raise Argon2::Errors::InvalidTCost if t_cost.nil?
|
201
|
+
raise Argon2::Errors::InvalidMCost if m_cost.nil?
|
202
|
+
raise Argon2::Errors::InvalidPCost if p_cost.nil?
|
203
|
+
|
204
|
+
# Undo the 2^m_cost operation when encoding the hash to get the original
|
205
|
+
# m_cost input back.
|
206
|
+
m_cost = Math.log2(m_cost[1].to_i).to_i
|
207
|
+
|
208
|
+
{
|
209
|
+
variant: variant.to_str,
|
210
|
+
version: version[1].to_i,
|
211
|
+
t_cost: t_cost[1].to_i,
|
212
|
+
m_cost: m_cost,
|
213
|
+
p_cost: p_cost[1].to_i,
|
214
|
+
salt: salt.to_str,
|
215
|
+
checksum: checksum.to_str
|
216
|
+
}
|
217
|
+
end
|
218
|
+
# rubocop:enable Metrics/AbcSize
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'argon2/version'
|
6
|
+
|
7
|
+
version = Argon2::VERSION
|
8
|
+
repo_url = 'https://github.com/sorcery/argon2'
|
9
|
+
|
10
|
+
Gem::Specification.new do |s|
|
11
|
+
s.version = version
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
s.name = 'sorcery-argon2'
|
14
|
+
s.summary = 'A Ruby wrapper for the Argon2 Password hashing algorithm'
|
15
|
+
s.description =
|
16
|
+
'Provides a minimal ruby wrapper for the Argon2 password hashing algorithm.'
|
17
|
+
|
18
|
+
s.required_ruby_version = '>= 2.5.0'
|
19
|
+
|
20
|
+
s.license = 'MIT'
|
21
|
+
s.author = 'Josh Buker'
|
22
|
+
s.email = 'crypto@joshbuker.com'
|
23
|
+
s.homepage = repo_url
|
24
|
+
s.metadata = {
|
25
|
+
'bug_tracker_uri' => "#{repo_url}/issues",
|
26
|
+
'changelog_uri' => "#{repo_url}/releases/tag/v#{version}",
|
27
|
+
'documentation_uri' => 'https://rubydoc.info/gems/sorcery-argon2',
|
28
|
+
'source_code_uri' => "#{repo_url}/tree/v#{version}"
|
29
|
+
}
|
30
|
+
|
31
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
32
|
+
s.files << `find ext`.split
|
33
|
+
|
34
|
+
s.bindir = 'exe'
|
35
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
|
+
s.require_paths = ['lib']
|
37
|
+
|
38
|
+
s.add_dependency 'ffi', '~> 1.14'
|
39
|
+
s.add_dependency 'ffi-compiler', '~> 1.0'
|
40
|
+
|
41
|
+
# Gems required for testing the wrapper locally.
|
42
|
+
s.add_development_dependency 'bundler', '~> 2.0'
|
43
|
+
s.add_development_dependency 'minitest', '~> 5.8'
|
44
|
+
s.add_development_dependency 'rake', '~> 13.0.1'
|
45
|
+
s.add_development_dependency 'rubocop', '~> 1.7'
|
46
|
+
s.add_development_dependency 'simplecov', '~> 0.20'
|
47
|
+
s.add_development_dependency 'simplecov-lcov', '~> 0.8'
|
48
|
+
|
49
|
+
# Argon2 C Extension
|
50
|
+
s.extensions << 'ext/argon2_wrap/extconf.rb'
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sorcery-argon2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Buker
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-04-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ffi-compiler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 13.0.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 13.0.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.7'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.7'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.20'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.20'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov-lcov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.8'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.8'
|
125
|
+
description: Provides a minimal ruby wrapper for the Argon2 password hashing algorithm.
|
126
|
+
email: crypto@joshbuker.com
|
127
|
+
executables: []
|
128
|
+
extensions:
|
129
|
+
- ext/argon2_wrap/extconf.rb
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".document"
|
133
|
+
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
134
|
+
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
135
|
+
- ".github/ISSUE_TEMPLATE/need_help.md"
|
136
|
+
- ".github/PULL_REQUEST_TEMPLATE.md"
|
137
|
+
- ".github/workflows/ruby.yml"
|
138
|
+
- ".gitignore"
|
139
|
+
- ".gitmodules"
|
140
|
+
- ".rubocop.yml"
|
141
|
+
- CHANGELOG.md
|
142
|
+
- CODE_OF_CONDUCT.md
|
143
|
+
- Gemfile
|
144
|
+
- LICENSE.md
|
145
|
+
- MAINTAINING.md
|
146
|
+
- README.md
|
147
|
+
- Rakefile
|
148
|
+
- SECURITY.md
|
149
|
+
- bin/console
|
150
|
+
- bin/setup
|
151
|
+
- bin/test
|
152
|
+
- ext/argon2_wrap/Makefile
|
153
|
+
- ext/argon2_wrap/argon_wrap.c
|
154
|
+
- ext/argon2_wrap/extconf.rb
|
155
|
+
- ext/argon2_wrap/test.c
|
156
|
+
- lib/argon2.rb
|
157
|
+
- lib/argon2/constants.rb
|
158
|
+
- lib/argon2/engine.rb
|
159
|
+
- lib/argon2/errors.rb
|
160
|
+
- lib/argon2/ffi_engine.rb
|
161
|
+
- lib/argon2/password.rb
|
162
|
+
- lib/argon2/version.rb
|
163
|
+
- sorcery-argon2.gemspec
|
164
|
+
homepage: https://github.com/sorcery/argon2
|
165
|
+
licenses:
|
166
|
+
- MIT
|
167
|
+
metadata:
|
168
|
+
bug_tracker_uri: https://github.com/sorcery/argon2/issues
|
169
|
+
changelog_uri: https://github.com/sorcery/argon2/releases/tag/v1.0.0
|
170
|
+
documentation_uri: https://rubydoc.info/gems/sorcery-argon2
|
171
|
+
source_code_uri: https://github.com/sorcery/argon2/tree/v1.0.0
|
172
|
+
post_install_message:
|
173
|
+
rdoc_options: []
|
174
|
+
require_paths:
|
175
|
+
- lib
|
176
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: 2.5.0
|
181
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
requirements: []
|
187
|
+
rubygems_version: 3.1.2
|
188
|
+
signing_key:
|
189
|
+
specification_version: 4
|
190
|
+
summary: A Ruby wrapper for the Argon2 Password hashing algorithm
|
191
|
+
test_files: []
|