ttcrypt 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+