iodine 0.1.21 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
@@ -0,0 +1,17 @@
1
+ /*
2
+ copyright: Boaz segev, 2016
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef RUBY_RACK_IO_H
8
+ #define RUBY_RACK_IO_H
9
+ #include <ruby.h>
10
+ #include "http_request.h"
11
+
12
+ extern struct _RackIO_ {
13
+ VALUE (*new)(http_request_s *request, VALUE env);
14
+ void (*init)(void);
15
+ } RackIO;
16
+
17
+ #endif /* RUBY_RACK_IO_H */
@@ -0,0 +1,213 @@
1
+ #include "rb-registry.h"
2
+ #include <ruby.h>
3
+ #include "spnlock.h"
4
+
5
+ // #define RUBY_REG_DBG
6
+
7
+ // the registry global
8
+ static struct Registry {
9
+ struct Object *obj_pool;
10
+ struct Object *first;
11
+ VALUE owner;
12
+ spn_lock_i lock;
13
+ } registry = {
14
+ .obj_pool = NULL, .first = NULL, .owner = 0, .lock = SPN_LOCK_INIT};
15
+
16
+ #define try_lock_registry() spn_trylock(&registry.lock)
17
+ #define unlock_registry() spn_unlock(&registry.lock)
18
+ #define lock_registry() spn_lock(&registry.lock)
19
+
20
+ // the references struct (bin-tree)
21
+ struct Object {
22
+ struct Object *next;
23
+ VALUE obj;
24
+ int count;
25
+ };
26
+
27
+ // manage existing objects - add a reference
28
+ int add_reference(VALUE obj) {
29
+ struct Object *line;
30
+ lock_registry();
31
+ line = registry.first;
32
+ while (line) {
33
+ if (line->obj == obj) {
34
+ line->count++;
35
+ unlock_registry();
36
+ return 1;
37
+ }
38
+ line = line->next;
39
+ }
40
+ unlock_registry();
41
+ return 0;
42
+ }
43
+
44
+ // add an object to the registry
45
+ //
46
+ // allow multiple registrartions (bag)
47
+ static VALUE register_object(VALUE obj) {
48
+ if (!obj || obj == Qnil)
49
+ return 0;
50
+ if (add_reference(obj))
51
+ return obj;
52
+ struct Object *line;
53
+ lock_registry();
54
+ if (registry.obj_pool) {
55
+ line = registry.obj_pool;
56
+ registry.obj_pool = registry.obj_pool->next;
57
+ } else {
58
+ line = malloc(sizeof(struct Object));
59
+ }
60
+ if (!line) {
61
+ perror("No Memory");
62
+ unlock_registry();
63
+ return 0;
64
+ }
65
+ line->obj = obj;
66
+ line->next = registry.first;
67
+ line->count = 1;
68
+ registry.first = line;
69
+ unlock_registry();
70
+ return obj;
71
+ }
72
+
73
+ // free a single registry
74
+ //
75
+ // free only one.
76
+ static void unregister_object(VALUE obj) {
77
+ if (!obj || obj == Qnil)
78
+ return;
79
+ lock_registry();
80
+ struct Object *line = registry.first;
81
+ struct Object *prev = NULL;
82
+ while (line) {
83
+ if (line->obj == obj) {
84
+ line->count--;
85
+ if (!line->count) {
86
+ if (line == registry.first)
87
+ registry.first = line->next;
88
+ else if (prev) // must be true, really
89
+ prev->next = line->next;
90
+ // move the object container to the discarded object pool
91
+ line->next = registry.obj_pool;
92
+ registry.obj_pool = line;
93
+ }
94
+ goto finish;
95
+ }
96
+ prev = line;
97
+ line = line->next;
98
+ }
99
+ finish:
100
+ unlock_registry();
101
+ }
102
+
103
+ // // Replaces one registry object with another,
104
+ // // allowing updates to the Registry with no memory allocations.
105
+ // //
106
+ // // returns 0 if all OK, returns -1 if it couldn't replace the object.
107
+ // static int replace_object(VALUE obj, VALUE new_obj) {
108
+ // int ret = -1;
109
+ // if (obj == new_obj)
110
+ // return 0;
111
+ // pthread_mutex_lock(&registry_lock);
112
+ // struct Object* line = registry.first;
113
+ // while (line) {
114
+ // if (line->obj == obj) {
115
+ // line->obj = new_obj;
116
+ // ret = 0;
117
+ // goto finish;
118
+ // }
119
+ // line = line->next;
120
+ // }
121
+ // finish:
122
+ // pthread_mutex_unlock(&registry_lock);
123
+ // return ret;
124
+ // }
125
+
126
+ // a callback for the GC (marking active objects)
127
+ static void registry_mark(void *ignore) {
128
+ #ifdef RUBY_REG_DBG
129
+ Registry.print();
130
+ #endif
131
+ lock_registry();
132
+ struct Object *line = registry.first;
133
+ while (line) {
134
+ if (line->obj)
135
+ rb_gc_mark(line->obj);
136
+ line = line->next;
137
+ }
138
+ unlock_registry();
139
+ }
140
+
141
+ // clear the registry (end of lifetime)
142
+ static void registry_clear(void *ignore) {
143
+ lock_registry();
144
+ struct Object *line;
145
+ struct Object *to_free;
146
+ // free active object references
147
+ line = registry.first;
148
+ while (line) {
149
+ to_free = line;
150
+ line = line->next;
151
+ free(to_free);
152
+ }
153
+ registry.first = NULL;
154
+ // free container pool
155
+ line = registry.obj_pool;
156
+ while (line) {
157
+ to_free = line;
158
+ line = line->next;
159
+ free(to_free);
160
+ }
161
+ registry.obj_pool = NULL;
162
+ registry.owner = 0;
163
+ unlock_registry();
164
+ }
165
+
166
+ // the data-type used to identify the registry
167
+ // this sets the callbacks.
168
+ static struct rb_data_type_struct my_registry_type_struct = {
169
+ .wrap_struct_name = "RubyReferencesIn_C_Land",
170
+ .function.dfree = (void (*)(void *))registry_clear,
171
+ .function.dmark = (void (*)(void *))registry_mark,
172
+ };
173
+
174
+ // initialize the registry
175
+ static void init(VALUE owner) {
176
+ lock_registry();
177
+ if (registry.owner)
178
+ goto finish;
179
+ if (!owner)
180
+ owner = rb_cObject;
181
+ registry.owner = owner;
182
+ VALUE rReferences =
183
+ rb_define_class_under(owner, "RubyObjectRegistry_for_C_land", rb_cData);
184
+ VALUE r_registry =
185
+ TypedData_Wrap_Struct(rReferences, &my_registry_type_struct, &registry);
186
+ rb_ivar_set(owner, rb_intern("registry"), r_registry);
187
+ finish:
188
+ unlock_registry();
189
+ }
190
+
191
+ // print data, for testing
192
+ static void print(void) {
193
+ lock_registry();
194
+ struct Object *line = registry.first;
195
+ fprintf(stderr, "Registry owner is %lu\n", registry.owner);
196
+ long index = 0;
197
+ while (line) {
198
+ fprintf(stderr, "[%lu] => %d X obj %lu type %d at %p\n", index++,
199
+ line->count, line->obj, TYPE(line->obj), line);
200
+ line = line->next;
201
+ }
202
+ fprintf(stderr, "Total of %lu registered objects being marked\n", index);
203
+ unlock_registry();
204
+ }
205
+
206
+ ////////////////////////////////////////////
207
+ // The API gateway
208
+ struct ___RegistryClass___ Registry = {
209
+ .init = init,
210
+ .remove = unregister_object,
211
+ .add = register_object,
212
+ .print = print,
213
+ };
@@ -0,0 +1,33 @@
1
+ /*
2
+ copyright: Boaz segev, 2016
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef RB_REGISTRY_HELPER_H
8
+ #define RB_REGISTRY_HELPER_H
9
+ #include <ruby.h>
10
+
11
+ /**
12
+ This is a "Registry" helper for Ruby extentions.
13
+
14
+ The registry allows "registering" Ruby objects to be marked by the Ruby's GC.
15
+ This prevents the need for global variables such as Ruby arrays or Hashes
16
+ with Ruby objects and allows easy management of Ruby objects owned by C code.
17
+
18
+ GC requires a callback "mark" to inform it which objects are still
19
+ referenced.
20
+
21
+ Our library creates, holds and releases myriad Ruby objects.
22
+
23
+ Hence, our server class needs to include an object binary tree to
24
+ handle ruby registry and keep references to the Ruby objects.
25
+ */
26
+ extern struct ___RegistryClass___ {
27
+ void (*init)(VALUE owner);
28
+ void (*remove)(VALUE obj);
29
+ VALUE (*add)(VALUE obj);
30
+ void (*print)(void);
31
+ } Registry;
32
+
33
+ #endif // RB_REGISTRY_HELPER_H
data/ext/iodine/sha1.c ADDED
@@ -0,0 +1,359 @@
1
+ /*
2
+ (un)copyright: Boaz segev, 2016
3
+ License: Public Domain except for any non-public-domain algorithms, which are
4
+ subject to their own licenses.
5
+
6
+ Feel free to copy, use and enjoy in accordance with to the license(s).
7
+ */
8
+ #ifndef _GNU_SOURCE
9
+ #define _GNU_SOURCE
10
+ #endif
11
+ #include "sha1.h"
12
+ /*****************************************************************************
13
+ Useful Macros - Not all of them are used here, but it's a copy-paste convenience
14
+ */
15
+
16
+ /** 32Bit left rotation, inlined. */
17
+ #define left_rotate32(i, bits) \
18
+ (((uint32_t)(i) << (bits)) | ((uint32_t)(i) >> (32 - (bits))))
19
+ /** 32Bit right rotation, inlined. */
20
+ #define right_rotate32(i, bits) \
21
+ (((uint32_t)(i) >> (bits)) | ((uint32_t)(i) << (32 - (bits))))
22
+ /** 64Bit left rotation, inlined. */
23
+ #define left_rotate64(i, bits) \
24
+ (((uint64_t)(i) << (bits)) | ((uint64_t)(i) >> (64 - (bits))))
25
+ /** 64Bit right rotation, inlined. */
26
+ #define right_rotate64(i, bits) \
27
+ (((uint64_t)(i) >> (bits)) | ((uint64_t)(i) << (64 - (bits))))
28
+ /** unknown size element - left rotation, inlined. */
29
+ #define left_rotate(i, bits) (((i) << (bits)) | ((i) >> (sizeof((i)) - (bits))))
30
+ /** unknown size element - right rotation, inlined. */
31
+ #define right_rotate(i, bits) \
32
+ (((i) >> (bits)) | ((i) << (sizeof((i)) - (bits))))
33
+ /** inplace byte swap 16 bit integer */
34
+ #define bswap16(i) \
35
+ do { \
36
+ (i) = (((i)&0xFFU) << 8) | (((i)&0xFF00U) >> 8); \
37
+ } while (0);
38
+ /** inplace byte swap 32 bit integer */
39
+ #define bswap32(i) \
40
+ do { \
41
+ (i) = (((i)&0xFFUL) << 24) | (((i)&0xFF00UL) << 8) | \
42
+ (((i)&0xFF0000UL) >> 8) | (((i)&0xFF000000UL) >> 24); \
43
+ } while (0);
44
+ /** inplace byte swap 64 bit integer */
45
+ #define bswap64(i) \
46
+ do { \
47
+ (i) = (((i)&0xFFULL) << 56) | (((i)&0xFF00ULL) << 40) | \
48
+ (((i)&0xFF0000ULL) << 24) | (((i)&0xFF000000ULL) << 8) | \
49
+ (((i)&0xFF00000000ULL) >> 8) | (((i)&0xFF0000000000ULL) >> 24) | \
50
+ (((i)&0xFF000000000000ULL) >> 40) | \
51
+ (((i)&0xFF00000000000000ULL) >> 56); \
52
+ } while (0);
53
+
54
+ const uint8_t sha1_padding[64] = {0x80, 0};
55
+
56
+ #ifdef __BIG_ENDIAN__
57
+ /** Converts a 4 byte string to a uint32_t word. careful with alignment! */
58
+ #define str2word(c) (*((uint32_t *)(c)))
59
+ #else
60
+ /**
61
+ Converts a 4 byte string to a Big Endian uint32_t word. (ignores alignment!)
62
+ */
63
+ #define str2word(c) \
64
+ (((*((uint32_t *)(c))) & 0xFFUL) << 24) | \
65
+ (((*((uint32_t *)(c))) & 0xFF00UL) << 8) | \
66
+ (((*((uint32_t *)(c))) & 0xFF0000UL) >> 8) | \
67
+ (((*((uint32_t *)(c))) & 0xFF000000UL) >> 24)
68
+ #endif
69
+ /**
70
+ Process the buffer once full.
71
+ */
72
+ static inline void perform_all_rounds(sha1_s *s, const uint8_t *buffer) {
73
+ /* collect data */
74
+ uint32_t a = s->digest.i[0];
75
+ uint32_t b = s->digest.i[1];
76
+ uint32_t c = s->digest.i[2];
77
+ uint32_t d = s->digest.i[3];
78
+ uint32_t e = s->digest.i[4];
79
+ uint32_t t, w[16];
80
+ /* copy data to words, performing byte swapping as needed */
81
+ w[0] = str2word(buffer);
82
+ w[1] = str2word(buffer + 4);
83
+ w[2] = str2word(buffer + 8);
84
+ w[3] = str2word(buffer + 12);
85
+ w[4] = str2word(buffer + 16);
86
+ w[5] = str2word(buffer + 20);
87
+ w[6] = str2word(buffer + 24);
88
+ w[7] = str2word(buffer + 28);
89
+ w[8] = str2word(buffer + 32);
90
+ w[9] = str2word(buffer + 36);
91
+ w[10] = str2word(buffer + 40);
92
+ w[11] = str2word(buffer + 44);
93
+ w[12] = str2word(buffer + 48);
94
+ w[13] = str2word(buffer + 52);
95
+ w[14] = str2word(buffer + 56);
96
+ w[15] = str2word(buffer + 60);
97
+ /* perform rounds */
98
+ #define perform_single_round(num) \
99
+ t = left_rotate32(a, 5) + e + w[num] + ((b & c) | ((~b) & d)) + 0x5A827999; \
100
+ e = d; \
101
+ d = c; \
102
+ c = left_rotate32(b, 30); \
103
+ b = a; \
104
+ a = t;
105
+
106
+ #define perform_four_rounds(i) \
107
+ perform_single_round(i); \
108
+ perform_single_round(i + 1); \
109
+ perform_single_round(i + 2); \
110
+ perform_single_round(i + 3);
111
+
112
+ perform_four_rounds(0);
113
+ perform_four_rounds(4);
114
+ perform_four_rounds(8);
115
+ perform_four_rounds(12);
116
+
117
+ #undef perform_single_round
118
+ #define perform_single_round(i) \
119
+ w[(i)&15] = left_rotate32((w[(i - 3) & 15] ^ w[(i - 8) & 15] ^ \
120
+ w[(i - 14) & 15] ^ w[(i - 16) & 15]), \
121
+ 1); \
122
+ t = left_rotate32(a, 5) + e + w[(i)&15] + ((b & c) | ((~b) & d)) + \
123
+ 0x5A827999; \
124
+ e = d; \
125
+ d = c; \
126
+ c = left_rotate32(b, 30); \
127
+ b = a; \
128
+ a = t;
129
+
130
+ perform_four_rounds(16);
131
+
132
+ #undef perform_single_round
133
+ #define perform_single_round(i) \
134
+ w[(i)&15] = left_rotate32((w[(i - 3) & 15] ^ w[(i - 8) & 15] ^ \
135
+ w[(i - 14) & 15] ^ w[(i - 16) & 15]), \
136
+ 1); \
137
+ t = left_rotate32(a, 5) + e + w[(i)&15] + (b ^ c ^ d) + 0x6ED9EBA1; \
138
+ e = d; \
139
+ d = c; \
140
+ c = left_rotate32(b, 30); \
141
+ b = a; \
142
+ a = t;
143
+
144
+ perform_four_rounds(20);
145
+ perform_four_rounds(24);
146
+ perform_four_rounds(28);
147
+ perform_four_rounds(32);
148
+ perform_four_rounds(36);
149
+
150
+ #undef perform_single_round
151
+ #define perform_single_round(i) \
152
+ w[(i)&15] = left_rotate32((w[(i - 3) & 15] ^ w[(i - 8) & 15] ^ \
153
+ w[(i - 14) & 15] ^ w[(i - 16) & 15]), \
154
+ 1); \
155
+ t = left_rotate32(a, 5) + e + w[(i)&15] + ((b & (c | d)) | (c & d)) + \
156
+ 0x8F1BBCDC; \
157
+ e = d; \
158
+ d = c; \
159
+ c = left_rotate32(b, 30); \
160
+ b = a; \
161
+ a = t;
162
+
163
+ perform_four_rounds(40);
164
+ perform_four_rounds(44);
165
+ perform_four_rounds(48);
166
+ perform_four_rounds(52);
167
+ perform_four_rounds(56);
168
+ #undef perform_single_round
169
+ #define perform_single_round(i) \
170
+ w[(i)&15] = left_rotate32((w[(i - 3) & 15] ^ w[(i - 8) & 15] ^ \
171
+ w[(i - 14) & 15] ^ w[(i - 16) & 15]), \
172
+ 1); \
173
+ t = left_rotate32(a, 5) + e + w[(i)&15] + (b ^ c ^ d) + 0xCA62C1D6; \
174
+ e = d; \
175
+ d = c; \
176
+ c = left_rotate32(b, 30); \
177
+ b = a; \
178
+ a = t;
179
+ perform_four_rounds(60);
180
+ perform_four_rounds(64);
181
+ perform_four_rounds(68);
182
+ perform_four_rounds(72);
183
+ perform_four_rounds(76);
184
+
185
+ /* store data */
186
+ s->digest.i[4] += e;
187
+ s->digest.i[3] += d;
188
+ s->digest.i[2] += c;
189
+ s->digest.i[1] += b;
190
+ s->digest.i[0] += a;
191
+ }
192
+
193
+ /* ***************************************************************************
194
+ SHA-1 hashing
195
+ */
196
+
197
+ /**
198
+ Initialize or reset the `sha1` object. This must be performed before hashing
199
+ data using sha1.
200
+ */
201
+ sha1_s bscrypt_sha1_init(void) {
202
+ return (sha1_s){.digest.i[0] = 0x67452301,
203
+ .digest.i[1] = 0xefcdab89,
204
+ .digest.i[2] = 0x98badcfe,
205
+ .digest.i[3] = 0x10325476,
206
+ .digest.i[4] = 0xc3d2e1f0};
207
+ }
208
+
209
+ /**
210
+ Writes data to the sha1 buffer.
211
+ */
212
+ void bscrypt_sha1_write(sha1_s *s, const void *data, size_t len) {
213
+ size_t in_buffer = s->length & 63;
214
+ size_t partial = 64 - in_buffer;
215
+ s->length += len;
216
+ if (partial > len) {
217
+ memcpy(s->buffer + in_buffer, data, len);
218
+ return;
219
+ }
220
+ if (in_buffer) {
221
+ memcpy(s->buffer + in_buffer, data, partial);
222
+ len -= partial;
223
+ data += partial;
224
+ perform_all_rounds(s, s->buffer);
225
+ }
226
+ while (len >= 64) {
227
+ perform_all_rounds(s, data);
228
+ data += 64;
229
+ len -= 64;
230
+ }
231
+ if (len) {
232
+ memcpy(s->buffer + in_buffer, data, len);
233
+ }
234
+ return;
235
+ }
236
+
237
+ char *bscrypt_sha1_result(sha1_s *s) {
238
+ size_t in_buffer = s->length & 63;
239
+ if (in_buffer > 55) {
240
+ memcpy(s->buffer + in_buffer, sha1_padding, 64 - in_buffer);
241
+ perform_all_rounds(s, s->buffer);
242
+ memcpy(s->buffer, sha1_padding + 1, 56);
243
+ } else if (in_buffer != 55) {
244
+ memcpy(s->buffer + in_buffer, sha1_padding, 56 - in_buffer);
245
+ } else {
246
+ s->buffer[55] = sha1_padding[0];
247
+ }
248
+ /* store the length in BITS - alignment should be promised by struct */
249
+ /* this must the number in BITS, encoded as a BIG ENDIAN 64 bit number */
250
+ uint64_t *len = (uint64_t *)(s->buffer + 56);
251
+ *len = s->length << 3;
252
+ #ifndef __BIG_ENDIAN__
253
+ bswap64(*len);
254
+ #endif
255
+ perform_all_rounds(s, s->buffer);
256
+
257
+ /* change back to little endian, if required */
258
+ #ifndef __BIG_ENDIAN__
259
+ bswap32(s->digest.i[0]);
260
+ bswap32(s->digest.i[1]);
261
+ bswap32(s->digest.i[2]);
262
+ bswap32(s->digest.i[3]);
263
+ bswap32(s->digest.i[4]);
264
+ #endif
265
+ // fprintf(stderr, "result requested, in hex, is:");
266
+ // for (size_t i = 0; i < 20; i++)
267
+ // fprintf(stderr, "%02x", (unsigned int)(s->digest.str[i] & 0xFF));
268
+ // fprintf(stderr, "\r\n");
269
+ return (char *)s->digest.str;
270
+ }
271
+
272
+ /*******************************************************************************
273
+ SHA-1 testing
274
+ */
275
+ #if defined(DEBUG) && DEBUG == 1
276
+ #include <time.h>
277
+
278
+ // clang-format off
279
+ #if defined(TEST_OPENSSL) && defined(__has_include) && __has_include(<openssl/sha.h>)
280
+ # include <openssl/sha.h>
281
+ # define HAS_OPEN_SSL 1
282
+ #endif
283
+ // clang-format on
284
+
285
+ void bscrypt_test_sha1(void) {
286
+ struct {
287
+ char *str;
288
+ char hash[21];
289
+ } sets[] = {
290
+ {"The quick brown fox jumps over the lazy dog",
291
+ {0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1,
292
+ 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12,
293
+ 0}}, // a set with a string
294
+ {"",
295
+ {
296
+ 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf,
297
+ 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
298
+ }}, // an empty set
299
+ {NULL, {0}} // Stop
300
+ };
301
+ int i = 0;
302
+ sha1_s sha1;
303
+ fprintf(stderr, "===================================\n");
304
+ fprintf(stderr, "bscrypt SHA-1 struct size: %lu\n", sizeof(sha1_s));
305
+ fprintf(stderr, "+ bscrypt");
306
+ while (sets[i].str) {
307
+ sha1 = bscrypt_sha1_init();
308
+ bscrypt_sha1_write(&sha1, sets[i].str, strlen(sets[i].str));
309
+ if (strcmp(bscrypt_sha1_result(&sha1), sets[i].hash)) {
310
+ fprintf(stderr,
311
+ ":\n--- bscrypt SHA-1 Test FAILED!\nstring: %s\nexpected: ",
312
+ sets[i].str);
313
+ char *p = sets[i].hash;
314
+ while (*p)
315
+ fprintf(stderr, "%02x", *(p++) & 0xFF);
316
+ fprintf(stderr, "\ngot: ");
317
+ p = bscrypt_sha1_result(&sha1);
318
+ while (*p)
319
+ fprintf(stderr, "%02x", *(p++) & 0xFF);
320
+ fprintf(stderr, "\n");
321
+ return;
322
+ }
323
+ i++;
324
+ }
325
+ fprintf(stderr, " SHA-1 passed.\n");
326
+
327
+ #ifdef HAS_OPEN_SSL
328
+ fprintf(stderr, "===================================\n");
329
+ fprintf(stderr, "bscrypt SHA-1 struct size: %lu\n", sizeof(sha1_s));
330
+ fprintf(stderr, "OpenSSL SHA-1 struct size: %lu\n", sizeof(SHA_CTX));
331
+ fprintf(stderr, "===================================\n");
332
+
333
+ unsigned char hash[SHA512_DIGEST_LENGTH + 1];
334
+ hash[SHA512_DIGEST_LENGTH] = 0;
335
+ clock_t start;
336
+ start = clock();
337
+ for (size_t i = 0; i < 100000; i++) {
338
+ sha1 = bscrypt_sha1_init();
339
+ bscrypt_sha1_write(&sha1, "The quick brown fox jumps over the lazy dog ",
340
+ 43);
341
+ bscrypt_sha1_result(&sha1);
342
+ }
343
+ fprintf(stderr, "bscrypt 100K SHA-1: %lf\n",
344
+ (double)(clock() - start) / CLOCKS_PER_SEC);
345
+
346
+ hash[SHA_DIGEST_LENGTH] = 0;
347
+ SHA_CTX o_sh1;
348
+ start = clock();
349
+ for (size_t i = 0; i < 100000; i++) {
350
+ SHA1_Init(&o_sh1);
351
+ SHA1_Update(&o_sh1, "The quick brown fox jumps over the lazy dog", 43);
352
+ SHA1_Final(hash, &o_sh1);
353
+ }
354
+ fprintf(stderr, "OpenSSL 100K SHA-1: %lf\n",
355
+ (double)(clock() - start) / CLOCKS_PER_SEC);
356
+
357
+ #endif
358
+ }
359
+ #endif