xcrypt 0.1.2-arm-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/xcrypt/arm-linux/libxcrypt.so +0 -0
- data/lib/xcrypt/ffi.rb +79 -0
- data/lib/xcrypt/version.rb +5 -0
- data/lib/xcrypt.rb +249 -0
- metadata +89 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 21eca2c864456dc81c260c68c09042df2d5977ab4b1e0a7fbc6ecd14a6932ef8
|
|
4
|
+
data.tar.gz: 3694326527500e11e5a9b67635bf2a394b6c07a7214b56e2c5569c4be464f662
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4bd2246605e4f54a9e6fbeb803676e32fb7bf7d8b17e7abe4f2278de58ad18c7864879e8dfff41941e33e18c3a80ac82a24c0edc048b684a54fdd15e5f500d82
|
|
7
|
+
data.tar.gz: 5cf3b11c7d3c5bbd9352b5d564a5d3c527e4ee00f944aa45df79fc11352461ed027f3f47e678ee486ee7b1f4854a9c5d685dfddae285b215676eb45d3014d3a8
|
|
Binary file
|
data/lib/xcrypt/ffi.rb
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
5
|
+
module XCrypt
|
|
6
|
+
# Low-level FFI bindings for libxcrypt.
|
|
7
|
+
#
|
|
8
|
+
# Consumers should use the high-level {XCrypt} module methods instead of
|
|
9
|
+
# calling into this module directly.
|
|
10
|
+
#
|
|
11
|
+
# The shared library loaded here is built from the libxcrypt submodule
|
|
12
|
+
# (ext/libxcrypt). Run <tt>bundle exec rake compile</tt> to build it
|
|
13
|
+
# before loading this gem.
|
|
14
|
+
module FFI
|
|
15
|
+
extend ::FFI::Library
|
|
16
|
+
|
|
17
|
+
# Size constants mirrored from <crypt.h>.
|
|
18
|
+
CRYPT_OUTPUT_SIZE = 384
|
|
19
|
+
CRYPT_MAX_PASSPHRASE_SIZE = 512
|
|
20
|
+
CRYPT_GENSALT_OUTPUT_SIZE = 192
|
|
21
|
+
CRYPT_DATA_RESERVED_SIZE = 767
|
|
22
|
+
CRYPT_DATA_INTERNAL_SIZE = 30_720
|
|
23
|
+
|
|
24
|
+
# sizeof(struct crypt_data) == 32768 bytes, as guaranteed by the header.
|
|
25
|
+
CRYPT_DATA_SIZE = 32_768
|
|
26
|
+
|
|
27
|
+
# crypt_checksalt(3) return codes.
|
|
28
|
+
CRYPT_SALT_OK = 0
|
|
29
|
+
CRYPT_SALT_INVALID = 1
|
|
30
|
+
CRYPT_SALT_METHOD_DISABLED = 2
|
|
31
|
+
CRYPT_SALT_METHOD_LEGACY = 3
|
|
32
|
+
CRYPT_SALT_TOO_CHEAP = 4
|
|
33
|
+
|
|
34
|
+
# Load the shared library built from the libxcrypt submodule.
|
|
35
|
+
# It lives in a platform-specific subdirectory next to this file.
|
|
36
|
+
begin
|
|
37
|
+
_ext = ::FFI::Platform.mac? ? "bundle" : "so"
|
|
38
|
+
_lib = File.expand_path(
|
|
39
|
+
"../#{::FFI::Platform::ARCH}-#{::FFI::Platform::OS}/libxcrypt.#{_ext}",
|
|
40
|
+
__FILE__
|
|
41
|
+
)
|
|
42
|
+
ffi_lib _lib
|
|
43
|
+
rescue LoadError
|
|
44
|
+
raise LoadError,
|
|
45
|
+
"XCrypt native extension not found. " \
|
|
46
|
+
"Build it with: rake compile"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# char *crypt_rn(const char *phrase, const char *setting,
|
|
50
|
+
# void *data, int size)
|
|
51
|
+
#
|
|
52
|
+
# Thread-safe variant that writes to a caller-supplied buffer. Returns
|
|
53
|
+
# NULL on error instead of a magic error string.
|
|
54
|
+
attach_function :crypt_rn, [:string, :string, :pointer, :int], :pointer
|
|
55
|
+
|
|
56
|
+
# char *crypt_gensalt_rn(const char *prefix, unsigned long count,
|
|
57
|
+
# const char *rbytes, int nrbytes,
|
|
58
|
+
# char *output, int output_size)
|
|
59
|
+
#
|
|
60
|
+
# Thread-safe salt generator that writes to a caller-supplied buffer.
|
|
61
|
+
# Pass NULL for rbytes to let the library obtain entropy from the OS.
|
|
62
|
+
attach_function :crypt_gensalt_rn,
|
|
63
|
+
[:string, :ulong, :pointer, :int, :pointer, :int],
|
|
64
|
+
:pointer
|
|
65
|
+
|
|
66
|
+
# int crypt_checksalt(const char *setting)
|
|
67
|
+
#
|
|
68
|
+
# Inspects a setting string and reports whether it is valid.
|
|
69
|
+
attach_function :crypt_checksalt, [:string], :int
|
|
70
|
+
|
|
71
|
+
# const char *crypt_preferred_method(void)
|
|
72
|
+
#
|
|
73
|
+
# Returns the prefix string of the library's preferred (strongest)
|
|
74
|
+
# hashing method.
|
|
75
|
+
attach_function :crypt_preferred_method, [], :string
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private_constant :FFI
|
|
79
|
+
end
|
data/lib/xcrypt.rb
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Top-level module providing a high-level Ruby interface to libxcrypt, a
|
|
4
|
+
# modern library for one-way hashing of passwords.
|
|
5
|
+
#
|
|
6
|
+
# All public methods are available directly on the module.
|
|
7
|
+
# The most common entry points are the algorithm-specific convenience methods
|
|
8
|
+
# ({yescrypt}, {bcrypt}, {sha512}, etc.) and {verify}.
|
|
9
|
+
#
|
|
10
|
+
# @example Hash a password with yescrypt (the strongest supported algorithm)
|
|
11
|
+
# hash = XCrypt.yescrypt("correct horse battery staple")
|
|
12
|
+
# XCrypt.verify("correct horse battery staple", hash) #=> true
|
|
13
|
+
#
|
|
14
|
+
# @example Hash with an explicit cost factor
|
|
15
|
+
# hash = XCrypt.bcrypt("hunter2", cost: 12)
|
|
16
|
+
#
|
|
17
|
+
# @example Use the generic interface
|
|
18
|
+
# hash = XCrypt.crypt("hunter2", algorithm: :sha512)
|
|
19
|
+
module XCrypt
|
|
20
|
+
require "xcrypt/version"
|
|
21
|
+
require "xcrypt/ffi"
|
|
22
|
+
|
|
23
|
+
# Raised when hashing or salt generation fails. Common causes include an
|
|
24
|
+
# unsupported algorithm, a malformed setting string, or a passphrase that
|
|
25
|
+
# exceeds {FFI::CRYPT_MAX_PASSPHRASE_SIZE} bytes.
|
|
26
|
+
Error ||= Class.new(StandardError)
|
|
27
|
+
|
|
28
|
+
# Maps each supported algorithm name to its setting-string prefix.
|
|
29
|
+
#
|
|
30
|
+
# The prefix is the leading characters of any hash produced by that
|
|
31
|
+
# algorithm and is used to identify the algorithm from an existing hash.
|
|
32
|
+
#
|
|
33
|
+
# @return [Hash{Symbol => String}]
|
|
34
|
+
ALGORITHMS = {
|
|
35
|
+
yescrypt: "$y$",
|
|
36
|
+
gost_yescrypt: "$gy$",
|
|
37
|
+
scrypt: "$7$",
|
|
38
|
+
bcrypt: "$2b$",
|
|
39
|
+
sha512: "$6$",
|
|
40
|
+
sha256: "$5$",
|
|
41
|
+
sha1: "$sha1$",
|
|
42
|
+
sun_md5: "$md5",
|
|
43
|
+
md5: "$1$",
|
|
44
|
+
bsdi_des: "_",
|
|
45
|
+
des: "",
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
PREFIXES = ALGORITHMS.invert.freeze
|
|
49
|
+
private_constant :ALGORITHMS, :PREFIXES
|
|
50
|
+
|
|
51
|
+
extend self
|
|
52
|
+
|
|
53
|
+
# @!method yescrypt(phrase, setting = nil, cost: nil)
|
|
54
|
+
# Hash +phrase+ using yescrypt, the strongest supported algorithm.
|
|
55
|
+
# @param phrase [String] the password to hash
|
|
56
|
+
# @param setting [String, nil] an existing hash or salt string to use as
|
|
57
|
+
# the setting; a new setting is generated automatically when +nil+
|
|
58
|
+
# @param cost [Integer, nil] work-factor override; uses the library
|
|
59
|
+
# default when +nil+
|
|
60
|
+
# @return [String] the hashed password
|
|
61
|
+
# @raise [ArgumentError] if +setting+ belongs to a different algorithm
|
|
62
|
+
# @raise [Error] if hashing fails
|
|
63
|
+
|
|
64
|
+
# @!method gost_yescrypt(phrase, setting = nil, cost: nil)
|
|
65
|
+
# Hash +phrase+ using GOST R 34.11-2012 combined with yescrypt.
|
|
66
|
+
# @param (see #yescrypt)
|
|
67
|
+
# @return (see #yescrypt)
|
|
68
|
+
# @raise (see #yescrypt)
|
|
69
|
+
|
|
70
|
+
# @!method scrypt(phrase, setting = nil, cost: nil)
|
|
71
|
+
# Hash +phrase+ using scrypt.
|
|
72
|
+
# @param (see #yescrypt)
|
|
73
|
+
# @return (see #yescrypt)
|
|
74
|
+
# @raise (see #yescrypt)
|
|
75
|
+
|
|
76
|
+
# @!method bcrypt(phrase, setting = nil, cost: nil)
|
|
77
|
+
# Hash +phrase+ using bcrypt (Blowfish-based password hashing).
|
|
78
|
+
# @param (see #yescrypt)
|
|
79
|
+
# @return (see #yescrypt)
|
|
80
|
+
# @raise (see #yescrypt)
|
|
81
|
+
|
|
82
|
+
# @!method sha512(phrase, setting = nil, cost: nil)
|
|
83
|
+
# Hash +phrase+ using SHA-512 crypt.
|
|
84
|
+
# @param (see #yescrypt)
|
|
85
|
+
# @return (see #yescrypt)
|
|
86
|
+
# @raise (see #yescrypt)
|
|
87
|
+
|
|
88
|
+
# @!method sha256(phrase, setting = nil, cost: nil)
|
|
89
|
+
# Hash +phrase+ using SHA-256 crypt.
|
|
90
|
+
# @param (see #yescrypt)
|
|
91
|
+
# @return (see #yescrypt)
|
|
92
|
+
# @raise (see #yescrypt)
|
|
93
|
+
|
|
94
|
+
# @!method sha1(phrase, setting = nil, cost: nil)
|
|
95
|
+
# Hash +phrase+ using HMAC-SHA1 NetBSD crypt.
|
|
96
|
+
# @param (see #yescrypt)
|
|
97
|
+
# @return (see #yescrypt)
|
|
98
|
+
# @raise (see #yescrypt)
|
|
99
|
+
|
|
100
|
+
# @!method sun_md5(phrase, setting = nil, cost: nil)
|
|
101
|
+
# Hash +phrase+ using SunMD5 (Solaris MD5 crypt).
|
|
102
|
+
# @param (see #yescrypt)
|
|
103
|
+
# @return (see #yescrypt)
|
|
104
|
+
# @raise (see #yescrypt)
|
|
105
|
+
|
|
106
|
+
# @!method md5(phrase, setting = nil, cost: nil)
|
|
107
|
+
# Hash +phrase+ using MD5 crypt.
|
|
108
|
+
# @param (see #yescrypt)
|
|
109
|
+
# @return (see #yescrypt)
|
|
110
|
+
# @raise (see #yescrypt)
|
|
111
|
+
|
|
112
|
+
# @!method bsdi_des(phrase, setting = nil, cost: nil)
|
|
113
|
+
# Hash +phrase+ using BSDi extended DES crypt.
|
|
114
|
+
# @param (see #yescrypt)
|
|
115
|
+
# @return (see #yescrypt)
|
|
116
|
+
# @raise (see #yescrypt)
|
|
117
|
+
|
|
118
|
+
# @!method des(phrase, setting = nil, cost: nil)
|
|
119
|
+
# Hash +phrase+ using traditional DES crypt.
|
|
120
|
+
# @param (see #yescrypt)
|
|
121
|
+
# @return (see #yescrypt)
|
|
122
|
+
# @raise (see #yescrypt)
|
|
123
|
+
|
|
124
|
+
ALGORITHMS.each_key do |algorithm|
|
|
125
|
+
define_method(algorithm) do |phrase, setting = nil, cost: nil|
|
|
126
|
+
if setting
|
|
127
|
+
setting_algorithm = detect_algorithm(setting)
|
|
128
|
+
if setting_algorithm != algorithm
|
|
129
|
+
raise ArgumentError, "setting algorithm #{setting_algorithm.inspect} does not match expected #{algorithm.inspect}"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
crypt(phrase, setting, algorithm:, cost:)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Returns the names of all supported algorithms.
|
|
137
|
+
#
|
|
138
|
+
# @return [Array<Symbol>] algorithm names in order from strongest to weakest
|
|
139
|
+
def algorithms = ALGORITHMS.keys
|
|
140
|
+
|
|
141
|
+
# Detects which algorithm produced a given setting or hash string by
|
|
142
|
+
# matching its leading prefix against {ALGORITHMS}.
|
|
143
|
+
#
|
|
144
|
+
# @param setting [String] a setting string or an existing password hash
|
|
145
|
+
# @return [Symbol, nil] the algorithm name, or +nil+ if the prefix is
|
|
146
|
+
# unrecognized
|
|
147
|
+
def detect_algorithm(setting) = PREFIXES[setting[/\A\$\w+\$?|_/].to_s]
|
|
148
|
+
|
|
149
|
+
# Hashes +phrase+ using libxcrypt's +crypt_rn+ function.
|
|
150
|
+
#
|
|
151
|
+
# When both +setting+ and +algorithm+ are omitted, a fresh setting is
|
|
152
|
+
# generated with the library's default algorithm. The result is always a
|
|
153
|
+
# self-describing string whose leading prefix identifies the algorithm and
|
|
154
|
+
# encodes the salt, making it safe to store directly.
|
|
155
|
+
#
|
|
156
|
+
# @param phrase [String] the password to hash
|
|
157
|
+
# @param setting [String, Symbol, nil] an existing hash or salt string, or
|
|
158
|
+
# an algorithm +Symbol+ as shorthand for passing only +algorithm:+;
|
|
159
|
+
# generates a fresh setting when +nil+
|
|
160
|
+
# @param algorithm [Symbol, nil] algorithm to use when generating a new
|
|
161
|
+
# setting; ignored when +setting+ is already a String
|
|
162
|
+
# @param cost [Integer, nil] work-factor override passed to
|
|
163
|
+
# {generate_setting}; uses the library default when +nil+
|
|
164
|
+
# @return [String] the hashed password
|
|
165
|
+
# @raise [Error] if +crypt_rn+ returns +NULL+, indicating an invalid
|
|
166
|
+
# setting or an unsupported algorithm
|
|
167
|
+
def crypt(phrase, setting = nil, algorithm: nil, cost: nil)
|
|
168
|
+
setting, algorithm = nil, setting if setting.is_a? Symbol
|
|
169
|
+
setting ||= generate_setting(algorithm, cost:)
|
|
170
|
+
data = ::FFI::MemoryPointer.new(:uint8, FFI::CRYPT_DATA_SIZE)
|
|
171
|
+
result_ptr = FFI.crypt_rn(phrase, setting, data, FFI::CRYPT_DATA_SIZE)
|
|
172
|
+
raise Error, "crypt failed: invalid setting or unsupported algorithm" if result_ptr.null?
|
|
173
|
+
result_ptr.read_string
|
|
174
|
+
ensure
|
|
175
|
+
data&.clear
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Verifies that +phrase+ matches a previously computed +hash+.
|
|
179
|
+
#
|
|
180
|
+
# Returns +false+ immediately for any hash value that would cause
|
|
181
|
+
# libxcrypt to return a magic failure token (strings beginning with +"*"+),
|
|
182
|
+
# or for empty or +nil+ input, guarding against invalid-hash oracle attacks.
|
|
183
|
+
# The final comparison is performed in constant time to prevent timing
|
|
184
|
+
# attacks.
|
|
185
|
+
#
|
|
186
|
+
# @param phrase [String] the candidate password
|
|
187
|
+
# @param hash [String, nil] the stored password hash to verify against
|
|
188
|
+
# @return [Boolean] +true+ if +phrase+ matches +hash+, +false+ otherwise
|
|
189
|
+
def verify(phrase, hash)
|
|
190
|
+
return false if hash.nil? || hash.empty? || hash.start_with?("*")
|
|
191
|
+
result = crypt(phrase, hash)
|
|
192
|
+
secure_compare(result, hash)
|
|
193
|
+
rescue Error
|
|
194
|
+
false
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Generates a fresh setting string suitable for passing to {crypt}.
|
|
198
|
+
#
|
|
199
|
+
# Delegates to libxcrypt's +crypt_gensalt_rn+, which draws entropy from
|
|
200
|
+
# the OS to produce the random salt component. When +algorithm+ is +nil+,
|
|
201
|
+
# the library selects its preferred (strongest) algorithm.
|
|
202
|
+
#
|
|
203
|
+
# @param algorithm [Symbol, nil] the desired algorithm; uses the library
|
|
204
|
+
# default when +nil+
|
|
205
|
+
# @param cost [Integer, nil] work-factor for the generated setting; a value
|
|
206
|
+
# of +0+ selects the library's own default cost
|
|
207
|
+
# @return [String] a setting string beginning with the algorithm prefix
|
|
208
|
+
# @raise [ArgumentError] if +algorithm+ is not a key in {ALGORITHMS}
|
|
209
|
+
# @raise [Error] if +crypt_gensalt_rn+ returns +NULL+
|
|
210
|
+
def generate_setting(algorithm = nil, cost: nil)
|
|
211
|
+
prefix = ALGORITHMS.fetch(algorithm) { raise ArgumentError, "unknown algorithm: #{algorithm.inspect}" } if algorithm
|
|
212
|
+
cost ||= 0
|
|
213
|
+
|
|
214
|
+
output = ::FFI::MemoryPointer.new(:char, FFI::CRYPT_GENSALT_OUTPUT_SIZE)
|
|
215
|
+
result_ptr = FFI.crypt_gensalt_rn(prefix, cost, nil, 0, output, FFI::CRYPT_GENSALT_OUTPUT_SIZE)
|
|
216
|
+
raise Error, "crypt_gensalt failed: unknown prefix or unsupported algorithm" if result_ptr.null?
|
|
217
|
+
|
|
218
|
+
result_ptr.read_string
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
private
|
|
222
|
+
|
|
223
|
+
# Compares two strings in constant time to prevent timing attacks.
|
|
224
|
+
#
|
|
225
|
+
# Pads or truncates +trusted+ to match +untrusted+'s byte length before
|
|
226
|
+
# comparing so that the number of loop iterations is always the same
|
|
227
|
+
# regardless of content. A separate length check at the end ensures that a
|
|
228
|
+
# length-padded match is still rejected.
|
|
229
|
+
#
|
|
230
|
+
# Uses {OpenSSL.fixed_length_secure_compare} when available (Ruby >= 2.7
|
|
231
|
+
# with openssl >= 2.2); otherwise falls back to a pure-Ruby XOR loop.
|
|
232
|
+
#
|
|
233
|
+
# @param trusted [String] the known-good value (e.g., the output of {crypt})
|
|
234
|
+
# @param untrusted [String] the value supplied by the caller
|
|
235
|
+
# @return [Boolean] +true+ only when both strings are identical
|
|
236
|
+
def secure_compare(trusted, untrusted)
|
|
237
|
+
return false unless trusted.respond_to? :to_str and trusted = trusted.to_str.b
|
|
238
|
+
return false unless untrusted.respond_to? :to_str and untrusted = untrusted.to_str.b
|
|
239
|
+
|
|
240
|
+
# avoid ability for attacker to guess length of string by timing attack
|
|
241
|
+
comparable = trusted[0, untrusted.bytesize].ljust(untrusted.bytesize, "\0".b)
|
|
242
|
+
|
|
243
|
+
result = defined?(OpenSSL.fixed_length_secure_compare) ?
|
|
244
|
+
OpenSSL.fixed_length_secure_compare(comparable, untrusted) :
|
|
245
|
+
comparable.each_byte.zip(untrusted.each_byte).reduce(0) { |acc, (a, b)| acc | (a ^ b) }.zero?
|
|
246
|
+
|
|
247
|
+
trusted.bytesize == untrusted.bytesize and result
|
|
248
|
+
end
|
|
249
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: xcrypt
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.2
|
|
5
|
+
platform: arm-linux
|
|
6
|
+
authors:
|
|
7
|
+
- Konstantin Haase
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ffi
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rake-compiler-dock
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
description: |
|
|
55
|
+
Ruby FFI bindings for libxcrypt, a modern library for one-way hashing of
|
|
56
|
+
passwords. Supports yescrypt, bcrypt, SHA-512, SHA-256, and other
|
|
57
|
+
algorithms provided by the bundled libxcrypt source (ext/libxcrypt).
|
|
58
|
+
email: ruby-xcrypt@rkh.im
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- lib/xcrypt.rb
|
|
64
|
+
- lib/xcrypt/arm-linux/libxcrypt.so
|
|
65
|
+
- lib/xcrypt/ffi.rb
|
|
66
|
+
- lib/xcrypt/version.rb
|
|
67
|
+
homepage: https://github.com/rkh/ruby-xcrypt
|
|
68
|
+
licenses:
|
|
69
|
+
- MIT
|
|
70
|
+
metadata:
|
|
71
|
+
source_code_uri: https://github.com/rkh/ruby-xcrypt
|
|
72
|
+
rdoc_options: []
|
|
73
|
+
require_paths:
|
|
74
|
+
- lib
|
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: '3.0'
|
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '0'
|
|
85
|
+
requirements: []
|
|
86
|
+
rubygems_version: 4.0.3
|
|
87
|
+
specification_version: 4
|
|
88
|
+
summary: Ruby FFI bindings for libxcrypt
|
|
89
|
+
test_files: []
|