ttcrypt 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,373 @@
1
+ //
2
+ // RsaKey.cpp
3
+ // zcoin
4
+ //
5
+ // Created by Sergey Chernov on 02.06.14.
6
+ // Copyright (c) 2014 thrift. All rights reserved.
7
+ //
8
+
9
+ /*
10
+ This program is free software: you can redistribute it and/or modify
11
+ it under the terms of the GNU General Public License as published by
12
+ the Free Software Foundation, either version 3 of the License, or
13
+ (at your option) any later version.
14
+
15
+ This program is distributed in the hope that it will be useful,
16
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ GNU General Public License for more details.
19
+
20
+ You should have received a copy of the GNU General Public License
21
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
22
+ */
23
+
24
+ #include "rsa_key.h"
25
+ #include "ttcrypt.h"
26
+ #include <assert.h>
27
+ #include <future>
28
+
29
+ using namespace ttcrypt;
30
+ using namespace thrift;
31
+
32
+ namespace ttcrypt {
33
+
34
+ static byte_buffer mgf1(const byte_buffer& z, size_t l) {
35
+ const size_t hLen = 20;
36
+ byte_buffer t;
37
+ for (unsigned i = 0; i <= l / hLen; i++)
38
+ t += sha1(z + i2osp(i, 4));
39
+ return byte_buffer(t, 0, l - 1);
40
+ }
41
+
42
+ byte_buffer eme_oaep_encode(const byte_buffer& message, size_t emLen,
43
+ const byte_buffer& p, const byte_buffer* pseed) {
44
+ const size_t hLen = 20;
45
+ const size_t hLen2 = 2 * hLen;
46
+
47
+ size_t mLen = message.size();
48
+
49
+ if (emLen <= hLen2 + 1)
50
+ throw rsa_key::error("padded length too small");
51
+ if (mLen >= emLen - 1 - hLen2 || mLen > emLen - 2 * hLen2 - 1)
52
+ throw rsa_key::error("message too long");
53
+
54
+ byte_buffer ps = byte_buffer('\0', emLen - mLen - hLen2 - 1);
55
+ byte_buffer pHash = sha1(p);
56
+ byte_buffer db = pHash + ps + "\001" + message;
57
+ byte_buffer seed = (pseed != 0) ? *pseed : byte_buffer::random(hLen);
58
+ byte_buffer dbMask = mgf1(seed, emLen - hLen);
59
+ byte_buffer maskedDb = db ^ dbMask;
60
+ byte_buffer seedMask = mgf1(maskedDb, hLen);
61
+ byte_buffer maskedSeed = seed ^ seedMask;
62
+
63
+ return maskedSeed + maskedDb;
64
+ }
65
+
66
+ byte_buffer eme_oaep_decode(const byte_buffer& message, const byte_buffer& p) {
67
+ const size_t hLen = 20;
68
+ if (message.size() < hLen * 2 + 1)
69
+ throw rsa_key::error("message is too short");
70
+
71
+ byte_buffer maskedSeed(message, 0, hLen - 1);
72
+ byte_buffer maskedDb(message, hLen, -1);
73
+ auto seedMask = mgf1(maskedDb, hLen);
74
+ auto seed = maskedSeed ^ seedMask;
75
+ auto db_mask = mgf1(seed, message.size() - hLen);
76
+ auto db = maskedDb ^ db_mask;
77
+ auto pHash = sha1(p);
78
+
79
+ int index = db.index_of('\001', hLen);
80
+ if (index < 0)
81
+ throw rsa_key::error("message is invalid");
82
+
83
+ byte_buffer pHash2(db, 0, hLen - 1);
84
+ byte_buffer ps(db, hLen, index - 1);
85
+ byte_buffer m(db, index + 1, -1);
86
+
87
+ for (auto x : ps) {
88
+ if (x != '\0')
89
+ throw rsa_key::error("message is invalid");
90
+ }
91
+
92
+ if (pHash2 != pHash)
93
+ throw rsa_key::error("wrong p value");
94
+
95
+ return m;
96
+ }
97
+
98
+ byte_buffer rsa_key::rsadp(const byte_buffer& ciphertext) const {
99
+ big_integer c = big_integer(ciphertext);
100
+ if (c >= n - 1)
101
+ throw rsa_key::error("cipertext is too long");
102
+ if (fast_key) {
103
+ // Fast
104
+ auto m1 = powmod_sec((c % p), dp, p);
105
+ auto m2 = powmod_sec((c % q), dq, q);
106
+ auto h = ((m1 - m2) * q_inv) % p;
107
+ return i2osp(m2 + q * h, byte_size - 1);
108
+ } else {
109
+ // slow c^d mod n
110
+ if (d == 0)
111
+ throw rsa_key::error("missing private key");
112
+ return i2osp(powmod_sec(c, d, n), byte_size - 1);
113
+ }
114
+ }
115
+
116
+ byte_buffer emsa_pss_encode(const byte_buffer& message, size_t emBits,
117
+ byte_buffer (*hash)(const byte_buffer&), const byte_buffer *_salt = 0) {
118
+ auto mHash = hash(message);
119
+ auto hLen = mHash.size();
120
+
121
+ // TODO: implement bits logic!
122
+ auto emLen = (emBits + 7) / 8;
123
+
124
+ // required: emLen < hLen + sLen + 2
125
+ size_t sLen;
126
+ byte_buffer salt;
127
+ if (_salt) {
128
+ salt = *_salt;
129
+ sLen = salt.size();
130
+ } else {
131
+ sLen = emLen - hLen - 2;
132
+ salt = byte_buffer::random(sLen);
133
+ }
134
+
135
+ if (emLen < hLen + sLen + 2)
136
+ throw rsa_key::error("invliad salt length");
137
+
138
+ auto M1 = byte_buffer('\0', 8) + mHash + salt;
139
+ auto H = hash(M1);
140
+ auto PS = byte_buffer('\0', emLen - sLen - hLen - 2);
141
+ auto DB = PS.append_byte(1) + salt;
142
+ auto dbMask = mgf1(H, emLen - hLen - 1);
143
+
144
+ auto maskedDb = DB ^ dbMask;
145
+
146
+ // Clear leftmost bits
147
+ auto clear_bits = 8 * emLen - emBits;
148
+ if (clear_bits > 0)
149
+ maskedDb.set(0, clear_left_bits(maskedDb.at(0), clear_bits));
150
+
151
+ return maskedDb + H + "\xbc";
152
+ }
153
+
154
+ bool emsa_pss_verify(const byte_buffer& source_message,
155
+ const byte_buffer& encoded_message, size_t emBits,
156
+ byte_buffer (*hash)(const byte_buffer&), size_t sLen) {
157
+
158
+ auto mHash = hash(source_message);
159
+ auto emLen = (emBits + 7) / 8;
160
+
161
+ size_t hLen = mHash.size();
162
+ if (sLen == 0)
163
+ sLen = emLen - hLen - 2;
164
+
165
+ if (emLen < hLen + sLen + 2 || encoded_message[-1] != 0xbc)
166
+ return false;
167
+
168
+ byte_buffer maskedDB(encoded_message, 0, emLen - hLen - 2); // range is inclusive!
169
+
170
+ // Check MSB bits are cleared
171
+ auto clear_bits = 8 * emLen - emBits;
172
+ if (clear_bits > 0) {
173
+ byte bitmask = 0x80;
174
+ for (unsigned bit_no = 0; bit_no++ < clear_bits;) {
175
+ if ((maskedDB[0] & bitmask) != 0) // Compiler should optimize this
176
+ return false;
177
+ }
178
+ }
179
+
180
+ byte_buffer H(encoded_message, emLen - hLen - 1, emLen - 2); // range inclusive
181
+ auto dbMask = mgf1(H, emLen - hLen - 1);
182
+
183
+ auto DB = maskedDB ^ dbMask;
184
+
185
+ DB.set(0, clear_left_bits(DB[0], clear_bits));
186
+
187
+ for (unsigned i = 0; i < emLen - hLen - sLen - 2; i++) {
188
+ if (DB[i] != 0) {
189
+ return false;
190
+ }
191
+ }
192
+
193
+ if (DB[emLen - hLen - sLen - 2] != 1)
194
+ return false;
195
+
196
+ byte_buffer salt(DB, -sLen, -1);
197
+
198
+ auto M1 = byte_buffer('\0', 8) + mHash + salt;
199
+ auto H1 = hash(M1);
200
+ return H == H1;
201
+ }
202
+
203
+ // Note that signing does not require blinding - it is not prone to the
204
+ // timing attack
205
+ byte_buffer rsa_key::rsasp1(const byte_buffer &message) const {
206
+ big_integer m = message;
207
+ if (m >= n)
208
+ throw rsa_key::error("message representative is too long");
209
+ if (fast_key) {
210
+ // Fast
211
+ auto s1 = powmod(m % p, dp, p);
212
+ auto s2 = powmod(m % q, dq, q);
213
+ auto h = (s1 - s2) * q_inv % p;
214
+ auto s = s2 + q * h;
215
+ return i2osp(s, byte_size);
216
+ } else {
217
+ // slow
218
+ if (d == 0)
219
+ throw rsa_key::error("missing private key");
220
+ return i2osp(powmod(m, d, n), byte_size);
221
+ }
222
+ }
223
+
224
+ byte_buffer rsa_key::rsavp1(const byte_buffer& signature) const {
225
+ big_integer s = signature;
226
+ if (s > n - 1)
227
+ throw invalid_argument("signature representative too big");
228
+ require_public_key();
229
+ return i2osp(powmod_sec(s, e, n), byte_size);
230
+ }
231
+
232
+ bool rsa_key::verify(const byte_buffer& message, const byte_buffer& signature,
233
+ byte_buffer (*hash)(const byte_buffer&), size_t s_len) const {
234
+ if (signature.size() != byte_size)
235
+ return false;
236
+ try {
237
+ return emsa_pss_verify(message, rsavp1(signature), bits_size - 1, hash,
238
+ s_len);
239
+ } catch (const invalid_argument& e) {
240
+ return false;
241
+ }
242
+ }
243
+
244
+ void rsa_key::normalize_key() {
245
+ if (n == 0)
246
+ n = p * q;
247
+ if ((dp == 0 || dq == 0 || q_inv == 0) && p != 0 && q != 0) {
248
+ dp = inverse(e, p - 1);
249
+ dq = inverse(e, q - 1);
250
+ q_inv = inverse(q, p);
251
+ fast_key = true;
252
+ } else
253
+ fast_key = p != 0 && q != 0 && dp != 0 && dq != 0 && q_inv != 0;
254
+ bits_size = n.bit_length();
255
+ byte_size = (bits_size + 7) / 8;
256
+ }
257
+
258
+ static big_integer prime(unsigned bits) {
259
+ // Set 2 MSB bits to ensire we git enough big pq
260
+ // and calculate margin
261
+ big_integer r = (1_b << (bits - 2)) + (1_b << (bits - 1));
262
+ big_integer s = (1_b << bits) - 1;
263
+ while (1) {
264
+ auto p = next_prime(big_integer::random_between(r, s));
265
+ if (p <= s)
266
+ return p;
267
+ // loop if prime is too big (unlikely)
268
+ }
269
+ throw logic_error("failed prime generation");
270
+ }
271
+
272
+ rsa_key rsa_key::generate(unsigned int k, size_t e) {
273
+ if (e == 0)
274
+ e = 0x10001;
275
+
276
+ if (e < 3 || (e != 0x10001 && !big_integer(e).is_prime()))
277
+ throw rsa_key::error("exponent should be prime number >= 3");
278
+
279
+ // v2.2 Algorithm
280
+ while (true) {
281
+ auto future = std::async(std::launch::async, [k] {return prime(k/2);});
282
+ auto q = prime(k - k / 2);
283
+ auto p = future.get();
284
+ if (p == q)
285
+ continue;
286
+
287
+ auto n = p * q;
288
+ if (n.bit_length() != k) {
289
+ // logic error: bit length mismatch, regenerating
290
+ continue;
291
+ }
292
+
293
+ auto Ln = lcm(p - 1, q - 1);
294
+ if (gcd(e, Ln) != 1)
295
+ continue;
296
+
297
+ if (p > q)
298
+ swap(p, q);
299
+
300
+ return rsa_key(n, e, p, q);
301
+ }
302
+ throw logic_error("pq generation failed");
303
+ }
304
+
305
+ void rsa_key::set(string name, const thrift::big_integer &value) {
306
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
307
+ switch (name[0]) {
308
+ case 'e':
309
+ e = value;
310
+ break;
311
+ case 'p':
312
+ this->p = value;
313
+ break;
314
+ case 'q':
315
+ if (name == "qinv" || name == "q_inv")
316
+ q_inv = value;
317
+ else
318
+ q = value;
319
+ break;
320
+ case 'n':
321
+ n = value;
322
+ break;
323
+ case 'd':
324
+ if (name == "dp") {
325
+ dp = value;
326
+ } else if (name == "dq") {
327
+ dq = value;
328
+ } else
329
+ d = value;
330
+ break;
331
+ default:
332
+ throw error("unknown paramerer ");
333
+ break;
334
+ }
335
+ }
336
+
337
+ unordered_map<string, big_integer> rsa_key::get_params(bool include_all) const
338
+ noexcept {
339
+ unordered_map<string, big_integer> params;
340
+ if (n != 0) {
341
+ params["n"] = n;
342
+ }
343
+ if (e != 0) {
344
+ params["e"] = e;
345
+ }
346
+ if (p != 0) {
347
+ params["p"] = p;
348
+ }
349
+ if (q != 0) {
350
+ params["q"] = q;
351
+ }
352
+
353
+ if (d != 0 && ((p == 0 && q == 0) || include_all)) {
354
+ params["d"] = q;
355
+ }
356
+
357
+ if (dp != 0 && include_all) {
358
+ params["dp"] = dp;
359
+ }
360
+
361
+ if (dq != 0 && include_all) {
362
+ params["dq"] = dq;
363
+ }
364
+
365
+ if (q_inv != 0 && include_all) {
366
+ params["qinv"] = q_inv;
367
+ }
368
+
369
+ return params;
370
+ }
371
+
372
+ }
373
+
@@ -0,0 +1,248 @@
1
+ //
2
+ // RsaKey.h
3
+ //
4
+ // Created by Sergey Chernov on 02.06.14.
5
+ // Copyright (c) 2014 thrift. All rights reserved.
6
+ //
7
+
8
+ /*
9
+ This program is free software: you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation, either version 3 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+ #ifndef __zcoin__RsaKey__
24
+ #define __zcoin__RsaKey__
25
+
26
+ #include <iostream>
27
+ #include <unordered_map>
28
+ #include "byte_buffer.h"
29
+ #include "big_integer.h"
30
+ #include "ttcrypt.h"
31
+
32
+ namespace ttcrypt {
33
+
34
+ using namespace thrift;
35
+
36
+ byte_buffer eme_oaep_encode( const byte_buffer& message,size_t emLen, const byte_buffer& p=byte_buffer(), const byte_buffer *seed=nullptr);
37
+ byte_buffer eme_oaep_decode(const byte_buffer& message,const byte_buffer& p="");
38
+
39
+ byte_buffer emsa_pss_encode(const byte_buffer& message,size_t emBits,byte_buffer (*hash)(const byte_buffer&),const byte_buffer* salt);
40
+ bool emsa_pss_verify(const byte_buffer& source_message,
41
+ const byte_buffer& encoded_message,
42
+ size_t emBits,
43
+ byte_buffer (*hash)(const byte_buffer&),
44
+ size_t debug_sLen=0);
45
+
46
+ /**
47
+ PKCS#1 v2.2 RSA algorythm (only STRONG encoding e.g. OAEP/PSS, weak 1.5 is NOT supported by purpose!). Full implementation
48
+ (key generation, construction from parts, encryption, signing).
49
+ */
50
+ class rsa_key {
51
+ public:
52
+
53
+ class error : public invalid_argument {
54
+ public:
55
+ error(const char* reason) : invalid_argument(reason){}
56
+ };
57
+
58
+ /**
59
+ Construct from { {name, value} } paris, like { { "e", 123 }, { "n":data_n } }. @see #set().
60
+ */
61
+ rsa_key(const std::initializer_list<std::pair<string, big_integer>>& params)
62
+ {
63
+ set_params(params);
64
+ }
65
+
66
+ /**
67
+ Construct from any map-like container that provides pair iterator with pair.first
68
+ and pair.second members. @see #set()
69
+ */
70
+ template <class Tmap>
71
+ rsa_key(const Tmap& map)
72
+ {
73
+ set_params(map);
74
+ }
75
+
76
+ rsa_key()
77
+ {}
78
+
79
+ /**
80
+ Set the named parameter to a given value. Supported values are: n, e, d, p, q, dp, dq, qinv.
81
+ Not case sesitive. Call normalize_key() when done changing parameters.
82
+ */
83
+ void set(string name, const big_integer& value);
84
+
85
+ /**
86
+ Construct private key from parts. If not all parts are provided, recalculates missing ones that
87
+ is somewhat slow.
88
+ */
89
+ rsa_key(const big_integer& N, const big_integer& E, const big_integer& P,const big_integer& Q,
90
+ const big_integer& dP=0, const big_integer& dQ=0, const big_integer& qInv=9)
91
+ : n(N), e(E), p(P), q(Q), dp(dP), dq(dQ), q_inv(qInv)
92
+ {
93
+ normalize_key();
94
+ }
95
+
96
+ /**
97
+ Construct "slow" ptivate key from parts.
98
+ */
99
+ rsa_key(const big_integer& N,const big_integer& E, const big_integer& D) : n(N), e(E), d(D), fast_key(false) {
100
+ normalize_key();
101
+ }
102
+
103
+ /**
104
+ Construct public key.
105
+ */
106
+ rsa_key(const big_integer& N,const big_integer& E) : n(N), e(E) { normalize_key(); }
107
+
108
+ /**
109
+ Test that private key present
110
+ */
111
+ bool is_private() const {
112
+ return (p != 0 && q != 0) || d != 0;
113
+ }
114
+
115
+ /**
116
+ construct and return public key (strip private component if any)
117
+ */
118
+ rsa_key public_key() const {
119
+ return rsa_key(n,e);
120
+ }
121
+
122
+ /**
123
+ RSAES-OAEP encrypt a message.
124
+ */
125
+ byte_buffer encrypt(const byte_buffer& plaintext) const {
126
+ return rsaep(eme_oaep_encode(plaintext, byte_size - 1,"", pseed));
127
+ }
128
+
129
+ /**
130
+ RSAES-OAEP decrypt a given cipertext. requires private key.
131
+ */
132
+ byte_buffer decrypt(const byte_buffer& ciphertext) const {
133
+ return eme_oaep_decode( rsadp( ciphertext) );
134
+ }
135
+
136
+ /**
137
+ Create RSASSA-PSS signature for a given message. Requires private key.
138
+ @param message what to sign
139
+ @param hash default to sha256
140
+ @param salt to use with a given salt. By default, uses random salt of maximum available length. When
141
+ using custom salt, you'll need to provide its length on verification (s_len parameter)
142
+
143
+ */
144
+ byte_buffer sign(const byte_buffer& message, byte_buffer (*hash)(const byte_buffer&)=sha256, const byte_buffer* salt=0) const {
145
+ return rsasp1(emsa_pss_encode(message, bits_size-1, hash, salt));
146
+ }
147
+
148
+ /**
149
+ Verify RSASSA-PSS signature.
150
+
151
+ @param message message to verify
152
+ @param signature PSS signature of the message
153
+ @param hash sha1, sha256 or other hash function (by default sha256)
154
+ @param s_len if manual salt was used, provide its length. by default, salt uses
155
+ all available space left.
156
+ @return true if the signature is consistent with the message, false otherwise (message is tampered or the signature
157
+ is broken)
158
+ */
159
+ bool verify(const byte_buffer& message,
160
+ const byte_buffer& signature,
161
+ byte_buffer (*hash)(const byte_buffer&)=sha256,
162
+ size_t s_len=0) const;
163
+
164
+ /** :nodoc: set seed to debug OAEP encryption. Please DO NOT use
165
+ */
166
+ void debug_seed(const byte_buffer& s) {
167
+ pseed = &s;
168
+ }
169
+
170
+ /**
171
+ @return key size in bits
172
+ */
173
+ unsigned size_in_bits() const { return bits_size; }
174
+
175
+ /**
176
+ @return key size in bytes
177
+ */
178
+ unsigned size_in_bytes() const { return byte_size; }
179
+
180
+ /**
181
+ Generate new key pair of the specified strength. If the system has more than one CPU core,
182
+ use 2 cores to generate key parts.
183
+ @param bits_strength desired strength in bits. it is recommended to use ar least 2048 bits and
184
+ sizes that are multiplication of 256.
185
+ @param e public exponent, positive integer, this implementation requires it to be prime >= 3.
186
+ default of 0 uses "good" value 0x10001.
187
+ */
188
+ static rsa_key generate(unsigned bits_strength,size_t e=0);
189
+
190
+
191
+ /**
192
+ Update key parameters from map-like container that provides pair iterator with pair.first
193
+ and pair.second members. @see #set()
194
+ */
195
+ template <class Tmap>
196
+ void set_params(const Tmap& map) {
197
+ for( auto pair: map ) {
198
+ set(pair.first, pair.second);
199
+ }
200
+ normalize_key();
201
+ }
202
+
203
+ unordered_map<string,big_integer> get_params(bool include_all = false) const noexcept;
204
+
205
+ /**
206
+ recalculate parts of the key after chaging it with
207
+ #set().
208
+ */
209
+ void normalize_key();
210
+
211
+ /**
212
+ Turn on or off use of blinding algorithm (that repels timing attack) on decrypt and verify
213
+ routines only, as if makes it slower the longer the key is used.
214
+ */
215
+ void use_blinding(bool use) noexcept {
216
+ _use_blinding = use;
217
+ }
218
+
219
+ private:
220
+ big_integer n, e, p, q, d, dp, dq, q_inv;
221
+ unsigned byte_size=0, bits_size=0;
222
+ bool fast_key=false, _use_blinding=false;
223
+
224
+ const byte_buffer *pseed = NULL;
225
+
226
+ big_integer powmod_sec(const big_integer& x, const big_integer& y, const big_integer& mod) const noexcept {
227
+ return _use_blinding ? ::powmod_sec(x, y, mod) : ::powmod(x, y, mod);
228
+ }
229
+
230
+ byte_buffer rsaep(const byte_buffer& plaintext) const {
231
+ auto m = os2ip(plaintext);
232
+ require_public_key();
233
+ return i2osp(powmod(m, e, n), byte_size); // Encryption does not need blinding!
234
+ }
235
+
236
+ void require_public_key() const {
237
+ if( n == 0 || e == 0 )
238
+ throw error("missing public key");
239
+ }
240
+
241
+ byte_buffer rsasp1(const byte_buffer& m) const;
242
+ byte_buffer rsavp1(const byte_buffer& s) const;
243
+
244
+ byte_buffer rsadp(const byte_buffer& c) const;
245
+ };
246
+ }
247
+
248
+ #endif /* defined(__zcoin__RsaKey__) */
@@ -0,0 +1,66 @@
1
+ /*
2
+ * ruby_cpp_tools.cpp
3
+ *
4
+ * Created on: 18 июня 2014 г.
5
+ * Author: sergeych
6
+ * Copyright (C) 2014 Thrift, Sergey S. Chernov
7
+ *
8
+ This program is free software: you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation, either version 3 of the License, or
11
+ (at your option) any later version.
12
+
13
+ This program is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ */
21
+
22
+ #include <iostream>
23
+ #include "ruby_cpp_tools.h"
24
+
25
+ struct unblock_data {
26
+ unblock_data(const std::function<void*()> &_block) :
27
+ block(_block) {
28
+ }
29
+ const std::function<void*()> &block;
30
+ std::exception_ptr exception_ptr = NULL;
31
+ };
32
+
33
+ extern "C" {
34
+ static void* unblock_executor(void* ptr) {
35
+ unblock_data *data = (unblock_data*) ptr;
36
+ try {
37
+ return data->block();
38
+ } catch (...) {
39
+ data->exception_ptr = std::current_exception();
40
+ }
41
+ return 0;
42
+ }
43
+ }
44
+
45
+ VALUE ruby_unblock2(const std::function<void*()>& block) {
46
+ unblock_data d(block);
47
+ VALUE ret = (VALUE) rb_thread_call_without_gvl(unblock_executor, &d,
48
+ NULL, NULL);
49
+
50
+ if (d.exception_ptr)
51
+ std::rethrow_exception(d.exception_ptr);
52
+
53
+ return ret;
54
+ }
55
+
56
+ void ruby_unblock(const std::function<void(void)>& block) {
57
+ unblock_data d((std::function<void*()>&) block);
58
+ rb_thread_call_without_gvl(unblock_executor, &d,
59
+ NULL, NULL);
60
+
61
+ if (d.exception_ptr)
62
+ std::rethrow_exception(d.exception_ptr);
63
+ }
64
+
65
+
66
+