argon2id 0.5.0-arm-linux → 0.7.0-arm-linux

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: 156930188b0394a7ddd38100a7a1b9b1e0b43f588ebf91a29c5ef91c6696e617
4
- data.tar.gz: 2cf9f11297c29cde9ee56c5f77d3d62e3fad525034b0fc884b2c73b35ea10246
3
+ metadata.gz: 7d14957b6bba67784852e425efe227220815876f00a554ba789d81c5ccb604e1
4
+ data.tar.gz: e061b46af6eceb50737bf1e61bea9167e1fa4c6115b13e2f85c316abfd39c428
5
5
  SHA512:
6
- metadata.gz: 05ae8042eb93f40aad7d93ad7854a24dfe803f10195bc411a8909897da0fb3a148a7ef75a9d4ca85017b2e7fa5fb1f2331cf05d29e56bbfc7df8cacf97bd4a8c
7
- data.tar.gz: fa7e0d38bcc32b7243ce4826dbe7b3028b0b08f5600ae779bd1bccd7f300d6c1f478d83d75ab1d31fb1fe0d6c100b78a0f978ec3d0749072f041e90a449d6887
6
+ metadata.gz: 0b6fa14875c0866163b3b84e0656deddc368c806168565df914fb75c4724548d2635ae07db3ac8100e4ea6d1252e00f86ad1059ec06eff008d27d61466264ef5
7
+ data.tar.gz: 34f62833eb93eb4db3b987916c79207993a008d9a199da89ff698b102adb27db6db61e9789160b26c07f41664001db0b73fbca70f22bdfed912d784297a51a8d
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
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