argon2id 0.6.0-x86_64-darwin → 0.8.0.rc1-x86_64-darwin
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 +4 -4
- data/CHANGELOG.md +31 -0
- data/README.md +96 -13
- data/Rakefile +9 -5
- data/argon2id.gemspec +4 -3
- data/lib/argon2id/3.1/argon2id.bundle +0 -0
- data/lib/argon2id/3.2/argon2id.bundle +0 -0
- data/lib/argon2id/3.3/argon2id.bundle +0 -0
- data/lib/argon2id/3.4/argon2id.bundle +0 -0
- data/lib/argon2id/extension.rb +8 -6
- data/lib/argon2id/password.rb +7 -0
- data/lib/argon2id/version.rb +1 -1
- data/test/argon2id/test_password.rb +554 -0
- data/test/test_argon2id.rb +66 -0
- metadata +11 -12
- data/lib/argon2id/2.6/argon2id.bundle +0 -0
- data/lib/argon2id/2.7/argon2id.bundle +0 -0
- data/lib/argon2id/3.0/argon2id.bundle +0 -0
- data/test/test_password.rb +0 -232
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fdd98f6b16d85f231c39f64181187b27969135b7005a502c5b7f55ef2109f7c8
|
4
|
+
data.tar.gz: 5936e4e00441305b9916fbfd1994f9cae99334e7f4ff6190c9c46b5c41396cbe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1555ef49630f9844efe8e799addd07b9325042678efe597356ba3c534e1a57b05e95e342fb49c4f7df283ba24e8eea0fae697909b920ee4614477e8ebc74a3d
|
7
|
+
data.tar.gz: 6f0adfca1b817a3504e544023e3a0b37ca8e3087072778b896b6bc9761f01a64d9a46912d821f177f915ac4a4821efa7c5ce8e41787e6ec05f23405969265910
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.8.0.rc1] - 2024-12-16
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Add Ruby 3.4.0-rc1 support to the precompiled, native gems
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
|
16
|
+
- Provide separate precompiled, native gems for GNU and Musl
|
17
|
+
- Require glibc 2.29+ for x86-linux-gnu and x86_64-linux-gnu (and recommend
|
18
|
+
RubyGems 3.3.22+ and Bundler 2.3.21+)
|
19
|
+
|
20
|
+
### Removed
|
21
|
+
|
22
|
+
- Drop support for Ruby versions older than 3.1 as they do not ship with a
|
23
|
+
version of RubyGems new enough to handle the new Musl gems
|
24
|
+
|
25
|
+
## [0.7.0] - 2024-11-08
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
|
29
|
+
- Fixed verifying Argon2id encoded hashes without a version number on JRuby
|
30
|
+
|
31
|
+
### Added
|
32
|
+
|
33
|
+
- Added a new `Argon2id::Password.valid_hash?` API for testing if a given
|
34
|
+
encoded hash is a valid Argon2id hash or not (e.g. if you want to check
|
35
|
+
which hashing function was used to store a user's password)
|
36
|
+
|
8
37
|
## [0.6.0] - 2024-11-05
|
9
38
|
|
10
39
|
### Changed
|
@@ -99,6 +128,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
99
128
|
reference C implementation of Argon2, the password-hashing function that won
|
100
129
|
the Password Hashing Competition.
|
101
130
|
|
131
|
+
[0.8.0.rc1]: https://github.com/mudge/argon2id/releases/tag/v0.8.0.rc1
|
132
|
+
[0.7.0]: https://github.com/mudge/argon2id/releases/tag/v0.7.0
|
102
133
|
[0.6.0]: https://github.com/mudge/argon2id/releases/tag/v0.6.0
|
103
134
|
[0.5.0]: https://github.com/mudge/argon2id/releases/tag/v0.5.0
|
104
135
|
[0.4.1]: https://github.com/mudge/argon2id/releases/tag/v0.4.1
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@ Ruby bindings to [Argon2][], the password-hashing function that won the 2015
|
|
5
5
|
|
6
6
|
[](https://github.com/mudge/argon2id/actions)
|
7
7
|
|
8
|
-
**Current version:** 0.
|
8
|
+
**Current version:** 0.8.0.rc1
|
9
9
|
**Bundled Argon2 version:** libargon2.1 (20190702)
|
10
10
|
|
11
11
|
```ruby
|
@@ -26,7 +26,9 @@ password.salt #=> "e-\xA7\x04U\x81\xA6{v\xF0x\xED\xCC\xD3\x96\xE3"
|
|
26
26
|
* [Usage](#usage)
|
27
27
|
* [Hashing passwords](#hashing-passwords)
|
28
28
|
* [Verifying passwords](#verifying-passwords)
|
29
|
+
* [Validating encoded hashes](#validating-encoded-hashes)
|
29
30
|
* [Errors](#errors)
|
31
|
+
* [Usage with Active Record](#usage-with-active-record)
|
30
32
|
* [Requirements](#requirements)
|
31
33
|
* [Native gems](#native-gems)
|
32
34
|
* [Verifying the gems](#verifying-the-gems)
|
@@ -55,6 +57,8 @@ password.salt #=> "e-\xA7\x04U\x81\xA6{v\xF0x\xED\xCC\xD3\x96\xE3"
|
|
55
57
|
|
56
58
|
— [OWASP Password Storage Cheat Sheet][]
|
57
59
|
|
60
|
+
See also [argon2-cffi's "Why 'just use bcrypt' Is Not the Best Answer (Anymore)"](https://argon2-cffi.readthedocs.io/en/23.1.0/argon2.html#why-just-use-bcrypt-is-not-the-best-answer-anymore).
|
61
|
+
|
58
62
|
## Usage
|
59
63
|
|
60
64
|
Install argon2id as a dependency:
|
@@ -147,6 +151,18 @@ password.is_password?("opensesame") #=> true
|
|
147
151
|
password.is_password?("notopensesame") #=> false
|
148
152
|
```
|
149
153
|
|
154
|
+
> [!CAUTION]
|
155
|
+
> `Argon2id::Password#==` only works if the plain text password is on the right, e.g. the following behaviour may be surprising:
|
156
|
+
>
|
157
|
+
> ```ruby
|
158
|
+
> password = Argon2id::Password.create("password")
|
159
|
+
> password == "password" #=> true
|
160
|
+
> "password" == password #=> false
|
161
|
+
> password == password #=> false
|
162
|
+
> ```
|
163
|
+
>
|
164
|
+
> If you want to avoid this ambiguity, prefer the `Argon2id::Password#is_password?` alias instead.
|
165
|
+
|
150
166
|
The various parts of the encoded hash can be retrieved:
|
151
167
|
|
152
168
|
```ruby
|
@@ -160,6 +176,18 @@ password.output
|
|
160
176
|
#=> "\x9D\xFE\xB9\x10\xE8\v\xAD\x03\x11\xFE\xE2\x0F\x9C\x0E+\x12\xC1y\x87\xB4\xCA\xC9\f.\xF5M[0!\xC6\x8B\xFE"
|
161
177
|
```
|
162
178
|
|
179
|
+
### Validating encoded hashes
|
180
|
+
|
181
|
+
If you need to check ahead of time whether an encoded password hash is a valid Argon2id hash (e.g. if you're migrating between hashing functions and need to test what kind of password has been stored for a user), you can use `Argon2id::Password.valid_hash?` like so:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
Argon2id::Password.valid_hash?("$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc")
|
185
|
+
#=> true
|
186
|
+
|
187
|
+
Argon2id::Password.valid_hash?("$2a$12$stsRn7Mi9r02.keRyF4OK.Aq4UWOU185lWggfUQfcupAi.b7AI/nS")
|
188
|
+
#=> false
|
189
|
+
```
|
190
|
+
|
163
191
|
### Errors
|
164
192
|
|
165
193
|
Any errors returned from Argon2 will be raised as `Argon2id::Error`, e.g.
|
@@ -169,11 +197,67 @@ Argon2id::Password.create("password", salt_len: 0)
|
|
169
197
|
# Salt is too short (Argon2id::Error)
|
170
198
|
```
|
171
199
|
|
200
|
+
### Usage with Active Record
|
201
|
+
|
202
|
+
If you're planning to use this with Active Record instead of [Rails' own
|
203
|
+
bcrypt-based
|
204
|
+
`has_secure_password`](https://api.rubyonrails.org/v8.0/classes/ActiveModel/SecurePassword/ClassMethods.html),
|
205
|
+
you can use the following as a starting point:
|
206
|
+
|
207
|
+
#### The `User` model
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
require "argon2id"
|
211
|
+
|
212
|
+
# Schema: User(name: string, password_digest:string)
|
213
|
+
class User < ApplicationRecord
|
214
|
+
attr_reader :password
|
215
|
+
|
216
|
+
validates :password_digest, presence: true
|
217
|
+
validates :password, confirmation: true, allow_blank: true
|
218
|
+
|
219
|
+
def password=(unencrypted_password)
|
220
|
+
if unencrypted_password.nil?
|
221
|
+
@password = nil
|
222
|
+
self.password_digest = nil
|
223
|
+
elsif !unencrypted_password.empty?
|
224
|
+
@password = unencrypted_password
|
225
|
+
self.password_digest = Argon2id::Password.create(unencrypted_password)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def authenticate(unencrypted_password)
|
230
|
+
password_digest? && Argon2id::Password.new(password_digest).is_password?(unencrypted_password) && self
|
231
|
+
end
|
232
|
+
|
233
|
+
def password_salt
|
234
|
+
Argon2id::Password.new(password_digest).salt if password_digest?
|
235
|
+
end
|
236
|
+
end
|
237
|
+
```
|
238
|
+
|
239
|
+
This can then be used like so:
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
user = User.new(name: "alice", password: "", password_confirmation: "diffpassword")
|
243
|
+
user.save #=> false, password required
|
244
|
+
user.password = "password"
|
245
|
+
user.save #=> false, confirmation doesn't match
|
246
|
+
user.password_confirmation = "password"
|
247
|
+
user.save #=> true
|
248
|
+
|
249
|
+
user.authenticate("notright") #=> false
|
250
|
+
user.authenticate("password") #=> user
|
251
|
+
|
252
|
+
User.find_by(name: "alice")&.authenticate("notright") #=> false
|
253
|
+
User.find_by(name: "alice")&.authenticate("password") #=> user
|
254
|
+
```
|
255
|
+
|
172
256
|
## Requirements
|
173
257
|
|
174
258
|
This gem requires any of the following to run:
|
175
259
|
|
176
|
-
* [Ruby](https://www.ruby-lang.org/en/)
|
260
|
+
* [Ruby](https://www.ruby-lang.org/en/) 3.1 to 3.4.0-rc1
|
177
261
|
* [JRuby](https://www.jruby.org) 9.4
|
178
262
|
* [TruffleRuby](https://www.graalvm.org/ruby/) 24.1
|
179
263
|
|
@@ -187,11 +271,10 @@ This gem requires any of the following to run:
|
|
187
271
|
Where possible, a pre-compiled native gem will be provided for the following platforms:
|
188
272
|
|
189
273
|
* Linux
|
190
|
-
* `aarch64-linux`
|
191
|
-
*
|
192
|
-
* [musl](https://musl.libc.org/)-based systems such as [Alpine](https://alpinelinux.org) are supported as long as a [glibc-compatible library is installed](https://wiki.alpinelinux.org/wiki/Running_glibc_programs)
|
274
|
+
* `aarch64-linux`, `arm-linux`, `x86-linux`, `x86_64-linux` (requires [glibc](https://www.gnu.org/software/libc/) 2.29+, RubyGems 3.3.22+ and Bundler 2.3.21+)
|
275
|
+
* [musl](https://musl.libc.org/)-based systems such as [Alpine](https://alpinelinux.org) are supported with Bundler 2.5.6+
|
193
276
|
* macOS `x86_64-darwin` and `arm64-darwin`
|
194
|
-
* Windows `x64-
|
277
|
+
* Windows `x64-mingw-ucrt`
|
195
278
|
* Java: any platform running JRuby 9.4 or higher
|
196
279
|
|
197
280
|
### Verifying the gems
|
@@ -201,11 +284,11 @@ notes](https://github.com/mudge/argon2id/releases) for each version and can be
|
|
201
284
|
checked with `sha256sum`, e.g.
|
202
285
|
|
203
286
|
```console
|
204
|
-
$ gem fetch argon2id -v 0.
|
205
|
-
Fetching argon2id-0.
|
206
|
-
Downloaded argon2id-0.
|
207
|
-
$ sha256sum argon2id-0.
|
208
|
-
|
287
|
+
$ gem fetch argon2id -v 0.7.0
|
288
|
+
Fetching argon2id-0.7.0-arm64-darwin.gem
|
289
|
+
Downloaded argon2id-0.7.0-arm64-darwin
|
290
|
+
$ sha256sum argon2id-0.7.0-arm64-darwin.gem
|
291
|
+
26bba5bcefa56827c728222e6df832aef5c8c4f4d3285875859a1d911477ec68 argon2id-0.7.0-arm64-darwin.gem
|
209
292
|
```
|
210
293
|
|
211
294
|
[GPG](https://www.gnupg.org/) signatures are attached to each release (the
|
@@ -215,8 +298,8 @@ from a public keyserver, e.g. `gpg --keyserver keyserver.ubuntu.com --recv-key
|
|
215
298
|
0x39AC3530070E0F75`):
|
216
299
|
|
217
300
|
```console
|
218
|
-
$ gpg --verify argon2id-0.
|
219
|
-
gpg: Signature made
|
301
|
+
$ gpg --verify argon2id-0.7.0-arm64-darwin.gem.sig argon2id-0.7.0-arm64-darwin.gem
|
302
|
+
gpg: Signature made Fri 8 Nov 13:45:18 2024 GMT
|
220
303
|
gpg: using RSA key 702609D9C790F45B577D7BEC39AC3530070E0F75
|
221
304
|
gpg: Good signature from "Paul Mucur <mudge@mudge.name>" [unknown]
|
222
305
|
gpg: aka "Paul Mucur <paul@ghostcassette.com>" [unknown]
|
data/Rakefile
CHANGED
@@ -5,18 +5,22 @@ require "minitest/test_task"
|
|
5
5
|
CLEAN.add("lib/**/*.{o,so,bundle}", "pkg")
|
6
6
|
|
7
7
|
cross_platforms = %w[
|
8
|
-
aarch64-linux
|
9
|
-
|
8
|
+
aarch64-linux-gnu
|
9
|
+
aarch64-linux-musl
|
10
|
+
arm-linux-gnu
|
11
|
+
arm-linux-musl
|
10
12
|
arm64-darwin
|
11
13
|
x64-mingw-ucrt
|
12
14
|
x64-mingw32
|
13
|
-
x86-linux
|
15
|
+
x86-linux-gnu
|
16
|
+
x86-linux-musl
|
14
17
|
x86-mingw32
|
15
18
|
x86_64-darwin
|
16
|
-
x86_64-linux
|
19
|
+
x86_64-linux-gnu
|
20
|
+
x86_64-linux-musl
|
17
21
|
].freeze
|
18
22
|
|
19
|
-
ENV["RUBY_CC_VERSION"] = %w[3.
|
23
|
+
ENV["RUBY_CC_VERSION"] = %w[3.4.0 3.3.5 3.2.0 3.1.0].join(":")
|
20
24
|
|
21
25
|
gemspec = Gem::Specification.load("argon2id.gemspec")
|
22
26
|
|
data/argon2id.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
"source_code_uri" => "https://github.com/mudge/argon2id",
|
19
19
|
"rubygems_mfa_required" => "true"
|
20
20
|
}
|
21
|
-
s.required_ruby_version = ">=
|
21
|
+
s.required_ruby_version = ">= 3.1.0"
|
22
22
|
s.extensions = ["ext/argon2id/extconf.rb"]
|
23
23
|
s.files = [
|
24
24
|
"CHANGELOG.md",
|
@@ -48,11 +48,12 @@ Gem::Specification.new do |s|
|
|
48
48
|
"lib/argon2id/extension.rb",
|
49
49
|
"lib/argon2id/password.rb",
|
50
50
|
"lib/argon2id/version.rb",
|
51
|
-
"test/test_password.rb"
|
51
|
+
"test/argon2id/test_password.rb",
|
52
|
+
"test/test_argon2id.rb"
|
52
53
|
]
|
53
54
|
s.rdoc_options = ["--main", "README.md"]
|
54
55
|
|
55
56
|
s.add_development_dependency("rake-compiler", "~> 1.2")
|
56
|
-
s.add_development_dependency("rake-compiler-dock", "~> 1.
|
57
|
+
s.add_development_dependency("rake-compiler-dock", "~> 1.7.0.rc1")
|
57
58
|
s.add_development_dependency("minitest", "~> 5.25")
|
58
59
|
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/argon2id/extension.rb
CHANGED
@@ -15,10 +15,11 @@ if RUBY_PLATFORM == "java"
|
|
15
15
|
output = Java::byte[hashlen].new
|
16
16
|
params = Java::OrgBouncycastleCryptoParams::Argon2Parameters::Builder
|
17
17
|
.new(Java::OrgBouncycastleCryptoParams::Argon2Parameters::ARGON2_id)
|
18
|
-
.
|
19
|
-
.with_parallelism(parallelism)
|
20
|
-
.with_memory_as_kb(m_cost)
|
18
|
+
.with_version(Java::OrgBouncycastleCryptoParams::Argon2Parameters::ARGON2_VERSION_13)
|
21
19
|
.with_iterations(t_cost)
|
20
|
+
.with_memory_as_kb(m_cost)
|
21
|
+
.with_parallelism(parallelism)
|
22
|
+
.with_salt(salt_bytes)
|
22
23
|
.build
|
23
24
|
generator = Java::OrgBouncycastleCryptoGenerators::Argon2BytesGenerator.new
|
24
25
|
|
@@ -43,10 +44,11 @@ if RUBY_PLATFORM == "java"
|
|
43
44
|
other_output = Java::byte[output.bytesize].new
|
44
45
|
params = Java::OrgBouncycastleCryptoParams::Argon2Parameters::Builder
|
45
46
|
.new(Java::OrgBouncycastleCryptoParams::Argon2Parameters::ARGON2_id)
|
46
|
-
.
|
47
|
-
.with_parallelism(parallelism)
|
48
|
-
.with_memory_as_kb(m_cost)
|
47
|
+
.with_version(version)
|
49
48
|
.with_iterations(t_cost)
|
49
|
+
.with_memory_as_kb(m_cost)
|
50
|
+
.with_parallelism(parallelism)
|
51
|
+
.with_salt(salt.to_java_bytes)
|
50
52
|
.build
|
51
53
|
generator = Java::OrgBouncycastleCryptoGenerators::Argon2BytesGenerator.new
|
52
54
|
generator.init(params)
|
data/lib/argon2id/password.rb
CHANGED
@@ -100,6 +100,13 @@ module Argon2id
|
|
100
100
|
)
|
101
101
|
end
|
102
102
|
|
103
|
+
# Check an encoded hash is a valid Argon2id hash.
|
104
|
+
#
|
105
|
+
# Returns true if so and false if not.
|
106
|
+
def self.valid_hash?(encoded)
|
107
|
+
PATTERN.match?(String(encoded))
|
108
|
+
end
|
109
|
+
|
103
110
|
# Create a new Password with the given encoded password hash.
|
104
111
|
#
|
105
112
|
# password = Argon2id::Password.new("$argon2id$v=19$m=19456,t=2,p=1$FI8yp1gXbthJCskBlpKPoQ$nOfCCpS2r+I8GRN71cZND4cskn7YKBNzuHUEO3YpY2s")
|
data/lib/argon2id/version.rb
CHANGED
@@ -0,0 +1,554 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest/autorun"
|
4
|
+
require "argon2id"
|
5
|
+
|
6
|
+
class StringLike
|
7
|
+
def initialize(str)
|
8
|
+
@str = str
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
@str
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TestPassword < Minitest::Test
|
17
|
+
def test_valid_hash_with_argon2id_hash_returns_true
|
18
|
+
assert Argon2id::Password.valid_hash?(
|
19
|
+
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" \
|
20
|
+
"$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc"
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_valid_hash_with_versionless_argon2id_hash_returns_true
|
25
|
+
assert Argon2id::Password.valid_hash?(
|
26
|
+
"$argon2id$m=65536,t=2,p=1$c29tZXNhbHQ" \
|
27
|
+
"$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc"
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_valid_hash_with_argon2i_hash_returns_false
|
32
|
+
refute Argon2id::Password.valid_hash?(
|
33
|
+
"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" \
|
34
|
+
"$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_valid_hash_with_partial_argon2id_hash_returns_false
|
39
|
+
refute Argon2id::Password.valid_hash?(
|
40
|
+
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_valid_hash_with_argon2id_hash_with_null_bytes_returns_false
|
45
|
+
refute Argon2id::Password.valid_hash?(
|
46
|
+
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" \
|
47
|
+
"$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc\x00foo"
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_valid_hash_with_bcrypt_hash_returns_false
|
52
|
+
refute Argon2id::Password.valid_hash?(
|
53
|
+
"$2a$12$stsRn7Mi9r02.keRyF4OK.Aq4UWOU185lWggfUQfcupAi.b7AI/nS"
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_valid_hash_with_nil_returns_false
|
58
|
+
refute Argon2id::Password.valid_hash?(nil)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_valid_hash_with_coercible_argon2id_hash_returns_true
|
62
|
+
assert Argon2id::Password.valid_hash?(
|
63
|
+
StringLike.new(
|
64
|
+
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" \
|
65
|
+
"$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc"
|
66
|
+
)
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_valid_hash_with_coercible_bcrypt_hash_returns_false
|
71
|
+
refute Argon2id::Password.valid_hash?(
|
72
|
+
StringLike.new(
|
73
|
+
"$2a$12$stsRn7Mi9r02.keRyF4OK.Aq4UWOU185lWggfUQfcupAi.b7AI/nS"
|
74
|
+
)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_new_m_65536_t_2_p_1_equals_password
|
79
|
+
password = Argon2id::Password.new(
|
80
|
+
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" \
|
81
|
+
"$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc"
|
82
|
+
)
|
83
|
+
|
84
|
+
assert password == "password"
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_new_m_262144_t_2_p_1_equals_password
|
88
|
+
password = Argon2id::Password.new(
|
89
|
+
"$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ" \
|
90
|
+
"$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow"
|
91
|
+
)
|
92
|
+
|
93
|
+
assert password == "password"
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_new_m_256_t_2_p_1_equals_password
|
97
|
+
password = Argon2id::Password.new(
|
98
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
99
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
100
|
+
)
|
101
|
+
|
102
|
+
assert password == "password"
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_new_m_256_t_2_p_2_equals_password
|
106
|
+
password = Argon2id::Password.new(
|
107
|
+
"$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ" \
|
108
|
+
"$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc"
|
109
|
+
)
|
110
|
+
|
111
|
+
assert password == "password"
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_new_m_65536_t_1_p_1_equals_password
|
115
|
+
password = Argon2id::Password.new(
|
116
|
+
"$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ" \
|
117
|
+
"$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg"
|
118
|
+
)
|
119
|
+
|
120
|
+
assert password == "password"
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_new_m_65536_t_4_p_1_equals_password
|
124
|
+
password = Argon2id::Password.new(
|
125
|
+
"$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ" \
|
126
|
+
"$kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw"
|
127
|
+
)
|
128
|
+
|
129
|
+
assert password == "password"
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_new_m_65536_t_2_p_1_equals_differentpassword
|
133
|
+
password = Argon2id::Password.new(
|
134
|
+
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" \
|
135
|
+
"$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94"
|
136
|
+
)
|
137
|
+
|
138
|
+
assert password == "differentpassword"
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_new_m_65536_t_2_p_1_with_diffsalt_equals_password
|
142
|
+
password = Argon2id::Password.new(
|
143
|
+
"$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ" \
|
144
|
+
"$vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw"
|
145
|
+
)
|
146
|
+
|
147
|
+
assert password == "password"
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_new_with_versionless_hash_equals_password
|
151
|
+
password = Argon2id::Password.new(
|
152
|
+
"$argon2id$m=256,t=2,p=1$c29tZXNhbHQ" \
|
153
|
+
"$2gcOV25Q8vOKPIl8vdxsf7QCjocJcf+erntOGHkpXm4"
|
154
|
+
)
|
155
|
+
|
156
|
+
assert password == "password"
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_new_with_non_argon2id_hash_raises_argument_error
|
160
|
+
assert_raises(ArgumentError) do
|
161
|
+
Argon2id::Password.new(
|
162
|
+
"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" \
|
163
|
+
"$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
|
164
|
+
)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_new_with_invalid_hash_raises_argument_error
|
169
|
+
assert_raises(ArgumentError) do
|
170
|
+
Argon2id::Password.new("not a valid hash")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_new_with_nil_raises_argument_error
|
175
|
+
assert_raises(ArgumentError) do
|
176
|
+
Argon2id::Password.new(nil)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_new_with_coercible_equals_password
|
181
|
+
password = Argon2id::Password.new(
|
182
|
+
StringLike.new(
|
183
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
184
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
185
|
+
)
|
186
|
+
)
|
187
|
+
|
188
|
+
assert password == "password"
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_encoded_returns_the_full_encoded_hash
|
192
|
+
password = Argon2id::Password.new(
|
193
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
194
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
195
|
+
)
|
196
|
+
|
197
|
+
assert_equal(
|
198
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4",
|
199
|
+
password.encoded
|
200
|
+
)
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_version_returns_the_version
|
204
|
+
password = Argon2id::Password.new(
|
205
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
206
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
207
|
+
)
|
208
|
+
|
209
|
+
assert_equal(19, password.version)
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_version_with_no_version_returns_the_default_version
|
213
|
+
password = Argon2id::Password.new(
|
214
|
+
"$argon2id$m=256,t=2,p=1$c29tZXNhbHQ" \
|
215
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
216
|
+
)
|
217
|
+
|
218
|
+
assert_equal(16, password.version)
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_m_cost_returns_m_cost
|
222
|
+
password = Argon2id::Password.new(
|
223
|
+
"$argon2id$m=256,t=2,p=1$c29tZXNhbHQ" \
|
224
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
225
|
+
)
|
226
|
+
|
227
|
+
assert_equal(256, password.m_cost)
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_t_cost_returns_t_cost
|
231
|
+
password = Argon2id::Password.new(
|
232
|
+
"$argon2id$m=256,t=2,p=1$c29tZXNhbHQ" \
|
233
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
234
|
+
)
|
235
|
+
|
236
|
+
assert_equal(2, password.t_cost)
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_parallelism_returns_parallelism
|
240
|
+
password = Argon2id::Password.new(
|
241
|
+
"$argon2id$m=256,t=2,p=1$c29tZXNhbHQ" \
|
242
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
243
|
+
)
|
244
|
+
|
245
|
+
assert_equal(1, password.parallelism)
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_salt_returns_decoded_salt
|
249
|
+
password = Argon2id::Password.new(
|
250
|
+
"$argon2id$m=256,t=2,p=1$c29tZXNhbHQ" \
|
251
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
252
|
+
)
|
253
|
+
|
254
|
+
assert_equal("somesalt", password.salt)
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_salt_returns_decoded_binary_salt
|
258
|
+
password = Argon2id::Password.new(
|
259
|
+
"$argon2id$v=19$m=256,t=2,p=1$FImSDfu1p8vf1mZBL2PCkg" \
|
260
|
+
"$vG4bIkTJGMx6OvkLuKTeq37DTyAf8gF2Ouf3zSLlYVc"
|
261
|
+
)
|
262
|
+
|
263
|
+
assert_equal(
|
264
|
+
"\x14\x89\x92\r\xFB\xB5\xA7\xCB\xDF\xD6fA/c\xC2\x92".b,
|
265
|
+
password.salt
|
266
|
+
)
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_output_returns_decoded_output
|
270
|
+
password = Argon2id::Password.new(
|
271
|
+
"$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ" \
|
272
|
+
"$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg"
|
273
|
+
)
|
274
|
+
|
275
|
+
assert_equal(
|
276
|
+
"\xF6\xA5\xAD\xC1\xBAr=\xDD\xEF\x9BZ\xC1\xD4d\xE1\x80\xFC\xD9\xDF\xFC\x9D\x1C\xBFv\xCC\xA2\xFE\xD7\x95\xD9\xCA\x98".b,
|
277
|
+
password.output
|
278
|
+
)
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_to_s_returns_the_full_encoded_hash
|
282
|
+
password = Argon2id::Password.new(
|
283
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
284
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
285
|
+
)
|
286
|
+
|
287
|
+
assert_equal(
|
288
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4",
|
289
|
+
password.to_s
|
290
|
+
)
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_equals_correct_password_returns_true
|
294
|
+
password = Argon2id::Password.new(
|
295
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
296
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
297
|
+
)
|
298
|
+
|
299
|
+
assert password == "password"
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_equals_incorrect_password_returns_false
|
303
|
+
password = Argon2id::Password.new(
|
304
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
305
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
306
|
+
)
|
307
|
+
|
308
|
+
refute password == "differentpassword"
|
309
|
+
end
|
310
|
+
|
311
|
+
def test_equals_nil_returns_false
|
312
|
+
password = Argon2id::Password.new(
|
313
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
314
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
315
|
+
)
|
316
|
+
|
317
|
+
refute password == nil
|
318
|
+
end
|
319
|
+
|
320
|
+
def test_equals_coercible_correct_password_returns_true
|
321
|
+
password = Argon2id::Password.new(
|
322
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
323
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
324
|
+
)
|
325
|
+
|
326
|
+
assert password == StringLike.new("password")
|
327
|
+
end
|
328
|
+
|
329
|
+
def test_equals_coercible_incorrect_password_returns_false
|
330
|
+
password = Argon2id::Password.new(
|
331
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
332
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
333
|
+
)
|
334
|
+
|
335
|
+
refute password == StringLike.new("differentpassword")
|
336
|
+
end
|
337
|
+
|
338
|
+
def test_is_password_correct_password_returns_true
|
339
|
+
password = Argon2id::Password.new(
|
340
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
341
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
342
|
+
)
|
343
|
+
|
344
|
+
assert password.is_password?("password")
|
345
|
+
end
|
346
|
+
|
347
|
+
def test_is_password_incorrect_password_returns_false
|
348
|
+
password = Argon2id::Password.new(
|
349
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
350
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
351
|
+
)
|
352
|
+
|
353
|
+
refute password.is_password?("differentpassword")
|
354
|
+
end
|
355
|
+
|
356
|
+
def test_is_password_nil_returns_false
|
357
|
+
password = Argon2id::Password.new(
|
358
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
359
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
360
|
+
)
|
361
|
+
|
362
|
+
refute password.is_password?(nil)
|
363
|
+
end
|
364
|
+
|
365
|
+
def test_is_password_coercible_correct_password_returns_true
|
366
|
+
password = Argon2id::Password.new(
|
367
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
368
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
369
|
+
)
|
370
|
+
|
371
|
+
assert password.is_password?(StringLike.new("password"))
|
372
|
+
end
|
373
|
+
|
374
|
+
def test_is_password_coercible_incorrect_password_returns_false
|
375
|
+
password = Argon2id::Password.new(
|
376
|
+
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" \
|
377
|
+
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
|
378
|
+
)
|
379
|
+
|
380
|
+
refute password.is_password?(StringLike.new("differentpassword"))
|
381
|
+
end
|
382
|
+
|
383
|
+
def test_create_password_returns_password
|
384
|
+
password = Argon2id::Password.create("password")
|
385
|
+
|
386
|
+
assert_instance_of Argon2id::Password, password
|
387
|
+
end
|
388
|
+
|
389
|
+
def test_create_password_uses_version_13
|
390
|
+
password = Argon2id::Password.create("password")
|
391
|
+
|
392
|
+
assert_equal 0x13, password.version
|
393
|
+
end
|
394
|
+
|
395
|
+
def test_create_password_uses_default_t_cost
|
396
|
+
password = Argon2id::Password.create("password")
|
397
|
+
|
398
|
+
assert_equal 2, password.t_cost
|
399
|
+
end
|
400
|
+
|
401
|
+
def test_create_password_uses_default_m_cost
|
402
|
+
password = Argon2id::Password.create("password")
|
403
|
+
|
404
|
+
assert_equal 19_456, password.m_cost
|
405
|
+
end
|
406
|
+
|
407
|
+
def test_create_password_uses_default_parallelism
|
408
|
+
password = Argon2id::Password.create("password")
|
409
|
+
|
410
|
+
assert_equal 1, password.parallelism
|
411
|
+
end
|
412
|
+
|
413
|
+
def test_create_password_uses_default_salt_len
|
414
|
+
password = Argon2id::Password.create("password")
|
415
|
+
|
416
|
+
assert_equal 16, password.salt.bytesize
|
417
|
+
end
|
418
|
+
|
419
|
+
def test_create_password_uses_default_output_len
|
420
|
+
password = Argon2id::Password.create("password")
|
421
|
+
|
422
|
+
assert_equal 32, password.output.bytesize
|
423
|
+
end
|
424
|
+
|
425
|
+
def test_create_password_with_t_cost_changes_t_cost
|
426
|
+
password = Argon2id::Password.create("password", t_cost: 1)
|
427
|
+
|
428
|
+
assert_equal(1, password.t_cost)
|
429
|
+
end
|
430
|
+
|
431
|
+
def test_create_password_with_too_small_t_cost_raises_error
|
432
|
+
assert_raises(Argon2id::Error) do
|
433
|
+
Argon2id::Password.create("password", t_cost: 0)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def test_create_password_with_m_cost_changes_m_cost
|
438
|
+
password = Argon2id::Password.create("password", m_cost: 8)
|
439
|
+
|
440
|
+
assert_equal(8, password.m_cost)
|
441
|
+
end
|
442
|
+
|
443
|
+
def test_create_password_with_too_small_m_cost_raises_error
|
444
|
+
assert_raises(Argon2id::Error) do
|
445
|
+
Argon2id::Password.create("password", m_cost: 0)
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
def test_create_password_with_parallelism_changes_parallelism
|
450
|
+
password = Argon2id::Password.create("password", parallelism: 2)
|
451
|
+
|
452
|
+
assert_equal(2, password.parallelism)
|
453
|
+
end
|
454
|
+
|
455
|
+
def test_create_password_with_too_small_parallelism_raises_error
|
456
|
+
assert_raises(Argon2id::Error) do
|
457
|
+
Argon2id::Password.create("password", parallelism: 0)
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
def test_create_password_with_too_small_salt_raises_error
|
462
|
+
assert_raises(Argon2id::Error) do
|
463
|
+
Argon2id::Password.create("password", salt_len: 0)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def test_create_password_with_output_len_changes_output_len
|
468
|
+
password = Argon2id::Password.create("password", output_len: 8)
|
469
|
+
|
470
|
+
assert_equal 8, password.output.bytesize
|
471
|
+
end
|
472
|
+
|
473
|
+
def test_create_password_with_too_output_len_raises_error
|
474
|
+
assert_raises(Argon2id::Error) do
|
475
|
+
Argon2id::Password.create("password", output_len: 0)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def test_create_password_inherits_t_cost_from_argon2id
|
480
|
+
Argon2id.t_cost = 1
|
481
|
+
|
482
|
+
password = Argon2id::Password.create("password")
|
483
|
+
|
484
|
+
assert_equal(1, password.t_cost)
|
485
|
+
ensure
|
486
|
+
Argon2id.t_cost = Argon2id::DEFAULT_T_COST
|
487
|
+
end
|
488
|
+
|
489
|
+
def test_create_password_inherits_m_cost_from_argon2id
|
490
|
+
Argon2id.m_cost = 8
|
491
|
+
|
492
|
+
password = Argon2id::Password.create("password")
|
493
|
+
|
494
|
+
assert_equal(8, password.m_cost)
|
495
|
+
ensure
|
496
|
+
Argon2id.m_cost = Argon2id::DEFAULT_M_COST
|
497
|
+
end
|
498
|
+
|
499
|
+
def test_create_password_inherits_parallelism_from_argon2id
|
500
|
+
Argon2id.parallelism = 2
|
501
|
+
|
502
|
+
password = Argon2id::Password.create("password")
|
503
|
+
|
504
|
+
assert_equal(2, password.parallelism)
|
505
|
+
ensure
|
506
|
+
Argon2id.parallelism = Argon2id::DEFAULT_PARALLELISM
|
507
|
+
end
|
508
|
+
|
509
|
+
def test_create_password_inherits_salt_len_from_argon2id
|
510
|
+
Argon2id.salt_len = 8
|
511
|
+
|
512
|
+
password = Argon2id::Password.create("password")
|
513
|
+
|
514
|
+
assert_equal(8, password.salt.bytesize)
|
515
|
+
ensure
|
516
|
+
Argon2id.salt_len = Argon2id::DEFAULT_SALT_LEN
|
517
|
+
end
|
518
|
+
|
519
|
+
def test_create_password_inherits_output_len_from_argon2id
|
520
|
+
Argon2id.output_len = 8
|
521
|
+
|
522
|
+
password = Argon2id::Password.create("password")
|
523
|
+
|
524
|
+
assert_equal(8, password.output.bytesize)
|
525
|
+
ensure
|
526
|
+
Argon2id.output_len = Argon2id::DEFAULT_OUTPUT_LEN
|
527
|
+
end
|
528
|
+
|
529
|
+
def test_create_password_equals_correct_password
|
530
|
+
password = Argon2id::Password.create("password")
|
531
|
+
|
532
|
+
assert password == "password"
|
533
|
+
end
|
534
|
+
|
535
|
+
def test_create_password_does_not_equal_incorrect_password
|
536
|
+
password = Argon2id::Password.create("password")
|
537
|
+
|
538
|
+
refute password == "differentpassword"
|
539
|
+
end
|
540
|
+
|
541
|
+
def test_hashing_password_verifies_correct_password
|
542
|
+
hash = Argon2id::Password.create("password").to_s
|
543
|
+
password = Argon2id::Password.new(hash)
|
544
|
+
|
545
|
+
assert password == "password"
|
546
|
+
end
|
547
|
+
|
548
|
+
def test_hashing_password_does_not_verify_incorrect_password
|
549
|
+
hash = Argon2id::Password.create("password").to_s
|
550
|
+
password = Argon2id::Password.new(hash)
|
551
|
+
|
552
|
+
refute password == "differentpassword"
|
553
|
+
end
|
554
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest/autorun"
|
4
|
+
require "argon2id"
|
5
|
+
|
6
|
+
class TestArgon2id < Minitest::Test
|
7
|
+
def test_t_cost_is_default_t_cost
|
8
|
+
assert_equal 2, Argon2id.t_cost
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_m_cost_is_default_m_cost
|
12
|
+
assert_equal 19_456, Argon2id.m_cost
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_parallelism_is_default_parallelism
|
16
|
+
assert_equal 1, Argon2id.parallelism
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_salt_len_is_default_salt_len
|
20
|
+
assert_equal 16, Argon2id.salt_len
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_output_len_is_default_output_len
|
24
|
+
assert_equal 32, Argon2id.output_len
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_t_cost_can_be_overridden
|
28
|
+
Argon2id.t_cost = 1
|
29
|
+
|
30
|
+
assert_equal 1, Argon2id.t_cost
|
31
|
+
ensure
|
32
|
+
Argon2id.t_cost = Argon2id::DEFAULT_T_COST
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_m_cost_can_be_overridden
|
36
|
+
Argon2id.m_cost = 256
|
37
|
+
|
38
|
+
assert_equal 256, Argon2id.m_cost
|
39
|
+
ensure
|
40
|
+
Argon2id.m_cost = Argon2id::DEFAULT_M_COST
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_parallelism_can_be_overridden
|
44
|
+
Argon2id.parallelism = 2
|
45
|
+
|
46
|
+
assert_equal 2, Argon2id.parallelism
|
47
|
+
ensure
|
48
|
+
Argon2id.parallelism = Argon2id::DEFAULT_PARALLELISM
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_salt_len_can_be_overridden
|
52
|
+
Argon2id.salt_len = 8
|
53
|
+
|
54
|
+
assert_equal 8, Argon2id.salt_len
|
55
|
+
ensure
|
56
|
+
Argon2id.salt_len = Argon2id::DEFAULT_SALT_LEN
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_output_len_can_be_overridden
|
60
|
+
Argon2id.output_len = 16
|
61
|
+
|
62
|
+
assert_equal 16, Argon2id.output_len
|
63
|
+
ensure
|
64
|
+
Argon2id.output_len = Argon2id::DEFAULT_OUTPUT_LEN
|
65
|
+
end
|
66
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: argon2id
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0.rc1
|
5
5
|
platform: x86_64-darwin
|
6
6
|
authors:
|
7
7
|
- Paul Mucur
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.7.0.rc1
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.7.0.rc1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,16 +83,15 @@ files:
|
|
83
83
|
- ext/argon2id/libargon2/thread.c
|
84
84
|
- ext/argon2id/libargon2/thread.h
|
85
85
|
- lib/argon2id.rb
|
86
|
-
- lib/argon2id/2.6/argon2id.bundle
|
87
|
-
- lib/argon2id/2.7/argon2id.bundle
|
88
|
-
- lib/argon2id/3.0/argon2id.bundle
|
89
86
|
- lib/argon2id/3.1/argon2id.bundle
|
90
87
|
- lib/argon2id/3.2/argon2id.bundle
|
91
88
|
- lib/argon2id/3.3/argon2id.bundle
|
89
|
+
- lib/argon2id/3.4/argon2id.bundle
|
92
90
|
- lib/argon2id/extension.rb
|
93
91
|
- lib/argon2id/password.rb
|
94
92
|
- lib/argon2id/version.rb
|
95
|
-
- test/test_password.rb
|
93
|
+
- test/argon2id/test_password.rb
|
94
|
+
- test/test_argon2id.rb
|
96
95
|
homepage: https://github.com/mudge/argon2id
|
97
96
|
licenses:
|
98
97
|
- BSD-3-Clause
|
@@ -113,15 +112,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
112
|
requirements:
|
114
113
|
- - ">="
|
115
114
|
- !ruby/object:Gem::Version
|
116
|
-
version: '
|
115
|
+
version: '3.1'
|
117
116
|
- - "<"
|
118
117
|
- !ruby/object:Gem::Version
|
119
|
-
version: 3.
|
118
|
+
version: 3.5.dev
|
120
119
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
120
|
requirements:
|
122
|
-
- - "
|
121
|
+
- - ">"
|
123
122
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
123
|
+
version: 1.3.1
|
125
124
|
requirements: []
|
126
125
|
rubygems_version: 3.3.26
|
127
126
|
signing_key:
|
Binary file
|
Binary file
|
Binary file
|
data/test/test_password.rb
DELETED
@@ -1,232 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "minitest/autorun"
|
4
|
-
require "argon2id"
|
5
|
-
|
6
|
-
class TestPassword < Minitest::Test
|
7
|
-
def test_create_returns_encoded_password_with_defaults
|
8
|
-
password = Argon2id::Password.create("opensesame")
|
9
|
-
|
10
|
-
assert password.to_s.start_with?("$argon2id$")
|
11
|
-
assert password.to_s.include?("t=2")
|
12
|
-
assert password.to_s.include?("m=19456")
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_create_options_can_override_parameters
|
16
|
-
password = Argon2id::Password.create("opensesame", t_cost: 2, m_cost: 256)
|
17
|
-
|
18
|
-
assert password.to_s.include?("t=2")
|
19
|
-
assert password.to_s.include?("m=256")
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_create_uses_argon2id_configuration
|
23
|
-
Argon2id.t_cost = 2
|
24
|
-
Argon2id.m_cost = 256
|
25
|
-
|
26
|
-
password = Argon2id::Password.create("opensesame")
|
27
|
-
|
28
|
-
assert password.to_s.include?("t=2")
|
29
|
-
assert password.to_s.include?("m=256")
|
30
|
-
ensure
|
31
|
-
Argon2id.t_cost = Argon2id::DEFAULT_T_COST
|
32
|
-
Argon2id.m_cost = Argon2id::DEFAULT_M_COST
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_create_coerces_pwd_to_string
|
36
|
-
password = Argon2id::Password.create(123, t_cost: 2, m_cost: 256)
|
37
|
-
|
38
|
-
assert password.to_s.start_with?("$argon2id$")
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_create_coerces_costs_to_integer
|
42
|
-
password = Argon2id::Password.create("opensesame", t_cost: "2", m_cost: "256", parallelism: "1", salt_len: "8", output_len: "32")
|
43
|
-
|
44
|
-
assert password.to_s.start_with?("$argon2id$")
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_create_raises_if_given_non_integer_costs
|
48
|
-
assert_raises(ArgumentError) do
|
49
|
-
Argon2id::Password.create("opensesame", t_cost: "not an integer")
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def test_equals_correct_password
|
54
|
-
password = Argon2id::Password.create("opensesame", t_cost: 2, m_cost: 256)
|
55
|
-
|
56
|
-
assert password == "opensesame"
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_does_not_equal_invalid_password
|
60
|
-
password = Argon2id::Password.create("opensesame", t_cost: 2, m_cost: 256)
|
61
|
-
|
62
|
-
refute password == "notopensesame"
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_is_password_returns_true_with_correct_password
|
66
|
-
password = Argon2id::Password.create("opensesame", t_cost: 2, m_cost: 256)
|
67
|
-
|
68
|
-
assert password.is_password?("opensesame")
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_is_password_returns_false_with_incorrect_password
|
72
|
-
password = Argon2id::Password.create("opensesame", t_cost: 2, m_cost: 256)
|
73
|
-
|
74
|
-
refute password.is_password?("notopensesame")
|
75
|
-
end
|
76
|
-
|
77
|
-
def test_salt_returns_the_original_salt
|
78
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
79
|
-
|
80
|
-
assert_equal "somesalt", password.salt
|
81
|
-
end
|
82
|
-
|
83
|
-
def test_salt_returns_raw_bytes
|
84
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$KmIxrXv4lrnSJPO0LN7Gdw$lB3724qLPL9MNi10lkvIb4VxIk3q841CLvq0WTCZ0VQ")
|
85
|
-
|
86
|
-
assert_equal "*b1\xAD{\xF8\x96\xB9\xD2$\xF3\xB4,\xDE\xC6w".b, password.salt
|
87
|
-
end
|
88
|
-
|
89
|
-
def test_raises_for_invalid_hashes
|
90
|
-
assert_raises(ArgumentError) do
|
91
|
-
Argon2id::Password.new("not a valid hash")
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def test_raises_for_partial_hashes
|
96
|
-
assert_raises(ArgumentError) do
|
97
|
-
Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$KmIxrXv4lrnSJPO0LN7Gdw")
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def test_raises_for_hashes_with_null_bytes
|
102
|
-
assert_raises(ArgumentError) do
|
103
|
-
Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4\x00foo")
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def test_raises_for_non_argon2id_hashes
|
108
|
-
assert_raises(ArgumentError) do
|
109
|
-
Argon2id::Password.new("$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8")
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def test_salt_supports_versionless_hashes
|
114
|
-
password = Argon2id::Password.new("$argon2id$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
115
|
-
|
116
|
-
assert_equal "somesalt", password.salt
|
117
|
-
end
|
118
|
-
|
119
|
-
def test_coerces_given_hash_to_string
|
120
|
-
password = Argon2id::Password.create("password")
|
121
|
-
|
122
|
-
assert Argon2id::Password.new(password) == "password"
|
123
|
-
end
|
124
|
-
|
125
|
-
def test_extracting_version_from_hash
|
126
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
127
|
-
|
128
|
-
assert_equal 19, password.version
|
129
|
-
end
|
130
|
-
|
131
|
-
def test_extracting_version_from_versionless_hash
|
132
|
-
password = Argon2id::Password.new("$argon2id$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
133
|
-
|
134
|
-
assert_equal 16, password.version
|
135
|
-
end
|
136
|
-
|
137
|
-
def test_extracting_time_cost_from_hash
|
138
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
139
|
-
|
140
|
-
assert_equal 2, password.t_cost
|
141
|
-
end
|
142
|
-
|
143
|
-
def test_extracting_time_cost_from_versionless_hash
|
144
|
-
password = Argon2id::Password.new("$argon2id$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
145
|
-
|
146
|
-
assert_equal 2, password.t_cost
|
147
|
-
end
|
148
|
-
|
149
|
-
def test_extracting_memory_cost_from_hash
|
150
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
151
|
-
|
152
|
-
assert_equal 256, password.m_cost
|
153
|
-
end
|
154
|
-
|
155
|
-
def test_extracting_memory_cost_from_versionless_hash
|
156
|
-
password = Argon2id::Password.new("$argon2id$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
157
|
-
|
158
|
-
assert_equal 256, password.m_cost
|
159
|
-
end
|
160
|
-
|
161
|
-
def test_extracting_parallelism_from_hash
|
162
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
163
|
-
|
164
|
-
assert_equal 1, password.parallelism
|
165
|
-
end
|
166
|
-
|
167
|
-
def test_extracting_parallelism_from_versionless_hash
|
168
|
-
password = Argon2id::Password.new("$argon2id$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
169
|
-
|
170
|
-
assert_equal 1, password.parallelism
|
171
|
-
end
|
172
|
-
|
173
|
-
def test_extracting_output_from_hash
|
174
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
175
|
-
|
176
|
-
assert_equal "\x9D\xFE\xB9\x10\xE8\v\xAD\x03\x11\xFE\xE2\x0F\x9C\x0E+\x12\xC1y\x87\xB4\xCA\xC9\f.\xF5M[0!\xC6\x8B\xFE".b, password.output
|
177
|
-
end
|
178
|
-
|
179
|
-
def test_libargon2_test_case_1
|
180
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
181
|
-
|
182
|
-
assert password == "password"
|
183
|
-
end
|
184
|
-
|
185
|
-
def test_libargon2_test_case_1_returns_false_with_incorrect_password
|
186
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
187
|
-
|
188
|
-
refute password == "not password"
|
189
|
-
end
|
190
|
-
|
191
|
-
def test_libargon2_test_case_2
|
192
|
-
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc")
|
193
|
-
|
194
|
-
assert password == "password"
|
195
|
-
end
|
196
|
-
|
197
|
-
def test_encoded_password_does_not_include_trailing_null_byte
|
198
|
-
password = Argon2id::Password.create("password", t_cost: 2, m_cost: 256, salt_len: 8)
|
199
|
-
|
200
|
-
refute password.to_s.end_with?("\x00")
|
201
|
-
end
|
202
|
-
|
203
|
-
def test_raises_with_too_short_output
|
204
|
-
assert_raises(Argon2id::Error) do
|
205
|
-
Argon2id::Password.create("password", t_cost: 2, m_cost: 256, salt_len: 8, output_len: 1)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
def test_raises_with_too_few_threads_and_compute_lanes
|
210
|
-
assert_raises(Argon2id::Error) do
|
211
|
-
Argon2id::Password.create("password", t_cost: 2, m_cost: 256, parallelism: 0, salt_len: 8)
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def test_raises_with_too_small_memory_cost
|
216
|
-
assert_raises(Argon2id::Error) do
|
217
|
-
Argon2id::Password.create("password", t_cost: 2, m_cost: 0, salt_len: 8)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def test_raises_with_too_small_time_cost
|
222
|
-
assert_raises(Argon2id::Error) do
|
223
|
-
Argon2id::Password.create("password", t_cost: 0, m_cost: 256, salt_len: 8)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
def test_raises_with_too_short_salt
|
228
|
-
assert_raises(Argon2id::Error) do
|
229
|
-
Argon2id::Password.create("password", t_cost: 2, m_cost: 256, salt_len: 0)
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|