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.
- checksums.yaml +7 -0
- data/LICENSE +42 -0
- data/README.md +325 -0
- data/ext/yescrypt/extconf.rb +7 -0
- data/ext/yescrypt/insecure_memzero.h +34 -0
- data/ext/yescrypt/sha256.c +256 -0
- data/ext/yescrypt/sha256.h +43 -0
- data/ext/yescrypt/yescrypt-common.c +381 -0
- data/ext/yescrypt/yescrypt-opt.c +358 -0
- data/ext/yescrypt/yescrypt.h +124 -0
- data/ext/yescrypt/yescrypt_ext.c +407 -0
- data/lib/yescrypt/version.rb +5 -0
- data/lib/yescrypt.rb +79 -0
- metadata +118 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1ba0d0c9b88163285669166982ad7cb47cfcd51ad07935b30d5215b51a8c2c8b
|
|
4
|
+
data.tar.gz: 14f1b653e6c920e6c0f1e973b877c7d146c68006018bddbfcf5728ac33b6aeda
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 412ec3b20fd3180dfac8fe5e8b5851d645027055f9f9752056059f1fb6443febec4969498b4061500e4f2664503ce41c2bebdc4590c966a239422c8e565054b8
|
|
7
|
+
data.tar.gz: 45314045bb1f25679b68336b17aa4a59e9bfa5725ef8ca5c9548bfed2d91d9b4330f1deeb34b596ee70579c98e277334bc48268b406f96865552891e33bd4e76
|
data/LICENSE
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Suleyman Musayev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
The yescrypt C implementation is based on work by:
|
|
26
|
+
|
|
27
|
+
Copyright (c) 2009 Colin Percival (scrypt)
|
|
28
|
+
Copyright (c) 2013-2021 Alexander Peslyak (yescrypt)
|
|
29
|
+
|
|
30
|
+
Redistribution and use in source and binary forms, with or without
|
|
31
|
+
modification, are permitted provided that the following conditions
|
|
32
|
+
are met:
|
|
33
|
+
1. Redistributions of source code must retain the above copyright
|
|
34
|
+
notice, this list of conditions and the following disclaimer.
|
|
35
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
36
|
+
notice, this list of conditions and the following disclaimer in the
|
|
37
|
+
documentation and/or other materials provided with the distribution.
|
|
38
|
+
|
|
39
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
40
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
41
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
42
|
+
ARE DISCLAIMED.
|
data/README.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# yescrypt
|
|
2
|
+
|
|
3
|
+
A Ruby C extension wrapping the [yescrypt](https://www.openwall.com/yescrypt/) password hashing algorithm.
|
|
4
|
+
|
|
5
|
+
yescrypt is the default password hash in modern Linux distributions (glibc 2.36+). It extends [scrypt](https://www.tarsnap.com/scrypt.html) with pwxform for stronger time-memory tradeoff resistance, making it significantly more costly for attackers using GPUs or custom hardware.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- High-level `create` / `verify` API modeled after bcrypt-ruby
|
|
10
|
+
- Low-level `kdf` access for custom use cases
|
|
11
|
+
- Constant-time hash comparison via `OpenSSL.fixed_length_secure_compare`
|
|
12
|
+
- Thread-safe: releases the GVL during hashing so other Ruby threads can run
|
|
13
|
+
- GC-compaction safe with pinned string references
|
|
14
|
+
- Sensitive buffers are zeroed after use
|
|
15
|
+
- Supports all three yescrypt flavors: WORM, RW, and DEFAULTS
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Ruby >= 2.7.2
|
|
20
|
+
- A C99-compatible compiler (gcc, clang, etc.)
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Add to your Gemfile:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
gem "yescrypt"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then run:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
bundle install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or install directly:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
gem install yescrypt
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The C extension compiles automatically during installation. No external libraries are needed -- yescrypt, SHA-256, HMAC, and PBKDF2 are all bundled.
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
require "yescrypt"
|
|
48
|
+
|
|
49
|
+
# Hash a password (uses secure defaults: N=2^12, r=32, p=1)
|
|
50
|
+
hash = Yescrypt.create("my secret password")
|
|
51
|
+
# => "$y$j..."
|
|
52
|
+
|
|
53
|
+
# Verify a password against a stored hash
|
|
54
|
+
Yescrypt.verify("my secret password", hash) # => true
|
|
55
|
+
Yescrypt.verify("wrong password", hash) # => false
|
|
56
|
+
|
|
57
|
+
# Check if a hash needs rehashing (e.g., after changing cost parameters)
|
|
58
|
+
Yescrypt.cost_matches?(hash) # => true (matches current defaults)
|
|
59
|
+
Yescrypt.cost_matches?(hash, n_log2: 14, r: 32, p: 1) # => false
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API Reference
|
|
63
|
+
|
|
64
|
+
### `Yescrypt.create(password, **options)`
|
|
65
|
+
|
|
66
|
+
Hash a password and return an encoded string suitable for storage.
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
hash = Yescrypt.create("password")
|
|
70
|
+
hash = Yescrypt.create("password", n_log2: 14, r: 32, p: 1, t: 0, flags: Yescrypt::DEFAULTS)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Parameters:**
|
|
74
|
+
|
|
75
|
+
| Parameter | Type | Default | Description |
|
|
76
|
+
|-----------|---------|--------------------------|------------------------------------------------|
|
|
77
|
+
| password | String | *(required)* | The plaintext password |
|
|
78
|
+
| n_log2: | Integer | `12` | Log2 of the block count (memory cost). N = 2^n_log2 |
|
|
79
|
+
| r: | Integer | `32` | Block size parameter (affects memory per block) |
|
|
80
|
+
| p: | Integer | `1` | Parallelism parameter |
|
|
81
|
+
| t: | Integer | `0` | Time parameter (extra mixing rounds) |
|
|
82
|
+
| flags: | Integer | `Yescrypt::DEFAULTS` (2) | Flavor flags (see [Flavors](#flavors)) |
|
|
83
|
+
|
|
84
|
+
**Returns:** A frozen, US-ASCII encoded string starting with `$y$`.
|
|
85
|
+
|
|
86
|
+
A random 16-byte salt is generated automatically using `SecureRandom`.
|
|
87
|
+
|
|
88
|
+
### `Yescrypt.verify(password, hash)`
|
|
89
|
+
|
|
90
|
+
Verify a plaintext password against an encoded hash.
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
Yescrypt.verify("password", hash) # => true or false
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Returns `false` for any invalid input (wrong types, malformed hash, wrong password) rather than raising exceptions. Uses constant-time comparison to prevent timing attacks.
|
|
97
|
+
|
|
98
|
+
### `Yescrypt.cost_matches?(hash, **options)`
|
|
99
|
+
|
|
100
|
+
Check whether an existing hash was generated with the given parameters. Useful for determining if a password needs rehashing after a cost parameter change.
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
if Yescrypt.verify(password, stored_hash)
|
|
104
|
+
unless Yescrypt.cost_matches?(stored_hash, n_log2: 14)
|
|
105
|
+
# Rehash with new cost parameters
|
|
106
|
+
new_hash = Yescrypt.create(password, n_log2: 14)
|
|
107
|
+
save_hash(new_hash)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Parameters:** Same keyword arguments as `create`. Omitted parameters default to the current defaults.
|
|
113
|
+
|
|
114
|
+
**Returns:** `true` if all parameters (n_log2, r, p, t, flags) match, `false` otherwise.
|
|
115
|
+
|
|
116
|
+
### `Yescrypt.default_params`
|
|
117
|
+
|
|
118
|
+
Returns a frozen hash of the current default parameters:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
Yescrypt.default_params
|
|
122
|
+
# => { n_log2: 12, r: 32, p: 1, t: 0, flags: 2 }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `Yescrypt.decode_params(hash)`
|
|
126
|
+
|
|
127
|
+
Parse the parameters from an encoded yescrypt hash string.
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
params = Yescrypt.decode_params(hash)
|
|
131
|
+
# => { n_log2: 12, r: 32, p: 1, t: 0, flags: 2 }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Returns:** A frozen hash with `:n_log2`, `:r`, `:p`, `:t`, `:flags` keys, or `nil` if the string is not a valid yescrypt hash.
|
|
135
|
+
|
|
136
|
+
### `Yescrypt.gensalt(**options)`
|
|
137
|
+
|
|
138
|
+
Generate a setting/salt string for low-level use. You typically don't need this directly -- `create` calls it internally.
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
salt = SecureRandom.random_bytes(16)
|
|
142
|
+
setting = Yescrypt.gensalt(n_log2: 12, r: 32, p: 1, t: 0, flags: Yescrypt::DEFAULTS, salt: salt)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The `salt:` keyword is required and must be a binary String (typically 16 random bytes).
|
|
146
|
+
|
|
147
|
+
### `Yescrypt.kdf(password, salt, **options)`
|
|
148
|
+
|
|
149
|
+
Low-level KDF function that returns raw hash bytes. Unlike `create`, this gives you direct control over all parameters and returns unencoded binary output.
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
raw = Yescrypt.kdf("password", "salt" * 4,
|
|
153
|
+
n: 4096, r: 32, p: 1, flags: Yescrypt::DEFAULTS, t: 0, outlen: 32)
|
|
154
|
+
raw.bytesize # => 32
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Parameters:**
|
|
158
|
+
|
|
159
|
+
| Parameter | Type | Default | Description |
|
|
160
|
+
|-----------|---------|--------------|------------------------------------------|
|
|
161
|
+
| password | String | *(required)* | The plaintext password |
|
|
162
|
+
| salt | String | *(required)* | Salt bytes (any length) |
|
|
163
|
+
| n: | Integer | *(required)* | Block count (must be a power of 2, >= 2) |
|
|
164
|
+
| r: | Integer | *(required)* | Block size parameter |
|
|
165
|
+
| p: | Integer | *(required)* | Parallelism parameter |
|
|
166
|
+
| flags: | Integer | *(required)* | Flavor flags |
|
|
167
|
+
| t: | Integer | `0` | Time parameter |
|
|
168
|
+
| outlen: | Integer | `32` | Output length in bytes (1..1024) |
|
|
169
|
+
|
|
170
|
+
**Returns:** A binary String of `outlen` bytes.
|
|
171
|
+
|
|
172
|
+
Note: `n` here is the actual block count (e.g., `4096`), not the log2 value used in `create` (where you'd use `n_log2: 12`).
|
|
173
|
+
|
|
174
|
+
## Flavors
|
|
175
|
+
|
|
176
|
+
yescrypt supports three operational flavors:
|
|
177
|
+
|
|
178
|
+
| Constant | Value | Description |
|
|
179
|
+
|-----------------------|-------|-------------------------------------------------------------------------|
|
|
180
|
+
| `Yescrypt::WORM` | 0 | Classic scrypt-compatible mode. Read-only memory access pattern. |
|
|
181
|
+
| `Yescrypt::RW` | 1 | yescrypt with read-write memory access. Stronger against TMTO attacks. |
|
|
182
|
+
| `Yescrypt::DEFAULTS` | 2 | Recommended mode. Includes RW access plus pwxform strengthening. |
|
|
183
|
+
|
|
184
|
+
`DEFAULTS` is recommended for new applications. `WORM` is useful if you need scrypt compatibility.
|
|
185
|
+
|
|
186
|
+
## Choosing Parameters
|
|
187
|
+
|
|
188
|
+
The cost parameters control the trade-off between security and performance:
|
|
189
|
+
|
|
190
|
+
- **n_log2** (memory cost): Each increment doubles memory usage and time. Start with `12` (4096 blocks) and increase if your server can handle it. `14` (16384 blocks) is a good choice for higher security.
|
|
191
|
+
- **r** (block size): Controls memory per block (128 * r bytes). The default `32` (4 KB per block) is well-suited for modern CPUs. Increasing `r` increases memory bandwidth requirements.
|
|
192
|
+
- **p** (parallelism): Number of independent mixing operations. Keep at `1` unless you specifically need parallel computation. Increasing `p` scales CPU time linearly without increasing memory.
|
|
193
|
+
- **t** (time): Extra mixing rounds. Each increment of `t` adds N more mixing iterations. Keep at `0` unless you want to increase CPU time without increasing memory.
|
|
194
|
+
|
|
195
|
+
### Memory usage
|
|
196
|
+
|
|
197
|
+
Approximate memory per hash operation: `128 * r * N` bytes, where `N = 2^n_log2`.
|
|
198
|
+
|
|
199
|
+
| n_log2 | r | Memory |
|
|
200
|
+
|--------|-----|------------|
|
|
201
|
+
| 10 | 8 | 1 MB |
|
|
202
|
+
| 12 | 32 | 16 MB |
|
|
203
|
+
| 14 | 32 | 64 MB |
|
|
204
|
+
| 16 | 32 | 256 MB |
|
|
205
|
+
|
|
206
|
+
### Benchmarking
|
|
207
|
+
|
|
208
|
+
Test different parameters to find the right trade-off for your application:
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
require "benchmark"
|
|
212
|
+
|
|
213
|
+
[10, 12, 14].each do |n|
|
|
214
|
+
time = Benchmark.realtime { Yescrypt.create("test", n_log2: n, r: 32, p: 1) }
|
|
215
|
+
puts "n_log2=#{n}: #{(time * 1000).round}ms"
|
|
216
|
+
end
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Thread Safety
|
|
220
|
+
|
|
221
|
+
yescrypt releases the Ruby GVL (Global VM Lock) during the CPU-intensive hashing computation. This means other Ruby threads can execute while a hash is being computed:
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
threads = 4.times.map do |i|
|
|
225
|
+
Thread.new { Yescrypt.create("password_#{i}", n_log2: 4, r: 1, p: 1) }
|
|
226
|
+
end
|
|
227
|
+
hashes = threads.map(&:value) # all 4 run concurrently
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Each call allocates its own working memory, so there is no shared mutable state between threads.
|
|
231
|
+
|
|
232
|
+
## Hash Format
|
|
233
|
+
|
|
234
|
+
The encoded hash string follows the crypt(3) modular format:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
$y$j<params>$<salt>$<hash>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
- `$y$` -- yescrypt identifier prefix
|
|
241
|
+
- `j` -- flavor indicator
|
|
242
|
+
- `<params>` -- base64-encoded N_log2, r, p, t, and flags
|
|
243
|
+
- `<salt>` -- base64-encoded salt
|
|
244
|
+
- `<hash>` -- base64-encoded 256-bit derived key
|
|
245
|
+
|
|
246
|
+
Example:
|
|
247
|
+
```
|
|
248
|
+
$y$jD5.7C....$aBcDeFgHiJkLmNoPqR..$xYzAbCdEfGhIjKlMnOpQrStUvWxYz01234567890AB
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Error Handling
|
|
252
|
+
|
|
253
|
+
The gem defines a single exception class:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
Yescrypt::Error < StandardError
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Raised by low-level functions (`kdf`, `gensalt`, `_hash_password`) when the C library reports a failure (e.g., memory allocation failure, invalid setting string).
|
|
260
|
+
|
|
261
|
+
The high-level `verify` method never raises -- it returns `false` on any error. The high-level `create` method raises `TypeError` if the password is not a String, and may raise `Yescrypt::Error` on internal failures.
|
|
262
|
+
|
|
263
|
+
## Comparison with Other Password Hashing Gems
|
|
264
|
+
|
|
265
|
+
| Feature | yescrypt | bcrypt-ruby | scrypt |
|
|
266
|
+
|------------------------|--------------------|--------------------|--------------------|
|
|
267
|
+
| Memory-hard | Yes | No | Yes |
|
|
268
|
+
| TMTO resistance | Strong (pwxform) | N/A | Moderate |
|
|
269
|
+
| Linux default (2024+) | Yes | No | No |
|
|
270
|
+
| GVL released | Yes | Yes | Yes |
|
|
271
|
+
| Constant-time verify | Yes | Yes | Yes |
|
|
272
|
+
|
|
273
|
+
## Development
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Clone the repository
|
|
277
|
+
git clone https://github.com/msuliq/yescrypt.git
|
|
278
|
+
cd yescrypt
|
|
279
|
+
|
|
280
|
+
# Install dependencies
|
|
281
|
+
bundle install
|
|
282
|
+
|
|
283
|
+
# Compile the C extension
|
|
284
|
+
bundle exec rake compile
|
|
285
|
+
|
|
286
|
+
# Run tests
|
|
287
|
+
bundle exec rake test
|
|
288
|
+
|
|
289
|
+
# Run both (compile + test)
|
|
290
|
+
bundle exec rake
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Project Structure
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
yescrypt/
|
|
297
|
+
ext/yescrypt/ # C extension source
|
|
298
|
+
extconf.rb # Build configuration
|
|
299
|
+
yescrypt_ext.c # Ruby/C bridge
|
|
300
|
+
yescrypt-opt.c # Core yescrypt algorithm (smix, salsa20, pwxform)
|
|
301
|
+
yescrypt-common.c # Encoding, decoding, salt generation, yescrypt_r
|
|
302
|
+
yescrypt.h # Public C API header
|
|
303
|
+
sha256.c # SHA-256 / HMAC / PBKDF2 implementation
|
|
304
|
+
sha256.h # SHA-256 header
|
|
305
|
+
insecure_memzero.h # Secure memory zeroing
|
|
306
|
+
lib/
|
|
307
|
+
yescrypt.rb # High-level Ruby API (create, verify, cost_matches?)
|
|
308
|
+
yescrypt/version.rb # Version constant
|
|
309
|
+
test/
|
|
310
|
+
yescrypt_test.rb # Minitest test suite
|
|
311
|
+
test_helper.rb # Test setup
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## License
|
|
315
|
+
|
|
316
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
317
|
+
|
|
318
|
+
The bundled yescrypt C implementation is based on work by Colin Percival and Alexander Peslyak, used under a permissive BSD-style license.
|
|
319
|
+
|
|
320
|
+
## References
|
|
321
|
+
|
|
322
|
+
- [yescrypt - password hashing competition](https://www.openwall.com/yescrypt/)
|
|
323
|
+
- [yescrypt specification (PDF)](https://www.openwall.com/yescrypt/yescrypt-v2.pdf)
|
|
324
|
+
- [scrypt: A new key derivation function](https://www.tarsnap.com/scrypt.html) by Colin Percival
|
|
325
|
+
- [glibc yescrypt support (2.36+)](https://sourceware.org/glibc/wiki/Release/2.36)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2014 Alexander Peslyak
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6
|
+
* modification, are permitted.
|
|
7
|
+
*
|
|
8
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
9
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
10
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
11
|
+
* ARE DISCLAIMED.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
#ifndef INSECURE_MEMZERO_H
|
|
15
|
+
#define INSECURE_MEMZERO_H
|
|
16
|
+
|
|
17
|
+
#include <string.h>
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* Securely zero memory - uses volatile pointer writes to prevent
|
|
21
|
+
* the compiler from optimizing away the clearing operation.
|
|
22
|
+
*
|
|
23
|
+
* Note: explicit_bzero/memset_s would be preferred but are not
|
|
24
|
+
* reliably available across platforms under strict C99 mode.
|
|
25
|
+
*/
|
|
26
|
+
static void insecure_memzero(void *buf, size_t len)
|
|
27
|
+
{
|
|
28
|
+
volatile unsigned char *p = (volatile unsigned char *)buf;
|
|
29
|
+
while (len--) {
|
|
30
|
+
*p++ = 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#endif /* INSECURE_MEMZERO_H */
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SHA-256 / HMAC-SHA256 / PBKDF2-HMAC-SHA256 implementation.
|
|
3
|
+
* Based on the public domain implementation by Colin Percival.
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2009 Colin Percival
|
|
6
|
+
* Copyright (c) 2013-2021 Alexander Peslyak
|
|
7
|
+
*
|
|
8
|
+
* Redistribution and use in source and binary forms, with or without
|
|
9
|
+
* modification, are permitted.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include <string.h>
|
|
13
|
+
#include <stdint.h>
|
|
14
|
+
|
|
15
|
+
#include "sha256.h"
|
|
16
|
+
#include "insecure_memzero.h"
|
|
17
|
+
|
|
18
|
+
static inline uint32_t be32dec(const void *pp)
|
|
19
|
+
{
|
|
20
|
+
const uint8_t *p = (const uint8_t *)pp;
|
|
21
|
+
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) |
|
|
22
|
+
((uint32_t)p[2] << 8) | (uint32_t)p[3];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static inline void be32enc(void *pp, uint32_t x)
|
|
26
|
+
{
|
|
27
|
+
uint8_t *p = (uint8_t *)pp;
|
|
28
|
+
p[0] = (x >> 24) & 0xff;
|
|
29
|
+
p[1] = (x >> 16) & 0xff;
|
|
30
|
+
p[2] = (x >> 8) & 0xff;
|
|
31
|
+
p[3] = x & 0xff;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static inline void be64enc(void *pp, uint64_t x)
|
|
35
|
+
{
|
|
36
|
+
uint8_t *p = (uint8_t *)pp;
|
|
37
|
+
p[0] = (x >> 56) & 0xff;
|
|
38
|
+
p[1] = (x >> 48) & 0xff;
|
|
39
|
+
p[2] = (x >> 40) & 0xff;
|
|
40
|
+
p[3] = (x >> 32) & 0xff;
|
|
41
|
+
p[4] = (x >> 24) & 0xff;
|
|
42
|
+
p[5] = (x >> 16) & 0xff;
|
|
43
|
+
p[6] = (x >> 8) & 0xff;
|
|
44
|
+
p[7] = x & 0xff;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#define ROR32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
|
|
48
|
+
#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
|
49
|
+
#define Maj(x, y, z) (((x) & (y)) | ((z) & ((x) | (y))))
|
|
50
|
+
#define S0(x) (ROR32(x, 2) ^ ROR32(x, 13) ^ ROR32(x, 22))
|
|
51
|
+
#define S1(x) (ROR32(x, 6) ^ ROR32(x, 11) ^ ROR32(x, 25))
|
|
52
|
+
#define s0(x) (ROR32(x, 7) ^ ROR32(x, 18) ^ ((x) >> 3))
|
|
53
|
+
#define s1(x) (ROR32(x, 17) ^ ROR32(x, 19) ^ ((x) >> 10))
|
|
54
|
+
|
|
55
|
+
#define RND(a, b, c, d, e, f, g, h, k) \
|
|
56
|
+
h += S1(e) + Ch(e, f, g) + k; \
|
|
57
|
+
d += h; \
|
|
58
|
+
h += S0(a) + Maj(a, b, c);
|
|
59
|
+
|
|
60
|
+
#define RNDr(S, W, i, ii) \
|
|
61
|
+
RND(S[(64 - i) % 8], S[(65 - i) % 8], S[(66 - i) % 8], \
|
|
62
|
+
S[(67 - i) % 8], S[(68 - i) % 8], S[(69 - i) % 8], \
|
|
63
|
+
S[(70 - i) % 8], S[(71 - i) % 8], \
|
|
64
|
+
W[i + ii] + K[i + ii])
|
|
65
|
+
|
|
66
|
+
static const uint32_t K[64] = {
|
|
67
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
|
68
|
+
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
69
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
70
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
71
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
|
72
|
+
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
73
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
|
74
|
+
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
75
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
76
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
77
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
|
78
|
+
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
79
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
|
80
|
+
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
81
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
82
|
+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
static void SHA256_Transform(uint32_t state[8], const uint8_t block[64])
|
|
86
|
+
{
|
|
87
|
+
uint32_t W[64];
|
|
88
|
+
uint32_t S[8];
|
|
89
|
+
int i;
|
|
90
|
+
|
|
91
|
+
for (i = 0; i < 16; i++)
|
|
92
|
+
W[i] = be32dec(&block[i * 4]);
|
|
93
|
+
for (i = 16; i < 64; i++)
|
|
94
|
+
W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
|
|
95
|
+
|
|
96
|
+
memcpy(S, state, sizeof(S));
|
|
97
|
+
|
|
98
|
+
for (i = 0; i < 64; i += 8) {
|
|
99
|
+
RNDr(S, W, 0, i);
|
|
100
|
+
RNDr(S, W, 1, i);
|
|
101
|
+
RNDr(S, W, 2, i);
|
|
102
|
+
RNDr(S, W, 3, i);
|
|
103
|
+
RNDr(S, W, 4, i);
|
|
104
|
+
RNDr(S, W, 5, i);
|
|
105
|
+
RNDr(S, W, 6, i);
|
|
106
|
+
RNDr(S, W, 7, i);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (i = 0; i < 8; i++)
|
|
110
|
+
state[i] += S[i];
|
|
111
|
+
|
|
112
|
+
insecure_memzero(W, sizeof(W));
|
|
113
|
+
insecure_memzero(S, sizeof(S));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
void SHA256_Init(SHA256_CTX *ctx)
|
|
117
|
+
{
|
|
118
|
+
ctx->count = 0;
|
|
119
|
+
ctx->state[0] = 0x6a09e667;
|
|
120
|
+
ctx->state[1] = 0xbb67ae85;
|
|
121
|
+
ctx->state[2] = 0x3c6ef372;
|
|
122
|
+
ctx->state[3] = 0xa54ff53a;
|
|
123
|
+
ctx->state[4] = 0x510e527f;
|
|
124
|
+
ctx->state[5] = 0x9b05688c;
|
|
125
|
+
ctx->state[6] = 0x1f83d9ab;
|
|
126
|
+
ctx->state[7] = 0x5be0cd19;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void SHA256_Update(SHA256_CTX *ctx, const void *in, size_t len)
|
|
130
|
+
{
|
|
131
|
+
uint64_t bitcount = ctx->count;
|
|
132
|
+
size_t r = (bitcount >> 3) & 0x3f;
|
|
133
|
+
const uint8_t *src = (const uint8_t *)in;
|
|
134
|
+
|
|
135
|
+
ctx->count = bitcount + ((uint64_t)len << 3);
|
|
136
|
+
|
|
137
|
+
if (len < 64 - r) {
|
|
138
|
+
memcpy(&ctx->buf[r], src, len);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
memcpy(&ctx->buf[r], src, 64 - r);
|
|
143
|
+
SHA256_Transform(ctx->state, ctx->buf);
|
|
144
|
+
src += 64 - r;
|
|
145
|
+
len -= 64 - r;
|
|
146
|
+
|
|
147
|
+
while (len >= 64) {
|
|
148
|
+
SHA256_Transform(ctx->state, src);
|
|
149
|
+
src += 64;
|
|
150
|
+
len -= 64;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
memcpy(ctx->buf, src, len);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
void SHA256_Final(uint8_t digest[32], SHA256_CTX *ctx)
|
|
157
|
+
{
|
|
158
|
+
uint8_t len[8];
|
|
159
|
+
|
|
160
|
+
be64enc(len, ctx->count);
|
|
161
|
+
|
|
162
|
+
SHA256_Update(ctx, "\x80", 1);
|
|
163
|
+
while ((ctx->count >> 3) % 64 != 56)
|
|
164
|
+
SHA256_Update(ctx, "\0", 1);
|
|
165
|
+
SHA256_Update(ctx, len, 8);
|
|
166
|
+
|
|
167
|
+
for (int i = 0; i < 8; i++)
|
|
168
|
+
be32enc(&digest[i * 4], ctx->state[i]);
|
|
169
|
+
|
|
170
|
+
insecure_memzero(ctx, sizeof(*ctx));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* HMAC-SHA256 */
|
|
174
|
+
void HMAC_SHA256_Init(HMAC_SHA256_CTX *ctx, const void *key, size_t keylen)
|
|
175
|
+
{
|
|
176
|
+
uint8_t khash[32];
|
|
177
|
+
uint8_t pad[64];
|
|
178
|
+
size_t i;
|
|
179
|
+
|
|
180
|
+
if (keylen > 64) {
|
|
181
|
+
SHA256_CTX shactx;
|
|
182
|
+
SHA256_Init(&shactx);
|
|
183
|
+
SHA256_Update(&shactx, key, keylen);
|
|
184
|
+
SHA256_Final(khash, &shactx);
|
|
185
|
+
key = khash;
|
|
186
|
+
keylen = 32;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
SHA256_Init(&ctx->ictx);
|
|
190
|
+
memset(pad, 0x36, 64);
|
|
191
|
+
for (i = 0; i < keylen; i++)
|
|
192
|
+
pad[i] ^= ((const uint8_t *)key)[i];
|
|
193
|
+
SHA256_Update(&ctx->ictx, pad, 64);
|
|
194
|
+
|
|
195
|
+
SHA256_Init(&ctx->octx);
|
|
196
|
+
memset(pad, 0x5c, 64);
|
|
197
|
+
for (i = 0; i < keylen; i++)
|
|
198
|
+
pad[i] ^= ((const uint8_t *)key)[i];
|
|
199
|
+
SHA256_Update(&ctx->octx, pad, 64);
|
|
200
|
+
|
|
201
|
+
insecure_memzero(khash, sizeof(khash));
|
|
202
|
+
insecure_memzero(pad, sizeof(pad));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
void HMAC_SHA256_Update(HMAC_SHA256_CTX *ctx, const void *in, size_t len)
|
|
206
|
+
{
|
|
207
|
+
SHA256_Update(&ctx->ictx, in, len);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
void HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX *ctx)
|
|
211
|
+
{
|
|
212
|
+
uint8_t ihash[32];
|
|
213
|
+
SHA256_Final(ihash, &ctx->ictx);
|
|
214
|
+
SHA256_Update(&ctx->octx, ihash, 32);
|
|
215
|
+
SHA256_Final(digest, &ctx->octx);
|
|
216
|
+
insecure_memzero(ihash, sizeof(ihash));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* PBKDF2-HMAC-SHA256 */
|
|
220
|
+
void PBKDF2_SHA256(const uint8_t *passwd, size_t passwdlen,
|
|
221
|
+
const uint8_t *salt, size_t saltlen,
|
|
222
|
+
uint64_t c, uint8_t *buf, size_t dkLen)
|
|
223
|
+
{
|
|
224
|
+
HMAC_SHA256_CTX PShctx, hctx;
|
|
225
|
+
uint8_t U[32], T[32];
|
|
226
|
+
uint8_t ivec[4];
|
|
227
|
+
size_t i, j, k, clen;
|
|
228
|
+
|
|
229
|
+
HMAC_SHA256_Init(&PShctx, passwd, passwdlen);
|
|
230
|
+
HMAC_SHA256_Update(&PShctx, salt, saltlen);
|
|
231
|
+
|
|
232
|
+
for (i = 0; i * 32 < dkLen; i++) {
|
|
233
|
+
be32enc(ivec, (uint32_t)(i + 1));
|
|
234
|
+
memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX));
|
|
235
|
+
HMAC_SHA256_Update(&hctx, ivec, 4);
|
|
236
|
+
HMAC_SHA256_Final(U, &hctx);
|
|
237
|
+
memcpy(T, U, 32);
|
|
238
|
+
|
|
239
|
+
for (j = 2; j <= c; j++) {
|
|
240
|
+
HMAC_SHA256_Init(&hctx, passwd, passwdlen);
|
|
241
|
+
HMAC_SHA256_Update(&hctx, U, 32);
|
|
242
|
+
HMAC_SHA256_Final(U, &hctx);
|
|
243
|
+
for (k = 0; k < 32; k++)
|
|
244
|
+
T[k] ^= U[k];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
clen = dkLen - i * 32;
|
|
248
|
+
if (clen > 32)
|
|
249
|
+
clen = 32;
|
|
250
|
+
memcpy(&buf[i * 32], T, clen);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
insecure_memzero(&PShctx, sizeof(PShctx));
|
|
254
|
+
insecure_memzero(U, sizeof(U));
|
|
255
|
+
insecure_memzero(T, sizeof(T));
|
|
256
|
+
}
|