argon2id 0.5.0-java → 0.7.0-java

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10458122f81ed4191ad7b8d994930c60631482c8a4ef92f333667ae68bac3320
4
- data.tar.gz: f86fa7d205cfffa8eef2333e9f5fb8dd98235be96a9a0ea10a6cc38764934d49
3
+ metadata.gz: 80c55593bcbc6cf76d949f2f587ce63eb889f9fa06f7a054adb25b1369e36398
4
+ data.tar.gz: 85354c4570b9266deaf39384bed27bf08091850b73d5cdeadb646af47aa29e01
5
5
  SHA512:
6
- metadata.gz: 26c9bcf40c47d5c1c7488689a521548e195d5cc33839d397edbb83e124301053fff65dcb2cd9e85a1040720127c28b5c23602f99730dea7d2749f465c9284d43
7
- data.tar.gz: 5ea94aaf0ae8b076c8f1eff1a72e000ed6a8d0bdf469eac4c0ad2bc1fb78b2a938e99fa5daa8a604b982a7645dceef7451903cc7829620c6bbbc36a3565ce2c1
6
+ metadata.gz: 2a9bb956f5d0aeadde01d2cf994d417573a7b24b3e53d74f1f66c90b0def02799bc7e23e8b721c10caaa7d1b7b63230b5ae56d9847261544db96254184215b8b
7
+ data.tar.gz: 3470403d3516979b84407c0092c15b5f9458bb99df66309ffb8e0345722a9e56965e6a59109305e322809de0f6db2f9bdf9bc0a7b61c29101a0eb5e9d971eb2f
data/CHANGELOG.md CHANGED
@@ -5,6 +5,24 @@ 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.7.0] - 2024-11-08
9
+
10
+ ### Fixed
11
+
12
+ - Fixed verifying Argon2id encoded hashes without a version number on JRuby
13
+
14
+ ### Added
15
+
16
+ - Added a new `Argon2id::Password.valid_hash?` API for testing if a given
17
+ encoded hash is a valid Argon2id hash or not (e.g. if you want to check
18
+ which hashing function was used to store a user's password)
19
+
20
+ ## [0.6.0] - 2024-11-05
21
+
22
+ ### Changed
23
+
24
+ - Move the internal API to `Argon2id::Password` and make it explicitly private
25
+
8
26
  ## [0.5.0] - 2024-11-02
9
27
 
10
28
  ### Removed
@@ -93,6 +111,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
93
111
  reference C implementation of Argon2, the password-hashing function that won
94
112
  the Password Hashing Competition.
95
113
 
114
+ [0.7.0]: https://github.com/mudge/argon2id/releases/tag/v0.7.0
115
+ [0.6.0]: https://github.com/mudge/argon2id/releases/tag/v0.6.0
96
116
  [0.5.0]: https://github.com/mudge/argon2id/releases/tag/v0.5.0
97
117
  [0.4.1]: https://github.com/mudge/argon2id/releases/tag/v0.4.1
98
118
  [0.4.0]: https://github.com/mudge/argon2id/releases/tag/v0.4.0
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.5.0
8
+ **Current version:** 0.7.0
9
9
  **Bundled Argon2 version:** libargon2.1 (20190702)
10
10
 
11
11
  ```ruby
@@ -26,6 +26,7 @@ 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)
30
31
  * [Requirements](#requirements)
31
32
  * [Native gems](#native-gems)
@@ -55,6 +56,8 @@ password.salt #=> "e-\xA7\x04U\x81\xA6{v\xF0x\xED\xCC\xD3\x96\xE3"
55
56
 
56
57
  — [OWASP Password Storage Cheat Sheet][]
57
58
 
59
+ 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).
60
+
58
61
  ## Usage
59
62
 
60
63
  Install argon2id as a dependency:
@@ -147,6 +150,18 @@ password.is_password?("opensesame") #=> true
147
150
  password.is_password?("notopensesame") #=> false
148
151
  ```
149
152
 
153
+ > [!CAUTION]
154
+ > `Argon2id::Password#==` only works if the plain text password is on the right, e.g. the following behaviour may be surprising:
155
+ >
156
+ > ```ruby
157
+ > password = Argon2id::Password.create("password")
158
+ > password == "password" #=> true
159
+ > "password" == password #=> false
160
+ > password == password #=> false
161
+ > ```
162
+ >
163
+ > If you want to avoid this ambiguity, prefer the `Argon2id::Password#is_password?` alias instead.
164
+
150
165
  The various parts of the encoded hash can be retrieved:
151
166
 
152
167
  ```ruby
@@ -160,6 +175,18 @@ password.output
160
175
  #=> "\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
176
  ```
162
177
 
178
+ ### Validating encoded hashes
179
+
180
+ 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:
181
+
182
+ ```ruby
183
+ Argon2id::Password.valid_hash?("$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc")
184
+ #=> true
185
+
186
+ Argon2id::Password.valid_hash?("$2a$12$stsRn7Mi9r02.keRyF4OK.Aq4UWOU185lWggfUQfcupAi.b7AI/nS")
187
+ #=> false
188
+ ```
189
+
163
190
  ### Errors
164
191
 
165
192
  Any errors returned from Argon2 will be raised as `Argon2id::Error`, e.g.
@@ -201,11 +228,11 @@ notes](https://github.com/mudge/argon2id/releases) for each version and can be
201
228
  checked with `sha256sum`, e.g.
202
229
 
203
230
  ```console
204
- $ gem fetch argon2id -v 0.4.1
205
- Fetching argon2id-0.4.1-arm64-darwin.gem
206
- Downloaded argon2id-0.4.1-arm64-darwin
207
- $ sha256sum argon2id-0.4.1-arm64-darwin.gem
208
- c74c06c2c4ce70d6c3822f05d83bab4ea431dd16ec086c9c856da3c6e0d9bbe9 argon2id-0.4.1-arm64-darwin.gem
231
+ $ gem fetch argon2id -v 0.6.0
232
+ Fetching argon2id-0.6.0-arm64-darwin.gem
233
+ Downloaded argon2id-0.6.0-arm64-darwin
234
+ $ sha256sum argon2id-0.6.0-arm64-darwin.gem
235
+ 18f1f04be4b5e7badb4d491762e57874febeeb46c64ce1b0a5e3a75b39b5baeb argon2id-0.6.0-arm64-darwin.gem
209
236
  ```
210
237
 
211
238
  [GPG](https://www.gnupg.org/) signatures are attached to each release (the
@@ -215,8 +242,8 @@ from a public keyserver, e.g. `gpg --keyserver keyserver.ubuntu.com --recv-key
215
242
  0x39AC3530070E0F75`):
216
243
 
217
244
  ```console
218
- $ gpg --verify argon2id-0.4.1-arm64-darwin.gem.sig argon2id-0.4.1-arm64-darwin.gem
219
- gpg: Signature made Sat 2 Nov 20:50:54 2024 GMT
245
+ $ gpg --verify argon2id-0.6.0-arm64-darwin.gem.sig argon2id-0.6.0-arm64-darwin.gem
246
+ gpg: Signature made Tue 5 Nov 11:30:47 2024 GMT
220
247
  gpg: using RSA key 702609D9C790F45B577D7BEC39AC3530070E0F75
221
248
  gpg: Good signature from "Paul Mucur <mudge@mudge.name>" [unknown]
222
249
  gpg: aka "Paul Mucur <paul@ghostcassette.com>" [unknown]
data/Rakefile CHANGED
@@ -20,16 +20,20 @@ ENV["RUBY_CC_VERSION"] = %w[3.3.0 3.2.0 3.1.0 3.0.0 2.7.0 2.6.0].join(":")
20
20
 
21
21
  gemspec = Gem::Specification.load("argon2id.gemspec")
22
22
 
23
- if RUBY_PLATFORM == "java"
24
- gemspec.files.reject! { |path| File.fnmatch?("ext/*", path) }
25
- gemspec.extensions.clear
26
- gemspec.platform = Gem::Platform.new("java")
27
- gemspec.required_ruby_version = ">= 3.1.0"
28
- end
29
-
30
23
  Gem::PackageTask.new(gemspec).define
31
24
 
25
+ namespace :java do
26
+ java_gemspec = gemspec.dup
27
+ java_gemspec.files.reject! { |path| File.fnmatch?("ext/*", path) }
28
+ java_gemspec.extensions.clear
29
+ java_gemspec.platform = Gem::Platform.new("java")
30
+ java_gemspec.required_ruby_version = ">= 3.1.0"
31
+
32
+ Gem::PackageTask.new(java_gemspec).define
33
+ end
34
+
32
35
  Rake::ExtensionTask.new("argon2id", gemspec) do |e|
36
+ e.lib_dir = "lib/argon2id"
33
37
  e.cross_compile = true
34
38
  e.cross_platform = cross_platforms
35
39
  end
@@ -57,15 +61,6 @@ namespace :gem do
57
61
  SCRIPT
58
62
  end
59
63
  end
60
-
61
- desc "Compile gem for JRuby"
62
- task :jruby do
63
- RakeCompilerDock.sh <<~SCRIPT, rubyvm: "jruby", platform: "jruby", verbose: true
64
- gem install bundler --no-document &&
65
- bundle &&
66
- bundle exec rake gem
67
- SCRIPT
68
- end
69
64
  end
70
65
 
71
66
  task default: [:compile, :test]
data/argon2id.gemspec CHANGED
@@ -45,11 +45,11 @@ Gem::Specification.new do |s|
45
45
  "ext/argon2id/libargon2/thread.c",
46
46
  "ext/argon2id/libargon2/thread.h",
47
47
  "lib/argon2id.rb",
48
+ "lib/argon2id/extension.rb",
48
49
  "lib/argon2id/password.rb",
49
50
  "lib/argon2id/version.rb",
50
- "test/test_hash_encoded.rb",
51
- "test/test_password.rb",
52
- "test/test_verify.rb"
51
+ "test/argon2id/test_password.rb",
52
+ "test/test_argon2id.rb"
53
53
  ]
54
54
  s.rdoc_options = ["--main", "README.md"]
55
55
 
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ if RUBY_PLATFORM == "java"
4
+ require "java"
5
+ require "openssl"
6
+
7
+ module Argon2id
8
+ Error = Class.new(StandardError)
9
+
10
+ class Password
11
+ def self.hash_encoded(t_cost, m_cost, parallelism, pwd, salt, hashlen)
12
+ raise Error, "Salt is too short" if salt.empty?
13
+
14
+ salt_bytes = salt.to_java_bytes
15
+ output = Java::byte[hashlen].new
16
+ params = Java::OrgBouncycastleCryptoParams::Argon2Parameters::Builder
17
+ .new(Java::OrgBouncycastleCryptoParams::Argon2Parameters::ARGON2_id)
18
+ .with_salt(salt_bytes)
19
+ .with_parallelism(parallelism)
20
+ .with_memory_as_kb(m_cost)
21
+ .with_iterations(t_cost)
22
+ .build
23
+ generator = Java::OrgBouncycastleCryptoGenerators::Argon2BytesGenerator.new
24
+
25
+ generator.init(params)
26
+ generator.generate_bytes(pwd.to_java_bytes, output)
27
+
28
+ encoder = Java::JavaUtil::Base64.get_encoder.without_padding
29
+ encoded_salt = encoder.encode_to_string(salt_bytes)
30
+ encoded_output = encoder.encode_to_string(output)
31
+
32
+ "$argon2id$v=19$m=#{m_cost},t=#{t_cost},p=#{parallelism}" \
33
+ "$#{encoded_salt}$#{encoded_output}"
34
+ rescue Java::JavaLang::IllegalStateException => e
35
+ raise Error, e.message
36
+ end
37
+
38
+ private_class_method :hash_encoded
39
+
40
+ private
41
+
42
+ def verify(pwd)
43
+ other_output = Java::byte[output.bytesize].new
44
+ params = Java::OrgBouncycastleCryptoParams::Argon2Parameters::Builder
45
+ .new(Java::OrgBouncycastleCryptoParams::Argon2Parameters::ARGON2_id)
46
+ .with_salt(salt.to_java_bytes)
47
+ .with_parallelism(parallelism)
48
+ .with_memory_as_kb(m_cost)
49
+ .with_iterations(t_cost)
50
+ .with_version(version)
51
+ .build
52
+ generator = Java::OrgBouncycastleCryptoGenerators::Argon2BytesGenerator.new
53
+ generator.init(params)
54
+ generator.generate_bytes(pwd.to_java_bytes, other_output)
55
+
56
+ Java::OrgBouncycastleUtil::Arrays.constant_time_are_equal?(
57
+ output.to_java_bytes,
58
+ other_output
59
+ )
60
+ end
61
+ end
62
+ end
63
+ else
64
+ begin
65
+ ::RUBY_VERSION =~ /(\d+\.\d+)/
66
+ require_relative "#{Regexp.last_match(1)}/argon2id"
67
+ rescue LoadError
68
+ require "argon2id/argon2id"
69
+ end
70
+ end
@@ -89,7 +89,7 @@ module Argon2id
89
89
  # #=> "$argon2id$v=19$m=12288,t=3,p=1$JigW7fFn+N3NImt+aWpuzw$eM5F1cKeIBALNTU6LuWra75Zi2nymGvQLWzJzVFv0Nc"
90
90
  def self.create(pwd, t_cost: Argon2id.t_cost, m_cost: Argon2id.m_cost, parallelism: Argon2id.parallelism, salt_len: Argon2id.salt_len, output_len: Argon2id.output_len)
91
91
  new(
92
- Argon2id.hash_encoded(
92
+ hash_encoded(
93
93
  Integer(t_cost),
94
94
  Integer(m_cost),
95
95
  Integer(parallelism),
@@ -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")
@@ -127,7 +134,7 @@ module Argon2id
127
134
  # password == "password" #=> true
128
135
  # password == "notpassword" #=> false
129
136
  def ==(other)
130
- Argon2id.verify(encoded, String(other))
137
+ verify(String(other))
131
138
  end
132
139
 
133
140
  alias_method :is_password?, :==
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Argon2id
4
- VERSION = "0.5.0"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/argon2id.rb CHANGED
@@ -1,18 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- if RUBY_PLATFORM == "java"
4
- require "openssl"
5
- else
6
- begin
7
- ::RUBY_VERSION =~ /(\d+\.\d+)/
8
- require_relative "#{Regexp.last_match(1)}/argon2id.so"
9
- rescue LoadError
10
- require "argon2id.so"
11
- end
12
- end
13
-
14
- require "argon2id/version"
3
+ require "argon2id/extension"
15
4
  require "argon2id/password"
5
+ require "argon2id/version"
16
6
 
17
7
  module Argon2id
18
8
  # The default "time cost" of 2 iterations recommended by OWASP.
@@ -52,57 +42,4 @@ module Argon2id
52
42
  # The default desired length of the hash in bytes used by Argon2id::Password.create
53
43
  attr_accessor :output_len
54
44
  end
55
-
56
- if RUBY_PLATFORM == "java"
57
- Error = Class.new(StandardError)
58
-
59
- def self.hash_encoded(t_cost, m_cost, parallelism, pwd, salt, hashlen)
60
- output = hash_raw(t_cost, m_cost, parallelism, pwd, salt, hashlen)
61
-
62
- encoder = Java::JavaUtil::Base64.get_encoder.without_padding
63
- encoded_salt = encoder.encode_to_string(salt.to_java_bytes)
64
- encoded_output = encoder.encode_to_string(output)
65
-
66
- "$argon2id$v=19$m=#{Integer(m_cost)},t=#{Integer(t_cost)}," \
67
- "p=#{Integer(parallelism)}$#{encoded_salt}$#{encoded_output}"
68
- end
69
-
70
- def self.verify(encoded, pwd)
71
- password = Password.new(encoded)
72
- other_raw = hash_raw(
73
- password.t_cost,
74
- password.m_cost,
75
- password.parallelism,
76
- String(pwd),
77
- password.salt,
78
- password.output.bytesize
79
- )
80
-
81
- Java::OrgBouncycastleUtil::Arrays.constant_time_are_equal(
82
- password.output.to_java_bytes,
83
- other_raw
84
- )
85
- end
86
-
87
- def self.hash_raw(t_cost, m_cost, parallelism, pwd, salt, hashlen)
88
- raise Error, "Salt is too short" if String(salt).empty?
89
-
90
- hash = Java::byte[Integer(hashlen)].new
91
- params = Java::OrgBouncycastleCryptoParams::Argon2Parameters::Builder
92
- .new(Java::OrgBouncycastleCryptoParams::Argon2Parameters::ARGON2_id)
93
- .with_salt(String(salt).to_java_bytes)
94
- .with_parallelism(Integer(parallelism))
95
- .with_memory_as_kb(Integer(m_cost))
96
- .with_iterations(Integer(t_cost))
97
- .build
98
- generator = Java::OrgBouncycastleCryptoGenerators::Argon2BytesGenerator.new
99
-
100
- generator.init(params)
101
- generator.generate_bytes(String(pwd).to_java_bytes, hash)
102
-
103
- hash
104
- rescue Java::JavaLang::IllegalStateException => e
105
- raise Error, e.message
106
- end
107
- end
108
45
  end
@@ -0,0 +1,548 @@
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_default_t_cost
390
+ password = Argon2id::Password.create("password")
391
+
392
+ assert_equal 2, password.t_cost
393
+ end
394
+
395
+ def test_create_password_uses_default_m_cost
396
+ password = Argon2id::Password.create("password")
397
+
398
+ assert_equal 19_456, password.m_cost
399
+ end
400
+
401
+ def test_create_password_uses_default_parallelism
402
+ password = Argon2id::Password.create("password")
403
+
404
+ assert_equal 1, password.parallelism
405
+ end
406
+
407
+ def test_create_password_uses_default_salt_len
408
+ password = Argon2id::Password.create("password")
409
+
410
+ assert_equal 16, password.salt.bytesize
411
+ end
412
+
413
+ def test_create_password_uses_default_output_len
414
+ password = Argon2id::Password.create("password")
415
+
416
+ assert_equal 32, password.output.bytesize
417
+ end
418
+
419
+ def test_create_password_with_t_cost_changes_t_cost
420
+ password = Argon2id::Password.create("password", t_cost: 1)
421
+
422
+ assert_equal(1, password.t_cost)
423
+ end
424
+
425
+ def test_create_password_with_too_small_t_cost_raises_error
426
+ assert_raises(Argon2id::Error) do
427
+ Argon2id::Password.create("password", t_cost: 0)
428
+ end
429
+ end
430
+
431
+ def test_create_password_with_m_cost_changes_m_cost
432
+ password = Argon2id::Password.create("password", m_cost: 8)
433
+
434
+ assert_equal(8, password.m_cost)
435
+ end
436
+
437
+ def test_create_password_with_too_small_m_cost_raises_error
438
+ assert_raises(Argon2id::Error) do
439
+ Argon2id::Password.create("password", m_cost: 0)
440
+ end
441
+ end
442
+
443
+ def test_create_password_with_parallelism_changes_parallelism
444
+ password = Argon2id::Password.create("password", parallelism: 2)
445
+
446
+ assert_equal(2, password.parallelism)
447
+ end
448
+
449
+ def test_create_password_with_too_small_parallelism_raises_error
450
+ assert_raises(Argon2id::Error) do
451
+ Argon2id::Password.create("password", parallelism: 0)
452
+ end
453
+ end
454
+
455
+ def test_create_password_with_too_small_salt_raises_error
456
+ assert_raises(Argon2id::Error) do
457
+ Argon2id::Password.create("password", salt_len: 0)
458
+ end
459
+ end
460
+
461
+ def test_create_password_with_output_len_changes_output_len
462
+ password = Argon2id::Password.create("password", output_len: 8)
463
+
464
+ assert_equal 8, password.output.bytesize
465
+ end
466
+
467
+ def test_create_password_with_too_output_len_raises_error
468
+ assert_raises(Argon2id::Error) do
469
+ Argon2id::Password.create("password", output_len: 0)
470
+ end
471
+ end
472
+
473
+ def test_create_password_inherits_t_cost_from_argon2id
474
+ Argon2id.t_cost = 1
475
+
476
+ password = Argon2id::Password.create("password")
477
+
478
+ assert_equal(1, password.t_cost)
479
+ ensure
480
+ Argon2id.t_cost = Argon2id::DEFAULT_T_COST
481
+ end
482
+
483
+ def test_create_password_inherits_m_cost_from_argon2id
484
+ Argon2id.m_cost = 8
485
+
486
+ password = Argon2id::Password.create("password")
487
+
488
+ assert_equal(8, password.m_cost)
489
+ ensure
490
+ Argon2id.m_cost = Argon2id::DEFAULT_M_COST
491
+ end
492
+
493
+ def test_create_password_inherits_parallelism_from_argon2id
494
+ Argon2id.parallelism = 2
495
+
496
+ password = Argon2id::Password.create("password")
497
+
498
+ assert_equal(2, password.parallelism)
499
+ ensure
500
+ Argon2id.parallelism = Argon2id::DEFAULT_PARALLELISM
501
+ end
502
+
503
+ def test_create_password_inherits_salt_len_from_argon2id
504
+ Argon2id.salt_len = 8
505
+
506
+ password = Argon2id::Password.create("password")
507
+
508
+ assert_equal(8, password.salt.bytesize)
509
+ ensure
510
+ Argon2id.salt_len = Argon2id::DEFAULT_SALT_LEN
511
+ end
512
+
513
+ def test_create_password_inherits_output_len_from_argon2id
514
+ Argon2id.output_len = 8
515
+
516
+ password = Argon2id::Password.create("password")
517
+
518
+ assert_equal(8, password.output.bytesize)
519
+ ensure
520
+ Argon2id.output_len = Argon2id::DEFAULT_OUTPUT_LEN
521
+ end
522
+
523
+ def test_create_password_equals_correct_password
524
+ password = Argon2id::Password.create("password")
525
+
526
+ assert password == "password"
527
+ end
528
+
529
+ def test_create_password_does_not_equal_incorrect_password
530
+ password = Argon2id::Password.create("password")
531
+
532
+ refute password == "differentpassword"
533
+ end
534
+
535
+ def test_hashing_password_verifies_correct_password
536
+ hash = Argon2id::Password.create("password").to_s
537
+ password = Argon2id::Password.new(hash)
538
+
539
+ assert password == "password"
540
+ end
541
+
542
+ def test_hashing_password_does_not_verify_incorrect_password
543
+ hash = Argon2id::Password.create("password").to_s
544
+ password = Argon2id::Password.new(hash)
545
+
546
+ refute password == "differentpassword"
547
+ end
548
+ 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,22 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: argon2id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: java
6
6
  authors:
7
7
  - Paul Mucur
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-02 00:00:00.000000000 Z
11
+ date: 2024-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
+ name: rake-compiler
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
17
  - - "~>"
17
18
  - !ruby/object:Gem::Version
18
19
  version: '1.2'
19
- name: rake-compiler
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -25,12 +25,12 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
+ name: rake-compiler-dock
28
29
  requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - "~>"
31
32
  - !ruby/object:Gem::Version
32
33
  version: '1.5'
33
- name: rake-compiler-dock
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -39,12 +39,12 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.5'
41
41
  - !ruby/object:Gem::Dependency
42
+ name: minitest
42
43
  requirement: !ruby/object:Gem::Requirement
43
44
  requirements:
44
45
  - - "~>"
45
46
  - !ruby/object:Gem::Version
46
47
  version: '5.25'
47
- name: minitest
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '5.25'
55
55
  description: Ruby bindings to Argon2, the password-hashing function that won the 2015
56
56
  Password Hashing Competition.
57
- email:
57
+ email:
58
58
  executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
@@ -66,11 +66,11 @@ files:
66
66
  - Rakefile
67
67
  - argon2id.gemspec
68
68
  - lib/argon2id.rb
69
+ - lib/argon2id/extension.rb
69
70
  - lib/argon2id/password.rb
70
71
  - lib/argon2id/version.rb
71
- - test/test_hash_encoded.rb
72
- - test/test_password.rb
73
- - test/test_verify.rb
72
+ - test/argon2id/test_password.rb
73
+ - test/test_argon2id.rb
74
74
  homepage: https://github.com/mudge/argon2id
75
75
  licenses:
76
76
  - BSD-3-Clause
@@ -81,7 +81,7 @@ metadata:
81
81
  homepage_uri: https://github.com/mudge/argon2id
82
82
  source_code_uri: https://github.com/mudge/argon2id
83
83
  rubygems_mfa_required: 'true'
84
- post_install_message:
84
+ post_install_message:
85
85
  rdoc_options:
86
86
  - "--main"
87
87
  - README.md
@@ -98,8 +98,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
100
  requirements: []
101
- rubygems_version: 3.3.25
102
- signing_key:
101
+ rubygems_version: 3.5.22
102
+ signing_key:
103
103
  specification_version: 4
104
104
  summary: Ruby bindings to Argon2
105
105
  test_files: []
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "minitest/autorun"
4
- require "argon2id"
5
-
6
- class TestHashEncoded < Minitest::Test
7
- def test_valid_password_and_salt_encodes_successfully
8
- encoded = Argon2id.hash_encoded(2, 256, 1, "password", "somesalt", 32)
9
-
10
- assert_equal "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4", encoded
11
- end
12
-
13
- def test_password_with_parallelism_of_two
14
- encoded = Argon2id.hash_encoded(2, 256, 2, "password", "somesalt", 32)
15
-
16
- assert_equal "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc", encoded
17
- end
18
-
19
- def test_valid_password_does_not_include_trailing_null_byte
20
- encoded = Argon2id.hash_encoded(2, 256, 1, "password", "somesalt", 32)
21
-
22
- refute encoded.end_with?("\x00")
23
- end
24
-
25
- def test_raises_with_too_short_output
26
- assert_raises(Argon2id::Error) do
27
- Argon2id.hash_encoded(2, 256, 1, "password", "somesalt", 1)
28
- end
29
- end
30
-
31
- def test_raises_with_too_few_lanes
32
- assert_raises(Argon2id::Error) do
33
- Argon2id.hash_encoded(2, 256, 0, "password", "somesalt", 32)
34
- end
35
- end
36
-
37
- def test_raises_with_too_small_memory_cost
38
- assert_raises(Argon2id::Error) do
39
- Argon2id.hash_encoded(2, 0, 1, "password", "somesalt", 32)
40
- end
41
- end
42
-
43
- def test_raises_with_too_small_time_cost
44
- assert_raises(Argon2id::Error) do
45
- Argon2id.hash_encoded(0, 256, 1, "password", "somesalt", 32)
46
- end
47
- end
48
-
49
- def test_raises_with_too_short_salt
50
- assert_raises(Argon2id::Error) do
51
- Argon2id.hash_encoded(2, 256, 1, "password", "", 32)
52
- end
53
- end
54
- end
@@ -1,172 +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_non_argon2id_hashes
102
- assert_raises(ArgumentError) do
103
- Argon2id::Password.new("$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8")
104
- end
105
- end
106
-
107
- def test_salt_supports_versionless_hashes
108
- password = Argon2id::Password.new("$argon2id$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
109
-
110
- assert_equal "somesalt", password.salt
111
- end
112
-
113
- def test_coerces_given_hash_to_string
114
- password = Argon2id::Password.create("password")
115
-
116
- assert Argon2id::Password.new(password) == "password"
117
- end
118
-
119
- def test_extracting_version_from_hash
120
- password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
121
-
122
- assert_equal 19, password.version
123
- end
124
-
125
- def test_extracting_version_from_versionless_hash
126
- password = Argon2id::Password.new("$argon2id$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
127
-
128
- assert_equal 16, password.version
129
- end
130
-
131
- def test_extracting_time_cost_from_hash
132
- password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
133
-
134
- assert_equal 2, password.t_cost
135
- end
136
-
137
- def test_extracting_time_cost_from_versionless_hash
138
- password = Argon2id::Password.new("$argon2id$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_memory_cost_from_hash
144
- password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
145
-
146
- assert_equal 256, password.m_cost
147
- end
148
-
149
- def test_extracting_memory_cost_from_versionless_hash
150
- password = Argon2id::Password.new("$argon2id$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_parallelism_from_hash
156
- password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
157
-
158
- assert_equal 1, password.parallelism
159
- end
160
-
161
- def test_extracting_parallelism_from_versionless_hash
162
- password = Argon2id::Password.new("$argon2id$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
163
-
164
- assert_equal 1, password.parallelism
165
- end
166
-
167
- def test_extracting_output_from_hash
168
- password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
169
-
170
- 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
171
- end
172
- end
data/test/test_verify.rb DELETED
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "minitest/autorun"
4
- require "argon2id"
5
-
6
- class TestVerify < Minitest::Test
7
- def test_returns_true_with_correct_password
8
- assert Argon2id.verify(
9
- "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4",
10
- "password"
11
- )
12
- end
13
-
14
- def test_returns_false_with_incorrect_password
15
- refute Argon2id.verify(
16
- "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4",
17
- "not password"
18
- )
19
- end
20
-
21
- def test_raises_if_given_invalid_encoded
22
- assert_raises(ArgumentError) do
23
- Argon2id.verify("", "opensesame")
24
- end
25
- end
26
-
27
- def test_raises_if_given_encoded_with_null_byte
28
- assert_raises(ArgumentError) do
29
- Argon2id.verify(
30
- "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4\x00foo",
31
- "password"
32
- )
33
- end
34
- end
35
- end