argon2id 0.5.0-x64-mingw-ucrt → 0.7.0-x64-mingw-ucrt

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: 046246d2626875dc6a452e42067e5a5bc46810d36dcd4cbb3b848ede3050475e
4
- data.tar.gz: 77859d74535214733394e760c57a24e4f1ef5bb3c56ce08ed43d7d1018c0844f
3
+ metadata.gz: fe40a011ddccdbc2ebb6856704b5442180f2414b5ee3101a7953715f891ba0f3
4
+ data.tar.gz: fe8532d266ac9245174d3de13513c70e957cb1c11f5ceab196a269e8338f4031
5
5
  SHA512:
6
- metadata.gz: 33cdbc4f20b4abbf01c153c8ad23d5b319ec57244a9170dfbc15875a25de62f93dd2c37929fea7800e54fc338f82631ac89e779fb26af3a6acfee5514393e4cd
7
- data.tar.gz: 1142a07f1833a94374c766cbf339528adffb021f36d9fce45bbc974e327c61aef867389bc81662ecbdae71fa3769b44fba3fa79d5e16864a2fb5bfd2a3fba3a6
6
+ metadata.gz: 1554613d298bedebea3eb86a059be6bd3210cbd9c13fccdfc156d7d874ffeecaaf82f5f8c2e515001684557884c8bfa77d28feb43573de51132e0555bf52b705
7
+ data.tar.gz: e0bf4231265949e306f9486e45e46975c86107655743cc1c1965b17daa6d146a36bc2701f57af6dbb07a1e6787de6920d2123cd19f3e1457c530d93765c32488
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
 
@@ -5,21 +5,11 @@
5
5
 
6
6
  #define UNUSED(x) (void)(x)
7
7
 
8
- VALUE mArgon2id, cArgon2idError;
9
-
10
- /* call-seq: hash_encoded(t_cost, m_cost, parallelism, pwd, salt, output_len)
11
- *
12
- * Hashes a password with Argon2id, producing an encoded hash.
13
- *
14
- * - +t_cost+: number of iterations
15
- * - +m_cost+: sets memory usage to +m_cost+ kibibytes
16
- * - +parallelism+: number of threads and compute lanes
17
- * - +pwd+: the password
18
- * - +salt+: the salt
19
- * - +output_len+: desired length of the hash in bytes
20
- */
8
+ VALUE mArgon2id, cArgon2idError, cArgon2idPassword;
9
+ ID id_encoded;
10
+
21
11
  static VALUE
22
- rb_argon2id_hash_encoded(VALUE module, VALUE iterations, VALUE memory, VALUE threads, VALUE pwd, VALUE salt, VALUE hashlen)
12
+ rb_argon2id_hash_encoded(VALUE klass, VALUE iterations, VALUE memory, VALUE threads, VALUE pwd, VALUE salt, VALUE hashlen)
23
13
  {
24
14
  uint32_t t_cost, m_cost, parallelism;
25
15
  size_t encodedlen, outlen;
@@ -27,7 +17,7 @@ rb_argon2id_hash_encoded(VALUE module, VALUE iterations, VALUE memory, VALUE thr
27
17
  int result;
28
18
  VALUE hash;
29
19
 
30
- UNUSED(module);
20
+ UNUSED(klass);
31
21
 
32
22
  t_cost = FIX2INT(iterations);
33
23
  m_cost = FIX2INT(memory);
@@ -53,16 +43,12 @@ rb_argon2id_hash_encoded(VALUE module, VALUE iterations, VALUE memory, VALUE thr
53
43
  return hash;
54
44
  }
55
45
 
56
- /* call-seq: verify(encoded, pwd)
57
- *
58
- * Verifies a password against an encoded string.
59
- */
60
46
  static VALUE
61
- rb_argon2id_verify(VALUE module, VALUE encoded, VALUE pwd) {
47
+ rb_argon2id_verify(VALUE self, VALUE pwd) {
62
48
  int result;
49
+ VALUE encoded;
63
50
 
64
- UNUSED(module);
65
-
51
+ encoded = rb_ivar_get(self, id_encoded);
66
52
  result = argon2id_verify(StringValueCStr(encoded), StringValuePtr(pwd), RSTRING_LEN(pwd));
67
53
  if (result == ARGON2_OK) {
68
54
  return Qtrue;
@@ -80,8 +66,11 @@ rb_argon2id_verify(VALUE module, VALUE encoded, VALUE pwd) {
80
66
  void
81
67
  Init_argon2id(void)
82
68
  {
69
+ id_encoded = rb_intern("@encoded");
70
+
83
71
  mArgon2id = rb_define_module("Argon2id");
84
72
  cArgon2idError = rb_define_class_under(mArgon2id, "Error", rb_eStandardError);
85
- rb_define_singleton_method(mArgon2id, "hash_encoded", rb_argon2id_hash_encoded, 6);
86
- rb_define_singleton_method(mArgon2id, "verify", rb_argon2id_verify, 2);
73
+ cArgon2idPassword = rb_define_class_under(mArgon2id, "Password", rb_cObject);
74
+ rb_define_private_method(rb_singleton_class(cArgon2idPassword), "hash_encoded", rb_argon2id_hash_encoded, 6);
75
+ rb_define_private_method(cArgon2idPassword, "verify", rb_argon2id_verify, 1);
87
76
  }
@@ -14,4 +14,4 @@ $CPPFLAGS << " " << "-I$(srcdir)/libargon2"
14
14
  have_header("stdint.h")
15
15
  have_header("argon2.h")
16
16
 
17
- create_makefile "argon2id"
17
+ create_makefile "argon2id/argon2id"
Binary file
Binary file
Binary file
@@ -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