argon2id 0.8.0.rc1-aarch64-linux-musl
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 +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
@@ -0,0 +1,57 @@
|
|
1
|
+
/*
|
2
|
+
* Argon2 reference source code package - reference C implementations
|
3
|
+
*
|
4
|
+
* Copyright 2015
|
5
|
+
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
6
|
+
*
|
7
|
+
* You may use this work under the terms of a Creative Commons CC0 1.0
|
8
|
+
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
9
|
+
* these licenses can be found at:
|
10
|
+
*
|
11
|
+
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
12
|
+
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
*
|
14
|
+
* You should have received a copy of both of these licenses along with this
|
15
|
+
* software. If not, they may be obtained at the above URLs.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#ifndef ENCODING_H
|
19
|
+
#define ENCODING_H
|
20
|
+
#include "argon2.h"
|
21
|
+
|
22
|
+
#define ARGON2_MAX_DECODED_LANES UINT32_C(255)
|
23
|
+
#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8)
|
24
|
+
#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12)
|
25
|
+
|
26
|
+
/*
|
27
|
+
* encode an Argon2 hash string into the provided buffer. 'dst_len'
|
28
|
+
* contains the size, in characters, of the 'dst' buffer; if 'dst_len'
|
29
|
+
* is less than the number of required characters (including the
|
30
|
+
* terminating 0), then this function returns ARGON2_ENCODING_ERROR.
|
31
|
+
*
|
32
|
+
* on success, ARGON2_OK is returned.
|
33
|
+
*/
|
34
|
+
int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
|
35
|
+
argon2_type type);
|
36
|
+
|
37
|
+
/*
|
38
|
+
* Decodes an Argon2 hash string into the provided structure 'ctx'.
|
39
|
+
* The only fields that must be set prior to this call are ctx.saltlen and
|
40
|
+
* ctx.outlen (which must be the maximal salt and out length values that are
|
41
|
+
* allowed), ctx.salt and ctx.out (which must be buffers of the specified
|
42
|
+
* length), and ctx.pwd and ctx.pwdlen which must hold a valid password.
|
43
|
+
*
|
44
|
+
* Invalid input string causes an error. On success, the ctx is valid and all
|
45
|
+
* fields have been initialized.
|
46
|
+
*
|
47
|
+
* Returned value is ARGON2_OK on success, other ARGON2_ codes on error.
|
48
|
+
*/
|
49
|
+
int decode_string(argon2_context *ctx, const char *str, argon2_type type);
|
50
|
+
|
51
|
+
/* Returns the length of the encoded byte stream with length len */
|
52
|
+
size_t b64len(uint32_t len);
|
53
|
+
|
54
|
+
/* Returns the length of the encoded number num */
|
55
|
+
size_t numlen(uint32_t num);
|
56
|
+
|
57
|
+
#endif
|
@@ -0,0 +1,194 @@
|
|
1
|
+
/*
|
2
|
+
* Argon2 reference source code package - reference C implementations
|
3
|
+
*
|
4
|
+
* Copyright 2015
|
5
|
+
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
6
|
+
*
|
7
|
+
* You may use this work under the terms of a Creative Commons CC0 1.0
|
8
|
+
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
9
|
+
* these licenses can be found at:
|
10
|
+
*
|
11
|
+
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
12
|
+
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
*
|
14
|
+
* You should have received a copy of both of these licenses along with this
|
15
|
+
* software. If not, they may be obtained at the above URLs.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#include <stdint.h>
|
19
|
+
#include <string.h>
|
20
|
+
#include <stdlib.h>
|
21
|
+
|
22
|
+
#include "argon2.h"
|
23
|
+
#include "core.h"
|
24
|
+
|
25
|
+
#include "blake2/blamka-round-ref.h"
|
26
|
+
#include "blake2/blake2-impl.h"
|
27
|
+
#include "blake2/blake2.h"
|
28
|
+
|
29
|
+
|
30
|
+
/*
|
31
|
+
* Function fills a new memory block and optionally XORs the old block over the new one.
|
32
|
+
* @next_block must be initialized.
|
33
|
+
* @param prev_block Pointer to the previous block
|
34
|
+
* @param ref_block Pointer to the reference block
|
35
|
+
* @param next_block Pointer to the block to be constructed
|
36
|
+
* @param with_xor Whether to XOR into the new block (1) or just overwrite (0)
|
37
|
+
* @pre all block pointers must be valid
|
38
|
+
*/
|
39
|
+
static void fill_block(const block *prev_block, const block *ref_block,
|
40
|
+
block *next_block, int with_xor) {
|
41
|
+
block blockR, block_tmp;
|
42
|
+
unsigned i;
|
43
|
+
|
44
|
+
copy_block(&blockR, ref_block);
|
45
|
+
xor_block(&blockR, prev_block);
|
46
|
+
copy_block(&block_tmp, &blockR);
|
47
|
+
/* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */
|
48
|
+
if (with_xor) {
|
49
|
+
/* Saving the next block contents for XOR over: */
|
50
|
+
xor_block(&block_tmp, next_block);
|
51
|
+
/* Now blockR = ref_block + prev_block and
|
52
|
+
block_tmp = ref_block + prev_block + next_block */
|
53
|
+
}
|
54
|
+
|
55
|
+
/* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
|
56
|
+
(16,17,..31)... finally (112,113,...127) */
|
57
|
+
for (i = 0; i < 8; ++i) {
|
58
|
+
BLAKE2_ROUND_NOMSG(
|
59
|
+
blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2],
|
60
|
+
blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5],
|
61
|
+
blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8],
|
62
|
+
blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11],
|
63
|
+
blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14],
|
64
|
+
blockR.v[16 * i + 15]);
|
65
|
+
}
|
66
|
+
|
67
|
+
/* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
|
68
|
+
(2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
|
69
|
+
for (i = 0; i < 8; i++) {
|
70
|
+
BLAKE2_ROUND_NOMSG(
|
71
|
+
blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16],
|
72
|
+
blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33],
|
73
|
+
blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64],
|
74
|
+
blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81],
|
75
|
+
blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112],
|
76
|
+
blockR.v[2 * i + 113]);
|
77
|
+
}
|
78
|
+
|
79
|
+
copy_block(next_block, &block_tmp);
|
80
|
+
xor_block(next_block, &blockR);
|
81
|
+
}
|
82
|
+
|
83
|
+
static void next_addresses(block *address_block, block *input_block,
|
84
|
+
const block *zero_block) {
|
85
|
+
input_block->v[6]++;
|
86
|
+
fill_block(zero_block, input_block, address_block, 0);
|
87
|
+
fill_block(zero_block, address_block, address_block, 0);
|
88
|
+
}
|
89
|
+
|
90
|
+
void fill_segment(const argon2_instance_t *instance,
|
91
|
+
argon2_position_t position) {
|
92
|
+
block *ref_block = NULL, *curr_block = NULL;
|
93
|
+
block address_block, input_block, zero_block;
|
94
|
+
uint64_t pseudo_rand, ref_index, ref_lane;
|
95
|
+
uint32_t prev_offset, curr_offset;
|
96
|
+
uint32_t starting_index;
|
97
|
+
uint32_t i;
|
98
|
+
int data_independent_addressing;
|
99
|
+
|
100
|
+
if (instance == NULL) {
|
101
|
+
return;
|
102
|
+
}
|
103
|
+
|
104
|
+
data_independent_addressing =
|
105
|
+
(instance->type == Argon2_i) ||
|
106
|
+
(instance->type == Argon2_id && (position.pass == 0) &&
|
107
|
+
(position.slice < ARGON2_SYNC_POINTS / 2));
|
108
|
+
|
109
|
+
if (data_independent_addressing) {
|
110
|
+
init_block_value(&zero_block, 0);
|
111
|
+
init_block_value(&input_block, 0);
|
112
|
+
|
113
|
+
input_block.v[0] = position.pass;
|
114
|
+
input_block.v[1] = position.lane;
|
115
|
+
input_block.v[2] = position.slice;
|
116
|
+
input_block.v[3] = instance->memory_blocks;
|
117
|
+
input_block.v[4] = instance->passes;
|
118
|
+
input_block.v[5] = instance->type;
|
119
|
+
}
|
120
|
+
|
121
|
+
starting_index = 0;
|
122
|
+
|
123
|
+
if ((0 == position.pass) && (0 == position.slice)) {
|
124
|
+
starting_index = 2; /* we have already generated the first two blocks */
|
125
|
+
|
126
|
+
/* Don't forget to generate the first block of addresses: */
|
127
|
+
if (data_independent_addressing) {
|
128
|
+
next_addresses(&address_block, &input_block, &zero_block);
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
/* Offset of the current block */
|
133
|
+
curr_offset = position.lane * instance->lane_length +
|
134
|
+
position.slice * instance->segment_length + starting_index;
|
135
|
+
|
136
|
+
if (0 == curr_offset % instance->lane_length) {
|
137
|
+
/* Last block in this lane */
|
138
|
+
prev_offset = curr_offset + instance->lane_length - 1;
|
139
|
+
} else {
|
140
|
+
/* Previous block */
|
141
|
+
prev_offset = curr_offset - 1;
|
142
|
+
}
|
143
|
+
|
144
|
+
for (i = starting_index; i < instance->segment_length;
|
145
|
+
++i, ++curr_offset, ++prev_offset) {
|
146
|
+
/*1.1 Rotating prev_offset if needed */
|
147
|
+
if (curr_offset % instance->lane_length == 1) {
|
148
|
+
prev_offset = curr_offset - 1;
|
149
|
+
}
|
150
|
+
|
151
|
+
/* 1.2 Computing the index of the reference block */
|
152
|
+
/* 1.2.1 Taking pseudo-random value from the previous block */
|
153
|
+
if (data_independent_addressing) {
|
154
|
+
if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) {
|
155
|
+
next_addresses(&address_block, &input_block, &zero_block);
|
156
|
+
}
|
157
|
+
pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK];
|
158
|
+
} else {
|
159
|
+
pseudo_rand = instance->memory[prev_offset].v[0];
|
160
|
+
}
|
161
|
+
|
162
|
+
/* 1.2.2 Computing the lane of the reference block */
|
163
|
+
ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
|
164
|
+
|
165
|
+
if ((position.pass == 0) && (position.slice == 0)) {
|
166
|
+
/* Can not reference other lanes yet */
|
167
|
+
ref_lane = position.lane;
|
168
|
+
}
|
169
|
+
|
170
|
+
/* 1.2.3 Computing the number of possible reference block within the
|
171
|
+
* lane.
|
172
|
+
*/
|
173
|
+
position.index = i;
|
174
|
+
ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
|
175
|
+
ref_lane == position.lane);
|
176
|
+
|
177
|
+
/* 2 Creating a new block */
|
178
|
+
ref_block =
|
179
|
+
instance->memory + instance->lane_length * ref_lane + ref_index;
|
180
|
+
curr_block = instance->memory + curr_offset;
|
181
|
+
if (ARGON2_VERSION_10 == instance->version) {
|
182
|
+
/* version 1.2.1 and earlier: overwrite, not XOR */
|
183
|
+
fill_block(instance->memory + prev_offset, ref_block, curr_block, 0);
|
184
|
+
} else {
|
185
|
+
if(0 == position.pass) {
|
186
|
+
fill_block(instance->memory + prev_offset, ref_block,
|
187
|
+
curr_block, 0);
|
188
|
+
} else {
|
189
|
+
fill_block(instance->memory + prev_offset, ref_block,
|
190
|
+
curr_block, 1);
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
/*
|
2
|
+
* Argon2 reference source code package - reference C implementations
|
3
|
+
*
|
4
|
+
* Copyright 2015
|
5
|
+
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
6
|
+
*
|
7
|
+
* You may use this work under the terms of a Creative Commons CC0 1.0
|
8
|
+
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
9
|
+
* these licenses can be found at:
|
10
|
+
*
|
11
|
+
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
12
|
+
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
*
|
14
|
+
* You should have received a copy of both of these licenses along with this
|
15
|
+
* software. If not, they may be obtained at the above URLs.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#if !defined(ARGON2_NO_THREADS)
|
19
|
+
|
20
|
+
#include "thread.h"
|
21
|
+
#if defined(_WIN32)
|
22
|
+
#include <windows.h>
|
23
|
+
#endif
|
24
|
+
|
25
|
+
int argon2_thread_create(argon2_thread_handle_t *handle,
|
26
|
+
argon2_thread_func_t func, void *args) {
|
27
|
+
if (NULL == handle || func == NULL) {
|
28
|
+
return -1;
|
29
|
+
}
|
30
|
+
#if defined(_WIN32)
|
31
|
+
*handle = _beginthreadex(NULL, 0, func, args, 0, NULL);
|
32
|
+
return *handle != 0 ? 0 : -1;
|
33
|
+
#else
|
34
|
+
return pthread_create(handle, NULL, func, args);
|
35
|
+
#endif
|
36
|
+
}
|
37
|
+
|
38
|
+
int argon2_thread_join(argon2_thread_handle_t handle) {
|
39
|
+
#if defined(_WIN32)
|
40
|
+
if (WaitForSingleObject((HANDLE)handle, INFINITE) == WAIT_OBJECT_0) {
|
41
|
+
return CloseHandle((HANDLE)handle) != 0 ? 0 : -1;
|
42
|
+
}
|
43
|
+
return -1;
|
44
|
+
#else
|
45
|
+
return pthread_join(handle, NULL);
|
46
|
+
#endif
|
47
|
+
}
|
48
|
+
|
49
|
+
void argon2_thread_exit(void) {
|
50
|
+
#if defined(_WIN32)
|
51
|
+
_endthreadex(0);
|
52
|
+
#else
|
53
|
+
pthread_exit(NULL);
|
54
|
+
#endif
|
55
|
+
}
|
56
|
+
|
57
|
+
#endif /* ARGON2_NO_THREADS */
|
@@ -0,0 +1,67 @@
|
|
1
|
+
/*
|
2
|
+
* Argon2 reference source code package - reference C implementations
|
3
|
+
*
|
4
|
+
* Copyright 2015
|
5
|
+
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
6
|
+
*
|
7
|
+
* You may use this work under the terms of a Creative Commons CC0 1.0
|
8
|
+
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
|
9
|
+
* these licenses can be found at:
|
10
|
+
*
|
11
|
+
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
12
|
+
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
*
|
14
|
+
* You should have received a copy of both of these licenses along with this
|
15
|
+
* software. If not, they may be obtained at the above URLs.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#ifndef ARGON2_THREAD_H
|
19
|
+
#define ARGON2_THREAD_H
|
20
|
+
|
21
|
+
#if !defined(ARGON2_NO_THREADS)
|
22
|
+
|
23
|
+
/*
|
24
|
+
Here we implement an abstraction layer for the simpĺe requirements
|
25
|
+
of the Argon2 code. We only require 3 primitives---thread creation,
|
26
|
+
joining, and termination---so full emulation of the pthreads API
|
27
|
+
is unwarranted. Currently we wrap pthreads and Win32 threads.
|
28
|
+
|
29
|
+
The API defines 2 types: the function pointer type,
|
30
|
+
argon2_thread_func_t,
|
31
|
+
and the type of the thread handle---argon2_thread_handle_t.
|
32
|
+
*/
|
33
|
+
#if defined(_WIN32)
|
34
|
+
#include <process.h>
|
35
|
+
typedef unsigned(__stdcall *argon2_thread_func_t)(void *);
|
36
|
+
typedef uintptr_t argon2_thread_handle_t;
|
37
|
+
#else
|
38
|
+
#include <pthread.h>
|
39
|
+
typedef void *(*argon2_thread_func_t)(void *);
|
40
|
+
typedef pthread_t argon2_thread_handle_t;
|
41
|
+
#endif
|
42
|
+
|
43
|
+
/* Creates a thread
|
44
|
+
* @param handle pointer to a thread handle, which is the output of this
|
45
|
+
* function. Must not be NULL.
|
46
|
+
* @param func A function pointer for the thread's entry point. Must not be
|
47
|
+
* NULL.
|
48
|
+
* @param args Pointer that is passed as an argument to @func. May be NULL.
|
49
|
+
* @return 0 if @handle and @func are valid pointers and a thread is successfully
|
50
|
+
* created.
|
51
|
+
*/
|
52
|
+
int argon2_thread_create(argon2_thread_handle_t *handle,
|
53
|
+
argon2_thread_func_t func, void *args);
|
54
|
+
|
55
|
+
/* Waits for a thread to terminate
|
56
|
+
* @param handle Handle to a thread created with argon2_thread_create.
|
57
|
+
* @return 0 if @handle is a valid handle, and joining completed successfully.
|
58
|
+
*/
|
59
|
+
int argon2_thread_join(argon2_thread_handle_t handle);
|
60
|
+
|
61
|
+
/* Terminate the current thread. Must be run inside a thread created by
|
62
|
+
* argon2_thread_create.
|
63
|
+
*/
|
64
|
+
void argon2_thread_exit(void);
|
65
|
+
|
66
|
+
#endif /* ARGON2_NO_THREADS */
|
67
|
+
#endif
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,71 @@
|
|
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_version(Java::OrgBouncycastleCryptoParams::Argon2Parameters::ARGON2_VERSION_13)
|
19
|
+
.with_iterations(t_cost)
|
20
|
+
.with_memory_as_kb(m_cost)
|
21
|
+
.with_parallelism(parallelism)
|
22
|
+
.with_salt(salt_bytes)
|
23
|
+
.build
|
24
|
+
generator = Java::OrgBouncycastleCryptoGenerators::Argon2BytesGenerator.new
|
25
|
+
|
26
|
+
generator.init(params)
|
27
|
+
generator.generate_bytes(pwd.to_java_bytes, output)
|
28
|
+
|
29
|
+
encoder = Java::JavaUtil::Base64.get_encoder.without_padding
|
30
|
+
encoded_salt = encoder.encode_to_string(salt_bytes)
|
31
|
+
encoded_output = encoder.encode_to_string(output)
|
32
|
+
|
33
|
+
"$argon2id$v=19$m=#{m_cost},t=#{t_cost},p=#{parallelism}" \
|
34
|
+
"$#{encoded_salt}$#{encoded_output}"
|
35
|
+
rescue Java::JavaLang::IllegalStateException => e
|
36
|
+
raise Error, e.message
|
37
|
+
end
|
38
|
+
|
39
|
+
private_class_method :hash_encoded
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def verify(pwd)
|
44
|
+
other_output = Java::byte[output.bytesize].new
|
45
|
+
params = Java::OrgBouncycastleCryptoParams::Argon2Parameters::Builder
|
46
|
+
.new(Java::OrgBouncycastleCryptoParams::Argon2Parameters::ARGON2_id)
|
47
|
+
.with_version(version)
|
48
|
+
.with_iterations(t_cost)
|
49
|
+
.with_memory_as_kb(m_cost)
|
50
|
+
.with_parallelism(parallelism)
|
51
|
+
.with_salt(salt.to_java_bytes)
|
52
|
+
.build
|
53
|
+
generator = Java::OrgBouncycastleCryptoGenerators::Argon2BytesGenerator.new
|
54
|
+
generator.init(params)
|
55
|
+
generator.generate_bytes(pwd.to_java_bytes, other_output)
|
56
|
+
|
57
|
+
Java::OrgBouncycastleUtil::Arrays.constant_time_are_equal?(
|
58
|
+
output.to_java_bytes,
|
59
|
+
other_output
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
begin
|
66
|
+
::RUBY_VERSION =~ /(\d+\.\d+)/
|
67
|
+
require_relative "#{Regexp.last_match(1)}/argon2id"
|
68
|
+
rescue LoadError
|
69
|
+
require "argon2id/argon2id"
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
|
5
|
+
module Argon2id
|
6
|
+
# The Password class encapsulates an encoded Argon2id password hash.
|
7
|
+
#
|
8
|
+
# To hash a plain text password, use Argon2id::Password.create:
|
9
|
+
#
|
10
|
+
# password = Argon2id::Password.create("password")
|
11
|
+
# password.to_s
|
12
|
+
# #=> "$argon2id$v=19$m=19456,t=2,p=1$+Lrjry9Ifq0poLr15OGU1Q$utkDvejJB0ugwm4s9+a+vF6+1a/W+Y3CYa5Wte/85ig"
|
13
|
+
#
|
14
|
+
# To wrap an encoded Argon2id password hash, use Argon2id::Password.new:
|
15
|
+
#
|
16
|
+
# password = Argon2id::Password.new("$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4")
|
17
|
+
#
|
18
|
+
# You can then verify it matches a given plain text:
|
19
|
+
#
|
20
|
+
# password == "password" #=> true
|
21
|
+
# password == "not password" #=> false
|
22
|
+
#
|
23
|
+
# password.is_password?("password") #=> true
|
24
|
+
# password.is_password?("not password") #=> false
|
25
|
+
#
|
26
|
+
# You can read various parameters out of a password hash:
|
27
|
+
#
|
28
|
+
# password.version #=> 19
|
29
|
+
# password.m_cost #=> 19456
|
30
|
+
# password.t_cost #=> 2
|
31
|
+
# password.parallelism #=> 1
|
32
|
+
# password.salt #=> "somesalt"
|
33
|
+
class Password
|
34
|
+
# A regular expression to match valid hashes.
|
35
|
+
PATTERN = %r{
|
36
|
+
\A
|
37
|
+
\$
|
38
|
+
argon2id
|
39
|
+
(?:\$v=(\d+))?
|
40
|
+
\$m=(\d+)
|
41
|
+
,t=(\d+)
|
42
|
+
,p=(\d+)
|
43
|
+
\$
|
44
|
+
([a-zA-Z0-9+/]+)
|
45
|
+
\$
|
46
|
+
([a-zA-Z0-9+/]+)
|
47
|
+
\z
|
48
|
+
}x.freeze
|
49
|
+
|
50
|
+
# The encoded password hash.
|
51
|
+
attr_reader :encoded
|
52
|
+
|
53
|
+
# The version number of the hashing function.
|
54
|
+
attr_reader :version
|
55
|
+
|
56
|
+
# The "time cost" of the hashing function.
|
57
|
+
attr_reader :t_cost
|
58
|
+
|
59
|
+
# The "memory cost" of the hashing function.
|
60
|
+
attr_reader :m_cost
|
61
|
+
|
62
|
+
# The number of threads and compute lanes of the hashing function.
|
63
|
+
attr_reader :parallelism
|
64
|
+
|
65
|
+
# The salt.
|
66
|
+
attr_reader :salt
|
67
|
+
|
68
|
+
# The hash output.
|
69
|
+
attr_reader :output
|
70
|
+
|
71
|
+
# Create a new Password object that hashes a given plain text password +pwd+.
|
72
|
+
#
|
73
|
+
# - +:t_cost+: integer (default 2) the "time cost" given as a number of iterations
|
74
|
+
# - +:m_cost+: integer (default 19456) the "memory cost" given in kibibytes
|
75
|
+
# - +:parallelism+: integer (default 1) the number of threads and compute lanes to use
|
76
|
+
# - +:salt_len+: integer (default 16) the salt size in bytes
|
77
|
+
# - +:output_len+: integer (default 32) the desired length of the hash in bytes
|
78
|
+
#
|
79
|
+
# For example, with the default configuration:
|
80
|
+
#
|
81
|
+
# password = Argon2id::Password.create("password")
|
82
|
+
# password.to_s
|
83
|
+
# #=> "$argon2id$v=19$m=19456,t=2,p=1$FI8yp1gXbthJCskBlpKPoQ$nOfCCpS2r+I8GRN71cZND4cskn7YKBNzuHUEO3YpY2s"
|
84
|
+
#
|
85
|
+
# When overriding the configuration:
|
86
|
+
#
|
87
|
+
# password = Argon2id::Password.create("password", t_cost: 3, m_cost: 12288)
|
88
|
+
# password.to_s
|
89
|
+
# #=> "$argon2id$v=19$m=12288,t=3,p=1$JigW7fFn+N3NImt+aWpuzw$eM5F1cKeIBALNTU6LuWra75Zi2nymGvQLWzJzVFv0Nc"
|
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
|
+
new(
|
92
|
+
hash_encoded(
|
93
|
+
Integer(t_cost),
|
94
|
+
Integer(m_cost),
|
95
|
+
Integer(parallelism),
|
96
|
+
String(pwd),
|
97
|
+
OpenSSL::Random.random_bytes(Integer(salt_len)),
|
98
|
+
Integer(output_len)
|
99
|
+
)
|
100
|
+
)
|
101
|
+
end
|
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
|
+
|
110
|
+
# Create a new Password with the given encoded password hash.
|
111
|
+
#
|
112
|
+
# password = Argon2id::Password.new("$argon2id$v=19$m=19456,t=2,p=1$FI8yp1gXbthJCskBlpKPoQ$nOfCCpS2r+I8GRN71cZND4cskn7YKBNzuHUEO3YpY2s")
|
113
|
+
#
|
114
|
+
# Raises an ArgumentError if given an invalid hash.
|
115
|
+
def initialize(encoded)
|
116
|
+
raise ArgumentError, "invalid hash" unless PATTERN =~ String(encoded)
|
117
|
+
|
118
|
+
@encoded = $&
|
119
|
+
@version = Integer($1 || 0x10)
|
120
|
+
@m_cost = Integer($2)
|
121
|
+
@t_cost = Integer($3)
|
122
|
+
@parallelism = Integer($4)
|
123
|
+
@salt = $5.unpack1("m")
|
124
|
+
@output = $6.unpack1("m")
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return the encoded password hash.
|
128
|
+
alias_method :to_s, :encoded
|
129
|
+
|
130
|
+
# Compare the password with the given plain text, returning true if it
|
131
|
+
# verifies successfully.
|
132
|
+
#
|
133
|
+
# password = Argon2id::Password.new("$argon2id$v=19$m=19456,t=2,p=1$FI8yp1gXbthJCskBlpKPoQ$nOfCCpS2r+I8GRN71cZND4cskn7YKBNzuHUEO3YpY2s")
|
134
|
+
# password == "password" #=> true
|
135
|
+
# password == "notpassword" #=> false
|
136
|
+
def ==(other)
|
137
|
+
verify(String(other))
|
138
|
+
end
|
139
|
+
|
140
|
+
alias_method :is_password?, :==
|
141
|
+
end
|
142
|
+
end
|
data/lib/argon2id.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "argon2id/extension"
|
4
|
+
require "argon2id/password"
|
5
|
+
require "argon2id/version"
|
6
|
+
|
7
|
+
module Argon2id
|
8
|
+
# The default "time cost" of 2 iterations recommended by OWASP.
|
9
|
+
DEFAULT_T_COST = 2
|
10
|
+
|
11
|
+
# The default "memory cost" of 19 mebibytes recommended by OWASP.
|
12
|
+
DEFAULT_M_COST = 19_456
|
13
|
+
|
14
|
+
# The default 1 thread and compute lane recommended by OWASP.
|
15
|
+
DEFAULT_PARALLELISM = 1
|
16
|
+
|
17
|
+
# The default salt length of 16 bytes.
|
18
|
+
DEFAULT_SALT_LEN = 16
|
19
|
+
|
20
|
+
# The default desired hash length of 32 bytes.
|
21
|
+
DEFAULT_OUTPUT_LEN = 32
|
22
|
+
|
23
|
+
@t_cost = DEFAULT_T_COST
|
24
|
+
@m_cost = DEFAULT_M_COST
|
25
|
+
@parallelism = DEFAULT_PARALLELISM
|
26
|
+
@salt_len = DEFAULT_SALT_LEN
|
27
|
+
@output_len = DEFAULT_OUTPUT_LEN
|
28
|
+
|
29
|
+
class << self
|
30
|
+
# The default number of iterations used by Argon2id::Password.create
|
31
|
+
attr_accessor :t_cost
|
32
|
+
|
33
|
+
# The default memory cost in kibibytes used by Argon2id::Password.create
|
34
|
+
attr_accessor :m_cost
|
35
|
+
|
36
|
+
# The default number of threads and compute lanes used by Argon2id::Password.create
|
37
|
+
attr_accessor :parallelism
|
38
|
+
|
39
|
+
# The default salt size in bytes used by Argon2id::Password.create
|
40
|
+
attr_accessor :salt_len
|
41
|
+
|
42
|
+
# The default desired length of the hash in bytes used by Argon2id::Password.create
|
43
|
+
attr_accessor :output_len
|
44
|
+
end
|
45
|
+
end
|