argon2id 0.8.0.rc1-x86_64-linux-musl
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +142 -0
- data/Gemfile +9 -0
- data/LICENSE +11 -0
- data/README.md +371 -0
- data/Rakefile +70 -0
- data/argon2id.gemspec +59 -0
- data/ext/argon2id/argon2id.c +76 -0
- data/ext/argon2id/extconf.rb +17 -0
- data/ext/argon2id/libargon2/LICENSE +314 -0
- data/ext/argon2id/libargon2/argon2.c +452 -0
- data/ext/argon2id/libargon2/argon2.h +437 -0
- data/ext/argon2id/libargon2/blake2/blake2-impl.h +156 -0
- data/ext/argon2id/libargon2/blake2/blake2.h +89 -0
- data/ext/argon2id/libargon2/blake2/blake2b.c +390 -0
- data/ext/argon2id/libargon2/blake2/blamka-round-opt.h +471 -0
- data/ext/argon2id/libargon2/blake2/blamka-round-ref.h +56 -0
- data/ext/argon2id/libargon2/core.c +648 -0
- data/ext/argon2id/libargon2/core.h +228 -0
- data/ext/argon2id/libargon2/encoding.c +463 -0
- data/ext/argon2id/libargon2/encoding.h +57 -0
- data/ext/argon2id/libargon2/ref.c +194 -0
- data/ext/argon2id/libargon2/thread.c +57 -0
- data/ext/argon2id/libargon2/thread.h +67 -0
- data/lib/argon2id/3.1/argon2id.so +0 -0
- data/lib/argon2id/3.2/argon2id.so +0 -0
- data/lib/argon2id/3.3/argon2id.so +0 -0
- data/lib/argon2id/3.4/argon2id.so +0 -0
- data/lib/argon2id/extension.rb +71 -0
- data/lib/argon2id/password.rb +142 -0
- data/lib/argon2id/version.rb +5 -0
- data/lib/argon2id.rb +45 -0
- data/test/argon2id/test_password.rb +554 -0
- data/test/test_argon2id.rb +66 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f40685fcf47831c564dc9e211b7012e4d18e79eacae0605b04abae24c92684b0
|
4
|
+
data.tar.gz: 19e682ff6e29b4385200b3b77068f088ef7cf8e9ced387f31d0dd7562a951488
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1c4d00bbddbd88c45cba8dae5f6a89179f090956794b3300ba3b8247a78531f4361c6ca7c5bd0745bd62ab106501e177ebf2a3c410db8bca2b975c01ffc88f56
|
7
|
+
data.tar.gz: cbd1c49cfc4ce934ed41015ef61c8c8b0bf8623dd46e5ddb1d143e852e8b1ca73b18b9596ef31e4c501f2068def890fb6f8c2b7c00f99086af0de6882aac4a2f
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
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
|
+
|
37
|
+
## [0.6.0] - 2024-11-05
|
38
|
+
|
39
|
+
### Changed
|
40
|
+
|
41
|
+
- Move the internal API to `Argon2id::Password` and make it explicitly private
|
42
|
+
|
43
|
+
## [0.5.0] - 2024-11-02
|
44
|
+
|
45
|
+
### Removed
|
46
|
+
|
47
|
+
- No longer expose the `type` of an encoded hash as it must always be an
|
48
|
+
Argon2id hash
|
49
|
+
|
50
|
+
## [0.4.1] - 2024-11-02
|
51
|
+
|
52
|
+
### Changed
|
53
|
+
|
54
|
+
- Refactor verification on JRuby to avoid parsing encoded hashes unnecessarily
|
55
|
+
- No longer describe the gem in terms of bindings to the reference C
|
56
|
+
implementation given the Bouncy Castle-based JRuby implementation
|
57
|
+
- Only wrap `IllegalStateException` with `Argon2id::Error` on JRuby
|
58
|
+
|
59
|
+
## [0.4.0] - 2024-11-02
|
60
|
+
|
61
|
+
### Added
|
62
|
+
|
63
|
+
- Added support for JRuby 9.4 by adding an implementation of Argon2id hashing
|
64
|
+
and verification using JRuby-OpenSSL's Bouncy Castle internals
|
65
|
+
- Added `output` to `Argon2id::Password` instances so the actual "output" part
|
66
|
+
of a password hash can be retrieved (and compared)
|
67
|
+
|
68
|
+
### Changed
|
69
|
+
|
70
|
+
- Verifying a password will now consistently raise an `ArgumentError` when
|
71
|
+
given an invalid encoded hash rather than an `Argon2id::Error`
|
72
|
+
|
73
|
+
## [0.3.0] - 2024-11-01
|
74
|
+
|
75
|
+
### Added
|
76
|
+
|
77
|
+
- Expose all parameters of a hash through new readers on `Argon2id::Password`:
|
78
|
+
namely, `type`, `version`, `m_cost`, `t_cost`, and `parallelism`
|
79
|
+
|
80
|
+
### Changed
|
81
|
+
|
82
|
+
- Remove the dependency on the `base64` gem by inlining the definition of
|
83
|
+
`Base64.decode64` (thanks to @etiennebarrie for the tip)
|
84
|
+
|
85
|
+
## [0.2.1] - 2024-11-01
|
86
|
+
|
87
|
+
### Added
|
88
|
+
|
89
|
+
- Anything that can be coerced to a String can now be passed to
|
90
|
+
`Argon2id::Password.new`
|
91
|
+
|
92
|
+
## [0.2.0] - 2024-11-01
|
93
|
+
|
94
|
+
### Added
|
95
|
+
|
96
|
+
- The original salt for an `Argon2id::Password` can now be retrieved with
|
97
|
+
`Argon2id::Password#salt`
|
98
|
+
|
99
|
+
### Changed
|
100
|
+
|
101
|
+
- Encoded hashes are now validated when initialising an `Argon2id::Password`,
|
102
|
+
raising an `ArgumentError` if they are invalid
|
103
|
+
|
104
|
+
## [0.1.2] - 2024-11-01
|
105
|
+
|
106
|
+
### Fixed
|
107
|
+
|
108
|
+
- Validate that the encoded hash passed to `Argon2id::Password.new` is a
|
109
|
+
null-terminated C string, raising an `ArgumentError` if it contains extra null
|
110
|
+
bytes
|
111
|
+
|
112
|
+
## [0.1.1] - 2024-11-01
|
113
|
+
|
114
|
+
### Added
|
115
|
+
|
116
|
+
- RDoc documentation for the API
|
117
|
+
|
118
|
+
### Fixed
|
119
|
+
|
120
|
+
- Saved a superfluous extra byte when allocating the buffer for the encoded
|
121
|
+
hash
|
122
|
+
|
123
|
+
## [0.1.0] - 2024-10-31
|
124
|
+
|
125
|
+
### Added
|
126
|
+
|
127
|
+
- The initial version of the Argon2id gem, providing Ruby bindings to the
|
128
|
+
reference C implementation of Argon2, the password-hashing function that won
|
129
|
+
the Password Hashing Competition.
|
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
|
133
|
+
[0.6.0]: https://github.com/mudge/argon2id/releases/tag/v0.6.0
|
134
|
+
[0.5.0]: https://github.com/mudge/argon2id/releases/tag/v0.5.0
|
135
|
+
[0.4.1]: https://github.com/mudge/argon2id/releases/tag/v0.4.1
|
136
|
+
[0.4.0]: https://github.com/mudge/argon2id/releases/tag/v0.4.0
|
137
|
+
[0.3.0]: https://github.com/mudge/argon2id/releases/tag/v0.3.0
|
138
|
+
[0.2.1]: https://github.com/mudge/argon2id/releases/tag/v0.2.1
|
139
|
+
[0.2.0]: https://github.com/mudge/argon2id/releases/tag/v0.2.0
|
140
|
+
[0.1.2]: https://github.com/mudge/argon2id/releases/tag/v0.1.2
|
141
|
+
[0.1.1]: https://github.com/mudge/argon2id/releases/tag/v0.1.1
|
142
|
+
[0.1.0]: https://github.com/mudge/argon2id/releases/tag/v0.1.0
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Copyright (c) 2024 Paul Mucur.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
4
|
+
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
6
|
+
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
10
|
+
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,371 @@
|
|
1
|
+
# Argon2id - Ruby bindings to the OWASP recommended password-hashing function
|
2
|
+
|
3
|
+
Ruby bindings to [Argon2][], the password-hashing function that won the 2015
|
4
|
+
[Password Hashing Competition][].
|
5
|
+
|
6
|
+
[![Build Status](https://github.com/mudge/argon2id/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mudge/argon2id/actions)
|
7
|
+
|
8
|
+
**Current version:** 0.8.0.rc1
|
9
|
+
**Bundled Argon2 version:** libargon2.1 (20190702)
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
Argon2id::Password.create("password").to_s
|
13
|
+
#=> "$argon2id$v=19$m=19456,t=2,p=1$agNV6OfDL1OwE44WdrFCJw$ITrBwvCsW4b5GjgZuL67RCcvVMEWBWXtASc9TVyI3rY"
|
14
|
+
|
15
|
+
password = Argon2id::Password.new("$argon2id$v=19$m=19456,t=2,p=1$ZS2nBFWBpnt28HjtzNOW4w$SQ+p+dIcWbpzWpZQ/ZZFj8IQkyhYZf127U4QdkRmKFU")
|
16
|
+
password == "password" #=> true
|
17
|
+
password == "not password" #=> false
|
18
|
+
|
19
|
+
password.m_cost #=> 19456
|
20
|
+
password.salt #=> "e-\xA7\x04U\x81\xA6{v\xF0x\xED\xCC\xD3\x96\xE3"
|
21
|
+
```
|
22
|
+
|
23
|
+
## Table of contents
|
24
|
+
|
25
|
+
* [Why Argon2id?](#why-argon2id)
|
26
|
+
* [Usage](#usage)
|
27
|
+
* [Hashing passwords](#hashing-passwords)
|
28
|
+
* [Verifying passwords](#verifying-passwords)
|
29
|
+
* [Validating encoded hashes](#validating-encoded-hashes)
|
30
|
+
* [Errors](#errors)
|
31
|
+
* [Usage with Active Record](#usage-with-active-record)
|
32
|
+
* [Requirements](#requirements)
|
33
|
+
* [Native gems](#native-gems)
|
34
|
+
* [Verifying the gems](#verifying-the-gems)
|
35
|
+
* [Installing the `ruby` platform gem](#installing-the-ruby-platform-gem)
|
36
|
+
* [Thanks](#thanks)
|
37
|
+
* [Contact](#contact)
|
38
|
+
* [License](#license)
|
39
|
+
* [Dependencies](#dependencies)
|
40
|
+
|
41
|
+
## Why Argon2id?
|
42
|
+
|
43
|
+
> Argon2 is a password-hashing function that summarizes the state of the art in
|
44
|
+
> the design of memory-hard functions and can be used to hash passwords for
|
45
|
+
> credential storage, key derivation, or other applications.
|
46
|
+
>
|
47
|
+
> It has a simple design aimed at the highest memory filling rate and effective
|
48
|
+
> use of multiple computing units, while still providing defense against
|
49
|
+
> tradeoff attacks (by exploiting the cache and memory organization of the
|
50
|
+
> recent processors).
|
51
|
+
|
52
|
+
— [Argon2][]
|
53
|
+
|
54
|
+
> Argon2 was the winner of the 2015 Password Hashing Competition. Out of the
|
55
|
+
> three Argon2 versions, use the Argon2id variant since it provides a balanced
|
56
|
+
> approach to resisting both side-channel and GPU-based attacks.
|
57
|
+
|
58
|
+
— [OWASP Password Storage Cheat Sheet][]
|
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
|
+
|
62
|
+
## Usage
|
63
|
+
|
64
|
+
Install argon2id as a dependency:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
# In your Gemfile
|
68
|
+
gem "argon2id"
|
69
|
+
|
70
|
+
# Or without Bundler
|
71
|
+
gem install argon2id
|
72
|
+
```
|
73
|
+
|
74
|
+
Include in your code:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
require "argon2id"
|
78
|
+
```
|
79
|
+
|
80
|
+
### Hashing passwords
|
81
|
+
|
82
|
+
Hash a plain text password (e.g. from user input) with
|
83
|
+
`Argon2id::Password.create`:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
password = Argon2id::Password.create("opensesame")
|
87
|
+
```
|
88
|
+
|
89
|
+
The encoded value of the resulting hash is available via
|
90
|
+
`Argon2id::Password#to_s` (ideal for persisting somewhere):
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
password.to_s
|
94
|
+
#=> "$argon2id$v=19$m=19456,t=2,p=1$ZS2nBFWBpnt28HjtzNOW4w$SQ+p+dIcWbpzWpZQ/ZZFj8IQkyhYZf127U4QdkRmKFU"
|
95
|
+
```
|
96
|
+
|
97
|
+
By default, `Argon2id::Password.create` will use the second set of parameters
|
98
|
+
recommended by [OWASP][OWASP Password Storage Cheat Sheet] but these can be
|
99
|
+
overridden by passing keyword arguments to `Argon2id::Password.create`:
|
100
|
+
|
101
|
+
* `t_cost`: the "time cost" given as a number of iterations (defaults to 2)
|
102
|
+
* `m_cost`: the "memory cost" given in kibibytes (defaults to 19 mebibytes)
|
103
|
+
* `parallelism`: the number of threads and compute lanes to use (defaults to 1)
|
104
|
+
* `salt_len`: the salt size in bytes (defaults to 16)
|
105
|
+
* `output_len`: the desired length of the hash in bytes (defaults to 32)
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
password = Argon2id::Password.create("opensesame", t_cost: 3, m_cost: 12288)
|
109
|
+
password.to_s
|
110
|
+
#=> "$argon2id$v=19$m=12288,t=3,p=1$uukIsLS6y6etvsgoN20kVg$exMvDX/P9exvEPmnZL2gZClRyMdrnqjqyysLMP/VUWA"
|
111
|
+
```
|
112
|
+
|
113
|
+
If you want to override the parameters for all calls to
|
114
|
+
`Argon2id::Password.create`, you can set them on `Argon2id` directly:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
Argon2id.t_cost = 3
|
118
|
+
Argon2id.m_cost = 12288
|
119
|
+
Argon2id.parallelism = 1
|
120
|
+
Argon2id.salt_len = 16
|
121
|
+
Argon2id.output_len = 32
|
122
|
+
```
|
123
|
+
|
124
|
+
### Verifying passwords
|
125
|
+
|
126
|
+
To verify a password against a hash, use `Argon2id::Password#==`:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
password = Argon2id::Password.create("opensesame")
|
130
|
+
password == "opensesame" #=> true
|
131
|
+
password == "notopensesame" #=> false
|
132
|
+
```
|
133
|
+
|
134
|
+
Or, if you only have the encoded hash (e.g. retrieved from storage):
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
password = Argon2id::Password.new("$argon2id$v=19$m=19456,t=2,p=1$ZS2nBFWBpnt28HjtzNOW4w$SQ+p+dIcWbpzWpZQ/ZZFj8IQkyhYZf127U4QdkRmKFU")
|
138
|
+
password == "opensesame" #=> true
|
139
|
+
password == "notopensesame" #=> false
|
140
|
+
```
|
141
|
+
|
142
|
+
> [!WARNING]
|
143
|
+
> `Argon2id::Password.new` does not support hashes generated from other Argon2
|
144
|
+
> variants such as Argon2i and Argon2d.
|
145
|
+
|
146
|
+
For compatibility with [bcrypt-ruby][], `Argon2id::Password#==` is aliased to `Argon2id::Password.is_password?`:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
password = Argon2id::Password.new("$argon2id$v=19$m=19456,t=2,p=1$ZS2nBFWBpnt28HjtzNOW4w$SQ+p+dIcWbpzWpZQ/ZZFj8IQkyhYZf127U4QdkRmKFU")
|
150
|
+
password.is_password?("opensesame") #=> true
|
151
|
+
password.is_password?("notopensesame") #=> false
|
152
|
+
```
|
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
|
+
|
166
|
+
The various parts of the encoded hash can be retrieved:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
170
|
+
password.version #=> 19
|
171
|
+
password.m_cost #=> 256
|
172
|
+
password.t_cost #=> 2
|
173
|
+
password.parallelism #=> 1
|
174
|
+
password.salt #=> "somesalt"
|
175
|
+
password.output
|
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"
|
177
|
+
```
|
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
|
+
|
191
|
+
### Errors
|
192
|
+
|
193
|
+
Any errors returned from Argon2 will be raised as `Argon2id::Error`, e.g.
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
Argon2id::Password.create("password", salt_len: 0)
|
197
|
+
# Salt is too short (Argon2id::Error)
|
198
|
+
```
|
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
|
+
|
256
|
+
## Requirements
|
257
|
+
|
258
|
+
This gem requires any of the following to run:
|
259
|
+
|
260
|
+
* [Ruby](https://www.ruby-lang.org/en/) 3.1 to 3.4.0-rc1
|
261
|
+
* [JRuby](https://www.jruby.org) 9.4
|
262
|
+
* [TruffleRuby](https://www.graalvm.org/ruby/) 24.1
|
263
|
+
|
264
|
+
> [!NOTE]
|
265
|
+
> The JRuby version of the gem uses
|
266
|
+
> [JRuby-OpenSSL](https://github.com/jruby/jruby-openssl)'s implementation of
|
267
|
+
> Argon2 while the others use the reference C implementation.
|
268
|
+
|
269
|
+
### Native gems
|
270
|
+
|
271
|
+
Where possible, a pre-compiled native gem will be provided for the following platforms:
|
272
|
+
|
273
|
+
* Linux
|
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+
|
276
|
+
* macOS `x86_64-darwin` and `arm64-darwin`
|
277
|
+
* Windows `x64-mingw-ucrt`
|
278
|
+
* Java: any platform running JRuby 9.4 or higher
|
279
|
+
|
280
|
+
### Verifying the gems
|
281
|
+
|
282
|
+
SHA256 checksums are included in the [release
|
283
|
+
notes](https://github.com/mudge/argon2id/releases) for each version and can be
|
284
|
+
checked with `sha256sum`, e.g.
|
285
|
+
|
286
|
+
```console
|
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
|
292
|
+
```
|
293
|
+
|
294
|
+
[GPG](https://www.gnupg.org/) signatures are attached to each release (the
|
295
|
+
assets ending in `.sig`) and can be verified if you import [our signing key
|
296
|
+
`0x39AC3530070E0F75`](https://mudge.name/39AC3530070E0F75.asc) (or fetch it
|
297
|
+
from a public keyserver, e.g. `gpg --keyserver keyserver.ubuntu.com --recv-key
|
298
|
+
0x39AC3530070E0F75`):
|
299
|
+
|
300
|
+
```console
|
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
|
303
|
+
gpg: using RSA key 702609D9C790F45B577D7BEC39AC3530070E0F75
|
304
|
+
gpg: Good signature from "Paul Mucur <mudge@mudge.name>" [unknown]
|
305
|
+
gpg: aka "Paul Mucur <paul@ghostcassette.com>" [unknown]
|
306
|
+
gpg: WARNING: This key is not certified with a trusted signature!
|
307
|
+
gpg: There is no indication that the signature belongs to the owner.
|
308
|
+
Primary key fingerprint: 7026 09D9 C790 F45B 577D 7BEC 39AC 3530 070E 0F75
|
309
|
+
```
|
310
|
+
|
311
|
+
The fingerprint should be as shown above or you can independently verify it
|
312
|
+
with the ones shown in the footer of https://mudge.name.
|
313
|
+
|
314
|
+
### Installing the `ruby` platform gem
|
315
|
+
|
316
|
+
> [!WARNING]
|
317
|
+
> We strongly recommend using the native gems where possible to avoid the need
|
318
|
+
> for compiling the C extension and its dependencies which will take longer and
|
319
|
+
> be less reliable.
|
320
|
+
|
321
|
+
If you wish to compile the gem, you will need to explicitly install the `ruby` platform gem:
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
# In your Gemfile with Bundler 2.3.18+
|
325
|
+
gem "argon2id", force_ruby_platform: true
|
326
|
+
|
327
|
+
# With Bundler 2.1+
|
328
|
+
bundle config set force_ruby_platform true
|
329
|
+
|
330
|
+
# With older versions of Bundler
|
331
|
+
bundle config force_ruby_platform true
|
332
|
+
|
333
|
+
# Without Bundler
|
334
|
+
gem install argon2id --platform=ruby
|
335
|
+
```
|
336
|
+
|
337
|
+
You will need a full compiler toolchain for compiling Ruby C extensions (see
|
338
|
+
[Nokogiri's "The Compiler
|
339
|
+
Toolchain"](https://nokogiri.org/tutorials/installing_nokogiri.html#appendix-a-the-compiler-toolchain))
|
340
|
+
plus the toolchain required for compiling the vendored version of Argon2.
|
341
|
+
|
342
|
+
## Thanks
|
343
|
+
|
344
|
+
* Thanks to [Mike Dalessio](https://github.com/flavorjones) for his advice and
|
345
|
+
[Ruby C Extensions Explained](https://github.com/flavorjones/ruby-c-extensions-explained)
|
346
|
+
project
|
347
|
+
|
348
|
+
## Contact
|
349
|
+
|
350
|
+
All issues and suggestions should go to [GitHub
|
351
|
+
Issues](https://github.com/mudge/argon2id/issues).
|
352
|
+
|
353
|
+
## License
|
354
|
+
|
355
|
+
This library is licensed under the BSD 3-Clause License, see `LICENSE`.
|
356
|
+
|
357
|
+
Copyright © 2024, Paul Mucur.
|
358
|
+
|
359
|
+
### Dependencies
|
360
|
+
|
361
|
+
The source code of [Argon2][] is distributed in the gem. This code is copyright
|
362
|
+
© 2015 Daniel Dinu, Dmitry Khovratovich (main authors), Jean-Philippe Aumasson
|
363
|
+
and Samuel Neves, and dual licensed under the [CC0 License][] and the [Apache
|
364
|
+
2.0 License][].
|
365
|
+
|
366
|
+
[Argon2]: https://github.com/P-H-C/phc-winner-argon2/
|
367
|
+
[OWASP Password Storage Cheat Sheet]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
|
368
|
+
[bcrypt-ruby]: https://github.com/bcrypt-ruby/bcrypt-ruby
|
369
|
+
[CC0 License]: https://creativecommons.org/about/cc0
|
370
|
+
[Apache 2.0 License]: https://www.apache.org/licenses/LICENSE-2.0
|
371
|
+
[Password Hashing Competition]: https://www.password-hashing.net
|
data/Rakefile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require "rake/extensiontask"
|
2
|
+
require "rake_compiler_dock"
|
3
|
+
require "minitest/test_task"
|
4
|
+
|
5
|
+
CLEAN.add("lib/**/*.{o,so,bundle}", "pkg")
|
6
|
+
|
7
|
+
cross_platforms = %w[
|
8
|
+
aarch64-linux-gnu
|
9
|
+
aarch64-linux-musl
|
10
|
+
arm-linux-gnu
|
11
|
+
arm-linux-musl
|
12
|
+
arm64-darwin
|
13
|
+
x64-mingw-ucrt
|
14
|
+
x64-mingw32
|
15
|
+
x86-linux-gnu
|
16
|
+
x86-linux-musl
|
17
|
+
x86-mingw32
|
18
|
+
x86_64-darwin
|
19
|
+
x86_64-linux-gnu
|
20
|
+
x86_64-linux-musl
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
ENV["RUBY_CC_VERSION"] = %w[3.4.0 3.3.5 3.2.0 3.1.0].join(":")
|
24
|
+
|
25
|
+
gemspec = Gem::Specification.load("argon2id.gemspec")
|
26
|
+
|
27
|
+
Gem::PackageTask.new(gemspec).define
|
28
|
+
|
29
|
+
namespace :java do
|
30
|
+
java_gemspec = gemspec.dup
|
31
|
+
java_gemspec.files.reject! { |path| File.fnmatch?("ext/*", path) }
|
32
|
+
java_gemspec.extensions.clear
|
33
|
+
java_gemspec.platform = Gem::Platform.new("java")
|
34
|
+
java_gemspec.required_ruby_version = ">= 3.1.0"
|
35
|
+
|
36
|
+
Gem::PackageTask.new(java_gemspec).define
|
37
|
+
end
|
38
|
+
|
39
|
+
Rake::ExtensionTask.new("argon2id", gemspec) do |e|
|
40
|
+
e.lib_dir = "lib/argon2id"
|
41
|
+
e.cross_compile = true
|
42
|
+
e.cross_platform = cross_platforms
|
43
|
+
end
|
44
|
+
|
45
|
+
Minitest::TestTask.create
|
46
|
+
|
47
|
+
begin
|
48
|
+
require "ruby_memcheck"
|
49
|
+
|
50
|
+
namespace :test do
|
51
|
+
RubyMemcheck::TestTask.new(valgrind: :compile)
|
52
|
+
end
|
53
|
+
rescue LoadError
|
54
|
+
# Only define the test:valgrind task if ruby_memcheck is installed
|
55
|
+
end
|
56
|
+
|
57
|
+
namespace :gem do
|
58
|
+
cross_platforms.each do |platform|
|
59
|
+
desc "Compile and build native gem for #{platform}"
|
60
|
+
task platform do
|
61
|
+
RakeCompilerDock.sh <<~SCRIPT, platform: platform, verbose: true
|
62
|
+
gem install bundler --no-document &&
|
63
|
+
bundle &&
|
64
|
+
bundle exec rake native:#{platform} pkg/#{gemspec.full_name}-#{Gem::Platform.new(platform)}.gem PATH="/usr/local/bin:$PATH"
|
65
|
+
SCRIPT
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
task default: [:compile, :test]
|
data/argon2id.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/argon2id/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "argon2id"
|
7
|
+
s.version = Argon2id::VERSION
|
8
|
+
s.summary = "Ruby bindings to Argon2"
|
9
|
+
s.description = "Ruby bindings to Argon2, the password-hashing function that won the 2015 Password Hashing Competition."
|
10
|
+
s.license = "BSD-3-Clause"
|
11
|
+
s.authors = ["Paul Mucur"]
|
12
|
+
s.homepage = "https://github.com/mudge/argon2id"
|
13
|
+
s.metadata = {
|
14
|
+
"bug_tracker_uri" => "https://github.com/mudge/argon2id/issues",
|
15
|
+
"changelog_uri" => "https://github.com/mudge/argon2id/blob/main/CHANGELOG.md",
|
16
|
+
"funding_uri" => "https://github.com/sponsors/mudge",
|
17
|
+
"homepage_uri" => "https://github.com/mudge/argon2id",
|
18
|
+
"source_code_uri" => "https://github.com/mudge/argon2id",
|
19
|
+
"rubygems_mfa_required" => "true"
|
20
|
+
}
|
21
|
+
s.required_ruby_version = ">= 3.1.0"
|
22
|
+
s.extensions = ["ext/argon2id/extconf.rb"]
|
23
|
+
s.files = [
|
24
|
+
"CHANGELOG.md",
|
25
|
+
"Gemfile",
|
26
|
+
"LICENSE",
|
27
|
+
"README.md",
|
28
|
+
"Rakefile",
|
29
|
+
"argon2id.gemspec",
|
30
|
+
"ext/argon2id/argon2id.c",
|
31
|
+
"ext/argon2id/extconf.rb",
|
32
|
+
"ext/argon2id/libargon2/LICENSE",
|
33
|
+
"ext/argon2id/libargon2/argon2.c",
|
34
|
+
"ext/argon2id/libargon2/argon2.h",
|
35
|
+
"ext/argon2id/libargon2/blake2/blake2-impl.h",
|
36
|
+
"ext/argon2id/libargon2/blake2/blake2.h",
|
37
|
+
"ext/argon2id/libargon2/blake2/blake2b.c",
|
38
|
+
"ext/argon2id/libargon2/blake2/blamka-round-opt.h",
|
39
|
+
"ext/argon2id/libargon2/blake2/blamka-round-ref.h",
|
40
|
+
"ext/argon2id/libargon2/core.c",
|
41
|
+
"ext/argon2id/libargon2/core.h",
|
42
|
+
"ext/argon2id/libargon2/encoding.c",
|
43
|
+
"ext/argon2id/libargon2/encoding.h",
|
44
|
+
"ext/argon2id/libargon2/ref.c",
|
45
|
+
"ext/argon2id/libargon2/thread.c",
|
46
|
+
"ext/argon2id/libargon2/thread.h",
|
47
|
+
"lib/argon2id.rb",
|
48
|
+
"lib/argon2id/extension.rb",
|
49
|
+
"lib/argon2id/password.rb",
|
50
|
+
"lib/argon2id/version.rb",
|
51
|
+
"test/argon2id/test_password.rb",
|
52
|
+
"test/test_argon2id.rb"
|
53
|
+
]
|
54
|
+
s.rdoc_options = ["--main", "README.md"]
|
55
|
+
|
56
|
+
s.add_development_dependency("rake-compiler", "~> 1.2")
|
57
|
+
s.add_development_dependency("rake-compiler-dock", "~> 1.7.0.rc1")
|
58
|
+
s.add_development_dependency("minitest", "~> 5.25")
|
59
|
+
end
|