argon2id 0.8.0.rc1-x86_64-linux-gnu
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
@@ -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
|