iodine 0.7.60 → 0.8.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 +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -21
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -148
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
|
@@ -0,0 +1,923 @@
|
|
|
1
|
+
#ifndef H___IODINE_UTILS___H
|
|
2
|
+
#define H___IODINE_UTILS___H
|
|
3
|
+
#include "iodine.h"
|
|
4
|
+
|
|
5
|
+
/* *****************************************************************************
|
|
6
|
+
URL encoding Helpers
|
|
7
|
+
***************************************************************************** */
|
|
8
|
+
|
|
9
|
+
/** Decodes percent encoding, including the `%uxxxx` javascript extension and
|
|
10
|
+
* converting `+` to spaces. */
|
|
11
|
+
FIO_IFUNC VALUE iodine_utils_encode_with_encoding(
|
|
12
|
+
int argc,
|
|
13
|
+
VALUE *argv,
|
|
14
|
+
VALUE self,
|
|
15
|
+
int (*writer)(fio_str_info_s *dest,
|
|
16
|
+
fio_string_realloc_fn reallocate,
|
|
17
|
+
const void *encoded,
|
|
18
|
+
size_t len)) {
|
|
19
|
+
if (!argc || argc > 2)
|
|
20
|
+
rb_raise(rb_eArgError, "Wrong number of arguments (%d)", argc);
|
|
21
|
+
rb_check_type(argv[0], RUBY_T_STRING);
|
|
22
|
+
if (!RSTRING_LEN(argv[0]))
|
|
23
|
+
return argv[0];
|
|
24
|
+
rb_encoding *enc = NULL;
|
|
25
|
+
if (argc == 2)
|
|
26
|
+
enc = (TYPE(argv[1]) == T_STRING) ? rb_enc_find(RSTRING_PTR(argv[1]))
|
|
27
|
+
: rb_enc_get(argv[1]);
|
|
28
|
+
if (!enc)
|
|
29
|
+
enc = IodineUTF8Encoding;
|
|
30
|
+
FIO_STR_INFO_TMP_VAR(tmp, 512);
|
|
31
|
+
char *org = tmp.buf;
|
|
32
|
+
writer(&tmp,
|
|
33
|
+
FIO_STRING_ALLOC_COPY,
|
|
34
|
+
RSTRING_PTR(argv[0]),
|
|
35
|
+
RSTRING_LEN(argv[0]));
|
|
36
|
+
self = rb_str_new(tmp.buf, tmp.len);
|
|
37
|
+
rb_enc_associate(self, enc);
|
|
38
|
+
if (org != tmp.buf)
|
|
39
|
+
FIO_STRING_FREE2(tmp);
|
|
40
|
+
return self;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
FIO_IFUNC VALUE
|
|
44
|
+
iodine_utils_encode_internal(VALUE mod,
|
|
45
|
+
VALUE arg,
|
|
46
|
+
int (*writer)(fio_str_info_s *dest,
|
|
47
|
+
fio_string_realloc_fn reallocate,
|
|
48
|
+
const void *encoded,
|
|
49
|
+
size_t len)) {
|
|
50
|
+
rb_check_type(arg, RUBY_T_STRING);
|
|
51
|
+
if (!RSTRING_LEN(arg))
|
|
52
|
+
return arg;
|
|
53
|
+
FIO_STR_INFO_TMP_VAR(tmp, 512);
|
|
54
|
+
const char *org = tmp.buf;
|
|
55
|
+
writer(&tmp, FIO_STRING_ALLOC_COPY, RSTRING_PTR(arg), RSTRING_LEN(arg));
|
|
56
|
+
arg = rb_str_new(tmp.buf, tmp.len);
|
|
57
|
+
rb_enc_associate(arg, IodineUTF8Encoding);
|
|
58
|
+
if (org != tmp.buf)
|
|
59
|
+
FIO_STRING_FREE2(tmp);
|
|
60
|
+
return arg;
|
|
61
|
+
}
|
|
62
|
+
FIO_IFUNC VALUE
|
|
63
|
+
iodine_utils_encode1_internal(VALUE mod,
|
|
64
|
+
VALUE arg,
|
|
65
|
+
int (*writer)(fio_str_info_s *dest,
|
|
66
|
+
fio_string_realloc_fn reallocate,
|
|
67
|
+
const void *encoded,
|
|
68
|
+
size_t len)) {
|
|
69
|
+
rb_check_type(arg, RUBY_T_STRING);
|
|
70
|
+
if (!RSTRING_LEN(arg))
|
|
71
|
+
return arg;
|
|
72
|
+
FIO_STR_INFO_TMP_VAR(tmp, 512);
|
|
73
|
+
const char *org = tmp.buf;
|
|
74
|
+
writer(&tmp, FIO_STRING_ALLOC_COPY, RSTRING_PTR(arg), RSTRING_LEN(arg));
|
|
75
|
+
rb_str_set_len(arg, 0);
|
|
76
|
+
rb_str_cat(arg, tmp.buf, tmp.len);
|
|
77
|
+
rb_enc_associate(arg, IodineUTF8Encoding);
|
|
78
|
+
if (org != tmp.buf)
|
|
79
|
+
FIO_STRING_FREE2(tmp);
|
|
80
|
+
return arg;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Encodes a String using percent encoding (i.e., URI encoding). */
|
|
84
|
+
FIO_SFUNC VALUE iodine_utils_encode_url(VALUE mod, VALUE arg) {
|
|
85
|
+
return iodine_utils_encode_internal(mod, arg, fio_string_write_url_enc);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Encodes a String using percent encoding (i.e., URI encoding). */
|
|
89
|
+
FIO_SFUNC VALUE iodine_utils_encode_url1(VALUE mod, VALUE arg) {
|
|
90
|
+
return iodine_utils_encode1_internal(mod, arg, fio_string_write_url_enc);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Encodes a String using percent encoding (i.e., URI encoding). */
|
|
94
|
+
FIO_SFUNC VALUE iodine_utils_encode_path(VALUE mod, VALUE arg) {
|
|
95
|
+
return iodine_utils_encode_internal(mod, arg, fio_string_write_url_enc);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Encodes a String in place using percent encoding (i.e., URI encoding). */
|
|
99
|
+
FIO_SFUNC VALUE iodine_utils_encode_path1(VALUE mod, VALUE arg) {
|
|
100
|
+
return iodine_utils_encode1_internal(mod, arg, fio_string_write_url_enc);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Decodes percent encoding, including the `%uxxxx` javascript extension and
|
|
104
|
+
* converting `+` to spaces. */
|
|
105
|
+
FIO_SFUNC VALUE iodine_utils_decode_url(int argc, VALUE *argv, VALUE self) {
|
|
106
|
+
return iodine_utils_encode_with_encoding(argc,
|
|
107
|
+
argv,
|
|
108
|
+
self,
|
|
109
|
+
fio_string_write_url_dec);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Decodes percent encoding in place, including the `%uxxxx` javascript
|
|
113
|
+
* extension and converting `+` to spaces. */
|
|
114
|
+
FIO_SFUNC VALUE iodine_utils_decode_url1(VALUE mod, VALUE arg) {
|
|
115
|
+
return iodine_utils_encode1_internal(mod, arg, fio_string_write_url_dec);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Decodes percent encoding, including the `%uxxxx` javascript extension. */
|
|
119
|
+
FIO_SFUNC VALUE iodine_utils_decode_path(int argc, VALUE *argv, VALUE self) {
|
|
120
|
+
return iodine_utils_encode_with_encoding(argc,
|
|
121
|
+
argv,
|
|
122
|
+
self,
|
|
123
|
+
fio_string_write_path_dec);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Decodes percent encoding in place, including the `%uxxxx` javascript. */
|
|
127
|
+
FIO_SFUNC VALUE iodine_utils_decode_path1(VALUE mod, VALUE arg) {
|
|
128
|
+
return iodine_utils_encode1_internal(mod, arg, fio_string_write_path_dec);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Escapes String using HTML escape encoding. */
|
|
132
|
+
FIO_SFUNC VALUE iodine_utils_encode_html(VALUE mod, VALUE arg) {
|
|
133
|
+
return iodine_utils_encode_internal(mod, arg, fio_string_write_html_escape);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Escapes String in place using HTML escape encoding.
|
|
138
|
+
*
|
|
139
|
+
* Note: this function significantly increases the number of escaped characters
|
|
140
|
+
* when compared to the native implementation.
|
|
141
|
+
*/
|
|
142
|
+
FIO_SFUNC VALUE iodine_utils_encode_html1(VALUE mod, VALUE arg) {
|
|
143
|
+
return iodine_utils_encode1_internal(mod, arg, fio_string_write_html_escape);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Decodes an HTML escaped String. */
|
|
147
|
+
FIO_SFUNC VALUE iodine_utils_decode_html(int argc, VALUE *argv, VALUE self) {
|
|
148
|
+
return iodine_utils_encode_with_encoding(argc,
|
|
149
|
+
argv,
|
|
150
|
+
self,
|
|
151
|
+
fio_string_write_html_unescape);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Decodes an HTML escaped String in place. */
|
|
155
|
+
FIO_SFUNC VALUE iodine_utils_decode_html1(VALUE mod, VALUE arg) {
|
|
156
|
+
return iodine_utils_encode1_internal(mod,
|
|
157
|
+
arg,
|
|
158
|
+
fio_string_write_html_unescape);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* *****************************************************************************
|
|
162
|
+
Time to String Helpers
|
|
163
|
+
***************************************************************************** */
|
|
164
|
+
|
|
165
|
+
FIO_IFUNC time_t iodine_utils_rb2time(VALUE rtm) {
|
|
166
|
+
rtm = (rtm == Qnil ? LONG2NUM(0)
|
|
167
|
+
: rb_funcallv(rtm, rb_intern("to_i"), 0, NULL));
|
|
168
|
+
return NUM2LONG(rtm) ? NUM2LONG(rtm) : fio_time_real().tv_sec;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Takes a Time object and returns a String conforming to RFC 2109. */
|
|
172
|
+
FIO_SFUNC VALUE iodine_utils_rfc2109(VALUE mod, VALUE rtm) {
|
|
173
|
+
time_t time_requested = iodine_utils_rb2time(rtm);
|
|
174
|
+
VALUE str = rb_str_buf_new(34);
|
|
175
|
+
rb_str_set_len(str, fio_time2rfc2109(RSTRING_PTR(str), time_requested));
|
|
176
|
+
rb_enc_associate(str, IodineUTF8Encoding);
|
|
177
|
+
return str;
|
|
178
|
+
(void)mod;
|
|
179
|
+
}
|
|
180
|
+
/** Takes a Time object and returns a String conforming to RFC 2822. */
|
|
181
|
+
FIO_SFUNC VALUE iodine_utils_rfc2822(VALUE mod, VALUE rtm) {
|
|
182
|
+
time_t time_requested = iodine_utils_rb2time(rtm);
|
|
183
|
+
VALUE str = rb_str_buf_new(34);
|
|
184
|
+
rb_str_set_len(str, fio_time2rfc2822(RSTRING_PTR(str), time_requested));
|
|
185
|
+
rb_enc_associate(str, IodineUTF8Encoding);
|
|
186
|
+
return str;
|
|
187
|
+
(void)mod;
|
|
188
|
+
}
|
|
189
|
+
/** Takes a Time object and returns a String conforming to RFC 7231. */
|
|
190
|
+
FIO_SFUNC VALUE iodine_utils_rfc7231(VALUE mod, VALUE rtm) {
|
|
191
|
+
time_t time_requested = iodine_utils_rb2time(rtm);
|
|
192
|
+
VALUE str = rb_str_buf_new(34);
|
|
193
|
+
rb_str_set_len(str, fio_time2rfc7231(RSTRING_PTR(str), time_requested));
|
|
194
|
+
rb_enc_associate(str, IodineUTF8Encoding);
|
|
195
|
+
return str;
|
|
196
|
+
(void)mod;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* *****************************************************************************
|
|
200
|
+
String Secure Compare
|
|
201
|
+
***************************************************************************** */
|
|
202
|
+
// clang-format off
|
|
203
|
+
/**
|
|
204
|
+
Securely compares two String objects to see if they are equal.
|
|
205
|
+
|
|
206
|
+
Designed to be secure against timing attacks when both String objects are of the same length.
|
|
207
|
+
|
|
208
|
+
require 'iodine'
|
|
209
|
+
require 'rack'
|
|
210
|
+
require 'benchmark'
|
|
211
|
+
def prove_secure_compare(name, mthd, length = 4096)
|
|
212
|
+
a = 0;
|
|
213
|
+
b = 0;
|
|
214
|
+
str1 = Array.new(length) { 'a' } .join; str2 = Array.new(length) { 'a' } .join;
|
|
215
|
+
bm = Benchmark.measure do
|
|
216
|
+
1024.times do
|
|
217
|
+
tmp = Benchmark.measure {4096.times {mthd.call(str1, str2)}}
|
|
218
|
+
str1[0] = 'b'
|
|
219
|
+
tmp2 = Benchmark.measure {4096.times {mthd.call(str1, str2)}}
|
|
220
|
+
str1[0] = 'a'
|
|
221
|
+
tmp = tmp2.total - tmp.total
|
|
222
|
+
a += 1 if tmp >= 0
|
|
223
|
+
b += 1 if tmp <= 0
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
puts "#{name} timing ratio #{a}:#{b}\n#{bm.to_s}\n"
|
|
227
|
+
end
|
|
228
|
+
prove_secure_compare("String == (short string)", (Proc.new {|a,b| a == b } ), 47);
|
|
229
|
+
prove_secure_compare("Iodine::Utils.secure_compare (short string)", Iodine::Utils.method(:secure_compare), 47)
|
|
230
|
+
prove_secure_compare("Rack::Utils.secure_compare (short string)", Rack::Utils.method(:secure_compare), 47)
|
|
231
|
+
prove_secure_compare("String == (long string)", (Proc.new {|a,b| a == b } ), 1024)
|
|
232
|
+
prove_secure_compare("Iodine::Utils.secure_compare (long string)", Iodine::Utils.method(:secure_compare), 1024)
|
|
233
|
+
# prove_secure_compare("Rack::Utils.secure_compare (short string)", Rack::Utils.method(:secure_compare), 1024) # VERY slow
|
|
234
|
+
|
|
235
|
+
*/
|
|
236
|
+
FIO_SFUNC VALUE iodine_utils_is_eq(VALUE mod, VALUE a, VALUE b) { // clang-format on
|
|
237
|
+
rb_check_type(a, RUBY_T_STRING);
|
|
238
|
+
rb_check_type(b, RUBY_T_STRING);
|
|
239
|
+
if (RSTRING_LEN(a) != RSTRING_LEN(b))
|
|
240
|
+
return RUBY_Qfalse;
|
|
241
|
+
return fio_ct_is_eq(RSTRING_PTR(a), RSTRING_PTR(b), RSTRING_LEN(a))
|
|
242
|
+
? RUBY_Qtrue
|
|
243
|
+
: RUBY_Qfalse;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* *****************************************************************************
|
|
247
|
+
Randomness and Friends
|
|
248
|
+
***************************************************************************** */
|
|
249
|
+
|
|
250
|
+
FIO_DEFINE_RANDOM128_FN(static, iodine_random, 10, 0)
|
|
251
|
+
|
|
252
|
+
FIO_SFUNC VALUE iodine_utils_hmac512(VALUE self, VALUE secret, VALUE massage) {
|
|
253
|
+
rb_check_type(secret, RUBY_T_STRING);
|
|
254
|
+
rb_check_type(massage, RUBY_T_STRING);
|
|
255
|
+
fio_buf_info_s k = IODINE_RSTR_INFO(secret);
|
|
256
|
+
fio_buf_info_s m = IODINE_RSTR_INFO(massage);
|
|
257
|
+
fio_u512 h = fio_sha512_hmac(k.buf, k.len, m.buf, m.len);
|
|
258
|
+
FIO_STR_INFO_TMP_VAR(out, 128);
|
|
259
|
+
fio_string_write_base64enc(&out, NULL, h.u8, 64, 0);
|
|
260
|
+
return rb_str_new(out.buf, out.len);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
FIO_SFUNC VALUE iodine_utils_hmac256(VALUE self, VALUE secret, VALUE massage) {
|
|
264
|
+
rb_check_type(secret, RUBY_T_STRING);
|
|
265
|
+
rb_check_type(massage, RUBY_T_STRING);
|
|
266
|
+
fio_buf_info_s k = IODINE_RSTR_INFO(secret);
|
|
267
|
+
fio_buf_info_s m = IODINE_RSTR_INFO(massage);
|
|
268
|
+
fio_u256 h = fio_sha256_hmac(k.buf, k.len, m.buf, m.len);
|
|
269
|
+
FIO_STR_INFO_TMP_VAR(out, 64);
|
|
270
|
+
fio_string_write_base64enc(&out, NULL, h.u8, 32, 0);
|
|
271
|
+
return rb_str_new(out.buf, out.len);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
FIO_SFUNC VALUE iodine_utils_sha256(VALUE self, VALUE data) {
|
|
275
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
276
|
+
fio_u256 h = fio_sha256(RSTRING_PTR(data), RSTRING_LEN(data));
|
|
277
|
+
return rb_str_new((const char *)h.u8, 32);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
FIO_SFUNC VALUE iodine_utils_sha512(VALUE self, VALUE data) {
|
|
281
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
282
|
+
fio_u512 h = fio_sha512(RSTRING_PTR(data), RSTRING_LEN(data));
|
|
283
|
+
return rb_str_new((const char *)h.u8, 64);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
FIO_SFUNC VALUE iodine_utils_sha3_256(VALUE self, VALUE data) {
|
|
287
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
288
|
+
uint8_t out[32];
|
|
289
|
+
fio_sha3_256(out, RSTRING_PTR(data), RSTRING_LEN(data));
|
|
290
|
+
return rb_str_new((const char *)out, 32);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
FIO_SFUNC VALUE iodine_utils_sha3_512(VALUE self, VALUE data) {
|
|
294
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
295
|
+
uint8_t out[64];
|
|
296
|
+
fio_sha3_512(out, RSTRING_PTR(data), RSTRING_LEN(data));
|
|
297
|
+
return rb_str_new((const char *)out, 64);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
FIO_SFUNC VALUE iodine_utils_sha3_224(VALUE self, VALUE data) {
|
|
301
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
302
|
+
uint8_t out[28];
|
|
303
|
+
fio_sha3_224(out, RSTRING_PTR(data), RSTRING_LEN(data));
|
|
304
|
+
return rb_str_new((const char *)out, 28);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
FIO_SFUNC VALUE iodine_utils_sha3_384(VALUE self, VALUE data) {
|
|
308
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
309
|
+
uint8_t out[48];
|
|
310
|
+
fio_sha3_384(out, RSTRING_PTR(data), RSTRING_LEN(data));
|
|
311
|
+
return rb_str_new((const char *)out, 48);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Computes SHAKE128 extendable-output function.
|
|
316
|
+
*
|
|
317
|
+
* @param data [String] Input data to hash
|
|
318
|
+
* @param length: [Integer] Desired output length in bytes (default: 32)
|
|
319
|
+
* @return [String] Binary string of specified length
|
|
320
|
+
*/
|
|
321
|
+
FIO_SFUNC VALUE iodine_utils_shake128(int argc, VALUE *argv, VALUE self) {
|
|
322
|
+
fio_buf_info_s data = FIO_BUF_INFO0;
|
|
323
|
+
int64_t length = 32;
|
|
324
|
+
iodine_rb2c_arg(argc,
|
|
325
|
+
argv,
|
|
326
|
+
IODINE_ARG_BUF(data, 0, NULL, 1),
|
|
327
|
+
IODINE_ARG_NUM(length, 0, "length", 0));
|
|
328
|
+
if (length < 1 || length > 0x0FFFFFFF)
|
|
329
|
+
rb_raise(rb_eArgError, "length must be between 1 and 268435455");
|
|
330
|
+
VALUE out = rb_str_buf_new((long)length);
|
|
331
|
+
rb_str_set_len(out, (long)length);
|
|
332
|
+
fio_shake128(RSTRING_PTR(out), (size_t)length, data.buf, data.len);
|
|
333
|
+
return out;
|
|
334
|
+
(void)self;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Computes SHAKE256 extendable-output function.
|
|
339
|
+
*
|
|
340
|
+
* @param data [String] Input data to hash
|
|
341
|
+
* @param length: [Integer] Desired output length in bytes (default: 64)
|
|
342
|
+
* @return [String] Binary string of specified length
|
|
343
|
+
*/
|
|
344
|
+
FIO_SFUNC VALUE iodine_utils_shake256(int argc, VALUE *argv, VALUE self) {
|
|
345
|
+
fio_buf_info_s data = FIO_BUF_INFO0;
|
|
346
|
+
int64_t length = 64;
|
|
347
|
+
iodine_rb2c_arg(argc,
|
|
348
|
+
argv,
|
|
349
|
+
IODINE_ARG_BUF(data, 0, NULL, 1),
|
|
350
|
+
IODINE_ARG_NUM(length, 0, "length", 0));
|
|
351
|
+
if (length < 1 || length > 0x0FFFFFFF)
|
|
352
|
+
rb_raise(rb_eArgError, "length must be between 1 and 268435455");
|
|
353
|
+
VALUE out = rb_str_buf_new((long)length);
|
|
354
|
+
rb_str_set_len(out, (long)length);
|
|
355
|
+
fio_shake256(RSTRING_PTR(out), (size_t)length, data.buf, data.len);
|
|
356
|
+
return out;
|
|
357
|
+
(void)self;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Computes SHA-1 hash (20 bytes).
|
|
362
|
+
*
|
|
363
|
+
* WARNING: SHA-1 is cryptographically broken. Use only for legacy protocols
|
|
364
|
+
* that require it (e.g., WebSockets, TOTP compatibility).
|
|
365
|
+
*
|
|
366
|
+
* @param data [String] Input data to hash
|
|
367
|
+
* @return [String] 20-byte binary hash
|
|
368
|
+
*/
|
|
369
|
+
FIO_SFUNC VALUE iodine_utils_sha1(VALUE self, VALUE data) {
|
|
370
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
371
|
+
fio_sha1_s h = fio_sha1(RSTRING_PTR(data), (uint64_t)RSTRING_LEN(data));
|
|
372
|
+
return rb_str_new((const char *)h.digest, 20);
|
|
373
|
+
(void)self;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Computes CRC32 checksum (ITU-T V.42 / ISO 3309 / gzip polynomial 0xEDB88320).
|
|
378
|
+
*
|
|
379
|
+
* Uses a slicing-by-8 algorithm for high throughput. This is the standard CRC32
|
|
380
|
+
* used by gzip, zlib, and Ethernet — NOT the Castagnoli (CRC32-C) variant.
|
|
381
|
+
*
|
|
382
|
+
* Supports incremental computation: pass the previous return value as
|
|
383
|
+
* `initial_crc` to continue a checksum over multiple buffers.
|
|
384
|
+
*
|
|
385
|
+
* @param data [String] Input data to checksum
|
|
386
|
+
* @param initial_crc: [Integer] Starting CRC value (default: 0)
|
|
387
|
+
* @return [Integer] 32-bit CRC32 checksum
|
|
388
|
+
*/
|
|
389
|
+
FIO_SFUNC VALUE iodine_utils_crc32(int argc, VALUE *argv, VALUE self) {
|
|
390
|
+
fio_buf_info_s data = FIO_BUF_INFO0;
|
|
391
|
+
uint32_t initial_crc = 0;
|
|
392
|
+
iodine_rb2c_arg(argc,
|
|
393
|
+
argv,
|
|
394
|
+
IODINE_ARG_BUF(data, 0, NULL, 1),
|
|
395
|
+
IODINE_ARG_U32(initial_crc, 0, "initial_crc", 0));
|
|
396
|
+
uint32_t crc = fio_crc32(data.buf, data.len, initial_crc);
|
|
397
|
+
return UINT2NUM(crc);
|
|
398
|
+
(void)self;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Computes facil.io Risky Hash (non-cryptographic, fast).
|
|
403
|
+
*
|
|
404
|
+
* @param data [String] Input data to hash
|
|
405
|
+
* @param seed: [Integer] Optional seed value (default: 0)
|
|
406
|
+
* @return [Integer] 64-bit hash value
|
|
407
|
+
*/
|
|
408
|
+
FIO_SFUNC VALUE iodine_utils_risky_hash(int argc, VALUE *argv, VALUE self) {
|
|
409
|
+
fio_buf_info_s data = FIO_BUF_INFO0;
|
|
410
|
+
uint64_t seed = 0;
|
|
411
|
+
iodine_rb2c_arg(argc,
|
|
412
|
+
argv,
|
|
413
|
+
IODINE_ARG_BUF(data, 0, NULL, 1),
|
|
414
|
+
IODINE_ARG_U64(seed, 0, "seed", 0));
|
|
415
|
+
uint64_t hash = fio_risky_hash(data.buf, data.len, seed);
|
|
416
|
+
return ULL2NUM(hash);
|
|
417
|
+
(void)self;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Computes facil.io Risky256 Hash (non-cryptographic, 256-bit).
|
|
422
|
+
*
|
|
423
|
+
* @param data [String] Input data to hash
|
|
424
|
+
* @return [String] 32-byte binary hash
|
|
425
|
+
*/
|
|
426
|
+
FIO_SFUNC VALUE iodine_utils_risky256(VALUE self, VALUE data) {
|
|
427
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
428
|
+
fio_u256 h = fio_risky256(RSTRING_PTR(data), (uint64_t)RSTRING_LEN(data));
|
|
429
|
+
return rb_str_new((const char *)h.u8, 32);
|
|
430
|
+
(void)self;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Computes facil.io Risky512 Hash (non-cryptographic, 512-bit).
|
|
435
|
+
*
|
|
436
|
+
* First 256 bits are identical to risky256 (SHAKE-style extension).
|
|
437
|
+
*
|
|
438
|
+
* @param data [String] Input data to hash
|
|
439
|
+
* @return [String] 64-byte binary hash
|
|
440
|
+
*/
|
|
441
|
+
FIO_SFUNC VALUE iodine_utils_risky512(VALUE self, VALUE data) {
|
|
442
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
443
|
+
fio_u512 h = fio_risky512(RSTRING_PTR(data), (uint64_t)RSTRING_LEN(data));
|
|
444
|
+
return rb_str_new((const char *)h.u8, 64);
|
|
445
|
+
(void)self;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Computes facil.io Risky256 HMAC (non-cryptographic, keyed 256-bit).
|
|
450
|
+
*
|
|
451
|
+
* @param key [String] The secret key
|
|
452
|
+
* @param data [String] Input data to hash
|
|
453
|
+
* @return [String] 32-byte binary HMAC
|
|
454
|
+
*/
|
|
455
|
+
FIO_SFUNC VALUE iodine_utils_risky256_hmac(VALUE self, VALUE key, VALUE data) {
|
|
456
|
+
rb_check_type(key, RUBY_T_STRING);
|
|
457
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
458
|
+
fio_u256 h = fio_risky256_hmac(RSTRING_PTR(key),
|
|
459
|
+
(size_t)RSTRING_LEN(key),
|
|
460
|
+
RSTRING_PTR(data),
|
|
461
|
+
(size_t)RSTRING_LEN(data));
|
|
462
|
+
return rb_str_new((const char *)h.u8, 32);
|
|
463
|
+
(void)self;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Computes facil.io Risky512 HMAC (non-cryptographic, keyed 512-bit).
|
|
468
|
+
*
|
|
469
|
+
* @param key [String] The secret key
|
|
470
|
+
* @param data [String] Input data to hash
|
|
471
|
+
* @return [String] 64-byte binary HMAC
|
|
472
|
+
*/
|
|
473
|
+
FIO_SFUNC VALUE iodine_utils_risky512_hmac(VALUE self, VALUE key, VALUE data) {
|
|
474
|
+
rb_check_type(key, RUBY_T_STRING);
|
|
475
|
+
rb_check_type(data, RUBY_T_STRING);
|
|
476
|
+
fio_u512 h = fio_risky512_hmac(RSTRING_PTR(key),
|
|
477
|
+
(size_t)RSTRING_LEN(key),
|
|
478
|
+
RSTRING_PTR(data),
|
|
479
|
+
(size_t)RSTRING_LEN(data));
|
|
480
|
+
return rb_str_new((const char *)h.u8, 64);
|
|
481
|
+
(void)self;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Generates cryptographically secure random bytes using the system CSPRNG.
|
|
486
|
+
*
|
|
487
|
+
* Falls back to the facil.io PRNG if the system CSPRNG fails.
|
|
488
|
+
*
|
|
489
|
+
* @param bytes: [Integer] Number of bytes to generate (default: 32)
|
|
490
|
+
* @return [String] Binary string of random bytes
|
|
491
|
+
*/
|
|
492
|
+
FIO_SFUNC VALUE iodine_utils_secure_random(int argc, VALUE *argv, VALUE self) {
|
|
493
|
+
size_t size = 32;
|
|
494
|
+
iodine_rb2c_arg(argc, argv, IODINE_ARG_SIZE_T(size, 0, "bytes", 0));
|
|
495
|
+
if ((size - 1) > 0x0FFFFFFF)
|
|
496
|
+
rb_raise(rb_eRangeError, "`bytes` count is out of range.");
|
|
497
|
+
VALUE r = rb_str_buf_new(size);
|
|
498
|
+
if (fio_rand_bytes_secure(RSTRING_PTR(r), size))
|
|
499
|
+
fio_rand_bytes(RSTRING_PTR(r), size);
|
|
500
|
+
rb_str_set_len(r, size);
|
|
501
|
+
return r;
|
|
502
|
+
(void)self;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
FIO_SFUNC VALUE iodine_utils_blake2b(int argc, VALUE *argv, VALUE self) {
|
|
506
|
+
fio_buf_info_s data = FIO_BUF_INFO0;
|
|
507
|
+
fio_buf_info_s key = FIO_BUF_INFO0;
|
|
508
|
+
int64_t len = 64;
|
|
509
|
+
iodine_rb2c_arg(argc,
|
|
510
|
+
argv,
|
|
511
|
+
IODINE_ARG_BUF(data, 0, NULL, 1),
|
|
512
|
+
IODINE_ARG_BUF(key, 0, "key", 0),
|
|
513
|
+
IODINE_ARG_NUM(len, 0, "len", 0));
|
|
514
|
+
if (len < 1 || len > 64)
|
|
515
|
+
rb_raise(rb_eArgError, "len must be between 1 and 64");
|
|
516
|
+
uint8_t out[64];
|
|
517
|
+
fio_blake2b_hash(out, (size_t)len, data.buf, data.len, key.buf, key.len);
|
|
518
|
+
return rb_enc_str_new((const char *)out, (long)len, IodineBinaryEncoding);
|
|
519
|
+
(void)self;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
FIO_SFUNC VALUE iodine_utils_blake2s(int argc, VALUE *argv, VALUE self) {
|
|
523
|
+
fio_buf_info_s data = FIO_BUF_INFO0;
|
|
524
|
+
fio_buf_info_s key = FIO_BUF_INFO0;
|
|
525
|
+
int64_t len = 32;
|
|
526
|
+
iodine_rb2c_arg(argc,
|
|
527
|
+
argv,
|
|
528
|
+
IODINE_ARG_BUF(data, 0, NULL, 1),
|
|
529
|
+
IODINE_ARG_BUF(key, 0, "key", 0),
|
|
530
|
+
IODINE_ARG_NUM(len, 0, "len", 0));
|
|
531
|
+
if (len < 1 || len > 32)
|
|
532
|
+
rb_raise(rb_eArgError, "len must be between 1 and 32");
|
|
533
|
+
uint8_t out[32];
|
|
534
|
+
fio_blake2s_hash(out, (size_t)len, data.buf, data.len, key.buf, key.len);
|
|
535
|
+
return rb_enc_str_new((const char *)out, (long)len, IodineBinaryEncoding);
|
|
536
|
+
(void)self;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
FIO_SFUNC VALUE iodine_utils_hmac_sha1(VALUE self,
|
|
540
|
+
VALUE secret,
|
|
541
|
+
VALUE massage) {
|
|
542
|
+
rb_check_type(secret, RUBY_T_STRING);
|
|
543
|
+
rb_check_type(massage, RUBY_T_STRING);
|
|
544
|
+
fio_buf_info_s k = IODINE_RSTR_INFO(secret);
|
|
545
|
+
fio_buf_info_s m = IODINE_RSTR_INFO(massage);
|
|
546
|
+
fio_sha1_s h = fio_sha1_hmac(k.buf, k.len, m.buf, m.len);
|
|
547
|
+
FIO_STR_INFO_TMP_VAR(out, 40);
|
|
548
|
+
fio_string_write_base64enc(&out, NULL, h.digest, 20, 0);
|
|
549
|
+
return rb_str_new(out.buf, out.len);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
FIO_SFUNC VALUE iodine_utils_hmac_poly(VALUE self,
|
|
553
|
+
VALUE secret,
|
|
554
|
+
VALUE massage) {
|
|
555
|
+
rb_check_type(secret, RUBY_T_STRING);
|
|
556
|
+
rb_check_type(massage, RUBY_T_STRING);
|
|
557
|
+
fio_buf_info_s k = IODINE_RSTR_INFO(secret);
|
|
558
|
+
fio_buf_info_s m = IODINE_RSTR_INFO(massage);
|
|
559
|
+
fio_u256 fallback = {0};
|
|
560
|
+
fio_u128 h;
|
|
561
|
+
if (k.len < 256) {
|
|
562
|
+
fio_memcpy255x(fallback.u8, k.buf, k.len);
|
|
563
|
+
if (k.len < 10)
|
|
564
|
+
fallback = fio_sha512(k.buf, k.len).u256[0];
|
|
565
|
+
k.buf = (char *)fallback.u8;
|
|
566
|
+
}
|
|
567
|
+
fio_poly1305_auth(h.u8, m.buf, m.len, NULL, 0, k.buf);
|
|
568
|
+
|
|
569
|
+
FIO_STR_INFO_TMP_VAR(out, 32);
|
|
570
|
+
fio_string_write_base64enc(&out, NULL, h.u8, 16, 0);
|
|
571
|
+
return rb_str_new(out.buf, out.len);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
FIO_SFUNC VALUE iodine_utils_uuid(int argc, VALUE *argv, VALUE self) {
|
|
575
|
+
VALUE r = Qnil;
|
|
576
|
+
fio_u128 rand = iodine_random128();
|
|
577
|
+
fio_buf_info_s secret = {0};
|
|
578
|
+
fio_buf_info_s info = {0};
|
|
579
|
+
FIO_STR_INFO_TMP_VAR(str, 128);
|
|
580
|
+
iodine_rb2c_arg(argc,
|
|
581
|
+
argv,
|
|
582
|
+
IODINE_ARG_BUF(secret, 0, "secret", 0),
|
|
583
|
+
IODINE_ARG_BUF(info, 0, "info", 0));
|
|
584
|
+
if (secret.buf && info.buf) {
|
|
585
|
+
fio_sha512_s sh2 = fio_sha512_init();
|
|
586
|
+
fio_u1024 mk = {0};
|
|
587
|
+
if (secret.len <= 128) {
|
|
588
|
+
fio_memcpy255x(mk.u8, secret.buf, secret.len);
|
|
589
|
+
mk.u64[15] ^= secret.len;
|
|
590
|
+
for (size_t i = 0; i < 16; ++i)
|
|
591
|
+
mk.u64[i] ^= 0x3636363636363636ULL;
|
|
592
|
+
secret.buf = (char *)mk.u8;
|
|
593
|
+
secret.len = 128;
|
|
594
|
+
}
|
|
595
|
+
fio_sha512_consume(&sh2, secret.buf, secret.len);
|
|
596
|
+
fio_sha512_consume(&sh2, info.buf, info.len);
|
|
597
|
+
fio_u512 tmp = fio_sha512_finalize(&sh2);
|
|
598
|
+
rand.u64[0] = tmp.u64[0] + tmp.u64[1] + tmp.u64[2] + tmp.u64[3];
|
|
599
|
+
rand.u64[1] = tmp.u64[4] + tmp.u64[5] + tmp.u64[6] + tmp.u64[7];
|
|
600
|
+
/* support the vendor specific UUID variant. */
|
|
601
|
+
rand.u8[6] &= 0x0F;
|
|
602
|
+
rand.u8[6] |= 0x80;
|
|
603
|
+
rand.u8[8] &= 0x3F;
|
|
604
|
+
rand.u8[8] |= 0x80;
|
|
605
|
+
} else if (secret.buf || info.buf) {
|
|
606
|
+
if (info.buf)
|
|
607
|
+
secret = info;
|
|
608
|
+
uint64_t tmp = fio_risky_hash(secret.buf, secret.len, 0);
|
|
609
|
+
rand.u64[0] += tmp;
|
|
610
|
+
rand.u64[1] -= tmp;
|
|
611
|
+
goto random_uuid;
|
|
612
|
+
} else {
|
|
613
|
+
random_uuid:
|
|
614
|
+
/* support the random UUID version significant bits. */
|
|
615
|
+
rand.u8[6] &= 0x0F;
|
|
616
|
+
rand.u8[6] |= 0x40;
|
|
617
|
+
rand.u8[8] &= 0x3F;
|
|
618
|
+
rand.u8[8] |= 0x80;
|
|
619
|
+
}
|
|
620
|
+
fio_ltoa16u(str.buf, rand.u32[0], 8);
|
|
621
|
+
str.buf[8] = '-';
|
|
622
|
+
fio_ltoa16u(str.buf + 9, rand.u16[2], 4);
|
|
623
|
+
str.buf[13] = '-';
|
|
624
|
+
fio_ltoa16u(str.buf + 14, rand.u16[3], 4);
|
|
625
|
+
str.buf[18] = '-';
|
|
626
|
+
fio_ltoa16u(str.buf + 19, rand.u16[4], 4);
|
|
627
|
+
str.buf[23] = '-';
|
|
628
|
+
fio_ltoa16u(str.buf + 24, rand.u16[5], 4);
|
|
629
|
+
fio_ltoa16u(str.buf + 28, rand.u32[3], 8);
|
|
630
|
+
str.buf[36] = '0';
|
|
631
|
+
r = rb_str_new(str.buf, 36);
|
|
632
|
+
return r;
|
|
633
|
+
(void)self;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** Generates random data, high entropy, not cryptographically tested. */
|
|
637
|
+
FIO_SFUNC VALUE iodine_utils_random(int argc, VALUE *argv, VALUE self) {
|
|
638
|
+
VALUE r = Qnil;
|
|
639
|
+
size_t size = 16;
|
|
640
|
+
iodine_rb2c_arg(argc, argv, IODINE_ARG_SIZE_T(size, 0, "bytes", 0));
|
|
641
|
+
if ((size - 1) > 0x0FFFFFFF)
|
|
642
|
+
rb_raise(rb_eRangeError, "`bytes` count is out of range.");
|
|
643
|
+
r = rb_str_buf_new(size);
|
|
644
|
+
iodine_random_bytes(RSTRING_PTR(r), size);
|
|
645
|
+
rb_str_set_len(r, size);
|
|
646
|
+
return r;
|
|
647
|
+
(void)self;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Generates a Time-based One-Time Password (TOTP) code.
|
|
652
|
+
*
|
|
653
|
+
* Returns a 6-digit TOTP code as an Integer, compatible with Google
|
|
654
|
+
* Authenticator and similar apps.
|
|
655
|
+
*
|
|
656
|
+
* # Generate TOTP for current time window
|
|
657
|
+
* code = Iodine::Utils.totp(secret: my_secret)
|
|
658
|
+
*
|
|
659
|
+
* # Generate TOTP with custom interval (default is 30 seconds)
|
|
660
|
+
* code = Iodine::Utils.totp(secret: my_secret, interval: 60)
|
|
661
|
+
*
|
|
662
|
+
* # Generate TOTP for a different time window (offset in interval units)
|
|
663
|
+
* code = Iodine::Utils.totp(secret: my_secret, offset: -1) # previous
|
|
664
|
+
* window
|
|
665
|
+
*
|
|
666
|
+
* Parameters:
|
|
667
|
+
* - `secret:` (required) - The shared secret key (raw bytes or Base32 decoded)
|
|
668
|
+
* - `offset:` (optional) - Time offset in interval units (default: 0)
|
|
669
|
+
* - `interval:` (optional) - Time window in seconds (default: 30)
|
|
670
|
+
*
|
|
671
|
+
* Returns: Integer - A 6-digit TOTP code
|
|
672
|
+
*/
|
|
673
|
+
FIO_SFUNC VALUE iodine_utils_totp(int argc, VALUE *argv, VALUE self) {
|
|
674
|
+
fio_buf_info_s secret = {0};
|
|
675
|
+
int64_t offset = 0;
|
|
676
|
+
size_t interval = 0;
|
|
677
|
+
|
|
678
|
+
iodine_rb2c_arg(argc,
|
|
679
|
+
argv,
|
|
680
|
+
IODINE_ARG_BUF(secret, 0, "secret", 1),
|
|
681
|
+
IODINE_ARG_NUM(offset, 0, "offset", 0),
|
|
682
|
+
IODINE_ARG_SIZE_T(interval, 0, "interval", 0));
|
|
683
|
+
if (!interval)
|
|
684
|
+
interval = 30;
|
|
685
|
+
|
|
686
|
+
uint32_t otp = fio_otp(secret, .offset = offset, .interval = interval);
|
|
687
|
+
return UINT2NUM(otp);
|
|
688
|
+
(void)self;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Generates a new TOTP secret suitable for Google Authenticator.
|
|
693
|
+
*
|
|
694
|
+
* # Generate a secret with default length (20 bytes)
|
|
695
|
+
* secret = Iodine::Utils.totp_secret
|
|
696
|
+
*
|
|
697
|
+
* # Generate a longer secret (32 bytes)
|
|
698
|
+
* secret = Iodine::Utils.totp_secret(len: 32)
|
|
699
|
+
*
|
|
700
|
+
* The secret is generated using cryptographically secure random bytes
|
|
701
|
+
* and encoded in Base32 (uppercase, no padding) for compatibility with
|
|
702
|
+
* authenticator apps.
|
|
703
|
+
*
|
|
704
|
+
* Parameters:
|
|
705
|
+
* - `len:` (optional) - Length of the secret in bytes (default: 20, range:
|
|
706
|
+
* 10-64)
|
|
707
|
+
*
|
|
708
|
+
* Returns: String - Base32 encoded secret suitable for QR codes and
|
|
709
|
+
* authenticator apps
|
|
710
|
+
*/
|
|
711
|
+
FIO_SFUNC VALUE iodine_utils_totp_secret(int argc, VALUE *argv, VALUE self) {
|
|
712
|
+
int64_t len = 20;
|
|
713
|
+
iodine_rb2c_arg(argc, argv, IODINE_ARG_NUM(len, 0, "len", 0));
|
|
714
|
+
|
|
715
|
+
if (len < 10 || len > 64)
|
|
716
|
+
rb_raise(rb_eArgError, "len must be between 10 and 64");
|
|
717
|
+
|
|
718
|
+
/* Generate a good enough random key */
|
|
719
|
+
uint8_t key[64];
|
|
720
|
+
if (fio_rand_bytes_secure(key, (size_t)len))
|
|
721
|
+
fio_rand_bytes(key, (size_t)len);
|
|
722
|
+
|
|
723
|
+
/* Base32 encode (output is roughly 8/5 of input, plus null terminator) */
|
|
724
|
+
char encoded[128];
|
|
725
|
+
size_t encoded_len = fio_otp_print_key(encoded, key, (size_t)len);
|
|
726
|
+
|
|
727
|
+
return rb_str_new(encoded, (long)encoded_len);
|
|
728
|
+
(void)self;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Verifies a TOTP code against a secret with time window tolerance.
|
|
733
|
+
*
|
|
734
|
+
* # Verify a TOTP code with default settings
|
|
735
|
+
* valid = Iodine::Utils.totp_verify(code: user_code, secret: my_secret)
|
|
736
|
+
*
|
|
737
|
+
* # Verify with larger time window (allows more clock drift)
|
|
738
|
+
* valid = Iodine::Utils.totp_verify(code: user_code, secret: my_secret,
|
|
739
|
+
* window: 2)
|
|
740
|
+
*
|
|
741
|
+
* # Verify with custom interval (must match the interval used to generate)
|
|
742
|
+
* valid = Iodine::Utils.totp_verify(code: user_code, secret: my_secret,
|
|
743
|
+
* interval: 60)
|
|
744
|
+
*
|
|
745
|
+
* The window parameter specifies how many intervals to check on either side
|
|
746
|
+
* of the current time. For example, window: 1 checks current ± 1 interval.
|
|
747
|
+
*
|
|
748
|
+
* Parameters:
|
|
749
|
+
* - `code:` (required) - The TOTP code to verify (Integer)
|
|
750
|
+
* - `secret:` (required) - The shared secret key (raw bytes or Base32 decoded)
|
|
751
|
+
* - `window:` (optional) - Number of intervals to check on each side (default:
|
|
752
|
+
* 1, range: 0-10)
|
|
753
|
+
* - `interval:` (optional) - Time window in seconds (default: 30)
|
|
754
|
+
*
|
|
755
|
+
* Returns: true if the code is valid, false otherwise
|
|
756
|
+
*/
|
|
757
|
+
FIO_SFUNC VALUE iodine_utils_totp_verify(int argc, VALUE *argv, VALUE self) {
|
|
758
|
+
fio_buf_info_s secret = FIO_BUF_INFO0;
|
|
759
|
+
int64_t code = 0;
|
|
760
|
+
int64_t window = 1;
|
|
761
|
+
size_t interval = 0;
|
|
762
|
+
|
|
763
|
+
iodine_rb2c_arg(argc,
|
|
764
|
+
argv,
|
|
765
|
+
IODINE_ARG_NUM(code, 0, "code", 1),
|
|
766
|
+
IODINE_ARG_BUF(secret, 0, "secret", 1),
|
|
767
|
+
IODINE_ARG_NUM(window, 0, "window", 0),
|
|
768
|
+
IODINE_ARG_SIZE_T(interval, 0, "interval", 0));
|
|
769
|
+
|
|
770
|
+
if (!interval)
|
|
771
|
+
interval = 30;
|
|
772
|
+
if (window < 0 || window > 10)
|
|
773
|
+
rb_raise(rb_eArgError, "window must be between 0 and 10");
|
|
774
|
+
|
|
775
|
+
/* Check code against each offset in the window */
|
|
776
|
+
for (int64_t offset = -window; offset <= window; ++offset) {
|
|
777
|
+
uint32_t expected = fio_otp(secret, .offset = offset, .interval = interval);
|
|
778
|
+
if (expected == (uint32_t)code)
|
|
779
|
+
return Qtrue;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return Qfalse;
|
|
783
|
+
(void)self;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/* *****************************************************************************
|
|
787
|
+
Create Methods in Module
|
|
788
|
+
***************************************************************************** */
|
|
789
|
+
|
|
790
|
+
FIO_SFUNC void iodine_utils_define_methods(VALUE m) {
|
|
791
|
+
rb_define_singleton_method(m, "escape_path", iodine_utils_encode_path, 1);
|
|
792
|
+
rb_define_singleton_method(m, "escape_path!", iodine_utils_encode_path1, 1);
|
|
793
|
+
rb_define_singleton_method(m, "unescape_path", iodine_utils_decode_path, -1);
|
|
794
|
+
rb_define_singleton_method(m, "unescape_path!", iodine_utils_decode_path1, 1);
|
|
795
|
+
rb_define_singleton_method(m, "escape", iodine_utils_encode_url, 1);
|
|
796
|
+
rb_define_singleton_method(m, "escape!", iodine_utils_encode_url1, 1);
|
|
797
|
+
rb_define_singleton_method(m, "unescape", iodine_utils_decode_url, -1);
|
|
798
|
+
rb_define_singleton_method(m, "unescape!", iodine_utils_decode_url1, 1);
|
|
799
|
+
rb_define_singleton_method(m, "escape_html", iodine_utils_encode_html, 1);
|
|
800
|
+
rb_define_singleton_method(m, "escape_html!", iodine_utils_encode_html1, 1);
|
|
801
|
+
rb_define_singleton_method(m, "unescape_html", iodine_utils_decode_html, -1);
|
|
802
|
+
rb_define_singleton_method(m, "unescape_html!", iodine_utils_decode_html1, 1);
|
|
803
|
+
rb_define_singleton_method(m, "rfc2109", iodine_utils_rfc2109, 1);
|
|
804
|
+
rb_define_singleton_method(m, "rfc2822", iodine_utils_rfc2822, 1);
|
|
805
|
+
rb_define_singleton_method(m, "time2str", iodine_utils_rfc7231, 1);
|
|
806
|
+
rb_define_singleton_method(m, "secure_compare", iodine_utils_is_eq, 2);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Adds the `Iodine::Utils` methods to the modules passed as arguments.
|
|
811
|
+
*
|
|
812
|
+
* If no modules were passed to the `monkey_patch` method, `Rack::Utils` will be
|
|
813
|
+
* monkey patched.
|
|
814
|
+
*
|
|
815
|
+
*/
|
|
816
|
+
FIO_SFUNC VALUE iodine_utils_monkey_patch(int argc, VALUE *argv, VALUE self) {
|
|
817
|
+
VALUE default_module;
|
|
818
|
+
if (!argc) {
|
|
819
|
+
argc = 1;
|
|
820
|
+
argv = &default_module;
|
|
821
|
+
rb_require("rack");
|
|
822
|
+
default_module = rb_define_module_under(rb_define_module("Rack"), "Utils");
|
|
823
|
+
}
|
|
824
|
+
for (int i = 0; i < argc; ++i) {
|
|
825
|
+
rb_check_type(argv[i], T_MODULE);
|
|
826
|
+
iodine_utils_define_methods(argv[i]);
|
|
827
|
+
}
|
|
828
|
+
return self;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/** Initialize Iodine::Utils */ // clang-format off
|
|
832
|
+
/**
|
|
833
|
+
|
|
834
|
+
# Utility Helpers - Iodine's Helpers
|
|
835
|
+
|
|
836
|
+
These are some unescaping / decoding helpers provided by Iodine.
|
|
837
|
+
|
|
838
|
+
These **should** be faster then their common Ruby / Rack alternative.
|
|
839
|
+
|
|
840
|
+
Performance may differ according to architecture and compiler used. Please measure:
|
|
841
|
+
|
|
842
|
+
require 'iodine'
|
|
843
|
+
require 'rack'
|
|
844
|
+
require 'cgi'
|
|
845
|
+
require 'benchmark/ips'
|
|
846
|
+
encoded = '%E3 + %83 + %AB + %E3 + %83 + %93 + %E3 + %82 + %A4 + %E3 + %82 + %B9 + %E3 + %81 + %A8' # a String in need of decoding
|
|
847
|
+
decoded = Rack::Utils.unescape(encoded, "binary")
|
|
848
|
+
html_xss = "<script>alert('avoid xss attacks')</script>"
|
|
849
|
+
html_xss_safe = Rack::Utils.escape_html html_xss
|
|
850
|
+
short_str1 = Array.new(64) { 'a' } .join
|
|
851
|
+
short_str2 = Array.new(64) { 'a' } .join
|
|
852
|
+
long_str1 = Array.new(4094) { 'a' } .join
|
|
853
|
+
long_str2 = Array.new(4094) { 'a' } .join
|
|
854
|
+
now_preclaculated = Time.now
|
|
855
|
+
Benchmark.ips do |bm|
|
|
856
|
+
bm.report(" Iodine rfc2822") { Iodine::Utils.rfc2822(now_preclaculated) }
|
|
857
|
+
bm.report(" Rack rfc2822") { Rack::Utils.rfc2822(now_preclaculated) }
|
|
858
|
+
bm.compare!
|
|
859
|
+
end; Benchmark.ips do |bm|
|
|
860
|
+
bm.report("Iodine unescape") { Iodine::Utils.unescape encoded }
|
|
861
|
+
bm.report(" Rack unescape") { Rack::Utils.unescape encoded }
|
|
862
|
+
bm.compare!
|
|
863
|
+
end; Benchmark.ips do |bm|
|
|
864
|
+
bm.report("Iodine escape") { Iodine::Utils.escape decoded }
|
|
865
|
+
bm.report(" Rack escape") { Rack::Utils.escape decoded }
|
|
866
|
+
bm.compare!
|
|
867
|
+
end; Benchmark.ips do |bm|
|
|
868
|
+
bm.report("Iodine escape HTML") { Iodine::Utils.escape_html html_xss }
|
|
869
|
+
bm.report(" Rack escape HTML") { Rack::Utils.escape_html html_xss }
|
|
870
|
+
bm.compare!
|
|
871
|
+
end; Benchmark.ips do |bm|
|
|
872
|
+
bm.report("Iodine unescape HTML") { Iodine::Utils.unescape_html html_xss_safe }
|
|
873
|
+
bm.report(" CGI unescape HTML") { CGI.unescapeHTML html_xss_safe }
|
|
874
|
+
bm.compare!
|
|
875
|
+
end; Benchmark.ips do |bm|
|
|
876
|
+
bm.report("Iodine secure compare (short)") { Iodine::Utils.secure_compare short_str1, short_str2 }
|
|
877
|
+
bm.report(" Rack secure compare (short)") { Rack::Utils.secure_compare short_str1, short_str2 }
|
|
878
|
+
bm.compare!
|
|
879
|
+
end; Benchmark.ips do |bm|
|
|
880
|
+
bm.report("Iodine secure compare (long)") { Iodine::Utils.secure_compare long_str1, long_str2 }
|
|
881
|
+
bm.report(" Rack secure compare (long)") { Rack::Utils.secure_compare long_str1, long_str2 }
|
|
882
|
+
bm.compare!
|
|
883
|
+
end && nil
|
|
884
|
+
|
|
885
|
+
*/
|
|
886
|
+
static void Init_Iodine_Utils(void) {
|
|
887
|
+
VALUE m = rb_define_module_under(iodine_rb_IODINE, "Utils");
|
|
888
|
+
iodine_utils_define_methods(m);
|
|
889
|
+
/* non-standard helpers */
|
|
890
|
+
rb_define_singleton_method(m, "monkey_patch", iodine_utils_monkey_patch, -1);
|
|
891
|
+
rb_define_singleton_method(m, "random", iodine_utils_random, -1);
|
|
892
|
+
rb_define_singleton_method(m, "uuid", iodine_utils_uuid, -1);
|
|
893
|
+
rb_define_singleton_method(m, "totp", iodine_utils_totp, -1);
|
|
894
|
+
rb_define_singleton_method(m, "totp_secret", iodine_utils_totp_secret, -1);
|
|
895
|
+
rb_define_singleton_method(m, "totp_verify", iodine_utils_totp_verify, -1);
|
|
896
|
+
rb_define_singleton_method(m, "hmac512", iodine_utils_hmac512, 2);
|
|
897
|
+
rb_define_singleton_method(m, "hmac256", iodine_utils_hmac256, 2);
|
|
898
|
+
rb_define_singleton_method(m, "hmac160", iodine_utils_hmac_sha1, 2);
|
|
899
|
+
rb_define_singleton_method(m, "hmac128", iodine_utils_hmac_poly, 2);
|
|
900
|
+
rb_define_singleton_method(m, "sha256", iodine_utils_sha256, 1);
|
|
901
|
+
rb_define_singleton_method(m, "sha512", iodine_utils_sha512, 1);
|
|
902
|
+
rb_define_singleton_method(m, "sha3_224", iodine_utils_sha3_224, 1);
|
|
903
|
+
rb_define_singleton_method(m, "sha3_256", iodine_utils_sha3_256, 1);
|
|
904
|
+
rb_define_singleton_method(m, "sha3_384", iodine_utils_sha3_384, 1);
|
|
905
|
+
rb_define_singleton_method(m, "sha3_512", iodine_utils_sha3_512, 1);
|
|
906
|
+
rb_define_singleton_method(m, "shake128", iodine_utils_shake128, -1);
|
|
907
|
+
rb_define_singleton_method(m, "shake256", iodine_utils_shake256, -1);
|
|
908
|
+
rb_define_singleton_method(m, "sha1", iodine_utils_sha1, 1);
|
|
909
|
+
rb_define_singleton_method(m, "crc32", iodine_utils_crc32, -1);
|
|
910
|
+
rb_define_singleton_method(m, "risky_hash", iodine_utils_risky_hash, -1);
|
|
911
|
+
rb_define_singleton_method(m, "risky256", iodine_utils_risky256, 1);
|
|
912
|
+
rb_define_singleton_method(m, "risky512", iodine_utils_risky512, 1);
|
|
913
|
+
rb_define_singleton_method(m, "risky256_hmac", iodine_utils_risky256_hmac, 2);
|
|
914
|
+
rb_define_singleton_method(m, "risky512_hmac", iodine_utils_risky512_hmac, 2);
|
|
915
|
+
rb_define_singleton_method(m, "secure_random", iodine_utils_secure_random, -1);
|
|
916
|
+
rb_define_singleton_method(m, "blake2b", iodine_utils_blake2b, -1);
|
|
917
|
+
rb_define_singleton_method(m, "blake2s", iodine_utils_blake2s, -1);
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
fio_state_callback_add(FIO_CALL_IN_CHILD, iodine_random_on_fork, NULL);
|
|
922
|
+
} // clang-format on
|
|
923
|
+
#endif /* H___IODINE_UTILS___H */
|