argon2id 0.6.0-x86_64-darwin → 0.8.0.rc1-x86_64-darwin

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
2
  SHA256:
3
- metadata.gz: 5f2fa3a0c89adf3fca0711750c56329d74466bc801a7381f9ce2ae83ed68e29e
4
- data.tar.gz: bcca62709a26e6c5ee39355e6b5561a82f899aca4aa79f69e41610ec105645da
3
+ metadata.gz: fdd98f6b16d85f231c39f64181187b27969135b7005a502c5b7f55ef2109f7c8
4
+ data.tar.gz: 5936e4e00441305b9916fbfd1994f9cae99334e7f4ff6190c9c46b5c41396cbe
5
5
  SHA512:
6
- metadata.gz: a28748a557f2fec6624a6d59bffc112457749bb761e2505088bb13ebed463e4015d863bd9d1e123cdbd216d705019f4c27e6a1868c6f9d32a8fee2e92c2c4aa5
7
- data.tar.gz: 8fcbb5a4c7cc8a2cda024eb1db7eb0a3b74c797bbd78ffed169ad3a85f293efac09b6a7c187b14a388bdb0170d2ed0791a69963dcd667cb619ee10d721fe5834
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
  [![Build Status](https://github.com/mudge/argon2id/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mudge/argon2id/actions)
7
7
 
8
- **Current version:** 0.6.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/) 2.6 to 3.3
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` and `arm-linux` (requires [glibc](https://www.gnu.org/software/libc/) 2.29+)
191
- * `x86-linux` and `x86_64-linux` (requires [glibc](https://www.gnu.org/software/libc/) 2.17+)
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-mingw32` and `x64-mingw-ucrt`
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.5.0
205
- Fetching argon2id-0.5.0-arm64-darwin.gem
206
- Downloaded argon2id-0.5.0-arm64-darwin
207
- $ sha256sum argon2id-0.5.0-arm64-darwin.gem
208
- 871e9d9bcad09e75620ce9ddd32cd99a4ebc3a6db1516e487680787faa7368a3 argon2id-0.5.0-arm64-darwin.gem
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.5.0-arm64-darwin.gem.sig argon2id-0.5.0-arm64-darwin.gem
219
- gpg: Signature made Sat 2 Nov 21:09:51 2024 GMT
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
- arm-linux
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.3.0 3.2.0 3.1.0 3.0.0 2.7.0 2.6.0].join(":")
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 = ">= 2.6.0"
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.5")
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
@@ -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
- .with_salt(salt_bytes)
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
- .with_salt(salt.to_java_bytes)
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)
@@ -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")
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Argon2id
4
- VERSION = "0.6.0"
4
+ VERSION = "0.8.0.rc1"
5
5
  end
@@ -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.6.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-05 00:00:00.000000000 Z
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: '1.5'
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: '1.5'
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: '2.6'
115
+ version: '3.1'
117
116
  - - "<"
118
117
  - !ruby/object:Gem::Version
119
- version: 3.4.dev
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: '0'
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
@@ -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