yescrypt 0.1.0

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.
@@ -0,0 +1,407 @@
1
+ /*
2
+ * Ruby C extension wrapper for yescrypt.
3
+ */
4
+
5
+ #include <ruby.h>
6
+ #include <ruby/thread.h>
7
+ #include <ruby/encoding.h>
8
+ #include <string.h>
9
+ #include <stdlib.h>
10
+
11
+ #include "yescrypt.h"
12
+ #include "insecure_memzero.h"
13
+
14
+ static VALUE rb_mYescrypt;
15
+ static VALUE rb_eYescryptError;
16
+
17
+ /* Cached symbol IDs */
18
+ static ID id_n, id_r, id_p, id_t_param, id_flags, id_outlen;
19
+ static ID id_n_log2, id_salt;
20
+
21
+ #define MAX_KDF_OUTLEN 1024
22
+ #define MAX_N_LOG2 24
23
+ #define MAX_R 256
24
+ #define MAX_P 256
25
+ #define MAX_T 100
26
+
27
+ /* Default parameter values (single source of truth for C side) */
28
+ #define DEFAULT_N_LOG2_VAL 12
29
+ #define DEFAULT_R_VAL 32
30
+ #define DEFAULT_P_VAL 1
31
+ #define DEFAULT_T_VAL 0
32
+ #define DEFAULT_FLAGS_VAL YESCRYPT_DEFAULTS
33
+
34
+ static void validate_flags(int flags)
35
+ {
36
+ int base = flags & 0x3; /* lowest 2 bits: WORM/RW/DEFAULTS */
37
+ if (base > YESCRYPT_DEFAULTS)
38
+ rb_raise(rb_eArgError, "invalid base flag value: %d", base);
39
+
40
+ int extra = flags & ~0x3;
41
+ int valid_extra = YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_8 | YESCRYPT_SIMPLE_8;
42
+ if (extra & ~valid_extra)
43
+ rb_raise(rb_eArgError, "invalid flags: 0x%x", flags);
44
+ }
45
+
46
+ /* --- GVL-free structs for kdf and hash_password --- */
47
+
48
+ struct kdf_nogvl_args {
49
+ const yescrypt_shared_t *shared;
50
+ yescrypt_local_t *local;
51
+ const uint8_t *password;
52
+ size_t password_len;
53
+ const uint8_t *salt;
54
+ size_t salt_len;
55
+ const yescrypt_params_t *params;
56
+ uint8_t *buf;
57
+ size_t buflen;
58
+ int ret;
59
+ };
60
+
61
+ static void *kdf_nogvl(void *data)
62
+ {
63
+ struct kdf_nogvl_args *args = data;
64
+ args->ret = yescrypt_kdf(
65
+ args->shared, args->local,
66
+ args->password, args->password_len,
67
+ args->salt, args->salt_len,
68
+ args->params, args->buf, args->buflen);
69
+ return NULL;
70
+ }
71
+
72
+ struct hash_nogvl_args {
73
+ const yescrypt_shared_t *shared;
74
+ yescrypt_local_t *local;
75
+ const uint8_t *password;
76
+ size_t password_len;
77
+ const uint8_t *setting;
78
+ uint8_t *buf;
79
+ size_t buflen;
80
+ uint8_t *result;
81
+ };
82
+
83
+ static void *hash_nogvl(void *data)
84
+ {
85
+ struct hash_nogvl_args *args = data;
86
+ args->result = yescrypt_r(
87
+ args->shared, args->local,
88
+ args->password, args->password_len,
89
+ args->setting,
90
+ args->buf, args->buflen);
91
+ return NULL;
92
+ }
93
+
94
+ /* --- Cleanup helpers for rb_ensure --- */
95
+
96
+ struct kdf_cleanup_ctx {
97
+ yescrypt_shared_t shared;
98
+ yescrypt_local_t local;
99
+ };
100
+
101
+ static VALUE kdf_cleanup(VALUE arg)
102
+ {
103
+ struct kdf_cleanup_ctx *ctx = (struct kdf_cleanup_ctx *)arg;
104
+ yescrypt_free_local(&ctx->local);
105
+ yescrypt_free_shared(&ctx->shared);
106
+ return Qnil;
107
+ }
108
+
109
+ /*
110
+ * Yescrypt.kdf(password, salt, n:, r:, p:, flags:, t:, outlen:)
111
+ *
112
+ * Low-level KDF function. n, r, p, and flags are required.
113
+ * Returns raw hash bytes.
114
+ */
115
+ static VALUE kdf_body(VALUE arg);
116
+
117
+ struct kdf_call_ctx {
118
+ VALUE password;
119
+ VALUE salt;
120
+ VALUE opts;
121
+ struct kdf_cleanup_ctx *cleanup;
122
+ };
123
+
124
+ static VALUE rb_yescrypt_kdf(int argc, VALUE *argv, VALUE self)
125
+ {
126
+ (void)self;
127
+ VALUE password, salt, opts;
128
+ rb_scan_args(argc, argv, "2:", &password, &salt, &opts);
129
+
130
+ Check_Type(password, T_STRING);
131
+ Check_Type(salt, T_STRING);
132
+
133
+ if (NIL_P(opts))
134
+ opts = rb_hash_new();
135
+
136
+ struct kdf_cleanup_ctx cleanup;
137
+ yescrypt_init_shared(&cleanup.shared);
138
+ yescrypt_init_local(&cleanup.local);
139
+
140
+ struct kdf_call_ctx ctx = { password, salt, opts, &cleanup };
141
+ return rb_ensure(kdf_body, (VALUE)&ctx, kdf_cleanup, (VALUE)&cleanup);
142
+ }
143
+
144
+ static VALUE kdf_body(VALUE arg)
145
+ {
146
+ struct kdf_call_ctx *ctx = (struct kdf_call_ctx *)arg;
147
+
148
+ VALUE v_N = rb_hash_aref(ctx->opts, ID2SYM(id_n));
149
+ VALUE v_r = rb_hash_aref(ctx->opts, ID2SYM(id_r));
150
+ VALUE v_p = rb_hash_aref(ctx->opts, ID2SYM(id_p));
151
+ VALUE v_t = rb_hash_aref(ctx->opts, ID2SYM(id_t_param));
152
+ VALUE v_flags = rb_hash_aref(ctx->opts, ID2SYM(id_flags));
153
+ VALUE v_outlen = rb_hash_aref(ctx->opts, ID2SYM(id_outlen));
154
+
155
+ if (NIL_P(v_N) || NIL_P(v_r) || NIL_P(v_p) || NIL_P(v_flags))
156
+ rb_raise(rb_eArgError, "n, r, p, and flags are required for kdf");
157
+
158
+ uint64_t N = NUM2ULL(v_N);
159
+ uint32_t r = NUM2UINT(v_r);
160
+ uint32_t p = NUM2UINT(v_p);
161
+ uint32_t t = NIL_P(v_t) ? 0 : NUM2UINT(v_t);
162
+ int flags = NUM2INT(v_flags);
163
+ size_t outlen = NIL_P(v_outlen) ? 32 : NUM2SIZET(v_outlen);
164
+
165
+ /* Input validation */
166
+ if (N == 0 || (N & (N - 1)) != 0)
167
+ rb_raise(rb_eArgError, "n must be a power of 2");
168
+ if (N > ((uint64_t)1 << MAX_N_LOG2))
169
+ rb_raise(rb_eArgError, "n must not exceed 2^%d", MAX_N_LOG2);
170
+ if (r == 0 || r > MAX_R)
171
+ rb_raise(rb_eArgError, "r must be between 1 and %d", MAX_R);
172
+ if (p == 0 || p > MAX_P)
173
+ rb_raise(rb_eArgError, "p must be between 1 and %d", MAX_P);
174
+ if (t > MAX_T)
175
+ rb_raise(rb_eArgError, "t must be between 0 and %d", MAX_T);
176
+ if (outlen == 0 || outlen > MAX_KDF_OUTLEN)
177
+ rb_raise(rb_eArgError, "outlen must be between 1 and %d", MAX_KDF_OUTLEN);
178
+ validate_flags(flags);
179
+
180
+ yescrypt_params_t params;
181
+ params.flags = (yescrypt_flags_t)flags;
182
+ params.N = N;
183
+ params.r = r;
184
+ params.p = p;
185
+ params.t = t;
186
+ params.g = 0;
187
+
188
+ uint8_t buf[MAX_KDF_OUTLEN];
189
+
190
+ /* Pin strings so GC compaction won't move them while GVL is released */
191
+ VALUE pw_pinned = rb_str_new_frozen(ctx->password);
192
+ VALUE salt_pinned = rb_str_new_frozen(ctx->salt);
193
+
194
+ struct kdf_nogvl_args args;
195
+ args.shared = &ctx->cleanup->shared;
196
+ args.local = &ctx->cleanup->local;
197
+ args.password = (const uint8_t *)RSTRING_PTR(pw_pinned);
198
+ args.password_len = RSTRING_LEN(pw_pinned);
199
+ args.salt = (const uint8_t *)RSTRING_PTR(salt_pinned);
200
+ args.salt_len = RSTRING_LEN(salt_pinned);
201
+ args.params = &params;
202
+ args.buf = buf;
203
+ args.buflen = outlen;
204
+
205
+ rb_thread_call_without_gvl(kdf_nogvl, &args, RUBY_UBF_IO, NULL);
206
+
207
+ if (args.ret != 0) {
208
+ insecure_memzero(buf, sizeof(buf));
209
+ rb_raise(rb_eYescryptError, "yescrypt_kdf failed");
210
+ }
211
+
212
+ VALUE result = rb_str_new((const char *)buf, outlen);
213
+ insecure_memzero(buf, sizeof(buf));
214
+ RB_GC_GUARD(pw_pinned);
215
+ RB_GC_GUARD(salt_pinned);
216
+ return result;
217
+ }
218
+
219
+ /*
220
+ * Yescrypt.gensalt(n_log2:, r:, p:, t:, flags:, salt:)
221
+ *
222
+ * Generate a yescrypt setting/salt string.
223
+ * The salt: parameter is required.
224
+ */
225
+ static VALUE rb_yescrypt_gensalt(int argc, VALUE *argv, VALUE self)
226
+ {
227
+ (void)self;
228
+ VALUE opts;
229
+ rb_scan_args(argc, argv, "0:", &opts);
230
+
231
+ if (NIL_P(opts))
232
+ opts = rb_hash_new();
233
+
234
+ VALUE v_N = rb_hash_aref(opts, ID2SYM(id_n_log2));
235
+ VALUE v_r = rb_hash_aref(opts, ID2SYM(id_r));
236
+ VALUE v_p = rb_hash_aref(opts, ID2SYM(id_p));
237
+ VALUE v_t = rb_hash_aref(opts, ID2SYM(id_t_param));
238
+ VALUE v_flags = rb_hash_aref(opts, ID2SYM(id_flags));
239
+ VALUE v_salt = rb_hash_aref(opts, ID2SYM(id_salt));
240
+
241
+ if (NIL_P(v_salt))
242
+ rb_raise(rb_eArgError, "salt is required");
243
+
244
+ Check_Type(v_salt, T_STRING);
245
+
246
+ uint32_t N_log2 = NIL_P(v_N) ? DEFAULT_N_LOG2_VAL : NUM2UINT(v_N);
247
+ uint32_t r = NIL_P(v_r) ? DEFAULT_R_VAL : NUM2UINT(v_r);
248
+ uint32_t p = NIL_P(v_p) ? DEFAULT_P_VAL : NUM2UINT(v_p);
249
+ uint32_t t = NIL_P(v_t) ? DEFAULT_T_VAL : NUM2UINT(v_t);
250
+ int flags = NIL_P(v_flags) ? DEFAULT_FLAGS_VAL : NUM2INT(v_flags);
251
+
252
+ /* Input validation */
253
+ if (N_log2 == 0 || N_log2 > MAX_N_LOG2)
254
+ rb_raise(rb_eArgError, "n_log2 must be between 1 and %d", MAX_N_LOG2);
255
+ if (r == 0 || r > MAX_R)
256
+ rb_raise(rb_eArgError, "r must be between 1 and %d", MAX_R);
257
+ if (p == 0 || p > MAX_P)
258
+ rb_raise(rb_eArgError, "p must be between 1 and %d", MAX_P);
259
+ if (t > MAX_T)
260
+ rb_raise(rb_eArgError, "t must be between 0 and %d", MAX_T);
261
+ validate_flags(flags);
262
+
263
+ const uint8_t *salt_ptr = (const uint8_t *)RSTRING_PTR(v_salt);
264
+ size_t salt_len = RSTRING_LEN(v_salt);
265
+
266
+ uint8_t buf[YESCRYPT_MAX_ENCODED];
267
+ uint8_t *result = yescrypt_gensalt(N_log2, r, p, t,
268
+ (yescrypt_flags_t)flags, salt_ptr, salt_len,
269
+ buf, sizeof(buf));
270
+
271
+ if (!result)
272
+ rb_raise(rb_eYescryptError, "failed to generate salt");
273
+
274
+ VALUE str = rb_str_new_cstr((const char *)result);
275
+ rb_enc_associate(str, rb_usascii_encoding());
276
+ OBJ_FREEZE(str);
277
+ return str;
278
+ }
279
+
280
+ /*
281
+ * Yescrypt._hash_password(password, setting)
282
+ *
283
+ * Internal: Hash a password using a setting string (from gensalt).
284
+ * Returns the full encoded hash string (frozen).
285
+ */
286
+ static VALUE hash_body(VALUE arg);
287
+
288
+ struct hash_call_ctx {
289
+ VALUE password;
290
+ VALUE setting;
291
+ struct kdf_cleanup_ctx *cleanup;
292
+ };
293
+
294
+ static VALUE rb_yescrypt_hash(VALUE self, VALUE password, VALUE setting)
295
+ {
296
+ (void)self;
297
+ Check_Type(password, T_STRING);
298
+ Check_Type(setting, T_STRING);
299
+
300
+ struct kdf_cleanup_ctx cleanup;
301
+ yescrypt_init_shared(&cleanup.shared);
302
+ yescrypt_init_local(&cleanup.local);
303
+
304
+ struct hash_call_ctx ctx = { password, setting, &cleanup };
305
+ return rb_ensure(hash_body, (VALUE)&ctx, kdf_cleanup, (VALUE)&cleanup);
306
+ }
307
+
308
+ static VALUE hash_body(VALUE arg)
309
+ {
310
+ struct hash_call_ctx *ctx = (struct hash_call_ctx *)arg;
311
+
312
+ uint8_t buf[YESCRYPT_MAX_ENCODED];
313
+
314
+ /* Pin strings so GC compaction won't move them while GVL is released */
315
+ VALUE pw_pinned = rb_str_new_frozen(ctx->password);
316
+ VALUE setting_pinned = rb_str_new_frozen(ctx->setting);
317
+
318
+ struct hash_nogvl_args args;
319
+ args.shared = &ctx->cleanup->shared;
320
+ args.local = &ctx->cleanup->local;
321
+ args.password = (const uint8_t *)RSTRING_PTR(pw_pinned);
322
+ args.password_len = RSTRING_LEN(pw_pinned);
323
+ args.setting = (const uint8_t *)RSTRING_PTR(setting_pinned);
324
+ args.buf = buf;
325
+ args.buflen = sizeof(buf);
326
+
327
+ rb_thread_call_without_gvl(hash_nogvl, &args, RUBY_UBF_IO, NULL);
328
+
329
+ RB_GC_GUARD(pw_pinned);
330
+ RB_GC_GUARD(setting_pinned);
331
+
332
+ if (!args.result) {
333
+ insecure_memzero(buf, sizeof(buf));
334
+ rb_raise(rb_eYescryptError, "yescrypt hash failed");
335
+ }
336
+
337
+ VALUE str = rb_str_new_cstr((const char *)args.result);
338
+ insecure_memzero(buf, sizeof(buf));
339
+ rb_enc_associate(str, rb_usascii_encoding());
340
+ OBJ_FREEZE(str);
341
+ return str;
342
+ }
343
+
344
+ /*
345
+ * Yescrypt.decode_params(hash_string)
346
+ *
347
+ * Parse the parameter segment from an encoded yescrypt string.
348
+ * Returns a frozen Hash with :n_log2, :r, :p, :t, :flags or nil if invalid.
349
+ */
350
+ static VALUE rb_yescrypt_decode_params(VALUE self, VALUE str)
351
+ {
352
+ (void)self;
353
+ if (NIL_P(str) || TYPE(str) != T_STRING)
354
+ return Qnil;
355
+
356
+ const uint8_t *setting = (const uint8_t *)RSTRING_PTR(str);
357
+ uint32_t N_log2, r, p, t;
358
+ yescrypt_flags_t flags;
359
+
360
+ const uint8_t *result = yescrypt_decode_params_ext(
361
+ setting, &N_log2, &r, &p, &t, &flags);
362
+
363
+ if (!result)
364
+ return Qnil;
365
+
366
+ VALUE hash = rb_hash_new();
367
+ rb_hash_aset(hash, ID2SYM(id_n_log2), UINT2NUM(N_log2));
368
+ rb_hash_aset(hash, ID2SYM(id_r), UINT2NUM(r));
369
+ rb_hash_aset(hash, ID2SYM(id_p), UINT2NUM(p));
370
+ rb_hash_aset(hash, ID2SYM(id_t_param), UINT2NUM(t));
371
+ rb_hash_aset(hash, ID2SYM(id_flags), INT2NUM((int)flags));
372
+ OBJ_FREEZE(hash);
373
+ return hash;
374
+ }
375
+
376
+ void Init_yescrypt_ext(void)
377
+ {
378
+ /* Cache intern IDs */
379
+ id_n = rb_intern("n");
380
+ id_r = rb_intern("r");
381
+ id_p = rb_intern("p");
382
+ id_t_param = rb_intern("t");
383
+ id_flags = rb_intern("flags");
384
+ id_outlen = rb_intern("outlen");
385
+ id_n_log2 = rb_intern("n_log2");
386
+ id_salt = rb_intern("salt");
387
+
388
+ rb_mYescrypt = rb_define_module("Yescrypt");
389
+ rb_eYescryptError = rb_define_class_under(rb_mYescrypt, "Error", rb_eStandardError);
390
+
391
+ rb_define_module_function(rb_mYescrypt, "kdf", rb_yescrypt_kdf, -1);
392
+ rb_define_module_function(rb_mYescrypt, "gensalt", rb_yescrypt_gensalt, -1);
393
+ rb_define_module_function(rb_mYescrypt, "decode_params", rb_yescrypt_decode_params, 1);
394
+ rb_define_private_method(rb_singleton_class(rb_mYescrypt), "_hash_password", rb_yescrypt_hash, 2);
395
+
396
+ /* Flavor flag constants */
397
+ rb_define_const(rb_mYescrypt, "WORM", INT2FIX(YESCRYPT_WORM));
398
+ rb_define_const(rb_mYescrypt, "RW", INT2FIX(YESCRYPT_RW));
399
+ rb_define_const(rb_mYescrypt, "DEFAULTS", INT2FIX(YESCRYPT_DEFAULTS));
400
+
401
+ /* Default parameter constants (single source of truth) */
402
+ rb_define_const(rb_mYescrypt, "DEFAULT_N_LOG2", INT2FIX(DEFAULT_N_LOG2_VAL));
403
+ rb_define_const(rb_mYescrypt, "DEFAULT_R", INT2FIX(DEFAULT_R_VAL));
404
+ rb_define_const(rb_mYescrypt, "DEFAULT_P", INT2FIX(DEFAULT_P_VAL));
405
+ rb_define_const(rb_mYescrypt, "DEFAULT_T", INT2FIX(DEFAULT_T_VAL));
406
+ rb_define_const(rb_mYescrypt, "DEFAULT_FLAGS", INT2FIX(DEFAULT_FLAGS_VAL));
407
+ }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yescrypt
4
+ VERSION = "0.1.0"
5
+ end
data/lib/yescrypt.rb ADDED
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "yescrypt/version"
4
+ require "yescrypt/yescrypt_ext"
5
+ require "securerandom"
6
+ require "openssl"
7
+
8
+ module Yescrypt
9
+ class << self
10
+ # High-level: hash a password, returning an encoded string.
11
+ #
12
+ # @param password [String] the plaintext password
13
+ # @param options [Hash] override defaults (n_log2:, r:, p:, t:, flags:)
14
+ # @return [String] encoded yescrypt hash (starts with "$y$")
15
+ def create(password, **options)
16
+ raise TypeError, "no implicit conversion of #{password.class} into String" unless password.is_a?(String)
17
+ opts = default_params.merge(options)
18
+ salt = SecureRandom.random_bytes(16)
19
+ setting = gensalt(
20
+ n_log2: opts[:n_log2],
21
+ r: opts[:r],
22
+ p: opts[:p],
23
+ t: opts[:t],
24
+ flags: opts[:flags],
25
+ salt: salt
26
+ )
27
+ _hash_password(password, setting)
28
+ end
29
+
30
+ # High-level: verify a password against an encoded hash.
31
+ #
32
+ # @param password [String] the plaintext password
33
+ # @param hash [String] the encoded hash from .create
34
+ # @return [Boolean]
35
+ def verify(password, hash)
36
+ return false unless password.is_a?(String) && hash.is_a?(String)
37
+ return false unless hash.start_with?("$y$")
38
+
39
+ rehash = _hash_password(password, hash)
40
+ begin
41
+ OpenSSL.fixed_length_secure_compare(rehash, hash)
42
+ rescue ArgumentError
43
+ false
44
+ end
45
+ rescue Error
46
+ false
47
+ end
48
+
49
+ # Check if hash parameters match the given configuration.
50
+ #
51
+ # @param hash [String] encoded hash string
52
+ # @param options [Hash] expected parameters (n_log2:, r:, p:, t:, flags:)
53
+ # @return [Boolean]
54
+ def cost_matches?(hash, **options)
55
+ params = decode_params(hash)
56
+ return false unless params
57
+
58
+ opts = default_params.merge(options)
59
+ params[:n_log2] == opts[:n_log2] &&
60
+ params[:r] == opts[:r] &&
61
+ params[:p] == opts[:p] &&
62
+ params[:t] == opts[:t] &&
63
+ params[:flags] == opts[:flags]
64
+ end
65
+
66
+ # Returns the default parameter hash, derived from constants defined in C.
67
+ def default_params
68
+ DEFAULT_PARAMS
69
+ end
70
+ end
71
+
72
+ DEFAULT_PARAMS = {
73
+ n_log2: DEFAULT_N_LOG2,
74
+ r: DEFAULT_R,
75
+ p: DEFAULT_P,
76
+ t: DEFAULT_T,
77
+ flags: DEFAULT_FLAGS
78
+ }.freeze
79
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yescrypt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Suleyman Musayev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake-compiler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ description: Ruby C extension wrapping the yescrypt password hashing algorithm. yescrypt
70
+ is the default password hash in modern Linux distributions (glibc 2.36+). It extends
71
+ scrypt with pwxform for stronger time-memory tradeoff resistance. Supports YESCRYPT_WORM
72
+ (scrypt-compatible), YESCRYPT_RW, and YESCRYPT_DEFAULTS flavors.
73
+ email:
74
+ - slmusayev@gmail.com
75
+ executables: []
76
+ extensions:
77
+ - ext/yescrypt/extconf.rb
78
+ extra_rdoc_files: []
79
+ files:
80
+ - LICENSE
81
+ - README.md
82
+ - ext/yescrypt/extconf.rb
83
+ - ext/yescrypt/insecure_memzero.h
84
+ - ext/yescrypt/sha256.c
85
+ - ext/yescrypt/sha256.h
86
+ - ext/yescrypt/yescrypt-common.c
87
+ - ext/yescrypt/yescrypt-opt.c
88
+ - ext/yescrypt/yescrypt.h
89
+ - ext/yescrypt/yescrypt_ext.c
90
+ - lib/yescrypt.rb
91
+ - lib/yescrypt/version.rb
92
+ homepage: https://github.com/msuliq/yescrypt
93
+ licenses:
94
+ - MIT
95
+ metadata:
96
+ rubygems_mfa_required: 'true'
97
+ homepage_uri: https://github.com/msuliq/yescrypt
98
+ source_code_uri: https://github.com/msuliq/yescrypt
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: 2.7.2
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.4.19
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: 'yescrypt: a memory-hard password hashing function used in modern Linux.'
118
+ test_files: []